[Single chip notes] State machine efficient key scanning, identification of short press, long press and release detection method

Buttons are one of the simplest and cheapest ways of human-computer interaction. To achieve effective scanning and processing of one or more buttons, here is the code I modified:

The implemented code mainly consists of four parts:

The first part: the initialization part of the button

void Key_Configuration(void)
{
	return;
}

Here we need to do different configuration methods according to the IC used. I use the 51 core. During the initialization process, the I/O port is quasi-bidirectional by default. If the pull-up processing is performed, the button is active at a low level, so there is no processing directly. jump out.

Part 2: Level reading of keys

//Only read the initial key level state, and further process it in the state machine
static u8 Key_Read(void)
{
    if(!READ_KEY1)  
		return KEY1_PRES;
    if(!READ_KEY2)  
		return KEY2_PRES;       
    if(!READ_KEY3)  
		return KEY3_PRES;
    if(!READ_KEY4)  
		return KEY4_PRES;
    if(!READ_KEY5)  
		return KEY5_PRES;
	
	return KEY_NONE;
}

According to the specific environment and functions used, each reading level here only reads one valid level and has a priority. It can be seen from the code that the priority order is: KEY1>KEY2>KEY3>KEY4>KEY5. Of course, how many buttons need to be used depends on the needs of the project, and how many independent buttons are theoretically supported.

The third part: the key determination part of the state machine

//state machine
static u8 Key_Scan(void)
{
	static u8 state = 0; //Key initialization state
	static u8 KEY_LAST=0,KEY_NOW=0; //Record the state of the level twice
	u8 KEY_VALUE=0;

	KEY_NOW = Key_Read();//Read key value
   
	switch(state)
	{
		case 0:
		{
			if(KEY_NOW != KEY_LAST) state = 1; //There is a button pressed
		}break;
		case 1:
		{
			if(KEY_NOW == KEY_LAST) state = 2; //The button is valid after eliminating the bucket
			else state = 0; // think false touch
		}break;
		case 2: //after the fight
		{
			if(KEY_NOW == KEY_LAST) //still pressed
			{
			  	state = 3;
			}
			else//Release, short press
			{
				state = 0;
				
				KEY_VALUE = KEY_LAST|KEY_SHORT; //Return key value short press	
			}
		}break;
		
		case 3: // Judge long press and short press
		{
			if(KEY_NOW == KEY_LAST)
			{
			    static u8 cnt = 0;
				if(cnt++ > 120) //1200ms
				{
					cnt = 0;
					state = 4;
					KEY_VALUE = KEY_LAST|KEY_LONG; //Return key value long press
				}			  
			}
			else
			{
				state = 0;
				KEY_VALUE = KEY_LAST|KEY_SHORT; //Return key value short press			
			}
		}break;
		case 4://Long press release detection
		{
			if(KEY_NOW != KEY_LAST)
				state = 0;
		}break;
	}//switch
	
	KEY_LAST = KEY_NOW; //更新
	return KEY_VALUE;
}

This part is also the entire core code part. First, three static variables are defined, the state of the button, the currently read key value KEY_NOW, the last key value KEY_LAST, and the returned valid key value KEY_VALUE. Next step by step research:

The initial value of state is 0, enter switch and case, and the time interval for each case judgment is determined by the fourth part, here I choose 10ms

Judgment 0, as long as the key value recorded last time and the key value read this time do not want to wait, enter 1. There is another function here, that is, the release detection of short presses. This is also the reason why there is no else. For details, see State 4.

Judgment 1, the key values ​​of the last time and this time are equal, note that KEY_LAST has been updated after case 0, that is to say, the key value read this time after 10ms is still equal to the last key value, here we think it is valid to press down, not caused by accidental touch and interference, enter 2 in this case. Otherwise it returns to 0.

Judgment 2, here is from 1, that is, the key key value at this time is valid, here is to judge the last updated KEY_LAST and the new key value read this time, if you don't want to wait, prove that the hand has been Release (in the case of a single key). In this way, it is recognized as a short press, the state returns to 0 and the key value of the short press is returned, at 0. If the button has not been released, there is a tendency to long press, enter 3.

