矩阵键盘的状态机扫描:告别延时,拥抱高效
在嵌入式开发中,矩阵键盘的扫描一直是一个基础但重要的话题。传统的方法往往让我们陷入"等待"的思维模式------检测按键、延时消抖、再次确认。这种方式简单直接,但就像在繁忙的十字路口让所有车辆都停下来等待一辆自行车通过一样,效率低下。
今天,我想分享一种完全不同的思维方式:状态机+中断驱动的矩阵键盘扫描方案。
核心理念:化整为零,分而治之
传统方法的困境
想象一下,你正在阅读一本书,但每翻一页就要停下来等待10毫秒才能继续阅读。这就是传统delay()扫描的工作方式------它让整个系统停下来等待按键稳定。
新方法的思路
矩阵键盘采用行列式电路结构,通过8个GPIO引脚控制16个按键的检测。其中4个引脚配置为输出模式,用于行扫描;另外4个引脚配置为输入模式,用于列检测。通过循环扫描方式,依次将每一行设置为低电平,同时读取各列的电平状态,从而确定按键的物理位置。

1.如该图所示,上四个引脚设置为output模式。左四个引脚设置为input模式
2.我们定义一个定时器中断1~2ms执行一次,通过循环扫描方式
3.每次执行扫描函数前需将四个output设置为高电平(1),依次将一个output引脚设置为低电平(0),同时input依次读取引脚电平,若按键按下且该output设为低电平(0),从而确定按键的物理位置。变量i++
4.四个input都读取过电平后,此时i=4,随即将第二个output设置为低电平,同理确定按键的物理位置
5.循环一轮,则需要执行扫描函数16次,使用状态机来进行消抖,若中断为1~2ms即完整扫描一次为16~32ms,从而完成消抖

例如当9键按下时,我们可以扫描到第9键为低电平(0)可以判断出按键位置
我们采用"分时扫描"的策略:
-
每次只检查一个按键(1毫秒内完成)
-
16个按键轮流检查,16毫秒完成一轮
-
每个按键都有独立的状态机跟踪其状态
这就像高效的餐厅服务员,不是等一桌客人全部点完菜才去下一桌,而是在各桌之间轮流服务,确保每位客人都得到及时关注。
状态机:给每个按键一个"记忆"
-
待机状态:等待被按下
-
确认状态:检测到按下,但需要再次确认(消抖)
-
保持状态:确认按下,等待释放
这个简单的三状态机解决了机械按键的抖动问题,但不是通过"等待"来解决,而是通过"多次确认"来解决。
两种硬件布局,两种优雅解法
情况一:规整布局(连续引脚)
当引脚排列整齐时,我们可以用数学的优雅来解决问题:
行选 = 1 << (基础行号 + 当前行索引)
列读 = 读取(1 << (基础列号 + 当前列索引))
这就像图书馆里按编号排列的书架,你可以用简单的公式找到任何一本书。
情况二:灵活布局(不连续引脚)
现实往往不完美,引脚可能分散在不同位置。这时我们采用"分情况讨论"的策略:
如果 是第一列:读取引脚A
如果 是第二列:读取引脚B
...
这就像在图书馆里,有些书按编号排列,有些按主题排列,你需要不同的查找策略。
定时器中断:系统的心跳
整个方案建立在1-2毫秒的定时器中断上,这个中断就像系统的心跳:
-
每次"心跳"检查一个按键
-
不占用主循环的时间
-
确保响应时间的确定性
对比优势:不仅仅是技术,更是设计哲学
| 维度 | 传统延时法 | 状态机中断法 |
|---|---|---|
| 思维方式 | 批量处理,等待完成 | 分时处理,持续进行 |
| 系统响应 | 按键期间无响应 | 始终保持响应 |
| 资源使用 | 独占式使用CPU | 共享式使用CPU |
| 设计理念 | 简单但低效 | 复杂但高效 |
这种方案比较适合:
-
需要快速响应的实时系统
-
需要同时处理多个任务的复杂应用
-
电池供电的低功耗设备
-
对用户体验要求高的产品
希望这篇博客能给你带来新的设计灵感。在实际项目中,选择哪种方案不仅取决于技术需求,更取决于你对系统整体性能的追求。