第一次接触ESP32的ADC功能时,我被它丰富的配置选项和潜在的应用场景所震撼。作为一款集成了Wi-Fi和蓝牙功能的微控制器,ESP32内部集成了两个12位SAR型模数转换器(ADC1和ADC2),这在物联网设备开发中简直是神器。
ADC1和ADC2各有8个通道(共16个通道),但实际可用通道数会根据使用场景有所变化。比如当Wi-Fi功能启用时,ADC2的某些通道会被占用。我刚开始使用时没注意这个细节,导致读取值异常,后来查阅技术手册才明白这个限制。
重要提示:ESP32的ADC参考电压默认为3.3V,但实际供电电压波动会影响ADC精度。我在面包板上测试时发现,当USB供电电压不稳时,ADC读数会有明显波动。
ESP32的ADC引脚不是随意分配的,每个通道对应固定的GPIO引脚。这是我整理的ADC通道与GPIO对应关系:
| ADC单元 | 通道 | GPIO引脚 |
|---|---|---|
| ADC1 | 0 | GPIO36 |
| ADC1 | 3 | GPIO39 |
| ADC2 | 0 | GPIO4 |
| ADC2 | 1 | GPIO0 |
特别注意:GPIO0通常用于烧录模式选择,用作ADC输入时要小心。我有次忘记断开下载器,导致ADC读数一直异常。
ESP32 ADC默认12位分辨率(0-4095),但可以通过编程设置为9-12位。降低分辨率可以提高采样速度,这在需要快速采样的场景很有用。
cpp复制// 设置ADC分辨率
analogReadResolution(12); // 9-12位可选
衰减配置也很关键,它决定了ADC的输入电压范围:
| 衰减值 | 最大输入电压 |
|---|---|
| 0dB | 1.1V |
| 2.5dB | 1.5V |
| 6dB | 2.2V |
| 11dB | 3.9V |
我常用11dB衰减,这样可以直接测量0-3.3V信号。但要注意,衰减值增大会引入更多噪声。
最简单的ADC读取只需要一行代码:
cpp复制int rawValue = analogRead(GPIO_NUM_36);
但实际项目中这样直接使用会有问题。我发现连续读取时,值会有波动。后来采用多次采样取平均的方法:
cpp复制#define SAMPLE_TIMES 32
int stableADCRead(int pin) {
long sum = 0;
for(int i=0; i<SAMPLE_TIMES; i++){
sum += analogRead(pin);
delayMicroseconds(100);
}
return sum / SAMPLE_TIMES;
}
ESP32 ADC有个特性:非线性。特别是在电压范围两端,误差更明显。官方提供了两种校准方法:
我开发了一个简单的两点校准函数:
cpp复制float adcCalibrate(int raw, float v1, int raw1, float v2, int raw2) {
float slope = (v2 - v1) / (raw2 - raw1);
return v1 + slope * (raw - raw1);
}
// 使用示例:
// 先测量0V(0)和3.3V(4095)对应的原始值
// 然后调用:voltage = adcCalibrate(rawValue, 0, raw0, 3.3, rawMax);
经过多次实验,我总结了几个有效降低ADC噪声的技巧:
利用ADC可以方便地监测电池电压。我常用这个分压电路:
code复制电池+ → [R1 100k] → ADC引脚 → [R2 220k] → 地
计算电压的公式:
cpp复制float batteryVoltage = (rawValue / 4095.0) * 3.3 * (100 + 220) / 220;
注意:分压电阻要选用1%精度的,普通5%电阻会导致测量误差很大。
当需要同时采样多个传感器时,要注意ESP32 ADC的以下限制:
我的解决方案是采用时分复用:
cpp复制void readMultiADC() {
static uint8_t current_ch = 0;
const uint8_t pins[] = {36, 39, 34, 35};
int value = analogRead(pins[current_ch]);
processADCData(current_ch, value);
current_ch = (current_ch + 1) % 4;
}
现象:ADC值在无输入变化时波动较大
可能原因:
我的排查步骤:
现象:测量已知电压时误差超过5%
解决方法:
现象:启用Wi-Fi后ADC2读数异常
根本原因:Wi-Fi射频工作时会占用ADC2资源
解决方案:
在最近的环境监测项目中,我需要用ESP32同时读取光照、温度和湿度传感器的模拟输出。遇到的主要挑战是如何在Wi-Fi持续连接的情况下保证ADC采样精度。最终方案是:
关键代码片段:
cpp复制#define FILTER_WINDOW 10
int filterBuffer[FILTER_WINDOW];
uint8_t filterIndex = 0;
int filteredADCRead(int pin) {
filterBuffer[filterIndex] = analogRead(pin);
filterIndex = (filterIndex + 1) % FILTER_WINDOW;
long sum = 0;
for(int i=0; i<FILTER_WINDOW; i++) {
sum += filterBuffer[i];
}
return sum / FILTER_WINDOW;
}
这个方案将ADC读数的波动从±30降低到了±5以内,完全满足项目要求。整个调试过程让我深刻理解了ESP32 ADC的特性和优化方法。