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