Multi-key state machine

1. Simple Key detection

I remember when beginning to learn microcontroller, write key scanning is this:

if(KEY1 == 0)
{
    delay_ms(20);
    if(KEY1 == 0)
    {
        while(KEY1 == 0);
        // 按键按下处理代码
    }
}

Look, there is a 20ms eliminate jitter time, that I will die here 20ms, etc., as well as wait for key release, and I'm letting go, how can you like? No way can only do overtime. I want to do a long press 1s it? Extremely thin Si fear, for the application of the actual project is a very bad thing, it will not only slow down your entire system, there will be multiple keys sometimes undetectable problem. There is no better way to achieve it? The answer is yes, think about it, if the 20ms delay timer to do, not can it! ! !

2. State Machine

First we have to understand what is the state machine? The degree course is to ask your mother! ! !
The state machine can be grouped into four elements, i.e. , current state, condition, action, state times . This induction, mainly for reasons of internal causal link state machine. "Current state" and "conditions" due to the "operation" and "Next State" is the fruit . Detailed follows:
current state : is a state in which the current.
Conditions : migration when a condition is met, it will trigger an action, or to perform a state.
Action : action after the conditions are met to perform. After the operation is finished, you can migrate to a new state, you can still keep the original state. Operation is not required, when the condition is satisfied, may not perform any action, directly migrated to a new state.
NEXT STATE : new state after the conditions are met to move. "Second state" with respect to the "current state" is concerned, the "Next State" Once activated, is transformed into a new "current state" of the.
With the support of the theory, there is not found, the state machine of this mechanism, in fact, can run into many scenes of the matter, it is not limited to the keys.

3. The key structure and enumeration

Key state can be divided into: fails, press, press, raised four. Of course, you can also press their own demands to points. Ado, directly on the code analysis.

typedef enum _KEY_STATUS_LIST
{
    KEY_NULL = 0x00,
    KEY_SURE = 0x01,
    KEY_UP   = 0x02,
    KEY_DOWN = 0x04,
    KEY_LONG = 0x08,
}KEY_STATUS_LIST;

I here defines a key state enumeration, comprising five elements, KEY_NULLindicates no action KEY_SUREto confirm a state, KEY_UPrepresents the key lift,
KEY_DOWNshowing the button is pressed, KEY_LONGit represents a long press.

typedef enum _KEY_LIST
{
    KEY0,
    KEY1,
    KEY2,
    KEY_NUM,
}KEY_LIST;

Again an enumeration, here are your keys, KEY_NUMyou can automatically count the number of keys you here KEY_NUM value of 3, as to why I do not say that he asked of the mother. Why use enumerate here? Here throwing a brick, read on.

typedef struct _KEY_COMPONENTS
{
    uint8_t KEY_SHIELD;       //按键屏蔽0:屏蔽,1:不屏蔽
    uint8_t KEY_COUNT;        //按键长按计数
    uint8_t KEY_LEVEL;        //虚拟当前IO电平,按下1,抬起0
    uint8_t KEY_DOWN_LEVEL;   //按下时IO实际的电平
    uint8_t KEY_STATUS;       //按键状态
    uint8_t KEY_EVENT;        //按键事件
    uint8_t (*READ_PIN)(void);//读IO电平函数
}KEY_COMPONENTS;
extern KEY_COMPONENTS Key_Buf[KEY_NUM];

KEY_SHIELDShielding the key, the key is not used represents 0, 1 indication; KEY_COUNTpress counters, like the stopwatch, and then starts counting the press; KEY_LEVELvirtual key depressed level KEY_DOWN_LEVEL, the actual key depression IO level, both variable, mainly in order to function unified package, for example, you press a button high, a low-level press, I do not care so much, anyway, I am with you KEY_DOWN_LEVELvalues, equal I think you hit, then the KEY_LEVELset, on the contrary it is clear; KEY_STATUSwhat we call a key state, which is responsible for recording a time key state; KEY_EVENTrepresents a key event, I've divided the three events, there are press, lift and press. (*READ_PIN)Is a function pointer variables, you need to read IO function interface to it.
Finally, do not forget, this structure is defined using the variables (here is the statement oh), there are several keys to define the structure of several types of variables. We found no used here KEY_NUM, the benefits of a , do not need to increase the key changes.

4. Function keys IO

First level IO ports have to read it.

static uint8_t KEY0_ReadPin(void)
{
    return _KEY0;
}

static uint8_t KEY1_ReadPin(void)
{
    return _KEY1;
}

static uint8_t KEY2_ReadPin(void)
{
    return _KEY2;
}

This is very simple, that is your IO port level back to me on it. You can go to achieve according to their own microcontroller. With the structure type variables of these functions can not be defined yet.

KEY_COMPONENTS Key_Buf[KEY_NUM] = {
{1,0,0,0,KEY_NULL,KEY_NULL,KEY0_ReadPin},
{1,0,0,0,KEY_NULL,KEY_NULL,KEY1_ReadPin},
{1,0,0,0,KEY_NULL,KEY_NULL,KEY2_ReadPin},
};

