【源码详解~蓝桥杯单片机必备技能】2.利用状态机思维实现矩阵键盘单击、双击、长按


前言

再学完上一篇文章所讲内容的基础上,我们来看一些更有挑战性的


1.按键长按

蓝桥杯以前考过:
在这里插入图片描述

1.1.按键过程抽象:

1.1.1.按键状态分类

在上一节的基础上,我们可以将按键过程抽象为四个状态:

按键抬起状态(KEY_UP):按键没有按下。

按键消抖状态(KEY_DEBOUNCE):消除按键抖动。

按键按下状态(KEY_DOWN):确认按键已经按下。

按键长按状态(KEY_LONG):确认按键已经长按。

1.1.2.按键状态转换图

在这里插入图片描述

1.2.代码逐句分析:

代码如下(示例):

#include "STC15F2K60S2.h"
 
//矩阵键盘的行列定义
sbit Heng1 = P3^3;  
sbit Heng2 = P3^2;
sbit Lie1 = P3^5;
sbit Lie2 = P3^4;
 
#define Key_Num  4
#define uint8_t  unsigned char
#define uint16_t unsigned int
 
typedef enum
{
      KEY_UP = 0,
      KEY_DEBOUNCE,
      KEY_DOWN,
      KEY_LONG
}KEY_STATE;  //按键的四种状态,通过枚举类型实现
 
//数组的特性,部分初始化对于没给初值的其他数组元素自动补0
KEY_STATE KeyState[Key_Num] = {KEY_UP};  
uint8_t KeyFlag[Key_Num] = {0};      
uint8_t KeyFlag_Long[Key_Num] = {0};   //按键长按标志,1按下,0没按 
uint16_t KeyFlag_Count = 0;

void Key_State(uint8_t i,bit Lie)
{
      switch(KeyState[i])
      {
            case KEY_UP:
            {
                  if(Lie == 0)   //读到低电平,按键进入抖动状态
                  {
                       KeyState[i] = KEY_DEBOUNCE;  
                  }
            }
            break;
            case KEY_DEBOUNCE:
            {
                  if(Lie == 0)  //读到低电平,按键进入按下状态,并将按键按下标志置1,同时开始按键计时
                  {
                        KeyState[i] = KEY_DOWN;  
                        KeyFlag[i] = 1;
                        KeyFlag_Count = 0;         
                  }
                  else   //读到高电平,按键进入抬起状态
                  {
                        KeyState[i] = KEY_UP;
                  }
            }
            break;
            case KEY_DOWN:
            {
                  if((Lie == 0)&&(++KeyFlag_Count >= 100))   //低电平的情况下,每10ms加1,加到100正好1s
                  {
                        KeyState[i] = KEY_LONG;
                        KeyFlag_Long[i] = 1;
                        KeyFlag_Count = 0;
                  }
                  else if(Lie == 0)    //低电平的情况下,时间不足1s,保持按下状态
                  {
                        KeyState[i] = KEY_DOWN;
                  }
                  else
                  {
                        KeyState[i] = KEY_UP;  
                        KeyFlag[i] = 0;
                  }
            break;
            }
            case KEY_LONG:
            {
                  if(Lie == 1)   //读到高电平,按键进入抬起状态,同时将长按键、短按键标志置0
                  {
                        KeyState[i] = KEY_UP;  
                        KeyFlag_Long[i] = 0;
                        KeyFlag[i] = 0;
                  }
            }
            break;
      }
}

void Scan_Key()
{
      Lie1 = 1; Lie2 = 1;
      Heng1 = 0;   Heng2 = 1;
      Key_State(0,Lie1);   //扫描S12按键
      Key_State(1,Lie2);   //扫描S16按键
      
      Heng1 = 1;   Heng2 = 0;
      Key_State(2,Lie1);   //扫描S13按键
      Key_State(3,Lie2);   //扫描S17按键                     
}

2.按键双击

必备技巧,很有可能考

2.1.按键过程抽象:

2.1.1.按键状态分类

在上一节的基础上,更进一步,我们可以将按键过程抽象为五个状态:

按键抬起状态(KEY_UP):按键没有按下。

按键消抖状态(KEY_DEBOUNCE):消除按键抖动。

按键按下状态(KEY_DOWN):确认按键已经按下。

按键等待状态(KEY_WAIT):按键确认按下后又松开。

按键双击状态(KEY_DOUBLE):确认按键为双击。

2.1.2.按键状态转换图

在这里插入图片描述

2.2.代码逐句分析:

typedef enum
{
      KEY_UP = 0,
      KEY_DEBOUNCE,
      KEY_DOWN,
      KEY_DOUBLE,
      KEY_WAIT
}KEY_STATE;     //按键的五种状态,通过枚举类型实现

KEY_STATE KeyState[Key_Num] = {KEY_UP};
uint8_t KeyFlag[Key_Num] = {0};
uint8_t KeyFlag_Double[Key_Num] = {0};  //按键双击标志,1按下,0没按 
uint16_t KeyFlag_Count = 0;

void Key_State(uint8_t i,bit Lie)
{
      switch(KeyState[i])
      {
            case KEY_UP:
            {
                  if(Lie == 0)   //读到低电平,按键进入抖动状态
                  {
                       KeyState[i] = KEY_DEBOUNCE;  
                  }
                  else   //读到高电平,按键按下标志置0
                  {
                       KeyFlag[i] = 0; 
                  }
            }
            break;
            case KEY_DEBOUNCE:  
            {
                  if(Lie == 0)  //读到低电平,按键进入按下状态
                  {
                        KeyState[i] = KEY_DOWN;  
                  }
                  else  //读到高电平,按键进入抬起状态
                  {
                        KeyState[i] = KEY_UP;
                  }
            }
            break;
            case KEY_DOWN:
            {
                  if(Lie == 1)  //读到高电平,按键进入等待状态
                  {
                        KeyState[i] = KEY_WAIT;  
                        KeyFlag_Count = 0;
                  }
            break;
            }
            case KEY_WAIT:
            {
                  if((Lie == 1)&&(++KeyFlag_Count >= 20))  //高电平的情况下,每10ms加1,加到20正好0.2s,如果大于0.2秒,进入抬起状态,下一次按下,不视为双击
                  {
                       KeyState[i] = KEY_UP; 
                       KeyFlag[i] = 1; 
                       KeyFlag_Count = 0;
                  }
                  else if(Lie == 1)
                  {
                       KeyState[i] = KEY_WAIT; 
                  }
                  else  //小于0.2s再次读到低电平视为双击,将双击标志位置1
                  {
                        KeyState[i] = KEY_DOUBLE; 
                        KeyFlag_Double[i] = 1;
                  }
            }
            break;
            case KEY_DOUBLE:
            {
                  if(Lie == 1)  //读到高电平,按键进入抬起状态
                  {
                        KeyState[i] = KEY_UP;  
                        KeyFlag_Double[i] = 0;
                  }
            }
            break;
      }
}

3.总结

不论长按还是双击,代码主体依然是上一篇文章按键扫描的内容,基本做到只在扫描代码的基础上做加法,而不是减法,这样更利于理解和记忆。

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值