initial commit

This commit is contained in:
Mikkeli Matlock
2026-02-15 02:48:59 +09:00
commit 19db125619
258 changed files with 345581 additions and 0 deletions

View File

@@ -0,0 +1,292 @@
/*
* Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
* All rights reserved
*/
#include "multi_button.h"
// Macro for callback execution with null check
#define EVENT_CB(ev) do { if(handle->cb[ev]) handle->cb[ev](handle); } while(0)
// Button handle list head
static Button* head_handle = NULL;
// Forward declarations
static void button_handler(Button* handle);
static inline uint8_t button_read_level(Button* handle);
/**
* @brief Initialize 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* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)
{
if (!handle || !pin_level) return; // parameter validation
memset(handle, 0, sizeof(Button));
handle->event = (uint8_t)BTN_NONE_PRESS;
handle->hal_button_level = pin_level;
handle->button_level = !active_level; // initialize to opposite of active level
handle->active_level = active_level;
handle->button_id = button_id;
handle->state = BTN_STATE_IDLE;
}
/**
* @brief Attach the button event callback function
* @param handle: the button handle struct
* @param event: trigger event type
* @param cb: callback function
* @retval None
*/
void button_attach(Button* handle, ButtonEvent event, BtnCallback cb)
{
if (!handle || event >= BTN_EVENT_COUNT) return; // parameter validation
handle->cb[event] = cb;
}
/**
* @brief Detach the button event callback function
* @param handle: the button handle struct
* @param event: trigger event type
* @retval None
*/
void button_detach(Button* handle, ButtonEvent event)
{
if (!handle || event >= BTN_EVENT_COUNT) return; // parameter validation
handle->cb[event] = NULL;
}
/**
* @brief Get the button event that happened
* @param handle: the button handle struct
* @retval button event
*/
ButtonEvent button_get_event(Button* handle)
{
if (!handle) return BTN_NONE_PRESS;
return (ButtonEvent)(handle->event);
}
/**
* @brief Get the repeat count of button presses
* @param handle: the button handle struct
* @retval repeat count
*/
uint8_t button_get_repeat_count(Button* handle)
{
if (!handle) return 0;
return handle->repeat;
}
/**
* @brief Reset button state to idle
* @param handle: the button handle struct
* @retval None
*/
void button_reset(Button* handle)
{
if (!handle) return;
handle->state = BTN_STATE_IDLE;
handle->ticks = 0;
handle->repeat = 0;
handle->event = (uint8_t)BTN_NONE_PRESS;
handle->debounce_cnt = 0;
}
/**
* @brief Check if button is currently pressed
* @param handle: the button handle struct
* @retval 1: pressed, 0: not pressed, -1: error
*/
int button_is_pressed(Button* handle)
{
if (!handle) return -1;
return (handle->button_level == handle->active_level) ? 1 : 0;
}
/**
* @brief Read button level with inline optimization
* @param handle: the button handle struct
* @retval button level
*/
static inline uint8_t button_read_level(Button* handle)
{
return handle->hal_button_level(handle->button_id);
}
/**
* @brief Button driver core function, driver state machine
* @param handle: the button handle struct
* @retval None
*/
static void button_handler(Button* handle)
{
uint8_t read_gpio_level = button_read_level(handle);
// Increment ticks counter when not in idle state
if (handle->state > BTN_STATE_IDLE) {
handle->ticks++;
}
/*------------Button debounce handling---------------*/
if (read_gpio_level != handle->button_level) {
// Continue reading same new level for debounce
if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {
handle->button_level = read_gpio_level;
handle->debounce_cnt = 0;
}
} else {
// Level not changed, reset counter
handle->debounce_cnt = 0;
}
/*-----------------State machine-------------------*/
switch (handle->state) {
case BTN_STATE_IDLE:
if (handle->button_level == handle->active_level) {
// Button press detected
handle->event = (uint8_t)BTN_PRESS_DOWN;
EVENT_CB(BTN_PRESS_DOWN);
handle->ticks = 0;
handle->repeat = 1;
handle->state = BTN_STATE_PRESS;
} else {
handle->event = (uint8_t)BTN_NONE_PRESS;
}
break;
case BTN_STATE_PRESS:
if (handle->button_level != handle->active_level) {
// Button released
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
handle->ticks = 0;
handle->state = BTN_STATE_RELEASE;
} else if (handle->ticks > LONG_TICKS) {
// Long press detected
handle->event = (uint8_t)BTN_LONG_PRESS_START;
EVENT_CB(BTN_LONG_PRESS_START);
handle->state = BTN_STATE_LONG_HOLD;
}
break;
case BTN_STATE_RELEASE:
if (handle->button_level == handle->active_level) {
// Button pressed again
handle->event = (uint8_t)BTN_PRESS_DOWN;
EVENT_CB(BTN_PRESS_DOWN);
if (handle->repeat < PRESS_REPEAT_MAX_NUM) {
handle->repeat++;
}
EVENT_CB(BTN_PRESS_REPEAT);
handle->ticks = 0;
handle->state = BTN_STATE_REPEAT;
} else if (handle->ticks > SHORT_TICKS) {
// Timeout reached, determine click type
if (handle->repeat == 1) {
handle->event = (uint8_t)BTN_SINGLE_CLICK;
EVENT_CB(BTN_SINGLE_CLICK);
} else if (handle->repeat == 2) {
handle->event = (uint8_t)BTN_DOUBLE_CLICK;
EVENT_CB(BTN_DOUBLE_CLICK);
}
handle->state = BTN_STATE_IDLE;
}
break;
case BTN_STATE_REPEAT:
if (handle->button_level != handle->active_level) {
// Button released
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
if (handle->ticks < SHORT_TICKS) {
handle->ticks = 0;
handle->state = BTN_STATE_RELEASE; // Continue waiting for more presses
} else {
handle->state = BTN_STATE_IDLE; // End of sequence
}
} else if (handle->ticks > SHORT_TICKS) {
// Held down too long, treat as normal press
handle->state = BTN_STATE_PRESS;
}
break;
case BTN_STATE_LONG_HOLD:
if (handle->button_level == handle->active_level) {
// Continue holding
handle->event = (uint8_t)BTN_LONG_PRESS_HOLD;
EVENT_CB(BTN_LONG_PRESS_HOLD);
} else {
// Released from long press
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
handle->state = BTN_STATE_IDLE;
}
break;
default:
// Invalid state, reset to idle
handle->state = BTN_STATE_IDLE;
break;
}
}
/**
* @brief Start the button work, add the handle into work list
* @param handle: target handle struct
* @retval 0: succeed, -1: already exist, -2: invalid parameter
*/
int button_start(Button* handle)
{
if (!handle) return -2; // invalid parameter
Button* 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 from work list
* @param handle: target handle struct
* @retval None
*/
void button_stop(Button* handle)
{
if (!handle) return; // parameter validation
Button** curr;
for (curr = &head_handle; *curr; ) {
Button* entry = *curr;
if (entry == handle) {
*curr = entry->next;
entry->next = NULL; // clear next pointer
return;
} else {
curr = &entry->next;
}
}
}
/**
* @brief Background ticks, timer repeat invoking interval 5ms
* @param None
* @retval None
*/
void button_ticks(void)
{
Button* target;
for (target = head_handle; target; target = target->next) {
button_handler(target);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
* All rights reserved
*/
#ifndef _MULTI_BUTTON_H_
#define _MULTI_BUTTON_H_
#include <stdint.h>
#include <string.h>
// Configuration constants - can be modified according to your needs
#define TICKS_INTERVAL 5 // ms - timer interrupt interval
#define DEBOUNCE_TICKS 3 // MAX 7 (0 ~ 7) - debounce filter depth
#define SHORT_TICKS (300 / TICKS_INTERVAL) // short press threshold
#define LONG_TICKS (1000 / TICKS_INTERVAL) // long press threshold
#define PRESS_REPEAT_MAX_NUM 15 // maximum repeat counter value
// Forward declaration
typedef struct _Button Button;
// Button callback function type
typedef void (*BtnCallback)(Button* btn_handle);
// Button event types
typedef enum {
BTN_PRESS_DOWN = 0, // button pressed down
BTN_PRESS_UP, // button released
BTN_PRESS_REPEAT, // repeated press detected
BTN_SINGLE_CLICK, // single click completed
BTN_DOUBLE_CLICK, // double click completed
BTN_LONG_PRESS_START, // long press started
BTN_LONG_PRESS_HOLD, // long press holding
BTN_EVENT_COUNT, // total number of events
BTN_NONE_PRESS // no event
} ButtonEvent;
// Button state machine states
typedef enum {
BTN_STATE_IDLE = 0, // idle state
BTN_STATE_PRESS, // pressed state
BTN_STATE_RELEASE, // released state waiting for timeout
BTN_STATE_REPEAT, // repeat press state
BTN_STATE_LONG_HOLD // long press hold state
} ButtonState;
// Button structure
struct _Button {
uint16_t ticks; // tick counter
uint8_t repeat : 4; // repeat counter (0-15)
uint8_t event : 4; // current event (0-15)
uint8_t state : 3; // state machine state (0-7)
uint8_t debounce_cnt : 3; // debounce counter (0-7)
uint8_t active_level : 1; // active GPIO level (0 or 1)
uint8_t button_level : 1; // current button level
uint8_t button_id; // button identifier
uint8_t (*hal_button_level)(uint8_t button_id); // HAL function to read GPIO
BtnCallback cb[BTN_EVENT_COUNT]; // callback function array
Button* next; // next button in linked list
};
#ifdef __cplusplus
extern "C" {
#endif
// Public API functions
void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id);
void button_attach(Button* handle, ButtonEvent event, BtnCallback cb);
void button_detach(Button* handle, ButtonEvent event);
ButtonEvent button_get_event(Button* handle);
int button_start(Button* handle);
void button_stop(Button* handle);
void button_ticks(void);
// Utility functions
uint8_t button_get_repeat_count(Button* handle);
void button_reset(Button* handle);
int button_is_pressed(Button* handle);
#ifdef __cplusplus
}
#endif
#endif