1. 基于ST AN2834的ADC工作流程详解
最近在调试STM32芯片的ADC模块时,发现手册上的参数指标和实际测试结果总是存在偏差。为了彻底搞懂这个问题,我仔细研读了ST官方的应用笔记AN2834,对SAR型ADC的工作原理和误差来源有了更深入的理解。本文将结合我的实测经验,详细解析ADC的转换流程和各类误差特性。
ADC(模数转换器)作为连接模拟世界和数字系统的桥梁,其精度直接影响整个测量系统的可靠性。STM32系列单片机内置的SAR型ADC具有转换速度快、功耗低的优点,但同时也存在一些特有的误差特性需要特别注意。
提示:本文以STM32H743VIT6的12位ADC为例,但所述原理适用于大多数SAR型ADC。
1.1 SAR ADC内部结构解析
SAR(Successive Approximation Register,逐次逼近型)ADC的核心是一个电容阵列和比较器组成的闭环系统。其转换步数等于ADC的位数,例如12位ADC就需要12个比较周期。

电容阵列的巧妙之处在于:
- 总电容值恒定为2C(忽略寄生电容)
- 通过开关S1-S11的控制实现电压分压
- 最后一个S11是平衡电容,用于减少线性误差
实际工作时序分为以下几个阶段:
1.1.1 采样阶段(Sample State)
- 比较器输入端VCOMP = 0
- 开关Sb闭合接地
- 开关Sa连接Vin,所有S1-S11闭合
- 此时电容阵列正在充电,输入电压Vin被采样
1.1.2 保持阶段(Hold State)
- Sb断开
- Sa切换连接VREF,S1-S11全部断开并接地
- 此时VCOMP = -Vin,进入保持状态
1.1.3 比较阶段(Conversion Steps)
这才是真正的转换过程,以12位ADC为例:
-
第一次比较(MSB确定):
- S1切换到VREF
- 比较VCOMP = Vin - VREF/2
- 若Vin > VREF/2则MSB=1,否则MSB=0
-
第二次比较:
- 根据MSB结果选择比较电压:
- 若MSB=0:比较VREF/4
- 若MSB=1:比较VREF*3/4
- 确定次高位
- 根据MSB结果选择比较电压:
-
后续位依次类推,直到LSB
这个二进制搜索算法使得N位ADC只需N次比较即可完成转换,效率极高。但同时也带来了一些固有误差,我们将在第3章详细分析。
1.2 关键时序参数配置
在实际应用中,需要特别注意以下几个时序参数:
-
采样时间(Sampling Time):
- 必须保证足够长使采样电容充分充电
- 公式:Tsmin = 7.5 * Rs * (Cs + Cp)
- Rs:信号源阻抗
- Cs:采样电容(STM32约4pF)
- Cp:寄生电容
-
转换时间(Conversion Time):
- 12位ADC至少需要12个ADC时钟周期
- 总时间 = 采样时间 + 12 * Tadc
-
时钟配置:
- ADC时钟不宜超过器件规格(如STM32H7最大36MHz)
- 过高的时钟会导致转换精度下降
经验:在STM32CubeMX中配置时,建议先用默认参数测试,再根据实际信号特性调整采样时间。
2. ADC误差来源全解析
理解ADC误差是进行高精度测量的前提。根据AN2834,ADC误差主要分为两大类:ADC自身误差和环境引入误差。
2.1 ADC固有误差
2.1.1 偏移误差(Offset Error)
理想情况下,第一次转换应该发生在0.5LSB处。但实际会有偏差:
code复制Eo = 实际转换点 - 0.5LSB
- 正偏移:实际转换点 > 0.5LSB
- 负偏移:实际转换点 < 0.5LSB

校准方法:
STM32提供了内置的偏移校准寄存器ADC_OFR,可以通过写入补偿值来校正。
2.1.2 增益误差(Gain Error)
影响转换曲线的斜率,定义为最后一次实际转换点与理想值的偏差:
code复制Eg = 实际最后转换点 - (VREF - 0.5LSB)

校准方法:
使用ADC_CALFACT寄存器进行增益校准,通常在出厂时已经校准,但高精度应用可能需要重新校准。
2.1.3 微分线性误差(DNL)
衡量ADC相邻码的转换间隔与理想1LSB的差异:
code复制Ed = 实际步长 - 1LSB

注意:DNL>1LSB会导致丢码(Missing Code),这是ADC的严重缺陷。
2.1.4 积分线性误差(INL)
表示整个转换范围内实际转换曲线与端点连线的最大偏差:
code复制EL = 实际转换点 - 端点连线对应值

