first commit
This commit is contained in:
292
Drivers/BSP/KEY/MultiButton.c
Normal file
292
Drivers/BSP/KEY/MultiButton.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
|
||||
* All rights reserved
|
||||
*/
|
||||
|
||||
#include "MultiButton.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);
|
||||
}
|
||||
}
|
||||
84
Drivers/BSP/KEY/MultiButton.h
Normal file
84
Drivers/BSP/KEY/MultiButton.h
Normal 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
|
||||
147
Drivers/BSP/KEY/key.c
Normal file
147
Drivers/BSP/KEY/key.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @文件 key.c
|
||||
* @作者 阜阳师范大学物电学院
|
||||
* @版本 V0.1
|
||||
* @日期 2026-01-15
|
||||
* @简介 按键驱动 - 基于 MultiButton 的高可移植性实现
|
||||
* @说明
|
||||
*
|
||||
****
|
||||
*/
|
||||
|
||||
#include "key.h"
|
||||
|
||||
|
||||
// 按键配置结构体
|
||||
typedef struct {
|
||||
uint16_t pin; // GPIO引脚
|
||||
GPIO_TypeDef* port; // GPIO端口
|
||||
KEY_TYPE key_type; // 按键ID
|
||||
} KeyConfig_t;
|
||||
|
||||
/*
|
||||
*******************详细引脚映射*****************************
|
||||
| 按键名称 | 端口 | 引脚 | 位定义 | 功能说明 |
|
||||
|---------|------|------|--------|----------|
|
||||
| KEY_ENTER | GPIOB | PB15 | KEY_ENTER_BIT | 确认键 |
|
||||
| KEY_UP | GPIOD | PD11 | KEY_UP_BIT | 上键 |
|
||||
| KEY_DOWN | GPIOD | PD10 | KET_DOWN_BIT | 下键 |
|
||||
| KEY_LEFT | GPIOD | PD13 | KET_LEFT_BIT | 左键 |
|
||||
| KEY_RIGHT | GPIOD | PD8 | KET_RIGHT_BIT | 右键 |
|
||||
| KEY_ESC | GPIOD | PD12 | KET_ESC_BIT | 取消键 |
|
||||
| KEY_ADD | GPIOD | PD14 | KET_ADD_BIT | 加键 |
|
||||
| KEY_DEC | GPIOD | PD9 | KET_DEC_BIT | 减键 |
|
||||
| KEY_RESET | GPIOD | PD15 | KET_RESET_BIT | 复位键 |
|
||||
***************************************************************
|
||||
*/
|
||||
// 按键配置表
|
||||
static const KeyConfig_t key_configs[] = {
|
||||
{15, GPIOB, KEY_ENTER},
|
||||
{11, GPIOD, KEY_UP},
|
||||
{10, GPIOD, KEY_DOWN},
|
||||
{13, GPIOD, KEY_LEFT},
|
||||
{8, GPIOD, KEY_RIGHT},
|
||||
{12, GPIOD, KEY_ESC},
|
||||
{14, GPIOD, KEY_ADD},
|
||||
{9, GPIOD, KEY_DEC},
|
||||
{15, GPIOD, KEY_RESET},
|
||||
};
|
||||
#define KEY_COUNT (sizeof(key_configs) / sizeof(key_configs[0]))
|
||||
|
||||
// 按键句柄数组
|
||||
static Button btn_handles[KEY_COUNT];
|
||||
// 业务逻辑回调函数指针(由main.c注册)
|
||||
static KeyCallback key_callback = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* @brief 读取按键GPIO电平
|
||||
* @param button_id 按键ID
|
||||
* @retval 0:按下, 1:释放
|
||||
*/
|
||||
static uint8_t button_read_level(uint8_t button_id)
|
||||
{
|
||||
uint16_t pin_bit = HAL_GPIO_ReadPin(key_configs[button_id].port, key_configs[button_id].pin);
|
||||
return pin_bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 统一的按键回调函数(内部使用,调用业务逻辑回调)
|
||||
* @param btn 按键句柄指针
|
||||
*/
|
||||
static void button_callback(Button* btn)
|
||||
{
|
||||
if (btn != NULL && key_callback != NULL)
|
||||
{
|
||||
// 通过button_id获取对应的按键类型
|
||||
uint8_t button_id = btn->button_id;
|
||||
if (button_id < KEY_COUNT)
|
||||
{
|
||||
// 调用业务逻辑层注册的回调函数
|
||||
key_callback(key_configs[button_id].key_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief 注册按键回调函数(供业务逻辑层调用)
|
||||
* @param callback 回调函数指针
|
||||
*/
|
||||
void Key_RegisterCallback(KeyCallback callback)
|
||||
{
|
||||
key_callback = callback;
|
||||
}
|
||||
/**
|
||||
* @brief 使能GPIO时钟
|
||||
* @param port: GPIO端口
|
||||
* @retval 无
|
||||
* @note 使用switch-case结构,代码更简洁清晰,支持所有GPIO端口(A-G)
|
||||
*/
|
||||
static void KEY_GPIO_ClockEnable(GPIO_TypeDef *port)
|
||||
{
|
||||
switch ((uint32_t)port) {
|
||||
case (uint32_t)GPIOA: __HAL_RCC_GPIOA_CLK_ENABLE(); break;
|
||||
case (uint32_t)GPIOB: __HAL_RCC_GPIOB_CLK_ENABLE(); break;
|
||||
case (uint32_t)GPIOC: __HAL_RCC_GPIOC_CLK_ENABLE(); break;
|
||||
case (uint32_t)GPIOD: __HAL_RCC_GPIOD_CLK_ENABLE(); break;
|
||||
case (uint32_t)GPIOE: __HAL_RCC_GPIOE_CLK_ENABLE(); break;
|
||||
case (uint32_t)GPIOF: __HAL_RCC_GPIOF_CLK_ENABLE(); break;
|
||||
case (uint32_t)GPIOG: __HAL_RCC_GPIOG_CLK_ENABLE(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
/**************************************************************************************
|
||||
* FunctionName : Key_Init()
|
||||
* Description : 按键硬件初始化(使用MultiButton库)
|
||||
* EntryParameter : none
|
||||
* ReturnValue : none
|
||||
**************************************************************************************/
|
||||
void Key_Init(void)
|
||||
{
|
||||
GPIO_InitTypeDef gpio_init_struct;
|
||||
|
||||
// 统一配置GPIO参数
|
||||
gpio_init_struct.Mode = GPIO_MODE_INPUT;
|
||||
gpio_init_struct.Pull = GPIO_PULLUP;
|
||||
gpio_init_struct.Speed = GPIO_SPEED_HIGH;
|
||||
|
||||
// 批量初始化GPIO
|
||||
for (uint8_t i = 0; i < KEY_COUNT; i++)
|
||||
{
|
||||
/* 使能对应GPIO时钟 */
|
||||
KEY_GPIO_ClockEnable(key_configs[i].port);
|
||||
|
||||
// 配置并初始化引脚
|
||||
gpio_init_struct.Pin = key_configs[i].pin;
|
||||
HAL_GPIO_Init(key_configs[i].port, &gpio_init_struct);
|
||||
}
|
||||
|
||||
// 批量初始化MultiButton按键(active_level=0表示低电平有效)
|
||||
for (uint8_t button_id = 0; button_id < KEY_COUNT; button_id++)
|
||||
{
|
||||
button_init(&btn_handles[button_id], button_read_level, 0, button_id);
|
||||
button_attach(&btn_handles[button_id], BTN_SINGLE_CLICK, button_callback);
|
||||
button_start(&btn_handles[button_id]);
|
||||
}
|
||||
}
|
||||
|
||||
35
Drivers/BSP/KEY/key.h
Normal file
35
Drivers/BSP/KEY/key.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef __KEY_H__
|
||||
#define __KEY_H__
|
||||
|
||||
|
||||
|
||||
#include "./SYSTEM/sys/sys.h"
|
||||
#include "MultiButton.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
KEY_NONE = 0, // 没有按键
|
||||
|
||||
KEY_ENTER, // 确认
|
||||
KEY_UP, // 向上
|
||||
KEY_DOWN, // 向下
|
||||
KEY_ESC, //取消
|
||||
KEY_ADD, //加
|
||||
KEY_DEC, //减
|
||||
KEY_LEFT, //左
|
||||
KEY_RIGHT, //右
|
||||
KEY_RESET, //复位
|
||||
KEY_FACTORY, //工厂
|
||||
} KEY_TYPE;
|
||||
|
||||
// 按键回调函数类型定义(业务逻辑层使用)
|
||||
// 参数: key_type - 按键类型
|
||||
typedef void (*KeyCallback)(KEY_TYPE key_type);
|
||||
|
||||
|
||||
|
||||
// 按键驱动函数(在key.c中实现,使用MultiButton库)
|
||||
void Key_Init(void); // 按键初始化
|
||||
void Key_RegisterCallback(KeyCallback callback); // 注册按键回调函数(业务逻辑层调用)
|
||||
#define Button_Ticks() button_ticks()
|
||||
#endif
|
||||
Reference in New Issue
Block a user