chrg: 完成V1.1.31版本迭代更新

1.  更新各文件版本号至V1.1.31,固件版本寄存器设为0x012F
2.  南向通道选通:仅在GPIO选通成功后才更新物理通道缓存
3.  重构寄存器地址解码逻辑,统一0x06/0x10功能码的地址校验
4.  新增功率板短路/OCP/动态测试状态定义0x05/0x06/0x07
5.  移除电流命令覆盖逻辑,改为按顺序排队等待回码
6.  优化轮询线程:每次发送前重新选通通道,选通失败则跳过当前任务
7.  新增测试态退出确认流程,初始化和关机时需确认功率板退出测试态
8.  增加跨通道写入保护,拒绝多寄存器写跨通道操作
9.  补充多处中文注释,优化调试日志输出格式
This commit is contained in:
wrh
2026-09-17 12:05:37 +08:00
parent c55f05e838
commit b58b47f64a
7 changed files with 278 additions and 83 deletions
+59 -32
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_comm.c
完成日期 :
当前版本号 : V1.1.26
当前版本号 : V1.1.31
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
版本历史 : 创建原始版本
说明 :
@@ -955,6 +955,41 @@ static rt_uint16_t src_type_lookup(rt_uint16_t reg_val,struct chrg_switch_t *pSW
}
return 0;
}
/*****************************************************************
函数名称: chrg_comm_reg_decode
函数描述: 将上位机分区地址解码为0起始通道号和通道内寄存器偏移
输入参数: partition:分区高字节 address:分区低字节地址
ch:通道输出 reg_offset:通道内偏移输出
输出参数: ch、reg_offset
返回说明: 0:地址有效 <0:分区、地址或参数无效
其它说明: 统一供0x06和0x10使用,避免两条写入路径采用不同通道算法
*****************************************************************/
static int chrg_comm_reg_decode(rt_uint8_t partition,
rt_uint16_t address, eIDX_SOU_CH *ch, rt_uint16_t *reg_offset)
{
rt_uint16_t regs_per_channel;
if ((ch == RT_NULL) || (reg_offset == RT_NULL)) {
return -1;
}
if (partition == HOLD_REG_SINK_BASE) {
regs_per_channel = TOTAL_SINK_CH_REGS;
} else if (partition == HOLD_REG_SRC_BASE) {
regs_per_channel = TOTAL_SRC_CH_REGS;
} else if (partition == HOLD_REG_SYS_BASE) {
regs_per_channel = TOTAL_SYS_CH_REGS;
} else {
return -1;
}
if (address >= (regs_per_channel * TOTAL_SOU_CHS)) {
return -1;
}
*ch = (eIDX_SOU_CH)(address / regs_per_channel);
*reg_offset = address % regs_per_channel;
return 0;
}
/*****************************************************************
函数名称: funcWriteHoldingRegister
函数描述: 写单个保持寄存器(0x06功能码)
@@ -963,7 +998,7 @@ static rt_uint16_t src_type_lookup(rt_uint16_t reg_val,struct chrg_switch_t *pSW
输出参数: -
返回说明: MB_EX_NONE=成功, 其他=Modbus异常码
其它说明: 新协议采用标准0x06功能码,寄存器地址区分功能区块:
0x0000~0x0073 负载, 0x0080~0x0097 电源, 0x00A0~0x00A7 系统
高字节选择分区,低字节按每通道50个地址计算通道和寄存器偏移
*****************************************************************/
eMBException funcWriteHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
rt_uint8_t *resFrame, rt_uint16_t *resLen)
@@ -975,8 +1010,9 @@ eMBException funcWriteHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
rt_uint8_t regAddress_H = 0;
rt_uint16_t sou_value = 0;
/* ---- 提取通道号和区块内偏移 ---- */
eIDX_SOU_CH ch;
rt_uint16_t reg_offset; // 区块内寄存器偏移
/* 使用越界枚举作为无效初值,禁止地址校验失败后误用随机通道。 */
eIDX_SOU_CH ch = TOTAL_SOU_CHS;
rt_uint16_t reg_offset = 0; // 区块内寄存器偏移
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
@@ -1010,26 +1046,8 @@ eMBException funcWriteHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
}
/* ---- 地址范围校验 ---- */
if(regAddress_H == HOLD_REG_SINK_BASE){
if (REG_IS_SINK(regAddress)) {
ch = (eIDX_SOU_CH)REG_GET_SINK_CH(regAddress);
reg_offset = REG_GET_SINK_OFFSET(regAddress);
}
}else if (regAddress_H == HOLD_REG_SRC_BASE) {
if (REG_IS_SRC(regAddress)) {
ch = (eIDX_SOU_CH)REG_GET_SRC_CH(regAddress);
reg_offset = REG_GET_SRC_OFFSET(regAddress);
}
} else if (regAddress_H == HOLD_REG_SYS_BASE) {
if (REG_IS_SYS(regAddress)) {
ch = (eIDX_SOU_CH)REG_GET_SYS_CH(regAddress);
reg_offset = REG_GET_SYS_OFFSET(regAddress);
}
} else {
return MB_EX_ILLEGAL_DATA_ADDRESS;
}
if (ch > IDX_SOU_CH4) {
if (chrg_comm_reg_decode(regAddress_H, regAddress,
&ch, &reg_offset) != 0) {
return MB_EX_ILLEGAL_DATA_ADDRESS;
}
@@ -1139,6 +1157,11 @@ eMBException funcWriteMultipleHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t
#else
rt_uint8_t regAddress_H = 0;
rt_uint16_t regBaseOffset = 0;
rt_uint16_t regEndOffset = 0;
rt_uint16_t first_reg_offset = 0;
rt_uint16_t last_reg_offset = 0;
eIDX_SOU_CH first_ch = TOTAL_SOU_CHS;
eIDX_SOU_CH last_ch = TOTAL_SOU_CHS;
#endif
// ========== 公共校验逻辑(只写一遍)==========
@@ -1211,17 +1234,21 @@ eMBException funcWriteMultipleHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t
// 正常产品模式:分区地址逻辑
regAddress_H = reqFrame[2];
regBaseOffset = reqFrame[3];
regEndOffset = regBaseOffset + cntReg - 1U;
/* 起止地址必须属于同一有效通道,禁止一帧0x10跨通道改写参数。 */
if ((chrg_comm_reg_decode(regAddress_H, regBaseOffset,
&first_ch, &first_reg_offset) != 0) ||
(chrg_comm_reg_decode(regAddress_H, regEndOffset,
&last_ch, &last_reg_offset) != 0) ||
(first_ch != last_ch) ||
(last_reg_offset != (first_reg_offset + cntReg - 1U))) {
return MB_EX_ILLEGAL_DATA_ADDRESS;
}
for (int i = 0; i < cntReg; i++)
{
rt_uint16_t curr_part_offset = regBaseOffset + i;
eIDX_SOU_CH ch = (eIDX_SOU_CH)(curr_part_offset / TOTAL_HOLD_CH_REGS);
rt_uint16_t ch_reg_offset = curr_part_offset % TOTAL_HOLD_CH_REGS;
if (ch > IDX_SOU_CH4) {
//rt_kprintf("WRITE MULTI REG: CH ERROR, ch=%d (part offset=%d)\n", ch, curr_part_offset);
return MB_EX_ILLEGAL_DATA_ADDRESS;
}
eIDX_SOU_CH ch = first_ch;
rt_uint16_t ch_reg_offset = first_reg_offset + i;
pSW = &g_shadow_sw[ch];
chrg_shadow_sync(ch); // 首次写同步真实状态
rt_uint16_t sou_value = u8v_to_u16(&pWriteData[i * 2]);
+5 -5
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_comm.h
完成日期 :
当前版本号 : V1.1.24
当前版本号 : V1.1.31
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
版本历史 : 创建原始版本
说明 :
@@ -40,10 +40,10 @@ typedef enum {
// ========== 新寄存器地址映射 (使用0x06/0x10标准Modbus功能码) ==========
// 详细寄存器偏移定义见 chrg_regs.h
// 判断寄存器地址属于哪个区块的宏
#define REG_IS_SINK(addr) (((addr / 4) < TOTAL_SINK_CH_REGS))
#define REG_IS_SRC(addr) (((addr / 4) < TOTAL_SRC_CH_REGS))
#define REG_IS_SYS(addr) (((addr / 4) < TOTAL_SYS_CH_REGS))
// 分区低字节按4通道连续排列,每通道占用TOTAL_xxx_CH_REGS个地址
#define REG_IS_SINK(addr) ((addr) < (TOTAL_SINK_CH_REGS * 4U))
#define REG_IS_SRC(addr) ((addr) < (TOTAL_SRC_CH_REGS * 4U))
#define REG_IS_SYS(addr) ((addr) < (TOTAL_SYS_CH_REGS * 4U))
// 根据地址提取通道号和区块内偏移
#define REG_GET_SINK_CH(addr) ((addr) / TOTAL_SINK_CH_REGS)
+43 -30
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_roll_sou.c
完成日期 :
当前版本号 : V1.1.24
当前版本号 : V1.1.31
主要功能 : 实现南向通信轮巡请求命令。以独立线程处理。
版本历史 : 创建原始版本
说明 :
@@ -168,23 +168,7 @@ int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs,
pBatch->cur_idx = 0;
}
/*
* 电流调节采用最新值有效策略。若同一通道仍有尚未发送的电流命令,
* 直接覆盖旧值,避免四通道快速测试时队列积累过时的电流设置。
* 当前命令已经发送时不覆盖,保证响应仍与原命令匹配。
*/
if ((count == 1) && (regs[0] == (REG_CURRENT_OUT + 1))) {
for (i = pBatch->cur_idx; i < pBatch->reg_cnt; i++) {
if (pBatch->reg_list[i] == regs[0]) {
pBatch->val_list[i] = vals[0];
pBatch->pending = RT_TRUE;
pROLL->run = RUN_COM;
rt_mutex_release(pTHR->mutex);
return 0;
}
}
}
/* 电流命令与其它写命令一致,完整追加并等待回码,不再覆盖旧命令。 */
if ((pBatch->reg_cnt > SOU_COM_BATCH_REG_MAX) ||
(count > (SOU_COM_BATCH_REG_MAX - pBatch->reg_cnt))) {
LOG_E("sou com batch reg full: ch=%d queued=%d append=%d max=%d",
@@ -923,10 +907,13 @@ void chrg_roll_sou_thread_entry (void *data)
measure_once = RT_TRUE;
response_part = MB_R_PART1;
chrg_led_flashing(IDX_LED3, TIMES_ONE, PULSE_TIME*2, PULSE_TIME*2);
if (idx != (rt_uint8_t)pROLL->idx) {
pROLL->idx = (eIDX_SOU_CH)idx;
chrg_south_switch((eIDX_SOU_CH)idx);
/* 插入命令可能改变物理选通,每次自动查询前必须重新选择通道。 */
if (chrg_south_switch((eIDX_SOU_CH)idx) != 0) {
sou_measure_once_finish(idx);
rt_thread_mdelay(1);
continue;
}
pROLL->idx = (eIDX_SOU_CH)idx;
if (g_sou_version_ready[idx] == 0U) {
version_probe = RT_TRUE;
chrg_eload_read_version(ID_ELOAD);
@@ -943,11 +930,14 @@ void chrg_roll_sou_thread_entry (void *data)
continue;
} else { // 通道启用
chrg_led_flashing(IDX_LED3, TIMES_ONE, PULSE_TIME*2, PULSE_TIME*2);
if (idx != (rt_uint8_t)pROLL->idx) {
pROLL->idx = (eIDX_SOU_CH)idx;
chrg_south_switch((eIDX_SOU_CH)idx);
pROLL->part = MB_R_PART1;
/* 不信任缓存通道,发送普通轮询前重新写入选通GPIO。 */
if (chrg_south_switch((eIDX_SOU_CH)idx) != 0) {
CHRG_CHANNEL_NEXT(index);
rt_thread_mdelay(1);
continue;
}
pROLL->idx = (eIDX_SOU_CH)idx;
pROLL->part = response_part;
if (g_sou_version_ready[idx] == 0U) {
version_probe = RT_TRUE;
chrg_eload_read_version(ID_ELOAD);
@@ -962,7 +952,11 @@ void chrg_roll_sou_thread_entry (void *data)
break;
case RUN_LCD: {
chrg_south_switch((eIDX_SOU_CH)single_ch);
if (chrg_south_switch((eIDX_SOU_CH)single_ch) != 0) {
sou_single_command_finish(run);
continue;
}
pROLL->idx = (eIDX_SOU_CH)single_ch;
chrg_eload_write_reg(ID_ELOAD, single_reg, single_value);
}
break;
@@ -976,9 +970,17 @@ void chrg_roll_sou_thread_entry (void *data)
}
batch_ch = command_ch;
command_valid = RT_TRUE;
chrg_south_switch((eIDX_SOU_CH)command_ch);
/* COM队列通道必须与物理选通一致,选通失败时禁止发送本条命令。 */
if (chrg_south_switch((eIDX_SOU_CH)command_ch) != 0) {
sou_com_command_failed(batch_ch, command_reg, command_value,
"channel switch", &retry_count[batch_ch]);
continue;
}
pROLL->idx = (eIDX_SOU_CH)command_ch;
#if SOUTH_COMMAND_DEBUG
rt_kprintf("ch=%d",command_ch);
rt_kprintf("[SOU_CMD] queue_ch=%u phy_ch=%u reg=0x%04X value=%u\r\n",
command_ch + 1U, chrgsouth.idx + 1U,
command_reg, command_value);
#endif
chrg_eload_write_reg(ID_ELOAD, command_reg, command_value);
}
@@ -995,7 +997,14 @@ void chrg_roll_sou_thread_entry (void *data)
rt_thread_mdelay(1);
continue;
}
chrg_south_switch((eIDX_SOU_CH)single_ch);
if (chrg_south_switch((eIDX_SOU_CH)single_ch) != 0) {
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
pROLL->updata_result = -1;
pROLL->send_flag = 0;
rt_mutex_release(pTHR->mutex);
continue;
}
pROLL->idx = (eIDX_SOU_CH)single_ch;
ret = chrg_eload_send_ymodem_data(pROLL->updata_buf, pROLL->leng);
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
@@ -1007,7 +1016,11 @@ void chrg_roll_sou_thread_entry (void *data)
}
case RUN_CONSOLE: {
chrg_south_switch((eIDX_SOU_CH)single_ch);
if (chrg_south_switch((eIDX_SOU_CH)single_ch) != 0) {
sou_single_command_finish(run);
continue;
}
pROLL->idx = (eIDX_SOU_CH)single_ch;
chrg_eload_refresh(ID_ELOAD, single_part);
}
break;
+5 -1
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_south.c
完成日期 :
当前版本号 : V1.1.24
当前版本号 : V1.1.31
主要功能 : 实现南向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
版本历史 : 创建原始版本
说明 :
@@ -141,6 +141,10 @@ int chrg_south_switch (eIDX_SOU_CH ch)
return -1;
}
#endif
/* 仅在GPIO选通成功后更新物理通道,供发送日志和轮询校验使用。 */
if (ret == 0) {
chrgsouth.idx = ch;
}
return ret;
}
+4 -1
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_eload.h
完成日期 :
当前版本号 : V1.1.24
当前版本号 : V1.1.28
主要功能 : 源载命令数据解析,封装,读写请求,寄存器定义,数据结构定义。
版本历史 : 创建原始版本
说明 :
@@ -61,6 +61,9 @@ static const rt_uint8_t LOAD[30] = {
#define STA1_READY (0x01) // 就绪/停止
#define STA1_RUNNING (0x02) // 运行
#define STA1_FAULT (0x03) // 故障
#define STA1_SHORT_TEST (0x05) // 短路测试中
#define STA1_OCP_TEST (0x06) // OCP测试中
#define STA1_DYNA_TEST (0x07) // 动态测试中
#define REG_CV 0X07 // 电压地址
#define REG_REL_ON (0X01) // 主继电器控制寄存器
+117 -14
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_sink.c
完成日期 :
当前版本号 : V1.1.26
当前版本号 : V1.1.28
主要功能 : 北向数据sink请求数据填充及发送。
版本历史 : 创建原始版本
说明 :
@@ -25,11 +25,13 @@
#define SINK_UNLOAD_CONFIRM_COUNT 2U // 连续满足低电流条件的量测帧数
#define SINK_SHORT_KEEPALIVE_MS 30U // 短路测试WORK_START保活下发周期
#define SINK_INIT_NORTH_RELAY_DELAY_MS 30U // 辅助继电器回码后打开北向继电器的等待时间
#define SINK_TEST_STATUS_RETRY_MAX 3U // 测试态退出确认的连续重发计数上限
#define SINK_INIT_STAGE_CLEAR_RELAY 0U
#define SINK_INIT_STAGE_AUX_RELAY 1U
#define SINK_INIT_STAGE_DELAY 2U
#define SINK_INIT_STAGE_REST 3U
#define SINK_INIT_STAGE_TEST_STOP 0U
#define SINK_INIT_STAGE_TEST_STATUS 1U
#define SINK_INIT_STAGE_AUX_RELAY 2U
#define SINK_INIT_STAGE_DELAY 3U
#define SINK_INIT_STAGE_REST 4U
static rt_uint8_t g_sink_init_stage[TOTAL_SOU_CHS] = {0};
static rt_tick_t g_sink_init_north_relay_tick[TOTAL_SOU_CHS] = {0};
@@ -61,6 +63,43 @@ static rt_uint32_t g_sink_von_voltage_seq[TOTAL_SOU_CHS] = {0};
static rt_uint8_t g_sink_von_stable_count[TOTAL_SOU_CHS] = {0};
/* 自动停止带载后的恢复确认计数,独立于首次握手的电压确认计数。 */
static rt_uint8_t g_sink_load_restart_confirm_count[TOTAL_SOU_CHS] = {0};
static rt_uint32_t g_sink_init_test_sample_seq[TOTAL_SOU_CHS] = {0};
static rt_uint8_t g_sink_init_test_stop_retry[TOTAL_SOU_CHS] = {0};
static rt_uint32_t g_sink_stop_test_sample_seq[TOTAL_SOU_CHS] = {0};
static rt_uint8_t g_sink_stop_test_stop_retry[TOTAL_SOU_CHS] = {0};
static rt_bool_t g_sink_stop_test_confirmed[TOTAL_SOU_CHS] = {RT_FALSE};
static rt_uint8_t chrg_sink_power_status_get(eIDX_SOU_CH idx)
{
rt_uint16_t status1;
if (idx >= TOTAL_SOU_CHS) {
return 0U;
}
status1 = chrgsouth.eload[idx].status1;
if (((status1 & 0x00FFU) == 0U) && ((status1 >> 8) != 0U)) {
status1 >>= 8;
}
return (rt_uint8_t)status1;
}
static rt_bool_t chrg_sink_power_test_active(eIDX_SOU_CH idx)
{
rt_uint8_t status1 = chrg_sink_power_status_get(idx);
return (status1 == STA1_SHORT_TEST) ||
(status1 == STA1_OCP_TEST) ||
(status1 == STA1_DYNA_TEST);
}
static rt_bool_t chrg_sink_test_exit_required(eIDX_SOU_CH idx,
const struct chrg_switch_t *pSW)
{
return chrg_sink_power_test_active(idx) ||
((pSW != RT_NULL) &&
((pSW->sink.test.test_mode != SINK_NO_TEST) ||
(pSW->sink.test.test_mode_old != SINK_NO_TEST)));
}
enum {
SINK_UNLOAD_NEXT_RUNNING = 0,
@@ -304,7 +343,7 @@ static int chrg_sink_init_submit_stage(eIDX_SOU_CH idx,
total = chrg_sink_init_build_commands(pSW, regs, vals);
switch (g_sink_init_stage[idx]) {
case SINK_INIT_STAGE_CLEAR_RELAY:
case SINK_INIT_STAGE_TEST_STOP:
start = 0U;
count = 1U;
break;
@@ -344,7 +383,11 @@ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
return -1;
}
g_sink_init_in_progress[idx] = RT_TRUE;
g_sink_init_stage[idx] = SINK_INIT_STAGE_CLEAR_RELAY;
/* 功率板回读处于Short/OCP/DYNA时,先用WORK_STOP退出测试态。 */
g_sink_init_stage[idx] = chrg_sink_test_exit_required(idx, pSW) ?
SINK_INIT_STAGE_TEST_STOP : SINK_INIT_STAGE_AUX_RELAY;
g_sink_init_test_sample_seq[idx] = 0U;
g_sink_init_test_stop_retry[idx] = 0U;
g_sink_init_north_relay_tick[idx] = 0U;
/* 入队失败也保留事务和等待入口,由等待分支持续重提当前阶段。 */
pSW->sink.On_work = 1;
@@ -398,7 +441,29 @@ static int chrg_sink_work_init_aux_wait(eIDX_SOU_CH idx,
if (batch_result <= 0) {
return chrg_sink_init_submit_stage(idx, pSW);
}
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_CLEAR_RELAY) {
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_TEST_STOP) {
g_sink_init_stage[idx] = SINK_INIT_STAGE_TEST_STATUS;
g_sink_init_test_sample_seq[idx] = chrg_eload_get_sample_seq(idx);
return (chrg_sou_measure_once_submit(idx) == 0) ? 0 : -1;
}
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_TEST_STATUS) {
rt_uint32_t sample_seq = chrg_eload_get_sample_seq(idx);
if (sample_seq == g_sink_init_test_sample_seq[idx]) {
return (chrg_sou_measure_once_submit(idx) == 0) ? 0 : -1;
}
g_sink_init_test_sample_seq[idx] = sample_seq;
if (chrg_sink_power_test_active(idx)) {
if (g_sink_init_test_stop_retry[idx] >=
SINK_TEST_STATUS_RETRY_MAX) {
/* 达到一轮上限后重新计数,但仍保持在安全确认流程。 */
g_sink_init_test_stop_retry[idx] = 0U;
}
g_sink_init_test_stop_retry[idx]++;
g_sink_init_stage[idx] = SINK_INIT_STAGE_TEST_STOP;
return chrg_sink_init_submit_stage(idx, pSW);
}
g_sink_init_test_stop_retry[idx] = 0U;
g_sink_init_stage[idx] = SINK_INIT_STAGE_AUX_RELAY;
return chrg_sink_init_submit_stage(idx, pSW);
}
@@ -636,29 +701,43 @@ static int chrg_sink_work_low_aux_wait(eIDX_SOU_CH idx,
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 清除运行、使能和在线标志,关闭北向继电器
返回说明: 0:关机成功 <0:停止命令提交失败
其它说明: 短路关机先退出短路再发送WORK_STOP,普通关机只发送WORK_STOP
其它说明: 功率板处于测试态时先发送WORK_STOP;短路兼容退出命令随后发送
*****************************************************************/
static int chrg_sink_work_stop_prepare(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t regs[2];
rt_uint16_t values[2];
rt_uint16_t regs[3];
rt_uint16_t values[3];
rt_uint8_t count = 0U;
rt_bool_t was_short;
rt_bool_t power_test_active;
rt_bool_t preserve_test_retry;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
was_short = (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) ||
preserve_test_retry = (pSW->sink.work_mode == SINK_WORK_STOP_WAIT) &&
!g_sink_stop_test_confirmed[idx] &&
(g_sink_stop_test_stop_retry[idx] != 0U);
power_test_active = chrg_sink_power_test_active(idx);
was_short = (power_test_active &&
(chrg_sink_power_status_get(idx) == STA1_SHORT_TEST)) ||
(pSW->sink.test.test_mode_old == SINK_TEST_SHORT) ||
(pSW->sink.test.test_mode == SINK_TEST_SHORT) ||
g_sink_stop_from_short[idx];
g_sink_stop_from_short[idx] = was_short;
g_sink_stop_test_sample_seq[idx] = 0U;
if (!preserve_test_retry) {
g_sink_stop_test_stop_retry[idx] = 0U;
}
g_sink_stop_test_confirmed[idx] = RT_FALSE;
/* WORK_STOP必须是退出Short/OCP/DYNA的第一条南向命令。 */
regs[count] = REG_WORK;
values[count++] = WORK_STOP;
if (was_short) {
regs[count] = ModbusRTU_ShortTest_CMD_MODE_FLAG;
values[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
}
regs[count] = REG_WORK;
values[count++] = WORK_STOP;
g_sink_init_in_progress[idx] = RT_FALSE;
g_sink_init_stage_submitted[idx] = RT_FALSE;
if (chrg_sou_com_batch_cancel(idx) != 0) {
@@ -887,6 +966,30 @@ static int chrg_sink_work_current_wait(eIDX_SOU_CH idx,
return chrg_sink_work_protocol_unload(idx, pSW);
}
if ((next_action == SINK_UNLOAD_NEXT_STOP_RELAY) &&
!g_sink_stop_test_confirmed[idx]) {
rt_uint32_t test_sample_seq = chrg_eload_get_sample_seq(idx);
if (g_sink_stop_test_sample_seq[idx] == 0U) {
g_sink_stop_test_sample_seq[idx] = test_sample_seq;
return (chrg_sou_measure_once_submit(idx) == 0) ? 0 : -1;
}
if (test_sample_seq == g_sink_stop_test_sample_seq[idx]) {
return (chrg_sou_measure_once_submit(idx) == 0) ? 0 : -1;
}
g_sink_stop_test_sample_seq[idx] = test_sample_seq;
if (chrg_sink_power_test_active(idx)) {
if (g_sink_stop_test_stop_retry[idx] >=
SINK_TEST_STATUS_RETRY_MAX) {
/* 达到一轮上限后重新计数,但仍保持在安全确认流程。 */
g_sink_stop_test_stop_retry[idx] = 0U;
}
g_sink_stop_test_stop_retry[idx]++;
return chrg_sink_work_stop_prepare(idx, pSW);
}
g_sink_stop_test_confirmed[idx] = RT_TRUE;
}
if (!g_sink_unload_batch_confirmed[idx]) {
/* 从最后一条写回码之后建立基线,排除RUN_COM之前残留的旧量测。 */
g_sink_unload_sample_seq[idx] = chrg_eload_get_sample_seq(idx);
+45
View File
@@ -29,6 +29,51 @@
- `apps/chrg` 各C源文件和头文件的文件头版本统一为 `V1.1.24`。
- Bootloader版本保持 `V1.0.0` 不变。
- 后续每次修改应用代码时,必须同步更新应用版本号、对外版本编码和本修改日志。
## V1.1.31 - 2026-09-16
### 通道保护代码注释
- 补充上位机地址解码、非法通道初值、跨通道写保护的中文注释。
- 补充南向物理选通同步、自动轮询重新选通及电流命令顺序入队的中文注释。
- 协议板版本号更新为 `V1.1.31`,固件版本寄存器更新为 `0x012F`。
## V1.1.30 - 2026-09-16
### 南向通道选择与地址校验
- 南向物理选通成功后统一记录 `chrgsouth.idx`,自动轮询每次发送前重新选通目标通道。
- COM、LCD、升级和控制台发送路径同步更新当前通道,选通失败时禁止继续发送。
- 单寄存器写初始化通道变量,并通过统一解码函数拒绝非法分区和越界地址。
- 多寄存器写按每通道50个地址计算通道,并拒绝跨通道写入。
- `SOUTH_COMMAND_DEBUG` 日志增加队列通道、实际物理通道、寄存器和值。
- 协议板版本号更新为 `V1.1.30`,固件版本寄存器更新为 `0x012E`。
## V1.1.29 - 2026-09-16
### 取消电流命令覆盖去重
- 删除南向队列对未发送电流命令的覆盖逻辑。
- 同一通道新增的电流设置按提交顺序逐条发送,并分别等待功率板回码。
- 协议板版本号更新为 `V1.1.29`,固件版本寄存器更新为 `0x012D`。
- 待验证:快速连续设置电流时,确认每条电流命令都能在南向日志和功率板侧收到。
## V1.1.28 - 2026-09-16
### 测试态退出结果确认
- 初始化发送 `WORK_STOP` 后,通过新的 `P1` 量测重新读取功率板 `status1`。
- 只有确认 `status1` 不再是 `0x05/0x06/0x07`,才继续打开辅助继电器和后续初始化命令。
- 关机流程同样在关闭南向继电器前确认测试态已退出;每连续重发 `WORK_STOP` 3 次后重新计数,仍未退出时保持安全等待并继续重试。
- 协议板版本号更新为 `V1.1.28`,固件版本寄存器更新为 `0x012C`。
## V1.1.27 - 2026-09-16
### 功率板测试状态退出保护
- 新增功率板 `status1=0x05/0x06/0x07` 的 Short、OCP、DYNA 状态定义。
- 初始化前识别功率板测试状态,优先发送 `WORK_STOP` 后再继续初始化。
- 负载关机流程调整为 `WORK_STOP` 优先,避免测试退出命令被后续启动状态覆盖。
- 上位机版本寄存器 `0x0300` 更新为 `0x012B`(`V1.1.27`)。
## V1.1.23 - 2026-09-11