一、有限状态机简介
有限状态机FSM(finite state machine )是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。有限状态自动机可以表示为一个有向图。
1、分类
- Moore 型有限状态机:输出只和状态有关而与输入无关
- Mealy 型有限状态机:输出不仅和状态有关而且和输入有关系
2、组成
- 状态(State):明确系统的各个状态
- 状态转移(Transition):定义状态之间的转移,并确定在满足什么条件时进行转移
- 行为动作(Action):针对不同的状态,定义对应的动作,这些动作可能是在进入状态时进行,退出状态时进行,或者在特定转移时进行。
- 事件(Event):当状态机处于某个状态时,只有外界告诉状态机要干什么事情的时候,状态机才会去执行具体的行为,来完成外界想要它完成的操作。
二、状态机消抖
1、状态转移图
2、说明
按键默认状态Free:表明没有按键按下。
当检测到按键按下(即KEY1电平为低时),状态转移为Press_Debonce,按下消抖状态,如果按键消抖状态中的PresstimeCnt计数器计数大于10,电平任然为低电平,表示按键确实按下了,不是抖动,状态转移为Press;如果在PresstimeCnt计数器计数到10之前,读取到KEY1的电平为高,表明按键存在抖动,转到Free状态。
在Press状态下,如果读取到KEY1电平为高(按键释放),则转移状态为R_Fliter(按键释放消抖)。
在R_Fliter状态下,如果R_FilterTimeCnt计数器计数到10时,仍然没读取到低电平,这表示按键确实松开了,状态转移到Free;否则如果读取到低电平,表明按键存在抖动,重新转移到R_Filter状态,将R_FilterTimeCnt清0,重新计数。
3、代码实现
- KEYFSM.h
#ifndef __KEYFSM_H #define __KEYFSM_H #include "main.h" #define KEY2_GPIO_PIN GPIO_PIN_12 #define KEY2_GPIO_PORT GPIOB #define KEY2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define READ_KEY1() HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_GPIO_PIN) typedef enum { Free = 0x00 , //按键释放 Press_Debounce, //按下消抖 Press, //按键按下 R_Filter, //按键释放消抖 } FSM_State; typedef enum { RELEASE , //按键释放 PRESSED //按键按下 } KEY_State; void KEYFSM_Init(void); //按键初始化 KEY_State KEYFSM_Scan(void); //FSM按键扫描 #endif //__KEYFSM_H
- KEYFSM.c
#include "KeyFSM.h" FSM_State g_Statue = Free; //默认按键没有按下 u8 PresstimeCnt = 0; //按下消抖计时 u8 R_FilterTimeCnt = 0; //释放消抖计时 //使用状态实现按键扫描功能的初始化 void KEYFSM_Init(void){ KEY2_GPIO_CLK_ENABLE(); //时钟使能 GPIO_InitTypeDef GPIO_InitStructure = {0}; GPIO_InitStructure.Pin = KEY2_GPIO_PIN; GPIO_InitStructure.Mode = GPIO_MODE_INPUT; //输入模式 GPIO_InitStructure.Pull = GPIO_PULLUP; //上拉 HAL_GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure); } KEY_State KEYFSM_Scan(void){ switch (g_Statue) { case Free: //没按键按下 if(!READ_KEY1()) { //检测到有按键可能按下, g_Statue = Press_Debounce ; //状态转移:Press_Debounce PresstimeCnt = 0; //将PresstimeCnt计数器清0 } break; case Press_Debounce: //按键按下消抖状态 PresstimeCnt++; //计数器自增 if(READ_KEY1()) g_Statue = Free; //按键出现抖动 else if (PresstimeCnt > 10){ //没抖动,按键确实按下 g_Statue = Press; return PRESSED; //返回按键状态,按键按下 } break; case Press: if(READ_KEY1()){ //检测按键可能释放 R_FilterTimeCnt = 0; g_Statue = R_Filter; //跳转到按键释放消抖状态 } break; case R_Filter: R_FilterTimeCnt++; if(R_FilterTimeCnt >= 10){ //按键正常释放 g_Statue = Free; } else if(!READ_KEY1()) //按键存在抖动 R_FilterTimeCnt = 0; break; default : //意外情况 g_Statue = Free; break; } return RELEASE; //返回按键状态 }