265 lines
7.1 KiB
C
265 lines
7.1 KiB
C
|
|
/*
|
||
|
|
* 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);
|
||
|
|
}
|
||
|
|
}
|