Pi-Face 扩展板实战:树莓派 GPIO 与 CM4 引脚对照及 ADC 读取
2026/9/23 3:29:03 网站建设 项目流程

Pi-Face 这名字一出来,熟悉树莓派生态的人大概能猜到七八分:它是一块给 Raspberry Pi 用的 GPIO 扩展板。我第一次接触它,是在一个物理计算教学项目的实验清单里,当时配套文档只有几页 PDF,但照着接线、跑通示例代码只花了不到二十分钟,这种“插上去就能用”的体验在 GPIO 外设里相当难得。后来我自己做控制类原型也经常把它翻出来用,所以这次就专门聊聊这块板子的设计思路、引脚映射,以及这段时间大家经常搜的 Compute Module 4 GPIO 引脚图到底该怎么对照。

它可以做两路数字输入、两路数字输出和一路模拟输入,核心价值是把树莓派裸奔的 GPIO 转换成安全、稳定的控制接口。适合刚接触硬件编程的学生、准备物理计算课件的老师,也适合想快速验证控制逻辑的创客。下面把硬件原理、引脚映射和实操代码一次讲透。

1. 项目定位与整体设计思路:GPIO 扩展板为什么还有存在价值

1.1 裸 GPIO 的痛点与 Pi-Face 的答案

树莓派的 40-Pin GPIO 排针本身就能直接输出高低电平、读取按键状态,为什么还要专门买一块 Pi-Face?这个问题我经常在交流会上被问到。答案是:裸针能干活,但干得不够体面。直接拿杜邦线接 LED,限流电阻算不对就容易超电流;接按键,去抖和上拉要自己处理;想读模拟量,几乎没门,因为树莓派的 GPIO 是纯数字接口,没有内置 ADC。Pi-Face 把这些外围电路统一做在一块板子上,输入侧加了缓冲器,输出侧用了达林顿驱动管,模拟侧直接焊了一颗 10 位 ADC,用户层面看到的只有干净的接口和稳定的事件。

这块板子出自大学实验室,带着很明显的教学基因。它不是把引脚全部引出了事,而是刻意把功能裁剪成“2 路数字输入、2 路数字输出、1 路模拟输入”的最小集合。这个裁剪很关键:初学者面对 40 个引脚会懵,面对“两个按钮、两个灯、一个旋钮”却很容易理解。你仔细观察会发现,现实里要控制的对象,绝大多数可以归约成这几个基本量:开关量检测、开关量输出、连续量采集。把这三种量练熟,再往上做传感器网络或者电机控制,思路都是相通的。

1.2 硬件架构拆解:数字输入、数字输出、模拟输入三条链路

Pi-Face 的硬件链路可以拆成三条独立通路来理解,这也是它调试起来舒服的根本原因。数字输入链路由板载按钮和外部接线端子共用,信号经过缓冲芯片整形后才进入树莓派 GPIO,缓冲器一方面做电平整形,让输入信号变得干净利落,另一方面提供静电防护,避免直插树莓派引脚时把 BCM 芯片打坏。数字输出链路则正好相反,树莓派 GPIO 先给到达林顿驱动管的基极,再由驱动管去点亮板载 LED 或驱动外部负载。用达林顿而不是直接用 GPIO 驱动,是为了把电流负载从主芯片上剥离出去,树莓派的 GPIO 灌拉电流能力有限,直接驱动继电器或小电机很容易把引脚烧出暗病。

模拟输入链路的关键是一颗 MCP3002,这是 Microchip 的 10 位、双通道 ADC,树莓派通过 SPI 总线向它发起转换请求并读取结果。三条链路互不干扰,这是分区设计带来的好处。实际调试时我经常只接其中一条链路来定位问题,比如把模拟输入短路到地,如果读数能稳定回到接近 0,说明 ADC 和 SPI 通信是健康的,问题大概率在外围传感器上,这种隔离排查法能省掉大量时间。

