第八章 矩阵按键
1. 导入
在第七章中,我们学习了独立按键 的使用,每个按键占用一个I/O口,当按键数量较多时会迅速消耗单片机资源。为解决此问题,本章引入矩阵按键(也称行列式按键),通过"行线+列线"组合识别多个按键,显著节省I/O口。
矩阵按键广泛应用于键盘、遥控器、控制面板等设备中。本章目标:
- 理解矩阵按键的结构与工作原理;
- 掌握"行扫描法"和"列扫描法"识别按键;
- 实现4×4矩阵键盘的按键识别;
- 编写可靠的按键扫描函数;
- 为后续实现密码锁、计算器等复杂输入系统打下基础。
2. 硬件设计
2.1 矩阵按键结构
以最常见的4×4矩阵键盘为例,由4条行线(Row)和4条列线(Col)组成,共可连接16个按键。
Col0 | Col1 | Col2 | Col3 | |
---|---|---|---|---|
Row0 | K1 | K2 | K3 | K4 |
Row1 | K5 | K6 | K7 | K8 |
Row2 | K9 | K10 | K11 | K12 |
Row3 | K13 | K14 | K15 | K16 |
2.2 电路连接
- 行线(Row0~Row3) :接P1.0 ~ P1.3,配置为输出;
- 列线(Col0~Col3) :接P1.4 ~ P1.7,配置为输入,需内部或外部上拉电阻;
- 每个按键位于行线与列线交叉点,按下时导通。
实际连接示例:
- P1.0 → Row0
- P1.1 → Row1
- P1.2 → Row2
- P1.3 → Row3
- P1.4 → Col0
- P1.5 → Col1
- P1.6 → Col2
- P1.7 → Col3
注意:P1口有内部上拉,可直接使用;若使用P0口,需外加上拉电阻。
3. 软件设计
3.1 工作原理
矩阵按键识别采用扫描法,基本步骤如下:
- 所有行线输出高电平;
- 读取列线状态,若某列为低,说明有键按下;
- 进入识别阶段:逐行输出低电平,其余行高;
- 再读列线,根据哪一行拉低及哪一列读低,确定具体按键;
- 软件消抖并返回键值。
3.2 行扫描法实现
c
#include <reg52.h>
// 定义行和列
sbit ROW0 = P1^0;
sbit ROW1 = P1^1;
sbit ROW2 = P1^2;
sbit ROW3 = P1^3;
#define COL_PORT (P1 >> 4) // 列线为P1.4~7,右移4位获取低4位
// 按键延时
void delay_ms(unsigned int ms) {
unsigned int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 125; j++);
}
// 矩阵按键扫描函数,返回键值(1~16),0表示无按键
unsigned char matrix_key_scan() {
P1 = 0xF0; // 行线输出高(P1.0~3=1),列线输入(P1.4~7上拉)
if ((P1 & 0xF0) != 0xF0) { // 列线有低电平,说明有键按下
delay_ms(10); // 消抖
if ((P1 & 0xF0) != 0xF0) {
// 开始逐行扫描
P1 = 0xFE; // Row0=0, Row1~3=1
if ((P1 & 0xF0) != 0xF0) {
switch (P1 & 0xF0) {
case 0xE0: return 1;
case 0xD0: return 2;
case 0xB0: return 3;
case 0x70: return 4;
}
}
P1 = 0xFD; // Row1=0
if ((P1 & 0xF0) != 0xF0) {
switch (P1 & 0xF0) {
case 0xE0: return 5;
case 0xD0: return 6;
case 0xB0: return 7;
case 0x70: return 8;
}
}
P1 = 0xFB; // Row2=0
if ((P1 & 0xF0) != 0xF0) {
switch (P1 & 0xF0) {
case 0xE0: return 9;
case 0xD0: return 10;
case 0xB0: return 11;
case 0x70: return 12;
}
}
P1 = 0xF7; // Row3=0
if ((P1 & 0xF0) != 0xF0) {
switch (P1 & 0xF0) {
case 0xE0: return 13;
case 0xD0: return 14;
case 0xB0: return 15;
case 0x70: return 16;
}
}
}
}
return 0; // 无按键
}
3.3 键值映射优化
为便于使用,可定义键值数组:
c
unsigned char code key_table[4][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
扫描时通过行列索引查表:
c
unsigned char matrix_key_scan_simple() {
unsigned char row, col;
// 设置行为输出,列为输入
P1 = 0xF0;
if ((P1 & 0xF0) != 0xF0) {
delay_ms(10);
if ((P1 & 0xF0) != 0xF0) {
// 扫描每一行
for (row = 0; row < 4; row++) {
P1 = (P1 & 0xF0) | (0x0F & ~(1 << row)); // 第row行输出0
col = (P1 >> 4) & 0x0F; // 读取列
if (col != 0x0F) {
while((P1 >> 4) != 0x0F); // 等待释放
return key_table[row][7 - col]; // 查表(需根据实际连接调整)
}
}
}
}
return 0;
}
实际中需根据硬件连接调整行列对应关系。
3.4 应用示例:按键控制数码管显示
目标:按下矩阵键1~9,数码管显示对应数字。
c
void main() {
unsigned char key;
unsigned char code seg_code[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
while(1) {
key = matrix_key_scan();
if (key >= 1 && key <= 9) {
P0 = seg_code[key]; // 段码输出
P2 = 0x0E; // 选中第一位
delay_ms(5);
}
}
}
3.5 支持多按键识别(扩展)
上述方法仅支持单键识别。若需支持多键,需加入更复杂的逻辑或使用专用芯片。
3.6 编译与下载
- Keil中创建工程;
- 确保矩阵按键焊接或连接正确;
- 编译生成HEX;
- 下载至单片机;
- 按下按键,观察数码管或串口是否返回正确键值。
若无响应:
- 检查行列线是否接反;
- 测量按下时是否真正导通;
- 确认P1口方向控制正确。
##4. 小结
本章通过实现矩阵按键识别,掌握了高效输入系统的设计方法,主要内容包括:
- 硬件结构:理解4×4矩阵键盘的行列布局与I/O节省优势;
- 扫描原理:掌握行扫描法识别具体按键位置;
- 软件实现:编写可靠的按键扫描函数,支持消抖与等待释放;
- 应用扩展:实现按键控制数码管显示;
- 开发能力:为复杂人机交互系统奠定基础。
4.1 常见问题与解决
问题 | 原因 | 解决方法 |
---|---|---|
无法识别按键 | 行列接反、方向错误 | 检查I/O配置,确认输出/输入 |
误识别 | 抖动未消除 | 增加消抖延时或改进逻辑 |
只能识别部分键 | 扫描顺序错误 | 逐行测试,用万用表测量电平 |
重影 | 未等待释放 | 加入while 等待松手 |
亮度闪烁 | 扫描时间过长 | 将按键扫描放入定时中断或优化循环 |
4.2 下一步学习建议
- 学习外部中断结合矩阵键盘实现快速响应;
- 使用定时器中断周期性扫描键盘,解放主程序;
- 实现计算器 或密码锁综合项目;
- 引入LCD1602显示更多信息。
本章标志着你已掌握高效的多键输入技术,下一章将进入定时器/计数器模块的深入学习,实现精准延时与中断控制。