☰
STM32矩阵键盘扫描原理与实现:4×4键盘驱动代码详解
2026/9/27 1:27:20 网站建设 项目流程

1. 从按键需求到矩阵方案:为什么偏偏是4×4

做嵌入式开发的朋友,尤其是玩STM32的,迟早会遇到按键设计的问题。项目简单点的,一个GPIO接一个轻触开关,读电平就能判断。但一旦按键数量超过四个、八个,比如密码锁、菜单选择、计算器界面,你总觉得引脚不够用。实际上STM32的GPIO口数量并不少,但真到了产品设计阶段,每个引脚都要精打细算——要留足串口、I2C、SPI、ADC、PWM这些外设通道,再富余的引脚也架不住一颗按键占一个IO。

这时候就应该用矩阵扫描结构。4×4矩阵键盘,核心思路就是用8个GPIO口驱动16个独立按键,通过对行列的组合检测来识别哪个键被按下。相比独立按键方案,省了一半以上的引脚资源,而且扩展性极强——3×3是9键、4×4是16键、5×5是25键,只要行线和列线够用,按键数量随便涨。

不少初学者第一次接触矩阵键盘是在开发板上,板载一个4×4的薄膜键盘,拿到手就开始写扫描代码,结果发现要么按键没反应,要么按一个键同时触发好几个键。这个问题几乎每个人都会遇到,原因不复杂,要么是引脚配置不对,要么是扫描逻辑里漏掉了消抖和防串键处理。我从第一次写完矩阵键盘驱动到稳定运行,前前后后踩了不少坑,这篇就把整个设计过程完整拆开讲,从硬件原理到代码实现都给你理一遍。

这个方案适合谁?正在做STM32入门项目的学生、准备毕业设计的本科生、做小型工控或智能家居项目需要多按键输入的工程师,都能从这套思路里直接拿代码改改就用。我不讲那些花哨的技巧,就讲最实在的扫描原理、代码架构和调试方法。

2. 矩阵键盘的核心原理:行列交叉与扫描时序

2.1 4×4矩阵的物理结构

矩阵键盘外观上是一个4行×4列的按键阵列,但电路板上的走线方式完全不同。你需要先明白它的物理本质:16个按键被排列在8条导线的交叉点上,每条行线连接4个按键的一端,每条列线连接4个按键的另一端。

实际接线一般是这样的:行线Row1~Row4接到单片机的GPIO并配置为输入模式带上拉,列线Col1~Col4接到另外4个GPIO并配置为推挽输出。扫描时让列线逐列输出低电平,同时读行线电平,如果哪一行变成了低电平,就说明这个交叉点上的按键被按下了。比如第2列输出低、第3行读到低,那就是第3行第2列的按键被触发。

这个交叉点的概念是关键。很多人画原理图的时候习惯把16个按键分别编号,但在矩阵扫描程序里,按键的位置是“行号+列号”的组合坐标,而不是一个绝对编号。所以做硬件设计的时候,按键布局最好画成规规矩矩的4×4网格,软件里用坐标映射表转换成实际键值,不然写代码的时候算坐标能算到怀疑人生。

2.2 逐列扫描到底在扫什么

逐列扫描法是矩阵键盘最常用的读取方式,你每次只驱动一列为低电平,其余列保持高电平,然后读取全部行线的状态。4列就需要4次这样的操作,每次操作能确认4个按键的状态,组合起来就是16个按键的完整状态。

我画个简单的逻辑说明:

  • 第1次:Col1=0,Col2/3/4=1,读Row1~Row4
  • 第2次:Col2=0,Col1/3/4=1,读Row1~Row4
  • 第3次:Col3=0,Col1/2/4=1,读Row1~Row4
  • 第4次:Col4=0,Col1/2/3=1,读Row1~Row4

4轮扫描完成后,你手上就有4组4位的数据,每组数据对应一列下四个行的状态。把这16个bit凑齐,就是整个键盘的瞬时状态快照。

实际代码里很少真的做4次完整的“输出低电平→读输入→改输出”循环,那样太慢。更常见的做法是循环遍历列索引,每次循环设置对应的列引脚输出低、其余输出高,然后立即读GPIO端口的输入寄存器,把行值提取出来。对STM32来说,一次完整的16键扫描只需要几十微秒,主频72MHz下基本可以忽略不计。

2.3 为什么行列必须加电阻

