十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于STM32的蓝牙机械键盘DIY:从裸机矩阵扫描到BLE HID

基于STM32的蓝牙机械键盘DIY:从裸机矩阵扫描到BLE HID 简介基于STM32的硬核DIY蓝牙机械键盘完整覆盖轴体选型、键盘阵列排版、PCB设计、程序开发与外壳CNC制作全流程面向嵌入式新手与进阶者适合毕设、课程设计或工程实训。资源包共289个文件、约29.69MB包含C源码、头文件、Keil工程配置、PCB原理图与封装库、DXF外壳加工图以及PDF说明文档还附带编译中间文件、调试配置等便于理解工程构建细节。已有354人学习下载。通过该项目可以系统掌握一套完整蓝牙键盘的软硬件协同开发方法从STM32外设驱动、矩阵键盘扫描算法、低功耗蓝牙通信到PCB布局布线、机械结构设计与CNC加工参数设定均有关键示例。对于希望亲手打造一把专属机械键盘、深入理解其工作原理或学习该领域完整产品链路的开发者来说是一份高质量、可跟做的参考资料。1. 基于STM32的硬核蓝牙键盘不借现成固件自己从裸机写起当你拆开一把量产机械键盘看到主控芯片上印着ARM标记时会不会想过一个问题这颗芯片到底是怎么把一次按键变成电脑能认出的“A”键的市面上有QMK、ZMK这些成熟的开源方案它们是生产力工具但真要把键盘的工作原理吃透还是得回到最硬核的路子选一颗STM32自己写矩阵扫描、消抖、键值映射再把数据通过蓝牙HID协议送出去。这篇文章就顺着这条路线走一遍目标是你也能做出一把能连手机、连PC的蓝牙机械键盘。适合已经会写C语言、玩过STM32寄存器和基本外设的工程师也适合想做量产前验证的嵌入式开发。2. 硬件选型与电路设计主控、蓝牙模块与按键矩阵2.1 主控选型为什么我最常用STM32F103C8T6把键盘当成一个低速人机交互设备主控的算力冗余可以很大但引脚数量、调试便利性和功耗控制更重要。STM32F103C8T6这颗256KB Flash的LQFP48芯片在DIY键盘圈里几乎成了默认起点。它的主频72MHz跑一个全键盘的扫描周期只需要几十微秒剩下的CPU时间可以全部SleepRAM和Flash也足够放下一套完整的HID报告描述符以及带层级的键位映射表。我一般把GPIO分配成三组矩阵行、矩阵列、蓝牙串口。以60%键盘常见的5x14矩阵为例引脚需求是18个IO口加上一个串口。STM32F103C8T6有37个可用GPIO完全够用。功能引脚说明矩阵行PA0-PA4开漏输出输入扫描矩阵列PB0-PB5下拉输入检测按键串口TXPA9接蓝牙模块RX串口RXPA10接蓝牙模块TX唤醒引脚PC13外部中断任意键唤醒这里有个容易踩的坑矩阵行引脚一定要选支持开漏输出的引脚而且需要在初始化时配置为输出模式不能省。另外如果你手头的芯片是APM32或者GD32可以直接用STM32的工程但在CubeMX里选型号时注意内核版本是否一致我遇到过有人把GD32F103当STM32F103烧录后时钟不对实际上是因为启动文件选择了错误的等待周期。2.2 蓝牙模块选型经典蓝牙SPP与BLE HID之间的选择很多人的第一反应是买HC-05因为网上资料最多。但HC-05蓝牙模块连接不上或者连上了却无法作为键盘输入原因不在接线而在协议HC-05是经典蓝牙SPP从机它只能做串口透传不携带HID服务。也就是说SPP链路两端都知道“我传的是字节”但主机侧操作系统不会感知到它是一个键盘。做蓝牙机械键盘必须让模块支持HID over GATT也就是BLE HID。所以我推荐用基于CC2541或者N51822的BLE模块例如JDY-08或者AT-09这些模块内部已经实现了蓝牙协议栈外部通过UART接收控制命令和HID报告。它们支持主机HID角色开机后广播为“Keyboard”电脑和手机可以直接识别。选择模块时注意三个参数供电电压是否兼容3.3V串口电平是否与STM32一致模块是否支持从AT指令模式下切换到透传模式。JDY-08用的是蓝牙4.0 BLE可以理解成“蓝牙LE”的代表它最大的价值是功耗低广播状态下电流不到1mA。接线很简单STM32 TX接模块RXSTM32 RX接模块TXVCC接3.3VGND共地。有一个细节模块的RX端口如果不做电平转换容易烧毁。我用的是双向逻辑电平转换芯片实际上STM32和CC2541都是3.3V供电不用转换但如果你的模块是宽压的要确认它的RX能不能容忍5V。2.3 按键矩阵与二极管防冲突电路设计机械键盘的按键不是每个脚接一个IO而是使用矩阵扫描。以5行14列为例每行接一个IO每列接另一个IO按键放在行和列交叉点上。扫描时依次把每一行拉低然后读取列电平如果列读到低说明该行某列有按键按下。这样18个IO可以支持70个按键。但矩阵有个经典问题多个按键同时按下时会产生电流倒灌比如按住“S”和“D”时再按“W”会导致“W”所在的行列都导通造成鬼键。解决方法是每个按键串联一个1N4148二极管方向为“行到列”导通阻断反向电流。原理是二极管单向导电性让扫描信号只能从行流向列无法从列流向另一个列。需要注意的是二极管负极一定要朝列引脚焊接如果方向反了扫描会乱。关于晶振STM32F103C8T6支持内部RC振荡器但蓝牙HID的波特率精度要求不高用内部8MHz也跑得动。如果追求稳定用8MHz外部晶振时负载电容一般取12pF到22pF计算方法为晶振手册给出的CL18pF匹配电容2*(CL-Cstray)杂散电容约3pF所以我一般用30pF并联。你有示波器时可以实测波形压摆率再微调电容大小。3. STM32固件开发从矩阵扫描到蓝牙HID上报3.1 开发环境用STM32CubeMX配置时钟、串口和GPIO先花十分钟在STM32CubeMX里生成初始化代码。选择芯片型号STM32F103C8Tx在RCC选项卡把HSE设为Crystal配成72MHz主频。串口选USART1波特率设为96008位数据、1位停止位、无校验。注意BLE模块出厂默认波特率不一定相同JDY-08默认是9600但AT-09可能是115200必须先用USB转TTL连接模块发送AT指令确认或修改。GPIO配置里把矩阵行设为推挽输出初始输出高矩阵列设为下拉输入使能内部下拉电阻PC13设为外部中断下降沿触发。配置完成后生成MDK-ARM工程用Keil5打开。这里有个坑如果你同时安装了C51和STM32的Keil一定要确认工具栏里的Device选择的是STM32F103C8而不是C51。我遇到过工程编译无误但烧录后无反应最后发现是Keil的Device选择错误导致。3.2 按键扫描与消抖基于状态机的实现按键扫描用定时器中断来做定时周期1ms每次中断执行一次扫描。需要注意的是不要在主循环里延时扫描因为蓝牙模块低功耗时串口发送需要处理器响应主循环一旦被阻塞按键响应就不实时了。定时器初始化用TIM2配置为1ms更新中断。扫描逻辑用传统的方法每1ms扫描一行把当前行置低其他行置高读取列值然后恢复所有行为高。这样70个按键全部扫完需要14ms对于人手的按压力度来说已经足够快。如果遇到无响应或粘滞可以缩短到0.5ms扫描一行或使用并行扫描。消抖可以采用状态机而非简单延时。定义一个结构体typedef struct { uint8_t state; // 0松开1按下2抖动 uint8_t cnter; uint8_t key_val; } Key_t;每次扫描后把当前列值写入输入缓存与上次状态对比。如果连续4次扫描到同一状态就认为状态稳定。手指抖动时间一般在5-10ms因此4ms的连续采样可以滤掉大部分抖动又不会觉得延迟太高。代码段示例// 每个按键的采样处理 for(uint8_t c0; cCOL_NUM; c){ uint8_t cur (col_input[c] (1row)); if(keys[i].state 0 cur 1) { keys[i].cnter; if(keys[i].cnter 4) { keys[i].state 1; key_event[i] 1; // 标记按下事件 keys[i].cnter 0; } } else if(keys[i].state 1 cur 0) { keys[i].cnter; if(keys[i].cnter 4) { keys[i].state 0; key_event[i] 2; // 标记释放事件 keys[i].cnter 0; } } else { keys[i].cnter 0; } }消抖参数cnter设置为4意思是连续4次扫描信号一致才认为状态变化。如果扫描周期是1ms那么按键延迟是4ms体感上可忽略。如果你的键盘偶尔出现连续输入就把cnter增大到6代价是延迟变成6ms。3.3 蓝牙HID键值映射与串口帧协议扫描到键值后需要把它映射成HID Usage ID。比如标准键盘字母A对应0x04B对应0x05这在USB HID Usage Table里是固定的。蓝牙HID复用同一套Usage ID所以我们可以直接建立一个数组把行号和列号映射到HID键值。例如对于空格键我设置了宏定义#define KEY_A 0x04 #define KEY_B 0x05然后在矩阵初始化时填入每个按键的HID键值。蓝牙模块需要的数据帧格式是模块厂商定义好的以JDY-08为例它的透传模式本身支持HID键值透传只需要通过AT指令进入HID模式。模块手册一般会说“模块在HID模式下串口发送的每个字节会被作为HID键值报告”。那我们就只需要把按键的状态以HID报告格式发出去。HID报告格式是8字节第一字节是修饰键Shift、Ctrl等第二字节固定0后面六个字节是当前同时按下的按键键值。当没有按键按下时六个字节全0。因为BLE HID规范要求按下的键值主动上报所以我们在每次按键状态变化时构造8字节报告通过串口发送给模块。发送部分使用DMA串口发送避免阻塞CPU。关键代码uint8_t report[8] {0,0,0,0,0,0,0,0}; // 按键扫描后发现按下事件 if(key_event PRESSED) { // 查找码值并加入report report[2] key_hid[code]; // 更新修饰键标志 if(code KEY_LEFT_SHIFT) report[0] | 0x02; } // 发送report到蓝牙模块 HAL_UART_Transmit_DMA(huart1, report, 8);参数说明第一个字节0x00表示组合键为空如果同时按下Shift和A则应为0x02和0x04。六个键值字节按顺序填入如果按键超过6个必须丢弃最旧的键。这个限制是BLE HID协议本身规定的日常输入足够用。4. 蓝牙BLE配对、休眠与功耗优化4.1 BLE模块的AT指令配置与主从机连接把JDY-08插上USB转TTL先接电脑调试。打开串口助手发送“AT”得到“OK”后再设置模块为HID模式。我习惯用的配置流程是指令含义预期返回AT测试通信OKATNAMEDIYKB修改蓝牙名称OKAOKATROLE1设置为从机键盘角色OKATHID1开启HID功能OKATRESET重启模块OK注意顺序不能错先设HID1再RESET否则模块可能依然处于SPP模式。有读者问为什么他的模块HC-05蓝牙模块连接不上就是因为HC-05根本没有HID模式。配置完成后手机上打开蓝牙搜索应该能找到“DIYKB”设备配对码通常是0000。连接后可以用串口助手往模块发0x04观察手机是否出现字母“A”。这一步非常重要它验证了模块本身工作正常。如果手机没有任何反应说明模块的HID服务没被正确初始化需要重新执行AT指令或换一个模块固件。4.2 低功耗策略STM32的停止模式与按键唤醒键盘平时没人按键蓝牙模块和STM32都处于空转状态。为了让电池能用得更久最直接的方式是让STM32进入停止模式并把还未使用的GPIO全部设为模拟输入。停止模式是Cortex-M3内核的Sleep-on-exit模式它会关闭HSI/HSE时钟但保留SRAM内容。唤醒源选择PC13外部中断任意按键按下都会产生下降沿触发EXTI15_10中断。在中断里清除标志后系统重启重新初始化外设并发送一个“唤醒”指令给蓝牙模块。由于双事件中断调用系统整体唤醒时间大约2us足够快。实现停止模式的代码必须在所有按键释放后进入。我一般设置一个无按键时间计数器超过500ms没有键事件就进入停止模式。代码如下HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);进入停止模式前要先把串口DMA和定时器关闭因为唤醒后这些外设状态会丢失。唤醒后需要对系统时钟重新初始化特别是RCC的HSE配置否则串口波特率会漂移。蓝牙模块本身的功耗也很关键。JDY-08广播状态下工作电流约2mA连接后约0.9mA。如果让它一直广播电池依然会耗尽。可以在没有连接时通过一个GPIO控制模块VCCSTM32进入停止模式前拉低模块电源唤醒后再上电。注意模块重新上电后需要2秒时间启动所以唤醒时不能立刻发送HID报告要等待模块的READY信号。4.3 实测功耗数据与参数调整实际测试时我用万用表串联电源分别测量三种状态下的电流状态电流说明全速扫描无休眠12mA所有外设开启串口监听停止模式无模块供电0.04mASTM32停止模式功耗停止模式蓝牙模块供电开启2.5mA模块在广播从表中可以看到最大的耗电项是蓝牙模块。因此让模块也跟随键盘休眠能省掉大部分电量。加上电池容量以3.7V 400mAh电池为例每天使用4小时每小时30分钟打字大约能坚持两周这对于DIY键盘已经算是很够用的续航。如果你的键盘希望支持一键唤醒后马上输入那建议把模块始终保持连接但代价是休眠时模块仍会维持连接功耗会高一些。这里需要自己平衡我倾向于用带自动息屏的模块固件比如JDY-08在无数据超过30秒后会自动进入睡眠再按任意键可以唤醒。5. 调试技巧与硬核验证5.1 用逻辑分析仪抓取串口数据定位扫描Bug当按键没反应或者乱字符串时先把蓝牙模块断开直接抓STM32的TX引脚。我手头有一个24MHz的8通道逻辑分析仪把它跨接在PA9和GND上在电脑上用PulseView软件设置波特率9600然后按一下按键看有没有一帧0x04字节发出。如果逻辑分析仪完全看不到波形问题在扫描或事件触发如果能看到字节但内容不对问题在HID映射如果波形正确但手机没反应问题在蓝牙模块。这一招能快速缩小排查范围比自己瞎猜效率高。尤其当键盘出现100ms以上的延迟时优先看扫描周期是否被扫描的时间拖慢。5.2 蓝牙连不上的排查清单从HC-05连接不上开始说起如果沿用HC-05的思路你可能会卡很久。我总结过的几个常见问题串口电平不对模块要求3.3V但STM32配置成了5V推挽。检查CubeMX中GPIO输出模式是否设成了开漏因为开漏输出需要外部上拉否则TX没有高电平。波特率不一致模块出厂可能是115200你却用9600发送AT指令这就是HC-05连接不上的第一个原因。解决方法是先恢复出厂设置按住模块上的按钮上电再发送ATORGL。模块不在HID模式如果模块是SPP模式手机根本不会把它识别成键盘。确认ATHID1执行成功。5.3 延迟与续航的实测方法想量化“用手感”可以做一个简易延迟测试用一个光电二极管贴在显示器上点击空格键的同一瞬间用示波器同时抓取按键信号和屏幕上升沿。我的键盘实测从按下到显示器响应大约18ms其中蓝牙模块内部延迟占8ms这个数值在可接受范围内。如果感觉延迟偏高可以看逻辑分析仪上的串口帧间隔如果发现同一键的重复上报间隔太长就把按键连续上报的周期从5ms调到2ms。续航验证方法是拿一个可编程电子负载设置恒定电流但更直观的办法是记录电池开路电压每八小时记录一次通过电压下降斜率估算剩余电量。把测量数据与前面4.3节中记录的电流相乘就能得到一个实际容量和预期续航的对比。这些数据对你将来调整休眠策略很有价值。本文还有配套的精品资源点击获取
返回列表