前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【STM32H7教程】第44章 STM32H7的ADC基础知识和HAL库API

【STM32H7教程】第44章 STM32H7的ADC基础知识和HAL库API

作者头像
Simon223
发布2020-01-13 17:09:30
5.2K0
发布2020-01-13 17:09:30
举报

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第44章       STM32H7的ADC基础知识和HAL库API

本章节为大家讲解ADC(Analog-to-digital converters,模数转换器),极具项目使用价值,因为STM32H7的ADC已经高达16位分辨率,支持3.6Msps采样率。

44.1 初学者重要提示

44.2 ADC基础知识

44.3 ADC的HAL库用法

44.4 源文件stm32h7xx_hal_adc.c

44.5 总结

44.1 初学者重要提示

  1.   STM32H7虽然支持差分,但不支持负压测量。
  2.   STM32H7的ADC采集通道体验快速通道Fast Channels和低速通道Slow Channels的区别,详情看本章2.12小节的电气特性。
  3.   STM32H7的ADC支持过采样,通过过采样技术可以做到26位分辨率。
  4.   ADC的专业术语诠释文档,推荐大家看看:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=89414

44.2 ADC基础知识

ADC的几个关键知识点放在开头说:

  •   STM32H7支持三路ADC,分别是ADC1,ADC2和ADC3。其中ADC1和ADC2可以组成双ADC模式,ADC3是独立的。这个跟STM32F4有所不同,F4的ADC1,ADC2和ADC3可以组成三ADC模式。
  •   可以配置为16bit,14bit,12bit,10bit或者8bit分辨率,分辨率越低可以做到的采样率越高,因为转换时间要短。
  •   每个ADC都支持20路采样通道。其中有6路快速通道和14路慢速通道,慢速和快速的区别主要是支持的最高采样率不同,慢速通道要比快速通道低。
  •   支持单独输入和差分输入,其中差分输入不支持负压测量。
  •   支持偏移校准和线性度校准,STM32F1的时候还带校准功能,到了STM32F4取消掉了,H7又恢复了校准功能。
  •   支持规则通道和注入通道两种采样方式。
  •   支持低功耗特性,系统在低频工作时保持最佳 ADC 性能(提供自动延迟插入)。
  •   具有五条专用的内部通道,内部参考电压 VrefInt,内部温度传感器和VBAT 监测通道 VBAT/4都是连接到 ADC3。另外内部 DAC 通道 1 和通道 2,连接到 ADC2。
  •   支持过采样,最高可以调整到26bit采样率。
  •   ADC采样的数据可接入DFSDM数字滤波器进行后期处理。
  •   每个ADC支持三路模拟看门狗。

44.2.1 ADC硬件框图

认识一个外设,最好的方式就是看他的框图,方便我们快速的了解ADC的基本功能,然后再看手册了解细节。框图如下所示(ADC1和ADC2):

相比前面章节讲解的外设,ADC的框图相对较复杂,因为涉及到控制寄存器较多。通过这个框图,我们可以得到如下信息:

  •   ADC_INP[0:19]和ADC_INN[0:19]

INP是差分正向输入,INN是差分反向输入。

ADC_INP[0:5]和ADC_INN[0:5]是快速通道。

ADC_INP[6:19]和ADC_INN[6:19]是慢速通道。

  •   adc_ext_trg[20:0]

共有21路触发用于规则通道,ADC1和ADC2共用的,而ADC3是独立的。

  •   adc_jext_trg[20:0]

共有21路触发用于注入通道,ADC1和ADC2共用的,而ADC3是独立的。                                                                                                   

  •   adc_awd1,adc_awd2和adc_awd3

每个ADC都支持三个模拟看门狗。

  • adc_it

ADC中断。

  •   adc_hclk

ADC的AHB时钟。

  •   adc_ker_ck

ADC的内核时钟。

  •   adc_dma

用于ADC的DMA请求。

  •   dac_out1,dac_out2,Vsense,Vrefint和Vbat

五条专用的内部通道,内部参考电压 VrefInt,内部温度传感器和VBAT 监测通道 VBAT/4都是连接到 ADC3。另外内部 DAC 通道 1 和通道 2,连接到 ADC2。

44.2.2 ADC时钟源选择

ADC有两种时钟源可供选择,可以使用来自AHB总线的系统时钟(属于同步时钟,对应下面框图的adc_hclk),也可以使用PLL2,PLL3,HSE,HSI或者CSI时钟(属于异步时钟,对应下面框图的adc_ker_ck)。

结合上面的框图,ADC的时钟源要注意以下几个问题:

  •   ADC1,ADC2和ADC3共用选择的时钟。
  •   ADC的时钟源使用AHB时钟,且使用注入模式,那么在16bit,14bit,12bit或者10bit分辨率时,ADC的时钟不能超过AHB时钟的四分之一。8bit模式时,不能超过AHB时钟的三分之一。
  •   选择AHB时钟的话,ADC的配置中提供了不分频,二分频和四分频。如果选择了不分频,那么配置AHB的时钟输出时也不可以设置分频,即RCC的CFGR寄存器配置不可分频。
  •   如果使用PLL时钟,运行期间要一直开启,不可关闭。

最后特别注意一点,如果STM32H7工作在400MHz,ADC使用AHB做时钟源,超频是不可避免的。ADC1和ADC2位于200MHz的AHB1总线时钟,而ADC3位于200MHz的AHB4下。根据上面的框图,ADCx_CCR寄存器的CKMODE最高可以选择4分频,那么就是50MHz,而ADC数据手册限制最高是36MHz,也就是说已经超频了。

使用AHB作为时钟源的好处就是定时器等外部触发方式的效果好。

44.2.3 ADC的采样时间和转换时间

STM32H7的ADC采样速度,即转换时间 = 采样时间 + 逐次逼近时间。

采样时间是可配置的,通过ADCx_SMPR1 和 ADCx_SMPR2 寄存器中的 SMP[2:0] 位就可以编程所有ADC通道,可选采样时间值如下:

  •   SMP = 000: 1.5 个 ADC 时钟周期
  •   SMP = 001: 2.5 个 ADC 时钟周期
  •   SMP = 010: 8.5 个 ADC 时钟周期
  •   SMP = 011: 16.5 个 ADC 时钟周期
  •   SMP = 100: 32.5 个 ADC 时钟周期
  •   SMP = 101: 64.5 个 ADC 时钟周期
  •   SMP = 110: 387.5 个 ADC 时钟周期
  •   SMP = 111: 810.5 个 ADC 时钟周期

不同ADC分辨率对应的逐次逼近时间不同,具体数值如下:

比如配置SMP = 110,采用16位分辨率,那么:

ADC的转换时间 =采样时间 + 逐次逼近时间

= 387.5个ADC时钟周期 + 8.5个ADC时钟周期

= 396个ADC时钟周期。

44.2.4 ADC单次转换和连续转换

STM32H7的ADC支持单次转换和连续转换。

  •   单次转换

在单次转换模式下,ADC会将通道的所有转换执行一次。

  •   连续转换

该模式仅适用于常规通道。

在连续转换模式下,如果发生软件或硬件触发,ADC会执行所有常规通道的转换,随后会自动重启并继续执行每个通道的转换。

44.2.5 ADC外部触发采样

STM32H7既可以选择软件触发也可以选择外部硬件触发,并且可以设置触发边沿。

  •   这里有一点要特别注意,对于ADC1和ADC2是共用相同的规则通道触发和注入通道触发:
  •   外部触发支持上升沿、下降沿和双沿触发。
  •   规则通道支持的外部触发源如下:
代码语言:javascript
复制
#define ADC_EXTERNALTRIG_T1_CC1           ((uint32_t)0x00000000)
#define ADC_EXTERNALTRIG_T1_CC2           ((uint32_t)ADC_CFGR_EXTSEL_0)
#define ADC_EXTERNALTRIG_T1_CC3           ((uint32_t)ADC_CFGR_EXTSEL_1)
#define ADC_EXTERNALTRIG_T2_CC2           ((uint32_t)(ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T3_TRGO          ((uint32_t)ADC_CFGR_EXTSEL_2)
#define ADC_EXTERNALTRIG_T4_CC4           ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_EXT_IT11         ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1))
#define ADC_EXTERNALTRIG_T8_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1 |
 ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T8_TRGO2         ((uint32_t) ADC_CFGR_EXTSEL_3)
#define ADC_EXTERNALTRIG_T1_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T1_TRGO2         ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1))
#define ADC_EXTERNALTRIG_T2_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T4_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2))
#define ADC_EXTERNALTRIG_T6_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T15_TRGO         ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1))
#define ADC_EXTERNALTRIG_T3_CC4           ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_HR1_ADCTRG1      ((uint32_t) ADC_CFGR_EXTSEL_4)
#define ADC_EXTERNALTRIG_HR1_ADCTRG3      ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_LPTIM1_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_1))
#define ADC_EXTERNALTRIG_LPTIM2_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_1| ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_LPTIM3_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_2))
  •   注入通道支持的外部触发源如下:
代码语言:javascript
复制
#define ADC_EXTERNALTRIGINJEC_T1_TRGO       ((uint32_t)0x00000000)                                                                             
#define ADC_EXTERNALTRIGINJEC_T1_CC4        ((uint32_t)ADC_JSQR_JEXTSEL_0)                                                                     
#define ADC_EXTERNALTRIGINJEC_T2_TRGO       ((uint32_t)ADC_JSQR_JEXTSEL_1)                                                                     
#define ADC_EXTERNALTRIGINJEC_T2_CC1        ((uint32_t)(ADC_JSQR_JEXTSEL_1 | ADC_JSQR_JEXTSEL_0))                                              
#define ADC_EXTERNALTRIGINJEC_T3_CC4        ((uint32_t)ADC_JSQR_JEXTSEL_2)                                                                     
#define ADC_EXTERNALTRIGINJEC_T4_TRGO       ((uint32_t)(ADC_JSQR_JEXTSEL_2 | ADC_JSQR_JEXTSEL_0))                                              
#define ADC_EXTERNALTRIGINJEC_EXT_IT15      ((uint32_t)(ADC_JSQR_JEXTSEL_2 | ADC_JSQR_JEXTSEL_1))                                              
#define ADC_EXTERNALTRIGINJEC_T8_CC4        ((uint32_t)(ADC_JSQR_JEXTSEL_2 | ADC_JSQR_JEXTSEL_1 | 
ADC_JSQR_JEXTSEL_0))                         
#define ADC_EXTERNALTRIGINJEC_T1_TRGO2      ((uint32_t)ADC_JSQR_JEXTSEL_3)                                                                     
#define ADC_EXTERNALTRIGINJEC_T8_TRGO       ((uint32_t)(ADC_JSQR_JEXTSEL_3 | ADC_JSQR_JEXTSEL_0))                                              
#define ADC_EXTERNALTRIGINJEC_T8_TRGO2      ((uint32_t)(ADC_JSQR_JEXTSEL_3 | ADC_JSQR_JEXTSEL_1))                                              
#define ADC_EXTERNALTRIGINJEC_T3_CC3        ((uint32_t)(ADC_JSQR_JEXTSEL_3 | ADC_JSQR_JEXTSEL_1 | ADC_JSQR_JEXTSEL_0))                        
#define ADC_EXTERNALTRIGINJEC_T3_TRGO       ((uint32_t)(ADC_JSQR_JEXTSEL_3 | ADC_JSQR_JEXTSEL_2))                                              
#define ADC_EXTERNALTRIGINJEC_T3_CC1        ((uint32_t)(ADC_JSQR_JEXTSEL_3 | ADC_JSQR_JEXTSEL_2 | ADC_JSQR_JEXTSEL_0))                         
#define ADC_EXTERNALTRIGINJEC_T6_TRGO       ((uint32_t)(ADC_JSQR_JEXTSEL_3 | ADC_JSQR_JEXTSEL_2 | ADC_JSQR_JEXTSEL_1))                         
#define ADC_EXTERNALTRIGINJEC_T15_TRGO      ((uint32_t)(ADC_JSQR_JEXTSEL_3 | ADC_JSQR_JEXTSEL_2 | ADC_JSQR_JEXTSEL_1 | ADC_JSQR_JEXTSEL_0))   
#define ADC_EXTERNALTRIGINJEC_HR1_ADCTRG2   ((uint32_t)ADC_JSQR_JEXTSEL_4)                                                                     
#define ADC_EXTERNALTRIGINJEC_HR1_ADCTRG4   ((uint32_t)(ADC_JSQR_JEXTSEL_4 | ADC_JSQR_JEXTSEL_0))                                              
#define ADC_EXTERNALTRIGINJEC_LPTIM1_OUT    ((uint32_t)(ADC_JSQR_JEXTSEL_4 | ADC_JSQR_JEXTSEL_1))                                              
#define ADC_EXTERNALTRIGINJEC_LPTIM2_OUT    ((uint32_t)(ADC_JSQR_JEXTSEL_4 | ADC_JSQR_JEXTSEL_1 | ADC_JSQR_JEXTSEL_0))                        
#define ADC_EXTERNALTRIGINJEC_LPTIM3_OUT    ((uint32_t)(ADC_JSQR_JEXTSEL_4 | ADC_JSQR_JEXTSEL_2))  

