开了 16 倍过采样,数据还是跳?别怪 ADC,看看你的信号有没有“呼吸”
摘要:ADC 开了过采样,数据依然跳动或不准?因为你把“过采样”当成了“滤波”。本文解决的是:如何用噪声或抖动(Dithering)把 12 位 ADC 变成 14 位分辨率。
一、问题描述(Bug 现场)
场景 1:
我有一个稳定的 1.650V 电压。
不开过采样:读数是 2048。
开了 16 倍过采样:读数还是 2048。
我想看到 2048.5,看不到。
场景 2:
我有一个变化的电压。
不开过采样:数据乱跳。
开了 16 倍过采样:数据平滑了。
但我不知道这到底是滤波还是精度提高了。
二、核心原理(一句话讲透)
过采样解决的不是“跳动”,而是“分辨率”。
1. 什么是 12 位 ADC 的死穴?
12 位 ADC 的最小单位是1 LSB。
假设参考电压 3.3V:
1 LSB = 3.3V / 4096 ≈ 0.8 mVADC 天生看不见比 0.8mV 更小的变化。
2. 过采样是怎么“造”出小数点的?
过采样利用噪声,让信号在2048和2049之间来回跳。
跳 50 次 2048,跳 50 次 2049。
加起来除以 100。
结果 = 2048.5。
这就是 13 位、14 位分辨率。
3. 反直觉真相
如果你的信号太干净(像电池电压),过采样完全没用。
因为没有噪声,信号永远不会跳变,永远是 2048。
三、工程级解决方案(直接照抄)
方案 1:给信号加点“呼吸”(推荐)
如果信号是一条直线,你必须人为制造抖动。
怎么做:
在 ADC 输入前端,叠加一个微小的正弦波 或三角波(幅度 1~2 LSB)。
硬件做法:
用一个 GPIO 产生 PWM,经过 RC 滤波,接到 ADC 输入端。
这就是Dithering(抖动) 技术。
方案 2:软件注入抖动(快速验证)
不想改硬件?用代码骗 ADC。
// 伪代码:软件注入抖动 for (int i = 0; i < 16; i++) { // 故意切换 GPIO 产生一点串扰噪声 GPIO_Toggle(PIN_NOISE); delay_us(1); ADC_Start(); sum += ADC_GetValue(); } result = sum >> 4; // 右移 4 位 = 16 倍方案 3:区分“过采样”和“滤波”
如果你只是想让数据平滑(不想提高分辨率):
直接用均值滤波:
// 这是滤波,不是过采样 avg = (val1 + val2 + ... + val16) / 16;区别:
滤波:去掉毛刺,数据更稳,但还是整数。
过采样:得到小数,分辨率更高。
四、什么时候不能用?(避坑)
信号是直流(DC):
电池电压、基准电压。别用过采样,没用。
信号变化很快:
电机电流。过采样会导致数据滞后。
精度要求不高:
只想看个大概。别用,浪费 CPU。
五、总结 Checklist
[ ] 我想提高的是“分辨率”(看小数),还是“稳定性”(去抖动)?
[ ] 我的信号上有噪声吗?如果没有,我打算加 Dither 吗?
[ ] 我能接受采样率降低 16 倍吗?
[ ] 我是不是把“均值滤波”误当成“过采样”了?
💡一句话总结:
过采样是为了“看清小数点后面的世界”,不是为了“消灭毛刺”。
References
STM32 AN2668 – Oversampling techniques with STM32 ADC
Analog Devices MT-001 – Taking the Mystery out of Oversampling
如果你曾经为了追求高精度而盲目开过采样,欢迎在评论区交流。
原创文章,转载请注明出处。
