battery_display.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /**
  2. ******************************************************************************
  3. * @file battery_display.c
  4. * @brief 电池电量 LED 显示模块实现
  5. * 双时间槽分时复用控制反并联 LED, 实现独立闪烁.
  6. ******************************************************************************
  7. */
  8. /* Includes ------------------------------------------------------------------*/
  9. #include "battery_display.h"
  10. #include "led_driver.h"
  11. #include "app_state.h"
  12. /* Private define ------------------------------------------------------------*/
  13. /* 充电状态值 */
  14. #define CHG_CHARGING 0x01u
  15. #define CHG_ABNORMAL 0xFEu
  16. /* ========== 电池LED时间槽控制 ========== */
  17. /* 电池灯闪烁速率: 1s = 500个周期亮 + 500个周期灭 */
  18. #define BAT_BLINK_ON_CYCLES 500u
  19. #define BAT_BLINK_OFF_CYCLES 500u
  20. /* 充电异常闪烁: 100ms = 50个周期亮 + 50个周期灭 */
  21. #define BAT_ERR_ON_CYCLES 50u
  22. #define BAT_ERR_OFF_CYCLES 50u
  23. /* Private variables ---------------------------------------------------------*/
  24. /* 每颗电池灯(0-3)在各自槽位中的亮灭状态: 0=灭, 1=亮 */
  25. static volatile uint8_t g_batSlotState[4] = {0u, 0u, 0u, 0u};
  26. static volatile uint16_t g_batBlinkCnt = 0u; /* 闪烁周期计数器 */
  27. static volatile uint8_t g_batBlinkPattern = 0u; /* 0=常亮, 1=1s闪烁, 2=异常闪烁 */
  28. static volatile uint8_t g_batBlinkLed = 0xFFu; /* 闪烁的灯索引(0~3), 0xFF=无 */
  29. /* ========== LED12/LED13 引脚位偏移 ========== */
  30. /* PA8 → bit8 → 位偏移 8 → MODER bit 16/17 */
  31. /* PA11 → bit11 → 位偏移 11 → MODER bit 22/23 */
  32. #define LED12_PIN_BIT 8u
  33. #define LED13_PIN_BIT 11u
  34. /* ========== 函数实现 ========== */
  35. /**
  36. * @brief 根据 g_batteryPct / g_chgIng / g_chgFull 更新电池 LED 显示状态
  37. *
  38. * 充电闪烁逻辑 (用槽位独立控制):
  39. * level=1: 灯0 1s闪烁
  40. * level=2: 灯0常亮, 灯1 1s闪烁
  41. * level=3: 灯0+1常亮, 灯2 1s闪烁
  42. * level=4: 灯0+1+2常亮, 灯3 1s闪烁
  43. * 充满: 4颗灯常亮
  44. * 充电异常: 灯0 100ms闪烁, 其余灭
  45. */
  46. void UpdateBatteryLEDs(void)
  47. {
  48. if (!g_batteryReceived) return;
  49. uint8_t pct = g_batteryPct;
  50. uint8_t chg = g_chgIng;
  51. /* 将 1-100% 转换为 4 段 (0-4颗灯亮) */
  52. uint8_t level;
  53. if (pct >= 75u) level = 4u; /* 75~100%: 4颗灯 */
  54. else if (pct >= 50u) level = 3u; /* 50~74%: 3颗灯 */
  55. else if (pct >= 25u) level = 2u; /* 25~49%: 2颗灯 */
  56. else if (pct >= 1u) level = 1u; /* 1~24%: 1颗灯 */
  57. else level = 0u;
  58. /* 默认: 所有灯灭 */
  59. for (uint8_t i = 0; i < 4u; i++) g_batSlotState[i] = 0u;
  60. g_batBlinkPattern = 0u;
  61. g_batBlinkLed = 0xFFu;
  62. g_batBlinkCnt = 0u;
  63. /* 将电池LED从状态3(1kHz翻转)中排除, 由时间槽接管 */
  64. g_leds[11].state = 0u;
  65. g_leds[12].state = 0u;
  66. /* 充电异常: 灯0 100ms闪烁 */
  67. if (chg == CHG_ABNORMAL)
  68. {
  69. g_batBlinkPattern = 2u; /* 100ms闪烁模式 */
  70. return;
  71. }
  72. /* ---- 设置各灯槽状态 ---- */
  73. switch (level)
  74. {
  75. case 4u:
  76. g_batSlotState[0] = 1u;
  77. g_batSlotState[1] = 1u;
  78. g_batSlotState[2] = 1u;
  79. g_batSlotState[3] = 1u;
  80. break;
  81. case 3u:
  82. g_batSlotState[0] = 1u;
  83. g_batSlotState[1] = 1u;
  84. g_batSlotState[2] = 1u;
  85. /* 灯3灭 */
  86. break;
  87. case 2u:
  88. g_batSlotState[0] = 1u;
  89. g_batSlotState[1] = 1u;
  90. break;
  91. case 1u:
  92. g_batSlotState[0] = 1u;
  93. break;
  94. default: /* 0: 全灭 */
  95. break;
  96. }
  97. /* 充满(100%): 无论是否插充电器, 4颗全亮 */
  98. if (g_chgFull == 1u && level >= 4u)
  99. {
  100. for (uint8_t i = 0; i < 4u; i++) g_batSlotState[i] = 1u;
  101. return;
  102. }
  103. /* 充电中: 冲到哪格就哪格闪 */
  104. if (chg == CHG_CHARGING && level > 0u)
  105. {
  106. g_batBlinkPattern = 1u; /* 1s闪烁 */
  107. g_batBlinkLed = (uint8_t)(level - 1u); /* 闪烁的灯索引 */
  108. if (level == 1u)
  109. {
  110. g_batSlotState[0] = 1u;
  111. }
  112. else
  113. {
  114. /* 前面所有灯常亮, 最后一格闪烁 */
  115. for (uint8_t i = 0; i < level; i++)
  116. g_batSlotState[i] = 1u;
  117. }
  118. }
  119. }
  120. /**
  121. * @brief 双时间槽更新 (TIM3 中断中每 1ms 调用)
  122. * 前半程(槽A): 正向灯亮/反向灯灭
  123. * 后半程(槽B): 反向灯亮/正向灯灭
  124. */
  125. void BatterySlotUpdate(void)
  126. {
  127. static uint8_t s_slotPhase = 0u; /* 0=前半程, 1=后半程 */
  128. static uint16_t s_cycleCnt = 0u; /* 周期计数(用于闪烁) */
  129. /* 未收到主板电池数据前, 电池LED由上电全亮状态3控制, 不接管 */
  130. if (!g_batteryReceived) return;
  131. s_slotPhase ^= 1u; /* 每 1ms 切换一次槽位 */
  132. /* 闪烁控制: 每完成一个完整周期(前后半程)计数一次 */
  133. if (s_slotPhase == 0u)
  134. {
  135. s_cycleCnt++;
  136. }
  137. /* 判断当前是否在闪烁的"亮"区间 */
  138. uint8_t blinkOn = 1u;
  139. if (g_batBlinkPattern == 1u)
  140. {
  141. /* 1s闪烁: 500个周期亮, 500个周期灭 */
  142. uint16_t c = (uint16_t)(s_cycleCnt % (BAT_BLINK_ON_CYCLES + BAT_BLINK_OFF_CYCLES));
  143. blinkOn = (c < BAT_BLINK_ON_CYCLES) ? 1u : 0u;
  144. }
  145. else if (g_batBlinkPattern == 2u)
  146. {
  147. /* 100ms闪烁: 50个周期亮, 50个周期灭 */
  148. uint16_t c = (uint16_t)(s_cycleCnt % (BAT_ERR_ON_CYCLES + BAT_ERR_OFF_CYCLES));
  149. blinkOn = (c < BAT_ERR_ON_CYCLES) ? 1u : 0u;
  150. }
  151. /* ---- LED12(PA8): 灯0(正向) / 灯1(反向) ---- */
  152. uint8_t needA0 = (g_batBlinkLed == 0u) ? (blinkOn ? g_batSlotState[0] : 0u) : g_batSlotState[0];
  153. uint8_t needB1 = (g_batBlinkLed == 1u) ? (blinkOn ? g_batSlotState[1] : 0u) : g_batSlotState[1];
  154. if (s_slotPhase == 0u)
  155. {
  156. /* 槽A: 控制灯0 */
  157. if (needA0)
  158. {
  159. GPIOA->MODER = (GPIOA->MODER & ~(3UL << (LED12_PIN_BIT * 2u))) | (1UL << (LED12_PIN_BIT * 2u));
  160. GPIOA->BSRR = GPIO_PIN_8; /* ODR=1 → 灯0亮 */
  161. }
  162. else
  163. {
  164. GPIOA->MODER &= ~(3UL << (LED12_PIN_BIT * 2u)); /* 浮空输入 → 灯0灭 */
  165. GPIOA->PUPDR &= ~(3UL << (LED12_PIN_BIT * 2u));
  166. }
  167. }
  168. else
  169. {
  170. /* 槽B: 控制灯1(反向) */
  171. if (needB1)
  172. {
  173. GPIOA->MODER = (GPIOA->MODER & ~(3UL << (LED12_PIN_BIT * 2u))) | (1UL << (LED12_PIN_BIT * 2u));
  174. GPIOA->BSRR = (GPIO_PIN_8 << 16); /* ODR=0 → 灯1亮 */
  175. }
  176. else
  177. {
  178. GPIOA->MODER &= ~(3UL << (LED12_PIN_BIT * 2u)); /* 浮空输入 → 灯1灭 */
  179. GPIOA->PUPDR &= ~(3UL << (LED12_PIN_BIT * 2u));
  180. }
  181. }
  182. /* ---- LED13(PA11): 灯2(正向) / 灯3(反向) ---- */
  183. uint8_t needA2 = (g_batBlinkLed == 2u) ? (blinkOn ? g_batSlotState[2] : 0u) : g_batSlotState[2];
  184. uint8_t needB3 = (g_batBlinkLed == 3u) ? (blinkOn ? g_batSlotState[3] : 0u) : g_batSlotState[3];
  185. if (s_slotPhase == 0u)
  186. {
  187. /* 槽A: 控制灯2 */
  188. if (needA2)
  189. {
  190. GPIOA->MODER = (GPIOA->MODER & ~(3UL << (LED13_PIN_BIT * 2u))) | (1UL << (LED13_PIN_BIT * 2u));
  191. GPIOA->BSRR = GPIO_PIN_11; /* ODR=1 → 灯2亮 */
  192. }
  193. else
  194. {
  195. GPIOA->MODER &= ~(3UL << (LED13_PIN_BIT * 2u));
  196. GPIOA->PUPDR &= ~(3UL << (LED13_PIN_BIT * 2u));
  197. }
  198. }
  199. else
  200. {
  201. /* 槽B: 控制灯3(反向) */
  202. if (needB3)
  203. {
  204. GPIOA->MODER = (GPIOA->MODER & ~(3UL << (LED13_PIN_BIT * 2u))) | (1UL << (LED13_PIN_BIT * 2u));
  205. GPIOA->BSRR = (GPIO_PIN_11 << 16); /* ODR=0 → 灯3亮 */
  206. }
  207. else
  208. {
  209. GPIOA->MODER &= ~(3UL << (LED13_PIN_BIT * 2u));
  210. GPIOA->PUPDR &= ~(3UL << (LED13_PIN_BIT * 2u));
  211. }
  212. }
  213. }