docs: 更新修改日志并新增北向功率板升级计划文档
chore: 升级所有相关文件版本号至V1.1.39 feat: 实现北向事务隔离逻辑,新增通道切换清理和事务号匹配机制 feat: 新增北向请求开始函数,处理通道切换后的接收清理和事务号生成 perf: 调整北向回码等待超时时间,量测回码等待80ms,配置回码等待150ms refactor: 修改北向回码匹配函数,增加通道和事务号校验
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_comm.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.37
|
||||
当前版本号 : V1.1.39
|
||||
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "stm32f4xx_hal.h"
|
||||
|
||||
#define HOLD_REG_FIRMWARE_VERSION 0x0300U /* 协议板固件版本,只读 */
|
||||
#define CHRG_FIRMWARE_VERSION 0x0135U /* 当前协议板版本:V1.1.37 */
|
||||
#define CHRG_FIRMWARE_VERSION 0x0137U /* 当前协议板版本:V1.1.39 */
|
||||
|
||||
|
||||
rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_north.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.24
|
||||
当前版本号 : V1.1.39
|
||||
主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -332,6 +332,50 @@ int chrg_north_switch (eIDX_NOR_CH ch)
|
||||
chrgios[IDX_GPO_NOR_SW_B].base, (eIDX_CH)ch);
|
||||
}
|
||||
|
||||
#define NORTH_SWITCH_SETTLE_MS 2U
|
||||
#define NORTH_RX_RESET_TIMEOUT_MS 30U
|
||||
|
||||
/*
|
||||
* 开始一次北向请求:切换物理通道后先让接收线程清理旧事务,
|
||||
* 再为当前通道生成新的内部事务号,避免旧通道尾帧被误配给新请求。
|
||||
* 事务号和清理序号都不进入北向协议帧,只用于协议板内部并发隔离。
|
||||
*/
|
||||
rt_uint32_t chrg_north_request_begin(eIDX_NOR_CH ch)
|
||||
{
|
||||
struct chrg_north_t *pNOR = &chrgnorth;
|
||||
rt_uint32_t reset_request;
|
||||
rt_tick_t start_tick;
|
||||
|
||||
if ((ch >= TOTAL_NOR_CHS) || (pNOR->tty == RT_NULL)) {
|
||||
return 0U;
|
||||
}
|
||||
pNOR->idx = ch;
|
||||
/* MUX切换稳定后再请求接收线程清理,避免旧通道尾包残留到新请求。 */
|
||||
rt_thread_mdelay(NORTH_SWITCH_SETTLE_MS);
|
||||
pNOR->transaction_id[ch]++;
|
||||
if (pNOR->transaction_id[ch] == 0U) {
|
||||
pNOR->transaction_id[ch] = 1U;
|
||||
}
|
||||
/* 请求号采用非零序号;回绕时跳过0,便于区分未初始化状态。 */
|
||||
reset_request = pNOR->rx_reset_request + 1U;
|
||||
if (reset_request == 0U) {
|
||||
reset_request = 1U;
|
||||
}
|
||||
pNOR->rx_reset_request = reset_request;
|
||||
/* 接收线程观察到请求号变化后执行实际清理,发送线程只负责发起请求。 */
|
||||
(void)chrg_tty_break_recv(pNOR->tty);
|
||||
start_tick = rt_tick_get();
|
||||
while (pNOR->rx_reset_ack != reset_request) {
|
||||
if ((rt_tick_t)(rt_tick_get() - start_tick) >=
|
||||
rt_tick_from_millisecond(NORTH_RX_RESET_TIMEOUT_MS)) {
|
||||
LOG_W("north rx reset timeout: ch=%d", ch + 1U);
|
||||
return 0U;
|
||||
}
|
||||
rt_thread_mdelay(1);
|
||||
}
|
||||
return pNOR->transaction_id[ch];
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_north_send
|
||||
函数描述: 北向通道数据发送
|
||||
@@ -670,6 +714,8 @@ static int chrg_north_pickup_frame (struct rt_ringbuffer *rb,
|
||||
}
|
||||
pFrame->pMB->length = pFrame->len_msg;
|
||||
pFrame->pMB->payload = RT_NULL;
|
||||
pFrame->pMB->channel = (rt_uint8_t)idx;
|
||||
pFrame->pMB->transaction_id = chrgnorth.transaction_id[idx];
|
||||
pFrame->pMB->payload = (rt_uint8_t *)rt_malloc(pFrame->len_msg);
|
||||
if (RT_NULL == pFrame->pMB->payload) {
|
||||
LOG_E("malloc buf failed.");
|
||||
@@ -711,6 +757,28 @@ static int chrg_north_pickup_frame (struct rt_ringbuffer *rb,
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static void chrg_north_rx_reset_request_process(struct chrg_north_t *pNOR,
|
||||
struct chrg_tty_t *pTTY, eIDX_NOR_CH *pFrameIdx)
|
||||
{
|
||||
rt_uint8_t discard[32];
|
||||
|
||||
if ((pNOR == RT_NULL) || (pTTY == RT_NULL) ||
|
||||
(pFrameIdx == RT_NULL) ||
|
||||
(pNOR->rx_reset_ack == pNOR->rx_reset_request)) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* 只由北向接收线程执行:在同一上下文中复位环形缓冲区、未完成帧,
|
||||
* 并排空UART驱动FIFO,避免与接收操作并发访问这些状态。
|
||||
*/
|
||||
rt_ringbuffer_reset(pNOR->rb);
|
||||
chrg_north_frame_reset(&pNOR->frame, RT_TRUE);
|
||||
while (rt_device_read(pTTY->dev, 0, discard, sizeof(discard)) > 0) {
|
||||
}
|
||||
*pFrameIdx = pNOR->idx;
|
||||
pNOR->rx_reset_ack = pNOR->rx_reset_request;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_north_thread_entry
|
||||
函数描述: 北向通道数据接收线程体
|
||||
@@ -756,6 +824,10 @@ void chrg_north_thread_entry (void *data)
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (pNOR->rx_reset_ack != pNOR->rx_reset_request) {
|
||||
chrg_north_rx_reset_request_process(pNOR, pTTY, &frame_idx);
|
||||
continue;
|
||||
}
|
||||
if (pNOR->frame.findhead &&
|
||||
((rt_tick_t)(rt_tick_get() - pNOR->frame.last_tick) >=
|
||||
rt_tick_from_millisecond(NORTH_FRAME_TIMEOUT_MS))) {
|
||||
@@ -765,6 +837,11 @@ void chrg_north_thread_entry (void *data)
|
||||
|
||||
rt_memset(pNOR->rx_buf, 0, sizeof(pNOR->rx_buf));
|
||||
len = chrg_tty_recv(pTTY, pNOR->rx_buf, sizeof(pNOR->rx_buf));
|
||||
if (pNOR->rx_reset_ack != pNOR->rx_reset_request) {
|
||||
/* 切换请求到达期间读出的字节属于旧事务,整批丢弃。 */
|
||||
chrg_north_rx_reset_request_process(pNOR, pTTY, &frame_idx);
|
||||
continue;
|
||||
}
|
||||
if (len <= 0) {
|
||||
rt_thread_mdelay(1);
|
||||
continue;
|
||||
@@ -779,6 +856,7 @@ void chrg_north_thread_entry (void *data)
|
||||
rt_ringbuffer_reset(pNOR->rb);
|
||||
chrg_north_frame_reset(&pNOR->frame, RT_TRUE);
|
||||
frame_idx = pNOR->idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
put_len = rt_ringbuffer_put(pNOR->rb, pNOR->rx_buf, len);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_north.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.24
|
||||
当前版本号 : V1.1.39
|
||||
主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -133,6 +133,11 @@ struct chrg_north_t {
|
||||
struct rt_ringbuffer *rb;
|
||||
struct pickup_info_t frame;
|
||||
rt_uint32_t config_version[TOTAL_NOR_CHS]; // COM配置版本,用于识别等待期间到达的旧回码
|
||||
/* 每个物理通道独立递增;仅在协议板内部标识一次北向请求。 */
|
||||
volatile rt_uint32_t transaction_id[TOTAL_NOR_CHS];
|
||||
/* 通道切换时由发送线程递增请求号,接收线程完成清理后写回确认号。 */
|
||||
volatile rt_uint32_t rx_reset_request;
|
||||
volatile rt_uint32_t rx_reset_ack;
|
||||
struct chrg_trim trim[TOTAL_NOR_CHS];
|
||||
rt_uint8_t rx_buf[SIZE_BUF_TTY];
|
||||
rt_uint8_t free_flag;
|
||||
@@ -178,6 +183,9 @@ extern void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
|
||||
*****************************************************************/
|
||||
extern int chrg_north_switch (eIDX_NOR_CH ch);
|
||||
|
||||
/* 物理通道切换后清理旧接收数据并建立新事务,返回0表示准备失败。 */
|
||||
extern rt_uint32_t chrg_north_request_begin(eIDX_NOR_CH ch);
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_north_send
|
||||
函数描述: 北向通道数据发送
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_roll_nor.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.35
|
||||
当前版本号 : V1.1.39
|
||||
主要功能 : 实现北向通信轮巡请求命令。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -27,7 +27,8 @@
|
||||
#define LOG_TAG "chrg.rollnor"
|
||||
#include <rtdbg.h>
|
||||
|
||||
#define NORTH_RESPONSE_TIMEOUT_MS 50U // 单次北向请求等待匹配回码的总时间
|
||||
#define NORTH_RESPONSE_TIMEOUT_MEASURE_MS 80U // 0x20量测回码等待时间,避免单通道阻塞过久
|
||||
#define NORTH_RESPONSE_TIMEOUT_CONFIG_MS 150U // 协议/CC配置回码等待时间,给功率板生效留出时间
|
||||
#define NORTH_REQUEST_MAX_ATTEMPTS 1U // 每次轮到通道只发送并等待一次,超时后留给下一轮
|
||||
#define NORTH_POLL_INTERVAL_MS 10U // 一个通道事务结束后到下一通道的轮询间隔
|
||||
#define SOURCE_PROTOCOL_WAIT_MS 100U // 每通道0xD0应答后的协议生效保护时间
|
||||
@@ -593,15 +594,17 @@ static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW)
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_roll_nor_response_matches
|
||||
函数描述: 校验北向回码与当前请求的通道角色、命令和固定长度是否匹配
|
||||
函数描述: 校验北向回码与当前请求的通道、事务号、角色、命令和固定长度是否匹配
|
||||
输入参数: pMB:已通过north.c结构及CRC校验的回码 expected_idx:请求通道
|
||||
expected_id:请求角色 expected_cmd:请求命令
|
||||
expected_id:请求角色 expected_cmd:请求命令 transaction_id:请求内部事务号
|
||||
输出参数: -
|
||||
返回说明: 0:回码匹配 <0:参数错误或回码与请求不匹配
|
||||
其它说明: 本函数只判断请求归属,业务解析在配置版本复核通过后执行
|
||||
其它说明: 通道和事务号只用于协议板内部,不改变北向协议帧;本函数只判断请求归属,
|
||||
业务解析在配置版本复核通过后执行
|
||||
*****************************************************************/
|
||||
static int chrg_roll_nor_response_matches(const struct mb_msg_t *pMB,
|
||||
rt_uint8_t expected_idx, eIDX_ID expected_id, rt_uint8_t expected_cmd)
|
||||
rt_uint8_t expected_idx, eIDX_ID expected_id, rt_uint8_t expected_cmd,
|
||||
rt_uint32_t transaction_id)
|
||||
{
|
||||
if ((pMB == RT_NULL) || (pMB->payload == RT_NULL) ||
|
||||
(pMB->length < MIN_FRAME_LENGTH) ||
|
||||
@@ -616,6 +619,10 @@ static int chrg_roll_nor_response_matches(const struct mb_msg_t *pMB,
|
||||
pMB->payload[3], expected_cmd);
|
||||
return -2;
|
||||
}
|
||||
if ((pMB->channel != expected_idx) ||
|
||||
(pMB->transaction_id != transaction_id)) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if ((expected_cmd == SRC_CMD_GET_VOLTAGE) && (pMB->length != 21U)) {
|
||||
return -3;
|
||||
@@ -679,7 +686,7 @@ static void chrg_roll_nor_drain_mailbox(struct chrg_thread_t *pTN)
|
||||
*****************************************************************/
|
||||
static int chrg_roll_nor_wait_response(struct chrg_thread_t *pTN,
|
||||
rt_uint8_t expected_idx, eIDX_ID expected_id, rt_uint8_t expected_cmd,
|
||||
struct mb_msg_t **matched_response)
|
||||
rt_uint32_t transaction_id, struct mb_msg_t **matched_response)
|
||||
{
|
||||
rt_tick_t start_tick;
|
||||
rt_tick_t timeout_tick;
|
||||
@@ -694,7 +701,9 @@ static int chrg_roll_nor_wait_response(struct chrg_thread_t *pTN,
|
||||
}
|
||||
*matched_response = RT_NULL;
|
||||
start_tick = rt_tick_get();
|
||||
timeout_tick = rt_tick_from_millisecond(NORTH_RESPONSE_TIMEOUT_MS);
|
||||
timeout_tick = rt_tick_from_millisecond(
|
||||
(expected_cmd == SRC_CMD_GET_VOLTAGE) ?
|
||||
NORTH_RESPONSE_TIMEOUT_MEASURE_MS : NORTH_RESPONSE_TIMEOUT_CONFIG_MS);
|
||||
|
||||
while (1) {
|
||||
elapsed = (rt_tick_t)(rt_tick_get() - start_tick);
|
||||
@@ -711,7 +720,7 @@ static int chrg_roll_nor_wait_response(struct chrg_thread_t *pTN,
|
||||
}
|
||||
|
||||
if (chrg_roll_nor_response_matches(pMB, expected_idx,
|
||||
expected_id, expected_cmd) == 0) {
|
||||
expected_id, expected_cmd, transaction_id) == 0) {
|
||||
*matched_response = pMB;
|
||||
return 0;
|
||||
}
|
||||
@@ -841,6 +850,7 @@ static int chrg_roll_nor_auto_request(rt_uint8_t idx,
|
||||
rt_uint8_t expected_cmd = 0; // 本次请求期望收到的命令码
|
||||
rt_uint8_t max_attempts; // 本次请求允许的总发送次数
|
||||
eIDX_ID expected_id = ID_SINK; // 本次请求发送时的角色快照
|
||||
rt_uint32_t transaction_id = 0; // 本次请求内部事务号
|
||||
rt_uint32_t request_version = 0; // 本次请求发送时的COM配置版本
|
||||
int send_ret; // 当前命令的串口发送结果
|
||||
int wait_ret; // 当前命令的回码等待结果
|
||||
@@ -862,6 +872,7 @@ static int chrg_roll_nor_auto_request(rt_uint8_t idx,
|
||||
expected_id = pSW->id;
|
||||
expected_cmd = chrg_roll_nor_expected_cmd(pSW);
|
||||
request_version = pNOR->config_version[idx];
|
||||
transaction_id = pNOR->transaction_id[idx];
|
||||
/* 新事务发送前清理已结束或已超时事务遗留的邮箱回码。 */
|
||||
chrg_roll_nor_drain_mailbox(pTN);
|
||||
}
|
||||
@@ -875,7 +886,7 @@ static int chrg_roll_nor_auto_request(rt_uint8_t idx,
|
||||
|
||||
if (send_ret > 0) {
|
||||
wait_ret = chrg_roll_nor_wait_response(pTN, idx,
|
||||
expected_id, expected_cmd, &pMB);
|
||||
expected_id, expected_cmd, transaction_id, &pMB);
|
||||
if ((wait_ret == 0) && (pMB != RT_NULL)) {
|
||||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||||
if ((pNOR->config_version[idx] != request_version) ||
|
||||
@@ -1047,8 +1058,12 @@ void chrg_roll_nor_thread_entry (void *data)
|
||||
continue;
|
||||
} else {
|
||||
chrg_led_flashing(IDX_LED2, TIMES_ONE, PULSE_TIME*2, PULSE_TIME*2);
|
||||
chrg_north_switch((eIDX_NOR_CH)idx);
|
||||
chrgnorth.idx = idx;
|
||||
if ((chrg_north_switch((eIDX_NOR_CH)idx) != 0) ||
|
||||
(chrg_north_request_begin((eIDX_NOR_CH)idx) == 0U)) {
|
||||
CHRG_CHANNEL_NEXT(index);
|
||||
rt_thread_mdelay(NORTH_POLL_INTERVAL_MS);
|
||||
continue;
|
||||
}
|
||||
pROLL->idx = idx;
|
||||
(void)chrg_roll_nor_auto_request(idx, pSW,
|
||||
pTHR, pTN, pNOR);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_def.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.37
|
||||
当前版本号 : V1.1.39
|
||||
主要功能 : 全局宏定义,常量,及结构体。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -15,8 +15,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 37U
|
||||
#define CHRG_VERSION_STRING "V1.1.37"
|
||||
#define CHRG_VERSION_PATCH 39U
|
||||
#define CHRG_VERSION_STRING "V1.1.39"
|
||||
|
||||
/*
|
||||
* Channel polling mode:
|
||||
@@ -114,6 +114,8 @@ struct vc_pd_t {
|
||||
struct mb_msg_t {
|
||||
rt_uint16_t length; // 数据长度
|
||||
rt_uint8_t *payload; // 数据区
|
||||
rt_uint8_t channel; // 接收物理通道(北向消息使用),用于匹配当前轮询通道
|
||||
rt_uint32_t transaction_id; // 北向请求内部事务号,不进入通信协议帧,仅用于丢弃旧回码
|
||||
};
|
||||
|
||||
struct pickup_info_t {
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# 修改日志
|
||||
|
||||
## V1.1.39 - 2026-09-24
|
||||
|
||||
### 北向事务隔离逻辑注释
|
||||
|
||||
- 补充通道切换接收清理、内部事务号和回码匹配逻辑的中文注释。
|
||||
- 协议板版本寄存器 `0x0300` 更新为 `0x0137`。
|
||||
|
||||
## V1.1.38 - 2026-09-24
|
||||
|
||||
### 北向事务超时与通道切换隔离
|
||||
|
||||
- 北向 `0x20` 量测回码等待 `80ms`,协议和CC配置回码等待 `150ms`。
|
||||
- 北向邮箱消息增加物理通道和内部事务号,只接受当前请求对应的回码,不改变北向协议帧。
|
||||
- 物理通道切换后清空驱动接收FIFO、环形缓冲区和未完成帧,等待接收线程确认后再发送。
|
||||
- 协议板版本寄存器 `0x0300` 更新为 `0x0136`。
|
||||
|
||||
## V1.1.37 - 2026-09-21
|
||||
|
||||
### Sink功率板角色同步
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
# 功率板升级:北向转发计划
|
||||
|
||||
## 1. 范围与原则
|
||||
|
||||
北向链路为“PC 上位机 ↔ 协议板 App”。协议板通过功能码 `0x20` 进入指定功率板的升级代理模式,之后北向链路切换为原始 YMODEM 字节流。
|
||||
|
||||
当前功率板 Boot 仅将 YMODEM 数据区直接写入 `APP_FLASH_BASE_ADDR`,本阶段升级文件必须是可直接启动的裸 APP 镜像。若交付物带 `.upk` 头、分区或校验字段,必须先实现 Boot 解包和最终完整性校验,未完成前不得透传该 `.upk`。
|
||||
|
||||
升级期间采用以下原则:
|
||||
|
||||
1. PC 是 YMODEM 主机,负责包号推进、NAK 重发和完整时序。
|
||||
2. 协议板只拼帧、校验并转发,不在内部自动重发 YMODEM 数据帧。
|
||||
3. 协议板给 PC 的 ACK、NAK、`C`、CAN 必须来自南向功率板 Boot,不允许提前伪造成功应答。
|
||||
4. 同一时刻只允许一个北向连接升级一个功率板通道。
|
||||
|
||||
## 2. 北向进入和退出命令
|
||||
|
||||
### 2.1 进入升级
|
||||
|
||||
PC 发送 8 字节 Modbus RTU 扩展命令:
|
||||
|
||||
```text
|
||||
Byte 0 协议板地址
|
||||
Byte 1 0x20
|
||||
Byte 2 保留,固定 0x00
|
||||
Byte 3 保留,固定 0x00
|
||||
Byte 4 功率板通道号,范围 0..TOTAL_SOU_CHS-1
|
||||
Byte 5 0xFF,进入升级
|
||||
Byte 6 CRC_L
|
||||
Byte 7 CRC_H
|
||||
```
|
||||
|
||||
协议板必须完成以下操作后才能回复成功:
|
||||
|
||||
1. 校验地址、长度、通道和 Modbus CRC。
|
||||
2. 锁定目标通道并停止普通南向轮询。
|
||||
3. 南向发送进入升级控制帧。
|
||||
4. 收到功率板 Boot 的 ACK;若同时收到 `C`,必须保留并在北向响应完成后转发。
|
||||
|
||||
成功响应:
|
||||
|
||||
```text
|
||||
协议板地址 + 0x06 + CRC_L + CRC_H
|
||||
```
|
||||
|
||||
广播地址 `0x00` 不回复。失败使用现有 Modbus 异常响应,不得进入原始 YMODEM 模式。
|
||||
|
||||
### 2.2 退出升级/跳转 App
|
||||
|
||||
在升级模式中,PC 发送同格式的 8 字节命令,Byte 5 为 `0x00`。协议板南向发送跳转 App 控制帧;功率板 Boot 必须先发送 `ACK` 并确认 UART 发送完成,再执行跳转。协议板收到该 ACK 后退出升级模式并恢复普通轮询。
|
||||
|
||||
结束空 SOH 获得 ACK 只表示 YMODEM 传输结束;PC 仍须发送退出命令,并在恢复通信后读取功率板版本确认升级结果。
|
||||
|
||||
## 3. 北向 YMODEM 包格式
|
||||
|
||||
进入升级成功后,北向不再使用 Modbus 封装,直接传输 YMODEM 字节流。
|
||||
|
||||
| 类型 | 完整长度 | 格式 |
|
||||
| --- | ---: | --- |
|
||||
| 元数据 SOH | 133 | `01 + pkt + ~pkt + 128B Data + CRC16_H + CRC16_L` |
|
||||
| 数据 STX | 1029 | `02 + pkt + ~pkt + 1024B Data + CRC16_H + CRC16_L` |
|
||||
| 可选数据 SOH | 133 | `01 + pkt + ~pkt + 128B Data + CRC16_H + CRC16_L` |
|
||||
| EOT | 1 | `04` |
|
||||
| CAN | 1 或 2 | `18`,取消时建议连续两个 |
|
||||
|
||||
CRC 使用 CRC-16/XMODEM:`poly=0x1021`、`init=0x0000`、不反射、`xorout=0x0000`。
|
||||
|
||||
元数据包固定为包号 0,文件名和长度对应完整功率板 `.upk` 文件:
|
||||
|
||||
```text
|
||||
文件名 + 0x00 + `.upk` 实际十进制长度 + 0x00 + 其余补 0x00
|
||||
```
|
||||
|
||||
数据从包号 1 开始。首个 STX 携带 `.upk` 文件偏移 `0~1023` 的原始字节,后续帧按文件偏移连续发送;协议板不解释 UPK 内部字段。最后一帧不足固定数据区时用 `0x1A` 补齐,但补齐字节不计入元数据中的 `.upk` 文件长度。
|
||||
|
||||
## 4. 北向状态机
|
||||
|
||||
```text
|
||||
N_IDLE
|
||||
收到合法 0x20/进入命令
|
||||
-> N_ENTERING
|
||||
|
||||
N_ENTERING
|
||||
南向确认进入 Boot
|
||||
-> 回复 0x20 成功
|
||||
-> N_WAIT_C
|
||||
失败/超时
|
||||
-> 回复异常
|
||||
-> N_IDLE
|
||||
|
||||
N_WAIT_C
|
||||
收到南向 C -> 原样转发 -> N_WAIT_META
|
||||
|
||||
N_WAIT_META
|
||||
收到完整且本地校验正确的 SOH/包0
|
||||
-> 原样提交南向
|
||||
-> N_WAIT_META_RESP
|
||||
|
||||
N_WAIT_META_RESP
|
||||
南向 ACK -> 转发 ACK
|
||||
南向 C -> 转发 C -> N_WAIT_DATA
|
||||
南向 NAK -> 转发 NAK -> N_WAIT_META
|
||||
|
||||
N_WAIT_DATA
|
||||
收到完整 SOH/STX 数据帧
|
||||
-> 原样提交南向
|
||||
-> N_WAIT_DATA_RESP
|
||||
收到 EOT
|
||||
-> 原样提交南向
|
||||
-> N_WAIT_EOT1_RESP
|
||||
收到 CAN
|
||||
-> 原样转发南向
|
||||
-> N_ABORT
|
||||
|
||||
N_WAIT_DATA_RESP
|
||||
南向 ACK -> 转发 ACK -> N_WAIT_DATA
|
||||
南向 NAK -> 转发 NAK -> N_WAIT_DATA
|
||||
南向 CAN -> 转发 CAN CAN -> N_ABORT
|
||||
|
||||
N_WAIT_EOT1_RESP
|
||||
南向 NAK -> 原样转发 -> N_WAIT_EOT2
|
||||
|
||||
N_WAIT_EOT2
|
||||
收到第二个 EOT -> 原样提交南向 -> N_WAIT_EOT2_RESP
|
||||
|
||||
N_WAIT_EOT2_RESP
|
||||
南向 ACK -> 转发 ACK
|
||||
南向 C -> 转发 C -> N_WAIT_END_SOH
|
||||
|
||||
N_WAIT_END_SOH
|
||||
收到包号0、128字节全0的 SOH
|
||||
-> 原样提交南向
|
||||
-> N_WAIT_END_ACK
|
||||
|
||||
N_WAIT_END_ACK
|
||||
南向 ACK -> 转发 ACK -> N_TRANSFER_DONE
|
||||
|
||||
N_TRANSFER_DONE
|
||||
收到合法 0x20/退出命令 -> N_EXITING
|
||||
|
||||
N_EXITING
|
||||
南向跳转确认 -> 回复命令成功 -> 恢复轮询 -> N_VERIFY
|
||||
|
||||
N_VERIFY
|
||||
PC通过恢复后的正常协议读取功率板版本
|
||||
版本正确 -> 升级确认成功 -> N_IDLE
|
||||
版本不符/无响应 -> 升级确认失败 -> N_IDLE
|
||||
```
|
||||
|
||||
## 5. 北向完整时序
|
||||
|
||||
```text
|
||||
PC -> 协议板 : 0x20进入命令
|
||||
协议板 -> 功率板 : 进入升级控制帧
|
||||
功率板 -> 协议板 : ACK, C
|
||||
协议板 -> PC : 0x20成功响应
|
||||
协议板 -> PC : C
|
||||
|
||||
PC -> 协议板 : SOH包0元数据
|
||||
协议板 -> 功率板 : 原样SOH
|
||||
功率板 -> 协议板 : ACK, C
|
||||
协议板 -> PC : ACK, C
|
||||
|
||||
PC -> 协议板 : STX包1
|
||||
协议板 -> 功率板 : 原样STX包1
|
||||
功率板 -> 协议板 : ACK
|
||||
协议板 -> PC : ACK
|
||||
...逐包停止等待...
|
||||
|
||||
PC -> 协议板 : EOT
|
||||
协议板 -> 功率板 : EOT
|
||||
功率板 -> 协议板 : NAK
|
||||
协议板 -> PC : NAK
|
||||
|
||||
PC -> 协议板 : EOT
|
||||
协议板 -> 功率板 : EOT
|
||||
功率板 -> 协议板 : ACK, C
|
||||
协议板 -> PC : ACK, C
|
||||
|
||||
PC -> 协议板 : SOH包0,Data全0
|
||||
协议板 -> 功率板 : 原样SOH
|
||||
功率板 -> 协议板 : ACK
|
||||
协议板 -> PC : ACK
|
||||
|
||||
PC -> 协议板 : 0x20退出命令
|
||||
协议板 -> 功率板 : 跳转App控制帧
|
||||
功率板 -> 协议板 : ACK(发送完成后才跳转App)
|
||||
协议板 -> PC : 0x20成功响应
|
||||
协议板 : 恢复轮询并刷新功率板版本
|
||||
PC -> 协议板 : 通过正常协议查询功率板版本并确认升级结果
|
||||
```
|
||||
|
||||
## 6. 北向校验和异常处理
|
||||
|
||||
- 北向可分多次收到一帧,必须按帧头确定 133/1029/1 字节并累计完整后再转发。
|
||||
- 同一帧内部超时建议 1000 ms;超时后清空半帧并向 PC 发送 NAK。
|
||||
- 等待南向应答时禁止接受下一数据帧,严格执行停止等待。
|
||||
- 任意传输阶段收到 PC 的 CAN 时,应转发南向、终止会话并恢复普通轮询。
|
||||
- 本地包号反码或 CRC 错误:不转发南向,直接向 PC 发送 NAK。
|
||||
- 南向 NAK:原样转发 PC,由 PC 重发;协议板不得自行重发。
|
||||
- 南向应答超时:尽可能向功率板和 PC 各发送 CAN CAN,结束会话;不得盲目重发已可能写入的帧。
|
||||
- 北向 ACK 等待建议由 PC 设置为 10 秒;进入升级擦除等待建议 15 秒。
|
||||
- 升级会话空闲超过 15 秒,发送 CAN CAN、清理缓存、恢复普通轮询。
|
||||
|
||||
## 7. 北向实施清单
|
||||
|
||||
1. 删除数据帧和 EOT 的协议板内部自动重发,改为原样转发 NAK。
|
||||
2. 为两次 EOT 建立独立状态,禁止把第一次 EOT 的 NAK当成普通失败。
|
||||
3. 校验元数据包号、连续数据包号和结束空包内容。
|
||||
4. 只有收到南向真实应答后才回复 PC。
|
||||
5. 结束后执行退出命令和版本读取,区分“传输完成”与“升级确认成功”。
|
||||
6. 日志至少记录通道、状态、帧类型、包号、长度、南向应答和失败原因。
|
||||
7. 联调前确认升级文件为裸 APP 镜像;若使用 UPK,先完成 Boot 端解包和完整性校验。
|
||||
@@ -0,0 +1,207 @@
|
||||
# 功率板升级:南向发送与应答计划
|
||||
|
||||
## 1. 范围与原则
|
||||
|
||||
南向链路为“协议板 App ↔ 目标功率板 Boot”。当前功率板 Boot 仅将 YMODEM 数据区直接写入 `APP_FLASH_BASE_ADDR`,因此本阶段只允许传输裸 APP 镜像。协议板不修改镜像内容,只负责锁定物理通道、连续发送完整 YMODEM 帧、采集功率板的单字节应答,并将应答按原顺序交给北向状态机。UPK 解包、分区、版本、加密和最终 CRC 校验属于后续 Boot 能力,未实现前不得向本 Boot 透传 `.upk`。
|
||||
|
||||
南向升级期间停止普通 Modbus 轮询、LCD 插入命令、控制台命令和其他通道请求。升级数据不得经过普通 Modbus解析器。
|
||||
|
||||
协议板只校验 YMODEM 帧长度、包号反码和 CRC16;当前 Boot 的 APP 可写范围是 `0x08003800 + 0xB000`(44 KB),裸 APP 镜像长度必须受此范围限制。
|
||||
|
||||
## 2. 串口和控制帧
|
||||
|
||||
南向串口参数:115200 bps、8N1、无流控。每次发送前必须确认目标通道已选通,并正确控制 RS485 收发方向。
|
||||
|
||||
进入功率板 Boot:
|
||||
|
||||
```text
|
||||
03 06 00 0F 00 FF F8 6B
|
||||
```
|
||||
|
||||
功率板正常应答:
|
||||
|
||||
```text
|
||||
06 43
|
||||
```
|
||||
|
||||
其中 `06` 表示进入命令执行成功,`43` 表示 Boot 已准备接收 CRC 模式 YMODEM。
|
||||
|
||||
跳转功率板 App:
|
||||
|
||||
```text
|
||||
03 06 00 0F 00 0F F8 2F
|
||||
```
|
||||
|
||||
正常应答为 `06`;`15` 表示失败。Boot 必须在 UART 完整发送 `06` 后才关闭 DMA/中断并跳转 App,避免协议板因未收到应答而超时。
|
||||
|
||||
## 3. 南向帧和应答映射
|
||||
|
||||
| 协议板发送 | 功率板 Boot 正常回复 | 协议板处理 |
|
||||
| --- | --- | --- |
|
||||
| 进入升级控制帧 | `ACK + C` | ACK确认进入,C进入应答队列,按顺序交北向 |
|
||||
| 元数据 SOH/包0 | `ACK + C` | 两字节均按顺序交北向 |
|
||||
| 普通 SOH/STX | `ACK` | 交北向,允许下一帧 |
|
||||
| 校验错误的数据帧 | `NAK` | 原样交北向,不在南向自动重发 |
|
||||
| 第一次 EOT | `NAK` | 这是正常握手,原样交北向 |
|
||||
| 第二次 EOT | `ACK + C` | 两字节均按顺序交北向 |
|
||||
| 结束空 SOH/包0 | `ACK` | 交北向,标记传输完成 |
|
||||
| CAN | `ACK`或无回复 | 停止会话并清理缓存 |
|
||||
| 跳转 App 控制帧 | `ACK` | 退出升级模式并恢复轮询 |
|
||||
|
||||
## 4. 南向状态机
|
||||
|
||||
```text
|
||||
S_IDLE
|
||||
北向进入请求 -> 锁通道、停轮询 -> S_ENTER_TX
|
||||
|
||||
S_ENTER_TX
|
||||
UART发送完成 -> S_ENTER_WAIT
|
||||
|
||||
S_ENTER_WAIT
|
||||
ACK -> 记录进入成功
|
||||
C -> 记录Boot就绪 -> S_WAIT_META
|
||||
NAK/超时 -> S_ABORT
|
||||
|
||||
S_WAIT_META
|
||||
收到北向完整元数据帧 -> S_META_TX
|
||||
|
||||
S_META_TX
|
||||
UART发送完成 -> S_META_WAIT
|
||||
|
||||
S_META_WAIT
|
||||
ACK -> 转交北向
|
||||
C -> 转交北向 -> S_WAIT_DATA
|
||||
NAK -> 转交北向 -> S_WAIT_META
|
||||
|
||||
S_WAIT_DATA
|
||||
收到SOH/STX -> S_DATA_TX
|
||||
收到EOT -> S_EOT1_TX
|
||||
|
||||
S_DATA_TX
|
||||
UART发送完成 -> S_DATA_WAIT
|
||||
|
||||
S_DATA_WAIT
|
||||
ACK -> 转交北向 -> S_WAIT_DATA
|
||||
NAK -> 转交北向 -> S_WAIT_DATA
|
||||
CAN -> 转交北向 -> S_ABORT
|
||||
|
||||
S_EOT1_TX
|
||||
UART发送完成 -> S_EOT1_WAIT
|
||||
|
||||
S_EOT1_WAIT
|
||||
NAK -> 转交北向 -> S_WAIT_EOT2
|
||||
|
||||
S_WAIT_EOT2
|
||||
收到第二次EOT -> S_EOT2_TX
|
||||
|
||||
S_EOT2_TX
|
||||
UART发送完成 -> S_EOT2_WAIT
|
||||
|
||||
S_EOT2_WAIT
|
||||
ACK -> 转交北向
|
||||
C -> 转交北向 -> S_WAIT_END_SOH
|
||||
|
||||
S_WAIT_END_SOH
|
||||
收到结束空SOH -> S_END_TX
|
||||
|
||||
S_END_TX
|
||||
UART发送完成 -> S_END_WAIT
|
||||
|
||||
S_END_WAIT
|
||||
ACK -> 转交北向 -> S_TRANSFER_DONE
|
||||
|
||||
S_TRANSFER_DONE
|
||||
收到退出请求 -> S_JUMP_TX
|
||||
|
||||
S_JUMP_TX
|
||||
UART发送完成 -> S_JUMP_WAIT
|
||||
|
||||
S_JUMP_WAIT
|
||||
ACK -> 确认已转交北向 -> 恢复轮询 -> S_IDLE
|
||||
NAK/超时 -> S_ABORT
|
||||
|
||||
S_ABORT
|
||||
清理应答队列和半帧,解除通道锁,恢复轮询 -> S_IDLE
|
||||
```
|
||||
|
||||
## 5. 发送完成与计时起点
|
||||
|
||||
`chrg_send_sou_upadata_data()` 返回成功只能表示帧已提交到发送线程,不能表示 UART 已发送完成。
|
||||
|
||||
南向必须区分:
|
||||
|
||||
1. 提交成功;
|
||||
2. 通道切换成功;
|
||||
3. UART 开始发送;
|
||||
4. UART 发送完成;
|
||||
5. 功率板返回应答。
|
||||
|
||||
所有应答超时必须从 UART 发送完成时开始计算,不能从放入缓存时开始。建议:
|
||||
|
||||
| 阶段 | 超时 |
|
||||
| --- | ---: |
|
||||
| 进入升级并擦除 Flash | 15 秒 |
|
||||
| 元数据应答 | 10 秒 |
|
||||
| 普通数据帧应答 | 10 秒 |
|
||||
| EOT/结束空包应答 | 10 秒 |
|
||||
| 跳转 App 应答 | 5 秒 |
|
||||
|
||||
## 6. 南向应答队列
|
||||
|
||||
功率板应答是单字节流,必须使用有序队列保存 `ACK/NAK/C/CAN`,不能使用单个覆盖变量。
|
||||
|
||||
- `ACK + C` 必须保留两个字节和原顺序。
|
||||
- 队列满时可优先丢弃等待阶段重复出现的周期 `C`,不能丢弃 ACK、NAK 或 CAN。
|
||||
- 切换通道、进入会话、退出会话前必须清空旧应答。
|
||||
- 等待当前帧 ACK 时收到周期 `C`,可以暂存或忽略,但不能将其误判为当前帧成功。
|
||||
- 普通 Modbus 调试文本或其他字节不得进入 YMODEM 应答队列。
|
||||
|
||||
## 7. 重试和异常策略
|
||||
|
||||
YMODEM 数据帧的重试由北向 PC 负责。南向代理不得在收到 NAK 或应答超时后自行重发上一帧,否则会破坏第一次 EOT 的正常 NAK 时序,并可能在 ACK 丢失时重复写入。
|
||||
|
||||
控制帧也不能无条件重发:进入命令的 ACK 丢失但功率板已经进入 Boot 时,再发送 Modbus 控制帧会被当成 YMODEM 垃圾数据。处理方式:
|
||||
|
||||
- 只有收到 ACK 才确认进入升级成功;随后收到 C 才确认功率板 Boot 已准备接收元数据。若只收到 C,应记录异常并终止本次会话,不能向北向伪造进入成功响应。
|
||||
- ACK 或 C 缺失时终止本次会话,要求重新复位/重新进入,不盲目重发控制帧。
|
||||
- 南向 NAK、CAN 或应答超时应明确上报北向;不得伪造 ACK。
|
||||
- 发送失败、通道切换失败或队列溢出时立即中止会话。
|
||||
- 北向取消或会话异常终止时,尽可能向功率板连续发送两个 CAN,再清理状态并恢复普通轮询。
|
||||
|
||||
## 8. 与功率板 Boot 的接口要求
|
||||
|
||||
功率板 Boot 必须满足:
|
||||
|
||||
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 跳转。
|
||||
|
||||
## 9. 南向验证顺序
|
||||
|
||||
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 帧。
|
||||
|
||||
## 10. 南向实施清单
|
||||
|
||||
1. 删除南向收到 NAK或超时后的内部 YMODEM重发。
|
||||
2. 增加独立的 EOT1、EOT2和结束空包状态。
|
||||
3. 以 UART发送完成事件启动应答计时。
|
||||
4. 保证 ACK、C 等应答有序排队和转交。
|
||||
5. 进入和退出控制帧禁止盲目重试。
|
||||
6. 升级期间锁定通道并完全停止普通轮询。
|
||||
7. 完成后恢复轮询并提供版本确认结果。
|
||||
8. 先修复 Boot:进入控制帧后清接收缓存、跳转前回 ACK、UART 单一 DMA 接收重启路径。
|
||||
9. 未实现 Boot 端 UPK 解包和最终完整性校验前,升级文件限定为裸 APP 镜像。
|
||||
Reference in New Issue
Block a user