1.3 为什么把 ADC 挂在 SPI 上

既然只是读一颗 ADC,为什么树莓派原生不提供模拟输入端口?我一般用这个例子向新手解释:芯片内部是数字世界,外部才是模拟世界,中间必须有 ADC 这层翻译官。Pi-Face 选择把 ADC 挂在 SPI 上,是因为 SPI 是树莓派上速度最快、最稳定的同步串行接口之一。MCP3002 的典型转换速率在 1.8 Mbps 左右,对于旋钮、温度、光照这些慢变信号绰绰有余。相比之下 I2C 虽然只需要两根线,但协议开销更大,设备地址冲突也更多;UART 是异步通信,还得额外约定波特率。SPI 的四根线(SCLK、MOSI、MISO、CE)在逻辑上更纯粹,非常适合作教学示范,也方便用示波器直接观察时序。

提示:MCP3002 虽然是双通道,但 Pi-Face 通常只把其中一路引到端子上,另一路留给板载校准或悬空。如果你做多路采集,可以外接模拟开关,或者直接换 MCP3008(8 通道版本),引脚和 SPI 协议基本兼容,代码只需要改命令字节的通道位。

2. 引脚映射与 Compute Module 4 GPIO 对照

2.1 标准 40-Pin GPIO 关键引脚速查

Pi-Face 全部引脚都通过树莓派顶部的排针引出,不额外占用 USB 或 CSI/DSI 接口,这是它最大的优势之一。要理解它,你必须先会看树莓派的 GPIO 编号体系。树莓派上有三套命名在打架:物理引脚号、BCM 编号、wiringPi 编号(现在基本淘汰)。Pi-Face 文档里使用的是 BCM 编号,这也是所有 Python 库默认的编号方式。

我整理了一张 Pi-Face 最常涉及的引脚对照表,建议收藏:

信号BCM GPIO40-Pin 物理引脚说明
SPI0 SCLKGPIO1123SPI 时钟
SPI0 MOSIGPIO1019主出从入,写配置命令
SPI0 MISOGPIO921主入从出,读 ADC 结果
SPI0 CE0GPIO824片选,选通 MCP3002
数字输入 1GPIO1711经缓冲器输入
数字输入 2GPIO1812经缓冲器输入
数字输出 1GPIO2215经驱动管输出
数字输出 2GPIO2316经驱动管输出
5V-2 或 4驱动管及外部负载供电
3.3V-1 或 17缓冲器与 ADC 供电
GND-6、9、14、20 等公共地

不同版本的 Pi-Face 对输出引脚的 BCM 编号可能有差异,具体以板子丝印为准。关键是记住:SPI0 的四根线是固定的,输入输出通道大多落在 GPIO17、GPIO18、GPIO22、GPIO23 这几个口上,因为它们不参与任何外设复用,是最安全的通用输入输出口。这也是为什么很多 GPIO 扩展板都默认用这四个口,算是一个行业惯例。

2.2 CM4 的 100-Pin 连接器与转接思路

最近很多人开始用 Compute Module 4 做产品,搜“raspberry pi compute module 4 gpio引脚图”的概率也高了很多。CM4 和普通树莓派最大的差异是,它没有 40-Pin 排针,所有 GPIO 都分散在两个 100-Pin 的高速板对板连接器(J1、J2)上。我第一次拿到 CM4 核心板时也愣了一下,因为光靠肉眼根本分不清哪个脚是 GPIO17。

但你要记住一个关键事实:CM4 的 BCM 编号体系没有变。GPIO11 仍然是 SCLK,GPIO9 仍然是 MISO,GPIO17 仍然是那个可用的通用输入脚。变的只是物理位置。在官方 CM4 IO Board 上,40-Pin 排针的物理位置和标准树莓派一致,所以 Pi-Face 插上去照样能用。如果你用的是第三方载板,就得回到 CM4 数据手册里的 J1/J2 引脚表,按“信号名 -> BCM 编号 -> 连接器引脚号”三步去找,不要凭记忆乱接线。

