历史上的今天
返回首页

历史上的今天

今天是:2024年10月09日(星期三)

正在发生

2019年10月09日 | STM32ADC使用方法解析

2019-10-09 来源:eefocus

因项目需要使用到STM32的ADC功能,虽然对ADC的使用并不陌生,但是第一接触stm32的ADC功能还是有种无从下手的感觉,主要是因为STM32ADC设计较为复杂,一时对相关的专业术语较为生疏,此外固件库中涉及到的函数虽都进行了分门别类但是还是感觉函数较多,难以很快掌握,现就将个人理解写出,以便大家共同进步。


一、stm32 ADC特点


认识事物首先还是要从整体上把握,ADC也一样的道理,STM32的ADC外设提供了非常完善的功能,虽然其中很大一部分不见得能在项目中使用,但加深对其的认识对于合理利用其资源也是非常有必要的。


下面以项目采用的STM32L151为例,其他类型大同小异。参考RM0038 Reference manual(CD00240193.pdf)


先看原文:


• 12-bit, 10-bit, 8-bit or 6-bit configurable resolution  

• Interrupt generation at the end of regular conversions, end of injected conversions, and in case of analog watchdog or overrun events (for regular conversions)


//在规则转换结束、注入转换结束以及模拟看门狗溢出和overrun事件(规则转换模式)发生时能产生中断


• Single and continuous conversion modes //有单次和连续转换模式

• Scan mode for automatic conversions in a fully programmable order  //在自动转换中支持完成可编程的扫描模式

• Programmable data alignment with in-built data coherency //数据对齐方式可编程

• Programmable and individual sampling time for each ADC channel  //可以为每个通道设置独立的采样时间

• External trigger option with configurable edge detection for both regular and injected

conversions  //外部触发模式对规则和注入转换模式均支持可配置的边沿触发方式

• Discontinuous mode   //非连续模式

• ADC conversion time: 1 μs at full speed (ADC clocked at 16 MHz) down to 4 μs at low speed (ADC clocked at 4 MHz), independent of the APB clock//最低采样时间

• Automatic power-up/power-down to reduce the power consumption//自动的上下电便于节能

• ADC supply requirements:

– 2.4 V to 3.6 V at full speed or with reference zooming (VREF+ < VDDA)

– down to 1.8 V at slower speeds //工作电压范围

• ADC input range: VREF– ≤ VIN ≤ VREF+

• Automatic programmable hardware delay insertion between conversions

• DMA request generation during regular channel conversion  //规则转换支持DMA


从官方的描述中可以了解到STM32ADC所提供的强大功能,及一些工作特性,那么如何才能使用这些功能呢?


首先要明确两个概念


1.规则组和注入组


区分二者非常重要,因为它关系到实际ADC的能力,这里有篇介绍感觉不错,推荐给大家,(http://wenku.baidu.com/link?url=blwVQAxelZqJCbVMuq3QsH9N-J3A8LNlhTTwO01yVsT2z8UCn4tjPyHGeRvR5g925gqJjRVSHYHRXDcstUdvnjZwSDyCfTmrxlq6z2oqwS7)我的理解是规则组就是按照一定的规则执行(大多用于大规模的连续转换条件下)而注入组则多执行单次的转换,只在我们需要时转换。在我们的项目中会不定时的读取某个挂在ADC上的温度传感器,因此对其的读取采用了注入组,同时还需要周期性的连续读取数个通道的ADC数据多次,因此对其的操作选用了规则组的方式。


单次/连续和扫描模式


单次对应于连续,这个很容易理解,也即单次只进行一次转换,连续方式中在触发转换以后会一直进行转换。


而扫描模式则表示,在一个通道转换结束后马上进行下个通道的转换(通道转换顺序需指定)其不论在单次还是在连续模式下均有意义。


理解了这些就可以进行初步的编程,考虑到对寄存器的操作需要对寄存器较为熟悉,而项目工期又比较紧张,因此采用了固件库的方式。


2.STM32固件库简单介绍


项目中采用了V1.2版的固件库(stsw-stm32077),在固件库中集成了大量的示例程序,对于快速上手非常有帮助。




在来看在stm32l1xx_adc.h中所提供的函数:


/*  Function used to set the ADC configuration to the default reset state *****/   

void ADC_DeInit(ADC_TypeDef* ADCx); 



/* Initialization and Configuration functions *********************************/ 

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);