44.2.6 ADC多通道连接方式

ADC1,ADC2和ADC3均支持 20条通道扫描采样(注意,部分引脚是多个ADC共用的):

  1.   6 路快速模拟输入 (ADCx_INP[0]/INN[0] 到 ADCx_INP[5]/INN[5])
  2.   14 路慢速模拟输入 (ADCx_INP[6]/INN[6] 到 ADCx_INP[19]/INN[19])
  3.   ADC 连接至 5 路内部模拟输入:
  •   内部温度传感器 (VSENSE) 连接到 ADC3_INP18
  •   内部参考电压 (VREFINT) 连接到 ADC3_INP19
  •  VBAT 监测通道 (VBAT/4) 连接到 ADC3_INP17
  •  DAC内部通道 1连接到 ADC2_INP16
  •  DAC内部通道 2连接到 ADC2_INP17

反映到硬件上,这些通道的连接方式就是下面这样(以ADC3为例):

44.2.7 ADC多通道扫描时序

ADC的多通道采样过程是单个ADC通过多路选择器不断切换不同的通道进行采样的,也就是说当前通道采集完成后才会进行下一个通道的采样。

通过下面这四幅时序图可以让大家有个感性的认识:

单次转换序列,软件触发:

ADSTART表示软件启动转换。

EOC表示一个通道转换结束。

EOS表示所有通道转换结束。

关于这个时序图的解读:

  •   配置为单次转换的话,每次软件启动,所有通道仅进行一次转换,如果需要再次转换,需要再启动一次。
  •   每个通过转换完毕有个EOC标志,所有通道转换完毕有个EOS标志。

连续转换序列,软件触发:

ADSTART表示软件启动转换。

ADSTP表示停止转换。

EOC表示一个通道转换结束。

EOS表示所有通道转换结束。

关于这个时序图的解读:

  •   配置为连续转换的话,软件启动ADSTART会开启所有通道转换,全部转换完毕后,继续进行下一轮转换。调用了停止转换ADSTP后,会停止转换。
  •   每个通过转换完毕有个EOC标志,所有通道转换完毕有个EOS标志。

单次转换序列,硬件触发:

ADSTART表示软件启动转换。

EOC表示一个通道转换结束。

EOS表示所有通道转换结束。

TRGX表示硬件上升沿触发。

关于这个时序图的解读:

  •   软件启动ADSTART后还不会开启转换,TRGX硬件上升沿触发才会启动转换,全部转换完毕后,再来一个TRGX硬件上升沿触发会继续进行下一轮转换。如果所有通道转换期间有个TRGX硬件上升沿会被忽略。
  •   每个通过转换完毕有个EOC标志,所有通道转换完毕有个EOS。

连续转换序列,硬件触发:

ADSTART表示软件启动转换。

EOC表示一个通道转换结束。

EOS表示所有通道转换结束。

TRGX表示硬件上升沿触发。

关于这个时序图的解读:

  •   软件启动ADSTART后还不会开启转换,TRGX硬件下降沿触发才会启动转换,一旦开启转换, 所有通道的转换会一轮接一轮的进行下去,也就是说TRGX硬件下降沿会被忽略。调用了停止转换ADSTP后,会停止转换。停止后再有TRGX硬件下降沿触发不会再转换,必须再次启动ADSTART才行。
  •   每个通过转换完毕有个EOC标志,所有通道转换完毕有个EOS。

44.2.8 ADC单端和差分的支持

初学的话,容易有几个概念搞不清楚,单极性,双极性,真差分和伪差分。在此贴里面有一个专门的截图:http://www.armbbs.cn/forum.php?mod=viewthread&tid=89397 (暂未找到这个图片的原始出处,所以先不放在教程里面)。

单极性,双极性比较好理解,就是单电源供电或者双电源供电,这里的双电源是指的正负电压供电。

  •   单端输入是第1幅图的效果,ADC读取VIN和GND的差值。
  •   伪差分AIP-AIN就是第5幅图,内部ADC读取AIP和AIN的差值,但允许AIN上有一个很小的共模电压,比如正负0.3V。
  •   真差分是AIP-AIN就是第2幅或者第5幅图,其内部AIP和AIN分别有一个ADC,分别读取转换AIP-GND,和AIN-GND,再对这两个数字值做差,所以AIN上也可以接收很大的共模值。

STM32H7的差分属于单极性真差分,也就是不可以测量负压。另外要注意下面内容:

  •   单端输入模式下,通道i转换的模拟电压是VINP[i]正向复用引脚与 VREF-之差。
  •   差分输入模式下,通道i转换的模拟电压是VINP[i]正向复用引脚与 VINN[i]反向复用引脚之差。
  •   差分模式的输出数据是无符号数据。当VINP[i]为VREF-、VINN[i]为VREF+时,输出数据为0x0000(16 位分辨率模式);当VINP[i]为VREF+、VINN[i]为VREF-时,输出数据为0xFFFF。对应的公式如下:

(1)当 ADC 配置为差分模式时,两路输入的偏置电压均为 Vref+/2。

(2)输入信号应为差分信号且共模电压应固定。

44.2.9 ADC过采样机制

过采样的意思就是提高单位时间的采样次数,比如原来每秒采集1次,那么16倍过采样就是每秒要采集16次。

STM32H7最高支持1024倍过采样,1024次采样数据累加后存到ADC的数据寄存器里面。如果想求1024次采集的平均数,也不需要用户参与计算,ADC的CFGR2寄存器OVSS[3:0]位支持右移操作(右移1位到11位均可配置),可以方便的求平均。

这个功能在实际项目还是非常实用的。比如下面的测试:

  •   测试条件

做了一个ADC3+DMA的多通道采样。

通道1:PC0采集2.5V的稳压基准。

通道2:Vbat/4。

通道3:VrefInt。

通道4:温度。

  •   不做任何处理的效果
  •   16倍过采样求平均后的效果。

44.2.10   ADC的Vbat/4,VrefInt和温度采样

  •   Vbat/4电池监测电压

Vbat/4连接至ADC3_INP17,所以可以使用ADC3的通道17进行测量。为什么不是直接测试Vbat,

因为Vbat电压有可能高于Vdda,导致ADC3测量电压超出范围。Vbat的测量框图如下:

注意:必须将 VBATEN 位置1才能使能内部通道 ADC3_INP17采集。

  •   VrefInt内部电源模块参考电压

VrefInt连接至ADC3_INP19,所以可以使用ADC3的通道19进行测量。可以通过监测内部电源模块参考电压VrefInt来评估ADC Vref+电压的参考值。VrefInt的测量框图如下:

注意:必须将 ADCx_CCR 寄存器中的 VREFEN 位置1才能使能内部通道 ADC3_INP19采集。

  •   温度测量

STM32H7带有温度传感器,可以使用ADC3_INP18进行测量,不过读取出来的还是个电压值,需要将其转换为温度值,调用下面的转换公式即可:

TS_CAL1 = *(__IO uint16_t *)(0x1FF1E820);

TS_CAL2 = *(__IO uint16_t *)(0x1FF1E840);

TS_CAL2表示温度110℃时的ADC测量值,读取地址0x1FF1E820可以获得。

TS_CAL1表示温度30℃时的ADC测量值,读取地址0x1FF1E840可以获得。

TS_DATA表示当前的测量值,获得当前的测试值代入上面公式就可以获取温度。

温度测量的框图如下:

注意:必须将VSENSEEN位置1才能使能内部通道 ADC3_VINP18采集。

44.2.11   ADC校准问题

STM32H7的ADC支持偏移校准和线性度校准,两种校准实现都比较方便,HAL库已经为我们做好了,直接调用API即可,但是使用中务必注意此贴的问题:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=91436

44.2.12   ADC电气特性(重要)

如果使用ADC的话,部分电气特性一定要了解:

通过上面的截图,我们要了解到以下几点:

  •   BOOST=1的时候,ADC的最高采样率36MHz。(BOOST位可以通过寄存器配置)
  •   BOOST=0的时候,ADC的最高采样率20MHz。
  •   对于快速通道(Fast Channels),配置的分辨率越低,支持的最高采样率越高。
  •   对于慢速通道(Slow Channels),不同分辨率支持的最高采样率一样。
  •   支持的测量范围是0 – Vref+,不支持负压测量。以我们V7开发板为例,稳压基准Vref是通过跳线帽设置的,可以选择2.5V,也可以选择3.3V。
  •   差分测量时,共模电压的典型值是Vref / 2。
  •   ADC的输入阻抗最大值是50KΩ。

44.3 ADC的HAL库用法

ADC的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置时钟,并根据需要配置NVIC、中断和DMA。

只是ADC涉及到定义非常多,下面我们逐一展开为大家做个说明。

44.3.1 ADC寄存器结构体ADC_TypeDef

ADC相关的寄存器是通过HAL库中的结构体ADC_TypeDef和ADC_Common_TypeDef定义的,在stm32h743xx.h中可以找到它们的具体定义如下:

代码语言:javascript
复制
/**
  * @brief Analog to Digital Converter
  */
typedef struct
{
  __IO uint32_t ISR;        /*!< ADC Interrupt and Status Register,                 Address offset: 0x00 */
  __IO uint32_t IER;        /*!< ADC Interrupt Enable Register,                     Address offset: 0x04 */
  __IO uint32_t CR;         /*!< ADC control register,                              Address offset: 0x08 */
  __IO uint32_t CFGR;       /*!< ADC Configuration register,                        Address offset: 0x0C */
  __IO uint32_t CFGR2;      /*!< ADC Configuration register 2,                      Address offset: 0x10 */
  __IO uint32_t SMPR1;      /*!< ADC sample time register 1,                        Address offset: 0x14 */
  __IO uint32_t SMPR2;      /*!< ADC sample time register 2,                        Address offset: 0x18 */
  __IO uint32_t PCSEL;      /*!< ADC pre-channel selection,                         Address offset: 0x1C */
  __IO uint32_t LTR1;       /*!< ADC watchdog Lower threshold register 1,           Address offset: 0x20 */
  __IO uint32_t HTR1;       /*!< ADC watchdog higher threshold register 1,          Address offset: 0x24 */
  uint32_t      RESERVED1;  /*!< Reserved, 0x028                                                         */
  uint32_t      RESERVED2;  /*!< Reserved, 0x02C                                                         */
  __IO uint32_t SQR1;       /*!< ADC regular sequence register 1,                   Address offset: 0x30 */
  __IO uint32_t SQR2;       /*!< ADC regular sequence register 2,                   Address offset: 0x34 */
  __IO uint32_t SQR3;       /*!< ADC regular sequence register 3,                   Address offset: 0x38 */
  __IO uint32_t SQR4;       /*!< ADC regular sequence register 4,                   Address offset: 0x3C */
  __IO uint32_t DR;         /*!< ADC regular data register,                         Address offset: 0x40 */
  uint32_t      RESERVED3;  /*!< Reserved, 0x044                                                         */
  uint32_t      RESERVED4;  /*!< Reserved, 0x048                                                         */
  __IO uint32_t JSQR;       /*!< ADC injected sequence register,                    Address offset: 0x4C */
  uint32_t      RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C                                                 */
  __IO uint32_t OFR1;       /*!< ADC offset register 1,                             Address offset: 0x60 */
  __IO uint32_t OFR2;       /*!< ADC offset register 2,                             Address offset: 0x64 */
  __IO uint32_t OFR3;       /*!< ADC offset register 3,                             Address offset: 0x68 */
  __IO uint32_t OFR4;       /*!< ADC offset register 4,                             Address offset: 0x6C */
  uint32_t      RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C                                                 */
  __IO uint32_t JDR1;       /*!< ADC injected data register 1,                      Address offset: 0x80 */
  __IO uint32_t JDR2;       /*!< ADC injected data register 2,                      Address offset: 0x84 */
  __IO uint32_t JDR3;       /*!< ADC injected data register 3,                      Address offset: 0x88 */
  __IO uint32_t JDR4;       /*!< ADC injected data register 4,                      Address offset: 0x8C */
  uint32_t      RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C                                                 */
  __IO uint32_t AWD2CR;     /*!< ADC  Analog Watchdog 2 Configuration Register,     Address offset: 0xA0 */
  __IO uint32_t AWD3CR;     /*!< ADC  Analog Watchdog 3 Configuration Register,     Address offset: 0xA4 */
  uint32_t      RESERVED8;  /*!< Reserved, 0x0A8                                                         */
  uint32_t      RESERVED9;  /*!< Reserved, 0x0AC                                                         */
  __IO uint32_t LTR2;       /*!< ADC watchdog Lower threshold register 2,           Address offset: 0xB0 */
  __IO uint32_t HTR2;       /*!< ADC watchdog Higher threshold register 2,          Address offset: 0xB4 */
  __IO uint32_t LTR3;       /*!< ADC watchdog Lower threshold register 3,           Address offset: 0xB8 */
  __IO uint32_t HTR3;       /*!< ADC watchdog Higher threshold register 3,          Address offset: 0xBC */
  __IO uint32_t DIFSEL;     /*!< ADC  Differential Mode Selection Register,         Address offset: 0xC0 */
  __IO uint32_t CALFACT;    /*!< ADC  Calibration Factors,                          Address offset: 0xC4 */
  __IO uint32_t CALFACT2;   /*!< ADC  Linearity Calibration Factors,                Address offset: 0xC8 */
} ADC_TypeDef;

