内容
按下S1-S16键,对应数码管最左边显示0-F
矩阵按键简介
独立按键与单片机连接时,每一个按键都需要单片机的一个I/O 口,若某单片机系统需较多按键,如果用独立按键便会占用过多的I/O口资源;而单片机
系统中I/O口资源往往比较宝贵,当用到多个按键时为了减少I/O口引脚,引入了矩阵按键;
以4*4矩阵键盘为例,键排成4行4列,第一行将每个按键的一端连接在一起构成行线,第一列将每个按键的另一端连接在一起构成列线,这样便一共有4行4列共8根线,我们将这8根线连接到单片机的8个I/O口上,通过程序扫描键盘就可检测16个键;
用这种方法也可实现3行3列9个键、5行5列25个键、6行6列36个键甚至更多;
无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样的,也就是检测与该键对应的I/O口是否为低电平;
独立键盘有一端固定为低电平,此种方式编程比较简单;而矩阵键盘两端都与单片机I/O口相连,因此在检测时需编程通过单片机I/O口送出低电平;
检测方法有多种,最常用的是行列扫描和线翻转法:
- 行列扫描法检测时,(可以看作是每次把一列当作独立按键来检测)依次送一列为低电平,其余几列全为高电平(行全为高电平),如果检测到该列有行电平变低,即该列有按键按下,就可以确定列,然后立即检测该行哪列为低电平,则可确定行,这样我们就可确认当前被按下的键是哪一行哪一列的;当然我们也可以依次将行线置低电平(其余行列为高电平),扫描列是否有低电平;
- 线翻转法,就是使所有行线为低电平时,检测所有列线是否有低电平,如果有,就记录列线值;然后再翻转,使所有列线都为低电平,检测所有行线的值,由于有按键按下,行线的值也会有变化,记录行线的值;得到的行列值就是按下的按键;
矩阵键盘也少不了按键消抖的环节;
原理图
线路图
由线路图可知,P17-14控制行,P13-10控制列
思路
使用行列扫描法,每次把一列当作独立按键来检测,依次让每列为低电位,如果某列有行变为低电位,则该行和列即是按下的按键;
注意消抖;
编码
main.c
/*
* @Description: 矩阵按键-按下S1-S16键,对应数码管最左边显示0-F
*/
#include "reg52.h"
typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;
#define KEY_MATRIX_PORT P1 // 使用宏定义矩阵按键控制口
#define SMG_A_DP_PORT P0 // 使用宏定义数码管段码口
// 共阴极数码管显示0~F的段码数据
u8 gsmg_code[17] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
/**
* @description: 延时函数(循环一次大约10us)
* @param {u16} ten_us
* @return {*}
*/
void delay_10us(u16 ten_us)
{
while (ten_us--)
;
}
/**
* @description: 使用行列式扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* @return {u8} key的键值
*/
u8 key_matrix_ranks_scan(void)
{
u8 key_value = 0;
KEY_MATRIX_PORT = 0xf7; // 给第一列赋值0,其余全为1
if (KEY_MATRIX_PORT != 0xf7) // 判断第一列按键是否按下(如果有按键按下,即其中有行变为低位,则两边就不相等)
{
delay_10us(1000); // 消抖
switch (KEY_MATRIX_PORT) // 保存第一列按键按下后的键值
{
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 (KEY_MATRIX_PORT != 0xf7)
; // 等待按键松开
KEY_MATRIX_PORT = 0xfb; // 给第二列赋值0,其余全为1
if (KEY_MATRIX_PORT != 0xfb) // 判断第二列按键是否按下
{
delay_10us(1000); // 消抖
switch (KEY_MATRIX_PORT) // 保存第二列按键按下后的键值
{
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 (KEY_MATRIX_PORT != 0xfb)
; // 等待按键松开
KEY_MATRIX_PORT = 0xfd; // 给第三列赋值0,其余全为1
if (KEY_MATRIX_PORT != 0xfd) // 判断第三列按键是否按下
{
delay_10us(1000); // 消抖
switch (KEY_MATRIX_PORT) // 保存第三列按键按下后的键值
{
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 (KEY_MATRIX_PORT != 0xfd)
; // 等待按键松开
KEY_MATRIX_PORT = 0xfe; // 给第四列赋值0,其余全为1
if (KEY_MATRIX_PORT != 0xfe) // 判断第四列按键是否按下
{
delay_10us(1000); // 消抖
switch (KEY_MATRIX_PORT) // 保存第四列按键按下后的键值
{
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 (KEY_MATRIX_PORT != 0xfe)
; // 等待按键松开
return key_value;
}
/**
* @description: 使用线翻转扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* @return {u8} key的键值
*/
u8 key_matrix_flip_scan(void)
{
static u8 key_value = 0;
KEY_MATRIX_PORT = 0x0f; // 给所有行赋值0,列全为1
if (KEY_MATRIX_PORT != 0x0f) // 判断按键是否按下
{
delay_10us(1000); // 消抖
if (KEY_MATRIX_PORT != 0x0f)
{
// 测试列
KEY_MATRIX_PORT = 0x0f;
switch (KEY_MATRIX_PORT) // 保存行为0,按键按下后的列值
{
case 0x07:
key_value = 1;
break;
case 0x0b:
key_value = 2;
break;
case 0x0d:
key_value = 3;
break;
case 0x0e:
key_value = 4;
break;
}
// 测试行
KEY_MATRIX_PORT = 0xf0;
switch (KEY_MATRIX_PORT) // 保存列为0,按键按下后的键值
{
case 0x70:
key_value = key_value;
break;
case 0xb0:
key_value = key_value + 4;
break;
case 0xd0:
key_value = key_value + 8;
break;
case 0xe0:
key_value = key_value + 12;
break;
}
while (KEY_MATRIX_PORT != 0xf0)
; // 等待按键松开
}
}
else
key_value = 0;
return key_value;
}
void main()
{
u8 key = 0;
while (1)
{
key = key_matrix_ranks_scan();
if (key != 0)
SMG_A_DP_PORT = gsmg_code[key - 1]; // 得到的按键值减1换算成数组下标对应0-F段码
}
}
编译和结果
按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机
结果:按下S1-S16键,对应数码管最左边显示0-F