diff --git a/apps/boot/application/main.c b/apps/boot/application/main.c index 7c48401..314d8be 100644 --- a/apps/boot/application/main.c +++ b/apps/boot/application/main.c @@ -240,10 +240,10 @@ int main (void) VERSION_MAIN, VERSION_SUB, VERSION_FIX, BUILD_TIMESTAMP); #endif APP_Init(); - boot_JumpToAPP(); + //boot_JumpToAPP(); while (1) { - //APP_Running(); + APP_Running(); } return -1; diff --git a/apps/chrg/applications/thread/chrg_comm.c b/apps/chrg/applications/thread/chrg_comm.c index b7bd820..d020026 100644 --- a/apps/chrg/applications/thread/chrg_comm.c +++ b/apps/chrg/applications/thread/chrg_comm.c @@ -43,6 +43,7 @@ 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重发。 */ @@ -54,6 +55,16 @@ 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); /* 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=待生效,防止重复覆盖 */ @@ -1399,9 +1410,112 @@ static void chrg_ymodem_protocol_reset(void) 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 函数描述: 停止普通南向请求并进入指定通道的升级透传模式 @@ -1443,6 +1557,15 @@ static int chrg_ymodem_session_start(eIDX_SOU_CH ch) 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; } @@ -1460,6 +1583,13 @@ static void chrg_ymodem_session_stop(void) 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; @@ -1498,14 +1628,20 @@ eMBException funYmodemUpadata (rt_uint8_t *reqFrame, rt_uint16_t reqLen, return MB_EX_SLAVE_BUSY; } } else if (reqFrame[5] == 0x00) { + chrg_eload_clear_ymodem_resp(); + if (chrg_ymodem_send_control_frame(g_ymodem_jump_frame) != 0) { + chrg_ymodem_session_stop(); + return MB_EX_SLAVE_DEVICE_FAILURE; + } chrg_ymodem_session_stop(); } else { return MB_EX_ILLEGAL_DATA_VALUE; } /* 保留原0x20响应内容,外层统一补设备地址和Modbus CRC。 */ - (void)mb_ymodem_updata_cb(&ystate, resFrame); - *resLen = 1; + resFrame[0] = reqFrame[0]; + resFrame[1] = ACK; + *resLen = 2; return MB_EX_NONE; } @@ -1689,9 +1825,16 @@ static int chrg_ymodem_resend_last(void) } 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; } @@ -1724,6 +1867,7 @@ static int chrg_ymodem_process(const rt_uint8_t *data, rt_uint16_t length) continue; } g_ymodem_rx_len = 0; + g_ymodem_rx_tick = rt_tick_get(); } copy_len = g_ymodem_expected_len - g_ymodem_rx_len; @@ -1738,8 +1882,13 @@ static int chrg_ymodem_process(const rt_uint8_t *data, rt_uint16_t length) } 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; } @@ -1764,13 +1913,28 @@ static int chrg_ymodem_process(const rt_uint8_t *data, rt_uint16_t length) } 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(); @@ -1794,35 +1958,42 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM) pCOMM->tx_len = 0; now = rt_tick_get(); - if ((s_first_soh_rcvd == 0) && (g_ymodem_waiting_ack == 0) && - ((now - g_ymodem_c_tick) >= rt_tick_from_millisecond(YMODEM_C_PERIOD_MS))) { - pCOMM->tx_buf[0] = ACK_C; + 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; - g_ymodem_c_tick = now; + rt_kprintf("[UPG] RX timeout -> NAK state=%s\r\n", + chrg_ymodem_state_name(ystate)); return 1; } - south_resp = chrg_eload_get_ymodem_resp(); + 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_buf[1] = ACK_C; - pCOMM->tx_len = 2; + pCOMM->tx_len = 1; } else if (ystate == Ymodem_Rec_End) { pCOMM->tx_buf[0] = ACK; - pCOMM->tx_buf[1] = ACK_C; - pCOMM->tx_len = 2; + 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; @@ -1834,6 +2005,8 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM) 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; @@ -1843,6 +2016,8 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM) 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; @@ -1854,6 +2029,9 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM) 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; @@ -2444,11 +2622,14 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch, */ if (diff_info.cc_changed && protocol_data_changed) { /* Protocol-before-CC test flow is disabled in this version. */ - pReal->sub = IDX_SET_SINK_CC_BEFORE_PD; + pReal->sub = diff_info.cv_changed ? + IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE : + IDX_SET_SINK_CC_BEFORE_PD; } else if (diff_info.cc_changed) { pReal->sub = IDX_SET_SINK_CCLINE; } else { - pReal->sub = IDX_SET_SINK_PD_ONLY; + pReal->sub = diff_info.cv_changed ? + IDX_SET_SINK_PD_WITH_MODE : IDX_SET_SINK_PD_ONLY; } pReal->sink.work_mode = SINK_WORK_HANDSHAKE_PREPARE; } else if (protocol_voltage_only || protocol_current_change) { @@ -2457,7 +2638,8 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch, * 低电流待机本身已经WORK_STOP,无需重复提交卸载命令;运行中 * 则等待实际电流降到安全范围后再重发协议。 */ - pReal->sub = IDX_SET_SINK_PD_ONLY; + pReal->sub = diff_info.cv_changed ? + IDX_SET_SINK_PD_WITH_MODE : IDX_SET_SINK_PD_ONLY; if (old_work_mode == SINK_WORK_LOAD_STANDBY) { pReal->sink.work_mode = SINK_WORK_WAIT; } else { @@ -2792,6 +2974,11 @@ static int chrg_sink_diff_send_reg(eIDX_SOU_CH ch, rt_uint16_t vals[2]; rt_uint8_t count = 0; + if (pDiff->cv_changed) { + regs[count] = REG_MODE; + vals[count++] = pReal->CV_mode; + } + if (pReal->CV_mode == 0x01) { if (pDiff->voltage_changed) { regs[count] = REG_VOLTAGE_OUT + 1; @@ -2801,11 +2988,6 @@ static int chrg_sink_diff_send_reg(eIDX_SOU_CH ch, regs[count] = REG_CURRENT_OUT + 1; vals[count++] = pReal->sink.loadc; } - if (pDiff->cv_changed) { - regs[count] = REG_MODE; - vals[count++] = pReal->CV_mode; - } - if ((count > 0) && (chrg_sou_com_batch_submit(ch, regs, vals, count) != 0)) { LOG_E("CH%d sink diff batch submit failed", ch + 1); return -1; diff --git a/apps/chrg/applications/thread/chrg_north.h b/apps/chrg/applications/thread/chrg_north.h index b8c7d3f..24d3c99 100644 --- a/apps/chrg/applications/thread/chrg_north.h +++ b/apps/chrg/applications/thread/chrg_north.h @@ -24,9 +24,9 @@ #define WORK_MIN_VOLT 500 // 工作电压下限,单位mV #define NORTH_FRAME_DEBUG 0x00 // 北向UART原始收发帧 #define NORTH_SOURCE_DEBUG 0x01 // Source协议解析及组帧 -#define NORTH_SINK_DEBUG 0x00 // Sink协议解析及组帧 +#define NORTH_SINK_DEBUG 0x01 // Sink协议解析及组帧 #define SOUTH_FRAME_DEBUG 0x00 // 南向UART原始收发帧 -#define SOUTH_COMMAND_DEBUG 0x01 // 南向寄存器写命令 +#define SOUTH_COMMAND_DEBUG 0x00 // 南向寄存器写命令 #define COMM_FRAME_DEBUG 0x00 // COM UART原始收发帧 #define COMM_DEBUG 0x00 // COM寄存器读写过程 #define COMM_TRIM_DEBUG 0x00 // COM校准参数解析 diff --git a/apps/chrg/applications/thread/chrg_roll_nor.c b/apps/chrg/applications/thread/chrg_roll_nor.c index 2fb0829..7843710 100644 --- a/apps/chrg/applications/thread/chrg_roll_nor.c +++ b/apps/chrg/applications/thread/chrg_roll_nor.c @@ -250,8 +250,33 @@ static rt_bool_t chrg_roll_nor_sink_wait_process(rt_uint8_t ch, return RT_TRUE; } + if (pSW->sub == IDX_SET_SINK_MODE_AFTER_PD) { + rt_uint16_t reg = REG_MODE; + rt_uint16_t value = pSW->CV_mode; + + if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, ®, &value, 1U) != 0) { + return RT_TRUE; + } + pSW->sub = IDX_WAIT_SINK_MODE_AFTER_PD; + return RT_TRUE; + } + + if (pSW->sub == IDX_WAIT_SINK_MODE_AFTER_PD) { + if (chrg_sou_com_batch_is_pending((eIDX_SOU_CH)ch)) { + return RT_TRUE; + } + if (chrg_sou_com_batch_get_result((eIDX_SOU_CH)ch) != 1) { + pSW->sub = IDX_SET_SINK_MODE_AFTER_PD; + return RT_TRUE; + } + pSW->sub = IDX_GET_SINK_VC; + chrg_roll_nor_sink_query_wait_start(ch); + return RT_TRUE; + } + if ((pSW->sub != IDX_WAIT_SINK_CC) && - (pSW->sub != IDX_WAIT_SINK_CC_BEFORE_PD)) { + (pSW->sub != IDX_WAIT_SINK_CC_BEFORE_PD) && + (pSW->sub != IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE)) { /* COM在等待期间提交新配置时,清理旧事务留下的CC计时记录。 */ g_sink_cc_wait_mask &= (rt_uint8_t)~bit; g_sink_cc_wait_start[ch] = 0U; @@ -262,8 +287,10 @@ static rt_bool_t chrg_roll_nor_sink_wait_process(rt_uint8_t ch, } } else { if ((g_sink_cc_wait_mask & bit) == 0U) { - if (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) { - pSW->sub = IDX_SET_SINK_PD; + if ((pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) || + (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE)) { + pSW->sub = (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE) ? + IDX_SET_SINK_PD_WITH_MODE : IDX_SET_SINK_PD; } else { pSW->sub = IDX_GET_SINK_VC; chrg_roll_nor_sink_query_wait_start(ch); @@ -275,14 +302,16 @@ static rt_bool_t chrg_roll_nor_sink_wait_process(rt_uint8_t ch, return RT_TRUE; } - if (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) { + if ((pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) || + (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE)) { /* CC先设置完成后进入协议设置,不立即查询电压。 */ g_sink_query_throttle[ch] = RT_FALSE; g_sink_query_first_pending[ch] = RT_FALSE; g_sink_query_tick[ch] = 0U; g_sink_cc_wait_mask &= (rt_uint8_t)~bit; g_sink_cc_wait_start[ch] = 0U; - pSW->sub = IDX_SET_SINK_PD; + pSW->sub = (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE) ? + IDX_SET_SINK_PD_WITH_MODE : IDX_SET_SINK_PD; return RT_TRUE; } @@ -545,12 +574,14 @@ static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW) return SINK_CMD_GET_PD; case IDX_SET_SINK_PD: case IDX_SET_SINK_PD_ONLY: + case IDX_SET_SINK_PD_WITH_MODE: #if 0 case IDX_SET_SINK_PD_BEFORE_CC: #endif return pSW->sink.protocol; case IDX_SET_SINK_CCLINE: case IDX_SET_SINK_CC_BEFORE_PD: + case IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE: return SINK_CMD_SET_CC; case IDX_SET_SINK_CCLVL: return SINK_CMD_SET_LEVEL; @@ -762,6 +793,9 @@ static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW, case IDX_SET_SINK_CC_BEFORE_PD: *next_sub = IDX_WAIT_SINK_CC_BEFORE_PD; return sink_set_cc_line(pSW->sink.cc_set); + case IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE: + *next_sub = IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE; + return sink_set_cc_line(pSW->sink.cc_set); case IDX_SET_SINK_CCLVL: //CC电平 return sink_set_cc_lvl(pSW->sink.cc_lvl); case IDX_SET_SINK_PD: @@ -771,6 +805,9 @@ static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW, case IDX_SET_SINK_PD_ONLY: *next_sub = IDX_WAIT_SINK_PD_ACK; return chrg_roll_nor_sink_pro_set(pSW); + case IDX_SET_SINK_PD_WITH_MODE: + *next_sub = IDX_SET_SINK_MODE_AFTER_PD; + return chrg_roll_nor_sink_pro_set(pSW); #if 0 case IDX_SET_SINK_PD_BEFORE_CC: /* 协议回码确认后再设置CC,不提前查询电压。 */ @@ -885,7 +922,8 @@ static int chrg_roll_nor_auto_request(rt_uint8_t idx, (rt_uint8_t)pSW->source.protocol); } else if ((expected_id == ID_SINK) && ((request_sub == IDX_SET_SINK_CCLINE) || - (request_sub == IDX_SET_SINK_CC_BEFORE_PD))) { + (request_sub == IDX_SET_SINK_CC_BEFORE_PD) || + (request_sub == IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE))) { /* 北向Sink继电器保持接通,仅等待目标CC配置稳定。 */ chrg_roll_nor_sink_cc_wait_start(idx); } else if ((expected_id == ID_SINK) && diff --git a/apps/chrg/applications/thread/chrg_roll_nor.h b/apps/chrg/applications/thread/chrg_roll_nor.h index 5e638a3..7a3ff22 100644 --- a/apps/chrg/applications/thread/chrg_roll_nor.h +++ b/apps/chrg/applications/thread/chrg_roll_nor.h @@ -52,6 +52,11 @@ enum { IDX_SET_SINK_CC_BEFORE_PD, // 先设置CC,稳定后再设置协议 IDX_WAIT_SINK_CC_BEFORE_PD, // 等待CC稳定后进入协议设置 IDX_WAIT_SINK_PD_ACK, // 协议回码确认后的非发送过渡状态 + IDX_SET_SINK_PD_WITH_MODE, + IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE, + IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE, + IDX_SET_SINK_MODE_AFTER_PD, + IDX_WAIT_SINK_MODE_AFTER_PD, #if 0 IDX_SET_SINK_PD_BEFORE_CC, // 先设置协议,协议回码后再设置CC #endif diff --git a/docs/power_board_upgrade_issues_and_plan.md b/docs/power_board_upgrade_issues_and_plan.md new file mode 100644 index 0000000..1063714 --- /dev/null +++ b/docs/power_board_upgrade_issues_and_plan.md @@ -0,0 +1,167 @@ +# 功率板升级问题与修改计划 + +## 1. 升级链路 + +```text +上位机 + ↓ +协议板 App + ↓ 进入升级指令 +协议板南向 UART + ↓ +功率板 Boot + ↓ 0x06、0x43 +协议板 App + ↓ +上位机 +进入升级控制帧: +03 06 00 0F 00 FF F8 6B +跳转 App 控制帧: +03 06 00 0F 00 0F F8 2F +功率板 Boot 使用裸 BIN + YModem 接收升级数据。 +2. 已确认的问题 +2.1 协议板发送日志含义不准确 +协议板日志: +[UPG] CTRL ENTER try=1 tx=0 +tx=0 只表示发送请求已经提交到南向升级发送流程,不能证明 UART 已经在物理线上发送成功。 +需要通过串口逻辑分析仪确认实际线路是否出现完整控制帧。 +2.2 功率板 Boot 控制帧接收依赖一次完整 DMA 回调 +当前 Boot 主要在以下条件同时满足时识别进入升级帧: +received_len == 8 +usart1_dma_rx_buf[0] == 0x03 +UPDATE_FRAME_MATCH +如果控制帧被 DMA 空闲中断拆成多次接收,当前逻辑可能无法在拼接完成后重新判断控制帧。 +2.3 功率板 Boot 只有擦除成功后才发送应答 +处理流程: +收到完整控制帧 + ↓ +Flash_Erase_App_Region() + ↓ +擦除成功:发送 0x06、0x43 +擦除失败:发送 0x15 +因此: +- 完全没有响应:优先检查 UART 接收、帧匹配、通道切换和线路; +- 返回 0x15:重点检查 Flash 擦除; +- 返回 0x06 但协议板没有收到:检查南向返回线路和协议板接收线程。 +2.4 Flash 分区注释与实际大小不一致 +当前配置: +APP_FLASH_BASE_ADDR = 0x08003800 +APP_FLASH_SIZE = 0x0000B000 +0xB000 实际为 44 KB,而代码注释写成了 40 KB。 +同时擦除配置为: +NbPages = 22 +PAGE_SIZE = 0x800 +22 页也是 44 KB。需要确认这是否符合实际功率板 Flash 分区规划。 +2.5 南向通道和收发方向需要单独确认 +协议板升级前会执行: +chrg_south_switch(ch) +串口发送前后还会切换 RS485 收发方向。通道选择错误、方向控制延迟不足或线路未接通,都可能导致功率板无法收到控制帧或协议板收不到 0x06。 +3. 修改计划 +3.1 完善功率板 Boot 控制帧接收 +在 DMA 接收回调中: +1. 累积多次接收到的数据; +2. 达到 8 字节后统一匹配进入升级帧和跳转帧; +3. 匹配成功后设置对应 pending 标志; +4. 匹配失败时清理缓存并等待下一帧; +5. 增加接收长度和帧内容调试输出,或使用 GPIO 翻转辅助定位。 +3.2 完善协议板发送状态 +协议板需要区分: +- 发送请求提交成功; +- UART 实际发送完成; +- 功率板返回 0x06; +- 功率板返回 0x15; +- 等待超时。 +日志中应明确打印实际发送长度和发送帧内容。 +3.3 完善应答重发机制 +控制帧和 YModem 数据帧均应保留最近一次发送内容: +- 等待 ACK 超时后重发; +- 收到 NAK 后重发; +- 超过最大重试次数后退出升级; +- 收到 CAN 后立即中止; +- 半帧超时后清理接收缓存并等待下一帧。 +3.4 核对 Flash 配置 +需要确认: +- 功率板 MCU 型号; +- Flash 页面大小; +- Boot 起始地址; +- App 起始地址; +- App 最大容量; +- Boot 工程链接地址; +- App 工程链接地址; +- YModem 文件最大长度。 +确认后统一修正宏定义、擦除页数和注释。 +3.5 保持升级状态期间停止普通南向轮询 +进入升级模式后: +- 停止普通 Modbus 轮询; +- 锁定目标功率板通道; +- 只转发控制帧、YModem 数据帧和单字节应答; +- 升级结束或失败后恢复普通轮询。 +4. 待验证项目 +4.1 控制帧发送 +使用逻辑分析仪确认协议板实际发送: +03 06 00 0F 00 FF F8 6B +确认: +- 波特率为 115200; +- 数据格式为 8-N-1; +- 通道选择正确; +- RS485 发送方向正确; +- 帧没有被截断。 +4.2 功率板接收 +在功率板 Boot 的: +HAL_UARTEx_RxEventCallback() +检查: +Size +usart1_dma_rx_buf[0..7] +期望: +Size = 8 +03 06 00 0F 00 FF F8 6B +4.3 功率板帧处理 +确认以下变量和流程: +g_update_prepare_pending = 1 +Check_Updata() +Ymodem_HandleControlFrame() +Flash_Erase_App_Region() +记录 Flash 擦除返回值。 +4.4 功率板应答 +确认功率板 UART 线上依次发送: +06 43 +其中: +- 0x06 表示控制帧处理成功; +- 0x43 表示进入 YModem 接收等待。 +4.5 协议板接收 +确认协议板南向接收线程能识别: +0x06 +0x15 +0x43 +0x18 +并将应答正确放入 YModem 应答队列。 +4.6 YModem 数据传输 +确认: +- 上位机发送的是裸 BIN; +- 首包为 YModem 元数据包; +- 数据包号连续; +- 功率板每包返回 0x06; +- 丢包或 CRC 错误时返回 0x15; +- 重发后包号状态正确; +- EOT 流程能够正常结束。 +4.7 异常场景 +需要测试: +- 控制帧拆包; +- 控制帧丢失; +- 0x06 丢失; +- YModem 数据包丢失; +- YModem 数据包 CRC 错误; +- 升级中途断电; +- 升级中途复位; +- BIN 文件超过 App 分区; +- Flash 擦除失败; +- 跳转帧发送失败。 +5. 暂停调试时的结论 +当前暂不进行硬件调试。后续恢复调试时,优先使用逻辑分析仪确认以下最小链路: +协议板发送控制帧 +→ 功率板 DMA 收到 8 字节 +→ 功率板识别控制帧 +→ Flash 擦除成功 +→ 功率板发送 0x06 +→ 协议板接收到 0x06 +只有这条链路确认成功后,再继续验证 YModem 数据包传输。 \ No newline at end of file