STM32学习笔记之按键扫描

按键扫描的两种模式

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和蜂鸣器的翻转。

原子哥视频讲解:选自,STM32F4之寄存器实现按键输入实验,P17,
https://www.bilibili.com/video/BV1Rx411R75t?p=16

©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页