void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);

void ADC_CommonInit(ADC_CommonInitTypeDef* ADC_CommonInitStruct);

void ADC_CommonStructInit(ADC_CommonInitTypeDef* ADC_CommonInitStruct);

void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);

void ADC_BankSelection(ADC_TypeDef* ADCx, uint8_t ADC_Bank);



/* Power saving functions *****************************************************/

void ADC_PowerDownCmd(ADC_TypeDef* ADCx, uint32_t ADC_PowerDown, FunctionalState NewState);

void ADC_DelaySelectionConfig(ADC_TypeDef* ADCx, uint8_t ADC_DelayLength);



/* Analog Watchdog configuration functions ************************************/

void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);

void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold,uint16_t LowThreshold);

void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);



/* Temperature Sensor & Vrefint (Voltage Reference internal) management function */

void ADC_TempSensorVrefintCmd(FunctionalState NewState);



/* Regular Channels Configuration functions ***********************************/

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);

void ADC_SoftwareStartConv(ADC_TypeDef* ADCx);

FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);

void ADC_EOCOnEachRegularChannelCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

void ADC_ContinuousModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);

void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);



/* Regular Channels DMA Configuration functions *******************************/

void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);

void ADC_DMARequestAfterLastTransferCmd(ADC_TypeDef* ADCx, FunctionalState NewState);



/* Injected channels Configuration functions **********************************/

void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);

void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length);

void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset);

void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv);

void ADC_ExternalTrigInjectedConvEdgeConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConvEdge);

void ADC_SoftwareStartInjectedConv(ADC_TypeDef* ADCx);

FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx);

void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel);



/* Interrupts and flags management functions **********************************/

void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint16_t ADC_FLAG);

void ADC_ClearFlag(ADC_TypeDef* ADCx, uint16_t ADC_FLAG);

ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);

void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);


/* ******************* flags management functions **********************************/


FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint16_t ADC_FLAG);


void ADC_ClearFlag(ADC_TypeDef* ADCx, uint16_t ADC_FLAG);


从上面不同的颜色及注释均能看到函数按功能分成几部分相互之间相互独立(flags management 除外)明白了这些对与我们使用库函数编程非常有帮助。




二、stm32 ADC的初始化


外设的初始化无外乎包含时钟、GPIO功能(复用型)、外设功能设置、中断配置等步骤,项目中参考了固件库示例程序初始化部分,下面就以示例程序(ADC1_Freeze)初始化为例讲解。


1.找到ADC_Config


void ADC_Config(void)

{

  /* Enable The HSI (16Mhz) */

  RCC_HSICmd(ENABLE);//时钟源选择及配置,参见时钟树

  

  /* Check that HSI oscillator is ready */

  while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);

   

  /* Configure RV3 input voltage */



  /* Enable the GPIOF Clock */

  RCC_AHBPeriphClockCmd(IDD_MEASUREMENT_GPIO_CLK, ENABLE);

  

  /* Configure PF.10 (ADC Channel31) or PA.12 (ADC channel 18) in analog mode */


//GPIO部分,比较乱,对实用到的GPIO都配置为模拟输入的方式

  GPIO_InitStructure.GPIO_Pin = GPIO_PIN_X;

  GPIO_Init(IDD_MEASUREMENT_GPIO, &GPIO_InitStructure);

  

  /* Configure the IDD input */

  

  /* Configure PF.11 (ADC channel 1b) or PA.05 (ADC channel 5) in analog mode */

  GPIO_InitStructure.GPIO_Pin = GPIO_PIN_Y;

  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;

  GPIO_Init(IDD_MEASUREMENT_GPIO, &GPIO_InitStructure);



  /* ADC1 Configuration ------------------------------------------------------*/

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//ADC1时钟,建议和上面时钟放一起



  /* ADC1 DeInit */  

  ADC_DeInit(ADC1);

  

