蓝桥杯嵌入式CT117E硬件开发平台经验分享08 | 按键状态机

蓝桥杯嵌入式CT117E硬件开发平台 | 按键状态机实现方法

蓝桥杯历年赛题有一届考到了按键多个状态,比如长按短按,长按快速累加等。因此本驱动则是针对这种任务要求而设计。具体思路和实现方法如下:

  1. 思路:利用定时器间隔扫描按键驱动函数,定时器间隔时间则可以作为消抖时间,从而实现消抖过程而不用延时函数消抖。比如定时器设计5MS中断一次:2次中断则检测一次按键值,若按键值不为0xff(根据经验06章节讲的按键延时消抖方法设计的按键编码来更改的,按键没有按下则值为0xff,按下的话返回对应的按键编码值:0 - 4)则存取按键值为第一次按键扫描值,进入第二层状态,否则还是继续间隔扫描,检测按键值不为0xff时再存入按键值;在第二层状态中:再过两次中断(10MS)再次扫描按键,如果此次按键值等于第一次按键扫描值,则确定了有按键按下,进入第三层状态;在第三层状态中:再过两次中断判断按键有无松开,若是没有,则累加计时,若计时超过长按时间(自己设置的多久算长按),则做一个变量累加计时,这个计时则是判断长按多久要设置加的值快速加一,若按键松开,则判断计时时间是否小于按键长按时间,若小于长按时间则算是短按。 --总结:其实按键状态机无非就是依靠多个标志变量判断当前按键所处状态,对于有时许处理要求的任务来说,状态设计一定是重点。
  2. 实现方法:首先是.h中设计的宏定义和按键状态机结构体。具体如下:
#define RB1	   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define RB2	   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define RB3    GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define RB4    GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)
typedef struct{		
	u8 KEY_VALUE;   //使用数据后及时清除到 0xff !!!
	JUDGE_ENUM IS_Key_ShortPress;  //使用数据后及时清除到FAUSE状态!!!
	JUDGE_ENUM IS_Key_LongPress;  //判断长按否? 不需要人为清除
	JUDGE_ENUM IS_Key_LongFresh;  //长按刷新,长按多久按键计算快速累加或累减一次使用数据后及时清除到FAUSE状态!!!!
	JUDGE_ENUM IS_Key_Touch;   //按键是否按下
}KEYSCANF_TypeDef;
extern KEYSCANF_TypeDef KEYSCANF_Structure;

/********************当前设置的按键扫描中断为 5MS************/
#define KEY_LONGSTATUS_Time   800000/GENERAL_TIM_SetUs  // 多久算长按
#define KEY_EliShaking_Time   10000/GENERAL_TIM_SetUs    // 按键消抖值
#define Key_LongTimeSet       100000/GENERAL_TIM_SetUs  //按键长按后多久更新一次 比如长按后加一,本设置为多久加一
  1. 其次则是.c文件中的函数设计,思路已经写过,直接上代码:
/**
  * @说明     按键编码函数
  * @参数     none
  * @返回值   0xff  ;  1 - 4
  */
