/* * led.c - 系统状态指示灯驱动(事件查询方式,100ms检查周期) * 硬件:PC0(红), PC1(绿), PC2(蓝) - 低电平点亮 */ #include #include #include /* 使用RT-Thread标准日志系统 */ #define DBG_TAG "led" #define DBG_LVL DBG_WARNING #include /** * @brief 外部引入的状态 */ #include "ym310_protecl.h" extern rt_bool_t ym310_mqtt_is_connected(rt_uint8_t client_idx); extern SYS_STATUS sys_sta; extern uint8_t calculate_hours_diff(uint8_t start, uint8_t end); #define PIN_LED_RED GET_PIN(B, 7) #define PIN_LED_GREEN GET_PIN(B, 8) #define PIN_LED_BLUE GET_PIN(C, 13) /* 时间常量定义(单位:100ms)*/ #define LED_BLINK_ON_TIME 3 // 亮的时间:300ms #define LED_BLINK_OFF_TIME 2 // 灭的时间:200ms /* 闪烁次数(使用 light_work_duration)*/ #define BLINK_COUNT (dev_info.light_work_duration) // 光控时长 = 闪烁次数 #define SK_BLINK_COUNT (calculate_hours_diff(dev_info.time_start, dev_info.time_end)) // 时控时长 = 闪烁次数 #define PAUSE_TIME 20 // 一组闪烁完成后暂停:2000ms /* 长工作状态(light_work_duration == 0):亮2秒,灭2秒 */ #define LONG_WORK_ON_TIME 20 // 亮2秒 #define LONG_WORK_OFF_TIME 20 // 灭2秒 #define SET_EFFECT_BLINK_COUNT 3 // 设置生效闪烁次数 /* LED 状态定义 */ typedef enum { LED_STATE_NORMAL = 0, LED_STATE_ERROR, LED_STATE_POWER_OFF, } led_state_t; /* 异常类型定义 */ typedef enum { ERROR_NONE = 0, ERROR_RAIN, // 雨控 - 黄色闪烁(带次数/联网指示) ERROR_TEMP, // 温控 - 黄色常亮 ERROR_UNDER_VOLTAGE, // 欠压 - 红色闪烁(带次数/联网指示) ERROR_TILT, // 倾倒 - 红色常亮 } error_type_t; /* LED 工作模式 */ typedef enum { LED_MODE_NORMAL = 0, LED_MODE_SET_EFFECT, } led_mode_t; /* 工作状态变量 */ static led_state_t current_state = LED_STATE_NORMAL; static error_type_t current_error = ERROR_NONE; static led_mode_t current_mode = LED_MODE_NORMAL; /* 设置生效事件标志 */ static volatile uint8_t set_effect_pending = 0; /* 定时器控制块 */ static rt_timer_t led_timer = RT_NULL; /* ========== 统一状态机变量 ========== */ static uint32_t tick = 0; // 通用计时器 static uint8_t phase = 0; // 0:亮状态, 1:灭状态, (长工作模式: 0亮1灭) static uint8_t blink_done = 0; // 当前已完成的闪烁次数 static uint8_t in_pause = 0; // 是否处于长暂停状态 static uint8_t need_reset = 1; // 是否需要重置状态机 static uint8_t is_long_work = 0; // 是否处于长工作模式(duration==0) /* 设置生效事件变量 */ static uint32_t set_effect_tick = 0; static uint8_t set_effect_phase = 0; static uint8_t set_effect_count = 0; /* ========== 颜色控制函数 ========== */ static void set_led_color(uint8_t red, uint8_t green, uint8_t blue) { rt_pin_write(PIN_LED_RED, red ? PIN_HIGH : PIN_LOW); rt_pin_write(PIN_LED_GREEN, green ? PIN_HIGH : PIN_LOW); rt_pin_write(PIN_LED_BLUE, blue ? PIN_HIGH : PIN_LOW); } #define COLOR_OFF() set_led_color(0, 0, 0) #define COLOR_RED() set_led_color(1, 0, 0) #define COLOR_GREEN() set_led_color(0, 1, 0) #define COLOR_BLUE() set_led_color(0, 0, 1) #define COLOR_YELLOW() set_led_color(1, 1, 0) #define COLOR_CYAN() set_led_color(0, 1, 1) #define COLOR_WHITE() set_led_color(1, 1, 1) #define COLOR_PINK() set_led_color(1, 0, 1) /* ========== NTP时间同步判断 ========== */ /** * @brief 判断NTP时间是否已同步成功 * @return 1=已同步, 0=未同步 * * 判断逻辑:当前系统时间若为2026年5月及以前,认为未同步 * 因为设备实际运行时间在2026年6月之后 */ static uint8_t is_ntp_synced(void) { extern struct rtc_time time_now; /* 计算当前时间的 (年*12 + 月) 值 */ uint32_t current_value = (uint32_t)(time_now.year - 2000) * 12 + time_now.month; /* 2026年5月的参考值:26*12 + 5 = 317 */ uint32_t threshold = 26 * 12 + 5; // rt_kprintf("current_value %d threshold %d res = %d" , current_value ,threshold, (current_value > threshold)); /* 如果当前时间 <= 2026年5月,认为NTP未同步 */ if (current_value <= threshold) { return 0; /* 未同步 */ } return 1; /* 已同步 */ } /* ========== 辅助函数 ========== */ static uint8_t is_power_off(void) { return (dev_info.power_state == 0); } /** * @brief 获取正常状态(无异常)下的基础颜色 * @param charging 充电状态 (0:未充电, 2:充电中) */ static void set_normal_color(uint8_t charging) { if (charging == 2) { COLOR_BLUE(); } else if (dev_info.time_ctrl_enable == 0) { COLOR_GREEN(); // 光控 } else { COLOR_PINK(); // 时控 } } /** * @brief 获取当前应该显示的颜色(用于闪烁时的亮状态) * @param charging 充电状态 (0:未充电, 2:充电中) * @param network_ok 网络状态 * @param is_last 是否为本次闪烁周期的最后一次 */ static void set_on_color(uint8_t charging, uint8_t network_ok, uint8_t is_last) { if (!network_ok && is_last) { COLOR_CYAN(); // 断网时显示青色 return; } if (is_last && !is_ntp_synced()) // ntp失效 显示白色 { COLOR_WHITE(); return; } switch (current_error) { case ERROR_UNDER_VOLTAGE: COLOR_RED(); break; case ERROR_RAIN: COLOR_YELLOW(); break; default: set_normal_color(charging); break; } } /** * @brief 长工作模式处理(duration == 0):2秒亮,2秒灭 * @param charging 充电状态 * @param network_ok 网络状态(长工作模式下不使用断网指示) */ static void long_work_mode_process(uint8_t charging, uint8_t network_ok) { tick++; if (phase == 0) /* 亮状态 */ { if (tick >= LONG_WORK_ON_TIME) { COLOR_OFF(); phase = 1; tick = 0; } } else /* 灭状态 */ { if (tick >= LONG_WORK_OFF_TIME) { switch (current_error) { case ERROR_UNDER_VOLTAGE: COLOR_RED(); break; case ERROR_RAIN: COLOR_YELLOW(); break; default: set_normal_color(charging); break; } phase = 0; tick = 0; } } } /** * @brief 快速闪烁模式处理(duration >= 1):支持次数控制和断网指示 * @param charging 充电状态 * @param network_ok 网络状态 * @param blink_count 闪烁次数 */ static void fast_blink_mode_process(uint8_t charging, uint8_t network_ok, uint8_t blink_count) { if (need_reset) { need_reset = 0; phase = 0; blink_done = 0; in_pause = 0; tick = 0; uint8_t is_last = (blink_count == 1); set_on_color(charging, network_ok, is_last); return; } tick++; if (in_pause) { if (tick >= PAUSE_TIME) { in_pause = 0; blink_done = 0; phase = 0; tick = 0; uint8_t is_last = (blink_count == 1); set_on_color(charging, network_ok, is_last); } return; } if (phase == 0) { if (tick >= LED_BLINK_ON_TIME) { COLOR_OFF(); phase = 1; tick = 0; } } else { if (tick >= LED_BLINK_OFF_TIME) { blink_done++; if (blink_done >= blink_count) { in_pause = 1; tick = 0; return; } phase = 0; tick = 0; uint8_t is_last = (blink_done + 1 >= blink_count); set_on_color(charging, network_ok, is_last); } } } /** * @brief 统一状态处理(正常/闪烁类异常共用) */ static void unified_state_process(void) { static uint8_t last_effective_count = 0xff; uint8_t charging = sys_sta.work_state; uint8_t network_ok = ym310_mqtt_is_connected(0); uint8_t effective_count; if (is_power_off()) { COLOR_OFF(); return; } uint8_t is_pink_mode = (current_state == LED_STATE_NORMAL && current_error == ERROR_NONE && charging != 2 && dev_info.time_ctrl_enable == 1); if (is_pink_mode) { effective_count = SK_BLINK_COUNT; } else { effective_count = BLINK_COUNT; } if (last_effective_count != effective_count) { last_effective_count = effective_count; need_reset = 1; is_long_work = 0; tick = 0; phase = 0; blink_done = 0; in_pause = 0; } if (effective_count == 0) { if (!is_long_work) { is_long_work = 1; need_reset = 1; phase = 0; tick = 0; } long_work_mode_process(charging, network_ok); } else { if (is_long_work) { is_long_work = 0; need_reset = 1; } fast_blink_mode_process(charging, network_ok, effective_count); } } /** * @brief 常亮状态处理(温控、倾倒) */ static void solid_state_process(void) { if (is_power_off()) { COLOR_OFF(); return; } switch (current_error) { case ERROR_TEMP: COLOR_YELLOW(); break; case ERROR_TILT: COLOR_RED(); break; default: COLOR_OFF(); break; } } /** * @brief 设置生效事件处理 */ static void set_effect_process(void) { set_effect_tick++; if (set_effect_phase == 0) { if (set_effect_tick >= LED_BLINK_ON_TIME) { COLOR_OFF(); set_effect_phase = 1; set_effect_tick = 0; } } else { if (set_effect_tick >= LED_BLINK_OFF_TIME) { set_effect_count++; if (set_effect_count >= SET_EFFECT_BLINK_COUNT) { LOG_I("Set effect completed\n"); current_mode = LED_MODE_NORMAL; need_reset = 1; is_long_work = 0; return; } set_effect_phase = 0; COLOR_WHITE(); set_effect_tick = 0; } } } /** * @brief 检查设置生效事件 */ static void check_set_effect_event(void) { if (set_effect_pending) { LOG_I("Set effect event triggered\n"); set_effect_pending = 0; current_mode = LED_MODE_SET_EFFECT; set_effect_tick = 0; set_effect_phase = 0; set_effect_count = 0; COLOR_WHITE(); } } /** * @brief 主定时器回调 */ static void led_timer_callback(void *parameter) { check_set_effect_event(); if (current_mode == LED_MODE_SET_EFFECT) { set_effect_process(); return; } if (is_power_off()) { COLOR_OFF(); return; } led_update_status(); switch (current_state) { case LED_STATE_NORMAL: unified_state_process(); break; case LED_STATE_ERROR: if (current_error == ERROR_RAIN || current_error == ERROR_UNDER_VOLTAGE) { unified_state_process(); // 闪烁,复用正常逻辑 } else { solid_state_process(); // 常亮 } break; default: COLOR_OFF(); break; } } /* ========== 外部API ========== */ void led_trigger_set_effect(void) { set_effect_pending = 1; LOG_I("Set effect event pending\n"); } void led_update_status(void) { if (current_mode != LED_MODE_NORMAL) return; led_state_t old_state = current_state; error_type_t old_error = current_error; if (is_power_off()) { current_state = LED_STATE_POWER_OFF; current_error = ERROR_NONE; COLOR_OFF(); return; } if (sys_sta.tilt_protect == VALID) { current_error = ERROR_TILT; current_state = LED_STATE_ERROR; } else if (sys_sta.temp_protect == VALID) { current_error = ERROR_TEMP; current_state = LED_STATE_ERROR; } else if (sys_sta.undervolt_protect == VALID) { current_error = ERROR_UNDER_VOLTAGE; current_state = LED_STATE_ERROR; } else if (sys_sta.rain_protect == VALID) { current_error = ERROR_RAIN; current_state = LED_STATE_ERROR; } else { current_state = LED_STATE_NORMAL; current_error = ERROR_NONE; } if (old_state != current_state || old_error != current_error) { LOG_I("State: %s, error=%d\n", current_state == LED_STATE_NORMAL ? "NORMAL" : "ERROR", current_error); need_reset = 1; is_long_work = 0; tick = 0; } } /** * @brief LED初始化 */ int led_init(void) { rt_pin_mode(PIN_LED_RED, PIN_MODE_OUTPUT); rt_pin_mode(PIN_LED_GREEN, PIN_MODE_OUTPUT); rt_pin_mode(PIN_LED_BLUE, PIN_MODE_OUTPUT); COLOR_OFF(); led_timer = rt_timer_create("led_timer", led_timer_callback, RT_NULL, RT_TICK_PER_SECOND / 10, RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER); if (led_timer == RT_NULL) { LOG_E("Failed to create led timer\n"); return -RT_ERROR; } rt_timer_start(led_timer); LOG_I("LED driver initialized\n"); LOG_I(" Light work duration: %d hours\n", BLINK_COUNT); LOG_I(" Duration=0: 2s ON/2s OFF (long work mode)\n"); LOG_I(" Duration>=1: blink times depends on mode (pink uses time-control duration)\n"); LOG_I(" Rain/Under-voltage: blink with color (Yellow/Red)\n"); LOG_I(" Temp/Tilt: solid color (Yellow/Red)\n"); LOG_I(" Last blink of each group: Cyan if disconnected\n"); return RT_EOK; } INIT_APP_EXPORT(led_init);