基于有限状态机与STM32实现按键扫描

有限状态机其实是一种概念性机器,表示有限个状态以及在这些状态之间的转移和动作等行为的框图(在程序上)。

以我的程序绘成的图为例:

以高电平作为标志,在S1时检测输入电平是否为高,是高电平则运行至S2,否则保持在S1;

在S2时再次判断判断输入电平是否为高,若为低则说明刚刚的改变是干扰,回到S1,若为高电平则说明按键被按下,运行至S3;

在S3可判断这次对按键的操作是长按还是短按,具体方法是判断电平是否还是为高,若为高则令状态机保持在S3,同时定义一个变量(建议用指针,具体见代码),使其一直自加。若为低则运行至S4;

在S4时判断那个变量是否大于某个数值,大于则为长按,小于则为短按,并执行相关的操作,执行完之后令状态机回到S1

这种写法是可以利用时间片的形式,每隔10ms运行一次状态机,即可实现消抖的目的,还可以对长按的时间精确控制。

运用状态机+时间片,大幅提升CPU的资源的利用率,而且效率高了不少。

时间片的话举个例子,比如我一个周期30ms,10ms需要执行一次按键扫描,20ms执行一次ADC,那么利用时间片的话程序就是:
注:Time是在定时器中断里每隔1ms自加一次。

if( Time %10 == 0 ){
      KeyScan();
}
 
if( Time %20 == 0 ){
      ADC();
}
 
if( Time > 30 ){
    Time = 1;
}

贴上我写的完整代码:

Key.h:

#ifndef _BSP_KEY_H
#define _BSP_KEY_H
 
#include "stm32f10x.h"
 
#define    KEY0_GPIO_PORT        GPIOE
#define KEY0_GPIO_PIN        GPIO_Pin_0
#define KEY0_GPIO_CLOCK        RCC_APB2Periph_GPIOE
 
#define    KEY1_GPIO_PORT        GPIOE
#define KEY1_GPIO_PIN        GPIO_Pin_1
#define KEY1_GPIO_CLOCK        RCC_APB2Periph_GPIOE
 
#define    KEY2_GPIO_PORT        GPIOE
#define KEY2_GPIO_PIN        GPIO_Pin_2
#define KEY2_GPIO_CLOCK        RCC_APB2Periph_GPIOE
 
#define ON                1
#define OFF                0       
#define LONGTIME            8000
 
//---------定义状态Sx的枚举类型
typedef enum {
    FsmState_1 = 1,
    FsmState_2 = 2,
    FsmState_3 = 3,
    FsmState_4 = 4,
    FsmState_5 = 5,
}FsmState_x;
 
//---------定义按键的结构体 
typedef struct FsmTable_s{
    uint8_t event;                      /* 触发事件 */
    uint8_t CurState;                /* 当前状态 */
    void (*EventFunction)(void);        /* 动作函数 */
    uint16_t Time;                      /* 时间计数 */
    uint16_t GpioPin;          /* 按键引脚 */
    GPIO_TypeDef * GpioPort;      /* 引脚GPIO */ 
}Key;
 
void key_GPIO_Init(void);
void Key_1_EventFunction(void);
void Key_1_LongEventFunction(void);
void Key_2_EventFunction(void);
void Key_2_LongEventFunction(void);
void Key_Fsm(Key *key);
 
#endif

Key.c:

/**********************************************
 *                                            
 * 状态机相关                                  
 *                                            
 **********************************************/
 
/**********************************************
 *
 * 按键1短按时的事件函数
 *
 *********************************************/
void Key_1_EventFunction()
{
    
}
 
/**********************************************
 *
 * 按键1长按时的事件函数
 *
 *********************************************/
void Key_1_LongEventFunction()
{
    
}
 
/**********************************************
 *
 * 按键2短按时的事件函数
 *
 *********************************************/
void Key_2_EventFunction()
{
    
}
 
/**********************************************
 *
 * 按键2长按时的事件函数
 *
 *********************************************/
void Key_2_LongEventFunction()
{
    
}
 
/**********************************************
 *
 * 基于状态机的按键扫描
 * 输入:Key类型的指针变量
 * 输出:无
 *
 *********************************************/
void Key_Fsm(Key *key)
{
    void (*EventFunction)(void);
    
    switch( key->CurState )
    {
        case FsmState_1:{//--------状态1
            if( GPIO_ReadInputDataBit(key->GpioPort,key->GpioPin) == ON ){
                key->CurState = FsmState_2;
            }
            
        }break;
        
        case FsmState_2:{//--------状态2
            if( GPIO_ReadInputDataBit(key->GpioPort,key->GpioPin) == ON ){
                key->CurState = FsmState_3;
            }
            else{//--------干扰返回状态1
                key->CurState = FsmState_1;
            }
        }break;
        
        case FsmState_3:{
            if( GPIO_ReadInputDataBit(key->GpioPort,key->GpioPin) == ON ){
                key->Time++;//--------变量自加
                key->CurState = FsmState_3;//--------保持在状态3
            }
            else{
                key->CurState = FsmState_4;
            }
        }break;
        
        case FsmState_4:{
            EventFunction = key->EventFunction;//--------短按函数
            if( (key->Time > LONGTIME) && (key->GpioPin == KEY0_GPIO_PIN) ){
                key->Time = 0;
                EventFunction = Key_1_LongEventFunction;//--------Key1的长按函数
            }
 
            if( (key->Time > LONGTIME) && (key->GpioPin == KEY1_GPIO_PIN) ){
                key->Time = 0;
                EventFunction = Key_2_LongEventFunction;//--------Key2的长按函数
            }
            key->CurState = FsmState_1;//--------返回状态1
            EventFunction();//--------执行对应的函数
        }break;
    }
}

main.c:

#include "bsp_key.h"
 
extern uint16_t TimeSlice;
 
int main()
{
    Key *key_1,*key_2;
    Key key1,key2;
    
    key_1 = &key1;
    key_2 = &key2;
    
    TIM_TimeBase_Init();
    key_GPIO_Init();
    
    key_1->GpioPin  = KEY0_GPIO_PIN;
    key_1->GpioPort = KEY0_GPIO_PORT;
    key_1->CurState = FsmState_2;
    key_1->EventFunction = Key_1_EventFunction;
    
    key_2->GpioPin  = KEY1_GPIO_PIN;
    key_2->GpioPort = KEY1_GPIO_PORT;
    key_2->CurState = FsmState_1;
    key_2->EventFunction = Key_2_EventFunction;
    
    while(1)
    {
    //--------------按键扫描,10ms执行一次--------------//
        if( TimeSlice %10==0 ){
            Key_Fsm(key_1);
            Key_Fsm(key_2);
        }
    }
}

 原文地址: https://blog.csdn.net/Unlimited_Bit/article/details/95811859

原文地址:https://www.cnblogs.com/since1996/p/13558065.html