STM32独立按键扫描,支持同时按下、长按、快速键值

背景

有个项目在实际应用中,采用8个独立按键,每个按键都赋予不同功能,实际使用过程中很多时候都是需要比较特殊的按键操作,例如:长按10s按键、长按5s按键,或者长按需要有快速按键值的反馈,这个情况就类似,我们需要快速增加一个设定值时,按住加号+按键不松手,这个按键值就一直增大。

原理图设计

如下图所示,采用STM32F103C8T6,进行按键设计,这里使用按键较多

KEY_TimeSet            ---->              时间设定功能,单击进去时间设定

KEY_Program/Back  ---->              返回按键,菜单选择

KEY_ON/OFF           ---->               启动按键

KEY_Temp+             ---->               设定温度增加按键,支持快速按键功能

KEY_Temp-              ---->               设定温度减少按键,支持快速按键功能

KEY_Threshold+     ---->                参数调整按键,支持快速按键功能

KEY_Threshold-      ---->               参数调整按键,支持快速按键功能

KEY_AutoTuning     ---->               单击功能

STM32独立按键扫描,支持同时按下、长按、快速键值_第1张图片

STM32独立按键扫描,支持同时按下、长按、快速键值_第2张图片

软件设计 

软件采用stm32cubemx,如下图所示,时钟选用外部时钟倍频到72Mhz

STM32独立按键扫描,支持同时按下、长按、快速键值_第3张图片

 对应原理图STM32对应管脚应配置为输入模式

这里就不再贴图了,比较简单,配置对应IO输入模式即可。

这里使用FREERTOS功能,可以通过如下配置,添加自己的任务或者信号量,队列等。

使用FREERTOS的目的,是将按键扫描的结果的有效值,放进队列中,供对应的按键处理任务进行使用。

STM32独立按键扫描,支持同时按下、长按、快速键值_第4张图片

配置按键扫描定时器,20ms扫描一次按键

STM32独立按键扫描,支持同时按下、长按、快速键值_第5张图片

实际代码如下

按键扫描中断函数,进行按键扫描,并将有效键值放入到队列


void TIM2_IRQHandler(void)  
{  
	uint8_t key;
	uint16_t msg;
	portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	
	//按键扫描函数
	//printf("RK\r\n");//test tim2 20ms 中断
	key = ReadKey();
	if(key != NO_KEY)
	{		
		msg = MSG_KEY_DOWN | key;
		xQueueSendFromISR( segscanQueue, &msg, &xHigherPriorityTaskWoken );
	}
	portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );

}

读取按键流程如下



/******************************************************************************
* 函数名称: KeyPro()
* 功能描述: 按键扫描
* 输入参数: 
* 输出参数: 
* 返 回 值: 无
* 其它说明: 
* 修改日期      版本号      修改人     修改内容
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* 2013/02/22    V1.0.0.0       
******************************************************************************/
static unsigned char ReadKey(void)
{
	static unsigned char KeyState = 0, KeyOldValue = NO_KEY;
	static unsigned char KeyOldReturn = 0, KeyTimeCnt = 0;
	static unsigned int no_key_times = 0;
	unsigned char KeyReturn = NO_KEY, KeyCurValue = NO_KEY;
	KeyCurValue = (KEY8_VAL << 7) | (KEY7_VAL << 6) | (KEY6_VAL << 5) | (KEY5_VAL << 4) | (KEY4_VAL << 3) | (KEY3_VAL << 2) | (KEY2_VAL<<1) | KEY1_VAL; //读取6个按键值

	switch(KeyState)
	{
		case 0:
		{
            if(NO_KEY != KeyCurValue)        //检测到有键按下
            {
               KeyOldValue =  KeyCurValue;   //保存键值
               KeyState++;                   //转到消抖确认状态 
               KeyTimeCnt = 0;
			   no_key_times = 0;
            }
			else
			{
				no_key_times++;
				if(no_key_times >= 500)//500*20ms=10s
				{
					no_key_times = 0;
					KeyReturn = NO_KEY_10S_DOWN;
					KeyOldReturn = NO_KEY;
				}
			}
			break;
		}
        case 1:
		{
            if(KeyCurValue == KeyOldValue)   //和上次按键相同确认有键按下
            {
                /*switch(KeyCurValue)          //键盘编码 返回编码值
                {
					case SET_KEY_DOWN:
						KeyReturn = SET_KEY_DOWN;
					break;
					case UP_KEY_DOWN:
						KeyReturn = UP_KEY_DOWN;
					break;
					case DOWN_KEY_DOWN:
						KeyReturn = DOWN_KEY_DOWN;
					break;
					case ENTER_KEY_DOWN:
						KeyReturn = ENTER_KEY_DOWN;
					break;
					case ONOFF_KEY_DOWN:
						KeyReturn = ONOFF_KEY_DOWN;
					break;
					default:
						KeyReturn = NO_KEY;
					break;
                }*/
				KeyState++;                  //转入等待按键释放状态
				
				if(KeyCurValue == KEY7_DOWN)
				{
					KeyState = 4;//单独对时间按键进行处理
				}
				else
				{
					KeyReturn = KeyCurValue;
					KeyOldReturn = KeyReturn;
					//这里针对按键做优化处理,长按加速、和检测长按释放
					if(KeyOldReturn == KEY1_DOWN || KeyOldReturn == KEY2_DOWN)//增加快速按键值
					{
						KeyState++;	
					}
				}
            }
            else
                KeyState--;                  //两次键值不同 返回等待按键状态
            KeyTimeCnt = 0;
            KeyOldValue = KeyCurValue; 
			break;
		}
        case 2:  
		{
            if(NO_KEY == KeyCurValue)           // 按键已经释放
            {
                KeyState = 0;
                KeyOldReturn = NO_KEY; 				
            }
            else
            {

            }
			break;
		}
        case 3:
		{
            if(NO_KEY == KeyCurValue)           // 按键已经释放
            {
                KeyState = 0;
                KeyOldReturn = NO_KEY; 
            }
            else
            {
                KeyTimeCnt++;
                if(KeyTimeCnt >= 10)      
                {
                    KeyReturn = KeyOldReturn;  //增加快速键值
                    KeyTimeCnt = 0; 
                }
            }
			break;
		}
		//增加时间按键的长按、短按检测
		case 4:  
		{
			if(NO_KEY == KeyCurValue)           // 按键已经释放
			{
					KeyReturn = KEY7_DOWN;
					KeyOldReturn = KeyReturn;	
					KeyState = 0;				
			}
			else
			{
					KeyTimeCnt++;
					if(KeyTimeCnt >= 100)      
					{
							KeyReturn = KEY7_DOWN_LONG;  //检测到长按
							KeyOldReturn = KeyReturn;
						  KeyTimeCnt = 0; 
							KeyState = 5;
					}
			}
			break;
		}
		case 5:  
		{
			if(NO_KEY == KeyCurValue)           // 按键已经释放
			{
					KeyState = 0;
					KeyOldReturn = NO_KEY;	
			}
			else
			{

			}
			break;
		}
		default:
		{
			KeyState = 0;
			break;
		}
    }
    return KeyReturn;
}

