×
单片机 > 单片机程序设计 > 详情

STM32F10X ADC多通道读取小教程(包含DMA)

发布时间:2020-06-16 发布时间:
|

前沿:


ADC采样,说白了就是采集电压,这个功能是极其重要的,通常的我们的都是对各种传感器采集电压,来进行判断,开环闭环控制,今天,向大家介绍ADC的多通道采样,和DMA的采样方式。DMA的采样方式,可有效节省CMU在ADC的运行时间,提高效率,尤其是在系统构建的时候,必须要考虑好,你的效率问题。


这里我用的开发板时正点原子的MiniSTM32,芯片型号为STM32F103RCT6.   This is easy, so you can do that very easily!

[objc] view plain copy

***REMENBER STM32 is you! :)  

我们在这里进行通俗的讲解,详细的内容,可以在以后再去看STM32手册。

了解ADC:


有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行 ;ADC的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中 ;模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高 / 低阈值。


接下来让我们分析两种方式(注意这里我们这里的代码是连续的):

第一种:

void adc_Config(void){

 

ADC_InitTypeDef ADC_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);

RCC_ADCCLKConfig(RCC_PCLK2_Div6);    //这里ADC分频,ADC工作频率最高不超过14M

这里我们使用ADC1 ,相应的打开时钟,最后一步很重要,就是要给ADC分频,有个要求就是ADC工作频率不能超过14MHz


GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;

GPIO_InitStructure.GPIO_Pin  =GPIO_Pin_1;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA,&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;

GPIO_InitStructure.GPIO_Pin  =GPIO_Pin_3;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA,&GPIO_InitStructure);

以上初始化使到的Pin,有PA1 --ADC1的通道1,PA3 --ADC1的通道3 GPIOMODE 为 GPIO_Mode_AIN 模拟输入! 

  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

ADC_InitStructure.ADC_ScanConvMode = DISABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

ADC_InitStructure.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None;

ADC_InitStructure.ADC_NbrOfChannel = 1;

ADC_Init(ADC1,&ADC_InitStructure);

这里进行ADC配置,同样的这里有些东西非常讲究! ADC模式我们就采用独立模式ADC_Mode_Independent,接下来我们会看到是否使用扫描模式ADC_ScanConvMode,是否使用连续模式ADC_ContinuousConvMode,在多通道的时候,我们会考虑扫描模式,就是多通道按照顺序一个个扫描过来,一般的我们会结合DMA一起使用,连续模式就是,在触发一次后是否进行连续读取ADC的值。这里我们都不使能,相关的使用内容,在后面DMA例子中说明。ADC_DataAlign_Right是指数据对其方式:

比如说AD转换后数字量保存在ADCH,ADCL两个寄存器中

左对齐就是AD值的最高位就是ADCH的最高位了,ADCL的低位就会有的用不到,读出来就为0

右对齐就是AD值的最低位是ADCL的最低位,而ADCH的高位就会有的用不到,读出来也为0

左对齐:11111111   11110000

             MSB                       LSB

右对齐:00001111   11111111

             MSB                        LSB

这里一般的我们选择右对齐! 左对齐的话,由于ADC采样为12位,最大为0x0FFF,所以左对齐  得到X>>4才是真实的值。

这里我们都是采用的软件的触发方式,所以,外部触发就不使用,就是ADC_ExternalTrigConv_None

  ADC_Cmd(ADC1,ENABLE);

  

ADC_ResetCalibration(ADC1);

while(ADC_GetResetCalibrationStatus(ADC1));

ADC_StartCalibration(ADC1);

  while(ADC_GetCalibrationStatus(ADC1));

}

这里使能ADC1, 同时的还有必要操作,就是ADC的校准,先复位后校准!这里配置函数依据结束!接下来为读取函数:



float getADC_Val(uint8_t channel){

    

  uint16_t tempdata;

  float out_val;  

    ADC_RegularChannelConfig(ADC1,channel,1,ADC_SampleTime_55Cycles5);

  ADC_SoftwareStartConvCmd(ADC1,ENABLE);

  

  while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));    //等待读取结束

    tempdata =  ADC_GetConversionValue(ADC1);

  

  out_val =tempdata*3.3/0xfff;

  return out_val;

}

参数:

这里由于我们不扫面,也不连续读取,要得时候在读取,所以每次读取都要初始化通道,并且软件触发一下: 

ADC_RegularChannelConfig 规则组初始化,第三个参数为规则组的排序号,最后的参数为采样周期55.5:计算采样时间为(周期+12.5)/(72M/分频) = 时间  ,这里为  (55.5+12.5)/(72/6)=5.667us

ADC_SoftwareStartConvCmd 这里软件触发

ADC_GetConversionValue读取相应的值

out_val =tempdata*3.3/0xfff;

这里的转化为固定格式,12位ADC读取电压0~3.3V ,很好理解。

以上为多通道的非DMA模式,接下来是DMA模式:


//@作者:junwencui

//@weather:sunny

 __IO uint16_t ADC_GetValue[2]={0,0};

 

 

 void pADCInit(uint8_t length){    

    DMA_InitTypeDef DMA_InitStructure;

ADC_InitTypeDef ADC_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

DMA_DeInit(DMA1_Channel1);

 

DMA_InitStructure.DMA_PeripheralBaseAddr= (u32)(&(ADC1->DR));

DMA_InitStructure.DMA_MemoryBaseAddr=(u32)ADC_GetValue;

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

DMA_InitStructure.DMA_BufferSize = length;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

 

DMA_Init(DMA1_Channel1,&DMA_InitStructure);

 

DMA_Cmd(DMA1_Channel1,ENABLE);

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=length;

 

ADC_Init(ADC1,&ADC_InitStructure);

 

RCC_ADCCLKConfig(RCC_PCLK2_Div6);    

 

ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_55Cycles5); 

  ADC_RegularChannelConfig(ADC1,ADC_Channel_3,2,ADC_SampleTime_55Cycles5); 

ADC_DMACmd(ADC1,ENABLE);

ADC_Cmd(ADC1,ENABLE);

 

ADC_ResetCalibration(ADC1);

while(ADC_GetResetCalibrationStatus(ADC1));

ADC_StartCalibration(ADC1);

while(ADC_GetCalibrationStatus(ADC1));

    

ADC_SoftwareStartConvCmd(ADC1,ENABLE);

}


这里使用DMA模式,更具32手册,得到 ADC1对应DMA1的通道1:

也会发现,现在我们使用的都是扫描模式,和连续触发模式。工作时,就自动的按照规则组的排序一个个读取过来,并且重复此工作,不占用CMU, 

规则组排序读取配置:


  ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_55Cycles5);


  ADC_RegularChannelConfig(ADC1,ADC_Channel_3,2,ADC_SampleTime_55Cycles5);

对于DMA的使用这里不在阐述。可以看手册和结构体含义理解使用方式!!!!

val =  (float)ADC_GetValue[channel]/4096*3.3;

DMA数据会存储在ADC_GetValue 中,使用的时候输入01234等ADC_GetValue数组的序号,就可以方便读取!



写到这里,就差不多了!


对于ADC的使用,有很多小技巧,需要平时留意! 

关键字:STM32F10X  ADC  多通道读取  DMA 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32单片机的复用端口初始化的步骤及方法