打印
[技术问答]

单片机基于事件的按键处理编程思想

[复制链接]
286|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
belindagraham|  楼主 | 2023-7-27 23:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
功能有如下几个方面:
1、多键扫描处理,提高处理速度
2、支持长按处理(单次触发或一直触发)
3、支持按下、弹起、按住、松开、长按的事件
4、项目中只需要修改扫描键值,然后在各事件中判断对应键值(单键或多键)
主要编程思路如下:
1、变量的说明
//长按键的时长
#define longkey_times 2000
//长按单次模式定义,如果要长按时一直执行,请注释下一行
#define LONG_PROCESS_ONCE
uint8_t  KEY_PRESS;  //当前按下的键值
uint8_t  KEY_NOT_PRESS;  //当前未被按的键值
uint8_t  KEY_LAST;  //上一次的键值
uint8_t  KEY_LONG;  //长按的键值
uint8_t  KEY_DOWN;  //按下的键值
uint8_t  KEY_UP; //弹起的键值
uint8_t  KEY_UP_NL; //弹起的键值不带长按键
uint32_t KEY_TICKS;  //按键时间,用于长按计时
2、按键相关函数说明
//按键处理程序
void JUDGE_KEY(bool SINGLE_KEY); //键值扫描及逻辑处理
void KEY_LONG_PROCESS(void);  //长按事件
void KEY_PRESS_PROCESS(void);  //按住状态事件
void KEY_NOT_PRESS_PROCESS(void); //松开状态事件
void KEY_DOWN_PROCESS(void); //按下事件
void KEY_UP_PROCESS(void);  //弹起事件
3、按键扫描及逻辑处理思路
//bool SINGLE_KEY 防抖开关,True时打开
void JUDGE_KEY(bool SINGLE_KEY)
{
    uint8_t TEMP_KEY;  //临时的键值缓存
    TEMP_KEY = PIND & 0x0C; //批量扫描IO,并生成键值,用户需结合项目自已修改,PIND
                            //此处表示PD0-7的端口,不同单片机不一样,0x0C只取出
//PD2 PD3的值  
    TEMP_KEY ^= 0x0C;       //此处主要是把键值取反,如果你的按键是低电平触发的话
                            //如果你的按键是高电平触发,则删除此行,不需要取反
    if(TEMP_KEY > 0)  //键值大于0,表示有按键按着
    {
     delay(10); //防抖延时
     //以下再一次批量扫描键值
    KEY_PRESS = PIND & 0x0C;         
    KEY_PRESS ^= 0x0C;

    //如果防抖开关有效且两次键值不一致,返回不处理
    if(TEMP_KEY!=KEY_PRESS && SINGLE_KEY)
   {
      return;
    }
    }
    else //无按键动作,当前按下的键值=0
    {
    KEY_PRESS=0;
    }
   //核心按键逻辑判断
    KEY_DOWN=(KEY_LAST^KEY_PRESS) & KEY_PRESS;   //按下的键值
    KEY_UP=(KEY_LAST^KEY_PRESS) & KEY_LAST;//弹起的键值(包含长按键)
    KEY_UP_NL=(~KEY_LONG) & KEY_UP; //弹起的键值(不包含长按键)
    KEY_NOT_PRESS=~KEY_PRESS;  //未按的键状态值
          if(KEY_LONG & KEY_UP)
    {
     KEY_LONG=0;
    }
    if(KEY_PRESS > 0)  //当前有按键值按下
    {
       if(KEY_LAST & KEY_PRESS)     //如果当前的值与上次按下的值有相同的地方
                                              //表示有键一直按着,否则可能只是切换了其他按键
       {
            //millis()函数是Arduino的开机时间毫秒计数,其他单片机自己实现
             if(millis() - KEY_TICKS > longkey_times)   //按键时间大于长按时间
            {
                 KEY_LONG =KEY_LAST & KEY_PRESS;    //长按键值等于一直按住的值
                 KEY_LONG_PROCESS();   //长按键处理
                  #ifdef LONG_PROCESS_ONCE  //如果是长按单次处理
                  KEY_TICKS=millis(); //更新长按时间标记,避免进入长按判断
                 #endif
             }
        }
       else
       {
            KEY_TICKS=millis();  //切换了其他键,更新长按时间标记,避免进入长按判断
        }
    }
    else   //当前无按键按下
    {
      KEY_TICKS=millis();  //更新长按时间标记,避免进入长按判断
    }
    if(KEY_UP > 0)  //如果有弹起的按键值
    {
    KEY_UP_PROCESS();    //按键弹起时处理
    KEY_UP = 0;  //复位弹起的键值
    }
    if(KEY_DOWN > 0)
    {
    KEY_DOWN_PROCESS();  //按键按下时处理
    }
    if(KEY_PRESS > 0)
    {
    KEY_PRESS_PROCESS();  //按键按着状态处理
    }               
                if(KEY_NOT_PRESS)
                {
     KEY_NOT_PRESS_PROCESS();  //按键弹起状态处理
    }
     KEY_LAST=KEY_PRESS; //更新上一次的键值
}
4、按键逻辑处理算法详解
   4.1首次按下的键,先用异或^进行上次扫描键值和本次扫描键值计算,取得不一样的键位,不一样的键位和本次扫描键位相同,则表示首次按下。