typedef struct
{
__IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1/3 base address + 0x300 */
uint32_t RESERVED; /*!< Reserved, ADC1/3 base address + 0x304 */
__IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1/3 base address + 0x308 */
__IO uint32_t CDR; /*!< ADC common regular data register for dual Address offset: ADC1/3 base address+0x30C */
__IO uint32_t CDR2; /*!< ADC common regular data register for 32-bit dual mode Address offset: ADC1/3 base address + 0x310 */
} ADC_Common_TypeDef;

__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:

代码语言:javascript
复制
#define     __O     volatile             /*!< Defines 'write only' permissions */
#define     __IO    volatile             /*!< Defines 'read / write' permissions */

结构体变量ADC_TypeDef用于ADC1,ADC2和ADC3,每个ADC都有一组。结构体变量ADC_Common_TypeDef是公共寄存器,ADC1和ADC2共用一组,而ADC3单独用一组。

下面我们再看ADC1,ADC2和ADC3以及公共寄存器的定义,在stm32h743xx.h文件。

代码语言:javascript
复制
#define PERIPH_BASE           ((uint32_t)0x40000000)
#define D2_AHB1PERIPH_BASE    (PERIPH_BASE + 0x00020000)
#define D3_AHB1PERIPH_BASE    (PERIPH_BASE + 0x18020000)

#define ADC1_BASE             (D2_AHB1PERIPH_BASE + 0x2000)
#define ADC2_BASE             (D2_AHB1PERIPH_BASE + 0x2100)
#define ADC12_COMMON_BASE     (D2_AHB1PERIPH_BASE + 0x2300)
#define ADC3_BASE             (D3_AHB1PERIPH_BASE + 0x6000)
#define ADC3_COMMON_BASE      (D3_AHB1PERIPH_BASE + 0x6300)

#define ADC1                ((ADC_TypeDef *) ADC1_BASE) <----- 展开这个宏,(ADC_TypeDef *) 0x40022000
#define ADC2                ((ADC_TypeDef *) ADC2_BASE)
#define ADC3                ((ADC_TypeDef *) ADC3_BASE)
#define ADC12_COMMON        ((ADC_Common_TypeDef *) ADC12_COMMON_BASE)
#define ADC3_COMMON         ((ADC_Common_TypeDef *) ADC3_COMMON_BASE)

我们访问ADC1的ISR寄存器可以采用这种形式:ADC1->ISR = 0;

44.3.2 ADC句柄结构体ADC_HandleTypeDef

HAL库在ADC_TypeDef的基础上封装了一个结构体ADC_HandleTypeDef,定义如下:

代码语言:javascript
复制
typedef struct
{
  ADC_TypeDef                   *Instance;              
  ADC_InitTypeDef               Init;                  
  DMA_HandleTypeDef             *DMA_Handle;            
  HAL_LockTypeDef               Lock;                  
  __IO uint32_t                 State;                 
  __IO uint32_t                 ErrorCode;             
  ADC_InjectionConfigTypeDef    InjectionConfig ;      
}ADC_HandleTypeDef;

下面将这几个参数逐一做个说明。

  • ADC_TypeDef  *Instance

这个参数是寄存器的例化,方便操作寄存器,比如使能ADC内部稳压器。

SET_BIT(hadc->Instance->CR,  ADC_CR_ADVREGEN);

  • DMA_InitTypeDef  Init;  

这个参数是用户接触最多的,用于配置ADC的基本参数,像ADC时钟、分辨率、扫描模式、过采样等。ADC_InitTypeDef结构体的定义如下:

代码语言:javascript
复制
typedef struct
{
  uint32_t ClockPrescaler;        
  uint32_t Resolution;           
  uint32_t ScanConvMode;        
  uint32_t EOCSelection;         
  FunctionalState LowPowerAutoWait;     
  FunctionalState ContinuousConvMode;  
  uint32_t NbrOfConversion;     
  FunctionalState DiscontinuousConvMode; 
  uint32_t NbrOfDiscConversion;  
  uint32_t ExternalTrigConv;     
  uint32_t ExternalTrigConvEdge;  
  uint32_t ConversionDataManagement;
  uint32_t Overrun;               
  uint32_t LeftBitShift;           
  FunctionalState BoostMode;        
  FunctionalState OversamplingMode;       
  ADC_OversamplingTypeDef Oversampling; 
}ADC_InitTypeDef;

具体每个成员的含义在本章3.3小节有说明。

  • DMA_HandleTypeDef    *DMA_Handle;

如果ADC使用DMA模式的话,此参数用于关联DMA的句柄,方便DMA的配置。

  • HAL_LockTypeDef   Lock

__IO uint32_t    State;

__IO uint32_t    ErrorCode

这三个变量主要供函数内部使用。Lock用于设置锁状态,State用于设置ADC通信状态,而ErrorCode用于配置代码错误。

  • ADC_InjectionConfigTypeDef    InjectionConfig

用于配置ADC注入模式。

44.3.3 ADC参数初始化结构体ADC_InitTypeDef

参数初始化结构体ADC _InitTypeDef要注意的事项比较多,所以专门开一个小节单独说明。

跟寄存器结构体ADC_TypeDef一样,参数初始化结构体ADC_InitTypeDef也是封装在了ADC的句柄结构体ADC_HandleTypeDef里面,定义如下:

代码语言:javascript
复制
typedef struct
{
  uint32_t ClockPrescaler;        
  uint32_t Resolution;           
  uint32_t ScanConvMode;        
  uint32_t EOCSelection;         
  FunctionalState LowPowerAutoWait;     
  FunctionalState ContinuousConvMode;  
  uint32_t NbrOfConversion;     
  FunctionalState DiscontinuousConvMode; 
  uint32_t NbrOfDiscConversion;  
  uint32_t ExternalTrigConv;     
  uint32_t ExternalTrigConvEdge;  
  uint32_t ConversionDataManagement;
  uint32_t Overrun;               
  uint32_t LeftBitShift;           
  FunctionalState BoostMode;        
  FunctionalState OversamplingMode;       
  ADC_OversamplingTypeDef Oversampling; 
}ADC_InitTypeDef;

下面将这几个参数逐一做说明:

  ClockPrescaler

用于ADC的时钟分频设置,ADC有两种时钟源可供选择,可以使用来自AHB总线的系统时钟(属于同步时钟),也可以使用PLL2,PLL3,HSE,HSI或者CSI时钟(属于异步时钟)。

  •   ADC1,ADC2和ADC3共用选择的时钟。
  •   ADC的时钟源使用AHB时钟,且使用注入模式,那么在16bit,14bit,12bit或者10bit分辨率时,ADC的时钟不能超过AHB时钟的四分之一。8bit模式时,不能超过AHB时钟的三分之一。
  •   选择AHB时钟的话,ADC的配置中提供了不分频,二分频和四分频。如果选择了不分频,那么配置AHB的时钟输出时也不可以设置分频,即RCC的CFGR寄存器配置不可分频。
  •   如果使用PLL时钟,运行期间要一直开启,不可关闭。
  •   此参数仅可以在关闭ADC的时候配置。

有以下几种参数可供选择:

代码语言:javascript
复制
/** @defgroup ADC_ClockPrescaler ADC clock source and clock prescaler
  * @{
  */
#define ADC_CLOCK_SYNC_PCLK_DIV1   ((uint32_t)ADC_CCR_CKMODE_0)  
#define ADC_CLOCK_SYNC_PCLK_DIV2   ((uint32_t)ADC_CCR_CKMODE_1) 
#define ADC_CLOCK_SYNC_PCLK_DIV4   ((uint32_t)ADC_CCR_CKMODE)   

#define ADC_CLOCKPRESCALER_PCLK_DIV1   ADC_CLOCK_SYNC_PCLK_DIV1   /* 这三个仅仅是为了兼容,已经不推荐使用 */
#define ADC_CLOCKPRESCALER_PCLK_DIV2   ADC_CLOCK_SYNC_PCLK_DIV2   
#define ADC_CLOCKPRESCALER_PCLK_DIV4   ADC_CLOCK_SYNC_PCLK_DIV4    

#define ADC_CLOCK_ASYNC_DIV1       ((uint32_t)0x00000000)                                       
#define ADC_CLOCK_ASYNC_DIV2       ((uint32_t)ADC_CCR_PRESC_0)                                  
#define ADC_CLOCK_ASYNC_DIV4       ((uint32_t)ADC_CCR_PRESC_1)                                   
#define ADC_CLOCK_ASYNC_DIV6       ((uint32_t)(ADC_CCR_PRESC_1|ADC_CCR_PRESC_0))                 
#define ADC_CLOCK_ASYNC_DIV8       ((uint32_t)(ADC_CCR_PRESC_2))                                
#define ADC_CLOCK_ASYNC_DIV10      ((uint32_t)(ADC_CCR_PRESC_2|ADC_CCR_PRESC_0))                 
#define ADC_CLOCK_ASYNC_DIV12      ((uint32_t)(ADC_CCR_PRESC_2|ADC_CCR_PRESC_1))                 
#define ADC_CLOCK_ASYNC_DIV16      ((uint32_t)(ADC_CCR_PRESC_2|ADC_CCR_PRESC_1|ADC_CCR_PRESC_0)) 
#define ADC_CLOCK_ASYNC_DIV32      ((uint32_t)(ADC_CCR_PRESC_3))                                
#define ADC_CLOCK_ASYNC_DIV64      ((uint32_t)(ADC_CCR_PRESC_3|ADC_CCR_PRESC_0))                 
#define ADC_CLOCK_ASYNC_DIV128     ((uint32_t)(ADC_CCR_PRESC_3|ADC_CCR_PRESC_1))                
#define ADC_CLOCK_ASYNC_DIV256     ((uint32_t)(ADC_CCR_PRESC_3|ADC_CCR_PRESC_1|ADC_CCR_PRESC_0)) 

  Resolution

用于ADC的分辨率配置,支持如下几种:

代码语言:javascript
复制
/** @defgroup ADC_Resolution ADC Resolution
  * @{
  */
#define ADC_RESOLUTION_16B      ((uint32_t)0x00000000)                             
#define ADC_RESOLUTION_14B      ((uint32_t)ADC_CFGR_RES_0)                         
#define ADC_RESOLUTION_12B      ((uint32_t)ADC_CFGR_RES_1)                        
#define ADC_RESOLUTION_10B      ((uint32_t)(ADC_CFGR_RES_1 | ADC_CFGR_RES_0))      
#define ADC_RESOLUTION_8B       ((uint32_t)ADC_CFGR_RES_2)    

  ScanConvMode

用于使能或者禁止ADC的扫描模式,即多通道转换。此参数配合成员DiscontinuousConvMode可以将主转换序列分成多个子系列进行逐步转换。

  •   禁止扫描模式,表示单通道转换,成员NbrOfConversion和InjectedNbrOfConversion不起作用,等效为1,即仅进行一个通道转换。
  •   使能扫描模式,表示支持多通道转换,成员NbrOfConversion和InjectedNbrOfConversion分别用于规则通道和注入通道转换序列数。

支持的参数如下:

代码语言:javascript
复制
#define ADC_SCAN_DISABLE         ((uint32_t)0x00000000)      
#define ADC_SCAN_ENABLE          ((uint32_t)0x00000001)  

  EOCSelection

