Short-press, long-press, single-click, and multi-click driver implementation of the buttons in the microcontroller

In most of the key drivers that have been touched, the delay function is used to debounce, or after the key state is scanned, the event is directly processed in the key scanning program. I have been thinking about a key driver that does not use the delay function and has strong compatibility and portability. It took a long time before to finally write this driver, it is not very mature, please correct me for the shortcomings.

Before explaining, let's explain how to distinguish between buttons, short presses, long presses, single clicks, and multiple clicks. I am very grateful to Mr. Zhang Jun for the explanation of buttons in the book "Artisan Mobile Phones", which gave me a lot of inspiration.

For a detailed description, please refer to Notes 13—Keyboard Talk starting on page 180 of the Craftsman ’s Notes.

In this driver, a timer, 6 bytes of static variable resource overhead is required. The static variable is an unsigned 16-bit variable: key_longtime (for timing when the button is pressed for a long time), two unsigned 8-bit variables: key_time (the timing of the end of the button action), C (status flag), and a button flag structure body; the flow of the key scan program is as follows:


Figure 1 Key Scan Program Flow

This key scan program is executed in the timer interrupt service function with a timing of 1ms. code show as below:

key.h file

#ifndef _KEY_H
#define _KEY_H
#include "stm8l15x.h"
#include "bsp.h"

typedef enum{
  NoClick = (u8)0x01, //The button number of no button
  SingleClick = (u8)0x02, //Click
  DoubleClick = (u8)0x04, //Double click
  ThreeClick = (u8)0x08, //ThreeClick
  SingleLongClick = (u8)0x03            //长按
}KEY_NUM_FLAG;

typedef struct{
  bool state; //button state
  u8 num; //key number 
}KEY_NUM_STATE;

extern volatile KEY_NUM_STATE KEY_Flag;

#define KEY_Init() GPIO_Init(GPIOE,GPIO_Pin_7,GPIO_Mode_In_PU_No_IT)//Key I/O initialization
#define KEY GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_7) 		    //Get button status

void KEYFlag_Init(void);
void KEY_Scan(void);

#endif

key.c file

#include "key.h"

#define LONGMULTIHIT 0 //0, do not use the long-press multi-click function 1, use it (the long-press needs to be released to respond)
#define MAXKEYNUM ThreeClick //The range of key numbers

//This data is the data that puts the key scan in the interrupt with a timing of 1ms. The interrupt time is different and needs to be modified.
#define LONGCLICKTIME 500 //Long press the button to time
#define STOPCLICKTIME 200 //End button action timing

/*
 * KEY_Flag.num (key number)
 * 0000 0010 short click
 * 0000 0100 short double click
 * 0000 1000 short three hits
 * 0001 0000 short four hits
 *     .       .
 *     .       .
 *     .       .
 *
 *
 * 0000 0011 long click
 * 0000 0111 long double click
 *     .       .
 *     .       .
 *     .       .
 *
 *
 * KEY_Flag.state is TRUE to confirm the state, used to distinguish single-click and multi-click
 *
 * Be sure to clear the button number after using the button number (set to NoClick)
 *
 */

volatile KEY_NUM_STATE KEY_Flag;

/* Button initialization, executed before the program starts, can also be used to clear the button number */
void KEYFlag_Init(void)
{
	KEY_Flag.num = NoClick;
	KEY_Flag.state = FALSE;
}

/************************************************
Function name: KEY_Scan
Function: key scan, in interrupt timed 1ms
Parameters: none
Return value: None
Author: zkx
*************************************************/
void KEY_Scan(void)
{
  static u16 key_longtime = 0; // long press timing
  static u8 key_time = 0; //End action timing
  static u8 C = 255; //Key state flag
  if(C == 255) // let go detection
  {
    if(KEY == 0) //key pressed
    {
      key_longtime ++; // long press timing
      if(key_longtime>LONGCLICKTIME) //Confirm button long press
      {
        C = 1;
      }
      key_time = 0;
    }
    else if(key_longtime>50) //The button is pressed and released, short press, more than 50ms to debounce
    {
      C = 0; //short press the button
      key_time = 0;
    }
	
#if LONGMULTIHIT
   else if(KEY_Flag.num != NoClick) //After the button action is over, there is no operation
   {
     key_time++;
   }
  }
#else
  }
   if(KEY_Flag.num != NoClick) //After the key action is over, there is no operation
   {
     key_time++;
   }
#endif

  if(C!=255)
  {
    if((C!=254)&&(KEY_Flag.num<=ThreeClick)) //The key value is not operated, the key usage range
    {
      KEY_Flag.num = (KEY_Flag.num<<1)+C;
      key_longtime = 0;
      C = 254;
    }
    if(KEY)
    {
      C = 255;
    }
  }
  
  if(key_time>STOPCLICKTIME) //No action time, confirm the current state
  {
    KEY_Flag.state = TRUE;
  }
}

The usage method is described below. After the key IO initialization (KEY_Init()) and the key number initialization (KEYFlag_Init()), the key scan program (KEY_Scan()) is placed in the timer interrupt service function with a timing of 1ms. Then forget about the key scanner.

When you need to use a key, read the key number directly. There are two cases here.

1. There is no need to distinguish between multi-clicks, only single-click and long-press: directly judge the value of KEY_Flag.num where the button needs to be used, so as to judge the state of the button. At the end of use, you need to set KEY_Flag.num to NoClick

2. It is necessary to distinguish between multiple clicks: in the place where the button needs to be used, first determine whether KEY_Flag.state is TRUE, and then determine the value of KEY_Flag.num if it is TRUE, so as to determine the state of the button. The key number initialization (KEYFlag_Init()) needs to be executed at the end of use.

In order to prevent the button number KEY_Flag.num from being cluttered by mistake, you can first determine whether KEY_Flag.state is TRUE at the end of the large loop (while(1)), and then execute the button number initialization (KEYFlag_Init()) if it is TRUE. It can be seen here that this operation does not need to initialize the key number every time it is used, and it is indeed unnecessary to do so, so it is good to clear it once in a large loop. (This method is only applicable to the front-end and back-end systems)

Guess you like

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