4、模拟信号与光敏电阻:理解ADC(模数转换)原理
好,咱们进入第四章。这一章要聊的,是让ESP32真正“感知”世界的关键一步——模拟信号读取。
你想想看,我们之前控制的LED灯,要么亮要么灭,这是数字信号。但现实世界呢?光线是渐变的,温度是连续的,声音是波动的。这些都是模拟信号。ESP32是个数字芯片,它只认0和1。那怎么让它理解“光线有点暗”这种模糊概念?
答案就是ADC——模数转换器。
4.1 什么是ADC?
ADC的全称是Analog-to-Digital Converter,中文叫模数转换器。它的工作说白了就一句话:把连续的电压值,映射成离散的数字值。
ESP32内置了两个ADC模块,分别是ADC1和ADC2。其中ADC1有8个通道,对应GPIO32到GPIO39。我个人习惯优先用ADC1,因为ADC2在Wi-Fi开启时会被占用,容易出问题。嗯,这里要注意,如果你同时用Wi-Fi和ADC2,读取结果可能会飘。
ESP32的ADC是12位精度的。12位是什么意思?就是它能将0~3.3V的电压范围,分成2^12 = 4096个等级。所以理论上,它能分辨的最小电压变化是:
3.3V / 4096 ≈ 0.8mV
也就是说,电压每变化0.8毫伏,ADC读数就会跳1。这个精度对于检测光线变化来说,绰绰有余。
核心概念:
- 分辨率:12位 → 0~4095
- 参考电压:默认0~3.3V
- 转换公式:数字值 = (模拟电压 / 3.3V) × 4095
4.2 光敏电阻与分压电路
光敏电阻,英文叫LDR(Light Dependent Resistor)。它的特性很简单:光线越强,电阻越小;光线越暗,电阻越大。
我在项目中遇到过一个问题:直接把光敏电阻接到ESP32的引脚上,读到的值要么是0,要么是4095,完全没有中间变化。为什么会这样?
因为光敏电阻本身只是一个可变电阻,它不能直接输出电压。你需要搭一个分压电路,才能把电阻变化转换成电压变化。
最简单的分压电路是这样的:
3.3V —— 10kΩ电阻 —— 测量点 —— 光敏电阻 —— GND
│
GPIO (ADC输入)
这个10kΩ电阻叫下拉电阻。当光线变强,光敏电阻阻值变小,测量点的电压就升高。当光线变暗,光敏电阻阻值变大,测量点的电压就降低。这样,ADC就能读到对应的电压变化了。
小提示:10kΩ是常用值,但你可以根据光敏电阻的型号调整。如果读数范围太小,试试1kΩ或100kΩ。我一般会先用手电筒照一下,看看读数能不能覆盖到100~4000之间。
4.3 硬件接线
你需要准备这些:
- ESP32开发板 × 1
- 光敏电阻(LDR) × 1
- 10kΩ电阻 × 1
- 面包板 × 1
- 杜邦线若干
接线方式如下:
| 光敏电阻一端 | → | 3.3V |
|---|---|---|
| 光敏电阻另一端 | → | GPIO34(ADC1通道6) |
| GPIO34 | → | 10kΩ电阻 → GND |
注意,我选的是GPIO34。这个引脚只支持输入,没有内部上拉,非常适合做ADC。你如果换别的引脚,记得查一下数据手册,确认它属于ADC1的通道。
4.4 代码实现:analogRead
ESP32的Arduino核心库提供了analogRead()函数,用法非常简单。来看代码:
// 光敏电阻 - ADC读取示例
// 引脚定义
const int ldrPin = 34; // 光敏电阻连接到GPIO34
int ldrValue = 0; // 存储ADC读取值
void setup() {
Serial.begin(115200); // 初始化串口,波特率115200
pinMode(ldrPin, INPUT); // 设置引脚为输入模式
Serial.println("光敏电阻测试开始...");
}
void loop() {
ldrValue = analogRead(ldrPin); // 读取ADC值 (0~4095)
// 打印到串口监视器
Serial.print("光线强度: ");
Serial.println(ldrValue);
delay(500); // 每500ms读取一次
}
这段代码的逻辑很直白:每半秒读一次光敏电阻的电压值,然后通过串口打印出来。你用手遮住光敏电阻,数值会下降;用手电筒照它,数值会上升。
注意:ESP32的ADC在靠近0V和3.3V时,线性度会变差。也就是说,读数为0时实际电压可能还有0.1V,读数为4095时实际电压可能只有3.2V。这不是bug,是芯片特性。如果你需要高精度测量,建议用外部ADC芯片,比如ADS1115。
4.5 观察与分析
上传代码后,打开串口监视器(波特率设置成115200)。你会看到类似这样的输出:
光线强度: 2450
光线强度: 2438
光线强度: 1023
光线强度: 987
光线强度: 3890
数值在跳动,这是正常的。因为环境光本身就在变化,加上ADC本身有噪声。你可以试试这些操作:
- 完全遮住光敏电阻:读数会降到100以下
- 用手电筒直射:读数会冲到4000以上
- 放在窗边:读数会随着云层飘动而变化
我曾经在做一个智能窗帘项目时,发现白天和晚上的读数差异只有500左右。排查了半天,才发现是分压电阻选错了。换成1kΩ后,差异拉到了3000。所以,分压电阻的选型直接影响你的测量范围。
4.6 避坑指南
这里总结几个我踩过的坑:
- 引脚选择:不要用GPIO0、GPIO2、GPIO12这些特殊引脚。它们在上电时有特殊功能,会影响ADC读数。
- 电源噪声:如果读数跳得很厉害,试试在3.3V和GND之间加一个100μF的电解电容。这能滤掉电源纹波。
- 参考电压:ESP32的ADC参考电压不是精确的3.3V,而是随芯片个体和温度变化的。如果你需要精确测量,要用
analogReadMilliVolts()函数,或者自己校准。
进阶技巧:你可以用analogReadMilliVolts()直接读取毫伏值,省去手动换算的麻烦。用法是:int mV = analogReadMilliVolts(ldrPin);。这样读出来的就是实际电压,单位是毫伏。
4.7 本章小结
这一章我们做了三件事:
- 理解了ADC的原理——把模拟电压转成数字值
- 搭了光敏电阻的分压电路——把电阻变化转成电压变化
- 用
analogRead()读取了光线强度——在串口监视器里看到了实时数据
你现在已经能让ESP32“看见”光了。下一章,我们会用这个光线数据,去控制LED的亮度。说白了,就是让灯学会“看天色”。
嗯,先到这里。去试试你的光敏电阻吧,看看你办公室的光线能读到多少?