#ifdef USE_STM32L152D_EVAL

  /* Select ADC Bank channel */

  ADC_BankSelection(ADC1, ADC_Bank_B);

#endif

  

  /* ADC1 Configuration of channel18/31 and channel5/1b : continuous mode, external trigger (TIM2) *///ADC初始化部分,根据需要选择配置

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12bit 分辨率

  ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描方式开启

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换

  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Falling;//上升沿触发

  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;//外部触发,与上配置匹配


//项目采用了软件触发,触发方式:无 与示例不同

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐

  ADC_InitStructure.ADC_NbrOfConversion = 2;//转换的个数(扫描方式特有)项目该项设置为6 (最大为16),连续采用6个通道的数据

  ADC_Init(ADC1, &ADC_InitStructure);





  /* ADC1 Regular Channel Config *///规则通道顺序配置,项目中将使用到的四个通道分别设置为1-4的顺序

  ADC_RegularChannelConfig(ADC1, IDD_MEASUREMENT_ADC_CHANNEL, 1, ADC_SampleTime_192Cycles);

  ADC_RegularChannelConfig(ADC1, RV3_MEASUREMENT_ADC_CHANNEL, 2, ADC_SampleTime_192Cycles);


//这里没设置注入通道,注入通道需要将通道配置才能使用,这样每个通道才对应于一个注入通道数据寄存器(数目为4),因此可选择通道数目最大为4,配置方式与上相同,只需更换配置函数为ADC_InjectedChannelConfig即可。项目设置了4个注入通道



//此外示例没有采用中断和DMA方式,这样采样得到的数据很容易丢失,因为规则通道公用一个数据寄存器,因此需要在一个转换周期内读出数据,紧接着读取下一个通道的数据。


  /* Enables the ADC1 Power Down during Delay */ //节能模式,可选

  ADC_PowerDownCmd(ADC1, ADC_PowerDown_Idle_Delay, ENABLE);

  

  /* Delay until the converted data has been read */

  ADC_DelaySelectionConfig(ADC1, ADC_DelayLength_Freeze);



  /* Enable ADC1 *///使能外设电源,外设开始工作

  ADC_Cmd(ADC1, ENABLE);     

  

  /* Wait until the ADC1 is ready *///等待外设稳定工作ADC_GetFlagStatus

  while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET)

  {

  }

}




//初始化部分完成


三、对外设的使用


外设的使用无外乎选择合理的方式读取到通道数据。


首先是读取注入通道的数据,注入通道需要启动转换,所采用函数均在库函数中注入组的函数组中(flag除外)


以注入组1为例


int  ADC_Injected_Read(void)


{


int ad_val;


 ADC_SoftwareStartInjectedConv(ADC1);//软件触发,启动转换


while(RESET ==ADC_GetFlagStatus(ADC1,ADC_FLAG_JEOC));//等待注入组转换完成


ad_val = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);  //获取注入通道1数据,对应于初始化配置中的ADC通道


return ad_val;      //数据未做进一步处理


}


规则组采用DMA传输,需在初始化中进行设置。

推荐阅读

史海拾趣

COILCRAFT公司的发展小趣事

随着电子行业的不断发展,COILCRAFT公司意识到技术创新是保持竞争力的关键。于是,公司加大了对研发团队的投入,不断推出新产品和新技术。其中,射频芯片电感器和功率磁性元件的推出,极大地满足了通信设备、网络设备等领域对高性能磁性元件的需求。此外,COILCRAFT还具备定制磁性元件的能力,能够根据客户的精确要求进行定制生产,这一服务赢得了众多客户的青睐。

Digital Equipment Corp公司的发展小趣事

1957年,麻省理工学院的工程师肯尼斯·奥尔森(Kenneth Olsen)和哈伯特·安德森(Harlan Anderson)共同创立了Digital Equipment Corporation(DEC)。他们最初的目标是开发和生产创新的数字计算设备。凭借他们在数字技术领域的专业知识和独特视角,DEC推出了首批数字实验室和数字系统组件,这些产品在市场上取得了初步的成功,为DEC的发展奠定了坚实的基础。

