/** ****************************************************************************** * @file led_driver.c * @brief LED 驱动模块实现 * 管理 13 个反向并联 LED 的底层状态机和 GPIO 操作. * 同时提供基于应用状态的 LED 显示更新函数. ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "led_driver.h" #include "app_state.h" /* Private define ------------------------------------------------------------*/ /* ========== LED 引脚定义 (13个) ========== */ /* 避免与BSP头文件中 LED3_PIN/LED3_GPIO_PORT 冲突, 使用 GPIO_LEDx_PIN/GPIO_LEDx_PORT 命名 */ #define GPIO_LED1_PIN GPIO_PIN_1 #define GPIO_LED1_PORT GPIOA #define GPIO_LED2_PIN GPIO_PIN_7 #define GPIO_LED2_PORT GPIOB #define GPIO_LED3_PIN GPIO_PIN_6 #define GPIO_LED3_PORT GPIOB #define GPIO_LED4_PIN GPIO_PIN_5 #define GPIO_LED4_PORT GPIOB #define GPIO_LED5_PIN GPIO_PIN_0 #define GPIO_LED5_PORT GPIOA #define GPIO_LED6_PIN GPIO_PIN_2 #define GPIO_LED6_PORT GPIOB #define GPIO_LED7_PIN GPIO_PIN_4 #define GPIO_LED7_PORT GPIOB #define GPIO_LED8_PIN GPIO_PIN_3 #define GPIO_LED8_PORT GPIOB #define GPIO_LED9_PIN GPIO_PIN_12 #define GPIO_LED9_PORT GPIOA #define GPIO_LED10_PIN GPIO_PIN_1 #define GPIO_LED10_PORT GPIOB #define GPIO_LED11_PIN GPIO_PIN_4 #define GPIO_LED11_PORT GPIOA #define GPIO_LED12_PIN GPIO_PIN_8 #define GPIO_LED12_PORT GPIOA #define GPIO_LED13_PIN GPIO_PIN_11 #define GPIO_LED13_PORT GPIOA /* ========== 闪烁计时参数 (基于TIM3 100us = 0.1ms) ========== */ #define BLINK_500MS_PERIOD_TICKS 2500u /* 500ms周期 (2Hz) = 每250ms翻转 */ #define BLINK_1S_PERIOD_TICKS 5000u /* 1s周期 (1Hz) = 每500ms翻转 */ /* 状态3 (双灯同亮) ~1kHz 翻转: 每 0.5ms (5 ticks) */ #define BLINK_STATE3_TICKS 5u /* Private variables ---------------------------------------------------------*/ /* 13个LED控制块 */ LED_Control_t g_leds[LED_COUNT]; /* ========== 私有函数声明 ========== */ static void LED_ApplyStateToRegister_Internal(uint8_t ledIdx); /* ========== 初始化 ========== */ /** * @brief 初始化 LED 控制块 * 为每颗 LED 填充端口、引脚及引脚序号, 状态默认为 0 (灭). */ void LED_InitControlBlock(void) { /* LED引脚配置表: 索引0-12 */ const struct { GPIO_TypeDef *port; uint16_t pin; } ledcfg[LED_COUNT] = { {GPIOA, GPIO_PIN_1}, /* 0: LED1 */ {GPIOB, GPIO_PIN_7}, /* 1: LED2 */ {GPIOB, GPIO_PIN_6}, /* 2: LED3 */ {GPIOB, GPIO_PIN_5}, /* 3: LED4 */ {GPIOA, GPIO_PIN_0}, /* 4: LED5 */ {GPIOB, GPIO_PIN_2}, /* 5: LED6 */ {GPIOB, GPIO_PIN_4}, /* 6: LED7 */ {GPIOB, GPIO_PIN_3}, /* 7: LED8 */ {GPIOA, GPIO_PIN_12}, /* 8: LED9 */ {GPIOB, GPIO_PIN_1}, /* 9: LED10 */ {GPIOA, GPIO_PIN_4}, /* 10: LED11 */ {GPIOA, GPIO_PIN_8}, /* 11: LED12 */ {GPIOA, GPIO_PIN_11}, /* 12: LED13 */ }; for (uint8_t i = 0; i < LED_COUNT; i++) { g_leds[i].port = ledcfg[i].port; g_leds[i].pin = ledcfg[i].pin; g_leds[i].state = 0; /* 计算引脚序号 (pinNum): 找到位索引 */ uint16_t p = ledcfg[i].pin; uint8_t n = 0; while (p > 1) { p >>= 1; n++; } g_leds[i].pinNum = n; } } /* ========== 状态控制 ========== */ /** * @brief 设置 LED 状态 (0-5) * @param ledIdx 索引 (0-12) * @param state 0=灭, 1=正向, 2=反向, 3=双灯, 4=500ms周期闪烁, 5=1s周期闪烁 */ void LED_SetState(uint8_t ledIdx, uint8_t state) { if (ledIdx >= LED_COUNT) return; if (state > 5u) state = 5u; g_leds[ledIdx].state = state; } /** * @brief 将单个 LED 当前状态应用到 GPIO 寄存器 * 可在中断内和主循环中安全调用 */ void LED_ApplyStateToRegister(uint8_t ledIdx) { if (ledIdx >= LED_COUNT) return; LED_ApplyStateToRegister_Internal(ledIdx); } /** * @brief 将所有 LED 状态应用到 GPIO 寄存器 */ void LED_ApplyAllStates(void) { for (uint8_t i = 0; i < LED_COUNT; i++) { LED_ApplyStateToRegister_Internal(i); } } /* ========== 定时器回调 ========== */ /** * @brief 每 100us 调用一次, 处理所有 LED 的定时翻转 */ void LED_Tick100us(void) { static uint32_t s_1kHzTick = 0u; /* 状态3 计数 */ static uint32_t s_blink500ms = 0u; /* 状态4 计数 */ static uint32_t s_blink1s = 0u; /* 状态5 计数 */ /* 状态3 (双灯同亮): 每 0.5ms 翻转 => ~1kHz */ s_1kHzTick++; if (s_1kHzTick >= BLINK_STATE3_TICKS) { s_1kHzTick = 0u; for (uint8_t i = 0; i < LED_COUNT; i++) { if (g_leds[i].state == 3u) { g_leds[i].port->ODR ^= (1UL << g_leds[i].pinNum); } } } /* 状态4 (500ms周期闪烁): 每 2500 ticks 翻转 */ s_blink500ms++; if (s_blink500ms >= BLINK_500MS_PERIOD_TICKS) { s_blink500ms = 0u; for (uint8_t i = 0; i < LED_COUNT; i++) { if (g_leds[i].state == 4u) { g_leds[i].port->ODR ^= (1UL << g_leds[i].pinNum); } } } /* 状态5 (1s周期闪烁): 每 5000 ticks 翻转 */ s_blink1s++; if (s_blink1s >= BLINK_1S_PERIOD_TICKS) { s_blink1s = 0u; for (uint8_t i = 0; i < LED_COUNT; i++) { if (g_leds[i].state == 5u) { g_leds[i].port->ODR ^= (1UL << g_leds[i].pinNum); } } } /* 状态6 (呼吸灯): 纯软件 PWM (针对推挽输出的独立灯如 LED11) */ static uint8_t s_breathPwmCount = 0u; /* PWM 周期计数 (0-99) */ static uint8_t s_breathDuty = 0u; /* 当前占空比 (0-100) */ static int8_t s_breathDir = 1; /* 呼吸方向: 1=渐亮, -1=渐隐 */ static uint8_t s_breathStepCount = 0u; /* 占空比调整分频 */ /* 每 100us 执行一次: 100个tick = 10ms PWM周期 = 100Hz 刷新率 */ s_breathPwmCount++; if (s_breathPwmCount >= 100u) { s_breathPwmCount = 0u; /* 每 10ms 调整一次亮度, 单程 100 * 10ms = 1秒, 完整呼吸周期 2秒 */ s_breathStepCount++; if (s_breathStepCount >= 1u) { s_breathStepCount = 0u; /* 更新占空比 */ if (s_breathDir == 1) { if (s_breathDuty < 100u) s_breathDuty++; else s_breathDir = -1; } else { if (s_breathDuty > 0u) s_breathDuty--; else s_breathDir = 1; } } } /* 执行 PWM 输出 */ for (uint8_t i = 0; i < LED_COUNT; i++) { if (g_leds[i].state == 6u) { if (s_breathPwmCount < s_breathDuty) { g_leds[i].port->BSRR = (1UL << g_leds[i].pinNum); /* 亮 (输出高) */ } else { g_leds[i].port->BSRR = (1UL << (g_leds[i].pinNum + 16u)); /* 灭 (输出低) */ } } } } /* ========== LED 显示更新 ========== */ /** * @brief 更新音量LED显示 (LED1~LED10, 索引0-9) * g_volBass → LED1~LED5 (索引0-4, SW1) * g_volLR → LED6~LED10 (索引5-9, SW2) * 方向: 倒序从LED5→LED1, LED10→LED6 逐级点亮 * 每颗灯对应2个等级 (正向+反向, 每级10%) */ void UpdateVolumeLEDs(void) { int16_t level; /* ----- 低音音量 (SW1) LED1~LED5 (索引0-4), 倒序: LED5→LED1 ----- */ if (g_volBassReceived) { level = g_volBassLevel; if (level < 0) level = 0; if (level > 100) level = 100; /* 换算10段 */ uint8_t seg = (uint8_t)((level + 9) / 10); if (seg > 10u) seg = 10u; for (uint8_t i = 0; i < 5u; i++) { uint8_t idx = (uint8_t)(4u - i); uint8_t rOn = (seg > (i * 2u)); uint8_t fOn = (seg > (i * 2u + 1u)); if (fOn && rOn) LED_SetState(idx, 3u); else if (rOn) LED_SetState(idx, 2u); else if (fOn) LED_SetState(idx, 1u); else LED_SetState(idx, 0u); } } /* ----- 主音量LR (SW2) LED6~LED10 (索引5-9), 倒序: LED10→LED6 ----- */ if (g_volLRReceived) { level = g_volLRLevel; if (level < 0) level = 0; if (level > 100) level = 100; uint8_t seg = (uint8_t)((level + 9) / 10); if (seg > 10u) seg = 10u; for (uint8_t i = 0; i < 5u; i++) { uint8_t idx = (uint8_t)(9u - i); uint8_t rOn = (seg > (i * 2u)); uint8_t fOn = (seg > (i * 2u + 1u)); if (fOn && rOn) LED_SetState(idx, 3u); else if (rOn) LED_SetState(idx, 2u); else if (fOn) LED_SetState(idx, 1u); else LED_SetState(idx, 0u); } } LED_ApplyAllStates(); } /** * @brief 更新输入源指示灯 (LED11, 索引10) * AUX输入: 常亮 (状态1) * BT已连接: 灭 (状态0) * BT断开: 1s周期闪烁 (状态5) */ void UpdateSourceLED(void) { if (!g_sourceReceived) return; uint8_t src = g_inputSource; uint8_t btCon = g_btConnState; if (src == 0x00u) /* INPUT_SRC_AUX (V2.0) */ { g_leds[10].state = 1u; /* 常亮 */ GPIOA->MODER = (GPIOA->MODER & ~(3UL << 8)) | (1UL << 8); /* PA4输出 */ GPIOA->BSRR = GPIO_PIN_4; } else if (src == 0x01u) /* INPUT_SRC_BT */ { /* 已连接 或 正在播放 (Airplay等协议可能无连接状态但有播放状态) -> 长灭 */ if (btCon == 0x01u || g_audioPlaying == 0x01u) { g_leds[10].state = 0u; /* 灭 */ GPIOA->BSRR = (GPIO_PIN_4 << 16); GPIOA->MODER &= ~(3UL << 8); } else { /* 未连接 且 未播放 -> 呼吸灯渐隐渐亮 */ g_leds[10].state = 6u; /* 状态6: 呼吸灯 */ GPIOA->MODER = (GPIOA->MODER & ~(3UL << 8)) | (1UL << 8); GPIOA->BSRR = (GPIO_PIN_4 << 16); /* 初始灭 */ } } else { g_leds[10].state = 0u; /* 未知: 灭 */ GPIOA->BSRR = (GPIO_PIN_4 << 16); GPIOA->MODER &= ~(3UL << 8); } /* 不调 LED_ApplyAllStates, 避免覆盖其他LED */ } /* ========== 私有函数实现 ========== */ /** * @brief 直接操作寄存器将单个LED状态应用到GPIO */ static void LED_ApplyStateToRegister_Internal(uint8_t ledIdx) { LED_Control_t *led = &g_leds[ledIdx]; GPIO_TypeDef *gpio = led->port; uint8_t n = led->pinNum; uint8_t st = led->state; uint32_t moderMask = (3UL << (n * 2u)); switch (st) { case 0u: /* 全灭: 浮空输入 (高阻态) */ gpio->BSRR = (1UL << (n + 16u)); /* 先确保 ODR=0 */ gpio->MODER &= ~moderMask; /* 00 = 输入模式 */ gpio->PUPDR &= ~(3UL << (n * 2u)); /* 00 = 无上拉 */ gpio->ODR &= ~(1UL << n); /* 再清一次 ODR */ break; case 1u: /* 正向亮: 推挽输出, 高电平 */ case 4u: /* 500ms周期闪烁: 初始为正向亮 */ case 5u: /* 1s周期闪烁: 初始为正向亮 */ gpio->MODER = (gpio->MODER & ~moderMask) | (1UL << (n * 2u)); /* 01=输出 */ gpio->BSRR = (1UL << n); /* ODR 置 1 */ break; case 2u: /* 反向亮: 推挽输出, 低电平 */ gpio->MODER = (gpio->MODER & ~moderMask) | (1UL << (n * 2u)); /* 01=输出 */ gpio->BSRR = (1UL << (n + 16u)); /* ODR 清零 */ break; case 3u: /* 双灯同亮: 推挽输出, 先高电平, 中断中交替翻转 */ gpio->MODER = (gpio->MODER & ~moderMask) | (1UL << (n * 2u)); /* 01=输出 */ gpio->BSRR = (1UL << n); /* ODR 置 1 */ break; default: break; } }