Files

161 lines
4.8 KiB
C
Raw Permalink Normal View History

2026-01-09 16:52:44 +08:00
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include "button.h"
static const uint16_t button_double_click_time[] = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
static const uint16_t button_long_press_time[] = {3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000};
static uint32_t button_get_tick(void)
{
return HAL_GetTick();
}
uint8_t button_read_pin(button_t* btn)
{
uint8_t logic = (HAL_GPIO_ReadPin(btn->gpio_port, btn->gpio_pin) == GPIO_PIN_SET) ? 1 : 0;
return (btn->config.active_level == BUTTON_ACTIVE_HIGH) ? logic : !logic;
}
void button_init(button_t* btn, GPIO_TypeDef* port, uint16_t pin, const button_config_t* pconfig)
{
btn->gpio_port = port;
btn->gpio_pin = pin;
if (pconfig)
btn->config = *pconfig;
else {
btn->config.double_click_level = BUTTON_DOUBLE_CLICK_TIME_200MS;
btn->config.long_press_level = BUTTON_LONG_PRESS_TIME_3S;
btn->config.active_level = BUTTON_ACTIVE_LOW;
btn->config.debounce_time_ms = BUTTON_DEBOUNCE_DEFAULT;
}
btn->is_pressed = 0;
btn->prev_pressed = 0;
btn->stable_count = 0;
btn->press_time = 0;
btn->release_time = 0;
btn->click_count = 0;
btn->long_press_triggered = 0;
btn->paused = 0;
btn->single_click_callback = NULL;
btn->double_click_callback = NULL;
btn->long_press_callback = NULL;
}
// 更新配置
void button_set_config(button_t* btn, const button_config_t* cfg)
{
if (cfg) {
button_config_t tmp = *cfg;
btn->config = tmp;
button_reset_state(btn);
}
}
// 设置回调
void button_set_callbacks(button_t* btn, void (*single_click_cb)(void), void (*double_click_cb)(void),
void (*long_press_cb)(void))
{
btn->single_click_callback = single_click_cb;
btn->double_click_callback = double_click_cb;
btn->long_press_callback = long_press_cb;
}
void button_pause(button_t* btn)
{
btn->paused = 1;
button_reset_state(btn);
}
void button_resume(button_t* btn)
{
btn->paused = 0;
button_reset_state(btn);
}
void button_reset_state(button_t* btn)
{
btn->is_pressed = 0;
btn->prev_pressed = 0;
btn->stable_count = 0;
btn->press_time = 0;
btn->release_time = 0;
btn->click_count = 0;
btn->long_press_triggered = 0;
}
button_event_t button_scan(button_t* btn)
{
if (btn->paused) return BUTTON_NONE;
static uint8_t debounced = 0;
static uint32_t debounce_tick = 0;
static uint8_t last_raw = 0xFF;
uint32_t now = button_get_tick();
uint8_t raw = button_read_pin(btn);
if (raw != debounced) {
if (raw != last_raw) {
debounce_tick = now;
last_raw = raw;
}
if ((now - debounce_tick) >= btn->config.debounce_time_ms) {
debounced = raw;
}
} else {
last_raw = raw;
}
uint8_t stable = debounced;
button_event_t event = BUTTON_NONE;
if (stable && btn->is_pressed == 0) {
btn->is_pressed = 1;
btn->press_time = now;
btn->long_press_triggered = 0;
}
if (!stable && btn->is_pressed == 1) {
btn->is_pressed = 0;
if (!btn->long_press_triggered) {
uint32_t pdur = now - btn->press_time;
uint16_t t_double = button_double_click_time[btn->config.double_click_level];
if (pdur > btn->config.debounce_time_ms) {
btn->click_count++;
btn->release_time = now;
}
}
}
if (btn->is_pressed && !btn->long_press_triggered) {
uint16_t t_long = button_long_press_time[btn->config.long_press_level];
if ((now - btn->press_time) >= t_long) {
btn->long_press_triggered = 1;
btn->click_count = 0;
event = BUTTON_LONG_PRESS;
if (btn->long_press_callback) btn->long_press_callback();
return event;
}
}
if (btn->click_count > 0 && btn->is_pressed == 0) {
uint16_t t_double = button_double_click_time[btn->config.double_click_level];
uint32_t idle = now - btn->release_time;
if (btn->click_count == 2) {
btn->click_count = 0;
event = BUTTON_DOUBLE_CLICK;
if (btn->double_click_callback) btn->double_click_callback();
return event;
} else if (btn->click_count == 1 && idle >= t_double) {
btn->click_count = 0;
event = BUTTON_SINGLE_CLICK;
if (btn->single_click_callback) btn->single_click_callback();
return event;
}
}
return BUTTON_NONE;
}