mirror of
https://github.com/m5stack/M5Chain-Series-Internal-FW.git
synced 2026-05-20 11:49:54 -07:00
161 lines
4.8 KiB
C
161 lines
4.8 KiB
C
/*
|
|
* 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;
|
|
}
|