Merge pull request 'Feature' (#4) from feature into master
Reviewed-on: chrg/chrg#4
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_comm.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.10
|
||||
当前版本号 : V1.1.24
|
||||
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "stm32f4xx_hal.h"
|
||||
|
||||
#define HOLD_REG_FIRMWARE_VERSION 0x0300U /* 协议板固件版本,只读 */
|
||||
#define CHRG_FIRMWARE_VERSION 0x011AU /* 当前协议板版本:V1.1.10 */
|
||||
#define CHRG_FIRMWARE_VERSION 0x0127U /* 当前协议板版本:V1.1.23 */
|
||||
|
||||
rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR];
|
||||
rt_uint8_t ucCoilBuf[(TOTAL_COIL_REGS+BITS_UCHAR-1)/BITS_UCHAR];
|
||||
@@ -618,7 +618,7 @@ eMBException funcReadHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||||
|
||||
/*
|
||||
* 协议板版本号使用独立的只读地址0x0300,不占用现有通道保持寄存器空间。
|
||||
* 当前版本V1.1.10编码为0x011A,Modbus响应按高字节在前返回01 1A。
|
||||
* 当前版本V1.1.23编码为0x0127,Modbus响应按高字节在前返回01 27。
|
||||
* 该特殊地址只支持一次读取一个寄存器,避免与普通连续寄存器混用。
|
||||
*/
|
||||
if (regAddress == HOLD_REG_FIRMWARE_VERSION) {
|
||||
@@ -2563,6 +2563,7 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
|
||||
rt_bool_t protocol_voltage_only;
|
||||
rt_bool_t protocol_current_change;
|
||||
rt_bool_t protocol_data_changed;
|
||||
rt_bool_t von_changed;
|
||||
rt_uint8_t curr_test;
|
||||
rt_uint8_t curr_test_old;
|
||||
rt_uint8_t old_cv_mode = pReal->CV_mode;
|
||||
@@ -2570,6 +2571,7 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
|
||||
rt_uint16_t old_loadc = pReal->sink.loadc;
|
||||
rt_uint8_t old_on_work = pReal->sink.On_work;
|
||||
rt_uint8_t old_work_mode = pReal->sink.work_mode;
|
||||
rt_bool_t old_test_or_load_active;
|
||||
rt_uint16_t sink_work = pShadow->sink.Now_State;
|
||||
chrg_param_diff_t diff_info;
|
||||
|
||||
@@ -2587,14 +2589,53 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
|
||||
!full_handshake_change;
|
||||
protocol_voltage_only = diff_info.pro_voltage_changed &&
|
||||
!full_handshake_change && !protocol_current_change;
|
||||
von_changed = (pReal->sink.volt_on != pShadow->sink.volt_on);
|
||||
/* 完整握手或协议软卸载会先清理本通道批次,V_ON由对应状态机在
|
||||
* WORK_STOP之后重新加入;普通参数变化仍在这里立即下发。 */
|
||||
if ((sink_work == 0x01) && (old_on_work == 1U) && von_changed &&
|
||||
!full_handshake_change && !protocol_voltage_only &&
|
||||
!protocol_current_change &&
|
||||
(pReal->sink.test.test_mode != SINK_TEST_SHORT) &&
|
||||
(pShadow->sink.test.test_mode != SINK_TEST_SHORT)) {
|
||||
rt_uint16_t reg = Eload_Start_V_ON;
|
||||
rt_uint16_t val = (pShadow->sink.volt_on <= 500U) ?
|
||||
500U : pShadow->sink.volt_on;
|
||||
|
||||
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, ®,
|
||||
&val, 1U) != 0) {
|
||||
return MB_EX_SLAVE_BUSY;
|
||||
}
|
||||
}
|
||||
rt_memcpy(pReal, pShadow, sizeof(struct chrg_switch_t));
|
||||
|
||||
if (sink_work == 0x01) {
|
||||
curr_test = pReal->sink.test.test_mode;
|
||||
curr_test_old = pReal->sink.test.test_mode_old;
|
||||
if (((curr_test_old == 0) && (curr_test != 0)) ||
|
||||
((curr_test == 0) && (curr_test_old != 0))) {
|
||||
if (curr_test_old == 0) {
|
||||
old_test_or_load_active = (old_on_work == 1U) &&
|
||||
((old_work_mode == SINK_WORK_RUNING) ||
|
||||
(old_work_mode == SINK_WORK_LOAD_STANDBY) ||
|
||||
(old_work_mode == SINK_WORK_ON_TEST_SHORT) ||
|
||||
(old_work_mode == SINK_WORK_ON_TEST_DYNA) ||
|
||||
(old_work_mode == SINK_WORK_ON_TEST_OCP));
|
||||
if (curr_test != curr_test_old) {
|
||||
/* 测试模式切换优先清理旧批次,防止旧WORK_START插入新测试。 */
|
||||
if (chrg_sou_com_batch_cancel((eIDX_SOU_CH)ch) != 0) {
|
||||
return MB_EX_SLAVE_BUSY;
|
||||
}
|
||||
if (curr_test == 0) {
|
||||
/* Protocol-before-CC test flow is disabled in this version. */
|
||||
pReal->sub = IDX_SET_SINK_CC_BEFORE_PD;
|
||||
pReal->sink.work_mode = SINK_WORK_INIT;
|
||||
} else {
|
||||
/* 普通负载或旧测试运行时,先停止电子负载再启动新测试。 */
|
||||
if (old_test_or_load_active) {
|
||||
rt_uint16_t stop_reg = REG_WORK;
|
||||
rt_uint16_t stop_val = WORK_STOP;
|
||||
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch,
|
||||
&stop_reg, &stop_val, 1U) != 0) {
|
||||
return MB_EX_SLAVE_BUSY;
|
||||
}
|
||||
}
|
||||
if (curr_test == SINK_TEST_DYNA) {
|
||||
pReal->sink.work_mode = SINK_WORK_ON_TEST_DYNA;
|
||||
} else if (curr_test == SINK_TEST_OCP) {
|
||||
@@ -2603,10 +2644,6 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
|
||||
pReal->sink.work_mode = SINK_WORK_ON_TEST_SHORT;
|
||||
}
|
||||
pReal->sink.test.test_mode_old = curr_test;
|
||||
} else {
|
||||
/* Protocol-before-CC test flow is disabled in this version. */
|
||||
pReal->sub = IDX_SET_SINK_CC_BEFORE_PD;
|
||||
pReal->sink.work_mode = SINK_WORK_INIT;
|
||||
}
|
||||
} else if (pReal->sink.On_work == 0) {
|
||||
pReal->sink.On_work = 1;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_comm.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.10
|
||||
当前版本号 : V1.1.21
|
||||
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_north.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.10
|
||||
当前版本号 : V1.1.14
|
||||
主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -212,13 +212,13 @@ void chrg_source_setting(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_sink_parse
|
||||
函数描述: 解析北向负载板0x20回码,提取D+/D-电压到VZ/VF
|
||||
函数描述: 解析北向负载板0x20回码,提取实际电压和D+/D-电压
|
||||
输入参数: frame_data:完整北向回码 idx:当前通道 pSW:通道控制结构体
|
||||
pNOR:北向总控结构体
|
||||
输出参数: 更新pSW->VZ(D+)、pSW->VF(D-)
|
||||
输出参数: 更新pSW->Sink_voltage、VZ(D+)、VF(D-)
|
||||
返回说明: -
|
||||
其它说明: Sink电压状态机判定已迁移至chrg_sink_volt_judge,
|
||||
由北向轮询线程基于eload.c更新的Now_voltage驱动
|
||||
其它说明: Sink电压状态机可通过SINK_VON_VOLTAGE_SOURCE选择使用本回码
|
||||
实际电压或功率板实际电压
|
||||
*****************************************************************/
|
||||
void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
|
||||
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
|
||||
@@ -227,9 +227,14 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
|
||||
return;
|
||||
}
|
||||
|
||||
pSW->Sink_voltage = (rt_uint16_t)(((frame_data[4] << 8) |
|
||||
frame_data[5]) * 10U);
|
||||
pSW->Sink_voltage_seq++;
|
||||
pSW->VZ = ((frame_data[14] << 8) | frame_data[15]);
|
||||
pSW->VF = ((frame_data[16] << 8) | frame_data[17]);
|
||||
|
||||
//rt_kprintf("VZ=%d,VF=%d\n",pSW->VZ,pSW->VF);
|
||||
|
||||
#if NORTH_SINK_DEBUG
|
||||
LOG_D("Sink[%d] real volt: %d mV", idx,
|
||||
((frame_data[4] << 8) | frame_data[5]) * 10);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_north.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.0
|
||||
当前版本号 : V1.1.22
|
||||
主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -16,17 +16,38 @@
|
||||
#include "chrg_sink.h"
|
||||
#include "chrg_def.h"
|
||||
|
||||
#define THR_NAME_NORTH "thr.nor"
|
||||
/* V_On启停判定电压源:切换后需重新编译固件。 */
|
||||
#define SINK_VON_VOLTAGE_FROM_PROTOCOL 1U
|
||||
#define SINK_VON_VOLTAGE_FROM_POWER 2U
|
||||
#ifndef SINK_VON_VOLTAGE_SOURCE
|
||||
#define SINK_VON_VOLTAGE_SOURCE SINK_VON_VOLTAGE_FROM_POWER
|
||||
#endif
|
||||
#if (SINK_VON_VOLTAGE_SOURCE != SINK_VON_VOLTAGE_FROM_PROTOCOL) && \
|
||||
(SINK_VON_VOLTAGE_SOURCE != SINK_VON_VOLTAGE_FROM_POWER)
|
||||
#error "Invalid SINK_VON_VOLTAGE_SOURCE"
|
||||
#endif
|
||||
|
||||
/* 南向COM批次中单通道连续收到有效回码后的最大发送条数。 */
|
||||
#define SOU_COM_BURST_MAX 6U
|
||||
|
||||
/* 自动停止带载后,电压恢复时需要连续确认的新P1采样帧数。 */
|
||||
#define SINK_LOAD_RESTART_CONFIRM_COUNT 3U
|
||||
|
||||
/* 主继电器闭合后的纹波瞬态过滤参数,单位为新的P1采样帧。 */
|
||||
#define RIPPLE_RELAY_IGNORE_P1_COUNT 5U
|
||||
#define RIPPLE_MEDIAN_WINDOW_SIZE 3U
|
||||
|
||||
#define THR_NAME_NORTH "thr.nor"
|
||||
#define DEV_NAME_NORTH "uart3"
|
||||
|
||||
#define LEN_FRAME_HEAD (4) // [帧长, 0x00, id, 命令]
|
||||
#define MIN_FRAME_LENGTH (5) // 帧最短5字节
|
||||
#define WORK_MIN_VOLT 500 // 工作电压下限,单位mV
|
||||
#define NORTH_FRAME_DEBUG 0x00 // 北向UART原始收发帧
|
||||
#define NORTH_SOURCE_DEBUG 0x01 // Source协议解析及组帧
|
||||
#define NORTH_SINK_DEBUG 0x01 // Sink协议解析及组帧
|
||||
#define NORTH_SOURCE_DEBUG 0x00 // Source协议解析及组帧
|
||||
#define NORTH_SINK_DEBUG 0x00 // Sink协议解析及组帧
|
||||
#define SOUTH_FRAME_DEBUG 0x00 // 南向UART原始收发帧
|
||||
#define SOUTH_COMMAND_DEBUG 0x00 // 南向寄存器写命令
|
||||
#define SOUTH_COMMAND_DEBUG 0x01 // 南向寄存器写命令
|
||||
#define COMM_FRAME_DEBUG 0x00 // COM UART原始收发帧
|
||||
#define COMM_DEBUG 0x00 // COM寄存器读写过程
|
||||
#define COMM_TRIM_DEBUG 0x00 // COM校准参数解析
|
||||
@@ -56,6 +77,8 @@ struct chrg_switch_t {
|
||||
rt_uint8_t change_flag; //正在调整功率标志位
|
||||
rt_uint16_t Now_voltage; // 当前电压 mV
|
||||
rt_uint16_t Now_current; // 当前电流 mA
|
||||
rt_uint16_t Sink_voltage; // 协议板0x20回码的实际电压 mV
|
||||
rt_uint32_t Sink_voltage_seq; // 协议板实际电压有效回码序号
|
||||
rt_uint16_t VZ;
|
||||
rt_uint16_t VF;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_roll_nor.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.10
|
||||
当前版本号 : V1.1.14
|
||||
主要功能 : 实现北向通信轮巡请求命令。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -1033,7 +1033,7 @@ void chrg_roll_nor_thread_entry (void *data)
|
||||
source_waiting = chrg_roll_nor_source_wait_process(idx, pSW);
|
||||
sink_waiting = chrg_roll_nor_sink_wait_process(idx, pSW);
|
||||
|
||||
/* 全通道Sink电压判定:使用eload实际电压Now_voltage与V_On比较,
|
||||
/* 全通道Sink电压判定:使用配置的V_On电压源与V_On比较,
|
||||
* 判定后统一由chrg_sink_work执行;短路测试由该入口负责WORK_START保活。 */
|
||||
for (i = 0; i < TOTAL_NOR_CHS; i++) {
|
||||
struct chrg_switch_t *psw = &pNOR->sw[i];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_roll_nor.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.10
|
||||
当前版本号 : V1.1.14
|
||||
主要功能 : 实现北向通信轮巡请求命令。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_roll_sou.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.0
|
||||
当前版本号 : V1.1.23
|
||||
主要功能 : 实现南向通信轮巡请求命令。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -26,7 +26,6 @@
|
||||
#define SOU_MUTEX_TIMEOUT_MS 100
|
||||
#define SOU_RESPONSE_TIMEOUT_MS 20
|
||||
#define SOU_COM_MAX_ATTEMPTS 3
|
||||
#define SOU_COM_BURST_MAX 10U
|
||||
#define SOU_COM_RETRY_YIELD_MS 1U
|
||||
#define SOU_REL_SWITCH_DELAY_MS 100U
|
||||
#define SOU_CHANNEL_BIT(ch) ((rt_uint8_t)(1U << (ch)))
|
||||
@@ -50,6 +49,37 @@ static rt_uint8_t g_sou_com_batch_rr_next_ch = 0U;
|
||||
static rt_uint8_t g_sou_com_batch_burst_ch = TOTAL_SOU_CHS;
|
||||
static rt_uint8_t g_sou_com_batch_burst_count = 0U;
|
||||
|
||||
static rt_bool_t sou_com_batch_is_priority(rt_uint8_t ch,
|
||||
sou_com_batch_ch_t *pBatch)
|
||||
{
|
||||
rt_uint16_t reg;
|
||||
rt_uint16_t value;
|
||||
|
||||
if ((ch >= TOTAL_SOU_CHS) || (pBatch == RT_NULL) ||
|
||||
(pBatch->cur_idx >= pBatch->reg_cnt)) {
|
||||
return RT_FALSE;
|
||||
}
|
||||
reg = pBatch->reg_list[pBatch->cur_idx];
|
||||
value = pBatch->val_list[pBatch->cur_idx];
|
||||
if ((reg == REG_WORK) && (value == WORK_STOP)) {
|
||||
return RT_TRUE;
|
||||
}
|
||||
if ((reg == ModbusRTU_ShortTest_CMD_MODE_FLAG) &&
|
||||
(value == ModbusRTU_DYNA_CMD_MODE_STOP)) {
|
||||
return RT_TRUE;
|
||||
}
|
||||
if ((reg == REG_REL_ON) && (value == ALL_REL_OFF)) {
|
||||
return RT_TRUE;
|
||||
}
|
||||
if ((reg == REG_WORK) && (value == WORK_START) &&
|
||||
(chrgnorth.sw[ch].sink.work_mode == SINK_WORK_ON_TEST_SHORT ||
|
||||
chrgnorth.sw[ch].sink.work_mode == SINK_WORK_ON_TEST_DYNA ||
|
||||
chrgnorth.sw[ch].sink.work_mode == SINK_WORK_ON_TEST_OCP)) {
|
||||
return RT_TRUE;
|
||||
}
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
struct chrg_rollsou_t chrgrollsou = {
|
||||
.run = RUN_AUTO,
|
||||
.ch = IDX_SOU_CH1,
|
||||
@@ -539,6 +569,25 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val
|
||||
}
|
||||
|
||||
now = rt_tick_get();
|
||||
/* 测试切换和停止命令先执行,避免被其它通道的长测试批次拖延。 */
|
||||
for (offset = 0; offset < TOTAL_SOU_CHS; offset++) {
|
||||
idx = (rt_uint8_t)((g_sou_com_batch_rr_next_ch + offset) %
|
||||
TOTAL_SOU_CHS);
|
||||
sou_com_batch_ch_t *pBatch = &g_sou_com_batch.ch[idx];
|
||||
if ((pBatch->pending != RT_TRUE) ||
|
||||
(pBatch->cur_idx >= pBatch->reg_cnt) ||
|
||||
!sou_com_batch_is_priority(idx, pBatch) ||
|
||||
((pBatch->next_send_tick != 0U) &&
|
||||
((rt_int32_t)(now - pBatch->next_send_tick) < 0))) {
|
||||
continue;
|
||||
}
|
||||
pBatch->next_send_tick = 0U;
|
||||
*ch = idx;
|
||||
*reg = pBatch->reg_list[pBatch->cur_idx];
|
||||
*value = pBatch->val_list[pBatch->cur_idx];
|
||||
rt_mutex_release(pTHR->mutex);
|
||||
return 1;
|
||||
}
|
||||
/* 短路保活的WORK_START达到周期后优先下发,避免长批次挤占30ms保活窗口。 */
|
||||
for (offset = 0; offset < TOTAL_SOU_CHS; offset++) {
|
||||
idx = (rt_uint8_t)((g_sou_com_batch_rr_next_ch + offset) %
|
||||
@@ -620,6 +669,13 @@ static int sou_com_batch_complete(rt_uint8_t ch, rt_uint16_t reg, rt_uint16_t va
|
||||
}
|
||||
|
||||
pBatch->cur_idx++;
|
||||
if ((reg == REG_REL_ON) &&
|
||||
(((value & MAIN_REL_ON) != 0U) || (value == FU_REL_ON))) {
|
||||
chrg_eload_ripple_filter_relay_start(ch);
|
||||
}
|
||||
if ((reg == REG_WORK) && (value == WORK_STOP)) {
|
||||
chrg_eload_ripple_filter_relay_stop(ch);
|
||||
}
|
||||
/* 暂时关闭副继电器与主继电器之间的固定延时;需要恢复时重新启用本段。 */
|
||||
if ((reg == REG_REL_ON) && (value == ALL_REL_OFF) &&
|
||||
(pBatch->cur_idx < pBatch->reg_cnt) &&
|
||||
@@ -921,7 +977,9 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
batch_ch = command_ch;
|
||||
command_valid = RT_TRUE;
|
||||
chrg_south_switch((eIDX_SOU_CH)command_ch);
|
||||
#if SOUTH_COMMAND_DEBUG
|
||||
rt_kprintf("ch=%d",command_ch);
|
||||
#endif
|
||||
chrg_eload_write_reg(ID_ELOAD, command_reg, command_value);
|
||||
}
|
||||
break;
|
||||
@@ -984,6 +1042,15 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
"batch changed", &retry_count[batch_ch]);
|
||||
}
|
||||
} else if (run != RUN_AUTO) {
|
||||
if ((run == RUN_LCD) && (single_reg == REG_REL_ON) &&
|
||||
(((single_value & MAIN_REL_ON) != 0U) ||
|
||||
(single_value == FU_REL_ON))) {
|
||||
chrg_eload_ripple_filter_relay_start(single_ch);
|
||||
}
|
||||
if ((run == RUN_LCD) && (single_reg == REG_WORK) &&
|
||||
(single_value == WORK_STOP)) {
|
||||
chrg_eload_ripple_filter_relay_stop(single_ch);
|
||||
}
|
||||
sou_single_command_finish(run);
|
||||
} else {
|
||||
if (version_probe) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_roll_sou.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.0
|
||||
当前版本号 : V1.1.21
|
||||
主要功能 : 实现南向通信轮巡请求命令。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_eload.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.0
|
||||
当前版本号 : V1.1.22
|
||||
主要功能 : 源载命令数据解析,封装,读写请求。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -20,6 +20,14 @@
|
||||
#include "chrg_south.h"
|
||||
|
||||
#define ELOAD_VALID_VOLTAGE_MIN_MV 2000U
|
||||
#define DPDM_HOST_READ_SCALE 10U
|
||||
|
||||
static rt_uint16_t chrg_eload_dpdm_host_value(rt_uint16_t value)
|
||||
{
|
||||
rt_uint32_t scaled_value = (rt_uint32_t)value * DPDM_HOST_READ_SCALE;
|
||||
|
||||
return (scaled_value > 0xFFFFU) ? 0xFFFFU : (rt_uint16_t)scaled_value;
|
||||
}
|
||||
|
||||
extern void LCD_SHOW(rt_uint8_t idx, struct eload_p1_t *pP1, struct chrg_eload_t *pLOAD, struct chrg_switch_t *pSW, rt_uint8_t online);
|
||||
|
||||
@@ -41,9 +49,104 @@ rt_uint32_t chrg_eload_get_sample_seq(rt_uint8_t idx)
|
||||
}
|
||||
return g_eload_sample_seq[idx];
|
||||
}
|
||||
static rt_uint16_t ripple_base[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t ripple_cal_flag[TOTAL_SOU_CHS] = {0};
|
||||
rt_uint16_t real_ripple[TOTAL_SOU_CHS] = {0};
|
||||
typedef struct {
|
||||
rt_uint16_t samples[RIPPLE_MEDIAN_WINDOW_SIZE];
|
||||
rt_uint16_t filtered_value;
|
||||
rt_uint8_t sample_count;
|
||||
rt_uint8_t sample_next;
|
||||
rt_uint8_t ignore_count;
|
||||
rt_uint8_t ignore_total;
|
||||
rt_tick_t relay_start_tick;
|
||||
rt_uint32_t frame_count;
|
||||
rt_bool_t relay_started;
|
||||
} chrg_ripple_filter_t;
|
||||
|
||||
static chrg_ripple_filter_t g_ripple_filter[TOTAL_SOU_CHS] = {0};
|
||||
|
||||
static rt_uint16_t chrg_eload_ripple_median(rt_uint16_t value0,
|
||||
rt_uint16_t value1, rt_uint16_t value2)
|
||||
{
|
||||
if (value0 > value1) {
|
||||
rt_uint16_t temp = value0;
|
||||
value0 = value1;
|
||||
value1 = temp;
|
||||
}
|
||||
if (value1 > value2) {
|
||||
rt_uint16_t temp = value1;
|
||||
value1 = value2;
|
||||
value2 = temp;
|
||||
}
|
||||
if (value0 > value1) {
|
||||
value1 = value0;
|
||||
}
|
||||
return value1;
|
||||
}
|
||||
|
||||
static rt_uint16_t chrg_eload_ripple_filter_update(rt_uint8_t idx,
|
||||
rt_uint16_t raw_value)
|
||||
{
|
||||
chrg_ripple_filter_t *pFilter = &g_ripple_filter[idx];
|
||||
|
||||
pFilter->frame_count++;
|
||||
|
||||
if (pFilter->ignore_count > 0U) {
|
||||
pFilter->ignore_count--;
|
||||
return pFilter->filtered_value;
|
||||
}
|
||||
pFilter->samples[pFilter->sample_next] = raw_value;
|
||||
pFilter->sample_next = (pFilter->sample_next + 1U) % RIPPLE_MEDIAN_WINDOW_SIZE;
|
||||
if (pFilter->sample_count < RIPPLE_MEDIAN_WINDOW_SIZE) {
|
||||
pFilter->sample_count++;
|
||||
}
|
||||
if (pFilter->sample_count < RIPPLE_MEDIAN_WINDOW_SIZE) {
|
||||
pFilter->filtered_value = raw_value;
|
||||
} else {
|
||||
pFilter->filtered_value = chrg_eload_ripple_median(
|
||||
pFilter->samples[0], pFilter->samples[1], pFilter->samples[2]);
|
||||
}
|
||||
return pFilter->filtered_value;
|
||||
}
|
||||
|
||||
void chrg_eload_ripple_filter_reset(rt_uint8_t idx)
|
||||
{
|
||||
chrg_ripple_filter_t *pFilter;
|
||||
|
||||
if (idx >= TOTAL_SOU_CHS) {
|
||||
return;
|
||||
}
|
||||
pFilter = &g_ripple_filter[idx];
|
||||
pFilter->sample_count = 0U;
|
||||
pFilter->sample_next = 0U;
|
||||
pFilter->ignore_count = (rt_uint8_t)RIPPLE_RELAY_IGNORE_P1_COUNT;
|
||||
pFilter->ignore_total = pFilter->ignore_count;
|
||||
}
|
||||
|
||||
void chrg_eload_ripple_filter_relay_start(rt_uint8_t idx)
|
||||
{
|
||||
chrg_ripple_filter_t *pFilter;
|
||||
|
||||
if (idx >= TOTAL_SOU_CHS) {
|
||||
return;
|
||||
}
|
||||
pFilter = &g_ripple_filter[idx];
|
||||
chrg_eload_ripple_filter_reset(idx);
|
||||
pFilter->relay_start_tick = rt_tick_get();
|
||||
pFilter->frame_count = 0U;
|
||||
pFilter->relay_started = RT_TRUE;
|
||||
}
|
||||
|
||||
void chrg_eload_ripple_filter_relay_stop(rt_uint8_t idx)
|
||||
{
|
||||
chrg_ripple_filter_t *pFilter;
|
||||
|
||||
if (idx >= TOTAL_SOU_CHS) {
|
||||
return;
|
||||
}
|
||||
pFilter = &g_ripple_filter[idx];
|
||||
pFilter->relay_started = RT_FALSE;
|
||||
pFilter->relay_start_tick = 0U;
|
||||
pFilter->frame_count = 0U;
|
||||
}
|
||||
|
||||
/* ======================== Ymodem 升级应答缓存 ======================== */
|
||||
static rt_uint8_t g_eload_ymodem_resp = 0; // 南向返回的ymodem应答字节(0=无应答)
|
||||
@@ -304,7 +407,6 @@ int chrg_eload_parse_version (rt_uint8_t idx, rt_uint8_t *data, rt_uint16_t len)
|
||||
{
|
||||
rt_uint16_t version;
|
||||
|
||||
/* A one-register Modbus 0x03 response is 7 bytes including CRC16. */
|
||||
if ((idx >= TOTAL_SOU_CHS) || (data == RT_NULL) ||
|
||||
(len != (MODBUSRTU_SW_VERSION_LEN * 2U + 5U)) ||
|
||||
(data[0] != ID_ELOAD) || (data[1] != MB_REGISTER_RD) ||
|
||||
@@ -313,7 +415,7 @@ int chrg_eload_parse_version (rt_uint8_t idx, rt_uint8_t *data, rt_uint16_t len)
|
||||
}
|
||||
|
||||
version = u8v_to_u16(&data[3]);
|
||||
/* HOLD_REG_CH0_VERSION is the per-channel south board version register. */
|
||||
|
||||
usRegHoldBuf[HOLD_REG_CH0_VERSION + idx * TOTAL_HOLD_CH_REGS] = version;
|
||||
return 0;
|
||||
}
|
||||
@@ -340,9 +442,7 @@ int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16
|
||||
return -1;
|
||||
}
|
||||
struct eload_p1_t *pP1 = (struct eload_p1_t *)&data[3];
|
||||
// for(int i = 0;i<36;i++){
|
||||
// rt_kprintf("%02x ",data[i+3]);
|
||||
// }
|
||||
|
||||
pLOAD->status1 = pP1->status1;
|
||||
pLOAD->status2 = pP1->status2;
|
||||
pLOAD->fault1 = pP1->fault1;
|
||||
@@ -360,7 +460,10 @@ int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16
|
||||
pLOAD->vpkp = swap_u16(pP1->Vpkp);
|
||||
pLOAD->vpkn = swap_u16(pP1->Vpkn);
|
||||
pLOAD->vpp = swap_u16(pP1->Vpp);
|
||||
pLOAD->Ripple = swap_u16(pP1->Ripple);
|
||||
pLOAD->Ripple = chrg_eload_ripple_filter_update(idx,
|
||||
swap_u16(pP1->Ripple));
|
||||
// if(idx == IDX_CH3)
|
||||
// rt_kprintf("-----Ripple = %d-------\n",pLOAD->Ripple);
|
||||
/* 保留功率板返回的真实电流,供卸载保护判断使用。 */
|
||||
pSW->Now_current = pLOAD->current;
|
||||
/*
|
||||
@@ -374,6 +477,7 @@ int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16
|
||||
pLOAD->vpkn = 0;
|
||||
pLOAD->vpp = 0;
|
||||
pLOAD->Ripple = 0;
|
||||
chrg_eload_ripple_filter_reset(idx);
|
||||
pSW->VZ = 0;
|
||||
pSW->VF = 0;
|
||||
}
|
||||
@@ -381,14 +485,6 @@ int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16
|
||||
/* 电压、电流均更新完成后再发布序号,避免状态机读取到半更新数据。 */
|
||||
g_eload_sample_seq[idx]++;
|
||||
#if LCD_OPEN
|
||||
// for(int i = 0;i<4;i++){
|
||||
// if(pNOR->sw[i].change_flag == 0x01){
|
||||
// pNOR->free_flag = 0x00;
|
||||
// }else {
|
||||
// pNOR->free_flag = 0x01;
|
||||
// }
|
||||
// }
|
||||
// rt_kprintf("ch=%d,free_flag=%d,",idx,pNOR->free_flag);
|
||||
if(pNOR->free_flag==0x01){
|
||||
LCD_SHOW(idx, pP1, pLOAD, pSW, pSOU->online[idx]);
|
||||
}
|
||||
@@ -415,9 +511,11 @@ int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16
|
||||
usRegHoldBuf[HOLD_REG_CH0_VPKN+idx*TOTAL_HOLD_CH_REGS] = pLOAD->vpkn;
|
||||
usRegHoldBuf[HOLD_REG_CH0_VPP+idx*TOTAL_HOLD_CH_REGS] = pLOAD->vpp;
|
||||
usRegHoldBuf[HOLD_REG_CH0_Ripple+idx*TOTAL_HOLD_CH_REGS] = pLOAD->Ripple;
|
||||
usRegHoldBuf[HOLD_REG_CH0_VZ+idx*TOTAL_HOLD_CH_REGS] = pSW->VZ;
|
||||
usRegHoldBuf[HOLD_REG_CH0_VF+idx*TOTAL_HOLD_CH_REGS] = pSW->VF;
|
||||
// rt_kprintf("---------NOW VOLT END = %d\n----------",pSW->Now_voltage);
|
||||
/* 上位机D+/D-寄存器按0.001V解释,内部/LCD仍保持0.01V原始值。 */
|
||||
usRegHoldBuf[HOLD_REG_CH0_VZ+idx*TOTAL_HOLD_CH_REGS] =
|
||||
chrg_eload_dpdm_host_value(pSW->VZ);
|
||||
usRegHoldBuf[HOLD_REG_CH0_VF+idx*TOTAL_HOLD_CH_REGS] =
|
||||
chrg_eload_dpdm_host_value(pSW->VF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_eload.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.0
|
||||
当前版本号 : V1.1.22
|
||||
主要功能 : 源载命令数据解析,封装,读写请求,寄存器定义,数据结构定义。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -299,6 +299,9 @@ extern int chrg_eload_parse_version (rt_uint8_t idx, rt_uint8_t *data, rt_uint16
|
||||
其它说明: 每成功解析一帧P1数据后递增,用于区分控制命令前后的新旧量测
|
||||
*****************************************************************/
|
||||
extern rt_uint32_t chrg_eload_get_sample_seq(rt_uint8_t idx);
|
||||
extern void chrg_eload_ripple_filter_reset(rt_uint8_t idx);
|
||||
extern void chrg_eload_ripple_filter_relay_start(rt_uint8_t idx);
|
||||
extern void chrg_eload_ripple_filter_relay_stop(rt_uint8_t idx);
|
||||
// 定义2条继电器指令,控制主继电器和滤波继电器(保留原有有效指令,移除未使用的停止指令)
|
||||
static const rt_uint8_t EXTRA_CMD_SOURCE[8] = {0x03, 0x06, 0x00, 0x01, 0x00, 0x01, 0x18, 0x28};
|
||||
extern int chrg_eload_send_relay(rt_uint8_t cmd_type);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_sink.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.0
|
||||
当前版本号 : V1.1.24
|
||||
主要功能 : 北向数据sink请求数据填充及发送。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -24,6 +24,16 @@
|
||||
#define SINK_UNLOAD_CURRENT_MAX_MA 200U // 判定负载已安全卸载的最大实际电流
|
||||
#define SINK_UNLOAD_CONFIRM_COUNT 2U // 连续满足低电流条件的量测帧数
|
||||
#define SINK_SHORT_KEEPALIVE_MS 30U // 短路测试WORK_START保活下发周期
|
||||
#define SINK_INIT_NORTH_RELAY_DELAY_MS 30U // 辅助继电器回码后打开北向继电器的等待时间
|
||||
|
||||
#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
|
||||
|
||||
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};
|
||||
static rt_bool_t g_sink_init_in_progress[TOTAL_SOU_CHS] = {RT_FALSE};
|
||||
|
||||
/* 主继电器闭合跟踪:1=已提交ALL_REL_ON(主继电器应已闭合) 0=未闭合/已断开。
|
||||
* 用于0V硬保护区分"首次开载/握手恢复"(主继电器未闭合,Now_voltage=0属正常)
|
||||
@@ -41,6 +51,12 @@ static rt_tick_t g_sink_unload_query_tick[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t g_sink_unload_confirm_count[TOTAL_SOU_CHS] = {0};
|
||||
/* 记录各通道短路测试最近一次WORK_START的发送时间。 */
|
||||
static rt_tick_t g_sink_short_start_tick[TOTAL_SOU_CHS] = {0};
|
||||
/* 只消费新的V_On电压采样,避免同一帧重复计入稳定确认。 */
|
||||
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};
|
||||
|
||||
enum {
|
||||
SINK_UNLOAD_NEXT_RUNNING = 0,
|
||||
@@ -235,47 +251,27 @@ int sink_set_pd (rt_uint8_t pd, rt_uint8_t *buf)
|
||||
return chrg_north_send(frame, len);
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_sink_work_init
|
||||
函数描述: 执行负载INIT入口,将副继电器和初始化命令按顺序提交南向
|
||||
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
|
||||
输出参数: 立即打开北向Sink继电器和enable,并进入INIT_AUX_WAIT后台确认
|
||||
返回说明: 0:初始化批次提交成功 <0:参数或命令提交失败
|
||||
其它说明: 南向顺序为FU_REL_ON、CURRENT=0、MODE、VON和可选测试停止;
|
||||
北向按“CC线->协议->电压查询”顺序执行,南北向从批次入队后并行执行
|
||||
*****************************************************************/
|
||||
static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
static rt_uint8_t chrg_sink_init_build_commands(struct chrg_switch_t *pSW,
|
||||
rt_uint16_t *regs, rt_uint16_t *vals)
|
||||
{
|
||||
struct chrg_north_t *pNOR = &chrgnorth;
|
||||
rt_uint16_t regs[6];
|
||||
rt_uint16_t vals[6];
|
||||
rt_bool_t first_start;
|
||||
rt_uint8_t count;
|
||||
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
/* COM进入INIT前已经设置On_work,使用原工作状态区分首次提交和后台重试。 */
|
||||
first_start = (pSW->sink.work_mode != SINK_WORK_INIT_AUX_WAIT) ?
|
||||
RT_TRUE : RT_FALSE;
|
||||
regs[0] = ModbusRTU_ShortTest_CMD_MODE_FLAG;
|
||||
vals[0] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
if (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) {
|
||||
/* 短路测试退出:先断开全部继电器,再打开辅助继电器,最后停止短路测试。 */
|
||||
regs[0] = REG_REL_ON; vals[0] = ALL_REL_OFF;
|
||||
regs[1] = REG_REL_ON; vals[1] = ALL_REL_OFF;
|
||||
regs[2] = REG_REL_ON; vals[2] = FU_REL_ON;
|
||||
regs[3] = REG_CURRENT_OUT + 1; vals[3] = 0;
|
||||
regs[4] = REG_MODE; vals[4] = pSW->CV_mode;
|
||||
regs[5] = Eload_Start_V_ON; vals[5] = (pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on;
|
||||
count = 6U;
|
||||
} else {
|
||||
regs[1] = REG_REL_ON; vals[1] = FU_REL_ON;
|
||||
regs[2] = REG_CURRENT_OUT + 1; vals[2] = 0;
|
||||
regs[3] = REG_MODE; vals[3] = pSW->CV_mode;
|
||||
regs[4] = Eload_Start_V_ON; vals[4] =
|
||||
(pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on;
|
||||
regs[5] = ModbusRTU_ShortTest_CMD_MODE_FLAG;
|
||||
vals[5] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
count = 6U;
|
||||
} else {
|
||||
regs[0] = REG_REL_ON; vals[0] = FU_REL_ON;
|
||||
regs[1] = REG_CURRENT_OUT + 1; vals[1] = 0;
|
||||
regs[2] = REG_MODE; vals[2] = pSW->CV_mode;
|
||||
regs[3] = Eload_Start_V_ON; vals[3] =
|
||||
(pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on;
|
||||
count = 4U;
|
||||
count = 5U;
|
||||
if (pSW->sink.test.test_mode_old == SINK_TEST_OCP) {
|
||||
regs[count] = ModbusRTU_OCPTest_CMD_MODE_FLAG;
|
||||
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
@@ -284,18 +280,67 @@ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
}
|
||||
}
|
||||
if (chrg_sou_com_batch_submit(idx, regs, vals, count) != 0) {
|
||||
return count;
|
||||
}
|
||||
|
||||
static int chrg_sink_init_submit_stage(eIDX_SOU_CH idx,
|
||||
struct chrg_switch_t *pSW)
|
||||
{
|
||||
rt_uint16_t regs[6];
|
||||
rt_uint16_t vals[6];
|
||||
rt_uint8_t total;
|
||||
rt_uint8_t start;
|
||||
rt_uint8_t count;
|
||||
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
total = chrg_sink_init_build_commands(pSW, regs, vals);
|
||||
|
||||
switch (g_sink_init_stage[idx]) {
|
||||
case SINK_INIT_STAGE_CLEAR_RELAY:
|
||||
start = 0U;
|
||||
count = 1U;
|
||||
break;
|
||||
case SINK_INIT_STAGE_AUX_RELAY:
|
||||
start = 1U;
|
||||
count = 1U;
|
||||
break;
|
||||
case SINK_INIT_STAGE_REST:
|
||||
start = 2U;
|
||||
count = (rt_uint8_t)(total - 2U);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return chrg_sou_com_batch_submit(idx, ®s[start], &vals[start], count);
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_sink_work_init
|
||||
函数描述: 执行负载INIT入口,首帧先退出短路测试
|
||||
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
|
||||
输出参数: 进入INIT_AUX_WAIT后台等待南向回码
|
||||
返回说明: 0:初始化命令提交成功 <0:参数或命令提交失败
|
||||
其它说明: 收到01 02有效回码后等待30ms,再打开北向Sink继电器
|
||||
*****************************************************************/
|
||||
static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
{
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
/* 初始化必须优先于本通道尚未发送的旧命令,避免旧WORK_START插入初始化流程。 */
|
||||
if (chrg_sou_com_batch_cancel(idx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
g_sink_init_in_progress[idx] = RT_TRUE;
|
||||
g_sink_init_stage[idx] = SINK_INIT_STAGE_CLEAR_RELAY;
|
||||
g_sink_init_north_relay_tick[idx] = 0U;
|
||||
if (chrg_sink_init_submit_stage(idx, pSW) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pSW->sink.On_work = 1;
|
||||
if (first_start) {
|
||||
/* 南向批次已入队,北向按“CC->协议”顺序立即开始完整握手。 */
|
||||
chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SINK);
|
||||
pNOR->enable[idx] = 1;
|
||||
/* Protocol-before-CC test flow is disabled in this version. */
|
||||
pSW->sub = IDX_SET_SINK_CC_BEFORE_PD;
|
||||
}
|
||||
pSW->sink.work_mode = SINK_WORK_INIT_AUX_WAIT;
|
||||
return 0;
|
||||
}
|
||||
@@ -311,6 +356,7 @@ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
static int chrg_sink_work_init_aux_wait(eIDX_SOU_CH idx,
|
||||
struct chrg_switch_t *pSW)
|
||||
{
|
||||
struct chrg_north_t *pNOR = &chrgnorth;
|
||||
struct chrg_south_t *pSOU = &chrgsouth;
|
||||
int batch_result;
|
||||
|
||||
@@ -320,13 +366,40 @@ static int chrg_sink_work_init_aux_wait(eIDX_SOU_CH idx,
|
||||
if (chrg_sou_com_batch_is_pending(idx)) {
|
||||
return 0;
|
||||
}
|
||||
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_DELAY) {
|
||||
if ((rt_int32_t)(rt_tick_get() -
|
||||
g_sink_init_north_relay_tick[idx]) < 0) {
|
||||
return 0;
|
||||
}
|
||||
chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SINK);
|
||||
#if SOUTH_COMMAND_DEBUG
|
||||
rt_kprintf("-------D+D- OPEN------\n");
|
||||
#endif
|
||||
pNOR->enable[idx] = 1;
|
||||
pSW->sub = IDX_SET_SINK_CC_BEFORE_PD;
|
||||
g_sink_init_stage[idx] = SINK_INIT_STAGE_REST;
|
||||
if (chrg_sink_init_submit_stage(idx, pSW) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
batch_result = chrg_sou_com_batch_get_result(idx);
|
||||
if (batch_result <= 0) {
|
||||
/* 北向保持当前事务,南向独立重试完整初始化批次。 */
|
||||
return chrg_sink_work_init(idx, pSW);
|
||||
return chrg_sink_init_submit_stage(idx, pSW);
|
||||
}
|
||||
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_CLEAR_RELAY) {
|
||||
g_sink_init_stage[idx] = SINK_INIT_STAGE_AUX_RELAY;
|
||||
return chrg_sink_init_submit_stage(idx, pSW);
|
||||
}
|
||||
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_AUX_RELAY) {
|
||||
g_sink_init_stage[idx] = SINK_INIT_STAGE_DELAY;
|
||||
g_sink_init_north_relay_tick[idx] = rt_tick_get() +
|
||||
rt_tick_from_millisecond(SINK_INIT_NORTH_RELAY_DELAY_MS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 北向可能已推进到CC或电压查询,此处只更新南向完成状态。 */
|
||||
g_sink_init_in_progress[idx] = RT_FALSE;
|
||||
pSOU->online[idx] = 1;
|
||||
pSW->sink.test.test_mode_old = 0;
|
||||
pSW->sink.work_mode = SINK_WORK_WAIT;
|
||||
@@ -420,6 +493,9 @@ static int chrg_sink_work_volt_runing(eIDX_SOU_CH idx, struct chrg_switch_t *pSW
|
||||
}
|
||||
/* RUNING批次必须完整发送:ALL_REL_ON、目标负载值、WORK_START。 */
|
||||
ret = chrg_sou_com_batch_submit(idx, regs, vals, 3U);
|
||||
#if SOUTH_COMMAND_DEBUG
|
||||
rt_kprintf("------ALL_REL_ON-------\n");
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
g_sink_rel_main_closed[idx] = 1;
|
||||
}
|
||||
@@ -444,9 +520,16 @@ static int chrg_sink_work_load_stop(eIDX_SOU_CH idx,
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
/* 自动停止优先于同通道残留启动批次,禁止STOP后继续发送WORK_START。 */
|
||||
g_sink_init_in_progress[idx] = RT_FALSE;
|
||||
if (chrg_sou_com_batch_cancel(idx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (chrg_sou_com_batch_submit(idx, ®, &val, 1U) != 0) {
|
||||
return -1;
|
||||
}
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
g_sink_load_restart_confirm_count[idx] = 0U;
|
||||
pSW->sink.work_mode = SINK_WORK_LOAD_STANDBY;
|
||||
return 0;
|
||||
}
|
||||
@@ -490,6 +573,7 @@ static int chrg_sink_nor_work_low(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
return -1;
|
||||
}
|
||||
/* 低压保护优先级最高,先丢弃该通道残留的启动或设定命令。 */
|
||||
g_sink_init_in_progress[idx] = RT_FALSE;
|
||||
if (chrg_sou_com_batch_cancel(idx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -551,6 +635,7 @@ static int chrg_sink_work_stop_prepare(eIDX_SOU_CH idx,
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
g_sink_init_in_progress[idx] = RT_FALSE;
|
||||
if (chrg_sou_com_batch_cancel(idx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -662,15 +747,23 @@ static int chrg_sink_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
*****************************************************************/
|
||||
static int chrg_sink_work_protocol_unload(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
{
|
||||
rt_uint16_t reg = REG_WORK;
|
||||
rt_uint16_t value = WORK_STOP;
|
||||
rt_uint16_t regs[2] = {REG_WORK, Eload_Start_V_ON};
|
||||
rt_uint16_t values[2];
|
||||
|
||||
if (chrg_sou_com_batch_submit(idx, ®, &value, 1U) != 0) {
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
values[0] = WORK_STOP;
|
||||
values[1] = (pSW->sink.volt_on <= 500U) ? 500U : pSW->sink.volt_on;
|
||||
|
||||
if (chrg_sou_com_batch_submit(idx, regs, values, 2U) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* WORK_STOP回码只确认命令接收;还需连续读取两帧合格的实际电流。 */
|
||||
chrg_sink_unload_confirm_reset(idx);
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
g_sink_load_restart_confirm_count[idx] = 0U;
|
||||
pSW->sink.work_mode = SINK_WORK_UNLOAD_WAIT;
|
||||
return 0;
|
||||
}
|
||||
@@ -680,31 +773,35 @@ static int chrg_sink_work_protocol_unload(eIDX_SOU_CH idx, struct chrg_switch_t
|
||||
函数描述: 执行协议或CC完整重握手前的南向安全断开和副继电器恢复
|
||||
输入参数: idx:南向功率板通道 pSW:当前通道控制结构体
|
||||
输出参数: 命令提交成功后进入SINK_WORK_HANDSHAKE_WAIT
|
||||
返回说明: 0:四条命令提交成功 <0:参数或命令提交失败
|
||||
其它说明: 命令顺序固定为WORK_STOP、CURRENT=1000mA、ALL_REL_OFF、FU_REL_ON;
|
||||
WORK_STOP已关闭电子负载,1000mA仅为功率板停止阶段保存的电流设定值;
|
||||
返回说明: 0:两条命令提交成功 <0:参数或命令提交失败
|
||||
其它说明: 命令顺序固定为WORK_STOP、Eload_Start_V_ON;
|
||||
WORK_STOP已关闭电子负载,V_ON随后更新到功率板;
|
||||
主副继电器全部断开并确认回码后再单独打开副继电器;
|
||||
北向Sink继电器保持接通,低电流确认完成后才允许重新握手
|
||||
*****************************************************************/
|
||||
static int chrg_sink_work_handshake_prepare(eIDX_SOU_CH idx,
|
||||
struct chrg_switch_t *pSW)
|
||||
{
|
||||
rt_uint16_t reg = REG_WORK;
|
||||
rt_uint16_t value = WORK_STOP;
|
||||
rt_uint16_t regs[2] = {REG_WORK, Eload_Start_V_ON};
|
||||
rt_uint16_t values[2];
|
||||
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
values[0] = WORK_STOP;
|
||||
values[1] = (pSW->sink.volt_on <= 500U) ? 500U : pSW->sink.volt_on;
|
||||
/* 新CC/协议事务必须抢占本通道尚未发送的旧启动和设定命令。 */
|
||||
if (chrg_sou_com_batch_cancel(idx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (chrg_sou_com_batch_submit(idx, ®, &value, 1U) != 0) {
|
||||
if (chrg_sou_com_batch_submit(idx, regs, values, 2U) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 协议或CC切换先停止电子负载,连续2次确认低电流后再操作继电器。 */
|
||||
chrg_sink_unload_confirm_reset(idx);
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
g_sink_load_restart_confirm_count[idx] = 0U;
|
||||
pSW->sink.work_mode = SINK_WORK_HANDSHAKE_WAIT;
|
||||
return 0;
|
||||
}
|
||||
@@ -716,7 +813,7 @@ static int chrg_sink_work_handshake_prepare(eIDX_SOU_CH idx,
|
||||
full_handshake:RT_TRUE为完整重握手 RT_FALSE为协议参数软卸载
|
||||
输出参数: 确认成功后将sink.work_mode切换为SINK_WORK_WAIT
|
||||
返回说明: 0:继续等待或确认成功 <0:状态查询或优先量测请求失败
|
||||
其它说明: 软卸载只要求WORK_STOP收到匹配回码后建立量测基线;完整重握手
|
||||
其它说明: 软卸载要求WORK_STOP和V_ON收到匹配回码后建立量测基线;完整重握手
|
||||
还要求CURRENT=1000mA、ALL_REL_OFF和FU_REL_ON回码成功,但保持北向Sink继电器接通
|
||||
*****************************************************************/
|
||||
static int chrg_sink_work_current_wait(eIDX_SOU_CH idx,
|
||||
@@ -828,13 +925,15 @@ static int chrg_sink_TEST_DYNA_ON(rt_uint8_t ch, struct chrg_switch_t *pSW)
|
||||
*****************************************************************/
|
||||
static int chrg_sink_TEST_SHORT_ON(rt_uint8_t ch, struct chrg_switch_t *pSW)
|
||||
{
|
||||
rt_uint16_t regs[3] = {ModbusRTU_ShortTest_Hold_Time,ModbusRTU_ShortTest_CMD_MODE_FLAG, REG_WORK};
|
||||
rt_uint16_t vals[3] = {0,0,0};
|
||||
rt_uint16_t regs[4] = {Eload_Start_V_ON,
|
||||
ModbusRTU_ShortTest_Hold_Time, ModbusRTU_ShortTest_CMD_MODE_FLAG,
|
||||
REG_WORK};
|
||||
rt_uint16_t vals[4] = {SINK_SHORT_TEST_VON_MV, 0, 0, 0};
|
||||
/* 外部工程固定短路测试保持60秒且不带载。 */
|
||||
vals[0] = 60000;
|
||||
vals[1] = ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR;
|
||||
vals[2] = WORK_START;
|
||||
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 3) == 0) {
|
||||
vals[1] = 60000;
|
||||
vals[2] = ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR;
|
||||
vals[3] = WORK_START;
|
||||
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 4) == 0) {
|
||||
g_sink_short_start_tick[ch] = rt_tick_get();
|
||||
return 0;
|
||||
}
|
||||
@@ -875,9 +974,18 @@ static rt_uint8_t chrg_sink_voltage_confirm_count(rt_uint16_t target_volt_mv)
|
||||
SINK_VOLT_CONFIRM_5V_COUNT : SINK_VOLT_CONFIRM_NORMAL_COUNT;
|
||||
}
|
||||
|
||||
static rt_uint16_t chrg_sink_von_voltage_get(const struct chrg_switch_t *pSW)
|
||||
{
|
||||
#if SINK_VON_VOLTAGE_SOURCE == SINK_VON_VOLTAGE_FROM_PROTOCOL
|
||||
return pSW->Sink_voltage;
|
||||
#else
|
||||
return pSW->Now_voltage;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_sink_volt_judge
|
||||
函数描述: 使用南向功率板实际电压Now_voltage与V_On比较,驱动负载进入RUNING
|
||||
函数描述: 使用配置的V_On电压源与V_On比较,控制负载启停
|
||||
输入参数: idx:南向通道 pSW:通道控制结构体
|
||||
输出参数: 更新sink.work_mode并调用chrg_sink_work
|
||||
返回说明: 0:处理完成 <0:参数错误
|
||||
@@ -885,76 +993,132 @@ static rt_uint8_t chrg_sink_voltage_confirm_count(rt_uint16_t target_volt_mv)
|
||||
int chrg_sink_volt_judge(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
{
|
||||
rt_uint8_t stable_confirm_count;
|
||||
rt_uint32_t voltage_seq;
|
||||
static rt_uint8_t volt_low[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t no_curr[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t volt_set_ok[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t volt_stable_count[TOTAL_SOU_CHS] = {0};
|
||||
static rt_tick_t running_start_tick[TOTAL_SOU_CHS] = {0};
|
||||
rt_uint16_t von_voltage;
|
||||
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
/* 初始化及短路退出批次完成前,禁止电压判定提前启动普通负载。 */
|
||||
if (g_sink_init_in_progress[idx] ||
|
||||
(pSW->sink.work_mode == SINK_WORK_INIT) ||
|
||||
(pSW->sink.work_mode == SINK_WORK_INIT_AUX_WAIT)) {
|
||||
if (g_sink_init_in_progress[idx] &&
|
||||
(pSW->sink.work_mode != SINK_WORK_INIT) &&
|
||||
(pSW->sink.work_mode != SINK_WORK_INIT_AUX_WAIT)) {
|
||||
pSW->sink.work_mode = SINK_WORK_INIT_AUX_WAIT;
|
||||
}
|
||||
return chrg_sink_work(idx, pSW);
|
||||
}
|
||||
|
||||
#if SINK_VON_VOLTAGE_SOURCE == SINK_VON_VOLTAGE_FROM_PROTOCOL
|
||||
voltage_seq = pSW->Sink_voltage_seq;
|
||||
#else
|
||||
voltage_seq = chrg_eload_get_sample_seq(idx);
|
||||
#endif
|
||||
if (voltage_seq == g_sink_von_voltage_seq[idx]) {
|
||||
return chrg_sink_work(idx, pSW);
|
||||
}
|
||||
g_sink_von_voltage_seq[idx] = voltage_seq;
|
||||
/* 卸载和协议重配尚未完成时,旧电压只能用于推进安全流程,不能触发WORK_START。 */
|
||||
if ((pSW->sink.work_mode == SINK_WORK_UNLOAD_WAIT) ||
|
||||
(pSW->sink.work_mode == SINK_WORK_HANDSHAKE_WAIT) ||
|
||||
((pSW->sink.work_mode != SINK_WORK_RUNING) &&
|
||||
(pSW->sink.work_mode != SINK_WORK_LOAD_STANDBY) &&
|
||||
(pSW->sub != IDX_GET_SINK_VC))) {
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
g_sink_load_restart_confirm_count[idx] = 0U;
|
||||
return chrg_sink_work(idx, pSW);
|
||||
}
|
||||
von_voltage = chrg_sink_von_voltage_get(pSW);
|
||||
stable_confirm_count = chrg_sink_voltage_confirm_count(
|
||||
pSW->sink.pro_loadv);
|
||||
|
||||
if ((pSW->sink.On_work == 1) && (pSW->sink.test.test_mode == 0)) {
|
||||
if (pSW->Now_voltage <= WORK_MIN_VOLT) {
|
||||
volt_stable_count[idx] = 0;
|
||||
if (von_voltage <= WORK_MIN_VOLT) {
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
g_sink_load_restart_confirm_count[idx] = 0U;
|
||||
if (volt_set_ok[idx] == 1 && ++volt_low[idx] >= 50) {
|
||||
volt_set_ok[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
if (pSW->sink.work_mode == SINK_WORK_RUNING) {
|
||||
return chrg_sink_work_load_stop(idx, pSW);
|
||||
}
|
||||
} else {
|
||||
volt_low[idx] = 0;
|
||||
volt_set_ok[idx] = 1;
|
||||
pSW->change_flag = 0;
|
||||
if (pSW->sink.work_mode == SINK_WORK_LOW_MONITOR) {
|
||||
/* 副继电器恢复后首次重新检测到输入电压,只启动协议重握手。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
no_curr[idx] = 0;
|
||||
pSW->sink.work_mode = SINK_WORK_WAIT;
|
||||
} else if ((pSW->sink.work_mode == SINK_WORK_LOW) ||
|
||||
(pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT)) {
|
||||
/* 掉电安全批次完成前,不得进入RUNING。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
no_curr[idx] = 0;
|
||||
} else if (pSW->Now_voltage > pSW->sink.volt_on) {
|
||||
} else if (von_voltage > pSW->sink.volt_on) {
|
||||
if ((pSW->sink.work_mode != SINK_WORK_RUNING) &&
|
||||
(pSW->sink.work_mode != SINK_WORK_LOAD_STANDBY)) {
|
||||
if (volt_stable_count[idx] < stable_confirm_count) {
|
||||
volt_stable_count[idx]++;
|
||||
if (g_sink_von_stable_count[idx] < stable_confirm_count) {
|
||||
g_sink_von_stable_count[idx]++;
|
||||
}
|
||||
if (volt_stable_count[idx] >= stable_confirm_count) {
|
||||
if (g_sink_von_stable_count[idx] >= stable_confirm_count) {
|
||||
/* 连续达到当前目标对应的确认帧数后进入RUNING。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
pSW->sink.work_mode = SINK_WORK_RUNING;
|
||||
running_start_tick[idx] = rt_tick_get();
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
} else if (pSW->sink.work_mode == SINK_WORK_LOAD_STANDBY) {
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
if (chrg_sink_load_should_start(pSW)) {
|
||||
/* 自动停止后只接受新的电压采样;达到恢复确认帧数才允许重启。 */
|
||||
if (g_sink_load_restart_confirm_count[idx] <
|
||||
SINK_LOAD_RESTART_CONFIRM_COUNT) {
|
||||
g_sink_load_restart_confirm_count[idx]++;
|
||||
}
|
||||
if (g_sink_load_restart_confirm_count[idx] >=
|
||||
SINK_LOAD_RESTART_CONFIRM_COUNT) {
|
||||
g_sink_load_restart_confirm_count[idx] = 0U;
|
||||
pSW->sink.work_mode = SINK_WORK_RUNING;
|
||||
running_start_tick[idx] = rt_tick_get();
|
||||
}
|
||||
} else {
|
||||
g_sink_load_restart_confirm_count[idx] = 0U;
|
||||
}
|
||||
} else if ((pSW->Now_current > 200) || (pSW->sink.loadc < 200)) {
|
||||
volt_stable_count[idx] = 0;
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
no_curr[idx] = 0;
|
||||
} else if ((rt_tick_t)(rt_tick_get() - running_start_tick[idx]) >=
|
||||
rt_tick_from_millisecond(SINK_CURRENT_START_GRACE_MS)) {
|
||||
volt_stable_count[idx] = 0;
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
if (++no_curr[idx] >= 2) {
|
||||
pSW->sink.work_mode = SINK_WORK_WAIT;
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
volt_stable_count[idx] = 0;
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
g_sink_load_restart_confirm_count[idx] = 0U;
|
||||
no_curr[idx] = 0;
|
||||
/* 配置电压源低于负载设置的V_On时,立即停止电子负载。 */
|
||||
if (pSW->sink.work_mode == SINK_WORK_RUNING) {
|
||||
return chrg_sink_work_load_stop(idx, pSW);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
volt_low[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
volt_set_ok[idx] = 0;
|
||||
volt_stable_count[idx] = 0;
|
||||
g_sink_von_stable_count[idx] = 0U;
|
||||
}
|
||||
return chrg_sink_work(idx, pSW);
|
||||
}
|
||||
@@ -1027,6 +1191,13 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
}
|
||||
work_mode = pSW->sink.work_mode;
|
||||
workmode = work_mode;
|
||||
/* 初始化事务独占该通道,防止其它路径改成RUNING后追加普通启动命令。 */
|
||||
if (g_sink_init_in_progress[idx] &&
|
||||
(work_mode != SINK_WORK_INIT) &&
|
||||
(work_mode != SINK_WORK_INIT_AUX_WAIT)) {
|
||||
pSW->sink.work_mode = SINK_WORK_INIT_AUX_WAIT;
|
||||
work_mode = SINK_WORK_INIT_AUX_WAIT;
|
||||
}
|
||||
if (work_mode != SINK_WORK_ON_TEST_SHORT) {
|
||||
g_sink_short_start_tick[idx] = 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_sink.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.0
|
||||
当前版本号 : V1.1.23
|
||||
主要功能 : 北向数据sink请求数据填充及发送,命令字定义。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -22,7 +22,9 @@ struct chrg_switch_t;
|
||||
#define SINK_TEST_OCP 2
|
||||
#define SINK_TEST_DYNA 3
|
||||
/* 短路测试期间按该周期补发WORK_START,防止功率板因超时停止。 */
|
||||
#define SINK_SHORT_START_INTERVAL_MS 30U
|
||||
#define SINK_SHORT_START_INTERVAL_MS 50U
|
||||
/* 短路测试不受普通V_ON门槛限制,功率板使用最低启动电压。 */
|
||||
#define SINK_SHORT_TEST_VON_MV 500U
|
||||
//短路测试结构体
|
||||
struct test_short {
|
||||
rt_uint16_t hold_time; // 短路测试时间
|
||||
@@ -212,8 +214,8 @@ enum {
|
||||
|
||||
/* Sink电压稳定确认参数,供chrg_sink_volt_judge使用。 */
|
||||
#define SINK_VOLT_5V_TARGET_MV 5000U
|
||||
#define SINK_VOLT_CONFIRM_NORMAL_COUNT 5U
|
||||
#define SINK_VOLT_CONFIRM_5V_COUNT 15U
|
||||
#define SINK_VOLT_CONFIRM_NORMAL_COUNT 3U
|
||||
#define SINK_VOLT_CONFIRM_5V_COUNT 12U
|
||||
#define SINK_CURRENT_START_GRACE_MS 200U
|
||||
|
||||
/* 主继电器闭合后的0V持续监控:由北向轮询线程每轮调用,掉电立即转掉电保护。 */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_source.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.10
|
||||
当前版本号 : V1.1.14
|
||||
主要功能 : 北向数据source请求数据填充及发送。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_source.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.10
|
||||
当前版本号 : V1.1.14
|
||||
主要功能 : 北向数据source请求数据填充及发送,命令字定义。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
*** Using Compiler 'V6.16', folder: 'D:\keil mdk 2\ARM\ARMCLANG\Bin'
|
||||
Build target 'chrg'
|
||||
compiling chrg_north_pkg.c...
|
||||
compiling chrg_vcom.c...
|
||||
compiling chrg_thread.c...
|
||||
compiling chrg_lcd.c...
|
||||
compiling chrg_rel.c...
|
||||
compiling chrg_eload.c...
|
||||
compiling chrg_south.c...
|
||||
compiling chrg_sink.c...
|
||||
compiling chrg_north.c...
|
||||
compiling chrg_source.c...
|
||||
compiling chrg_roll_nor.c...
|
||||
compiling chrg_roll_sou.c...
|
||||
compiling chrg_comm.c...
|
||||
linking...
|
||||
Program Size: Code=151456 RO-data=15928 RW-data=9356 ZI-data=16052
|
||||
FromELF: creating hex file...
|
||||
After Build - User command #1: fromelf --bin .\build\keil\Obj\chrg.axf -o build/chrg.bin
|
||||
".\build\keil\Obj\chrg.axf" - 0 Error(s), 0 Warning(s).
|
||||
Build Time Elapsed: 00:00:05
|
||||
@@ -0,0 +1,9 @@
|
||||
*** Using Compiler 'V6.16', folder: 'D:\keil mdk 2\ARM\ARMCLANG\Bin'
|
||||
Build target 'chrg'
|
||||
compiling chrg_comm.c...
|
||||
linking...
|
||||
Program Size: Code=151160 RO-data=15928 RW-data=9356 ZI-data=16036
|
||||
FromELF: creating hex file...
|
||||
After Build - User command #1: fromelf --bin .\build\keil\Obj\chrg.axf -o build/chrg.bin
|
||||
".\build\keil\Obj\chrg.axf" - 0 Error(s), 0 Warning(s).
|
||||
Build Time Elapsed: 00:00:02
|
||||
+47
-951
File diff suppressed because it is too large
Load Diff
+88
-2502
File diff suppressed because it is too large
Load Diff
+119
@@ -1,5 +1,124 @@
|
||||
# 修改日志
|
||||
|
||||
## V1.1.23 - 2026-09-11
|
||||
|
||||
### 负载初始化继电器时序调整
|
||||
|
||||
- 负载初始化先单独下发辅助继电器 `FU_REL_ON (0x01 0x02)`。
|
||||
- 只有收到匹配回码后,等待 `30ms` 才打开北向负载继电器。
|
||||
- 北向继电器打开后,再继续下发电流、模式、V_On等初始化参数。
|
||||
- 辅助继电器回码失败时只重发当前初始化阶段,不提前打开北向继电器。
|
||||
- 协议板版本号更新为 `V1.1.23`,固件版本寄存器更新为 `0x0127`。
|
||||
|
||||
### 待验证
|
||||
|
||||
- 上电初始化日志中确认先收到 `01 02`,约30ms后才出现北向继电器打开。
|
||||
- 辅助继电器无回码时确认北向继电器保持关闭。
|
||||
|
||||
## V1.1.22 - 2026-09-11
|
||||
|
||||
### 增加纹波滤波调试打印
|
||||
|
||||
- 记录每个通道主继电器闭合的 tick 和忽略帧配置。
|
||||
- 打印忽略帧的序号、原始纹波值及继电器闭合后的经过时间。
|
||||
- 打印实际进入滤波的帧序号、原始值、滤波值及经过时间。
|
||||
- 协议板版本号更新为 `V1.1.22`,固件版本寄存器更新为 `0x0126`。
|
||||
- 待验证:确认宏调整后忽略帧数量和实际进入滤波的时间同步变化。
|
||||
|
||||
## V1.1.21 - 2026-09-11
|
||||
|
||||
### 继电器切换纹波滤波
|
||||
|
||||
- 主继电器闭合回码成功后,忽略 `RIPPLE_RELAY_IGNORE_P1_COUNT` 个新P1采样帧;当前值为 `2U`,可在 `chrg_north.h` 中调整。
|
||||
- 后续纹波持续使用三点中值滤波,滤除单帧尖峰;连续异常纹波仍会正常显示。
|
||||
- LCD和上位机纹波寄存器统一使用滤波后的纹波值。
|
||||
- 协议板版本号更新为 `V1.1.21`,固件版本寄存器更新为 `0x0125`。
|
||||
|
||||
### 验证记录
|
||||
|
||||
- 待硬件验证:继电器闭合瞬间出现约500mV尖峰时,LCD和上位机不显示该尖峰;忽略2帧后再累计3帧并更新为稳定纹波。
|
||||
- 待硬件验证:持续三帧以上的高纹波仍能在LCD和上位机显示。
|
||||
|
||||
## V1.1.20 - 2026-09-10
|
||||
|
||||
### D+/D-上位机显示单位修正
|
||||
|
||||
- 上位机读取各通道 `D+`、`D-` 保持寄存器时,返回值由内部原始值放大 `10` 倍,适配上位机按 `0.001V` 解析。
|
||||
- LCD显示、北向回码解析及内部控制值保持原有 `0.01V` 单位,不受影响。
|
||||
- 增加16位寄存器上限保护,放大结果超过 `65535` 时返回 `65535`。
|
||||
- 协议板版本号更新为 `V1.1.20`,固件版本寄存器更新为 `0x0124`。
|
||||
|
||||
### 验证记录
|
||||
|
||||
- 待硬件验证:D+或D-内部值为 `260` 时,LCD继续显示 `2.60V`,上位机读取值为 `2600` 并显示 `2.600V`。
|
||||
|
||||
## V1.1.19 - 2026-09-10
|
||||
|
||||
### 短路测试绕过 V_ON 门槛
|
||||
|
||||
- 短路测试启动批次增加 `Eload_Start_V_ON=500mV`,保证5V输入时不因用户设置的6V V_ON而拒绝短路启动。
|
||||
- 短路测试期间忽略普通 V_ON 更新,保持功率板使用500mV短路启动门槛。
|
||||
- 退出短路测试时由初始化流程恢复用户设定的 V_ON。
|
||||
- 协议板版本号更新为 `V1.1.19`,固件版本寄存器更新为 `0x0123`。
|
||||
|
||||
### 验证记录
|
||||
|
||||
- 待硬件验证:V_ON=6V、输入PD5V时,短路模式仍能启动并按周期发送 `WORK_START`。
|
||||
- 待硬件验证:退出短路模式后,功率板恢复用户设定的 V_ON。
|
||||
|
||||
## V1.1.18 - 2026-09-10
|
||||
|
||||
### 修复 CC 切换时 V_ON 丢失
|
||||
|
||||
- 修复 V_ON 与 CC 线同时修改时,V_ON 命令先入队后被完整握手清队列取消的问题。
|
||||
- 协议软卸载和完整握手统一按 `WORK_STOP -> Eload_Start_V_ON` 顺序下发。
|
||||
- 握手或命令重试时会重新携带当前 V_ON,避免功率板继续使用旧阈值。
|
||||
- 协议板版本号更新为 `V1.1.18`,固件版本寄存器更新为 `0x0122`。
|
||||
|
||||
### 验证记录
|
||||
|
||||
- 待硬件验证:同时修改 V_ON 与 CC 线,确认功率板收到 `Eload_Start_V_ON`。
|
||||
- 待硬件验证:确认 CC 切换失败重试、协议电压切换和其他通道调度正常。
|
||||
|
||||
## V1.1.17 - 2026-09-10
|
||||
|
||||
### 协议切换期间禁止误启动
|
||||
|
||||
- 将 V_On 稳定确认计数提升为通道级状态,并在协议软卸载、完整握手和自动停止时清零。
|
||||
- `UNLOAD_WAIT`、`HANDSHAKE_WAIT` 以及协议配置未完成前,电压采样只推进安全流程,不允许进入 `RUNING` 或发送 `WORK_START`。
|
||||
- 协议/模式配置完成并回到 `IDX_GET_SINK_VC` 后,才重新累计 V_On 稳定确认帧,避免停止后约40ms因旧电压再次启动。
|
||||
- 协议板版本号更新为 `V1.1.17`,固件版本寄存器更新为 `0x0121`。
|
||||
|
||||
### 验证记录
|
||||
|
||||
- 待硬件验证:协议电压切换时确认 `WORK_STOP` 后、协议配置完成前不再出现 `WORK_START`。
|
||||
- 待硬件验证:确认协议切换、自动停止恢复和三个通道并行调度不受影响。
|
||||
|
||||
## V1.1.16 - 2026-09-09
|
||||
|
||||
### Sink 自动停止后的恢复保护
|
||||
|
||||
- 新增 `SINK_LOAD_RESTART_CONFIRM_COUNT`,位于 `chrg_north.h`,当前设为 `2U`。
|
||||
- 功率板电压作为 V_On 判定源时,仅消费新的 P1 采样帧,旧采样不再重复累计稳定次数。
|
||||
- 自动停止带载时取消本通道残留南向批次,再单独下发 `WORK_STOP`,避免残留 `WORK_START` 在停止后执行。
|
||||
- 自动恢复带载前必须收到连续 `2` 帧新的、满足 `V_On` 的电压采样。
|
||||
|
||||
### 验证记录
|
||||
|
||||
- 待硬件验证:触发 CH1 自动停止后,确认停止后 2 帧新P1到达前不再出现 `WORK_START`;同时确认其它通道南向命令正常调度。
|
||||
|
||||
## V1.1.15 - 2026-09-09
|
||||
|
||||
### 运行中 V_On 更新
|
||||
|
||||
- Sink 通道处于运行状态时,参数生效检测到 `V_On` 改变,会向对应功率板下发 `Eload_Start_V_ON`。
|
||||
- 下发值保持既有下限:小于或等于 `500mV` 时按 `500mV` 发送。
|
||||
- 南向队列提交失败时,本次参数生效返回忙,协议板运行参数保持原 `V_On`。
|
||||
|
||||
### 验证记录
|
||||
|
||||
- 待硬件验证:运行中修改 `SINK_REG_VON_VOLT (0x0B)` 并执行参数生效,确认功率板收到 `Eload_Start_V_ON` 新值。
|
||||
|
||||
## V1.1.9 - 2026-08-25
|
||||
|
||||
### Source 复位后的初始化流程
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# 协议板 Boot:UPK 打包规则
|
||||
|
||||
## 1. 适用范围
|
||||
|
||||
本文用于实现协议板 App 固件的 `.upk` 打包和解析工具,适配 `apps/boot` 当前 Boot。传输层 YMODEM 规则见[协议板Boot_YMODEM上位机实现规则.md](协议板Boot_YMODEM上位机实现规则.md)。
|
||||
|
||||
## 2. 1K 文件布局
|
||||
|
||||
当前 Boot 收到第一个文件数据帧时,只取其中前 96 字节作为 UPK 头,**会忽略该帧其余数据**。因此打包文件必须使用固定 1024 字节首数据块:
|
||||
|
||||
```text
|
||||
输出 .upk 文件
|
||||
偏移 0x000 ~ 0x05F : 96 字节 UPK 逻辑头
|
||||
偏移 0x060 ~ 0x3FF : 928 字节 0x00 填充
|
||||
偏移 0x400 起 : package body(未加密 BIN 或 AES 加密后的 body)
|
||||
```
|
||||
|
||||
- `.upk` 实际文件大小:`1024 + pkg_size`。
|
||||
- YMODEM 元数据包的文件大小必须为上述实际文件大小。
|
||||
- 上位机必须从偏移 0 开始以 `STX/1024` 发送;第一个 STX 是头区,真正 body 从第二个 STX 开始。
|
||||
- YMODEM 最后一帧的 `0x1A` 补齐仅属于传输层,不能计入文件大小、`raw_size` 或 `pkg_size`。
|
||||
|
||||
### YMODEM 1K 帧字节数
|
||||
|
||||
`STX/1024` 中的 `1024` 指数据区长度,不是串口线上完整帧长度。每个完整 STX 数据帧固定为 **1029 字节**:
|
||||
|
||||
```text
|
||||
STX 1 字节
|
||||
PacketNo 1 字节
|
||||
~PacketNo 1 字节
|
||||
Data 1024 字节
|
||||
CRC16 2 字节
|
||||
总计 1029 字节
|
||||
```
|
||||
|
||||
因此,UPK 首数据块和后续固件数据块的有效数据均为 1024 字节,实际串口发送时均应连续发送 1029 字节。YMODEM 元数据使用 SOH 帧,完整帧为 133 字节(128 字节数据区 + 5 字节控制/校验字段)。
|
||||
|
||||
界面中的“表头尺寸 `1024 (ymodem1k)`”应改名为“YMODEM 首数据块/头区尺寸”,并固定为 1024;UPK 逻辑头固定为 96,不能让用户修改。
|
||||
|
||||
## 3. 输入、分区与版本
|
||||
|
||||
| 项目 | 规则 |
|
||||
| --- | --- |
|
||||
| 输入文件 | 仅协议板 App 裸 `bin`,例如 `apps/chrg/build/chrg.bin`;拒绝 `.hex`、`.elf`、已有 `.upk`。 |
|
||||
| `name[4]` | 固定 ASCII `upk\0`。 |
|
||||
| `part_name[16]` | 仅允许 `app`、`download`、`factory`,ASCII NUL 结尾;协议板常规升级默认 `app`,Boot 会修正为 `download` 后安装到 `app`。 |
|
||||
| `fw_old_ver[16]` | 目标设备当前版本,前 4 字节为数值 `major, minor, fix, build`,其余 `0x00`。 |
|
||||
| `fw_new_ver[16]` | 新固件版本,编码同上。 |
|
||||
| 版本输入 | 界面应分别提供“当前/旧版本”与“新版本”;不能只保留一个版本号,且不得写 ASCII 版本字符串。 |
|
||||
| `user_string[16]` | ASCII 水印最多 15 字节,NUL 结尾,其余 `0x00`。 |
|
||||
| `timestamp` | 当前 Unix 时间戳,`uint32` 小端。 |
|
||||
| 大小限制 | `raw_size <= 262144` 且 `pkg_size <= 262144`,即 App/download 分区各 256KB。 |
|
||||
|
||||
## 4. 加密、大小与 CRC
|
||||
|
||||
1. `raw_body` 为原始 `.bin`:`raw_size = len(raw_body)`,`raw_crc = crc32_boot(raw_body)`。
|
||||
2. 未加密:`package_body = raw_body`,`pkg_size = raw_size`,`pkg_crc = raw_crc`,`config[1]=0x00`。
|
||||
3. AES:`config[1]=0x01`,使用 AES-256-CBC;Key=`0123456789ABCDEF0123456789ABCDEF`,IV=`0123456789ABCDEF`。加密前 body 必须补齐到 16 字节整数倍;`pkg_size`、`pkg_crc`对应加密后的 body。Key、IV 必须与 Boot 编译配置一致。
|
||||
4. `config[0]`、`config[2]`、`config[3]` 固定 `0x00`。
|
||||
5. `head_crc = crc32_boot(逻辑头前 92 字节)`;最后 4 字节按小端写入 `head_crc`。
|
||||
6. 所有 `uint32` 字段按 STM32 小端序写入。
|
||||
|
||||
`crc32_boot()` 必须与 `apps/boot/application/func/utils.c` 的 `crc32_step(0xFFFFFFFF, data, size)` 完全一致;不能直接使用未校验的库默认 CRC32。
|
||||
|
||||
## 5. 96 字节逻辑头字段
|
||||
|
||||
```text
|
||||
0x00 name[4]
|
||||
0x04 config[4]
|
||||
0x08 fw_old_ver[16]
|
||||
0x18 fw_new_ver[16]
|
||||
0x28 user_string[16]
|
||||
0x38 part_name[16]
|
||||
0x48 raw_size uint32 little-endian
|
||||
0x4C pkg_size uint32 little-endian
|
||||
0x50 timestamp uint32 little-endian
|
||||
0x54 raw_crc uint32 little-endian
|
||||
0x58 pkg_crc uint32 little-endian
|
||||
0x5C head_crc uint32 little-endian
|
||||
```
|
||||
|
||||
## 6. 打包完成前的强制自检
|
||||
|
||||
1. 重新解析刚生成的 `.upk`。
|
||||
2. 校验总长度是否为 `1024 + pkg_size`,并检查 `0x60~0x3FF` 全部为 `0x00`。
|
||||
3. 校验 `name`、分区名、版本字段、大小限制和 `head_crc`。
|
||||
4. 校验 `pkg_crc`;未加密时还必须校验 `raw_crc`。加密时使用相同 Key/IV 解密后校验 `raw_crc`。
|
||||
5. 解析页显示 `raw_size`、`pkg_size`、全部 CRC、加密状态、分区、旧/新版本和时间戳;任一项异常时禁止输出成功文件。
|
||||
|
||||
## 7. 当前样本
|
||||
|
||||
工程中的 `apps/chrg/chrg.upk` 已验证:实际长度 `155816`,`pkg_size=154792`,两者相差 1024,符合本规则。
|
||||
|
||||
## 8. 代码依据
|
||||
|
||||
- `apps/boot/application/include/firmware.h`
|
||||
- `apps/boot/application/func/firmware.c`
|
||||
- `apps/boot/application/func/app.c`
|
||||
- `apps/boot/application/func/utils.c`
|
||||
@@ -0,0 +1,213 @@
|
||||
# 协议板 Boot:YMODEM 上位机实现规则
|
||||
|
||||
## 1. 适用范围
|
||||
|
||||
本文适用于 **PC 上位机通过 USART1 直连协议板 Boot**,传输协议为 YMODEM + CRC16。
|
||||
|
||||
不适用于“协议板 App 通过南向 Modbus `0x20` 转发功率板 Boot”的链路;该链路的报文封装、应答和状态机需要独立实现。
|
||||
|
||||
## 2. 基本要求
|
||||
|
||||
| 项目 | 规则 |
|
||||
| --- | --- |
|
||||
| 串口 | USART1,115200 bps,8N1,无校验、无流控 |
|
||||
| 升级文件 | 必须发送已由现有打包工具生成的完整 `.upk` 文件 |
|
||||
| 不支持的文件 | 裸 `.bin`,以及自行封装/加密但不符合工程 UPK 格式的文件 |
|
||||
| 启动依据 | 等待 Boot 发出 ASCII `C`(`0x43`)后再开始传输 |
|
||||
| Boot 上电等待 | 首次等待主机开始约 5 秒;超时后可能跳转 App |
|
||||
| 传输中的帧间等待 | Boot 约 10 秒;上位机不得使相邻有效帧间隔达到 10 秒 |
|
||||
| 单帧发送方式 | 一个完整 YMODEM 帧必须连续写入串口;不得把同一帧拆开并在中间产生明显空闲间隔 |
|
||||
|
||||
Boot 每约 1 秒发送一次 `C`。若没有收到 `C`,上位机不可直接发送 YMODEM 首帧;应提示设备未进入 Boot,或重新复位/触发其进入 Boot。Boot 的接收层按 UART 收帧处理,不会为上位机重组被空闲间隔拆断的 YMODEM 包。
|
||||
|
||||
## 3. YMODEM 常量与帧格式
|
||||
|
||||
| 名称 | 值 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| SOH | `0x01` | 128 字节数据包 |
|
||||
| STX | `0x02` | 1024 字节数据包 |
|
||||
| EOT | `0x04` | 数据结束 |
|
||||
| ACK | `0x06` | 接收成功 |
|
||||
| NAK | `0x15` | 接收失败/请求重发 |
|
||||
| CAN | `0x18` | 取消传输 |
|
||||
| C | `0x43` | 请求以 CRC16 模式发送 |
|
||||
|
||||
数据包格式:
|
||||
|
||||
```text
|
||||
Header + PacketNo + (~PacketNo) + Data + CRC16_H + CRC16_L
|
||||
```
|
||||
|
||||
| 包类型 | Data 长度 | 总长度 |
|
||||
| --- | ---: | ---: |
|
||||
| SOH | 128 | 133 |
|
||||
| STX | 1024 | 1029 |
|
||||
|
||||
CRC 算法必须为 **CRC-16/XMODEM**:
|
||||
|
||||
```text
|
||||
poly=0x1021, init=0x0000, RefIn=false, RefOut=false, XorOut=0x0000
|
||||
```
|
||||
|
||||
CRC 覆盖 `Data` 的全部固定长度字节(128 或 1024),并按 **高字节、低字节** 放在帧尾。
|
||||
|
||||
## 4. 包序号与数据填充
|
||||
|
||||
1. 元数据包必须使用 `SOH`,`PacketNo=0x00`,反码为 `0xFF`。
|
||||
2. 首个文件数据包使用 `PacketNo=0x01`;每得到 ACK 后序号加 1,按 8 位自然回绕。
|
||||
3. 本 Boot 的 `.upk` 使用 1K 首数据块布局,因此文件数据包必须使用 `STX`(1024 字节);最后一包不足 1024 字节时仍应补齐到 1024 字节,建议补 `0x1A`。
|
||||
4. Boot 校验包号、包号反码、帧长度与 CRC。任一校验失败会立即回复 NAK;当前 Boot 随后会重置 YMODEM 包号和流程。
|
||||
5. 对于业务层处理失败产生的 NAK,可以重发**原完整帧**,包号不得递增。对于 CRC、包号或帧长度错误产生的 NAK,不能只重发当前帧,必须重新进入 Boot 并从元数据包开始完整传输。
|
||||
|
||||
## 5. 元数据包规则
|
||||
|
||||
第一个包固定是包号 0 的 SOH 包,128 字节 Data 的内容为:
|
||||
|
||||
```text
|
||||
ASCII 文件名 + 0x00 + ASCII 十进制文件大小 + 0x00 + 其余补 0x00
|
||||
```
|
||||
|
||||
示例(文件名仅用于协议描述,实际可使用真实 `.upk` 文件名):
|
||||
|
||||
```text
|
||||
protocol_v1.2.upk\0 123456\0 [其余填 0]
|
||||
```
|
||||
|
||||
文件大小必须是待传输 `.upk` 的实际字节数。传输结束包也是 SOH/包号 0,但其 128 字节 Data 必须全部为 `0x00`。
|
||||
|
||||
## 6. `.upk` 文件要求
|
||||
|
||||
Boot 在接收数据后按 UPK 格式处理,而不是把它当作裸固件。
|
||||
|
||||
- 头部标识必须为 `UPK_IDENTIFIER=0x006B7075`(`"upk"`)。
|
||||
- UPK 逻辑头为 1 字节对齐、总计 96 字节,包含版本、目标分区、原始/包体大小、CRC 等字段。
|
||||
- 工程启用了 AES-256-CBC 解密组件,但不强制加密;`config[1]=0` 的未加密包和 `config[1]=1` 的 AES-256-CBC 包均可被当前 Boot 处理。
|
||||
- Boot 根据 UPK 头确定下载与安装流程;当前配置会先写 download 分区,再更新 app 分区。
|
||||
|
||||
打包输出的 `.upk` 必须符合[协议板Boot_UPK打包规则.md](协议板Boot_UPK打包规则.md),特别是 1024 字节首数据块布局。YMODEM 元数据中的文件大小使用该 `.upk` 的实际字节数。
|
||||
|
||||
## 7. 完整正常时序
|
||||
|
||||
```text
|
||||
Boot -> PC : C (每秒一次,等待开始)
|
||||
PC -> Boot: SOH, 00, FF, 元数据, CRC
|
||||
Boot -> PC : ACK
|
||||
Boot -> PC : C (必须等待此 C)
|
||||
|
||||
PC -> Boot: STX, 01, FE, 数据块, CRC
|
||||
Boot -> PC : ACK
|
||||
PC -> Boot: STX, 02, FD, 数据块, CRC
|
||||
Boot -> PC : ACK
|
||||
... (直至全部 .upk 数据发送完成)
|
||||
|
||||
PC -> Boot: EOT
|
||||
Boot -> PC : NAK
|
||||
PC -> Boot: EOT
|
||||
Boot -> PC : ACK
|
||||
Boot -> PC : C
|
||||
PC -> Boot: SOH, 00, FF, 128 字节全 0, CRC
|
||||
Boot -> PC : ACK
|
||||
|
||||
Boot : 校验 UPK、写 Flash、安装 App、校验 App,并可能跳转 App
|
||||
```
|
||||
|
||||
关键点:
|
||||
|
||||
- 元数据 ACK 后,必须等 Boot 的第二个 `C` 才能发送数据包。
|
||||
- 第一个 EOT 后,必须等 NAK;第二个 EOT 后,必须等 ACK。
|
||||
- 第二个 EOT 的 ACK 后,必须等 `C`,再发送结束空包。
|
||||
- Boot 处理擦除和 Flash 写入期间,ACK 可能延迟;PC 的单帧 ACK 等待建议设为 3~10 秒。
|
||||
|
||||
## 8. 上位机状态机(可直接实现)
|
||||
|
||||
```text
|
||||
WAIT_C_START
|
||||
收到 C -> SEND_META
|
||||
|
||||
SEND_META
|
||||
发送包号 0 元数据 -> WAIT_META_ACK
|
||||
|
||||
WAIT_META_ACK
|
||||
ACK -> WAIT_C_DATA
|
||||
NAK/超时 -> 重发元数据
|
||||
|
||||
WAIT_C_DATA
|
||||
C -> SEND_DATA(包号=1)
|
||||
|
||||
SEND_DATA
|
||||
发送当前数据包 -> WAIT_DATA_ACK
|
||||
|
||||
WAIT_DATA_ACK
|
||||
ACK -> 若还有数据:包号+1,SEND_DATA;否则 SEND_EOT_1
|
||||
NAK/超时 -> 重发当前数据包
|
||||
|
||||
SEND_EOT_1
|
||||
发送 EOT -> WAIT_EOT_NAK
|
||||
|
||||
WAIT_EOT_NAK
|
||||
NAK -> SEND_EOT_2
|
||||
超时 -> 重发 EOT(保持本状态)
|
||||
|
||||
SEND_EOT_2
|
||||
发送 EOT -> WAIT_EOT_ACK
|
||||
|
||||
WAIT_EOT_ACK
|
||||
ACK -> WAIT_C_END
|
||||
NAK/超时 -> 重发 EOT
|
||||
|
||||
WAIT_C_END
|
||||
C -> SEND_END_PACKET
|
||||
|
||||
SEND_END_PACKET
|
||||
发送包号 0 的全 0 SOH 包 -> WAIT_END_ACK
|
||||
|
||||
WAIT_END_ACK
|
||||
ACK -> TRANSFER_FINISHED
|
||||
NAK/超时 -> 重发结束空包
|
||||
```
|
||||
|
||||
## 9. 超时、重发、取消与结果判定
|
||||
|
||||
| 事件 | 上位机处理 |
|
||||
| --- | --- |
|
||||
| 收到 ACK | 仅推进到下一协议状态;数据包 ACK 后才递增包号 |
|
||||
| 收到明确 NAK | 业务层 NAK 可重发当前完整帧;协议校验 NAK 应重新进入 Boot,从元数据包开始传输 |
|
||||
| 等待 ACK 超时 | 标记“传输结果不确定”,停止本次会话;重新进入 Boot 后从元数据包开始完整传输 |
|
||||
| 明确 NAK 重发耗尽 | 发送 CAN 两次,标记升级传输失败 |
|
||||
| 收到 CAN | 立即停止,标记 Boot 取消升级 |
|
||||
| 用户取消 | 发送 CAN 两次,停止发送后续字节 |
|
||||
| 无 C | 不开始发送,提示设备未进入 Boot 或串口参数/连线错误 |
|
||||
|
||||
Boot 对单个 CAN 会进入取消流程并回 ACK;上位机仍建议按标准发送两个 CAN,并接受可能出现的 ACK。
|
||||
|
||||
> **当前 Boot 的可靠性限制:** 成功接收一个数据包后,Boot 已把期望包号加一;若该包的 ACK 在串口上丢失,上位机重发同一包会被 Boot 判为“重复包”并回复 NAK,同时触发 YMODEM 重置。因此“ACK 超时后无条件重发当前包”不能可靠恢复,必须重新进入 Boot 并从元数据包开始传输。若要支持当前会话内的可靠自动重发,需要修改 Boot,使其保留期望包号,并对 CRC 正确的重复帧直接回复 ACK、不再次写入数据。
|
||||
|
||||
`WAIT_*_ACK` 的建议超时为 3~10 秒,元数据后的 `WAIT_C_DATA`、结束阶段的 `WAIT_C_END` 建议为 10 秒。上位机可将串口接收设计为持续缓存并按字节解析,忽略调试文本或仅识别 `C/ACK/NAK/CAN` 控制字符,避免因分段到达造成误判。
|
||||
|
||||
## 10. 成功判定与日志
|
||||
|
||||
结束空包得到 ACK 仅表示 **YMODEM 文件传输完成**,不是最终“固件安装成功”的结构化回码。随后 Boot 仍需校验 `.upk`、写下载区、安装 App、校验 App。
|
||||
|
||||
上位机应分两级显示结果:
|
||||
|
||||
1. `传输完成`:收到结束空包 ACK。
|
||||
2. `升级确认成功`:设备重启并进入 App 后,通过应用通信读取新固件版本(推荐)或观察 Boot/App 启动日志确认。
|
||||
|
||||
建议记录:开始时间、文件名/大小、每个包号、重试次数、控制字符、异常原因、传输完成时间和最终版本读取结果。
|
||||
|
||||
## 11. 与功率板 Boot 转发升级的边界
|
||||
|
||||
若上位机目标是功率板,而物理串口只连接协议板 App,则不能直接把本 YMODEM 原始字节流写到串口。需要按协议板 App 已实现/约定的 Modbus 扩展功能码 `0x20` 封装,再由 App 南向转发给功率板 Boot,并处理 App 的转发 ACK、`C`、NAK、超时和重发。
|
||||
|
||||
本文件可作为功率板转发方案中“YMODEM 内层状态机”的依据;外层 Modbus `0x20` 帧格式和升级进入/退出控制需以 `apps/chrg/applications/thread/chrg_comm.c`、`chrg_comm.h` 的实际实现为准,不能与协议板 Boot 直连流程混用。
|
||||
|
||||
## 12. 代码依据
|
||||
|
||||
- `apps/boot/application/main.c`:USART1/USART2 初始化及 Boot 启动。
|
||||
- `apps/boot/application/func/transfer.c`:USART1 接收传输层。
|
||||
- `apps/boot/application/include/ymodem.h`:YMODEM 常量、包定义。
|
||||
- `apps/boot/application/func/ymodem.c`:YMODEM 状态机、C/ACK/NAK/EOT 时序和超时。
|
||||
- `apps/boot/application/func/app.c`:UPK 接收、校验、写入和安装流程。
|
||||
- `apps/boot/application/include/firmware.h`:UPK 头和分区相关定义。
|
||||
- `apps/boot/application/func/utils.c`:CRC-16/XMODEM 实现。
|
||||
- `apps/boot/application/include/app_config.h`:超时、分区和 AES 配置。
|
||||
Reference in New Issue
Block a user