4、模拟信号采集:ADC原理、ESP32的ADC通道、光敏电阻读取、电压换算

好,咱们进入第四章。前面几章我们一直在跟数字信号打交道——GPIO要么高要么低,I2C传的是字节。但真实世界不是非0即1的,温度、光照、声音,这些都是连续变化的模拟量。ESP32怎么理解这些?靠的就是ADC——模数转换器。

这一章,我会带你从ADC的原理讲起,然后直接上手ESP32的ADC通道,接一个光敏电阻,把光照强度变成电压值读出来。最后,我们还得把ADC的原始读数换算成真实的电压。嗯,这部分坑不少,我当年第一次用ESP32的ADC时就被坑过,后面会跟你细说。

4.1 ADC是什么?为什么需要它?

ADC,全称Analog-to-Digital Converter,模数转换器。说白了,就是把连续变化的电压,变成单片机能理解的数字。

你想想看,ESP32的CPU只能处理0和1,它没法直接理解“2.5伏”这个模拟量。ADC的作用就是每隔一段时间,去“看一眼”输入引脚上的电压,然后给它分配一个数字编号。这个编号的精度,取决于ADC的分辨率。

分辨率 数字范围 可区分的电压等级数
8-bit 0 ~ 255 256
10-bit 0 ~ 1023 1024
12-bit 0 ~ 4095 4096
16-bit 0 ~ 65535 65536

ESP32的ADC默认是12-bit分辨率。这意味着,如果参考电压是3.3V,它能将0~3.3V的电压范围,划分成4096个等级。每个等级对应的电压步进大约是3.3V / 4096 ≈ 0.8mV。这个精度,对于大多数传感器采集来说,完全够用了。

核心概念:ADC的“位数”越高,能分辨的电压变化就越细微。12-bit的ESP32,理论上能感知到0.8mV的电压变化。

4.2 ESP32的ADC通道与硬件限制

ESP32内部集成了两个ADC模块:ADC1和ADC2。它们各有8个通道,但ADC2的部分通道会被Wi-Fi占用,这是个非常容易踩的坑。

我个人习惯是:只要能用ADC1,就绝不用ADC2。为什么?因为Wi-Fi一开,ADC2的某些通道读数就会变得不稳定,甚至直接罢工。我在一个项目中就吃过这个亏,当时用ADC2通道6接了一个电流传感器,Wi-Fi一连接,数据全乱套了。排查了半天才发现是通道冲突。

ADC模块 可用通道 对应GPIO 注意事项
ADC1 CH0 ~ CH7 GPIO36, 37, 38, 39, 32, 33, 34, 35 Wi-Fi不影响,推荐使用
ADC2 CH0 ~ CH7 GPIO4, 0, 2, 15, 13, 12, 14, 27 Wi-Fi开启时,部分通道不可用

避坑指南:我曾经在一个需要同时采集传感器数据和连接Wi-Fi的项目中,因为使用了ADC2的GPIO12,导致Wi-Fi连接后传感器读数跳变。后来全部切换到ADC1的GPIO32~39,问题才解决。记住:有Wi-Fi的场景,只用ADC1。

4.3 实战:读取光敏电阻

理论说完了,咱们直接动手。光敏电阻(LDR)的阻值会随光照强度变化——光照越强,阻值越低。我们把它和一个固定电阻串联,组成分压电路,中间点接到ESP32的ADC引脚上。这样,光照变化就会引起电压变化,ADC就能读出来了。

电路连接非常简单:

  • 光敏电阻一端接3.3V
  • 光敏电阻另一端(信号端)接一个10kΩ电阻到GND
  • 信号端同时接到ESP32的GPIO34(ADC1_CH6)

下面是一段完整的Arduino代码,用来读取光敏电阻的ADC值并换算成电压:

// 光敏电阻读取示例 - ESP32 ADC
#define LDR_PIN 34          // ADC1_CH6

void setup() {
  Serial.begin(115200);
  analogReadResolution(12); // 设置ADC分辨率为12位
  // 可选:设置衰减,允许测量更高电压
  // analogSetAttenuation(ADC_11db); // 0~3.6V范围
}

void loop() {
  int adcValue = analogRead(LDR_PIN);          // 读取原始ADC值 (0~4095)
  float voltage = adcValue * (3.3 / 4095.0);   // 换算成电压

  Serial.print("ADC原始值: ");
  Serial.print(adcValue);
  Serial.print("  |  电压值: ");
  Serial.print(voltage, 3);  // 保留3位小数
  Serial.println(" V");

  delay(500);
}

这段代码跑起来后,你用手电筒照光敏电阻,串口输出的电压值会明显上升;用手挡住,电压值会下降。嗯,这就是模拟信号采集最直观的体验。

小技巧:如果你发现电压值始终偏低或偏高,可以试试调整衰减(attenuation)。ESP32的ADC默认衰减是0dB,测量范围约0~1.1V。调用analogSetAttenuation(ADC_11db)后,测量范围可以扩展到0~3.6V,更适合3.3V系统。

4.4 电压换算与校准

上面代码里的换算公式 voltage = adcValue * (3.3 / 4095.0),看起来没问题吧?但实际用起来,你会发现读数跟万用表测出来的电压总有偏差。为什么?

因为ESP32的ADC线性度不够好,而且不同芯片之间还有差异。3.3V参考电压本身也不是绝对精确的。我做过对比测试,在2.0V附近,ADC读数换算出来的电压,跟实际电压可能差0.05V~0.1V。这在一些对精度要求高的场景下,是不能接受的。

解决办法有两个:

  1. 软件校准:用万用表测出几个点的实际电压,然后做线性拟合,修正换算公式。
  2. 硬件校准:使用外部精密电压基准芯片(如TL431),给ADC提供一个已知的参考电压。

对于大多数物联网项目,软件校准就够用了。下面是一个简单的两点校准示例:

// 两点校准法
// 假设你测得:
// 实际电压1.0V时,ADC读数为1240
// 实际电压2.5V时,ADC读数为3100

float calibratedVoltage(int adcValue) {
  // 斜率 = (2.5 - 1.0) / (3100 - 1240)
  float slope = 1.5 / 1860.0;
  // 截距 = 1.0 - slope * 1240
  float intercept = 1.0 - slope * 1240;
  return adcValue * slope + intercept;
}

经验之谈:ESP32的ADC在低电压段(0~0.5V)和高电压段(3.0~3.3V)的非线性最严重。如果你需要高精度测量,尽量让传感器信号落在0.5V~2.8V这个区间内。我一般会通过调整分压电阻,把信号“搬”到这个区间再送ADC。

4.5 本章知识体系

为了让你更直观地理解本章的知识脉络,我画了一张流程图,把ADC采集的完整链路串起来:

ADC信号采集与处理流程 ADC信号采集与处理流程 物理量(光照) 光敏电阻(LDR) 分压电路(LDR + 10kΩ) ESP32 ADC(12-bit) 电压换算 + 校准 关键参数 • 分辨率:12-bit • 范围:0~4095 • 参考电压:3.3V • 推荐通道:ADC1

从这张图可以看得很清楚:物理量(光照)→ 传感器(LDR)→ 分压电路 → ADC采样 → 软件换算与校准。每一步都有它的作用,缺一不可。

4.6 本章小结

这一章我们干了三件事:

  • 理解了ADC的原理——把连续电压变成离散数字,分辨率决定了精度。
  • 摸清了ESP32的ADC通道——ADC1和ADC2的区别,以及Wi-Fi带来的坑。
  • 动手读取了光敏电阻——从电路搭建到代码实现,再到电压换算和校准。