模块熟悉
第 10 课:电位器与 ADC
从"高或低"跨到"连续的值",这是模拟量世界的大门
我是吴老师。这节课是「模块熟悉」单元的开篇,也是整套课里一个重要的分界线。
前面几节课你接触的输入,都是数字的——按钮要么按下、要么没按,没有中间状态。非黑即白。
从这节课开始,我们进入模拟的世界——连续变化的量。光线有多亮、温度有多高、声音有多响,这些都不是"是/否",而是"多少"。
而电位器,是最适合入门模拟输入的东西。因为它最简单:你转一下旋钮,数值就变。
一、先认识电位器
电位器就是一个能拧的旋钮。你家里的音响音量旋钮、老式台灯的调光旋钮,都是这东西。
它长这样:一个蓝色的方块,上面有个小十字槽,可以用小螺丝刀拧。
| 针脚 | 接到 |
|---|---|
| VCC | 3.3V |
| GND | GND |
OUT |
一个模拟引脚(GPIO0 ~ GPIO5) |
注意最后一行——模拟引脚只有 6 个。 这件事第 4 课提过:
ESP32-C3 能用的引脚有 13 个,但支持模拟输入的只有
GPIO0到GPIO5这 6 个。你手上的光敏、热敏、麦克风、电位器,全都要往这 6 个里面挤。 接之前先规划好。
好,接好了。现在跑一下程序,拧旋钮看数字——你大概会看到它从 0 慢慢变到 4095。
为什么是 4095? 这就是这节课要讲的东西。
二、ADC:把电压翻译成数字
先抛出一个问题。
你的手拧旋钮,这是个物理动作。板子读到的是一串数字。这中间发生了什么?
答案叫 ADC(Analog-to-Digital Converter,模数转换器)。
它干的事,说白了就是"翻译":
- 引脚上的电压是连续的——理论上可以是 1.23456…… 伏,有无数种可能
- 而数字只能是一格一格的,不能有无穷多种
ADC 把电压切成 4096 格,看它在第几格。
| 电压 | 读到的数字 |
|---|---|
| 0V | 0 |
| 1.65V(大约一半) | 2048(大约一半) |
| 3.3V | 4095 |
为什么要切成 4096 格? 因为 ESP32-C3 的 ADC 是 12 位的。
"12 位"的意思是:用 12 个二进制位来表示一个数。 2 的 12 次方 = 4096,所以能表示 0 到 4095 这 4096 个值。
打个比方:你用一把尺子量东西。尺子上有多少条刻度线,你就能量得多细。
- 8 位 ADC = 尺子上 256 条刻度 → 比较粗
- 10 位 ADC = 1024 条刻度 → 中等(这是老式 Arduino Uno 的水平)
- 12 位 ADC = 4096 条刻度 → 比较细(我们的板子)
这里有个特别容易搞混的地方,我要提醒你:
网上很多教程写"模拟读数范围是 0~1023"——那是 Arduino Uno,不是我们的板子。
你要是照着一份 Uno 的教程写
if (读数 > 500),在你的板子上可能完全不 work,因为你的范围是 0~4095,500 在那上面是非常暗的水平。看教程先看它是哪块板子。 这个坑很常见。
三、电位器凭什么能调电压
现在回答另一个问题:转旋钮,电压为什么会变?
答案是分压。
电位器内部其实是两个电阻串联,而那个旋钮控制的是两个电阻的比例:
- 你往一头拧 → 上面那个电阻变小,中间点的电压接近 3.3V
- 你往另一头拧 → 下面那个电阻变小,中间点的电压接近 0V
- 拧到中间 → 电压大概是一半
而板子读的,就是那个"中间点"的电压。
所以:
| 你拧到哪 | 中间点电压 | 读数 |
|---|---|---|
| 一头 | ≈ 3.3V | ≈ 4095 |
| 中间 | ≈ 1.65V | ≈ 2048 |
| 另一头 | ≈ 0V | ≈ 0 |
这套"两个电阻分压"的结构,不只是电位器有——光敏、热敏内部也是同样的原理。
区别只是:电位器是你用手转,光敏是光线强弱自动改变其中一个电阻的阻值,热敏是温度改变。
所以这节课学明白了,后面光敏、热敏、麦克风那几节课,你就只需要学"是什么在改变电阻",原理部分是共通的。
四、动手:读一个模拟值
代码跟读按钮几乎一样,就一个词的区别:
int value = analogRead(0); // 读 GPIO0 上的模拟值注意是 analogRead(模拟读),不是 digitalRead(数字读)。
| 函数 | 读什么 | 返回什么 |
|---|---|---|
digitalRead |
数字 | HIGH 或 LOW(只有两种) |
analogRead |
模拟 | 0 到 4095 之间的一个整数 |
完整代码:
void setup() {
Serial.begin(115200);
// 模拟输入不需要 pinMode
}
void loop() {
Serial.println(analogRead(0));
delay(200);
}有个细节要说明
你注意到没有——我没有写 pinMode。
按钮那节课我写了 pinMode(4, INPUT_PULLUP),这里为什么不用?
因为模拟引脚不需要设置方向。它天生就是用来读的,不存在"输入还是输出"的选择,所以不用 pinMode。
那要不要
INPUT_PULLUP呢?也不要。上拉电阻是为了让数字引脚有个确定的默认电平(还记得那个"悬空乱飘"的问题吗)。
而模拟引脚读的是电压值本身——如果给它加上拉,那就等于给读数加了个偏移量,反而读不准了。模拟输入要的就是那个原始电压。
烧进去,打开串口监视器,然后慢慢拧旋钮。
你会看到数字平滑地变化,从 0 到 4095,中间有无数个值。
这就是"模拟"和"数字"的直观区别。 按钮只有两种可能,而它有你拧不完的中间值。
五、把数字变回电压
现在你已经会"读"了。再往前一步:怎么知道它对应的电压是多少?
其实很简单——反着算:
| 数字 | 电压 |
|---|---|
| 0 | 0V |
| 2048 | 3.3V ÷ 2 = 1.65V |
| 4095 | 3.3V |
所以换算公式是: 电压 = 读数 ÷ 4095 × 3.3
这里有个坑: 严格来说,4095 对应的是 3.3V,但实际板子上不一定正好是 3.3V。
而且 ADC 本身有误差,参考电压也可能有偏差。所以别指望换算出小数点后两位——你的读数大概有个百分之几的误差是正常的。
想知道准不准?用万用表量一下那个引脚的实际电压,跟你算出来的比一比。 这就是第 5 课那个本事用在这儿。
验证一下:你把旋钮拧到中间,代码读出来 2048,然后拿万用表量 OUT 脚——应该是 1.65V 左右。
对上了,说明你理解了;对不上,那就有意思了——去查为什么。
六、这节课真正的意义
表面上看,你只是学会了读一个旋钮。
但实际发生的事比这大得多。
你回想一下:
- 按钮只能告诉你"按了"或"没按"——两种可能
- 电位器能告诉你 0 到 4095 之间的4096 种可能
这个跳跃,就是数字世界和模拟世界的分界线。
为什么这件事重要? 因为真实世界绝大多数东西都是连续的:
| 现实中的量 | 是"是/否"还是"多少" |
|---|---|
| 灯开着吗 | 是/否 |
| 房间有多亮 | 多少 |
| 有人经过吗 | 是/否 |
| 现在多少度 | 多少 |
| 门关好了吗 | 是/否 |
| 声音有多响 | 多少 |
"是/否"类的问题,用 digitalRead;"多少"类的问题,用 analogRead。
你后面要学的光敏、热敏、麦克风,全是第二类。所以这节课的 analogRead 和那个 0~4095,你会反复用到。
还有一件事值得你自己想一想:既然 ADC 把连续的电压切成了 4096 格,那切掉的那些部分去哪了?
答案是——丢掉了。1.23456V 和 1.23457V 可能落在同一格里,读出来是同一个数。
这就是"精度"的含义:格子越密,丢失的信息越少。这就是为什么大家追求"16 位 ADC""24 位 ADC"——不是为了显示更多位数,是为了少丢一点信息。
自测
答案与解析
因为它是 12 位 ADC——2 的 12 次方 = 4096,所以范围是 0~4095。注意:网上很多教程写 0~1023,那是 Arduino Uno(10 位),不是我们的板子。 照着 Uno 的教程写判断条件,在你的板子上会完全不对。
pinMode?答案与解析
pinMode 的作用是分配岗位——一个引脚既可以输出也可以输入,得告诉它干哪个。而模拟引脚不存在这个选择,它就是用来读电压的。顺带一提:也不要给它加 INPUT_PULLUP,那会给读数叠加上一个偏移量,反而不准了。
答案与解析
这叫分压。往一头转 R1 变小、中间点接近 3.3V;往另一头转 R2 变小、中间点接近 0V。光敏、热敏内部也是同样的结构——只不过改变电阻的不是你的手,而是光线和温度。这就是为什么这几节课的原理是共通的。
答题要点
答题要点应该包含:"12 位"指用 12 个二进制位表示结果,能表示 2¹² = 4096 个不同的值(0~4095)。位数越高,格数越多,同一段电压被切得越细。打个比方就是尺子上的刻度线——8 位只有 256 条刻度,12 位有 4096 条,刻度越密量得越细。本质原因:连续的电压不可能被完全精确地表示成有限的数字,切片的时候必然会丢掉一点信息——位数越高,丢得越少。
拓展ADC 把连续电压切成 4096 格,那被"切掉"的那部分去哪了?这会带来什么问题?
这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。
提示:搜"量化误差 是什么"、"ADC 分辨率 精度"。想一想:如果两个不同的电压落在同一格里,读出来会是什么结果?这在什么场景下会变成麻烦?
拓展万用表也能测电压,它和 ADC 有什么区别?为什么万用表能显示 1.653V 这么精确?
这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。
提示:搜"万用表 位数 ADC 位数 区别"、"万用表 双积分 ADC"。想一想:万用表一秒钟测几次?它用"慢"换来了什么?
本节你要动手做的事
- [ ] 接好电位器:
VCC→3.3V,GND→GND,OUT→ 一个模拟引脚(GPIO0~GPIO5) - [ ] 跑通读数:拧旋钮,看着数字在 0~4095 之间变化
- [ ] 记两个极端:拧到两个尽头,记下最大和最小的读数分别是多少
- [ ] 用万用表验证:拧到中间,看代码读数是多少;再用万用表量
OUT脚的电压,看看跟你算出来的一样不一样 - [ ] 做个"亮度调节":用这个读数去控制 LED 的亮灭——比如"读数过半就亮灯"(提示:这需要
if,第 9 课学的)
这节课的内容不多,但它的位置很关键。
后面你要学的光敏、热敏、麦克风,全是模拟输入——它们读回来的都是一段连续的值,都用 analogRead,都在 0~4095 之间。
而它们内部的原理,跟今天这个电位器一模一样:都是"某个东西改变了电阻,导致分压点电压变化"。
区别只是——电位器是手拧的,光敏是光控制的,热敏是温度控制的。
你今天把这一套走通了,后面那几节课,你就只需要关心一件事:是什么在改变那个电阻。
下一节我们学光敏——你用手一遮,数字就变。那是模拟输入里最直观的一个。