按键组(基于51单片机)

一、按键组基础概念(普中 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. 扫描原理
  1. 行扫描:先将所有行置低电平、列置高电平,检测列电平是否变化(判断是否有按键按下)。
  2. 逐行置 0:依次将每一行置低电平,其余行置高电平,检测哪一列电平变低,确定按键的行和列。
  3. 键值映射 :通过行号和列号计算按键索引,从KeyCodeTable中获取对应键值(如第 0 行第 1 列对应键值 '1')。

四、实战注意事项

  1. 硬件接线:普中 51 的按键需确认上拉电阻是否接好(开发板已自带,无需外接),避免电平悬空导致检测异常。
  2. 延时参数:消抖延时需匹配单片机晶振(普中默认 11.0592MHz),若晶振为 12MHz,需调整延时函数的参数。
  3. 中断优化:若主循环有其他耗时操作(如数码管显示),可将按键检测放到外部中断(如 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 操作)
响应速度 稍慢 更快
适用场景 新手学习、简单项目 对响应速度要求高的项目

总结

  1. 普中 51 的按键组分独立式 (4 个按键,P3 口,低电平触发)和矩阵式(4×4,P1 口,行列扫描),核心是检测 IO 口电平变化。

  2. 机械按键必须做软件消抖(延时 20ms 左右),避免抖动导致误触发。

  3. 独立按键适合少按键场景(≤8 个),矩阵按键适合多按键场景,可节省 IO 口资源。 在 51 单片机的矩阵按键检测中,行列扫描 (也叫逐行扫描)和线翻转法是两种最核心的检测机制,两者都是为了解决矩阵按键 "定位按下按键的行和列" 的问题,但实现思路、效率和代码逻辑有明显区别。下面结合普中 51 单片机的 4×4 矩阵按键(P1 口:P1.0-P1.3 为行,P1.4-P1.7 为列)详细讲解。

    一、先明确矩阵按键的硬件基础

    4×4 矩阵按键由4 行(Row)4 列(Column) 交叉组成,每个按键对应 "一行一列" 的交点:

  4. 行线(P1.0-P1.3):可配置为输出或输入

  5. 列线(P1.4-P1.7):可配置为输出或输入

  6. 按键按下时,对应的行线和列线会导通;未按下时,行、列线相互隔离。

  7. 优点:逻辑简单,新手易理解,兼容性好(适配所有矩阵按键);

  8. 缺点:效率较低(需逐行扫描),若主循环有其他耗时操作,可能导致按键响应延迟。

  9. 优点:效率高(无需逐行扫描,两次翻转即可定位),按键响应更快;

  10. 缺点:逻辑稍复杂,需理解 IO 口输入输出方向的翻转,对新手不友好。

  11. 行列扫描法是矩阵按键的基础方法,核心是 "逐行置低→检测列",逻辑简单易理解,适合 51 单片机新手入门;

  12. 线翻转法是优化方法,核心是 "两次翻转 IO 口方向",分别读取列号和行号,检测效率更高,适合对按键响应速度有要求的场景;

  13. 两种方法都需做软件消抖,且普中 51 单片机的矩阵按键默认接 P1 口,只需对应修改 IO 口配置即可适配。

相关推荐
项目題供诗13 小时前
51单片机入门-LED点阵屏(九)
单片机·嵌入式硬件·51单片机
恶魔泡泡糖13 小时前
51单片机ADC模数转换
单片机·嵌入式硬件·51单片机
单片机设计星球13 小时前
51单片机的【体温心率计】仿真设计
单片机·嵌入式硬件·51单片机
项目題供诗13 小时前
51单片机入门-DS1302时钟(十)
单片机·嵌入式硬件·51单片机
恶魔泡泡糖13 小时前
51单片机DAC数模转换
单片机·嵌入式硬件·51单片机
单片机设计星球13 小时前
51单片机的【智能台灯】仿真设计
单片机·嵌入式硬件·51单片机
智者知已应修善业14 小时前
【51单片机8位密码锁】2023-2-22
c语言·经验分享·笔记·单片机·嵌入式硬件·算法·51单片机
普中科技14 小时前
【普中51单片机开发攻略--基于普中-2&普中-3&普中-4】-- 第 22 章 串口通信实验
单片机·嵌入式硬件·51单片机·串口通信·开发板·普中科技
小龙报16 天前
【51单片机】 给单片机加 “安全锁”!看门狗 WDT:原理 + 配置 + 复位验证全拆解,让程序稳定不跑飞
驱动开发·stm32·单片机·嵌入式硬件·物联网·51单片机·硬件工程