【普中51单片机开发攻略--基于普中-2&普中-3&普中-4】-- 第 14 章 矩阵按键实验

(1)实验平台:普中51单片机开发板-A2&A3&A4


上一章我们介绍了独立按键的构成以及使用方法, 本章我们介绍按键电路的另一种模型: 矩阵按键。 开发板上板载了一个 4*4 矩阵键盘。 本章所要实现的功能是: 通过开发板上的矩阵键盘控制静态数码管显示对应的键值 0-F。 学习本章可以参考前面的实验章节内容。 本章分为如下几部分内容:

[14.1 矩阵按键介绍](#14.1 矩阵按键介绍)

[14.2 硬件设计](#14.2 硬件设计)

[14.3 软件设计](#14.3 软件设计)

[14.4 实验现象](#14.4 实验现象)


14.1 矩阵按键介绍

独立按键与单片机连接时, 每一个按键都需要单片机的一个 I/O 口, 若某单片机系统需较多按键, 如果用独立按键便会占用过多的 I/O 口资源。 单片机系统中 I/O 口资源往往比较宝贵, 当用到多个按键时为了减少 I/O 口引脚, 引入了矩阵按键。

本章以 4*4 矩阵键盘为例讲解其工作原理和检测方法。 开发板上将 16 个按键排成 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 口送出低电平。 检测方法有多种, 最常用的是行列扫描和线翻转法。

行列扫描法检测时, 先送一列为低电平, 其余几列全为高电平(此时我们确定了列数), 然后立即轮流检测一次各行是否有低电平, 若检测到某一行为低电平(这时我们又确定了行数), 则我们便可确认当前被按下的键是哪一行哪一列的, 用同样方法轮流送各列一次低电平, 再轮流检测一次各行是否变为低电平,这样即可检测完所有的按键, 当有键被按下时便可判断出按下的键是哪一个键。当然我们也可以将行线置低电平, 扫描列是否有低电平。 从而达到整个键盘的检测。

线翻转法, 就是使所有行线为低电平时, 检测所有列线是否有低电平, 如果有, 就记录列线值; 然后再翻转, 使所有列线都为低电平, 检测所有行线的值,由于有按键按下, 行线的值也会有变化, 记录行线的值。 从而就可以检测到全部按键。

矩阵键盘也少不了按键消抖的环节, 本章实验中采用的是行列扫描法来检测哪个按键按下。

14.2 硬件设计

本实验使用到硬件资源如下:

(1) 静态数码管

(2) 4*4 矩阵按键

静态数码管模块电路在前面章节都介绍过, 这里就不多说, 开发板上的矩阵按键模块电路如下图所示:

从上图中可以看出, 4*4 矩阵按键引出的 8 根控制线直接连接到 51 单片机的P1 口上。 电路中的 P17 连接矩阵键盘的第 1 行, P13 连接矩阵键盘第 1 列。

14.3 软件设计

本章所要实现的功能是: 通过数码管显示矩阵按键 S1-S16 按下后键值 0-F。

我们打开"\4--实验程序\1--基础实验\9-矩阵按键实验" 工程, 控制代码全部都在 main.c 中, 代码如下:

cpp 复制代码
/**************************************************************************************
深圳市普中科技有限公司(PRECHIN 普中)
技术支持:www.prechin.net
PRECHIN
 普中

实验名称:矩阵按键实验
接线说明:	
实验现象:下载程序后,按下"矩阵按键"模块中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};	

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

/*******************************************************************************
* 函 数 名       : key_matrix_ranks_scan
* 函数功能		 : 使用行列式扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* 输    入       : 无
* 输    出    	 : key_value:1-16,对应S1-S16键,
				   0:按键未按下
*******************************************************************************/
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;		
}

/*******************************************************************************
* 函 数 名       : key_matrix_flip_scan
* 函数功能		 : 使用线翻转扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* 输    入       : 无
* 输    出    	 : key_value:1-16,对应S1-S16键,
				   0:按键未按下
*******************************************************************************/
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;		
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
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段码		
	}		
}

本实验核心代码为 key_matrix_ranks_scan 函数和 key_matrix_flip_scan函数, 前者是使用行列式扫描方式实现, 而后者是使用线翻转式扫描方式实现,实现功能一致, 二者可选其一。 对于初学者, 可能行列式扫描方式更易于理解,因为比较接近独立按键的编程方式。

行列式扫描原理比较简单, 与独立式按键操作类似, 即给每一列赋值 0, 此时的矩阵按键就被分割成独立按键, 然后再判断每一列中的按键按下情况, 并返回对应的键值。 如此循环 4 组, 就可将 4 列 4 行按键按下键值全部得到。

而线翻转式扫描相对较难理解, 不过静下心, 在纸上画画, 列举几个数据也是比较容易理解的。

14.4 实验现象

使用 USB 线将开发板和电脑连接成功后(电脑能识别开发板上 CH340 串口) ,把编译后产生的.hex 文件烧入到芯片内, 实现现象如下: 当按下 S1-S16 键, 最左边数码管对应显示 0-F。

相关推荐
搞机械的假程序猿2 小时前
普中51单片机学习笔记-LCD1602液晶显示
笔记·学习·51单片机
Mapple.2 小时前
TMS320C6748的初始化与STM32做个类比
stm32·单片机·嵌入式硬件
俊俊谢2 小时前
华大HC32F460轮询方式SPI通讯配置
单片机·嵌入式硬件·spi·hc32f460
兆龙电子单片机设计3 小时前
【STM32项目开源】STM32单片机智能家居安防控制系统
stm32·单片机·开源·毕业设计·电子信息
听情歌落俗3 小时前
MPU6050
单片机·嵌入式硬件
d111111111d3 小时前
STM32定时器中断配置详解:以TIM2为例
笔记·stm32·单片机·嵌入式硬件·学习
Q_21932764553 小时前
基于51单片机的智能家居防火防盗报警系统设计
嵌入式硬件·51单片机·智能家居
QK_003 小时前
STM32--USART(串口)
stm32·单片机·嵌入式硬件
jencepan3 小时前
纳安级功耗,5.5V/2A,23-6封装,单节锂电升压DCDC方案,晶艺LA2112N
单片机·嵌入式硬件