/** ****************************************************************************** * @文件 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]); } }