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

mac2026-02-06  0

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

链接: 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.无松手检测(我觉得没啥用就删了)

矩阵按键就是这个: 这是原理图: (后面发现这个图上应该是S1,S2,S3,S4在第一行才对) 二,扫描思想: 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.对应到按键上是这样的: 我在想可能是原理图画反了吗,原理图应该横着过去是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; }

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

最新回复(0)