用于中断或者查询模式时,转换结束标志EOC (End Of Conversion)的选择。参数可以单通道转换结束或者序列转换结束:

代码语言:javascript
复制
#define ADC_EOC_SINGLE_CONV         ((uint32_t) ADC_ISR_EOC)    /*!< End of unitary conversion flag  */
#define ADC_EOC_SEQ_CONV            ((uint32_t) ADC_ISR_EOS)    /*!< End of sequence conversions flag */

  LowPowerAutoWait

用于使能或者禁止低功耗自动延迟等待模式。仅当用户调用函数HAL_ADC_GetValue()获取规则通道数据或者调用函数HAL_ADCEx_InjectedGetValue()获取注入通道数据后才会开启下一次ADC转换。

  •   此功能可自动将ADC触发频率调整为读取数据速度,即完全由用户决定的自适应模式。这样可以避免溢出,适用于低频应用。
  •   此模式仅可用于ADC的查询模式,不可用于中断或者DMA模式,即调用函数HAL_ADC_Start_IT()或者HAL_ADC_Start_DMA(),因为这种模式会立即清除EOC转换结束标志,从而释放中断请求向量序列。
  •   使用查询模式的调用方法:
    •   调用函数HAL_ADC_Start启动转换。
    •   调用函数HAL_ADC_PollForConversion等待转换结束,然后调用函数HAL_ADC_GetValue可以获取当前的转换值,并且会自动启动下次转换。
    •   如果是注入通道,就是调用函数HAL_ADCExInjected_Start()启动转换,调用函数HAL_ADCEx_InjectedGetValue()获取转换数据。

参数可以是使能ENABLE或者禁能DISABLE。

  ContinuousConvMode

用于配置使用单次转换还是连续转换,此参数仅对规则通道有效。触发方式可以选择软件触发或者外部触发。

参数可以是使能ENABLE,表示连续转换或者DISABLE禁能,表示单次转换。

  NbrOfConversion

用于配置规则通道要转换的通道数。

  •   如果要进行多个通道的转换,参数成员ScanConvMode必须使能。
  •   通道数范围1到16。
  •   仅当规则通道上没有后续的转换时才可以修改此参数(即ADC禁止的情况下或者ADC单次转换模式,又或者无外部触发)。

  DiscontinuousConvMode

用于配置ADC规则组转换序列的不连续方式。这里的不连续含义是指每次触发进行一个子组的转换。注意跟参数成员ContinuousConvMode的含义区分开。

  •   使能此参数,要转换的整个序列通道1,2,6,7,8,10,设置2个为一个子组,那么第1次触发会进行通道1和通道2的转换,下次触发进行通道6和通道7的转换,以此进行了。
  •   禁止此参数,要转换的整个序列通道1,2,6,7,8,10,那么第1次触发会进行所有通道的转换。

使用此参数要注意以下两点:

  •   只有参数成员ScanConvMode使能的情况下,此参数才有使用的意义,否则忽略此参数。
  •   只有参数成员ContinuousConvMode禁止的情况下,此参数才有使用的意义,否则忽略此参数。也就是说这两个参数不能同时使能。

参数可以是使能ENABLE或者禁止DISABLE。

  NbrOfDiscConversion

使能了参数DiscontinuousConvMode的情况下,用于设置子组的大小。

  •   仅用于规则通道。
  •   如果参数DiscontinuousConvMode禁止的情况下,忽略此参数。
  •   此参数范围1到8。

  ExternalTrigConv

用于规则通道外部触发源的选择。如果使能了软件触发,那么外部触发将被关闭,使用软件触发。ADC1,ADC2和ADC3支持的触发源是相同的。具体支持的触发源参数如下:

代码语言:javascript
复制
/** @defgroup ADC_regular_external_trigger_source ADC group regular trigger source
  * @{
  */
/* External triggers of regular group for ADC1, ADC2, ADC3 */
#define ADC_EXTERNALTRIG_T1_CC1           ((uint32_t)0x00000000)
#define ADC_EXTERNALTRIG_T1_CC2           ((uint32_t)ADC_CFGR_EXTSEL_0)
#define ADC_EXTERNALTRIG_T1_CC3           ((uint32_t)ADC_CFGR_EXTSEL_1)
#define ADC_EXTERNALTRIG_T2_CC2           ((uint32_t)(ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T3_TRGO          ((uint32_t)ADC_CFGR_EXTSEL_2)
#define ADC_EXTERNALTRIG_T4_CC4           ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_EXT_IT11         ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1))
#define ADC_EXTERNALTRIG_T8_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1 |
 ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T8_TRGO2         ((uint32_t) ADC_CFGR_EXTSEL_3)
#define ADC_EXTERNALTRIG_T1_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T1_TRGO2         ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1))
#define ADC_EXTERNALTRIG_T2_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1 |
 ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T4_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2))
#define ADC_EXTERNALTRIG_T6_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 |
 ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_T15_TRGO         ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 |
 ADC_CFGR_EXTSEL_1))
#define ADC_EXTERNALTRIG_T3_CC4           ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 |
 ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_HR1_ADCTRG1      ((uint32_t) ADC_CFGR_EXTSEL_4)
#define ADC_EXTERNALTRIG_HR1_ADCTRG3      ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_LPTIM1_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_1))
#define ADC_EXTERNALTRIG_LPTIM2_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_1| ADC_CFGR_EXTSEL_0))
#define ADC_EXTERNALTRIG_LPTIM3_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_2))

  ExternalTrigConvEdge

如果使用外部触发的话,设置触发沿类型,支持上升沿、下降沿或者双沿触发。

代码语言:javascript
复制
#define ADC_EXTERNALTRIGCONVEDGE_NONE           ((uint32_t)0x00000000)        
#define ADC_EXTERNALTRIGCONVEDGE_RISING         ((uint32_t)ADC_CFGR_EXTEN_0)  
#define ADC_EXTERNALTRIGCONVEDGE_FALLING        ((uint32_t)ADC_CFGR_EXTEN_1)  
#define ADC_EXTERNALTRIGCONVEDGE_RISINGFALLING  ((uint32_t)ADC_CFGR_EXTEN)  

注意,如果使能了软件触发,那么外部触发将被关闭,使用软件触发,此参数已经不起作用。

  ConversionDataManagement

此参数成员用于ADC采集数据的管理,可以存到ADC的DR寄存器,传输给DFSDM,又或者通过DMA的单次或者循环模式传输数据到指定地址。

代码语言:javascript
复制
/** @defgroup ADC_ConversionDataManagement ADC Conversion Data Management
  * @{
  */
#define ADC_CONVERSIONDATA_DR    ((uint32_t)0x00000000)  
#define ADC_CONVERSIONDATA_DFSDM               ((uint32_t)ADC_CFGR_DMNGT_1)                     
#define ADC_CONVERSIONDATA_DMA_ONESHOT         ((uint32_t)ADC_CFGR_DMNGT_0)                      
#define ADC_CONVERSIONDATA_DMA_CIRCULAR        ((uint32_t)(ADC_CFGR_DMNGT_0 | ADC_CFGR_DMNGT_1)) 

使用此参数成员注意以下问题:

  •   如果参数成员ContinuousConvMode配置为连续转换模式,那么DMA必须配置为循环模式,否则会造成DMA传输溢出。
  •   仅当规则通道上没有后续的转换时才可以修改此参数(即ADC禁止的情况下或者ADC单次转换模式,又或者无外部触发)。

  Overrun

用于配置ADC转换数据未及时读取,造成溢出时的处理,可以选择继续保留上次转换的数据,也可以选择新转换的数据覆盖,具体支持的参数如下:

代码语言:javascript
复制
#define ADC_OVR_DATA_PRESERVED             ((uint32_t)0x00000000)         
#define ADC_OVR_DATA_OVERWRITTEN           ((uint32_t)ADC_CFGR_OVRMOD)   

使用此参数成员注意以下问题:

  此参数仅可用于规则通道。

  •   如果此参数设置为保留上次转换的数据,且使用函数HAL_ADC_Start_IT的中断服务程序里面去的清除转换结束标志会导致保存的数据被释放。针对这种情况,用户可以在中断服务程序的回调函数HAL_ADC_ConvCpltCallback里面保存转换的数据(此回调函数是在清除转换结束标志前调用)。
  •   如果ADC转换采用查询或者中断方式且此参数被设置为保留上次转换的数据,会产生错误报告。此参数设置为覆盖方式,那么用户不读取数据时,不会被视为溢出错误。
  •   如果ADC转换采用DMA方式,不管此参数配置为何种方式,都会报告溢出错误(因为DMA是需要处理所有转换的数据)。

  LeftBitShift

用于设置ADC转换结果的左移位数,使用或者没有使用过采样的情况下,都可以使用此参数。

具体支持的参数如下:

代码语言:javascript
复制
#define ADC_LEFTBITSHIFT_NONE  ((uint32_t)0x00000000)                                                                           
#define ADC_LEFTBITSHIFT_1     ((uint32_t)ADC_CFGR2_LSHIFT_0)                                                                   
#define ADC_LEFTBITSHIFT_2     ((uint32_t)ADC_CFGR2_LSHIFT_1)                                                                   
#define ADC_LEFTBITSHIFT_3     ((uint32_t)(ADC_CFGR2_LSHIFT_1 | ADC_CFGR2_LSHIFT_0))                                            
#define ADC_LEFTBITSHIFT_4     ((uint32_t)ADC_CFGR2_LSHIFT_2)                                                                   
#define ADC_LEFTBITSHIFT_5     ((uint32_t)(ADC_CFGR2_LSHIFT_2 | ADC_CFGR2_LSHIFT_0))                                            
#define ADC_LEFTBITSHIFT_6     ((uint32_t)(ADC_CFGR2_LSHIFT_2 | ADC_CFGR2_LSHIFT_1))                                            
#define ADC_LEFTBITSHIFT_7     ((uint32_t)(ADC_CFGR2_LSHIFT_2 | ADC_CFGR2_LSHIFT_1 | ADC_CFGR2_LSHIFT_0))                       
#define ADC_LEFTBITSHIFT_8     ((uint32_t)ADC_CFGR2_LSHIFT_3)                                                                   
#define ADC_LEFTBITSHIFT_9     ((uint32_t)(ADC_CFGR2_LSHIFT_3 | ADC_CFGR2_LSHIFT_0))                                            
#define ADC_LEFTBITSHIFT_10    ((uint32_t)(ADC_CFGR2_LSHIFT_3 | ADC_CFGR2_LSHIFT_1))                                            
#define ADC_LEFTBITSHIFT_11    ((uint32_t)(ADC_CFGR2_LSHIFT_3 | ADC_CFGR2_LSHIFT_1 | ADC_CFGR2_LSHIFT_0))                       
#define ADC_LEFTBITSHIFT_12    ((uint32_t)(ADC_CFGR2_LSHIFT_3 | ADC_CFGR2_LSHIFT_2))                                            
#define ADC_LEFTBITSHIFT_13    ((uint32_t)(ADC_CFGR2_LSHIFT_3 | ADC_CFGR2_LSHIFT_2 | ADC_CFGR2_LSHIFT_0))                       
#define ADC_LEFTBITSHIFT_14    ((uint32_t)(ADC_CFGR2_LSHIFT_3 | ADC_CFGR2_LSHIFT_2 | ADC_CFGR2_LSHIFT_1))                       
#define ADC_LEFTBITSHIFT_15    ((uint32_t)(ADC_CFGR2_LSHIFT_3 | ADC_CFGR2_LSHIFT_2 | ADC_CFGR2_LSHIFT_1 |
 ADC_CFGR2_LSHIFT_0)) 

  BoostMode

用于设置ADC的BOOST模式,当ADC的时钟高于20MHz时,必须使能此位。

参数可以是使能ENABLE或者禁止DISABLE。

  OversamplingMode

此参数成员用于使能或者禁止过采样模式。只有当ADC规则通道或者注入通道没有数据转换时才可以修改此参数。

参数可以是使能ENABLE或者禁止DISABLE。

  Oversampling

此参数是ADC_OversamplingTypeDef类型结构体变量,用于设置过采样的相关参数。

44.3.4 ADC通道配置结构体ADC_ChannelConfTypeDef

结构体变量ADC_ChannelConfTypeDef用于配置ADC规则通道的一些特性,定义如下:

代码语言:javascript
复制
typedef struct
{
  uint32_t Channel;               
  uint32_t Rank;                  
  uint32_t SamplingTime;          
  uint32_t SingleDiff;            
  uint32_t OffsetNumber;          
  uint32_t Offset;                
  FunctionalState OffsetRightShift;  
  FunctionalState OffsetSignedSaturation; 
}ADC_ChannelConfTypeDef;

