Files
ESPC3-wireless/app/button/button_event.c
2024-03-28 12:19:52 +08:00

395 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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");
}