历史上的今天
返回首页

历史上的今天

今天是: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的使用,有很多小技巧,需要平时留意! 

推荐阅读

史海拾趣

Altus Technology Inc公司的发展小趣事

Altus Technology Inc自成立以来,始终将技术创新作为公司发展的核心驱动力。在早期,公司研发团队通过不懈努力,成功开发出了一款具有革命性的芯片,这款芯片在性能和功耗上均优于当时的同类产品。凭借这一技术突破,Altus在市场上迅速获得了一席之地,并吸引了大量合作伙伴和投资人的关注。随着技术的不断迭代和升级,Altus逐渐在电子行业中树立了技术领先的形象,并持续推出了一系列创新产品,巩固了市场地位。

DMS Electronic Components, Inc公司的发展小趣事

DMS自创立之初,就致力于电子元器件的技术创新。公司投入大量研发资源,成功开发出一系列高性能、低成本的电子元器件,这些产品迅速在市场上获得了认可。随着技术的不断进步,DMS不断推出新产品,满足了客户日益增长的需求,逐渐在电子行业中树立了技术领先的形象。

COTO TECHNOLOGY公司的发展小趣事

随着业务的不断扩展,COTO TECHNOLOGY开始寻求全球范围内的合作与发展。1998年,公司收购了位于荷兰的Philips干簧开关事业部,组建了Coto Technology B.V.,进一步增强了其在全球市场的竞争力。此后,COTO还通过与其他企业的战略合作,不断拓宽业务领域,实现了从单一产品制造商向综合性电子解决方案提供商的转变。

远东福斯特公司的发展小趣事

近年来,随着全球电子产业的快速发展,中国市场的重要性日益凸显。COTO TECHNOLOGY也看到了这一机遇,开始深耕中国市场,寻求与当地企业的合作。通过与成都迈极芯科技等公司的战略合作,COTO不仅将先进的技术和产品引入中国,还为中国半导体测试产业提供了更优质的产品和服务。这一系列的合作不仅促进了COTO在中国市场的发展,也推动了中国电子产业的进步。

这五个故事只是COTO TECHNOLOGY发展历程中的一部分,但它们充分展示了这家公司在电子行业中的成长与变迁。从初创时期的线圈绕组制造商,到后来的干簧继电器领导者,再到全球范围内的扩张与战略合作,COTO始终保持着对技术的追求和对市场的敏锐洞察。这些故事不仅反映了COTO的发展历程,也见证了整个电子行业的变革与进步。

HN Electronic Components GmbH & Co Kg公司的发展小趣事

背景:2008年全球金融危机爆发,电子行业受到重创,市场需求大幅下降。

发展:面对严峻的市场环境,HN Electronics迅速调整战略,削减非核心业务,加强成本控制,并加大在研发领域的投入,以技术创新为突破口,寻找新的增长点。

关键事件:在金融危机期间,HN Electronics成功开发出具有更高性价比的物联网传感器芯片,满足了市场对智能家居、智慧城市等新兴领域的需求,从而实现了业绩的稳步增长。

EDAC公司的发展小趣事

面对数字化浪潮的冲击,ECS-D公司积极拥抱数字化转型,通过引入先进的信息技术和智能化设备,提升企业的运营效率和创新能力。公司建立了数字化管理平台,实现了生产、销售、财务等各个环节的信息化管理。同时,ECS-D公司还加强了对员工的数字化培训和教育,提升员工的数字化素养和创新能力。这些数字化转型的举措使ECS-D公司在激烈的市场竞争中保持了领先地位,也为公司的未来发展奠定了坚实的基础。

以上五个故事均以ECS公司的发展为主题,分别从不同角度描述了这些公司在电子行业中的成长历程和成功经验。这些故事旨在展示ECS公司如何通过技术创新、全球化战略、品质管理、绿色环保理念和数字化转型等方式实现持续发展和壮大。

问答坊 | AI 解惑

Windows CEnet的串口通讯类设计.pdf

Windows CEnet的串口通讯类设计.pdf…

查看全部问答>

智能家居十大精选案例

为广大读者奉献了众多内容翔实,且各具特色的智能家居案例及解决方案,在此我们为大家精挑细选出其中最具代表性的十篇案例。      一;视得安罗格朗Axolute智能家居案例 二;Axolute智能家居DIY方案 Axolute智能家居 ...…

查看全部问答>

关于wince5 串口中断服务函数SerialDispatchThread的疑问

在SerialDispatchThread函数中有下面语句,请问: /* Wait for the event that any serial port action creates.      */     while ( !pSerialHead->KillRxThread ) {         DEB ...…

查看全部问答>

对ldr指令的疑惑

(1) adr         r0, _start                   /* 把_start的相对地址移到r0       */ ldr       & ...…

查看全部问答>

串口驱动只能接收16字节,16字节后的数据丢失问题?

用wince下的串口驱动移植的。 写完测试的时候都是在16字节以内进行的,收发正常。现在PC端每次发送超过16字节的数据,我的WINCE设备这边只能收到16字节,每次发送超过16字节的就丢失了。 芯片用得是16554. 这个可能是什么原因啊? 很奇怪我把FIFO ...…

查看全部问答>

C指针学习,求指点!

本人菜鸟一个,指针不是很好,是应该去好好研究《C与指针》呢,还是去边读代码边学习呢???望高手指点一二!在此谢过…

查看全部问答>

MSP430f149技术讨论贴

本帖最后由 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;                           & ...…

查看全部问答>

继续 代码大全(2)

上一个代码大全的帖子 已经到了8页了,有点长。 于是决定写一个新帖。 同样希望以后写的更简洁,更有说服力。…

查看全部问答>

做IAP远程更新程序

stm32f103做IAP功能, 程序在仿真的时候是能正常跳转到APP程序执行,复位后程序不能正常从boot跳转到刷好的APP程序执行 有什么解决办法么 求帮助…

查看全部问答>