| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- /**
- ******************************************************************************
- * @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;
- }
|