4. ADC核心原理:逐次逼近型ADC、采样时间、分辨率、参考电压、转换模式

ADC,模数转换器,说白了就是把模拟电压变成数字。单片机里最常见的是逐次逼近型(SAR)ADC。我刚开始接触时也觉得它挺神秘,后来调了几个项目才摸透它的脾气。

4.1 逐次逼近型ADC的工作原理

SAR ADC的核心思想是“二分法”比较。它内部有个DAC、一个比较器,还有逐次逼近寄存器(SAR)。过程是这样的:

  1. 采样:采样开关闭合,外部电压给内部采样电容充电。
  2. 保持:开关断开,电容上电压被“冻结”。
  3. 逐次比较:先从最高位(MSB)开始猜。比如12位ADC,先猜2048(半量程)。比较器判断:采样电压比2048对应的DAC电压高还是低?高了,这一位保留1;低了,置0。
  4. 下一位:接着猜1024或3072,以此类推,直到最低位(LSB)确定。

你想想看,这个过程就像猜数字游戏。每次猜中间值,12位ADC只需要12次比较就能出结果。速度很快,功耗也低。

核心要点:SAR ADC的转换时间 = 采样时间 + (位数 × 比较周期)。采样时间往往比比较时间还长,很多人容易忽略这一点。

逐次逼近型ADC工作流程 模拟输入 采样保持 比较器 SAR逻辑 内部DAC 数字输出 关键步骤说明 ① 采样开关闭合,外部电压给内部电容充电 ② 保持阶段,开关断开,电容电压被“冻结” ③ 从MSB开始逐位比较,每次猜中间值 ④ 12位ADC只需12次比较,输出并行数字结果

4.2 采样时间——最容易翻车的参数

采样时间是什么?就是采样开关闭合,让外部电压给内部采样电容充电的时间。这个时间不够,电压还没稳定,转换结果肯定不准。

我记得有个项目,采集温度传感器信号,采样时间设得太短,结果跳得厉害。查了半天才发现是采样电容没充满。

避坑指南:我曾经遇到过采样时间不足导致ADC结果非线性。特别是信号源内阻大的情况,RC时间常数大,采样时间必须加长。公式很简单:采样时间 ≥ 9 × R_source × C_sample。R_source是信号源内阻,C_sample是ADC内部采样电容(通常在几pF到十几pF)。

一般单片机寄存器里都有采样时间配置位。比如STM32的SMP位,可以设1.5周期到239.5周期。我习惯的做法是:先按最坏情况算一遍,再实测看结果是否稳定。

4.3 分辨率与参考电压

分辨率就是ADC能分辨的最小电压变化。12位ADC,参考电压3.3V,那么LSB = 3.3V / 4096 ≈ 0.8mV。16位ADC的话,LSB ≈ 50μV。

参考电压(Vref)直接影响测量范围。内部参考通常精度一般,外部参考更稳定。我做过一个精密测量项目,用了外部2.5V基准,温漂5ppm/°C,效果比内部参考好很多。

分辨率 位数 量化级数 LSB(Vref=3.3V) 典型应用
8位 8 256 12.9 mV 按键检测、粗略电压监测
10位 10 1024 3.22 mV 电池电压检测
12位 12 4096 0.8 mV 传感器采集、音频
16位 16 65536 50.3 μV 精密测量、称重传感器

个人经验:参考电压引脚一定要加去耦电容。我见过有人没加,结果ADC结果跟着电源纹波跳。一个0.1μF陶瓷电容紧贴Vref引脚,能解决大部分问题。

4.4 转换模式——选对模式省一半功夫

单片机ADC通常支持好几种转换模式。选对了,代码简单效率高;选错了,CPU忙死还容易丢数据。

单次转换模式

每次触发,只转一次。转完就停,等下次触发。适合采集频率不高的场景,比如几秒读一次温度。我习惯在低功耗项目里用这个模式,转完就睡。

连续转换模式

启动后一直转,转完自动开始下一次。适合需要连续采样的场景,比如音频采集。但要注意,连续模式会占用DMA或中断资源。

扫描模式

多通道场景用的。配置好通道序列,启动后ADC自动把序列里的通道挨个转一遍。比如同时采集三路模拟信号,用扫描模式一次搞定。

间断模式

这个比较特殊。把一组通道分成若干“组”,每次触发只转一组。适合需要分时处理多通道,又不想占用太多CPU时间的场景。

模式选择口诀:单次省电,连续省事,扫描省心,间断省CPU。实际项目中,我经常把单次+中断配合使用,既省电又及时。

4.5 寄存器配置实战要点

配置ADC寄存器时,有几个关键点要注意:

  • 时钟分频:ADC时钟不能太高,一般限制在几十MHz以内。太高了精度会下降。
  • 对齐方式:左对齐还是右对齐?12位数据存在16位寄存器里,左对齐适合直接做比例运算,右对齐适合直接读数值。
  • 触发源:软件触发、定时器触发、外部引脚触发。定时器触发适合固定频率采样。
  • 数据寄存器:单通道用普通数据寄存器,多通道用DMA搬数据,不然CPU会被频繁中断淹没。
// 以STM32为例,配置ADC1单次转换、12位、右对齐
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;   // 开启ADC时钟
ADC1->CR1 = 0;                         // 12位分辨率(默认)
ADC1->CR2 = ADC_CR2_ADON;              // 开启ADC
ADC1->SMPR2 = 7 << 0;                  // 通道0采样时间:239.5周期
ADC1->SQR3 = 0;                        // 规则序列1:通道0

// 启动转换
ADC1->CR2 |= ADC_CR2_SWSTART;
while(!(ADC1->SR & ADC_SR_EOC));       // 等待转换完成
uint16_t value = ADC1->DR;             // 读取结果

避坑提醒:我曾经在连续模式下忘了清EOC标志,结果下一次转换结果覆盖了上一次。后来养成习惯:读数据寄存器前先检查EOC,读完再清标志。

ADC这块,说白了就是理解采样时间、分辨率、参考电压这三个核心参数,再选对转换模式。寄存器配置其实就那么几个位,关键是理解每个位背后的物理意义。嗯,多动手调几次,自然就熟了。