From 9254c82547ad6d10153c83d6713424cdee37b883 Mon Sep 17 00:00:00 2001 From: wrh Date: Mon, 28 Sep 2026 20:51:04 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=B8=85=E7=90=86=E6=97=A7YMODEM?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E8=87=B3V1.1.43?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除chrg_comm.h和chrg_def.h中的旧YMODEM状态枚举及注释代码 - 删除chrg_comm.c中已废弃的旧YMODEM重发和会话管理代码 - 更新协议板版本寄存器编码为十进制143 - 新增chrg_ymodem_proxy模块的详细中文注释,明确升级代理职责 - 更新changelog记录V1.1.43版本变更 - 删除无用的Allegro日志文件,优化PCB设计相关文档 本次提交将YMODEM升级功能重构为独立代理模块,不再依赖旧版自动重发逻辑,提升升级流程的可维护性和可靠性。 --- apps/chrg/applications/thread/chrg_comm.c | 741 +----------------- apps/chrg/applications/thread/chrg_comm.h | 10 - apps/chrg/applications/utils/chrg_def.h | 14 +- .../applications/utils/chrg_ymodem_proxy.c | 94 ++- .../applications/utils/chrg_ymodem_proxy.h | 42 +- apps/chrg/chrg.upk | Bin 155816 -> 0 bytes docs/Allegro.jrl | 74 +- docs/HC_DFCD_PROTOCOL.DSNlck | Bin 0 -> 16384 bytes docs/allegro.jrl,1 | 89 ++- docs/hc_dfcd_protocol.opj | 5 +- docs/修改日志.md | 19 + docs/功率板升级_北向转发计划.md | 30 +- docs/功率板升级_南向应答计划.md | 88 ++- 13 files changed, 296 insertions(+), 910 deletions(-) delete mode 100644 apps/chrg/chrg.upk create mode 100644 docs/HC_DFCD_PROTOCOL.DSNlck diff --git a/apps/chrg/applications/thread/chrg_comm.c b/apps/chrg/applications/thread/chrg_comm.c index 9c29767..597df85 100644 --- a/apps/chrg/applications/thread/chrg_comm.c +++ b/apps/chrg/applications/thread/chrg_comm.c @@ -27,7 +27,7 @@ #include "stm32f4xx_hal.h" #define HOLD_REG_FIRMWARE_VERSION 0x0300U /* 协议板固件版本,只读 */ -#define CHRG_FIRMWARE_VERSION 0x013AU /* 当前协议板版本:V1.1.42 */ +#define CHRG_FIRMWARE_VERSION 143U /* 当前协议板版本:V1.1.43,按十进制版本号编码 */ rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR]; @@ -38,36 +38,6 @@ rt_uint16_t usRegProBuf[TOTAL_HOLD_ALL_REGS]; //北向protcal??? //rt_uint16_t usRegTestBuf[TOTAL_TEST_REGS]; rt_uint8_t g_proto_pData[80] = {0};//上位机协议指令,控制四个通道;二维 rt_uint16_t g_proto_reqLen = 0; -#if 0 /* 旧YMODEM重发状态机已由chrg_ymodem_proxy替代。 */ -rt_uint8_t mobude_ymodem_buf[MB_YMODEM_DATA_SIZE] = {0};//南向功率板升级缓冲区 -rt_uint8_t mobude_ymodem_buf_2[MB_YMODEM_DATA_SIZE] = {0};//南向功率板升级缓冲区 - -#define YMODEM_C_PERIOD_MS 1000 -#define YMODEM_META_ACK_TIMEOUT_MS 3000 -#define YMODEM_DATA_ACK_TIMEOUT_MS 1000 -#define YMODEM_RX_FRAME_TIMEOUT_MS 300 -#define YMODEM_MAX_RETRIES 3 - -/* COM侧升级会话状态:接收缓存负责拼帧,备份缓存负责超时/NAK重发。 */ -static rt_uint16_t g_ymodem_rx_len = 0; -static rt_uint16_t g_ymodem_expected_len = 0; -static rt_uint16_t g_ymodem_last_len = 0; -static rt_uint8_t g_ymodem_waiting_ack = 0; -static rt_uint8_t g_ymodem_retry_count = 0; -static rt_uint8_t g_ymodem_eot_done = 0; -static rt_tick_t g_ymodem_send_tick = 0; -static rt_tick_t g_ymodem_c_tick = 0; -static rt_tick_t g_ymodem_rx_tick = 0; -static rt_uint8_t g_ymodem_deferred_resp = 0; -static rt_uint32_t g_ymodem_frame_count = 0; -static rt_uint32_t g_ymodem_send_count = 0; - -static const rt_uint8_t g_ymodem_update_frame[8] = - {ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0xFF, 0xF8, 0x6B}; -static const rt_uint8_t g_ymodem_jump_frame[8] = - {ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0x0F, 0xF8, 0x2F}; -static void chrg_ymodem_session_stop(void); -#endif /* Modbus 影子寄存器: 0x06/0x10 写操作暂存到此, PARAM_EFFECT 生效后复制到实际 pNOR->sw */ static struct chrg_switch_t g_shadow_sw[TOTAL_NOR_CHS]; static rt_uint8_t g_shadow_dirty[TOTAL_NOR_CHS] = {0}; /* 0=已生效 1=待生效,防止重复覆盖 */ @@ -422,19 +392,6 @@ eMBErrorCode mb_reg_test_cb (rt_uint8_t *req, rt_uint8_t *pucRegBuffer, rt_uint1 return eStatus; } -/***************************************************************** -函数名称: mb_ymodem_updata_cb -*****************************************************************/ -eMBErrorCode mb_ymodem_updata_cb (rt_uint8_t *state, rt_uint8_t *pucRegBuffer) -{ - if ((state == RT_NULL) || (pucRegBuffer == RT_NULL)) { - return MB_EINVAL; - } - /* Ymodem_No的枚举值为0xFF,不能用只覆盖0和1的switch生成响应。 */ - pucRegBuffer[0] = (*state == Ymodem_No) ? ACK_C : ACK; - return MB_ENOERR; -} - eMBErrorCode mb_reg_coils_cb (rt_uint8_t *req, rt_uint8_t *pucRegBuffer, rt_uint16_t usAddress, rt_uint16_t usNCoils, eMBRegisterMode eMode) { @@ -1417,227 +1374,6 @@ eMBException funcTrimELoad (rt_uint8_t *reqFrame, rt_uint16_t reqLen, 其它说明: - *****************************************************************/ -#if 0 /* 旧YMODEM时序只作参考,禁止编译和运行。 */ -Ymodem_state ystate = Ymodem_No; -rt_uint8_t s_first_soh_rcvd = 0; - -/***************************************************************** -函数名称: chrg_ymodem_protocol_reset -函数描述: 清空COM侧Ymodem拼帧、重发和握手状态 -输入参数: - -输出参数: - -返回说明: - -其它说明: 不改变南向轮询模式和目标通道 -*****************************************************************/ -static void chrg_ymodem_protocol_reset(void) -{ - rt_memset(mobude_ymodem_buf, 0, sizeof(mobude_ymodem_buf)); - rt_memset(mobude_ymodem_buf_2, 0, sizeof(mobude_ymodem_buf_2)); - g_ymodem_rx_len = 0; - g_ymodem_expected_len = 0; - g_ymodem_last_len = 0; - g_ymodem_waiting_ack = 0; - g_ymodem_retry_count = 0; - g_ymodem_eot_done = 0; - g_ymodem_send_tick = 0; - g_ymodem_c_tick = rt_tick_get(); - g_ymodem_rx_tick = 0; - g_ymodem_deferred_resp = 0; - g_ymodem_frame_count = 0; - g_ymodem_send_count = 0; - s_first_soh_rcvd = 0; -} - -static const char *chrg_ymodem_state_name(Ymodem_state state) -{ - switch (state) { - case Ymodem_Wait: - return "WAIT"; - case Ymodem_Rec_Start: - return "START"; - case Ymodem_Rec_1024: - return "DATA"; - case Ymodem_Rec_End: - return "EOT"; - case Ymodem_Rec_Finish: - return "FINISH"; - default: - return "IDLE"; - } -} - -static const char *chrg_ymodem_resp_name(rt_uint8_t response) -{ - switch (response) { - case ACK: - return "ACK"; - case NAK: - return "NAK"; - case ACK_C: - return "C"; - case YM_CAN: - return "CAN"; - default: - return "NONE"; - } -} - -static rt_uint8_t chrg_ymodem_take_south_resp(void) -{ - rt_uint8_t resp; - - if (g_ymodem_deferred_resp != 0) { - resp = g_ymodem_deferred_resp; - g_ymodem_deferred_resp = 0; - return resp; - } - return chrg_eload_get_ymodem_resp(); -} - -static int chrg_ymodem_wait_south_ack(rt_uint32_t timeout_ms) -{ - rt_tick_t start = rt_tick_get(); - rt_uint8_t resp; - - while ((rt_tick_t)(rt_tick_get() - start) < - rt_tick_from_millisecond(timeout_ms)) { - resp = chrg_eload_get_ymodem_resp(); - if (resp == ACK) { - rt_kprintf("[UPG] south=%s state=%s\r\n", - chrg_ymodem_resp_name(resp), chrg_ymodem_state_name(ystate)); - return 0; - } - if (resp == NAK || resp == YM_CAN) { - rt_kprintf("[UPG] south=%s state=%s\r\n", - chrg_ymodem_resp_name(resp), chrg_ymodem_state_name(ystate)); - return -2; - } - if (resp != 0 && g_ymodem_deferred_resp == 0) { - g_ymodem_deferred_resp = resp; - } - rt_thread_mdelay(1); - } - return -1; -} - -static int chrg_ymodem_send_control_frame(const rt_uint8_t *frame) -{ - rt_uint8_t retry; - int wait_result; - const char *control_name = (frame[5] == 0xFF) ? "ENTER" : "JUMP"; - - rt_kprintf("[UPG] CTRL %s begin\r\n", control_name); - - for (retry = 0; retry < YMODEM_MAX_RETRIES; retry++) { - int send_result = chrg_send_sou_upadata_data(frame, 8); - rt_kprintf("[UPG] CTRL %s try=%u tx=%d\r\n", - control_name, (unsigned int)(retry + 1), send_result); - if (send_result == 0) { - wait_result = chrg_ymodem_wait_south_ack(YMODEM_META_ACK_TIMEOUT_MS); - if (wait_result == 0) { - rt_kprintf("[UPG] CTRL %s done\r\n", control_name); - return 0; - } - rt_kprintf("[UPG] CTRL %s wait=%d\r\n", control_name, wait_result); - } - } - rt_kprintf("[UPG] CTRL %s failed\r\n", control_name); - return -1; -} - -static void chrg_ymodem_session_stop(void); - -/***************************************************************** -函数名称: chrg_ymodem_session_start -函数描述: 停止普通南向请求并进入指定通道的升级透传模式 -输入参数: ch:需要升级的南向功率板通道 -输出参数: - -返回说明: 0:成功 <0:线程互斥锁未就绪或加锁失败 -其它说明: 不修改chrgnorth.enable和chrgsouth.enable,升级结束后恢复自动轮询 -*****************************************************************/ -static int chrg_ymodem_session_start(eIDX_SOU_CH ch) -{ - struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; - struct chrg_rollsou_t *pROLL = &chrgrollsou; - struct chrg_south_t *pSOU = &chrgsouth; - - if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL)) { - return -1; - } - if (rt_mutex_take(pTHR->mutex, rt_tick_from_millisecond(100)) != RT_EOK) { - return -2; - } - /* 先阻止南向线程继续发普通Modbus请求,等待已发请求退出邮箱等待。 */ - pROLL->run = RUN_UPDATA; - pROLL->ch = (rt_uint8_t)ch; - pROLL->send_flag = 0; - pROLL->leng = 0; - pROLL->updata_result = 0; - rt_mutex_release(pTHR->mutex); - rt_thread_mdelay(25); - - chrg_eload_clear_ymodem_resp(); - if (pSOU->rb != RT_NULL) { - rt_ringbuffer_reset(pSOU->rb); - } - chrg_ymodem_protocol_reset(); - - rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER); - pROLL->mode = 1; - pROLL->run = RUN_UPDATA; - pROLL->ch = (rt_uint8_t)ch; - rt_mutex_release(pTHR->mutex); - ystate = Ymodem_Wait; - rt_kprintf("[UPG] START ch=%u mode=%u run=%u state=%s\r\n", - (unsigned int)ch, (unsigned int)pROLL->mode, - (unsigned int)pROLL->run, chrg_ymodem_state_name(ystate)); - - /* 功率板完成擦除后才返回ACK,并立即开始发送C。 */ - if (chrg_ymodem_send_control_frame(g_ymodem_update_frame) != 0) { - chrg_ymodem_session_stop(); - return -3; - } - return 0; -} - -/***************************************************************** -函数名称: chrg_ymodem_session_stop -函数描述: 退出南向升级透传模式并恢复普通自动轮询 -输入参数: - -输出参数: - -返回说明: - -其它说明: 清除残留升级应答,避免其被普通Modbus解析器误识别 -*****************************************************************/ -static void chrg_ymodem_session_stop(void) -{ - struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU]; - struct chrg_rollsou_t *pROLL = &chrgrollsou; - struct chrg_south_t *pSOU = &chrgsouth; - - rt_kprintf("[UPG] STOP state=%s rx_frames=%lu tx_frames=%lu retries=%u eot=%u\r\n", - chrg_ymodem_state_name(ystate), - (unsigned long)g_ymodem_frame_count, - (unsigned long)g_ymodem_send_count, - (unsigned int)g_ymodem_retry_count, - (unsigned int)g_ymodem_eot_done); - - if (pTHR->mutex != RT_NULL) { - rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER); - pROLL->mode = 0; - pROLL->send_flag = 0; - pROLL->leng = 0; - pROLL->updata_result = 0; - pROLL->run = RUN_AUTO; - rt_mutex_release(pTHR->mutex); - } - chrg_eload_clear_ymodem_resp(); - if (pSOU->rb != RT_NULL) { - rt_ringbuffer_reset(pSOU->rb); - } - chrg_ymodem_protocol_reset(); - ystate = Ymodem_No; -} -#endif eMBException funYmodemUpadata (rt_uint8_t *reqFrame, rt_uint16_t reqLen, rt_uint8_t *resFrame, rt_uint16_t *resLen) @@ -1759,450 +1495,6 @@ static int chrg_comm_parse_msg (struct chrg_comm_t *pCOMM) return ret; } -#if 0 /* 旧版协议板自动重发实现,禁止编译。 */ -/* - * 旧版Ymodem处理代码保留在下方注释中作为历史参考。当前实现使用完整拼帧、 - * CRC校验和停止等待重发机制,不再依赖一次串口接收刚好得到一整帧。 - */ - -/***************************************************************** -函数名称: chrg_ymodem_frame_size -函数描述: 根据Ymodem控制字符取得一帧应接收的总长度 -输入参数: header:Ymodem帧头或控制字符 -输出参数: - -返回说明: 133:SOH帧 1029:STX帧 1:EOT/CAN 0:无效字符 -其它说明: - -*****************************************************************/ -static rt_uint16_t chrg_ymodem_frame_size(rt_uint8_t header) -{ - if (header == SOH) { - return YMODEM_SOH_SIZE; - } - if (header == STX) { - return YMODEM_STX_SIZE; - } - if ((header == EOT) || (header == YM_CAN)) { - return 1; - } - return 0; -} - -/***************************************************************** -函数名称: chrg_ymodem_crc16 -函数描述: 计算Ymodem数据区使用的CRC16-XMODEM校验值 -输入参数: data:数据区地址 length:数据区长度 -输出参数: - -返回说明: CRC16-XMODEM校验值 -其它说明: 初值0,多项式0x1021,结果按高字节在前写入Ymodem帧 -*****************************************************************/ -static rt_uint16_t chrg_ymodem_crc16(const rt_uint8_t *data, rt_uint16_t length) -{ - rt_uint16_t crc = 0; - rt_uint16_t i; - rt_uint8_t bit; - - for (i = 0; i < length; i++) { - crc ^= (rt_uint16_t)data[i] << 8; - for (bit = 0; bit < 8; bit++) { - crc = (crc & 0x8000) ? (rt_uint16_t)((crc << 1) ^ 0x1021) : - (rt_uint16_t)(crc << 1); - } - } - return crc; -} - -/***************************************************************** -函数名称: chrg_ymodem_frame_valid -函数描述: 校验Ymodem数据帧长度、包号反码和数据区CRC -输入参数: frame:完整Ymodem帧 length:帧总长度 -输出参数: - -返回说明: RT_TRUE:有效 RT_FALSE:无效 -其它说明: EOT和CAN为单字节控制帧,不包含包号与CRC -*****************************************************************/ -static rt_bool_t chrg_ymodem_frame_valid(const rt_uint8_t *frame, rt_uint16_t length) -{ - rt_uint16_t data_len; - rt_uint16_t frame_crc; - - if ((frame == RT_NULL) || (length != chrg_ymodem_frame_size(frame[0]))) { - return RT_FALSE; - } - if ((frame[0] == EOT) || (frame[0] == YM_CAN)) { - return RT_TRUE; - } - if ((rt_uint8_t)(frame[1] + frame[2]) != 0xFF) { - return RT_FALSE; - } - data_len = (frame[0] == SOH) ? 128 : 1024; - frame_crc = ((rt_uint16_t)frame[length - 2] << 8) | frame[length - 1]; - return chrg_ymodem_crc16(&frame[YMODEM_DATA_OFFSET], data_len) == frame_crc; -} - -/***************************************************************** -函数名称: chrg_ymodem_resend_last -函数描述: 将最近一次完整Ymodem帧重新提交给南向发送线程 -输入参数: - -输出参数: - -返回说明: 0:提交成功 -1:无可重发帧 其它:南向提交错误 -其它说明: 仅在NAK或等待应答超时时调用,始终使用原帧实际长度 -*****************************************************************/ -static int chrg_ymodem_resend_last(void) -{ - int ret; - - if (g_ymodem_last_len == 0) { - return -1; - } - ret = chrg_send_sou_upadata_data(mobude_ymodem_buf_2, g_ymodem_last_len); - if (ret == 0) { - g_ymodem_send_count++; - g_ymodem_retry_count++; - g_ymodem_send_tick = rt_tick_get(); - g_ymodem_waiting_ack = 1; - rt_kprintf("[UPG] RETX south frame=%02X pkt=%u len=%u count=%lu retry=%u state=%s\r\n", - mobude_ymodem_buf_2[0], (unsigned int)mobude_ymodem_buf_2[1], - (unsigned int)g_ymodem_last_len, - (unsigned long)g_ymodem_send_count, - (unsigned int)g_ymodem_retry_count, - chrg_ymodem_state_name(ystate)); - } - return ret; -} - -/***************************************************************** -函数名称: chrg_ymodem_process -函数描述: 累计COM串口分段数据,形成完整Ymodem帧后提交到南向线程 -输入参数: data:本次串口数据 length:本次数据长度 -输出参数: - -返回说明: 0:处理完成 -2:仍在等待上一帧应答 -3:帧校验失败 -4:提交失败 -其它说明: 一帧可由多次串口接收组成,只有完整且校验通过后才会发送到功率板 -*****************************************************************/ -static int chrg_ymodem_process(const rt_uint8_t *data, rt_uint16_t length) -{ - rt_uint16_t offset = 0; - rt_uint16_t copy_len; - int ret; - - if ((data == RT_NULL) || (length == 0)) { - return 0; - } - while (offset < length) { - if (g_ymodem_waiting_ack != 0) { - return -2; - } - if (g_ymodem_expected_len == 0) { - g_ymodem_expected_len = chrg_ymodem_frame_size(data[offset]); - if (g_ymodem_expected_len == 0) { - offset++; - continue; - } - g_ymodem_rx_len = 0; - g_ymodem_rx_tick = rt_tick_get(); - } - - copy_len = g_ymodem_expected_len - g_ymodem_rx_len; - if (copy_len > (length - offset)) { - copy_len = length - offset; - } - rt_memcpy(&mobude_ymodem_buf[g_ymodem_rx_len], &data[offset], copy_len); - g_ymodem_rx_len += copy_len; - offset += copy_len; - if (g_ymodem_rx_len < g_ymodem_expected_len) { - continue; - } - - if (!chrg_ymodem_frame_valid(mobude_ymodem_buf, g_ymodem_rx_len)) { - rt_kprintf("[UPG] RX invalid header=0x%02X pkt=%u len=%u state=%s\r\n", - mobude_ymodem_buf[0], (unsigned int)mobude_ymodem_buf[1], - (unsigned int)g_ymodem_rx_len, - chrg_ymodem_state_name(ystate)); - g_ymodem_rx_len = 0; - g_ymodem_expected_len = 0; - g_ymodem_rx_tick = 0; - return -3; - } - - g_ymodem_last_len = g_ymodem_rx_len; - rt_memcpy(mobude_ymodem_buf_2, mobude_ymodem_buf, g_ymodem_last_len); - if (mobude_ymodem_buf[0] == SOH) { - if (g_ymodem_eot_done != 0) { - /* 第二次EOT之后的0号空SOH是Ymodem收尾帧,只回复ACK。 */ - ystate = Ymodem_Rec_Finish; - } else if ((mobude_ymodem_buf[1] == 0) && (s_first_soh_rcvd == 0)) { - /* 只有传输开始时的0号SOH是文件名/大小帧,需要回复ACK+C。 */ - ystate = Ymodem_Rec_Start; - s_first_soh_rcvd = 1; - } else { - /* 包号非0的SOH是普通128字节固件数据帧,只回复ACK。 */ - ystate = Ymodem_Rec_1024; - } - } else if (mobude_ymodem_buf[0] == STX) { - ystate = Ymodem_Rec_1024; - } else if (mobude_ymodem_buf[0] == EOT) { - ystate = Ymodem_Rec_End; - } else { - ystate = Ymodem_Rec_Finish; - } - g_ymodem_frame_count++; - rt_kprintf("[UPG] RX frame=%02X pkt=%u len=%u total=%lu state=%s\r\n", - mobude_ymodem_buf[0], (unsigned int)mobude_ymodem_buf[1], - (unsigned int)g_ymodem_last_len, - (unsigned long)g_ymodem_frame_count, - chrg_ymodem_state_name(ystate)); - - ret = chrg_send_sou_upadata_data(mobude_ymodem_buf_2, g_ymodem_last_len); - g_ymodem_rx_len = 0; - g_ymodem_expected_len = 0; - g_ymodem_rx_tick = 0; - if (ret != 0) { - rt_kprintf("[UPG] TX south failed frame=%02X pkt=%u ret=%d\r\n", - mobude_ymodem_buf_2[0], (unsigned int)mobude_ymodem_buf_2[1], ret); - return -4; - } - g_ymodem_send_count++; - rt_kprintf("[UPG] TX south frame=%02X pkt=%u len=%u count=%lu state=%s\r\n", - mobude_ymodem_buf_2[0], (unsigned int)mobude_ymodem_buf_2[1], - (unsigned int)g_ymodem_last_len, - (unsigned long)g_ymodem_send_count, - chrg_ymodem_state_name(ystate)); - g_ymodem_retry_count = 0; - g_ymodem_waiting_ack = 1; - g_ymodem_send_tick = rt_tick_get(); - } - return 0; -} - -/***************************************************************** -函数名称: ymodem_send -函数描述: 处理南向Ymodem应答、生成上位机应答并执行超时重发 -输入参数: pCOMM:COM通信对象 -输出参数: pCOMM->tx_buf和pCOMM->tx_len -返回说明: 1:需要向上位机发送数据 0:本轮无数据发送 -其它说明: 首包前每秒发送一次C;SOH首包和第二次EOT后按Ymodem规则发送ACK+C -*****************************************************************/ -static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM) -{ - rt_uint8_t south_resp; - rt_uint32_t timeout_ms; - rt_tick_t now; - - pCOMM->tx_len = 0; - now = rt_tick_get(); - if ((g_ymodem_expected_len != 0) && (g_ymodem_rx_tick != 0) && - ((now - g_ymodem_rx_tick) >= - rt_tick_from_millisecond(YMODEM_RX_FRAME_TIMEOUT_MS))) { - g_ymodem_rx_len = 0; - g_ymodem_expected_len = 0; - g_ymodem_rx_tick = 0; - pCOMM->tx_buf[0] = NAK; - pCOMM->tx_len = 1; - rt_kprintf("[UPG] RX timeout -> NAK state=%s\r\n", - chrg_ymodem_state_name(ystate)); - return 1; - } - - south_resp = chrg_ymodem_take_south_resp(); - if (south_resp == ACK) { - g_ymodem_waiting_ack = 0; - g_ymodem_retry_count = 0; - if (ystate == Ymodem_Rec_Start) { - pCOMM->tx_buf[0] = ACK; - pCOMM->tx_len = 1; - } else if (ystate == Ymodem_Rec_End) { - pCOMM->tx_buf[0] = ACK; - pCOMM->tx_len = 1; - g_ymodem_eot_done = 1; - ystate = Ymodem_Rec_Finish; - } else { - pCOMM->tx_buf[0] = ACK; - pCOMM->tx_len = 1; - } - rt_kprintf("[UPG] SOUTH ACK state=%s retries=%u\r\n", - chrg_ymodem_state_name(ystate), (unsigned int)g_ymodem_retry_count); - return 1; - } - if (south_resp == NAK) { - rt_kprintf("[UPG] SOUTH NAK state=%s retry=%u\r\n", - chrg_ymodem_state_name(ystate), (unsigned int)g_ymodem_retry_count); - if (g_ymodem_retry_count >= YMODEM_MAX_RETRIES) { - pCOMM->tx_buf[0] = YM_CAN; - pCOMM->tx_buf[1] = YM_CAN; - pCOMM->tx_len = 2; - chrg_ymodem_session_stop(); - return 1; - } - (void)chrg_ymodem_resend_last(); - return 0; - } - if (south_resp == ACK_C) { - rt_kprintf("[UPG] SOUTH C state=%s waiting=%u\r\n", - chrg_ymodem_state_name(ystate), (unsigned int)g_ymodem_waiting_ack); - /* 等待当前帧ACK时忽略功率板周期C,避免打乱上位机停止等待握手。 */ - if (g_ymodem_waiting_ack != 0) { - return 0; - } - pCOMM->tx_buf[0] = ACK_C; - pCOMM->tx_len = 1; - return 1; - } - if (south_resp == YM_CAN) { - rt_kprintf("[UPG] SOUTH CAN state=%s\r\n", - chrg_ymodem_state_name(ystate)); - pCOMM->tx_buf[0] = YM_CAN; - pCOMM->tx_buf[1] = YM_CAN; - pCOMM->tx_len = 2; - chrg_ymodem_session_stop(); - return 1; - } - - if (g_ymodem_waiting_ack != 0) { - timeout_ms = (ystate == Ymodem_Rec_Start) ? - YMODEM_META_ACK_TIMEOUT_MS : YMODEM_DATA_ACK_TIMEOUT_MS; - if ((now - g_ymodem_send_tick) >= rt_tick_from_millisecond(timeout_ms)) { - rt_kprintf("[UPG] ACK timeout state=%s retry=%u\r\n", - chrg_ymodem_state_name(ystate), - (unsigned int)g_ymodem_retry_count); - if (g_ymodem_retry_count >= YMODEM_MAX_RETRIES) { - pCOMM->tx_buf[0] = YM_CAN; - pCOMM->tx_buf[1] = YM_CAN; - pCOMM->tx_len = 2; - chrg_ymodem_session_stop(); - return 1; - } - (void)chrg_ymodem_resend_last(); - } - } - return 0; -} - -///***************************************************************** -//函数名称: ymodem_send -//函数描述: 每隔一秒发一次0x43告诉上位机准备数据 -//输入参数: pCOMM: 消息结构 -//输出参数: - -//返回说明: - -//其它说明: 分命令处理对应消息。地址为广播时不应答 -//*****************************************************************/ -////返回1:需要发送数据;返回0:无需发送 -//uint8_t y_time = 0; -//uint8_t ymodem_send(struct chrg_comm_t *pCOMM,uint8_t state){ -// -// struct chrg_rollsou_t *pROLL = &chrgrollsou; -// static rt_uint8_t number = 4; -// uint8_t need_tx = 0; -// if(pROLL->send_flag==0)y_time++; -// pCOMM->tx_len = 0; //每次进来先清空发送长度 - -// /* ======== 尚未收到上位机第一个数据包: 定时发送'C'握手 ======== */ -// if (!s_first_soh_rcvd) { -// if (y_time > 20) { -// pCOMM->tx_buf[0] = ACK_C; -// pCOMM->tx_len = 1; -// need_tx = 1; -// y_time = 0; -// } -// return need_tx; -// } -// if(state == Ymodem_Rec_End){ -// //if (y_time > 20) { -// pCOMM->tx_buf[0] = ACK; -// pCOMM->tx_buf[1] = ACK_C; -// pCOMM->tx_len = 2; -// need_tx = 1; -// y_time = 0; -// number--; -// if(number == 0) -// { -// state = Ymodem_Wait; -// number = 4; -// } -// state = Ymodem_Wait; -// //} -// } -// /* ======== 已收到上位机数据包: 南向有应答立刻转发给上位机 ======== */ -// uint8_t south_resp = chrg_eload_get_ymodem_resp(); -// if (south_resp != 0) { -// switch (south_resp) { -// case ACK: -// memset(mobude_ymodem_buf_2,0,sizeof(mobude_ymodem_buf)); -// switch(state){ -// case Ymodem_Rec_End: -// case Ymodem_Rec_Start: -// pCOMM->tx_buf[0] = ACK; -// pCOMM->tx_buf[1] = ACK_C; -// pCOMM->tx_len = 2; -// break; -// case Ymodem_Rec_1024: -// pCOMM->tx_buf[0] = ACK; -// pCOMM->tx_len = 1; -// } -// need_tx = 1; -// /* Rec_End收到ACK → 南向已完成2次EOT握手,切到收尾阶段 */ -// break; -// case NAK: -// chrg_send_sou_upadata_data(mobude_ymodem_buf_2,1029); -// break; -// default: -// break; -// } -// y_time = 0; -// rt_kprintf("REC DATA = %02x\n",south_resp); -// } -// -// return need_tx; -//} - -///***************************************************************** -//函数名称: Ymodem_Process -//函数描述: 处理ymodem协议数据 -//输入参数: pCOMM: 消息结构 -//输出参数: - -//返回说明: - -//其它说明: 分命令处理对应消息。地址为广播时不应答 -//*****************************************************************/ -//uint16_t len = 0; -//eMBException Ymodem_Process(rt_uint8_t *reqFrame, rt_uint16_t reqLen, -// rt_uint8_t *resFrame, rt_uint16_t *resLen) -//{ -// eMBException eStatus = MB_EX_NONE; -// struct chrg_rollsou_t *pROLL = &chrgrollsou; -// rt_kprintf("TX_LEN = %d\n",reqLen); -//// for(int i=0;isend_flag = 1; -// s_first_soh_rcvd = 1; -// break; -// case STX: -// ystate = Ymodem_Rec_1024; -// memset(mobude_ymodem_buf,0,sizeof(mobude_ymodem_buf)); -// memcpy(mobude_ymodem_buf,reqFrame,reqLen); -// memcpy(mobude_ymodem_buf_2,reqFrame,reqLen); -// chrg_send_sou_upadata_data(mobude_ymodem_buf,reqLen); -// pROLL->send_flag = 1; -// break; -// case EOT: -// ystate = Ymodem_Rec_End; -// memset(mobude_ymodem_buf,0,sizeof(mobude_ymodem_buf)); -// memcpy(mobude_ymodem_buf,reqFrame,reqLen); -// memcpy(mobude_ymodem_buf_2,reqFrame,reqLen); -// chrg_send_sou_upadata_data(mobude_ymodem_buf,reqLen); -// pROLL->send_flag = 1; -// break; -// } -// return eStatus; -//} - -#endif /***************************************************************** 函数名称: chrg_comm_updata_north 函数描述: 解析南向程序数据包 @@ -2214,8 +1506,6 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM) static int chrg_comm_updata_north (struct chrg_comm_t *pCOMM) { int ret = 0; - int process_ret; - rt_uint8_t need_tx; if (pCOMM == RT_NULL) { return -1; @@ -2230,40 +1520,15 @@ static int chrg_comm_updata_north (struct chrg_comm_t *pCOMM) } if (pCOMM->rx_len > 0) { - process_ret = chrg_ymodem_proxy_feed_north(pCOMM->rx_buf, + (void)chrg_ymodem_proxy_feed_north(pCOMM->rx_buf, pCOMM->rx_len); - (void)process_ret; } pCOMM->tx_len = chrg_ymodem_proxy_poll(pCOMM->tx_buf, sizeof(pCOMM->tx_buf)); - need_tx = (pCOMM->tx_len > 0U) ? 1U : 0U; - if (need_tx != 0U) { + if (pCOMM->tx_len > 0U) { ret = chrg_tty_send(pCOMM->tty, pCOMM->tx_buf, pCOMM->tx_len); } -// uint8_t need_tx = 0; -// pCOMM->tx_len = 0; //进来先清空发送缓冲区 -// eMBException eStatus = MB_EX_NONE; -// //收到有效数据才解析Ymodem - -// if(pCOMM->rx_len == 8 && pCOMM->rx_buf[1]==0x20){ -// eStatus = funYmodemUpadata(pCOMM->rx_buf, pCOMM->rx_len, pCOMM->tx_buf, &pCOMM->tx_len); -// } -// else if(pCOMM->rx_len == 8 && pCOMM->rx_buf[1]==0x06){ -// eStatus = funcWriteHoldingRegister(pCOMM->rx_buf, pCOMM->rx_len, pCOMM->tx_buf, &pCOMM->tx_len); -// } -// else if(pCOMM->rx_len != 0){ -// rt_kprintf("len = %d\n",pCOMM->rx_len); -// Ymodem_Process(pCOMM->rx_buf, pCOMM->rx_len, pCOMM->tx_buf, &pCOMM->tx_len); -// } - -// //统一调用定时应答生成 -// need_tx = ymodem_send(pCOMM,ystate); - -// //只有标志为1且长度有效才发送 -// if(need_tx && pCOMM->tx_len > 0){ -// ret = chrg_tty_send(pCOMM->tty, pCOMM->tx_buf, pCOMM->tx_len); -// } return ret; } /***************************************************************** diff --git a/apps/chrg/applications/thread/chrg_comm.h b/apps/chrg/applications/thread/chrg_comm.h index 067112d..e51bae1 100644 --- a/apps/chrg/applications/thread/chrg_comm.h +++ b/apps/chrg/applications/thread/chrg_comm.h @@ -172,16 +172,6 @@ struct code_func_t { #define MB_YMODEM_DATA_SIZE (1029) -////升级协议状态 -//typedef enum { -// Ymodem_No = 0, -// Ymodem_Wait, -// Ymodem_Rec_Start, -// Ymodem_Rec_1024, -// Ymodem_Rec_End, -// Ymodem_Rec_Finish -//} Ymodem_state; - #define THR_NAME_COMM "thr.comm" #define DEV_NAME_COMM "uart1" diff --git a/apps/chrg/applications/utils/chrg_def.h b/apps/chrg/applications/utils/chrg_def.h index b0b736e..976d76e 100644 --- a/apps/chrg/applications/utils/chrg_def.h +++ b/apps/chrg/applications/utils/chrg_def.h @@ -14,8 +14,8 @@ /* CHRG application version. Keep this value synchronized with the changelog. */ #define CHRG_VERSION_MAJOR 1U #define CHRG_VERSION_MINOR 1U -#define CHRG_VERSION_PATCH 42U -#define CHRG_VERSION_STRING "V1.1.42" +#define CHRG_VERSION_PATCH 43U +#define CHRG_VERSION_STRING "V1.1.43" /* * Channel polling mode: @@ -56,16 +56,6 @@ enum { }; /* ======================== Ymodem 协议常量 (CRC-16/XMODEM) ======================== */ -/* Ymodem 协议状态枚举 */ -typedef enum { - Ymodem_No = 0xFF, // 非升级模式 - Ymodem_Wait = 0, // 空闲等待,定时发送'C' - Ymodem_Rec_Start, // 收到文件名帧(SOH),待CRC校验 - Ymodem_Rec_1024, // 数据帧接收中(SOH=128B / STX=1024B) - Ymodem_Rec_End, // 收到EOT,传输结束握手 - Ymodem_Rec_Finish // 升级完成,准备收尾 -} Ymodem_state; - /* Ymodem 协议控制字符 */ #define SOH 0x01 // 128字节数据包帧头 #define STX 0x02 // 1024字节数据包帧头 diff --git a/apps/chrg/applications/utils/chrg_ymodem_proxy.c b/apps/chrg/applications/utils/chrg_ymodem_proxy.c index 8a23cf7..e6062be 100644 --- a/apps/chrg/applications/utils/chrg_ymodem_proxy.c +++ b/apps/chrg/applications/utils/chrg_ymodem_proxy.c @@ -1,3 +1,8 @@ +/* + * 功率板YMODEM升级代理。 + * 北向负责拼帧和本地校验,南向应答原样转发给PC; + * 本模块不自动重发数据帧或EOT,重发决策统一由PC执行。 + */ #include #include @@ -11,27 +16,27 @@ #define LOG_TAG "chrg.upgrade" #include -#define YMODEM_RX_FRAME_TIMEOUT_MS 1000U -#define YMODEM_SESSION_IDLE_MS 15000U -#define YMODEM_ENTER_TIMEOUT_MS 15000U -#define YMODEM_RESPONSE_TIMEOUT_MS 10000U +#define YMODEM_RX_FRAME_TIMEOUT_MS 1000U // PC分段数据拼帧超时 +#define YMODEM_SESSION_IDLE_MS 15000U // 整个升级会话无有效数据超时 +#define YMODEM_ENTER_TIMEOUT_MS 15000U // 功率板进入Boot及擦除等待时间 +#define YMODEM_RESPONSE_TIMEOUT_MS 10000U // 南向单帧应答等待时间 typedef struct { - chrg_ymodem_proxy_state_t state; - rt_uint8_t ch; - rt_uint8_t expected_packet; - rt_uint8_t current_packet; - rt_uint8_t meta_ack_seen; - rt_uint8_t eot2_ack_seen; - rt_uint8_t deferred_c; - rt_uint16_t rx_len; - rt_uint16_t expected_len; - rt_tick_t rx_tick; - rt_tick_t response_tick; - rt_tick_t activity_tick; - rt_uint8_t rx_buf[YMODEM_STX_SIZE]; - rt_uint8_t north_pending[2]; - rt_uint8_t north_pending_len; + chrg_ymodem_proxy_state_t state; // 当前升级阶段 + rt_uint8_t ch; // 被锁定的南向功率板通道 + rt_uint8_t expected_packet; // PC下一帧应使用的包号 + rt_uint8_t current_packet; // 当前已转发帧的包号,仅用于日志 + rt_uint8_t meta_ack_seen; // 元数据ACK已收到,后续才接受'C' + rt_uint8_t eot2_ack_seen; // 第二个EOT的ACK已收到 + rt_uint8_t deferred_c; // 进入Boot时与ACK同批到达的'C'延后转发 + rt_uint16_t rx_len; // 北向当前已拼接字节数 + rt_uint16_t expected_len; // 根据SOH/STX计算的完整帧长度 + rt_tick_t rx_tick; // 当前半帧起始时间 + rt_tick_t response_tick; // 最近一帧提交南向的时间 + rt_tick_t activity_tick; // 最近一次有效会话活动时间 + rt_uint8_t rx_buf[YMODEM_STX_SIZE]; // 北向最大STX帧拼帧缓冲区 + rt_uint8_t north_pending[2]; // 待转发给PC的单字节或CAN CAN + rt_uint8_t north_pending_len; // 北向待发应答长度 } chrg_ymodem_proxy_t; static chrg_ymodem_proxy_t g_proxy = { @@ -39,6 +44,7 @@ static chrg_ymodem_proxy_t g_proxy = { .ch = 0xFFU }; +/* 功率板进入Boot和跳转App的固定Modbus控制帧。 */ static const rt_uint8_t g_enter_frame[8] = {ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0xFF, 0xF8, 0x6B}; static const rt_uint8_t g_exit_frame[8] = @@ -62,7 +68,6 @@ static const char *chrg_ymodem_proxy_state_name(chrg_ymodem_proxy_state_t state) case YMODEM_PROXY_WAIT_END_ACK: return "END_ACK"; case YMODEM_PROXY_TRANSFER_DONE: return "DONE"; case YMODEM_PROXY_EXITING: return "EXITING"; - case YMODEM_PROXY_VERIFY: return "VERIFY"; default: return "ABORT"; } } @@ -171,6 +176,7 @@ static rt_bool_t chrg_ymodem_frame_valid(const rt_uint8_t *frame, frame_crc) ? RT_TRUE : RT_FALSE; } +/* 元数据必须是包号0的SOH,文件名以.upk结尾,文件长度为十进制数字。 */ static rt_bool_t chrg_ymodem_metadata_valid(const rt_uint8_t *frame) { const rt_uint8_t *data = &frame[YMODEM_DATA_OFFSET]; @@ -192,6 +198,7 @@ static rt_bool_t chrg_ymodem_metadata_valid(const rt_uint8_t *frame) return digit_seen; } +/* YMODEM收尾帧必须是包号0的SOH,且数据区128字节全为0。 */ static rt_bool_t chrg_ymodem_end_soh_valid(const rt_uint8_t *frame) { rt_uint16_t i; @@ -202,6 +209,7 @@ static rt_bool_t chrg_ymodem_end_soh_valid(const rt_uint8_t *frame) return RT_TRUE; } +/* 向南向线程只提交一次帧,成功后进入真实应答等待状态。 */ static int chrg_ymodem_proxy_submit_frame(const rt_uint8_t *frame, rt_uint16_t length, chrg_ymodem_proxy_state_t next_state) { @@ -217,6 +225,7 @@ static int chrg_ymodem_proxy_submit_frame(const rt_uint8_t *frame, return 0; } +/* 锁定南向物理通道,停止普通Modbus轮询并清理历史应答。 */ static int chrg_ymodem_proxy_prepare_channel(rt_uint8_t ch) { struct chrg_thread_t *thread = &chrgthr[IDX_THR_ROLL_SOU]; @@ -238,6 +247,13 @@ static int chrg_ymodem_proxy_prepare_channel(rt_uint8_t ch) return 0; } +/***************************************************************** +函数名称: chrg_ymodem_proxy_enter +函数描述: 进入指定功率板的Boot升级模式 +输入参数: ch:南向通道编号 +返回说明: 0:收到功率板ACK <0:进入失败 +其它说明: 仅在真实ACK到达后允许COM回复PC成功 +*****************************************************************/ int chrg_ymodem_proxy_enter(rt_uint8_t ch) { rt_tick_t start; @@ -270,6 +286,13 @@ int chrg_ymodem_proxy_enter(rt_uint8_t ch) return -4; } +/***************************************************************** +函数名称: chrg_ymodem_proxy_exit +函数描述: 跳转功率板App并恢复普通南向轮询 +输入参数: ch:当前升级通道 +返回说明: 0:收到跳转ACK <0:状态或通信失败 +其它说明: 仅TRANSFER_DONE状态可退出,退出后强制刷新版本 +*****************************************************************/ int chrg_ymodem_proxy_exit(rt_uint8_t ch) { rt_tick_t start; @@ -297,6 +320,7 @@ int chrg_ymodem_proxy_exit(rt_uint8_t ch) return -3; } +/* 根据当前阶段校验完整帧,校验成功才允许提交南向。 */ static int chrg_ymodem_proxy_handle_frame(const rt_uint8_t *frame, rt_uint16_t length) { @@ -344,6 +368,13 @@ static int chrg_ymodem_proxy_handle_frame(const rt_uint8_t *frame, return -2; } +/***************************************************************** +函数名称: chrg_ymodem_proxy_feed_north +函数描述: 接收PC分段数据并拼成完整YMODEM帧 +输入参数: data:本次北向数据 length:数据长度 +返回说明: 0:已接收或提交 <0:状态、帧或提交错误 +其它说明: 等待南向应答时拒绝下一帧;CAN在任意阶段优先处理 +*****************************************************************/ int chrg_ymodem_proxy_feed_north(const rt_uint8_t *data, rt_uint16_t length) { @@ -394,6 +425,7 @@ int chrg_ymodem_proxy_feed_north(const rt_uint8_t *data, return 0; } +/* 仅按当前状态接受功率板ACK/NAK/C/CAN,不伪造成功应答。 */ static void chrg_ymodem_proxy_handle_south(rt_uint8_t response) { g_proxy.activity_tick = rt_tick_get(); @@ -468,6 +500,13 @@ static void chrg_ymodem_proxy_handle_south(rt_uint8_t response) } } +/***************************************************************** +函数名称: chrg_ymodem_proxy_poll +函数描述: 推进南向应答、半帧超时、单帧超时和会话空闲超时 +输入参数: output:北向待发缓冲区 capacity:缓冲区容量 +返回说明: 0:无待发数据 >0:需要发给PC的字节数 +其它说明: 超时只取消会话,不自动重发已可能写入Flash的帧 +*****************************************************************/ rt_uint16_t chrg_ymodem_proxy_poll(rt_uint8_t *output, rt_uint16_t capacity) { @@ -523,18 +562,3 @@ rt_uint16_t chrg_ymodem_proxy_poll(rt_uint8_t *output, g_proxy.north_pending_len = 0U; return length; } - -rt_bool_t chrg_ymodem_proxy_active(void) -{ - return (g_proxy.state != YMODEM_PROXY_IDLE) ? RT_TRUE : RT_FALSE; -} - -rt_uint8_t chrg_ymodem_proxy_channel(void) -{ - return g_proxy.ch; -} - -chrg_ymodem_proxy_state_t chrg_ymodem_proxy_state(void) -{ - return g_proxy.state; -} diff --git a/apps/chrg/applications/utils/chrg_ymodem_proxy.h b/apps/chrg/applications/utils/chrg_ymodem_proxy.h index 8002b8a..8af7114 100644 --- a/apps/chrg/applications/utils/chrg_ymodem_proxy.h +++ b/apps/chrg/applications/utils/chrg_ymodem_proxy.h @@ -1,35 +1,39 @@ +/* + * 功率板YMODEM升级代理接口。 + * PC负责重发和包号推进,协议板只校验、转发并透传功率板应答。 + */ #ifndef __CHRG_YMODEM_PROXY_H__ #define __CHRG_YMODEM_PROXY_H__ #include typedef enum { - YMODEM_PROXY_IDLE = 0, - YMODEM_PROXY_ENTERING, - YMODEM_PROXY_WAIT_C, - YMODEM_PROXY_WAIT_META, - YMODEM_PROXY_WAIT_META_RESP, - YMODEM_PROXY_WAIT_DATA, - YMODEM_PROXY_WAIT_DATA_RESP, - YMODEM_PROXY_WAIT_EOT1_RESP, - YMODEM_PROXY_WAIT_EOT2, - YMODEM_PROXY_WAIT_EOT2_RESP, - YMODEM_PROXY_WAIT_END_SOH, - YMODEM_PROXY_WAIT_END_ACK, - YMODEM_PROXY_TRANSFER_DONE, - YMODEM_PROXY_EXITING, - YMODEM_PROXY_VERIFY, - YMODEM_PROXY_ABORT + YMODEM_PROXY_IDLE = 0, // 未进入升级模式 + YMODEM_PROXY_ENTERING, // 已发送进入Boot控制帧,等待ACK + YMODEM_PROXY_WAIT_C, // 进入Boot成功,等待功率板'C' + YMODEM_PROXY_WAIT_META, // 等待PC发送包号0元数据SOH + YMODEM_PROXY_WAIT_META_RESP, // 元数据已转发,等待南向ACK和'C' + YMODEM_PROXY_WAIT_DATA, // 等待PC发送下一个数据包或首个EOT + YMODEM_PROXY_WAIT_DATA_RESP, // 数据包已转发,等待南向ACK/NAK + YMODEM_PROXY_WAIT_EOT1_RESP, // 第一个EOT已转发,等待南向NAK + YMODEM_PROXY_WAIT_EOT2, // 等待PC发送第二个EOT + YMODEM_PROXY_WAIT_EOT2_RESP, // 第二个EOT已转发,等待ACK和'C' + YMODEM_PROXY_WAIT_END_SOH, // 等待包号0且数据全零的结束SOH + YMODEM_PROXY_WAIT_END_ACK, // 结束SOH已转发,等待南向ACK + YMODEM_PROXY_TRANSFER_DONE, // YMODEM传输完成,等待0x20退出命令 + YMODEM_PROXY_EXITING, // 已发送跳转App控制帧,等待ACK + YMODEM_PROXY_ABORT // 异常取消,等待CAN发送完成 } chrg_ymodem_proxy_state_t; +/* 锁定目标通道并进入功率板Boot,收到真实ACK后才返回成功。 */ extern int chrg_ymodem_proxy_enter(rt_uint8_t ch); +/* 仅传输完成后允许跳转App,收到ACK后恢复普通轮询。 */ extern int chrg_ymodem_proxy_exit(rt_uint8_t ch); +/* 累计PC分段数据,形成完整YMODEM帧后校验并提交南向。 */ extern int chrg_ymodem_proxy_feed_north(const rt_uint8_t *data, rt_uint16_t length); +/* 推进南向应答和超时状态,返回待发给PC的ACK/NAK/C/CAN。 */ extern rt_uint16_t chrg_ymodem_proxy_poll(rt_uint8_t *output, rt_uint16_t capacity); -extern rt_bool_t chrg_ymodem_proxy_active(void); -extern rt_uint8_t chrg_ymodem_proxy_channel(void); -extern chrg_ymodem_proxy_state_t chrg_ymodem_proxy_state(void); #endif diff --git a/apps/chrg/chrg.upk b/apps/chrg/chrg.upk deleted file mode 100644 index 9918460fb53567c05d4dd9f025e318b6f4cc9418..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155816 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 z 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 diff --git a/docs/Allegro.jrl b/docs/Allegro.jrl index d8145ef..95ae216 100644 --- a/docs/Allegro.jrl +++ b/docs/Allegro.jrl @@ -1,59 +1,15 @@ -\t (00:00:28) Allegro 17.2 S079 Windows SPB 64-bit Edition -\t (00:00:28) Journal start - Sun Sep 20 09:40:58 2026 -\t (00:00:28) Host=DESKTOP-5RA9F3K User=Adminuser Pid=2796 CPUs=20 -\t (00:00:28) CmdLine= D:\Cadence\SPB_17.2\tools\bin\Allegro.exe D:\HC-PMP-SW\Power_ControlPannel\MP_Control_sw_405\HC_DFCD\HC_DFCD_LCD_V_1.0\docs\HC_DFCD_LCD_V2_0(1).brd -\t (00:00:28) -\d (00:00:28) Design opened: D:/HC-PMP-SW/Power_ControlPannel/MP_Control_sw_405/MP_SW_Chrg/chrg/docs/HC_DFCD_Protocol_V3_0.brd -\i (00:00:28) generaledit -\i (00:00:28) zoom out 1 -\i (00:00:28) setwindow pcb -\i (00:00:28) zoom out 18.0833 55.1949 -\i (00:00:28) trapsize 5515 -\i (00:00:29) zoom out 1 -\i (00:00:29) setwindow pcb -\i (00:00:29) zoom out 18.3039 52.8786 -\i (00:00:29) trapsize 11030 -\i (00:00:29) zoom out 1 -\i (00:00:29) setwindow pcb -\i (00:00:29) zoom out 58.3881 52.4375 -\i (00:00:29) trapsize 22060 -\i (00:00:29) zoom out 1 -\i (00:00:29) setwindow pcb -\i (00:00:29) zoom out 217.2174 50.6728 -\i (00:00:29) trapsize 44119 -\i (00:00:29) zoom out 1 -\i (00:00:29) setwindow pcb -\i (00:00:29) zoom out 535.3173 195.5990 -\i (00:00:29) trapsize 88239 -\i (00:00:29) zoom in 1 -\i (00:00:29) setwindow pcb -\i (00:00:29) zoom in -61.1751 55.2998 -\i (00:00:29) trapsize 44119 -\i (00:00:30) zoom in 1 -\i (00:00:30) setwindow pcb -\i (00:00:30) zoom in -42.6449 29.7106 -\i (00:00:30) trapsize 22060 -\i (00:00:30) zoom in 1 -\i (00:00:30) setwindow pcb -\i (00:00:30) zoom in -42.6449 29.7107 -\i (00:00:30) trapsize 11030 -\i (00:00:30) zoom in 1 -\i (00:00:30) setwindow pcb -\i (00:00:30) zoom in -41.3213 29.7107 -\i (00:00:30) trapsize 5515 -\i (00:00:32) drag_start grid -14.2983 71.4034 -\i (00:00:33) drag_stop -24.7766 57.2852 -\i (00:00:33) zoom out 1 -\i (00:00:33) setwindow pcb -\i (00:00:33) zoom out 18.5706 69.5283 -\i (00:00:33) trapsize 11030 -\i (00:00:42) zoom in 1 -\i (00:00:42) setwindow pcb -\i (00:00:42) zoom in 93.0217 64.4546 -\i (00:00:42) trapsize 5515 -\i (00:00:52) zoom out 1 -\i (00:00:52) setwindow pcb -\i (00:00:52) zoom out 72.8372 58.1677 -\i (00:00:52) trapsize 11030 -\i (00:02:28) exit -\t (00:02:29) Journal end - Sun Sep 20 09:42:59 2026 +\t (00:00:02) allegro 17.2 S079 Windows SPB 64-bit Edition +\t (00:00:02) Journal start - Mon Sep 28 10:44:47 2026 +\t (00:00:02) Host=DESKTOP-5RA9F3K User=Adminuser Pid=40156 CPUs=20 +\t (00:00:02) CmdLine= D:\Cadence\SPB_17.2\tools\bin\allegro.exe +\t (00:00:02) + (00:00:02) Loading axlcore.cxt +\t (00:00:08) Starting new design... +\i (00:00:09) trapsize 29310 +\i (00:00:09) generaledit +\i (00:00:13) trapsize 1230 +\i (00:00:13) QtSignal SPBFoldDockArea FoldAreaTabWidget currentChanged unnamed +\i (00:00:13) opencd D:/HC-PMP-SW/Power_ControlPannel/MP_Control_sw_405/south/HC_DFCD_Source_Eload_V1_1/PCB/HC_DFCD_Source_Eload_V1_1.brd +\t (00:00:13) Opening existing design... +\i (00:00:13) trapsize 283 +\t (00:00:13) Journal end - Mon Sep 28 10:44:58 2026 diff --git a/docs/HC_DFCD_PROTOCOL.DSNlck b/docs/HC_DFCD_PROTOCOL.DSNlck new file mode 100644 index 0000000000000000000000000000000000000000..1d58a19ae53bcf18855956be74d4fbab1467910e GIT binary patch literal 16384 zcmeIyu?Ye(6adgKqF|?ug_V=oxqyQxh>hbof|KY7I*7(Z5Kfy*bA{ad;m9ZXcR5~2 zu9w;EbY9%!Csr|xXPU&gF+Rw7dEPONJfBj@KYhvbkoY5kNB-Bcf1;lPo7m-ch-Iu} zoBMqn%Q3Ewd5pf!e9eo^`=X}4t){tiZM9v@_kH~?Ons980RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly&{u&Mwv6*> literal 0 HcmV?d00001 diff --git a/docs/allegro.jrl,1 b/docs/allegro.jrl,1 index 76642e2..23a79c5 100644 --- a/docs/allegro.jrl,1 +++ b/docs/allegro.jrl,1 @@ -1,15 +1,78 @@ -\t (00:00:05) allegro 17.2 S079 Windows SPB 64-bit Edition -\t (00:00:05) Journal start - Wed Sep 16 22:17:09 2026 -\t (00:00:05) Host=DESKTOP-5RA9F3K User=Adminuser Pid=19892 CPUs=20 -\t (00:00:05) CmdLine= D:\Cadence\SPB_17.2\tools\bin\allegro.exe +\t (00:00:05) Allegro 17.2 S079 Windows SPB 64-bit Edition +\t (00:00:05) Journal start - Mon Sep 28 10:42:59 2026 +\t (00:00:05) Host=DESKTOP-5RA9F3K User=Adminuser Pid=31016 CPUs=20 +\t (00:00:05) CmdLine= D:\Cadence\SPB_17.2\tools\bin\Allegro.exe D:\HC-PMP-SW\Power_ControlPannel\MP_Control_sw_405\MP_SW_Chrg\chrg\docs\HC_DFCD_Protocol_V3_0.brd \t (00:00:05) (00:00:05) Loading axlcore.cxt -\t (00:00:06) Starting new design... -\i (00:00:06) trapsize 29310 -\i (00:00:07) generaledit -\i (00:00:08) trapsize 1230 -\i (00:00:08) QtSignal SPBFoldDockArea FoldAreaTabWidget currentChanged unnamed -\i (00:00:08) opencd D:/HC-PMP-SW/Power_ControlPannel/MP_Control_sw_405/south/HC_DFCD_Source_Eload_V1_1/PCB/HC_DFCD_Source_Eload_V1_1.brd -\t (00:00:08) Opening existing design... -\i (00:00:08) trapsize 283 -\t (00:00:08) Journal end - Wed Sep 16 22:17:12 2026 +\t (00:00:06) Opening existing design... +\i (00:00:06) QtSignal SPBFoldDockArea FoldAreaTabWidget currentChanged HC_DFCD_Protocol_V3_0 +\d (00:00:06) Design opened: D:/HC-PMP-SW/Power_ControlPannel/MP_Control_sw_405/MP_SW_Chrg/chrg/docs/HC_DFCD_Protocol_V3_0.brd +\i (00:00:08) trapsize 3087 +\i (00:00:08) trapsize 3181 +\i (00:00:08) trapsize 3224 +\i (00:00:09) trapsize 2757 +\i (00:00:09) trapsize 2757 +\i (00:00:10) generaledit +\i (00:00:13) xhilite +\i (00:00:13) xname_flush +\i (00:00:13) done +\i (00:00:13) generaledit +\i (00:01:43) xhilite +\i (00:01:43) xname_flush +\i (00:01:43) done +\i (00:01:43) generaledit +\i (00:01:46) exit +\e (00:01:46) Do you want to save the changes you made to HC_DFCD_Protocol_V3_0.brd? +\i (00:01:46) fillin no +\t (00:01:47) Journal end - Mon Sep 28 10:44:41 2026 +i (00:01:38) trapsize 1379 +\i (00:01:38) zoom in 1 +\i (00:01:38) setwindow pcb +\i (00:01:38) zoom in -9.3258 62.5298 +\i (00:01:38) trapsize 689 +\i (00:01:38) zoom in 1 +\i (00:01:38) setwindow pcb +\i (00:01:38) zoom in -9.3258 62.5298 +\i (00:01:38) trapsize 345 +\i (00:01:38) zoom in 1 +\i (00:01:38) setwindow pcb +\i (00:01:38) zoom in -9.3258 62.5298 +\i (00:01:38) trapsize 172 +\i (00:01:38) zoom out 1 +\i (00:01:38) setwindow pcb +\i (00:01:38) zoom out -8.5709 62.0783 +\i (00:01:38) trapsize 345 +\i (00:01:38) zoom out 1 +\i (00:01:38) setwindow pcb +\i (00:01:38) zoom out -8.5916 62.0782 +\i (00:01:38) trapsize 689 +\i (00:01:38) zoom out 1 +\i (00:01:38) setwindow pcb +\i (00:01:38) zoom out -8.6055 62.0645 +\i (00:01:38) trapsize 1379 +\i (00:01:38) zoom out 1 +\i (00:01:38) setwindow pcb +\i (00:01:38) zoom out -8.6055 62.0645 +\i (00:01:38) trapsize 2757 +\i (00:01:39) zoom out 1 +\i (00:01:39) setwindow pcb +\i (00:01:39) zoom out -8.2746 62.0646 +\i (00:01:39) trapsize 5515 +\i (00:01:39) zoom out 1 +\i (00:01:39) setwindow pcb +\i (00:01:39) zoom out -8.2745 62.0645 +\i (00:01:39) trapsize 11030 +\i (00:01:39) zoom out 1 +\i (00:01:39) setwindow pcb +\i (00:01:39) zoom out -2.9386 62.0645 +\i (00:01:39) trapsize 22059 +\i (00:01:39) zoom out 1 +\i (00:01:39) setwindow pcb +\i (00:01:39) zoom out 94.1228 62.0646 +\i (00:01:39) trapsize 44119 +\i (00:01:41) pick grid 679.1382 312.9521 +\t (00:01:41) last pick: 679.1400 312.9500 +\i (00:01:42) exit +\e (00:01:42) Do you want to save the changes you made to HC_DFCD_Protocol_V3_0.brd? +\i (00:01:43) fillin no +\t (00:01:43) Journal end - Mon Sep 28 10:44:38 2026 diff --git a/docs/hc_dfcd_protocol.opj b/docs/hc_dfcd_protocol.opj index 0241c3f..64c55ba 100644 --- a/docs/hc_dfcd_protocol.opj +++ b/docs/hc_dfcd_protocol.opj @@ -20,5 +20,6 @@ (Doc (Type "COrCapturePMDoc") (Frame - (Placement "44 2 3 -1 -1 -8 -31 0 200 0 150")) - (Tab 0)))) + (Placement "44 0 1 -1 -1 -8 -31 0 200 0 150")) + (Tab 0))) + (MPSSessionName "Adminuser")) diff --git a/docs/修改日志.md b/docs/修改日志.md index add4eba..97b05fb 100644 --- a/docs/修改日志.md +++ b/docs/修改日志.md @@ -1,5 +1,24 @@ # 修改日志 +## V1.1.43 - 2026-09-28 + +### YMODEM升级代理代码清理 + +- 删除 `chrg_comm.c` 中已被独立代理模块替代的旧YMODEM自动重发代码和注释代码。 +- 删除无调用的旧状态枚举和升级查询接口。 +- 补充YMODEM代理状态、会话上下文、校验和超时流程的中文注释。 +- 协议板版本寄存器 `0x0300` 更新为十进制编码 `143`,后续版本不再使用十六进制编码。 + +## V1.1.42 - 2026-09-28 + +### 功率板YMODEM北向升级代理 + +- 新增独立 `chrg_ymodem_proxy` 模块,管理指定通道的升级会话、拼帧、校验和超时。 +- 严格区分元数据、数据包、两次EOT和结束空SOH状态。 +- 功率板NAK原样转发给PC,取消协议板对数据帧和EOT的内部自动重发。 +- 退出升级并收到真实ACK后恢复南向轮询,强制刷新目标功率板版本。 +- 协议板版本寄存器 `0x0300` 更新为 `0x013A`。 + ## V1.1.41 - 2026-09-28 ### Sink协议类型切换复位时序 diff --git a/docs/功率板升级_北向转发计划.md b/docs/功率板升级_北向转发计划.md index 952715a..f03fbe4 100644 --- a/docs/功率板升级_北向转发计划.md +++ b/docs/功率板升级_北向转发计划.md @@ -1,10 +1,22 @@ # 功率板升级:北向转发计划 +## 实施状态 + +已在 `chrg_ymodem_proxy.c/.h` 实现独立的非阻塞转发状态机,并接入COM北向入口和现有南向发送线程。当前实现包括: + +- 指定通道进入/退出升级,以功率板真实ACK决定成功响应。 +- SOH/STX分段拼帧、包号反码和CRC-16/XMODEM校验。 +- `.upk` 元数据、连续数据包号、包号1固定STX和结束空SOH校验。 +- 两次EOT独立状态,ACK/NAK/C/CAN均来自南向功率板。 +- NAK原样转发给PC,协议板不再自动重发数据帧或EOT。 +- 半帧1秒超时、南向应答10秒超时和会话15秒空闲取消。 +- 跳转App确认后恢复普通轮询,并强制目标通道重新读取版本。 + ## 1. 范围与原则 北向链路为“PC 上位机 ↔ 协议板 App”。协议板通过功能码 `0x20` 进入指定功率板的升级代理模式,之后北向链路切换为原始 YMODEM 字节流。 -当前功率板 Boot 仅将 YMODEM 数据区直接写入 `APP_FLASH_BASE_ADDR`,本阶段升级文件必须是可直接启动的裸 APP 镜像。若交付物带 `.upk` 头、分区或校验字段,必须先实现 Boot 解包和最终完整性校验,未完成前不得透传该 `.upk`。 +目标方案要求功率板Boot解析完整 `.upk` 文件:跳过YMODEM元数据,解析UPK头区,再将package body安装到 `APP_FLASH_BASE_ADDR`。协议板只透传UPK的YMODEM帧,不解析或重打包UPK。 升级期间采用以下原则: @@ -71,7 +83,18 @@ CRC 使用 CRC-16/XMODEM:`poly=0x1021`、`init=0x0000`、不反射、`xorout=0 文件名 + 0x00 + `.upk` 实际十进制长度 + 0x00 + 其余补 0x00 ``` -数据从包号 1 开始。首个 STX 携带 `.upk` 文件偏移 `0~1023` 的原始字节,后续帧按文件偏移连续发送;协议板不解释 UPK 内部字段。最后一帧不足固定数据区时用 `0x1A` 补齐,但补齐字节不计入元数据中的 `.upk` 文件长度。 +数据从包号1开始,功率板UPK固定采用以下布局: + +```text +包号0:YMODEM元数据,功率板Boot读取文件名/文件长度,不写Flash +包号1:UPK文件偏移0~1023 + 前96字节为UPK逻辑头,后928字节为0x00填充 + 功率板Boot解析并校验UPK头,不写APP区 +包号2起:UPK package body + 功率板Boot从APP首地址0x08003800开始安装 +``` + +协议板只按文件偏移原样转发,不解释或修改UPK内容。最后一帧不足1024字节时用 `0x1A` 补齐,但补齐字节不计入元数据文件长度、`pkg_size`或CRC。 ## 4. 北向状态机 @@ -197,6 +220,7 @@ PC -> 协议板 : 通过正常协议查询功率板版本并确认升级结果 - 等待南向应答时禁止接受下一数据帧,严格执行停止等待。 - 任意传输阶段收到 PC 的 CAN 时,应转发南向、终止会话并恢复普通轮询。 - 本地包号反码或 CRC 错误:不转发南向,直接向 PC 发送 NAK。 +- 包号1必须是完整1024字节UPK头区;北向只做YMODEM校验,UPK头和包体校验由功率板Boot完成。 - 南向 NAK:原样转发 PC,由 PC 重发;协议板不得自行重发。 - 南向应答超时:尽可能向功率板和 PC 各发送 CAN CAN,结束会话;不得盲目重发已可能写入的帧。 - 北向 ACK 等待建议由 PC 设置为 10 秒;进入升级擦除等待建议 15 秒。 @@ -210,4 +234,4 @@ PC -> 协议板 : 通过正常协议查询功率板版本并确认升级结果 4. 只有收到南向真实应答后才回复 PC。 5. 结束后执行退出命令和版本读取,区分“传输完成”与“升级确认成功”。 6. 日志至少记录通道、状态、帧类型、包号、长度、南向应答和失败原因。 -7. 联调前确认升级文件为裸 APP 镜像;若使用 UPK,先完成 Boot 端解包和完整性校验。 +7. 联调前先完成功率板Boot的UPK解析、安装和完整性校验;北向只允许发送符合功率板UPK规则的完整 `.upk` 文件。 diff --git a/docs/功率板升级_南向应答计划.md b/docs/功率板升级_南向应答计划.md index e9f5115..05c098d 100644 --- a/docs/功率板升级_南向应答计划.md +++ b/docs/功率板升级_南向应答计划.md @@ -2,11 +2,11 @@ ## 1. 范围与原则 -南向链路为“协议板 App ↔ 目标功率板 Boot”。当前功率板 Boot 仅将 YMODEM 数据区直接写入 `APP_FLASH_BASE_ADDR`,因此本阶段只允许传输裸 APP 镜像。协议板不修改镜像内容,只负责锁定物理通道、连续发送完整 YMODEM 帧、采集功率板的单字节应答,并将应答按原顺序交给北向状态机。UPK 解包、分区、版本、加密和最终 CRC 校验属于后续 Boot 能力,未实现前不得向本 Boot 透传 `.upk`。 +南向链路为“协议板 App ↔ 目标功率板 Boot”。功率板Boot按UPK规则接收完整 `.upk` 文件;协议板不修改UPK内容,只负责锁定物理通道、连续发送完整YMODEM帧、采集功率板的单字节应答,并将应答按原顺序交给北向状态机。 南向升级期间停止普通 Modbus 轮询、LCD 插入命令、控制台命令和其他通道请求。升级数据不得经过普通 Modbus解析器。 -协议板只校验 YMODEM 帧长度、包号反码和 CRC16;当前 Boot 的 APP 可写范围是 `0x08003800 + 0xB000`(44 KB),裸 APP 镜像长度必须受此范围限制。 +功率板UPK采用与协议板Boot相同的“1024字节首块”结构:前96字节为UPK逻辑头,后928字节固定为 `0x00`,package body从文件偏移 `0x400` 开始。协议板只校验YMODEM帧长度、包号反码和CRC16;UPK头、大小、加密和CRC由功率板Boot解析。 ## 2. 串口和控制帧 @@ -40,6 +40,8 @@ | --- | --- | --- | | 进入升级控制帧 | `ACK + C` | ACK确认进入,C进入应答队列,按顺序交北向 | | 元数据 SOH/包0 | `ACK + C` | 两字节均按顺序交北向 | +| UPK头区 STX/包1 | `ACK`或`NAK` | Boot解析96字节头并检查928字节填充,不写APP区 | +| UPK包体 STX/包2起 | `ACK`或`NAK` | 从APP首地址开始安装,仅接收`pkg_size`范围内的数据 | | 普通 SOH/STX | `ACK` | 交北向,允许下一帧 | | 校验错误的数据帧 | `NAK` | 原样交北向,不在南向自动重发 | | 第一次 EOT | `NAK` | 这是正常握手,原样交北向 | @@ -74,9 +76,17 @@ S_META_WAIT NAK -> 转交北向 -> S_WAIT_META S_WAIT_DATA - 收到SOH/STX -> S_DATA_TX + 收到包号1的STX -> S_UPK_HEAD_TX + 收到包号2起的SOH/STX -> S_DATA_TX 收到EOT -> S_EOT1_TX +S_UPK_HEAD_TX + UART发送完成 -> S_UPK_HEAD_WAIT + +S_UPK_HEAD_WAIT + 功率板解析UPK头成功并回复ACK -> 转交北向 -> S_WAIT_DATA + 功率板回复NAK/CAN -> 转交北向 -> S_ABORT + S_DATA_TX UART发送完成 -> S_DATA_WAIT @@ -175,26 +185,65 @@ YMODEM 数据帧的重试由北向 PC 负责。南向代理不得在收到 NAK 1. 控制帧可以跨多次 DMA/空闲中断接收并累计到完整 8 字节后匹配。 2. 进入升级时擦除成功回复 `ACK + C`,失败回复 NAK。 3. 支持标准 YMODEM 包号、反码和 CRC16 校验。 -4. 第一次 EOT 回复 NAK,第二次 EOT 回复 `ACK + C`,结束空 SOH 回复 ACK。 -5. 明确 App 起始地址、最大 UPK/package body 长度和 Flash 擦除范围;当前记录的 `0x08003800 + 0xB000` 为 44 KB,必须与链接脚本和功率板 UPK 分区规则一致。 -6. 跳转 App 前关闭中断、DMA、SysTick和相关外设,设置 MSP 与 VTOR。 -7. 处理进入控制帧后,先清除控制帧接收缓存和长度计数,再启动 YMODEM 接收;不得将控制帧的 8 字节与元数据 SOH 拼接。 -8. UART 接收只保留一条 DMA-to-idle 重启路径;禁止 UART IRQ 与接收回调同时中止或重复启动 DMA。 -9. 跳转控制帧匹配成功后,先发送 ACK 并等待发送完成,再执行 App 跳转。 +4. 包号0只解析YMODEM文件名和文件长度,不写Flash。 +5. 包号1必须为STX/1024:解析前96字节UPK头,校验标识、头CRC、目标分区、`raw_size`、`pkg_size`和加密配置;检查后928字节均为 `0x00`;整包不写APP区。 +6. 包号2起才处理package body,并从 `0x08003800` 开始安装;不得把UPK头区写入APP。 +7. 只处理UPK头声明的 `pkg_size` 字节,忽略最后YMODEM帧的 `0x1A`补齐;不得按整帧把补齐数据计入固件长度。 +8. 完成接收后校验package CRC;若启用加密,按UPK配置解密后写APP并校验raw CRC;未加密时直接写APP并校验raw CRC。 +9. APP首地址必须是裸固件向量表,写入完成后校验MSP和Reset_Handler范围;任何UPK校验失败都不得跳转APP。 +10. 第一次EOT回复NAK,第二次EOT仅在UPK接收和校验成功后回复 `ACK + C`,结束空SOH回复ACK。 +11. 明确APP起始地址、最大 `raw_size/pkg_size`和Flash擦除范围;当前APP区为 `0x08003800~0x0800E7FF`,大小 `0xB000`(44KB),必须与App链接脚本一致。 +12. 跳转App前关闭中断、DMA、SysTick和相关外设,设置MSP与VTOR。 +13. 处理进入控制帧后,先清除控制帧接收缓存和长度计数,再启动YMODEM接收;不得将控制帧的8字节与元数据SOH拼接。 +14. UART接收只保留一条DMA-to-idle重启路径;禁止UART IRQ与接收回调同时中止或重复启动DMA。 +15. 跳转控制帧匹配成功后,先发送ACK并等待发送完成,再执行App跳转。 -## 9. 南向验证顺序 +## 9. 功率板Boot的UPK安装流程 + +```text +收到YMODEM包号0 + -> 解析文件名和.upk实际长度 + -> ACK + C + +收到YMODEM包号1(STX/1024) + -> 解析前96字节UPK逻辑头 + -> 校验head_crc、大小、分区、加密配置和928字节零填充 + -> 初始化package CRC/解密状态 + -> 不写APP区 + -> ACK + +收到YMODEM包号2起 + -> 按pkg_size截取有效package body + -> 更新pkg_crc + -> 未加密:从0x08003800顺序写入 + -> 已加密:按UPK配置解密后从0x08003800顺序写入 + -> 超出pkg_size的0x1A填充不写入 + -> 每帧成功后ACK + +收到两次EOT + -> 校验累计pkg_size和pkg_crc + -> 校验写入APP的raw_size和raw_crc + -> 校验APP向量表 + -> 全部成功后完成标准EOT和结束空SOH握手 + -> 任一失败回复CAN或明确失败并禁止跳转 +``` + +## 10. 南向验证顺序 1. 逻辑分析仪确认进入帧完整发送,波特率和通道正确。 2. 确认功率板完整接收 8 字节并完成 Flash 擦除。 3. 确认线路依次出现 `06 43`,协议板应答队列也依次取到 `06 43`。 -4. 使用一个元数据包和一个 STX 包验证停止等待。 -5. 验证标准两次 EOT和结束空 SOH。 -6. 验证 CRC 错误时 NAK 能原样传到 PC,并由 PC 重发。 -7. 验证 ACK 丢失、半帧、超时、CAN、断电和超出功率板 UPK 分区容量的文件。 -8. 跳转后读取功率板新版本,确认升级真正成功。 -9. 连续发送 `ACK+C`、EOT、结束空 SOH 和跳转控制帧,确认 Boot 未丢字节、重复启动 DMA 或将控制帧拼入 YMODEM 帧。 +4. 确认包号0不写Flash,包号1只解析UPK头且不写APP。 +5. 确认包号2的数据区首字节写入 `0x08003800`,该地址内容为APP向量表。 +6. 验证最后一帧的 `0x1A`补齐不计入大小和CRC。 +7. 验证头CRC、package CRC、raw CRC、加密和超长UPK异常路径。 +8. 验证标准两次EOT和结束空SOH。 +9. 验证CRC错误时NAK能原样传到PC,并由PC重发。 +10. 验证ACK丢失、半帧、超时、CAN和断电。 +11. 跳转后读取功率板新版本,确认升级真正成功。 +12. 连续发送 `ACK+C`、EOT、结束空SOH和跳转控制帧,确认Boot未丢字节、重复启动DMA或将控制帧拼入YMODEM帧。 -## 10. 南向实施清单 +## 11. 南向实施清单 1. 删除南向收到 NAK或超时后的内部 YMODEM重发。 2. 增加独立的 EOT1、EOT2和结束空包状态。 @@ -203,5 +252,6 @@ YMODEM 数据帧的重试由北向 PC 负责。南向代理不得在收到 NAK 5. 进入和退出控制帧禁止盲目重试。 6. 升级期间锁定通道并完全停止普通轮询。 7. 完成后恢复轮询并提供版本确认结果。 -8. 先修复 Boot:进入控制帧后清接收缓存、跳转前回 ACK、UART 单一 DMA 接收重启路径。 -9. 未实现 Boot 端 UPK 解包和最终完整性校验前,升级文件限定为裸 APP 镜像。 +8. 修复Boot:进入控制帧后清接收缓存、跳转前回ACK、UART使用单一DMA接收重启路径。 +9. 修改功率板Boot:包号1解析UPK头但不写APP,包号2起安装package body。 +10. 增加UPK长度、头CRC、package CRC、raw CRC、解密和向量表校验。