第10章 矩阵按键实验
第十章 矩阵按键实验
1. 矩阵按键介绍
通过上一章我们知道了:独立按键在于单片机连接的时候,每一个按键都需要单片机的一个I/O口,若某单片机系统需较多按键, 如果用独立按键便会占用过多的 I/O 口资源。 单片机系统中 I/O 口资源往往比较宝贵, 当用到多个按键时为了减少 I/O 口引脚, 引入了矩阵按键。
本章以 4*4 矩阵键盘为例讲解其工作原理和检测方法。开发板上将 16 个按键排成 4 行 4 列, 第一行将每个按键的一端连接在一起构成行线, 第一列将每个按键的另一端连接在一起构成列线, 这样便一共有 4 行 4 列共 8 根线, 我们将这 8 根线连接到单片机的 8 个 I/O 口上, 通过程序扫描键盘就可检测 16 个键。
无论是对立按键还是矩阵键盘,检测是否按下都是一样的(检测该端口是否为低电平)
关于矩阵按键更多参考:矩阵键盘_百度百科 (baidu.com)
独立键盘有一端固定为低电平, 此种方式编程比较简单。 而矩阵键盘两端都与单片机 I/O 口相连, 因此在检测时需编程通过单片机 I/O 口送出低电平。 检测方法有多种, 最常用的是行列扫描和线翻转法。
-
行列扫描法:先给一列为低电平,其余列为高电平,然后立即检测一次各行是否为低电平,若检测到某一行为低电平, 则我们便可确认当前被按下的键是哪一行哪一列的。参考:51单片机 | 矩阵键盘行扫描 - hugh.dong - 博客园 (cnblogs.com)
-
线翻转法:使所有的行线为低电平,检测所有列线是否有低电平,如果有, 就记录列线值; 然后再翻转, 使所有列线都为低电平, 检测所有行线的值,由于有按键按下, 行线的值也会有变化, 记录行线的值。 从而就可以检测到全部按键。参考:单片机外设矩阵键盘之线反转法识别原理与示例 - IT宝库 (itbaoku.cn)
本章我们使用行列扫描法
2. 硬件设计
本实验使用到硬件资源如下:
-
静态数码管
-
矩阵按键
静态数码管模块我们已经介绍过了,请看矩阵按键模块:
我们可以看到该模块独立,4*4 矩阵按键引出的 8 根控制管脚并未直接连接到 51 单片机的 IO 上, 而是连接到 JP3 端子上。Hx(14)代表的是行,Lx(14)代表的是列。(4行4列)
参考代码使用 P1 口来检测 4*4 矩阵按键, 使用 P0 口控制静态数码管。 单片机的 P17 口连接矩阵键盘的第 1 行, P13 口连接矩阵键盘第 1列。
3. 软件设计
本章我们要实现的功能是:通过数码管显示矩阵按键S1~S16按下后显示0-F
#include <REGX52.H>
// 共阴极段码数据
unsigned char gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
// 延时函数
void delay_us(unsigned int ten_us)
{
while(ten_us--);
}
// 扫描按键函数
int key_scan()
{
unsigned int key_value; // 定义键值
// 我们使用的是行列扫描法
P1 = 0xf7; // 给第一列赋值0,其余全为1
/*
为什么可以实现这个功能?
这行代码是将 P1 的值设为 0xF7,即二进制形式为 11110111。
11110111即代表了P1.7~P1.1,意味着,P1.7~P1.4和P1.2~P1.0被赋值为1,而P1.3被赋值为0
看我们的接线就知道-P1.3确实是接在L1上的-即代表了第一列
从而我们就完成了给第一列赋0(即输出模式)的操作-其他位全不打开
*/
if(P1 != 0xf7) // 判断第一列按键是否按下
{
delay_us(1000);// 消抖
switch(P1) // 保存第一列按键按下后的键值
{
/*
下面对比段码值,再返回就行了,注意要返回第一列的数据嗷
我们举一个例子 1234
5678
9ABC
DF
比如第一列的5,通过查询段码值,是0xb7
那么我们直接写case 0xb7 让循环找这个数据,如果找到了,就返回5,然后数码管就显示5了
*/
case 0x77:
key_value = 1;
break;
case 0xb7:
key_value = 5;
break;
case 0xd7:
key_value = 9;
break;
case 0xe7:
key_value = 13;
break;
}
}while(P1 != 0xf7); // 等待按键松开
P1 = 0xfb; // 给第二列赋值0,其余全为1
if(P1 != 0xfb) // 判断第二列按键是否按下
{
delay_us(1000);//消抖
switch(P1)//保存第二列按键按下后的键值
{
case 0x7b:
key_value = 2;
break;
case 0xbb:
key_value = 6;
break;
case 0xdb:
key_value = 10;
break;
case 0xeb:
key_value = 14;
break;
}
}while(P1 != 0xfb);//等待按键松开
P1 = 0xfd; // 给第三列赋值0,其余全为1
if(P1 != 0xfd) // 判断第三列按键是否按下
{
delay_us(1000); // 消抖
switch(P1) // 保存第三列按键按下后的键值
{
case 0x7d:
key_value = 3;
break;
case 0xbd:
key_value = 7;
break;
case 0xdd:
key_value = 11;
break;
case 0xed:
key_value = 15;
break;
}
}while(P1 != 0xfd);//等待按键松开
P1 = 0xfe;//给第四列赋值0,其余全为1
if(P1 != 0xfe)//判断第四列按键是否按下
{
delay_us(1000);//消抖
switch(P1)//保存第四列按键按下后的键值
{
case 0x7e:
key_value = 4;
break;
case 0xbe:
key_value = 8;
break;
case 0xde:
key_value = 12;
break;
case 0xee:
key_value = 16;
break;
}
}while(P1 != 0xfe);//等待按键松开
return key_value; // 返回我们保存的值
}
// 主函数
void main()
{
int key; // 定义需要返回的值
while(1)
{
key = key_scan(); // 返回的值
if(key != 0)
// 如果key不等于0,即k=1~f
P0 = ~gsmg_code[key-1]; // 得到的按键值减1换算成数组下标对应0-F段码
// 为什么需要减1呢?(1~f) 、
// 因为学过C语言知道,数组的下标是从0开始的,而键值是从1开始的,所以要key要减1才能对得上数组
}
}
4. 小结
可以看到本章明显难了很多,代码量加大,其实还好,内容有很多重复的,理解了第一列后面就好做了,再来复盘一下整个程序
-
头文件、数码管段码、延时函数:这些不必解释看过很多了
-
扫描函数:重点,先理解第一列是怎么实现的,首先我们给P1赋值(即单片机与矩阵键盘连接)为0xf7(十六进制转化为二进制1111 0111)这时候,P1.3就是0了(即与L1连接的)低电平输出,其他关闭
-
那么怎么判断L1是否按下就是判断P1等不等于1111 0111 ,不等于就代表按下,那么我们就开始记录按下的什么:用switch循环,通过对比段码值,然后返回相应的值(1 5 9 13)
-
后面L2、L3、L4仿照L1来就行了
-
既然我们有了返回的值,就用主函数来表示:先判断key是不是等于0,如果不是P0(数码管与单片机相连)就等于key-1,我们知道数码管是一个数组,但是我们返回的整数,那么返回1【key-1】= 0,数组的第0位就是第一个数字0
我们这里使用的行列扫描法:通过代码我们可以看出来,怎么使用?先给第一列全低电平,也就是全输出,当该列有一行按下,就会根据键值返回我们要显示的数字,以此类推,L2,L3,L4
当然不要忘记了,最后数组内的数字要减1,因为C语言中数组下标要从0开始
2024.6.25 第一次修订
2024.8.19 第二次修订,后期不再维护
本文作者:hazy1k
本文链接:https://www.cnblogs.com/hazy1k/p/18366795
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步