/* * Protection Detection Module - v1.4 (Fixed timing issues) * * 功能:统一保护检测线程,实现欠压保护、温控保护 * * 设计原则: * - 保护检测线程基础时间单位100ms * - 各保护模块独立计数,互不干扰 * - 定时修复:使用毫秒计时,确保触发时间准确(60秒 = 60000ms) * * Version: 1.4.0 (Lamp protection removed) */ /*********************↓ include ↓************************/ #define DBG_TAG "PROTECT" #define DBG_LVL DBG_INFO #include #include #include #include #include #include "ecode.h" #include "ym310_protecl.h" /*********************↑ include ↑************************/ /*********************↓ define ↓************************/ /* 保护线程基础时间片(毫秒) */ #define PROTECT_BASE_MS 100 /* 欠压保护参数(毫秒:60秒 = 60000ms) */ #define UVP_TRIGGER_MS 60000 #define UVP_RECOVER_MS 60000 #define UVP_PRE_TRIGGER_MS 10000 /* 温控保护参数(毫秒:60秒 = 60000ms) */ #define TEMP_TRIGGER_MS 60000 #define TEMP_RECOVER_MS 60000 /* 保护状态值定义 */ #define PROTECT_NONE 0 #define PROTECT_LOW_TEMP 0x7A #define PROTECT_HIGH_TEMP 0x9D #define PROTECT_UNDER_VOLT 0x9D /*********************↑ define ↑************************/ /*********************↓ 外部变量声明 ↓***********************/ extern SYS_STATUS sys_sta; extern LIMIT limit; extern DEV_INFO dev_info; extern void save_dev_info(void); extern void report_now(void); /*********************↑ 外部变量声明 ↑***********************/ /*********************↓ 静态变量 ↓************************/ static rt_thread_t s_protect_thread = RT_NULL; /* 欠压保护状态机 */ static struct { uint32_t low_voltage_start_ms; /* 电压过低开始时间(毫秒) */ uint32_t normal_voltage_start_ms; /* 电压正常开始时间(毫秒) */ uint8_t pre_protect; } s_uvp = {0}; /* 温控保护状态机 */ static struct { uint32_t high_temp_start_ms; /* 高温开始时间(毫秒) */ uint32_t low_temp_start_ms; /* 低温开始时间(毫秒) */ uint32_t normal_temp_start_ms; /* 温度正常开始时间(毫秒) */ uint8_t protect_type; } s_temp = {0}; /*********************↑ 静态变量 ↑************************/ /*********************↓ 欠压保护模块 ↓***********************/ static void uvp_detect(uint16_t voltage_mv) { uint32_t now = rt_tick_get_millisecond(); char buf[50] = {0}; if (sys_sta.undervolt_protect == PROTECT_NONE) { if (voltage_mv < limit.uvon) { if (s_uvp.low_voltage_start_ms == 0) { s_uvp.low_voltage_start_ms = now; s_uvp.normal_voltage_start_ms = 0; LOG_D("[UVP] Voltage low (%dmV < %dmV), start counting", voltage_mv, limit.uvon); } uint32_t elapsed = now - s_uvp.low_voltage_start_ms; if (elapsed >= UVP_PRE_TRIGGER_MS && !s_uvp.pre_protect) { s_uvp.pre_protect = 1; LOG_W("[UVP] Pre-protect, voltage=%dmV < %dmV for %lu ms", voltage_mv, limit.uvon, elapsed); } if (elapsed >= UVP_TRIGGER_MS) { sys_sta.undervolt_protect = PROTECT_UNDER_VOLT; s_uvp.normal_voltage_start_ms = 0; LOG_W("[UVP] Under-voltage protection activated, voltage=%dmV, lasted %lu ms", voltage_mv, elapsed); sprintf(buf ,"欠压保护触发(%dmV < %dmV)" , voltage_mv, limit.uvon); Recode_e_code(e_protect, 1, buf); rt_thread_mdelay(1000); report_now(); } } else { s_uvp.low_voltage_start_ms = 0; s_uvp.pre_protect = 0; } } else if (sys_sta.undervolt_protect == PROTECT_UNDER_VOLT) { if (voltage_mv > limit.uvoff) { if (s_uvp.normal_voltage_start_ms == 0) { s_uvp.normal_voltage_start_ms = now; LOG_D("[UVP] Voltage normal (%dmV > %dmV), start recovery count", voltage_mv, limit.uvoff); } uint32_t elapsed = now - s_uvp.normal_voltage_start_ms; if (elapsed >= UVP_RECOVER_MS) { sys_sta.undervolt_protect = PROTECT_NONE; s_uvp.low_voltage_start_ms = 0; s_uvp.pre_protect = 0; LOG_I("[UVP] Under-voltage protection cleared, voltage=%dmV, stable for %lu ms", voltage_mv, elapsed); sprintf(buf ,"欠压保护解除(%dmV > %dmV)" , voltage_mv, limit.uvoff); Recode_e_code(e_protect, 0, buf); rt_thread_mdelay(1000); report_now(); } } else { s_uvp.normal_voltage_start_ms = 0; } } } /*********************↑ 欠压保护模块 ↑***********************/ /*********************↓ 温控保护模块 ↓***********************/ /* 温度源切换防抖时间(毫秒) */ #define TEMP_SOURCE_STABLE_MS 5000 /* 5秒内稳定才切换 */ static void temp_detect(void) { uint32_t now = rt_tick_get_millisecond(); uint8_t is_high_temp = 0; uint8_t is_low_temp = 0; /* 静态变量:保存当前源和切换计时 */ static uint8_t s_current_source = 0; /* 当前使用的温度源 */ static uint8_t s_pending_source = 0; /* 待切换的源 */ static uint32_t s_pending_start_ms = 0; /* 待切换开始时间 */ /* ========== 1. 确定当前可用温度源 ========== */ uint8_t available_source = 0; int16_t raw_temp = 0; /* 优先级:SHT30 > NTC > PLAT > 默认值 */ if (sensor.valid_sht == VALID) { available_source = 1; raw_temp = sensor.temp_sht; } else if (sensor.valid_ntc == VALID) { available_source = 2; raw_temp = sensor.temp_ntc; } else if (sensor.valid_plat == VALID) { available_source = 3; raw_temp = sensor.temp_plat; } else { available_source = 4; raw_temp = 220; /* 默认22.0°C */ } /* ========== 2. 源切换防抖逻辑 ========== */ if (available_source != s_current_source) { /* 检测到源变化 */ if (available_source != s_pending_source) { /* 新的源变化,重新开始计时 */ s_pending_source = available_source; s_pending_start_ms = now; LOG_D("[TEMP] Source change pending: %d -> %d", s_current_source, s_pending_source); } else if ((now - s_pending_start_ms) >= TEMP_SOURCE_STABLE_MS) { /* 稳定时间达到,执行切换 */ s_current_source = s_pending_source; LOG_I("[TEMP] Source switched: %d", s_current_source); s_pending_source = 0; s_pending_start_ms = 0; } } else { /* 源未变化,清除待切换状态 */ s_pending_source = 0; s_pending_start_ms = 0; } /* ========== 3. 获取最终温度值 ========== */ if (s_current_source == 0) { /* 首次运行,直接使用当前可用源 */ s_current_source = available_source; LOG_I("[TEMP] Initial source: %d", s_current_source); } /* 根据情况获取温度值 */ int16_t temp_value = 0; if (s_pending_source != 0 && (now - s_pending_start_ms) < TEMP_SOURCE_STABLE_MS) { /* 切换期间:使用新源的数据,避免使用已失效的旧源 */ temp_value = raw_temp; LOG_D("[TEMP] Switch in progress, using source %d temporarily", available_source); } else { /* 正常情况:根据最终确定的源获取温度 */ switch (s_current_source) { case 1: temp_value = sensor.temp_sht; break; case 2: temp_value = sensor.temp_ntc; break; case 3: temp_value = sensor.temp_plat; break; case 4: temp_value = 220; break; default: temp_value = 220; break; } } sys_sta.board_temp = temp_value; /* 可选:每10秒打印一次当前状态(调试用)*/ static uint32_t last_log_ms = 0; if ((now - last_log_ms) >= 10000) { last_log_ms = now; LOG_D("[TEMP] Source=%d%s, Temp=%d.%d°C", s_current_source, (s_pending_source != 0) ? "(switching)" : "", sys_sta.board_temp / 10, sys_sta.board_temp % 10); } /* ========== 4. 原有的温控保护判断逻辑(完全不变) ========== */ if (s_temp.protect_type == PROTECT_NONE) { if (sys_sta.board_temp > limit.overtemp) { is_high_temp = 1; } else if (sys_sta.board_temp < limit.lowtemp) { is_low_temp = 1; } if (is_high_temp) { if (s_temp.high_temp_start_ms == 0) { s_temp.high_temp_start_ms = now; s_temp.low_temp_start_ms = 0; LOG_D("[TEMP] High temp (%d.%d°C > %d.%d°C), start counting", sys_sta.board_temp / 10, sys_sta.board_temp % 10, limit.overtemp / 10, limit.overtemp % 10); } uint32_t elapsed = now - s_temp.high_temp_start_ms; if (elapsed >= TEMP_TRIGGER_MS) { s_temp.protect_type = PROTECT_HIGH_TEMP; sys_sta.temp_protect = PROTECT_HIGH_TEMP; LOG_W("[TEMP] High-temperature protection, temp=%d.%d°C, lasted %lu ms", sys_sta.board_temp / 10, sys_sta.board_temp % 10, elapsed); Recode_e_code(e_protect, 2, "高温保护触发"); rt_thread_mdelay(1000); report_now(); } } else if (is_low_temp) { if (s_temp.low_temp_start_ms == 0) { s_temp.low_temp_start_ms = now; s_temp.high_temp_start_ms = 0; LOG_D("[TEMP] Low temp (%d.%d°C < %d.%d°C), start counting", sys_sta.board_temp / 10, sys_sta.board_temp % 10, limit.lowtemp / 10, limit.lowtemp % 10); } uint32_t elapsed = now - s_temp.low_temp_start_ms; if (elapsed >= TEMP_TRIGGER_MS) { s_temp.protect_type = PROTECT_LOW_TEMP; sys_sta.temp_protect = PROTECT_LOW_TEMP; LOG_W("[TEMP] Low-temperature protection, temp=%d.%d°C, lasted %lu ms", sys_sta.board_temp / 10, sys_sta.board_temp % 10, elapsed); Recode_e_code(e_protect, 3, "低温保护触发"); rt_thread_mdelay(1000); report_now(); } } else { s_temp.high_temp_start_ms = 0; s_temp.low_temp_start_ms = 0; } } else if (s_temp.protect_type != PROTECT_NONE) { uint8_t is_recovered = 0; if (s_temp.protect_type == PROTECT_HIGH_TEMP) { if (sys_sta.board_temp < limit.unovert) is_recovered = 1; } else if (s_temp.protect_type == PROTECT_LOW_TEMP) { if (sys_sta.board_temp > limit.unlowt) is_recovered = 1; } if (is_recovered) { if (s_temp.normal_temp_start_ms == 0) { s_temp.normal_temp_start_ms = now; LOG_D("[TEMP] Temperature recovered, start recovery count"); } uint32_t elapsed = now - s_temp.normal_temp_start_ms; if (elapsed >= TEMP_RECOVER_MS) { LOG_I("[TEMP] Temperature protection cleared, temp=%d.%d°C, stable for %lu ms", sys_sta.board_temp / 10, sys_sta.board_temp % 10, elapsed); Recode_e_code(e_protect, 0, (s_temp.protect_type == PROTECT_HIGH_TEMP) ? "高温保护解除" : "低温保护解除"); s_temp.protect_type = PROTECT_NONE; sys_sta.temp_protect = PROTECT_NONE; s_temp.high_temp_start_ms = 0; s_temp.low_temp_start_ms = 0; s_temp.normal_temp_start_ms = 0; report_now(); } } else { s_temp.normal_temp_start_ms = 0; } } } /*********************↑ 温控保护模块 ↑***********************/ /*********************↓ 主保护线程(100ms) ↓***********************/ static void protect_thread_entry(void *param) { // LOG_I("[PROTECT] Protection monitor thread started (base=%dms)", PROTECT_BASE_MS); while (1) { uvp_detect(sys_sta.battery_voltage); temp_detect(); static uint32_t debug_counter = 0; debug_counter++; if (debug_counter >= 100) /* 100 * 100ms = 10秒 */ { debug_counter = 0; LOG_D("[PROTECT] UVP: state=%d, pre=%d | TEMP: type=%d", sys_sta.undervolt_protect, s_uvp.pre_protect, s_temp.protect_type); } rt_thread_mdelay(PROTECT_BASE_MS); } } static void protect_monitor_start(void) { if (s_protect_thread != RT_NULL) { LOG_D("[PROTECT] Already running"); return; } memset(&s_uvp, 0, sizeof(s_uvp)); memset(&s_temp, 0, sizeof(s_temp)); s_protect_thread = rt_thread_create("protect", protect_thread_entry, NULL, 2048, 12, 10); if (s_protect_thread == RT_NULL) { LOG_E("[PROTECT] Failed to create thread"); return; } rt_thread_startup(s_protect_thread); // LOG_I("[PROTECT] Protection monitor started"); } /*********************↑ 主保护线程 ↑***********************/ /*********************↓ 自动初始化 ↓***********************/ static int protect_auto_init(void) { protect_monitor_start(); return 0; } INIT_APP_EXPORT(protect_auto_init); /*********************↑ 自动初始化 ↑***********************/ /*********************↓ MSH命令 ↓***********************/ static void cmd_protect_status(int argc, char **argv) { (void)argc; (void)argv; uint32_t now = rt_tick_get_millisecond(); rt_kprintf("\n========== PROTECTION STATUS ==========\n"); rt_kprintf("[UVP] Status: "); if (sys_sta.undervolt_protect == PROTECT_UNDER_VOLT) rt_kprintf("ACTIVE\n"); else if (s_uvp.pre_protect) rt_kprintf("PRE-ACTIVE\n"); else if (s_uvp.low_voltage_start_ms != 0) { uint32_t elapsed = now - s_uvp.low_voltage_start_ms; rt_kprintf("COUNTING (%lu/%lu ms)\n", elapsed, UVP_TRIGGER_MS); } else rt_kprintf("NORMAL\n"); rt_kprintf(" Voltage: %dmV, UVON=%dmV, UVOFF=%dmV\n", sys_sta.battery_voltage, limit.uvon, limit.uvoff); rt_kprintf("[TEMP] Status: "); if (s_temp.protect_type == PROTECT_HIGH_TEMP) rt_kprintf("HIGH-TEMP PROTECT\n"); else if (s_temp.protect_type == PROTECT_LOW_TEMP) rt_kprintf("LOW-TEMP PROTECT\n"); else if (s_temp.high_temp_start_ms != 0) { uint32_t elapsed = now - s_temp.high_temp_start_ms; rt_kprintf("HIGH COUNTING (%lu/%lu ms)\n", elapsed, TEMP_TRIGGER_MS); } else if (s_temp.low_temp_start_ms != 0) { uint32_t elapsed = now - s_temp.low_temp_start_ms; rt_kprintf("LOW COUNTING (%lu/%lu ms)\n", elapsed, TEMP_TRIGGER_MS); } else rt_kprintf("NORMAL\n"); rt_kprintf(" Temp: %d.%d°C\n", sys_sta.board_temp / 10, sys_sta.board_temp % 10); rt_kprintf("======================================\n"); } MSH_CMD_EXPORT(cmd_protect_status, Show protection status); static void cmd_protect_clear(int argc, char **argv) { if (argc < 2) { rt_kprintf("Usage: protect_clear [uvp|temp|all]\n"); return; } if (strcmp(argv[1], "uvp") == 0 || strcmp(argv[1], "all") == 0) { sys_sta.undervolt_protect = PROTECT_NONE; memset(&s_uvp, 0, sizeof(s_uvp)); LOG_I("[CMD] UVP cleared"); } if (strcmp(argv[1], "temp") == 0 || strcmp(argv[1], "all") == 0) { sys_sta.temp_protect = PROTECT_NONE; memset(&s_temp, 0, sizeof(s_temp)); LOG_I("[CMD] TEMP cleared"); } rt_kprintf("Protection status cleared\n"); cmd_protect_status(0, NULL); } MSH_CMD_EXPORT(cmd_protect_clear, Clear protection status : protect_clear[uvp | temp | all]); /*********************↑ MSH命令 ↑***********************/