更新按键驱动

This commit is contained in:
LokLiang
2025-02-18 17:41:45 +08:00
parent f455349861
commit 141a9970d8
14 changed files with 649 additions and 466 deletions

View File

@@ -4,12 +4,13 @@ list(APPEND incs "../components/system/source")
list(APPEND incs "../components/system/source/k_kit") list(APPEND incs "../components/system/source/k_kit")
list(APPEND incs "../components/system/source/shell") list(APPEND incs "../components/system/source/shell")
list(APPEND srcs "../app/app_main.c") list(APPEND srcs "app_main.c")
list(APPEND srcs "../app/console.c") list(APPEND srcs "console.c")
list(APPEND srcs "../app/drivers/data_port/sb_data_port.c") list(APPEND srcs "drivers/data_port/sb_data_port.c")
list(APPEND srcs "../app/drivers/data_port/uart/uart_port.c") list(APPEND srcs "drivers/data_port/uart/uart_port.c")
list(APPEND srcs "../app/button/button_event.c") list(APPEND srcs "button/button.c")
list(APPEND srcs "../app/config/board_config.c") list(APPEND srcs "button/multi_button.c")
list(APPEND srcs "config/board_config.c")
idf_component_register( idf_component_register(
INCLUDE_DIRS ${incs} INCLUDE_DIRS ${incs}

View File

@@ -12,7 +12,7 @@
#include "esp_err.h" #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 优先级的工作队列 os_work_q_t g_work_q_hdl_low; // 低于 default_os_work_q_hdl 优先级的工作队列
static void _init_nvs(void); 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); static void _vset_cb(sh_t *sh_hdl);
void work_app_main(void *arg) void work_app_main(void *arg)
@@ -39,9 +39,7 @@ void work_app_main(void *arg)
_init_nvs(); _init_nvs();
/* 初始化按键检测和控制 */ /* 初始化按键检测和控制 */
button_init(PIN_BIT(g_cfg_board->key_boot.pin), g_cfg_board->key_boot.en_lev); btn_attach(&g_cfg_board->key_boot, _change_mode_event_button, EVENT_PRESS_UP | EVENT_PRESS_DOWN | EVENT_LONG_PRESS_HOLD);
button_event_add_callback(g_cfg_board->key_boot.pin, _change_mode_event_button);
/* 启动 shell */ /* 启动 shell */
SYS_LOG_INF("app start"); 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[] = { static const char *const stat_tab[] = {
[BUTTON_UP] = "up", [PRESS_UP] = "up",
[BUTTON_DOWN] = "down", [PRESS_DOWN] = "down",
[BUTTON_HELD] = "held", [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) static void _vset_cb(sh_t *sh_hdl)

131
app/button/README.md Normal file
View File

@@ -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)
{}
}
```

69
app/button/button.c Normal file
View File

@@ -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);
}

23
app/button/button.h Normal file
View File

@@ -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);

View File

@@ -1,395 +0,0 @@
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#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");
}

View File

@@ -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);

264
app/button/multi_button.c Normal file
View File

@@ -0,0 +1,264 @@
/*
* Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
* 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);
}
}

73
app/button/multi_button.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
* All rights reserved
* https://github.com/0x1abin/MultiButton
*/
#pragma once
#include <stdint.h>
#include <string.h>
// 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

View File

@@ -19,7 +19,7 @@ static cfg_board_t const s_cfg_board_default = {
/* 启动按键 */ /* 启动按键 */
.key_boot = { .key_boot = {
.pin = 9, // 用于切换灯效 .pin = 0, // 用于切换灯效
.en_lev = 0, // 用于切换灯效 .en_lev = 0, // 用于切换灯效
}, },
}; };

View File

@@ -20,18 +20,21 @@
#pragma once #pragma once
#include "drivers/chip/_hal.h" #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; // 触发电平 uint8_t en_lev; // 触发电平
} cfg_board_key_t; } cfg_board_pin_io_t;
typedef struct // 数据结构一旦定下不可随意变更 typedef struct // 数据结构一旦定下不可随意变更
{ {
/* 硬件描述类 */ /* 硬件描述类 */
hal_uart_hdl_t uart_console; // 控制台 hal_uart_hdl_t uart_console; // 控制台
cfg_board_key_t key_boot; // 启动按键 cfg_board_pin_io_t key_boot; // 启动按键
/* 产品功能描述类 */ /* 产品功能描述类 */

View File

@@ -1,5 +1,6 @@
# INCS # INCS
list(APPEND incs "../sal") list(APPEND incs "../sal")
list(APPEND incs "../components/system/include")
# SRCS # SRCS

View File

@@ -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_thread.c")
list(APPEND srcs "esp32/kernel/os_kit.c") list(APPEND srcs "esp32/kernel/os_kit.c")
list(APPEND srcs "esp32/kernel/os_service.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/chip/uart_esp32.c")
list(APPEND srcs "esp32/soc_shell.c") list(APPEND srcs "esp32/soc_shell.c")
list(APPEND srcs "esp32/os_entry.c") list(APPEND srcs "esp32/os_entry.c")

View File

@@ -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;
}