1. 项目缘起:为什么要在树莓派上做机械按键扩展板?
玩树莓派的朋友,尤其是那些喜欢捣鼓复古游戏机、自制街机、智能家居中控或者各种DIY控制台的朋友,肯定都遇到过一个问题:树莓派自带的GPIO引脚虽然强大,但直接用来接按钮、开关,总觉得有点“大材小用”且不方便。特别是当你需要连接十几个甚至几十个机械按键时,杜邦线会乱成一团,可靠性也堪忧。这时候,一块专门为树莓派3B(当然,也兼容其他40针GPIO的型号)设计的机械按键扩展板(Shield)就显得非常有必要了。
我最初想做这个项目,是因为在搭建一个复古风格的桌面控制台。我需要一排手感清脆、反馈明确的机械按键来控制音乐播放、灯光开关,甚至是一些自定义的宏命令。用薄膜按键或者触摸屏,总感觉少了点“仪式感”和“操控感”。市面上的成品扩展板要么功能太单一(比如只有几个按钮),要么集成度太高(带了屏幕、摇杆,价格不菲),要么就是针对特定游戏手柄布局的,灵活性不够。所以,我决定自己设计一块专注于“机械按键阵列”的扩展板,核心目标就三个:连接稳定、布局灵活、手感出色。
这块“Raspberry Pi 3B Mechanic Switches Shield”本质上就是一个GPIO接口的转接和扩展板。它直接插在树莓派的40针GPIO排针上,将树莓派的GPIO信号引出来,并通过板载的电路,将多个机械按键的开关信号,规整地映射到特定的GPIO引脚上。这样一来,你就不再需要为每一个按键单独焊接杜邦线,也避免了接线错误和接触不良的问题。板子设计好了,你只需要把喜欢的机械轴(比如青轴、红轴、茶轴)焊上去,再盖上键帽,一个专属于你的、手感一流的物理控制面板就诞生了。
2. 核心设计思路与方案选型
设计这样一块扩展板,听起来简单,但里面有几个关键点需要仔细考量。不同的选择,直接决定了最终板子的成本、复杂度和用户体验。
2.1 按键扫描方案:矩阵扫描 vs. 直接GPIO
这是最核心的电路设计决策。每个按键本质上是一个开关,我们需要检测它是否被按下。
直接GPIO连接:每个按键独占一个GPIO引脚,另一端统一接地或接VCC。树莓派程序直接读取每个引脚的电平。这种方法最简单,编程直观,没有任何延迟和“鬼键”(多个按键同时按下导致的误识别)问题。但是,树莓派3B的GPIO引脚虽然多,但实际可用的、适合做输入的也就20个左右。如果你需要32个甚至64个按键,这种方法就完全不够用了。
矩阵扫描:将按键排列成行和列的矩阵。例如,8行 x 8列可以管理64个按键,但只需要8+8=16个GPIO引脚。工作原理是,程序循环将每一行设置为低电平(输出),然后读取所有列(输入)的状态。如果某个按键被按下,对应的行列就会导通,从而在读取的列数据中检测到低电平。通过行列坐标就能唯一确定是哪个按键。
注意:矩阵扫描是键盘领域的标准做法,可以极大节省GPIO资源。但它引入了两个问题:1.扫描延迟:因为要逐行扫描,按键检测有微小延迟,但对于人手操作(毫秒级)来说完全无感。2.鬼键:在特定情况下(如同时按下三个特定位置的键),可能会产生一个虚拟的按键信号。这需要通过硬件(二极管)或软件算法来避免。
我的选择:为了支持更多按键(我计划至少做4x4=16个起步,并可扩展),矩阵扫描是唯一可行的方案。为了追求最好的兼容性和避免鬼键,我决定为每个按键都串联一个二极管,这是最稳妥的硬件防鬼键方案。虽然增加了焊接工作量和小部分成本,但换来的是100%可靠的按键识别,对于追求稳定的控制台项目来说是值得的。
2.2 机械轴选型与键座设计
既然是“机械”开关,轴体的选择就是灵魂。市面上主流的是MX轴和类MX轴。
Cherry MX原厂轴:手感标杆,质量稳定,但价格较高。常见的颜色有:
- 青轴:段落感最强,click声最清脆,适合打字和需要确认感的操作。
- 茶轴:有轻微的段落感,声音比青轴小,兼顾打字和游戏。
- 红轴:线性轴,直上直下,无段落感,触发压力小,适合游戏和快速连击。
- 黑轴:线性轴,但触发压力比红轴大,手感更重。
国产轴(如佳达隆Gateron、凯华Kailh Box):近年来品质提升巨大,手感多样,价格实惠,有很多创新轴体(如提前段落轴、静音轴)。对于DIY项目来说,性价比极高。
我的选择:考虑到项目的成本控制和手感尝鲜,我推荐使用国产热插拔轴座配合国产轴。热插拔轴座的好处是,你不需要焊接轴体!轴可以直接插在轴座上,后期想换手感(比如把青轴全换成静音红轴)只需要拔下来再插上去,无比方便。这大大提升了项目的可玩性和维护性。PCB设计时,就要选用兼容热插拔轴座的封装(通常是两个金属弹片孔)。
键帽方面,可以选择便宜的OEM高度ABS键帽,或者更有质感的PBT键帽。对于功能控制台,甚至可以选择无刻键帽或者自己定制印字的键帽,颜值更高。
2.3 与树莓派的接口与供电
树莓派3B的GPIO提供3.3V和5V电源。机械键盘轴体和LED(如果你要加背光)的工作电压通常是5V。但需要注意的是,树莓派的GPIO引脚输入电平绝对不能超过3.3V,否则可能损坏CPU。
这就引出了一个关键设计:电平转换。我们的矩阵扫描电路,如果直接用5V供电,那么按键按下时,传递给树莓派GPIO的将是5V高电平,这是危险的。因此,必须在扩展板上设计电平转换电路。
- 简单电阻分压:对于每个输入到树莓派的信号线,可以用两个电阻组成分压电路,将5V降到3.3V左右。这种方法成本低,但会增加元件数量和布局复杂度,且对电阻精度有一定要求。
- 专用电平转换芯片:如TXS0108E等双向电平转换芯片。方案更专业、稳定,但成本高,且需要更多的PCB空间。
- 利用二极管和上拉电阻:这是更巧妙也是更常见的做法。将矩阵的“行”设置为输出,“列”设置为输入。在树莓派端,将作为输入的“列”引脚通过一个上拉电阻连接到3.3V。在扩展板上,当某一行输出低电平时,如果该行某列的按键被按下,电流会从树莓派的3.3V上拉电阻,经过按键、二极管,流向行的低电平。此时,树莓派读取到的该列引脚电平会被拉低(接近0V)。由于电流方向是从树莓派流向扩展板,且回路中串联了二极管防止倒灌,因此扩展板一侧的电压不会影响到树莓派的3.3V引脚。这种方法安全地实现了电平隔离,无需额外转换芯片,是我最终采用的方案。
供电则直接从树莓派的5V引脚(Pin 2或4)和GND引脚取电,为整个扩展板的扫描电路和可能的LED供电。
3. 电路设计与PCB绘制实战
确定了矩阵扫描(带二极管)、热插拔轴座、3.3V上拉电平隔离的方案后,就可以开始具体设计了。
3.1 原理图设计
我使用KiCad这款免费开源软件进行设计。首先创建原理图。
- 创建元件库:需要自制两个元件符号。一个是“机械轴开关”,它有两个引脚(常开触点)。另一个是“热插拔轴座”,它对应PCB上的两个焊盘。
- 绘制矩阵:假设设计一个4行4列的矩阵。放置16个开关符号。每个开关串联一个二极管的符号(注意二极管方向,阴极朝向“行”线方向)。
- 连接网络:将第1行的所有开关的一端(通过二极管后)连接在一起,命名为“Row1”。同样处理Row2, Row3, Row4。将第1列的所有开关的另一端连接在一起,命名为“Col1”。同样处理Col2, Col3, Col4。
- 添加上拉电阻和接口:放置4个电阻(阻值通常在1kΩ到10kΩ之间,我选用4.7kΩ),一端分别连接到Col1-Col4网络,另一端连接到名为“3V3”的网络。这个“3V3”网络最终会连接到树莓派的3.3V引脚。
- 设计连接器:放置一个2x20排针的符号,代表树莓派的40针GPIO接口。将Row1-Row4网络连接到排针上你计划用作输出的GPIO引脚(例如GPIO17, GPIO27, GPIO22, GPIO23)。将Col1-Col4网络连接到排针上你计划用作输入并启用内部上拉(或通过我们板载电阻上拉)的GPIO引脚(例如GPIO5, GPIO6, GPIO13, GPIO19)。最后,将“5V”和“GND”网络连接到排针对应的电源引脚。
3.2 PCB布局与布线
原理图检查无误后,导入PCB编辑器。
- 板框与定位:首先定义PCB的尺寸和形状。树莓派的GPIO排针位置是固定的,所以扩展板的金手指接口位置必须精确对应。测量树莓派3B上排针的间距(标准的2.54mm)和整体位置,在PCB上放置一个对应的2x20的排母(注意是母座,用来插在树莓派的公针上)。这是最关键的一步,位置错了整个板子就废了。我通常会在KiCad中导入一张树莓派的实物图作为背景来辅助定位。
- 元件布局:
- 将排母固定在板子底部。
- 在板子正面(Top Layer)规划按键的位置。16个键可以排成4x4的网格,间距遵循标准键帽的19.05mm(0.75英寸)间距。
- 在每个按键位置放置热插拔轴座的封装(两个独立的焊盘)。
- 将16个二极管放置在对应按键的附近,最好在背面(Bottom Layer)以节省空间。
- 将4个上拉电阻也放置在背面空闲区域。
- 电源滤波电容:在5V和GND入口附近,放置一个100nF的陶瓷电容,用于滤除高频噪声。
- 布线:
- 电源先行:先布5V和GND线。GND最好铺铜(铺地),可以增加稳定性和抗干扰能力。在KiCad中,我通常会在背面进行大面积GND铺铜。
- 信号线:然后连接行线(Row)和列线(Col)。由于矩阵线路简单,可以在背面用细线(如0.3mm宽度)连接。确保每条行线连接到该行所有按键的二极管阳极,每条列线连接到该列所有按键的另一端。
- 连接到排母:最后将Row1-4, Col1-4以及电源线引到排母对应的焊盘。
- 设计规则检查:布线完成后,一定要运行DRC,检查线宽、间距、有无未连接网络等错误。
3.3 打样与焊接
将设计好的PCB文件(Gerber格式)发给PCB打样厂商。为了美观和耐用,我选择了黑色阻焊油+沉金工艺。沉金能让金手指更耐磨,接触更好。
收到PCB空板后,开始焊接:
- 先焊小元件:按照“先低后高”的原则,先焊接电阻、二极管这些贴片元件。使用烙铁和焊锡丝,或者用锡膏进行热风枪回流焊(如果有条件)。
- 焊接轴座和排母:然后焊接热插拔轴座和2x20的排母。排母焊接要确保所有引脚都吃锡饱满,且没有连锡。
- 最后安装轴体和键帽:焊接完成后,用万用表通断档检查关键线路,确保没有短路或断路。确认无误后,就可以将机械轴体按压进热插拔轴座,然后盖上键帽。
4. 软件驱动与按键功能编程
硬件准备就绪后,剩下的就是让树莓派识别按键了。这里提供两种主流思路。
4.1 方案一:使用Python + GPIO库直接扫描
这是最直接、最可控的方法。我们以4x4矩阵为例,假设引脚连接如下:
- 行(输出): Row1 -> GPIO17, Row2 -> GPIO27, Row3 -> GPIO22, Row4 -> GPIO23
- 列(输入): Col1 -> GPIO5, Col2 -> GPIO6, Col3 -> GPIO13, Col4 -> GPIO19
import RPi.GPIO as GPIO import time # 定义行列引脚 rows = [17, 27, 22, 23] cols = [5, 6, 13, 19] # 按键映射表,4x4矩阵 keys = [ ['1', '2', '3', 'A'], ['4', '5', '6', 'B'], ['7', '8', '9', 'C'], ['*', '0', '#', 'D'] ] def setup(): GPIO.setmode(GPIO.BCM) # 设置行引脚为输出,并初始化为高电平(因为我们板子上拉是3.3V,行拉低才有效) for pin in rows: GPIO.setup(pin, GPIO.OUT, initial=GPIO.HIGH) # 设置列引脚为输入,并启用树莓派内部上拉(作为双重保险,即使板载电阻失效) for pin in cols: GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) def scan_keys(): pressed_key = None for i, row_pin in enumerate(rows): # 将当前行拉低 GPIO.output(row_pin, GPIO.LOW) # 稍微延迟,等待信号稳定 time.sleep(0.001) for j, col_pin in enumerate(cols): # 读取列引脚状态,内部已上拉,按下时为低电平 if GPIO.input(col_pin) == GPIO.LOW: pressed_key = keys[i][j] # 等待按键释放(防抖) while GPIO.input(col_pin) == GPIO.LOW: time.sleep(0.01) # 扫描完一行后,将该行恢复高电平 GPIO.output(row_pin, GPIO.HIGH) # 如果检测到按键,跳出循环(简单处理,一次只处理一个键) if pressed_key is not None: break return pressed_key def main(): setup() try: print("按键扫描开始,按下Ctrl+C退出") while True: key = scan_keys() if key: print(f"按键 [{key}] 被按下") # 这里可以添加按键处理逻辑,如触发命令、播放声音等 time.sleep(0.05) # 主循环延迟,降低CPU占用 except KeyboardInterrupt: pass finally: GPIO.cleanup() if __name__ == '__main__': main()这个程序会不断扫描矩阵,当检测到按键时打印键值。你可以根据key的值来执行不同的功能,比如调用os.system执行命令,或者控制其他GPIO设备。
4.2 方案二:模拟为USB键盘(进阶)
如果你希望这块扩展板被系统识别为一个标准的USB键盘(这样你可以在任何界面下使用,甚至进入BIOS),就需要用到USB HID功能。树莓派本身可以作为USB设备(gadget模式)。
- 启用USB Gadget模式:需要修改树莓派的
/boot/config.txt和/boot/cmdline.txt,并加载相关内核模块。这个过程相对复杂,且不同系统版本(Bullseye, Bookworm)步骤不同。 - 使用hid-gadget工具:社区有
hid-gadget这样的工具,可以模拟键盘按键。你需要编写一个程序,将GPIO扫描到的按键,转换成对应的USB HID键值(如KEY_A, KEY_ENTER等),然后通过hid-gadget发送给主机(树莓派自身)。 - 使用专用MCU:更专业和稳定的做法是,在扩展板上集成一块额外的微控制器(如ATmega32U4、RP2040),由这块MCU负责扫描矩阵,并通过其USB接口直接将自己枚举为一个USB键盘设备。树莓派通过USB线连接到这个“键盘”上。这样完全独立,不占用树莓派GPIO,也更稳定。但这属于更复杂的“子项目”了。
对于大多数控制台应用,方案一(Python直接控制)更简单灵活,因为你可以直接让按键触发特定的Python脚本,完成任何你想做的事(打开应用、发送网络请求、控制GPIO等)。
5. 项目扩展与进阶玩法
基础功能实现后,这个项目还有很多可以打磨和扩展的地方。
- 增加背光:每个轴体上可以加装LED。如果是单色LED,可以串联电阻后,由树莓派的GPIO直接驱动(需注意电流),或者用晶体管扩流。如果是RGB LED,则需要使用WS2812B这类可寻址LED,并连接到树莓派的一个GPIO,用
neopixel库控制,实现炫彩灯光效果。 - 增加编码器旋钮:除了按键,旋钮(旋转编码器)在音量调节、菜单选择上非常方便。可以预留一两个编码器的焊盘,编码器同样可以通过GPIO读取。
- 多层PCB与结构设计:为了更稳固,可以设计两块PCB。顶层PCB安装轴体和键帽,底层PCB放置主控芯片、电阻电容等。中间用铜柱固定,形成“夹心”结构,更接近客制化键盘的工艺。
- 外壳设计:用亚克力板激光切割或者3D打印一个外壳,将树莓派和扩展板封装进去,形成一个完整的、美观的桌面设备。
- 软件优化:
- 消抖处理:上面的示例代码用了简单的延时消抖。更可靠的做法是使用状态机或中断+计时器的方式来消抖。
- 多键无冲:完善扫描算法,实现真正的N键无冲(NKRO),即使同时按下所有键也能正确识别。
- 配置化:将按键映射关系(哪个物理按键对应什么功能)写进配置文件,甚至做一个简单的Web配置界面,这样不用改代码就能重新定义按键功能。
这个“Raspberry Pi 3B Mechanic Switches Shield”项目,从电路设计到PCB制作,再到软件编程,完整地走了一遍硬件DIY的流程。它不仅仅是一块扩展板,更是一个载体,让你能够把对机械手感的喜爱和树莓派的无限可能结合起来,创造出独一无二的交互设备。无论是作为一个学习电子设计的练手项目,还是作为一个实用工具的核心部件,它都能带来满满的成就感。