uint8_t Key_Scan(void)
{	
	uint8_t key_value = 0xff;
	if(RB1 == 0){
        key_value = 1;
	}
	if(RB2 == 0){
		key_value = 2;
	}
	if(RB3 == 0){
		key_value = 3;
	}
	if(RB4 == 0){
		key_value = 4;
	}
	return key_value;
}
/**
  * @说明     按键状态机函数
  * @参数     none
  * @返回值   None
  * @注意   1.按键状态机只做出 状态, 但不消除状态
            2.KEYSCANF_Structure.KEY_VALUE ,KEYSCANF_Structure.IS_Key_ShortPress 被使
			用后需要人为复位0xff,FAUSE,这样才能保证每一次的状态都能被处理
            3.Key_Interrup()函数需要放入定时器中断,且中断时间小于10MS,最好为10的公约数:如1,2,5MS
例程:
@1: 按键初始化放入main.c
@2: 中断放入函数
	extern void Key_Interrup(void);
	void TIM4_IRQHandler(void)
	{
		Key_Interrup();
	}
@3:按键处理函数
	void KEY_Proc(void)
	{
		static u8 i = 0;
		if(KEYSCANF_Structure.KEY_VALUE != 0xff)  // There are remaining parking spaces
		{				
			switch(KEYSCANF_Structure.KEY_VALUE)
			{
				case 1:
					if(KEYSCANF_Structure.IS_Key_ShortPress == TRUE)
					{
						KEYSCANF_Structure.IS_Key_ShortPress = FAUSE;   //!!!手动清除短按标志位
						短按处理自制函数();
					}
					break;
				case 2:	
					if(KEYSCANF_Structure.IS_Key_LongPress == TRUE)
					{
						长按处理自制函数();
					}
					break;
				case 3:	
					if(KEYSCANF_Structure.IS_Key_LongPress == TRUE)
					{
						if(KEYSCANF_Structure.IS_Key_LongFresh == TRUE)
						{
							KEYSCANF_Structure.IS_Key_LongFresh = FAUSE;   //手动清除快速按键刷新标志位
							长按处理快速累加/累减自制函数();
						}
					}
					break;
			}
			KEYSCANF_Structure.KEY_VALUE = 0xff;  //在此清除的目的在于 不至于在状态机中有按键值,而在按键处理函数检测时已经被状态机刷新销毁,上面switch判断后才能清除!
		}
	}

  * @包含出处  #include "bsp_key_statemachine.h"
  */
u8 keyCheck = 0;
uint8_t   keyState = 0;
uint16_t  keyPrev = 0xff;       //上一次的键值
u32 keyLongCheck = 0;
u16 Key_LongTime = 0;
u8 keyCountTime = 0;
KEYSCANF_TypeDef KEYSCANF_Structure = {0xff,FAUSE,FAUSE,FAUSE,FAUSE};
void Key_Interrup(void)      
{
	uint8_t keyPress = 0xff;
	keyCountTime ++;
	if(keyCountTime >= KEY_EliShaking_Time) //消抖完成
	{
		keyCountTime = 0;
		keyCheck = 1;
	}
	if(keyCheck == 1)
	{
		keyCheck = 0;
		keyPress = Key_Scan();
		switch(keyState)
		{
			case 0://按键未按下态
				if(keyPress != 0xff)//表示有按键按下
				{
					keyPrev = keyPress; //记录当前按键状态
					keyState = 1;
				}
				else
				{
					keyState = 0;
				}
			break;
			case 1://表示有按键按下,判断当前值和上一次的值是否一样,若不一样则为抖动!
				if(keyPress == keyPrev)//不是抖动
				{
					keyState = 2;
				}else{
					keyState = 0; //是抖动!返回上一层
					keyPrev = 0xff;
				}	 
				break;
			case 2:
				if(keyPress != keyPrev)//表示按键已松开,只有长按状态被清除,长按计时结束,短按状态不清除,等待用户使用并清除
				{		
					if(keyLongCheck < KEY_LONGSTATUS_Time)   //短按
					{
						KEYSCANF_Structure.IS_Key_ShortPress = TRUE;						
						KEYSCANF_Structure.KEY_VALUE = keyPrev;
					}
					KEYSCANF_Structure.IS_Key_LongPress = FAUSE;
					keyPrev = 0xff;
					keyState = 0;  // 按键结束返回初始状态机
					keyLongCheck = 0; //长按计时清0
					Key_LongTime = 0;
				}else{					
					keyLongCheck++;
					if(keyLongCheck >= KEY_LONGSTATUS_Time)//按下时间超过 长按时间
					{
						if(KEY_LONGSTATUS_Time > 65535)
							keyLongCheck = KEY_LONGSTATUS_Time;
						KEYSCANF_Structure.KEY_VALUE = keyPrev;//返回值标记哪个按键
						KEYSCANF_Structure.IS_Key_ShortPress = FAUSE;
						KEYSCANF_Structure.IS_Key_LongPress = TRUE;						
					}
				}
				break;
			default : break;
		}
	}
	if(KEYSCANF_Structure.IS_Key_LongPress == TRUE)   //按键长按 刷新,进行长按快速累减或累加使用
	{
		Key_LongTime ++;
		if(Key_LongTime >= Key_LongTimeSet)
		{
			Key_LongTime = 0;
			KEYSCANF_Structure.IS_Key_LongFresh = TRUE;
		}
	}
}

你可能感兴趣的:(单片机嵌入式,stm32,单片机,按键事件)