/****************************************************************************** * @file led.c * @brief LED 事件框架实现:只改 ledMask 对应通道,红绿互不抢 * @author cyWu <1917507415@qq.com> * @date 2026.08.25 * @version V1.0.0 * @history * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按通道掩码独立开关的 LED 事件框架 ******************************************************************************/ #include "led.h" #include static LED_EventTableItem_t s_led_priority_table[PRIORITY_MAX]; static LED_EventTableItem_t s_led_event_table[LED_EVENT_MAX]; static LED_SetState_t s_led_set_state; static TIME_Get_t s_time_get; static uint32_t get_tick_diff(uint32_t meiosis); static uint8_t led_lighting_update(LED_Config_t *led); #if (BLINK_ENABLE) static uint8_t led_blinking_update(LED_Config_t *led); #endif #if (ALTERNATING_BLINK_ENABLE) static uint8_t led_alternating_update(LED_Config_t *led); #endif /** * @brief 注册写灯回调,只对掩码里的通道动手 * @param setState 回调,led=通道掩码,state=LED_ON/OFF * @return 无 */ void LED_SetStateInit(LED_SetState_t setState) { s_led_set_state = setState; } /** * @brief 注册毫秒时间戳回调 * @param getTime 回调 * @return 无 */ void SET_TimeGetInit(TIME_Get_t getTime) { s_time_get = getTime; } /** * @brief 拷贝产品事件表 * @param ledEvent 长度必须为 LED_EVENT_MAX * @return 无 */ void LED_EventTableInit(LED_EventTableItem_t ledEvent[]) { if (ledEvent == NULL) { return; } memset(s_led_priority_table, 0, sizeof(s_led_priority_table)); memset(s_led_event_table, 0, sizeof(s_led_event_table)); memcpy(s_led_event_table, ledEvent, sizeof(s_led_event_table)); } /** * @brief 把事件挂到对应优先级槽;同优先级会被覆盖 * @param ledEvent 事件号 * @return 无 */ void LED_EventAdd(LED_Event_t ledEvent) { size_t i; LED_EventTableItem_t *item; LED_EventPriority_t prio; if ((s_time_get == NULL) || (s_led_set_state == NULL)) { return; } if (ledEvent >= LED_EVENT_MAX) { return; } for (i = 0; i < LED_EVENT_MAX; i++) { item = &s_led_event_table[i]; if (item->event != ledEvent) { continue; } prio = item->priority; if (prio >= PRIORITY_MAX) { return; } if (s_led_priority_table[prio].event != ledEvent) { item->config.currentTime = s_time_get(); if (s_led_priority_table[prio].event != LED_EVENT_NONE) { /* 同优先级换事件:只关掉旧灯里新事件用不到的通道 */ s_led_set_state((uint16_t)(s_led_priority_table[prio].config.ledMask ^ item->config.ledMask), LED_OFF); } } s_led_priority_table[prio] = *item; return; } } /** * @brief 清空优先级表(不改 GPIO,由调用方灭灯) * @return 无 */ void LED_EventAllDelete(void) { memset(s_led_priority_table, 0, sizeof(s_led_priority_table)); } /** * @brief 删除指定事件并关掉它占用的通道 * @param ledEvent 事件号 * @return 无 */ void LED_EventDelete(LED_Event_t ledEvent) { size_t i; for (i = 0; i < PRIORITY_MAX; i++) { if (s_led_priority_table[i].event != ledEvent) { continue; } memset(&s_led_priority_table[i], 0, sizeof(LED_EventTableItem_t)); if ((s_led_set_state != NULL) && (ledEvent < LED_EVENT_MAX)) { s_led_set_state(s_led_event_table[ledEvent].config.ledMask, LED_OFF); } return; } } /** * @brief 从高优先级往下跑事件 * @note handler 返回 1 会拦住更低优先级。红绿要共存时 handler 必须返回 0 * @return 无 */ void LED_EventHandle(void) { int i; uint8_t ret; for (i = (int)PRIORITY_MAX - 1; i >= 0; i--) { if (s_led_priority_table[i].event == LED_EVENT_NONE) { continue; } if (s_led_priority_table[i].ledEventHandler == NULL) { continue; } ret = s_led_priority_table[i].ledEventHandler(&s_led_priority_table[i].config); if (ret) { return; } } } /** * @brief 是否有事件在跑 * @return 1=有,0=无 */ uint8_t LED_IsEventActive(void) { size_t i; for (i = 0; i < PRIORITY_MAX; i++) { if (s_led_priority_table[i].event != LED_EVENT_NONE) { return 1; } } return 0; } /** * @brief 指定事件是否挂在优先级表里 * @param ledEvent 事件号 * @return 1=在,0=不在 */ uint8_t LED_IsEventExist(LED_Event_t ledEvent) { size_t i; for (i = 0; i < PRIORITY_MAX; i++) { if (s_led_priority_table[i].event == ledEvent) { return 1; } } return 0; } /** * @brief 按配置刷新一次灯效 * @param led 配置,不可为空 * @return 1=还在跑,0=次数用完 */ uint8_t LED_Update(LED_Config_t *led) { if (led == NULL) { return 0; } switch (led->state) { case LED_LIGHT: return led_lighting_update(led); #if (BLINK_ENABLE) case LED_BLINKING: return led_blinking_update(led); #endif #if (ALTERNATING_BLINK_ENABLE) case LED_ALTERNATING_BLINK: return led_alternating_update(led); #endif default: return 0; } } /** * @brief 毫秒时间差,兼容溢出 * @param meiosis 起点时间戳 * @return 差值 ms */ static uint32_t get_tick_diff(uint32_t meiosis) { uint32_t now; if (s_time_get == NULL) { return 0; } now = s_time_get(); if (now >= meiosis) { return now - meiosis; } return (0xFFFFFFFFu - meiosis) + now; } /** * @brief 常亮;runNum 按秒计,0=一直亮 * @param led 配置 * @return 1=还在亮,0=到点灭 */ static uint8_t led_lighting_update(LED_Config_t *led) { uint16_t sec_count; if (s_led_set_state == NULL) { return 0; } sec_count = (uint16_t)(get_tick_diff(led->currentTime) / 1000u); if ((led->runNum > 0) && (sec_count >= led->runNum)) { s_led_set_state(led->ledMask, LED_OFF); return 0; } s_led_set_state(led->ledMask, LED_ON); return 1; } #if (BLINK_ENABLE) /** * @brief 闪烁;runNum 按完整周期计,0=一直闪 * @param led 配置 * @return 1=还在闪,0=到点灭 */ static uint8_t led_blinking_update(LED_Config_t *led) { uint32_t run_cycle; uint16_t blink_count; uint32_t sec_left; if (s_led_set_state == NULL) { return 0; } run_cycle = led->blinkParams.onTime + led->blinkParams.offTime; if (run_cycle == 0) { return 0; } blink_count = (uint16_t)(get_tick_diff(led->currentTime) / run_cycle); sec_left = get_tick_diff(led->currentTime) % run_cycle; if ((led->runNum > 0) && (blink_count >= led->runNum)) { s_led_set_state(led->ledMask, LED_OFF); return 0; } if (sec_left <= led->blinkParams.onTime) { s_led_set_state(led->ledMask, led->blinkParams.orDer ? LED_OFF : LED_ON); } else { s_led_set_state(led->ledMask, led->blinkParams.orDer ? LED_ON : LED_OFF); } return 1; } #endif #if (ALTERNATING_BLINK_ENABLE) /** * @brief 两组灯按时间片轮流亮 * @param led 配置 * @return 1=还在跑,0=到点灭 */ static uint8_t led_alternating_update(LED_Config_t *led) { uint16_t blink_count; uint32_t sec_left; uint32_t time_slice; size_t i; if (s_led_set_state == NULL) { return 0; } if (led->alternatingBlinkParams.Cycle == 0) { return 0; } blink_count = (uint16_t)(get_tick_diff(led->currentTime) / led->alternatingBlinkParams.Cycle); sec_left = get_tick_diff(led->currentTime) % led->alternatingBlinkParams.Cycle; if ((led->runNum > 0) && (blink_count >= led->runNum)) { s_led_set_state(led->ledMask, LED_OFF); return 0; } time_slice = led->alternatingBlinkParams.Cycle / ALTERNAT_GROUP; s_led_set_state(led->ledMask, LED_OFF); for (i = 0; i < ALTERNAT_GROUP; i++) { if ((sec_left > (time_slice * i)) && (sec_left <= (time_slice * (i + 1)))) { s_led_set_state(led->alternatingBlinkParams.Group[i], LED_ON); break; } } return 1; } #endif