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 9918460..0000000 Binary files a/apps/chrg/chrg.upk and /dev/null differ 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 0000000..1d58a19 Binary files /dev/null and b/docs/HC_DFCD_PROTOCOL.DSNlck differ 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、解密和向量表校验。