基于STM32F1的16键矩阵按键扫描程序及注解

设计思路来自这位博主的博客:

链接: https://blog.csdn.net/wjfdsklfdkfksd/article/details/82085455

由于本人最近要用到矩阵按键了,之前在dsp上有个扫描程序挺好用的,想移植到stm32上没弄成,然后在网上找了好久也没找到简单易懂点的程序,各位大佬写的扫描程序真是让我有点头大,直到看到了这位博主的程序,跟我之前的程序思想类似,这里就结合我的经验改了改,也顺便把原理讲得更清楚一点,方便没用过的同学理解吧。

使用平台:stm32f1
优点:程序思想简单可靠,占用资源仅有io口,扫描耗时约60ms(跟防抖延时有关),响应速度快
缺点:使用固定的io口接矩阵按键,比如GPIOA0-7 ,若要修改io改程序比较麻烦
接线:
PA0-PA3对应接R4-R1
PA4-PA7对应接C1-C4
使用方法:无需初始化,调用函数KEYPAD_Scan()即可返回一个数值。

一,注意事项:
1.无按下时返回0。
2.请务必注意KEYPAD_Scan()声明的返回值num是用static定义的,也就是说,你按下一次后,num的内存不会释放,比如说,你先按下了按键4,那么后面你若没按其它按键,再调用KEYPAD_Scan()函数时依然返回的是4。如果不想要这样把static直接删除即可。(我就栽在这里了,找了半天才发现是static在作怪)
3.无松手检测(我觉得没啥用就删了)

矩阵按键就是这个:
基于STM32F1的16键矩阵按键扫描程序及注解_第1张图片
这是原理图:
(后面发现这个图上应该是S1,S2,S3,S4在第一行才对)
基于STM32F1的16键矩阵按键扫描程序及注解_第2张图片
二,扫描思想:
1.将高4位引脚下拉输入,低4位引脚上拉输入,这时若无按键按下读取PA7-PA0的电平应该是0x0F,假如是PA7对应的这一列有按键按下,那读取的电平应该是0x8F,如此,我们就确定了是哪一列有按键按下。这时低4位的值是没有意义的,把它置为0,保留PA4-PA7的值。

res1 = GPIO_ReadInputData(GPIOA);	//再次读取状态
res1 &= 0x00f0;  				//保留PA4-PA7的值

2.反过来将高4位引脚上拉输入,低4位引脚下拉输入,这时若无按键按下读取PA7-PA0的电平应该是0xF0,假如是PA0对应的这一行有按键按下,那读取的电平应该是0xF1,如此,我们就确定了是哪一行有按键按下。这时高4位的值是没有意义的,把它置为0,保留PA0-PA3的值。

res2 = GPIO_ReadInputData(GPIOA);	//再次读取状态
res2 &= 0x000f;				//保留PA0-PA3的值

3.将两次读取的电平做一个或运算,得到类似0x88这样的数值,就可以判断是哪一行哪一列被按下了。

res=res1|res2;  //相与,就知道哪一行哪一列被按下啦

4.对应到按键上是这样的:
基于STM32F1的16键矩阵按键扫描程序及注解_第3张图片
我在想可能是原理图画反了吗,原理图应该横着过去是s1,s2,s3,s4,结果与我算出来的值是对称的,但是接在实物上按我的程序来接线和使用是没问题的,已经验证。

三,程序:
button.h程序:

#include "delay.h"
#include "sys.h"
#ifndef BUTTON_H_
#define BUTTON_H_

void KEYPad_Init1(void);
void KEYPad_Init2(void);
u16 KEYPAD_Scan(void);



#endif /* BUTTON_H_ */

button.c程序:

#include "button.h"
#include "stm32f10x.h"
#include "delay.h"
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
 
/*本文件是矩阵按键功能文件,使用方法是:直接调KEYPAD_Scan函数得到一个返回值*/
 
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
 
//函数名:KEYPad_Init1
//参数:无
//功能:行检测初始化(低4位引脚上拉输入,高4位引脚下拉输入)
//返回值;无
void KEYPad_Init1(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	//上拉输入 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;	//下拉输入 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}
 
//函数名:KEYPad_Init2
//参数:无
//功能:列检测初始化(低4位引脚下拉输入,高4位引脚上拉输入)
//返回值:无
void KEYPad_Init2(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;	//下拉输入 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	//上拉输入 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}
 
//函数名:扫描函数
//入口参数:无
//返回值:键值
//功能:矩阵按键扫描,返回一个键值
u16 KEYPAD_Scan()
{
	static u16 num = 0; //保持按键值返回
	
	u16 readvalue = 0;
	u16 res=0;
	u16 res1=0;
	u16 res2=0;
	KEYPad_Init1();					//低4位引脚上拉输入,高4位引脚下拉输入
	readvalue = GPIO_ReadInputData(GPIOA);	//读GPIOA引脚电平
	readvalue &= 0x00ff;				//保留低8位的值(PA7-PA0)
	if(readvalue != 0x000f) 				//高4位引脚有一个被按下
	{
		delay_ms(10);//消抖10ms
		if(readvalue != 0x000f)
		{
			res1 = GPIO_ReadInputData(GPIOA);	//再次读取状态
			res1 &= 0x00f0;  				//保留PA4-PA7的值

			KEYPad_Init2();  				//低4位引脚下拉输入,高4位引脚上拉输入
			delay_ms(50);//经我测试,这里延迟50ms反应最快而很少出现不反应的状况
			res2 = GPIO_ReadInputData(GPIOA);	//再次读取状态
			res2 &= 0x000f;				//保留PA0-PA3的值
			
			res=res1|res2;					//相与,就知道哪一行哪一列被按下啦
			
			switch(res)
			{
			case 0x0011: num = 13;break;  
			case 0x0012: num = 9;break;  
			case 0x0014: num = 5;break; 
			case 0x0018: num = 1;break;  
			case 0x0021: num = 14;break;  
			case 0x0022: num = 10;break; 
			case 0x0024: num = 6;break;  
			case 0x0028: num = 2;break;  
			case 0x0041: num = 15;break;  
			case 0x0042: num = 11;break;  
			case 0x0044: num = 7;break;  
			case 0x0048: num = 3;break;  
			case 0x0081: num = 16;break;  
			case 0x0082: num = 12;break;  
			case 0x0084: num = 8;break;  
			case 0x0088: num = 4;break;  
			}

		}
	}
	return num;
}

其实仔细看程序还是挺简单的,如果有问题欢迎在评论区我们可以讨论,如有错误,请斧正。

你可能感兴趣的:(stm32学习)