这里必须强调下拉/上拉电阻的配置。如果用STM32的标准库或HAL库,行引脚可以配置为输入上拉模式,内部就有上拉电阻可用,省掉外接电阻。但如果你用的是老式51单片机或者裸芯片方案,外部必须加上拉电阻,否则按键悬空时行引脚电平不确定,读回来的数据会乱七八糟。

另外一个容易忽略的点:列引脚作为输出,理论上不需要接限流电阻,但当按键按下时列引脚和行引脚之间是直接导通的。如果行引脚配置成带上拉的输入,内部上拉电阻通常30~50kΩ,电流非常小,不会出问题。但如果行引脚配置成了推挽输出,麻烦就大了——两个推挽输出短接,一高一低就是短路风险,轻则读数错误,重则烧引脚。所以引脚方向配置一定要谨慎,行就是输入、列就是输出,别搞混。

3. 硬件连接与引脚规划:建工程前先想清楚的事

3.1 引脚分配策略

STM32的GPIO资源丰富,但并不代表你可以随意挑8个引脚出来接键盘。连接位置会影响后续的软件复杂度和代码可读性,我建议按以下规则规划:

  • 4条行线尽量接在同一个GPIO端口,比如PA0~PA3,这样读取的时候可以直接操作IDR寄存器,一次读出一个端口的所有引脚状态,不用挨个调用GPIO_ReadPin。
  • 4条列线也尽量接在同一个端口,比如PB0~PB3,设置输出时可以用BSRR寄存器一次搞定多个引脚的电平切换。
  • 行列引脚不要和调试接口、下载接口共用。STM32的PA13/PA14是SWD调试引脚,PA9/PA10是USART1,如果你把键盘接在这些引脚上,每次下载程序或者串口调试都要拔线,非常折腾。
  • 如果项目里同时用到ADC、PWM等模拟功能,行列引脚要避开这些有特殊功能的引脚,优先选普通的GPIO。

按这套规划接完线,你会发现代码写起来非常顺——行值读取可以用一个常量掩码从IDR寄存器里直接提出来,列扫描只需要循环切换一个变量然后写BSRR,整个驱动函数不超过50行就能搞定。

3.2 硬件实物连接示例

假设我们使用STM32F103C8T6最小系统板,典型的引脚分配如下:

  • 行0:PA0
  • 行1:PA1
  • 行2:PA2
  • 行3:PA3
  • 列0:PB0
  • 列1:PB1
  • 列2:PB2
  • 列3:PB3

矩阵键盘模块的排针通常是按行和列分别引出两组4Pin接口,你用杜邦线一一对应接好就行。如果是自制PCB,就在原理图里把16个按键排成4×4阵列,行线连到一起、列线连到一起,然后接到MCU引脚。

提示:很多现成的4×4矩阵键盘模块的引脚排列顺序并不是标准的先4行后4列,买回来之后务必先看模块背面的丝印标注,或者用万用表蜂鸣档量一下哪个引脚对应哪一行哪一列。我见过好几个人因为引脚顺序搞反,代码里行列坐标彻底错乱,查了半天才发现是接线问题。

3.3 用CubeMX配置GPIO的要点

如果你用STM32CubeMX生成初始化代码,配置其实很快。把PA0~PA3配置为GPIO_Input,Pull-up上拉;PB0~PB3配置为GPIO_Output,初始电平设为High。生成代码时注意看一下GPIO初始化函数里的模式参数:行引脚一定是GPIO_MODE_INPUT,带上拉;列引脚是GPIO_MODE_OUTPUT_PP,也就是推挽输出。

CubeMX生成的初始化代码默认把所有GPIO的速度都设置成Low,对按键扫描来说完全够用,不需要改成High。有些人在这一步纠结要不要开启时钟之类的,其实你只要确保GPIOA和GPIOB的时钟都使能了就行,初始化代码里CubeMX会自动处理。

硬件这块看似简单,但至少占掉整个项目三分之一的工作量。连接质量直接决定后续调试的效率,插排线的时候多花一分钟确认,后面能省一小时查bug的时间。

4. 核心代码实现:三种扫描策略的对比与取舍

4.1 基础轮询扫描:最直观的写法

轮询扫描就是在主循环里不停地执行扫描函数,每次获取完整键盘状态并处理。这是最基础的写法,也最适合理解矩阵键盘工作原理。

