嵌入式高精度温度测量:利用 Arduino ADC 采样抖动与过采样技术
👁 2 阅读 · 2026-08-27 · 嵌入式
在嵌入式系统中,使用 Arduino 内置 ADC 进行温度测量时,常受限于 10 位分辨率与噪声干扰。本文深入探讨采样抖动(Dithering)与过采样(Oversampling)原理,结合硬件电路与软件算法,在不增加外部高精度 ADC 的情况下,将有效分辨率提升至 13 位以上。文章提供完整代码示例与配置步骤,帮助开发者掌握低成本高精度测温的实用技巧。
# 嵌入式高精度温度测量:利用 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 过采样与平均算法
核心代码:
```c
#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. 完整代码示例
```c
// 高精度温度测量 - 过采样+抖动
// 适用于 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 级别。此方法成本低、实现简单,适用于大多数嵌入式温度采集场景。开发者可根据实际需求调整过采样次数与抖动幅度,在精度与速度间取得平衡。