目录
写在前面
矩阵键盘采用行列交叉的结构。按键位于行线和列线的交叉点上:
-
4×4矩阵键盘:由4根行线和4根列线交叉构成,形成16个按键。
-
减少I/O占用:16个按键仅需8个I/O口,远少于独立按键。
矩阵键盘扫描方法对比
| 方法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 逐行扫描法 | 每次将一行拉低,读取列线状态 | 逻辑直观,易于理解 | 代码较长,CPU利用率低 | 初学者学习,实时性要求不高的系统 |
| 行列扫描法 | 先所有列输出低电平读行线,再所有行输出低电平读列线 | 代码简洁,执行效率高 | 逻辑相对复杂 | 实际项目,需要高效扫描的场合 |
按键消抖处理
按键在按下和释放时会产生机械抖动,导致电平不稳定。处理方法:
-
软件消抖:检测到按键按下后延时10-20ms,再次检测确认。
-
硬件消抖(可选):在按键两端并联0.1μF电容。
硬件电路

示例代码
#include <reg51.h>
// ================================
// 硬件端口宏定义
// ================================
// 数码管显示端口定义
#define SMG_PORT P0 // 数码管数据线连接的端口
// 矩阵键盘端口定义
#define KEY_PORT P1 // 矩阵键盘连接的端口
// ================================
// 数码管显示编码(共阴)
// ================================
unsigned char code seg_table[16] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, // 0-7
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 // 8-15 (A-F)
};
// ================================
// 延时函数
// ================================
void delay_ms(unsigned int ms)
{
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 123; j++);
}
// ================================
// 键盘扫描函数
// ================================
unsigned char key_scan()
{
unsigned char x, y, key_value = 0;
// 第一步:所有列输出低电平,行作为输入
KEY_PORT = 0xF0; // 列输出0,行输入1
if(KEY_PORT != 0xF0)
{
delay_ms(10); // 延时消抖
if(KEY_PORT != 0xF0)
{
x = KEY_PORT & 0xF0; // 保存行状态
// 第二步:所有行输出低电平,列作为输入
KEY_PORT = 0x0F; // 行输出0,列输入1
y = KEY_PORT & 0x0F; // 保存列状态
// 组合行和列状态确定键值
switch(x | y)
{
case 0xEE: key_value = 1; break; // 第0行第0列
case 0xDE: key_value = 2; break; // 第0行第1列
case 0xBE: key_value = 3; break; // 第0行第2列
case 0x7E: key_value = 4; break; // 第0行第3列
case 0xED: key_value = 5; break; // 第1行第0列
case 0xDD: key_value = 6; break; // 第1行第1列
case 0xBD: key_value = 7; break; // 第1行第2列
case 0x7D: key_value = 8; break; // 第1行第3列
case 0xEB: key_value = 9; break; // 第2行第0列
case 0xDB: key_value = 10; break; // 第2行第1列
case 0xBB: key_value = 11; break; // 第2行第2列
case 0x7B: key_value = 12; break; // 第2行第3列
case 0xE7: key_value = 13; break; // 第3行第0列
case 0xD7: key_value = 14; break; // 第3行第1列
case 0xB7: key_value = 15; break; // 第3行第2列
case 0x77: key_value = 16; break; // 第3行第3列
default:
key_value = 0; // 无效按键
break;
}
if(key_value != 0) {
// 等待按键释放,增加消抖
delay_ms(10);
while((KEY_PORT & 0x0F) != 0x0F) {
delay_ms(1); // 短延时避免忙等待
}
delay_ms(10); // 释放消抖
}
}
}
return key_value;
}
// ================================
// 主函数
// ================================
void main()
{
unsigned char key_val;
// 系统初始化
SMG_PORT = 0x00; // 数码管初始不显示
KEY_PORT = 0xFF; // 键盘端口初始化为高电平
while(1)
{
key_val = key_scan();
if(key_val != 0)
{
// 在数码管上显示按键编号(1-16对应0-F)
SMG_PORT = seg_table[key_val - 1];
// 添加响应延时,避免重复检测
delay_ms(200);
}
}
}
完结,撒花~