This is not to say, against the above structure description to see that, and I pressed the button here are low.
The real key level IO functions get here

static void Get_Key_Level(void)
{
    uint8_t i;
    
    for(i = 0;i < KEY_NUM;i++)
    {
        if(Key_Buf[i].KEY_SHIELD == 0)
            continue;
        if(Key_Buf[i].READ_PIN() == Key_Buf[i].KEY_DOWN_LEVEL)
            Key_Buf[i].KEY_LEVEL = 1;
        else
            Key_Buf[i].KEY_LEVEL = 0;
    }
}

This function is mainly achieved package, two-step, first determine whether the key is enabled, each key level IO. If I add the key here want to change it? All without moving.

The key state machine

Focus here, prepare so much, finally the key state machine code implementation.

void ReadKeyStatus(void)
{
    uint8_t i;
    
    Get_Key_Level();
    
    for(i = 0;i < KEY_NUM;i++)
    {
        switch(Key_Buf[i].KEY_STATUS)
        {
            //状态0:没有按键按下
            case KEY_NULL:
                if(Key_Buf[i].KEY_LEVEL == 1)//有按键按下
                {
                    Key_Buf[i].KEY_STATUS = KEY_SURE;//转入状态1
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                else
                {
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;
            //状态1:按键按下确认
            case KEY_SURE:
                if(Key_Buf[i].KEY_LEVEL == 1)//确认和上次相同
                {
                    Key_Buf[i].KEY_STATUS = KEY_DOWN;//转入状态2
                    Key_Buf[i].KEY_EVENT = KEY_DOWN;//按下事件
                    Key_Buf[i].KEY_COUNT = 0;//计数器清零
                }
                else
                {
                    Key_Buf[i].KEY_STATUS = KEY_NULL;//转入状态0
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;
            //状态2:按键按下
            case KEY_DOWN:
                if(Key_Buf[i].KEY_LEVEL != 1)//按键释放,端口高电平
                {
                    Key_Buf[i].KEY_STATUS = KEY_NULL;//转入状态0
                    Key_Buf[i].KEY_EVENT = KEY_UP;//松开事件
                }
                else if((Key_Buf[i].KEY_LEVEL == 1) && (++Key_Buf[i].KEY_COUNT >= KEY_LONG_DOWN_DELAY)) //超过KEY_LONG_DOWN_DELAY没有释放
                {
                    Key_Buf[i].KEY_STATUS = KEY_LONG;//转入状态3
                    Key_Buf[i].KEY_EVENT = KEY_LONG;//长按事件
                    Key_Buf[i].KEY_COUNT = 0;//计数器清零
                }
                else
                {
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;
            //状态3:按键连续按下
            case KEY_LONG:
                if(Key_Buf[i].KEY_LEVEL != 1)//按键释放,端口高电平
                {
                    Key_Buf[i].KEY_STATUS = KEY_NULL;//转入状态0
                    Key_Buf[i].KEY_EVENT = KEY_UP;//松开事件
                    Key_Buf[i].KEY_EVENT = KEY_NULL;
                }
                else if((Key_Buf[i].KEY_LEVEL == 1) 
                && (++Key_Buf[i].KEY_COUNT >= KEY_LONG_DOWN_DELAY)) //超过KEY_LONG_DOWN_DELAY没有释放
                {
                    Key_Buf[i].KEY_EVENT = KEY_LONG;//长按事件
                    Key_Buf[i].KEY_COUNT = 0;//计数器清零
                }
                else
                {
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;
        }
    }
}

This is to get the current status of all keys, called once every 20ms, on it (use the timer).

            //状态0:没有按键按下
            case KEY_NULL:
                if(Key_Buf[i].KEY_LEVEL == 1)//有按键按下
                {
                    Key_Buf[i].KEY_STATUS = KEY_SURE;//转入状态1
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                else
                {
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;

Described herein do take a button, first come first IO level for the key, the key from the start state KEY_NULLstarts ( current state ), when the button is pressed ( conditions ), into KEY_SURE( NEXT STATE ), completing the first step exits;

            //状态1:按键按下确认
            case KEY_SURE:
                if(Key_Buf[i].KEY_LEVEL == 1)//确认和上次相同
                {
                    Key_Buf[i].KEY_STATUS = KEY_DOWN;//转入状态2
                    Key_Buf[i].KEY_EVENT = KEY_DOWN;//按下事件
                    Key_Buf[i].KEY_COUNT = 0;//计数器清零
                }
                else
                {
                    Key_Buf[i].KEY_STATUS = KEY_NULL;//转入状态0
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;

Timer to 20ms, just to shake, came in from the new state KEY_SUREbegan, and then judge whether the current button is pressed, if not pressed, then returned KEY_NULL, indicating that the last interference, if the button is pressed, it press to enter the real state KEY_DOWN, and we give KEY_EVENTthe event an assignment, I identify triggers a press event, KEY_COUNTcleared in preparation for the press count, then quit, you can determine this variable outside the event to decide whether to perform what tasks ( action );

            //状态2:按键按下
            case KEY_DOWN:
                if(Key_Buf[i].KEY_LEVEL != 1)//按键释放,端口高电平
                {
                    Key_Buf[i].KEY_STATUS = KEY_NULL;//转入状态0
                    Key_Buf[i].KEY_EVENT = KEY_UP;//松开事件
                }
                else if((Key_Buf[i].KEY_LEVEL == 1) 
                && (++Key_Buf[i].KEY_COUNT >= KEY_LONG_DOWN_DELAY)) //超过KEY_LONG_DOWN_DELAY没有释放
                {
                    Key_Buf[i].KEY_STATUS = KEY_LONG;//转入状态3
                    Key_Buf[i].KEY_EVENT = KEY_LONG;//长按事件
                    Key_Buf[i].KEY_COUNT = 0;//计数器清零
                }
                else
                {
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;

Then, after 20ms in, this time from the state KEY_DOWNbegin to determine whether the button is released, if it is released into the state KEY_NULL, while the mark for the event KEY_UP, if not released, we will count, while clearing the data symbol, the other the same, because we is 条件not met, no state transition, note that each time there is no change to come clear the event, or you go out to judge the mark and trigger action;

            //状态2:按键按下
            case KEY_DOWN:
                if(Key_Buf[i].KEY_LEVEL != 1)//按键释放,端口高电平
                {
                    Key_Buf[i].KEY_STATUS = KEY_NULL;//转入状态0
                    Key_Buf[i].KEY_EVENT = KEY_UP;//松开事件
                }
                else if((Key_Buf[i].KEY_LEVEL == 1) 
                && (++Key_Buf[i].KEY_COUNT >= KEY_LONG_DOWN_DELAY)) //超过KEY_LONG_DOWN_DELAY没有释放
                {
                    Key_Buf[i].KEY_STATUS = KEY_LONG;//转入状态3
                    Key_Buf[i].KEY_EVENT = KEY_LONG;//长按事件
                    Key_Buf[i].KEY_COUNT = 0;//计数器清零
                }
                else
                {
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;

After 20ms come Similarly, assuming press, or from the KEY_DOWNbeginning, the count value accumulated, when the accumulated set value, such as 25, i.e. 500ms, to meet the press 条件, long press state to migrate KEY_LONG, marked event KEY_LONG;

            //状态3:按键连续按下
            case KEY_LONG:
                if(Key_Buf[i].KEY_LEVEL != 1)//按键释放,端口高电平
                {
                    Key_Buf[i].KEY_STATUS = KEY_NULL;//转入状态0
                    Key_Buf[i].KEY_EVENT = KEY_UP;//松开事件
                    Key_Buf[i].KEY_EVENT = KEY_NULL;
                }
                else if((Key_Buf[i].KEY_LEVEL == 1) 
                && (++Key_Buf[i].KEY_COUNT >= KEY_LONG_DOWN_DELAY)) //超过KEY_LONG_DOWN_DELAY没有释放
                {
                    Key_Buf[i].KEY_EVENT = KEY_LONG;//长按事件
                    Key_Buf[i].KEY_COUNT = 0;//计数器清零
                }
                else
                {
                    Key_Buf[i].KEY_EVENT = KEY_NULL;//空事件
                }
                break;

After 20ms, into the state KEY_LONG, to determine whether the same release, release it into the KEY_NULLstate, marking release event, or continue to determine whether the long press. I am here to do is to have been pressed, it returns a press event every 500ms.
This is the whole state machine, this function does not want us to change, keys to increase or decrease does not affect me achieve this function, this is what I do in front of a bunch of enumeration, the benefits of the structure, function package, the benefits of two .

6. The key handler

Earlier we did a bunch of marking the event, the main function is to give us to do processing here is my key processing, on the main loop in on it.

void Task_KEY_Scan(void)
{
    ReadKeyStatus();
    
    if(Key_Buf[KEY0].KEY_EVENT == KEY_UP)
    {
        printf("KEY0 Down\n");
    }
    else if(Key_Buf[KEY0].KEY_EVENT == KEY_LONG)
    {
        printf("KEY0 Long Down\n");
    }
    
    if(Key_Buf[KEY1].KEY_EVENT == KEY_UP)
    {
        printf("KEY1 Down\n");
    }
    else if(Key_Buf[KEY1].KEY_EVENT == KEY_LONG)
    {
        printf("KEY1 Long Down\n");
    }
    
    if(Key_Buf[KEY2].KEY_EVENT == KEY_UP)
    {
        printf("KEY2 Down\n");
    }
    else if(Key_Buf[KEY2].KEY_EVENT == KEY_LONG)
    {
        printf("KEY2 Long Down\n");
    }
}

This is relatively simple, has been circulating decision marks each event, what kind of event you need to do comparison, the mark appears on the execution of your code. There are not found, here I can know which button labeled events, this is the ultimate usefulness of the enumeration, three benefits .
To end here, what lack of welcome to point out the comments below, there are good suggestions can be made, we learn together. Good things to share to everyone, will be back to update some practical things for everyone, I like to point attention Oh! ! ! ^ _ ^

Guess you like

Origin www.cnblogs.com/ZzJan/p/11334869.html