/** ****************************************************************************** * @file battery_display.c * @brief 电池电量 LED 显示模块实现 * 双时间槽分时复用控制反并联 LED, 实现独立闪烁. ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "battery_display.h" #include "led_driver.h" #include "app_state.h" /* Private define ------------------------------------------------------------*/ /* 充电状态值 */ #define CHG_CHARGING 0x01u #define CHG_ABNORMAL 0xFEu /* ========== 电池LED时间槽控制 ========== */ /* 电池灯闪烁速率: 1s = 500个周期亮 + 500个周期灭 */ #define BAT_BLINK_ON_CYCLES 500u #define BAT_BLINK_OFF_CYCLES 500u /* 充电异常闪烁: 100ms = 50个周期亮 + 50个周期灭 */ #define BAT_ERR_ON_CYCLES 50u #define BAT_ERR_OFF_CYCLES 50u /* Private variables ---------------------------------------------------------*/ /* 每颗电池灯(0-3)在各自槽位中的亮灭状态: 0=灭, 1=亮 */ static volatile uint8_t g_batSlotState[4] = {0u, 0u, 0u, 0u}; static volatile uint16_t g_batBlinkCnt = 0u; /* 闪烁周期计数器 */ static volatile uint8_t g_batBlinkPattern = 0u; /* 0=常亮, 1=1s闪烁, 2=异常闪烁 */ static volatile uint8_t g_batBlinkLed = 0xFFu; /* 闪烁的灯索引(0~3), 0xFF=无 */ /* ========== LED12/LED13 引脚位偏移 ========== */ /* PA8 → bit8 → 位偏移 8 → MODER bit 16/17 */ /* PA11 → bit11 → 位偏移 11 → MODER bit 22/23 */ #define LED12_PIN_BIT 8u #define LED13_PIN_BIT 11u /* ========== 函数实现 ========== */ /** * @brief 根据 g_batteryPct / g_chgIng / g_chgFull 更新电池 LED 显示状态 * * 充电闪烁逻辑 (用槽位独立控制): * level=1: 灯0 1s闪烁 * level=2: 灯0常亮, 灯1 1s闪烁 * level=3: 灯0+1常亮, 灯2 1s闪烁 * level=4: 灯0+1+2常亮, 灯3 1s闪烁 * 充满: 4颗灯常亮 * 充电异常: 灯0 100ms闪烁, 其余灭 */ void UpdateBatteryLEDs(void) { if (!g_batteryReceived) return; uint8_t pct = g_batteryPct; uint8_t chg = g_chgIng; /* 将 1-100% 转换为 4 段 (0-4颗灯亮) */ uint8_t level; if (pct >= 75u) level = 4u; /* 75~100%: 4颗灯 */ else if (pct >= 50u) level = 3u; /* 50~74%: 3颗灯 */ else if (pct >= 25u) level = 2u; /* 25~49%: 2颗灯 */ else if (pct >= 1u) level = 1u; /* 1~24%: 1颗灯 */ else level = 0u; /* 默认: 所有灯灭 */ for (uint8_t i = 0; i < 4u; i++) g_batSlotState[i] = 0u; g_batBlinkPattern = 0u; g_batBlinkLed = 0xFFu; g_batBlinkCnt = 0u; /* 将电池LED从状态3(1kHz翻转)中排除, 由时间槽接管 */ g_leds[11].state = 0u; g_leds[12].state = 0u; /* 充电异常: 灯0 100ms闪烁 */ if (chg == CHG_ABNORMAL) { g_batBlinkPattern = 2u; /* 100ms闪烁模式 */ return; } /* ---- 设置各灯槽状态 ---- */ switch (level) { case 4u: g_batSlotState[0] = 1u; g_batSlotState[1] = 1u; g_batSlotState[2] = 1u; g_batSlotState[3] = 1u; break; case 3u: g_batSlotState[0] = 1u; g_batSlotState[1] = 1u; g_batSlotState[2] = 1u; /* 灯3灭 */ break; case 2u: g_batSlotState[0] = 1u; g_batSlotState[1] = 1u; break; case 1u: g_batSlotState[0] = 1u; break; default: /* 0: 全灭 */ break; } /* 充满(100%): 无论是否插充电器, 4颗全亮 */ if (g_chgFull == 1u && level >= 4u) { for (uint8_t i = 0; i < 4u; i++) g_batSlotState[i] = 1u; return; } /* 充电中: 冲到哪格就哪格闪 */ if (chg == CHG_CHARGING && level > 0u) { g_batBlinkPattern = 1u; /* 1s闪烁 */ g_batBlinkLed = (uint8_t)(level - 1u); /* 闪烁的灯索引 */ if (level == 1u) { g_batSlotState[0] = 1u; } else { /* 前面所有灯常亮, 最后一格闪烁 */ for (uint8_t i = 0; i < level; i++) g_batSlotState[i] = 1u; } } } /** * @brief 双时间槽更新 (TIM3 中断中每 1ms 调用) * 前半程(槽A): 正向灯亮/反向灯灭 * 后半程(槽B): 反向灯亮/正向灯灭 */ void BatterySlotUpdate(void) { static uint8_t s_slotPhase = 0u; /* 0=前半程, 1=后半程 */ static uint16_t s_cycleCnt = 0u; /* 周期计数(用于闪烁) */ /* 未收到主板电池数据前, 电池LED由上电全亮状态3控制, 不接管 */ if (!g_batteryReceived) return; s_slotPhase ^= 1u; /* 每 1ms 切换一次槽位 */ /* 闪烁控制: 每完成一个完整周期(前后半程)计数一次 */ if (s_slotPhase == 0u) { s_cycleCnt++; } /* 判断当前是否在闪烁的"亮"区间 */ uint8_t blinkOn = 1u; if (g_batBlinkPattern == 1u) { /* 1s闪烁: 500个周期亮, 500个周期灭 */ uint16_t c = (uint16_t)(s_cycleCnt % (BAT_BLINK_ON_CYCLES + BAT_BLINK_OFF_CYCLES)); blinkOn = (c < BAT_BLINK_ON_CYCLES) ? 1u : 0u; } else if (g_batBlinkPattern == 2u) { /* 100ms闪烁: 50个周期亮, 50个周期灭 */ uint16_t c = (uint16_t)(s_cycleCnt % (BAT_ERR_ON_CYCLES + BAT_ERR_OFF_CYCLES)); blinkOn = (c < BAT_ERR_ON_CYCLES) ? 1u : 0u; } /* ---- LED12(PA8): 灯0(正向) / 灯1(反向) ---- */ uint8_t needA0 = (g_batBlinkLed == 0u) ? (blinkOn ? g_batSlotState[0] : 0u) : g_batSlotState[0]; uint8_t needB1 = (g_batBlinkLed == 1u) ? (blinkOn ? g_batSlotState[1] : 0u) : g_batSlotState[1]; if (s_slotPhase == 0u) { /* 槽A: 控制灯0 */ if (needA0) { GPIOA->MODER = (GPIOA->MODER & ~(3UL << (LED12_PIN_BIT * 2u))) | (1UL << (LED12_PIN_BIT * 2u)); GPIOA->BSRR = GPIO_PIN_8; /* ODR=1 → 灯0亮 */ } else { GPIOA->MODER &= ~(3UL << (LED12_PIN_BIT * 2u)); /* 浮空输入 → 灯0灭 */ GPIOA->PUPDR &= ~(3UL << (LED12_PIN_BIT * 2u)); } } else { /* 槽B: 控制灯1(反向) */ if (needB1) { GPIOA->MODER = (GPIOA->MODER & ~(3UL << (LED12_PIN_BIT * 2u))) | (1UL << (LED12_PIN_BIT * 2u)); GPIOA->BSRR = (GPIO_PIN_8 << 16); /* ODR=0 → 灯1亮 */ } else { GPIOA->MODER &= ~(3UL << (LED12_PIN_BIT * 2u)); /* 浮空输入 → 灯1灭 */ GPIOA->PUPDR &= ~(3UL << (LED12_PIN_BIT * 2u)); } } /* ---- LED13(PA11): 灯2(正向) / 灯3(反向) ---- */ uint8_t needA2 = (g_batBlinkLed == 2u) ? (blinkOn ? g_batSlotState[2] : 0u) : g_batSlotState[2]; uint8_t needB3 = (g_batBlinkLed == 3u) ? (blinkOn ? g_batSlotState[3] : 0u) : g_batSlotState[3]; if (s_slotPhase == 0u) { /* 槽A: 控制灯2 */ if (needA2) { GPIOA->MODER = (GPIOA->MODER & ~(3UL << (LED13_PIN_BIT * 2u))) | (1UL << (LED13_PIN_BIT * 2u)); GPIOA->BSRR = GPIO_PIN_11; /* ODR=1 → 灯2亮 */ } else { GPIOA->MODER &= ~(3UL << (LED13_PIN_BIT * 2u)); GPIOA->PUPDR &= ~(3UL << (LED13_PIN_BIT * 2u)); } } else { /* 槽B: 控制灯3(反向) */ if (needB3) { GPIOA->MODER = (GPIOA->MODER & ~(3UL << (LED13_PIN_BIT * 2u))) | (1UL << (LED13_PIN_BIT * 2u)); GPIOA->BSRR = (GPIO_PIN_11 << 16); /* ODR=0 → 灯3亮 */ } else { GPIOA->MODER &= ~(3UL << (LED13_PIN_BIT * 2u)); GPIOA->PUPDR &= ~(3UL << (LED13_PIN_BIT * 2u)); } } }