目的
ADC(模拟数字转换器)是现在单片机上基本都有的外设,可以把一个模拟的电压转换成数据。这篇文章将以 STM32F405RG 为基础介绍通过HAL库来使用ADC的一些基础功能。
基础说明
STM32的单片机通常都有多个ADC,每个ADC具有多个通道连接到外部的GPIO口。不同的ADC部分通道可能共用GPIO口。
引脚数量大于等于100的封装上通常会有 VREF 引脚,ADC可转换的电压不大于 VREF 上的电压;引脚数量小于100的封装其 VREF 直接就是在芯片内部连接到 VDDA 的。
和其他外设一样ADC工作需要时钟,STM32中ADC的时钟通常由PCLK2(ABP2外设时钟)分频得来,在 STM32F405RG 中最大可以达到36MHz。
STM32的ADC通常最大精度是12位,使用时也可以配置成10位、8位或6位等。精度会影响转换速度和数据计算。
ADC每处理一次数据分为 采样
和 转换
两个过程。 采样时间可以配置,比如 STM32F405RG 中最小为3个ADC时钟周期;转换时间和精度有关,12位精度下为12个ADC时钟周期、10位精度下为10个ADC时钟周期,依此类推。
所以每采样一个数据所需的时间为 (采样周期 + 转换周期) / ADC时钟频率
。比如当ADC时钟频率为30MHz,采样周期选最小值3,精度为12位时,每处理一次数据耗时为 (3+12)/30/1000000 (秒) = 0.5微秒。
ADC工作最后得到的数据换算成真实电压的时候主要和 VREF 以及精度有关。比如12位精度下 真实电压 = 数据 * VREF / (4096 - 1)
,10位精度下 真实电压 = 数据 * VREF / (1024 - 1)
,依此类推。但这里还有点争议,比如那个 -1
要不要?
这里主要考虑两个问题:ADC因为工作原理关系本身有1个分辨率的误差;单片机在除以4096、1024等这些数值的时候可以用右移的方式提高效率。所以通常可以不要 -1
,即12位精度下 真实电压 = 数据 * VREF / 4096
。
基础使用
HAL库对很多外设的使用设计了 轮询
、中断
、DMA
三种方式。STM32的ADC采样转换有 单次 / 连续 转换模式
。另外每个ADC每次处理时只能处理一个通道,所以多通道时还涉及 扫描 模式
。这些模式一组和就产生非常多的情况,下面主要以示例演示方式进行使用说明。
STM32的ADC还有规则转换(Regular)和注入转换(Injected),注入就相当于在正常规则转换过程中插入中断,优先进行注入转换,通常挺少用的,这里不进行演示。
下图是这里演示用到的电路图:
在上面电路下PA0上的电压为2.2V,PA1上的电压为1.1V,ADC在12位精度采样时数据分别为 2730 和 1365 左右。
配置选项说明
ADCs_Common_Settings
所有ADC公共设置
本文只介绍ADC工作于独立模式,所以这里不用调整;ADC_Settings
当前ADC设置Clock Prescaler
PCLK2时钟分频系数,分频后就是ADC时钟;Resolution
分辨率;Data Alignment
数据对齐方式,通常默认左对齐就行;Scan Conversion Mode
扫描模式,多通道时强制开启扫描模式;Continuous Conversion Mode
连续转换模式;Discontinuous Conversion Mode
DMA Continuous Requests
DMA连续请求;End Of Conversion Selection
ADC_Regular_ConversionMode
规则转换模式Number Of Conversion
要转换的通道数目External Trigge rConversion Source
转换触发源External Trigge rConversion Edge
转换触发规则Rank
通道转换序号Channel
该序号通道编号Sampling Time
该序号采样时间
ADC_Injected_ConversionMode
注入转换模式
挺少使用的,本文不涉及;WatchDog
模拟看门狗
输入通道上电压超出某个阈值时触发,本文不涉及;
轮询 单通道 单次
上面是个最基本的使用,只转换一个通道 ch0,单次转换模式。每次启动ADC转换一次数据,启动转换后等待转换完成,完成后读取结果。需要再次转换时就再来一遍这样的流程。
上面演示中的ch0接的就是PA0引脚,采样转换后的数据与预测的数据接近。
上面演示中主要用到了下面一些函数:
HAL_ADC_Start (ADC_HandleTypeDef *hadc)
启动ADCHAL_ADC_PollForConversion(ADC_HandleTypeDef * hadc, uint32_t Timeout)
等待规则通道转换完成uint32_t HAL_ADC_GetValue (ADC_HandleTypeDef * hadc)
获取数据
过程中如果想要改变通道可以使用下面方式:
ADC_ChannelConfTypeDef sConfig;
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
轮询 单通道 连续
连续转换下只要启动ADC后就会不停的工作直到使用 HAL_ADC_Stop (ADC_HandleTypeDef *hadc)
函数手动停止ADC。
连续情况下可以不使用 HAL_ADC_PollForConversion
,需要时去读取数据即可。
轮询 多通道 扫描
上面就是多通道轮询扫描处理了,需要注意的是采样转换顺序是依据 Rank 编号,而不是通道编号。
中断 单通道
上面演示中开启了ADC中断,重写 void HAL_ADC_ConvCpltCallback (ADC_HandleTypeDef *hadc)
函数,使用 HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc)
函数启动ADC。每次转换完成将触发 HAL_ADC_ConvCpltCallback
,在该函数中读取数据。
中断方式下也可以使用前面的方式改变采样通道。
这里需要注意的是——如果打断点调试,断点千万别打在 HAL_ADC_Start_IT(ADC_HandleTypeDef hadc) 后。*
中断 多通道 扫描
DMA 单通道 单次
使用 HAL_ADC_Start_DMA(ADC_HandleTypeDef * hadc, uint32_t * pData, uint32_t Length)
方法可以启动ADC DMA方式,完成一轮采集后会触发 HAL_ADC_ConvCpltCallback
。
DMA 单通道 连续
DMA连续采样需要注意的是DMA模式必须设定为Circular,启动转换后会持续不断的转换,每完成一轮就会触发一次 HAL_ADC_ConvCpltCallback
。可以使用 HAL_ADC_Stop_DMA(ADC_HandleTypeDef * hadc)
函数停止ADC工作。
DMA 多通道 连续
自校准
对于大多数系列的STM32单片机来说在启动ADC采样前需要进行校准,以提高采样准确性。通常自校准方式如下:
HAL_ADCEx_Calibration_Start (ADC_HandleTypeDef * hadc)
上面函数需要在ADC启动前使用。
总结
STM32用HAL库的情况下使用ADC基本功能还是挺简单的。比较常见的做法就是需要采样的通道由DMA在后台不停的采集到一个数组中,需要使用的时候再从这个数组中取数据。