ARTICLE DETAIL

资讯详情

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

矩阵键盘防鬼键:二极管隔离方案原理、接法与STM32实现

矩阵键盘防鬼键:二极管隔离方案原理、接法与STM32实现 1. 为什么你的4x4矩阵键盘一按两个键就乱码很久之前画过一块4x4矩阵按键的板子一共8个IO口原理图照着教科书画没串任何东西。单键扫描一切正常直到有一天我同时按住S1和S4想做个组合键出来的东西完全不对。用示波器抓行线电平才发现矩阵扫描电路存在一个电气旁路路径多个按键按下时电流不按我预期方向走。这个问题在STM32开发里太常见了尤其是做键盘、遥控器、工控面板这类需要监测大量按键的项目。矩阵按键本身是为了省IO口才出现的方案4x4只需要8个IO8x8只需要16个IO如果每个按键独立占一个引脚16键就要16个IOMCU引脚根本不够用。但省IO的代价就是按键信号在电路板上互相串扰而这种串扰不像软件抖动那样消抖就能解决它是硬件层面的电流通路问题。二极管隔离方案就是专门解决这个问题的在每个按键的通路上串一颗二极管利用单向导电性切断非法电流路径。听起来简单但等你实际布板、选型、调试的时候会发现里面有很多细节值得展开说。这篇文章我把整个方案的原理、接法、选型、软件配合、实测现象从头到尾理一遍做键盘类项目直接照抄就行。2. 矩阵扫描出错的根源电流会自己找绕路要理解二极管隔离为什么有效先得把矩阵扫描的等效电路看清楚。很多人设计时只画了行列交叉点放按键的示意图根本没考虑过键被按下时电流实际怎么流这才是问题的关键。2.1 单键按下时扫描逻辑本身没有问题矩阵按键的标准接法是这样的行线接MCU的输入引脚配置为内部上拉输入列线接MCU的输出引脚配置为推挽输出。扫描时逐列输出低电平其余列输出高电平然后读取所有行线的状态哪一行变成低电平就说明按下的键在本行。拿一个简单的例子说明列C2输出低电平其他列输出高电平此时读取行R3为低电平就能唯一确定按键位于第三行第二列。这个逻辑对单键按下完全成立而且很可靠。注意一个前提条件行线是高电平列线是低电平按键被按下时电流方向是从行线流向列线也就是从MCU的某个IO流入另一个IO。这个方向关系在后面的二极管接法里特别重要。2.2 多键按下时多了一条跨行列的电流路径假设用户同时按下了两个键一个在R1行C1列另一个在R2行C2列。扫描程序先输出C1为低、C2为高正常来说只有R1会被拉低。但实际上电流有可能走这样一条路径R1上的高电平经过按下的键1到达C1但因为C1已经是低电平电流没法到C2所以问题还没暴露。真正的问题出在另一种排列情况同时按下的两个键位于不同行不同列比如键A在R1行C1列键B在R2行C2列这时还有第三个键位于R1行C2列没被按下。扫描C1为低、C2为高时R1通过键A被拉低这是正常的。但同时R2的电平会被一条路径影响R2 → 键BR2C2按下 → C2 → 悬空点不对C2输出高电平这条路径没有低电平来源。关键场景有三个按键同时按下键1R1C1、键2R2C2、键3R1C2。扫描C1为低时R1被键1拉低正常但R2这条线呢R2 → 键2R2C2按下 → C2此时C2输出高电平没作用但C2还在某个时刻被扫描为低到时候R1又是高电平就会出现混乱。更经典的鬼键phantom key路径是这样产生的扫描时只有一列输出低其他列输出高但物理上所有列线都通过MCU内部输出端连接在一起推挽输出的P-MOS和N-MOS都不是理想的开关但主要路径不是这个。实际产生误判的路径是当两个或更多按键按下后存在某个组合使得本来没有按下的第三行或第三列被通过按键网络间接拉到低电平。举个例子按下键A行R1、列C1和键B行R2、列C2再按下键C行R1、列C2。当扫描到列C1为低、列C2为高时本应只有R1为低。但因为键C的存在R1的电流经过键C到达C2又从C2经过键B到达R2R2被抬高注意方向不对这时候C2输出高走不通。真正产生路径的是这样四个按键围成矩形其中三个按下第四个没按下扫描的某一时刻第四个键的位置会被误判为按下。电流具体路径是输出低电平的那一列 → 按下的某个键 → 某一行为低另一行通过另一个按下的键→输出高电平的那一列→再通过按下的键→返回到原本不应为低的那一行。因为行线是弱上拉输入外部强拉低的能力远大于内部上拉所以就能把行线拉低导致误读。这就是为什么不加二极管的矩阵键盘按下三个键时可能显示四个键甚至更多键都被按下。2.3 二极管在这里的真正作用让电流只能朝一个方向走如果每个按键都串联一只二极管那上面的旁路路径就断了。因为任何误判路径必然包含一段从列线流向行线的电流而二极管只允许从行线流向列线这个单一方向。从列往行方向二极管反向截止这条路就断了。虽然听起来只是加了一颗便宜的二极管但它从根源上改变了矩阵网络的导通行径把无向的电阻开关网络变成有向的二极管开关网络。这一颗二极管的价值是用最低成本把硬件层面的歧义性消除掉了。3. 二极管接法、选型和PCB布板时的实操细节方向不对、选型不当、丝印标记不清这三个坑我在不同项目里都踩过。下面把接法和选型一次说清楚。3.1 标准接法每颗按键串一只方向必须统一先画一个4x4矩阵的原理图结构示意C1 C2 C3 C4 R1 [BUTTONDIODE] [BUTTONDIODE] [BUTTONDIODE] [BUTTONDIODE] R2 [BUTTONDIODE] [BUTTONDIODE] [BUTTONDIODE] [BUTTONDIODE] R3 [BUTTONDIODE] [BUTTONDIODE] [BUTTONDIODE] [BUTTONDIODE] R4 [BUTTONDIODE] [BUTTONDIODE] [BUTTONDIODE] [BUTTONDIODE]具体连接时按键一端接行线另一端接二极管的阳极二极管的阴极接列线。也就是行线 → 按键 → 二极管阳极 → 二极管阴极 → 列线。扫描时列线输出低电平行线为高电平按下去之后电流方向是行线经过按键再经过二极管流向列线二极管正向导通压降约0.6V1N4148。反向路径也就是从列线流向行线二极管截止非法通路被切断。有个容易忽略的点反向接法二极管阳极接列线、阴极接按键再接行线在电路功能上也能工作原理是同样的——限制电流必须沿单一方向流过按键。但如果你有的键按正向接有的键按反向接那就等于没加隔离某些组合键照样串。所以规则只有一条所有按键的二极管方向保持一致设计时定好阳极朝行线、阴极朝列线后全板统一按这个方向画。3.2 二极管选型别拿1N4007,也别拿SS14乱顶这部分是很多人问得最多的地方直接给结论首选1N4148小信号开关二极管没有第二选择。矩阵键盘的电流非常小一般工作在微安到毫安级1N4148的100V耐压、450mA最大正向电流、4ns反向恢复时间在这个场景下绰绰有余。关键是它极便宜、封装多DO-35直插、SOD-323/0805贴片都有、货源充足。为什么不用1N40071N4007是整流二极管耐压1000V、电流1A它的反向恢复时间非常慢约30微秒量级。虽然键盘扫描速很慢看不出问题但它体积大且开关特性差用在信号通道完全不合适。有些工程师手边只有1N4007就拿去顶能用但没必要。为什么不用SS14或1N5819肖特基肖特基二极管正向压降确实低0.2-0.3V但它反向漏电流比硅管大得多在高温下漏电流会明显上升。矩阵键盘的行线是MCU内部上拉通常30-50kΩ一点点漏电流就可能在行线上产生不可忽略的电压偏移。而且肖特基反向耐压通常不高1N5819是40V对低压电路不是问题但漏电流这个属性让人不够放心。真正需要低压降的场景只有一种就是MCU供电电压非常低比如1.8V系统内部上拉再加上0.7V压降后行线高电平可能低于VIH阈值这时才考虑用低正向压降的肖特基管。还有人问能不能用BAT54也可以BAT54是双二极管共阳/共阴封装压降约0.3VSOT-23封装适合贴片量产漏电流比SS14又小一些是1N4148之外一个不错的替代价格稍贵。3.3 PCB布板丝印方向标记比想象中更关键二极管方向在原理图上一个斜杠就能标清楚到了PCB阶段如果不重视丝印方向标记贴片回来就是一片报废。我见过一次批量焊接把二极管贴反的情况16颗键只有4颗能正常用因为每行每列都有反向二极管串在通路里相当于开路。从那以后我在布板上强制定几个规矩丝印上画清楚二极管的阴极标记线通常是一条粗竖线和封装库里的阴极标记严格对应不要随手翻转封装。按键串联二极管后建议二极管尽量靠近按键摆放减少绕线造成的寄生电容和走线交叉。行线和列线的走线宽度按普通信号线处理即可不需要加粗但行列线之间最好拉开间距避免长距离平行走线防止信号耦合。如果板子面积允许加入测试焊盘TP点在每一根行线和列线上都留出测试点方便焊接后万用表测量。实际上二极管不是必须靠近按键你也可以把所有二极管集中在排线一端统一排列这样焊接也方便板上也更整齐。只要电气上保证行线→按键→二极管阳极→二极管阴极→列线这个顺序就没问题。4. 软件配合完整扫描函数、组合键判断与防抖有人以为加了二极管以后软件要专门写二极管模式的逻辑其实不用。扫描逻辑和普通矩阵键盘完全一样唯一区别是你现在可以放心地检测多键组合了。4.1 STM32 HAL库下的4x4矩阵扫描代码上面的代码我拆开说。R0-R3配置为上拉输入C0-C3配置为推挽输出。扫描时先让所有列输出高电平然后逐列将当前列置低读取整组行数据。HAL_GPIO_ReadPin需要逐个读所以写个循环。/* 硬件接线 * PA0-PA3 Row0-Row3配置为GPIO_MODE_INPUT GPIO_PULLUP * PB0-PB3 Col0-Col3配置为GPIO_MODE_OUTPUT_PP */ #define ROW_PORT GPIOA #define COL_PORT GPIOB uint16_t row_pins[4] {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3}; uint16_t col_pins[4] {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3}; // 返回一个16位矩阵状态bit [row*4col] 对应(row, col)是否按下 uint16_t matrix_scan_keyboard(void) { uint16_t state 0; // 全部列先置高避免上电瞬间乱拉 for (int col 0; col 4; col) { HAL_GPIO_WritePin(COL_PORT, col_pins[col], GPIO_PIN_SET); } for (int col 0; col 4; col) { // 当前列拉低其余列保持高 HAL_GPIO_WritePin(COL_PORT, col_pins[col], GPIO_PIN_RESET); // 稍等电压稳定几百纳秒足够这里给几个空指令或短延时 for (volatile int i 0; i 10; i); for (int row 0; row 4; row) { if (HAL_GPIO_ReadPin(ROW_PORT, row_pins[row]) GPIO_PIN_RESET) { state | (1U (row * 4 col)); } } // 恢复本列为高再处理下一列 HAL_GPIO_WritePin(COL_PORT, col_pins[col], GPIO_PIN_SET); } return state; }本质上就是逐列扫描加逐行读取。加了二极管之后这个函数逻辑一行都不用改。实际使用时扫描不能只读一次就判断键值机械按键有抖动需要做软件消抖。最简单可靠的方式是每5ms扫描一次连续两次读取的状态完全一致才认为键值有效。10ms或20ms级别的周期对按键来说已经很灵敏了人按一个键最少几十毫秒用5ms采样完全不会丢键。4.2 组合键检测的关键不要读到第一个键就返回不加二极管的扫描程序很多人的写法是扫到某个键就立刻返回键值因为反正常态就是单键操作。你要做组合键识别必须改为完整扫描整个矩阵把16位键值状态全部读出来。// 在while循环里做周期扫描 uint16_t now_state matrix_scan_keyboard(); uint16_t stable_state 0; // 10ms调用一次消抖使用 if (now_state last_state) { stable_state now_state; } else { last_state now_state; }拿到stable_state之后判断某个键是否按下用位测试#define KEY_S1_POS (0) // row0, col0 #define KEY_S4_POS (3) // row0, col3 if ((stable_state (1U KEY_S1_POS)) (stable_state (1U KEY_S4_POS))) { // S1S4同时按住触发组合快捷键 }同时要注意行和列的排列顺序不要搞混。如果PCB布局是竖排的扫描的row会在视觉上和物理位置有偏移这部分在调试时最容易出问题列一个映射表解决。4.3 关于行列反转法在二极管方案下的表现网上有很多矩阵键盘程序用的是行列反转法全部列输出高行输入读取然后全部列输入行输出分别反转扫描。这种方法在纯电阻矩阵下可用但在串了二极管的电路上由于二极管方向固定行列反转就不是简简单单把IO角色互换就能完成的了。如果你用的二极管是阳极接行线、阴极接列线的方向那行列反转法会失效因为反向扫描时电流方向改变二极管直接截止。所以二极管隔离方案的电路行列扫描法是最适合的方式。有些开源项目里写了一句支持行列反转结果把二极管方向换来换去最后还是绕回行列扫描。这里提醒大家别走弯路。5. 实测验证与踩坑记录从万用表到示波器焊好板子、写完代码接下来就是验证。如果只是单键扫描很难发现二极管方向有没有完全统一因为单键按下的正向导通路径才是工作路径反向路径根本没有机会暴露。所以验证方案必须包含多键组合测试。5.1 上电前的万用表检查先把MCU的供电断开用万用表二极管档蜂鸣档测每个按键两端的压降方向。正表笔接按键靠行线的一端负表笔接靠列线的一端如果二极管和接线都正常表上会显示0.6V左右的正向压降并伴有蜂鸣。反过来正表笔接列线端、负表笔接行线端表上应该显示OL或者反向截止状态不响。这一步就能筛掉绝大部分虚焊、反向、漏贴问题。全部16个按键逐个测一遍记录在表上再上电写程序。5.2 示波器观察行线波形区分正常与异常上电之后用示波器钩一个行引脚的波形单键扫描正常时应该能看出一帧帧的扫描方波。用两个探头同时钩住两个行引脚或者钩住一个行和一个列就可以看到处于不同扫描相位时的电平变化。多键同时按下时的正常波形特征是被按下的行引脚在对应列扫描窗口内出现低电平窗口之外始终为高。如果你看到某个没有按键的行也出现了低电平毛刺说明还有其他路径在拉低它这时可以检查二极管方向是否完全一致、是否有焊桥短路。5.3 常见硬件故障排查表直接列一张排查表按现象对号入座现象最可能原因排查方法所有按键全部无响应行线上拉未配置列线始终输出高检查GPIO配置测量列输出电平某一行4个键全部不识别该行对应二极管全部焊反/行线虚焊万用表二极管档逐键测压降某一列4个键全部不识别该列线短路或未连接断电后用蜂鸣档测列线到MCU引脚通断某几个键按下时串键/误触发其他键二极管方向不统一或存在漏贴全矩阵逐个二极管方向核对按键有时灵有时不灵该按键接触不良上拉电阻不够强检查按键封装用示波器抓毛刺多键组合时偶发多识别二极管压降过大弱化信号接线过长导致干扰降低扫描速度检查压降增加RC滤波多键组合偶发多识别这条我再补充一个场景有些板子为了省成本二极管型号混用部分1N4148、部分SS14不同压降导致阈值判断出现不确定性。这种混搭问题用万用表很难看出来但实际工作就会偶发乱码。所以BOM表里二极管一定要指定唯一料号贴片时注意来料批次的一致性。5.4 实际项目中的一个教训长线缆场景下加了二极管还是串键有次做一个遥控器面板按键板通过15cm的排线连接到主控板矩阵里加了二极管实验室测试一切正常装进壳子以后发现偶尔有按键乱跳。排查了很久才发现问题不在二极管而在排线的信号串扰扫描瞬间列线电平突变通过排线之间的互感耦合到了相邻的行线上在MCU输入引脚上产生了亚微秒级的负向毛刺碰巧被采样到。这个问题在缩短扫描周期或者增加扫描电流后会更严重真正的解决办法是降低GPIO翻转速度和增加列线驱动延时。具体做法有两种一是把列驱动到低之后插入一段几十微秒的延时等线上电压稳定了再读行值二是在行线的MCU端并联一个10nF的电容做低通滤波把高频毛刺滤掉。这也是我想强调的一个经验二极管隔离解决的是矩阵网络内部的逻辑串扰但PCB走线、排线串扰等物理层问题二极管根本管不到。做产品级键盘时千万不能以为加上二极管就万事大吉信号完整性的基本功还是得过关。6. 什么时候可以不加二极管以及替代方案对比看到这里可能有人会问我做了好几个矩阵键盘都没加二极管不也好好的吗确实很多场景下不加二极管也能工作因为误触发条件需要多个按键同时按下而你的产品操作逻辑里根本不允许这种情况。那到底怎么取舍我给出一个实用判断标准。6.1 不用加二极管的场景矩阵规模不超过3x3且产品逻辑明确只处理单键操作操作面板上按键间距很大用户没有同时按多个键的操作习惯软件上有能力通过连续扫描过滤非法组合比如只允许两个键同时按下时其中一个必然在同列或同行的产品软件可以排除行列重叠的组合剩下的边界情况就交给扫描顺序去兜底。这种情况下省去16颗二极管成本能省几毛钱焊接也省一步工序对大批量低成本产品是有意义的。6.2 必须加二极管的场景需要支持组合键、快捷键比如CtrlC类的双键组合矩阵规模达到5x5以上扫描到的非法组合几率成倍增加产品有按住某个键不放再操作其他键的交互逻辑按键面板可能被用户同时触碰多个按键例如电容触摸面板手势操作但又用机械按键做的。一句话总结只要你的软件有多键同开的需求或者产品交互存在误触后用户会试图同时按多个键的场景二极管就是必须的。6.3 替代方案与对比方案成本原理优点缺点每按键串联1N4148约1-2分钱/颗二极管单向导通切断旁路简单可靠完全消除鬼键增加焊接工序软件过滤非法组合0扫描后做数学判断行列重叠排除零成本只能处理部分场景不能完全防串独立按键逐引脚扫描0IO足够时每键占用一个IO无串键问题引脚占用多不适合大矩阵专用键盘编码芯片如TCA8418中等芯片内部完成去抖和键值编码节省MCU资源支持多键增加硬件成本和学习成本普通STM32项目8x8以下矩阵按键用二极管隔离方案是最均衡的选择成本可以忽略电路简单软件逻辑兼容常规扫描排查也容易。6.4 更极端场景的应对有些8x8甚至更大矩阵的项目比如某些多功能键盘会直接上键盘编码芯片让芯片把完整矩阵扫描、防抖、N键无冲都包了MCU通过I2C读取键值。这个方案确实省心但在多数嵌入式控制面板上没必要。STM32的IO资源通常足够覆盖10-20颗按键的矩阵IO口紧张的情况反而少见重点是不要让串键问题毁了整个项目的稳定性。7. 个人经验补充关于扫描周期、功耗和上拉电阻的几个细节最后补充几个我在多轮改板之后才悟出来的细节不算正题但影响实际体验。第一扫描周期不是越快越好。有人为了让按键秒响应把扫描周期压到1ms以下结果功耗上去了MCU频繁唤醒反而增加电源纹波有时候还会和PWM、ADC采样互相干扰。按键的人手响应时间在50ms以上量级扫描周期在10ms左右完全够用功耗也低。如果是电池供电的低功耗设备建议用定时器中断5-10ms扫描一次无按键时进入休眠按键触发外部中断唤醒再开始周期扫描这样可以兼顾响应速度和功耗。第二MCU内部上拉电阻的阻值范围很关键。STM32的内部上拉电阻典型值在30-50kΩ不同批次间有波动。如果按键板走线特别长、线缆电容大内部弱上拉可能无法在短时间内充到高电平阈值导致行线读取错误。遇到这种情况建议在每根行线上外部加一个4.7kΩ到10kΩ的上拉电阻让高电平恢复更快。加了二极管之后行线还有0.6V的压降损失外部上拉电阻也能提高噪声裕量这块儿不要省。第三GPIO配置不要随手照抄。列线配置为推挽输出没错但行线要配置为输入上拉这是常规操作。问题在于有些代码库在初始化时把行线也配置成了推挽输出高电平这在没有二极管时可能碰巧能用内部钳位保护但加了二极管之后两个推挽输出直接对战会产生大电流极端情况下甚至损坏IO。行线引脚定义好后用万用表量一下对地电压确认是高电平不是浮空再往下写代码。第四批量生产时建议加一道全矩阵自动测试程序。让每块板子出厂前自动执行一次全键按压测试16个键依次按下并反馈键值测试治具用亚克力板加压块一次性压住所有键电脑端直接比对测试结果。二极管方案的故障点主要集中在贴片和焊接环节这道测试能拦截绝大多数不良品比人工一个个按键去试效率高得多。矩阵键盘的二极管隔离方案说到底是个用了都说好的设计但真正做到位从原理理解、原理图绘制、PCB布板、软件适配到测试验证每一环都有自己的讲究。希望这篇文章能帮你把这条链路整个捋顺下次画键盘板直接一次搞定不用再回来返工。
返回列表