一:增加了纹波滤波,当纹波板有电阻时打开辅助继电器应该为3帧过冲,使用五次过滤可以消除异常纹波

二、修改了负载启动的顺序,先开启辅助继电器使地相通,再开启D+D-继电器

feat: 发布V1.1.23版本,优化负载初始化继电器时序

- 更新所有头文件版本号至V1.1.21~V1.1.23,协议板固件版本更新为0x0127
- 新增纹波滤波相关宏定义与函数,实现继电器闭合后纹波过滤
- 调整负载初始化流程:先下发辅助继电器,等待回码后延迟30ms再打开北向继电器
- 优化D+/D-寄存器上位机显示单位,适配0.001V解析格式
- 补充YMODEM升级文档说明,完善传输校验与重发规则
- 清理冗余调试打印与Allegro工程日志文件
This commit is contained in:
wrh
2026-09-14 15:13:20 +08:00
parent d231a49771
commit 05309dfb41
15 changed files with 1343 additions and 3490 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_comm.c
完成日期 :
当前版本号 : V1.1.19
当前版本号 : V1.1.23
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
版本历史 : 创建原始版本
说明 :
@@ -27,7 +27,7 @@
#include "stm32f4xx_hal.h"
#define HOLD_REG_FIRMWARE_VERSION 0x0300U /* 协议板固件版本,只读 */
#define CHRG_FIRMWARE_VERSION 0x0123U /* 当前协议板版本:V1.1.19 */
#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.19编码为0x0123,Modbus响应按高字节在前返回01 23。
* 当前版本V1.1.23编码为0x0127,Modbus响应按高字节在前返回01 27。
* 该特殊地址只支持一次读取一个寄存器,避免与普通连续寄存器混用。
*/
if (regAddress == HOLD_REG_FIRMWARE_VERSION) {
+1 -1
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_comm.h
完成日期 :
当前版本号 : V1.1.19
当前版本号 : V1.1.21
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
版本历史 : 创建原始版本
说明 :
@@ -233,6 +233,8 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
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);
+7 -3
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_north.h
完成日期 :
当前版本号 : V1.1.19
当前版本号 : V1.1.21
主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
版本历史 : 创建原始版本
说明 :
@@ -28,12 +28,16 @@
#endif
/* 南向COM批次中单通道连续收到有效回码后的最大发送条数。 */
#define SOU_COM_BURST_MAX 8U
#define SOU_COM_BURST_MAX 6U
/* 自动停止带载后,电压恢复时需要连续确认的新P1采样帧数。 */
#define SINK_LOAD_RESTART_CONFIRM_COUNT 3U
#define THR_NAME_NORTH "thr.nor"
/* 主继电器闭合后的纹波瞬态过滤参数,单位为新的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, 命令]
+17 -1
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_roll_sou.c
完成日期 :
当前版本号 : V1.1.14
当前版本号 : V1.1.22
主要功能 : 实现南向通信轮巡请求命令。以独立线程处理。
版本历史 : 创建原始版本
说明 :
@@ -619,6 +619,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) &&
@@ -983,6 +990,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
主要功能 : 实现南向通信轮巡请求命令。以独立线程处理。
版本历史 : 创建原始版本
说明 :
+115 -7
View File
@@ -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=无应答)
@@ -360,7 +463,8 @@ 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));
/* 保留功率板返回的真实电流,供卸载保护判断使用。 */
pSW->Now_current = pLOAD->current;
/*
@@ -374,6 +478,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;
}
@@ -415,8 +520,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;
/* 上位机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);
// rt_kprintf("---------NOW VOLT END = %d\n----------",pSW->Now_voltage);
return 0;
}
+4 -1
View File
@@ -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);
+99 -32
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_sink.c
完成日期 :
当前版本号 : V1.1.19
当前版本号 : V1.1.21
主要功能 : 北向数据sink请求数据填充及发送。
版本历史 : 创建原始版本
说明 :
@@ -24,6 +24,15 @@
#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};
/* 主继电器闭合跟踪:1=已提交ALL_REL_ON(主继电器应已闭合) 0=未闭合/已断开。
* 用于0V硬保护区分"首次开载/握手恢复"(主继电器未闭合,Now_voltage=0属正常)
@@ -241,31 +250,12 @@ 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;
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] = FU_REL_ON;
regs[2] = REG_CURRENT_OUT + 1; vals[2] = 0;
@@ -289,18 +279,70 @@ 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;
rt_bool_t short_exit;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
total = chrg_sink_init_build_commands(pSW, regs, vals);
short_exit = (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) ?
RT_TRUE : RT_FALSE;
switch (g_sink_init_stage[idx]) {
case SINK_INIT_STAGE_CLEAR_RELAY:
start = 0U;
count = 1U;
break;
case SINK_INIT_STAGE_AUX_RELAY:
start = short_exit ? 1U : 0U;
count = 1U;
break;
case SINK_INIT_STAGE_REST:
start = short_exit ? 2U : 1U;
count = (rt_uint8_t)(total - start);
break;
default:
return -1;
}
return chrg_sou_com_batch_submit(idx, &regs[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)
{
rt_bool_t short_exit;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
short_exit = (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) ?
RT_TRUE : RT_FALSE;
g_sink_init_stage[idx] = short_exit ? SINK_INIT_STAGE_CLEAR_RELAY :
SINK_INIT_STAGE_AUX_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;
}
@@ -316,6 +358,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;
@@ -325,10 +368,34 @@ 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);
rt_kprintf("-------D+D- OPEN------\n");
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或电压查询,此处只更新南向完成状态。 */
+1 -1
View File
@@ -1,7 +1,7 @@
/****************************************************************************
文件名称 : chrg_sink.c
完成日期 :
当前版本号 : V1.1.19
当前版本号 : V1.1.23
主要功能 : 北向数据sink请求数据填充及发送,命令字定义。
版本历史 : 创建原始版本
说明 :