ADC(模拟信号转数字信号)在嵌入式中应用还是挺多的,很多传感器上面采集到的信号是模拟信号(常见的温度、光敏传感器),由于模拟信号不过形象,所以很多时候把它转换为便于识别的数值信号。M3中的ADC转换器是12位精度,多达18个通道(16个外部通道+2个内部信号源),各通道的A/D转换可以单次、连续、扫描或间断模式执行,并且每个通道可以单独设置采样时间,ADC转换的结果也可以左对齐或右对齐的方式存放在16位的数据寄存器中。并且M3的中ADC还支持模拟看门狗以允许应用程序检测输入电压是否超出用户定义的高/低阀值,超出可以产生中断。注意ADC的输入时钟不得超过14MHz,ADC供电是在2.4-3.6V之间,ADC电压的测量范围是Vref+ - Vref-。
ADC的框架图
-
ADCx_INx:模拟输入信号,外部的16个模拟输入通道。
-
Vref+、 Vref-:模拟参考正负极。ADC使用的高端/正极参考电压,2.4V ≤ VREF+ ≤ VDDA。
-
VDDA、VSSA:ADC模拟电源,用来对ADC模块供电。
-
ADC的开关控制。
M3上电后,为了降低设备的功耗,外设的时钟基本上都是关闭的,所以在使用ADC之前需要先开启ADC模块的时钟。ADC模块输入的时钟最大为14M,RCC控制器为ADC提供一个专用的可编程预分频器。库函数是:
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
RCC_APB2PeriphResetCmd (RCC_APB2Periph_ADC1,ENABLE);
/**
* @brief Configures the ADC clock (ADCCLK).
* @param RCC_PCLK2: defines the ADC clock divider. This clock is derived from
* the APB2 clock (PCLK2).
* This parameter can be one of the following values:
* @arg RCC_PCLK2_Div2: ADC clock = PCLK2/2
* @arg RCC_PCLK2_Div4: ADC clock = PCLK2/4
* @arg RCC_PCLK2_Div6: ADC clock = PCLK2/6
* @arg RCC_PCLK2_Div8: ADC clock = PCLK2/8
* @retval None
*/
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
ADC时钟开启之后,ADC进入断电模式(低功耗),需要设置配置寄存器的ADON位将其唤醒,ADC唤醒后需要延迟一段时间(SSTAB)用于稳定ADC模块,再次设置ADON位时才会会进行装换。**可以通过清除ADON位停止转换,并将ADC置于断电模式。**ADC在开始精度转换前需要一个稳定时间tSTAB,用于稳定ADC模块,设置ADON触发转换,转换后数据存在在相应的数据寄存器中。
使能ADC模块的库函数是:
/*唤醒ADC模块*/
/*@brief Enables or disables the specified ADC peripheral.*/
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
- 模拟看门狗
可以设置寄存器来开启模拟看门狗的功能,模拟看门狗可以作用于1个或多个通道(需要配置AWDSGL、AWDEN、JAWDEN)。当电压不在高低阀值间时,将会产生中断。ADC_HTR和ADC_LTR分别是配置高低阀值的寄存器,AWDIE中断使能位。
对应的库函数:
/**
* @brief Enables or disables the analog watchdog on single/all regular
* or injected channels
* @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
* @param ADC_AnalogWatchdog: the ADC analog watchdog configuration.
* This parameter can be one of the following values:
* @arg ADC_AnalogWatchdog_SingleRegEnable: Analog watchdog on a single regular channel
* @arg ADC_AnalogWatchdog_SingleInjecEnable: Analog watchdog on a single injected channel
* @arg ADC_AnalogWatchdog_SingleRegOrInjecEnable: Analog watchdog on a single regular or injected channel
* @arg ADC_AnalogWatchdog_AllRegEnable: Analog watchdog on all regular channel
* @arg ADC_AnalogWatchdog_AllInjecEnable: Analog watchdog on all injected channel
* @arg ADC_AnalogWatchdog_AllRegAllInjecEnable: Analog watchdog on all regular and injected channels
* @arg ADC_AnalogWatchdog_None: No channel guarded by the analog watchdog
* @retval None
*/
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);/*假如模拟看门狗是单通道使用,则需要指定哪个通道使用模拟看门狗*/
- 通道采样时间
可以通过配置寄存器中的SMP[2:0]位来设置通道的采样时间,每个通道都是独立的。装换时间TCONV = 采样时间+12.5个周期。 - 通道选择
在任意多个通道上按照任意的顺序组合成转换组,规则组最多可以由16个序列组成,转换顺序在ADC_SQRx配置;而注入组最多由4个转换序列,转换顺序存放在ADC_JSQR中。如果在转换期间序列被修改,则当前的转换会被清除,将进行转换新选择的组
对应的库函数
/*此函数用于设置规则组通道序列以及采样的时间,*/
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
- 数据存放。
ADC配置寄存器中的ALIGN位可以设置转换后数据的对齐方式,可以选择左对齐或右对齐。其中注入通道转换后数据将存放在ADC注入数据寄存器中;而规则通道转换后数据将存放在ADC规则数据寄存器中的低16位,要是开启了DMA功能则会产生DMA中断把数据传送到指定的SRAM位置处。规则组中只有一个数据寄存器,所以在扫描或者是连续转换时会存在覆盖问题,所以需要使用DMA功能;而注入组中每个通道都对应着一个数据寄存器,无须使用DMA功能
相应的库函数:
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);/*获取规则组数据寄存器值*/
uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel);/*获取注入组数据寄存器值*/
/**
* @brief Enables or disables the specified ADC DMA request.
* @param ADCx: where x can be 1 or 3 to select the ADC peripheral.
* Note: ADC2 hasn't a DMA capability.
* @param NewState: new state of the selected ADC DMA transfer.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);/*开启DMA功能*/
- ADC转换模式。
单次转换模式:ADC只执行一次转换。可以通过设置寄存器中的ADON位启动(只适用于规则通道)也可以通道外部触发启动,这时CONT位0。转换结束EOC标志位被设置ADC停止。
连续转换模式:当前面ADC转换一结束就马上启动别一次转换。可以通过外部触发启动或者是设置ADON位,这时CONT位1。每个通道转换结束后转换结束标志(< J > EOC)被设置
扫描模式:可以配置寄存器的SCAN来使能。ADC将扫描所有被ADC_SQRx寄存器或ADC_JSQR选中的通道。每个组的每个通道装换结束时,下一个通道会被自动转换。如果同时配置为连续转换,则ADC一直循环转换。每个通道转换结束转换结束标志都会被设置
间断模式:可以用来执行一个短序列的转换,转换的通道是ADC_SQRx寄存器或ADC_JSQR所选择的转换序列组的一部分。转换触发信号产生后,将执行一轮转换,直到短序列中所有的通道都被转换结束为止,转换序列结束后不会自动从头开始。其中规则组一次最多转换8个通道,注入组一次最多转换4个通道。间断模式只能作用于组转换,并且注入组不能同时使用自动注入和间断模式。外部事件触发
触发注入:清除JAUTO,设置JSWSTART位,既可以触发注入转换功能。如此时有规则通道在转换,则暂停。注入通道序列被单次扫描转换。然后再恢复上次被中断的规则组通道转换。相当于注入转换的优先级高于规则组的也可以由外部JSWSTART事件触发
自动注入:设置寄存器的JAUTO位,注入组通道被自动转换。此模式必须禁止外部触发转换功能,可以用来转换在ADC_SQRx和 ADC_JSQR寄存器中设置的多至20个转换序列。此模式也可以同时配置为连续转换模式。
装换结束后数据将会存放在相应的数据寄存器中,EOC标志被设置,要是使能了中断,则会产生相应的中断。
由于规则组数据寄存器只有一个,所以在连续转换时会存在数据的覆盖,这里可以设置配置寄存器中的DMA位,在每次转换结束后,DMA把数据寄存器中的数据传输到SRAM中;而注入通道装换的数据总是存储在ADC_JDRx中,每个注入通道都有一个相应的数据寄存器来保存数据。
/*开始转换规则通道,软件设置硬件清除*/
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
/*配置和使能间断模式*/
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
/*规则组的外部触发使能,触发方式在ADC_InitTypeDef->ADC_ExternalTrigConv指定*/
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
- 校准
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。
通过设置ADC_CR2寄存器的CAL位启动校准。一旦校准结束,CAL位被硬件复位,可以开始正常转换。建议在上电时执行一次ADC校准。校准阶段结束后,校准码储存在ADC_DR中。M3自校准,转换结束后直接获取数据寄存器的数值既可
建议每次上电后执行一次校准,启动校准前,ADC必须处于关电状态(ADON)超过两个ADC时钟周期
对应的库函数是:
/*RSTCAL:复位校准和CAL:A/D校准都是由软件设置硬件清除,校准时ADC要处于关电状态*/
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
-
外部触发转换。
转换可以由外部事件触发,需要设置EXTTRIG控制位,需要配置触发事件(配置EXTSEL[2:0]和JEXTSEL[2:0])。只有在外部触发信号的上升沿才会启动转换。软件触发事件可以通过对寄存器ADC_CR2的SWSTART或JSWSTART位置’1’产生。 -
DMA请求。
规则通道转换的值存储在一个仅有的数据寄存器中,在转换多个规则通道时需要使用DMA,这样可以避免数据因覆盖而丢失。只有ADC1和ADC3拥有DMA功能,ADC2转换的数据可以通过双ADC模式利用ADC1的DMA功能传输。 -
ADC中断。ADC1和ADC2的中断映射在同一个中断向量上,ADC3的中断向量独立。中断有三种:规则组转换结束(EOC)中断;注入组转换结束(JEOC)中断;模拟看门狗中断(AWD)读取规则数据寄存器时会把EOC给清除
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);
ADC通道IO分部
更多的细节请查阅参考手册。需要特别注意的是,用来做ADC输入的IO不能被复用,否则会导致采集到的信号不准确
还有一点需要注意的是连接在ADC引脚上的设备要和控制器芯片的供电一样,即有共地线
![](/qrcode.jpg)
ADC的编程架构
独立模式单通道采集实验
- 初始ADC用到的GPIO引脚,配置为模拟输入。前提要开启相应GPIO的时钟。
- 设置ADC的时钟,包括开启时钟和设置预分频器参数。
- 设置ADC的工作参数并初始化
- 设置ADC转换通道顺序以及采样时间。
- 配置使能ADC中断,完成中断函数,并且配置中断。
- 使能ADC
- 使能软件触发ADC转换