【普中 51-Ai8051 开发攻略】-- 第 10 章 矩阵按键实验

(1)实验平台:

普中 51-Ai8051 开发板https://item.taobao.com/item.htm?abbucket=17&id=1026052331067(2)资料下载 :普中科技-各型号产品资料下载链接


矩阵键盘是一种广泛应用于电子设备的人机交互组件, 它通过排列成矩阵形式的按键来接收用户的输入。 本章分为如下几部分内容:

[10.1 实验介绍](#10.1 实验介绍)

[10.1.1 实验简介](#10.1.1 实验简介)

[10.1.2 实验目的](#10.1.2 实验目的)

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

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

[10.3.1 创建和配置工程](#10.3.1 创建和配置工程)

[10.3.1.1 GPIO 配置](#10.3.1.1 GPIO 配置)

[10.3.1.2 生成工程](#10.3.1.2 生成工程)

[10.3.2 添加用户驱动代码](#10.3.2 添加用户驱动代码)

[10.3.2.1 key.h 文件](#10.3.2.1 key.h 文件)

[10.3.2.2 key.c 文件](#10.3.2.2 key.c 文件)

[10.3.2.3 main.c 文件](#10.3.2.3 main.c 文件)

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


10.1 实验介绍

10.1.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 口送出低电平。 检测方法有多种, 本章使用类似独立按键的操作方式, 简单直接。

检测方法如下: 配置行为输出模式, 列为上拉输入模式, 对第一行输出低,其它行输出高, 判断列对应管脚低电平状态, 从而记录第一行对应的哪列按键按下。 其它行和列操作依此类推。 矩阵键盘也少不了按键消抖的环节。

矩阵键盘示意图如下所示:

10.1.2 实验目的

S1-S16 键按下, 数码管显示 0-F。

10.2 硬件设计

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

(1) GPIO

(2) 数码管模块

(3) 4*4 矩阵键盘模块

相关电路在前面章节已经介绍过, 此处省略。 矩阵键盘模块电路如下所示:

由图可知, 矩阵按键 H1-H4 连接 P10-P13, L1-L4 接 P14-P17。

10.3 软件设计

10.3.1 创建和配置工程

按照前面章节内容创建一份新工程, 并命名为 06-matrix_key, 如下图所示:

10.3.1.1 GPIO 配置

使能端口和时钟, 矩阵按键 L1-L4 对应 IO 设置为推挽输出模式, 使能上拉电阻; H1-H4 对应 IO 设置为输入模式, 使能上拉电阻; 数码管相关配置参考前面实验章节内容, 如下图所示:

10.3.1.2 生成工程

配置完成后, 按下代码生成按钮, 自动创建工程, 系统开始生成初始化代码。生成工程文件目录如下图所示:

在工程文件夹内新建一个 APP 文件夹, 用于存储用户编写的外设驱动, 方便后期工程管理和程序移植。 在 APP 文件夹下新建 key 文件夹, 并在该文件夹内新建 key.c 和 key.h 文件, 用于保存按键相关驱动, 这个在前面按键控制实验已经创建好, 这里直接复制过来追加矩阵按键驱动。 如下所示:

然后在导出的工程中添加 APP 组, 并将 key.c 和 smg.c 导入到工程组内, 最后添加头文件路径, 否则编译将报错。 如下:

添加完以后, 如下图所示:

10.3.2 添加用户驱动代码

10.3.2.1 key.h 文件

代码如下:

cpp 复制代码
#ifndef _key_H
#define _key_H

#include "config.h"

//管脚定义
#define KEY1     P32
#define KEY2     P33
#define KEY3     P34
#define KEY4     P35

//定义各个按键值 
#define KEY1_PRESS 		1
#define KEY2_PRESS		2
#define KEY3_PRESS		3
#define KEY4_PRESS		4



//管脚定义
#define KEY_MATRIX_PORT     P1


//函数声明
u8 KEY_Scan(u8 mode);
u8 key_matrix_ranks_scan(void);

#endif

该文件是在前面按键控制实验基础上增加了矩阵按键管脚定义和函数声明。

10.3.2.2 key.c 文件

代码如下:

cpp 复制代码
#include "key.h"

/*******************************************************************************
* 函 数 名         : KEY_Scan
* 函数功能		   : 按键扫描检测
* 输    入         : mode=0:单次按下按键
					 mode=1:连续按下按键
* 输    出         : 0:未有按键按下
					 KEY1_PRESS:KEY1键按下
					 KEY2_PRESS:KEY2键按下
					 KEY3_PRESS:KEY3键按下
					 KEY4_PRESS:KEY4键按下
*******************************************************************************/
u8 KEY_Scan(u8 mode)
{
	static u8 key=1;
	
	if(mode==1) //连续按键按下
		key=1;
	if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)) //任意一个按键按下
	{
		delay_ms(10);  //消抖
		key=0;
		if(KEY1==0)
			return KEY1_PRESS; 
		else if(KEY2==0)
			return KEY2_PRESS; 
		else if(KEY3==0)
			return KEY3_PRESS; 
		else if(KEY4==0)
			return KEY4_PRESS; 
	}
	else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)    //无按键按下
		key=1;
	return 0;
}



/*******************************************************************************
* 函 数 名       : key_matrix_ranks_scan
* 函数功能		 : 使用行列式扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* 输    入       : 无
* 输    出    	 : key_value:1-16,对应S1-S16键,
				   0:按键未按下
*******************************************************************************/
u8 key_matrix_ranks_scan(void)
{
	u8 key_value=0;

	KEY_MATRIX_PORT=0xef;//给第一列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xef)//判断第一列按键是否按下
	{
		delay_ms(5);//消抖
		switch(KEY_MATRIX_PORT)//保存第一列按键按下后的键值	
		{
			case 0xee: key_value=1;break;
			case 0xed: key_value=5;break;
			case 0xeb: key_value=9;break;
			case 0xe7: key_value=13;break;
		}
	}
	while(KEY_MATRIX_PORT!=0xef);//等待按键松开	
	
	KEY_MATRIX_PORT=0xdf;//给第二列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xdf)//判断第二列按键是否按下
	{
		delay_ms(5);//消抖
		switch(KEY_MATRIX_PORT)//保存第二列按键按下后的键值	
		{
			case 0xde: key_value=2;break;
			case 0xdd: key_value=6;break;
			case 0xdb: key_value=10;break;
			case 0xd7: key_value=14;break;
		}
	}
	while(KEY_MATRIX_PORT!=0xdf);//等待按键松开	
	
	KEY_MATRIX_PORT=0xbf;//给第三列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xbf)//判断第三列按键是否按下
	{
		delay_ms(5);//消抖
		switch(KEY_MATRIX_PORT)//保存第三列按键按下后的键值	
		{
			case 0xbe: key_value=3;break;
			case 0xbd: key_value=7;break;
			case 0xbb: key_value=11;break;
			case 0xb7: key_value=15;break;
		}
	}
	while(KEY_MATRIX_PORT!=0xbf);//等待按键松开	
	
	KEY_MATRIX_PORT=0x7f;//给第四列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0x7f)//判断第四列按键是否按下
	{
		delay_ms(5);//消抖
		switch(KEY_MATRIX_PORT)//保存第四列按键按下后的键值	
		{
			case 0x7e: key_value=4;break;
			case 0x7d: key_value=8;break;
			case 0x7b: key_value=12;break;
			case 0x77: key_value=16;break;
		}
	}
	while(KEY_MATRIX_PORT!=0x7f);//等待按键松开
	
	return key_value;		
}

key_matrix_ranks_scan 函数: 为矩阵按键扫描函数, 是按照前面介绍的检测原理实现, 对第一列输出低, 其它列输出高, 判断行对应管脚低电平状态, 从而记录第一列对应的哪行按键按下。 其它行和列操作依此类推。 若有按键按下,则返回对应键值, 此处键值是我们自定义, 当然用户也可更改。 S1-S16 对应的键值为数字 1-16, 没有按下则返回 0。

10.3.2.3 main.c 文件

cpp 复制代码
//<<AICUBE_USER_HEADER_REMARK_BEGIN>>
/* 深圳市普中科技有限公司(PRECHIN 普中)
 * 在线视频:https://space.bilibili.com/2146492485/video
       官网:www.prechin.cn

 * 实验名称:矩阵按键实验
 * 
 * 接线说明:参考电路图
 * 
 * 实验现象:程序下载成功后,S1-S16键按下,数码管显示0-F
 * 
 * 注意事项:
 * 
 */
//<<AICUBE_USER_HEADER_REMARK_END>>


#include "config.h"                     //默认已包含stdio.h、intrins.h、ai_usb.h等头文件


//<<AICUBE_USER_INCLUDE_BEGIN>>
// 在此添加用户头文件包含 
#include "key.h"
#include "smg.h"
//<<AICUBE_USER_INCLUDE_END>>


//<<AICUBE_USER_GLOBAL_DEFINE_BEGIN>>
// 在此添加用户全局变量定义、用户宏定义以及函数声明  
//<<AICUBE_USER_GLOBAL_DEFINE_END>>



////////////////////////////////////////
// 项目主函数
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void main(void)
{
    //<<AICUBE_USER_MAIN_INITIAL_BEGIN>>
    // 在此添加用户主函数初始化代码 
	u8 key=0;
	u8 keynum=0;	
    //<<AICUBE_USER_MAIN_INITIAL_END>>

    SYS_Init();

    //<<AICUBE_USER_MAIN_CODE_BEGIN>>
    // 在此添加主函数中运行一次的用户代码  
    //<<AICUBE_USER_MAIN_CODE_END>>

    while (1)
    {
        //<<AICUBE_USER_MAIN_LOOP_BEGIN>>
        // 在此添加主函数中用户主循环代码
		key=key_matrix_ranks_scan();
		if(key!=0)keynum=gsmg_code[key-1];
		SMG_Display(&keynum,8);	
        //<<AICUBE_USER_MAIN_LOOP_END>>
    }
}

主函数实现的功能比较简单, 首先将使用到的硬件初始化。 然后在 while 循环内调用矩阵按键扫描函数, 如果有按键按下 key 值不为 0, 则控制数码管显示对应值。

10.4 实验现象

将程序编译下载到目标板运行, 实验现象: S1-S16 键按下, 数码管显示 0-F。如下所示:

相关推荐
努力的小帅2 小时前
蓝桥杯——入门
c语言·单片机·蓝桥杯
歪歪歪比巴卜2 小时前
2026年AI新媒体运营工具怎么选?核心功能与适用场景解析
大数据·矩阵·新媒体运营
意法半导体STM323 小时前
【官方原创】STM32 USBx Host HID standardalone移植示例 LAT1449
开发语言·前端·stm32·单片机·嵌入式硬件
辰哥单片机设计3 小时前
STM32项目分享:空气质量检测系统(机智云)
stm32·单片机·嵌入式硬件
m0_743106464 小时前
【3D硬核】四元数(Quaternions)与旋转矩阵(Rotation)——三维空间中的旋转
人工智能·计算机视觉·3d·矩阵·几何学
云栖梦泽4 小时前
Linux内核与驱动:12.设备树实例分析
linux·c++·单片机
一月千帆4 小时前
基于STM32的智能小型洗碗机控制系统设计
stm32·单片机·嵌入式硬件
cmpxr_4 小时前
【算法】ECC验签名
单片机·算法
别或许4 小时前
线代中为什么左乘一个列满秩矩阵,不改变矩阵的秩?
人工智能·算法·矩阵