下面将这几个参数逐一做说明:

  Channel

具体支持的通道参数如下:

代码语言:javascript
复制
/** @defgroup ADC_channels ADC Channels
  * @{
  */
#define ADC_CHANNEL_0           ((uint32_t)(0x00000000))
#define ADC_CHANNEL_1           ((uint32_t)(ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_2           ((uint32_t)(ADC_SQR3_SQ10_1))
#define ADC_CHANNEL_3           ((uint32_t)(ADC_SQR3_SQ10_1 | ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_4           ((uint32_t)(ADC_SQR3_SQ10_2))
#define ADC_CHANNEL_5           ((uint32_t)(ADC_SQR3_SQ10_2 | ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_6           ((uint32_t)(ADC_SQR3_SQ10_2 | ADC_SQR3_SQ10_1))
#define ADC_CHANNEL_7           ((uint32_t)(ADC_SQR3_SQ10_2 | ADC_SQR3_SQ10_1 | ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_8           ((uint32_t)(ADC_SQR3_SQ10_3))
#define ADC_CHANNEL_9           ((uint32_t)(ADC_SQR3_SQ10_3 | ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_10          ((uint32_t)(ADC_SQR3_SQ10_3 | ADC_SQR3_SQ10_1))
#define ADC_CHANNEL_11          ((uint32_t)(ADC_SQR3_SQ10_3 | ADC_SQR3_SQ10_1 | ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_12          ((uint32_t)(ADC_SQR3_SQ10_3 | ADC_SQR3_SQ10_2))
#define ADC_CHANNEL_13          ((uint32_t)(ADC_SQR3_SQ10_3 | ADC_SQR3_SQ10_2 | ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_14          ((uint32_t)(ADC_SQR3_SQ10_3 | ADC_SQR3_SQ10_2 | ADC_SQR3_SQ10_1))
#define ADC_CHANNEL_15          ((uint32_t)(ADC_SQR3_SQ10_3 | ADC_SQR3_SQ10_2 | ADC_SQR3_SQ10_1 | ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_16          ((uint32_t)(ADC_SQR3_SQ10_4))
#define ADC_CHANNEL_17          ((uint32_t)(ADC_SQR3_SQ10_4 | ADC_SQR3_SQ10_0))
#define ADC_CHANNEL_18          ((uint32_t)(ADC_SQR3_SQ10_4 | ADC_SQR3_SQ10_1))
#define ADC_CHANNEL_19          ((uint32_t)(ADC_SQR3_SQ10_4 | ADC_SQR3_SQ10_1| ADC_SQR3_SQ10_0))

/* Note: Vbat/4, TempSensor and VREFINT internal channels are available on ADC3 only */
#define ADC_CHANNEL_VBAT_DIV4    ADC_CHANNEL_17
#define ADC_CHANNEL_TEMPSENSOR   ADC_CHANNEL_18
#define ADC_CHANNEL_VREFINT      ADC_CHANNEL_19

/* Note: DAC1CH1 and DAC1CH2 internal channels is available on ADC2 only  */
/*!< ADC internal channel connected to DAC1 channel 1, channel specific to ADC2 */
#define ADC_CHANNEL_DAC1CH1_ADC2 (ADC_CHANNEL_16)

/*!< ADC internal channel connected to DAC1 channel 2, channel specific to ADC2 */
#define ADC_CHANNEL_DAC1CH2_ADC2 (ADC_CHANNEL_17) 

这里要特别注意:

  •   VBAT、温度传感器和VREFINT仅ADC3可用。
  •   DAC1的两个通道在内部连接至ADC2,也就是说仅ADC2可用。

  Rank

用于配置规则通道的转换顺序,如果想禁止一个通道或者改变一个通道的顺序,可以使用新配置覆盖。

具体支持的参数如下:

代码语言:javascript
复制
/** @defgroup ADC_regular_rank ADC group regular sequencer rank
  * @{
  */
#define ADC_REGULAR_RANK_1    ((uint32_t)0x00000001)       /*!< ADC regular conversion rank 1  */
#define ADC_REGULAR_RANK_2    ((uint32_t)0x00000002)       /*!< ADC regular conversion rank 2  */
#define ADC_REGULAR_RANK_3    ((uint32_t)0x00000003)       /*!< ADC regular conversion rank 3  */
#define ADC_REGULAR_RANK_4    ((uint32_t)0x00000004)       /*!< ADC regular conversion rank 4  */
#define ADC_REGULAR_RANK_5    ((uint32_t)0x00000005)       /*!< ADC regular conversion rank 5  */
#define ADC_REGULAR_RANK_6    ((uint32_t)0x00000006)       /*!< ADC regular conversion rank 6  */
#define ADC_REGULAR_RANK_7    ((uint32_t)0x00000007)       /*!< ADC regular conversion rank 7  */
#define ADC_REGULAR_RANK_8    ((uint32_t)0x00000008)       /*!< ADC regular conversion rank 8  */
#define ADC_REGULAR_RANK_9    ((uint32_t)0x00000009)       /*!< ADC regular conversion rank 9  */
#define ADC_REGULAR_RANK_10   ((uint32_t)0x0000000A)       /*!< ADC regular conversion rank 10 */
#define ADC_REGULAR_RANK_11   ((uint32_t)0x0000000B)       /*!< ADC regular conversion rank 11 */
#define ADC_REGULAR_RANK_12   ((uint32_t)0x0000000C)       /*!< ADC regular conversion rank 12 */
#define ADC_REGULAR_RANK_13   ((uint32_t)0x0000000D)       /*!< ADC regular conversion rank 13 */
#define ADC_REGULAR_RANK_14   ((uint32_t)0x0000000E)       /*!< ADC regular conversion rank 14 */
#define ADC_REGULAR_RANK_15   ((uint32_t)0x0000000F)       /*!< ADC regular conversion rank 15 */
#define ADC_REGULAR_RANK_16   ((uint32_t)0x00000010)       /*!< ADC regular conversion rank 16 */

  SamplingTime

用于所选通道的采样时间配置,ADC的采样速度是由采样时间和转换时间同决定。用于内部通道测量时(VrefInt/Vbat/TempSensor),务必要遵循数据手册要求的参数范围。此参数成员具体支持的定义如下:

代码语言:javascript
复制
/** @defgroup ADC_sampling_times ADC Sampling Times
  * @{
  */
#define ADC_SAMPLETIME_1CYCLE_5       ((uint32_t)0x00000000)     
#define ADC_SAMPLETIME_2CYCLES_5      ((uint32_t)ADC_SMPR2_SMP10_0)                       
#define ADC_SAMPLETIME_8CYCLES_5      ((uint32_t)ADC_SMPR2_SMP10_1)                       
#define ADC_SAMPLETIME_16CYCLES_5     ((uint32_t)(ADC_SMPR2_SMP10_1 | ADC_SMPR2_SMP10_0))
#define ADC_SAMPLETIME_32CYCLES_5     ((uint32_t)ADC_SMPR2_SMP10_2)                       
#define ADC_SAMPLETIME_64CYCLES_5     ((uint32_t)(ADC_SMPR2_SMP10_2 | ADC_SMPR2_SMP10_0)) 
#define ADC_SAMPLETIME_387CYCLES_5    ((uint32_t)(ADC_SMPR2_SMP10_2 | ADC_SMPR2_SMP10_1))
#define ADC_SAMPLETIME_810CYCLES_5    ((uint32_t)ADC_SMPR2_SMP10) 

  SingleDiff

此参数成员用于选择单端输入还是差分输入。差分输入要用到输入通道i(正向输入)和i+1反向输入,用户仅需配置通道i即可,通道i+1会被自动配置。具体支持的定义如下:

代码语言:javascript
复制
/** @defgroup ADCEx_SingleDifferential ADC Extended Single-ended/Differential input mode
  * @{
  */
#define ADC_SINGLE_ENDED                ((uint32_t)0x00000000)     
#define ADC_DIFFERENTIAL_ENDED          ((uint32_t)ADC_CR_ADCALDIF) 

使用此参数要注意以下问题:

  •   如果用于差分模式,要注意选择的通道是正确的。
  •  如果配置了通道i为差分模式,不可再配置通道i+1。
  •   ADC禁止期间才可以修改此参数,ADC使能后修改此参数会被忽略,也不会报错。

  OffsetNumber

此参数成员用于选择偏移序号Offset Number,具体支持的参数定义如下:

代码语言:javascript
复制
/** @defgroup ADCEx_OffsetNumber ADC Extended Offset Number
  * @{
  */
#define ADC_OFFSET_NONE   ((uint32_t)0x00)     /*!< No offset correction                           */
#define ADC_OFFSET_1      ((uint32_t)0x01)     /*!< Offset correction to apply to a first channel  */
#define ADC_OFFSET_2      ((uint32_t)0x02)     /*!< Offset correction to apply to a second channel */
#define ADC_OFFSET_3      ((uint32_t)0x03)     /*!< Offset correction to apply to a third channel  */
#define ADC_OFFSET_4      ((uint32_t)0x04)     /*!< Offset correction to apply to a fourth channel */

注意,每个通道仅支持一个偏移设置。

  Offset

定义要从原始数据中减去的偏移量。

  •   偏移量必须是正数。
  •  根据用户配置的ADC分辨率为16bit,14bit,12bit,10bit或者8bit,偏移量的最小值为0x0000,最大值分别为0xFFFF,0x3FFF, 0xFFF, 0x3FF 和 0xFF。
  •  仅当规则通道或者注入通道上没有后续的转换时才可以修改此参数(即ADC禁止的情况下或者ADC单次转换模式,又或者无外部触发)。

  OffsetRightShift

此参数成员用于选择是否使能偏移校准后数据右移,仅适用于16bit或者8bit分辨率。

支持的参数可以是使能ENABLE或者禁止DISABLE。

  OffsetSignedSaturation

此参数成员用于选择是否使能有符号饱和特性,仅适用于16bit或者8bit分辨率。

支持的参数可以是使能ENABLE或者禁止DISABLE。

44.3.5 ADC过采样结构体ADC_OversamplingTypeDef

结构体ADC_OversamplingTypeDef主要用于过采样方面参数配置,定义如下:

代码语言:javascript
复制
typedef struct
{
  uint32_t Ratio;                        
  uint32_t RightBitShift;               
  uint32_t TriggeredMode;               
  uint32_t OversamplingStopReset;        
}ADC_OversamplingTypeDef;

下面将这几个参数逐一做说明:

  Ratio

此参数成员用于配置过采样率。

  RightBitShift

此参数用于设置右移,即分频因数。具体支持的参数如下:

代码语言:javascript
复制
/** @defgroup ADCEx_Right_Bit_Shift   ADC Extended Oversampling Right Shift
  * @{
  */
#define ADC_RIGHTBITSHIFT_NONE  ((uint32_t)0x00000000)                                              
#define ADC_RIGHTBITSHIFT_1     ((uint32_t)ADC_CFGR2_OVSS_0)                                         
#define ADC_RIGHTBITSHIFT_2     ((uint32_t)ADC_CFGR2_OVSS_1)                                         
#define ADC_RIGHTBITSHIFT_3     ((uint32_t)(ADC_CFGR2_OVSS_1 | ADC_CFGR2_OVSS_0))                   
#define ADC_RIGHTBITSHIFT_4     ((uint32_t)ADC_CFGR2_OVSS_2)                                        
#define ADC_RIGHTBITSHIFT_5     ((uint32_t)(ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSS_0))                    
#define ADC_RIGHTBITSHIFT_6     ((uint32_t)(ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSS_1))                    
#define ADC_RIGHTBITSHIFT_7     ((uint32_t)(ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSS_1 | ADC_CFGR2_OVSS_0))
#define ADC_RIGHTBITSHIFT_8     ((uint32_t)ADC_CFGR2_OVSS_3)                                         
#define ADC_RIGHTBITSHIFT_9     ((uint32_t)(ADC_CFGR2_OVSS_3 | ADC_CFGR2_OVSS_0))                   
#define ADC_RIGHTBITSHIFT_10    ((uint32_t)(ADC_CFGR2_OVSS_3 | ADC_CFGR2_OVSS_1))                    
#define ADC_RIGHTBITSHIFT_11    ((uint32_t)(ADC_CFGR2_OVSS_3 | ADC_CFGR2_OVSS_1 | ADC_CFGR2_OVSS_0)) 

  TriggeredMode

此参数成员用于过采样的触发模式配置,具体支持的定义如下:

