1.状态机的好处
状态机是一种用于管理程序或系统行为的模型,它将复杂的状态转换逻辑简化为可预测的模式。以下是状态机的核心优势:避免阻塞主程序,将一个长时间的动作分为多个短时间的动作。程序在执行时,执行其他程序+执行动作1+执行其他程序+执行动作2+执行其他程序+执行动作3。类似时间切片的轮询方法。
2.检测按键长按与短按
-
状态0:等待按键按下
-
状态1:消抖并检测短按
-
状态2:检测长按
-
状态3:长按保持状态
状态机关系图:
/*-----在.h文件中定义-----*/
typedef enum{
KEY_RELEASE = 0, //松手
KEY_SHORT, //短按
KEY_LONG, //长按
}KEY_EVENT;
/* 可调参数 ---------------------------------------------------- */
#define KEY_GPIO_PORT GPIOE
#define KEY_GPIO_PIN GPIO_Pin_2
#define KEY_DOWN_LEVEL 0 // 0 = 低电平按下
#define SHORT_TICKS 20 // 20*10 ms = 200 ms 作为短按
#define LONG_TICKS 100 // 100*10 ms = 1 s 作为长按
/*-------------------------------------------------------------*/
/*-----在.c文件中-----*/
KEY_EVENT key_fsm()
{
static uint8_t state = 0; // 状态
static uint16_t cnt = 0; // 计时器
KEY_EVENT ret = KEY_RELEASE;
switch(state)
{
case 0:
if(GPIO_ReadInputDataBit(KEY_GPIO_PORT, KEY_GPIO_PIN) == KEY_DOWN_LEVEL) //检测按键按下
{
state = 1;
cnt = 0;
}
break;
case 1:
if(GPIO_ReadInputDataBit(KEY_GPIO_PORT, KEY_GPIO_PIN) == KEY_DOWN_LEVEL) //确认按键按下+消抖
{
cnt++;
if(cnt >= SHORT_TICKS) //消抖(时间小于短按)并确认为短按
{
state = 2;
ret = KEY_SHORT; // 产生短按事件
}
}
else
{
state = 0; //抖动/误触
}
break;
case 2:
if(GPIO_ReadInputDataBit(KEY_GPIO_PORT, KEY_GPIO_PIN) == KEY_DOWN_LEVEL) //确认按键按下
{
cnt++;
if(cnt >= LONG_TICKS) //确认长按
{
state = 3;
ret = KEY_LONG; // 产生长按事件
}
}
else
{
state = 0; //短按后松开
}
break;
case 3:
if(GPIO_ReadInputDataBit(KEY_GPIO_PORT, KEY_GPIO_PIN) != KEY_DOWN_LEVEL)
{
state = 0;
}
break;
}
return ret;
}
代码实现:
3.检测按键双击
-
状态0:等待按键按下
-
状态1:检测第一次按下抬起
-
状态2:检测第二按下
-
状态3:长按保持状态
状态机关系图:
void Check_Key_click()
{
static uint8_t state = 0;
static uint16_t cnt_Down = 0; //用于按下计数
static uint16_t cnt_Up = 0; //用于松开计数
switch(state)
{
case 0:
if(!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) //检测按键按下
{
state = 1;
cnt_Down = 0;
cnt_Up = 0;
}
break;
case 1:
if(!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) //检测按键按下
{
cnt_Down++;
if(cnt_Down >= 5); //按下消抖50ms
if(cnt_Down >= 200) //按下不松手,超时2s
{
state = 0;
}
}
else if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) //检测按键松手
{
cnt_Up++;
if(cnt_Up >=5) //松手消抖50ms
{
state = 2; //松手后进入状态2
cnt_Down = 0; //计数清0
cnt_Up = 0;
}
}
break;
case 2: //状态2,再次检测按下
if(!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) //检测按键按下
{
cnt_Down ++;
if(cnt_Down >= 5) //按下消抖50ms
{
state = 3;
cnt_Down = 0;
}
}
cnt_Up++; //高电平计数器自增
if(cnt_Up >= 200) //检测第一次松手后,不及时按超时2s
{
state = 0;
}
break;
case 3:
if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) //检测按键按下
{
state = 0;
}
break;
}
}