0000 0010表示上次扫描的键,第1位是按下的状态
   0000 0110 表示本次扫描的键,第1位和第2位是按下的,
   我们要算出第2位是首次按下,则
  0000 0010 ^ 0000 0110=0000 0100
  0000 0100 & 0000 0110=0000 0100

又如 000 0010表示上次扫描的键,第1位是按下的
     0000 0100表示本次扫描的键,第2位是按下的,第1位已经松开
     我们要算出第2位是首次按下,则
     0000 0010 ^ 0000 0100=0000 0110
     0000 0110 & 0000 0100=0000 0100  
   (所以与本次扫描的键值与,可以得到首次按下的键值)
       KEY_DOWN=(KEY_LAST^KEY_PRESS) & KEY_PRESS;   //按下的键值

   4.2弹起的键值
      与按下的原理一样,不同的是要和上次扫描的键值相与
          0000 0010表示上次扫描的键,第1位是按下的状态
          0000 0100 表示本次扫描的键,第2位是按下的,
          我们要算出第1位是弹起,则
          0000 0010 ^ 0000 0100=0000 0110
          0000 0110 & 0000 0010=0000 0010

          KEY_UP=(KEY_LAST^KEY_PRESS) & KEY_LAST;//弹起的键值(包含长按键)
   4.3长按键一般单独处理,弹起时如果要排除,避免多次触发事件,需要计算出
      不包含长按键的键值,用如下公式
           KEY_UP_NL=(~KEY_LONG) & KEY_UP; //弹起的键值(不包含长按键)
   4.4 长按键的计算逻辑,见程序注释
5、如何使用

    5.1 设置好长按的时间
       #define longkey_times 2000  //这里表示2秒
    5.2 修改扫描键值
        TEMP_KEY = PIND & 0x0C; //批量扫描IO,并生成键值,用户需结合项目自已修改,PIND
                            //此处表示PD0-7的端口,不同单片机不一样,0x0C只取出 PD2 PD3的值  
         TEMP_KEY ^= 0x0C;       //此处主要是把键值取反,如果你的按键是低电平触发的话
        //还有一处地方也要一起改
        KEY_PRESS = PIND & 0x0C;         
        KEY_PRESS ^= 0x0C;

注意:51或其他单片机中,如果按键不在同一序列,比如P01 P03 P14 P16,则可以如下设置
    TEMP_KEY = P0 & 0x0A; //取出 P01 P03
    TEMP_KEY |=(P1 & 0x50); //取出 P14 P16


    TEMP_KEY ^= (0x0A|0x50);       //此处主要是把键值取反,如果你的按键是低电平触发的话,
                                    //如果你的按键是高电平触发,则删除此行,不需要取反
    //还有一处地方也要一起改
    KEY_PRESS = P0 & 0x0A; //取出 P01 P03   
    KEY_PRESS |=(P1 & 0x50); //取出 P14 P16
    KEY_PRESS ^= (0x0A|0x50);      //此处主要是把键值取反,如果你的按键是低电平触发的话,
                                 //如果你的按键是高电平触发,则删除此行,不需要取反
