diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b3dbd27..5b94ab8 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -4,12 +4,13 @@ list(APPEND incs "../components/system/source") list(APPEND incs "../components/system/source/k_kit") list(APPEND incs "../components/system/source/shell") -list(APPEND srcs "../app/app_main.c") -list(APPEND srcs "../app/console.c") -list(APPEND srcs "../app/drivers/data_port/sb_data_port.c") -list(APPEND srcs "../app/drivers/data_port/uart/uart_port.c") -list(APPEND srcs "../app/button/button_event.c") -list(APPEND srcs "../app/config/board_config.c") +list(APPEND srcs "app_main.c") +list(APPEND srcs "console.c") +list(APPEND srcs "drivers/data_port/sb_data_port.c") +list(APPEND srcs "drivers/data_port/uart/uart_port.c") +list(APPEND srcs "button/button.c") +list(APPEND srcs "button/multi_button.c") +list(APPEND srcs "config/board_config.c") idf_component_register( INCLUDE_DIRS ${incs} diff --git a/app/app_main.c b/app/app_main.c index 7464d0e..50f28bc 100644 --- a/app/app_main.c +++ b/app/app_main.c @@ -12,7 +12,7 @@ #include "esp_err.h" /* 通用模块 */ -#include "button/button_event.h" +#include "button/button.h" /* 应用模块 */ @@ -30,7 +30,7 @@ nvs_handle g_nvs_hdl; // nvs 句柄 os_work_q_t g_work_q_hdl_low; // 低于 default_os_work_q_hdl 优先级的工作队列 static void _init_nvs(void); -static int _change_mode_event_button(const button_event_t *event); +static void _change_mode_event_button(button_hdl_t *handle, press_event_t event); static void _vset_cb(sh_t *sh_hdl); void work_app_main(void *arg) @@ -39,9 +39,7 @@ void work_app_main(void *arg) _init_nvs(); /* 初始化按键检测和控制 */ - button_init(PIN_BIT(g_cfg_board->key_boot.pin), g_cfg_board->key_boot.en_lev); - - button_event_add_callback(g_cfg_board->key_boot.pin, _change_mode_event_button); + btn_attach(&g_cfg_board->key_boot, _change_mode_event_button, EVENT_PRESS_UP | EVENT_PRESS_DOWN | EVENT_LONG_PRESS_HOLD); /* 启动 shell */ SYS_LOG_INF("app start"); @@ -59,19 +57,17 @@ static void _init_nvs(void) } } -static int _change_mode_event_button(const button_event_t *event) +static void _change_mode_event_button(button_hdl_t *handle, press_event_t event) { static const char *const stat_tab[] = { - [BUTTON_UP] = "up", - [BUTTON_DOWN] = "down", - [BUTTON_HELD] = "held", + [PRESS_UP] = "up", + [PRESS_DOWN] = "down", + [LONG_PRESS_HOLD] = "held", }; - if (event->event < __ARRAY_SIZE(stat_tab)) + if (event < __ARRAY_SIZE(stat_tab) && stat_tab[event]) { - SYS_LOG_DBG("button stat: %s", stat_tab[event->event]); + SYS_LOG_DBG("button stat: %s", stat_tab[event]); } - - return 0; } static void _vset_cb(sh_t *sh_hdl) diff --git a/app/button/README.md b/app/button/README.md new file mode 100644 index 0000000..d588c5b --- /dev/null +++ b/app/button/README.md @@ -0,0 +1,131 @@ +# MultiButton + +## 简介 +MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。 + +## 使用方法 +1.先申请一个按键结构 + +```c +button_hdl_t button1; +``` +2.初始化按键对象,绑定按键的GPIO电平读取接口**read_button_pin()** ,后一个参数设置有效触发电平 + +```c +button_init(&button1, read_button_pin, 0, 0); +``` +3.注册按键事件 + +```c +button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler); +button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler); +... +``` +4.启动按键 + +```c +button_start(&button1); +``` +5.设置一个5ms间隔的定时器循环调用后台处理函数 + +```c +while(1) { + ... + if(timer_ticks == 5) { + timer_ticks = 0; + + button_ticks(); + } +} +``` + +## 特性 + +MultiButton 使用C语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理: + +```c +struct Button { + uint16_t ticks; + uint8_t repeat: 4; + uint8_t event : 4; + uint8_t state : 3; + uint8_t debounce_cnt : 3; + uint8_t active_level : 1; + uint8_t button_level : 1; + uint8_t button_id; + uint8_t (*hal_button_Level)(uint8_t button_id_); + button_callback_fn cb[number_of_event]; + struct Button* next; +}; +``` +这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。 + + +## 按键事件 + +事件 | 说明 +---|--- +PRESS_DOWN | 按键按下,每次按下都触发 +PRESS_UP | 按键弹起,每次松开都触发 +PRESS_REPEAT | 重复按下触发,变量repeat计数连击次数 +SINGLE_CLICK | 单击按键事件 +DOUBLE_CLICK | 双击按键事件 +LONG_PRESS_START | 达到长按时间阈值时触发一次 +LONG_PRESS_HOLD | 长按期间一直触发 + + +## Examples + +```c +#include "button.h" + +unit8_t btn1_id = 0; + +struct Button btn1; + +uint8_t read_button_GPIO(uint8_t button_id) +{ + // you can share the GPIO read function with multiple Buttons + switch(button_id) + { + case btn1_id: + return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin); + break; + + default: + return 0; + break; + } +} +void BTN1_PRESS_DOWN_Handler(void* btn) +{ + //do something... +} + +void BTN1_PRESS_UP_Handler(void* btn) +{ + //do something... +} + +... + +int main() +{ + button_init(&btn1, read_button_GPIO, 0, btn1_id); + button_attach(&btn1, PRESS_DOWN, BTN1_PRESS_DOWN_Handler); + button_attach(&btn1, PRESS_UP, BTN1_PRESS_UP_Handler); + button_attach(&btn1, PRESS_REPEAT, BTN1_PRESS_REPEAT_Handler); + button_attach(&btn1, SINGLE_CLICK, BTN1_SINGLE_Click_Handler); + button_attach(&btn1, DOUBLE_CLICK, BTN1_DOUBLE_Click_Handler); + button_attach(&btn1, LONG_PRESS_START, BTN1_LONG_PRESS_START_Handler); + button_attach(&btn1, LONG_PRESS_HOLD, BTN1_LONG_PRESS_HOLD_Handler); + button_start(&btn1); + + //make the timer invoking the button_ticks() interval 5ms. + //This function is implemented by yourself. + __timer_start(button_ticks, 0, 5); + + while(1) + {} +} +``` diff --git a/app/button/button.c b/app/button/button.c new file mode 100644 index 0000000..7ad945f --- /dev/null +++ b/app/button/button.c @@ -0,0 +1,69 @@ +#include "button.h" +#include "multi_button.h" + +#include "drivers/chip/gpio.h" +#include "os/os.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN +#define SYS_LOG_DOMAIN "BUTTON" +#include "sys_log.h" + +static os_work_t s_work_hdl; + +static uint8_t _port_pin_level(uint8_t button_id); +static void _work_handler_button(void *pvParameter); + +/** + * @brief 添加一个按键 + * + * @param pin 引脚配置 + * @param cb 回调函数 + * @param event_mask 触发事件源。 @ref EVENT_MASK + * @retval handler + */ +button_hdl_t *btn_attach(const cfg_board_pin_io_t *pin, + button_callback_fn cb, + uint8_t event_mask) +{ + button_hdl_t *handle = os_malloc(sizeof(button_hdl_t)); + if (handle == NULL) + { + SYS_LOG_WRN("operation fail"); + return NULL; + } + + drv_gpio_pin_configure(pin->pin, _GPIO_DIR_IN, pin->en_lev ? _GPIO_PUD_PULL_DOWN : _GPIO_PUD_PULL_UP); + + button_init(handle, _port_pin_level, pin->en_lev, pin->pin); + button_attach(handle, event_mask, cb); + button_start(handle); + + os_work_create_default(&s_work_hdl, "", _work_handler_button, NULL, OS_PRIORITY_HIGHEST, 10); + + return 0; +} + +void btn_delete(button_hdl_t *handler) +{ + if (handler) + { + button_stop(handler); + os_free(handler); + } +} + +press_event_t btn_get_event(button_hdl_t *handle) +{ + return get_button_event(handle); +} + +static void _work_handler_button(void *pvParameter) +{ + os_work_later(TICKS_INTERVAL); + button_ticks(); +} + +static uint8_t _port_pin_level(uint8_t button_id) +{ + return drv_gpio_pin_read(button_id); +} diff --git a/app/button/button.h b/app/button/button.h new file mode 100644 index 0000000..f01f3c1 --- /dev/null +++ b/app/button/button.h @@ -0,0 +1,23 @@ +/** + * @file button.h + * @author LokLiang + * @brief 按键接口,是结合 board_config 并对 multi_button 模块的封装 + * @version 0.1 + * @date 2024-04-23 + * + * @copyright Copyright (c) 2024 + * + */ + +#pragma once + +#include "multi_button.h" +#include "config/board_config.h" + +button_hdl_t *btn_attach(const cfg_board_pin_io_t *pin, + button_callback_fn cb, + uint8_t event_mask); + +void btn_delete(button_hdl_t *handler); + +press_event_t btn_get_event(button_hdl_t *handle); diff --git a/app/button/button_event.c b/app/button/button_event.c deleted file mode 100644 index 400cec5..0000000 --- a/app/button/button_event.c +++ /dev/null @@ -1,395 +0,0 @@ -#include -#include -#include -#include - -#include "driver/gpio.h" - -#include "os/os.h" - -#include "button_event.h" - -#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN -#define SYS_LOG_DOMAIN "BUTTON" -#include "sys_log.h" - -#define VOLTAGE_HIGH 1 // 高电平有效 -#define VOLTAGE_LOW 0 // 高电平有效 - -#define DEBOUNCE_CHECK_VOLTAGE_CHANGED 1 -#define DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED 0 - -#define DEBOUCE_CHECK_MAX_TIMES 5 - -typedef struct -{ - uint8_t pin; - uint8_t is_inverted; // 是否反向,如果为真的话,则当按钮按下后,引脚为低电平 - uint16_t history; - uint32_t down_time; - uint32_t next_long_time; - int current_voltage; // 当前引脚电平 - int debounce_check_counter; - uint8_t allow_multiple_long_press_event; - uint8_t long_press_event_sent; -} debounce_t; - -int pin_count = -1; -debounce_t *debounce; -static os_queue_t s_event_queue; // 当出现按键变化时被传送到的队列对象 -static button_callback_t *g_callback_list_head = NULL; -static button_callback_t *g_callback_list_tail = NULL; -static os_work_t s_work_hdl; - -static os_queue_t *_pulled_button_init(unsigned long long pin_select, gpio_pull_mode_t pull_mode); - -static uint32_t millis() -{ - return os_get_sys_time(); -} - -static int send_event(debounce_t *db, button_event_e ev) -{ - static char *const ev_tbl[] = { - [BUTTON_UP] = "release", - [BUTTON_DOWN] = "press", - [BUTTON_HELD] = "held"}; - SYS_ASSERT(ev < sizeof(ev_tbl), "ev: %d", ev); - SYS_LOG_DBG("event happend: pinnum: %d, ev: %s", db->pin, ev_tbl[ev]); - - button_event_t event = { - .pin = db->pin, - .event = ev, - }; - - os_queue_send(&s_event_queue, &event, 0); - - button_callback_t *tmp = g_callback_list_head; - button_callback_t *callback_list = NULL; - button_callback_t *callback_list_it = NULL; - while (tmp != NULL) - { - if (tmp->pin == db->pin) - { - button_callback_t *new_callback = NULL; - if (callback_list == NULL) - { - callback_list = os_malloc(sizeof(button_callback_t)); - callback_list_it = callback_list; - new_callback = callback_list_it; - } - else - { - new_callback = os_malloc(sizeof(button_callback_t)); - } - new_callback->pin = db->pin; - new_callback->event_callback = tmp->event_callback; - new_callback->next = NULL; - - if (new_callback != callback_list_it) - { - callback_list_it->next = new_callback; - callback_list_it = new_callback; - } - } - tmp = tmp->next; - } - - tmp = callback_list; - int stop_forward_event = 0; - while (tmp != NULL) - { - if (tmp->pin == db->pin) - { - SYS_LOG_DBG("do call back: pinnum: %d, ev: %s", db->pin, ev_tbl[ev]); - stop_forward_event = tmp->event_callback(&event); - if (stop_forward_event) - { - break; - } - } - tmp = tmp->next; - } - - // 释放临时遍历对象 - tmp = callback_list; - while (tmp) - { - button_callback_t *next_obj = tmp->next; - - os_free(tmp); - tmp = next_obj; - } - - return stop_forward_event; -} - -static int update_debounce_counter(int pin_index) -{ - debounce_t *button_event_info = &debounce[pin_index]; - int level = gpio_get_level(button_event_info->pin); - if (button_event_info->current_voltage != level) - { - button_event_info->debounce_check_counter += 1; - } - else if (button_event_info->current_voltage == level && button_event_info->debounce_check_counter > 0) - { - button_event_info->debounce_check_counter -= 1; - } - - // 防抖检测已经连续多次检测到相同的电平,则改变按钮当前的电平 - if (button_event_info->debounce_check_counter == DEBOUCE_CHECK_MAX_TIMES) - { - button_event_info->current_voltage = level; - button_event_info->debounce_check_counter = 0; - SYS_LOG_DBG("button voltage changed, pin:%d, voltage:%d", button_event_info->pin, level); - return DEBOUNCE_CHECK_VOLTAGE_CHANGED; - } - - return DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED; -} - -static bool is_button_down(int pin_index) -{ - debounce_t *button_event_info = &debounce[pin_index]; - - if (button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_LOW) - { - // 如果电平是反向的,并且当前是低电平,则认为按钮按下了 - return true; - } - else if (!button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_HIGH) - { - // 如果电平不是反向的,并且当前是高电平,则认为按钮按下了 - return true; - } - - return false; -} - -static bool is_button_up(int pin_index) -{ - debounce_t *button_event_info = &debounce[pin_index]; - - if (button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_HIGH) - { - // 如果电平是反向的,并且当前是高电平,则认为按钮释放了 - return true; - } - else if (!button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_LOW) - { - // 如果电平不是反向的,并且当前是低电平,则认为按钮释放了 - return true; - } - - return false; -} - -static void _work_handler_button(void *pvParameter) -{ - static uint8_t cancel_laster_event = 1; - - for (int idx = 0; idx < pin_count; idx++) - { - debounce_t *button_event_info = &debounce[idx]; - - int check_result = update_debounce_counter(idx); // 防抖处理 - if (check_result == DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED) - { - // 如果allow_multiple_long_press_event为false,则一次长按无论长按多久,只会触发一次长按事件 - if (!button_event_info->allow_multiple_long_press_event && button_event_info->long_press_event_sent != 0) - { - continue; - } - - // 电平没有变化,则检查按钮是否已经按下,以开始检查长按操作 - if (button_event_info->down_time && millis() >= button_event_info->next_long_time) - { - button_event_info->next_long_time = millis() + LONG_PRESS_REPEAT; - button_event_info->long_press_event_sent = 1; - cancel_laster_event = send_event(&debounce[idx], BUTTON_HELD); - if (cancel_laster_event) - { - button_event_info->down_time = 0; - } - } - } - else - { - // 电平有变化,检查是按下还是松开 - if (button_event_info->down_time == 0 && is_button_down(idx)) - { - cancel_laster_event = 0; - SYS_LOG_DBG("update downtime for pin:%d", button_event_info->pin); - button_event_info->down_time = millis(); - button_event_info->next_long_time = button_event_info->down_time + LONG_PRESS_DURATION; - cancel_laster_event = send_event(&debounce[idx], BUTTON_DOWN); - if (cancel_laster_event) - { - button_event_info->down_time = 0; - } - } - else if (button_event_info->down_time && is_button_up(idx)) - { - button_event_info->down_time = 0; - button_event_info->long_press_event_sent = 0; - cancel_laster_event = send_event(&debounce[idx], BUTTON_UP); - if (cancel_laster_event) - { - button_event_info->down_time = 0; - } - } - } - } - - os_work_later(10); -} - -/** - * @brief 初始化按电平式的按键检测。 - * 这将创建一个用于扫描按键的线程,当检测到按键变化时,将在线程中产生两种通知: - * 1. 发送到内部创建的队列中(提示:如果使用了 os_queue_regist() 注册工作项,该工作项将被自动唤醒) - * 2. 执行由 button_event_add_callback() 设置的回调函数(提示:同一引脚可设置多个回调) - * - * @param pin_select 按位表示 MCU 的引脚呈 - * @param en_lev 触发电平 - * @return os_queue_t* 由内部创建的消息队列 - */ -os_queue_t *button_init(unsigned long long pin_select, uint8_t en_lev) -{ - return _pulled_button_init(pin_select, en_lev == 0 ? GPIO_PULLUP_ONLY : GPIO_PULLDOWN_ONLY); -} - -static os_queue_t *_pulled_button_init(unsigned long long pin_select, gpio_pull_mode_t pull_mode) -{ - if (pin_count != -1) - { - SYS_LOG_WRN("Already initialized"); - return NULL; - } - - // Configure the pins - gpio_config_t io_conf; - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pull_up_en = (pull_mode == GPIO_PULLUP_ONLY || pull_mode == GPIO_PULLUP_PULLDOWN); - io_conf.pull_down_en = (pull_mode == GPIO_PULLDOWN_ONLY || pull_mode == GPIO_PULLUP_PULLDOWN); - io_conf.pin_bit_mask = pin_select; - io_conf.intr_type = GPIO_INTR_DISABLE; - gpio_config(&io_conf); - - // Scan the pin map to determine number of pins - pin_count = 0; - int pin = 0; - for (pin = 0; pin < sizeof(pin_select) * 8; pin++) - { - if ((1ULL << pin) & pin_select) - { - pin_count++; - } - } - - // Initialize global state and s_event_queue - debounce = os_calloc(sizeof(debounce_t) * pin_count); - // s_event_queue = xQueueCreate(4, sizeof(button_event_t)); - os_queue_create(&s_event_queue, 4, sizeof(button_event_t)); - - // Scan the pin map to determine each pin number, populate the state - uint32_t idx = 0; - for (pin = 0; pin < sizeof(pin_select) * 8; pin++) - { - if ((1ULL << pin) & pin_select) - { - SYS_LOG_DBG("Registering button input: %d", pin); - debounce_t *button_event_info = &debounce[idx]; - - button_event_info->pin = pin; - button_event_info->down_time = 0; - button_event_info->allow_multiple_long_press_event = 1; - button_event_info->is_inverted = (pull_mode == GPIO_PULLUP_ONLY); - idx++; - } - } - - os_work_create(&s_work_hdl, "", _work_handler_button, NULL, OS_PRIORITY_HIGHEST); - os_work_submit(default_os_work_q_hdl, &s_work_hdl, 1000); - - SYS_LOG_INF("Initialized"); - - return &s_event_queue; -} - -/** - * @brief 添加回调函数:当由 button_init() 设置的对应的引脚号产生按键状态变化时,由内部线程创建执行的回调函数。 - * - * @param pin 0..n - * @param callback int (*)(const button_event_t *event) - */ -void button_event_add_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback) -{ - button_callback_t *callback_obj = os_malloc(sizeof(button_callback_t)); - callback_obj->pin = pin; - callback_obj->event_callback = callback; - callback_obj->next = NULL; - if (g_callback_list_head == NULL) - { - g_callback_list_head = callback_obj; - g_callback_list_tail = g_callback_list_head; - } - else - { - g_callback_list_tail->next = callback_obj; - g_callback_list_tail = callback_obj; - } -} - -void button_event_remove_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback) -{ - button_callback_t *tmp = g_callback_list_head; - button_callback_t *previous_item = NULL; - SYS_LOG_DBG("remove pin:%d", pin); - while (tmp != NULL) - { - if (tmp->event_callback == callback && tmp->pin == pin) - { - if (previous_item) - { - previous_item->next = tmp->next; // 断开当前点节,并将前一个和后一个连接起来 - os_free(tmp); - SYS_LOG_DBG("found item"); - tmp = previous_item->next; - } - else - { - // 没有上一个节点,则将头部节点,设置为待移除节点的下一个节点 - if (tmp->next) - { - g_callback_list_head = tmp->next; - } - else - { - g_callback_list_head = NULL; - SYS_LOG_DBG("####!!!!#####!!!!"); - } - os_free(tmp); - tmp = g_callback_list_head; - SYS_LOG_DBG("case111"); - } - SYS_LOG_DBG("2222"); - } - else - { - previous_item = tmp; - tmp = tmp->next; - SYS_LOG_DBG("1111"); - } - } - - // 更新g_callback_list_tail指针 - while (tmp != NULL) - { - g_callback_list_tail = tmp; - tmp = tmp->next; - } - - SYS_LOG_DBG("doneeee1"); -} \ No newline at end of file diff --git a/app/button/button_event.h b/app/button/button_event.h deleted file mode 100644 index cf9ae36..0000000 --- a/app/button/button_event.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file button_event.h - * @author your name (you@domain.com) - * @brief - * @version 0.1 - * @date 2023-09-04 - * - * @copyright Copyright (c) 2023 - * - */ - -#pragma once - -#include "os/os.h" -#include "driver/gpio.h" - -#define PIN_BIT(x) (1ULL << x) - -#define LONG_PRESS_DURATION (1000) -#define LONG_PRESS_REPEAT (200) - -typedef enum -{ - BUTTON_UP, - BUTTON_DOWN, - BUTTON_HELD -} button_event_e; - -typedef struct -{ - uint32_t pin; - button_event_e event; -} button_event_t; - -typedef int (*BUTTON_EVENT_CALLBACK)(const button_event_t *event); - -typedef struct button_callback_s -{ - struct button_callback_s *next; - uint32_t pin; - BUTTON_EVENT_CALLBACK event_callback; -} button_callback_t; - -os_queue_t *button_init(unsigned long long pin_select, uint8_t en_lev); - -void button_event_add_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback); -void button_event_remove_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback); diff --git a/app/button/multi_button.c b/app/button/multi_button.c new file mode 100644 index 0000000..6bdf802 --- /dev/null +++ b/app/button/multi_button.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2016 Zibin Zheng + * All rights reserved + * https://github.com/0x1abin/MultiButton + */ + +#include "multi_button.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_ERR +#define SYS_LOG_DOMAIN "MAIN" +#include "sys_log.h" + +#define EVENT_CB(ev) \ + do \ + { \ + if ((1U << ev) & handle->event_cb) \ + handle->cb((void *)handle, ev); \ + } while (0) + +#define PRESS_REPEAT_MAX_NUM 15 /*!< The maximum value of the repeat counter */ + +// button handle list head. +static button_hdl_t *head_handle = NULL; + +static void button_handler(button_hdl_t *handle); + +/** + * @brief Initializes the button struct handle. + * @param handle: the button handle struct. + * @param pin_level: read the HAL GPIO of the connected button level. + * @param active_level: pressed GPIO level. + * @param button_id: the button id. + * @retval None + */ +void button_init(button_hdl_t *handle, uint8_t (*pin_level)(uint8_t button_id), uint8_t active_level, uint8_t button_id) +{ + memset(handle, 0, sizeof(button_hdl_t)); + handle->event = (uint8_t)NONE_PRESS; + handle->hal_button_Level = pin_level; + handle->button_level = !active_level; + handle->active_level = active_level; + handle->button_id = button_id; +} + +/** + * @brief Attach the button event callback function. + * @param handle: the button handle struct. + * @param event_mask: trigger event type. @ref EVENT_MASK + * @param cb: callback function. + * @retval None + */ +void button_attach(button_hdl_t *handle, uint8_t event_mask, button_callback_fn cb) +{ + handle->cb = cb; + handle->event_cb = event_mask; +} + +/** + * @brief Inquire the button event happen. + * @param handle: the button handle struct. + * @retval button event. + */ +press_event_t get_button_event(button_hdl_t *handle) +{ + return (press_event_t)(handle->event); +} + +/** + * @brief Button driver core function, driver state machine. + * @param handle: the button handle struct. + * @retval None + */ +static void button_handler(button_hdl_t *handle) +{ + uint8_t read_gpio_level = handle->hal_button_Level(handle->button_id); + + // ticks counter working.. + if ((handle->state) > 0) + handle->ticks++; + + /*------------button debounce handle---------------*/ + if (read_gpio_level != handle->button_level) + { // not equal to prev one + // continue read 3 times same new level change + if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) + { + handle->button_level = read_gpio_level; + handle->debounce_cnt = 0; + } + } + else + { // level not change ,counter reset. + handle->debounce_cnt = 0; + } + + /*-----------------State machine-------------------*/ + switch (handle->state) + { + case 0: + if (handle->button_level == handle->active_level) + { // start press down + handle->event = (uint8_t)PRESS_DOWN; + SYS_LOG_INF("PRESS_DOWN"); + EVENT_CB(PRESS_DOWN); + handle->ticks = 0; + handle->repeat = 1; + handle->state = 1; + } + else + { + handle->event = (uint8_t)NONE_PRESS; + } + break; + + case 1: + if (handle->button_level != handle->active_level) + { // released press up + handle->event = (uint8_t)PRESS_UP; + SYS_LOG_INF("PRESS_UP"); + EVENT_CB(PRESS_UP); + handle->ticks = 0; + handle->state = 2; + } + else if (handle->ticks > LONG_TICKS) + { + handle->event = (uint8_t)LONG_PRESS_START; + SYS_LOG_INF("LONG_PRESS_START"); + EVENT_CB(LONG_PRESS_START); + handle->state = 5; + } + break; + + case 2: + if (handle->button_level == handle->active_level) + { // press down again + handle->event = (uint8_t)PRESS_DOWN; + SYS_LOG_INF("PRESS_DOWN"); + EVENT_CB(PRESS_DOWN); + if (handle->repeat != PRESS_REPEAT_MAX_NUM) + { + handle->repeat++; + } + SYS_LOG_INF("PRESS_REPEAT"); + EVENT_CB(PRESS_REPEAT); // repeat hit + handle->ticks = 0; + handle->state = 3; + } + else if (handle->ticks > SHORT_TICKS) + { // released timeout + if (handle->repeat == 1) + { + handle->event = (uint8_t)SINGLE_CLICK; + SYS_LOG_INF("SINGLE_CLICK"); + EVENT_CB(SINGLE_CLICK); + } + else if (handle->repeat == 2) + { + handle->event = (uint8_t)DOUBLE_CLICK; + SYS_LOG_INF("DOUBLE_CLICK"); + EVENT_CB(DOUBLE_CLICK); // repeat hit + } + handle->state = 0; + } + break; + + case 3: + if (handle->button_level != handle->active_level) + { // released press up + handle->event = (uint8_t)PRESS_UP; + SYS_LOG_INF("PRESS_UP"); + EVENT_CB(PRESS_UP); + if (handle->ticks < SHORT_TICKS) + { + handle->ticks = 0; + handle->state = 2; // repeat press + } + else + { + handle->state = 0; + } + } + else if (handle->ticks > SHORT_TICKS) + { // SHORT_TICKS < press down hold time < LONG_TICKS + handle->state = 1; + } + break; + + case 5: + if (handle->button_level == handle->active_level) + { + // continue hold trigger + handle->event = (uint8_t)LONG_PRESS_HOLD; + SYS_LOG_INF("LONG_PRESS_HOLD"); + EVENT_CB(LONG_PRESS_HOLD); + } + else + { // released + handle->event = (uint8_t)PRESS_UP; + SYS_LOG_INF("PRESS_UP"); + EVENT_CB(PRESS_UP); + handle->state = 0; // reset + } + break; + default: + handle->state = 0; // reset + break; + } +} + +/** + * @brief Start the button work, add the handle into work list. + * @param handle: target handle struct. + * @retval 0: succeed. -1: already exist. + */ +int button_start(button_hdl_t *handle) +{ + button_hdl_t *target = head_handle; + while (target) + { + if (target == handle) + return -1; // already exist. + target = target->next; + } + handle->next = head_handle; + head_handle = handle; + return 0; +} + +/** + * @brief Stop the button work, remove the handle off work list. + * @param handle: target handle struct. + * @retval None + */ +void button_stop(button_hdl_t *handle) +{ + button_hdl_t **curr; + for (curr = &head_handle; *curr;) + { + button_hdl_t *entry = *curr; + if (entry == handle) + { + *curr = entry->next; + return; // glacier add 2021-8-18 + } + else + { + curr = &entry->next; + } + } +} + +/** + * @brief background ticks, timer repeat invoking interval 5ms. + * @param None. + * @retval None + */ +void button_ticks(void) +{ + button_hdl_t *target; + for (target = head_handle; target; target = target->next) + { + button_handler(target); + } +} diff --git a/app/button/multi_button.h b/app/button/multi_button.h new file mode 100644 index 0000000..6d869f2 --- /dev/null +++ b/app/button/multi_button.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Zibin Zheng + * All rights reserved + * https://github.com/0x1abin/MultiButton + */ + +#pragma once + +#include +#include + +// According to your need to modify the constants. +#define TICKS_INTERVAL 5 // ms +#define DEBOUNCE_TICKS 3 // MAX 7 (0 ~ 7) +#define SHORT_TICKS (300 / TICKS_INTERVAL) +#define LONG_TICKS (1000 / TICKS_INTERVAL) + +typedef struct Button button_hdl_t; + +typedef enum // 触发事件源 +{ + NONE_PRESS = 0, + PRESS_DOWN, // 按键按下,每次按下都触发 + PRESS_UP, // 按键弹起,每次松开都触发 + PRESS_REPEAT, // 重复按下触发,变量repeat计数连击次数 + SINGLE_CLICK, // 单击按键事件 + DOUBLE_CLICK, // 双击按键事件 + LONG_PRESS_START, // 达到长按时间阈值时触发一次 + LONG_PRESS_HOLD, // 长按期间一直触发 +} press_event_t; + +/* EVENT_MASK */ +#define EVENT_PRESS_DOWN (1U << PRESS_DOWN) // 按键按下,每次按下都触发 +#define EVENT_PRESS_UP (1U << PRESS_UP) // 按键弹起,每次松开都触发 +#define EVENT_PRESS_REPEAT (1U << PRESS_REPEAT) // 重复按下触发,变量repeat计数连击次数 +#define EVENT_SINGLE_CLICK (1U << SINGLE_CLICK) // 单击按键事件 +#define EVENT_DOUBLE_CLICK (1U << DOUBLE_CLICK) // 双击按键事件 +#define EVENT_LONG_PRESS_START (1U << LONG_PRESS_START) // 达到长按时间阈值时触发一次 +#define EVENT_LONG_PRESS_HOLD (1U << LONG_PRESS_HOLD) // 长按期间一直触发 + +typedef void (*button_callback_fn)(button_hdl_t *handle, press_event_t event); + +struct Button +{ + struct Button *next; + uint16_t ticks; + uint8_t repeat : 4; + uint8_t event : 4; + uint8_t state : 3; + uint8_t debounce_cnt : 3; + uint8_t active_level : 1; + uint8_t button_level : 1; + uint8_t button_id; + uint8_t event_cb; + uint8_t (*hal_button_Level)(uint8_t button_id); + button_callback_fn cb; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +void button_init(button_hdl_t *handle, uint8_t (*pin_level)(uint8_t button_id), uint8_t active_level, uint8_t button_id); +void button_attach(button_hdl_t *handle, uint8_t event_mask, button_callback_fn cb); +press_event_t get_button_event(button_hdl_t *handle); +int button_start(button_hdl_t *handle); +void button_stop(button_hdl_t *handle); +void button_ticks(void); + +#ifdef __cplusplus +} +#endif diff --git a/app/config/board_config.c b/app/config/board_config.c index 5a945f3..ef8212e 100644 --- a/app/config/board_config.c +++ b/app/config/board_config.c @@ -19,7 +19,7 @@ static cfg_board_t const s_cfg_board_default = { /* 启动按键 */ .key_boot = { - .pin = 9, // 用于切换灯效 + .pin = 0, // 用于切换灯效 .en_lev = 0, // 用于切换灯效 }, }; diff --git a/app/config/board_config.h b/app/config/board_config.h index 78428b0..bb7665f 100644 --- a/app/config/board_config.h +++ b/app/config/board_config.h @@ -20,18 +20,21 @@ #pragma once #include "drivers/chip/_hal.h" +#include "sdkconfig.h" -typedef struct // 按键配置 +#define GPIO_USED(pin) ((pin) < 255) + +typedef struct // 对应 GPIO 单个引脚的输入/输出配置的基础定义 { - uint8_t pin; // 引脚号 + uint8_t pin; // 引脚号 (0~254, 255 表示不使用) --OK uint8_t en_lev; // 触发电平 -} cfg_board_key_t; +} cfg_board_pin_io_t; typedef struct // 数据结构一旦定下不可随意变更 { /* 硬件描述类 */ hal_uart_hdl_t uart_console; // 控制台 - cfg_board_key_t key_boot; // 启动按键 + cfg_board_pin_io_t key_boot; // 启动按键 /* 产品功能描述类 */ diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 07a58a7..6e0a5ed 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,5 +1,6 @@ # INCS list(APPEND incs "../sal") +list(APPEND incs "../components/system/include") # SRCS diff --git a/sal/CMakeLists.txt b/sal/CMakeLists.txt index 0084825..9519c1d 100644 --- a/sal/CMakeLists.txt +++ b/sal/CMakeLists.txt @@ -13,6 +13,7 @@ list(APPEND srcs "esp32/kernel/os_semaphore.c") list(APPEND srcs "esp32/kernel/os_thread.c") list(APPEND srcs "esp32/kernel/os_kit.c") list(APPEND srcs "esp32/kernel/os_service.c") +list(APPEND srcs "esp32/chip/gpio_esp32.c") list(APPEND srcs "esp32/chip/uart_esp32.c") list(APPEND srcs "esp32/soc_shell.c") list(APPEND srcs "esp32/os_entry.c") diff --git a/sal/esp32/chip/gpio_esp32.c b/sal/esp32/chip/gpio_esp32.c new file mode 100644 index 0000000..42197a6 --- /dev/null +++ b/sal/esp32/chip/gpio_esp32.c @@ -0,0 +1,63 @@ +#include "drivers/chip/gpio.h" + +#include "driver/gpio.h" + +#define SYS_LOG_DOMAIN "gpio" +#include "sys_log.h" + +void drv_gpio_enable_all(void) +{ +} + +void drv_gpio_disable_all(void) +{ +} + +int drv_gpio_pin_configure(uint8_t pin, gpio_dir_t dir, gpio_pud_t pull) +{ + gpio_config_t io_conf; + static __typeof__(io_conf.mode) const mode_tab[] = { + [_GPIO_DIR_IN] = GPIO_MODE_INPUT, + [_GPIO_DIR_ANALOG] = GPIO_MODE_INPUT, + [_GPIO_DIR_OUT] = GPIO_MODE_OUTPUT, + [_GPIO_DIR_OPEN_DRAIN] = GPIO_MODE_OUTPUT_OD, + }; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = mode_tab[dir]; + io_conf.pin_bit_mask = (1ULL << pin); + io_conf.pull_up_en = pull == _GPIO_PUD_PULL_UP; + io_conf.pull_down_en = pull == _GPIO_PUD_PULL_DOWN; + gpio_config(&io_conf); + return 0; +} + +int drv_gpio_pin_read(uint8_t pin) +{ + return gpio_get_level(pin); +} + +void drv_gpio_pin_write(uint8_t pin, int value) +{ + gpio_set_level(pin, value); +} + +void drv_gpio_exti_callback_enable(gpio_exti_cb_fn cb) +{ +} + +void drv_gpio_exti_callback_disable(void) +{ +} + +void drv_gpio_exti_irq_enable(uint8_t pin, gpio_exti_edge_t polarity, int priority) +{ +} + +void drv_gpio_exti_irq_disable(uint8_t pin) +{ +} + +int drv_gpio_exti_get_pin(void) +{ + return 0; +}