恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
基于STM32的全键可编程键盘DIY:从原理图到固件实战
首页
资讯中心
/
基于STM32的全键可编程键盘DIY:从原理图到固件实战
基于STM32的全键可编程键盘DIY:从原理图到固件实战
发布时间:2026/9/9 15:39:08
简介面向客制化键盘开发者与嵌入式爱好者这套全键可编程USB键盘工程方案基于CH559单片机实现。键盘共102键除Fn切换键外101个按键均可在不断电状态下通过配套软件即时修改键值即改即用适应各类重映射需求。资源包共40个文件大小16.72MB。工程代码以12个.h头文件和9个.c源文件为主包含CH559初始化与HID键盘报告逻辑另附8份PDF文档涵盖USB2.0中文规范、HID协议英文原版、HID Usage Tables及CH559数据手册方便对照协议阅读源码。同时提供原理图PDF、Keil工程文件、Hex固件及清理脚本可直接编译烧录。目前已有2246人学习下载。这套资料从原理图、固件到协议文档一应俱全既能帮助入门者理解USB HID枚举与报告描述符实现也可作为客制化键盘改键方案的参考模板配合B站视频学习效果更佳。 先说结论这个项目做完你会发现键盘这事儿一旦入了门就回不去了。“全键可编程键盘”说白了就是让每一颗按键都能脱离固定功能由你自己定义它按下后该输出什么。功能层面它不止是“改键”还包含层切换、宏录制、组合键、启动指定程序甚至模拟鼠标动作。这项能力在办公场景能显著提速在游戏场景能大幅压缩操作路径在程序员手里还能变成一把输入效率极高的“生产工具”。这个项目的标题里写了“原理图程序”两层说明它不是拿现成套件拼个壳子而是从硬件电路到固件逻辑全链路自己做属于典型的嵌入式DIY项目也适合电子工程相关专业的学生作为毕业设计或课程项目的实战课题。我会按从硬件到软件的顺序拆解全程结合我自己实测的经验把原理图设计、PCB布局、固件框架、问题排查这几块讲透。1. 项目整体设计与思路拆解1.1 为什么选STM32F103C8T6做主控做可编程键盘市面上常见有三种路子用Arduino Pro Micro这类现成开发板、用树莓派Pico/RP2040或者像这个项目一样直接用STM32F103C8T6画最小系统板。我推荐STM32F103C8T6的原因很直接。第一成本低单颗芯片零售几块钱到十几块钱不等批量更便宜比买开发板划算得多。第二外设够用内置USB 2.0全速设备控制器可以直接跟电脑通信不需要外挂USB转串口芯片HID键盘的本质就是USB设备通信。第三性能余量大72MHz主频、64KB Flash、20KB RAM刷个键盘固件只占不到一半资源剩下空间足够塞自定义键值表、宏脚本甚至灯光控制逻辑。第四资料多芯片发布十几年了STM32标准外设库、HAL库、各路开源键盘项目遇到问题基本搜得到答案。如果用Arduino Pro Micro固件开发快但缺点是板载ATmega32U4只有2.5KB RAM复杂宏脚本和层逻辑跑起来捉襟见肘而且芯片本身不带大容量Flash可扩展性差。用RP2040开发体验好Type-C USB原生支持好但要自己买芯片的可能性和供货稳定性反而不如STM32。综合下来STM32F103C8T6属于“一个能打还便宜”的均衡选择。1.2 全键可编程键盘要解决的核心问题这个项目说的“全键可编程”本质上要解决三件事一按键扫描。键盘是矩阵结构几十颗按键不可能每颗独占一个IO口必须用行列交叉扫描的方式用少量IO读出所有按键状态。核心难点在于“消抖”和“防鬼影”。二USB HID协议。键盘和电脑之间不是随便发一串字节就能被识别必须遵循USB HID规范关键是正确配置描述符包括设备描述符、配置描述符、接口描述符和端点描述符尤其是“键盘报表描述符”定义了电脑如何解析你发过来的按键数据。三键值映射与持久化。全键可编程意味着按键布局不是写死在代码里的而是用户可以自己改。这就要求固件具备一套映射表机制同时把用户修改后的配置存到芯片Flash里断电不丢。这三块没有哪一块是“抄个代码就行”的尤其PCB和固件联调阶段每个模块都可能出幺蛾子。1.3 方案对比为什么不用QMK之类的现成固件做全键可编程键盘的过程中你大概率会搜到QMK、TMK、VIA这些开源固件项目它们功能强大且代码成熟为什么还要自己从原理图到程序重写一套这里有一个很容易被忽视的角度学习价值。QMK在标准化硬件上固然好用但如果你直接烧录QMK那么芯片底层的USB枚举、矩阵扫描、HID报告逻辑都被框架封装好了硬件原理图也必须贴合它的引脚分配原项目成果中“原理图程序”这两部分就都被替换了项目的完整性和原创性会明显削弱。而自己写一套固件虽然功能上初期不如QMK丰富但你能真正做到“电路自己画的、代码自己写的”这是完全不同的体验。而且自己写固件还有一个实际好处硬件自由度更高。QMK对矩阵引脚有固定要求自研固件则完全由你决定用哪几个引脚做行、哪几个做列后续要加背光、加旋转编码器硬件怎么改、程序怎么调心里全有数。所以我个人的建议是自研固件作为学习和项目底座等跑通了以后再去看QMK源码能学到更多设计思路。2. 硬件设计原理图怎么画才能少踩坑2.1 主控最小系统与USB接口电路细节STM32F103C8T6的最小系统由几部分组成3.3V供电、8MHz主晶振HSE、复位电路、BOOT配置、去耦电容、SWD调试接口。原理图里每一部分都有讲究不能只为了“能跑”而画。供电这块USB接入是5V而STM32工作电压是3.3V所以必须加LDO稳压。我常用AMS1117-3.3价格便宜、输出3.3V稳定但要注意它的压差比较大5V输入没问题输入侧和输出侧各放一颗10μF钽电容和一颗100nF陶瓷电容。如果追求低功耗或高转换效率也可以换成RT9013或XC6206系列。这里有个细节USB的5V和3.3V电源地要铺完整的地平面数字地和模拟地如果电路不复杂就统一不要分割否则反而容易引入噪声。晶振电路不能用芯片内部RC振荡器凑合USB通信对时钟精度有明确要求全速USB要求时钟误差在±0.25%以内内部RC振荡器偏差较大必须外接8MHz晶振。晶振两端各接一颗20pF左右的负载电容容值根据晶振手册选择PCB布局时晶振要贴近MCU的OSC_IN和OSC_OUT引脚走线尽量短不要绕圈。USB接口电路有几个容易被忽略的点。第一D和D-走线要等长且尽量短最好做成差分对。第二F103的USB_DP引脚内置了1.5kΩ上拉电阻用于告诉主机这是一个全速设备但这一路需要用软件控制使能所以PA12要配置为开漏输出程序里先拉高主机才能识别到设备插入。第三USB的D和D-信号线上各串联一颗22Ω电阻起到阻抗匹配的作用减少信号反射这个设计在USB高速布线里是标准做法全速下也建议保留。原理图设计时我建议用嘉立创EDA来画因为它的封装库全画完直接可以转PCB下单省去自己画封装的麻烦。还有一个经验电源和地网络一定要用网络标签而不是乱七八糟的连接线方便后期DRC检查。画完每部分后养成跑一遍原理图电气规则检查的习惯很多低级错误比如引脚悬空、引脚短接都能提前发现。2.2 按键矩阵设计二极管不是可选项机械键盘的按键排列本质是矩阵行线和列线交叉每个按键接在交叉点上。比如61键的60%布局用8行×8列就够覆盖了行列共16根线接MCU的16个GPIO远比61个独立GPIO省IO。但矩阵扫描有一个经典问题鬼影Ghosting。想象一下你同时按下A、S、D、X四个键因为矩阵的行列是导电连通的主控扫描时会误以为额外的按键也被按下输出错误。解决办法是在每个按键的开关支路上串联一颗二极管方向统一为从行线到列线或者反过来只要全板一致利用二极管的单向导通特性切断电流反窜路径从而彻底杜绝鬼影。二极管型号用1N4148就可以开关速度快、压降低、便宜适合按键这种低速数字信号。封装选SOD-123或0805贴片注意焊接时引脚方向不要装反方向错了这个按键就永远处于断路状态按下没反应。如果你做的是热插拔键盘二极管是焊在PCB上的换轴不影响但如果做的是焊接轴体焊接前一定要用万用表二极管档验证每一路的导通方向。实际排布时有一个经验尽量把同行的二极管阴极连到同一根行线同列的按键串联在列线上保持整个矩阵网络有规律不仅画原理图省心固件里扫描代码读到的数据也清晰。2.3 复位、BOOT跳线与PCB布局细节复位电路用一个10kΩ上拉电阻加一颗100nF电容接地接到NRST引脚即可正常工作时NRST保持高电平按下按键接地产生低电平复位。如果觉得没必要按键复位只保留上拉电阻和电容也行。BOOT0和BOOT1引脚建议通过10kΩ电阻下拉到地同时预留焊盘或者加一个小拨码开关。正常运行时从主Flash启动出问题时把BOOT0拉高再上电芯片就会进入系统存储器启动模式可以通过串口方式重新烧录固件相当于一个“救砖开关”。这在后面调试时会救你很多次。PCB布局的经验按照优先级来排STM32芯片居中晶振紧靠OSC引脚复位电路靠近NRST引脚去耦电容每对电源引脚放一颗100nF陶瓷电容并尽量靠近引脚。USB接口放在板边方便开口给外壳D/D-从USB座引出后平行走线到MCU。按键矩阵按标准键盘排列布阵行线和列线走线与按键位置自然重合能少打很多过孔。SWD调试接口SWDIO、SWCLK、GND、3V3引出到板边一排焊盘或排针调试完在量产版上可以去掉但开发阶段一定要留。我自己画到第二版的时候才意识到按键矩阵区域的地平面要尽量完整因为矩阵信号速度慢对地平面要求不高但USB和晶振附近的地一定要铺好否则枚举不稳定时非常难排查。3. 固件开发从矩阵扫描到USB HID3.1 矩阵扫描与按键消抖的两种常用方式固件端的核心任务之一是周期性地扫描矩阵获取所有按键的按下/释放状态。假设你的行线接MCU的PB0~PB7列线接PA0~PA7扫描逻辑是先把所有行线设为推挽输出并全部输出高电平然后逐行拉低同时读取所有列线的电平如果某列读到低电平说明该行该列交叉的按键被按下。这里要处理两个问题列线读输入时要配上拉输入模式或外部上拉没有按键按下时列线被上拉到高电平行列定义必须和原理图网络标号一一对应否则扫出来的键值全错。消抖是另一个关键点。机械按键按下时簧片接触会产生持续时间大约5~20ms的机械抖动如果直接把读到的高低电平当作最终状态一次按键会被误判为多次按下。消抖常见两种方案延时消抖检测到电平变化后延时10~20ms再读一次如果状态一致就确认有效。实现简单但轮询期间CPU干等浪费资源。定时扫描消抖用一个1ms或者5ms的定时器中断每次中断读取完整矩阵状态然后在状态机里做“连续N次读到相同状态才判定有效”的逻辑。资源利用率高也更专业。我实际测试下来定时扫描方案更稳。具体做法是在定时器中断里维护一个“当前扫描状态”和一个“上次稳定状态”当两者连续一致超过3次也就是15ms左右才更新稳定状态并触发按键事件。这套逻辑还能顺便区分“短按”和“长按”为后面的层切换和宏录制提供事件基础。3.2 USB HID键盘设备是如何上报按键的STM32固件里启用USB设备功能推荐直接用STM32CubeMX生成USB HID工程的骨架里面已经包含了完整的USB设备库和描述符但有两个地方需要手动改。第一个是设备描述符里的PID和VID。VID可以随意填一个厂家自己的编号但要注意避免与已有的冲突PID自定义即可。iManufacturer和iProduct字符串可以改成你自己的名字Windows设备管理器里会显示出来。第二个是键盘报表描述符这个决定了键盘发给电脑的数据格式。标准键盘HID报表描述符定义了一个8字节的输入报告格式是Byte 0: 修饰键Ctrl、Shift、Alt、GUI Byte 1: 保留字节 Byte 2~7: 最多同时按下的6个普通按键键值也就是说普通USB键盘同时按键的上限是6键加上修饰键最多可组合8个键。如果你希望实现全键无冲需要修改报表描述符把报告长度加大或者用多个报表交替上报但这超出标准HID Boot协议范围Windows下需要安装特殊驱动首次做键盘不建议搞单键识别和6键无冲足够用。程序里需要实现的底层逻辑是键盘扫描模块把“当前按下的键”转换成HID键值列表填充到8字节报告里通过USB中断端点上报给主机。主机检测到报告变化后就会通知当前前台程序按键按下或释放。注意键值和键盘上的字母不是直接对应的每个按键有自己的HID Usage ID比如字母A对应0x04B对应0x05数字1对应0x1E这些都要查HID键盘键值映射表。3.3 键值映射、层切换与配置存储的工程实现在代码层面全键可编程体现在一张映射表。最简单的做法是定义一个二维数组keymap[层索引][矩阵索引] HID键值矩阵扫描得到某个按键的物理位置后根据当前层号查表获得要上报的键值。比如默认层填的是标准QWERTY布局键值FN层填的是媒体按键键值按住FN键时当前层切换到FN层松手再切回。宏录制是进阶功能说白了就是把一次按键序列存到内存然后绑定到某个键上按下时按顺序回放。我的实现思路是在Flash里划分一块区域专门存宏数据宏内容按“键值延时时间”的格式连续存放回放时按时间顺序逐个上报按键并释放这样录制的动态操作每一帧都能还原。配置存储是全键可编程的另一个核心需求。STM32F103C8T6的Flash有64KB固件程序约占20KB上下剩余空间完全可以用来存配置。但要注意Flash写入的几个特性写入前必须按页擦除而且写操作不可在Flash里执行代码否则会卡死所以需要把Flash写入函数放到RAM中执行Flash擦写寿命约1万次正常用户改配置的频率不会磨损到但调试阶段频繁写入要留意。实际上我在程序里预留了一个“用户配置区”的起始地址比如0x08006000用户在PC端配置好键位后通过串口或HID自定义命令下发给固件固件先把整页数据缓存到RAM然后统一擦除再写入断电重启后从Flash加载配置。这套机制跑起来后才真正算得上“全键可编程”而不是“写死键位的单片机键盘”。4. 常见问题与排查技巧实录做这个项目踩坑是必然的我把最常遇到的问题连同排查思路整理出来希望能帮你少走弯路。4.1 USB无法枚举或电脑无任何反应这是最容易让新手崩溃的问题。插上USB后电脑完全没有“叮咚”的设备连接声设备管理器里也看不到未知设备。优先级排查步骤如下检查3.3V电压是否正常AMS1117输出端是否稳定在3.3V左右纹波是不是过大。没有电压一切免谈。示波器或逻辑分析仪看8MHz晶振是否正常起振如果波形是正弦波且频率接近8MHz晶振基本没问题。检查PA12USB_DP是否在程序初始化时被拉高F103的USB上拉由软件控制这一环节漏掉主机永远不会认识这个设备。如果以上都正常用万用表量一下USB座的D和D-对地阻抗确认是不是焊接连锡或者虚焊了。还有一个容易忽略的点USB线材。有些劣质USB线只有电源线没有数据线或者数据线质量差导致信号衰减插上去自然没反应。换一根短的高质量数据线再试。4.2 部分按键无响应或按键错乱按键按下无响应首先用万用表二极管档测按键两端确认轴体或键帽底座本身是好的再检查原理图矩阵网络标号是不是和实际PCB走线一致。我最常犯的错误是原理图里行线标了A0但PCB里走线走了A1结果代码扫描的A0引脚连的物理位置对不上错位一片。按键错乱还有一个经典原因二极管方向接反。如果某颗按键的二极管焊反了这个按键按下时会导致整行或整列状态异常甚至出现“按下A结果输出B”的鬼影现象。排查时用万用表蜂鸣档依次量每个二极管的正向导通方向确保行线到列线方向统一。4.3 偶尔出现双击或丢键按键偶尔触发两次多半是消抖参数太短。我实测机械轴的抖动时间集中在5~15ms把消抖窗口设到20ms左右比较稳妥。如果你用了薄膜键盘或者轴体老化抖动时间更长消抖窗口可以相应加大。丢键则和扫描速度有关。如果定时器中断扫描周期超过10ms快速打字时可能漏检短促按键。建议矩阵扫描周期设置在2~5ms之间配合消抖状态机既不丢键又不会抖动误触发。4.4 固件烧录失败与如何救砖开发阶段最棘手的问题不是软件逻辑而是固件烧不进去。解决办法是把BOOT0引脚跳线拉高用USB转串口模块连接PA9TX和PA10RX用FlyMcu或STM32CubeProgrammer通过串口方式擦除并烧录固件到系统存储器。等固件烧录成功后再把BOOT0拉回低电平正常从Flash启动即可。另一个经验是开发时不要关掉SWD调试接口功能。一旦固件里的GPIO初始化把SWD引脚复用成了普通IO下次就无法用调试器连上芯片了只能走串口烧录恢复。所以我的所有工程里SWD引脚在初始化阶段都保持默认调试功能测试阶段结束后再考虑复用。写在最后全键可编程键盘这个项目做下来最深的感触是“一体两面”四个字原理图和程序之间没有任何一处是可以脱离另一侧独立成立的硬件少画一条线软件就要多写很多补丁代码软件少写一层映射硬件再精巧它也就是一把普通键盘。个人建议开发顺序是先画好最小系统板并点亮LED验证基础电路再逐步接入矩阵扫描、USB HID最后再扩展层切换、宏录制这类高级功能。每完成一个阶段就立刻验证不要一口气全做完再调试否则出问题时排查范围会变得非常大。按这个节奏推进一个周末把核心功能跑通是完全有希望的。本文还有配套的精品资源点击获取