重构充电协议模块,统一源载参数管理与流程

1.  apps/chrg/applications/utils/chrg_def.h: 移除vc_pd_t结构体中mm数组下标,改为单实例
2.  apps/chrg/applications/utils/chrg_north_pkg.c: 修正CRC校验调用,调整pd_get参数访问方式
3.  apps/chrg/applications/utils/chrg_source.h: 重构src_vc_t和chrg_src_t结构体,移除冗余字段,新增set_volt/set_curr和pd_set单实例
4.  apps/chrg/applications/thread/chrg_roll_nor.c: 修正CC选择变量名,新增PD数据获取流程
5.  apps/chrg/applications/thread/chrg_lcd.c: 重构LCD解析逻辑,区分源载电压处理,新增工作状态触发
6.  apps/chrg/applications/thread/chrg_north.c: 新增协议电压规则修正函数,重构源参数控制逻辑
7.  apps/chrg/applications/thread/chrg_comm.c: 新增pd_changed参数差异标志,重构源参数读写与差异比较逻辑
8.  apps/chrg/applications/utils/chrg_source.c: 重构源工作状态机,新增电压补偿逻辑,重构PD协议填充函数
9.  docs\2026-07-29-self-loop-aging-test-plan.md: 新增四通道自举老化测试计划文档
This commit is contained in:
wrh
2026-07-29 11:27:29 +08:00
parent 4beeb02382
commit 79c63f7b8e
10 changed files with 558 additions and 142 deletions
+96 -41
View File
@@ -57,7 +57,7 @@ static rt_uint8_t g_shadow_dirty[TOTAL_NOR_CHS] = {0}; /* 0=已生效 1=待生
/*
* Source和Sink共用的参数差异结果。前五项是两个角色都具备的控制属性,
* 后三项只用于Sink快充协议配置;Source比较时保持为RT_FALSE。
* pd_changed用于Source完整协议参数比较,后三项用于Sink快充协议配置。
*/
typedef struct
{
@@ -66,6 +66,7 @@ typedef struct
rt_bool_t cv_changed;
rt_bool_t voltage_changed;
rt_bool_t current_changed;
rt_bool_t pd_changed;
rt_bool_t group_changed;
rt_bool_t pro_voltage_changed;
rt_bool_t pro_current_changed;
@@ -872,22 +873,24 @@ static rt_uint16_t src_proto_lookup(rt_uint16_t reg_val)
*****************************************************************/
static rt_uint16_t src_type_lookup(rt_uint16_t reg_val,struct chrg_switch_t *pSW)
{
struct vc_pd_t *pPD = &pSW->source.pd_set;
switch(reg_val){
case 0:
pSW->source.src = 0;
pSW->source.pps = 0;
pPD->src_type = TYPE_SRC_PDO;
pPD->pps_type = TYPE_PPS_SPRPPS;
break;
case 1:
pSW->source.src = 0;
pSW->source.pps = 1;
pPD->src_type = TYPE_SRC_PDO;
pPD->pps_type = TYPE_PPS_EPR;
break;
case 2:
pSW->source.src = 3;
pSW->source.pps = 0;
pPD->src_type = TYPE_SRC_APDO;
pPD->pps_type = TYPE_PPS_SPRPPS;
break;
case 3:
pSW->source.src = 3;
pSW->source.pps = 1;
pPD->src_type = TYPE_SRC_APDO;
pPD->pps_type = TYPE_PPS_EPR;
break;
default:
return 1;
@@ -1127,13 +1130,13 @@ eMBException funcWriteMultipleHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t
switch (sout_reg)
{
case REG_VOLTAGE_OUT + 1:
pSW->source.vc.set_voltage = sou_value;
pSW->source.set_volt = sou_value;
pSW->sink.loadv = sou_value;
chrg_set_sou_reg(ch, sout_reg, sou_value);
rt_kprintf("voltage=%d", sou_value);
break;
case REG_CURRENT_OUT + 1:
pSW->source.vc.set_current = sou_value;
pSW->source.set_curr = sou_value;
pSW->sink.loadc = sou_value;
chrg_set_sou_reg(ch, sout_reg, sou_value);
break;
@@ -2223,21 +2226,21 @@ static void chrg_mb_write_src(rt_uint8_t ch, rt_uint16_t reg_offset, rt_uint16_t
// rt_kprintf("[SRC CH%d] Mode=%d\n", ch + 1, sou_value);
break;
case SRC_REG_MIN_VOLT: // 0x03: 最小电压 (mV)
pSW->source.min_vol = sou_value;
pSW->source.pd_set.mm.min_vol = sou_value;
//rt_kprintf("[SRC CH%d] MinVolt=%d mV\n", ch + 1, sou_value);
break;
case SRC_REG_MAX_VOLT: // 0x04: 最大电压 (mV)
pSW->source.vc.set_voltage = sou_value;
pSW->source.pd_set.mm.max_vol = sou_value;
// rt_kprintf("[SRC CH%d] MaxVolt=%d mV\n", ch + 1, sou_value);
break;
case SRC_REG_MAX_CURR: // 0x05: 最大电流 (mA)
pSW->source.vc.set_current = sou_value;
pSW->source.pd_set.mm.max_cur = sou_value;
//rt_kprintf("[SRC CH%d] MaxCurr=%d mA\n", ch + 1, sou_value);
break;
case SRC_REG_CC_SEL: // 0x06: CC选择
pSW->source.cc_set = (rt_uint8_t)sou_value;
pSW->source.CC_Set = (rt_uint8_t)sou_value;
//rt_kprintf("[SRC CH%d] CC_SEL=%d\n", ch + 1, sou_value);
break;
@@ -2258,8 +2261,8 @@ static void chrg_mb_write_src(rt_uint8_t ch, rt_uint16_t reg_offset, rt_uint16_t
输入参数: role:当前角色 pReal:正在运行的参数 pShadow:待生效参数
输出参数: pDiff:各公共属性及Sink专用协议属性的变化标志
返回说明: -
其它说明: 本函数只计算差异,不修改状态、不发送命令;Source的电压电流
使用vc.set_voltage/set_current,Sink使用loadv/loadc
其它说明: 本函数只计算差异,不修改状态、不发送命令;Source直接比较
完整pd_set,并从pd_set.mm提取标准协议电压电流差异
*****************************************************************/
static void chrg_comm_get_param_diff(eIDX_ID role,
const struct chrg_switch_t *pReal,
@@ -2276,11 +2279,16 @@ static void chrg_comm_get_param_diff(eIDX_ID role,
pDiff->protocol_changed =
(pReal->source.protocol != pShadow->source.protocol);
pDiff->cc_changed =
(pReal->source.cc_set != pShadow->source.cc_set);
(pReal->source.CC_Set != pShadow->source.CC_Set);
/* 两个数组均来自静态零初始化结构,填充前未定义字节保持为0。 */
pDiff->pd_changed = (rt_memcmp(&pReal->source.pd_set,
&pShadow->source.pd_set, sizeof(pReal->source.pd_set)) != 0);
pDiff->voltage_changed =
(pReal->source.vc.set_voltage != pShadow->source.vc.set_voltage);
(pReal->source.pd_set.mm.max_vol !=
pShadow->source.pd_set.mm.max_vol);
pDiff->current_changed =
(pReal->source.vc.set_current != pShadow->source.vc.set_current);
(pReal->source.pd_set.mm.max_cur !=
pShadow->source.pd_set.mm.max_cur);
} else if (role == ID_SINK) {
pDiff->protocol_changed =
(pReal->sink.protocol != pShadow->sink.protocol);
@@ -2373,26 +2381,50 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
输入参数: ch:通道 pReal:运行参数 pShadow:待生效影子参数
输出参数: 更新pReal、Source状态和必要的南向寄存器
返回说明: MB_EX_NONE:成功 MB_EX_SLAVE_BUSY:命令提交失败
其它说明: 快充协议只重置北向sub并进入WAIT;标准协议直接更新CV、电压和电流
其它说明: 只复制Source配置字段,避免影子结构覆盖北向实时set_volt/set_curr;
快充参数变化进入WAIT,标准协议电压统一交给RUNING状态下发
*****************************************************************/
static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
struct chrg_switch_t *pReal, const struct chrg_switch_t *pShadow)
{
chrg_param_diff_t diff_info;
rt_bool_t north_config_changed;
struct vc_pd_t old_pd_set;
eSRC_PRO old_protocol = pReal->source.protocol;
rt_uint8_t old_cc_set = pReal->source.CC_Set;
rt_uint8_t old_now_state = pReal->source.Now_State;
rt_uint8_t old_work_mode = pReal->source.work_mode;
rt_uint8_t old_cv_mode = pReal->CV_mode;
rt_uint16_t old_voltage = pReal->source.vc.set_voltage;
rt_uint16_t old_current = pReal->source.vc.set_current;
rt_uint8_t old_proto_cmd = pReal->protoCmd;
rt_uint8_t old_sub = pReal->sub;
rt_uint16_t old_set_volt = pReal->source.set_volt;
rt_uint16_t old_set_curr = pReal->source.set_curr;
rt_uint16_t source_work = pShadow->source.Now_State;
rt_uint16_t regs[3];
rt_uint16_t vals[3];
rt_uint16_t regs[2];
rt_uint16_t vals[2];
rt_uint16_t clear_reg;
rt_uint16_t clear_val;
rt_uint16_t target_curr;
rt_uint8_t count = 0;
chrg_comm_get_param_diff(ID_SOURCE, pReal, pShadow, &diff_info);
rt_memcpy(pReal, pShadow, sizeof(struct chrg_switch_t));
rt_memcpy(&old_pd_set, &pReal->source.pd_set, sizeof(old_pd_set));
north_config_changed = diff_info.protocol_changed ||
diff_info.cc_changed || diff_info.pd_changed;
target_curr = pShadow->source.pd_set.mm.max_cur;
/* 影子区只拥有COM配置;北向解析产生的运行目标和回读数据继续由pReal持有。 */
pReal->CV_mode = pShadow->CV_mode;
pReal->protoCmd = pShadow->protoCmd;
pReal->source.protocol = pShadow->source.protocol;
pReal->source.CC_Set = pShadow->source.CC_Set;
pReal->source.Now_State = pShadow->source.Now_State;
rt_memcpy(&pReal->source.pd_set, &pShadow->source.pd_set,
sizeof(pReal->source.pd_set));
if (source_work == 0x01) {
if (pReal->source.On_Flag == 0) {
/* 首次启动仍需初始化功率板、继电器和WORK_START。 */
/* 新命令要求运行时直接置位,不依赖旧工作状态。 */
pReal->sub = IDX_SET_SOURCE_CC_SET;
pReal->source.On_Flag = 1;
pReal->source.work_mode = SOURCE_WORK_INIT;
@@ -2400,40 +2432,48 @@ static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
MB_EX_NONE : MB_EX_SLAVE_BUSY;
}
/* 协议或CC变化时,从CC设置开始重新执行北向配置链路。 */
if (diff_info.protocol_changed || diff_info.cc_changed) {
/* 协议、CC或任一pd_set字段变化时,重新执行北向配置链路。 */
if (north_config_changed) {
pReal->sub = IDX_SET_SOURCE_CC_SET;
}
pReal->source.work_mode = SOURCE_WORK_WAIT;
if (diff_info.protocol_changed || diff_info.cc_changed) {
/* 协议或CC切换前先清零南向输出,帮助功率板退出旧协议状态,
* 后续北向轮询再按CC、协议、VC顺序完成新协议握手。 */
clear_reg = REG_VOLTAGE_OUT + 1;
clear_val = 0;
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch,
&clear_reg, &clear_val, 1) != 0) {
goto source_effect_submit_failed;
}
pReal->source.set_volt = 0;
}
if (pReal->source.protocol != PRO_NONE) {
/* 快充目标值由北向协商回码决定,COM生效阶段不直接写功率板。 */
/* 快充只更新协议配置并等待协商回码,不直接改功率板输出。 */
return MB_EX_NONE;
}
/* 从快充切到标准协议时强制同步全部直接控制参数。 */
/* 标准协议的模式和电流可直接提交,电压只能由RUNING状态写入。 */
if (diff_info.protocol_changed || diff_info.cv_changed) {
regs[count] = REG_MODE;
vals[count++] = pReal->CV_mode;
}
if (diff_info.protocol_changed || diff_info.voltage_changed) {
regs[count] = REG_VOLTAGE_OUT + 1;
vals[count++] = pReal->source.vc.set_voltage;
}
if (diff_info.protocol_changed || diff_info.current_changed) {
regs[count] = REG_CURRENT_OUT + 1;
vals[count++] = pReal->source.vc.set_current;
vals[count++] = target_curr;
}
if ((count > 0) && (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch,
regs, vals, count) != 0)) {
pReal->CV_mode = old_cv_mode;
pReal->source.vc.set_voltage = old_voltage;
pReal->source.vc.set_current = old_current;
return MB_EX_SLAVE_BUSY;
goto source_effect_submit_failed;
}
pReal->source.work_mode = SOURCE_WORK_RUNING;
/* COM只更新标准协议配置并保持WAIT;下一帧北向解析负责比较
* pd_set目标与set_volt,再决定是否进入RUNING写电压。 */
pReal->source.set_curr = target_curr;
pReal->source.work_mode = SOURCE_WORK_WAIT;
return MB_EX_NONE;
}
@@ -2441,6 +2481,21 @@ static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
pReal->source.work_mode = SOURCE_WORK_STOP;
return (chrg_source_work_state(ch, pReal) == 0) ?
MB_EX_NONE : MB_EX_SLAVE_BUSY;
source_effect_submit_failed:
/* 南向命令提交失败必须恢复差异基准,保证下一次PARAM_EFFECT仍会重发。 */
pReal->CV_mode = old_cv_mode;
pReal->protoCmd = old_proto_cmd;
pReal->sub = old_sub;
pReal->source.protocol = old_protocol;
pReal->source.CC_Set = old_cc_set;
pReal->source.Now_State = old_now_state;
pReal->source.work_mode = old_work_mode;
pReal->source.set_volt = old_set_volt;
pReal->source.set_curr = old_set_curr;
rt_memcpy(&pReal->source.pd_set, &old_pd_set,
sizeof(pReal->source.pd_set));
return MB_EX_SLAVE_BUSY;
}
/*****************************************************************
+8 -4
View File
@@ -212,13 +212,16 @@ static int chrg_lcd_parse (rt_uint8_t *recv, rt_uint16_t len)
Recv_Data = recv[LCD_IDX_VAL+1]<<8 | recv[LCD_IDX_VAL]; // 拼接16位电压值
pSW = &pNOR->sw[ch];
// 按角色存入source/sink
pSW->source.vc.set_voltage = Recv_Data; // 电源侧电压(mV)
pSW->source.pd_set.mm.max_vol = Recv_Data; // 电源协议目标电压(mV)
pSW->source.set_volt = Recv_Data; // 当前准备写入功率板的电压目标
pSW->sink.loadv = Recv_Data; // 负载侧电压(mV,)
// 写入电压寄存器
/* Sink保留原直接设置方式;Source电压必须统一经过RUNING状态补偿后下发。 */
if (pSW->id == ID_SINK) {
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_VOLTAGE_OUT + 1, Recv_Data);
} else if (pSW->source.On_Flag == 1) {
pSW->source.work_mode = SOURCE_WORK_RUNING;
(void)chrg_source_work_state(ch, pSW);
}
else chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_VOLTAGE_OUT+1, 5000);
break;
}
@@ -231,7 +234,8 @@ static int chrg_lcd_parse (rt_uint8_t *recv, rt_uint16_t len)
Recv_Data = recv[LCD_IDX_VAL+1]<<8 | recv[LCD_IDX_VAL]; // 拼接16位电流值
pSW = &pNOR->sw[ch];
//按角色存入source/sink
pSW->source.vc.set_current = Recv_Data; // 电源侧电流(mA)
pSW->source.pd_set.mm.max_cur = Recv_Data; // 电源协议最大电流(mA)
pSW->source.set_curr = Recv_Data; // 当前准备写入功率板的电流目标
pSW->sink.loadc = Recv_Data; // 负载侧电流(mA)
// rt_kprintf("cur=%d",Recv_Data);
// 写入电流寄存器
+100 -13
View File
@@ -26,6 +26,94 @@
#define SOURCE_VOLT_ERROR_MV 200U
#define SOURCE_VOLT_RETRY_FRAMES 3U
#define SOURCE_QC_MIN_VOLT_MV 5000U
/*****************************************************************
函数名称: chrg_source_apply_protocol_voltage_rule
函数描述: 根据电源协议类型修正北向解析得到的目标电压
输入参数: pSW:当前通道控制结构体 volt_mv:北向解析电压(mV)
输出参数: -
返回说明: 应写入Source状态机的目标电压(mV)
其它说明: QC2.0/QC3.0在完成升压协商前必须保持默认5V,因此其北向
回码低于5000mV时统一按5000mV处理;其它协议保持原值
*****************************************************************/
static rt_uint16_t chrg_source_apply_protocol_voltage_rule(
const struct chrg_switch_t *pSW, rt_uint16_t volt_mv)
{
if (pSW == RT_NULL) {
return volt_mv;
}
if (((pSW->source.protocol == PRO_QC20) ||
(pSW->source.protocol == PRO_QC30)) &&
(volt_mv < SOURCE_QC_MIN_VOLT_MV)) {
return SOURCE_QC_MIN_VOLT_MV;
}
return volt_mv;
}
/*****************************************************************
函数名称: chrg_source_voltage_control
函数描述: 根据目标电压与实际电压统一控制Source电压下发和重试
输入参数: idx:当前通道 pSW:通道控制结构体 volt_mv:目标电压(mV)
curr_ma:目标电流(mA)
输出参数: 更新source.set_volt/set_curr、work_mode和电压重试计数
返回说明: 0:状态处理成功 <0:参数错误或命令提交失败
其它说明: set_volt同时作为上次目标,停止时会清零;目标变化时立即下发,
目标不变但实际误差连续3帧超过200mV时重发
*****************************************************************/
static int chrg_source_voltage_control(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, rt_uint16_t volt_mv, rt_uint16_t curr_ma)
{
static rt_uint8_t volt_retry_count[TOTAL_SOU_CHS] = {0};
rt_uint16_t volt_error;
rt_uint16_t old_volt_mv;
rt_uint16_t old_curr_ma;
rt_bool_t need_set = RT_FALSE;
int ret;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
old_volt_mv = pSW->source.set_volt;
old_curr_ma = pSW->source.set_curr;
pSW->source.set_volt = volt_mv;
pSW->source.set_curr = curr_ma;
if (!pSW->source.On_Flag) {
return 0;
}
volt_error = (pSW->Now_voltage >= volt_mv) ?
(pSW->Now_voltage - volt_mv) :
(volt_mv - pSW->Now_voltage);
if (volt_mv != old_volt_mv) {
/* 新目标与set_volt旧值不同,需要立即写入功率板。 */
need_set = RT_TRUE;
} else if (volt_error <= SOURCE_VOLT_ERROR_MV) {
volt_retry_count[idx] = 0;
} else {
if (volt_retry_count[idx] < SOURCE_VOLT_RETRY_FRAMES) {
volt_retry_count[idx]++;
}
if (volt_retry_count[idx] >= SOURCE_VOLT_RETRY_FRAMES) {
need_set = RT_TRUE;
}
}
pSW->source.work_mode = need_set ?
SOURCE_WORK_RUNING : SOURCE_WORK_WAIT;
ret = chrg_source_work_state(idx, pSW);
if ((ret == 0) && need_set) {
volt_retry_count[idx] = 0;
} else if ((ret != 0) && need_set) {
/* 入队失败时恢复旧目标,使下一帧仍能立即识别为目标变化。 */
pSW->source.set_volt = old_volt_mv;
pSW->source.set_curr = old_curr_ma;
}
return ret;
}
/*****************************************************************
函数名称: chrg_source_parse
@@ -34,8 +122,8 @@
pNOR:北向总控结构体
输出参数: 更新电压、电流、source.work_mode及电压重试计数
返回说明: -
其它说明: 标准协议只在本函数中识别,不使用北向返回值控制输出;快充协议
读取frame_data[8~11]中的协商目标值,并统一交给RUNING状态比较
其它说明: 标准协议使用COM配置目标参与实际电压校验;快充协议读取
frame_data[8~11]中的协商目标值,两个分支共用误差重试逻辑
*****************************************************************/
void chrg_source_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
@@ -47,9 +135,12 @@ void chrg_source_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
return;
}
/* 标准协议仍在北向解析入口处判断,但输出已经由INIT/COM直接完成。
* 不读取北向返回电压,也不在这里重复下发南向电压。 */
/* 标准协议不使用北向帧内目标值,但仍进入统一误差判断,确保INIT或
* COM下发的目标电压没有生效时能够自动重试。 */
if (pSW->source.protocol == PRO_NONE) {
(void)chrg_source_voltage_control(idx, pSW,
pSW->source.pd_set.mm.max_vol,
pSW->source.pd_set.mm.max_cur);
return;
}
@@ -58,11 +149,9 @@ void chrg_source_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
}
volt_mv = (frame_data[8] << 8) | frame_data[9];
curr_ma = (frame_data[10] << 8) | frame_data[11];
pSW->source.vc.set_voltage = volt_mv;
pSW->source.vc.set_current = curr_ma;
/* 北向只负责提取本次返回值;新旧电压比较放在RUNING状态完成。 */
pSW->source.work_mode = SOURCE_WORK_RUNING;
(void)chrg_source_work_state(idx, pSW);
// /* 特殊协议规则只修正运行目标,不覆盖北向原始回码和COM协议配置。 */
// volt_mv = chrg_source_apply_protocol_voltage_rule(pSW, volt_mv);
(void)chrg_source_voltage_control(idx, pSW, volt_mv, curr_ma);
}
/*****************************************************************
@@ -79,10 +168,8 @@ void chrg_source_setting(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
{
//执行输出调整
if (pSW->source.On_Flag == 1) {
if (pSW->source.protocol != PRO_NONE) {
/* 标准协议不依赖北向返回值;快充协议继续解析协商结果。 */
/* 标准协议与快充协议都由解析入口选择各自目标值并统一校验。 */
chrg_source_parse(frame_data, idx, pSW, pNOR);
}
}else if(pSW->source.On_Flag == 0){
//chrg_source_stop(frame_data,pSW,idx,&pNOR->enable[idx]);
pSW->sub = IDX_SET_SOURCE_STOP_STATE;
@@ -134,7 +221,7 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
pSW->change_flag = 0;
if ((rt_uint32_t)volt_mv + 2000 >= pSW->sink.pro_loadv) {
pSW->sink.work_mode = SINK_WORK_RUNING;
if (pSW->Now_current <= 200) {
if (pSW->Now_current <= 200 && (pSW->sink.pro_loadc >= 200)) {
if (++no_curr[idx] >= 3) {
pSW->sink.work_mode = SINK_WORK_WAIT;
no_curr[idx] = 0;
@@ -67,7 +67,7 @@ int sink_fill_pd_buf(rt_uint8_t group,rt_uint8_t pd_val, rt_uint16_t pd_volt, rt
case 0xF6: // QC3.0协议
case 0xF2: // FCP协议
buf[0] = pd_volt / 100; // 第5字节=电压(0.1V)
buf[1] = pd_curr / 100; // 第6字节=电流(0.1A)
buf[1] = 0x00;//pd_curr / 100; // 第6字节=电流(0.1A)
#if PRO_TEST_DEBUG
rt_kprintf("type: %02X, Voltage(0.1V)=%d\n", pd_val, pd_volt/100);
rt_kprintf("type: %02x, Curr(0.1A)=%d\n",pd_curr,pd_curr/100);
@@ -302,7 +302,7 @@ void chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,rt_uint8_t *enab
chrg_roll_nor_sw_uart((eIDX_NOR_CH)ch, ID_SOURCE);
switch(pSW->sub){
case IDX_SET_SOURCE_CC_SET:
source_set_cc_choose(pSW->source.cc_set);
source_set_cc_choose(pSW->source.CC_Set);
pSW->sub = IDX_SET_SOURCE_PD;
break;
case IDX_SET_SOURCE_PD:
@@ -310,8 +310,12 @@ void chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,rt_uint8_t *enab
pSW->sub = IDX_GET_SOURCE_VC;
break;
case IDX_GET_SOURCE_VC:
source_get_vc(pSW->source.vc.set_voltage, pSW->source.vc.set_current);
source_get_vc(pSW->source.set_volt, pSW->source.set_curr);
pSW->sub = IDX_GET_SOURCE_PD;
break;
case IDX_GET_SOURCE_PD:
source_get_pd();
pSW->sub = IDX_GET_SOURCE_VC;
default:
break;
+1 -1
View File
@@ -88,7 +88,7 @@ struct vc_pd_t {
rt_uint8_t group_num; // 挡位数量
rt_uint8_t src_type; // 源类型
rt_uint8_t pps_type; // PPS类型
struct vc_mm_t mm[2]; // 电压电流极值
struct vc_mm_t mm; // 电压电流极值
};
/* 邮件内容 */
@@ -41,7 +41,7 @@ int chrg_north_pkg_encode (eIDX_ID id, rt_uint8_t cmd, rt_uint8_t *data_in,
if (len_in > 0) {
rt_memcpy(&data_out[4], data_in, len_in);
}
data_out[len_in+4] = 0x2a;//calc_crc8(data_out, len_in+4);
data_out[len_in+4] = calc_crc8(data_out, len_in+4);//0x2a;
*len_out = 5+len_in;
return 0;
@@ -84,9 +84,9 @@ int chrg_north_pkg_decode (rt_uint8_t *data, rt_uint16_t length, struct chrg_swi
struct get_pd_pro_ack_t *pAck = (struct get_pd_pro_ack_t *)data;
pS->pd_get[0].src_type = pAck->type;
pS->pd_get[0].pps_type = pAck->pps;
pS->pd_get[0].mm[0].max_vol = pAck->vmax;
pS->pd_get[0].mm[0].min_vol = pAck->vmin;
pS->pd_get[0].mm[0].max_cur = pAck->curpwr;
pS->pd_get[0].mm.max_vol = pAck->vmax;
pS->pd_get[0].mm.min_vol = pAck->vmin;
pS->pd_get[0].mm.max_cur = pAck->curpwr;
}
break;
+12 -2
View File
@@ -140,8 +140,10 @@ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
struct chrg_south_t *pSOU = &chrgsouth;
struct chrg_north_t *pNOR = &chrgnorth;
rt_uint16_t regs[5] = {REG_CURRENT_OUT + 1, REG_MODE,Eload_Start_V_ON,0, 0};
rt_uint16_t vals[5] = {0, pSW->CV_mode,pSW->sink.volt_on, 0, 0};
rt_uint16_t regs[5] = {REG_CURRENT_OUT + 1, REG_MODE,
Eload_Start_V_ON, 0, 0};
rt_uint16_t vals[5] = {0, pSW->CV_mode, pSW->sink.volt_on,
0, 0};
rt_uint8_t count = 3;
if (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) {
@@ -251,12 +253,20 @@ static int chrg_sink_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
rt_uint16_t reg = REG_WORK;
rt_uint16_t value = WORK_STOP;
rt_uint16_t eload_reg = REG_ELOAD;
rt_uint16_t eload_value = ELOAD_LOAD;
int ret;
ret = chrg_sou_com_batch_submit(idx, &reg, &value, 1);
if (ret != 0) {
return ret;
}
/* 等待源模式停止命令入队后,再单独切回负载角色,避免同一批次
* 发生源载模式切换后后续负载初始化命令被功率板忽略。 */
ret = chrg_sou_com_batch_submit(idx, &eload_reg, &eload_value, 1);
if (ret != 0) {
return ret;
}
/* 停止成功入队后,清除旧Source运行标志。 */
pSW->source.On_Flag = 0;
+110 -60
View File
@@ -17,38 +17,41 @@
#include "chrg_roll_sou.h"
#include "chrg_rel.h"
#define SOURCE_VOLT_COMPENSATION_MV 200U
static int chrg_source_work_running(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW);
/*****************************************************************
函数名称: chrg_source_init_submit
函数描述: 将电源模式初始化参数批量提交到南向功率板命令队列
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:提交成功 <0:参数错误或队列提交失败
其它说明: 命令顺序为源载模式、CV/CC模式、继电器、初始电压、保护电流、启动
其它说明: INIT不写电压;电压统一由RUNING状态写入,避免多个入口重复控制
*****************************************************************/
int chrg_source_init_submit(rt_uint8_t idx, struct chrg_switch_t *pSW)
{
/* Source启动参数一次入队,最后发送WORK_START使功率板开始输出。 */
rt_uint16_t regs[6] = {
rt_uint16_t regs[5] = {
REG_ELOAD,
REG_MODE,
0x01,
REG_VOLTAGE_OUT + 1,
REG_CURRENT_OUT + 1,
REG_WORK
};
rt_uint16_t vals[6] = {
rt_uint16_t vals[5] = {
ELOAD_SOURCE,
pSW->CV_mode,
0x03,
0,
pSW->source.vc.set_current,
pSW->source.set_curr,
WORK_START
};
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
return chrg_sou_com_batch_submit(idx, regs, vals, 6);
return chrg_sou_com_batch_submit(idx, regs, vals, 5);
}
/*****************************************************************
@@ -57,7 +60,8 @@ int chrg_source_init_submit(rt_uint8_t idx, struct chrg_switch_t *pSW)
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 更新电源运行标志、通道使能、在线状态及work_mode
返回说明: 0:初始化命令提交成功 非0:初始化失败
其它说明: 成功后状态切换为SOURCE_WORK_WAIT,等待北向返回协商电压
其它说明: 标准协议从pd_set准备目标并转入RUNING;快充协议清空旧电压后
进入WAIT,等待北向协商结果,INIT本身不写功率板电压
*****************************************************************/
static int chrg_source_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
@@ -65,7 +69,15 @@ static int chrg_source_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
struct chrg_north_t *pNOR = &chrgnorth;
int ret;
/* 只有整批命令成功入队后,才打开通道并进入等待状态。 */
/* 最大电流是功率板启动保护值,快充回码到达后可再更新运行目标。 */
pSW->source.set_curr = pSW->source.pd_set.mm.max_cur;
if (pSW->source.protocol == PRO_NONE) {
pSW->source.set_volt = pSW->source.pd_set.mm.max_vol;
} else {
/* 禁止快充重新启动时沿用上一次协商电压。 */
pSW->source.set_volt = 0;
}
ret = chrg_source_init_submit(idx, pSW);
if (ret != 0) {
return ret;
@@ -75,6 +87,11 @@ static int chrg_source_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
pNOR->enable[idx] = 1;
pSOU->online[idx] = 1;
chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SOURCE);
if (pSW->source.protocol == PRO_NONE) {
/* 标准协议没有协商回码,INIT成功后立即由RUNING统一写入电压。 */
pSW->source.work_mode = SOURCE_WORK_RUNING;
return chrg_source_work_running(idx, pSW);
}
pSW->source.work_mode = SOURCE_WORK_WAIT;
return 0;
}
@@ -95,17 +112,18 @@ static int chrg_source_work_wait(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
/*****************************************************************
函数名称: chrg_source_work_running
函数描述: 执行电源RUNING状态,下发最新协商目标电压
函数描述: 执行电源RUNING状态,下发补偿后的最新目标电压
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 非0:命令提交失败
其它说明: 本状态只写电压寄存器,不重复发送WORK_START
其它说明: 功率板设定值固定比目标高200mV,防止限压导致输出进入稳流模式;
目标最大值为48000mV,无需做uint16_t饱和处理
*****************************************************************/
static int chrg_source_work_running(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
/* RUNING只下发协商后的目标电压,不重复发送启动命令。 */
/* 只对本次下发值补偿,不修改set_volt,防止多次执行时累计增加。 */
rt_uint16_t reg = REG_VOLTAGE_OUT + 1;
rt_uint16_t value = pSW->source.vc.set_voltage;
rt_uint16_t value = pSW->source.set_volt;
return chrg_sou_com_batch_submit(idx, &reg, &value, 1);
}
@@ -114,9 +132,9 @@ static int chrg_source_work_running(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
函数名称: chrg_source_work_stop
函数描述: 执行电源主动关机流程
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 清除运行、使能和在线标志,关闭北向继电器
输出参数: 清除目标电压、运行、使能和在线标志,关闭北向继电器
返回说明: 0:停止成功 非0:停止命令提交失败
其它说明: 成功后状态恢复为SOURCE_WORK_WAIT,等待下一次启动命令
其它说明: 先写电压0再发送WORK_STOP;只有整批命令入队成功后才清零set_volt
*****************************************************************/
static int chrg_source_work_stop(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
@@ -124,15 +142,16 @@ static int chrg_source_work_stop(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
struct chrg_north_t *pNOR = &chrgnorth;
int ret;
rt_uint16_t reg = REG_WORK;
rt_uint16_t value = WORK_STOP;
rt_uint16_t regs[2] = {REG_VOLTAGE_OUT + 1, REG_WORK};
rt_uint16_t vals[2] = {0, WORK_STOP};
/* 停止命令入队成功后再关闭使能和北向继电器。 */
ret = chrg_sou_com_batch_submit(idx, &reg, &value, 1);
/* 电压清零与停止必须保持顺序,避免下次启动继承旧目标而产生过压。 */
ret = chrg_sou_com_batch_submit(idx, regs, vals, 2);
if (ret != 0) {
return ret;
}
pSW->source.set_volt = 0;
pSW->source.On_Flag = 0;
pNOR->enable[idx] = 0;
pSOU->online[idx] = 0;
@@ -152,17 +171,18 @@ static int chrg_source_work_stop(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
*****************************************************************/
static int chrg_source_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
rt_uint16_t regs[2] = {REG_REL_ON, REG_WORK};
rt_uint16_t vals[2] = {ALL_REL_OFF, WORK_STOP};
rt_uint16_t regs[3] = {REG_REL_ON, REG_VOLTAGE_OUT + 1, REG_WORK};
rt_uint16_t vals[3] = {ALL_REL_OFF, 0, WORK_STOP};
int ret;
ret = chrg_sou_com_batch_submit(idx, regs, vals, 2);
ret = chrg_sou_com_batch_submit(idx, regs, vals, 3);
if (ret != 0) {
return ret;
}
/* 停止成功入队后,清除旧Sink运行标志。 */
pSW->sink.On_work = 0;
pSW->source.set_volt = 0;
/* 首次进入Source时必须从CC线配置开始,不能沿用Sink的sub状态。 */
pSW->sub = IDX_SET_SOURCE_CC_SET;
/* SWITCH只负责旧角色退出,后续启动统一交给Source INIT状态。 */
@@ -891,6 +911,8 @@ rt_uint8_t pd_fill_single_group_need_src_com(rt_uint8_t *data, rt_uint8_t start_
rt_uint8_t idx = start_idx;
struct chrg_src_t *source = &psw->source;
const struct vc_pd_t *pd = &source->pd_set;
const struct vc_mm_t *mm = &pd->mm;
// 1. src + pps(2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
@@ -901,23 +923,23 @@ rt_uint8_t pd_fill_single_group_need_src_com(rt_uint8_t *data, rt_uint8_t start_
data[idx++] = 0x13;
data[idx++] = 0x88;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_current >> 8;
data[idx++] = source->vc.set_current & 0xFF;
data[idx++] = mm->max_cur >> 8;
data[idx++] = mm->max_cur & 0xFF;
// 5. 保留位6字节(直接填0)
memset(&data[idx], 0x00, 6);
idx+=6;
data[idx++] = source->src;
data[idx++] = source->pps;
rt_kprintf("src = %d pps = %d\n",source->src,source->pps);
data[idx++] = pd->src_type;
data[idx++] = pd->pps_type;
//rt_kprintf("src = %d pps = %d\n", pd->src_type, pd->pps_type);
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = source->min_vol >> 8;
data[idx++] = source->min_vol & 0xFF;
data[idx++] = mm->min_vol >> 8;
data[idx++] = mm->min_vol & 0xFF;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_voltage >> 8;
data[idx++] = source->vc.set_voltage & 0xFF;
data[idx++] = mm->max_vol >> 8;
data[idx++] = mm->max_vol & 0xFF;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_current >> 8;
data[idx++] = source->vc.set_current & 0xFF;
data[idx++] = mm->max_cur >> 8;
data[idx++] = mm->max_cur & 0xFF;
// 5. 保留位6字节(直接填0)
memset(&data[idx], 0x00, 6);
idx+=6;
@@ -928,28 +950,30 @@ rt_uint8_t pd_fill_single_group_no_src_com(rt_uint8_t *data, rt_uint8_t start_id
rt_uint8_t idx = start_idx;
struct chrg_src_t *source = &psw->source;
const struct vc_mm_t *mm = &source->pd_set.mm;
// 1. src + pps(2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = source->min_vol >> 8;
data[idx++] = source->min_vol & 0xFF;
data[idx++] = mm->min_vol >> 8;
data[idx++] = mm->min_vol & 0xFF;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_voltage >> 8;
data[idx++] = source->vc.set_voltage & 0xFF;
data[idx++] = mm->max_vol >> 8;
data[idx++] = mm->max_vol & 0xFF;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_current >> 8;
data[idx++] = source->vc.set_current & 0xFF;
data[idx++] = mm->max_cur >> 8;
data[idx++] = mm->max_cur & 0xFF;
// 5. 保留位6字节(直接填0)
memset(&data[idx], 0x00, 6);
idx+=6;
return idx - start_idx; // 返回填充的总字节数
}
rt_uint8_t pd_fill_single_group_FCP_AFC(rt_uint8_t *data, rt_uint8_t start_idx,struct chrg_switch_t *psw){
rt_uint8_t pd_fill_single_group_AFC(rt_uint8_t *data, rt_uint8_t start_idx,struct chrg_switch_t *psw){
rt_uint8_t idx = start_idx;
struct chrg_src_t *source = &psw->source;
const struct vc_mm_t *mm = &source->pd_set.mm;
// 1. src + pps(2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
@@ -960,8 +984,8 @@ rt_uint8_t pd_fill_single_group_FCP_AFC(rt_uint8_t *data, rt_uint8_t start_idx,s
data[idx++] = 0x13;
data[idx++] = 0x88;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_current >> 8;
data[idx++] = source->vc.set_current & 0xFF;
data[idx++] = mm->max_cur >> 8;
data[idx++] = mm->max_cur & 0xFF;
// 5. 保留位6字节(直接填0)
memset(&data[idx], 0x00, 6);
idx+=6;
@@ -969,14 +993,14 @@ rt_uint8_t pd_fill_single_group_FCP_AFC(rt_uint8_t *data, rt_uint8_t start_idx,s
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_voltage >> 8;
data[idx++] = source->vc.set_voltage & 0xFF;
data[idx++] = mm->max_vol >> 8;
data[idx++] = mm->max_vol & 0xFF;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_voltage >> 8;
data[idx++] = source->vc.set_voltage & 0xFF;
data[idx++] = mm->max_vol >> 8;
data[idx++] = mm->max_vol & 0xFF;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_current >> 8;
data[idx++] = source->vc.set_current & 0xFF;
data[idx++] = mm->max_cur >> 8;
data[idx++] = mm->max_cur & 0xFF;
// 5. 保留位6字节(直接填0)
memset(&data[idx], 0x00, 6);
idx+=6;
@@ -987,6 +1011,7 @@ rt_uint8_t pd_fill_single_group_UFCS(rt_uint8_t *data, rt_uint8_t start_idx,stru
rt_uint8_t idx = start_idx;
struct chrg_src_t *source = &psw->source;
const struct vc_mm_t *mm = &source->pd_set.mm;
// 1. src + pps(2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
@@ -997,8 +1022,8 @@ rt_uint8_t pd_fill_single_group_UFCS(rt_uint8_t *data, rt_uint8_t start_idx,stru
data[idx++] = 0x13;
data[idx++] = 0x88;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_current >> 8;
data[idx++] = source->vc.set_current & 0xFF;
data[idx++] = mm->max_cur >> 8;
data[idx++] = mm->max_cur & 0xFF;
// 5. 保留位6字节(直接填0)
memset(&data[idx], 0x00, 6);
idx+=6;
@@ -1006,20 +1031,45 @@ rt_uint8_t pd_fill_single_group_UFCS(rt_uint8_t *data, rt_uint8_t start_idx,stru
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = source->min_vol >> 8;
data[idx++] = source->min_vol & 0xFF;
data[idx++] = mm->min_vol >> 8;
data[idx++] = mm->min_vol & 0xFF;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_voltage >> 8;
data[idx++] = source->vc.set_voltage & 0xFF;
data[idx++] = mm->max_vol >> 8;
data[idx++] = mm->max_vol & 0xFF;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->vc.set_current >> 8;
data[idx++] = source->vc.set_current & 0xFF;
data[idx++] = mm->max_cur >> 8;
data[idx++] = mm->max_cur & 0xFF;
// 5. 保留位6字节(直接填0)
memset(&data[idx], 0x00, 6);
idx+=6;
return idx - start_idx; // 返回填充的总字节数
}
static rt_uint8_t chrg_source_fill_AFC_three_gears(rt_uint8_t *data,
rt_uint8_t start_idx, struct chrg_switch_t *psw)
{
static const rt_uint16_t fixed_voltages[] = {5000U, 9000U, 12000U, 20000U};
const struct vc_mm_t *mm = &psw->source.pd_set.mm;
rt_uint8_t idx = start_idx;
rt_uint8_t gear;
for (gear = 0; gear < (sizeof(fixed_voltages) / sizeof(fixed_voltages[0]));
gear++) {
/* FCP固定电压档不使用PD源类型和PPS类型。 */
data[idx++] = 0x00;
data[idx++] = 0x00;
data[idx++] = fixed_voltages[gear] >> 8;
data[idx++] = fixed_voltages[gear] & 0xFF;
data[idx++] = fixed_voltages[gear] >> 8;
data[idx++] = fixed_voltages[gear] & 0xFF;
data[idx++] = mm->max_cur >> 8;
data[idx++] = mm->max_cur & 0xFF;
memset(&data[idx], 0x00, 6);
idx += 6;
}
return idx - start_idx;
}
int source_set_pro(rt_uint8_t pro, struct chrg_switch_t *pSW)
{
@@ -1032,14 +1082,14 @@ int source_set_pro(rt_uint8_t pro, struct chrg_switch_t *pSW)
rt_uint8_t idx = 1; //从data[1]开始填充
rt_uint8_t fill_len = 0;
rt_uint8_t protocal = pSW->source.protocol;
if(protocal == 0x0A){
if(protocal == PRO_PD0PSS){
fill_len = pd_fill_single_group_need_src_com(data, idx, pSW);
}else if(protocal == 0x03||protocal == 0x07||protocal == 0x08 ){
}else if(protocal == PRO_SCPB||protocal == PRO_QC30||protocal == PRO_QC20 ||protocal == PRO_FCP ){
fill_len = pd_fill_single_group_no_src_com(data, idx, pSW);
}else if(protocal == 0x0C){
}else if(protocal == PRO_UFCS){
fill_len = pd_fill_single_group_UFCS(data, idx, pSW);
}else{
fill_len = pd_fill_single_group_FCP_AFC(data, idx, pSW);
fill_len = chrg_source_fill_AFC_three_gears(data, idx, pSW);
}
idx += fill_len;
send_len = idx; // 总填充字节数
+5 -9
View File
@@ -137,8 +137,6 @@ struct set_qc_ack_t {
struct src_vc_t {
rt_uint16_t voltage; // 读取 0.01V
rt_uint16_t set_voltage; // 设置电压 mV
rt_uint16_t set_current; // 设置电流 mA
rt_uint16_t version; // 版本号
};
@@ -146,20 +144,18 @@ struct src_vc_t {
// source 参数结构
struct chrg_src_t {
eSRC_PRO protocol; // 协议类型
rt_uint8_t src; // 源类型
rt_uint8_t pps; // PPS类型
rt_uint8_t pro_gear_idx; // 挡位
rt_uint16_t min_vol; // 最小电压 mV
rt_uint8_t vol_lvl; // CC线电平
rt_uint8_t cc_set; // cc线选择 //0代表1+2,1代表cc1,2代表cc2
rt_uint8_t CC_Set; // CC线选择:0=CC1+CC2,1=CC1,2=CC2
rt_uint8_t Now_State; // 当前状态
rt_uint8_t On_Flag; // 启动总标志(0/1)
rt_uint8_t LCD_Set_Pro_Flag; // LCD设置协议标志位
rt_uint8_t change_sink_Flag; // 从source切换到sink标志位
rt_uint8_t work_mode;
struct src_vc_t vc; // 读取数据
rt_uint16_t set_volt; // 北向解析后准备写入功率板的目标电压(mV)
rt_uint16_t set_curr; // 北向解析后准备写入功率板的目标电流(mA)
struct src_vc_t vc; // 北向回读数据
struct vc_pd_t pd_get[2]; // 读取PD协议数据
struct vc_pd_t pd_set[2]; // 设置PD协议数据
struct vc_pd_t pd_set; // COM设置的协议挡位、类型和电压电流范围
};
// 电源工作状态机:COM只切换状态,具体功率板指令由状态处理函数下发
enum {
@@ -0,0 +1,210 @@
# 四通道自举老化测试计划
## 1. 测试目的
验证四通道在源载自连接条件下长期运行的稳定性,重点覆盖:
- Source/Sink启动、停止及角色切换流程;
- 标准、PD、QC、FCP、AFC及UFCS等已实现协议的协商稳定性;
- 四通道连续控制、南向指令执行及量测轮询稳定性;
- 电压、电流阶梯变化和长时间带载能力;
- 通信超时、协议掉线、功率板故障、异常复位和温升等问题。
本计划不包含短路和OCP破坏性测试。此类项目应使用独立限流电源、专用线材及人员值守,不能混入无人值守老化。
## 2. 测试连接
### 2.1 第一组连接
| 回路 | Source通道 | Sink通道 | 连接方式 |
| --- | --- | --- | --- |
| A | CH1 | CH2 | VBUS、GND及协议线完整连接 |
| B | CH3 | CH4 | VBUS、GND及协议线完整连接 |
### 2.2 反向角色连接
完成正向老化后不改变线束,停止全部通道并交换角色:
| 回路 | Source通道 | Sink通道 |
| --- | --- | --- |
| A | CH2 | CH1 |
| B | CH4 | CH3 |
线束必须满足目标电流要求。PD测试连接CC,QC/FCP/AFC测试连接D+、D-,并保证协议板GND与功率GND连接符合硬件设计。严禁两个Source输出直接相连。
## 3. 测试条件
- 使用待发布版本的主控、Source协议板、Sink协议板和功率板固件;
- 记录固件版本、硬件版本、设备序列号、线材规格及环境温度;
- 风扇、保险丝、急停和输入电源工作正常;
- 输入电源功率至少保留20%余量;
- 测试电流不得超过连接器、线材、功率板和协议档位中的最小额定值;
- 常规量测查询周期设为1秒,状态及故障寄存器至少每5秒记录一次;
- 30ms查询仅用于不超过60秒的波形专项采样,结束后恢复1秒周期;
- 上位机必须收到并校验上一条Modbus响应后再发送下一条命令;
- Modbus异常响应必须单独记录,特别是异常码`0x06`(设备忙)。
## 4. 单回路启动流程
每次启动必须按照以下顺序执行:
1. 向Source和Sink发送停止,确认功率板工作状态为停止;
2. 设置Source角色、协议、协议档位、最大电压、最大电流及CC线;
3. 设置Sink角色、协议、申请电压、申请电流、恒流模式及负载电流;
4. 对Source执行参数生效并启动;
5. 对Sink执行参数生效并启动;
6. 等待协议协商和南向功率板命令实际执行,不能将COM响应当作输出已经生效;
7. 连续读取量测值,电压稳定并保持3次有效采样后再进入老化计时;
8. 启动失败时先停止Sink,再停止Source,禁止直接反复发送启动命令。
正常停止顺序为:Sink停止并确认电流下降,然后Source停止并确认电压下降,最后才允许切换角色或协议。
## 5. 判定标准
### 5.1 单次启动合格条件
- Source和Sink协议类型与设定一致;
- 启动后5秒内进入稳定运行状态;
- 稳态电压与目标电压误差不超过200mV;
- 稳态电流误差不超过`max(50mA, 设定值的5%)`;
- 连续3次采样均满足要求后判定本次启动成功;
- 状态和故障寄存器无新增故障位;
- 无南向连续3次超时、队列取消、协议掉线或系统复位。
### 5.2 老化过程停止条件
出现以下任一情况应立即停止对应回路;出现过压、异味、冒烟或连接器异常发热时停止整机:
- 电压持续3秒超出目标值±500mV;
- 电流持续3秒超出设定值±10%;
- 协议掉线后5秒内不能自动恢复;
- 功率板报告欠压、过压、过流或其他故障;
- 连续3次COM无响应,或出现南向批量命令失败;
- 任一通道量测值连续5秒不更新;
- 设备复位、线程停止、LED2/LED3长时间停止刷新;
- 温度超过器件或整机规格。规格未明确时,连接器和线缆表面达到70摄氏度应停止测试。
## 6. 48小时老化安排
### 阶段A:低功率预老化,2小时
| 时间 | 回路A | 回路B | 目的 |
| --- | --- | --- | --- |
| 0~1小时 | 标准5V/0.5A | PD 5V/0.5A | 检查基础源载和PD启动 |
| 1~2小时 | QC3.0 9V/0.5A | FCP 9V/0.5A | 检查D+/D-协议带载稳定性 |
每15分钟执行一次停止、等待5秒、重新启动。阶段结束后检查连接器、继电器和线材温升。
### 阶段B:协议覆盖,6小时
共执行8个测试时段,每个时段45分钟。两组回路同时使用不同协议;后4个时段交换协议分配,确保两组回路都覆盖全部协议。
| 时段 | 回路A | 回路B |
| --- | --- | --- |
| 1 | 标准 | PD固定档 |
| 2 | PD PPS | QC2.0 |
| 3 | QC3.0 | FCP |
| 4 | AFC | UFCS |
| 5 | PD固定档 | 标准 |
| 6 | QC2.0 | PD PPS |
| 7 | FCP | QC3.0 |
| 8 | UFCS | AFC |
每个时段内轮换该协议支持的电压档位,负载电流在0.5A和1A之间交替。协议不支持对应电压时,使用协议实际支持的最近档位。
| 序号 | 协议 | 电压 | 负载电流 |
| --- | --- | --- | --- |
| 1 | 标准 | 5V、9V、12V | 0.5A、1A |
| 2 | PD固定档 | 5V、9V、12V、15V、20V | 0.5A、1A |
| 3 | PD PPS | 5V、9V、12V | 0.5A、1A |
| 4 | QC2.0 | 5V、9V、12V | 0.5A、1A |
| 5 | QC3.0 | 5V、9V、12V | 0.5A、1A |
| 6 | FCP | 5V、9V、12V | 0.5A、1A |
| 7 | AFC | 5V、9V、12V | 0.5A、1A |
| 8 | UFCS | 按当前固件支持档位 | 0.5A、1A |
未正式实现或尚未完成硬件验证的协议应标记为“不适用”,不得用其他协议的通过结果代替。
### 阶段C:额定功率长期运行,20小时
- 两组回路同时运行;
- 优先选择PD 20V和QC3.0 12V两个代表性协议;
- 电流按照额定能力的25%、50%、80%循环;
- 每个电流档保持30分钟;
- 每2小时停止两组回路,等待30秒后重新启动;
- 每4小时记录一次主控板、协议板、功率板、连接器和线缆温度;
- 第10小时交换两组回路使用的协议,避免固定通道只验证一种协议。
若设备额定值或散热条件不允许80%档位长时间运行,应以硬件额定连续功率为上限,并在记录中说明限制原因。
### 阶段D:动态调节与协议重协商,8小时
每个循环执行以下步骤,循环间隔不得小于30秒:
1. 在同一协议下按`0.5A -> 1A -> 2A -> 1A -> 0.5A`调节负载;
2. 每个电流档保持60秒;
3. 固定电流,在协议允许范围内按`5V -> 9V -> 12V -> 9V -> 5V`调节电压;
4. 每个电压档稳定后保持60秒;
5. 停止Sink,再停止Source;
6. 在PD与QC3.0之间切换协议并重新启动;
7. 检查切换前输出是否先降为0,以及新协议是否重新协商成功。
2A仅在源、载、线材及连接器均支持时使用,否则替换为额定电流的80%。
### 阶段E:角色切换,6小时
执行200次角色切换动作,即100个完整往返循环:
1. CH1 Source/CH2 Sink、CH3 Source/CH4 Sink运行60秒;
2. 停止Sink并确认电流下降;
3. 停止Source并确认电压下降;
4. 等待5秒;
5. 切换为CH2 Source/CH1 Sink、CH4 Source/CH3 Sink;
6. 运行60秒并记录结果;
7. 再按相同步骤切换回原角色,完成第二次切换动作和一个完整往返循环。
每20个完整往返循环更换一次协议,依次使用标准、PD、QC3.0、FCP和AFC。任何一次切换失败都必须保存前后30秒日志。
### 阶段F:通信和整机重启,6小时
- 正常运行时每秒读取一次全部四通道量测;
- 每10分钟执行一次四通道连续参数设置,命令之间等待Modbus响应;
- 四通道参数生效完成后等待300ms,再开始判断量测结果;
- 每30分钟执行一次全部停止和重新启动;
- 每小时执行一次整机断电重启,断电保持30秒;
- 重启后确认四通道初始状态安全,不允许未经上位机启动就输出或带载;
- 共完成不少于6次整机断电重启。
## 7. 数据记录
老化程序至少保存以下字段,建议使用CSV格式:
```text
timestamp,stage,cycle,pair,source_ch,sink_ch,protocol,
target_voltage_mv,target_current_ma,source_voltage_mv,source_current_ma,
sink_voltage_mv,sink_current_ma,source_status1,source_status2,
sink_status1,sink_status2,source_fault1,source_fault2,
sink_fault1,sink_fault2,com_error_count,south_timeout_count,
protocol_drop_count,restart_count,board_temp_c,connector_temp_c,result
```
发生异常时额外保存:
- 异常前后至少30秒的原始COM收发帧;
- Source/Sink协议板原始回码;
- 南向功率板写命令、响应、重试次数及失败寄存器;
- 四通道状态、故障、电压、电流和工作状态机;
- 异常时温度、输入电压及现场照片。
## 8. 最终通过条件
- 48小时计划全部完成,无安全类故障;
- 四个通道均完成Source和Sink角色老化;
- 所有声明支持的协议完成正向和反向连接验证;
- 200次角色切换动作(100个完整往返循环)无失败;
- 不少于6次整机断电重启均正常恢复;
- 无不明复位、线程阻塞、量测长期不更新或通道永久失联;
- 电压、电流和温升满足本计划判定标准;
- 所有异常均有日志、复现条件和明确结论。
测试完成后形成汇总表,按通道统计启动次数、成功率、协议掉线次数、南向超时次数、最大电压误差、最大电流误差和最高温度。任何未达到100%启动成功率的项目均不得直接判定整机老化通过。