Judgment 3, here is also very simple, to determine whether the key value is released, start counting every time you enter, enter once every 10ms, and count 120 times here, that is, it takes 1200ms to reach the condition and assign the returned key value to long press, and at the same time Enter 4, otherwise it will be recognized as a short press and re-enter 0 if the time is not reached.

Judgment 4, the function of the code here is mainly to detect the release of the long press. The reason is very simple. If the button is not released, then the historical key value must be equal to the current key value and not 0. After the button is released, read The key value must be 0, which jumps out of state 4.

Part 4: Entity Functions and Called Functions

static void KEY1_ShortHander(void)
{

}
static void KEY1_LongHander(void)
{

}
static void KEY2_ShortHander(void)
{

}
static void KEY2_LongHander(void)
{

}
static void KEY3_ShortHander(void)
{

}
static void KEY3_LongHander(void)
{

}
static void KEY4_ShortHander(void)
{

}
static void KEY4_LongHander(void)
{

}
static void KEY5_ShortHander(void)
{

}
static void KEY5_LongHander(void)
{

}


void Key_Hander(void) //Key handling function
{
	u8 KEY_NUM=0;
	static u32 LAST=0;
	if(Systick_ms-LAST<10)	return;
	LAST = Systick_ms;	
	
	KEY_NUM = Key_Scan(); //Key scan value
	if(KEY_NUM == KEY_NONE) return;
	
	// there is a button pressed
	if(KEY_NUM & KEY_SHORT) //短按
	{    
		if(KEY_NUM & KEY1_PRES)//KEY1_PRES
		{				
			KEY1_ShortHander();
		}
		else if(KEY_NUM & KEY2_PRES)//KEY2_PRES
		{
			KEY2_ShortHander();
		}		
		else if(KEY_NUM & KEY3_PRES)//KEY3_PRES
		{
			KEY3_ShortHander();
		}
		else if(KEY_NUM & KEY4_PRES)//KEY4_PRES
		{
			KEY4_ShortHander();
		}
		else if(KEY_NUM & KEY5_PRES)//KEY5_PRES
		{
			KEY5_ShortHander();
		}
	}
	else if(KEY_NUM & KEY_LONG) //长按
	{
		if(KEY_NUM & KEY1_PRES)//KEY1_PRES
		{
			KEY1_LongHander();
		}
		else if(KEY_NUM & KEY2_PRES)//KEY2_PRES
		{
			KEY2_LongHander();
		}		
		else if(KEY_NUM & KEY3_PRES)//KEY3_PRES
		{
			KEY3_LongHander();
		}
		else if(KEY_NUM & KEY4_PRES)//KEY4_PRES
		{
			KEY4_LongHander();
		}
		else if(KEY_NUM & KEY5_PRES)//KEY5_PRES
		{
			KEY5_LongHander();
		}
	}	
}

This part of the code is relatively clear. Through the analysis of the previous three parts, the result of the key value obtained by calling the first three parts is called here, and then the result is determined to be processed by the corresponding function and function. The function body is directly given here, and the user can directly Add the response code in the function body. But a more efficient way should be to replace the execution function with a marked variable, so that the program execution will be more organized and not occupy time slices incorrectly.

Finally attach the header file:

#ifndef __FY_KEY_H
#define __FY_KEY_H

#include "fy_includes.h"

#define READ_KEY1	P30
#define READ_KEY2	P14
#define READ_KEY3	P13
#define READ_KEY4	P10
//#define READ_KEY5	P17

#define KEY1_PRES 0x01
#define KEY2_PRES 0x02
#define KEY3_PRES 0x04
#define KEY4_PRES 0x08
#define KEY5_PRES 0x10

#define KEY_SHORT  0x40
#define KEY_LONG   0x80
#define KEY_NONE      0

#define HOT_KEY_PRES	KEY1_PRES
#define PWR_KEY_PRES	KEY2_PRES
#define H2_KEY_PRES	KEY3_PRES
#define MP3_KEY_PRES	KEY4_PRES

void Key_Configuration(void);
void Key_Hander(void);

#endif

/*********************************************END OF FILE**********************************************/

 By Urien March 19, 2018 10:47:36

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325859166&siteId=291194637