上一节我们把数字"骗"成了模拟(PWM)。这一节要面对反过来的难题:怎么"读"一个连续变化的电压。 而这一次骗不过去——树莓派根本没有测量电压的能力,必须外接一颗芯片。本节就讲这颗芯片(MCP3008)怎么接、怎么读。
7-3 讲过"数字输入":读一根针是高还是低。但真实世界里的东西(光有多亮、温度多高、旋钮转了多少)不是高或低,而是一个连续的值。 7-4 结尾留了一个问题:"输出可以骗,输入没法骗"——本节就是来解释这句话的。 这一节也是本章唯一需要同时用上前面所有知识的一节:BCM 编号(7-1)、装库与权限(7-2)、接线与限流(7-3)、PWM 调光(7-4)。
一、难题:数字输入只能看到"两档"
7-3 的按钮,读到的是"高 or 低"。这是数字输入的全部能力。
但如果我接一个电位器(可调电阻),想知道"旋钮转到哪个位置"呢?
旋钮从一头转到另一头,中间要经过无数个位置,每个位置对应一个中间的电压。这不是"两档",是一条连续的线。
旋钮位置: 最左 ────────────────────────→ 最右
电压: 0V ─── 0.8V ── 1.6V ── 2.4V ── 3.3V
数字针看到:低? 低? 低? 低? 高? ← 几乎全是"低",最后才是"高"
↑ 中间这一大段,数字输入完全分不出来
一根数字针套上去,只能告诉你"电压有没有超过门槛"——转了大半圈都读作"低",最后一下才变"高"。它把连续的世界压扁成了两档。
二、硬事实:树莓派没有 ADC
要读连续的电压,需要一个叫 ADC 的部件。
ADC = Analog-to-Digital Converter,模数转换器。它做的事就是:把"几点几伏"这个连续量,变成一个数字。
在 Arduino 上,analogRead(A0) 是一句理所当然的话——它芯片里就带着 ADC。
在树莓派上,这句话不存在。你翻遍文档也找不到 analogRead,因为它没有任何一根针能测量电压——40 针全都是数字针。
原因和 7-4 讲的"没有 DAC"是同一个:树莓派的主芯片(BCM 系列)是给跑操作系统的单板电脑设计的"应用处理器",它的本职工作是算数、管内存、跑 Linux;而 ADC/DAC 是"单片机的活"。树莓派从设计开始就没打算干这件事。
所以:在树莓派上做模拟输入的答案永远是——外接一颗 ADC 芯片。
顺带钉一个容易混的点:树莓派 Pico 是有 ADC 的(Pico 是微控制器,不是电脑——1-2 讲过"Pico 不是电脑")。别把"树莓派"和"树莓派 Pico"搞混,它们的定位完全不同。
三、解法:买一颗 ADC 芯片
最常用的两颗,选一颗就行:
| 芯片 | 通道数 | 分辨率 | 接口 | 特点 | 适合 |
|---|---|---|---|---|---|
| MCP3008 | 8 路 | 10 位 | SPI(4 根线) | 便宜、通道多、资料最多 | 入门首选(本节用它) |
| ADS1115 | 4 路 | 16 位 | I2C(2 根线) | 精度高得多、还带可编程放大器 | 要精确、或接线想更少 |
"位"是什么意思?它决定能分多少档:
- 10 位 = 2¹⁰ = 1024 档(MCP3008)→ 从 0V 到 3.3V 之间,每档约 3.2mV
- 16 位 = 2¹⁶ = 65536 档(ADS1115)→ 每档只有约 0.05mV,细了 64 倍
"精度差 64 倍"听起来很夸张,但对你的第一个项目毫无影响——测电位器、测光敏电阻,10 位完全够看。MCP3008 更便宜、通道更多、教程更多,所以入门选它。
四、接线:MCP3008(本章最复杂的一张表)
4.1 先做一件事:打开 SPI
接之前必须先在系统里把 SPI 打开(7-1 第四节说过"复用功能要先打开"):
sudo raspi-config
进 Interface Options → SPI → 选 Yes,然后重启。
怎么验证打开了:
ls /dev/spidev*
看到什么算成功:出现 /dev/spidev0.0 和 /dev/spidev0.1 两行。
没打开的话:这个命令什么都看不到(提示 No such file or directory)。这就是"接好了但读不到数"最常见的原因。
4.2 MCP3008 的 16 个脚
MCP3008 是块 16 脚的小芯片,脚位是固定的:
从左上角开始往下数(有凹口的一端朝上、对着自己):1 = CH0,2 = CH1,3 = CH2,4 = CH3,5 = CH4,6 = CH5,7 = CH6,8 = CH7; 从右下角开始往上数:9 = DGND,10 = CS,11 = DIN,12 = DOUT,13 = CLK,14 = AGND,15 = VREF,16 = VDD。
4.3 接线表
| MCP3008 的脚 | 接到树莓派的哪 | 说明 |
|---|---|---|
| 16 · VDD | 3.3V(物理针 1 或 17) | 芯片自己的电源 |
| 15 · VREF | 3.3V(同上) | 参考电压——决定量程上限 |
| 14 · AGND | GND | 模拟地 |
| 9 · DGND | GND | 数字地 |
| 13 · CLK | BCM 11(物理针 23) | SPI 时钟 |
| 12 · DOUT | BCM 9(物理针 21) | 芯片 → 树莓派(数据出) |
| 11 · DIN | BCM 10(物理针 19) | 树莓派 → 芯片(数据进) |
| 10 · CS | BCM 8(物理针 24) | 片选("我要跟你说话了") |
| 1 · CH0 | 电位器的中间脚 | 第 0 号通道的输入 |
为什么这么强调?因为 VREF 决定了量程:接 3.3V 时量程是 0~3.3V;如果你为了"量程更大"把它接到 5V,那么芯片的 DOUT(数据输出脚)也会用 5V 电平往树莓派送信号——而 7-1 说过,5V 进 GPIO 是在赌板子的命。
规则:凡是和树莓派信号线相连的芯片,供电跟着树莓派走 3.3V。想要量程更大,得加"电平转换"电路,那是另一门功课。
4.4 电位器怎么接(这一步很容易接错)
电位器有三个脚:两端的两个,加中间那个(叫"滑臂")。
| 电位器的脚 | 接到 |
|---|---|
| 任意一端 | 3.3V |
| 另一端 | GND |
| 中间脚(滑臂) | MCP3008 的 CH0 |
转动旋钮时,中间脚的电压就在 0V 和 3.3V 之间连续变化——它本质上是一个"分压器"。
新手常犯的错:把电位器当成一个"可变电阻",只接中间脚和某一端。 那样接,中间脚的电压是浮空的(7-3 讲过浮空:读数会乱跳),你会在程序里看到数字毫无规律地乱蹦。 必须三个脚都接上:两端一个接 3.3V、一个接 GND,中间脚送信号去 CH0。
接线前断电;接完用眼睛检查一遍再上电。
五、用 gpiozero 读它:简单得让人不太敢信
gpiozero 内置了 MCP3008 支持,所以你不用装任何新库(这也是本节选它的理由之一):
from gpiozero import MCP3008
pot = MCP3008(channel=0) # 第 0 号通道
print(pot.raw_value) # 0 ~ 1023 的整数(芯片给出的原始值)
print(pot.value) # 0 ~ 1 的小数(换算好的比例)
print(pot.value * 3.3) # 换算成"伏"
两个属性,一个公式,就是全部:
| 属性 | 范围 | 含义 |
|---|---|---|
raw_value |
0 ~ 1023 的整数 | 芯片直接给出的原始数字(10 位) |
value |
0.0 ~ 1.0 的小数 | 比例("满量程的百分之几") |
| 电压 | 0 ~ 3.3V | value × 3.3,或"raw_value ÷ 1023 × 3.3" |
对照表(这张表值得记):
raw_value |
value |
电压 | 电位器位置 |
|---|---|---|---|
| 0 | 0.000 | 0.00V | 转到最左 |
| 256 | 0.250 | 0.83V | 约 1/4 |
| 512 | 0.500 | 1.65V | 正中间 |
| 768 | 0.751 | 2.48V | 约 3/4 |
| 1023 | 1.000 | 3.30V | 转到最右 |
看最后一行:raw_value 最大是 1023,不是 1024——因为从 0 开始数,1024 档就是 0~1023(计数从 0 起,这是编程的通用规矩,6-6 下标从 0 起是同一个道理)。
六、动手 Demo:让旋钮说话(两版)
接线(先断电):按上一节的接线表接好 MCP3008 + 电位器,另外保留 7-3 那颗 LED(BCM 17 → 220Ω → GND)。
第 1 步 · 读出来,还画个小柱子
nano adc_read.py
from gpiozero import MCP3008
import time
pot = MCP3008(channel=0)
print("转动电位器,看读数变化。Ctrl+C 退出。")
try:
while True:
raw = pot.raw_value
ratio = pot.value
volt = ratio * 3.3
bar = "#" * int(ratio * 40) # 用 40 个字符表示位置
print(f"raw={raw:4d} value={ratio:.3f} {volt:5.2f}V |{bar:<40}|")
time.sleep(0.2)
except KeyboardInterrupt:
print("\n已结束")
finally:
pot.close()
跑:python3 adc_read.py
你会看到什么算成功(转动旋钮时,raw、value、电压和柱子一起变):
raw= 0 value=0.000 0.00V | |
raw= 256 value=0.250 0.83V |########## |
raw= 512 value=0.500 1.65V |#################### |
raw= 768 value=0.751 2.48V |############################## |
raw=1023 value=1.000 3.30V |########################################|
读结果:注意 raw 最大到 1023 就上不去了——这就是 10 位的天花板。
这个"柱子"值得多看一眼:"#" * int(ratio * 40) 的意思是把 0~1 的比例映射到 0~40 个字符。同一套"把连续量变成刻度"的思路,就是所有仪表盘、进度条、音量条的底层逻辑。
第 2 步 · 让旋钮控制灯的亮度(把 7-4 和 7-5 接起来)
nano adc_to_led.py
from gpiozero import MCP3008, PWMLED
import time
pot = MCP3008(channel=0)
led = PWMLED(17)
print("转动电位器 → 灯的亮度跟着变。Ctrl+C 退出。")
try:
while True:
led.value = pot.value # 直接把读到的比例送给灯
time.sleep(0.01)
finally:
led.off()
pot.close()
跑:python3 adc_to_led.py
你会看到什么算成功:转动旋钮,灯的亮度平滑跟着变。
读结果:注意中间那行 led.value = pot.value —— 一边是"读进来的模拟量",一边是"送出去的模拟量",这一行就是"闭环控制"的雏形。
这一行也解释了本课的一个大主题:树莓派真正擅长的不是"处理模拟信号",而是"做决定"——读一个值、算一算、再输出一个值。模拟量的采集和输出可以交给专门的芯片,中间的"判断与计算"归它。
七、30 秒实验:算一算"10 位"和"16 位"差多少
python3 -c "
print('MCP3008 (10 位):%.2f mV 一档' % (3.3/1024*1000))
print('ADS1115 (16 位):%.3f mV 一档' % (3.3/65536*1000))
print('相差倍数:%.0f 倍' % (65536/1024))
"
你会看到什么:
MCP3008 (10 位):3.22 mV 一档
ADS1115 (16 位):0.050 mV 一档
相差倍数:64 倍
读结果:"位数"就是"能分多细"。
- 3.22mV 是什么概念——3.3V 被切成 1024 段,每一段只有 3 毫伏多一点。测量"旋钮转到哪儿""屋里亮不亮"这类东西,这个精度绰绰有余。
- 那什么时候需要 16 位?当你要测的变化本身很小时——比如称重传感器(受力时电压只变几毫伏),这时 3.22mV 一档的分辨率就"看不见"那个变化了,必须用高精度的芯片。
一句话:先问"我要测的变化有多大",再决定"需要几位"。——这比"哪个芯片更好"这种问法有用得多。
八、常见误区
- "树莓派有
analogRead可以直接读模拟量" —— 没有。这是 Arduino 的写法。树莓派没有任何内置 ADC,读模拟电压必须外接芯片(MCP3008/ADS1115)。记住"树莓派 Pico 有 ADC、树莓派没有"这个区别。 - "把芯片的 VREF 接 5V 量程更大更好" —— 绝对不能。接 5V 会让芯片用 5V 电平往 GPIO 送信号,有烧板子的风险(7-1 的铁律)。和树莓派相连的芯片一律 3.3V。
- "电位器接中间脚就够了" —— 不行。两端必须分别接 3.3V 和 GND,否则中间脚浮空、读数乱跳。
- "接好了代码也抄对了,但读到的永远是 0" —— 十有八九是 SPI 没打开。先跑
ls /dev/spidev*确认;没有就回raspi-config打开并重启。 - "
raw_value最大是 1024" —— 是 1023。因为从 0 开始数,1024 档是 0~1023(和"列表下标从 0 开始"同一个道理)。 - "10 位太差了,必须上 16 位" —— 先看你要测什么。电位器、光敏、土壤湿度这类,10 位完全够;只有"变化极其微小"的信号(称重、精密传感器)才需要 16 位。
- "MCP3008 有 8 个通道,可以同时读 8 路" —— 它是轮流读的(一次选一个通道),不是同时。对绝大多数项目,轮流读快到看不出来(每秒能读十几万次)。
自测三问(点开对答案)
1. 为什么树莓派读不了模拟电压?这和上一节讲的"没有 DAC"有什么关系? 2. MCP3008 读回 `raw_value = 512`,对应的电压是多少?把你算的式子写出来。 3. 你把 ADC 接好了、代码也抄对了,但 `raw_value` 一直是 0,最该先查什么? **参考答案** 1. 因为**树莓派没有内置 ADC(模数转换器)**——它的 40 针**全部是数字针**,只能判断"高还是低",无法测量中间电压。 这和"没有 DAC"是**同一个根本原因**:**树莓派的主芯片是给"跑操作系统的电脑"设计的应用处理器,ADC/DAC 属于单片机的职能**。所以它俩都得靠外接芯片解决(输出用 DAC,输入用 ADC)。 (**注意别和 Pico 混**:Pico 是微控制器,**它自带 ADC**。) 2. **约 1.65V**。算法:**512 ÷ 1023 × 3.3 ≈ 0.5005 × 3.3 ≈ 1.65V**(用 `value` 表达就是 **`value = 0.500`,再乘 3.3**)。 注意分母是 **1023**(不是 1024)——因为 10 位的 1024 档是从 0 数到 1023。 3. **先查 SPI 有没有打开**:跑 `ls /dev/spidev*`,看不到设备就说明接口没开 → `sudo raspi-config` → Interface Options → SPI → 打开并重启。 **这个"读不到数先查接口开关"的顺序很重要**——它比"怀疑接线"和"怀疑代码"都更可能命中(因为接线和代码通常是你照着抄的,而"接口没开"是教程常常漏掉的一步)。- ☐ 下一节(7-6 Sense HAT 扩展板) —— 一块直接叠上去的官方扩展板:64 颗彩灯 + 一堆传感器,一装就能玩
- ☑ 本章第五节已读 —— 会用 ADC 读模拟量了,也知道了树莓派"能算不能测"的边界
相关笔记
- 模拟输出 —— 上一节:PWM 与占空比,本节 Demo 第 2 步用了它的
PWMLED - 数字输出与输入 —— 数字输入与"浮空"的概念,电位器只接中间脚时的乱跳就是浮空
- 认识树莓派的 GPIO 引脚 —— SPI 用哪四根针(BCM 8/9/10/11)与"复用功能要先打开"
- 设置 Raspberry Pi OS ——
raspi-config里那个 SPI 开关(本节第一次真的要用它) - 树莓派的型号 —— "Pico 是微控制器,不是电脑":为什么它有 ADC 而树莓派没有
- 🍓 树莓派 · 索引 —— 全课路线图