2.1.5 总未调整误差(TUE)
综合所有误差后的最差情况偏差:
code复制ET = 实际曲线与理想曲线的最大偏差

2.2 环境引入误差
2.2.1 电源噪声
开关电源(SMPS)会引入15kHz-1MHz的高频噪声。实测发现:
- 使用LDO供电时ADC噪声明显低于SMPS
- VDDA与VREF共用时会放大噪声影响
解决方案:
- 对精度要求高的场合使用LDO
- 采用独立的VREF基准源
- 增加π型滤波电路
2.2.2 参考电压稳定性
ADC转换公式:
code复制Digital_Code = Vin * 2^N / VREF
VREF的任何波动都会直接影响转换结果。需要关注:
- 温度漂移(ppm/°C)
- 长期稳定性
- 负载调整率
2.2.3 信号源阻抗影响
过高的源阻抗会导致采样电容充电不足:
code复制误差 ≈ Rs / (Rs + RADC) * Vin
其中RADC约为1kΩ(取决于具体型号)。
经验值:
- 源阻抗应小于10kΩ
- 高阻抗信号建议加缓冲放大器
3. 实战:ADC性能测试与优化
3.1 测试方案设计
为了全面评估ADC性能,我设计了以下测试流程:
-
静态测试:
- 使用精密可调电源提供直流电压
- 从0逐渐增加到VREF,记录每个码字的转换点
- 计算DNL/INL等参数
-
动态测试:
- 输入低频正弦波(1kHz)
- 采集足够多周期数据
- 进行FFT分析,计算SNR、THD等指标
3.2 硬件设计要点
-
PCB布局:
- 模拟和数字部分分区布局
- ADC电源引脚放置10μF+0.1μF去耦电容
- 信号走线尽量短
-
参考电压电路:
c复制// 推荐电路 VREF+ -- 10μF -- GND | 0.1μF | GND -
信号调理:
- 对高频噪声可增加RC低通滤波
- 阻抗匹配需考虑采样保持阶段的需求
3.3 软件配置技巧
3.3.1 STM32CubeMX配置
-
时钟树:
- 确保ADC时钟不超过规格
- 建议使用PLL分频得到精确时钟
-
ADC参数:
c复制hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.ScanConvMode = DISABLE; hadc1.Init.ContinuousConvMode = ENABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1;
3.3.2 校准流程
c复制// 偏移校准
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
// 手动增益校准(如果需要)
uint32_t calFactor = ADC_GET_CALIBRATION_FACTOR(&hadc1);
ADC_SET_CALIBRATION_FACTOR(&hadc1, calFactor);
3.4 常见问题排查
问题1:ADC读数不稳定
可能原因:
- 采样时间不足
- 电源噪声大
- 信号源阻抗过高
解决方案:
- 增加采样时间
- 检查去耦电容
- 添加电压跟随器
问题2:DNL指标差
可能原因:
- 电容阵列失配
- 比较器失调
解决方案:
- 启用硬件平均(OVS)
- 软件多次采样取平均
问题3:高频信号失真
可能原因:
- 采样时间不足
- 信号带宽超过Nyquist频率
解决方案:
- 调整采样时间
- 添加抗混叠滤波器
4. 高级应用技巧
4.1 过采样技术
通过提高采样率再数字滤波,可增加有效分辨率:
c复制// 4倍过采样可增加1位分辨率
uint32_t sum = 0;
for(int i=0; i<4; i++){
sum += HAL_ADC_GetValue(&hadc1);
}
uint16_t result = sum >> 2; // 右移2位相当于除以4
4.2 硬件平均模式
STM32的ADC支持硬件过采样:
c复制hadc1.Init.OversamplingMode = ENABLE;
hadc1.Init.Oversample.Ratio = ADC_OVERSAMPLING_RATIO_16;
hadc1.Init.Oversample.RightBitShift = ADC_RIGHTBITSHIFT_1;
hadc1.Init.Oversample.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
4.3 温度传感器读取
STM32内置温度传感器,读取方法:
c复制// 启用温度传感器通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 读取并转换为温度
uint32_t raw = HAL_ADC_GetValue(&hadc1);
float temp = ((raw * VREF)/4095 - V25)/Avg_Slope + 25;
经过这次深入的ADC研究,我总结了几个关键点:首先,理解SAR ADC的工作原理是优化性能的基础;其次,电源质量和参考电压稳定性对精度影响极大;最后,合理的软件配置可以显著提升实际测量效果。在后续项目中,我会特别注意在PCB设计阶段就考虑ADC的布局布线要求,避免后期难以解决的噪声问题。