4. ADC与模拟输入:analogRead函数、电位器读取电压、光敏电阻与光照传感器、ADC衰减与精度校准
各位同学,欢迎来到第四章。这一章我们聊聊模拟输入。说白了,就是让ESP32能「感知」真实世界的连续变化——比如旋钮转了多少度、光线暗了几分。
数字信号只有0和1,但现实世界是连续的电压。ADC(模数转换器)就是连接这两个世界的桥梁。ESP32内部集成了两个12位的SAR ADC,总共可以接18个通道。嗯,这里要注意,不是所有引脚都能做ADC,具体引脚号我建议你查一下官方数据手册,别焊完板子才发现引脚不对。
4.1 analogRead函数:最基础的模拟读取
ESP32的Arduino核心提供了analogRead(pin)函数。用法很简单,但背后有些细节值得说。
int adc_value = analogRead(36); // 读取GPIO36的模拟值
Serial.println(adc_value); // 输出0-4095之间的数值
返回值范围是0到4095,对应12位分辨率。为什么是4095?2的12次方减1,这个数字你应该记住。
analogReadResolution(12)显式设置分辨率。虽然默认就是12位,但写清楚能让代码更可维护。我在项目里吃过默认配置被意外改动的亏。
4.2 电位器读取电压:从旋钮到数值
电位器是最直观的模拟输入器件。三根引脚:两端接VCC和GND,中间抽头接ADC引脚。旋转旋钮,分压比变化,ADC读数跟着变。
我刚开始做智能家居面板时,用电位器模拟调光旋钮。代码很简单:
const int POT_PIN = 34;
int pot_value = 0;
void setup() {
Serial.begin(115200);
analogReadResolution(12);
}
void loop() {
pot_value = analogRead(POT_PIN);
float voltage = pot_value * (3.3 / 4095.0);
Serial.printf("ADC: %d, Voltage: %.2fV\n", pot_value, voltage);
delay(100);
}
这里有个坑:ESP32的ADC参考电压默认是3.3V,但实际VDD可能不是精确的3.3V。你想想看,如果板子供电是3.28V,那算出来的电压就有偏差。我建议你用万用表实测一下VDD,然后修正公式里的参考电压值。
| 电位器阻值 | 适用场景 | 注意事项 |
|---|---|---|
| 10kΩ | 通用模拟输入 | 功耗适中,最常用 |
| 1kΩ | 低阻抗场景 | 功耗较大,注意发热 |
| 100kΩ | 省电场景 | ADC输入阻抗匹配需注意 |
4.3 光敏电阻与光照传感器
光敏电阻(LDR)的阻值随光照变化。暗的时候几MΩ,亮的时候几百Ω。配合一个固定电阻组成分压电路,就能把光照变成电压。
典型电路:LDR接VCC,固定电阻(比如10kΩ)接GND,中间抽头接ADC。这样光照越强,LDR阻值越小,ADC读数越高。
const int LDR_PIN = 35;
int ldr_value = 0;
void setup() {
Serial.begin(115200);
}
void loop() {
ldr_value = analogRead(LDR_PIN);
// 简单映射:0-4095 -> 0-100%
int light_percent = map(ldr_value, 0, 4095, 0, 100);
Serial.printf("Light: %d%%\n", light_percent);
delay(500);
}
不过说实话,这种简单映射不太准。LDR的阻值-光照曲线是非线性的。如果你需要精确的勒克斯值,我建议用数字光照传感器(比如BH1750),或者自己标定一条曲线。
filtered_value = 0.8 * filtered_value + 0.2 * raw_value;这样既能响应变化,又不会太跳。
4.4 ADC衰减与精度校准
ESP32的ADC默认输入范围是0~3.3V。但如果你需要测量更高的电压怎么办?ADC提供了衰减选项,通过analogSetAttenuation()函数设置。
| 衰减设置 | 测量范围 | 适用场景 |
|---|---|---|
| ADC_0db | 0~1.1V | 高精度小信号 |
| ADC_2_5db | 0~1.5V | 中等精度 |
| ADC_6db | 0~2.2V | 常用范围 |
| ADC_11db | 0~3.3V | 默认模式,全量程 |
analogSetAttenuation(ADC_11db); // 设置0~3.3V量程
analogRead(36); // 读取值对应0~3.3V
这里有个重要的事:衰减越大,精度越低。ADC_0db的精度最好,但只能测到1.1V。ADC_11db能测3.3V,但精度会差一些。我一般这样选:如果信号范围确定,尽量用小衰减,把信号调理到合适的电压区间。
说到精度,ESP32的ADC其实有个「原罪」——它的线性度不太好。尤其是靠近0V和靠近满量程的区域,误差可能达到几十mV。官方后来推出了ADC校准方案,通过读取芯片内部eFuse中存储的校准参数来修正。
esp_adc_cal库,可以自动读取校准参数。Arduino环境下,你可以用analogSetVRef(1100)设置内部参考电压,或者用esp_adc_cal_characterize()函数获取校准后的特性。我建议量产项目一定要做校准,否则不同板子之间的读数差异会让你头疼。
下面这张图总结了ADC模拟输入的核心流程:
最后总结一下本章的核心:analogRead只是表面功夫,真正的功夫在电路匹配和校准上。你想想看,一个不稳定的ADC读数,后面再好的算法也是白搭。所以,花时间把前端电路调好,把校准参数配好,比什么都重要。