这里分享一个我踩过坑后养成的习惯:拿到任何一块 CM4 载板,第一件事不是接外设,而是用万用表蜂鸣档测一下 40-Pin 排针的 GND 和 3.3V 分布,确认默认上电不会把电源和地引反。CM4 核心板的引脚间距只有 0.5mm,比标准排针密得多,一旦接反,损坏的速度比普通树莓派快得多,返修成本也很高。

2.3 引脚冲突排查:从原理到实践的注意事项

Pi-Face 占用的 SPI0 与部分扩展板存在资源冲突,这是我在实际项目中遇到最多的问题。比如某些 LCD 屏幕、RFID 读卡器也默认挂在 SPI0 上,两者同时启用时,设备树会报错,或者出现其中一个设备时好时坏的现象。

排查思路其实很简单:先看设备树加载了哪些 overlay。执行dtoverlay -a可以列出所有可用 overlay,执行dtoverlay -l看当前加载的列表。如果发现 SPI 屏占了 spi0-1cs,而你又在跑 Pi-Face,要么把 Pi-Face 的 ADC 片选改到 CE1(GPIO7),要么换一个用 I2C 接口的屏幕。我在项目中一般优先把显示类外设挪到 I2C,把 SPI0 完整留给数据采集,因为 SPI 在采集上的时序稳定性更好。

另外要注意,CM4 系列的默认固件可能不会自动启用 SPI。有些第三方载板即使引出了排针,系统里也找不到/dev/spidev0.0,这不是硬件坏了,而是设备树里没有把 spi0 打开。这个问题在下一节实操里会详细讲。

3. 实操全流程:配置、接线、代码逐行过

3.1 系统配置:启用 SPI 与安装依赖

树莓派系统默认不启用 SPI 硬件接口,这是新手最容易卡住的地方。我建议直接用命令行操作,比图形界面的 raspi-config 更可控。执行:

sudo raspi-config

选择Interface Options -> SPI -> Enable,然后重启。重启后检查设备节点是否生成:

ls -l /dev/spidev*

正常情况下你会看到/dev/spidev0.0/dev/spidev0.1两个节点,分别对应 CE0 和 CE1。如果节点不存在,先确认config.txt里有没有以下两行:

dtparam=spi=on dtoverlay=spi0-1cs

注意第二行不是必须的,spi0-1cs的作用是只启用 CE0 一路片选,释放 GPIO7 给其他用途。如果 Pi-Face 用的是 CE0,这个配置能帮你多留出一个引脚。

接着安装 Python 依赖。传统做法是装python3-spidevRPi.GPIO,我现在的推荐是gpiozerospidev的组合,因为 gpiozero 对按钮、LED 这类基础外设封装得非常好,代码量能少一半:

sudo apt update sudo apt install python3-spidev python3-gpiozero

注意:新版系统里 RPi.GPIO 在部分内核上会有兼容警告,如果只是读写数字引脚,gpiozero 底层会自己选择合适的驱动方式,省去一堆麻烦。如果你的代码依赖 RPi.GPIO 的 PWM,再单独安装它即可。

3.2 接线步骤与负载能力边界

接线是整个过程中最容易出问题的环节。我的操作顺序是:先断电,再接 Pi-Face,最后上电检查。别嫌我啰嗦,带电插拔 GPIO 扩展板是烧芯片的头号原因。

Pi-Face 的接线分三类。第一类是板级连接,直接把板子对齐排针插上去,注意排针方向,别错位一格,错位通常会导致 5V 电源直接灌进某个 GPIO,后果不用多说。第二类是数字输入,把按钮或开关接到输入端子,一端接信号,另一端接 GND,Pi-Face 内部已经做了上拉处理,不需要额外接电阻。第三类是模拟输入,把电位器中间抽头接到模拟输入端子,另外两脚分别接 3.3V 和 GND,这样你转动旋钮时,ADC 读数就会在 0 到 1023 之间平滑变化。

