4. ADC核心原理:逐次逼近型ADC、采样时间、分辨率、参考电压、转换模式
ADC,模数转换器,说白了就是把模拟电压变成数字。单片机里最常见的是逐次逼近型(SAR)ADC。我刚开始接触时也觉得它挺神秘,后来调了几个项目才摸透它的脾气。
4.1 逐次逼近型ADC的工作原理
SAR ADC的核心思想是“二分法”比较。它内部有个DAC、一个比较器,还有逐次逼近寄存器(SAR)。过程是这样的:
- 采样:采样开关闭合,外部电压给内部采样电容充电。
- 保持:开关断开,电容上电压被“冻结”。
- 逐次比较:先从最高位(MSB)开始猜。比如12位ADC,先猜2048(半量程)。比较器判断:采样电压比2048对应的DAC电压高还是低?高了,这一位保留1;低了,置0。
- 下一位:接着猜1024或3072,以此类推,直到最低位(LSB)确定。
你想想看,这个过程就像猜数字游戏。每次猜中间值,12位ADC只需要12次比较就能出结果。速度很快,功耗也低。
核心要点:SAR ADC的转换时间 = 采样时间 + (位数 × 比较周期)。采样时间往往比比较时间还长,很多人容易忽略这一点。
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这块,说白了就是理解采样时间、分辨率、参考电压这三个核心参数,再选对转换模式。寄存器配置其实就那么几个位,关键是理解每个位背后的物理意义。嗯,多动手调几次,自然就熟了。