参考代码

This commit is contained in:
LokLiang
2024-03-28 12:19:52 +08:00
commit 7b86aa3362
96 changed files with 19986 additions and 0 deletions

395
app/button/button_event.c Normal file
View File

@@ -0,0 +1,395 @@
#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");
}