Gazelle Microcircuits Inc公司的发展小趣事
与-5V类似,-12V电压也在早期的电脑主板中被使用,但主要用于一些特殊的模拟电路或模块。然而,在现代主板设计中,-12V电压的需求已经大大减少或完全消失。
D+H Mechatronic AG公司的发展小趣事

随着市场竞争的加剧,D+H Mechatronic AG开始调整其市场布局策略。公司根据市场需求和产品特点,有针对性地开拓新的市场领域。同时,公司还加强了与客户的沟通和合作,深入了解客户需求并提供定制化的解决方案。这些举措使公司在激烈的市场竞争中保持了稳定的增长态势,并逐步巩固了其在电子行业中的地位。

Cermetek Microelectronics公司的发展小趣事

随着产品的推出,Cermetek Microelectronics公司开始注重品质管理。公司建立了严格的质量控制体系,从原材料采购到生产流程,再到最终的产品测试,都经过严格的把控。这种对品质的执着追求,使得公司的产品在市场上获得了良好的口碑。同时,公司还积极拓展市场,与多家知名企业建立了合作关系,进一步提升了品牌影响力。

Erocore Enterprise Co Ltd公司的发展小趣事

随着全球化趋势的加速,Erocore开始实施国际化战略。公司积极拓展海外市场,通过建立海外生产基地、设立销售办事处等方式,实现产品的全球布局。同时,Erocore也加强了与国际知名企业的合作与交流,引进先进的技术和管理经验,提升公司的整体竞争力。

问答坊 | AI 解惑

来秀秀你身边的智能卡吧!

智能交通,智能家居,摄像头 需要形形色色的智能卡 来晒晒你身边的智能卡吧!…

查看全部问答>

救急!关于个人GPS定位器

要做个GPS定位器,请问用什么开发板比较好???…

查看全部问答>

求助啊!我用AD0804采样不对啊。

用P1来读取。然后0804的CS给P2.7,WR给P2.6,RD给P2.5。用外部中断0来接收INTR。下面是程序。我用示波器看P1口一直都没有变化啊,都是高。。。。都要疯掉了。 #include #define uchar unsigned char; uchar Tcount,ADnum; sbit ADcs=P2^7; sbi ...…

查看全部问答>

求PC机与多单片机联机实例

求PC机与多单片机联机实例,哪位大哥有的给小弟发一下,电路和程序,小弟感激不尽!邮箱:gejizhe@163.com…

查看全部问答>

ULINK2无法连接STM32目标板

ULINK2无法连接STM32F103C8T6的目标板,显示的是 JTAG Commnication Failure 测量了 RESET,nJRST,TDO,TDI,TMS 信号是3.3的高电平 TCK RTCK电平时低电平。 VDD VDDA  VSS VSSA 都接到了 相应的电源和地上。 BOOT0  BOO ...…

查看全部问答>

新手请教

现在热线/热膜式传感器的响应时间大约是多少?能达到微秒级的吗?…

查看全部问答>

GSM教你理解上下拉电阻

上拉电阻1、当TTL电路驱动COMS电路时,如果TTL电路输出的高电平低于COMS电路的最低高电平(一般为3.5V),这时就需要在TTL的输出端接上拉电阻,以提高输出高电平的值。【TTL-CMOS匹配 输出电平】2、OC(集电极开路,TTL) 或OD(漏极开路,COMS)输出 ...…

查看全部问答>

LPC1300系列usb使用指南

LPC1300系列usb使用纤细指南,周立功的资料,讲的很详细。。…

查看全部问答>

专业功率放大器

现在的专业功放方案是用ClassAB好还是用Class D好?现在的Class D能做到多大的功率?…

查看全部问答>

理解FPGAs 中的亚稳态

这篇论文描述了在FPGA 中的亚稳态,它是怎样产生的,还有它是怎样导致设计的失败。介绍了如何计算亚稳态的平均无故障时间(MTBF),并且总结了各种器件和设计参数是如何影响平均无故障时间的。 介绍 亚稳态是当信号在无关的电路中或异步时钟域之间 ...…

查看全部问答>