#define ROW_PORT GPIOA #define ROW_PINS (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3) #define COL_PORT GPIOB #define COL_PIN_0 GPIO_PIN_0 #define COL_PIN_1 GPIO_PIN_1 #define COL_PIN_2 GPIO_PIN_2 #define COL_PIN_3 GPIO_PIN_3 uint8_t key_state[4][4]; void MatrixKey_Scan(void) { uint8_t col_pins[4] = {COL_PIN_0, COL_PIN_1, COL_PIN_2, COL_PIN_3}; uint8_t row_values; for (uint8_t col = 0; col < 4; col++) { // 先将所有列置高 HAL_GPIO_WritePin(COL_PORT, COL_PIN_0 | COL_PIN_1 | COL_PIN_2 | COL_PIN_3, GPIO_PIN_SET); // 当前列拉低 HAL_GPIO_WritePin(COL_PORT, col_pins[col], GPIO_PIN_RESET); // 稍微延时等待电平稳定 for (volatile uint8_t i = 0; i < 50; i++); // 读取行值,4个行引脚在同一端口 row_values = HAL_GPIO_ReadPin(ROW_PORT, ROW_PINS); // row_values里只保留了行引脚对应的位,逐一判断 for (uint8_t row = 0; row < 4; row++) { if ((row_values & (GPIO_PIN_0 << row)) == 0) { key_state[row][col] = 1; // 按键按下 } else { key_state[row][col] = 0; // 按键释放 } } } }

这段代码的逻辑很直白,每次扫描把4列轮一遍,和2.2节描述的过程完全一致。代码里的延时是为了等待GPIO输出稳定和信号传输延迟,这个延时大小和主频有关,一般几微秒到几十微秒就够。你写完后可以在主循环里连续调用这个扫描函数,在串口里打印按键状态来验证。

不过轮询扫描有个天生的弱点:如果主循环里还有其他任务,比如屏幕刷新、传感器读取,按键扫描的频率就会不稳定。极端情况下主循环被阻塞几十毫秒,按键响应就会出现明显延迟。解决这个问题的方法就是下面要讲的定时器扫描。

4.2 定时器中断扫描:稳定且不占用主循环

这个方案是我在实际项目里用得最多的。思路是用一个定时器产生固定频率的中断,比如1ms一次,在中断服务函数里执行矩阵键盘扫描逻辑。按键检测的实时性和主循环的负载完全解耦,无论主循环忙成什么样,按键都能按固定节奏被扫描。

// 定时器中断服务函数,1ms进入一次 void TIM2_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET) { __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); MatrixKey_Scan(); MatrixKey_DebounceHandle(); } }

MatrixKey_DebounceHandle是消抖处理函数,后面会详细讲。定时器扫描方案的好处是扫描周期固定,消抖、长按、连击这些时间参数都变得好算了——你知道每次进入中断的间隔,用简单的计数器就能实现精准的时间控制。

唯一要注意的是中断服务函数里不要写太复杂的逻辑。扫描本身很轻量,但如果你在中断里做按键事件的业务处理,比如触发某个协议发送、更新显示,就可能影响主程序的实时性。正确的做法是中断里只负责更新按键状态和事件标志,主循环检测到标志后再处理业务。

4.3 外部中断扫描:低功耗场景的选择

如果项目要求低功耗,系统大部分时间处于睡眠状态,轮询和定时器方案都不合适——轮询要CPU持续跑,定时器要周期性唤醒CPU。这时候可以考虑用外部中断:把4条列线接到EXTI引脚,按键按下时列线电平变化触发中断,在中断里唤醒CPU再执行完整扫描。

STM32的EXTI支持16条外部中断线,你可以选4条列线对应的引脚做EXTI输入。但要注意,外部中断触发的可靠性依赖按键按下的电平跳变。如果按键按下时列线没有产生足够明显的边沿,中断可能漏触发。所以这个方案通常配合一个额外的“按键按下检测”引脚,或者利用行线的电平变化来做唤醒源。

外部中断方案的代码复杂度比前两种都高,而且调试也更麻烦。低功耗之外的需求不建议一上来就选它,先用轮询或定时器跑通功能,最后再根据功耗需求切换方案。

4.4 三种方案怎么选

三种方案各有适用场景,我做了个对比:

