Files
chrg/apps/chrg/applications/utils/chrg_sink.c
T
wrh b58b47f64a chrg: 完成V1.1.31版本迭代更新
1.  更新各文件版本号至V1.1.31,固件版本寄存器设为0x012F
2.  南向通道选通:仅在GPIO选通成功后才更新物理通道缓存
3.  重构寄存器地址解码逻辑,统一0x06/0x10功能码的地址校验
4.  新增功率板短路/OCP/动态测试状态定义0x05/0x06/0x07
5.  移除电流命令覆盖逻辑,改为按顺序排队等待回码
6.  优化轮询线程:每次发送前重新选通通道,选通失败则跳过当前任务
7.  新增测试态退出确认流程,初始化和关机时需确认功率板退出测试态
8.  增加跨通道写入保护,拒绝多寄存器写跨通道操作
9.  补充多处中文注释,优化调试日志输出格式
2026-09-17 12:05:37 +08:00

1442 lines
53 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/****************************************************************************
文件名称 : chrg_sink.c
完成日期 :
当前版本号 : V1.1.28
主要功能 : 北向数据sink请求数据填充及发送。
版本历史 : 创建原始版本
说明 :
******************************************************************************/
#include <rtthread.h>
#include "chrg_sink.h"
#include "chrg_north.h"
#include "chrg_north_pkg.h"
#include "chrg_utils.h"
#include "chrg_roll_sou.h"
#include "chrg_roll_nor.h"
#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 200U // 判定负载已安全卸载的最大实际电流
#define SINK_UNLOAD_CONFIRM_COUNT 2U // 连续满足低电流条件的量测帧数
#define SINK_SHORT_KEEPALIVE_MS 30U // 短路测试WORK_START保活下发周期
#define SINK_INIT_NORTH_RELAY_DELAY_MS 30U // 辅助继电器回码后打开北向继电器的等待时间
#define SINK_TEST_STATUS_RETRY_MAX 3U // 测试态退出确认的连续重发计数上限
#define SINK_INIT_STAGE_TEST_STOP 0U
#define SINK_INIT_STAGE_TEST_STATUS 1U
#define SINK_INIT_STAGE_AUX_RELAY 2U
#define SINK_INIT_STAGE_DELAY 3U
#define SINK_INIT_STAGE_REST 4U
static rt_uint8_t g_sink_init_stage[TOTAL_SOU_CHS] = {0};
static rt_tick_t g_sink_init_north_relay_tick[TOTAL_SOU_CHS] = {0};
static rt_bool_t g_sink_init_in_progress[TOTAL_SOU_CHS] = {RT_FALSE};
/* 只有当前阶段入队成功后,才允许消费该阶段的南向批次结果。 */
static rt_bool_t g_sink_init_stage_submitted[TOTAL_SOU_CHS] = {RT_FALSE};
/* 主继电器闭合跟踪:1=已提交ALL_REL_ON(主继电器应已闭合) 0=未闭合/已断开。
* 用于0V硬保护区分"首次开载/握手恢复"(主继电器未闭合,Now_voltage=0属正常)
* 与"运行或待机中主继电器已闭合但输入电压为0"(协议板断电窗口,禁止开载)。 */
static rt_uint8_t g_sink_rel_main_closed[TOTAL_SOU_CHS] = {0};
/* 主继电器电压到位确认:1=提交闭合后实测电压已到达工作下限,继电器确认闭合。
* 持续0V监控只对已到位通道生效,避免继电器动作期间(电压尚未建立)误触发掉电保护。 */
static rt_uint8_t g_sink_rel_main_ready[TOTAL_SOU_CHS] = {0};
/* 卸载确认只接受停止批次成功后产生的新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};
/* 记录各通道短路测试最近一次WORK_START的发送时间。 */
static rt_tick_t g_sink_short_start_tick[TOTAL_SOU_CHS] = {0};
/* 锁存关机前的短路状态,防止关机过程中影子参数覆盖测试状态。 */
static rt_bool_t g_sink_stop_from_short[TOTAL_SOU_CHS] = {RT_FALSE};
/* 只消费新的V_On电压采样,避免同一帧重复计入稳定确认。 */
static rt_uint32_t g_sink_von_voltage_seq[TOTAL_SOU_CHS] = {0};
/* 协议切换期间清零,避免旧电压样本累计成启动确认。 */
static rt_uint8_t g_sink_von_stable_count[TOTAL_SOU_CHS] = {0};
/* 自动停止带载后的恢复确认计数,独立于首次握手的电压确认计数。 */
static rt_uint8_t g_sink_load_restart_confirm_count[TOTAL_SOU_CHS] = {0};
static rt_uint32_t g_sink_init_test_sample_seq[TOTAL_SOU_CHS] = {0};
static rt_uint8_t g_sink_init_test_stop_retry[TOTAL_SOU_CHS] = {0};
static rt_uint32_t g_sink_stop_test_sample_seq[TOTAL_SOU_CHS] = {0};
static rt_uint8_t g_sink_stop_test_stop_retry[TOTAL_SOU_CHS] = {0};
static rt_bool_t g_sink_stop_test_confirmed[TOTAL_SOU_CHS] = {RT_FALSE};
static rt_uint8_t chrg_sink_power_status_get(eIDX_SOU_CH idx)
{
rt_uint16_t status1;
if (idx >= TOTAL_SOU_CHS) {
return 0U;
}
status1 = chrgsouth.eload[idx].status1;
if (((status1 & 0x00FFU) == 0U) && ((status1 >> 8) != 0U)) {
status1 >>= 8;
}
return (rt_uint8_t)status1;
}
static rt_bool_t chrg_sink_power_test_active(eIDX_SOU_CH idx)
{
rt_uint8_t status1 = chrg_sink_power_status_get(idx);
return (status1 == STA1_SHORT_TEST) ||
(status1 == STA1_OCP_TEST) ||
(status1 == STA1_DYNA_TEST);
}
static rt_bool_t chrg_sink_test_exit_required(eIDX_SOU_CH idx,
const struct chrg_switch_t *pSW)
{
return chrg_sink_power_test_active(idx) ||
((pSW != RT_NULL) &&
((pSW->sink.test.test_mode != SINK_NO_TEST) ||
(pSW->sink.test.test_mode_old != SINK_NO_TEST)));
}
enum {
SINK_UNLOAD_NEXT_RUNNING = 0,
SINK_UNLOAD_NEXT_HANDSHAKE,
SINK_UNLOAD_NEXT_STOP_RELAY
};
/*****************************************************************
函数名称: chrg_sink_load_should_start
函数描述: 判断普通负载的设定值是否允许启动南向电子负载
输入参数: pSW:当前通道控制结构体
输出参数: -
返回说明: RT_TRUE:允许WORK_START RT_FALSE:应保持WORK_STOP
其它说明: 门限只作用于恒流模式的负载电流loadc;协议电流pro_loadc无论
大小都继续参与北向握手,恒压模式不受该恒流门限限制
*****************************************************************/
rt_bool_t chrg_sink_load_should_start(const struct chrg_switch_t *pSW)
{
if (pSW == RT_NULL) {
return RT_FALSE;
}
if (pSW->CV_mode != MODE_CONSTANT_CURRENT) {
return RT_TRUE;
}
return (pSW->sink.loadc >= SINK_LOAD_ENABLE_MIN_MA) ?
RT_TRUE : RT_FALSE;
}
/*****************************************************************
函数名称: 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_handshake_north_ready
函数描述: 判断完整握手的南向安全批次是否已经执行完成
输入参数: idx:南向功率板通道
输出参数: -
返回说明: RT_TRUE:辅助继电器命令已完成并已建立低电流量测基线
RT_FALSE:南向安全批次仍在执行
其它说明: 基线建立后返回成功,实际低电流确认仍由当前状态函数继续执行;
用于允许北向协议/CC与南向低电流确认并行推进
*****************************************************************/
/*****************************************************************
函数名称: 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电压电流查询请求
输入参数: vol:电压 cur:电流
输出参数: -
返回说明: <0: 错误 >0:发送长度
其它说明: -
*****************************************************************/
int sink_get_vc (rt_uint16_t vol, rt_uint16_t cur)
{
rt_uint16_t len = 0;
rt_uint8_t data[12] = {0};
rt_uint8_t frame[16] = {0};
u16_to_u8v(vol, &data[0]);
u16_to_u8v(cur, &data[2]);
data[4] = 0x00;
data[5] = 0x00;
chrg_north_pkg_encode(ID_SINK, SINK_CMD_GET_VOLTAGE, data, 6, frame, &len);
return chrg_north_send(frame, len);
}
/*****************************************************************
函数名称: sink_get_pd
函数描述: sink pd 协议查询请求
输入参数: -
输出参数: -
返回说明: <0: 错误 >0:发送长度
其它说明: -
*****************************************************************/
int sink_get_pd (void)
{
rt_uint16_t len = 0;
rt_uint8_t frame[16] = {0};
chrg_north_pkg_encode(ID_SINK, SINK_CMD_GET_PD, RT_NULL, 0, frame, &len);
return chrg_north_send(frame, len);
}
/*****************************************************************
函数名称: sink_set_cc_line
函数描述: sink 设置 cc 线
输入参数: line:线
输出参数: -
返回说明: <0: 错误 >0:发送长度
其它说明: -
*****************************************************************/
int sink_set_cc_line (rt_uint8_t line)
{
rt_uint16_t len = 0;
rt_uint8_t data[8] = {0};
rt_uint8_t frame[16] = {0};
data[0] = line;
chrg_north_pkg_encode(ID_SINK, SINK_CMD_SET_CC, data, 1, frame, &len);
// for(int i = 0; i < len; i++) {
// rt_kprintf("data[%d]=%02x\r\n", i, frame[i]);
// }
return chrg_north_send(frame, len);
}
/*****************************************************************
函数名称: sink_set_cc_lvl
函数描述: sink 设置 cc 等级
输入参数: lvl:等级
输出参数: -
返回说明: <0: 错误 >0:发送长度
其它说明: -
*****************************************************************/
int sink_set_cc_lvl (rt_uint8_t lvl)
{
rt_uint16_t len = 0;
rt_uint8_t data[8] = {0};
rt_uint8_t frame[16] = {0};
data[0] = lvl;
chrg_north_pkg_encode(ID_SINK, SINK_CMD_SET_LEVEL, data, 1, frame, &len);
return chrg_north_send(frame, len);
}
/*****************************************************************
函数名称: sink_set_pd
函数描述: sink 设置 PD 协议
输入参数: pd:协议命令号 *buf:协议数据,5字节
输出参数: -
返回说明: <0: 错误 >0:发送长度
其它说明: -
*****************************************************************/
int sink_set_pd (rt_uint8_t pd, rt_uint8_t *buf)
{
rt_uint16_t len = 0;
rt_uint8_t data[8] = {0};
rt_uint8_t frame[16] = {0};
int i = 0;
for (i = 0; i < 5; i++) {
data[i] = buf[i];
}
chrg_north_pkg_encode(ID_SINK, pd, data, 5, frame, &len);
return chrg_north_send(frame, len);
}
static rt_uint8_t chrg_sink_init_build_commands(struct chrg_switch_t *pSW,
rt_uint16_t *regs, rt_uint16_t *vals)
{
rt_uint8_t count;
regs[0] = REG_WORK;
vals[0] = WORK_STOP;
if (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) {
regs[1] = REG_REL_ON; vals[1] = ALL_REL_OFF;
regs[2] = REG_REL_ON; vals[2] = FU_REL_ON;
regs[3] = REG_CURRENT_OUT + 1; vals[3] = 0;
regs[4] = REG_MODE; vals[4] = pSW->CV_mode;
regs[5] = Eload_Start_V_ON; vals[5] = (pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on;
count = 6U;
} else {
regs[1] = REG_REL_ON; vals[1] = FU_REL_ON;
regs[2] = REG_CURRENT_OUT + 1; vals[2] = 0;
regs[3] = REG_MODE; vals[3] = pSW->CV_mode;
regs[4] = Eload_Start_V_ON; vals[4] =
(pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on;
count = 5U;
if (pSW->sink.test.test_mode_old == SINK_TEST_OCP) {
regs[count] = ModbusRTU_OCPTest_CMD_MODE_FLAG;
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
} 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;
}
}
return count;
}
static int chrg_sink_init_submit_stage(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t regs[6];
rt_uint16_t vals[6];
rt_uint8_t total;
rt_uint8_t start;
rt_uint8_t count;
int ret;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
g_sink_init_stage_submitted[idx] = RT_FALSE;
total = chrg_sink_init_build_commands(pSW, regs, vals);
switch (g_sink_init_stage[idx]) {
case SINK_INIT_STAGE_TEST_STOP:
start = 0U;
count = 1U;
break;
case SINK_INIT_STAGE_AUX_RELAY:
start = 1U;
count = 1U;
break;
case SINK_INIT_STAGE_REST:
start = 2U;
count = (rt_uint8_t)(total - 2U);
break;
default:
return -1;
}
ret = chrg_sou_com_batch_submit(idx, &regs[start], &vals[start], count);
if (ret == 0) {
g_sink_init_stage_submitted[idx] = RT_TRUE;
}
return ret;
}
/*****************************************************************
函数名称: chrg_sink_work_init
函数描述: 执行负载INIT入口,首帧先退出短路测试
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 进入INIT_AUX_WAIT后台等待南向回码
返回说明: 0:初始化命令提交成功 <0:参数或命令提交失败
其它说明: 收到01 02有效回码后等待30ms,再打开北向Sink继电器
*****************************************************************/
static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
/* 初始化必须优先于本通道尚未发送的旧命令,避免旧WORK_START插入初始化流程。 */
if (chrg_sou_com_batch_cancel(idx) != 0) {
return -1;
}
g_sink_init_in_progress[idx] = RT_TRUE;
/* 功率板回读处于Short/OCP/DYNA时,先用WORK_STOP退出测试态。 */
g_sink_init_stage[idx] = chrg_sink_test_exit_required(idx, pSW) ?
SINK_INIT_STAGE_TEST_STOP : SINK_INIT_STAGE_AUX_RELAY;
g_sink_init_test_sample_seq[idx] = 0U;
g_sink_init_test_stop_retry[idx] = 0U;
g_sink_init_north_relay_tick[idx] = 0U;
/* 入队失败也保留事务和等待入口,由等待分支持续重提当前阶段。 */
pSW->sink.On_work = 1;
pSW->sink.work_mode = SINK_WORK_INIT_AUX_WAIT;
return chrg_sink_init_submit_stage(idx, pSW);
}
/*****************************************************************
函数名称: chrg_sink_work_init_aux_wait
函数描述: 后台等待整组南向初始化命令完成,不阻塞北向协议握手
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 初始化完成后进入SINK_WORK_WAIT,失败时重提当前阶段
返回说明: 0:继续等待或初始化完成 <0:参数错误
其它说明: 重试时保留当前北向sub,禁止把正在执行的协议/CC事务拉回起点
*****************************************************************/
static int chrg_sink_work_init_aux_wait(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
struct chrg_north_t *pNOR = &chrgnorth;
struct chrg_south_t *pSOU = &chrgsouth;
int batch_result;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (chrg_sou_com_batch_is_pending(idx)) {
return 0;
}
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_DELAY) {
if ((rt_int32_t)(rt_tick_get() -
g_sink_init_north_relay_tick[idx]) < 0) {
return 0;
}
chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SINK);
#if SOUTH_COMMAND_DEBUG
rt_kprintf("-------D+D- OPEN------\n");
#endif
pNOR->enable[idx] = 1;
pSW->sub = IDX_SET_SINK_CC_BEFORE_PD;
g_sink_init_stage[idx] = SINK_INIT_STAGE_REST;
if (chrg_sink_init_submit_stage(idx, pSW) != 0) {
return -1;
}
return 0;
}
/* 上一阶段成功后,新阶段可能因锁/队列满未入队;不能沿用旧成功结果。 */
if (!g_sink_init_stage_submitted[idx]) {
return chrg_sink_init_submit_stage(idx, pSW);
}
batch_result = chrg_sou_com_batch_get_result(idx);
if (batch_result <= 0) {
return chrg_sink_init_submit_stage(idx, pSW);
}
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_TEST_STOP) {
g_sink_init_stage[idx] = SINK_INIT_STAGE_TEST_STATUS;
g_sink_init_test_sample_seq[idx] = chrg_eload_get_sample_seq(idx);
return (chrg_sou_measure_once_submit(idx) == 0) ? 0 : -1;
}
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_TEST_STATUS) {
rt_uint32_t sample_seq = chrg_eload_get_sample_seq(idx);
if (sample_seq == g_sink_init_test_sample_seq[idx]) {
return (chrg_sou_measure_once_submit(idx) == 0) ? 0 : -1;
}
g_sink_init_test_sample_seq[idx] = sample_seq;
if (chrg_sink_power_test_active(idx)) {
if (g_sink_init_test_stop_retry[idx] >=
SINK_TEST_STATUS_RETRY_MAX) {
/* 达到一轮上限后重新计数,但仍保持在安全确认流程。 */
g_sink_init_test_stop_retry[idx] = 0U;
}
g_sink_init_test_stop_retry[idx]++;
g_sink_init_stage[idx] = SINK_INIT_STAGE_TEST_STOP;
return chrg_sink_init_submit_stage(idx, pSW);
}
g_sink_init_test_stop_retry[idx] = 0U;
g_sink_init_stage[idx] = SINK_INIT_STAGE_AUX_RELAY;
return chrg_sink_init_submit_stage(idx, pSW);
}
if (g_sink_init_stage[idx] == SINK_INIT_STAGE_AUX_RELAY) {
g_sink_init_stage[idx] = SINK_INIT_STAGE_DELAY;
g_sink_init_north_relay_tick[idx] = rt_tick_get() +
rt_tick_from_millisecond(SINK_INIT_NORTH_RELAY_DELAY_MS);
return 0;
}
/* 北向可能已推进到CC或电压查询,此处只更新南向完成状态。 */
g_sink_init_in_progress[idx] = RT_FALSE;
g_sink_init_stage_submitted[idx] = RT_FALSE;
pSOU->online[idx] = 1;
pSW->sink.test.test_mode_old = 0;
pSW->sink.work_mode = SINK_WORK_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_volt_low_protect
函数描述: 主继电器闭合后持续监控南向实测输入电压,掉电立即转掉电保护
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 电压到位时确认ready;掉电时强制进入SINK_WORK_LOW并执行掉电批次
返回说明: 0:正常或保护批次已提交 <0:参数错误或保护批次提交失败
其它说明: 由北向轮询线程每轮调用,不依赖北向0x20回码。协议板断电时北向
回码会中断(volt_low计数不再累积),而南向P1量测由功率板独立
供电持续刷新,是本保护唯一可靠的电量来源。只有"提交闭合且实测
电压已到位"的通道才启用掉电判定,继电器动作期间不误触发
*****************************************************************/
int chrg_sink_volt_low_protect(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (g_sink_rel_main_closed[idx] == 0) {
return 0;
}
if (pSW->Now_voltage >= WORK_MIN_VOLT) {
/* 实测电压到达工作下限,确认主继电器已闭合。 */
g_sink_rel_main_ready[idx] = 1;
return 0;
}
if (g_sink_rel_main_ready[idx] != 0) {
/* 曾经到位后再次掉电:协议板处于断电窗口,禁止负载带载,
* 强制进入掉电安全处理等待协议板重新上电。 */
pSW->sink.work_mode = SINK_WORK_LOW;
return chrg_sink_work(idx, pSW);
}
/* 已提交闭合但电压尚未建立(继电器动作中),继续等待不保护。 */
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_volt_runing
函数描述: 执行负载RUNING状态,闭合全部继电器并启动负载
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 非0:命令提交失败
其它说明: 辅助继电器已在INIT、重握手或掉电恢复阶段打开;RUNING只发送
ALL_REL_ON、目标负载值和WORK_START,避免FU_REL_ON重复关闭主继电器
*****************************************************************/
static int chrg_sink_work_volt_runing(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
rt_uint16_t regs[3] = {REG_REL_ON, 0, REG_WORK};
rt_uint16_t vals[3] = {ALL_REL_ON, 0, WORK_START};
int ret;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (!chrg_sink_load_should_start(pSW)) {
/*
* 北向协议已完成,低电流设定仍需要接通主继电器,使负载通路
* 处于完整连接状态;但禁止电子负载带载,仅发送WORK_STOP,
* 不改写电流设定,避免空载待机影响下一次有效电流设置。
*/
rt_uint16_t stop_regs[2] = {REG_REL_ON, REG_WORK};
rt_uint16_t stop_vals[2] = {ALL_REL_ON, WORK_STOP};
ret = chrg_sou_com_batch_submit(idx, stop_regs, stop_vals, 2U);
if (ret == 0) {
pSW->sink.work_mode = SINK_WORK_LOAD_STANDBY;
g_sink_rel_main_closed[idx] = 1;
}
return ret;
}
/* 只有南向实测电压曾经到位后再次掉到低压,才判定为掉电。
* 初次RUNING时继电器批次刚入队,Now_voltage可能仍是上一帧的0,
* 此时不能取消尚未发送到WORK_START的启动批次。 */
if ((g_sink_rel_main_ready[idx] != 0) &&
(pSW->Now_voltage < WORK_MIN_VOLT)) {
pSW->sink.work_mode = SINK_WORK_LOW;
return 0;
}
if (pSW->CV_mode == MODE_CONSTANT_VOLTAGE) {
regs[1] = REG_VOLTAGE_OUT + 1;
vals[1] = pSW->sink.loadv;
} else {
regs[1] = REG_CURRENT_OUT + 1;
vals[1] = pSW->sink.loadc;
}
/* RUNING批次必须完整发送:ALL_REL_ON、目标负载值、WORK_START。 */
ret = chrg_sou_com_batch_submit(idx, regs, vals, 3U);
#if SOUTH_COMMAND_DEBUG
rt_kprintf("------ALL_REL_ON-------\n");
#endif
if (ret == 0) {
g_sink_rel_main_closed[idx] = 1;
}
return ret;
}
/*****************************************************************
函数名称: chrg_sink_work_load_stop
函数描述: 普通负载运行中设定电流降到最低门限以下时安全停止带载
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 命令入队成功后进入SINK_WORK_LOAD_STANDBY
返回说明: 0:命令提交成功 <0:参数或命令提交失败
其它说明: 命令只发送WORK_STOP;不改变北向协议、CC、电流设定和
主副继电器,保证后续恢复有效电流时无需重新协议握手
*****************************************************************/
static int chrg_sink_work_load_stop(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t reg = REG_WORK;
rt_uint16_t val = WORK_STOP;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
/* 自动停止优先于同通道残留启动批次,禁止STOP后继续发送WORK_START。 */
g_sink_init_in_progress[idx] = RT_FALSE;
if (chrg_sou_com_batch_cancel(idx) != 0) {
return -1;
}
if (chrg_sou_com_batch_submit(idx, &reg, &val, 1U) != 0) {
return -1;
}
g_sink_von_stable_count[idx] = 0U;
g_sink_load_restart_confirm_count[idx] = 0U;
pSW->sink.work_mode = SINK_WORK_LOAD_STANDBY;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_load_start
函数描述: 从主继电器未打开的低电流待机状态等待北向电压稳定后再启动负载
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 命令入队成功后进入SINK_WORK_RUNING
返回说明: 0:命令提交成功 <0:参数或命令提交失败
其它说明: 本状态只作为待启动标记,不直接下发继电器或WORK_START;必须由北向
解析确认实际电压稳定后转入SINK_WORK_RUNING,再执行标准启动顺序
*****************************************************************/
static int chrg_sink_work_load_start(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (!chrg_sink_load_should_start(pSW)) {
return -1;
}
return 0;
}
/*****************************************************************
函数名称: chrg_sink_nor_work_low
函数描述: 执行负载掉电处理,卸载后关闭主副继电器并重新打开副继电器
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 非0:命令提交失败
其它说明: 命令顺序为WORK_STOP、CURRENT=1000mA、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 reg = REG_WORK;
rt_uint16_t value = WORK_STOP;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
/* 低压保护优先级最高,先丢弃该通道残留的启动或设定命令。 */
g_sink_init_in_progress[idx] = RT_FALSE;
if (chrg_sou_com_batch_cancel(idx) != 0) {
return -1;
}
/* Low voltage stops the electronic load only. Do not alter South relays:
* an intentional protocol transition must not cause a relay-induced drop. */
if (chrg_sou_com_batch_submit(idx, &reg, &value, 1U) != 0) {
return -1;
}
/* 主继电器已随掉电批次断开,闭合跟踪与电压到位确认一并清除。 */
g_sink_rel_main_closed[idx] = 0;
g_sink_rel_main_ready[idx] = 0;
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;
}
/*****************************************************************
函数名称: chrg_sink_nor_work_off
函数描述: 执行负载主动关机流程
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 清除运行、使能和在线标志,关闭北向继电器
返回说明: 0:关机成功 <0:停止命令提交失败
其它说明: 功率板处于测试态时先发送WORK_STOP;短路兼容退出命令随后发送
*****************************************************************/
static int chrg_sink_work_stop_prepare(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t regs[3];
rt_uint16_t values[3];
rt_uint8_t count = 0U;
rt_bool_t was_short;
rt_bool_t power_test_active;
rt_bool_t preserve_test_retry;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
preserve_test_retry = (pSW->sink.work_mode == SINK_WORK_STOP_WAIT) &&
!g_sink_stop_test_confirmed[idx] &&
(g_sink_stop_test_stop_retry[idx] != 0U);
power_test_active = chrg_sink_power_test_active(idx);
was_short = (power_test_active &&
(chrg_sink_power_status_get(idx) == STA1_SHORT_TEST)) ||
(pSW->sink.test.test_mode_old == SINK_TEST_SHORT) ||
(pSW->sink.test.test_mode == SINK_TEST_SHORT) ||
g_sink_stop_from_short[idx];
g_sink_stop_from_short[idx] = was_short;
g_sink_stop_test_sample_seq[idx] = 0U;
if (!preserve_test_retry) {
g_sink_stop_test_stop_retry[idx] = 0U;
}
g_sink_stop_test_confirmed[idx] = RT_FALSE;
/* WORK_STOP必须是退出Short/OCP/DYNA的第一条南向命令。 */
regs[count] = REG_WORK;
values[count++] = WORK_STOP;
if (was_short) {
regs[count] = ModbusRTU_ShortTest_CMD_MODE_FLAG;
values[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
}
g_sink_init_in_progress[idx] = RT_FALSE;
g_sink_init_stage_submitted[idx] = RT_FALSE;
if (chrg_sou_com_batch_cancel(idx) != 0) {
return -1;
}
if (chrg_sou_com_batch_submit(idx, regs, values, count) != 0) {
return -1;
}
/* 短路退出和WORK_STOP全部收到匹配回码后,必须通过停止命令之后的
* 新P1量测连续确认低电流,才允许断开功率继电器。 */
chrg_sink_unload_confirm_reset(idx);
pSW->sink.work_mode = SINK_WORK_STOP_WAIT;
return 0;
}
static int chrg_sink_work_stop_relay(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t reg = REG_REL_ON;
rt_uint16_t value = ALL_REL_OFF;
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.work_mode = SINK_WORK_STOP_RELAY_WAIT;
return 0;
}
static int chrg_sink_work_stop_relay_wait(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
struct chrg_south_t *pSOU = &chrgsouth;
struct chrg_north_t *pNOR = &chrgnorth;
int result;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
if (chrg_sou_com_batch_is_pending(idx)) {
return 0;
}
result = chrg_sou_com_batch_get_result(idx);
if (result <= 0) {
return chrg_sink_work_stop_relay(idx, pSW);
}
pSW->sink.On_work = 0;
pSOU->online[idx] = 0;
pNOR->enable[idx] = 0;
g_sink_rel_main_closed[idx] = 0;
g_sink_rel_main_ready[idx] = 0;
g_sink_short_start_tick[idx] = 0U;
g_sink_stop_from_short[idx] = RT_FALSE;
pSW->sink.test.test_mode = SINK_NO_TEST;
pSW->sink.test.test_mode_old = SINK_NO_TEST;
chrg_nor_sw_rel((eIDX_NOR_CH)idx, 0);
/* 主动停止已经完成,退出异步等待状态,禁止下一轮重复执行。 */
pSW->sink.work_mode = SINK_WORK_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_switch
函数描述: 处理“电源切换到负载”的过渡状态
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 清除旧电源运行标志,设置负载北向入口和INIT状态
返回说明: 0:停止命令成功加入队列 非0:命令入队失败
其它说明: pSW->id已由COM更新为ID_SINK;先提交旧电源WORK_STOP,
后续负载INIT使用同一通道队列,保证停止命令先执行
*****************************************************************/
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;
/* Source接管后,旧Sink初始化上下文已失效;返回Sink必须重新初始化,
* 禁止把角色切换批次回码当作上次初始化阶段的完成结果。 */
g_sink_init_in_progress[idx] = RT_FALSE;
g_sink_init_stage_submitted[idx] = RT_FALSE;
g_sink_init_stage[idx] = SINK_INIT_STAGE_CLEAR_RELAY;
g_sink_init_north_relay_tick[idx] = 0U;
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;
/* 首次进入Sink时先设置目标CC线,CC稳定后再设置协议。 */
/* Protocol-before-CC test flow is disabled in this version. */
pSW->sub = IDX_SET_SINK_CC_BEFORE_PD;
/* SWITCH只负责旧角色退出,后续启动统一交给Sink INIT状态。 */
pSW->sink.work_mode = SINK_WORK_INIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_protocol_unload
函数描述: 协议电压或电流变化前的软卸载:停止电子负载
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 <0:命令提交失败
其它说明: 不操作北向和南向功率继电器;WORK_STOP完成后进入UNLOAD_WAIT,
连续确认两帧实际电流不大于200mA,才允许北向重配协议并使用软恢复流程
*****************************************************************/
static int chrg_sink_work_protocol_unload(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
rt_uint16_t regs[2] = {REG_WORK, Eload_Start_V_ON};
rt_uint16_t values[2];
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
values[0] = WORK_STOP;
values[1] = (pSW->sink.volt_on <= 500U) ? 500U : pSW->sink.volt_on;
if (chrg_sou_com_batch_submit(idx, regs, values, 2U) != 0) {
return -1;
}
/* WORK_STOP回码只确认命令接收;还需连续读取两帧合格的实际电流。 */
chrg_sink_unload_confirm_reset(idx);
g_sink_von_stable_count[idx] = 0U;
g_sink_load_restart_confirm_count[idx] = 0U;
pSW->sink.work_mode = SINK_WORK_UNLOAD_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_handshake_prepare
函数描述: 执行协议或CC完整重握手前的南向安全断开和副继电器恢复
输入参数: idx:南向功率板通道 pSW:当前通道控制结构体
输出参数: 命令提交成功后进入SINK_WORK_HANDSHAKE_WAIT
返回说明: 0:两条命令提交成功 <0:参数或命令提交失败
其它说明: 命令顺序固定为WORK_STOP、Eload_Start_V_ON;
WORK_STOP已关闭电子负载,V_ON随后更新到功率板;
主副继电器全部断开并确认回码后再单独打开副继电器;
北向Sink继电器保持接通,低电流确认完成后才允许重新握手
*****************************************************************/
static int chrg_sink_work_handshake_prepare(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t regs[2] = {REG_WORK, Eload_Start_V_ON};
rt_uint16_t values[2];
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
values[0] = WORK_STOP;
values[1] = (pSW->sink.volt_on <= 500U) ? 500U : pSW->sink.volt_on;
/* 新CC/协议事务必须抢占本通道尚未发送的旧启动和设定命令。 */
if (chrg_sou_com_batch_cancel(idx) != 0) {
return -1;
}
if (chrg_sou_com_batch_submit(idx, regs, values, 2U) != 0) {
return -1;
}
/* 协议或CC切换先停止电子负载,连续2次确认低电流后再操作继电器。 */
chrg_sink_unload_confirm_reset(idx);
g_sink_von_stable_count[idx] = 0U;
g_sink_load_restart_confirm_count[idx] = 0U;
pSW->sink.work_mode = SINK_WORK_HANDSHAKE_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_current_wait
函数描述: 等待负载安全断开批次完成,并连续确认两帧实际电流不大于200mA
输入参数: idx:南向功率板通道 pSW:当前通道控制结构体
full_handshake:RT_TRUE为完整重握手 RT_FALSE为协议参数软卸载
输出参数: 确认成功后将sink.work_mode切换为SINK_WORK_WAIT
返回说明: 0:继续等待或确认成功 <0:状态查询或优先量测请求失败
其它说明: 软卸载要求WORK_STOP和V_ON收到匹配回码后建立量测基线;完整重握手
还要求CURRENT=1000mA、ALL_REL_OFF和FU_REL_ON回码成功,但保持北向Sink继电器接通
*****************************************************************/
static int chrg_sink_work_current_wait(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, rt_uint8_t next_action)
{
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) {
/* 写命令失败时重新执行停止命令,禁止使用量测值提前放行。 */
if (next_action == SINK_UNLOAD_NEXT_HANDSHAKE) {
return chrg_sink_work_handshake_prepare(idx, pSW);
}
if (next_action == SINK_UNLOAD_NEXT_STOP_RELAY) {
return chrg_sink_work_stop_prepare(idx, pSW);
}
return chrg_sink_work_protocol_unload(idx, pSW);
}
if (batch_result == 0) {
/* 无有效批次结果时保持阻塞,并重新提交停止动作。 */
if (next_action == SINK_UNLOAD_NEXT_HANDSHAKE) {
return chrg_sink_work_handshake_prepare(idx, pSW);
}
if (next_action == SINK_UNLOAD_NEXT_STOP_RELAY) {
return chrg_sink_work_stop_prepare(idx, pSW);
}
return chrg_sink_work_protocol_unload(idx, pSW);
}
if ((next_action == SINK_UNLOAD_NEXT_STOP_RELAY) &&
!g_sink_stop_test_confirmed[idx]) {
rt_uint32_t test_sample_seq = chrg_eload_get_sample_seq(idx);
if (g_sink_stop_test_sample_seq[idx] == 0U) {
g_sink_stop_test_sample_seq[idx] = test_sample_seq;
return (chrg_sou_measure_once_submit(idx) == 0) ? 0 : -1;
}
if (test_sample_seq == g_sink_stop_test_sample_seq[idx]) {
return (chrg_sou_measure_once_submit(idx) == 0) ? 0 : -1;
}
g_sink_stop_test_sample_seq[idx] = test_sample_seq;
if (chrg_sink_power_test_active(idx)) {
if (g_sink_stop_test_stop_retry[idx] >=
SINK_TEST_STATUS_RETRY_MAX) {
/* 达到一轮上限后重新计数,但仍保持在安全确认流程。 */
g_sink_stop_test_stop_retry[idx] = 0U;
}
g_sink_stop_test_stop_retry[idx]++;
return chrg_sink_work_stop_prepare(idx, pSW);
}
g_sink_stop_test_confirmed[idx] = RT_TRUE;
}
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;
if (next_action == SINK_UNLOAD_NEXT_HANDSHAKE) {
/* 协议或CC切换保持南向继电器状态,低电流确认后直接开始北向配置。 */
pSW->sink.work_mode = SINK_WORK_WAIT;
} else if (next_action == SINK_UNLOAD_NEXT_STOP_RELAY) {
pSW->sink.work_mode = SINK_WORK_STOP_RELAY;
} else {
/* Keep relays closed and resume through the common
* voltage-confirmed RUNING path. */
pSW->sink.work_mode = SINK_WORK_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
函数描述: 配置并启动动态负载测试
输入参数: ch:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 非0:命令提交失败
其它说明: 一次提交两组负载、保持时间、斜率、测试命令和WORK_START
*****************************************************************/
static int chrg_sink_TEST_DYNA_ON(rt_uint8_t ch, struct chrg_switch_t *pSW)
{
struct test *pTest = &pSW->sink.test;
rt_uint16_t regs[8] = {REG_REL_ON, REG_REL_ON,
ModbusRTU_DYNA_L1, ModbusRTU_DYNA_T1, ModbusRTU_DYNA_L2,
ModbusRTU_DYNA_T2, ModbusRTU_DYNA_CMD_MODE_FLAG, REG_WORK};
rt_uint16_t vals[8] = {0x02, 0x03,
pTest->change_test.curr1_set, pTest->change_test.time1_set,
pTest->change_test.curr2_set, pTest->change_test.time2_set,
ModbusRTU_DYNA_CMD_MODE_RUN, WORK_START};
/*
* 动态测试可能在普通 RUNING 状态打开功率板继电器之前进入。
* 因此动态启动流程必须自行打开两路继电器,避免快速从 PD 设置
* 切换到动态测试时,负载已经启动但功率板继电器仍处于关闭状态。
*/
return chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 8);
}
/*****************************************************************
函数名称: chrg_sink_TEST_SHORT_ON
函数描述: 配置并启动负载短路测试
输入参数: ch:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 非0:命令提交失败
其它说明: 根据set_mode选择定时无载或持续保持模式
*****************************************************************/
static int chrg_sink_TEST_SHORT_ON(rt_uint8_t ch, struct chrg_switch_t *pSW)
{
rt_uint16_t regs[4] = {Eload_Start_V_ON,
ModbusRTU_ShortTest_Hold_Time, ModbusRTU_ShortTest_CMD_MODE_FLAG,
REG_WORK};
rt_uint16_t vals[4] = {SINK_SHORT_TEST_VON_MV, 0, 0, 0};
/* 外部工程固定短路测试保持60秒且不带载。 */
vals[1] = 60000;
vals[2] = ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR;
vals[3] = WORK_START;
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 4) == 0) {
g_sink_short_start_tick[ch] = rt_tick_get();
return 0;
}
return -1;
}
/* 短路测试保持期间只补发WORK_START,避免重复写入测试配置并堆积批次。 */
static int chrg_sink_TEST_SHORT_KEEPALIVE(rt_uint8_t ch)
{
rt_uint16_t reg = REG_WORK;
rt_uint16_t val = WORK_START;
rt_tick_t now = rt_tick_get();
if ((rt_tick_t)(now - g_sink_short_start_tick[ch]) <
rt_tick_from_millisecond(SINK_SHORT_START_INTERVAL_MS)) {
return 0;
}
if (chrg_sou_com_batch_is_pending((eIDX_SOU_CH)ch)) {
return 0;
}
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, &reg, &val, 1U) == 0) {
g_sink_short_start_tick[ch] = now;
}
return 0;
}
/*****************************************************************
函数名称: chrg_sink_voltage_confirm_count
函数描述: 根据Sink协议目标电压选择进入RUNING前所需的连续稳定帧数
输入参数: target_volt_mv:当前协议申请的目标电压,单位mV
输出参数: -
返回说明: 5V目标返回延长确认帧数,其它目标返回普通确认帧数
其它说明: 由chrg_sink_volt_judge调用
*****************************************************************/
static rt_uint8_t chrg_sink_voltage_confirm_count(rt_uint16_t target_volt_mv)
{
return (target_volt_mv == SINK_VOLT_5V_TARGET_MV) ?
SINK_VOLT_CONFIRM_5V_COUNT : SINK_VOLT_CONFIRM_NORMAL_COUNT;
}
static rt_uint16_t chrg_sink_von_voltage_get(const struct chrg_switch_t *pSW)
{
#if SINK_VON_VOLTAGE_SOURCE == SINK_VON_VOLTAGE_FROM_PROTOCOL
return pSW->Sink_voltage;
#else
return pSW->Now_voltage;
#endif
}
/*****************************************************************
函数名称: chrg_sink_volt_judge
函数描述: 使用配置的V_On电压源与V_On比较,控制负载启停
输入参数: idx:南向通道 pSW:通道控制结构体
输出参数: 更新sink.work_mode并调用chrg_sink_work
返回说明: 0:处理完成 <0:参数错误
*****************************************************************/
int chrg_sink_volt_judge(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
rt_uint8_t stable_confirm_count;
rt_uint32_t voltage_seq;
static rt_uint8_t volt_low[TOTAL_SOU_CHS] = {0};
static rt_uint8_t no_curr[TOTAL_SOU_CHS] = {0};
static rt_uint8_t volt_set_ok[TOTAL_SOU_CHS] = {0};
static rt_tick_t running_start_tick[TOTAL_SOU_CHS] = {0};
rt_uint16_t von_voltage;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
/* 初始化及短路退出批次完成前,禁止电压判定提前启动普通负载。 */
if (g_sink_init_in_progress[idx] ||
(pSW->sink.work_mode == SINK_WORK_INIT) ||
(pSW->sink.work_mode == SINK_WORK_INIT_AUX_WAIT)) {
/* 统一入口负责恢复等待阶段及停止优先级,不能在这里覆盖STOP。 */
return chrg_sink_work(idx, pSW);
}
#if SINK_VON_VOLTAGE_SOURCE == SINK_VON_VOLTAGE_FROM_PROTOCOL
voltage_seq = pSW->Sink_voltage_seq;
#else
voltage_seq = chrg_eload_get_sample_seq(idx);
#endif
if (voltage_seq == g_sink_von_voltage_seq[idx]) {
return chrg_sink_work(idx, pSW);
}
g_sink_von_voltage_seq[idx] = voltage_seq;
/* 卸载和协议重配尚未完成时,旧电压只能用于推进安全流程,不能触发WORK_START。 */
if ((pSW->sink.work_mode == SINK_WORK_UNLOAD_WAIT) ||
(pSW->sink.work_mode == SINK_WORK_HANDSHAKE_WAIT) ||
((pSW->sink.work_mode != SINK_WORK_RUNING) &&
(pSW->sink.work_mode != SINK_WORK_LOAD_STANDBY) &&
(pSW->sub != IDX_GET_SINK_VC))) {
g_sink_von_stable_count[idx] = 0U;
g_sink_load_restart_confirm_count[idx] = 0U;
return chrg_sink_work(idx, pSW);
}
von_voltage = chrg_sink_von_voltage_get(pSW);
stable_confirm_count = chrg_sink_voltage_confirm_count(
pSW->sink.pro_loadv);
if ((pSW->sink.On_work == 1) && (pSW->sink.test.test_mode == 0)) {
if (von_voltage <= WORK_MIN_VOLT) {
g_sink_von_stable_count[idx] = 0U;
g_sink_load_restart_confirm_count[idx] = 0U;
if (volt_set_ok[idx] == 1 && ++volt_low[idx] >= 50) {
volt_set_ok[idx] = 0;
no_curr[idx] = 0;
}
if (pSW->sink.work_mode == SINK_WORK_RUNING) {
return chrg_sink_work_load_stop(idx, pSW);
}
} else {
volt_low[idx] = 0;
volt_set_ok[idx] = 1;
pSW->change_flag = 0;
if (pSW->sink.work_mode == SINK_WORK_LOW_MONITOR) {
/* 副继电器恢复后首次重新检测到输入电压,只启动协议重握手。 */
g_sink_von_stable_count[idx] = 0U;
no_curr[idx] = 0;
pSW->sink.work_mode = SINK_WORK_WAIT;
} else if ((pSW->sink.work_mode == SINK_WORK_LOW) ||
(pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT)) {
/* 掉电安全批次完成前,不得进入RUNING。 */
g_sink_von_stable_count[idx] = 0U;
no_curr[idx] = 0;
} else if (von_voltage > pSW->sink.volt_on) {
if ((pSW->sink.work_mode != SINK_WORK_RUNING) &&
(pSW->sink.work_mode != SINK_WORK_LOAD_STANDBY)) {
if (g_sink_von_stable_count[idx] < stable_confirm_count) {
g_sink_von_stable_count[idx]++;
}
if (g_sink_von_stable_count[idx] >= stable_confirm_count) {
/* 连续达到当前目标对应的确认帧数后进入RUNING。 */
g_sink_von_stable_count[idx] = 0U;
pSW->sink.work_mode = SINK_WORK_RUNING;
running_start_tick[idx] = rt_tick_get();
no_curr[idx] = 0;
}
} else if (pSW->sink.work_mode == SINK_WORK_LOAD_STANDBY) {
no_curr[idx] = 0;
if (chrg_sink_load_should_start(pSW)) {
/* 自动停止后只接受新的电压采样;达到恢复确认帧数才允许重启。 */
if (g_sink_load_restart_confirm_count[idx] <
SINK_LOAD_RESTART_CONFIRM_COUNT) {
g_sink_load_restart_confirm_count[idx]++;
}
if (g_sink_load_restart_confirm_count[idx] >=
SINK_LOAD_RESTART_CONFIRM_COUNT) {
g_sink_load_restart_confirm_count[idx] = 0U;
pSW->sink.work_mode = SINK_WORK_RUNING;
running_start_tick[idx] = rt_tick_get();
}
} else {
g_sink_load_restart_confirm_count[idx] = 0U;
}
} else if ((pSW->Now_current > 200) || (pSW->sink.loadc < 200)) {
g_sink_von_stable_count[idx] = 0U;
no_curr[idx] = 0;
} else if ((rt_tick_t)(rt_tick_get() - running_start_tick[idx]) >=
rt_tick_from_millisecond(SINK_CURRENT_START_GRACE_MS)) {
g_sink_von_stable_count[idx] = 0U;
if (++no_curr[idx] >= 2) {
pSW->sink.work_mode = SINK_WORK_WAIT;
no_curr[idx] = 0;
}
}
} else {
g_sink_von_stable_count[idx] = 0U;
g_sink_load_restart_confirm_count[idx] = 0U;
no_curr[idx] = 0;
/* 配置电压源低于负载设置的V_On时,立即停止电子负载。 */
if (pSW->sink.work_mode == SINK_WORK_RUNING) {
return chrg_sink_work_load_stop(idx, pSW);
}
}
}
} else {
volt_low[idx] = 0;
no_curr[idx] = 0;
volt_set_ok[idx] = 0;
g_sink_von_stable_count[idx] = 0U;
}
return chrg_sink_work(idx, pSW);
}
void chrg_sink_short_keepalive_process(void)
{
eIDX_SOU_CH idx;
struct chrg_switch_t *pSW;
for (idx = IDX_SOU_CH1; idx < TOTAL_SOU_CHS; idx++) {
pSW = &chrgnorth.sw[idx];
if ((pSW->id != ID_SINK) ||
(pSW->sink.work_mode != SINK_WORK_ON_TEST_SHORT) ||
(pSW->sink.test.test_mode != SINK_TEST_SHORT)) {
g_sink_short_start_tick[idx] = 0U;
continue;
}
(void)chrg_sink_TEST_SHORT_KEEPALIVE(idx);
}
}
/*****************************************************************
函数名称: chrg_sink_TEST_OCP_ON
函数描述: 配置并启动负载OCP过流保护测试
输入参数: ch:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 非0:命令提交失败
其它说明: 一次提交功率继电器、OCP参数、测试命令和WORK_START;
set_mode为0时保持指定时间后恢复,为1时一直保持
*****************************************************************/
static int chrg_sink_TEST_OCP_ON(rt_uint8_t ch, struct chrg_switch_t *pSW)
{
struct test_OCP *pOCP = &pSW->sink.test.OCP_test;
rt_uint16_t regs[11] = {REG_REL_ON, REG_REL_ON,
ModbusRTU_OCPTest_StartCurrent, ModbusRTU_OCPTest_StopCurrent,
ModbusRTU_OCPTest_Step_CURR, ModbusRTU_OCPTest_Step_Hold_Time,
ModbusRTU_OCPTest_Vtrig_Stop, ModbusRTU_OCPTest_Hold_Time,
ModbusRTU_OCPTest_HOLD_CURR, ModbusRTU_OCPTest_CMD_MODE_FLAG,
REG_WORK};
rt_uint16_t vals[11] = {0x02, 0x03,
pOCP->start_curr, pOCP->end_curr, pOCP->change_curr,
pOCP->change_keep_time, pOCP->stop_volt, pOCP->hold_time,
pOCP->hold_curr, ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR,
WORK_START};
if (pOCP->set_mode != 0) {
vals[9] = ModbusRTU_DYNA_CMD_MODE_RUN_ALWAYS_KEPP;
}
return chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 11);
}
/*****************************************************************
函数名称: chrg_sink_work
函数描述: 负载工作状态机统一入口
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 根据sink.work_mode执行对应状态动作
返回说明: 0:状态处理成功 <0:参数、状态或命令提交失败
其它说明: 每通道记录上次成功状态,相同状态不重复下发寄存器
*****************************************************************/
int workmode = 0;
int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
static uint8_t last_state[TOTAL_SOU_CHS] ={100,100,100,100};
rt_uint8_t work_mode;
int ret = -1;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
work_mode = pSW->sink.work_mode;
workmode = work_mode;
/* 已开始的初始化只能继续等待,重复INIT也不能被last_state去重卡住。
* 停止、掉电保护和角色切换必须能抢占初始化,不能改回等待状态。 */
if (g_sink_init_in_progress[idx] &&
(work_mode != SINK_WORK_STOP) &&
(work_mode != SINK_WORK_LOW) &&
(work_mode != SINK_WORK_SWITCH)) {
pSW->sink.work_mode = SINK_WORK_INIT_AUX_WAIT;
work_mode = SINK_WORK_INIT_AUX_WAIT;
}
if (work_mode != SINK_WORK_ON_TEST_SHORT) {
g_sink_short_start_tick[idx] = 0;
}
/* 异步等待状态需要持续检查批次或新量测,其余相同状态保持去重。 */
if ((work_mode == last_state[idx]) &&
(work_mode != SINK_WORK_INIT) &&
(work_mode != SINK_WORK_STOP) &&
(work_mode != SINK_WORK_ON_TEST_SHORT) &&
(work_mode != SINK_WORK_INIT_AUX_WAIT) &&
(work_mode != SINK_WORK_STOP_WAIT) &&
(work_mode != SINK_WORK_STOP_RELAY_WAIT) &&
(work_mode != SINK_WORK_UNLOAD_WAIT) &&
(work_mode != SINK_WORK_LOW_AUX_WAIT) &&
(work_mode != SINK_WORK_HANDSHAKE_WAIT)) {
if (work_mode == SINK_WORK_ON_TEST_SHORT) {
return chrg_sink_TEST_SHORT_KEEPALIVE(idx);
}
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;
case SINK_WORK_RUNING:
ret = chrg_sink_work_volt_runing(idx,pSW);
break;
case SINK_WORK_ON_TEST_DYNA:
ret = chrg_sink_TEST_DYNA_ON(idx,pSW);
break;
case SINK_WORK_ON_TEST_SHORT:
if (work_mode != last_state[idx]) {
ret = chrg_sink_TEST_SHORT_ON(idx, pSW);
} else {
ret = chrg_sink_TEST_SHORT_KEEPALIVE(idx);
}
break;
case SINK_WORK_ON_TEST_OCP:
ret = chrg_sink_TEST_OCP_ON(idx,pSW);
break;
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_work_stop_prepare(idx, pSW);
break;
case SINK_WORK_STOP_WAIT:
ret = chrg_sink_work_current_wait(idx, pSW,
SINK_UNLOAD_NEXT_STOP_RELAY);
break;
case SINK_WORK_STOP_RELAY:
ret = chrg_sink_work_stop_relay(idx, pSW);
break;
case SINK_WORK_STOP_RELAY_WAIT:
ret = chrg_sink_work_stop_relay_wait(idx, pSW);
break;
case SINK_WORK_SWITCH:
ret = chrg_sink_work_switch(idx,pSW);
break;
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,
SINK_UNLOAD_NEXT_RUNNING);
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,
SINK_UNLOAD_NEXT_HANDSHAKE);
break;
case SINK_WORK_LOAD_STANDBY:
ret = 0;
break;
case SINK_WORK_LOAD_STOP:
ret = chrg_sink_work_load_stop(idx, pSW);
break;
case SINK_WORK_LOAD_START:
ret = chrg_sink_work_load_start(idx, pSW);
break;
default:
break;
}
if (ret == 0) {
last_state[idx] = work_mode;
}
return ret;
}