STM32F103 GPIO之按键检测
1.直接上代码
#include "stm32f10x.h"
#include "led.h"
#include "key.h"void KeyConfig(void)
{GPIO_InitTypeDef GPIO_InitStructure; //定义结构体RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //打开GPIOB的外设时钟/* 配置结构体并初始化到GPIOB */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //选择需要使用的引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //配置引脚输出模式GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化结构体
}int main (void)
{LedConfig();KeyConfig();while(1){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) == 1) //检测按键是否出现高电平状态{keyDelay(50); //延时去抖if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) == 1) //再次检测GPIO_ResetBits(GPIOC,GPIO_Pin_13); //点亮LED灯}else{GPIO_SetBits(GPIOC,GPIO_Pin_13); //熄灭LED灯}keyDelay(100); //普通延时}
}
2.代码解析
代码的整体思路是,初始化LED灯以及按键检测IO,每隔100ms检测一次按键电平状态是否发生改变。当检测到按键按下时(即检测到高电平时),将LED灯点亮,否则熄灭LED灯。
在初始化代码中,相对比起LED减少了一步-配置IO口的频率(GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;),该代码是针对当IO口需要输出时的引脚输出速率,但本次工程需要使用的是IO的输入状态。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
选择GPIOB_PIN_9引脚作为按键检测的输入引脚。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
定义引脚模式,此处因为引脚需要作用的功能是输入检测,所以需要配置为输入模式,而输入模式在官方固件库代码中有三种模式,
GPIO_Mode_IN_FLOATING;GPIO_Mode_IPD;GPIO_Mode_IPU
第一种模式两种状况,其一是用于 IIC,SPI,UART...等通讯协议中,即数据状态不确定的情况下;其二是当外围硬件已经接有上拉/下拉电阻的情况下。
第二、三种模式一般是用于 引脚需要固定在某一种状态,前者是下拉输入,后者是上拉输入。
本次工程中因为是按键检测,需要引脚长期处于某一种状态,以确保LED灯不会因为引脚的漂浮不定所造成误触发,所以配置为GPIO_Mode_IPD。同时在确定引脚模式之前应该查原理图,检测按键不按下时的电平状态,此时的电平状态即为配置初始状态。
此时引脚PB9硬件外围并没有接上/下拉电阻,引脚处于漂浮状态,而当按键按下时3.3V电压直达PB9,状态改变为高电平,松开后引脚又恢复到漂浮状态,所以此时需要给它一个初始电平,根据原理图理解,该初始电平状态为低电平时,后续引脚电平检测才能检测到电平的改变。
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) == 1) //检测按键是否出现高电平状态
{keyDelay(50); //延时去抖if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) == 1) //再次检测GPIO_ResetBits(GPIOC,GPIO_Pin_13); //点亮LED灯
}
else
{GPIO_SetBits(GPIOC,GPIO_Pin_13); //熄灭LED灯
}
传入形参中,前者为IO口所在的组,后者为具体IO。此语句是用于检测按键是否出现高电平。当出现高电平时,进入下一次检测,用以去除按键抖动,如果下一次检测依然出现高电平,则代表按键正常按下,执行点亮LED灯操作。当检测不到按键按下时熄灭LED灯。实现按键按下时点亮LED灯,松开熄灭LED灯。
3.花样按键
3.1矩阵按键
一个按键一个IO进行检测这种方法只适用于按键少的情况,但当按键的数量达到10+以上时,一IO一按键的状况就不实用了,因为IO口资源对于单片机来说是尤为珍贵的,对于这种情况,就有了一种应对方法——矩阵按键。
矩阵按键就是将N个按键,进行XY平面分布,组成一个阵列,随后分别将每一行每一列的引脚串联起来,让同一列的共用同一个引脚,同一行共用同一引脚,最后大致原理图如下:
我们用到的是3*4的矩阵键盘,根据原理图分析矩阵电路图的连接方式。
- 第一到四行分别连接单片机的PB6~PB9
- 第一到三列分别连接单片机的PA8-PA10
我们将PB6~PB9初始化配置为上拉输入模式,将PA8~PA10配置为推挽输出模式,并将其默认置0。在这种情况下当有按键按下时,引脚会被拉低,松开按键后引脚又会被芯片重新拉高。
举个例子,当按键1被按下时,PB9就会被拉低,此时如果按照51单片机的逻辑,我们只需要再次检测列引脚查看哪个被拉低,就可以确定按键在哪个位置。但在STM32中引脚的输入与输出是分开的,输入引脚的上拉电压在按键按下被接通后,在流入输出引脚后会被芯片内的NMOS对地放掉,所以引脚状态并不会发生改变。关于 I/O端口的基本结构可以看这位博主的文
在这种情况下我们需要将推挽输出的IO进行电平反转,再检测是哪一列,具体代码如下:
引脚初始化代码
void MatrixKeyConfig(void)
{GPIO_InitTypeDef GPIO_InitStructure; //定义结构体RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //打开GPIOA的外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //打开GPIOB的外设时钟/* 配置结构体并初始化到GPIOA */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10; //选择需要使用的引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //配置引脚输出模式GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化结构体GPIO_ResetBits(GPIOA, GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10);/* 配置结构体并初始化到GPIOB */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; //选择需要使用的引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置引脚输出模式GPIO_Init(GPIOB, &GPIO_InitStructure);
}
引脚输入的初始化可以参考上面的按键初始化 ,输出初始化参考前面讲过的LED。
矩阵检测
char MatrixCheck(void)
{uint16_t line;uint16_t keydata = 0;if((GPIO_ReadInputData(GPIOB)>>6) != 0x3ff){keyDelay(1);if((GPIO_ReadInputData(GPIOB)>>6) != 0x3ff){keydata = GPIO_ReadInputData(GPIOB)>>6; //记录当前按键状态line = 0x3ff-keydata; //得到当前被按下行数,8-4 4-3 2-2 1-1 line=0x08时,对应第四行被按下。GPIO_SetBits(GPIOA,GPIO_Pin_8); //IO反转,if((GPIO_ReadInputData(GPIOB)>>6) == keydata) //检测输入是否因为IO反转发生改变。{GPIO_ResetBits(GPIOA,GPIO_Pin_8); GPIO_SetBits(GPIOA,GPIO_Pin_9);if((GPIO_ReadInputData(GPIOB)>>6) == keydata){GPIO_ResetBits(GPIOA,GPIO_Pin_9);GPIO_SetBits(GPIOA,GPIO_Pin_10);if((GPIO_ReadInputData(GPIOB)>>6) == keydata){GPIO_ResetBits(GPIOA,GPIO_Pin_10);}else line |= 0x10; //代表第一列按键被按下}elseline |= 0x20; //代表第二列按键被按下}elseline |= 0x30; //代表第三列按键被按下GPIO_ResetBits(GPIOA, GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10); //重新拉低输出IOswitch(line){case 0x18:return '1';case 0x14:return '4';case 0x12:return '7';case 0x11:return '*';case 0x28:return '2';case 0x24:return '5';case 0x22:return '8';case 0x21:return '0';case 0x38:return '3';case 0x34:return '6';case 0x32:return '9';case 0x31:return '#';default:return 'n';}}}return 'n';
}
第一步检测按键状态是否发生改变,>>6 是为了将第一个按键所在的IO对齐数据的第一位。对于PB端口而言当高于PB5的端口没有被配置时都处于高阻态,所以他们的电平都是高电平,因此PB组IO在>>6后的默认数据为0x03ff。当检测到不为0x03ff时,代表有按键按下。使用keydata来储存按键状态,用0x03ff减去keydata就得到按键所在行数(行数对应8421,8代表在从下往上数第四行,4代表在第三行,以此类推),随后反转输出IO的电平,再检测PB口数据,将得到的数据与IO前的数据进行对比,如果不相等则代表上拉输入IO无法通过输出IO内部NMOS对地放电(有电势差才有电导通),因此可以确定列所在的位置。在完成对列的检测后仍需要对输出IO进行一个电平拉低。
上面我们得到一个完成的行列数据,随后将line参数传入switch进行一个选择,这完成了一整个矩阵按键的代码编写。
矩阵按键的具体应用
void MatrixUse(void)
{char keyData;keyData = MatrixCheck2();if(keyData == '1')GPIO_Write(GPIOA,GPIO_Pin_7); //点亮第一个LED灯else if(keyData == '2') GPIO_Write(GPIOA,GPIO_Pin_6); //点亮第二个LED灯else if(keyData == '3') GPIO_Write(GPIOA,GPIO_Pin_5); //点亮第三个LED灯else if(keyData == '4') GPIO_Write(GPIOA,GPIO_Pin_4); //点亮第四个LED灯else if(keyData == '0')GPIO_Write(GPIOA,0);
}
通过矩阵按键检测函数的返回值进行对应自定义操作,这里以点灯为例子。
3.2 矩阵按键在项目应用注意事项
在我们的具体项目应用中,引脚的电平状态不可能总是0x03ff,因此就必须要考虑只需要获得对应使用那一组引脚的数据,原理图中使用的是PB6-PB9,在二进制表中对应的位就是
0000 0011 1100 0000
将其转化为16进制数就是 0x03c0,得到这个数据由什么用呢,上面说到我们需要屏蔽其他位,只留下我们需要的位,此时我们只需要将GPIO_ReadInputData(GPIOB)函数得到的IO数据和0x03c0进行一个位与操作,就可以达到我们想要的效果,'&'运算符的特性是对0敏感,即有0必0。因此我们需要将3.1的代码进行一个修改。
char MatrixCheck2(void)
{uint16_t line;uint16_t keyDefault = 0x03c0; //需要用到引脚的所在位号uint16_t keydata = (GPIO_ReadInputData(GPIOB) & keyDefault); //此处获取一次的目的是DEBUG模式查看数值if((GPIO_ReadInputData(GPIOB) & keyDefault)!= keyDefault){keyDelay(1); //滤波if((GPIO_ReadInputData(GPIOB) & keyDefault)!= keyDefault){keydata = (GPIO_ReadInputData(GPIOB) & keyDefault); //记录当前按键状态line = (keyDefault-keydata)>>6; //得到当前被按下行数,">>6"将数据进行低位对齐 8-4 4-3 2-2 1-1 line=0x08时,对应第四行被按下。GPIO_SetBits(GPIOA,GPIO_Pin_8); //IO反转if((GPIO_ReadInputData(GPIOB) & keyDefault) == keydata){GPIO_ResetBits(GPIOA,GPIO_Pin_8);GPIO_SetBits(GPIOA,GPIO_Pin_9);if((GPIO_ReadInputData(GPIOB) & keyDefault) == keydata){GPIO_ResetBits(GPIOA,GPIO_Pin_9);GPIO_SetBits(GPIOA,GPIO_Pin_10);if((GPIO_ReadInputData(GPIOB) & keyDefault) == keydata){GPIO_ResetBits(GPIOA,GPIO_Pin_10);}else line |= 0x10; //代表第一列按键被按下}elseline |= 0x20; //代表第二列按键被按下}elseline |= 0x30; //代表第三列按键被按下GPIO_ResetBits(GPIOA, GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10); //重新拉低输出IOswitch(line){case 0x18:return '1';case 0x14:return '4';case 0x12:return '7';case 0x11:return '*';case 0x28:return '2';case 0x24:return '5';case 0x22:return '8';case 0x21:return '0';case 0x38:return '3';case 0x34:return '6';case 0x32:return '9';case 0x31:return '#';default:return 'n';}}}return 'n';
}
代码的大体思路与3.1所述的一样,(GPIO_ReadInputData(GPIOB) & keyDefault) == keydata 这局代码是为了在去除其他IO口干扰后再进行数据的比较。
3.3密码锁
密码锁的大致原理是,通过矩阵按键将输入的数据存入数组中,随后进行密码的自动校验,校验的方法使用strcmp() 函数。代码如下
int main(void)
{/*模块初始化*///GPIOinit...uint16_t keys_scan_count = 0; //按键输入次数char keys; //密码储存临时区域char keys_data[KeyWords_MAX]={0}; //密码储存数组 KeyWords_MAX为5char *openkeys = "13258"; //密码while(1){keys = MatrixCheck2();if(keys != 'n') //检测不为非法输入{if(keys == '#') //此处 # 用作清空输入{memset(keys_data,0,strlen(keys_data)); //清空数组GPIO_Write(GPIOA,0x00); //关闭数码管GPIO_SetBits(GPIOC,GPIO_Pin_13); //关闭LEDkeys_scan_count = 0; //计数清0continue;}GPIO_Write(GPIOA,regLed[keys-48]); //数码管显示当前输入数,字符串1对应的ASCII为48,所以强转HEX需要-48keys_data[keys_scan_count] = keys; //储存输入密码if(strcmp(keys_data,openkeys) == 0) GPIO_ResetBits(GPIOC,GPIO_Pin_13); //点亮LED灯keys_scan_count++; //输入次数+1if(keys_scan_count >=strlen(openkeys))keys_scan_count = 0; //大于最大密码数清0}keyDelay(100);}
}
从代码角度来理解就是,keys获取按键输入,随后进行字符检测,因为当无输入时MatrixCheck2()的返回值是'n',当不为'n'时代表有按键按下,进入下一步对键值检测是否为特殊字符'#',若是则清空输入数组,否则储存输入键值。
按键检测 提取码:8866
往期内容
STM32F103基于固件库创建工程模板
STM32F103 GPIO之点亮一个LED灯
STM32F103 GPIO之按键检测相关推荐
- linux 按键检测 防抖,GPIO输入——按键检测
当按下一个按键时,系统是如何检测到的呢? 我们通过LED灯的亮灭状态来间接完成按键检测.当按下按键时,LED灯亮,再次按下时,LED灯灭. 要完成这个实验,我们就会用到GPIO外设的基本输入功能. 查 ...
- 【STM32】GPIO输入—按键检测
Author:AXYZdong 自动化专业 工科男 有一点思考,有一点想法,有一点理性 文章目录 2.1硬件设计 2.2软件设计 2.2.1编程要点 2.2.2代码分析 1.按键引脚宏定义 2.按键 ...
- 11、GPIO输入—按键检测
文章目录 0.前言 1.硬件设计 2.软件设计 2.1.编程要点 2.2.代码分析 2.2.1.按键引脚宏定义 2.2.2.按键 GPIO 初始化函数 2.2.3.检测按键的状态 2.2.4.主函数 ...
- STM32——GPIO输入——按键检测
硬件介绍 当按键置空时,IO接地 按键按下之后,IO口接通3.3V高电压,电流比较大,为了避免损坏IO,这里需要加装一个限流电阻.可以看到IO口是默认低电平,按键按下后产生一个上升沿,和平常的电路设计 ...
- stm32实现GPIO输入按键检测
1.硬件设计 按键机械触点断开.闭合时,由于按键触点的弹性作用,按键开关不会马上稳定接通或一下就断开,使用按键时就会产生下图中的带纹波信号,需要软件消抖处理滤波 由于用软件消抖处理滤波不方便输入检测, ...
- GPIO 输入—按键检测
这里要用到一定的模电知识.电容两端电压不能突变,电感两端电流不能突变.这里利用了电容的放电延时实现硬件消抖.按键按下会有抖动,波形有毛刺,使得高低电平显现不明显,而按键按下时,电容放电一下,马上又被充 ...
- STC8H1K08 - GPIO 按键检测
文章目录 不使用硬件或软件消抖的按键检测 原理图 Keil 工程结构 源文件 参考 STC8H 系列单片机所有的 I/O 口均有 4 种工作模式:准双向口/弱上拉(标准 8051 输出口模式).推挽输 ...
- Proteus仿真STM32F103R6微控制器的GPIO(按键控制LED开关)
Proteus仿真STM32F103R6微控制器的GPIO,检查按键,控制LED灯的反转.. 输入:按键检测:输出:高低电平,控制LED. 一.原理图: 二.源码: #include "st ...
- Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用
首先看一下Openwrt系统中关于按键功能的使用和修改,以18.06版本为例 按键功能实现在脚本中, 比如18.06/package/base-files/files/etc/rc.button/re ...
最新文章
- 阿里达摩院已经研发出第一个可控的量子比特
- Terraform入门 - 3. 变更基础设施
- 驱动保护中的ObjectType_Callback探索
- C#编写简单的聊天程序
- mongodb 排序_技术分享 | MongoDB 一次排序超过内存限制的排查
- 陈艳青(为奥运冠军名字作诗)
- android改包名
- 设计模式(Design pattern—
- shell 数组详解
- MongoDB 在windows shell环境下的基本操作和命令的使用示例(一)
- Centos7下载linux内核源码
- html图片不要角,详细解说CSS折角的制作(不用图片)
- Cesium结合kriging.js制作降雨等值面
- python三行代码抠图_Python用5行代码如何实现批量抠图 Python用5行代码实现批量抠图方法...
- 真的是没有底线了,重新认识Java
- 萌宠历险记html5游戏在线玩,7724萌宠历险记
- 拥有WiFi 6+的华为路由 AX 3 Pro 到底香不香
- C语言或less或sass中,ceil floor 无法传入动态变量取整的办法
- 盘一盘那些开设了大数据专业的中国高校
- 《MySQL高级篇》三、存储引擎