为了编程方便,尽量使用同一序列的口,如果不同序列的口,那端口号也要能错开,如用了P01,就不要用P11了。
这样的话,才能方便计算,提高扫描效率,如果非要用,只能通过移位处理
如51或其他单片机中,想判断 P01 P02 P12 P13的键
TEMP_KEY = P1 & 0x0C; //取出 P12 P13
TEMP_KEY =TEMP_KEY<<1; //左移1位,避开P12和P02交叉重叠
TEMP_KEY |= (P0 & 0x06); //取出 P01 P02
TEMP_KEY ^= (0x18|0x06);       //此处主要是把键值取反,如果你的按键是低电平触发的话
                               //如果你的按键是高电平触发,则删除此行,不需要取反
这样键值里,0x02表示P01,0x04表示P02,0x08表示P12,0x10表示P13

5.3在单片机循环程序或定时器里,周期性调用扫描程序







使用特权

评论回复
沙发
dspmana| | 2023-8-4 18:53 | 只看该作者
单片机基于事件的按键处理当按键按下或释放时,会产生一个中断事件,单片机响应中断并执行相应的处理程序。

使用特权

评论回复
板凳
louliana| | 2023-8-4 20:08 | 只看该作者
优先级设置可根据需求选择,防抖动处理可通过软件或硬件电路来实现。

使用特权

评论回复
地板
linfelix| | 2023-8-4 20:36 | 只看该作者
在处理程序中,可以检测按键状态,并根据需要执行相应的操作

使用特权

评论回复
5
maudlu| | 2023-8-4 21:05 | 只看该作者
可以通过使用中断来实现。              

使用特权

评论回复
6
maqianqu| | 2023-8-4 21:21 | 只看该作者
根据按键的状态和事件类型,执行相应的操作。例如,可以在按键按下时执行某个函数或改变某个标志位,在按键释放时执行另一个函数或进行其他操作。

使用特权

评论回复
7
claretttt| | 2023-8-4 22:04 | 只看该作者
基于事件的按键处理是一种常见的单片机编程技术,它可以实现按键的即时响应和事件处理。

使用特权

评论回复
8
robincotton| | 2023-8-4 22:39 | 只看该作者
可以使用事件驱动编程模型来实现按键处理,这种模型可以使程序更加高效,按键处理更加可靠。

使用特权

评论回复
9
rosemoore| | 2023-8-5 10:10 | 只看该作者
如果系统中存在多个中断源或按键会产生抖动现象,需要适当设置中断优先级和进行按键防抖动处理。

使用特权

评论回复
10
vivilyly| | 2023-8-5 12:23 | 只看该作者
请确保主循环不会阻塞或长时间占用处理器,以便及时响应按键

使用特权

评论回复
11
tpgf| | 2023-8-9 11:31 | 只看该作者
请问 基于事件的具体含义是什么呢

使用特权

评论回复
12
tpgf| | 2023-8-9 13:18 | 只看该作者
这样的话可以实现功能的模块化

使用特权

评论回复
13
qcliu| | 2023-8-9 14:36 | 只看该作者
就是说把 每次按键都看成是一个事件来处理是吗

使用特权

评论回复
14
drer| | 2023-8-9 15:41 | 只看该作者
如果进行实时响应的话 如何避免误操作呢

使用特权

评论回复
15
coshi| | 2023-8-9 16:17 | 只看该作者
这只是一种处理问题的思路,如何**到编程上呢

使用特权

评论回复
16
kxsi| | 2023-8-9 16:49 | 只看该作者
我们其实编程都是这么编写的 那么怎么做才不是基于事件的呢

使用特权

评论回复
17
wiba| | 2023-8-9 17:53 | 只看该作者
这种程序是不是非常具有可移植性啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

1185

帖子

0

粉丝