你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32学习笔记之按键扫描

[复制链接]
STMCU小助手 发布时间:2022-7-5 17:13
按键扫描的两种模式
1.支持连续扫描模式
连续按下时,会认为有多个数值,比如我们的遥控板,一直按下则频道会一直增加。
2.不支持连续模式
连续按下时,仅认为只有一个数值,比如我们的电源按键,长时间的按下并不会一直有效,从而频繁的开关机,就算按的时间很长,也只会进行一次。
只有在前一时刻的状态与这个时候的状态不想同时,会记一个数值。比如之前是高电平,现在是低电平就记一次,一直是低电平,则不继续计数进行。

BU~$K(I]UUGP9@{W)0$X[)X.png

如何用C语言处理是否是连续模式?
关键字Static
Static声明的局部变量是具有记忆功能的。

例1:
20200731122637790.png

结果:1,1,1…

例2
static关键字具有存储记忆功能,能存入返回的上一时刻flag的值。
此时的flag++是在上一时刻的flag的基础上进行加1。

20200731122833667.png

结果:1,2,3…(具有记忆功能)

按键扫描,(不支持连续按下)的一般思路
  1. u8 KEY_Scan(void)
  2.     {
  3.      static u8 key_up=1;
  4.       if(key_up &&  KEY按下)//若key_up=1时,1&&KEY=KEY;若key_up=0时,0&&KEY=0;
  5.        {
  6.         delay_ms(10);//延时,防抖
  7.         key_up=0;//标记这次key已经按下
  8.         if(KEY确实按下)
  9.            {
  10.            return KEY_VALUE;
  11.             }
  12.         }
  13.         else if(KEY没有按下)  key_up=1;
  14.     }
复制代码

按键扫描(两种模式合二为一)的一般思路
多了一个mode模式,mode=1,支持连续按下;mode=0,不支持连续按下。

  1. u8 KEY_Scan(u8 mode)
  2.     {
  3.      static u8 key_up=1;//默认刚开始为高电平
  4.      if(mode==1) key_up=1;//mode=1,支持连续按,key-up永远等于1;mode=0,则此行代码无用,不支持连续按;
  5.       if(key_up &&  KEY按下)
  6.       {
  7.         delay_ms(10);//延时,防抖
  8.         key_up=0;//标记这次key已经按下
  9.         if(KEY确实按下)
  10.           {
  11.            return KEY_VALUE;//识别到按下,返回KEY的真实值
  12.           }
  13.         }
  14.         else if(KEY没有按下)  key_up=1;//表示没有扫描到按下,则返回 key_up=1
  15.        return 没有按下
  16.     }
复制代码

打开我们的按键实验工程可以看到,我们引入了
key.c文件以及头文件 key.h。下面我们首
先打开 key.c文件, 关键 代码如下:
  1. #include "key.h" #include "delay.h" //按键初始化函数
  2. void KEY_Init(void)
  3. {
  4. GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE, ENABLE);//使能 GPIOA,GPIOE时钟
  5. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //KEY0 KEY1 KEY2对应引脚
  6. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
  7. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  8. 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 ;//下拉
  9. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 GPIOA0 } //按键处理函数
  10. //返回按键值
  11. //mode:0,不支持连续按 ;1,支持连续按 ; //0,没有任何按键按下
  12. //1 KEY0按下 2 KEY1按下 3 KEY2按下 4 WKUP按下 WK_UP //注意此函数有响应优先级 ,KEY0>KEY1>KEY2>WK_UP!!

  13. u8 KEY_Scan(u8 mode)
  14.    {
  15. static u8 key_up=1;//按键按松开标志
  16. if(mode)key_up=1; //支持连按
  17. if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1)) { delay_ms(10);//去抖动
  18. key_up=0;
  19. if(KEY0==0)return 1;
  20. else if(KEY1==0)return 2;
  21. else if(KEY2==0)return 3;
  22. else if(WK_UP==1)return 4;
  23.     }
  24. else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
  25. return 0;// 无按键按下
  26. }
复制代码

这段代码包含 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里面的代码:

  1. #ifndef __KEY_H
  2. #define __KEY_H
  3. #include "sys.h" /*下面的方式是通过直接操作库函数方式读取 IO*/
  4. #define KEY0
  5. GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
  6. #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) //PE3
  7. #define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) //PE2
  8. #define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PA0
  9. #define KEY0_PRES 1
  10. #define KEY1_PRES 2
  11. #define KEY2_PRES 3
  12. #define WKUP_PRES 4
  13. void KEY_Init(void); //IO初始化
  14. u8 KEY_Scan(u8); //按键扫描函数
  15. #endif
复制代码

这段代码里面最关键就是4个宏定义

  1. #define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
  2. #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) //PE3
  3. #define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) //PE2
  4. #define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PA0
复制代码

这里使用的是调用库函数 来实现读取某个 IO口的 1个位的。同输出一样, 上面的功能也同样可以通过位带操作来简单的实现:

  1. #define KEY0 PEin(4) //PE4
  2. #define KEY1 PEin(3) //PE3
  3. #define KEY2 PEin(2) //P32
  4. #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里面编写的主函数代码如下:

  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "usart.h"
  4. #include "led.h"
  5. #include "beep.h"
  6. #include "key.h"
  7. int main(void)
  8. {
  9. u8 key; //保存键值
  10. delay_init(168); //初始化延时函数
  11. LED_Init(); //初始化 LED端口
  12. BEEP_Init(); //初始化蜂鸣器端口
  13. KEY_Init(); //初始化与按键连接的硬件接口
  14. LED0=0; //先点亮红灯
  15. while(1)
  16.   { key=KEY_Scan(0); //得到键值
  17. if(key)
  18.      { switch(key)
  19.               { case WKUP_PRES: //控制蜂鸣器
  20.                 BEEP=!BEEP;
  21.                 break;
  22.            case KEY0_PRES: //控制 LED0翻转
  23.                 LED0=!LED0;
  24.                 break;
  25.            case KEY1_PRES: //控制 LED1翻转
  26.                 LED1=!LED1;
  27.                 break;
  28.            case KEY2_PRES://同时控制 LED0,LED1翻转
  29.                 LED0=!LED0;
  30.                  LED1=!LED1;
  31.                  break;
  32.            }
  33.       }
  34.       else delay_ms(10);
  35.    }
  36. }
复制代码

主函数代码比较简单,先进行一系列的初始化操作,然后在死循环中调用按键扫描函数KEY_Scan()扫描按键值,最后根据按键值控制 LED和蜂鸣器的翻转。



收藏 评论0 发布时间:2022-7-5 17:13

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版