代码语言:javascript
复制
/** @defgroup ADCEx_Triggered_Oversampling_Mode   ADC Extended Triggered Regular Oversampling
  * @{
  */
#define ADC_TRIGGEREDMODE_SINGLE_TRIGGER      ((uint32_t)0x00000000)    
#define ADC_TRIGGEREDMODE_MULTI_TRIGGER       ((uint32_t)ADC_CFGR2_TROVS)

  OversamplingStopReset

此参数成员用于配置使用注入通道时,过采样的处理。可以选择保持原有过采样缓冲数据,或者缓冲数据清零。具体支持的定义如下:

代码语言:javascript
复制
/** @defgroup ADCEx_Regular_Oversampling_Mode   ADC Extended Regular Oversampling Continued or Resumed Mode
  * @{
  */
/*!<  Oversampling buffer maintained during injection sequence */
#define ADC_REGOVERSAMPLING_CONTINUED_MODE    ((uint32_t)0x00000000)    

/*!<  Oversampling buffer zeroed during injection sequence     */  
#define ADC_REGOVERSAMPLING_RESUMED_MODE      ((uint32_t)ADC_CFGR2_ROVSM) 

注意,如果规则通道和注入通道同时使用过采样,此参数成员的配置将被忽略,强制设置为ADC_REGOVERSAMPLING_RESUMED_MODE。

44.3.6 ADC模拟看门狗结构体ADC_AnalogWDGConfTypeDef

结构体ADC_AnalogWDGConfTypeDef主要用于模拟看门狗参数配置,ADC1,ADC2和ADC3都有三个模拟看门狗,结构体定义如下:

代码语言:javascript
复制
typedef struct
{
  uint32_t WatchdogNumber;    
  uint32_t WatchdogMode;      
  uint32_t Channel;         
  FunctionalState ITMode;          
  uint32_t HighThreshold;   
  uint32_t LowThreshold;     
}ADC_AnalogWDGConfTypeDef;

下面将这几个参数逐一做说明:

WatchdogNumber

此参数成员用于配置选择那个看门狗监测通道。

  •   模拟看门狗1仅可以监测1个通道,或者通过成员WatchdogMode配置选择监测所有通道。
  •  模拟看门狗2和3可以检测任意指定的通道,调用函数HAL_ADC_AnalogWDGConfig配置即可,要监测那个通道,调用一次这个函数。

具体支持的定义如下:

代码语言:javascript
复制
/** @defgroup ADCEx_analog_watchdog_number ADC Extended Analog Watchdog Selection
  * @{
  */
#define ADC_ANALOGWATCHDOG_1      ((uint32_t)0x00000001)   
#define ADC_ANALOGWATCHDOG_2      ((uint32_t)0x00000002)  
#define ADC_ANALOGWATCHDOG_3      ((uint32_t)0x00000003)  

 WatchdogMode

此参数用于设置模拟看门狗模式。

  •   模拟看门狗1可以配置监测单个通道或者所有通道,适用于规则通道和注入通道。
  •  模拟看门狗2和3不支持监测所有通道,但可以监测指定的多个通道,每调用一次函数HAL_ADC_AnalogWDGConfig可以指定一个通道,如果此参数配置为ADC_ANALOGWATCHDOG_NONE,那么参数Channel配置的通道将被复位。

此参数具体支持的定义如下:

代码语言:javascript
复制
/** @defgroup ADCEx_analog_watchdog_mode ADC Extended Analog Watchdog Mode
  * @{
  */
#define ADC_ANALOGWATCHDOG_NONE                 ((uint32_t) 0x00000000)                                             
#define ADC_ANALOGWATCHDOG_SINGLE_REG           ((uint32_t)(ADC_CFGR_AWD1SGL | ADC_CFGR_AWD1EN))                  
#define ADC_ANALOGWATCHDOG_SINGLE_INJEC         ((uint32_t)(ADC_CFGR_AWD1SGL | ADC_CFGR_JAWD1EN))                 
#define ADC_ANALOGWATCHDOG_SINGLE_REGINJEC      ((uint32_t)(ADC_CFGR_AWD1SGL | ADC_CFGR_AWD1EN |
 ADC_CFGR_JAWD1EN))
#define ADC_ANALOGWATCHDOG_ALL_REG              ((uint32_t) ADC_CFGR_AWD1EN)                                       
#define ADC_ANALOGWATCHDOG_ALL_INJEC            ((uint32_t) ADC_CFGR_JAWD1EN)                                    
#define ADC_ANALOGWATCHDOG_ALL_REGINJEC         ((uint32_t)(ADC_CFGR_AWD1EN | ADC_CFGR_JAWD1EN))    

  Channel

用于配置要监测的通道。

  •   对于模拟看门狗1,参数成员WatchdogMode配置为单个通道时,此参数才有意义。
  •   对于模拟看门狗2和3,每调用一次函数HAL_ADC_AnalogWDGConfig可以指定一个通道,如果要复位那个通道,将参数WatchdogMode配置为ADC_ANALOGWATCHDOG_NONE即可。

  ITMode

用于配置模拟看门狗为中断方式或者查询方式。

配置为ENABEL表示使用中断方式,配置为DISABLE表示查询方式。

  HighThreshold

用于配置模拟看门狗高阀值。根据配置的ADC的分辨率16, 14, 12, 10或者8bit,高阀值最小都是0x0000,最大值分别是0xFFFF, 0x3FFF, 0xFFF, 0x3FF 和 0xFF。

  LowThreshold

用于配置模拟看门狗低阀值。根据配置的ADC的分辨率16, 14, 12, 10或者8bit,高阀值最小都是0x0000,最大值分别是0xFFFF, 0x3FFF, 0xFFF, 0x3FF 和 0xFF。

44.3.7 ADC的状态标志清除问题

下面我们介绍__HAL_ADC_GET_FLAG函数。这个函数用来检查ADC标志位是否被设置。

代码语言:javascript
复制
/**
  * @brief Checks whether the specified ADC flag is set or not.
  * @param __HANDLE__: ADC handle
  * @param __FLAG__: ADC flag to check
  *        This parameter can be one of the following values:
  *            @arg ADC_FLAG_RDY     ADC Ready (ADRDY) flag
  *            @arg ADC_FLAG_EOSMP   ADC End of Sampling flag
  *            @arg ADC_FLAG_EOC     ADC End of Regular Conversion flag
  *            @arg ADC_FLAG_EOS     ADC End of Regular sequence of Conversions flag
  *            @arg ADC_FLAG_OVR     ADC overrun flag
  *            @arg ADC_FLAG_JEOC    ADC End of Injected Conversion flag
  *            @arg ADC_FLAG_JEOS    ADC End of Injected sequence of Conversions flag
  *            @arg ADC_FLAG_AWD1    ADC Analog watchdog 1 flag (main analog watchdog)
  *            @arg ADC_FLAG_AWD2    ADC Analog watchdog 2 flag (additional analog watchdog)
  *            @arg ADC_FLAG_AWD3    ADC Analog watchdog 3 flag (additional analog watchdog)
  *            @arg ADC_FLAG_JQOVF   ADC Injected Context Queue Overflow flag
  * @retval The new state of __FLAG__ (TRUE or FALSE).
  */
#define __HAL_ADC_GET_FLAG(__HANDLE__, __FLAG__) ((((__HANDLE__)->Instance->ISR) & (__FLAG__)) == (__FLAG__))

与标志获取函数__HAL_ADC_GET_FLAG对应的清除函数是__HAL_ADC_CLEAR_FLAG:

代码语言:javascript
复制
/**
  * @brief Clear a specified ADC flag
  * @param __HANDLE__: ADC handle
  * @param __FLAG__: ADC flag to clear
  *        This parameter can be one of the following values:
  *            @arg ADC_FLAG_RDY     ADC Ready (ADRDY) flag
  *            @arg ADC_FLAG_EOSMP   ADC End of Sampling flag
  *            @arg ADC_FLAG_EOC     ADC End of Regular Conversion flag
  *            @arg ADC_FLAG_EOS     ADC End of Regular sequence of Conversions flag
  *            @arg ADC_FLAG_OVR     ADC overrun flag
  *            @arg ADC_FLAG_JEOC    ADC End of Injected Conversion flag
  *            @arg ADC_FLAG_JEOS    ADC End of Injected sequence of Conversions flag
  *            @arg ADC_FLAG_AWD1    ADC Analog watchdog 1 flag (main analog watchdog)
  *            @arg ADC_FLAG_AWD2    ADC Analog watchdog 2 flag (additional analog watchdog)
  *            @arg ADC_FLAG_AWD3    ADC Analog watchdog 3 flag (additional analog watchdog)
  *            @arg ADC_FLAG_JQOVF   ADC Injected Context Queue Overflow flag
  * @note: bit cleared bit by writing 1 (writing 0 has no effect on any bit of register ISR)
  * @retval None
  */
#define __HAL_ADC_CLEAR_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->ISR) = (__FLAG__))

清除标志函数所支持的参数跟获取函数是一 一对应的。除了这两个函数,还有ADC的中断开启和中断关闭函数,有时候也要用到。                                                                                                                                                   

代码语言:javascript
复制
/**
  * @brief Enable an ADC interrupt.
  * @param __HANDLE__: ADC handle
  * @param __INTERRUPT__: ADC Interrupt to enable
   *          This parameter can be one of the following values:
  *            @arg ADC_IT_RDY    ADC Ready (ADRDY) interrupt source
  *            @arg ADC_IT_EOSMP  ADC End of Sampling interrupt source
  *            @arg ADC_IT_EOC    ADC End of Regular Conversion interrupt source
  *            @arg ADC_IT_EOS    ADC End of Regular sequence of Conversions interrupt source
  *            @arg ADC_IT_OVR    ADC overrun interrupt source
  *            @arg ADC_IT_JEOC   ADC End of Injected Conversion interrupt source
  *            @arg ADC_IT_JEOS   ADC End of Injected sequence of Conversions interrupt source
  *            @arg ADC_IT_AWD1   ADC Analog watchdog 1 interrupt source (main analog watchdog)
  *            @arg ADC_IT_AWD2   ADC Analog watchdog 2 interrupt source (additional analog watchdog)
  *            @arg ADC_IT_AWD3   ADC Analog watchdog 3 interrupt source (additional analog watchdog)
  *            @arg ADC_IT_JQOVF  ADC Injected Context Queue Overflow interrupt source
  * @retval None
  */
#define __HAL_ADC_ENABLE_IT(__HANDLE__, __INTERRUPT__) (((__HANDLE__)->Instance->IER) |= (__INTERRUPT__))

/**
  * @brief Disable an ADC interrupt.
  * @param __HANDLE__: ADC handle
  * @param __INTERRUPT__: ADC Interrupt to disable
  *            @arg ADC_IT_RDY    ADC Ready (ADRDY) interrupt source
  *            @arg ADC_IT_EOSMP  ADC End of Sampling interrupt source
  *            @arg ADC_IT_EOC    ADC End of Regular Conversion interrupt source
  *            @arg ADC_IT_EOS    ADC End of Regular sequence of Conversions interrupt source
  *            @arg ADC_IT_OVR    ADC overrun interrupt source
  *            @arg ADC_IT_JEOC   ADC End of Injected Conversion interrupt source
  *            @arg ADC_IT_JEOS   ADC End of Injected sequence of Conversions interrupt source
  *            @arg ADC_IT_AWD1   ADC Analog watchdog 1 interrupt source (main analog watchdog)
  *            @arg ADC_IT_AWD2   ADC Analog watchdog 2 interrupt source (additional analog watchdog)
  *            @arg ADC_IT_AWD3   ADC Analog watchdog 3 interrupt source (additional analog watchdog)
  *            @arg ADC_IT_JQOVF  ADC Injected Context Queue Overflow interrupt source
  * @retval None
  */
#define __HAL_ADC_DISABLE_IT(__HANDLE__, __INTERRUPT__) (((__HANDLE__)->Instance->IER) &= ~(__INTERRUPT__))

注意:操作ADC的寄存器不限制必须要用HAL库提供的API,比如要操作ADC1的寄存器IER,直接调用DMA1->IER操作即可。

44.3.8 ADC初始化流程总结

使用方法由HAL库提供:

  第1步:ADC时钟源选择。

两种时钟源可供选择,可以选择同步时钟,来自AHB;也可以选择异步时钟,来自系统时钟,PLL2或者PLL3的时钟。

比如使用PLL2:

