51单片机开发板实验:多按键检测实验
- 物联网-嵌入式工程师
- 2025-02-22
- 216热度
- 0评论
一、实验目标
当独立键盘与单片机相连时,每个按键都需占用单片机的一个I/O口。若单片机系统需集成大量按键,使用独立按键方式会显著消耗宝贵的I/O口资源。鉴于单片机系统中I/O口资源有限,为了优化资源利用,减少I/O口引脚的使用,当按键数量较多时,通常会引入矩阵键盘作为解决方案。
本实验以4x4矩阵键盘为例,详细阐述其工作原理及检测方法。在开发板上,16个按键被布置成4行4列的形式。第一行所有按键的一端相互连接,形成一根行线;同理,第一列的所有按键的另一端也相互连接,构成一根列线。依此类推,整个键盘仅通过4根行线和4根列线,共8根线,便实现了16个按键的连接。这8根线随后被接入单片机的8个I/O端口上。
通过编程实现键盘扫描,单片机能够检测并识别这16个按键的状态。这种方法不仅高效,而且具有扩展性。利用相同的原理,可以轻松地实现3x3矩阵键盘(共9个键)、5x5矩阵键盘(共25个键)、6x6矩阵键盘(共36个键)乃至更大规模的键盘布局,仅需相应地增加行线和列线的数量,并调整单片机的I/O端口配置即可。
二、矩阵键盘检测原理
无论是独立键盘还是矩阵键盘,单片机检测按键是否被按下的基本原理是相同的,即检测与该键对应的I/O口是否处于低电平状态。独立键盘由于一端已固定为低电平,因此在检测时单片机程序编写相对简便。而矩阵键盘的两端均连接至单片机的I/O口,检测时需通过编程控制单片机I/O口输出低电平。
在矩阵键盘的检测中,行列扫描法和线翻转法是两种常用的方法。行列扫描法首先选定一列输出低电平,其余列保持高电平,随后依次检测各行是否出现低电平。若某行检测到低电平,则结合之前选定的列,即可确定被按下键的具体位置。通过轮流对每列进行低电平输出并检测各行状态,可以实现对所有按键的检测。同样,也可以将行线置为低电平,扫描列线以检测按键。
线翻转法则是一种更为灵活的方法。它首先将所有行线置为低电平,检测列线中是否有低电平出现,并记录相关列线值。随后,翻转状态,将所有列线置为低电平,再次检测行线值并记录。由于按键按下会导致行线值的变化,因此通过比较两次检测的结果,可以识别出所有被按下的按键。
矩阵键盘在检测过程中还需进行按键消抖处理,以消除因按键机械特性引起的抖动对检测结果的影响。在本章的实验中,我们采用了行列扫描法来检测矩阵键盘的按键状态,并集成了按键消抖功能以确保检测的准确性和稳定性。
三、硬件电路
本实验所使用的硬件资源主要包括:
(1)动态数码管:用于显示数字、字符或简单图形。
(2)4x4 矩阵按键:本实验中采用4行4列的矩阵键盘布局,通过8根线(4根行线和4根列线)连接到单片机的I/O口上,支持最多16个按键的输入。
关于静态数码管模块电路,由于在前面章节已有详细介绍,此处不再赘述。开发板上的4x4矩阵按键模块电路,其设计原理基于矩阵扫描原理,通过行列线的组合来定位被按下的按键。具体电路图如下:
行线:四根行线分别连接至单片机P1.4~P1.7,用于输出低电平以选中某一行进行按键扫描。
列线:四根列线也连接至单片机P11.0~P1.3,用于检测当前被选中行中是否有按键被按下(即检测列线是否出现低电平)。
四、软件设计
虽然矩阵键盘充分利用了I/O端口资源,但增加了检测程序的复杂性。因为独立按键有一端固定为低电平,编写单片机键盘检测程序比较方便,而矩阵键盘两端都与I/O端口相连,因此检测程序需要向I/O端口人为送出低电平。具体检测方法:先送一行低电平,其余各行为高电平,现在已经确定了行数(送入低电平的行),然后循环检测与该行相交的各列是否有低电平,若检测到某列有低电平,说明与该行相交的某列按键被按下,即确定了当前按下的按键属于哪一行哪一列。同理,使用同样的方法轮流送各行一次低电平,再轮流检测各列是否有低电平,即可完成所有按键的检测。当然也可以先送各列为低电平,然后再循环检测各行是否有低电平。
1. 数据类型定义
u16:无符号16位整型,用于定义延时函数的参数。
u8:无符号8位整型,用于定义键值、数组索引等。
2. 宏定义
GPIO_DIG:定义LED连接的端口,即P0端口。
GPIO_KEY:定义键盘连接的端口,即P1端口。
3. 全局变量
KeyValue:用于存储从键盘读取到的键值。
4. 数组定义
smgduan:一个包含16个元素的数组,存储LED显示所需的段码,用于控制LED的显示。
5. 函数设计
delay(u16 i):延时函数,通过空循环实现简单的延时,参数i为延时长度。
KeyDown(void):键盘扫描函数,用于检测按键是否被按下,并确定按下的键值。首先通过列扫描确定是哪一列的按键被按下,然后通过行扫描确定具体的行,从而确定键值。
main(void):主函数,无限循环执行KeyDown函数以检测按键,并根据键值控制LED的显示。
6. 键盘扫描逻辑
首先,将列线(GPIO_KEY的低4位)置为低电平,其余为高电平,进行列扫描。
如果检测到列线状态不为0x0F,说明有按键被按下,进行延时消抖。
再次检测列线状态,如果仍然不为0x0F,则确认按键确实被按下。
接下来,通过列扫描确定是哪一列的按键被按下(实际代码中此步骤被重复了,应优化)。
然后,将行线(GPIO_KEY的高4位)置为低电平,其余为高电平,进行行扫描。
根据行扫描的结果,结合列扫描的结果,确定具体的键值。
7.LED显示逻辑
根据键值(KeyValue),从smgduan数组中选取对应的段码。
将段码取反后输出到GPIO_DIG端口,以控制LED的显示。这里取反是为了适应特定的硬件连接方式(假设LED是共阳极的,且高电平熄灭)。
五、编写程序
启动Keil软件后,创建一个新项目,选择单片机型号STC89C52RC。在项目中添加C语言源文件main.c。
在main.c文件内输入下面的源代码:
#include "reg52.h" // 引入8051单片机的寄存器定义
typedef unsigned int u16; // 定义无符号16位整数类型
typedef unsigned char u8; // 定义无符号8位整数类型
#define GPIO_DIG P0 // 定义LED连接的端口为P0
#define GPIO_KEY P1 // 定义键盘连接的端口为P1
u8 KeyValue; // 用于存储从键盘读取到的键值
// LED显示的段码表,用于控制LED的显示
u8 code smgduan[17]={
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
// 注意:这里定义了17个元素,但实际上可能只需要16个,最后一个元素可能未使用
};
// 简单的延时函数,通过空循环实现延时
void delay(u16 i)
{
while(i--);
// 注意:这种延时方式受CPU频率影响,不精确
}
// 键盘扫描函数,用于检测按键并确定键值
void KeyDown(void)
{
char a=0; // 用于延时去抖的计数器
GPIO_KEY=0x0f; // 将P1端口的低4位设为低电平,其余为高电平,进行列扫描
if(GPIO_KEY!=0x0f) // 如果检测到列线状态有变化,说明可能有按键被按下
{
delay(1000); // 延时消抖
if(GPIO_KEY!=0x0f) // 再次检测,确认按键确实被按下
{
// 接下来进行行扫描
GPIO_KEY=0XF0; // 将P1端口的高4位设为低电平,其余为高电平
switch(GPIO_KEY) // 通过检测行线的状态来确定具体的行
{
case(0X70): KeyValue=0; break; // 第1行
case(0Xb0): KeyValue=4; break; // 第2行,注意这里应该是KeyValue+=4
case(0Xd0): KeyValue=8; break; // 第3行,注意这里应该是KeyValue+=8
case(0Xe0): KeyValue=12; break; // 第4行,注意这里应该是KeyValue+=12
}
}
}
while((a<50)&&(GPIO_KEY!=0xf0)) // 等待按键释放
{
delay(100);
a++;
}
}
// 主函数
void main()
{
while(1) // 无限循环
{
KeyDown(); // 扫描键盘并确定键值
GPIO_DIG=~smgduan[KeyValue]; // 根据键值控制LED的显示(取反可能是为了适应共阳极LED)
}
}
六、实验现象
使用 USB 线将开发板和电脑连接成功后,把编写好的程序编译后将编译产生的.hex 文件烧入到芯片内,开发板会自动运行下载的程序,按下矩阵键盘任一按键,数码管显示不同的字符。