方案代码复杂度实时性CPU占用适用场景
轮询扫描低受主循环影响低~中学习验证、简单项目
定时器扫描中高,固定周期极低绝大多数实际项目
外部中断扫描高高,按需唤醒低低功耗设备

个人建议:如果你在做正式项目,直接上定时器扫描方案。轮询扫描适合快速验证硬件连接,写个demo看看每个按键能不能正常响应,但产品代码用轮询的话,后面主循环越来越复杂的时候你就等着头疼吧。

5. 消抖与防串键:矩阵键盘最容易翻车的两个细节

5.1 机械按键的抖动问题

机械按键按下和释放的瞬间,触点会因为机械弹跳产生一系列短暂的不稳定电平,持续时间通常5~20ms。如果不做消抖处理,一次按下可能会被识别成多次触发,轻则按键多跳一下,重则干扰整个系统的状态机。

消抖的传统方案是延时消抖,检测到电平变化后延时10~20ms再读一次,如果电平稳定就确认为有效变化。这个方法在轮询扫描中可以这样写:

uint8_t MatrixKey_GetDebouncedState(uint8_t row, uint8_t col) { uint8_t first_state = key_state[row][col]; HAL_Delay(15); uint8_t second_state = key_state[row][col]; return (first_state == second_state) ? first_state : 0; }

但这种方法有个问题:HAL_Delay会阻塞整个CPU 15ms,如果主循环还有别的事,这15ms就是纯纯的浪费。定时器扫描方案里更好的做法是用连续采样计数:连续N次扫描都读到同一个状态,才认为状态有效。

