# 嵌入式高精度温度测量:利用 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 级别。此方法成本低、实现简单,适用于大多数嵌入式温度采集场景。开发者可根据实际需求调整过采样次数与抖动幅度,在精度与速度间取得平衡。