diff --git a/apps/chrg/applications/thread/chrg_comm.c b/apps/chrg/applications/thread/chrg_comm.c index 9066f7b..bb40823 100644 --- a/apps/chrg/applications/thread/chrg_comm.c +++ b/apps/chrg/applications/thread/chrg_comm.c @@ -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; } /***************************************************************** diff --git a/apps/chrg/applications/thread/chrg_lcd.c b/apps/chrg/applications/thread/chrg_lcd.c index 90460e5..65a2ca1 100644 --- a/apps/chrg/applications/thread/chrg_lcd.c +++ b/apps/chrg/applications/thread/chrg_lcd.c @@ -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,) - // 写入电压寄存器 - if(pSW->id==ID_SINK) { - chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_VOLTAGE_OUT+1, Recv_Data); - } - else chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_VOLTAGE_OUT+1, 5000); + /* 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); + } 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); // 写入电流寄存器 diff --git a/apps/chrg/applications/thread/chrg_north.c b/apps/chrg/applications/thread/chrg_north.c index 68da6aa..d7bc703 100644 --- a/apps/chrg/applications/thread/chrg_north.c +++ b/apps/chrg/applications/thread/chrg_north.c @@ -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); - } + /* 标准协议与快充协议都由解析入口选择各自目标值并统一校验。 */ + 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; diff --git a/apps/chrg/applications/thread/chrg_roll_nor.c b/apps/chrg/applications/thread/chrg_roll_nor.c index 5467673..675f439 100644 --- a/apps/chrg/applications/thread/chrg_roll_nor.c +++ b/apps/chrg/applications/thread/chrg_roll_nor.c @@ -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; diff --git a/apps/chrg/applications/utils/chrg_def.h b/apps/chrg/applications/utils/chrg_def.h index 3ed8388..77b313c 100644 --- a/apps/chrg/applications/utils/chrg_def.h +++ b/apps/chrg/applications/utils/chrg_def.h @@ -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; // 电压电流极值 }; /* 邮件内容 */ diff --git a/apps/chrg/applications/utils/chrg_north_pkg.c b/apps/chrg/applications/utils/chrg_north_pkg.c index 049e96a..5241c60 100644 --- a/apps/chrg/applications/utils/chrg_north_pkg.c +++ b/apps/chrg/applications/utils/chrg_north_pkg.c @@ -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; diff --git a/apps/chrg/applications/utils/chrg_sink.c b/apps/chrg/applications/utils/chrg_sink.c index 1e12396..b75ab47 100644 --- a/apps/chrg/applications/utils/chrg_sink.c +++ b/apps/chrg/applications/utils/chrg_sink.c @@ -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, ®, &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; diff --git a/apps/chrg/applications/utils/chrg_source.c b/apps/chrg/applications/utils/chrg_source.c index 2834040..89d7599 100644 --- a/apps/chrg/applications/utils/chrg_source.c +++ b/apps/chrg/applications/utils/chrg_source.c @@ -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, ®, &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,17 +142,18 @@ 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, ®, &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; + pNOR->enable[idx] = 0; pSOU->online[idx] = 0; chrg_nor_sw_rel((eIDX_NOR_CH)idx, 0); pSW->source.work_mode = SOURCE_WORK_WAIT; @@ -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; // 总填充字节数 diff --git a/apps/chrg/applications/utils/chrg_source.h b/apps/chrg/applications/utils/chrg_source.h index d03c88f..43c394c 100644 --- a/apps/chrg/applications/utils/chrg_source.h +++ b/apps/chrg/applications/utils/chrg_source.h @@ -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 { diff --git a/docs/2026-07-29-self-loop-aging-test-plan.md b/docs/2026-07-29-self-loop-aging-test-plan.md new file mode 100644 index 0000000..e6f5a21 --- /dev/null +++ b/docs/2026-07-29-self-loop-aging-test-plan.md @@ -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%启动成功率的项目均不得直接判定整机老化通过。