按键的定义如下

这里根据原理,将按键信息写入到代码中,每次定时器中断到来都对如下管脚进行扫描,用来获取新的按键消息。

//-----------按键配置 配置-------------------------------------------------------------------

#define KEY1_VAL   			            PAin(8)//KEY_Temp+
#define KEY2_VAL  			            PBin(3)//KEY_Temp-
#define KEY3_VAL  			            PBin(15)//KEY_Threshold+
#define KEY4_VAL    			          PAin(15)//KEY_Threshold-
#define KEY5_VAL  			            PBin(14)//KEY_AutoTuning
#define KEY6_VAL   			            PAin(12)//KEY_TimeSet
#define KEY7_VAL   			            PBin(1)//
#define KEY8_VAL   			            PBin(0)


#define KEY1_DOWN                  0xFE
#define KEY1_DOWN_LONG             (KEY1_DOWN | KEY_DOWN_LONG)

#define KEY2_DOWN                  0xFD
#define KEY2_DOWN_LONG             (KEY2_DOWN | KEY_DOWN_LONG)

#define KEY3_DOWN                  0xFB
#define KEY3_DOWN_LONG             (KEY3_DOWN | KEY_DOWN_LONG)

#define KEY4_DOWN                  0xF7
#define KEY4_DOWN_LONG             (KEY4_DOWN | KEY_DOWN_LONG)

#define KEY5_DOWN                  0xEF
#define KEY5_DOWN_LONG             (KEY5_DOWN | KEY_DOWN_LONG)

#define KEY6_DOWN                  0xDF
#define KEY6_DOWN_LONG             (KEY6_DOWN | KEY_DOWN_LONG)

#define KEY7_DOWN                  0xBF
#define KEY7_DOWN_LONG             0x61

#define KEY8_DOWN                  0x7F
#define KEY8_DOWN_LONG             (KEY8_DOWN | KEY_DOWN_LONG)

#define KEY_1_4_DOWN               0xF6

#define NO_KEY_10S_DOWN            0x60
#define NO_KEY                     0XFF    

对按键消息的处理

如下代码所示,在任务中如果需要按键响应,就直接获取按键消息,如果拿到想要的按键消息,则进行按键动作处理。如下代码中如果监测到1、4按键同时按下,则进行恢复出厂设置的动作处理。

		if( xQueueReceive( pq, &Msg, 1000 ) == pdPASS )
		{
			if(MSG_KEY_DOWN == (MSG_NAME(Msg)))
			{
					key = MSG_DATA(Msg);
					if(key == KEY8_DOWN)//Esc
					{
						menu1();						
					}
					else if(key == KEY_1_4_DOWN)//回复出厂设置按键,移位按键和加按键同时按下
					{
						    //清屏
						Dispay_Clear();
						factorySetting();
					}
			}
		}

你可能感兴趣的:(嵌入式系统,stm32,按键,长按,同时按下,快速键值)