历史上的今天
今天是:2025年05月24日(星期六)
2019年05月24日 | STM32F10X ADC多通道读取小教程(包含DMA)
2019-05-24 来源:eefocus
前沿:
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的使用,有很多小技巧,需要平时留意!
史海拾趣
|
为广大读者奉献了众多内容翔实,且各具特色的智能家居案例及解决方案,在此我们为大家精挑细选出其中最具代表性的十篇案例。 一;视得安罗格朗Axolute智能家居案例 二;Axolute智能家居DIY方案 Axolute智能家居 ...… 查看全部问答> |
|
关于wince5 串口中断服务函数SerialDispatchThread的疑问 在SerialDispatchThread函数中有下面语句,请问: /* Wait for the event that any serial port action creates. */ while ( !pSerialHead->KillRxThread ) { DEB ...… 查看全部问答> |
|
用wince下的串口驱动移植的。 写完测试的时候都是在16字节以内进行的,收发正常。现在PC端每次发送超过16字节的数据,我的WINCE设备这边只能收到16字节,每次发送超过16字节的就丢失了。 芯片用得是16554. 这个可能是什么原因啊? 很奇怪我把FIFO ...… 查看全部问答> |
|
本帖最后由 paulhyde 于 2014-9-15 04:05 编辑 欢迎各位在本贴讨论msp430f149使用的相关经验,需要149的相关资料,可以留言。LCD1602,1CD12864,ADC12,DAC7512,TTF2.4,PID,资料很多, … 查看全部问答> |
|
UART1串口只能接收16个字节的数据,超过16个字节的数据全部丢失 void uart1IsrInit(void) { INT16U usFdiv; U1LCR = 0x87; & ...… 查看全部问答> |
|
stm32f103做IAP功能, 程序在仿真的时候是能正常跳转到APP程序执行,复位后程序不能正常从boot跳转到刷好的APP程序执行 有什么解决办法么 求帮助… 查看全部问答> |




