碎碎念:由于暂时签了一个offer,忙毕业论文相关的,好久没写了。今天写一下stm32中的状态机相关的代码学习。后续看上班岗位要写啥,估计会学习一下几个芯片或者rtos的api写法。还是要抽空学习总结知识。
先挂一个原作者的链接:
该项目是一个关于按键状态的小代码,代码量很小,其中比较值得注意的点是位域和状态机轮询的写法。相对应的还有一个项目则是事件驱动。我在自己的代码中,参考了AI,修改了部分宏定义为内联函数。而对于事件的回调操作,
由于使用的是轮询检查的方式,建议用于rtos系统交给任务队列处理或者用GPIO边沿中断处理。
基本准备
按键电路图

从图中可以看出使用的是没有硬件消抖的按键电路(也不重要,不管有没有消抖其实都会加数字防抖),同时需要芯片设置上拉,低电平有效。
/*Configure GPIO pins : Key1_Pin Key0_Pin */
GPIO_InitStruct.Pin = Key1_Pin|Key0_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
mutiButton 头文件
首先是相关头文件以及枚举数的定义。最开头的TICKs用于控制轮询的频率,随后是事前声明(一个好习惯,我以前不写事前声明的,值得学习),然后是控制事件的event以及state枚举数。
事件分为了8个事件,状态分为了5个状态
<details>
<summary>展开代码</summary>
#ifndef _MULTI_BUTTON_H
#define _MULTI_BUTTON_H
#include <stdint.h>
#include <string.h>
// 按键状态定义
#define TICKS_INTERVAL 5 // ms -timer interrupt interval
// 定时器中断/轮询的时间间隔,单位是毫秒(ms)所有以“ticks”为单位的计数器(短按/长按阈值、消抖计数等)都以此为基准。
// 增大它会降低时间分辨率并减少中断频率;减小它会提高分辨率但增加处理频率。
#define DEBOUNCE_TICKS 3 // MAX 7 (0~7) debounce filter depth
// 消抖滤波深度,以 ticks 为单位,需要连续多少个 ticks 的稳定读数才认为按键状态已稳定。3代表2^3-1
#define SHORT_TICKS (300 / TICKS_INTERVAL) // ms -short press time
#define LONG_TICKS (1000 / TICKS_INTERVAL) // ms -long press time
#define PRESS_PEPEAT_MAX_NUM 15 // maxmum repeat counter value 按键重复(repeat)计数器的最大值
// forword declaration
typedef struct _Button Button;
// Button callback function type
typedef void (*ButtonCallback)(Button *btn_handle);
// Button event type 8 events
typedef enum
{
BTN_PRESS_DOWN = 0, // button press down event
BTN_PRESS_UP, // button released
BTN_PRESS_REPEAT, // repeated press detected
BTN_SINGLE_CLICK, // single click event
BTN_DOUBLE_CLICK, // double click event
BTN_LONG_PRESS_START, // long press start event
BTN_LONG_PRESS_HOLD, // long press hold event
BTN_EVENT_COUNT, // total number of button events
BTN_NONE_PRESS // no button press event
} ButtonEvent;
// Button state machine states
typedef enum
{
BTN_STATE_IDLE = 0, // button idle state
BTN_STATE_PRESS, // button pressed state
BTN_STATE_WAIT_RELEASE, // button wait release state
BTN_STATE_REPEAT, // button repeat state
BTN_STATE_LONG_HOLD, // button long press hold state
} ButtonState;
在Button的结构体中使用了位域的代码写法,用于节省空间,类似寄存器
<detail>
<summary>展开代码</summary>
struct _Button
{
uint16_t ticks; // tick counter 2 bytes
uint8_t repeat : 4; // repeat counter (0-15 ) 0.5 byte
uint8_t event : 4; // button event(0-15) 0.5 byte 与repeat共用1字节
uint8_t state : 3; // button state(0-7) 1 byte
uint8_t debounce_cnt : 3; // debounce counter(0-7) 1 byte
uint8_t active_level : 1; // active GPIO level(0/1) 1 byte
uint8_t button_level : 1; // current button level (0/1) 1 byte 上述公用1字节
uint8_t button_id; // button identifier 1 byte
uint8_t (*hal_button_level)(uint8_t button_id); // Hal function to read GPIO level 4 bytes
ButtonCallback cb[BTN_EVENT_COUNT]; // callback functions for each event 4 bytes x 7 = 28 bytes
Button *next; // netxt button in linked list 4 bytes
};
然后就是相关的按键控制的函数,在代码中已经添加了相关注释
<details>
<summary>展开代码</summary>
#ifdef __cplusplus
extern "C"
{
#endif
// Public API functions
/*
@brief 按键初始化
*/
void button_init(Button *handle, uint8_t (*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id);
/*
@brief 绑定按键事件回调函数
*/
void button_attach(Button *handle, ButtonEvent event, ButtonCallback cb);
/*
@brief 解绑按键事件回调函数
*/
void button_detach(Button *handle, ButtonEvent event);
/*
@brief 获取按键事件
*/
ButtonEvent button_get_event(Button *handle);
/*
@brief 启动按键处理 or 停止按键处理
*/
int button_start(Button *handle);
void button_stop(Button *handle);
/*
@brief 按键滴答处理函数,在定时器中断或轮询中调用
*/
void button_ticks(void);
#ifdef __cplusplus
}
具体实现文件
在这里面使用了内联函数用于事件等安全检查相关定义,但是只保留了展位符号,BUTTON_ENTER_CRITICAL()以及BUTTON_EXIT_CRITICAL(),在注释中已经展示相关的中断禁用的功能,这里实际中需要考虑抢占的问题。
<details>
<summary>展开代码</summary>
#include "multi_button.h"
#include <string.h>
/* Optional concurrency protection:
* Define these to appropriate primitives for your platform, e.g.:
* #define BUTTON_ENTER_CRITICAL() __disable_irq()
* #define BUTTON_EXIT_CRITICAL() __enable_irq()
*
* If left undefined, they are no-ops. 不做任何操作,用于占位,具体配置相关的功能,如禁用中断
*/
#ifndef BUTTON_ENTER_CRITICAL
#define BUTTON_ENTER_CRITICAL() \
do \
{ \
} while (0)
#define BUTTON_EXIT_CRITICAL() \
do \
{ \
} while (0)
#endif
/* Make head_handle volatile to reduce likelihood of compiler reordering/
* caching when accessed from different contexts (e.g. ISR and main thread).
* Note: volatile alone is not sufficient for full synchronization; use the
* critical section macros above when modifying the list from different
* contexts.
*/
static volatile Button *head_handle = NULL; // Head of the linked list of buttons
/*Forward declarations*/
static void button_handler(Button *handle);
static inline uint8_t button_level(Button *handle);
/* Safe event invocation helper:
* - Read the callback pointer once into a local variable, then call through it.
* - This avoids the classic "check then call" race if another context clears
* a callback between the check and the call.
* 事件触发,使用内联函数取代宏
*/
static inline void event_invoke(Button *handle, ButtonEvent ev)
{
ButtonCallback cb = NULL;
if (!handle || ev >= BTN_EVENT_COUNT)
return;
/* single read of pointer */
cb = handle->cb[ev];
if (cb)
cb(handle);
}
基本函数定义
这部分函数,主要用于在状态机中检测状态、读取事件等,其本质功能是为了封装,以及安全检查,减少代码量。
<details>
<summary>展开代码</summary>
/**
* @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; // invalid parameter
memset(handle, 0, sizeof(Button));
handle->event = (uint8_t)BTN_NONE_PRESS;
handle->hal_button_level = pin_level;
/* initialize sampled level to opposite of active so first sample won't
* be misinterpreted as an immediate press */
handle->button_level = (uint8_t)!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
*
* Note: If callbacks can be attached/detached from a different context
* than where button_ticks() runs, callers should ensure synchronization.
* This function uses critical section macros to reduce race with the ISR.
*/
void button_attach(Button *handle, ButtonEvent event, ButtonCallback cb)
{
if (!handle || event >= BTN_EVENT_COUNT)
return; // invalid parameter
BUTTON_ENTER_CRITICAL();
handle->cb[event] = cb;
BUTTON_EXIT_CRITICAL();
}
/**
* @brief Detach the button event callback function
* @param handle: the button handle struct
* @param event: trigger event type
* @retval None
*
* Note: If callbacks can be attached/detached from a different context
* than where button_ticks() runs, callers should ensure synchronization.
* This function uses critical section macros to reduce race with the ISR.
*/
void button_detach(Button *handle, ButtonEvent event)
{
if (!handle || event >= BTN_EVENT_COUNT)
return; // invalid parameter
BUTTON_ENTER_CRITICAL();
handle->cb[event] = NULL;
BUTTON_EXIT_CRITICAL();
}
/**
* @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)
{
handle->state = BTN_STATE_IDLE;
handle->debounce_cnt = 0;
handle->ticks = 0;
handle->repeat = 0;
handle->event = (uint8_t)BTN_NONE_PRESS;
}
/**
* @brief Check if button is currently pressed
* @param handle: the button handle struct
* @retval 0: pressed, 1: not pressed, -1: error
*/
int8_t 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_level(Button *handle)
{
if (!handle)
return 0;
return handle->hal_button_level(handle->button_id);
}
状态机轮询
在这段函数中,展示了按键消抖(延迟查询以及复查),当通过消抖后检测事件,后文整理了相关的状态轮转方式。
状态机的写法主要是利用switch语法完成,而对于具体的行为则是使用回调函数进行解耦。
<details>
<summary>展开代码</summary>
static void button_handler(Button *handle)
{
uint8_t read_gpio_level = button_level(handle);
/* Increment ticks counter when not in idle state.
* ticks counts number of sampling intervals since entering non-idle.
*/
if (handle->state > BTN_STATE_IDLE)
{
handle->ticks++;
}
/*Button debounce handling*/
/* If level changed, continue reading new level for debounce*/
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 does not change, reset debounce counter*/
handle->debounce_cnt = 0;
}
/*-----------------State machine-------------------*/
switch (handle->state)
{
case BTN_STATE_IDLE:
if (handle->button_level == handle->active_level)
{ /*invoke press event*/
handle->event = (uint8_t)BTN_PRESS_DOWN;
event_invoke(handle, BTN_PRESS_DOWN);
/*button pressed*/
handle->repeat = 1;
handle->state = BTN_STATE_PRESS;
handle->ticks = 0;
}
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_invoke(handle, BTN_PRESS_UP);
handle->ticks = 0;
handle->state = BTN_STATE_WAIT_RELEASE;
}
else if (handle->ticks >= LONG_TICKS)
{ /* Long press detected*/
handle->event = (uint8_t)BTN_LONG_PRESS_START;
event_invoke(handle, BTN_LONG_PRESS_START);
handle->state = BTN_STATE_LONG_HOLD;
}
break;
case BTN_STATE_WAIT_RELEASE:
if (handle->button_level == handle->active_level)
{
/* Button pressed again before release timeout: treat as new press (multiple presses) */
handle->event = (uint8_t)BTN_PRESS_DOWN;
event_invoke(handle, BTN_PRESS_DOWN);
if (handle->repeat < PRESS_PEPEAT_MAX_NUM)
{
handle->repeat++;
}
/* Notify about repeat press sequence */
event_invoke(handle, 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_invoke(handle, BTN_SINGLE_CLICK);
}
else if (handle->repeat == 2)
{
handle->event = (uint8_t)BTN_DOUBLE_CLICK;
event_invoke(handle, 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_invoke(handle, BTN_PRESS_UP);
if (handle->ticks < SHORT_TICKS)
{
/* Released before short press timeout: go back to wait for more presses */
handle->ticks = 0;
handle->state = BTN_STATE_WAIT_RELEASE;
}
else
{
/* Released after short press timeout: end of press sequence */
handle->state = BTN_STATE_IDLE;
}
}
else if (handle->ticks >= SHORT_TICKS)
{
handle->state = BTN_STATE_PRESS;
}
break;
case BTN_STATE_LONG_HOLD:
if (handle->button_level == handle->active_level)
{ /* Continue holding: repeated long-hold events may be produced.
* Application should be prepared for frequent callbacks (every tick
* interval). If that is undesirable, throttle in the application
* or modify this code to generate hold events less frequently. */
handle->event = (uint8_t)BTN_LONG_PRESS_HOLD;
event_invoke(handle, BTN_LONG_PRESS_HOLD);
}else{
/* Button released*/
handle->event = (uint8_t)BTN_PRESS_UP;
event_invoke(handle, BTN_PRESS_UP);
handle->state = BTN_STATE_IDLE;
}
break;
default:
/*invalid state*/
handle->state = BTN_STATE_IDLE;
break;
}
}
代码功能介绍
- 先读取当前 GPIO 电平(通过 handle->hal_button_level(handle->button_id))。
- 去抖:如果采样与上次稳定值不同,则增加 debounce_cnt;当 debounce_cnt 达到 DEBOUNCE_TICKS 时才把 button_level 更新为新电平;否则若采样与当前稳定值相同则把 debounce_cnt 复位。
- 计时:在非 IDLE 状态下,ticks++ 用以测量按下保持时间(用于短按/长按判断)。
- 状态转换(FSM):
- IDLE -> PRESS(检测到按下): 触发 BTN_PRESS_DOWN 回调,repeat=1,ticks=0。
- PRESS -> RELEASE(检测到松开): 触发 BTN_PRESS_UP 回调,ticks=0。
- PRESS -> LONG_HOLD(ticks 超过 LONG_TICKS): 触发 BTN_LONG_PRESS_START,进入 LONG_HOLD;在 LONG_HOLD 状态会持续产生 BTN_LONG_PRESS_HOLD 事件直到松开。
- RELEASE -> REPEAT(在短时窗内再次按下): 触发 BTN_PRESS_DOWN 与 BTN_PRESS_REPEAT(更新 repeat 计数)。
- RELEASE -> IDLE(超时未再按): 根据 repeat 触发 BTN_SINGLE_CLICK / BTN_DOUBLE_CLICK。
- REPEAT 根据后续 ticks/松开决定回 RELEASE 或 IDLE 或回 PRESS。
- 因此状态机是基于“周期采样 + 去抖 + 基于 ticks 的定时阈值判定”的设计。每个采样周期都会评估一次去抖和状态转移。
启用、停止、单次轮询函数封装
/**
* @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
*
* This function uses critical section macros to protect the shared list.
*/
int button_start(Button *handle){
if(!handle)
return -2; // invalid parameter
/*Protect scanning and insertion against concurrent modification*/
BUTTON_ENTER_CRITICAL();
Button *target= (Button *)head_handle;
while(target){
if(target == handle){
/*Check if already in list*/
BUTTON_EXIT_CRITICAL();
return -1; // already exist
}
target = target->next;
}
/*insert at head*/
handle->next = head_handle;
head_handle = handle;
BUTTON_EXIT_CRITICAL();
return 0; // succeed
}
/**
* @brief Stop the button work, remove the handle from work list
* @param handle: target handle struct
* @retval None
*
* Removal uses the pointer-to-pointer technique and is protected by the
* critical section macros to avoid races with traversal in button_ticks().
*/
void button_stop(Button *handle)
{
if (!handle)
return; /* parameter validation */
BUTTON_ENTER_CRITICAL();
Button **curr;
for (curr = (Button **)&head_handle; *curr;)
{
Button *entry = *curr;
if (entry == handle)
{
*curr = entry->next;
entry->next = NULL; /* clear next pointer */
BUTTON_EXIT_CRITICAL();
return;
}
else
{
curr = &entry->next;
}
}
BUTTON_EXIT_CRITICAL();
}
/**
* @brief Background ticks, timer repeat invoking interval 5ms
* @param None
* @retval None
*
* Typically called from a periodic timer (e.g. every 5ms). If called from
* an ISR, keep callbacks short. If callbacks should run in thread context,
* have callbacks enqueue events to a queue and handle them later in a task.
*/
void button_ticks(void)
{
/* Snapshot the head to a local variable to reduce time spent with the
* (possible) volatile head_handle and to avoid holding the critical
* section for the whole traversal. If the list is modified concurrently,
* traversal may see a slightly stale view but won't crash (provided
* modifications use the same critical macros). */
Button *target;
/* read volatile once */
target = (Button *)head_handle;
for (; target; target = target->next)
{
button_handler(target);
}
}
自己需要初始化的函数示例
上述代码主要是对核心功能的设置,但是对于按键初始化、回调函数的设置并没有完成,可以参考原作者的几个示例文件,不过作者的示例文件主要是模拟代码,这里给出一个实际的按键结合代码,可能有点小bug。
从前文代码看出,按键组之间是通过链表进行整合管理的,在代码中需要对于不同的按键设置对应的回调函数,如果比较复杂,建议进行实例的拆分。
/*
* Simple demo wiring multi_button callbacks to hardware and printing events.
*
* Usage notes:
* - This example assumes:
* * MX_GPIO_Init() has configured Key0_Pin / Key1_Pin (inputs, pull-up)
* and LED0_Pin / LED1_Pin (outputs) as in your gpio.c.
* * printf is routed to UART via __io_putchar (you provided it).
* * button_ticks() will be called from the main loop (or a task)
* at a regular interval (e.g. every 5 ms). Calling button_ticks()
* from an ISR means callbacks would run in ISR context; avoid heavy
* operations in callbacks if you do that.
*
* - The callbacks below print event messages and toggle LEDs. LONG_PRESS_HOLD
* is throttled so it doesn't spam the console (it is called frequently).
*/
#include "button_demo.h"
#include "main.h" /* includes HAL and project pin defs */
#include "multi_button.h"
#include "gpio.h"
#include <stdio.h>
/* Forward: HAL read function for buttons used by multi_button */
static uint8_t button_gpio_read(uint8_t id);
/* Buttons (one per physical key) */
static Button btn0;
static Button btn1;
/* Throttle counters for LONG_PRESS_HOLD to avoid flooding UART */
static uint16_t long_hold_counters[2] = {0, 0};
/* --------- Callbacks for button 0 (Key0) --------- */
static void btn0_press_down_cb(Button *btn)
{
printf("[BTN0] PRESS DOWN\r\n");
/* Turn LED0 on (assuming active low LED; adjust if active high) */
HAL_GPIO_WritePin(GPIOE, LED0_Pin, GPIO_PIN_RESET);
}
static void btn0_press_up_cb(Button *btn)
{
printf("[BTN0] PRESS UP\r\n");
/* Turn LED0 off */
HAL_GPIO_WritePin(GPIOE, LED0_Pin, GPIO_PIN_SET);
}
static void btn0_single_click_cb(Button *btn)
{
printf("[BTN0] SINGLE CLICK\r\n");
/* Toggle LED1 to show a click */
HAL_GPIO_TogglePin(GPIOE, LED1_Pin);
}
static void btn0_double_click_cb(Button *btn)
{
printf("[BTN0] DOUBLE CLICK\r\n");
/* Toggle LED1 twice quickly (visual cue) */
HAL_GPIO_TogglePin(GPIOE, LED1_Pin);
HAL_Delay(80);
HAL_GPIO_TogglePin(GPIOE, LED1_Pin);
}
static void btn0_press_repeat_cb(Button *btn)
{
uint8_t rpt = button_get_repeat_count(btn);
printf("[BTN0] REPEAT count=%u\r\n", (unsigned)rpt);
}
/* Called once when long press threshold is reached */
static void btn0_long_start_cb(Button *btn)
{
printf("[BTN0] LONG PRESS START\r\n");
/* Indicate long press by turning LED0 on solid */
HAL_GPIO_WritePin(GPIOE, LED0_Pin, GPIO_PIN_RESET);
}
/* Called frequently while holding long; throttle to ~200ms intervals to avoid spam */
static void btn0_long_hold_cb(Button *btn)
{
uint8_t id = btn->button_id;
long_hold_counters[id]++;
/* Assuming button_ticks tick is ~5ms, 200ms ~= 40 ticks */
if ((long_hold_counters[id] % 40) == 0)
{
printf("[BTN0] LONG HOLD (periodic)\r\n");
/* blink LED1 to indicate continued hold */
HAL_GPIO_TogglePin(GPIOE, LED1_Pin);
}
}
/* --------- Callbacks for button 1 (Key1) - similar but distinct messages --------- */
static void btn1_press_down_cb(Button *btn)
{
printf("[BTN1] PRESS DOWN\r\n");
HAL_GPIO_WritePin(GPIOE, LED1_Pin, GPIO_PIN_RESET);
}
static void btn1_press_up_cb(Button *btn)
{
printf("[BTN1] PRESS UP\r\n");
HAL_GPIO_WritePin(GPIOE, LED1_Pin, GPIO_PIN_SET);
}
static void btn1_single_click_cb(Button *btn)
{
printf("[BTN1] SINGLE CLICK\r\n");
HAL_GPIO_TogglePin(GPIOE, LED0_Pin);
}
static void btn1_double_click_cb(Button *btn)
{
printf("[BTN1] DOUBLE CLICK\r\n");
HAL_GPIO_TogglePin(GPIOE, LED0_Pin);
HAL_Delay(80);
HAL_GPIO_TogglePin(GPIOE, LED0_Pin);
}
static void btn1_press_repeat_cb(Button *btn)
{
uint8_t rpt = button_get_repeat_count(btn);
printf("[BTN1] REPEAT count=%u\r\n", (unsigned)rpt);
}
static void btn1_long_start_cb(Button *btn)
{
printf("[BTN1] LONG PRESS START\r\n");
HAL_GPIO_WritePin(GPIOE, LED1_Pin, GPIO_PIN_RESET);
}
static void btn1_long_hold_cb(Button *btn)
{
uint8_t id = btn->button_id;
long_hold_counters[id]++;
if ((long_hold_counters[id] % 40) == 0)
{
printf("[BTN1] LONG HOLD (periodic)\r\n");
HAL_GPIO_TogglePin(GPIOE, LED0_Pin);
}
}
/* --------- Helper: map button id to HAL GPIO reading --------- */
static uint8_t button_gpio_read(uint8_t id)
{
/* Your gpio.c config uses pull-ups. Assume KEY pressed = GPIO_PIN_RESET (active low).
Return 1 for pressed, 0 for released (multi_button expects level values; we use 1 as active) */
if (id == 0)
{
return (HAL_GPIO_ReadPin(GPIOE, Key0_Pin) == GPIO_PIN_RESET) ? 1 : 0;
}
else if (id == 1)
{
return (HAL_GPIO_ReadPin(GPIOE, Key1_Pin) == GPIO_PIN_RESET) ? 1 : 0;
}
return 0;
}
/* --------- Initialization function to wire up buttons (call this from main) --------- */
void ButtonDemo_Init(void)
{
/* Initialize button structs: pin read function, active level (1 = pressed in our mapping), id */
button_init(&btn0, button_gpio_read, 1, 0);
button_init(&btn1, button_gpio_read, 1, 1);
/* Attach callbacks for btn0 */
button_attach(&btn0, BTN_PRESS_DOWN, btn0_press_down_cb);
button_attach(&btn0, BTN_PRESS_UP, btn0_press_up_cb);
button_attach(&btn0, BTN_SINGLE_CLICK, btn0_single_click_cb);
button_attach(&btn0, BTN_DOUBLE_CLICK, btn0_double_click_cb);
button_attach(&btn0, BTN_PRESS_REPEAT, btn0_press_repeat_cb);
button_attach(&btn0, BTN_LONG_PRESS_START, btn0_long_start_cb);
button_attach(&btn0, BTN_LONG_PRESS_HOLD, btn0_long_hold_cb);
/* Attach callbacks for btn1 */
button_attach(&btn1, BTN_PRESS_DOWN, btn1_press_down_cb);
button_attach(&btn1, BTN_PRESS_UP, btn1_press_up_cb);
button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_cb);
button_attach(&btn1, BTN_DOUBLE_CLICK, btn1_double_click_cb);
button_attach(&btn1, BTN_PRESS_REPEAT, btn1_press_repeat_cb);
button_attach(&btn1, BTN_LONG_PRESS_START, btn1_long_start_cb);
button_attach(&btn1, BTN_LONG_PRESS_HOLD, btn1_long_hold_cb);
/* Add buttons to the driver's work list */
button_start(&btn0);
button_start(&btn1);
printf("Button demo initialized (btn0 id=0, btn1 id=1)\r\n");
}
/* Example main loop usage:
*
* int main(void) {
* HAL_Init();
* SystemClock_Config();
* MX_GPIO_Init();
* MX_USART1_UART_Init(); // assuming printf -> USART1
* MX_TIM5_Init(); // if you use hardware timer for delays
*
* ButtonDemo_Init();
*
* while (1) {
* button_ticks(); // call at fixed period (e.g. every 5 ms)
* HAL_Delay(5);
* }
* }
*
* If you move button_ticks() into an RTOS task, ButtonDemo_Init() still applies,
* but ensure button_ticks() runs in task context or that callbacks only enqueue
* events (use xQueueSendFromISR / notifications if button_ticks runs in ISR).
*/
510

被折叠的 条评论
为什么被折叠?