代码语言:javascript
复制
RCC_PeriphClkInitTypeDef   RCC_PeriphClkInit;
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection    = RCC_ADCCLKSOURCE_PLL2;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);

  第2步:ADC输入引脚配置。

  •  调用函数__HAL_RCC_GPIOx_CLK_ENABLE使能时钟。
  •  调用函数HAL_GPIO_Init配置引脚工作在模拟模式。

  第3步:ADC中断配置(如果用到的话)。

  •  调用函数HAL_NVIC_EnableIRQ(ADCx_IRQn)使能ADC中断。
  •  将ADC中断处理函数HAL_ADC_IRQHandler()填到ADC中断服务程序ADCx_IRQHandler()中。

  第4步:ADC使用DMA方式配置(如果用到的话)

  •  调用函数HAL_DMA_Init配置DMA的相关参数。
  •  使用函数HAL_NVIC_SetPriority配置DMA优先级。
  •  使用函数HAL_NVIC_EnableIRQ使能DMA中断。
  •   将函数HAL_DMA_IRQHandler()填到中断服务程序HAL_DMA_IRQHandler()里面。
  •   传输结束后会调用函数HAL_DMA_IRQHandler(),此函数里面会执行回调函数HAL_ADC_ConvCpltCallback,HAL_ADC_ConvHalfCpltCallback等。

  第5步:配置ADC,ADC通道和模拟看门狗

  •   调用函数HAL_ADC_Init初始化ADC。
  •   调用函数HAL_ADC_ConfigChannel配置ADC通道各个参数。
  •   调用函数HAL_ADC_AnalogWDGConfig配置模拟看门狗。

  第6步:调用函数HAL_ADCEx_Calibration_Start做自动校准。

  第7步:ADC数值获取的三种方式。

  •   查询方式

HAL_ADC_Start()

HAL_ADC_PollForConversion()

HAL_ADC_GetValue()

HAL_ADC_Stop()

  •   中断方式

HAL_ADC_Start_IT()

HAL_ADC_ConvCpltCallback()

HAL_ADC_GetValue()

HAL_ADC_Stop_IT()

  •  DMA方式

HAL_ADC_Start_DMA()

HAL_ADC_ConvCpltCallback() 和 HAL_ADC_ConvHalfCpltCallback()

HAL_ADC_Stop_DMA()

44.4 源文件stm32h7xx_hal_adc.c

此文件涉及到的函数比较多,这里把我们几个常用的函数做个说明:

  •   HAL_ADC_Init
  •   HAL_ADC_ConfigChannel
  •   HAL_ADC_Start
  •   HAL_ADC_Start_DMA
  •   HAL_ADCEx_Calibration_Start

44.4.1 函数HAL_ADC_Init

函数原型:

代码语言:javascript
复制
HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc)
{
  HAL_StatusTypeDef tmp_hal_status = HAL_OK;

  ADC_Common_TypeDef *tmpADC_Common;
  uint32_t tmpCFGR = 0;
  __IO uint32_t wait_loop_index = 0;

  /* 程序中不重要的部分被清除掉,仅留下关键部分做注释 */

  /* 检测ADC句柄 */
  if(hadc == NULL)
  {
    return HAL_ERROR;
  }

  /* 复位状态初始化 */
  if (hadc->State == HAL_ADC_STATE_RESET)
  { 
    HAL_ADC_MspInit(hadc);
    ADC_CLEAR_ERRORCODE(hadc);
    hadc->Lock = HAL_UNLOCKED;
  }

  /*  ADC退出深度掉电模式 */
  if (HAL_IS_BIT_SET(hadc->Instance->CR, ADC_CR_DEEPPWD))
  {
    /* 退出深度掉电模式Exit deep power down mode */
    CLEAR_BIT(hadc->Instance->CR, ADC_CR_DEEPPWD);

    /* 退出深度掉电模式,一旦ADC稳压器使能,必须重新校准或者应用之前保存的校准值 */
  }


  if  (HAL_IS_BIT_CLR(hadc->Instance->CR, ADC_CR_ADVREGEN))
  {
    /* 使能ADC内部稳压器 */
SET_BIT(hadc->Instance->CR, ADC_CR_ADVREGEN);

    /* 等待ADC稳定 */
    wait_loop_index = (ADC_STAB_DELAY_US * (SystemCoreClock / (1000000 * 2)));
    while(wait_loop_index != 0)
    {
      wait_loop_index--;
    }
  }


  /* 检测ADC稳压器是否使能,潜在的时钟稳定会导致使失败  */
  if (HAL_IS_BIT_CLR(hadc->Instance->CR, ADC_CR_ADVREGEN))
  {
    /* 更新ADC状态 */
    SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);

    /* 设置内部错误 */
    SET_BIT(hadc->ErrorCode, HAL_ADC_ERROR_INTERNAL);

    tmp_hal_status = HAL_ERROR;
  }


  /* 如果ADC之前的配置成功且没有继续进行规则通道的 */
  if (HAL_IS_BIT_CLR(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL) &&
      (ADC_IS_CONVERSION_ONGOING_REGULAR(hadc) == RESET)  )
  {

    /* Initialize the ADC state */
    SET_BIT(hadc->State, HAL_ADC_STATE_BUSY_INTERNAL);

    /* 配置ADC的公共参数 */
    if((hadc->Instance == ADC1) || (hadc->Instance == ADC2))
    {
      tmpADC_Common = ADC12_COMMON_REGISTER(hadc);
    }
    else
    {
      tmpADC_Common = ADC3_COMMON_REGISTER(hadc);
    }

    if ((ADC_IS_ENABLE(hadc) == RESET)   &&
        (ADC_ANY_OTHER_ENABLED(hadc) == RESET) )
    {
      /* 配置CCR寄存器 */
      MODIFY_REG(tmpADC_Common->CCR, ADC_CCR_PRESC|ADC_CCR_CKMODE, hadc->Init.ClockPrescaler);
    }


    /* 配置ADC参数 */
    tmpCFGR  = ( ADC_CFGR_CONTINUOUS(hadc->Init.ContinuousConvMode)          |
                 hadc->Init.Overrun                                          |
                 hadc->Init.Resolution                                       |
                ADC_CFGR_REG_DISCONTINUOUS(hadc->Init.DiscontinuousConvMode)  );

    if (hadc->Init.DiscontinuousConvMode == ENABLE)
    {
      tmpCFGR |= ADC_CFGR_DISCONTINUOUS_NUM(hadc->Init.NbrOfDiscConversion);
    }

    /* 注意,如果参数ExternalTrigConvEdge设置为trigger edge none等效于软件启动 */
    if ((hadc->Init.ExternalTrigConv != ADC_SOFTWARE_START)
        && (hadc->Init.ExternalTrigConvEdge != ADC_EXTERNALTRIGCONVEDGE_NONE))
    {
      tmpCFGR |= ( hadc->Init.ExternalTrigConv |  hadc->Init.ExternalTrigConvEdge);
    }

    MODIFY_REG(hadc->Instance->CFGR, ADC_CFGR_FIELDS_1, tmpCFGR);


    /* 更新ADC参数 */
    if (ADC_IS_CONVERSION_ONGOING_REGULAR_INJECTED(hadc) == RESET)
    {
      tmpCFGR = ( ADC_CFGR_AUTOWAIT(hadc->Init.LowPowerAutoWait)       |
                  ADC_CFGR_DMACONTREQ(hadc->Init.ConversionDataManagement) );

      MODIFY_REG(hadc->Instance->CFGR, ADC_CFGR_FIELDS_2, tmpCFGR);

      if (hadc->Init.OversamplingMode == ENABLE)
      {
        if ((hadc->Init.ExternalTrigConv == ADC_SOFTWARE_START)
            || (hadc->Init.ExternalTrigConvEdge == ADC_EXTERNALTRIGCONVEDGE_NONE))
        {
          /* 软件启动不能用于多触发,只能单触发 */
          assert_param((hadc->Init.Oversampling.TriggeredMode == ADC_TRIGGEREDMODE_SINGLE_TRIGGER));
        }


       /* 配置过采样 */
       MODIFY_REG(hadc->Instance->CFGR2, ADC_CFGR2_FIELDS,
                                         ADC_CFGR2_ROVSE                       |
                                         (hadc->Init.Oversampling.Ratio << 16) |
                                         hadc->Init.Oversampling.RightBitShift |
                                         hadc->Init.Oversampling.TriggeredMode |
                                         hadc->Init.Oversampling.OversamplingStopReset);
      }
      else
      {
        /* 禁止规则通道过采样 */
        CLEAR_BIT( hadc->Instance->CFGR2, ADC_CFGR2_ROVSE);
      }
       
      /* 设置左移参数 */
      MODIFY_REG(hadc->Instance->CFGR2, ADC_CFGR2_LSHIFT, hadc->Init.LeftBitShift);

      /* 是否使能BOOST模式 */
      if(hadc->Init.BoostMode == ENABLE)
      {
        SET_BIT(hadc->Instance->CR, ADC_CR_BOOST);
      }
      else
      {
        CLEAR_BIT(hadc->Instance->CR, ADC_CR_BOOST);
      }

    }  

    /* 配置规则通道   */
    if (hadc->Init.ScanConvMode == ADC_SCAN_ENABLE)
    {
      /* 配置规格通道转换个数 */
      MODIFY_REG(hadc->Instance->SQR1, ADC_SQR1_L, (hadc->Init.NbrOfConversion - (uint8_t)1));
    }
    else
    {
      CLEAR_BIT(hadc->Instance->SQR1, ADC_SQR1_L);
    }


    /* 初始ADC就绪状态 */
    ADC_STATE_CLR_SET(hadc->State, HAL_ADC_STATE_BUSY_INTERNAL, HAL_ADC_STATE_READY);
  }
  else
  {
    /* 设置ADC状态错误 */
    SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);

    tmp_hal_status = HAL_ERROR;
  } 

  /* 返回状态 */
  return tmp_hal_status;
}

函数描述:

此函数用于初始化ADC1,ADC2或者ADC3。

函数参数:

  •   第1个参数是ADC_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数。结构体变量成员的详细介绍看本章3.2小节。
  •   返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

注意事项:

  1. 函数HAL_ADC_MspInit用于初始化定时器的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
  2. 如果形参hadc的结构体成员State没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量ADC_HandleTypeDef    AdcHandle。

对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个HAL_ADC_STATE_RESET = 0x00U。

解决办法有四

方法1:用户自己初始定时器和涉及到的GPIO等。

方法2:定义ADC_HandleTypeDef  AdcHandle为全局变量。

方法3:定义为局部变量要赋初始值ADC_HandleTypeDef    AdcHandle = {0}。

方法4:下面的方法:

代码语言:javascript
复制
if(HAL_ADC_DeInit(&AdcHandle)!= HAL_OK)
{
    Error_Handler();
}  
if(HAL_ADC_Init(&AdcHandle)!= HAL_OK)
{
    Error_Handler();
}
  1. ADC有两种时钟源可供选择,可以使用来自AHB总线的系统时钟(属于同步时钟),也可以使用PLL2,PLL3,HSE,HSI或者CSI时钟(属于异步时钟)。如果使用异步时钟,调用函数HAL_ADC_Init前要单独配置。而AHB是默认时钟,所以不必单独配置。
  2. 如果更新ADC的公共寄存器,需要关闭了所有ADC时才能更新。

使用举例:

代码语言:javascript
复制
ADC_HandleTypeDef   AdcHandle = {0};

__HAL_RCC_ADC12_CLK_ENABLE();

AdcHandle.Instance = ADC1;

/* 采用AHB同步时钟,4分频,即200MHz/4 = 50MHz */
AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;      
AdcHandle.Init.Resolution            = ADC_RESOLUTION_16B;            /* 16位分辨率 */
AdcHandle.Init.ScanConvMode          = ADC_SCAN_DISABLE;              /* 禁止扫描,因为仅开了一个通道 */
AdcHandle.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;           /* EOC转换结束标志 */
AdcHandle.Init.LowPowerAutoWait      = DISABLE;                       /* 禁止低功耗自动延迟特性 */
AdcHandle.Init.ContinuousConvMode    = DISABLE;                       /* 禁止自动转换,采用的定时器触发转换 */
AdcHandle.Init.NbrOfConversion       = 1;                             /* 使用了1个转换通道 */
AdcHandle.Init.DiscontinuousConvMode = DISABLE;                       /* 禁止不连续模式 */

 /* 禁止不连续模式后,此参数忽略,此位是用来配置不连续子组中通道数 */
