按键扫描的两种模式
1.支持连续扫描模式
连续按下时,会认为有多个数值,比如我们的遥控板,一直按下则频道会一直增加。
2.不支持连续模式
连续按下时,仅认为只有一个数值,比如我们的电源按键,长时间的按下并不会一直有效,从而频繁的开关机,就算按的时间很长,也只会进行一次。
只有在前一时刻的状态与这个时候的状态不想同时,会记一个数值。比如之前是高电平,现在是低电平就记一次,一直是低电平,则不继续计数进行。
如何用C语言处理是否是连续模式?
关键字Static
Static声明的局部变量是具有记忆功能的。
例1:
结果:1,1,1…
例2
static关键字具有存储记忆功能,能存入返回的上一时刻flag的值。
此时的flag++是在上一时刻的flag的基础上进行加1。
结果:1,2,3…(具有记忆功能)
按键扫描,(不支持连续按下)的一般思路
- u8 KEY_Scan(void)
- {
- static u8 key_up=1;
- if(key_up && KEY按下)//若key_up=1时,1&&KEY=KEY;若key_up=0时,0&&KEY=0;
- {
- delay_ms(10);//延时,防抖
- key_up=0;//标记这次key已经按下
- if(KEY确实按下)
- {
- return KEY_VALUE;
- }
- }
- else if(KEY没有按下) key_up=1;
- }
复制代码
按键扫描(两种模式合二为一)的一般思路
多了一个mode模式,mode=1,支持连续按下;mode=0,不支持连续按下。
- u8 KEY_Scan(u8 mode)
- {
- static u8 key_up=1;//默认刚开始为高电平
- if(mode==1) key_up=1;//mode=1,支持连续按,key-up永远等于1;mode=0,则此行代码无用,不支持连续按;
- if(key_up && KEY按下)
- {
- delay_ms(10);//延时,防抖
- key_up=0;//标记这次key已经按下
- if(KEY确实按下)
- {
- return KEY_VALUE;//识别到按下,返回KEY的真实值
- }
- }
- else if(KEY没有按下) key_up=1;//表示没有扫描到按下,则返回 key_up=1
- return 没有按下
- }
复制代码
打开我们的按键实验工程可以看到,我们引入了
key.c文件以及头文件 key.h。下面我们首
先打开 key.c文件, 关键 代码如下:
- #include "key.h" #include "delay.h" //按键初始化函数
- void KEY_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE, ENABLE);//使能 GPIOA,GPIOE时钟
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //KEY0 KEY1 KEY2对应引脚
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化 GPIOE2,3,4 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//WK_UP对应引脚 PA0 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 GPIOA0 } //按键处理函数
- //返回按键值
- //mode:0,不支持连续按 ;1,支持连续按 ; //0,没有任何按键按下
- //1 KEY0按下 2 KEY1按下 3 KEY2按下 4 WKUP按下 WK_UP //注意此函数有响应优先级 ,KEY0>KEY1>KEY2>WK_UP!!
- u8 KEY_Scan(u8 mode)
- {
- static u8 key_up=1;//按键按松开标志
- if(mode)key_up=1; //支持连按
- if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1)) { delay_ms(10);//去抖动
- key_up=0;
- if(KEY0==0)return 1;
- else if(KEY1==0)return 2;
- else if(KEY2==0)return 3;
- else if(WK_UP==1)return 4;
- }
- else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
- return 0;// 无按键按下
- }
复制代码
这段代码包含 2个函数, void KEY_Init(void)和 u8 KEY_Scan(u8 KEY_Init是用来初始化按键输入的 IO口的。实现 PA0、 PE2~4的输入设置,这里和第六章 的输出配置 差不多只是这里用来设置成的是输入而 第六章 是输出 。
KEY_Scan函数,则是用来扫描这 4个 IO口是否有按键按下。 KEY_Scan函数, 支持两种扫描方式,通过 mode参数来设置。当mode为 0的时候, KEY_Scan函数将不支持连续按, 扫描某个按键,该按键按下之后必须要松开,才能第二次触发,否则不会再响应这个按键,这样的好处就是可以防止按一次多次触发,而坏处就是在需要长按的时候比较不合适。
当mode为 1的时候, KEY_Scan函数将支持连续按,如果某个按键一直按下,则会 一直返回这个按键的键值,这样可以方便的实现长按检测。
有了mode这个参数,大家就可以根据自己的需要,选择不同的方式。这里要提醒大家,因为该函数里面有 static变量,所以该函数不是一个可重入函数,在有 OS的情况下,这个大家要留意下。 同时还有一点要注意的就是,该函数的按键扫描是有优先级的,最优先的是 KEY0,第二优先的是 KEY1 接着 KEY2 最后是 KEY3 KEY3对应 KEY_UP按键) 。该函数有返回值,如果有按键按下,则返回非 0值,如果没有或者按键不正确,则返回 0。接下来我们看看头文件key.h里面的代码:
- #ifndef __KEY_H
- #define __KEY_H
- #include "sys.h" /*下面的方式是通过直接操作库函数方式读取 IO*/
- #define KEY0
- GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
- #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) //PE3
- #define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) //PE2
- #define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PA0
- #define KEY0_PRES 1
- #define KEY1_PRES 2
- #define KEY2_PRES 3
- #define WKUP_PRES 4
- void KEY_Init(void); //IO初始化
- u8 KEY_Scan(u8); //按键扫描函数
- #endif
复制代码
这段代码里面最关键就是4个宏定义
- #define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
- #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) //PE3
- #define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) //PE2
- #define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PA0
复制代码
这里使用的是调用库函数 来实现读取某个 IO口的 1个位的。同输出一样, 上面的功能也同样可以通过位带操作来简单的实现:
- #define KEY0 PEin(4) //PE4
- #define KEY1 PEin(3) //PE3
- #define KEY2 PEin(2) //P32
- #define WK_UP PAin(0) //PA0
复制代码
用库函数实现的好处是在各个STM32芯片上面的移植性 非常好,不需要修改任何代码。
用位带操作的好处是简洁,至于使用哪种方法,看各位的爱好了。
在key.h中,我们还定义了 KEY0_PRES / KEY1_PRES/ KEY2_PRES/WKUP_PRESS等 4个宏定义,分别对应开发板四个按键 KEY0/KEY1/KEY2/ KEY_UP)按键按下时 KEY_Scan返回的值。 通过宏定义的方式判断返回值,方便大家记忆和使用 。最后,我们看看main.c里面编写的主函数代码如下:
- #include "sys.h"
- #include "delay.h"
- #include "usart.h"
- #include "led.h"
- #include "beep.h"
- #include "key.h"
- int main(void)
- {
- u8 key; //保存键值
- delay_init(168); //初始化延时函数
- LED_Init(); //初始化 LED端口
- BEEP_Init(); //初始化蜂鸣器端口
- KEY_Init(); //初始化与按键连接的硬件接口
- LED0=0; //先点亮红灯
- while(1)
- { key=KEY_Scan(0); //得到键值
- if(key)
- { switch(key)
- { case WKUP_PRES: //控制蜂鸣器
- BEEP=!BEEP;
- break;
- case KEY0_PRES: //控制 LED0翻转
- LED0=!LED0;
- break;
- case KEY1_PRES: //控制 LED1翻转
- LED1=!LED1;
- break;
- case KEY2_PRES://同时控制 LED0,LED1翻转
- LED0=!LED0;
- LED1=!LED1;
- break;
- }
- }
- else delay_ms(10);
- }
- }
复制代码
主函数代码比较简单,先进行一系列的初始化操作,然后在死循环中调用按键扫描函数KEY_Scan()扫描按键值,最后根据按键值控制 LED和蜂鸣器的翻转。
|