51单片机实现矩阵键盘与数码管显示
一、目的
通过AT89C51单片机实现数码管显示按下的按键值0~F,掌握矩阵键盘接口电路设计及编程应用。
二、矩阵键盘工作原理
1. 矩阵键盘结构
- 行列扫描法:将按键排列成N行×M列的矩阵
- 节省I/O口:4×4键盘只需8个I/O口,而独立键盘需要16个
- 工作方式:通过行线和列线的交叉点判断按键状态
2. 键盘扫描流程
1. 判断是否有按键按下:将所有行线置为低电平,检测列线状态
2. 延时消抖:确认按键按下后延时20ms,消除机械抖动
3. 识别按键位置:逐行扫描,确定具体按下的按键
4. 等待按键释放:检测按键释放后再进行处理
三、硬件设计
1. 电路连接图
矩阵键盘(4×4) AT89C51单片机
行线(R0-R3) → P1.0-P1.3
列线(C0-C3) → P1.4-P1.7
数码管段选信号 → P0.0-P0.7
数码管位选信号 → P2.0-P2.7
2. Proteus电路设计
1. 添加AT89C51单片机
2. 添加4×4矩阵键盘
3. 添加共阴极数码管
4. 添加限流电阻
5. 连接晶振电路(12MHz)和复位电路
设计原理图:
四、软件设计
1. 矩阵键盘扫描程序
#include <reg52.h>
#include <intrins.h>
typedef unsigned char uchar;
typedef unsigned int uint;
// 数码管段选端口
#define SEG_PORT P0
// 矩阵键盘端口
#define KEY_PORT P1
// 共阴极数码管0-F的段码表
uchar code seg_table[] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71
};
// 延时函数
void Delay_ms(uint ms)
{
uint i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 110; j++);
}
// 矩阵键盘扫描函数,返回键值(0-15)
uchar KeyScan()
{
uchar key_value = 0xFF; // 初始键值设为0xFF表示无按键按下
uchar temp;
// 第一步:判断是否有按键按下
KEY_PORT = 0xF0; // 行线置高,列线置低
temp = KEY_PORT; // 读取按键状态
temp &= 0xF0; // 屏蔽低4位(行线)
if(temp != 0xF0) // 如果有按键按下
{
Delay_ms(20); // 延时消抖
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0) // 再次确认有按键按下
{
// 第二步:逐行扫描确定按键位置
// 扫描第一行
KEY_PORT = 0xFE; // 0b11111110
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0)
{
if(temp == 0xE0) key_value = 0; // 第一行第一列
if(temp == 0xD0) key_value = 1; // 第一行第二列
if(temp == 0xB0) key_value = 2; // 第一行第三列
if(temp == 0x70) key_value = 3; // 第一行第四列
}
// 扫描第二行
KEY_PORT = 0xFD; // 0b11111101
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0)
{
if(temp == 0xE0) key_value = 4; // 第二行第一列
if(temp == 0xD0) key_value = 5; // 第二行第二列
if(temp == 0xB0) key_value = 6; // 第二行第三列
if(temp == 0x70) key_value = 7; // 第二行第四列
}
// 扫描第三行
KEY_PORT = 0xFB; // 0b11111011
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0)
{
if(temp == 0xE0) key_value = 8; // 第三行第一列
if(temp == 0xD0) key_value = 9; // 第三行第二列
if(temp == 0xB0) key_value = 10; // 第三行第三列
if(temp == 0x70) key_value = 11; // 第三行第四列
}
// 扫描第四行
KEY_PORT = 0xF7; // 0b11110111
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0)
{
if(temp == 0xE0) key_value = 12; // 第四行第一列
if(temp == 0xD0) key_value = 13; // 第四行第二列
if(temp == 0xB0) key_value = 14; // 第四行第三列
if(temp == 0x70) key_value = 15; // 第四行第四列
}
// 等待按键释放
while((KEY_PORT & 0xF0) != 0xF0);
Delay_ms(20); // 释放后延时消抖
}
}
return key_value;
}
// 显示函数
void Display(uchar num)
{
SEG_PORT = seg_table[num]; // 输出段码
}
// 主函数
void main()
{
uchar key;
while(1)
{
key = KeyScan(); // 扫描键盘
if(key != 0xFF) // 如果有按键按下
{
Display(key); // 显示键值
}
}
}
2. 键盘编码说明
```
矩阵键盘布局及编码:
C0 C1 C2 C3
R0 0 1 2 3
R1 4 5 6 7
R2 8 9 A B
R3 C D E F
五、步骤
1. Proteus电路设计
1. 新建Proteus项目
2. 添加AT89C51单片机
3. 添加4×4矩阵键盘
4. 添加共阴极数码管
5. 添加必要的电阻和电源
6. 按照电路连接图进行连线
2. 软件编程
1. 在Keil uVision中创建新项目
2. 编写键盘扫描和数码管显示程序
3. 编译程序,生成HEX文件
3. 仿真测试
1. 在Proteus中加载HEX文件
2. 设置晶振频率为12MHz
3. 运行仿真
4. 点击矩阵键盘上的按键,观察数码管显示结果
六、实验结果与分析
1. 功能验证
- 按下矩阵键盘上的按键,数码管显示对应数字0-9或字母A-F
- 按键识别准确,无误判现象
- 消抖处理有效,无连击现象
2. 技术要点
- 行列扫描法:通过逐行扫描和列线检测确定按键位置
- 按键消抖:软件延时20ms消除机械抖动
- 共阴极数码管:高电平点亮对应段,通过段码表转换数字
3. 常见问题及解决方法
- 按键无响应:检查连接线是否正确,I/O口设置是否匹配
- 显示乱码:检查段码表是否正确,共阴/共阳选择是否匹配
- 按键误判:增加消抖延时,或改进扫描算法
七、总结与拓展
1. 总结
通过本次实验,需要掌握:
- 矩阵键盘的工作原理和接口设计
- 行列扫描法识别按键的方法
- 键盘消抖处理技术
- 数码管显示数字和字母的实现方法
2. 拓展应用
矩阵键盘可应用于:
- 电子密码锁
- 简易计算器
- 电子时钟调整界面
- 智能家居控制面板
3. 改进方向
- 增加按键音效反馈
- 实现组合键功能
- 优化键盘扫描算法,提高响应速度
- 添加按键长按功能
八、附录
1. 完整代码
#include <reg52.h>
#include <intrins.h>
typedef unsigned char uchar;
typedef unsigned int uint;
// 数码管段选端口
#define SEG_PORT P0
// 矩阵键盘端口
#define KEY_PORT P1
// 共阴极数码管0-F的段码表
uchar code seg_table[] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71
};
// 延时函数
void Delay_ms(uint ms)
{
uint i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 110; j++);
}
// 矩阵键盘扫描函数,返回键值(0-15)
uchar KeyScan()
{
uchar key_value = 0xFF; // 初始键值设为0xFF表示无按键按下
uchar temp;
// 第一步:判断是否有按键按下
KEY_PORT = 0xF0; // 行线置高,列线置低
temp = KEY_PORT; // 读取按键状态
temp &= 0xF0; // 屏蔽低4位(行线)
if(temp != 0xF0) // 如果有按键按下
{
Delay_ms(20); // 延时消抖
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0) // 再次确认有按键按下
{
// 第二步:逐行扫描确定按键位置
// 扫描第一行
KEY_PORT = 0xFE; // 0b11111110
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0)
{
if(temp == 0xE0) key_value = 0; // 第一行第一列
if(temp == 0xD0) key_value = 1; // 第一行第二列
if(temp == 0xB0) key_value = 2; // 第一行第三列
if(temp == 0x70) key_value = 3; // 第一行第四列
}
// 扫描第二行
KEY_PORT = 0xFD; // 0b11111101
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0)
{
if(temp == 0xE0) key_value = 4; // 第二行第一列
if(temp == 0xD0) key_value = 5; // 第二行第二列
if(temp == 0xB0) key_value = 6; // 第二行第三列
if(temp == 0x70) key_value = 7; // 第二行第四列
}
// 扫描第三行
KEY_PORT = 0xFB; // 0b11111011
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0)
{
if(temp == 0xE0) key_value = 8; // 第三行第一列
if(temp == 0xD0) key_value = 9; // 第三行第二列
if(temp == 0xB0) key_value = 10; // 第三行第三列
if(temp == 0x70) key_value = 11; // 第三行第四列
}
// 扫描第四行
KEY_PORT = 0xF7; // 0b11110111
temp = KEY_PORT;
temp &= 0xF0;
if(temp != 0xF0)
{
if(temp == 0xE0) key_value = 12; // 第四行第一列
if(temp == 0xD0) key_value = 13; // 第四行第二列
if(temp == 0xB0) key_value = 14; // 第四行第三列
if(temp == 0x70) key_value = 15; // 第四行第四列
}
// 等待按键释放
while((KEY_PORT & 0xF0) != 0xF0);
Delay_ms(20); // 释放后延时消抖
}
}
return key_value;
}
// 显示函数
void Display(uchar num)
{
SEG_PORT = seg_table[num]; // 输出段码
}
// 主函数
void main()
{
uchar key;
while(1)
{
key = KeyScan(); // 扫描键盘
if(key != 0xFF) // 如果有按键按下
{
Display(key); // 显示键值
}
}
}
希望这篇博客对你理解51单片机矩阵键盘和数码管显示有所帮助!