feat: 调整负载启动、掉电恢复及协议切换时序

详细修改:
1. 新增chrg_eload_get_sample_seq接口获取量测更新序号
2. 关闭LCD接收功能避免阻塞量测上报
3. 新增Sink北向事务状态枚举,区分完整重握手和软卸载流程
4. 调整北向调试开关配置,开启南向寄存器写命令调试
5. 重构Sink状态机,拆分INIT等待、掉电恢复、软卸载等阶段
6. 优化协议切换逻辑:仅电压变化使用软卸载,其余变更执行完整重握手
7. 增加电压稳定确认次数,避免瞬态电压误触发运行状态
8. 新增南向批量命令状态查询和优先量测请求功能
9. 调整继电器操作时序,副继电器回码后再执行后续初始化命令
10. 新增修改日志文档记录版本变更
This commit is contained in:
wrh
2026-08-07 16:27:56 +08:00
parent d9a8c359c0
commit b96902f9b9
15 changed files with 855 additions and 90 deletions
+20 -14
View File
@@ -2336,8 +2336,8 @@ static void chrg_comm_get_param_diff(eIDX_ID role,
static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
struct chrg_switch_t *pReal, const struct chrg_switch_t *pShadow)
{
rt_bool_t protocol_or_cc_change;
rt_bool_t protocol_param_change;
rt_bool_t full_handshake_change;
rt_bool_t protocol_voltage_only;
rt_uint8_t curr_test;
rt_uint8_t curr_test_old;
rt_uint8_t old_cv_mode = pReal->CV_mode;
@@ -2347,11 +2347,14 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
chrg_param_diff_t diff_info;
chrg_comm_get_param_diff(ID_SINK, pReal, pShadow, &diff_info);
/* 协议类型或CC线变化:需卸载后从CC开始重新配置北向协议链路 */
protocol_or_cc_change = diff_info.protocol_changed || diff_info.cc_changed;
/* 同协议下组号/协议电压/协议电流变化:卸载后重发协议,不重设CC */
protocol_param_change = diff_info.group_changed ||
diff_info.pro_voltage_changed || diff_info.pro_current_changed;
/*
* 协议类型、CC、协议组号或协议电流变化都按完整重握手处理。
* 只有单独修改协议电压时才允许仅掉载而不切换南向继电器。
*/
full_handshake_change = diff_info.protocol_changed || diff_info.cc_changed ||
diff_info.group_changed || diff_info.pro_current_changed;
protocol_voltage_only = diff_info.pro_voltage_changed &&
!full_handshake_change;
rt_memcpy(pReal, pShadow, sizeof(struct chrg_switch_t));
if (sink_work == 0x01) {
@@ -2369,18 +2372,21 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
}
pReal->sink.test.test_mode_old = curr_test;
} else {
pReal->sub = IDX_SET_SINK_CCLINE;
pReal->sub = IDX_SET_SINK_PD;
pReal->sink.work_mode = SINK_WORK_INIT;
}
} else if (pReal->sink.On_work == 0) {
pReal->sink.On_work = 1;
pReal->sub = IDX_SET_SINK_CCLINE;
pReal->sink.work_mode = SINK_WORK_INIT;
} else if (protocol_or_cc_change) {
pReal->sub = IDX_SET_SINK_CCLINE;
pReal->sink.work_mode = SINK_WORK_PROTOCOL_UNLOAD;
} else if (protocol_param_change) {
pReal->sub = IDX_SET_SINK_PD;
pReal->sink.work_mode = SINK_WORK_INIT;
} else if (full_handshake_change) {
/* 协议类型或CC变化需要重发协议和CC;仅组号/协议电流变化只重发协议。 */
pReal->sub = (diff_info.protocol_changed || diff_info.cc_changed) ?
IDX_SET_SINK_PD : IDX_SET_SINK_PD_ONLY;
pReal->sink.work_mode = SINK_WORK_HANDSHAKE_PREPARE;
} else if (protocol_voltage_only) {
/* 仅协议电压变化时保持主副继电器状态,只停止负载并清零电流。 */
pReal->sub = IDX_SET_SINK_PD_ONLY;
pReal->sink.work_mode = SINK_WORK_PROTOCOL_UNLOAD;
} else if (chrg_sink_diff_send_reg((eIDX_SOU_CH)ch,
&diff_info, pReal) != 0) {
+20 -1
View File
@@ -369,7 +369,9 @@ void chrg_lcd_thread_entry (void *data)
return ;
}
#if LCD_RX_ENABLE
int len = 0;
#endif
struct chrg_tty_t *pTTY = RT_NULL;
struct chrg_lcd_t *pLCD = &chrglcd;
pTTY = chrg_tty_create(pLCD->devname, 921600, 0, -1, 1);
@@ -377,8 +379,10 @@ void chrg_lcd_thread_entry (void *data)
LOG_E("tty can't create.");
return ;
}
#if LCD_RX_ENABLE
/* LCD接收功能开启时,最多等待100ms屏幕回传数据。 */
chrg_tty_set_recv_tmo(pTTY, 100);
#endif
if (chrg_tty_connect(pTTY) != RT_EOK) {
chrg_tty_destory(pTTY);
return;
@@ -386,6 +390,12 @@ void chrg_lcd_thread_entry (void *data)
pLCD->tty = pTTY;
#if LCD_RX_ENABLE
/*
* 保留原LCD接收及控制解析流程,需要恢复屏幕控制时只需将
* LCD_RX_ENABLE改为1。接收关闭时本段不参与编译,避免chrg_tty_recv
* 在等待回传期间持有TTY锁并阻塞chrg_lcd_send。
*/
while (1) {
rt_memset(pLCD->rx_buf, 0, SIZE_BUF_TTY);
len = chrg_tty_recv(pTTY, pLCD->rx_buf, sizeof(pLCD->rx_buf));
@@ -399,6 +409,15 @@ void chrg_lcd_thread_entry (void *data)
chrg_lcd_parse(pLCD->rx_buf, len);
}
}
#else
/*
* 仅发送模式仍保留LCD线程和串口实例。线程低频休眠且不访问TTY,
* 量测解析中的LCD发送可以直接取得TTY锁,不再受100ms接收超时影响。
*/
while (1) {
rt_thread_mdelay(1000);
}
#endif
}
+3
View File
@@ -18,6 +18,9 @@
#define THR_NAME_LCD "thr.lcd"
#define DEV_NAME_LCD "uart2"
/* LCD发送保持启用;当前产品不使用屏幕回传控制,关闭接收可避免接收线程持有TTY锁阻塞量测上报。 */
#define LCD_RX_ENABLE 0
#define LCD_FRAME_HEAD 0x55
+51 -15
View File
@@ -24,10 +24,12 @@
#define DBG_LEVEL DBG_LOG
#include <rtdbg.h>
#define SOURCE_VOLT_ERROR_MV 200U
#define SOURCE_VOLT_RETRY_FRAMES 3U
#define SOURCE_QC_MIN_VOLT_MV 5000U
#define SOURCE_QC_SWITCH_ERROR 0x04U
#define SOURCE_VOLT_ERROR_MV 200U // Source目标电压允许误差,单位mV
#define SOURCE_VOLT_RETRY_FRAMES 3U // Source电压持续超差后的重发确认帧数
#define SOURCE_QC_MIN_VOLT_MV 5000U // QC2.0/QC3.0允许使用的最低目标电压
#define SOURCE_QC_SWITCH_ERROR 0x04U // Source协议或CC切换失败错误位
#define SINK_CURRENT_START_GRACE_MS 200U // Sink启动后暂不判断无电流的保护时间
#define SINK_VOLT_STABLE_CONFIRM_COUNT 2U // Sink进入RUNING前连续稳定电压帧数
/*****************************************************************
函数名称: chrg_source_apply_protocol_voltage_rule
@@ -188,10 +190,10 @@ void chrg_source_setting(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
函数描述: 解析北向负载板回码并根据电压、电流反馈驱动Sink状态机
输入参数: frame_data:完整北向回码 idx:当前通道 pSW:通道控制结构体
pNOR:北向总控结构体
输出参数: 更新VZ、VF、低压计数、无电流计数及sink.work_mode
输出参数: 更新VZ、VF、低压计数、稳定电压计数、无电流计数及sink.work_mode
返回说明: -
其它说明: 电压连续低于门槛进入LOW;电压达到目标附近进入RUNING;
运行电流连续过低时退回WAIT重新判断
其它说明: 电压连续低于门槛进入LOW;掉电处理恢复副继电器后,电压重新
出现时先重设协议和CC;连续两次达到目标附近才进入RUNING
*****************************************************************/
void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
@@ -200,6 +202,8 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
static rt_uint8_t volt_low[TOTAL_SOU_CHS] = {0};
static rt_uint8_t no_curr[TOTAL_SOU_CHS] = {0};
static rt_uint8_t volt_set_ok[TOTAL_SOU_CHS] = {0};
static rt_uint8_t volt_stable_count[TOTAL_SOU_CHS] = {0};
static rt_tick_t running_start_tick[TOTAL_SOU_CHS] = {0};
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return;
@@ -214,8 +218,9 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
if(pSW->sink.On_work == 1 && pSW->sink.test.test_mode == 0) {
if (volt_mv <= WORK_MIN_VOLT) { // 电压过低,禁止进入工作状态机
volt_stable_count[idx] = 0;
if (volt_set_ok[idx] == 1) {
if (++volt_low[idx] >= 3) {
if (++volt_low[idx] >= 2) {
volt_set_ok[idx] = 0;
no_curr[idx] = 0;
pSW->sink.work_mode = SINK_WORK_LOW;
@@ -226,18 +231,48 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
volt_low[idx] = 0;
volt_set_ok[idx] = 1;
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 && (pSW->sink.pro_loadc >= 200)) {
if (++no_curr[idx] >= 3) {
if (pSW->sink.work_mode == SINK_WORK_LOW_MONITOR) {
/* 副继电器恢复后首次重新检测到输入电压,只启动协议重握手。
* 当前回码不计入稳定电压次数,防止跳过协议设置直接带载。 */
volt_stable_count[idx] = 0;
no_curr[idx] = 0;
pSW->sub = IDX_SET_SINK_PD;
pSW->sink.work_mode = SINK_WORK_WAIT;
} else if ((pSW->sink.work_mode == SINK_WORK_LOW) ||
(pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT)) {
/* 掉电安全批次完成前,即使收到残留回码也不得进入RUNING。 */
volt_stable_count[idx] = 0;
no_curr[idx] = 0;
} else if ((rt_uint32_t)volt_mv + 2000 >= pSW->sink.pro_loadv) {
if ((pSW->sink.work_mode != SINK_WORK_RUNING) &&
(pSW->sink.work_mode != SINK_WORK_SOFT_RUNNING)) {
if (volt_stable_count[idx] < SINK_VOLT_STABLE_CONFIRM_COUNT) {
volt_stable_count[idx]++;
}
if (volt_stable_count[idx] >= SINK_VOLT_STABLE_CONFIRM_COUNT) {
/* 连续两次北向量测均达到目标电压附近后才允许带载,避免
* 单次瞬态电压或旧回码导致负载提前进入RUNING。 */
volt_stable_count[idx] = 0;
pSW->sink.work_mode =
(pSW->sink.work_mode == SINK_WORK_SOFT_RESUME_WAIT) ?
SINK_WORK_SOFT_RESUME : SINK_WORK_RUNING;
running_start_tick[idx] = rt_tick_get();
no_curr[idx] = 0;
}
} else if ((pSW->Now_current > 200) ||
(pSW->sink.pro_loadc < 200)) {
volt_stable_count[idx] = 0;
no_curr[idx] = 0;
} else if ((rt_tick_t)(rt_tick_get() - running_start_tick[idx]) >=
rt_tick_from_millisecond(SINK_CURRENT_START_GRACE_MS)) {
volt_stable_count[idx] = 0;
if (++no_curr[idx] >= 2) {
pSW->sink.work_mode = SINK_WORK_WAIT;
no_curr[idx] = 0;
}
}
else {
no_curr[idx] = 0;
}
} else {
volt_stable_count[idx] = 0;
no_curr[idx] = 0;
}
}
@@ -246,6 +281,7 @@ void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
volt_low[idx] = 0;
no_curr[idx] = 0;
volt_set_ok[idx] = 0;
volt_stable_count[idx] = 0;
}
chrg_sink_work(idx,pSW);
}
+2 -2
View File
@@ -22,11 +22,11 @@
#define LEN_FRAME_HEAD (4) // [帧长, 0x00, id, 命令]
#define MIN_FRAME_LENGTH (5) // 帧最短5字节
#define WORK_MIN_VOLT 3000 // 工作电压下限,单位mV
#define NORTH_FRAME_DEBUG 0x01 // 北向UART原始收发帧
#define NORTH_FRAME_DEBUG 0x00 // 北向UART原始收发帧
#define NORTH_SOURCE_DEBUG 0x00 // Source协议解析及组帧
#define NORTH_SINK_DEBUG 0x00 // Sink协议解析及组帧
#define SOUTH_FRAME_DEBUG 0x00 // 南向UART原始收发帧
#define SOUTH_COMMAND_DEBUG 0x00 // 南向寄存器写命令
#define SOUTH_COMMAND_DEBUG 0x01 // 南向寄存器写命令
#define COMM_FRAME_DEBUG 0x00 // COM UART原始收发帧
#define COMM_DEBUG 0x00 // COM寄存器读写过程
#define COMM_TRIM_DEBUG 0x00 // COM校准参数解析
+107 -10
View File
@@ -17,6 +17,7 @@
#include "chrg_sink.h"
#include "chrg_source.h"
#include "chrg_roll_nor.h"
#include "chrg_roll_sou.h"
#include "chrg_north.h"
#include "chrg_switch.h"
#include "chrg_utils.h"
@@ -32,6 +33,7 @@
#define SOURCE_CC_CLOSE_VALUE 0x03U // 0x17命令参数:同时断开CC1和CC2
#define SOURCE_CC_CLOSE_WAIT_MS 300U // 本轮所有CC_CLOSE应答后的共享保护时间
#define SOURCE_PROTOCOL_WAIT_MS 100U // 每通道0xD0应答后的协议生效保护时间
#define SINK_CC_STABLE_WAIT_MS 50U // Sink设置目标CC应答后的非阻塞稳定时间
#define SOURCE_CHANNEL_BIT(ch) ((rt_uint8_t)(1U << (ch)))
const rt_base_t sw_uart[TOTAL_NOR_CHS] = {
IDX_GPO_UART_SW1,
@@ -54,6 +56,8 @@ static rt_bool_t g_source_cc_close_wait_active = RT_FALSE;
static rt_tick_t g_source_cc_close_wait_start = 0U;
static rt_uint8_t g_source_protocol_wait_mask = 0U;
static rt_tick_t g_source_protocol_wait_start[TOTAL_NOR_CHS] = {0};
static rt_uint8_t g_sink_cc_wait_mask = 0U;
static rt_tick_t g_sink_cc_wait_start[TOTAL_NOR_CHS] = {0};
/*****************************************************************
函数名称: chrg_roll_nor_source_switch_reset
@@ -196,6 +200,78 @@ static rt_bool_t chrg_roll_nor_source_wait_process(rt_uint8_t ch,
}
return RT_FALSE;
}
/*****************************************************************
函数名称: chrg_roll_nor_sink_cc_wait_start
函数描述: Sink目标CC设置收到有效应答后启动该通道的稳定计时
输入参数: ch:北向通道编号
输出参数: 记录通道等待标志和起始tick
返回说明: -
其它说明: 每个通道独立计时,多个通道切换CC时等待时间不会串行累加
*****************************************************************/
static void chrg_roll_nor_sink_cc_wait_start(rt_uint8_t ch)
{
if (ch >= TOTAL_NOR_CHS) {
return;
}
g_sink_cc_wait_mask |= SOURCE_CHANNEL_BIT(ch);
g_sink_cc_wait_start[ch] = rt_tick_get();
}
/*****************************************************************
函数名称: chrg_roll_nor_sink_wait_process
函数描述: 管理Sink副继电器确认、安全卸载和CC设置后的稳定等待
输入参数: ch:北向通道编号 pSW:通道实时控制结构体
输出参数: 南向卸载完成后允许发送协议;CC稳定时间到达后切换为电压查询
返回说明: RT_TRUE:本轮仍需等待,不发送北向命令 RT_FALSE:可继续发送
其它说明: INIT只等待第一条FU_REL_ON完成;其余初始化命令入队后与北向
握手并行。软卸载和完整重握手仍必须完成低电流确认后才放行
*****************************************************************/
static rt_bool_t chrg_roll_nor_sink_wait_process(rt_uint8_t ch,
struct chrg_switch_t *pSW)
{
rt_uint8_t bit;
rt_tick_t elapsed;
if ((ch >= TOTAL_NOR_CHS) || (pSW == RT_NULL)) {
return RT_FALSE;
}
bit = SOURCE_CHANNEL_BIT(ch);
if ((pSW->id != ID_SINK) || !pSW->sink.On_work) {
g_sink_cc_wait_mask &= (rt_uint8_t)~bit;
g_sink_cc_wait_start[ch] = 0U;
return RT_FALSE;
}
/* 副继电器或安全卸载尚未确认时,禁止任何北向设置和电压查询。 */
if ((pSW->sink.work_mode == SINK_WORK_INIT_AUX_WAIT) ||
(pSW->sink.work_mode == SINK_WORK_UNLOAD_WAIT) ||
(pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT) ||
(pSW->sink.work_mode == SINK_WORK_HANDSHAKE_WAIT)) {
return RT_TRUE;
}
if (pSW->sub != IDX_WAIT_SINK_CC) {
/* COM在等待期间提交新配置时,清理旧事务留下的CC计时记录。 */
g_sink_cc_wait_mask &= (rt_uint8_t)~bit;
g_sink_cc_wait_start[ch] = 0U;
return RT_FALSE;
}
if ((g_sink_cc_wait_mask & bit) == 0U) {
pSW->sub = IDX_GET_SINK_VC;
return RT_FALSE;
}
elapsed = (rt_tick_t)(rt_tick_get() - g_sink_cc_wait_start[ch]);
if (elapsed < rt_tick_from_millisecond(SINK_CC_STABLE_WAIT_MS)) {
return RT_TRUE;
}
g_sink_cc_wait_mask &= (rt_uint8_t)~bit;
g_sink_cc_wait_start[ch] = 0U;
pSW->sub = IDX_GET_SINK_VC;
return RT_FALSE;
}
/*****************************************************************
函数名称: sink_fill_pd_buf
函数描述: 根据快充协议类型填充PD指令的5字节数据缓冲区
@@ -422,6 +498,7 @@ static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW)
case IDX_GET_SINK_PD:
return SINK_CMD_GET_PD;
case IDX_SET_SINK_PD:
case IDX_SET_SINK_PD_ONLY:
return pSW->sink.protocol;
case IDX_SET_SINK_CCLINE:
return SINK_CMD_SET_CC;
@@ -588,7 +665,7 @@ static int chrg_roll_nor_cmd (struct chrg_rollnor_t *pROLL)
返回说明: >0:发送长度 <0:发送失败或状态不支持
其它说明: Source按差异执行协议、CC或组合命令;涉及PD的运行中切换先发送CC_CLOSE;
停止状态启动和主动停止均不额外发送CC_CLOSE;
Sink依次执行CC线选择、协议设置、VC查询
Sink依次执行协议设置、目标CC线选择、稳定等待和VC查询
*****************************************************************/
static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,
rt_uint8_t *next_sub)
@@ -630,11 +707,14 @@ static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,
*next_sub = IDX_GET_SINK_VC;
return sink_get_pd();
case IDX_SET_SINK_CCLINE: //CC线选择
*next_sub = IDX_SET_SINK_PD;
*next_sub = IDX_WAIT_SINK_CC;
return sink_set_cc_line(pSW->sink.cc_set);
case IDX_SET_SINK_CCLVL: //CC电平
return sink_set_cc_lvl(pSW->sink.cc_lvl);
case IDX_SET_SINK_PD:
*next_sub = IDX_SET_SINK_CCLINE;
return chrg_roll_nor_sink_pro_set(pSW);
case IDX_SET_SINK_PD_ONLY:
*next_sub = IDX_GET_SINK_VC;
return chrg_roll_nor_sink_pro_set(pSW);
default:
@@ -731,8 +811,16 @@ static int chrg_roll_nor_auto_request(rt_uint8_t idx,
(request_sub == IDX_SET_SOURCE_PD)) {
/* 0xD0应答后每通道独立等待100ms,再连接目标CC。 */
chrg_roll_nor_source_protocol_wait_start(idx);
} else if ((expected_id == ID_SINK) &&
(request_sub == IDX_SET_SINK_CCLINE)) {
/* 北向Sink继电器保持接通,仅等待目标CC配置稳定。 */
chrg_roll_nor_sink_cc_wait_start(idx);
}
/* 业务解析可能主动切换sub,例如掉电恢复后要求重新设置协议。
* 只有sub仍属于本次请求时,才应用请求函数计算出的next_sub。 */
if (pSW->sub == request_sub) {
pSW->sub = next_sub;
}
pSW->sub = next_sub;
rt_mutex_release(pTHR->mutex);
chrg_roll_nor_free_response(pMB);
return 0;
@@ -770,7 +858,8 @@ void chrg_roll_nor_thread_entry (void *data)
rt_uint8_t index = 0, idx = 0, i = 0;
rt_uint8_t run = 0;
rt_bool_t source_waiting = RT_FALSE;
int source_work_ret = 0;
rt_bool_t sink_waiting = RT_FALSE;
int state_work_ret = 0;
rt_err_t ret = RT_EOK;
struct chrg_rollnor_t *pROLL = &chrgrollnor;
struct chrg_thread_t *pTN = &chrgthr[IDX_THR_NORTH];
@@ -790,7 +879,8 @@ void chrg_roll_nor_thread_entry (void *data)
idx = index%TOTAL_NOR_CHS;
pSW = &pNOR->sw[idx];
source_waiting = RT_FALSE;
source_work_ret = 0;
sink_waiting = RT_FALSE;
state_work_ret = 0;
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
/*
* INIT/STOP必须在enable过滤前执行:STOP会把enable清零,而COM
@@ -802,24 +892,31 @@ void chrg_roll_nor_thread_entry (void *data)
((pSW->source.work_mode == SOURCE_WORK_INIT) ||
(pSW->source.work_mode == SOURCE_WORK_STOP) ||
(pSW->source.work_mode == SOURCE_WORK_PROTOCOL_SWITCH))) {
source_work_ret = chrg_source_work_state(idx, pSW);
state_work_ret = chrg_source_work_state(idx, pSW);
} else if ((pSW->id == ID_SINK) &&
(pSW->sink.work_mode == SINK_WORK_INIT)) {
chrg_sink_work((eIDX_SOU_CH)idx, pSW);
((pSW->sink.work_mode == SINK_WORK_INIT) ||
(pSW->sink.work_mode == SINK_WORK_INIT_AUX_WAIT) ||
(pSW->sink.work_mode == SINK_WORK_UNLOAD_WAIT) ||
(pSW->sink.work_mode == SINK_WORK_LOW) ||
(pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT) ||
(pSW->sink.work_mode == SINK_WORK_HANDSHAKE_PREPARE) ||
(pSW->sink.work_mode == SINK_WORK_HANDSHAKE_WAIT))) {
state_work_ret = chrg_sink_work((eIDX_SOU_CH)idx, pSW);
}
/*
* 协议切换等待只推进时间状态,不发送串口命令。该检查位于enable
* 过滤前,使停止或角色切换后的通道也能及时清理残留事务记录。
*/
source_waiting = chrg_roll_nor_source_wait_process(idx, pSW);
sink_waiting = chrg_roll_nor_sink_wait_process(idx, pSW);
rt_mutex_release(pTHR->mutex);
if (source_work_ret != 0) {
if (state_work_ret != 0) {
/* 南向状态动作未成功入队时保留原状态,下轮重试且禁止北向先行。 */
index++;
rt_thread_mdelay(2);
continue;
}
if (source_waiting) {
if (source_waiting || sink_waiting) {
index++;
rt_thread_mdelay(2);
continue;
@@ -46,6 +46,10 @@ enum {
IDX_SET_SOURCE_CC_CLOSE, // 协议切换前发送0x17=0x03断开CC
IDX_WAIT_SOURCE_CC_CLOSE, // 等待参与切换的Source通道完成共享保护时间
IDX_WAIT_SOURCE_PROTOCOL, // 0xD0应答后的协议生效等待
/* Sink北向事务状态追加在末尾,避免改变已有sub数值。 */
IDX_SET_SINK_PD_ONLY, // 仅重发协议,协议应答后直接查询协商电压
IDX_WAIT_SINK_CC, // CC设置应答后的非阻塞稳定等待
};
struct chrg_rollnor_t {
+181 -2
View File
@@ -25,8 +25,11 @@
#define SOU_MUTEX_TIMEOUT_MS 100
#define SOU_RESPONSE_TIMEOUT_MS 20
#define SOU_COM_MAX_ATTEMPTS 3
/* #define SOU_REL_SWITCH_DELAY_MS 150U */ // 暂时关闭主副继电器切换延时
#define SOU_CHANNEL_BIT(ch) ((rt_uint8_t)(1U << (ch)))
chrg_rollsou_com_t g_sou_com_batch = {0};
static rt_uint8_t g_sou_measure_once_mask = 0U;
struct chrg_rollsou_t chrgrollsou = {
.run = RUN_AUTO,
@@ -96,6 +99,7 @@ int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs,
pBatch->pending = RT_FALSE;
pBatch->cur_idx = 0;
pBatch->reg_cnt = 0;
pBatch->next_send_tick = 0U;
}
/*
@@ -139,18 +143,141 @@ int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs,
rt_mutex_release(pTHR->mutex);
return -2;
}
if (pBatch->reg_cnt == 0U) {
pBatch->next_send_tick = 0U;
}
for (i = 0; i < count; i++) {
pBatch->reg_list[pBatch->reg_cnt] = regs[i];
pBatch->val_list[pBatch->reg_cnt] = vals[i];
pBatch->reg_cnt++;
}
pBatch->result = 0;
pBatch->pending = RT_TRUE;
pROLL->run = RUN_COM;
rt_mutex_release(pTHR->mutex);
return 0;
}
/*****************************************************************
函数名称: chrg_sou_com_batch_is_pending
函数描述: 查询指定通道是否仍有COM南向批量命令等待执行或等待有效应答
输入参数: ch:南向功率板通道
输出参数: -
返回说明: RT_TRUE:批量事务未完成或查询互斥锁失败 RT_FALSE:批量事务已完成
其它说明: 查询失败时按未完成处理,防止北向协议配置越过南向卸载命令提前执行
*****************************************************************/
rt_bool_t chrg_sou_com_batch_is_pending(eIDX_SOU_CH ch)
{
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
rt_bool_t pending;
if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL) ||
(rt_mutex_take(pTHR->mutex,
rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) {
return RT_TRUE;
}
pending = g_sou_com_batch.ch[ch].pending;
rt_mutex_release(pTHR->mutex);
return pending;
}
/*****************************************************************
函数名称: chrg_sou_com_batch_get_result
函数描述: 获取指定通道最近一次南向COM批次的执行结果
输入参数: ch:南向功率板通道
输出参数: -
返回说明: 1:全部命令收到匹配回码 0:执行中/无结果 -1:失败取消 -2:参数或锁错误
其它说明: pending清零既可能是成功也可能是失败,卸载确认必须通过本结果区分
*****************************************************************/
int chrg_sou_com_batch_get_result(eIDX_SOU_CH ch)
{
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
int result;
if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL) ||
(rt_mutex_take(pTHR->mutex,
rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) {
return -2;
}
result = g_sou_com_batch.ch[ch].result;
rt_mutex_release(pTHR->mutex);
return result;
}
/*****************************************************************
函数名称: chrg_sou_measure_once_submit
函数描述: 登记指定通道的一次性P1量测优先请求
输入参数: ch:南向功率板通道
输出参数: 设置通道请求位
返回说明: 0:登记成功或请求已存在 <0:参数或互斥锁错误
其它说明: RUN_COM批次完成并切回RUN_AUTO后,南向线程会优先执行该查询
*****************************************************************/
int chrg_sou_measure_once_submit(eIDX_SOU_CH ch)
{
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL) ||
(rt_mutex_take(pTHR->mutex,
rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) {
return -1;
}
g_sou_measure_once_mask |= SOU_CHANNEL_BIT(ch);
rt_mutex_release(pTHR->mutex);
return 0;
}
/*****************************************************************
函数名称: sou_measure_once_next
函数描述: 获取当前待执行的一次性P1量测通道
输入参数: ch:输出通道指针
输出参数: ch:最低编号的待查询通道
返回说明: 1:存在请求 0:无请求 <0:参数或互斥锁错误
其它说明: 请求位在本次查询完成或超时后清除,查询期间保持置位以防重复排队
*****************************************************************/
static int sou_measure_once_next(rt_uint8_t *ch)
{
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
rt_uint8_t idx;
if ((ch == RT_NULL) || (pTHR->mutex == RT_NULL) ||
(rt_mutex_take(pTHR->mutex,
rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) {
return -1;
}
for (idx = 0; idx < TOTAL_SOU_CHS; idx++) {
if ((g_sou_measure_once_mask & SOU_CHANNEL_BIT(idx)) != 0U) {
*ch = idx;
rt_mutex_release(pTHR->mutex);
return 1;
}
}
rt_mutex_release(pTHR->mutex);
return 0;
}
/*****************************************************************
函数名称: sou_measure_once_finish
函数描述: 结束指定通道的一次性P1量测请求
输入参数: ch:南向功率板通道
输出参数: 清除对应请求位
返回说明: -
其它说明: 有效回码和超时都会结束本次请求;负载状态机按量测序号决定是否重查
*****************************************************************/
static void sou_measure_once_finish(rt_uint8_t ch)
{
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL) ||
(rt_mutex_take(pTHR->mutex,
rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) != RT_EOK)) {
return;
}
g_sou_measure_once_mask &= (rt_uint8_t)~SOU_CHANNEL_BIT(ch);
rt_mutex_release(pTHR->mutex);
}
int chrg_sou_com_batch_add_reg(eIDX_SOU_CH ch, rt_uint16_t reg, rt_uint16_t val)
{
return chrg_sou_com_batch_submit(ch, &reg, &val, 1);
@@ -375,6 +502,8 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val
{
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
struct chrg_rollsou_t *pROLL = &chrgrollsou;
rt_bool_t pending_wait = RT_FALSE;
rt_tick_t now;
rt_uint8_t idx;
if ((RT_NULL == pTHR->mutex) ||
@@ -382,6 +511,7 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val
return -1;
}
now = rt_tick_get();
for (idx = 0; idx < TOTAL_SOU_CHS; idx++) {
sou_com_batch_ch_t *pBatch = &g_sou_com_batch.ch[idx];
if (pBatch->pending != RT_TRUE) {
@@ -389,10 +519,19 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val
}
if ((pBatch->reg_cnt == 0) || (pBatch->cur_idx >= pBatch->reg_cnt)) {
pBatch->pending = RT_FALSE;
pBatch->result = -1;
pBatch->cur_idx = 0;
pBatch->reg_cnt = 0;
pBatch->next_send_tick = 0U;
continue;
}
pending_wait = RT_TRUE;
if ((pBatch->next_send_tick != 0U) &&
((rt_int32_t)(now - pBatch->next_send_tick) < 0)) {
/* 当前通道处于继电器保护时间时,继续查找其它可发送通道。 */
continue;
}
pBatch->next_send_tick = 0U;
*ch = idx;
*reg = pBatch->reg_list[pBatch->cur_idx];
@@ -401,7 +540,9 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val
return 1;
}
pROLL->run = RUN_AUTO;
if (!pending_wait) {
pROLL->run = RUN_AUTO;
}
rt_mutex_release(pTHR->mutex);
return 0;
}
@@ -425,10 +566,22 @@ static int sou_com_batch_complete(rt_uint8_t ch, rt_uint16_t reg, rt_uint16_t va
}
pBatch->cur_idx++;
#if 0
/* 暂时关闭副继电器与主继电器之间的固定延时;需要恢复时重新启用本段。 */
if ((reg == REG_REL_ON) && (value == FU_REL_ON) &&
(pBatch->cur_idx < pBatch->reg_cnt) &&
(pBatch->reg_list[pBatch->cur_idx] == REG_REL_ON) &&
(pBatch->val_list[pBatch->cur_idx] == ALL_REL_ON)) {
pBatch->next_send_tick = rt_tick_get() +
rt_tick_from_millisecond(SOU_REL_SWITCH_DELAY_MS);
}
#endif
if (pBatch->cur_idx >= pBatch->reg_cnt) {
pBatch->pending = RT_FALSE;
pBatch->result = 1;
pBatch->cur_idx = 0;
pBatch->reg_cnt = 0;
pBatch->next_send_tick = 0U;
}
rt_mutex_release(pTHR->mutex);
return 0;
@@ -445,8 +598,10 @@ static int sou_com_batch_cancel(rt_uint8_t ch)
}
pBatch = &g_sou_com_batch.ch[ch];
pBatch->pending = RT_FALSE;
pBatch->result = -1;
pBatch->cur_idx = 0;
pBatch->reg_cnt = 0;
pBatch->next_send_tick = 0U;
rt_mutex_release(pTHR->mutex);
return 0;
}
@@ -546,6 +701,8 @@ void chrg_roll_sou_thread_entry (void *data)
rt_uint16_t single_reg = 0;
rt_uint16_t single_value = 0;
rt_bool_t command_valid = RT_FALSE;
rt_bool_t measure_once = RT_FALSE;
rt_uint8_t response_part = MB_R_PART1;
rt_err_t ret = RT_EOK;
struct chrg_rollsou_t *pROLL = &chrgrollsou;
struct chrg_thread_t *pTS = &chrgthr[IDX_THR_SOUTH];
@@ -559,13 +716,29 @@ void chrg_roll_sou_thread_entry (void *data)
single_value = pROLL->value;
rt_mutex_release(pTHR->mutex);
command_valid = RT_FALSE;
measure_once = RT_FALSE;
response_part = MB_R_PART1;
if ((run != RUN_COM) && (run != RUN_UPDATA)) {
retry_count = 0;
}
// rt_kprintf("------ROLL SOU START \n--------");
switch (run) {
case RUN_AUTO: {
int measure_ret = sou_measure_once_next(&idx);
if (measure_ret > 0) {
/* 卸载确认优先读取当前通道P1,不等待四通道普通轮询走完。 */
measure_once = RT_TRUE;
response_part = MB_R_PART1;
chrg_led_flashing(IDX_LED3, TIMES_ONE, PULSE_TIME*2, PULSE_TIME*2);
if (idx != (rt_uint8_t)pROLL->idx) {
pROLL->idx = (eIDX_SOU_CH)idx;
chrg_south_switch((eIDX_SOU_CH)idx);
}
chrg_eload_refresh(ID_ELOAD, MB_R_PART1);
break;
}
idx = index%(TOTAL_SOU_CHS); // [0, 3]
response_part = pROLL->part;
if (0 == chrgsouth.enable[idx]) { // 通道未启用
index++;
rt_thread_mdelay(10);
@@ -666,7 +839,7 @@ void chrg_roll_sou_thread_entry (void *data)
} else if (run != RUN_AUTO) {
sou_single_command_finish(run);
} else {
chrg_eload_parse(idx, pROLL->part, pMB_R->payload, pMB_R->length);
chrg_eload_parse(idx, response_part, pMB_R->payload, pMB_R->length);
}
} else if (run == RUN_COM) {
sou_com_command_failed(batch_ch, command_reg, command_value,
@@ -674,10 +847,16 @@ void chrg_roll_sou_thread_entry (void *data)
} else {
LOG_E("crc failed");
}
if ((run == RUN_AUTO) && measure_once) {
sou_measure_once_finish(idx);
}
sou_msg_free(&pMB_R);
} else {
sou_msg_free(&pMB_R);
if ((run == RUN_AUTO) && measure_once) {
sou_measure_once_finish(idx);
}
if ((run == RUN_COM) && command_valid) {
sou_com_command_failed(batch_ch, command_reg, command_value,
"timeout", &retry_count);
@@ -26,6 +26,8 @@ typedef struct
rt_uint16_t val_list[SOU_COM_BATCH_REG_MAX]; // 对应寄存器值数组
rt_uint8_t cur_idx; // 当前下发到第几条
rt_bool_t pending; // 本通道有待下发批量指令
rt_int8_t result; // 0:执行中/无结果 1:成功 -1:失败取消
rt_tick_t next_send_tick; // 非0时下一条命令不得早于该tick发送
} sou_com_batch_ch_t;
// COM批量下发总控结构体
@@ -97,6 +99,25 @@ extern int chrg_send_sou_upadata_data(const rt_uint8_t *buf, rt_uint16_t leng);
extern int chrg_sou_com_batch_add_reg(eIDX_SOU_CH ch, rt_uint16_t reg, rt_uint16_t val);
extern int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs,
const rt_uint16_t *vals, rt_uint8_t count);
extern rt_bool_t chrg_sou_com_batch_is_pending(eIDX_SOU_CH ch);
/*****************************************************************
函数名称: chrg_sou_com_batch_get_result
函数描述: 获取指定通道最近一次南向COM批次的执行结果
输入参数: ch:南向功率板通道
输出参数: -
返回说明: 1:全部命令收到匹配回码 0:执行中/无结果 -1:失败取消 -2:参数或锁错误
其它说明: 用于区分pending清零是正常完成还是重试耗尽后的失败取消
*****************************************************************/
extern int chrg_sou_com_batch_get_result(eIDX_SOU_CH ch);
/*****************************************************************
函数名称: chrg_sou_measure_once_submit
函数描述: 请求南向线程优先查询指定通道的一帧P1量测数据
输入参数: ch:南向功率板通道
输出参数: -
返回说明: 0:请求成功或已存在同通道请求 <0:参数或互斥锁错误
其它说明: 只登记一次性请求,实际切通道、发送0x03和解析仍由南向线程完成
*****************************************************************/
extern int chrg_sou_measure_once_submit(eIDX_SOU_CH ch);
/*****************************************************************
函数名称: chrg_sou_trim_submit
函数描述: 根据校准类型组装功率板校准指令并提交到南向COM队列
+21
View File
@@ -22,6 +22,25 @@
#define ELOAD_VALID_VOLTAGE_MIN_MV 2000U
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);
/* 每通道P1量测更新序号;32位读写在STM32F405上为原子访问。 */
static volatile rt_uint32_t g_eload_sample_seq[TOTAL_SOU_CHS] = {0};
/*****************************************************************
函数名称: chrg_eload_get_sample_seq
函数描述: 获取指定南向通道P1量测数据的更新序号
输入参数: idx:南向功率板通道
输出参数: -
返回说明: 当前通道量测序号,通道非法时返回0
其它说明: 卸载确认记录命令下发前序号,只接受序号变化后的新量测
*****************************************************************/
rt_uint32_t chrg_eload_get_sample_seq(rt_uint8_t idx)
{
if (idx >= TOTAL_SOU_CHS) {
return 0;
}
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};
@@ -332,6 +351,8 @@ int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16
}
pSW->Now_voltage = pLOAD->voltage;
pSW->Now_current = pLOAD->current;
/* 电压、电流均更新完成后再发布序号,避免状态机读取到半更新数据。 */
g_eload_sample_seq[idx]++;
#if LCD_OPEN
// for(int i = 0;i<4;i++){
// if(pNOR->sw[i].change_flag == 0x01){
@@ -268,6 +268,15 @@ extern int chrg_eload_refresh (rt_uint8_t addr, rt_uint8_t part);
其它说明: -
*****************************************************************/
extern int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16_t len);
/*****************************************************************
函数名称: chrg_eload_get_sample_seq
函数描述: 获取指定南向通道P1量测数据的更新序号
输入参数: idx:南向功率板通道
输出参数: -
返回说明: 当前通道量测序号,通道非法时返回0
其它说明: 每成功解析一帧P1数据后递增,用于区分控制命令前后的新旧量测
*****************************************************************/
extern rt_uint32_t chrg_eload_get_sample_seq(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);
+335 -33
View File
@@ -18,6 +18,64 @@
#include "chrg_def.h"
#include "chrg_rel.h"
#include "chrg_comm.h"
#include "chrg_eload.h"
#define SINK_UNLOAD_QUERY_INTERVAL_MS 20U // 卸载期间两次优先量测请求的最小间隔
#define SINK_UNLOAD_CURRENT_MAX_MA 100U // 判定负载已安全卸载的最大实际电流
#define SINK_UNLOAD_CONFIRM_COUNT 2U // 连续满足低电流条件的量测帧数
/* 卸载确认只接受停止批次成功后产生的新P1量测。 */
static rt_uint32_t g_sink_unload_sample_seq[TOTAL_SOU_CHS] = {0};
static rt_bool_t g_sink_unload_batch_confirmed[TOTAL_SOU_CHS] = {RT_FALSE};
static rt_tick_t g_sink_unload_query_tick[TOTAL_SOU_CHS] = {0};
static rt_uint8_t g_sink_unload_confirm_count[TOTAL_SOU_CHS] = {0};
/*****************************************************************
函数名称: chrg_sink_unload_confirm_reset
函数描述: 清除指定通道的卸载批次和低电流确认上下文
输入参数: idx:南向功率板通道
输出参数: 清零量测序号、批次确认标志、查询tick和连续确认次数
返回说明: -
其它说明: 每次开始软卸载或完整重握手前调用,禁止沿用旧量测结果
*****************************************************************/
static void chrg_sink_unload_confirm_reset(eIDX_SOU_CH idx)
{
if (idx >= TOTAL_SOU_CHS) {
return;
}
g_sink_unload_sample_seq[idx] = 0U;
g_sink_unload_batch_confirmed[idx] = RT_FALSE;
g_sink_unload_query_tick[idx] = 0U;
g_sink_unload_confirm_count[idx] = 0U;
}
/*****************************************************************
函数名称: chrg_sink_unload_query_submit
函数描述: 按最小间隔提交当前通道的一次性P1量测请求
输入参数: idx:南向功率板通道 force:RT_TRUE立即提交 RT_FALSE按间隔提交
输出参数: 更新本通道最近一次查询tick
返回说明: 0:请求成功、已存在或尚未到查询时间 <0:请求提交失败
其它说明: 电流长期不归零时保留普通轮询窗口,避免持续优先查询阻塞其它通道
*****************************************************************/
static int chrg_sink_unload_query_submit(eIDX_SOU_CH idx, rt_bool_t force)
{
rt_tick_t now;
if (idx >= TOTAL_SOU_CHS) {
return -1;
}
now = rt_tick_get();
if (!force &&
((rt_tick_t)(now - g_sink_unload_query_tick[idx]) <
rt_tick_from_millisecond(SINK_UNLOAD_QUERY_INTERVAL_MS))) {
return 0;
}
if (chrg_sou_measure_once_submit(idx) != 0) {
return -1;
}
g_sink_unload_query_tick[idx] = now;
return 0;
}
/*****************************************************************
函数名称: sink_get_vc
函数描述: sink电压电流查询请求
@@ -130,45 +188,85 @@ int sink_set_pd (rt_uint8_t pd, rt_uint8_t *buf)
/*****************************************************************
函数名称: chrg_sink_work_init
函数描述: 执行负载INIT状态,复位测试配置并初始化功率板
函数描述: 执行负载INIT入口,第一条南向命令只打开副继电器
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 设置运行、使能、在线标志并更新work_mode
返回说明: 0:初始化成功 <0:命令提交失败
其它说明: 成功后打开负载北向继电器并进入SINK_WORK_WAIT
输出参数: 禁止北向请求并进入SINK_WORK_INIT_AUX_WAIT
返回说明: 0:副继电器命令提交成功 <0:参数或命令提交失败
其它说明: FU_REL_ON同时保证主继电器关闭;收到有效回码前不得启动北向握手
*****************************************************************/
static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
struct chrg_north_t *pNOR = &chrgnorth;
rt_uint16_t reg = REG_REL_ON;
rt_uint16_t value = FU_REL_ON;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (chrg_sou_com_batch_submit(idx, &reg, &value, 1U) != 0) {
return -1;
}
/* 北向软件使能在副继电器回码后恢复,避免协议命令先于电压通路。 */
pSW->sink.On_work = 1;
pNOR->enable[idx] = 0;
pSW->sink.work_mode = SINK_WORK_INIT_AUX_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_init_aux_wait
函数描述: 等待启动副继电器回码,并异步提交其余功率板初始化命令
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 打开北向Sink继电器、恢复北向使能并进入SINK_WORK_WAIT
返回说明: 0:继续等待或初始化命令已入队 <0:参数或命令提交失败
其它说明: 其余初始化命令只要求成功入队,不等待执行完成;北向握手和
南向初始化可并行,后续RUNING命令追加到同一队列并保持顺序
*****************************************************************/
static int chrg_sink_work_init_aux_wait(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_uint8_t count = 3;
rt_uint16_t regs[4] = {REG_CURRENT_OUT + 1, REG_MODE,
Eload_Start_V_ON, 0};
rt_uint16_t vals[4] = {0};
rt_uint8_t count = 3U;
int batch_result;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
vals[1] = pSW->CV_mode;
vals[2] = pSW->sink.volt_on;
if (chrg_sou_com_batch_is_pending(idx)) {
return 0;
}
batch_result = chrg_sou_com_batch_get_result(idx);
if (batch_result <= 0) {
/* 副继电器无有效回码时重新执行INIT,北向继续保持禁止。 */
return chrg_sink_work_init(idx, pSW);
}
if (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) {
regs[count] = ModbusRTU_ShortTest_CMD_MODE_FLAG;
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
} else if (pSW->sink.test.test_mode_old == SINK_TEST_OCP) {
/* 从OCP测试返回普通负载时,先停止功率板的OCP测试状态机。 */
regs[count] = ModbusRTU_OCPTest_CMD_MODE_FLAG;
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
} else if (pSW->sink.test.test_mode_old == SINK_TEST_DYNA) {
regs[count] = ModbusRTU_DYNA_CMD_MODE_FLAG;
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
}
/* Close both power-board relays before starting sink init. */
regs[count] = REG_REL_ON;
vals[count++] = ALL_REL_OFF;
if (chrg_sou_com_batch_submit(idx, regs, vals, count) != 0) {
return -1;
}
pSW->sink.On_work = 1;
/* 初始化命令已按顺序入队,此时即可给协议板接通北向Sink路径。 */
pSOU->online[idx] = 1;
pNOR->enable[idx] = 1;
chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SINK); //打开北向继电器
chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SINK);
pSW->sub = IDX_SET_SINK_PD;
pSW->sink.test.test_mode_old = 0;
pSW->sink.work_mode = SINK_WORK_WAIT;
return 0;
@@ -180,7 +278,8 @@ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 非0:命令提交失败
其它说明: CV模式下写负载电压,其他模式下写负载电流
其它说明: CV模式下写负载电压,其他模式下写负载电流;副继电器0x02
收到有效回码后直接发送全部继电器0x03,当前不增加固定延时
*****************************************************************/
static int chrg_sink_work_volt_runing(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
@@ -197,20 +296,93 @@ static int chrg_sink_work_volt_runing(eIDX_SOU_CH idx, struct chrg_switch_t *pSW
return chrg_sou_com_batch_submit(idx, regs, vals, 4);
}
/*****************************************************************
函数名称: chrg_sink_work_soft_resume
函数描述: 仅协议电压变化完成后恢复负载,不操作南向主副继电器
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 命令入队成功后将工作状态切换为SINK_WORK_SOFT_RUNNING
返回说明: 0:命令提交成功 <0:参数或命令提交失败
其它说明: 命令顺序为目标电流/电压、WORK_START,保证软卸载期间继电器
始终保持原状态,避免恢复阶段因FU_REL_ON造成主继电器短暂断开
*****************************************************************/
static int chrg_sink_work_soft_resume(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t regs[2] = {0, REG_WORK};
rt_uint16_t vals[2] = {0, WORK_START};
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (pSW->CV_mode == MODE_CONSTANT_VOLTAGE) {
regs[0] = REG_VOLTAGE_OUT + 1;
vals[0] = pSW->sink.loadv;
} else {
regs[0] = REG_CURRENT_OUT + 1;
vals[0] = pSW->sink.loadc;
}
if (chrg_sou_com_batch_submit(idx, regs, vals, 2U) != 0) {
return -1;
}
pSW->sink.work_mode = SINK_WORK_SOFT_RUNNING;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_nor_work_low
函数描述: 执行负载低电压状态,断开功率继电器并停止负载
函数描述: 执行负载掉电处理,卸载后关闭主副继电器并重新打开副继电器
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 非0:命令提交失败
其它说明: 由北向负载解析连续检测到低电压后触发
其它说明: 命令顺序为WORK_STOP、CURRENT=0、ALL_REL_OFF、FU_REL_ON;
ALL_REL_OFF后立即恢复副继电器,为协议板重新得电保留通路
*****************************************************************/
static int chrg_sink_nor_work_low(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
rt_uint16_t regs[2] = {0x01, REG_WORK};
rt_uint16_t vals[2] = {0x00, WORK_STOP};
rt_uint16_t regs[4] = {
REG_WORK, REG_CURRENT_OUT + 1, REG_REL_ON, REG_REL_ON
};
rt_uint16_t vals[4] = {
WORK_STOP, 0, ALL_REL_OFF, FU_REL_ON
};
return chrg_sou_com_batch_submit(idx, regs, vals, 2);
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (chrg_sou_com_batch_submit(idx, regs, vals, 4U) != 0) {
return -1;
}
pSW->sink.work_mode = SINK_WORK_LOW_AUX_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_low_aux_wait
函数描述: 等待掉电安全批次和副继电器恢复命令全部收到有效回码
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 成功后进入SINK_WORK_LOW_MONITOR并恢复北向电压查询入口
返回说明: 0:继续等待或状态转换成功 <0:参数错误
其它说明: 批次失败时重新执行完整掉电处理,不允许跳过副继电器确认
*****************************************************************/
static int chrg_sink_work_low_aux_wait(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
int batch_result;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (chrg_sou_com_batch_is_pending(idx)) {
return 0;
}
batch_result = chrg_sou_com_batch_get_result(idx);
if (batch_result <= 0) {
return chrg_sink_nor_work_low(idx, pSW);
}
pSW->sub = IDX_GET_SINK_VC;
pSW->sink.work_mode = SINK_WORK_LOW_MONITOR;
return 0;
}
/*****************************************************************
@@ -270,8 +442,8 @@ static int chrg_sink_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
/* 停止成功入队后,清除旧Source运行标志。 */
pSW->source.On_Flag = 0;
/* 首次进入Sink时必须从CC线配置开始,不能沿用Source的sub状态。 */
pSW->sub = IDX_SET_SINK_CCLINE;
/* 首次进入Sink时从协议配置开始,协议应答后再设置目标CC线。 */
pSW->sub = IDX_SET_SINK_PD;
/* SWITCH只负责旧角色退出,后续启动统一交给Sink INIT状态。 */
pSW->sink.work_mode = SINK_WORK_INIT;
return 0;
@@ -279,26 +451,125 @@ static int chrg_sink_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
/*****************************************************************
函数名称: chrg_sink_work_protocol_unload
函数描述: 协议或CC线变化前的卸载状态:停止负载、电流归零、断开功率继电器
函数描述: 仅协议电压变化前的软卸载:停止负载并将设定电流归零
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 <0:命令提交失败
其它说明: 不操作北向继电器,仅卸载功率板侧;后续由WAIT→北向线程重配
CC/协议,再由RUNING状态闭合继电器并恢复目标负载电流
其它说明: 不操作北向和南向功率继电器;两条写命令完成后进入UNLOAD_WAIT,
连续确认两帧实际电流不大于100mA,才允许北向重配协议并使用软恢复流程
*****************************************************************/
static int chrg_sink_work_protocol_unload(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
rt_uint16_t regs[3] = {REG_WORK, REG_CURRENT_OUT + 1, REG_REL_ON};
rt_uint16_t vals[3] = {WORK_STOP, 0, ALL_REL_OFF};
rt_uint16_t regs[2] = {REG_WORK, REG_CURRENT_OUT + 1};
rt_uint16_t vals[2] = {WORK_STOP, 0};
if (chrg_sou_com_batch_submit(idx, regs, vals, 3) != 0) {
if (chrg_sou_com_batch_submit(idx, regs, vals, 2) != 0) {
return -1;
}
pSW->sink.work_mode = SINK_WORK_WAIT;
/* 写回码只确认命令接收;批次完成后还需连续读取两帧合格的实际电流。 */
chrg_sink_unload_confirm_reset(idx);
pSW->sink.work_mode = SINK_WORK_UNLOAD_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_handshake_prepare
函数描述: 执行协议或CC完整重握手前的南向安全断开和副继电器恢复
输入参数: idx:南向功率板通道 pSW:当前通道控制结构体
输出参数: 命令提交成功后进入SINK_WORK_HANDSHAKE_WAIT
返回说明: 0:四条命令提交成功 <0:参数或命令提交失败
其它说明: 命令顺序固定为WORK_STOP、CURRENT=0、ALL_REL_OFF、FU_REL_ON;
主副继电器全部断开并确认回码后再单独打开副继电器;
北向Sink继电器保持接通,低电流确认完成后才允许重新握手
*****************************************************************/
static int chrg_sink_work_handshake_prepare(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t regs[4] = {
REG_WORK, REG_CURRENT_OUT + 1, REG_REL_ON, REG_REL_ON
};
rt_uint16_t vals[4] = {
WORK_STOP, 0, ALL_REL_OFF, FU_REL_ON
};
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (chrg_sou_com_batch_submit(idx, regs, vals, 4) != 0) {
return -1;
}
/* 四条写回码完成后,还必须连续确认两帧实际电流不大于100mA。 */
chrg_sink_unload_confirm_reset(idx);
pSW->sink.work_mode = SINK_WORK_HANDSHAKE_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_current_wait
函数描述: 等待负载安全断开批次完成,并连续确认两帧实际电流不大于100mA
输入参数: idx:南向功率板通道 pSW:当前通道控制结构体
full_handshake:RT_TRUE为完整重握手 RT_FALSE为仅协议电压软卸载
输出参数: 确认成功后将sink.work_mode切换为SINK_WORK_WAIT
返回说明: 0:继续等待或确认成功 <0:状态查询或优先量测请求失败
其它说明: 只有WORK_STOP和CURRENT=0均收到匹配回码后才建立量测基线;
完整重握手还要求ALL_REL_OFF和FU_REL_ON回码成功,但保持北向Sink继电器接通
*****************************************************************/
static int chrg_sink_work_current_wait(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, rt_bool_t full_handshake)
{
rt_uint32_t sample_seq;
int batch_result;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (chrg_sou_com_batch_is_pending(idx)) {
return 0;
}
batch_result = chrg_sou_com_batch_get_result(idx);
if (batch_result < 0) {
/* 写命令失败时重新执行完整卸载批次,禁止使用量测值提前放行。 */
return full_handshake ? chrg_sink_work_handshake_prepare(idx, pSW) :
chrg_sink_work_protocol_unload(idx, pSW);
}
if (batch_result == 0) {
/* 无有效批次结果时保持阻塞,并重新提交完整卸载动作。 */
return full_handshake ? chrg_sink_work_handshake_prepare(idx, pSW) :
chrg_sink_work_protocol_unload(idx, pSW);
}
if (!g_sink_unload_batch_confirmed[idx]) {
/* 从最后一条写回码之后建立基线,排除RUN_COM之前残留的旧量测。 */
g_sink_unload_sample_seq[idx] = chrg_eload_get_sample_seq(idx);
g_sink_unload_batch_confirmed[idx] = RT_TRUE;
return chrg_sink_unload_query_submit(idx, RT_TRUE);
}
sample_seq = chrg_eload_get_sample_seq(idx);
if (sample_seq != g_sink_unload_sample_seq[idx]) {
g_sink_unload_sample_seq[idx] = sample_seq;
if (pSW->Now_current <= SINK_UNLOAD_CURRENT_MAX_MA) {
g_sink_unload_confirm_count[idx]++;
if (g_sink_unload_confirm_count[idx] >= SINK_UNLOAD_CONFIRM_COUNT) {
g_sink_unload_batch_confirmed[idx] = RT_FALSE;
g_sink_unload_confirm_count[idx] = 0U;
pSW->sink.work_mode = full_handshake ?
SINK_WORK_WAIT : SINK_WORK_SOFT_RESUME_WAIT;
return 0;
}
} else {
/* 必须是连续两帧合格量测;中间出现带载电流时重新计数。 */
g_sink_unload_confirm_count[idx] = 0U;
}
}
/* 当前帧仍有电流或查询未返回时,请求下一帧;接口按位登记,不会重复排队。 */
return chrg_sink_unload_query_submit(idx, RT_FALSE);
}
/*****************************************************************
函数名称: chrg_sink_TEST_DYNA_ON
函数描述: 配置并启动动态负载测试
@@ -399,14 +670,21 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
return -1;
}
work_mode = pSW->sink.work_mode;
// 本次状态和上次一致,直接跳过,不重复下发寄存器
if (work_mode == last_state[idx]) {
/* 异步等待状态需要持续检查批次或新量测,其余相同状态保持去重。 */
if ((work_mode == last_state[idx]) &&
(work_mode != SINK_WORK_INIT_AUX_WAIT) &&
(work_mode != SINK_WORK_UNLOAD_WAIT) &&
(work_mode != SINK_WORK_LOW_AUX_WAIT) &&
(work_mode != SINK_WORK_HANDSHAKE_WAIT)) {
return 0;
}
switch(work_mode){
case SINK_WORK_INIT:
ret = chrg_sink_work_init(idx,pSW);
break;
case SINK_WORK_INIT_AUX_WAIT:
ret = chrg_sink_work_init_aux_wait(idx, pSW);
break;
case SINK_WORK_WAIT:
ret = 0;
break;
@@ -425,6 +703,12 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
case SINK_WORK_LOW:
ret = chrg_sink_nor_work_low(idx,pSW);
break;
case SINK_WORK_LOW_AUX_WAIT:
ret = chrg_sink_work_low_aux_wait(idx, pSW);
break;
case SINK_WORK_LOW_MONITOR:
ret = 0;
break;
case SINK_WORK_STOP:
ret = chrg_sink_nor_work_off(idx,pSW);
break;
@@ -434,6 +718,24 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
case SINK_WORK_PROTOCOL_UNLOAD:
ret = chrg_sink_work_protocol_unload(idx, pSW);
break;
case SINK_WORK_UNLOAD_WAIT:
ret = chrg_sink_work_current_wait(idx, pSW, RT_FALSE);
break;
case SINK_WORK_SOFT_RESUME_WAIT:
ret = 0;
break;
case SINK_WORK_SOFT_RESUME:
ret = chrg_sink_work_soft_resume(idx, pSW);
break;
case SINK_WORK_SOFT_RUNNING:
ret = 0;
break;
case SINK_WORK_HANDSHAKE_PREPARE:
ret = chrg_sink_work_handshake_prepare(idx, pSW);
break;
case SINK_WORK_HANDSHAKE_WAIT:
ret = chrg_sink_work_current_wait(idx, pSW, RT_TRUE);
break;
default:
break;
}
+20 -11
View File
@@ -202,17 +202,26 @@ enum {
#define SINK_CMD_SET_AVS 0xE3 // AVS
enum {
SINK_WORK_INIT =0,
SINK_WORK_WAIT,
SINK_WORK_VOLT_OK,
SINK_WORK_RUNING,
SINK_WORK_LOW,
SINK_WORK_STOP,
SINK_WORK_SWITCH, // 停止旧电源后进入负载INIT
SINK_WORK_PROTOCOL_UNLOAD, // 协议/CC变化前卸载电流、停止负载、断开功率继电器
SINK_WORK_ON_TEST_DYNA,
SINK_WORK_ON_TEST_SHORT,
SINK_WORK_ON_TEST_OCP, // 配置并启动OCP过流保护测试
SINK_WORK_INIT = 0, // 启动入口:第一条南向命令只打开副继电器
SINK_WORK_INIT_AUX_WAIT, // 等待启动副继电器收到有效南向回码
SINK_WORK_WAIT, // 北向协议设置或电压查询等待状态
SINK_WORK_VOLT_OK, // 预留的电压达标状态
SINK_WORK_RUNING, // 电压稳定后提交继电器、电流和启动命令
SINK_WORK_LOW, // 运行电压过低,执行掉电安全处理
SINK_WORK_LOW_AUX_WAIT, // 等待掉电处理及副继电器恢复批次完成
SINK_WORK_LOW_MONITOR, // 副继电器已恢复,等待输入电压重新出现
SINK_WORK_STOP, // 用户主动停止负载
SINK_WORK_SWITCH, // 停止旧电源后进入负载INIT
SINK_WORK_PROTOCOL_UNLOAD, // 仅协议电压变化:掉载但不切换继电器
SINK_WORK_UNLOAD_WAIT, // 等待软卸载完成并连续两次确认电流不大于100mA
SINK_WORK_SOFT_RESUME_WAIT, // 继电器保持闭合,等待协议电压稳定后恢复负载
SINK_WORK_SOFT_RESUME, // 只恢复目标电流/电压和WORK_START,不写继电器
SINK_WORK_SOFT_RUNNING, // 软恢复后的稳定运行状态,禁止重复切换继电器
SINK_WORK_ON_TEST_DYNA, // 动态负载测试启动状态
SINK_WORK_ON_TEST_SHORT, // 短路测试启动状态
SINK_WORK_ON_TEST_OCP, // OCP过流保护测试启动状态
SINK_WORK_HANDSHAKE_PREPARE, // 完整重握手:停止、清流、全关继电器后打开副继电器
SINK_WORK_HANDSHAKE_WAIT, // 等待完整重握手批次及连续两次低电流确认
};
// sink 参数结构
+13 -1
View File
@@ -1,8 +1,20 @@
*** Using Compiler 'V6.16', folder: 'D:\keil mdk 2\ARM\ARMCLANG\Bin'
Build target 'chrg'
compiling chrg_north_pkg.c...
compiling chrg_lcd.c...
compiling chrg_thread.c...
compiling chrg_sink.c...
compiling chrg_vcom.c...
compiling chrg_rel.c...
compiling chrg_eload.c...
compiling chrg_south.c...
compiling chrg_roll_sou.c...
compiling chrg_north.c...
compiling chrg_source.c...
compiling chrg_roll_nor.c...
compiling chrg_comm.c...
linking...
Program Size: Code=144648 RO-data=15288 RW-data=9328 ZI-data=15824
Program Size: Code=145588 RO-data=15276 RW-data=9328 ZI-data=15944
FromELF: creating hex file...
After Build - User command #1: fromelf --bin .\build\keil\Obj\chrg.axf -o build/chrg.bin
".\build\keil\Obj\chrg.axf" - 0 Error(s), 0 Warning(s).
+47
View File
@@ -0,0 +1,47 @@
# 修改日志
## V1.1.0 - 2026-08-07
### 修改主题
负载启动、掉电恢复、协议重握手和协议电压软切换时序调整。
### 主要修改
- 负载启动时先向南向发送 `FU_REL_ON`,收到有效回码后才打开北向 Sink 继电器。
- 剩余的负载初始化命令在副继电器确认后入队,北向协议握手与南向初始化并行执行。
- `RUNING` 阶段的南向命令继续追加到同一队列,保持初始化配置、继电器、目标电流/电压和 `WORK_START` 的顺序。
- 掉电恢复时执行 `WORK_STOP -> CURRENT=0 -> ALL_REL_OFF -> FU_REL_ON`,副继电器恢复后等待输入电压重新出现。
- 掉电恢复检测到电压后重新设置协议和 CC,不能使用掉电前的电压样本直接进入 `RUNING`。
- 协议类型、CC、协议组号或协议电流变化进入完整重握手,关闭主副继电器后重新打开副继电器。
- 仅协议电压变化使用软卸载,只发送 `WORK_STOP` 和 `CURRENT=0`,不切换继电器。
- 软卸载恢复时只发送目标电流/电压和 `WORK_START`,不再发送继电器命令。
- 保留连续两次实际电流不大于 `100mA` 的安全确认,以及连续两次稳定电压确认。
- 暂时保持南向主副继电器之间的固定延时为屏蔽状态。
### 涉及文件
- `apps/chrg/applications/utils/chrg_sink.h`
- `apps/chrg/applications/utils/chrg_sink.c`
- `apps/chrg/applications/thread/chrg_comm.c`
- `apps/chrg/applications/thread/chrg_roll_nor.c`
- `apps/chrg/applications/thread/chrg_north.c`
### 编译验证
- Keil MDK 编译结果:`0 Error(s), 0 Warning(s)`。
- 尚未完成硬件验证,需重点观察南向命令顺序、北向协议握手时刻、掉电恢复和协议电压软切换。
### 后续记录格式
后续每次修改按以下格式追加:
```text
## Vx.y.z - YYYY-MM-DD
### 修改主题
### 主要修改
### 涉及文件
### 编译验证
### 硬件验证
```