负载能力这个边界必须说清楚。Pi-Face 的输出级用的是达林顿驱动管,它的电流能力比树莓派 GPIO 直接输出强不少,但也不是无限大。驱动单个 LED 完全没问题,驱动 5V 继电器线圈也勉强可以,但建议在线圈两端并联一个续流二极管,否则关断瞬间的反向电动势可能打坏驱动管。如果需要驱动电机或大功率负载,正确做法是让 Pi-Face 只做逻辑控制,后面再接继电器或 MOS 管驱动板,千万不要试图让这个小板子扛大电流。

3.3 Python 控制代码:读按钮、控灯、取模拟量

硬件接好之后,上电开机,写第一段测试代码。我习惯先读模拟量,因为这是最容易做自检的通道:把模拟输入悬空时读数不稳定是正常的,接上电位器后读数应该随旋钮单调变化。下面这段代码实现了三个功能:读两路按钮、控制两路输出、读取一路模拟电压:

import time import spidev from gpiozero import LED, Button spi = spidev.SpiDev() spi.open(0, 0) # SPI0, CE0 spi.max_speed_hz = 1000000 spi.mode = 0b00 def read_adc(channel): # MCP3002 命令字节: 起始位 + 单端 + 通道选择 if channel == 0: cmd = 0b01100000 else: cmd = 0b01110000 reply = spi.xfer2([cmd, 0, 0]) value = ((reply[1] & 0x03) << 8) | reply[2] return value btn1 = Button(17, pull_up=True) btn2 = Button(18, pull_up=True) led1 = LED(22) led2 = LED(23) try: while True: print(f"ADC0={read_adc(0):4d} ADC1={read_adc(1):4d} " f"B1={btn1.is_pressed} B2={btn2.is_pressed} " f"L1={led1.is_lit} L2={led2.is_lit}") # 按钮 1 按下时切换 LED1,按钮 2 按下时切换 LED2 if btn1.is_pressed: led1.toggle() if btn2.is_pressed: led2.toggle() time.sleep(0.1) except KeyboardInterrupt: spi.close() print("\n退出")

这段代码有几个设计细节值得讲。spi.max_speed_hz = 1000000是我反复试验后觉得比较稳妥的值,杜邦线长的时候 SPI 速率太高会导致 ADC 读数乱跳,降到 1MHz 左右牺牲一点采样率,换来的是稳定性;如果你的线很短或者用的是面包板,可以适当提高到 2MHz。spi.mode = 0b00是 MCP3002 要求的 SPI 模式,CPOL=0、CPHA=0,这两个参数写错的话,读出来的数据会整体错位,表现为数值杂乱无章。按钮那两行用了pull_up=True,确保浮空时读到确定的高电平,避免误触发。

运行后如果一切正常,你能看到串口不断刷新状态,按下按钮时对应布尔值变化,ADC 读数随电位器转动变化。这个“全状态回显”的写法是我做所有传感器调试前的习惯,先把所有输入输出都打出来,确认硬件通路没问题,再往上叠加业务逻辑。

3.4 扩展:把 Pi-Face 接入自动化场景

Pi-Face 这种结构化接口非常适合做无头模式下的自动化控制。我有一次用它做了一个办公室植物灌溉的原型:土壤湿度传感器接在模拟输入上,水泵继电器接在数字输出上,当湿度低于阈值时自动启动水泵,高于阈值就停止。整个代码在上一节的基础上只多了一个判断分支,但价值立刻就不一样了。

还有一次是拿它做门磁报警,门磁开关接数字输入,报警蜂鸣器接数字输出,配上systemd服务开机自启,放在角落里跑了一个多月,除了偶尔 SD 卡掉线需要重启,没有出过硬件问题。这种长时间运行的稳定性,正是 Pi-Face 相比面包板飞线方案最大的优势:没有松动的杜邦线,没有悬空的引脚,所有连接都被板子约束好了。

