From b96902f9b9cad85fe4066aca2259a81aaeb63f39 Mon Sep 17 00:00:00 2001 From: wrh Date: Fri, 7 Aug 2026 16:27:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E8=B4=9F=E8=BD=BD?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E3=80=81=E6=8E=89=E7=94=B5=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E5=8F=8A=E5=8D=8F=E8=AE=AE=E5=88=87=E6=8D=A2=E6=97=B6=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 详细修改: 1. 新增chrg_eload_get_sample_seq接口获取量测更新序号 2. 关闭LCD接收功能避免阻塞量测上报 3. 新增Sink北向事务状态枚举,区分完整重握手和软卸载流程 4. 调整北向调试开关配置,开启南向寄存器写命令调试 5. 重构Sink状态机,拆分INIT等待、掉电恢复、软卸载等阶段 6. 优化协议切换逻辑:仅电压变化使用软卸载,其余变更执行完整重握手 7. 增加电压稳定确认次数,避免瞬态电压误触发运行状态 8. 新增南向批量命令状态查询和优先量测请求功能 9. 调整继电器操作时序,副继电器回码后再执行后续初始化命令 10. 新增修改日志文档记录版本变更 --- apps/chrg/applications/thread/chrg_comm.c | 34 +- apps/chrg/applications/thread/chrg_lcd.c | 21 +- apps/chrg/applications/thread/chrg_lcd.h | 3 + apps/chrg/applications/thread/chrg_north.c | 68 +++- apps/chrg/applications/thread/chrg_north.h | 4 +- apps/chrg/applications/thread/chrg_roll_nor.c | 117 +++++- apps/chrg/applications/thread/chrg_roll_nor.h | 4 + apps/chrg/applications/thread/chrg_roll_sou.c | 183 ++++++++- apps/chrg/applications/thread/chrg_roll_sou.h | 21 + apps/chrg/applications/utils/chrg_eload.c | 21 + apps/chrg/applications/utils/chrg_eload.h | 9 + apps/chrg/applications/utils/chrg_sink.c | 368 ++++++++++++++++-- apps/chrg/applications/utils/chrg_sink.h | 31 +- apps/chrg/codex_build.log | 14 +- docs/修改日志.md | 47 +++ 15 files changed, 855 insertions(+), 90 deletions(-) create mode 100644 docs/修改日志.md diff --git a/apps/chrg/applications/thread/chrg_comm.c b/apps/chrg/applications/thread/chrg_comm.c index 44a9e23..f257d3a 100644 --- a/apps/chrg/applications/thread/chrg_comm.c +++ b/apps/chrg/applications/thread/chrg_comm.c @@ -2336,8 +2336,8 @@ static void chrg_comm_get_param_diff(eIDX_ID role, static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch, struct chrg_switch_t *pReal, const struct chrg_switch_t *pShadow) { - rt_bool_t protocol_or_cc_change; - rt_bool_t protocol_param_change; + rt_bool_t full_handshake_change; + rt_bool_t protocol_voltage_only; rt_uint8_t curr_test; rt_uint8_t curr_test_old; rt_uint8_t old_cv_mode = pReal->CV_mode; @@ -2347,11 +2347,14 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch, chrg_param_diff_t diff_info; chrg_comm_get_param_diff(ID_SINK, pReal, pShadow, &diff_info); - /* 协议类型或CC线变化:需卸载后从CC开始重新配置北向协议链路 */ - protocol_or_cc_change = diff_info.protocol_changed || diff_info.cc_changed; - /* 同协议下组号/协议电压/协议电流变化:卸载后重发协议,不重设CC */ - protocol_param_change = diff_info.group_changed || - diff_info.pro_voltage_changed || diff_info.pro_current_changed; + /* + * 协议类型、CC、协议组号或协议电流变化都按完整重握手处理。 + * 只有单独修改协议电压时才允许仅掉载而不切换南向继电器。 + */ + full_handshake_change = diff_info.protocol_changed || diff_info.cc_changed || + diff_info.group_changed || diff_info.pro_current_changed; + protocol_voltage_only = diff_info.pro_voltage_changed && + !full_handshake_change; rt_memcpy(pReal, pShadow, sizeof(struct chrg_switch_t)); if (sink_work == 0x01) { @@ -2369,18 +2372,21 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch, } pReal->sink.test.test_mode_old = curr_test; } else { - pReal->sub = IDX_SET_SINK_CCLINE; + pReal->sub = IDX_SET_SINK_PD; pReal->sink.work_mode = SINK_WORK_INIT; } } else if (pReal->sink.On_work == 0) { pReal->sink.On_work = 1; - pReal->sub = IDX_SET_SINK_CCLINE; - pReal->sink.work_mode = SINK_WORK_INIT; - } else if (protocol_or_cc_change) { - pReal->sub = IDX_SET_SINK_CCLINE; - pReal->sink.work_mode = SINK_WORK_PROTOCOL_UNLOAD; - } else if (protocol_param_change) { pReal->sub = IDX_SET_SINK_PD; + pReal->sink.work_mode = SINK_WORK_INIT; + } else if (full_handshake_change) { + /* 协议类型或CC变化需要重发协议和CC;仅组号/协议电流变化只重发协议。 */ + pReal->sub = (diff_info.protocol_changed || diff_info.cc_changed) ? + IDX_SET_SINK_PD : IDX_SET_SINK_PD_ONLY; + pReal->sink.work_mode = SINK_WORK_HANDSHAKE_PREPARE; + } else if (protocol_voltage_only) { + /* 仅协议电压变化时保持主副继电器状态,只停止负载并清零电流。 */ + pReal->sub = IDX_SET_SINK_PD_ONLY; pReal->sink.work_mode = SINK_WORK_PROTOCOL_UNLOAD; } else if (chrg_sink_diff_send_reg((eIDX_SOU_CH)ch, &diff_info, pReal) != 0) { diff --git a/apps/chrg/applications/thread/chrg_lcd.c b/apps/chrg/applications/thread/chrg_lcd.c index 65a2ca1..9439c26 100644 --- a/apps/chrg/applications/thread/chrg_lcd.c +++ b/apps/chrg/applications/thread/chrg_lcd.c @@ -369,7 +369,9 @@ void chrg_lcd_thread_entry (void *data) return ; } +#if LCD_RX_ENABLE int len = 0; +#endif struct chrg_tty_t *pTTY = RT_NULL; struct chrg_lcd_t *pLCD = &chrglcd; pTTY = chrg_tty_create(pLCD->devname, 921600, 0, -1, 1); @@ -377,8 +379,10 @@ void chrg_lcd_thread_entry (void *data) LOG_E("tty can't create."); return ; } - +#if LCD_RX_ENABLE + /* LCD接收功能开启时,最多等待100ms屏幕回传数据。 */ chrg_tty_set_recv_tmo(pTTY, 100); +#endif if (chrg_tty_connect(pTTY) != RT_EOK) { chrg_tty_destory(pTTY); return; @@ -386,6 +390,12 @@ void chrg_lcd_thread_entry (void *data) pLCD->tty = pTTY; +#if LCD_RX_ENABLE + /* + * 保留原LCD接收及控制解析流程,需要恢复屏幕控制时只需将 + * LCD_RX_ENABLE改为1。接收关闭时本段不参与编译,避免chrg_tty_recv + * 在等待回传期间持有TTY锁并阻塞chrg_lcd_send。 + */ while (1) { rt_memset(pLCD->rx_buf, 0, SIZE_BUF_TTY); len = chrg_tty_recv(pTTY, pLCD->rx_buf, sizeof(pLCD->rx_buf)); @@ -399,6 +409,15 @@ void chrg_lcd_thread_entry (void *data) chrg_lcd_parse(pLCD->rx_buf, len); } } +#else + /* + * 仅发送模式仍保留LCD线程和串口实例。线程低频休眠且不访问TTY, + * 量测解析中的LCD发送可以直接取得TTY锁,不再受100ms接收超时影响。 + */ + while (1) { + rt_thread_mdelay(1000); + } +#endif } diff --git a/apps/chrg/applications/thread/chrg_lcd.h b/apps/chrg/applications/thread/chrg_lcd.h index b6af7ec..5a76824 100644 --- a/apps/chrg/applications/thread/chrg_lcd.h +++ b/apps/chrg/applications/thread/chrg_lcd.h @@ -18,6 +18,9 @@ #define THR_NAME_LCD "thr.lcd" #define DEV_NAME_LCD "uart2" +/* LCD发送保持启用;当前产品不使用屏幕回传控制,关闭接收可避免接收线程持有TTY锁阻塞量测上报。 */ +#define LCD_RX_ENABLE 0 + #define LCD_FRAME_HEAD 0x55 diff --git a/apps/chrg/applications/thread/chrg_north.c b/apps/chrg/applications/thread/chrg_north.c index acb688f..9ca6584 100644 --- a/apps/chrg/applications/thread/chrg_north.c +++ b/apps/chrg/applications/thread/chrg_north.c @@ -24,10 +24,12 @@ #define DBG_LEVEL DBG_LOG #include -#define SOURCE_VOLT_ERROR_MV 200U -#define SOURCE_VOLT_RETRY_FRAMES 3U -#define SOURCE_QC_MIN_VOLT_MV 5000U -#define SOURCE_QC_SWITCH_ERROR 0x04U +#define SOURCE_VOLT_ERROR_MV 200U // Source目标电压允许误差,单位mV +#define SOURCE_VOLT_RETRY_FRAMES 3U // Source电压持续超差后的重发确认帧数 +#define SOURCE_QC_MIN_VOLT_MV 5000U // QC2.0/QC3.0允许使用的最低目标电压 +#define SOURCE_QC_SWITCH_ERROR 0x04U // Source协议或CC切换失败错误位 +#define SINK_CURRENT_START_GRACE_MS 200U // Sink启动后暂不判断无电流的保护时间 +#define SINK_VOLT_STABLE_CONFIRM_COUNT 2U // Sink进入RUNING前连续稳定电压帧数 /***************************************************************** 函数名称: chrg_source_apply_protocol_voltage_rule @@ -188,10 +190,10 @@ void chrg_source_setting(rt_uint8_t *frame_data, eIDX_SOU_CH idx, 函数描述: 解析北向负载板回码并根据电压、电流反馈驱动Sink状态机 输入参数: frame_data:完整北向回码 idx:当前通道 pSW:通道控制结构体 pNOR:北向总控结构体 -输出参数: 更新VZ、VF、低压计数、无电流计数及sink.work_mode +输出参数: 更新VZ、VF、低压计数、稳定电压计数、无电流计数及sink.work_mode 返回说明: - -其它说明: 电压连续低于门槛进入LOW;电压达到目标附近进入RUNING; - 运行电流连续过低时退回WAIT重新判断 +其它说明: 电压连续低于门槛进入LOW;掉电处理恢复副继电器后,电压重新 + 出现时先重设协议和CC;连续两次达到目标附近才进入RUNING *****************************************************************/ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx, struct chrg_switch_t *pSW, struct chrg_north_t *pNOR) @@ -200,6 +202,8 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx, static rt_uint8_t volt_low[TOTAL_SOU_CHS] = {0}; static rt_uint8_t no_curr[TOTAL_SOU_CHS] = {0}; static rt_uint8_t volt_set_ok[TOTAL_SOU_CHS] = {0}; + static rt_uint8_t volt_stable_count[TOTAL_SOU_CHS] = {0}; + static rt_tick_t running_start_tick[TOTAL_SOU_CHS] = {0}; if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { return; @@ -214,8 +218,9 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx, if(pSW->sink.On_work == 1 && pSW->sink.test.test_mode == 0) { if (volt_mv <= WORK_MIN_VOLT) { // 电压过低,禁止进入工作状态机 + volt_stable_count[idx] = 0; if (volt_set_ok[idx] == 1) { - if (++volt_low[idx] >= 3) { + if (++volt_low[idx] >= 2) { volt_set_ok[idx] = 0; no_curr[idx] = 0; pSW->sink.work_mode = SINK_WORK_LOW; @@ -226,18 +231,48 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx, volt_low[idx] = 0; volt_set_ok[idx] = 1; pSW->change_flag = 0; - if ((rt_uint32_t)volt_mv + 2000 >= pSW->sink.pro_loadv) { - pSW->sink.work_mode = SINK_WORK_RUNING; - if (pSW->Now_current <= 200 && (pSW->sink.pro_loadc >= 200)) { - if (++no_curr[idx] >= 3) { - pSW->sink.work_mode = SINK_WORK_WAIT; + if (pSW->sink.work_mode == SINK_WORK_LOW_MONITOR) { + /* 副继电器恢复后首次重新检测到输入电压,只启动协议重握手。 + * 当前回码不计入稳定电压次数,防止跳过协议设置直接带载。 */ + volt_stable_count[idx] = 0; + no_curr[idx] = 0; + pSW->sub = IDX_SET_SINK_PD; + pSW->sink.work_mode = SINK_WORK_WAIT; + } else if ((pSW->sink.work_mode == SINK_WORK_LOW) || + (pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT)) { + /* 掉电安全批次完成前,即使收到残留回码也不得进入RUNING。 */ + volt_stable_count[idx] = 0; + no_curr[idx] = 0; + } else if ((rt_uint32_t)volt_mv + 2000 >= pSW->sink.pro_loadv) { + if ((pSW->sink.work_mode != SINK_WORK_RUNING) && + (pSW->sink.work_mode != SINK_WORK_SOFT_RUNNING)) { + if (volt_stable_count[idx] < SINK_VOLT_STABLE_CONFIRM_COUNT) { + volt_stable_count[idx]++; + } + if (volt_stable_count[idx] >= SINK_VOLT_STABLE_CONFIRM_COUNT) { + /* 连续两次北向量测均达到目标电压附近后才允许带载,避免 + * 单次瞬态电压或旧回码导致负载提前进入RUNING。 */ + volt_stable_count[idx] = 0; + pSW->sink.work_mode = + (pSW->sink.work_mode == SINK_WORK_SOFT_RESUME_WAIT) ? + SINK_WORK_SOFT_RESUME : SINK_WORK_RUNING; + running_start_tick[idx] = rt_tick_get(); no_curr[idx] = 0; - } - } - else { + } + } else if ((pSW->Now_current > 200) || + (pSW->sink.pro_loadc < 200)) { + volt_stable_count[idx] = 0; no_curr[idx] = 0; + } else if ((rt_tick_t)(rt_tick_get() - running_start_tick[idx]) >= + rt_tick_from_millisecond(SINK_CURRENT_START_GRACE_MS)) { + volt_stable_count[idx] = 0; + if (++no_curr[idx] >= 2) { + pSW->sink.work_mode = SINK_WORK_WAIT; + no_curr[idx] = 0; + } } } else { + volt_stable_count[idx] = 0; no_curr[idx] = 0; } } @@ -246,6 +281,7 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx, volt_low[idx] = 0; no_curr[idx] = 0; volt_set_ok[idx] = 0; + volt_stable_count[idx] = 0; } chrg_sink_work(idx,pSW); } diff --git a/apps/chrg/applications/thread/chrg_north.h b/apps/chrg/applications/thread/chrg_north.h index ffa98fe..b3c9a78 100644 --- a/apps/chrg/applications/thread/chrg_north.h +++ b/apps/chrg/applications/thread/chrg_north.h @@ -22,11 +22,11 @@ #define LEN_FRAME_HEAD (4) // [帧长, 0x00, id, 命令] #define MIN_FRAME_LENGTH (5) // 帧最短5字节 #define WORK_MIN_VOLT 3000 // 工作电压下限,单位mV -#define NORTH_FRAME_DEBUG 0x01 // 北向UART原始收发帧 +#define NORTH_FRAME_DEBUG 0x00 // 北向UART原始收发帧 #define NORTH_SOURCE_DEBUG 0x00 // Source协议解析及组帧 #define NORTH_SINK_DEBUG 0x00 // Sink协议解析及组帧 #define SOUTH_FRAME_DEBUG 0x00 // 南向UART原始收发帧 -#define SOUTH_COMMAND_DEBUG 0x00 // 南向寄存器写命令 +#define SOUTH_COMMAND_DEBUG 0x01 // 南向寄存器写命令 #define COMM_FRAME_DEBUG 0x00 // COM UART原始收发帧 #define COMM_DEBUG 0x00 // COM寄存器读写过程 #define COMM_TRIM_DEBUG 0x00 // COM校准参数解析 diff --git a/apps/chrg/applications/thread/chrg_roll_nor.c b/apps/chrg/applications/thread/chrg_roll_nor.c index 35535b2..fd8e02a 100644 --- a/apps/chrg/applications/thread/chrg_roll_nor.c +++ b/apps/chrg/applications/thread/chrg_roll_nor.c @@ -17,6 +17,7 @@ #include "chrg_sink.h" #include "chrg_source.h" #include "chrg_roll_nor.h" +#include "chrg_roll_sou.h" #include "chrg_north.h" #include "chrg_switch.h" #include "chrg_utils.h" @@ -32,6 +33,7 @@ #define SOURCE_CC_CLOSE_VALUE 0x03U // 0x17命令参数:同时断开CC1和CC2 #define SOURCE_CC_CLOSE_WAIT_MS 300U // 本轮所有CC_CLOSE应答后的共享保护时间 #define SOURCE_PROTOCOL_WAIT_MS 100U // 每通道0xD0应答后的协议生效保护时间 +#define SINK_CC_STABLE_WAIT_MS 50U // Sink设置目标CC应答后的非阻塞稳定时间 #define SOURCE_CHANNEL_BIT(ch) ((rt_uint8_t)(1U << (ch))) const rt_base_t sw_uart[TOTAL_NOR_CHS] = { IDX_GPO_UART_SW1, @@ -54,6 +56,8 @@ static rt_bool_t g_source_cc_close_wait_active = RT_FALSE; static rt_tick_t g_source_cc_close_wait_start = 0U; static rt_uint8_t g_source_protocol_wait_mask = 0U; static rt_tick_t g_source_protocol_wait_start[TOTAL_NOR_CHS] = {0}; +static rt_uint8_t g_sink_cc_wait_mask = 0U; +static rt_tick_t g_sink_cc_wait_start[TOTAL_NOR_CHS] = {0}; /***************************************************************** 函数名称: chrg_roll_nor_source_switch_reset @@ -196,6 +200,78 @@ static rt_bool_t chrg_roll_nor_source_wait_process(rt_uint8_t ch, } return RT_FALSE; } + +/***************************************************************** +函数名称: chrg_roll_nor_sink_cc_wait_start +函数描述: Sink目标CC设置收到有效应答后启动该通道的稳定计时 +输入参数: ch:北向通道编号 +输出参数: 记录通道等待标志和起始tick +返回说明: - +其它说明: 每个通道独立计时,多个通道切换CC时等待时间不会串行累加 +*****************************************************************/ +static void chrg_roll_nor_sink_cc_wait_start(rt_uint8_t ch) +{ + if (ch >= TOTAL_NOR_CHS) { + return; + } + g_sink_cc_wait_mask |= SOURCE_CHANNEL_BIT(ch); + g_sink_cc_wait_start[ch] = rt_tick_get(); +} + +/***************************************************************** +函数名称: chrg_roll_nor_sink_wait_process +函数描述: 管理Sink副继电器确认、安全卸载和CC设置后的稳定等待 +输入参数: ch:北向通道编号 pSW:通道实时控制结构体 +输出参数: 南向卸载完成后允许发送协议;CC稳定时间到达后切换为电压查询 +返回说明: RT_TRUE:本轮仍需等待,不发送北向命令 RT_FALSE:可继续发送 +其它说明: INIT只等待第一条FU_REL_ON完成;其余初始化命令入队后与北向 + 握手并行。软卸载和完整重握手仍必须完成低电流确认后才放行 +*****************************************************************/ +static rt_bool_t chrg_roll_nor_sink_wait_process(rt_uint8_t ch, + struct chrg_switch_t *pSW) +{ + rt_uint8_t bit; + rt_tick_t elapsed; + + if ((ch >= TOTAL_NOR_CHS) || (pSW == RT_NULL)) { + return RT_FALSE; + } + bit = SOURCE_CHANNEL_BIT(ch); + + if ((pSW->id != ID_SINK) || !pSW->sink.On_work) { + g_sink_cc_wait_mask &= (rt_uint8_t)~bit; + g_sink_cc_wait_start[ch] = 0U; + return RT_FALSE; + } + /* 副继电器或安全卸载尚未确认时,禁止任何北向设置和电压查询。 */ + if ((pSW->sink.work_mode == SINK_WORK_INIT_AUX_WAIT) || + (pSW->sink.work_mode == SINK_WORK_UNLOAD_WAIT) || + (pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT) || + (pSW->sink.work_mode == SINK_WORK_HANDSHAKE_WAIT)) { + return RT_TRUE; + } + + if (pSW->sub != IDX_WAIT_SINK_CC) { + /* COM在等待期间提交新配置时,清理旧事务留下的CC计时记录。 */ + g_sink_cc_wait_mask &= (rt_uint8_t)~bit; + g_sink_cc_wait_start[ch] = 0U; + return RT_FALSE; + } + + if ((g_sink_cc_wait_mask & bit) == 0U) { + pSW->sub = IDX_GET_SINK_VC; + return RT_FALSE; + } + elapsed = (rt_tick_t)(rt_tick_get() - g_sink_cc_wait_start[ch]); + if (elapsed < rt_tick_from_millisecond(SINK_CC_STABLE_WAIT_MS)) { + return RT_TRUE; + } + + g_sink_cc_wait_mask &= (rt_uint8_t)~bit; + g_sink_cc_wait_start[ch] = 0U; + pSW->sub = IDX_GET_SINK_VC; + return RT_FALSE; +} /***************************************************************** 函数名称: sink_fill_pd_buf 函数描述: 根据快充协议类型填充PD指令的5字节数据缓冲区 @@ -422,6 +498,7 @@ static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW) case IDX_GET_SINK_PD: return SINK_CMD_GET_PD; case IDX_SET_SINK_PD: + case IDX_SET_SINK_PD_ONLY: return pSW->sink.protocol; case IDX_SET_SINK_CCLINE: return SINK_CMD_SET_CC; @@ -588,7 +665,7 @@ static int chrg_roll_nor_cmd (struct chrg_rollnor_t *pROLL) 返回说明: >0:发送长度 <0:发送失败或状态不支持 其它说明: Source按差异执行协议、CC或组合命令;涉及PD的运行中切换先发送CC_CLOSE; 停止状态启动和主动停止均不额外发送CC_CLOSE; - Sink依次执行CC线选择、协议设置、VC查询 + Sink依次执行协议设置、目标CC线选择、稳定等待和VC查询 *****************************************************************/ static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW, rt_uint8_t *next_sub) @@ -630,11 +707,14 @@ static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW, *next_sub = IDX_GET_SINK_VC; return sink_get_pd(); case IDX_SET_SINK_CCLINE: //CC线选择 - *next_sub = IDX_SET_SINK_PD; + *next_sub = IDX_WAIT_SINK_CC; return sink_set_cc_line(pSW->sink.cc_set); case IDX_SET_SINK_CCLVL: //CC电平 return sink_set_cc_lvl(pSW->sink.cc_lvl); case IDX_SET_SINK_PD: + *next_sub = IDX_SET_SINK_CCLINE; + return chrg_roll_nor_sink_pro_set(pSW); + case IDX_SET_SINK_PD_ONLY: *next_sub = IDX_GET_SINK_VC; return chrg_roll_nor_sink_pro_set(pSW); default: @@ -731,8 +811,16 @@ static int chrg_roll_nor_auto_request(rt_uint8_t idx, (request_sub == IDX_SET_SOURCE_PD)) { /* 0xD0应答后每通道独立等待100ms,再连接目标CC。 */ chrg_roll_nor_source_protocol_wait_start(idx); + } else if ((expected_id == ID_SINK) && + (request_sub == IDX_SET_SINK_CCLINE)) { + /* 北向Sink继电器保持接通,仅等待目标CC配置稳定。 */ + chrg_roll_nor_sink_cc_wait_start(idx); + } + /* 业务解析可能主动切换sub,例如掉电恢复后要求重新设置协议。 + * 只有sub仍属于本次请求时,才应用请求函数计算出的next_sub。 */ + if (pSW->sub == request_sub) { + pSW->sub = next_sub; } - pSW->sub = next_sub; rt_mutex_release(pTHR->mutex); chrg_roll_nor_free_response(pMB); return 0; @@ -770,7 +858,8 @@ void chrg_roll_nor_thread_entry (void *data) rt_uint8_t index = 0, idx = 0, i = 0; rt_uint8_t run = 0; rt_bool_t source_waiting = RT_FALSE; - int source_work_ret = 0; + rt_bool_t sink_waiting = RT_FALSE; + int state_work_ret = 0; rt_err_t ret = RT_EOK; struct chrg_rollnor_t *pROLL = &chrgrollnor; struct chrg_thread_t *pTN = &chrgthr[IDX_THR_NORTH]; @@ -790,7 +879,8 @@ void chrg_roll_nor_thread_entry (void *data) idx = index%TOTAL_NOR_CHS; pSW = &pNOR->sw[idx]; source_waiting = RT_FALSE; - source_work_ret = 0; + sink_waiting = RT_FALSE; + state_work_ret = 0; rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER); /* * INIT/STOP必须在enable过滤前执行:STOP会把enable清零,而COM @@ -802,24 +892,31 @@ void chrg_roll_nor_thread_entry (void *data) ((pSW->source.work_mode == SOURCE_WORK_INIT) || (pSW->source.work_mode == SOURCE_WORK_STOP) || (pSW->source.work_mode == SOURCE_WORK_PROTOCOL_SWITCH))) { - source_work_ret = chrg_source_work_state(idx, pSW); + state_work_ret = chrg_source_work_state(idx, pSW); } else if ((pSW->id == ID_SINK) && - (pSW->sink.work_mode == SINK_WORK_INIT)) { - chrg_sink_work((eIDX_SOU_CH)idx, pSW); + ((pSW->sink.work_mode == SINK_WORK_INIT) || + (pSW->sink.work_mode == SINK_WORK_INIT_AUX_WAIT) || + (pSW->sink.work_mode == SINK_WORK_UNLOAD_WAIT) || + (pSW->sink.work_mode == SINK_WORK_LOW) || + (pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT) || + (pSW->sink.work_mode == SINK_WORK_HANDSHAKE_PREPARE) || + (pSW->sink.work_mode == SINK_WORK_HANDSHAKE_WAIT))) { + state_work_ret = chrg_sink_work((eIDX_SOU_CH)idx, pSW); } /* * 协议切换等待只推进时间状态,不发送串口命令。该检查位于enable * 过滤前,使停止或角色切换后的通道也能及时清理残留事务记录。 */ source_waiting = chrg_roll_nor_source_wait_process(idx, pSW); + sink_waiting = chrg_roll_nor_sink_wait_process(idx, pSW); rt_mutex_release(pTHR->mutex); - if (source_work_ret != 0) { + if (state_work_ret != 0) { /* 南向状态动作未成功入队时保留原状态,下轮重试且禁止北向先行。 */ index++; rt_thread_mdelay(2); continue; } - if (source_waiting) { + if (source_waiting || sink_waiting) { index++; rt_thread_mdelay(2); continue; diff --git a/apps/chrg/applications/thread/chrg_roll_nor.h b/apps/chrg/applications/thread/chrg_roll_nor.h index 4c754ec..48e082e 100644 --- a/apps/chrg/applications/thread/chrg_roll_nor.h +++ b/apps/chrg/applications/thread/chrg_roll_nor.h @@ -46,6 +46,10 @@ enum { IDX_SET_SOURCE_CC_CLOSE, // 协议切换前发送0x17=0x03断开CC IDX_WAIT_SOURCE_CC_CLOSE, // 等待参与切换的Source通道完成共享保护时间 IDX_WAIT_SOURCE_PROTOCOL, // 0xD0应答后的协议生效等待 + + /* Sink北向事务状态追加在末尾,避免改变已有sub数值。 */ + IDX_SET_SINK_PD_ONLY, // 仅重发协议,协议应答后直接查询协商电压 + IDX_WAIT_SINK_CC, // CC设置应答后的非阻塞稳定等待 }; struct chrg_rollnor_t { diff --git a/apps/chrg/applications/thread/chrg_roll_sou.c b/apps/chrg/applications/thread/chrg_roll_sou.c index 974309c..7d5ede4 100644 --- a/apps/chrg/applications/thread/chrg_roll_sou.c +++ b/apps/chrg/applications/thread/chrg_roll_sou.c @@ -25,8 +25,11 @@ #define SOU_MUTEX_TIMEOUT_MS 100 #define SOU_RESPONSE_TIMEOUT_MS 20 #define SOU_COM_MAX_ATTEMPTS 3 +/* #define SOU_REL_SWITCH_DELAY_MS 150U */ // 暂时关闭主副继电器切换延时 +#define SOU_CHANNEL_BIT(ch) ((rt_uint8_t)(1U << (ch))) chrg_rollsou_com_t g_sou_com_batch = {0}; +static rt_uint8_t g_sou_measure_once_mask = 0U; struct chrg_rollsou_t chrgrollsou = { .run = RUN_AUTO, @@ -96,6 +99,7 @@ int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs, pBatch->pending = RT_FALSE; pBatch->cur_idx = 0; pBatch->reg_cnt = 0; + pBatch->next_send_tick = 0U; } /* @@ -139,18 +143,141 @@ int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs, rt_mutex_release(pTHR->mutex); return -2; } + if (pBatch->reg_cnt == 0U) { + pBatch->next_send_tick = 0U; + } for (i = 0; i < count; i++) { pBatch->reg_list[pBatch->reg_cnt] = regs[i]; pBatch->val_list[pBatch->reg_cnt] = vals[i]; pBatch->reg_cnt++; } + pBatch->result = 0; pBatch->pending = RT_TRUE; pROLL->run = RUN_COM; rt_mutex_release(pTHR->mutex); return 0; } +/***************************************************************** +函数名称: chrg_sou_com_batch_is_pending +函数描述: 查询指定通道是否仍有COM南向批量命令等待执行或等待有效应答 +输入参数: ch:南向功率板通道 +输出参数: - +返回说明: RT_TRUE:批量事务未完成或查询互斥锁失败 RT_FALSE:批量事务已完成 +其它说明: 查询失败时按未完成处理,防止北向协议配置越过南向卸载命令提前执行 +*****************************************************************/ +rt_bool_t chrg_sou_com_batch_is_pending(eIDX_SOU_CH ch) +{ + struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; + rt_bool_t pending; + + if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL) || + (rt_mutex_take(pTHR->mutex, + rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) { + return RT_TRUE; + } + + pending = g_sou_com_batch.ch[ch].pending; + rt_mutex_release(pTHR->mutex); + return pending; +} + +/***************************************************************** +函数名称: chrg_sou_com_batch_get_result +函数描述: 获取指定通道最近一次南向COM批次的执行结果 +输入参数: ch:南向功率板通道 +输出参数: - +返回说明: 1:全部命令收到匹配回码 0:执行中/无结果 -1:失败取消 -2:参数或锁错误 +其它说明: pending清零既可能是成功也可能是失败,卸载确认必须通过本结果区分 +*****************************************************************/ +int chrg_sou_com_batch_get_result(eIDX_SOU_CH ch) +{ + struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; + int result; + + if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL) || + (rt_mutex_take(pTHR->mutex, + rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) { + return -2; + } + result = g_sou_com_batch.ch[ch].result; + rt_mutex_release(pTHR->mutex); + return result; +} + +/***************************************************************** +函数名称: chrg_sou_measure_once_submit +函数描述: 登记指定通道的一次性P1量测优先请求 +输入参数: ch:南向功率板通道 +输出参数: 设置通道请求位 +返回说明: 0:登记成功或请求已存在 <0:参数或互斥锁错误 +其它说明: RUN_COM批次完成并切回RUN_AUTO后,南向线程会优先执行该查询 +*****************************************************************/ +int chrg_sou_measure_once_submit(eIDX_SOU_CH ch) +{ + struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; + + if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL) || + (rt_mutex_take(pTHR->mutex, + rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) { + return -1; + } + g_sou_measure_once_mask |= SOU_CHANNEL_BIT(ch); + rt_mutex_release(pTHR->mutex); + return 0; +} + +/***************************************************************** +函数名称: sou_measure_once_next +函数描述: 获取当前待执行的一次性P1量测通道 +输入参数: ch:输出通道指针 +输出参数: ch:最低编号的待查询通道 +返回说明: 1:存在请求 0:无请求 <0:参数或互斥锁错误 +其它说明: 请求位在本次查询完成或超时后清除,查询期间保持置位以防重复排队 +*****************************************************************/ +static int sou_measure_once_next(rt_uint8_t *ch) +{ + struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; + rt_uint8_t idx; + + if ((ch == RT_NULL) || (pTHR->mutex == RT_NULL) || + (rt_mutex_take(pTHR->mutex, + rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) { + return -1; + } + for (idx = 0; idx < TOTAL_SOU_CHS; idx++) { + if ((g_sou_measure_once_mask & SOU_CHANNEL_BIT(idx)) != 0U) { + *ch = idx; + rt_mutex_release(pTHR->mutex); + return 1; + } + } + rt_mutex_release(pTHR->mutex); + return 0; +} + +/***************************************************************** +函数名称: sou_measure_once_finish +函数描述: 结束指定通道的一次性P1量测请求 +输入参数: ch:南向功率板通道 +输出参数: 清除对应请求位 +返回说明: - +其它说明: 有效回码和超时都会结束本次请求;负载状态机按量测序号决定是否重查 +*****************************************************************/ +static void sou_measure_once_finish(rt_uint8_t ch) +{ + struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; + + if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL) || + (rt_mutex_take(pTHR->mutex, + rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) { + return; + } + g_sou_measure_once_mask &= (rt_uint8_t)~SOU_CHANNEL_BIT(ch); + rt_mutex_release(pTHR->mutex); +} + int chrg_sou_com_batch_add_reg(eIDX_SOU_CH ch, rt_uint16_t reg, rt_uint16_t val) { return chrg_sou_com_batch_submit(ch, ®, &val, 1); @@ -375,6 +502,8 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val { struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; struct chrg_rollsou_t *pROLL = &chrgrollsou; + rt_bool_t pending_wait = RT_FALSE; + rt_tick_t now; rt_uint8_t idx; if ((RT_NULL == pTHR->mutex) || @@ -382,6 +511,7 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val return -1; } + now = rt_tick_get(); for (idx = 0; idx < TOTAL_SOU_CHS; idx++) { sou_com_batch_ch_t *pBatch = &g_sou_com_batch.ch[idx]; if (pBatch->pending != RT_TRUE) { @@ -389,10 +519,19 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val } if ((pBatch->reg_cnt == 0) || (pBatch->cur_idx >= pBatch->reg_cnt)) { pBatch->pending = RT_FALSE; + pBatch->result = -1; pBatch->cur_idx = 0; pBatch->reg_cnt = 0; + pBatch->next_send_tick = 0U; continue; } + pending_wait = RT_TRUE; + if ((pBatch->next_send_tick != 0U) && + ((rt_int32_t)(now - pBatch->next_send_tick) < 0)) { + /* 当前通道处于继电器保护时间时,继续查找其它可发送通道。 */ + continue; + } + pBatch->next_send_tick = 0U; *ch = idx; *reg = pBatch->reg_list[pBatch->cur_idx]; @@ -401,7 +540,9 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val return 1; } - pROLL->run = RUN_AUTO; + if (!pending_wait) { + pROLL->run = RUN_AUTO; + } rt_mutex_release(pTHR->mutex); return 0; } @@ -425,10 +566,22 @@ static int sou_com_batch_complete(rt_uint8_t ch, rt_uint16_t reg, rt_uint16_t va } pBatch->cur_idx++; +#if 0 + /* 暂时关闭副继电器与主继电器之间的固定延时;需要恢复时重新启用本段。 */ + if ((reg == REG_REL_ON) && (value == FU_REL_ON) && + (pBatch->cur_idx < pBatch->reg_cnt) && + (pBatch->reg_list[pBatch->cur_idx] == REG_REL_ON) && + (pBatch->val_list[pBatch->cur_idx] == ALL_REL_ON)) { + pBatch->next_send_tick = rt_tick_get() + + rt_tick_from_millisecond(SOU_REL_SWITCH_DELAY_MS); + } +#endif if (pBatch->cur_idx >= pBatch->reg_cnt) { pBatch->pending = RT_FALSE; + pBatch->result = 1; pBatch->cur_idx = 0; pBatch->reg_cnt = 0; + pBatch->next_send_tick = 0U; } rt_mutex_release(pTHR->mutex); return 0; @@ -445,8 +598,10 @@ static int sou_com_batch_cancel(rt_uint8_t ch) } pBatch = &g_sou_com_batch.ch[ch]; pBatch->pending = RT_FALSE; + pBatch->result = -1; pBatch->cur_idx = 0; pBatch->reg_cnt = 0; + pBatch->next_send_tick = 0U; rt_mutex_release(pTHR->mutex); return 0; } @@ -546,6 +701,8 @@ void chrg_roll_sou_thread_entry (void *data) rt_uint16_t single_reg = 0; rt_uint16_t single_value = 0; rt_bool_t command_valid = RT_FALSE; + rt_bool_t measure_once = RT_FALSE; + rt_uint8_t response_part = MB_R_PART1; rt_err_t ret = RT_EOK; struct chrg_rollsou_t *pROLL = &chrgrollsou; struct chrg_thread_t *pTS = &chrgthr[IDX_THR_SOUTH]; @@ -559,13 +716,29 @@ void chrg_roll_sou_thread_entry (void *data) single_value = pROLL->value; rt_mutex_release(pTHR->mutex); command_valid = RT_FALSE; + measure_once = RT_FALSE; + response_part = MB_R_PART1; if ((run != RUN_COM) && (run != RUN_UPDATA)) { retry_count = 0; } // rt_kprintf("------ROLL SOU START \n--------"); switch (run) { case RUN_AUTO: { + int measure_ret = sou_measure_once_next(&idx); + if (measure_ret > 0) { + /* 卸载确认优先读取当前通道P1,不等待四通道普通轮询走完。 */ + measure_once = RT_TRUE; + response_part = MB_R_PART1; + chrg_led_flashing(IDX_LED3, TIMES_ONE, PULSE_TIME*2, PULSE_TIME*2); + if (idx != (rt_uint8_t)pROLL->idx) { + pROLL->idx = (eIDX_SOU_CH)idx; + chrg_south_switch((eIDX_SOU_CH)idx); + } + chrg_eload_refresh(ID_ELOAD, MB_R_PART1); + break; + } idx = index%(TOTAL_SOU_CHS); // [0, 3] + response_part = pROLL->part; if (0 == chrgsouth.enable[idx]) { // 通道未启用 index++; rt_thread_mdelay(10); @@ -666,7 +839,7 @@ void chrg_roll_sou_thread_entry (void *data) } else if (run != RUN_AUTO) { sou_single_command_finish(run); } else { - chrg_eload_parse(idx, pROLL->part, pMB_R->payload, pMB_R->length); + chrg_eload_parse(idx, response_part, pMB_R->payload, pMB_R->length); } } else if (run == RUN_COM) { sou_com_command_failed(batch_ch, command_reg, command_value, @@ -674,10 +847,16 @@ void chrg_roll_sou_thread_entry (void *data) } else { LOG_E("crc failed"); } + if ((run == RUN_AUTO) && measure_once) { + sou_measure_once_finish(idx); + } sou_msg_free(&pMB_R); } else { sou_msg_free(&pMB_R); + if ((run == RUN_AUTO) && measure_once) { + sou_measure_once_finish(idx); + } if ((run == RUN_COM) && command_valid) { sou_com_command_failed(batch_ch, command_reg, command_value, "timeout", &retry_count); diff --git a/apps/chrg/applications/thread/chrg_roll_sou.h b/apps/chrg/applications/thread/chrg_roll_sou.h index a009f2a..4287cf5 100644 --- a/apps/chrg/applications/thread/chrg_roll_sou.h +++ b/apps/chrg/applications/thread/chrg_roll_sou.h @@ -26,6 +26,8 @@ typedef struct rt_uint16_t val_list[SOU_COM_BATCH_REG_MAX]; // 对应寄存器值数组 rt_uint8_t cur_idx; // 当前下发到第几条 rt_bool_t pending; // 本通道有待下发批量指令 + rt_int8_t result; // 0:执行中/无结果 1:成功 -1:失败取消 + rt_tick_t next_send_tick; // 非0时下一条命令不得早于该tick发送 } sou_com_batch_ch_t; // COM批量下发总控结构体 @@ -97,6 +99,25 @@ extern int chrg_send_sou_upadata_data(const rt_uint8_t *buf, rt_uint16_t leng); extern int chrg_sou_com_batch_add_reg(eIDX_SOU_CH ch, rt_uint16_t reg, rt_uint16_t val); extern int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs, const rt_uint16_t *vals, rt_uint8_t count); +extern rt_bool_t chrg_sou_com_batch_is_pending(eIDX_SOU_CH ch); +/***************************************************************** +函数名称: chrg_sou_com_batch_get_result +函数描述: 获取指定通道最近一次南向COM批次的执行结果 +输入参数: ch:南向功率板通道 +输出参数: - +返回说明: 1:全部命令收到匹配回码 0:执行中/无结果 -1:失败取消 -2:参数或锁错误 +其它说明: 用于区分pending清零是正常完成还是重试耗尽后的失败取消 +*****************************************************************/ +extern int chrg_sou_com_batch_get_result(eIDX_SOU_CH ch); +/***************************************************************** +函数名称: chrg_sou_measure_once_submit +函数描述: 请求南向线程优先查询指定通道的一帧P1量测数据 +输入参数: ch:南向功率板通道 +输出参数: - +返回说明: 0:请求成功或已存在同通道请求 <0:参数或互斥锁错误 +其它说明: 只登记一次性请求,实际切通道、发送0x03和解析仍由南向线程完成 +*****************************************************************/ +extern int chrg_sou_measure_once_submit(eIDX_SOU_CH ch); /***************************************************************** 函数名称: chrg_sou_trim_submit 函数描述: 根据校准类型组装功率板校准指令并提交到南向COM队列 diff --git a/apps/chrg/applications/utils/chrg_eload.c b/apps/chrg/applications/utils/chrg_eload.c index 35274da..227f01e 100644 --- a/apps/chrg/applications/utils/chrg_eload.c +++ b/apps/chrg/applications/utils/chrg_eload.c @@ -22,6 +22,25 @@ #define ELOAD_VALID_VOLTAGE_MIN_MV 2000U extern void LCD_SHOW(rt_uint8_t idx, struct eload_p1_t *pP1, struct chrg_eload_t *pLOAD, struct chrg_switch_t *pSW, rt_uint8_t online); + +/* 每通道P1量测更新序号;32位读写在STM32F405上为原子访问。 */ +static volatile rt_uint32_t g_eload_sample_seq[TOTAL_SOU_CHS] = {0}; + +/***************************************************************** +函数名称: chrg_eload_get_sample_seq +函数描述: 获取指定南向通道P1量测数据的更新序号 +输入参数: idx:南向功率板通道 +输出参数: - +返回说明: 当前通道量测序号,通道非法时返回0 +其它说明: 卸载确认记录命令下发前序号,只接受序号变化后的新量测 +*****************************************************************/ +rt_uint32_t chrg_eload_get_sample_seq(rt_uint8_t idx) +{ + if (idx >= TOTAL_SOU_CHS) { + return 0; + } + return g_eload_sample_seq[idx]; +} static rt_uint16_t ripple_base[TOTAL_SOU_CHS] = {0}; static rt_uint8_t ripple_cal_flag[TOTAL_SOU_CHS] = {0}; rt_uint16_t real_ripple[TOTAL_SOU_CHS] = {0}; @@ -332,6 +351,8 @@ int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16 } pSW->Now_voltage = pLOAD->voltage; pSW->Now_current = pLOAD->current; + /* 电压、电流均更新完成后再发布序号,避免状态机读取到半更新数据。 */ + g_eload_sample_seq[idx]++; #if LCD_OPEN // for(int i = 0;i<4;i++){ // if(pNOR->sw[i].change_flag == 0x01){ diff --git a/apps/chrg/applications/utils/chrg_eload.h b/apps/chrg/applications/utils/chrg_eload.h index ceaa267..10b92e0 100644 --- a/apps/chrg/applications/utils/chrg_eload.h +++ b/apps/chrg/applications/utils/chrg_eload.h @@ -268,6 +268,15 @@ extern int chrg_eload_refresh (rt_uint8_t addr, rt_uint8_t part); 其它说明: - *****************************************************************/ extern int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16_t len); +/***************************************************************** +函数名称: chrg_eload_get_sample_seq +函数描述: 获取指定南向通道P1量测数据的更新序号 +输入参数: idx:南向功率板通道 +输出参数: - +返回说明: 当前通道量测序号,通道非法时返回0 +其它说明: 每成功解析一帧P1数据后递增,用于区分控制命令前后的新旧量测 +*****************************************************************/ +extern rt_uint32_t chrg_eload_get_sample_seq(rt_uint8_t idx); // 定义2条继电器指令,控制主继电器和滤波继电器(保留原有有效指令,移除未使用的停止指令) static const rt_uint8_t EXTRA_CMD_SOURCE[8] = {0x03, 0x06, 0x00, 0x01, 0x00, 0x01, 0x18, 0x28}; extern int chrg_eload_send_relay(rt_uint8_t cmd_type); diff --git a/apps/chrg/applications/utils/chrg_sink.c b/apps/chrg/applications/utils/chrg_sink.c index ed6b93a..fb9736a 100644 --- a/apps/chrg/applications/utils/chrg_sink.c +++ b/apps/chrg/applications/utils/chrg_sink.c @@ -18,6 +18,64 @@ #include "chrg_def.h" #include "chrg_rel.h" #include "chrg_comm.h" +#include "chrg_eload.h" + +#define SINK_UNLOAD_QUERY_INTERVAL_MS 20U // 卸载期间两次优先量测请求的最小间隔 +#define SINK_UNLOAD_CURRENT_MAX_MA 100U // 判定负载已安全卸载的最大实际电流 +#define SINK_UNLOAD_CONFIRM_COUNT 2U // 连续满足低电流条件的量测帧数 + +/* 卸载确认只接受停止批次成功后产生的新P1量测。 */ +static rt_uint32_t g_sink_unload_sample_seq[TOTAL_SOU_CHS] = {0}; +static rt_bool_t g_sink_unload_batch_confirmed[TOTAL_SOU_CHS] = {RT_FALSE}; +static rt_tick_t g_sink_unload_query_tick[TOTAL_SOU_CHS] = {0}; +static rt_uint8_t g_sink_unload_confirm_count[TOTAL_SOU_CHS] = {0}; + +/***************************************************************** +函数名称: chrg_sink_unload_confirm_reset +函数描述: 清除指定通道的卸载批次和低电流确认上下文 +输入参数: idx:南向功率板通道 +输出参数: 清零量测序号、批次确认标志、查询tick和连续确认次数 +返回说明: - +其它说明: 每次开始软卸载或完整重握手前调用,禁止沿用旧量测结果 +*****************************************************************/ +static void chrg_sink_unload_confirm_reset(eIDX_SOU_CH idx) +{ + if (idx >= TOTAL_SOU_CHS) { + return; + } + g_sink_unload_sample_seq[idx] = 0U; + g_sink_unload_batch_confirmed[idx] = RT_FALSE; + g_sink_unload_query_tick[idx] = 0U; + g_sink_unload_confirm_count[idx] = 0U; +} + +/***************************************************************** +函数名称: chrg_sink_unload_query_submit +函数描述: 按最小间隔提交当前通道的一次性P1量测请求 +输入参数: idx:南向功率板通道 force:RT_TRUE立即提交 RT_FALSE按间隔提交 +输出参数: 更新本通道最近一次查询tick +返回说明: 0:请求成功、已存在或尚未到查询时间 <0:请求提交失败 +其它说明: 电流长期不归零时保留普通轮询窗口,避免持续优先查询阻塞其它通道 +*****************************************************************/ +static int chrg_sink_unload_query_submit(eIDX_SOU_CH idx, rt_bool_t force) +{ + rt_tick_t now; + + if (idx >= TOTAL_SOU_CHS) { + return -1; + } + now = rt_tick_get(); + if (!force && + ((rt_tick_t)(now - g_sink_unload_query_tick[idx]) < + rt_tick_from_millisecond(SINK_UNLOAD_QUERY_INTERVAL_MS))) { + return 0; + } + if (chrg_sou_measure_once_submit(idx) != 0) { + return -1; + } + g_sink_unload_query_tick[idx] = now; + return 0; +} /***************************************************************** 函数名称: sink_get_vc 函数描述: sink电压电流查询请求 @@ -130,45 +188,85 @@ int sink_set_pd (rt_uint8_t pd, rt_uint8_t *buf) /***************************************************************** 函数名称: chrg_sink_work_init -函数描述: 执行负载INIT状态,复位测试配置并初始化功率板 +函数描述: 执行负载INIT入口,第一条南向命令只打开副继电器 输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体 -输出参数: 设置运行、使能、在线标志并更新work_mode -返回说明: 0:初始化成功 <0:命令提交失败 -其它说明: 成功后打开负载北向继电器并进入SINK_WORK_WAIT +输出参数: 禁止北向请求并进入SINK_WORK_INIT_AUX_WAIT +返回说明: 0:副继电器命令提交成功 <0:参数或命令提交失败 +其它说明: FU_REL_ON同时保证主继电器关闭;收到有效回码前不得启动北向握手 *****************************************************************/ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) +{ + struct chrg_north_t *pNOR = &chrgnorth; + rt_uint16_t reg = REG_REL_ON; + rt_uint16_t value = FU_REL_ON; + + if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { + return -1; + } + if (chrg_sou_com_batch_submit(idx, ®, &value, 1U) != 0) { + return -1; + } + + /* 北向软件使能在副继电器回码后恢复,避免协议命令先于电压通路。 */ + pSW->sink.On_work = 1; + pNOR->enable[idx] = 0; + pSW->sink.work_mode = SINK_WORK_INIT_AUX_WAIT; + return 0; +} + +/***************************************************************** +函数名称: chrg_sink_work_init_aux_wait +函数描述: 等待启动副继电器回码,并异步提交其余功率板初始化命令 +输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体 +输出参数: 打开北向Sink继电器、恢复北向使能并进入SINK_WORK_WAIT +返回说明: 0:继续等待或初始化命令已入队 <0:参数或命令提交失败 +其它说明: 其余初始化命令只要求成功入队,不等待执行完成;北向握手和 + 南向初始化可并行,后续RUNING命令追加到同一队列并保持顺序 +*****************************************************************/ +static int chrg_sink_work_init_aux_wait(eIDX_SOU_CH idx, + struct chrg_switch_t *pSW) { struct chrg_south_t *pSOU = &chrgsouth; struct chrg_north_t *pNOR = &chrgnorth; - rt_uint16_t regs[5] = {REG_CURRENT_OUT + 1, REG_MODE, - Eload_Start_V_ON, 0, 0}; - rt_uint16_t vals[5] = {0, pSW->CV_mode, pSW->sink.volt_on, - 0, 0}; - rt_uint8_t count = 3; + rt_uint16_t regs[4] = {REG_CURRENT_OUT + 1, REG_MODE, + Eload_Start_V_ON, 0}; + rt_uint16_t vals[4] = {0}; + rt_uint8_t count = 3U; + int batch_result; + + if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { + return -1; + } + vals[1] = pSW->CV_mode; + vals[2] = pSW->sink.volt_on; + if (chrg_sou_com_batch_is_pending(idx)) { + return 0; + } + batch_result = chrg_sou_com_batch_get_result(idx); + if (batch_result <= 0) { + /* 副继电器无有效回码时重新执行INIT,北向继续保持禁止。 */ + return chrg_sink_work_init(idx, pSW); + } if (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) { regs[count] = ModbusRTU_ShortTest_CMD_MODE_FLAG; vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP; } else if (pSW->sink.test.test_mode_old == SINK_TEST_OCP) { - /* 从OCP测试返回普通负载时,先停止功率板的OCP测试状态机。 */ regs[count] = ModbusRTU_OCPTest_CMD_MODE_FLAG; vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP; } else if (pSW->sink.test.test_mode_old == SINK_TEST_DYNA) { regs[count] = ModbusRTU_DYNA_CMD_MODE_FLAG; vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP; } - /* Close both power-board relays before starting sink init. */ - regs[count] = REG_REL_ON; - vals[count++] = ALL_REL_OFF; if (chrg_sou_com_batch_submit(idx, regs, vals, count) != 0) { return -1; } - pSW->sink.On_work = 1; + /* 初始化命令已按顺序入队,此时即可给协议板接通北向Sink路径。 */ pSOU->online[idx] = 1; pNOR->enable[idx] = 1; - - chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SINK); //打开北向继电器 + chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SINK); + pSW->sub = IDX_SET_SINK_PD; pSW->sink.test.test_mode_old = 0; pSW->sink.work_mode = SINK_WORK_WAIT; return 0; @@ -180,7 +278,8 @@ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) 输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体 输出参数: - 返回说明: 0:命令提交成功 非0:命令提交失败 -其它说明: CV模式下写负载电压,其他模式下写负载电流 +其它说明: CV模式下写负载电压,其他模式下写负载电流;副继电器0x02 + 收到有效回码后直接发送全部继电器0x03,当前不增加固定延时 *****************************************************************/ static int chrg_sink_work_volt_runing(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) { @@ -197,20 +296,93 @@ static int chrg_sink_work_volt_runing(eIDX_SOU_CH idx, struct chrg_switch_t *pSW return chrg_sou_com_batch_submit(idx, regs, vals, 4); } +/***************************************************************** +函数名称: chrg_sink_work_soft_resume +函数描述: 仅协议电压变化完成后恢复负载,不操作南向主副继电器 +输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体 +输出参数: 命令入队成功后将工作状态切换为SINK_WORK_SOFT_RUNNING +返回说明: 0:命令提交成功 <0:参数或命令提交失败 +其它说明: 命令顺序为目标电流/电压、WORK_START,保证软卸载期间继电器 + 始终保持原状态,避免恢复阶段因FU_REL_ON造成主继电器短暂断开 +*****************************************************************/ +static int chrg_sink_work_soft_resume(eIDX_SOU_CH idx, + struct chrg_switch_t *pSW) +{ + rt_uint16_t regs[2] = {0, REG_WORK}; + rt_uint16_t vals[2] = {0, WORK_START}; + + if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { + return -1; + } + if (pSW->CV_mode == MODE_CONSTANT_VOLTAGE) { + regs[0] = REG_VOLTAGE_OUT + 1; + vals[0] = pSW->sink.loadv; + } else { + regs[0] = REG_CURRENT_OUT + 1; + vals[0] = pSW->sink.loadc; + } + if (chrg_sou_com_batch_submit(idx, regs, vals, 2U) != 0) { + return -1; + } + pSW->sink.work_mode = SINK_WORK_SOFT_RUNNING; + return 0; +} + /***************************************************************** 函数名称: chrg_sink_nor_work_low -函数描述: 执行负载低电压状态,断开功率继电器并停止负载 +函数描述: 执行负载掉电处理,卸载后关闭主副继电器并重新打开副继电器 输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体 输出参数: - 返回说明: 0:命令提交成功 非0:命令提交失败 -其它说明: 由北向负载解析连续检测到低电压后触发 +其它说明: 命令顺序为WORK_STOP、CURRENT=0、ALL_REL_OFF、FU_REL_ON; + ALL_REL_OFF后立即恢复副继电器,为协议板重新得电保留通路 *****************************************************************/ static int chrg_sink_nor_work_low(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) { - rt_uint16_t regs[2] = {0x01, REG_WORK}; - rt_uint16_t vals[2] = {0x00, WORK_STOP}; + rt_uint16_t regs[4] = { + REG_WORK, REG_CURRENT_OUT + 1, REG_REL_ON, REG_REL_ON + }; + rt_uint16_t vals[4] = { + WORK_STOP, 0, ALL_REL_OFF, FU_REL_ON + }; - return chrg_sou_com_batch_submit(idx, regs, vals, 2); + if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { + return -1; + } + if (chrg_sou_com_batch_submit(idx, regs, vals, 4U) != 0) { + return -1; + } + pSW->sink.work_mode = SINK_WORK_LOW_AUX_WAIT; + return 0; +} + +/***************************************************************** +函数名称: chrg_sink_work_low_aux_wait +函数描述: 等待掉电安全批次和副继电器恢复命令全部收到有效回码 +输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体 +输出参数: 成功后进入SINK_WORK_LOW_MONITOR并恢复北向电压查询入口 +返回说明: 0:继续等待或状态转换成功 <0:参数错误 +其它说明: 批次失败时重新执行完整掉电处理,不允许跳过副继电器确认 +*****************************************************************/ +static int chrg_sink_work_low_aux_wait(eIDX_SOU_CH idx, + struct chrg_switch_t *pSW) +{ + int batch_result; + + if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { + return -1; + } + if (chrg_sou_com_batch_is_pending(idx)) { + return 0; + } + batch_result = chrg_sou_com_batch_get_result(idx); + if (batch_result <= 0) { + return chrg_sink_nor_work_low(idx, pSW); + } + + pSW->sub = IDX_GET_SINK_VC; + pSW->sink.work_mode = SINK_WORK_LOW_MONITOR; + return 0; } /***************************************************************** @@ -270,8 +442,8 @@ static int chrg_sink_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) /* 停止成功入队后,清除旧Source运行标志。 */ pSW->source.On_Flag = 0; - /* 首次进入Sink时必须从CC线配置开始,不能沿用Source的sub状态。 */ - pSW->sub = IDX_SET_SINK_CCLINE; + /* 首次进入Sink时从协议配置开始,协议应答后再设置目标CC线。 */ + pSW->sub = IDX_SET_SINK_PD; /* SWITCH只负责旧角色退出,后续启动统一交给Sink INIT状态。 */ pSW->sink.work_mode = SINK_WORK_INIT; return 0; @@ -279,26 +451,125 @@ static int chrg_sink_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) /***************************************************************** 函数名称: chrg_sink_work_protocol_unload -函数描述: 协议或CC线变化前的卸载状态:停止负载、电流归零、断开功率继电器 +函数描述: 仅协议电压变化前的软卸载:停止负载并将设定电流归零 输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体 输出参数: - 返回说明: 0:命令提交成功 <0:命令提交失败 -其它说明: 不操作北向继电器,仅卸载功率板侧;后续由WAIT→北向线程重配 - CC/协议,再由RUNING状态闭合继电器并恢复目标负载电流 +其它说明: 不操作北向和南向功率继电器;两条写命令完成后进入UNLOAD_WAIT, + 连续确认两帧实际电流不大于100mA,才允许北向重配协议并使用软恢复流程 *****************************************************************/ static int chrg_sink_work_protocol_unload(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) { - rt_uint16_t regs[3] = {REG_WORK, REG_CURRENT_OUT + 1, REG_REL_ON}; - rt_uint16_t vals[3] = {WORK_STOP, 0, ALL_REL_OFF}; + rt_uint16_t regs[2] = {REG_WORK, REG_CURRENT_OUT + 1}; + rt_uint16_t vals[2] = {WORK_STOP, 0}; - if (chrg_sou_com_batch_submit(idx, regs, vals, 3) != 0) { + if (chrg_sou_com_batch_submit(idx, regs, vals, 2) != 0) { return -1; } - pSW->sink.work_mode = SINK_WORK_WAIT; + /* 写回码只确认命令接收;批次完成后还需连续读取两帧合格的实际电流。 */ + chrg_sink_unload_confirm_reset(idx); + pSW->sink.work_mode = SINK_WORK_UNLOAD_WAIT; return 0; } +/***************************************************************** +函数名称: chrg_sink_work_handshake_prepare +函数描述: 执行协议或CC完整重握手前的南向安全断开和副继电器恢复 +输入参数: idx:南向功率板通道 pSW:当前通道控制结构体 +输出参数: 命令提交成功后进入SINK_WORK_HANDSHAKE_WAIT +返回说明: 0:四条命令提交成功 <0:参数或命令提交失败 +其它说明: 命令顺序固定为WORK_STOP、CURRENT=0、ALL_REL_OFF、FU_REL_ON; + 主副继电器全部断开并确认回码后再单独打开副继电器; + 北向Sink继电器保持接通,低电流确认完成后才允许重新握手 +*****************************************************************/ +static int chrg_sink_work_handshake_prepare(eIDX_SOU_CH idx, + struct chrg_switch_t *pSW) +{ + rt_uint16_t regs[4] = { + REG_WORK, REG_CURRENT_OUT + 1, REG_REL_ON, REG_REL_ON + }; + rt_uint16_t vals[4] = { + WORK_STOP, 0, ALL_REL_OFF, FU_REL_ON + }; + + if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { + return -1; + } + if (chrg_sou_com_batch_submit(idx, regs, vals, 4) != 0) { + return -1; + } + + /* 四条写回码完成后,还必须连续确认两帧实际电流不大于100mA。 */ + chrg_sink_unload_confirm_reset(idx); + pSW->sink.work_mode = SINK_WORK_HANDSHAKE_WAIT; + return 0; +} + +/***************************************************************** +函数名称: chrg_sink_work_current_wait +函数描述: 等待负载安全断开批次完成,并连续确认两帧实际电流不大于100mA +输入参数: idx:南向功率板通道 pSW:当前通道控制结构体 + full_handshake:RT_TRUE为完整重握手 RT_FALSE为仅协议电压软卸载 +输出参数: 确认成功后将sink.work_mode切换为SINK_WORK_WAIT +返回说明: 0:继续等待或确认成功 <0:状态查询或优先量测请求失败 +其它说明: 只有WORK_STOP和CURRENT=0均收到匹配回码后才建立量测基线; + 完整重握手还要求ALL_REL_OFF和FU_REL_ON回码成功,但保持北向Sink继电器接通 +*****************************************************************/ +static int chrg_sink_work_current_wait(eIDX_SOU_CH idx, + struct chrg_switch_t *pSW, rt_bool_t full_handshake) +{ + rt_uint32_t sample_seq; + int batch_result; + + if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { + return -1; + } + if (chrg_sou_com_batch_is_pending(idx)) { + return 0; + } + + batch_result = chrg_sou_com_batch_get_result(idx); + if (batch_result < 0) { + /* 写命令失败时重新执行完整卸载批次,禁止使用量测值提前放行。 */ + return full_handshake ? chrg_sink_work_handshake_prepare(idx, pSW) : + chrg_sink_work_protocol_unload(idx, pSW); + } + if (batch_result == 0) { + /* 无有效批次结果时保持阻塞,并重新提交完整卸载动作。 */ + return full_handshake ? chrg_sink_work_handshake_prepare(idx, pSW) : + chrg_sink_work_protocol_unload(idx, pSW); + } + + if (!g_sink_unload_batch_confirmed[idx]) { + /* 从最后一条写回码之后建立基线,排除RUN_COM之前残留的旧量测。 */ + g_sink_unload_sample_seq[idx] = chrg_eload_get_sample_seq(idx); + g_sink_unload_batch_confirmed[idx] = RT_TRUE; + return chrg_sink_unload_query_submit(idx, RT_TRUE); + } + + sample_seq = chrg_eload_get_sample_seq(idx); + if (sample_seq != g_sink_unload_sample_seq[idx]) { + g_sink_unload_sample_seq[idx] = sample_seq; + if (pSW->Now_current <= SINK_UNLOAD_CURRENT_MAX_MA) { + g_sink_unload_confirm_count[idx]++; + if (g_sink_unload_confirm_count[idx] >= SINK_UNLOAD_CONFIRM_COUNT) { + g_sink_unload_batch_confirmed[idx] = RT_FALSE; + g_sink_unload_confirm_count[idx] = 0U; + pSW->sink.work_mode = full_handshake ? + SINK_WORK_WAIT : SINK_WORK_SOFT_RESUME_WAIT; + return 0; + } + } else { + /* 必须是连续两帧合格量测;中间出现带载电流时重新计数。 */ + g_sink_unload_confirm_count[idx] = 0U; + } + } + + /* 当前帧仍有电流或查询未返回时,请求下一帧;接口按位登记,不会重复排队。 */ + return chrg_sink_unload_query_submit(idx, RT_FALSE); +} + /***************************************************************** 函数名称: chrg_sink_TEST_DYNA_ON 函数描述: 配置并启动动态负载测试 @@ -399,14 +670,21 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) return -1; } work_mode = pSW->sink.work_mode; - // 本次状态和上次一致,直接跳过,不重复下发寄存器 - if (work_mode == last_state[idx]) { + /* 异步等待状态需要持续检查批次或新量测,其余相同状态保持去重。 */ + if ((work_mode == last_state[idx]) && + (work_mode != SINK_WORK_INIT_AUX_WAIT) && + (work_mode != SINK_WORK_UNLOAD_WAIT) && + (work_mode != SINK_WORK_LOW_AUX_WAIT) && + (work_mode != SINK_WORK_HANDSHAKE_WAIT)) { return 0; } switch(work_mode){ case SINK_WORK_INIT: ret = chrg_sink_work_init(idx,pSW); break; + case SINK_WORK_INIT_AUX_WAIT: + ret = chrg_sink_work_init_aux_wait(idx, pSW); + break; case SINK_WORK_WAIT: ret = 0; break; @@ -425,6 +703,12 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) case SINK_WORK_LOW: ret = chrg_sink_nor_work_low(idx,pSW); break; + case SINK_WORK_LOW_AUX_WAIT: + ret = chrg_sink_work_low_aux_wait(idx, pSW); + break; + case SINK_WORK_LOW_MONITOR: + ret = 0; + break; case SINK_WORK_STOP: ret = chrg_sink_nor_work_off(idx,pSW); break; @@ -434,6 +718,24 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) case SINK_WORK_PROTOCOL_UNLOAD: ret = chrg_sink_work_protocol_unload(idx, pSW); break; + case SINK_WORK_UNLOAD_WAIT: + ret = chrg_sink_work_current_wait(idx, pSW, RT_FALSE); + break; + case SINK_WORK_SOFT_RESUME_WAIT: + ret = 0; + break; + case SINK_WORK_SOFT_RESUME: + ret = chrg_sink_work_soft_resume(idx, pSW); + break; + case SINK_WORK_SOFT_RUNNING: + ret = 0; + break; + case SINK_WORK_HANDSHAKE_PREPARE: + ret = chrg_sink_work_handshake_prepare(idx, pSW); + break; + case SINK_WORK_HANDSHAKE_WAIT: + ret = chrg_sink_work_current_wait(idx, pSW, RT_TRUE); + break; default: break; } diff --git a/apps/chrg/applications/utils/chrg_sink.h b/apps/chrg/applications/utils/chrg_sink.h index 56a8eec..89d75f4 100644 --- a/apps/chrg/applications/utils/chrg_sink.h +++ b/apps/chrg/applications/utils/chrg_sink.h @@ -202,17 +202,26 @@ enum { #define SINK_CMD_SET_AVS 0xE3 // AVS enum { - SINK_WORK_INIT =0, - SINK_WORK_WAIT, - SINK_WORK_VOLT_OK, - SINK_WORK_RUNING, - SINK_WORK_LOW, - SINK_WORK_STOP, - SINK_WORK_SWITCH, // 停止旧电源后进入负载INIT - SINK_WORK_PROTOCOL_UNLOAD, // 协议/CC变化前卸载电流、停止负载、断开功率继电器 - SINK_WORK_ON_TEST_DYNA, - SINK_WORK_ON_TEST_SHORT, - SINK_WORK_ON_TEST_OCP, // 配置并启动OCP过流保护测试 + SINK_WORK_INIT = 0, // 启动入口:第一条南向命令只打开副继电器 + SINK_WORK_INIT_AUX_WAIT, // 等待启动副继电器收到有效南向回码 + SINK_WORK_WAIT, // 北向协议设置或电压查询等待状态 + SINK_WORK_VOLT_OK, // 预留的电压达标状态 + SINK_WORK_RUNING, // 电压稳定后提交继电器、电流和启动命令 + SINK_WORK_LOW, // 运行电压过低,执行掉电安全处理 + SINK_WORK_LOW_AUX_WAIT, // 等待掉电处理及副继电器恢复批次完成 + SINK_WORK_LOW_MONITOR, // 副继电器已恢复,等待输入电压重新出现 + SINK_WORK_STOP, // 用户主动停止负载 + SINK_WORK_SWITCH, // 停止旧电源后进入负载INIT + SINK_WORK_PROTOCOL_UNLOAD, // 仅协议电压变化:掉载但不切换继电器 + SINK_WORK_UNLOAD_WAIT, // 等待软卸载完成并连续两次确认电流不大于100mA + SINK_WORK_SOFT_RESUME_WAIT, // 继电器保持闭合,等待协议电压稳定后恢复负载 + SINK_WORK_SOFT_RESUME, // 只恢复目标电流/电压和WORK_START,不写继电器 + SINK_WORK_SOFT_RUNNING, // 软恢复后的稳定运行状态,禁止重复切换继电器 + SINK_WORK_ON_TEST_DYNA, // 动态负载测试启动状态 + SINK_WORK_ON_TEST_SHORT, // 短路测试启动状态 + SINK_WORK_ON_TEST_OCP, // OCP过流保护测试启动状态 + SINK_WORK_HANDSHAKE_PREPARE, // 完整重握手:停止、清流、全关继电器后打开副继电器 + SINK_WORK_HANDSHAKE_WAIT, // 等待完整重握手批次及连续两次低电流确认 }; // sink 参数结构 diff --git a/apps/chrg/codex_build.log b/apps/chrg/codex_build.log index 12a827a..405751e 100644 --- a/apps/chrg/codex_build.log +++ b/apps/chrg/codex_build.log @@ -1,8 +1,20 @@ *** Using Compiler 'V6.16', folder: 'D:\keil mdk 2\ARM\ARMCLANG\Bin' Build target 'chrg' +compiling chrg_north_pkg.c... +compiling chrg_lcd.c... +compiling chrg_thread.c... +compiling chrg_sink.c... +compiling chrg_vcom.c... +compiling chrg_rel.c... compiling chrg_eload.c... +compiling chrg_south.c... +compiling chrg_roll_sou.c... +compiling chrg_north.c... +compiling chrg_source.c... +compiling chrg_roll_nor.c... +compiling chrg_comm.c... linking... -Program Size: Code=144648 RO-data=15288 RW-data=9328 ZI-data=15824 +Program Size: Code=145588 RO-data=15276 RW-data=9328 ZI-data=15944 FromELF: creating hex file... After Build - User command #1: fromelf --bin .\build\keil\Obj\chrg.axf -o build/chrg.bin ".\build\keil\Obj\chrg.axf" - 0 Error(s), 0 Warning(s). diff --git a/docs/修改日志.md b/docs/修改日志.md new file mode 100644 index 0000000..cc03c3c --- /dev/null +++ b/docs/修改日志.md @@ -0,0 +1,47 @@ +# 修改日志 + +## V1.1.0 - 2026-08-07 + +### 修改主题 + +负载启动、掉电恢复、协议重握手和协议电压软切换时序调整。 + +### 主要修改 + +- 负载启动时先向南向发送 `FU_REL_ON`,收到有效回码后才打开北向 Sink 继电器。 +- 剩余的负载初始化命令在副继电器确认后入队,北向协议握手与南向初始化并行执行。 +- `RUNING` 阶段的南向命令继续追加到同一队列,保持初始化配置、继电器、目标电流/电压和 `WORK_START` 的顺序。 +- 掉电恢复时执行 `WORK_STOP -> CURRENT=0 -> ALL_REL_OFF -> FU_REL_ON`,副继电器恢复后等待输入电压重新出现。 +- 掉电恢复检测到电压后重新设置协议和 CC,不能使用掉电前的电压样本直接进入 `RUNING`。 +- 协议类型、CC、协议组号或协议电流变化进入完整重握手,关闭主副继电器后重新打开副继电器。 +- 仅协议电压变化使用软卸载,只发送 `WORK_STOP` 和 `CURRENT=0`,不切换继电器。 +- 软卸载恢复时只发送目标电流/电压和 `WORK_START`,不再发送继电器命令。 +- 保留连续两次实际电流不大于 `100mA` 的安全确认,以及连续两次稳定电压确认。 +- 暂时保持南向主副继电器之间的固定延时为屏蔽状态。 + +### 涉及文件 + +- `apps/chrg/applications/utils/chrg_sink.h` +- `apps/chrg/applications/utils/chrg_sink.c` +- `apps/chrg/applications/thread/chrg_comm.c` +- `apps/chrg/applications/thread/chrg_roll_nor.c` +- `apps/chrg/applications/thread/chrg_north.c` + +### 编译验证 + +- Keil MDK 编译结果:`0 Error(s), 0 Warning(s)`。 +- 尚未完成硬件验证,需重点观察南向命令顺序、北向协议握手时刻、掉电恢复和协议电压软切换。 + +### 后续记录格式 + +后续每次修改按以下格式追加: + +```text +## Vx.y.z - YYYY-MM-DD + +### 修改主题 +### 主要修改 +### 涉及文件 +### 编译验证 +### 硬件验证 +```