/** ****************************************************************************** * @file input_scan.c * @brief 编码器/按键扫描模块实现 * SW_KEY(PF3): 短按切换音源, 长按5s断开蓝牙 * SW1_KEY(PB8): 短按低音静音 * SW2_KEY(PA15): 短按快速静音 * SW1编码器(PA5/PA6): 低音音量 * SW2编码器(PB0/PA7): 主音量LR ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "input_scan.h" #include "app_state.h" #include "led_driver.h" #include "uart_protocol.h" /* Private define ------------------------------------------------------------*/ /* ========== 按钮 / 编码器引脚定义 (外部上拉, 低电平有效) ========== */ #define SW_KEY_PIN GPIO_PIN_3 #define SW_KEY_PORT GPIOF #define SW1_A_PIN GPIO_PIN_5 #define SW1_A_PORT GPIOA #define SW1_B_PIN GPIO_PIN_6 #define SW1_B_PORT GPIOA #define SW1_KEY_PIN GPIO_PIN_8 #define SW1_KEY_PORT GPIOB #define SW2_A_PIN GPIO_PIN_0 #define SW2_A_PORT GPIOB #define SW2_B_PIN GPIO_PIN_7 #define SW2_B_PORT GPIOA #define SW2_KEY_PIN GPIO_PIN_15 #define SW2_KEY_PORT GPIOA /* 长按时间 */ #define LONG_PRESS_MS 5000u /* SW_KEY 长按 5s 断开蓝牙 */ /* 编码器变速参数 (按单脉冲间隔计算) */ #define ENC_SLOW_PULSE_MS 200u /* 单脉冲 >200ms: 慢速, 需4脉冲 */ #define ENC_FAST_PULSE_MS 75u /* 单脉冲 <75ms: 快速, 需1脉冲 */ /* Private variables ---------------------------------------------------------*/ /* ========== 按键事件标志 (由 main.c 读取处理) ========== */ volatile uint8_t g_swKeyEdge = 0u; volatile uint8_t g_sw1KeyEdge = 0u; volatile uint8_t g_sw2KeyEdge = 0u; volatile uint8_t g_swKeyLongPress = 0u; /* ========== 双键同按状态 ========== */ volatile uint8_t g_bothPressFlag = 0u; volatile uint16_t g_bothPressMs = 0u; /* ========== 按键内部状态 ========== */ static volatile uint8_t g_swKeyLast = 1u; /* 前一次稳定电平 (0=按下, 1=释放) */ static volatile uint8_t g_sw1KeyLast = 1u; static volatile uint8_t g_sw2KeyLast = 1u; static volatile uint8_t g_swKeyHeld = 0u; /* SW_KEY 正在被按住 */ static volatile uint16_t g_swKeyPressMs = 0u; /* SW_KEY 按下持续时间 */ static volatile uint8_t g_swKeyPressed_1 = 0u; /* 见过按下标志 */ static volatile uint8_t g_sw1KeyPressed_1 = 0u; static volatile uint8_t g_sw2KeyPressed_1 = 0u; /* ========== 编码器内部状态 ========== */ /* 编码器前一次AB状态 (位1=B, 位0=A) */ static volatile uint8_t g_enc1PrevAB = 0u; static volatile uint8_t g_enc2PrevAB = 0u; /* 脉冲计数器 */ static volatile int8_t g_enc1PulseCnt = 0; static volatile int8_t g_enc2PulseCnt = 0; /* 上次触发时间 (ms) */ static volatile uint32_t g_enc1LastMs = 0u; static volatile uint32_t g_enc2LastMs = 0u; /* ========== 编码器正交解码查找表 ========== */ /* index = (prevAB << 2) | currAB */ /* value: 0=无变化, -1=A方向(顺时针), 1=B方向(逆时针) [AB方向已反转] */ static const int8_t s_encTable[16] = { 0, 1, -1, 0, /* prev=00 */ -1, 0, 0, 1, /* prev=01 */ 1, 0, 0, -1, /* prev=10 */ 0, -1, 1, 0 /* prev=11 */ }; /* Private function prototypes -----------------------------------------------*/ static uint8_t Encoder_ReadAB(uint8_t encIdx); static int8_t Encoder_Decode(uint8_t encIdx, uint8_t currAB); static uint8_t ProcessOneEncoder(int8_t dir, volatile int8_t *pPulseCnt, volatile int16_t *pLevel, volatile uint32_t *pLastMs); /* ========== 初始化 ========== */ /** * @brief 读取编码器和按键的初始状态 * 防止 MCU 复位时按着的按键松开后产生误触发 */ void InputScan_Init(void) { /* 记录编码器初始 AB 状态 */ g_enc1PrevAB = (uint8_t)( ((HAL_GPIO_ReadPin(SW1_B_PORT, SW1_B_PIN) == GPIO_PIN_SET) ? 2u : 0u) | ((HAL_GPIO_ReadPin(SW1_A_PORT, SW1_A_PIN) == GPIO_PIN_SET) ? 1u : 0u)); g_enc2PrevAB = (uint8_t)( ((HAL_GPIO_ReadPin(SW2_B_PORT, SW2_B_PIN) == GPIO_PIN_SET) ? 2u : 0u) | ((HAL_GPIO_ReadPin(SW2_A_PORT, SW2_A_PIN) == GPIO_PIN_SET) ? 1u : 0u)); /* 按键初始状态与引脚一致 */ g_swKeyLast = (HAL_GPIO_ReadPin(SW_KEY_PORT, SW_KEY_PIN) == GPIO_PIN_RESET) ? 0u : 1u; g_sw1KeyLast = (HAL_GPIO_ReadPin(SW1_KEY_PORT, SW1_KEY_PIN) == GPIO_PIN_RESET) ? 0u : 1u; g_sw2KeyLast = (HAL_GPIO_ReadPin(SW2_KEY_PORT, SW2_KEY_PIN) == GPIO_PIN_RESET) ? 0u : 1u; /* 清除边缘标志 */ g_swKeyEdge = 0u; g_sw1KeyEdge = 0u; g_sw2KeyEdge = 0u; } /* ========== 按键扫描 ========== */ /** * @brief 扫描所有按键 (TIM3 中断中每 1ms 调用) */ void ScanButtons(void) { uint8_t swVal; /* ----- SW_KEY (PF3): 短按音源切换, 长按5s断开蓝牙 ----- */ swVal = (HAL_GPIO_ReadPin(SW_KEY_PORT, SW_KEY_PIN) == GPIO_PIN_RESET) ? 0u : 1u; if (swVal == 0u) { if (g_swKeyLast == 1u) { g_swKeyLast = 0u; g_swKeyHeld = 1u; g_swKeyPressMs = 0u; g_swKeyPressed_1 = 1u; } if (g_swKeyHeld) { g_swKeyPressMs++; if (g_swKeyPressMs >= LONG_PRESS_MS && g_swKeyLongPress == 0u) { g_swKeyLongPress = 1u; g_swKeyPressMs = 0u; } } } else if (g_swKeyLast == 0u) { g_swKeyLast = 1u; g_swKeyHeld = 0u; /* 必须完整 1→0→1, 且长按已处理则不触发短按 */ if (g_swKeyPressed_1 && g_swKeyLongPress == 0u) { g_swKeyEdge = 1u; } g_swKeyLongPress = 0u; g_swKeyPressed_1 = 0u; g_swKeyPressMs = 0u; } /* ----- SW1_KEY (PB8): 静音按键 (完整1→0→1才触发) ----- */ swVal = (HAL_GPIO_ReadPin(SW1_KEY_PORT, SW1_KEY_PIN) == GPIO_PIN_RESET) ? 0u : 1u; if (swVal == 0u && g_sw1KeyLast == 1u) { g_sw1KeyLast = 0u; g_sw1KeyPressed_1 = 1u; } else if (swVal == 1u && g_sw1KeyLast == 0u) { g_sw1KeyLast = 1u; if (g_sw1KeyPressed_1) { g_sw1KeyPressed_1 = 0u; g_sw1KeyEdge = 1u; } } /* ----- SW2_KEY (PA15): 快速静音 (完整1→0→1才触发) ----- */ swVal = (HAL_GPIO_ReadPin(SW2_KEY_PORT, SW2_KEY_PIN) == GPIO_PIN_RESET) ? 0u : 1u; if (swVal == 0u && g_sw2KeyLast == 1u) { g_sw2KeyLast = 0u; g_sw2KeyPressed_1 = 1u; } else if (swVal == 1u && g_sw2KeyLast == 0u) { g_sw2KeyLast = 1u; if (g_sw2KeyPressed_1) { g_sw2KeyPressed_1 = 0u; g_sw2KeyEdge = 1u; } } /* ----- 双键同按检测: 两键均按下持续10s触发软复位 ----- */ if (HAL_GPIO_ReadPin(SW1_KEY_PORT, SW1_KEY_PIN) == GPIO_PIN_RESET && HAL_GPIO_ReadPin(SW2_KEY_PORT, SW2_KEY_PIN) == GPIO_PIN_RESET) { if (!g_bothPressFlag) { g_bothPressFlag = 1u; g_bothPressMs = 0u; } } else { if (g_bothPressFlag) { g_bothPressFlag = 0u; g_bothPressMs = 0u; } } } /* ========== 编码器扫描 ========== */ /** * @brief 扫描所有编码器 (TIM3 中断中每 1ms 调用) */ void ScanEncoders(void) { int8_t dir; /* ----- SW1编码器: 低音音量 ----- */ dir = Encoder_Decode(0u, Encoder_ReadAB(0u)); if (ProcessOneEncoder(dir, &g_enc1PulseCnt, &g_volBassLevel, &g_enc1LastMs)) { g_flagSendVolBass = 1u; } /* ----- SW2编码器: 主音量LR ----- */ dir = Encoder_Decode(1u, Encoder_ReadAB(1u)); if (dir != 0 && g_mutedByBtn) { g_mutedByBtn = 0u; g_flagSendDacUnmute = 1u; } if (ProcessOneEncoder(dir, &g_enc2PulseCnt, &g_volLRLevel, &g_enc2LastMs)) { g_flagSendVolLR = 1u; } } /* ========== 私有函数 ========== */ /** * @brief 读取编码器AB引脚 * @param encIdx 0=编码器1(SW1), 1=编码器2(SW2) * @retval (B<<1 | A) 值 0-3 */ static uint8_t Encoder_ReadAB(uint8_t encIdx) { uint8_t a, b; if (encIdx == 0u) { a = (HAL_GPIO_ReadPin(SW1_A_PORT, SW1_A_PIN) == GPIO_PIN_SET) ? 1u : 0u; b = (HAL_GPIO_ReadPin(SW1_B_PORT, SW1_B_PIN) == GPIO_PIN_SET) ? 1u : 0u; } else { a = (HAL_GPIO_ReadPin(SW2_A_PORT, SW2_A_PIN) == GPIO_PIN_SET) ? 1u : 0u; b = (HAL_GPIO_ReadPin(SW2_B_PORT, SW2_B_PIN) == GPIO_PIN_SET) ? 1u : 0u; } return (uint8_t)((b << 1u) | a); } /** * @brief 解码编码器旋转方向 * @param encIdx 0=编码器1, 1=编码器2 * @param currAB 当前AB状态 * @retval 1=A方向, -1=B方向, 0=无变化 */ static int8_t Encoder_Decode(uint8_t encIdx, uint8_t currAB) { volatile uint8_t *pPrevAB; uint8_t prevAB; uint8_t index; int8_t result; pPrevAB = (encIdx == 0u) ? &g_enc1PrevAB : &g_enc2PrevAB; prevAB = *pPrevAB; index = (uint8_t)((prevAB << 2u) | currAB); result = s_encTable[index]; if (result != 0) { *pPrevAB = currAB; } return result; } /** * @brief 单编码器处理: 累计脉冲, 根据单脉冲速度动态调整阈值 * @retval 1: 触发了音量改变, 0: 未触发 */ static uint8_t ProcessOneEncoder(int8_t dir, volatile int8_t *pPulseCnt, volatile int16_t *pLevel, volatile uint32_t *pLastMs) { if (dir == 0) return 0u; uint32_t now = g_sysMs; uint32_t interval = now - *pLastMs; *pLastMs = now; if (dir > 0) { (*pPulseCnt)++; } else { (*pPulseCnt)--; } /* 速度判定基于相邻两次脉冲的间隔 */ uint8_t threshold; if (interval > ENC_SLOW_PULSE_MS) threshold = 4u; /* 慢速: 需4脉冲 */ else if (interval < ENC_FAST_PULSE_MS) threshold = 1u; /* 快速: 需1脉冲 */ else threshold = 2u; /* 中速: 需2脉冲 */ if (*pPulseCnt >= (int8_t)threshold) { *pPulseCnt = 0; int16_t level = *pLevel; if (level < 100) { *pLevel = level + 1; g_userActive = 1u; g_idleTimer = 0u; return 1u; } } else if (*pPulseCnt <= -(int8_t)threshold) { *pPulseCnt = 0; int16_t level = *pLevel; if (level > 0) { *pLevel = level - 1; g_userActive = 1u; g_idleTimer = 0u; return 1u; } } return 0u; }