AdcHandle.Init.NbrOfDiscConversion   = 1;                            
AdcHandle.Init.ExternalTrigConv      = ADC_EXTERNALTRIG_T1_CC1;            /* 定时器1的CC1触发 */
AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_RISING;    /* 上升沿触发 */
AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;/*DMA循环模式接收ADC转换的数据*/
AdcHandle.Init.BoostMode          = ENABLE;                   /* ADC时钟超过20MHz的话,使能boost */
AdcHandle.Init.Overrun            = ADC_OVR_DATA_OVERWRITTEN; /* ADC转换溢出的话,覆盖ADC的数据寄存器 */
AdcHandle.Init.OversamplingMode   = DISABLE;                  /* 禁止过采样 */

/* 初始化DMA */
if(HAL_DMA_Init(&DmaHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);     
}

44.4.2 函数HAL_ADC_ConfigChannel

函数原型:

代码语言:javascript
复制
HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef* hadc, ADC_ChannelConfTypeDef* sConfig)
{
  HAL_StatusTypeDef tmp_hal_status = HAL_OK;

  ADC_Common_TypeDef *tmpADC_Common;
  uint32_t tmpOffsetShifted;
  __IO uint32_t wait_loop_index = 0;

/* 程序中不重要的部分被清除掉,仅留下关键部分做注释 */
  /* 上锁 */
  __HAL_LOCK(hadc);


  /* 配置ADC参数 */
  if (ADC_IS_CONVERSION_ONGOING_REGULAR(hadc) == RESET)
  {
    /* ADC通道选择 */
    hadc->Instance->PCSEL |= (1U << sConfig->Channel);

    /* Rank 1 to 4 */
    if (sConfig->Rank < 5)
    {
      MODIFY_REG(hadc->Instance->SQR1,
                ADC_SQR1_RK(ADC_SQR2_SQ5, sConfig->Rank),
                ADC_SQR1_RK(sConfig->Channel, sConfig->Rank));
    }
    /* For Rank 5 to 9 */
    else if (sConfig->Rank < 10)
    {
      MODIFY_REG(hadc->Instance->SQR2,
                ADC_SQR2_RK(ADC_SQR2_SQ5, sConfig->Rank),
                ADC_SQR2_RK(sConfig->Channel, sConfig->Rank));
    }
    /* For Rank 10 to 14 */
    else if (sConfig->Rank < 15)
    {
      MODIFY_REG(hadc->Instance->SQR3,
                ADC_SQR3_RK(ADC_SQR3_SQ10, sConfig->Rank),
                ADC_SQR3_RK(sConfig->Channel, sConfig->Rank));
    }
    /* For Rank 15 to 16 */
    else
    {
      MODIFY_REG(hadc->Instance->SQR4,
                ADC_SQR4_RK(ADC_SQR4_SQ15, sConfig->Rank),
                ADC_SQR4_RK(sConfig->Channel, sConfig->Rank));
    }


    /* 更新ADC参数 */
    if (ADC_IS_CONVERSION_ONGOING_REGULAR_INJECTED(hadc) == RESET)
    {
      /* 通道采样时间配置 */
      /* For channels 10 to 19 */
      if (sConfig->Channel >= ADC_CHANNEL_10)
      {
        MODIFY_REG(hadc->Instance->SMPR2,
                  ADC_SMPR2(ADC_SMPR2_SMP10, sConfig->Channel),
                  ADC_SMPR2(sConfig->SamplingTime, sConfig->Channel));
      }
      else /* For channels 0 to 9 */
      {
        MODIFY_REG(hadc->Instance->SMPR1,
                  ADC_SMPR1(ADC_SMPR1_SMP0, sConfig->Channel),
                  ADC_SMPR1(sConfig->SamplingTime, sConfig->Channel));
      }

      /* 配置偏移 */
      tmpOffsetShifted = ADC_OFFSET_SHIFT_RESOLUTION(hadc, sConfig->Offset);
     
      switch (sConfig->OffsetNumber)
      {
        case ADC_OFFSET_1:
           MODIFY_REG(hadc->Instance->OFR1,
                   ADC_OFR_FIELDS,
                   ADC_OFR_CHANNEL(sConfig->Channel) | tmpOffsetShifted);
           MODIFY_REG(hadc->Instance->CFGR2, ADC_CFGR2_RSHIFT1, sConfig->OffsetRightShift);
           if(sConfig->OffsetSignedSaturation != DISABLE)
           {
              SET_BIT(hadc->Instance->OFR1, ADC_OFR1_SSATE);
           }
           else
           {
             CLEAR_BIT(hadc->Instance->OFR1, ADC_OFR1_SSATE);
           }
          break;
        
        case ADC_OFFSET_2:
          break;
        
        case ADC_OFFSET_3:
          break;
        
        case ADC_OFFSET_4:
          break;
        
        default :
          break;
      }  

    } 

    /* ADC参数更新 */
    /*  内部的 Vbat/VrefInt/TempSensor */
    if (ADC_IS_ENABLE(hadc) == RESET)
    {
      /* 配置差分模式 */
      if (sConfig->SingleDiff != ADC_DIFFERENTIAL_ENDED)
      {
        /* 禁止差分 */
        CLEAR_BIT(hadc->Instance->DIFSEL, ADC_DIFSEL_CHANNEL(sConfig->Channel));
      }
      else
      {
        /* 使能差分 */
        SET_BIT(hadc->Instance->DIFSEL, ADC_DIFSEL_CHANNEL(sConfig->Channel));
    
        /* 配置通道ADC_IN+1 (negative input)的采样时间     */
        /* 通道 9 to 15 (ADC1, ADC2) or to 11 (ADC3), SMPR2寄存器必须配置 */
        if (sConfig->Channel >= ADC_CHANNEL_9)
        {
          MODIFY_REG(hadc->Instance->SMPR2,
                  ADC_SMPR2(ADC_SMPR2_SMP10, sConfig->Channel +1),
                  ADC_SMPR2(sConfig->SamplingTime, sConfig->Channel +1));
        }
        else /* 对于通道0 to 8, SMPR1必须配置 */
        {
          MODIFY_REG(hadc->Instance->SMPR1,
              ADC_SMPR1(ADC_SMPR1_SMP0, sConfig->Channel +1),
               ADC_SMPR1(sConfig->SamplingTime, sConfig->Channel +1));
        }
      }
      /* 内部测量通道配置: Vbat/VrefInt/TempSensor */
      /* 公用寄存器配置 */
      if((hadc->Instance == ADC1) || (hadc->Instance == ADC2))
      {
        tmpADC_Common = ADC12_COMMON_REGISTER(hadc);
      }
      else
      {
      tmpADC_Common = ADC3_COMMON_REGISTER(hadc);
      }
    
      /* 具体内部通道的配置,省略未写 */
    } 

  } 
  else
  {
    SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);
    tmp_hal_status = HAL_ERROR;
  }

  /* 解锁 */
  __HAL_UNLOCK(hadc);

  /* 返回函数状态 */
  return tmp_hal_status;
}

函数描述:

调用函数HAL_ADC_Init配置了基础功能后,就可以调用此函数配置ADC的具体通道了。

函数参数:

  •  第1个参数是ADC_HandleTypeDef类型结构体指针变量。
  •  第2个参数是ADC_ChannelConfTypeDef类型结构体指针变量,用于配置ADC的采样时间,使用的通道号,单端或者差分方式的配置等。
  •  返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

注意事项:

  1. 第1个参数的结构体成员介绍在本章的3.2小节进行了详细说明。
  2. 第2个参数的结构体成员介绍在本章的3.4小节进行了详细说明。

使用举例:

代码语言:javascript
复制
ADC_ChannelConfTypeDef   sConfig = {0};

/* 配置ADC通道  */
sConfig.Channel      = ADC_CHANNEL_10;              /* 配置使用的ADC通道 */
sConfig.Rank         = ADC_REGULAR_RANK_1;          /* 采样序列里的第1个 */
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;     /* 采样周期 */
sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* 单端输入 */
sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* 无偏移 */ 
sConfig.Offset = 0;                                 /* 无偏移的情况下,此参数忽略 */

if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

44.4.3 函数HAL_ADC_Start

函数原型:

HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc)

函数描述:

调用函数HAL_ADC_Init配置了基础功能后,就可以调用此函数启动ADC了。

函数参数:

  •   第1个参数是ADC_HandleTypeDef类型结构体指针变量。
  •   返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

注意事项:

  1. 第1个参数的结构体成员介绍在本章的3.2小节进行了详细说明。

使用举例:

代码语言:javascript
复制
if (HAL_ADC_ConfigChannel(&AdcHandl) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

44.4.4 函数HAL_ADC_Start_DMA

函数原型:

HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)

函数描述:

调用函数HAL_ADC_Init配置了基础功能后,就可以调用此函数启动ADC的DMA方式了。

函数参数:

  •   第1个参数是ADC_HandleTypeDef类型结构体指针变量。
  •   第2个参数是ADC采样数据传输的目的地址。
  •   第3个参数是传输的数据长度。
  •   返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

注意事项:

  1. 第1个参数的结构体成员介绍在本章的3.2小节进行了详细说明。
  2. 这个函数会开启DMA的HT,TC,TE和MDE中断。
  3. 此函数用于单ADC模式,多ADC模式是调用的函数HAL_ADCEx_MultiModeStart_DMA。

使用举例:

代码语言:javascript
复制
uint16_t ADCxValues[4];
ADC_HandleTypeDef   AdcHandle = {0};

/* ADC和DMA的配置部分未贴,函数较多 */
if (HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)ADCxValues, 4) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

44.4.5 函数HAL_ADCEx_Calibration_Start

函数原型:

HAL_StatusTypeDef HAL_ADCEx_Calibration_Start(ADC_HandleTypeDef* hadc, uint32_t CalibrationMode, uint32_t SingleDiff)

函数描述:

调用函数HAL_ADC_Init配置了基础功能后,就可以调用此函数启动ADC的自校准功能了,支持偏移校准和线性度校准。

函数参数:

  •   第1个参数是ADC_HandleTypeDef类型结构体指针变量。
  •   第2个参数是校准模式选择:
    •   ADC_CALIB_OFFSET表示只运行偏移校准而不运行线性度校准。
    •   ADC_CALIB_OFFSET_LINEARITY表示同时运行偏移校准和线性度校准。
  •   第3个参数是单端或差分模式选择:
    •   ADC_SINGLE_ENDED表示单端模式。
    •   ADC_DIFFERENTIAL_ENDED表示差分模式。
  •   返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

注意事项:

  1. 第1个参数的结构体成员介绍在本章的3.2小节进行了详细说明。
  2. 必须在函数HAL_ADC_Start(或者中断和MDA方式的启动函数)执行前或者HAL_ADC_Stop(或者中断和MDA方式的停止函数)执行后才可以调用此校准函数。

使用举例:

代码语言:javascript
复制
/* 校准ADC,采用偏移校准 */
if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

44.5 总结

本章节就为大家讲解这么多,由于ADC用到的场合比较多,建议将常用的知识点熟练掌握。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-01-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第44章       STM32H7的ADC基础知识和HAL库API
    • 44.1 初学者重要提示
      • 44.2 ADC基础知识
        • 44.2.1 ADC硬件框图
        • 44.2.2 ADC时钟源选择
        • 44.2.3 ADC的采样时间和转换时间
        • 44.2.4 ADC单次转换和连续转换
        • 44.2.5 ADC外部触发采样
        • 44.2.6 ADC多通道连接方式
        • 44.2.7 ADC多通道扫描时序
        • 44.2.8 ADC单端和差分的支持
        • 44.2.9 ADC过采样机制
        • 44.2.10   ADC的Vbat/4,VrefInt和温度采样
        • 44.2.11   ADC校准问题
        • 44.2.12   ADC电气特性(重要)
      • 44.3 ADC的HAL库用法
        • 44.3.1 ADC寄存器结构体ADC_TypeDef
        • 44.3.2 ADC句柄结构体ADC_HandleTypeDef
        • 44.3.3 ADC参数初始化结构体ADC_InitTypeDef
        • 44.3.4 ADC通道配置结构体ADC_ChannelConfTypeDef
        • 44.3.5 ADC过采样结构体ADC_OversamplingTypeDef
        • 44.3.6 ADC模拟看门狗结构体ADC_AnalogWDGConfTypeDef
        • 44.3.7 ADC的状态标志清除问题
        • 44.3.8 ADC初始化流程总结
      • 44.4 源文件stm32h7xx_hal_adc.c
        • 44.4.1 函数HAL_ADC_Init
        • 44.4.2 函数HAL_ADC_ConfigChannel
        • 44.4.3 函数HAL_ADC_Start
        • 44.4.4 函数HAL_ADC_Start_DMA
        • 44.4.5 函数HAL_ADCEx_Calibration_Start
      • 44.5 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档