STM32F103单片机使用DMA功能读取ADC采样数据

860 阅读3分钟

  小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

  本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

  使用DMA功能操作外设时,可以极大的简化代码,提高程序的执行效率。特别是在需要频繁操作的外设上。比如现在要采集单片机16个ADC通道的电压值,就可以使用DMA功能,直接将ADC通道转换好的值,传输到数组中。需要操纵ADC的值时,直接去数组中拿数据就行。不需要再去判断ADC数据转换是否结束。下面直接通过代码来实现。

  首先初始化ADC,这里将ADC的16个采样通道全部开启。


void ADC1_Init ( void )
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef  ADC_InitStructure;  
	
    RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO, ENABLE );
    RCC_ADCCLKConfig ( RCC_PCLK2_Div6 );		// 72M / 6 = 12M  ADC最大不能超过14M
    
    // ADC1 --- ADC8
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init ( GPIOA, &GPIO_InitStructure );
    // ADC9 ADC10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init ( GPIOB, &GPIO_InitStructure );
   // ADC11 --- ADC 16
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init ( GPIOC, &GPIO_InitStructure );
    
    ADC_DeInit ( ADC1 );
   
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;					//独立模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;						//扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;					//连续转换
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;				//数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 16;								//ADC通道数
    ADC_Init ( ADC1, &ADC_InitStructure );
    
    //规则通道配置
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_4, 5, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_6, 7, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_7, 8, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_8, 9, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_9, 10, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_10, 11, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_11, 12, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_12, 13, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_13, 14, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_14, 15, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig ( ADC1, ADC_Channel_15, 16, ADC_SampleTime_239Cycles5 );
    
   ADC_DMACmd ( ADC1, ENABLE );					//使能ADC DMA传输
   
    ADC_Cmd ( ADC1, ENABLE );						//使能ADC1
	
    ADC_ResetCalibration ( ADC1 );					//使能复位校准
    while ( ADC_GetResetCalibrationStatus ( ADC1 ) );	//等待复位校准结束
	
    ADC_StartCalibration ( ADC1 );					//开启AD校准
    while ( ADC_GetCalibrationStatus ( ADC1 ) );	//等待校准结束
//    ADC_SoftwareStartConvCmd ( ADC1, ENABLE );	//启动转换
}

ADC设置为扫描模式,连续转换,16个通道依次扫描采样,一轮采样结束后,接着开启下一轮采样。

  下面初始化DMA功能。

void MYDMA_Config ( DMA_Channel_TypeDef *DMA_CHx, u32 cpar, u32 cmar, u16 cndtr )
{
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd ( RCC_AHBPeriph_DMA1, ENABLE );
	
    DMA_DeInit ( DMA_CHx );
	
    DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;			//外设基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = cmar;				//内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;			//数据传输从外设到内存
    DMA_InitStructure.DMA_BufferSize = cndtr;					//DMA缓存
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;	//外设地址不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;				//内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;			//内存地址1位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;				//循环工作模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;			//高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;				//没有设置内存到内存传输
	
    DMA_Init ( DMA_CHx, &DMA_InitStructure );
}

这里DMA的数据方向要设置为从外设到内存,也就是将外设ADC采样的数据结果保存到内部存储空间中。

初始化完成之后,在主函数中直接启动DMA转换功能。

#define ADC1_DR_Address    ((uint32_t)0x4001244C)
u16 AD_Value[6];
float volta[16];
u8 i, j;
uint16_t ADC1ConvertedValue[10][16];
uint32_t ADC1ConvertedVoltage[16];
int main ( void )
{
    delay_init();       //延时函数初始化
    NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 );
    uart_init ( 115200 );
    LED_Init();         //初始化与LED连接的硬件接口
    MYDMA_Config ( DMA1_Channel1, ( u32 ) &ADC1->DR, ( u32 ) &ADC1ConvertedValue, 160 );
    ADC1_Init();
    DMA_Cmd ( DMA1_Channel1, ENABLE );          //启动DMA通道
    ADC_SoftwareStartConvCmd ( ADC1, ENABLE );  //启动ADC1软件转换
    while ( 1 )
    {
        ADC1ConvertedVoltage[0] = 0;
        for ( i = 0; i < 16; i++ )
        {
            ADC1ConvertedVoltage[i] = 0;
            for ( j = 0; j < 10; j++ )			//采样10次取平均值
            {
                ADC1ConvertedVoltage[i] += ADC1ConvertedValue[j][i];
            }
            ADC1ConvertedVoltage[i] = ADC1ConvertedVoltage[i] / 10;
        }
		for(i=0;i<16;i++)
		{
			volta[i] = ADC1ConvertedVoltage[i] * 3.3 / 4096;
			printf("AD%2d:  value: %4d  vol: %f\r\n",i,ADC1ConvertedVoltage[i],volta[i]);

		}
		 printf ( "\r\n\r\n" );
       
        LED0 = 0;
        LED1 = 1;
        delay_ms ( 500 ); //延时300ms
        LED0 = 1;
        LED1 = 0;
        delay_ms ( 500 ); //延时300ms
    }
}

通过一个二维数据存储16个通道的转换值,每个通道存储10个数据然后取平均值。最后将采样的16个通道值依次打印出来。