uint8_t debounce_count[4][4] = {0}; void MatrixKey_DebounceHandle(void) { for (uint8_t row = 0; row < 4; row++) { for (uint8_t col = 0; col < 4; col++) { if (key_state[row][col] == 1) { if (debounce_count[row][col] < 3) { debounce_count[row][col]++; if (debounce_count[row][col] >= 3) { // 连续3次读到按下,确认按键事件 key_event[row][col] = 1; } } } else { debounce_count[row][col] = 0; } } } }

这个逻辑的核心思想是:按键必须先“连续3次扫描都检测到按下”才被确认为有效事件,任何一次的释放都会清零计数。由于定时器中断固定1ms进入一次,这3次计数实际上等效于3ms的消抖时间,但比单纯的延时消抖灵活得多——你想调消抖时间,改计数阈值就行,不用动延时函数。

5.2 串键现象:为什么按下两颗键会乱码

矩阵键盘有个特殊的问题,叫串键或鬼键。假设你同时按下第1行第1列和第2行第2列的按键,这时候第1行第2列的交点处虽然没被按下,但因为行线1和行线2都被列线1和列线2拉到了通路状态,程序可能会误判第1行第2列的按键也被按下了。

我举个例子解释得更清楚:

  • Row1-Col1接通
  • Row2-Col2接通

这时候Row1和Col2之间并没有按键导通,但当Col1输出低电平扫描时,电流路径是Col1→Row1→Row2→Col2,Row2读取到了低电平,程序就会误认为Row2-Col1也被按下。

解决串键有两种思路。第一种是硬件方案,在每颗按键上串联一颗二极管,方向从行线到列线,阻止电流倒灌。这个方案在正规键盘上很常见,但会增加硬件成本。第二种是软件方案,在扫描时只接受单键按下,检测到多个键同时按下就直接丢弃本轮扫描结果,不产生任何按键事件。

产品里如果允许组合键,比如游戏手柄的多个方向键同时按下,那软件方案行不通,必须加二极管。但大多数场合,比如密码输入、菜单选择,用户不会同时按两个键,软件方案完全够用,也省事。

5.3 长按与连击的事件处理

按键事件不一定只是按下和释放两种状态,实际产品里经常需要区分短按、长按和连击。基于定时器扫描框架,这个需求实现起来很自然。

在每次扫描中如果检测到按键有效按下,就把这个键的按下次数加1。当计数达到短按阈值比如50(也就是50ms)仍未释放,触发一次短按事件;继续计数到500(500ms)后,触发一次长按事件;如果长按期间每隔200ms触发一次连击事件,就是手动实现了一个类似电脑键盘的连发功能。

这些阈值参数可以根据你的使用场景随意调整。我建议把短按、长按、连击的阈值定义成宏,不要写死在代码里,后面调节试参数的时候不用翻逻辑代码。

6. 完整驱动代码与项目实战:一个密码锁的例子

6.1 驱动层代码封装思路

前面几节的代码片段是打散的,真正要用到项目里,最好封装成一个独立模块。我习惯建两个文件:matrix_key.h和matrix_key.c,对外只暴露几个接口,内部细节全部隐藏。

// matrix_key.h #ifndef __MATRIX_KEY_H #define __MATRIX_KEY_H #include "main.h" #define KEY_PRESS 1 #define KEY_RELEASE 0 typedef enum { KEY_0, // Row0-Col0 KEY_1, // Row0-Col1 KEY_2, // Row0-Col2 KEY_3, // Row0-Col3 KEY_4, // Row1-Col0 // ... 按实际布局对应 KEY_15 } MatrixKey_Num; typedef enum { KEY_EVENT_NONE, KEY_EVENT_SHORT, KEY_EVENT_LONG, KEY_EVENT_REPEAT } MatrixKey_Event; void MatrixKey_Init(void); void MatrixKey_Scan(void); void MatrixKey_Task(void); // 在定时器中断里调用 uint8_t MatrixKey_GetPressedKey(void); MatrixKey_Event MatrixKey_GetEvent(void); #endif

在.c文件里,扫描函数和消抖函数都被MatrixKey_Task封装起来,主程序只需要调用Task,然后轮询读取按键事件。这样主程序里的业务代码看起来很干净——你不需要关心扫描细节、消抖计数,只用关心“拿到了哪个键、这个键是短按还是长按”。

6.2 密码锁业务逻辑:怎么把这16个键用起来

拿一个常见的应用场景举例:4×4矩阵键盘做6位密码锁。密码为“123456”,键盘布局和电话键盘一致:

  • 第1行:1 2 3 A
  • 第2行:4 5 6 B
  • 第3行:7 8 9 C
  • 第4行:* 0 # D

主循环的逻辑应该是这样的:

uint8_t password[6] = {'1', '2', '3', '4', '5', '6'}; uint8_t input_buffer[6] = {0}; uint8_t input_index = 0; while (1) { MatrixKey_Event event = MatrixKey_GetEvent(); uint8_t key = MatrixKey_GetPressedKey(); if (event == KEY_EVENT_SHORT && key != 0xFF) { // 将键值映射为字符 char key_char = KeyMapToChar(key); if (key_char == '#') { // 确认密码 if (memcmp(input_buffer, password, 6) == 0) { // 密码正确,执行开锁操作 Unlock(); } else { // 密码错误,提示 ShowError(); } input_index = 0; } else if (key_char == '*') { // 删除一位 if (input_index > 0) input_index--; } else { if (input_index < 6) { input_buffer[input_index++] = key_char; } } } }

这样按键驱动层和业务层就彻底分离了。想换成8×8键盘,驱动层改扫描逻辑,业务层完全不用动;想改密码位数,业务层改一个数组大小就行。

6.3 调试过程实录:一次串键问题的排查

我第一次做矩阵键盘密码锁时遇到一个很诡异的现象:输入“123456”时,系统时不时会报密码错误。后来我在串口里把每次按键的键值打出来,发现输入“3”的时候偶尔会同时抓到一个“6”的事件。

排查过程是这样的:先怀疑硬件连接问题,用万用表量了所有行线和列线,确定没有虚焊或短接。然后怀疑消抖逻辑不够,把连续采样次数从3提高到5,发现问题还在。最后我在扫描函数里加了打印,发现按下Row0-Col2(也就是“3”)时,Row0-Col1偶尔读到低电平,这就是典型的串键——因为“2”和“3”在相邻的列上,按下“3”时机械结构可能轻微带动了旁边的“2”。

最终解决办法是调整扫描策略:每次扫描只记录单键按下,检测到多个键同时按下时丢弃本帧数据,并增加一个5ms的屏蔽窗口。改完之后这个现象就消失了,后来再也没复发过。

7. 常见问题与排查技巧实录:扫雷指南

7.1 按键完全无响应的排查路径

矩阵键盘接上后一个键都没反应,这是新手最容易遇到的问题。排查路线其实很固定,按顺序来就行:

第一步,检查电源和地。模块供电是否正常,MCU和模块是否共地。行线和列线如果分别接在两个不同的电源域上,电平参考不一致,读回来的数据肯定是错的。

第二步,检查引脚初始化配置。用调试器或者示波器量列引脚电平,看扫描时是否确实在轮番变化。如果列引脚始终是高电平,说明初始化配置可能有问题,或者代码里根本没有执行扫描函数。

第三步,检查行引脚读取。把行引脚配置成输入上拉,在按键按下的时候量行引脚电平,确认能拉低。如果行引脚内部上拉没生效,按键按下时读到的可能是高阻态,代码判断就会出错。

第四步,检查扫描函数是否被调用。常见问题是一开始把扫描函数写在主循环里,但主循环被某个阻塞函数卡住了,扫描一直没执行。你可以在扫描函数里放一个计数器,然后在串口或LED上输出,确认它在跑。

7.2 按键错乱与串键的快速定位

按键错乱分两种:一种是固定的错位,比如按下“1”识别成“3”,这种大概率是接线顺序错了——行线和列线没有一一对应;另一种是偶发的多键触发,这种多半是串键问题,需要加强行读取逻辑里的防串键处理。

排查错位问题,最快的方法是在扫描函数里把所有16个按键的原始坐标打印出来,然后逐个按键按下,记录每个键对应的行列组合。整理成一个表格,对照实际接线就能发现是哪条线接错了。

7.3 STM32调试环境相关的高频问题

写矩阵键盘这类基础外设驱动,调试环境本身也经常出问题。我列几个典型的:

Keil5里下载程序时提示找不到目标芯片,大概率是Debug配置里选错了调试器型号,或者SWD引脚被复用成了其他功能。PA13和PA14默认是SWD,如果你把这俩引脚配置成普通GPIO且程序里没有禁用JTAG的选项,下载器就连不上了。解决办法是把BOOT0拉高进入串口下载模式,或者按住复位键的同时点下载,时机掌握好就能连上。

另外一个常见问题:GPIO配置了内部上拉,但读到的电平不稳定。这个和外部电路的走线长度有关,如果按键模块离MCU比较远,建议用短一点的杜邦线,或者接个100nF的电容在按键两端做硬件滤波。

keil5新建工程时找不到STM32芯片型号,是因为没有装对应的芯片包,去Pack Installer里安装STM32F1系列的Device Family Pack就行。

STM32F103的USART1在PA9和PA10,如果你用了串口打印调试信息,这俩引脚就不能再接键盘。我就吃过这个亏,接线都焊好了才发现串口和矩阵键盘撞了引脚,最后只能飞线换引脚,非常痛苦。

7.4 关于GPIO的速度设置的取舍

STM32的GPIO输出速度有Low、Medium、High三档。很多人以为输出速度越快越好,其实不是。对矩阵键盘这种低频信号,Speed选Low完全够用,选High反而可能因为信号边沿太陡导致电磁干扰和串扰。

同样的道理也适用于I2C,I2C的速率都不快,GPIO速度选Low反而会让波形更干净。这个知识点对做硬件的新手很有用,能少踩很多坑。

8. 经验总结与进阶方向

做完一个4×4矩阵键盘驱动,其实你已经打通了STM32 GPIO输入输出、定时器、中断、状态检测这几项最基础也是最重要的技能。这个项目看似简单,但里面蕴含的知识点是很扎实的:GPIO模式配置、端口寄存器操作、定时器中断框架、消抖算法、状态机思想,全部都是后续做复杂项目的基础。

我用了很多次矩阵键盘,每换一个平台就重写一遍驱动,但核心思路从来没变过。后来我还把这份驱动移植到STM32F407、GD32和ESP32上,改动量很小。你只要把GPIO操作相关的宏和底层函数换一下,上面的消抖和事件逻辑完全可以复用。

进阶方向上,你可以给这个驱动加上“组合键”支持,比如同时按下“1”和“3”触发特殊功能,这在某些工控面板上很实用。也可以把扫描结果通过DMA直接搬运到内存,彻底解放CPU,不过对4×4键盘来说有点杀鸡用牛刀,8×8以上的矩阵才有必要。

最后分享一个我个人的习惯:每次拿到一块新的矩阵键盘模块,我做的第一件事不是急着写驱动,而是先用万用表把所有行线和列线之间的导通关系测一遍,画一个引脚映射表出来。这个步骤十分钟就能做完,但能省掉后面无数次的怀疑和重查。纸上开门见山,心里不慌。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询