diff --git a/apps/chrg/applications/thread/chrg_comm.c b/apps/chrg/applications/thread/chrg_comm.c index 9d08969..4796e4f 100644 --- a/apps/chrg/applications/thread/chrg_comm.c +++ b/apps/chrg/applications/thread/chrg_comm.c @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_comm.c 完成日期 : -当前版本号 : V1.1.10 +当前版本号 : V1.1.15 主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。 版本历史 : 创建原始版本 说明 : @@ -27,7 +27,7 @@ #include "stm32f4xx_hal.h" #define HOLD_REG_FIRMWARE_VERSION 0x0300U /* 协议板固件版本,只读 */ -#define CHRG_FIRMWARE_VERSION 0x011AU /* 当前协议板版本:V1.1.10 */ +#define CHRG_FIRMWARE_VERSION 0x011FU /* 当前协议板版本:V1.1.15 */ rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR]; rt_uint8_t ucCoilBuf[(TOTAL_COIL_REGS+BITS_UCHAR-1)/BITS_UCHAR]; @@ -618,7 +618,7 @@ eMBException funcReadHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen, /* * 协议板版本号使用独立的只读地址0x0300,不占用现有通道保持寄存器空间。 - * 当前版本V1.1.10编码为0x011A,Modbus响应按高字节在前返回01 1A。 + * 当前版本V1.1.15编码为0x011F,Modbus响应按高字节在前返回01 1F。 * 该特殊地址只支持一次读取一个寄存器,避免与普通连续寄存器混用。 */ if (regAddress == HOLD_REG_FIRMWARE_VERSION) { @@ -2563,6 +2563,7 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch, rt_bool_t protocol_voltage_only; rt_bool_t protocol_current_change; rt_bool_t protocol_data_changed; + rt_bool_t von_changed; rt_uint8_t curr_test; rt_uint8_t curr_test_old; rt_uint8_t old_cv_mode = pReal->CV_mode; @@ -2587,6 +2588,17 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch, !full_handshake_change; protocol_voltage_only = diff_info.pro_voltage_changed && !full_handshake_change && !protocol_current_change; + von_changed = (pReal->sink.volt_on != pShadow->sink.volt_on); + if ((sink_work == 0x01) && (old_on_work == 1U) && von_changed) { + rt_uint16_t reg = Eload_Start_V_ON; + rt_uint16_t val = (pShadow->sink.volt_on <= 500U) ? + 500U : pShadow->sink.volt_on; + + if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, ®, + &val, 1U) != 0) { + return MB_EX_SLAVE_BUSY; + } + } rt_memcpy(pReal, pShadow, sizeof(struct chrg_switch_t)); if (sink_work == 0x01) { diff --git a/apps/chrg/applications/thread/chrg_comm.h b/apps/chrg/applications/thread/chrg_comm.h index 1f3687d..76e30ec 100644 --- a/apps/chrg/applications/thread/chrg_comm.h +++ b/apps/chrg/applications/thread/chrg_comm.h @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_comm.h 完成日期 : -当前版本号 : V1.1.10 +当前版本号 : V1.1.15 主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。 版本历史 : 创建原始版本 说明 : diff --git a/apps/chrg/applications/thread/chrg_north.c b/apps/chrg/applications/thread/chrg_north.c index 16108f8..8fe2401 100644 --- a/apps/chrg/applications/thread/chrg_north.c +++ b/apps/chrg/applications/thread/chrg_north.c @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_north.c 完成日期 : -当前版本号 : V1.1.10 +当前版本号 : V1.1.14 主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。 版本历史 : 创建原始版本 说明 : @@ -212,13 +212,13 @@ void chrg_source_setting(rt_uint8_t *frame_data, eIDX_SOU_CH idx, /***************************************************************** 函数名称: chrg_sink_parse -函数描述: 解析北向负载板0x20回码,提取D+/D-电压到VZ/VF +函数描述: 解析北向负载板0x20回码,提取实际电压和D+/D-电压 输入参数: frame_data:完整北向回码 idx:当前通道 pSW:通道控制结构体 pNOR:北向总控结构体 -输出参数: 更新pSW->VZ(D+)、pSW->VF(D-) +输出参数: 更新pSW->Sink_voltage、VZ(D+)、VF(D-) 返回说明: - -其它说明: Sink电压状态机判定已迁移至chrg_sink_volt_judge, - 由北向轮询线程基于eload.c更新的Now_voltage驱动 +其它说明: Sink电压状态机可通过SINK_VON_VOLTAGE_SOURCE选择使用本回码 + 实际电压或功率板实际电压 *****************************************************************/ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx, struct chrg_switch_t *pSW, struct chrg_north_t *pNOR) @@ -227,6 +227,9 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx, return; } + pSW->Sink_voltage = (rt_uint16_t)(((frame_data[4] << 8) | + frame_data[5]) * 10U); + pSW->Sink_voltage_seq++; pSW->VZ = ((frame_data[14] << 8) | frame_data[15]); pSW->VF = ((frame_data[16] << 8) | frame_data[17]); diff --git a/apps/chrg/applications/thread/chrg_north.h b/apps/chrg/applications/thread/chrg_north.h index 24d3c99..a67152e 100644 --- a/apps/chrg/applications/thread/chrg_north.h +++ b/apps/chrg/applications/thread/chrg_north.h @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_north.h 完成日期 : -当前版本号 : V1.0 +当前版本号 : V1.1.14 主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。 版本历史 : 创建原始版本 说明 : @@ -16,6 +16,20 @@ #include "chrg_sink.h" #include "chrg_def.h" +/* V_On启停判定电压源:切换后需重新编译固件。 */ +#define SINK_VON_VOLTAGE_FROM_PROTOCOL 1U +#define SINK_VON_VOLTAGE_FROM_POWER 2U +#ifndef SINK_VON_VOLTAGE_SOURCE +#define SINK_VON_VOLTAGE_SOURCE SINK_VON_VOLTAGE_FROM_POWER +#endif +#if (SINK_VON_VOLTAGE_SOURCE != SINK_VON_VOLTAGE_FROM_PROTOCOL) && \ + (SINK_VON_VOLTAGE_SOURCE != SINK_VON_VOLTAGE_FROM_POWER) +#error "Invalid SINK_VON_VOLTAGE_SOURCE" +#endif + +/* 南向COM批次中单通道连续收到有效回码后的最大发送条数。 */ +#define SOU_COM_BURST_MAX 10U + #define THR_NAME_NORTH "thr.nor" #define DEV_NAME_NORTH "uart3" @@ -23,10 +37,10 @@ #define MIN_FRAME_LENGTH (5) // 帧最短5字节 #define WORK_MIN_VOLT 500 // 工作电压下限,单位mV #define NORTH_FRAME_DEBUG 0x00 // 北向UART原始收发帧 -#define NORTH_SOURCE_DEBUG 0x01 // Source协议解析及组帧 -#define NORTH_SINK_DEBUG 0x01 // Sink协议解析及组帧 +#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校准参数解析 @@ -56,6 +70,8 @@ struct chrg_switch_t { rt_uint8_t change_flag; //正在调整功率标志位 rt_uint16_t Now_voltage; // 当前电压 mV rt_uint16_t Now_current; // 当前电流 mA + rt_uint16_t Sink_voltage; // 协议板0x20回码的实际电压 mV + rt_uint32_t Sink_voltage_seq; // 协议板实际电压有效回码序号 rt_uint16_t VZ; rt_uint16_t VF; }; diff --git a/apps/chrg/applications/thread/chrg_roll_nor.c b/apps/chrg/applications/thread/chrg_roll_nor.c index 7b8a87a..4f60929 100644 --- a/apps/chrg/applications/thread/chrg_roll_nor.c +++ b/apps/chrg/applications/thread/chrg_roll_nor.c @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_roll_nor.c 完成日期 : -当前版本号 : V1.1.10 +当前版本号 : V1.1.14 主要功能 : 实现北向通信轮巡请求命令。以独立线程处理。 版本历史 : 创建原始版本 说明 : @@ -1033,7 +1033,7 @@ void chrg_roll_nor_thread_entry (void *data) source_waiting = chrg_roll_nor_source_wait_process(idx, pSW); sink_waiting = chrg_roll_nor_sink_wait_process(idx, pSW); - /* 全通道Sink电压判定:使用eload实际电压Now_voltage与V_On比较, + /* 全通道Sink电压判定:使用配置的V_On电压源与V_On比较, * 判定后统一由chrg_sink_work执行;短路测试由该入口负责WORK_START保活。 */ for (i = 0; i < TOTAL_NOR_CHS; i++) { struct chrg_switch_t *psw = &pNOR->sw[i]; diff --git a/apps/chrg/applications/thread/chrg_roll_nor.h b/apps/chrg/applications/thread/chrg_roll_nor.h index 74d3a7f..5bdffaa 100644 --- a/apps/chrg/applications/thread/chrg_roll_nor.h +++ b/apps/chrg/applications/thread/chrg_roll_nor.h @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_roll_nor.h 完成日期 : -当前版本号 : V1.1.10 +当前版本号 : V1.1.14 主要功能 : 实现北向通信轮巡请求命令。以独立线程处理。 版本历史 : 创建原始版本 说明 : diff --git a/apps/chrg/applications/thread/chrg_roll_sou.c b/apps/chrg/applications/thread/chrg_roll_sou.c index cc0a46d..65d6c89 100644 --- a/apps/chrg/applications/thread/chrg_roll_sou.c +++ b/apps/chrg/applications/thread/chrg_roll_sou.c @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_roll_sou.c 完成日期 : -当前版本号 : V1.0 +当前版本号 : V1.1.14 主要功能 : 实现南向通信轮巡请求命令。以独立线程处理。 版本历史 : 创建原始版本 说明 : @@ -26,7 +26,6 @@ #define SOU_MUTEX_TIMEOUT_MS 100 #define SOU_RESPONSE_TIMEOUT_MS 20 #define SOU_COM_MAX_ATTEMPTS 3 -#define SOU_COM_BURST_MAX 10U #define SOU_COM_RETRY_YIELD_MS 1U #define SOU_REL_SWITCH_DELAY_MS 100U #define SOU_CHANNEL_BIT(ch) ((rt_uint8_t)(1U << (ch))) diff --git a/apps/chrg/applications/utils/chrg_sink.c b/apps/chrg/applications/utils/chrg_sink.c index 9ee9355..fb28740 100644 --- a/apps/chrg/applications/utils/chrg_sink.c +++ b/apps/chrg/applications/utils/chrg_sink.c @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_sink.c 完成日期 : -当前版本号 : V1.0 +当前版本号 : V1.1.14 主要功能 : 北向数据sink请求数据填充及发送。 版本历史 : 创建原始版本 说明 : @@ -41,6 +41,10 @@ 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}; /* 记录各通道短路测试最近一次WORK_START的发送时间。 */ static rt_tick_t g_sink_short_start_tick[TOTAL_SOU_CHS] = {0}; +#if SINK_VON_VOLTAGE_SOURCE == SINK_VON_VOLTAGE_FROM_PROTOCOL +/* 只消费新的协议板0x20电压回码,避免同一帧重复计入稳定次数。 */ +static rt_uint32_t g_sink_von_voltage_seq[TOTAL_SOU_CHS] = {0}; +#endif enum { SINK_UNLOAD_NEXT_RUNNING = 0, @@ -263,9 +267,8 @@ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) regs[0] = REG_REL_ON; vals[0] = ALL_REL_OFF; regs[1] = REG_REL_ON; vals[1] = FU_REL_ON; regs[2] = REG_CURRENT_OUT + 1; vals[2] = 0; - regs[3] = REG_MODE; vals[3] = pSW->CV_mode; - regs[4] = Eload_Start_V_ON; vals[4] = - (pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on; + regs[3] = REG_MODE; vals[3] = pSW->CV_mode; + regs[4] = Eload_Start_V_ON; vals[4] = (pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on; regs[5] = ModbusRTU_ShortTest_CMD_MODE_FLAG; vals[5] = ModbusRTU_DYNA_CMD_MODE_STOP; count = 6U; @@ -875,9 +878,18 @@ static rt_uint8_t chrg_sink_voltage_confirm_count(rt_uint16_t target_volt_mv) SINK_VOLT_CONFIRM_5V_COUNT : SINK_VOLT_CONFIRM_NORMAL_COUNT; } +static rt_uint16_t chrg_sink_von_voltage_get(const struct chrg_switch_t *pSW) +{ +#if SINK_VON_VOLTAGE_SOURCE == SINK_VON_VOLTAGE_FROM_PROTOCOL + return pSW->Sink_voltage; +#else + return pSW->Now_voltage; +#endif +} + /***************************************************************** 函数名称: chrg_sink_volt_judge -函数描述: 使用南向功率板实际电压Now_voltage与V_On比较,驱动负载进入RUNING +函数描述: 使用配置的V_On电压源与V_On比较,控制负载启停 输入参数: idx:南向通道 pSW:通道控制结构体 输出参数: 更新sink.work_mode并调用chrg_sink_work 返回说明: 0:处理完成 <0:参数错误 @@ -890,20 +902,31 @@ int chrg_sink_volt_judge(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) 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}; + rt_uint16_t von_voltage; if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) { return -1; } +#if SINK_VON_VOLTAGE_SOURCE == SINK_VON_VOLTAGE_FROM_PROTOCOL + if (pSW->Sink_voltage_seq == g_sink_von_voltage_seq[idx]) { + return chrg_sink_work(idx, pSW); + } + g_sink_von_voltage_seq[idx] = pSW->Sink_voltage_seq; +#endif + von_voltage = chrg_sink_von_voltage_get(pSW); stable_confirm_count = chrg_sink_voltage_confirm_count( pSW->sink.pro_loadv); if ((pSW->sink.On_work == 1) && (pSW->sink.test.test_mode == 0)) { - if (pSW->Now_voltage <= WORK_MIN_VOLT) { + if (von_voltage <= WORK_MIN_VOLT) { volt_stable_count[idx] = 0; if (volt_set_ok[idx] == 1 && ++volt_low[idx] >= 50) { volt_set_ok[idx] = 0; no_curr[idx] = 0; } + if (pSW->sink.work_mode == SINK_WORK_RUNING) { + return chrg_sink_work_load_stop(idx, pSW); + } } else { volt_low[idx] = 0; volt_set_ok[idx] = 1; @@ -918,7 +941,7 @@ int chrg_sink_volt_judge(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) /* 掉电安全批次完成前,不得进入RUNING。 */ volt_stable_count[idx] = 0; no_curr[idx] = 0; - } else if (pSW->Now_voltage > pSW->sink.volt_on) { + } else if (von_voltage > pSW->sink.volt_on) { if ((pSW->sink.work_mode != SINK_WORK_RUNING) && (pSW->sink.work_mode != SINK_WORK_LOAD_STANDBY)) { if (volt_stable_count[idx] < stable_confirm_count) { @@ -934,6 +957,10 @@ int chrg_sink_volt_judge(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) } else if (pSW->sink.work_mode == SINK_WORK_LOAD_STANDBY) { volt_stable_count[idx] = 0; no_curr[idx] = 0; + if (chrg_sink_load_should_start(pSW)) { + /* 因输入电压不足停止带载后,电压恢复时重新进入稳定确认。 */ + pSW->sink.work_mode = SINK_WORK_WAIT; + } } else if ((pSW->Now_current > 200) || (pSW->sink.loadc < 200)) { volt_stable_count[idx] = 0; no_curr[idx] = 0; @@ -948,6 +975,10 @@ int chrg_sink_volt_judge(eIDX_SOU_CH idx, struct chrg_switch_t *pSW) } else { volt_stable_count[idx] = 0; no_curr[idx] = 0; + /* 配置电压源低于负载设置的V_On时,立即停止电子负载。 */ + if (pSW->sink.work_mode == SINK_WORK_RUNING) { + return chrg_sink_work_load_stop(idx, pSW); + } } } } else { diff --git a/apps/chrg/applications/utils/chrg_sink.h b/apps/chrg/applications/utils/chrg_sink.h index 92a185a..88bfee7 100644 --- a/apps/chrg/applications/utils/chrg_sink.h +++ b/apps/chrg/applications/utils/chrg_sink.h @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_sink.c 完成日期 : -当前版本号 : V1.0 +当前版本号 : V1.1.14 主要功能 : 北向数据sink请求数据填充及发送,命令字定义。 版本历史 : 创建原始版本 说明 : @@ -22,7 +22,7 @@ struct chrg_switch_t; #define SINK_TEST_OCP 2 #define SINK_TEST_DYNA 3 /* 短路测试期间按该周期补发WORK_START,防止功率板因超时停止。 */ -#define SINK_SHORT_START_INTERVAL_MS 30U +#define SINK_SHORT_START_INTERVAL_MS 50U //短路测试结构体 struct test_short { rt_uint16_t hold_time; // 短路测试时间 diff --git a/apps/chrg/applications/utils/chrg_source.c b/apps/chrg/applications/utils/chrg_source.c index 51276f4..dedc8da 100644 --- a/apps/chrg/applications/utils/chrg_source.c +++ b/apps/chrg/applications/utils/chrg_source.c @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_source.c 完成日期 : -当前版本号 : V1.1.10 +当前版本号 : V1.1.14 主要功能 : 北向数据source请求数据填充及发送。 版本历史 : 创建原始版本 说明 : diff --git a/apps/chrg/applications/utils/chrg_source.h b/apps/chrg/applications/utils/chrg_source.h index d45f908..1ed7427 100644 --- a/apps/chrg/applications/utils/chrg_source.h +++ b/apps/chrg/applications/utils/chrg_source.h @@ -1,7 +1,7 @@ /**************************************************************************** 文件名称 : chrg_source.c 完成日期 : -当前版本号 : V1.1.10 +当前版本号 : V1.1.14 主要功能 : 北向数据source请求数据填充及发送,命令字定义。 版本历史 : 创建原始版本 说明 : diff --git a/apps/chrg/codex_von_verify.log b/apps/chrg/codex_von_verify.log new file mode 100644 index 0000000..d8bf63b --- /dev/null +++ b/apps/chrg/codex_von_verify.log @@ -0,0 +1,9 @@ +*** Using Compiler 'V6.16', folder: 'D:\keil mdk 2\ARM\ARMCLANG\Bin' +Build target 'chrg' +compiling chrg_comm.c... +linking... +Program Size: Code=151160 RO-data=15928 RW-data=9356 ZI-data=16036 +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). +Build Time Elapsed: 00:00:02 diff --git a/docs/修改日志.md b/docs/修改日志.md index 5748045..c6b2ce1 100644 --- a/docs/修改日志.md +++ b/docs/修改日志.md @@ -1,5 +1,17 @@ # 修改日志 +## V1.1.15 - 2026-09-09 + +### 运行中 V_On 更新 + +- Sink 通道处于运行状态时,参数生效检测到 `V_On` 改变,会向对应功率板下发 `Eload_Start_V_ON`。 +- 下发值保持既有下限:小于或等于 `500mV` 时按 `500mV` 发送。 +- 南向队列提交失败时,本次参数生效返回忙,协议板运行参数保持原 `V_On`。 + +### 验证记录 + +- 待硬件验证:运行中修改 `SINK_REG_VON_VOLT (0x0B)` 并执行参数生效,确认功率板收到 `Eload_Start_V_ON` 新值。 + ## V1.1.9 - 2026-08-25 ### Source 复位后的初始化流程 diff --git a/docs/协议板Boot_YMODEM上位机实现规则.md b/docs/协议板Boot_YMODEM上位机实现规则.md new file mode 100644 index 0000000..1cd9e1f --- /dev/null +++ b/docs/协议板Boot_YMODEM上位机实现规则.md @@ -0,0 +1,208 @@ +# 协议板 Boot:YMODEM 上位机实现规则 + +## 1. 适用范围 + +本文适用于 **PC 上位机通过 USART1 直连协议板 Boot**,传输协议为 YMODEM + CRC16。 + +不适用于“协议板 App 通过南向 Modbus `0x20` 转发功率板 Boot”的链路;该链路的报文封装、应答和状态机需要独立实现。 + +## 2. 基本要求 + +| 项目 | 规则 | +| --- | --- | +| 串口 | USART1,115200 bps,8N1,无校验、无流控 | +| 升级文件 | 必须发送已由现有打包工具生成的完整 `.upk` 文件 | +| 不支持的文件 | 裸 `.bin`,以及自行封装/加密但不符合工程 UPK 格式的文件 | +| 启动依据 | 等待 Boot 发出 ASCII `C`(`0x43`)后再开始传输 | +| Boot 上电等待 | 首次等待主机开始约 5 秒;超时后可能跳转 App | +| 传输中的帧间等待 | Boot 约 10 秒;上位机不得使相邻有效帧间隔达到 10 秒 | + +Boot 每约 1 秒发送一次 `C`。若没有收到 `C`,上位机不可直接发送 YMODEM 首帧;应提示设备未进入 Boot,或重新复位/触发其进入 Boot。 + +## 3. YMODEM 常量与帧格式 + +| 名称 | 值 | 含义 | +| --- | --- | --- | +| SOH | `0x01` | 128 字节数据包 | +| STX | `0x02` | 1024 字节数据包 | +| EOT | `0x04` | 数据结束 | +| ACK | `0x06` | 接收成功 | +| NAK | `0x15` | 接收失败/请求重发 | +| CAN | `0x18` | 取消传输 | +| C | `0x43` | 请求以 CRC16 模式发送 | + +数据包格式: + +```text +Header + PacketNo + (~PacketNo) + Data + CRC16_H + CRC16_L +``` + +| 包类型 | Data 长度 | 总长度 | +| --- | ---: | ---: | +| SOH | 128 | 133 | +| STX | 1024 | 1029 | + +CRC 算法必须为 **CRC-16/XMODEM**: + +```text +poly=0x1021, init=0x0000, RefIn=false, RefOut=false, XorOut=0x0000 +``` + +CRC 覆盖 `Data` 的全部固定长度字节(128 或 1024),并按 **高字节、低字节** 放在帧尾。 + +## 4. 包序号与数据填充 + +1. 元数据包必须使用 `SOH`,`PacketNo=0x00`,反码为 `0xFF`。 +2. 首个文件数据包使用 `PacketNo=0x01`;每得到 ACK 后序号加 1,按 8 位自然回绕。 +3. 数据包正常使用 `STX`(1024 字节);最后一包不足 1024 字节时仍应补齐到 1024 字节,建议补 `0x1A`。 +4. Boot 校验包号、包号反码、帧长度与 CRC。任一校验失败会回复 NAK。 +5. 收到 NAK 时,必须重发**原完整帧**,包号不得递增。该 Boot 不将“前一帧重复包”当作成功帧,因此不能依赖重复包自动 ACK。 + +## 5. 元数据包规则 + +第一个包固定是包号 0 的 SOH 包,128 字节 Data 的内容为: + +```text +ASCII 文件名 + 0x00 + ASCII 十进制文件大小 + 0x00 + 其余补 0x00 +``` + +示例(文件名仅用于协议描述,实际可使用真实 `.upk` 文件名): + +```text +protocol_v1.2.upk\0 123456\0 [其余填 0] +``` + +文件大小必须是待传输 `.upk` 的实际字节数。传输结束包也是 SOH/包号 0,但其 128 字节 Data 必须全部为 `0x00`。 + +## 6. `.upk` 文件要求 + +Boot 在接收数据后按 UPK 格式处理,而不是把它当作裸固件。 + +- 头部标识必须为 `UPK_IDENTIFIER=0x006B7075`(`"upk"`)。 +- UPK 头为 1 字节对齐、总计 96 字节,包含版本、目标分区、原始/包体大小、CRC 等字段。 +- 工程当前启用 AES-256-CBC 解密;上位机职责仅为原样传输既有打包工具产生的 `.upk`,无需也不得自行改变加密、CRC 或头内容。 +- Boot 根据 UPK 头确定下载与安装流程;当前配置会先写 download 分区,再更新 app 分区。 + +## 7. 完整正常时序 + +```text +Boot -> PC : C (每秒一次,等待开始) +PC -> Boot: SOH, 00, FF, 元数据, CRC +Boot -> PC : ACK +Boot -> PC : C (必须等待此 C) + +PC -> Boot: STX/SOH, 01, FE, 数据块, CRC +Boot -> PC : ACK +PC -> Boot: STX/SOH, 02, FD, 数据块, CRC +Boot -> PC : ACK +... (直至全部 .upk 数据发送完成) + +PC -> Boot: EOT +Boot -> PC : NAK +PC -> Boot: EOT +Boot -> PC : ACK +Boot -> PC : C +PC -> Boot: SOH, 00, FF, 128 字节全 0, CRC +Boot -> PC : ACK + +Boot : 校验 UPK、写 Flash、安装 App、校验 App,并可能跳转 App +``` + +关键点: + +- 元数据 ACK 后,必须等 Boot 的第二个 `C` 才能发送数据包。 +- 第一个 EOT 后,必须等 NAK;第二个 EOT 后,必须等 ACK。 +- 第二个 EOT 的 ACK 后,必须等 `C`,再发送结束空包。 +- Boot 处理擦除和 Flash 写入期间,ACK 可能延迟;PC 的单帧 ACK 等待建议设为 3~10 秒。 + +## 8. 上位机状态机(可直接实现) + +```text +WAIT_C_START + 收到 C -> SEND_META + +SEND_META + 发送包号 0 元数据 -> WAIT_META_ACK + +WAIT_META_ACK + ACK -> WAIT_C_DATA + NAK/超时 -> 重发元数据 + +WAIT_C_DATA + C -> SEND_DATA(包号=1) + +SEND_DATA + 发送当前数据包 -> WAIT_DATA_ACK + +WAIT_DATA_ACK + ACK -> 若还有数据:包号+1,SEND_DATA;否则 SEND_EOT_1 + NAK/超时 -> 重发当前数据包 + +SEND_EOT_1 + 发送 EOT -> WAIT_EOT_NAK + +WAIT_EOT_NAK + NAK -> SEND_EOT_2 + 超时 -> 重发 EOT(保持本状态) + +SEND_EOT_2 + 发送 EOT -> WAIT_EOT_ACK + +WAIT_EOT_ACK + ACK -> WAIT_C_END + NAK/超时 -> 重发 EOT + +WAIT_C_END + C -> SEND_END_PACKET + +SEND_END_PACKET + 发送包号 0 的全 0 SOH 包 -> WAIT_END_ACK + +WAIT_END_ACK + ACK -> TRANSFER_FINISHED + NAK/超时 -> 重发结束空包 +``` + +## 9. 超时、重发、取消与结果判定 + +| 事件 | 上位机处理 | +| --- | --- | +| 收到 ACK | 仅推进到下一协议状态;数据包 ACK 后才递增包号 | +| 收到 NAK | 立即重发当前完整帧,不改变包号和文件偏移 | +| 单帧等待超时 | 重发当前完整帧;建议最多连续 3 次 | +| 重发次数耗尽 | 发送 CAN 两次,标记升级传输失败 | +| 收到 CAN | 立即停止,标记 Boot 取消升级 | +| 用户取消 | 发送 CAN 两次,停止发送后续字节 | +| 无 C | 不开始发送,提示设备未进入 Boot 或串口参数/连线错误 | + +Boot 对单个 CAN 会进入取消流程并回 ACK;上位机仍建议按标准发送两个 CAN,并接受可能出现的 ACK。 + +`WAIT_*_ACK` 的建议超时为 3~10 秒,元数据后的 `WAIT_C_DATA`、结束阶段的 `WAIT_C_END` 建议为 10 秒。上位机可将串口接收设计为持续缓存并按字节解析,忽略调试文本或仅识别 `C/ACK/NAK/CAN` 控制字符,避免因分段到达造成误判。 + +## 10. 成功判定与日志 + +结束空包得到 ACK 仅表示 **YMODEM 文件传输完成**,不是最终“固件安装成功”的结构化回码。随后 Boot 仍需校验 `.upk`、写下载区、安装 App、校验 App。 + +上位机应分两级显示结果: + +1. `传输完成`:收到结束空包 ACK。 +2. `升级确认成功`:设备重启并进入 App 后,通过应用通信读取新固件版本(推荐)或观察 Boot/App 启动日志确认。 + +建议记录:开始时间、文件名/大小、每个包号、重试次数、控制字符、异常原因、传输完成时间和最终版本读取结果。 + +## 11. 与功率板 Boot 转发升级的边界 + +若上位机目标是功率板,而物理串口只连接协议板 App,则不能直接把本 YMODEM 原始字节流写到串口。需要按协议板 App 已实现/约定的 Modbus 扩展功能码 `0x20` 封装,再由 App 南向转发给功率板 Boot,并处理 App 的转发 ACK、`C`、NAK、超时和重发。 + +本文件可作为功率板转发方案中“YMODEM 内层状态机”的依据;外层 Modbus `0x20` 帧格式和升级进入/退出控制需以 `apps/chrg/applications/thread/chrg_comm.c`、`chrg_comm.h` 的实际实现为准,不能与协议板 Boot 直连流程混用。 + +## 12. 代码依据 + +- `apps/boot/application/main.c`:USART1/USART2 初始化及 Boot 启动。 +- `apps/boot/application/func/transfer.c`:USART1 接收传输层。 +- `apps/boot/application/include/ymodem.h`:YMODEM 常量、包定义。 +- `apps/boot/application/func/ymodem.c`:YMODEM 状态机、C/ACK/NAK/EOT 时序和超时。 +- `apps/boot/application/func/app.c`:UPK 接收、校验、写入和安装流程。 +- `apps/boot/application/include/firmware.h`:UPK 头和分区相关定义。 +- `apps/boot/application/func/utils.c`:CRC-16/XMODEM 实现。 +- `apps/boot/application/include/app_config.h`:超时、分区和 AES 配置。