一、按键组基础概念(普中 51 单片机)
按键组是多个独立按键按一定规律排列(如矩阵或独立式)的输入模块,普中 51 开发板最常见的是4 个独立按键组 (独立式)和4×4 矩阵按键组,核心作用是通过检测 IO 口电平变化实现人机交互(如控制 LED、切换模式)。
1. 按键硬件原理
- 普中 51 的按键默认采用上拉电阻 + 低电平触发:按键未按下时,IO 口通过上拉电阻接 VCC(高电平);按下时,IO 口接地(低电平)。
- 机械按键存在抖动(按下 / 松开瞬间电平会多次跳变,持续约 10-20ms),需通过软件消抖处理。
二、独立按键组(普中 51 最常用)
普中 51 开发板通常在 P3 口配置 4 个独立按键(K1-K4),硬件连接如下:
表格
| 按键 | 单片机 IO 口 | 功能(常用) |
|---|---|---|
| K1 | P3_0 | 功能 1 |
| K2 | P3_1 | 功能 2 |
| K3 | P3_2 | 功能 3 |
| K4 | P3_3 | 功能 4 |
1. 核心代码(独立按键组控制 LED)
c
运行
#include <reg51.h>
// 定义端口(普中51开发板LED接P2口,按键接P3.0-P3.3)
sbit LED1 = P2^0;
sbit LED2 = P2^1;
sbit LED3 = P2^2;
sbit LED4 = P2^3;
sbit K1 = P3^0;
sbit K2 = P3^1;
sbit K3 = P3^2;
sbit K4 = P3^3;
// 软件消抖函数(延时20ms,适配51单片机11.0592MHz晶振)
void Delay20ms() {
unsigned char i, j;
i = 20;
j = 126;
do {
while (--j);
} while (--i);
}
// 按键检测函数
void Key_Scan() {
// 检测K1(P3.0)是否按下
if(K1 == 0) {
Delay20ms(); // 消抖
if(K1 == 0) { // 确认按键按下
LED1 = ~LED1; // 翻转LED1状态
// 等待按键松开(避免重复触发)
while(K1 == 0);
}
}
// 检测K2(P3.1)
if(K2 == 0) {
Delay20ms();
if(K2 == 0) {
LED2 = ~LED2;
while(K2 == 0);
}
}
// 检测K3(P3.2)
if(K3 == 0) {
Delay20ms();
if(K3 == 0) {
LED3 = ~LED3;
while(K3 == 0);
}
}
// 检测K4(P3.3)
if(K4 == 0) {
Delay20ms();
if(K4 == 0) {
LED4 = ~LED4;
while(K4 == 0);
}
}
}
void main() {
while(1) { // 死循环持续检测按键
Key_Scan();
}
}
2. 代码关键解释
- 端口定义 :
sbit是 51 单片机特有的位定义关键字,用于指定单个 IO 口(如 P3^0 表示 P3 口的第 0 位)。 - 软件消抖 :
Delay20ms()延时 20ms 跳过按键抖动期,确保检测到的电平是稳定的。 - 按键确认:两次检测 IO 口电平(先检测→延时→再检测),只有两次都是低电平才判定为按键按下,避免抖动误触发。
- 等待松开 :
while(K1 == 0);确保一次按键只执行一次操作(否则按住按键会反复触发)。
三、4×4 矩阵按键组(进阶)
当按键数量多(如 16 个)时,独立按键会占用过多 IO 口,矩阵按键通过 "行 + 列" 扫描仅用 8 个 IO 口实现 16 个按键检测,普中 51 的矩阵按键通常接 P1 口(P1.0-P1.3 为行,P1.4-P1.7 为列)。
1. 核心代码(矩阵按键扫描)
c
运行
#include <reg51.h>
// 定义矩阵按键端口(P1口)
unsigned char code KeyCodeTable[] = { // 按键对应键值
'0','1','2','3',
'4','5','6','7',
'8','9','A','B',
'C','D','E','F'
};
unsigned char KeyValue; // 存储当前按下的按键值
// 延时消抖函数
void Delay10ms() {
unsigned char i, j;
i = 10;
j = 184;
do {
while (--j);
} while (--i);
}
// 矩阵按键扫描函数
void MatrixKey_Scan() {
unsigned char i, j;
unsigned char temp;
P1 = 0xf0; // 行置0,列置1(P1.0-P1.3=0,P1.4-P1.7=1)
if(P1 != 0xf0) { // 检测是否有按键按下
Delay10ms(); // 消抖
if(P1 != 0xf0) {
// 逐行扫描
for(i=0; i<4; i++) {
P1 = ~(1<<i); // 第i行置0,其余行置1
temp = P1; // 保存当前P1口值
temp &= 0xf0; // 只保留列电平
if(temp != 0xf0) { // 检测到列有电平变化
// 逐列检测
for(j=0; j<4; j++) {
if(temp == ~(1<<(j+4))) {
KeyValue = KeyCodeTable[i*4+j]; // 获取键值
// 等待按键松开
while(P1 != 0xf0);
}
}
}
}
}
}
}
void main() {
KeyValue = 0; // 初始化键值
while(1) {
MatrixKey_Scan(); // 持续扫描矩阵按键
// 可添加键值处理逻辑(如显示到数码管)
}
}
2. 扫描原理
- 行扫描:先将所有行置低电平、列置高电平,检测列电平是否变化(判断是否有按键按下)。
- 逐行置 0:依次将每一行置低电平,其余行置高电平,检测哪一列电平变低,确定按键的行和列。
- 键值映射 :通过行号和列号计算按键索引,从
KeyCodeTable中获取对应键值(如第 0 行第 1 列对应键值 '1')。
四、实战注意事项
- 硬件接线:普中 51 的按键需确认上拉电阻是否接好(开发板已自带,无需外接),避免电平悬空导致检测异常。
- 延时参数:消抖延时需匹配单片机晶振(普中默认 11.0592MHz),若晶振为 12MHz,需调整延时函数的参数。
- 中断优化:若主循环有其他耗时操作(如数码管显示),可将按键检测放到外部中断(如 P3.2/INT0),提高响应速度。
总结
二、行列扫描法(逐行扫描)
1. 核心思路
"先确定行,再确定列":先将所有行线置低电平、列线置高电平,判断是否有按键按下;若有,再逐行置低、其余行置高,检测哪一列电平变低,最终定位按键的行和列。
2. 分步原理(普中 51 为例)
表格
| 步骤 | 操作(P1 口) | 目的 |
|---|---|---|
| 1 | 行(P1.0-P1.3)= 0(低电平),列(P1.4-P1.7)= 1(高电平) | 检测是否有按键按下:若任意按键按下,对应列线会被行线拉低,P1 口值≠0xF0 |
| 2 | 消抖(延时 10-20ms) | 排除机械抖动导致的误检测 |
| 3 | 逐行扫描:第 1 行置 0,其余 3 行置 1 → 检测列线电平 | 若某列电平变低,说明 "第 1 行 + 该列" 的按键按下;若无,继续扫第 2 行 |
| 4 | 重复步骤 3,依次扫描第 2、3、4 行 | 直到找到电平变低的列,定位行和列 |
| 5 | 等待按键松开 | 避免一次按键多次触发 |
3. 完整代码(普中 51 适配)
c
运行
#include <reg51.h>
// 键值表:对应4×4矩阵按键的16个按键
unsigned char code KeyCodeTable[] = {
'0','1','2','3', // 第0行:列0-3
'4','5','6','7', // 第1行:列0-3
'8','9','A','B', // 第2行:列0-3
'C','D','E','F' // 第3行:列0-3
};
unsigned char KeyValue = 0; // 存储按下的按键值
// 延时10ms(消抖用,11.0592MHz晶振)
void Delay10ms() {
unsigned char i, j;
i = 10;
j = 184;
do {
while (--j);
} while (--i);
}
// 行列扫描函数
void RowColScan() {
unsigned char row, col;
// 步骤1:检测是否有按键按下
P1 = 0xF0; // 行=0(P1.0-3),列=1(P1.4-7)
if(P1 != 0xF0) { // 有按键按下
Delay10ms(); // 步骤2:消抖
if(P1 != 0xF0) {
// 步骤3:逐行扫描
for(row=0; row<4; row++) {
P1 = ~(1 << row); // 第row行置0,其余行置1
// 读取列电平(只保留P1.4-7)
unsigned char col_status = P1 & 0xF0;
if(col_status != 0xF0) { // 检测到列电平变化
// 逐列判断
for(col=0; col<4; col++) {
if(col_status == ~(1 << (col+4))) {
KeyValue = KeyCodeTable[row*4 + col];
// 步骤5:等待按键松开
while((P1 & 0xF0) != 0xF0);
break;
}
}
break;
}
}
}
}
}
void main() {
while(1) {
RowColScan(); // 持续扫描按键
// 可添加键值处理逻辑(如显示到数码管)
}
}
4. 特点
三、线翻转法
1. 核心思路
"先测列,再测行,两次翻转定位":通过两次翻转 IO 口的输入输出方向(先行输出、列输入 → 列输出、行输入),分别获取列号和行号,最终定位按键。
2. 分步原理(普中 51 为例)
表格
| 步骤 | 操作(P1 口) | 目的 |
|---|---|---|
| 1 | 行(P1.0-3)= 0(输出低),列(P1.4-7)= 输入(高阻) | 按键按下时,对应列线被行线拉低 → 读取列号 |
| 2 | 消抖(延时 10-20ms) | 排除抖动 |
| 3 | 列(P1.4-7)= 0(输出低),行(P1.0-3)= 输入(高阻) | 按键按下时,对应行线被列线拉低 → 读取行号 |
| 4 | 行号 + 列号 → 定位按键 | 计算按键索引 |
| 5 | 等待按键松开 | 避免重复触发 |
3. 完整代码(普中 51 适配)
c
运行
#include <reg51.h>
unsigned char code KeyCodeTable[] = {
'0','1','2','3',
'4','5','6','7',
'8','9','A','B',
'C','D','E','F'
};
unsigned char KeyValue = 0;
// 延时10ms消抖
void Delay10ms() {
unsigned char i, j;
i = 10;
j = 184;
do {
while (--j);
} while (--i);
}
// 线翻转法扫描按键
void LineReverseScan() {
unsigned char row=0, col=0;
// 步骤1:行输出低,列输入 → 读列号
P1 = 0x0F; // 行(P1.0-3)=0,列(P1.4-7)=1(输入模式)
if(P1 != 0x0F) { // 有按键按下
Delay10ms();
if(P1 != 0x0F) {
// 读取列号(P1.4-7的电平)
switch(P1 & 0xF0) {
case 0x10: col = 0; break;
case 0x20: col = 1; break;
case 0x40: col = 2; break;
case 0x80: col = 3; break;
}
// 步骤3:列输出低,行输入 → 读行号
P1 = 0xF0; // 列(P1.4-7)=0,行(P1.0-3)=1(输入模式)
switch(P1 & 0x0F) {
case 0x01: row = 0; break;
case 0x02: row = 1; break;
case 0x04: row = 2; break;
case 0x08: row = 3; break;
}
// 步骤4:计算按键值
KeyValue = KeyCodeTable[row*4 + col];
// 步骤5:等待按键松开
while(P1 != 0xF0);
}
}
}
void main() {
while(1) {
LineReverseScan();
}
}
4. 特点
四、两种方法对比
表格
| 特性 | 行列扫描法 | 线翻转法 |
|---|---|---|
| 核心逻辑 | 逐行置低,检测列电平 | 两次翻转 IO,分别读行 / 列 |
| 代码复杂度 | 低(新手易上手) | 中(需理解 IO 翻转) |
| 检测效率 | 低(最多扫描 4 行) | 高(仅两次 IO 操作) |
| 响应速度 | 稍慢 | 更快 |
| 适用场景 | 新手学习、简单项目 | 对响应速度要求高的项目 |
总结
-
普中 51 的按键组分独立式 (4 个按键,P3 口,低电平触发)和矩阵式(4×4,P1 口,行列扫描),核心是检测 IO 口电平变化。
-
机械按键必须做软件消抖(延时 20ms 左右),避免抖动导致误触发。
-
独立按键适合少按键场景(≤8 个),矩阵按键适合多按键场景,可节省 IO 口资源。 在 51 单片机的矩阵按键检测中,行列扫描 (也叫逐行扫描)和线翻转法是两种最核心的检测机制,两者都是为了解决矩阵按键 "定位按下按键的行和列" 的问题,但实现思路、效率和代码逻辑有明显区别。下面结合普中 51 单片机的 4×4 矩阵按键(P1 口:P1.0-P1.3 为行,P1.4-P1.7 为列)详细讲解。
一、先明确矩阵按键的硬件基础
4×4 矩阵按键由4 行(Row) 和4 列(Column) 交叉组成,每个按键对应 "一行一列" 的交点:
-
行线(P1.0-P1.3):可配置为输出或输入
-
列线(P1.4-P1.7):可配置为输出或输入
-
按键按下时,对应的行线和列线会导通;未按下时,行、列线相互隔离。
-
优点:逻辑简单,新手易理解,兼容性好(适配所有矩阵按键);
-
缺点:效率较低(需逐行扫描),若主循环有其他耗时操作,可能导致按键响应延迟。
-
优点:效率高(无需逐行扫描,两次翻转即可定位),按键响应更快;
-
缺点:逻辑稍复杂,需理解 IO 口输入输出方向的翻转,对新手不友好。
-
行列扫描法是矩阵按键的基础方法,核心是 "逐行置低→检测列",逻辑简单易理解,适合 51 单片机新手入门;
-
线翻转法是优化方法,核心是 "两次翻转 IO 口方向",分别读取列号和行号,检测效率更高,适合对按键响应速度有要求的场景;
-
两种方法都需做软件消抖,且普中 51 单片机的矩阵按键默认接 P1 口,只需对应修改 IO 口配置即可适配。