Files
ESPC3-wireless/app/button/multi_button.c

265 lines
7.1 KiB
C
Raw Normal View History

2025-02-18 17:41:45 +08:00
/*
* 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);
}
}