您的位置:首页 > 其它

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

2018-02-19 13:42 316 查看
前沿:        今天是雨水,哎可是我这还是大太阳!!!
    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的结果可以左对齐或右
eef7
对齐方式存储在 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.667usADC_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