4. 常见问题与排查技巧实录

4.1 SPI 启用了但读不到数据

现象是/dev/spidev0.0存在,但 Python 里 open 时报FileNotFoundError,或者 xfer2 返回的全是 0。第一种情况通常是用户权限问题,试试让当前用户加入spi组:sudo usermod -aG spi 你的用户名,然后重新登录。第二种情况多半是接线问题,重点检查 MISO 是否真的接到了 GPIO9。我遇到过最隐蔽的情况是:有人把板子插反了一个引脚,SPI 写命令正常,但读数据永远不对,因为 MISO 根本没有物理连通,这种情况用万用表量一下排针对应关系立刻就能发现。

4.2 数字输入被误触发

按钮不按的时候偶尔会跳一下,或者完全紊乱。先别怀疑 Pi-Face 坏了,优先检查是不是按钮引脚浮空。虽然板载有上拉,但如果外部按钮线特别长,线缆本身就相当于一根天线,工频干扰和电机启停产生的尖峰都会耦合进来。解决办法有三个:在输入端并一个 0.1uF 电容做硬件滤波;在代码里做 20ms 到 50ms 的去抖;或者换用屏蔽线。我一般三重手段一起上,实测下来能解决 99% 的误触发问题。

4.3 模拟量读数跳变

ADC 读数不稳定,除了 SPI 速率过高以外,最常见的原因是参考电压不稳。树莓派的 3.3V 是从板载稳压器出来的,本身精度不错,但如果同一时刻有大负载在数字输出端切换,瞬间电流变化会引起电压波动,传导到 ADC 参考引脚上,读数就会跟着跳。我的建议是:模拟传感器和数字负载供电分开,模拟部分单独走板上的 3.3V 或外接一个低噪声 LDO,数字负载走 5V。另外,软件侧可以对连续采样做滑动平均,取 5 到 10 次采样的均值,呼吸灯一样平滑。

4.4 CM4 载板上的设备树覆盖问题

在 CM4 上跑 Pi-Face 遇到最多的是设备树问题。第三方载板的出厂固件往往只启用了和载板自身硬件相关的 overlay,SPI 接口默认关闭。解决办法是在config.txt里手动追加:

[all] dtparam=spi=on

然后重启,再检查/dev/spidev0.0。如果载板把 SPI0 的引脚复用成了其他功能,比如 MIPI 摄像头或者 PCIe,那 Pi-Face 就只能放弃 SPI0,改用软件模拟 SPI,或者把 ADC 换成 I2C 接口的型号。这里没有万能答案,必须回到载板的原理图确认引脚最终接到了哪里,这是 CM4 项目开发躲不掉的一步。

经验小结:凡是遇到“外设时好时坏”“读数偶尔全 0”“按键偶尔失灵”,先按这个顺序排查——电源稳定性、地线完整性、SPI 速率、软件去抖。八成问题都在这几项里。

在我实际用下来的感受里,Pi-Face 最打动人的不是性能参数,而是它把 GPIO 编程的门槛压到了一个“插上就能跑”的程度。对于新手,它是理解数字输入输出和 ADC 采样最好的入门教具;对于老手,它又是快速搭控制原型的可靠底板。如果你手头正好有闲置的树莓派或 CM4 开发板,不妨找一块 Pi-Face 接个电位器、两个按钮,跑一遍上面的代码,你会对 GPIO 的电气特性有非常直观的理解。

最后再分享一个小技巧:无论你用的是 Pi-Face 还是其他扩展板,接线前打印一张当前型号的 GPIO 引脚图放在工作台旁边,然后在代码里用 BCM 编号写死引脚,所有硬件接线表单独用 Markdown 维护一份。磨刀不误砍柴工,这套习惯能让你在项目切换时节省大量翻资料的时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询