Article directory
- Development board
- development environment
- Preface
- Anti-shake button
-
- Button hardware schematic diagram
- Software delay implementation ideas
- Purpose
- code
-
- Button status
- Key information
- Definitions related to buttons
- Button underlying configuration and status acquisition
- Summarize
Development board
Punctual Atomic STM32F103ZET6 battleship
Development environment
stm32cubeMX Clion
Foreword
When the microcontroller uses buttons, in order to eliminate the jitter of the buttons, the delay method is often used. However, this method puts the CPU in a waiting state and cannot perform other tasks until the delay function is completed. This blocking method is not conducive to Multi-tasking situation. Therefore, using a state machine can achieve non-blocking, allowing the CUP to perform tasks more efficiently.
Button vibration elimination
Key hardware schematic diagram
KEY0 KEY1 KEY2 is used this time
From the schematic diagram, we can see that when we press the button, it shows a low level, and the ideal waveform is as follows:
Actual waveform:
So we want to eliminate the jitter when pressing down and lifting up. For this jitter, our software delays 5-10ms to confirm the level status again. If it is still low level, it is considered that the button is pressed. Lift it up and the same as above.
Software delay implementation ideas
First of all, we abandon the direct delay_ms(10) method. We want to achieve a delay of about 10ms without blocking the CPU. We can adopt the following methods:
You can start a timer with a period of 10, and make a judgment in the callback function.
Here I use to obtain the tick count of the tick timer interrupt. When the difference between the two obtained ticks is 10, the above effect is achieved. This experiment will also implement long press detection. When the difference reaches the set value, it can be judged. Currently in long press state. Therefore, it is more convenient to use the system tick timer interrupt counting method. The specific implementation can be viewed in the code.
Experimental purpose
Implement the state machine method to detect the status of each button and implement the corresponding function (supports long press)
When KEY0 is pressed, the red light turns on. When you release it, the red light turns off. If you press and hold, the red light stays on until you release it.
Press KEY1 and the green light will be on. Release it and the green light will go off. Press and hold and the green light will stay on until you release it.
Press KEY0, the buzzer will sound, and the buzzer will turn off. If you press and hold, the buzzer will sound until you release it.
Code
Key status
//Button state machine typedef enum _KeyState {<!-- --> KEY_NULL = 0, //Indicates no key pressed KEY_DOWN_JUDDER, //Jitter when pressed KEY_DOWN, //Press KEY_LONG, //Long press KEY_UP_JUDDER, //raise jitter KEY_UP, //raise }KeyState;
Key information
This experiment uses three buttons
//Button ID typedef enum _KeyId {<!-- --> KEY_ID_0 = 0, KEY_ID_1 , KEY_ID_2 , KEY_ID_MAX }KeyId;
//Button structure typedef struct _keyInfo{<!-- --> KeyState keyState; //Key state machine uint8_t preKeyState; //The state of the last key press int32_t preTime; //Last time int32_t curTime; //Current time }KeyInfo;
Key related definitions
Can be modified by yourself
#define KEYPRESS 1 //Press level #define KEYRELEASE 0 //Raise the level #define KEYPRESSTIME 10 //Eliminate jitter for 10ms #define KEYLONGPRESSTIME 1000 //Short press for more than 1 second counts as long press
Button underlying configuration and status acquisition
bsp_key.c
/********************************************** ************************************ * @author: majj * @email: [email protected] * @date: 2023/9/8 21:13 * @version: 2.0 * @description: *************************************************** ******************************/ #include "bsp_key.h" KeyInfo keyInfo[KEY_ID_MAX]; KeyValueBuffer keyValueBuffer[KEY_ID_MAX]; /** * Get the key level value * @param keyID * @return */ GPIO_PinState GetKeyValue(uint8_t keyID) {<!-- --> GPIO_PinState value = GPIO_PIN_RESET; switch (keyID) {<!-- --> case KEY_ID_0: value = HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin); break; case KEY_ID_1: value = HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin); break; case KEY_ID_2: value = HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin); break; default: break; } return value; } /** * Write once status * @param keypad_status */ void KeyValueWrite(uint8_t id,KeyState keypad_status) {<!-- --> keyValueBuffer[id].valBuffer[keyValueBuffer[id].write] = keypad_status; if ( + + keyValueBuffer[id].write >= KEY_ID_MAX) {<!-- --> keyValueBuffer[id].write = 0; } } /** * Read the status once * @return */ KeyState KeyValueRead(uint8_t id) {<!-- --> KeyState key_event; if (keyValueBuffer[id].read == keyValueBuffer[id].write) {<!-- --> return KEY_NULL; } else {<!-- --> key_event = keyValueBuffer[id].valBuffer[keyValueBuffer[id].read]; if ( + + keyValueBuffer[id].read >= KEY_ID_MAX) {<!-- --> keyValueBuffer[id].read = 0; } return key_event; } } /** * Determine whether the case is pressed * @param keyId * @return */ uint8_t GetKeyIOState(KeyId keyId) {<!-- --> GPIO_PinState key_value = GetKeyValue(keyId); if (key_value == GPIO_PIN_RESET){<!-- --> return KEYPRESS; // The key is pressed }else{<!-- --> return KEYRELEASE; // Release the key } } /** * */ void KeyInfoInit() {<!-- --> for (int i = 0; i < KEY_ID_MAX; i + + ) {<!-- --> keyInfo[i].keyState = KEY_NULL; keyInfo[i].preKeyState = KEY_NULL; keyInfo[i].preTime = 0; keyInfo[i].curTime = 0; } }
bsp_key.h
/********************************************** ************************************ * @author: majj * @email: [email protected] * @date: 2023/9/8 21:13 * @version: 2.0 * @description: *************************************************** ******************************/ #ifndef KEY_STATE_BSP_KEY_H #define KEY_STATE_BSP_KEY_H #include "gpio.h" #include "Retarget.h" #define KEYPRESS 1 //Press level #define KEYRELEASE 0 //Raise the level #define KEYPRESSTIME 10 //Eliminate jitter for 10ms #define KEYLONGPRESSTIME 1000 //Short press for more than 1 second counts as long press //Button ID typedef enum _KeyId {<!-- --> KEY_ID_0 = 0, KEY_ID_1 , KEY_ID_2 , KEY_ID_MAX }KeyId; //Button state machine typedef enum _KeyState {<!-- --> KEY_NULL = 0, //Indicates no key pressed KEY_DOWN_JUDDER, //Jitter when pressed KEY_DOWN, //Press KEY_LONG, //Long press KEY_UP_JUDDER, //raise jitter KEY_UP, //raise }KeyState; //buffer structure typedef struct _KeyValueBuffer { GPIO_PinState valBuffer[KEY_ID_MAX]; //Key value buffer uint8_t read; //Buffer read pointer 1 uint8_t write; //buffer write pointer 1 } KeyValueBuffer; //Button structure typedef struct _keyInfo{<!-- --> KeyState keyState; //Key state machine uint8_t preKeyState; //The state of the last key press int32_t preTime; //Last time int32_t curTime; //Current time }KeyInfo; extern KeyInfo keyInfo[KEY_ID_MAX]; void KeyInfoInit(void); uint8_t GetKeyIOState(KeyId keyId); void KeyValueWrite(uint8_t id,KeyState keypad_status); KeyState KeyValueRead(uint8_t id); #endif //KEY_STATE_BSP_KEY_H
bsp_led.c
/********************************************** ************************************ * @author: majj * @email: [email protected] * @date: 2023/9/8 21:13 * @version: 2.0 * @description: *************************************************** ******************************/ #include "bsp_led.h" /** * Set LED0 status * @param flag */ void SetLed0State(uint8_t flag) {<!-- --> if(flag){<!-- --> HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_RESET); }else{<!-- --> HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET); } } /** * Set LED1 status * @param flag */ void SetLed1State(uint8_t flag) {<!-- --> if(flag){<!-- --> HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET); }else{<!-- --> HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET); } }
bsp_beep.c
/********************************************** ************************************ * @author: majj * @email: [email protected] * @date: 2023/9/8 21:14 * @version: 2.0 * @description: *************************************************** ******************************/ #include "bsp_beep.h" void SetBeepState(uint8_t flag) {<!-- --> if(flag){<!-- --> HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_SET); } else{<!-- --> HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_RESET); } }
Button state machine polling task
/********************************************** ************************************ * @author: majj * @email: 1289[email protected] * @date: 2023/9/8 22:36 * @version: 2.0 * @description: *************************************************** ******************************/ #include "key_status_controller.h" /** * Perform keystroke tasks * @param keyID * @param keyState */ void OperateKeyTask(KeyId keyID,KeyState keyState) {<!-- --> switch (keyID) {<!-- --> case KEY_ID_0: if(keyState == KEY_DOWN){<!-- --> SetLed0State(1); }else if(keyState == KEY_LONG){<!-- --> printf("KEY0 --long press\r\ "); SetLed0State(1); } else if(keyState ==KEY_NULL){<!-- --> SetLed0State(0); } break; case KEY_ID_1: if(keyState == KEY_DOWN){<!-- --> SetLed1State(1); }else if(keyState == KEY_LONG){<!-- --> SetLed1State(1); } else if(keyState ==KEY_NULL){<!-- --> SetLed1State(0); } break; case KEY_ID_2: if(keyState == KEY_DOWN){<!-- --> SetBeepState(1); }else if(keyState == KEY_LONG){<!-- --> printf("KEY2 --long press\r\ "); SetBeepState(1); } else if(keyState ==KEY_NULL ){<!-- --> SetBeepState(0); } break; default: break; } } /** * Detect button * @param keyID */ void DetectKeyState(KeyId keyID) {<!-- --> //Read key status information KeyState keyState = keyInfo[keyID].preKeyState; //Read key level changes uint8_t keyValue = GetKeyIOState(keyID); switch (keyState) {<!-- --> case KEY_NULL: if(keyValue == KEYPRESS){<!-- --> //Detect button press keyInfo[keyID].keyState = KEY_DOWN_JUDDER; keyInfo[keyID].preTime = keyInfo[keyID].curTime; } else{<!-- --> keyInfo[keyID].curTime = 0; keyInfo[keyID].preTime = 0; } break; case KEY_DOWN_JUDDER: if(keyValue == KEYPRESS){<!-- --> keyInfo[keyID].curTime = HAL_GetTick(); if((keyInfo[keyID].curTime - keyInfo[keyID].preTime) > KEYPRESSTIME){<!-- --> //Confirm press keyInfo[keyID].keyState = KEY_DOWN; keyInfo[keyID].preTime = keyInfo[keyID].curTime; } } else{<!-- --> keyInfo[keyID].keyState = KEY_NULL; } break; case KEY_DOWN: keyInfo[keyID].curTime = HAL_GetTick(); if((keyInfo[keyID].curTime - keyInfo[keyID].preTime) > KEYLONGPRESSTIME){<!-- --> //Confirm press keyInfo[keyID].keyState = KEY_LONG; keyInfo[keyID].preTime = keyInfo[keyID].curTime; }else{<!-- --> //dog if(keyValue == KEYRELEASE){<!-- --> keyInfo[keyID].keyState = KEY_UP_JUDDER; keyInfo[keyID].preTime = keyInfo[keyID].curTime; } } break; case KEY_LONG: keyInfo[keyID].curTime = HAL_GetTick(); if(keyValue == KEYRELEASE){<!-- --> //raise jitter keyInfo[keyID].keyState = KEY_UP_JUDDER; keyInfo[keyID].curTime = HAL_GetTick(); keyInfo[keyID].preTime = keyInfo[keyID].curTime; } break; case KEY_UP_JUDDER: if(keyValue == KEYRELEASE){<!-- --> keyInfo[keyID].curTime = HAL_GetTick(); if((keyInfo[keyID].curTime - keyInfo[keyID].preTime) > KEYPRESSTIME){<!-- --> //Confirm to lift keyInfo[keyID].keyState = KEY_UP; KeyValueWrite(keyID,keyInfo[keyID].keyState); } } break; case KEY_UP: keyInfo[keyID].keyState = KEY_NULL; KeyValueWrite(keyID,keyInfo[keyID].keyState); break; default: break; } //Execute action tasks OperateKeyTask(keyID,keyState); //Record this status keyInfo[keyID].preKeyState = keyInfo[keyID].keyState; } /** * Scan button */ void KeyScanTask() {<!-- --> for(int i = 0; i < KEY_ID_MAX; i + + ){<!-- --> DetectKeyState((KeyId)i); } }
Summary
The above is a simple and rough way to implement the button state machine. There are many shortcomings and it is for reference only. This routine uses stm32cube + clion and cannot be run directly on keil5, so the entire file is not uploaded.