嵌入式高精度温度测量:利用 Arduino ADC 采样抖动与过采样技术
1. 引言
在嵌入式温度采集系统中,Arduino 的 10 位 ADC(0-1023)在 5V 参考电压下理论分辨率约为 4.9mV,对应温度分辨率约 0.5°C(使用常见 NTC 或 LM35 传感器)。对于需要 0.1°C 精度的应用(如恒温箱、医疗设备),这远远不够。传统方案是外接 16 位 ADC(如 ADS1115),但成本与复杂度增加。本文介绍一种利用 ADC 采样抖动(Dithering)与过采样(Oversampling)技术,通过软件算法将有效分辨率提升至 13-14 位,无需额外硬件。
2. 原理讲解
2.1 采样抖动(Dithering)
ADC 量化误差是限制分辨率的主要因素。当输入信号接近量化边界时,微小变化可能被忽略。采样抖动技术通过向信号中注入已知的微小噪声(通常为白噪声),使量化误差随机化。多次采样后,这些随机误差在统计上可平均抵消,从而恢复出低于 1 LSB 的信号变化。
2.2 过采样与求平均
过采样是指以高于奈奎斯特频率的速率采样,然后对连续 N 个样本求平均值。根据信号处理理论,每增加 4 倍过采样率,有效分辨率可提升 1 位。公式为:
- 有效位数 = 原始位数 + log2(过采样率) / 2
例如,10 位 ADC,过采样率 256 次,则有效位数 = 10 + log2(256)/2 = 10 + 4 = 14 位。
2.3 结合抖动与过采样
单纯过采样要求输入信号包含足够的噪声,否则量化误差是确定性的,平均无益。因此,我们主动注入抖动噪声(如通过 PWM 引脚产生伪随机电压),使 ADC 输入信号在几个 LSB 范围内随机波动。这样,过采样平均才能有效提升分辨率。
3. 硬件配置
- Arduino Uno/Nano(ATmega328P)
- 温度传感器:建议使用 LM35(线性输出 10mV/°C)或 NTC 热敏电阻(需分压电路)
- 抖动噪声源:使用一个 PWM 引脚(如 D9)输出高频伪随机信号,经 RC 低通滤波后叠加到 ADC 输入。
电路连接:
- LM35 输出接 A0 引脚,同时通过 1kΩ 电阻连接到 D9(PWM 输出)。
- 在 A0 与 GND 之间并联 0.1μF 电容,滤除高频干扰。
- D9 输出经 10kΩ 电阻和 1μF 电容组成低通滤波器,产生约 0-5V 的缓慢变化噪声。
注意:抖动幅度应控制在 2-3 LSB(约 10-15mV),过大影响精度,过小无效。可通过调整 PWM 占空比和滤波电阻实现。
4. 软件实现步骤
4.1 配置 PWM 抖动源
使用 Arduino 的 analogWrite() 输出伪随机占空比,但需注意 PWM 频率(约 490Hz)可能不够高。更佳方案是使用定时器生成高频 PWM,但为简化,我们使用 random() 函数动态改变占空比,产生低频噪声。
4.2 过采样与平均算法
核心代码:
#define OVERSAMPLE 256 // 过采样次数,建议 256 或 512
uint32_t readOversampledADC(uint8_t pin) {
uint32_t sum = 0;
for (uint16_t i = 0; i < OVERSAMPLE; i++) {
// 产生抖动:随机改变 PWM 占空比
analogWrite(9, random(120, 135)); // 约 2-3 LSB 变化
delayMicroseconds(100); // 等待稳定
sum += analogRead(pin);
}
return sum;
}
float readTemperature() {
uint32_t raw = readOversampledADC(A0);
// 计算平均值,并转换为电压(参考电压 5V)
float voltage = (raw / (float)OVERSAMPLE) * (5.0 / 1023.0);
// LM35: 10mV/°C
float tempC = voltage * 100.0;
return tempC;
}
4.3 校准与优化
- 多次测量取中值滤波,去除异常值。
- 根据实际参考电压校准(使用
analogReference(INTERNAL)或外部基准)。 - 调整抖动幅度:通过示波器观察 A0 波形,确保噪声幅度在 10-20mV。
5. 完整代码示例
// 高精度温度测量 - 过采样+抖动
// 适用于 LM35 传感器
const int tempPin = A0;
const int ditherPin = 9;
const int OVERSAMPLE = 256;
void setup() {
Serial.begin(9600);
pinMode(ditherPin, OUTPUT);
// 设置 ADC 参考电压为 5V(默认)
analogReference(DEFAULT);
// 初始化随机种子
randomSeed(analogRead(A1));
}
void loop() {
float temp = readTemperature();
Serial.print("Temperature: ");
Serial.print(temp, 3); // 显示 3 位小数
Serial.println(" °C");
delay(1000);
}
uint32_t readOversampledADC(uint8_t pin) {
uint32_t sum = 0;
for (uint16_t i = 0; i < OVERSAMPLE; i++) {
// 产生抖动:随机改变 PWM 占空比,范围 120-135 对应约 2-3 LSB
analogWrite(ditherPin, random(120, 136));
delayMicroseconds(200); // 等待 RC 滤波稳定
sum += analogRead(pin);
}
return sum;
}
float readTemperature() {
uint32_t raw = readOversampledADC(tempPin);
float avg = (float)raw / OVERSAMPLE;
// 转换为电压:ADC 值 0-1023 对应 0-5V
float voltage = avg * (5.0 / 1023.0);
// LM35: 10mV/°C,所以温度 = 电压 * 100
float tempC = voltage * 100.0;
return tempC;
}
6. 注意事项
- 抖动信号幅度:过小无法改善分辨率,过大会引入误差。建议用示波器监测,调整 PWM 占空比范围。
-
采样时间:每次
analogRead()约 100μs,256 次采样约 25.6ms,加上延时约 50ms,可接受。若需更快,可减少过采样次数或使用 ADC 自由运行模式。 - 参考电压稳定性:使用内部参考(如 1.1V)可提高分辨率,但需传感器输出范围匹配。
- 传感器噪声:LM35 自身噪声较低,但 NTC 需注意自热效应,控制激励电流。
- 电源去耦:在 AREF 引脚加 0.1μF 电容,减少电源噪声。
7. 总结
通过采样抖动与过采样技术,Arduino 的 10 位 ADC 可达到 13-14 位有效分辨率,温度测量精度提升至 0.1°C 级别。此方法成本低、实现简单,适用于大多数嵌入式温度采集场景。开发者可根据实际需求调整过采样次数与抖动幅度,在精度与速度间取得平衡。