├── readme.md ├── stm32_button.c └── stm32_button.h /readme.md: -------------------------------------------------------------------------------- 1 | # STM32 Button 2 | 3 | ## 简介 4 | STM32 Button 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。 5 | 6 | 7 | ## 按键事件 8 | 9 | | 事件 | 说明 | 10 | | ---------------- | ------------------------------------ | 11 | | PRESS_DOWN | 按键按下,每次按下都触发 | 12 | | PRESS_UP | 按键弹起,每次松开都触发 | 13 | | PRESS_REPEAT | 重复按下触发,变量repeat计数连击次数 | 14 | | SINGLE_CLICK | 单击按键事件 | 15 | | DOUBLE_CLICK | 双击按键事件 | 16 | | LONG_PRESS_START | 达到长按时间阈值时触发一次 | 17 | | LONG_PRESS_HOLD | 长按期间一直触发 | 18 | 19 | 20 | ## Examples 21 | 22 | ```c 23 | #include "stm32_button.h" 24 | 25 | Button_t button1, button2; 26 | const Btn_init_attr attr = { .GPIO_PIN_x = GPIO_PIN_2, .GPIOx = GPIOC, .event = 27 | { PRESS_DOWN }, .event_num = ONE, .active_level = GPIO_PIN_RESET }; 28 | const Btn_init_attr attr2 = { .GPIO_PIN_x = GPIO_PIN_2, .GPIOx = GPIOC, .event = 29 | { PRESS_REPEAT }, .event_num = ONE, .active_level = GPIO_PIN_RESET }; 30 | 31 | button1 = button_init(&attr); 32 | button2 = button_init(&attr2); 33 | 34 | while (1) { 35 | HAL_Delay(5); // 5ms 36 | button_ticks(); 37 | } 38 | 39 | // 回调函数 40 | void button_callback(Button_t btn, PressEvent event, uint8_t repeat) { 41 | if (btn == button1) { 42 | if (event == PRESS_DOWN) { 43 | printf("PRESS_DOWN\n"); 44 | button_deInit(btn); 45 | printf("remove button1\n"); 46 | } 47 | } else if (btn == button2) { 48 | if (event == PRESS_REPEAT) { 49 | printf("PRESS_REPEAT -> %d\n", repeat); 50 | } 51 | } 52 | } 53 | ``` -------------------------------------------------------------------------------- /stm32_button.c: -------------------------------------------------------------------------------- 1 | #include "stm32_button.h" 2 | 3 | #define EVENT_CB(ev) \ 4 | if (handle->cb[ev]) \ 5 | handle->cb[ev]((Button_t)handle,(PressEvent)handle->event,(uint8_t)handle->repeat) 6 | 7 | typedef void (*BtnCallback)(void*, PressEvent, uint8_t); 8 | 9 | typedef struct { 10 | uint16_t ticks; 11 | uint8_t repeat :4; // max 15 12 | uint8_t event :4; 13 | uint8_t state :3; 14 | uint8_t debounce_cnt :3; 15 | uint8_t active_level :1; 16 | uint8_t button_level :1; 17 | GPIO_TypeDef *GPIOx; 18 | uint16_t GPIO_PIN_x; 19 | BtnCallback cb[EVENT_NUM]; 20 | struct Button *next; 21 | } Button; 22 | 23 | // button handle list head. 24 | static Button *head_handle = NULL; 25 | 26 | /** 27 | * @brief Initializes the button struct handle. 28 | * @param handle: the button handle struct. 29 | * @param attr: button attr instance. 30 | * @retval Button_t: success:handle,failed:NULL. 31 | */ 32 | Button_t button_init(const Btn_init_attr *attr) { 33 | // allocate memory. 34 | Button *thisHandle = (Button*) malloc(sizeof(Button)); 35 | if (!thisHandle) { 36 | return NULL; // allocate memory failed. 37 | } 38 | 39 | memset(thisHandle, 0, sizeof(Button)); 40 | thisHandle->event = (uint8_t) NONE_PRESS; 41 | thisHandle->GPIOx = attr->GPIOx; 42 | thisHandle->GPIO_PIN_x = attr->GPIO_PIN_x; 43 | thisHandle->active_level = (uint8_t) attr->active_level; 44 | thisHandle->button_level = !thisHandle->active_level; 45 | 46 | // Attach the button event callback function 47 | for (uint8_t i = 0; i < attr->event_num; i++) { 48 | thisHandle->cb[attr->event[i]] = 49 | (void (*)(void*, PressEvent, uint8_t)) button_callback; 50 | } 51 | 52 | // start button 53 | thisHandle->next = head_handle; 54 | head_handle = thisHandle; 55 | return (Button_t) thisHandle; // success 56 | } 57 | 58 | /** 59 | * @brief Button driver core function, driver state machine. 60 | * @param handle: the button handle struct. 61 | * @retval none. 62 | */ 63 | static void button_handler(Button *handle) { 64 | uint8_t read_gpio_level = (uint8_t) HAL_GPIO_ReadPin(handle->GPIOx, 65 | handle->GPIO_PIN_x); 66 | 67 | // ticks counter working.. 68 | if ((handle->state) > 0) 69 | handle->ticks++; 70 | 71 | /*------------button debounce handle---------------*/ 72 | if (read_gpio_level != handle->button_level) { // not equal to prev one 73 | // continue read 3 times same new level change 74 | if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) { 75 | handle->button_level = read_gpio_level; 76 | handle->debounce_cnt = 0; 77 | } 78 | } else { // leved not change ,counter reset. 79 | handle->debounce_cnt = 0; 80 | } 81 | 82 | /*-----------------State machine-------------------*/ 83 | switch (handle->state) { 84 | case 0: 85 | if (handle->button_level == handle->active_level) { // start press down 86 | handle->event = (uint8_t) PRESS_DOWN; 87 | EVENT_CB(PRESS_DOWN); 88 | handle->ticks = 0; 89 | handle->repeat = 1; 90 | handle->state = 1; 91 | } else { 92 | handle->event = (uint8_t) NONE_PRESS; 93 | } 94 | break; 95 | 96 | case 1: 97 | if (handle->button_level != handle->active_level) { // released press up 98 | handle->event = (uint8_t) PRESS_UP; 99 | EVENT_CB(PRESS_UP); 100 | handle->ticks = 0; 101 | handle->state = 2; 102 | } else if (handle->ticks > LONG_TICKS) { 103 | handle->event = (uint8_t) LONG_PRESS_START; 104 | EVENT_CB(LONG_PRESS_START); 105 | handle->state = 5; 106 | } 107 | break; 108 | 109 | case 2: 110 | if (handle->button_level == handle->active_level) { // press down again 111 | handle->event = (uint8_t) PRESS_DOWN; 112 | EVENT_CB(PRESS_DOWN); 113 | handle->repeat++; 114 | handle->event = (uint8_t) PRESS_REPEAT; 115 | EVENT_CB(PRESS_REPEAT); // repeat hit 116 | handle->ticks = 0; 117 | handle->state = 3; 118 | } else if (handle->ticks > SHORT_TICKS) { // released timeout 119 | if (handle->repeat == 1) { 120 | handle->event = (uint8_t) SINGLE_CLICK; 121 | EVENT_CB(SINGLE_CLICK); 122 | } else if (handle->repeat == 2) { 123 | handle->event = (uint8_t) DOUBLE_CLICK; 124 | EVENT_CB(DOUBLE_CLICK); // repeat hit 125 | } 126 | handle->state = 0; 127 | } 128 | break; 129 | 130 | case 3: 131 | if (handle->button_level != handle->active_level) { // released press up 132 | handle->event = (uint8_t) PRESS_UP; 133 | EVENT_CB(PRESS_UP); 134 | if (handle->ticks < SHORT_TICKS) { 135 | handle->ticks = 0; 136 | handle->state = 2; // repeat press 137 | } else { 138 | handle->state = 0; 139 | } 140 | } else if (handle->ticks > SHORT_TICKS) { 141 | handle->state = 0; 142 | } 143 | break; 144 | 145 | case 5: 146 | if (handle->button_level == handle->active_level) { 147 | // continue hold trigger 148 | handle->event = (uint8_t) LONG_PRESS_HOLD; 149 | EVENT_CB(LONG_PRESS_HOLD); 150 | } else { // released 151 | handle->event = (uint8_t) PRESS_UP; 152 | EVENT_CB(PRESS_UP); 153 | handle->state = 0; // reset 154 | } 155 | break; 156 | } 157 | } 158 | 159 | /** 160 | * @brief Stop the button work, remove the handle off work list. 161 | * @param handle: target handle struct. 162 | * @retval none. 163 | */ 164 | void button_deInit(Button_t handle) { 165 | Button **curr; 166 | for (curr = &head_handle; *curr;) { 167 | Button *entry = *curr; 168 | if (entry == (Button*) handle) { 169 | *curr = entry->next; 170 | free(entry); 171 | } else 172 | curr = &entry->next; 173 | } 174 | } 175 | 176 | /** 177 | * @brief background ticks, timer repeat invoking interval 5ms. 178 | * @param none. 179 | * @retval none. 180 | */ 181 | void button_ticks() { 182 | Button *target; 183 | for (target = head_handle; target; target = target->next) { 184 | button_handler(target); 185 | } 186 | } 187 | 188 | /** 189 | * @brief Callback Function. 190 | * @param btn: button instance. 191 | * @param event: button event. 192 | * @param repeat: button repeat. 193 | * @retval none. 194 | */ 195 | void __attribute__((weak)) button_callback(Button_t btn, PressEvent event, 196 | uint8_t repeat) { 197 | // nothing to do,please rewrite. 198 | } 199 | -------------------------------------------------------------------------------- /stm32_button.h: -------------------------------------------------------------------------------- 1 | #ifndef _STM32_BUTTON_H_ 2 | #define _STM32_BUTTON_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Modify according to your STM32 MCU/MPU 9 | #include "stm32f1xx_hal.h" 10 | 11 | // According to your need to modify the constants. 12 | #define TICKS_INTERVAL 10 // millisecond 13 | #define DEBOUNCE_TICKS 5 // MAX 8 14 | #define SHORT_TICKS (300 / TICKS_INTERVAL) 15 | #define LONG_TICKS (1000 / TICKS_INTERVAL) 16 | 17 | // button handler 18 | typedef void *Button_t; 19 | 20 | typedef enum { 21 | PRESS_DOWN = 0u, 22 | PRESS_UP, 23 | PRESS_REPEAT, 24 | SINGLE_CLICK, 25 | DOUBLE_CLICK, 26 | LONG_PRESS_START, 27 | LONG_PRESS_HOLD 28 | } PressEvent; 29 | 30 | typedef enum { 31 | ONE = 1u, TWO, THREE, FOUR, FIVE, SIX, SEVEN 32 | } EventNumber; 33 | 34 | // System only 35 | enum { 36 | EVENT_NUM = 7u, NONE_PRESS 37 | }; 38 | 39 | typedef struct { 40 | GPIO_TypeDef *GPIOx; 41 | uint16_t GPIO_PIN_x; 42 | GPIO_PinState active_level :1; 43 | PressEvent event[EVENT_NUM]; 44 | EventNumber event_num; 45 | } Btn_init_attr; 46 | 47 | #ifdef __cplusplus 48 | extern "C" 49 | { 50 | #endif 51 | 52 | Button_t button_init(const Btn_init_attr *attr); 53 | void button_deInit(Button_t handle); 54 | void button_ticks(void); 55 | void button_callback(Button_t btn, PressEvent event, uint8_t repeat); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #endif 62 | --------------------------------------------------------------------------------