refactor(chrg_north): 重构北向通信处理逻辑,增加配置版本校验机制

1.  apps/chrg/applications/thread/chrg_comm.c: 新增通道配置版本递增逻辑,避免旧回码干扰新配置
2.  apps/chrg/applications/thread/chrg_north.h: 更新config_version注释,补充用途说明
3.  apps/chrg/applications/thread/chrg_north.c: 重构帧头解析函数,新增缓冲区操作辅助函数和通道切换复位逻辑
4.  apps/chrg/applications/thread/chrg_roll_nor.c:
    - 新增超时、重发等宏定义
    - 重构协议设置函数,增加返回值处理
    - 新增回码匹配校验、邮箱清理、等待响应等辅助函数
    - 重构自动请求逻辑,增加重试和配置版本校验
    - 简化线程入口处理逻辑,统一回码释放流程
5.  apps/chrg/codex_build.log: 记录本次编译的代码尺寸变化
This commit is contained in:
wrh
2026-08-05 09:15:39 +08:00
parent f21492fa50
commit d9a8c359c0
182 changed files with 1265 additions and 82144 deletions
+76 -31
View File
@@ -2336,7 +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 pro_change;
rt_bool_t protocol_or_cc_change;
rt_bool_t protocol_param_change;
rt_uint8_t curr_test;
rt_uint8_t curr_test_old;
rt_uint8_t old_cv_mode = pReal->CV_mode;
@@ -2346,10 +2347,11 @@ 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);
/* Sink以下属性变化时需要从CC开始重新执行北向协议配置链路。 */
pro_change = diff_info.protocol_changed || diff_info.group_changed ||
diff_info.pro_voltage_changed || diff_info.pro_current_changed ||
diff_info.cc_changed || diff_info.cv_changed;
/* 协议类型或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;
rt_memcpy(pReal, pShadow, sizeof(struct chrg_switch_t));
if (sink_work == 0x01) {
@@ -2374,9 +2376,12 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
pReal->sink.On_work = 1;
pReal->sub = IDX_SET_SINK_CCLINE;
pReal->sink.work_mode = SINK_WORK_INIT;
} else if (pro_change) {
} else if (protocol_or_cc_change) {
pReal->sub = IDX_SET_SINK_CCLINE;
pReal->sink.work_mode = SINK_WORK_WAIT;
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_PROTOCOL_UNLOAD;
} else if (chrg_sink_diff_send_reg((eIDX_SOU_CH)ch,
&diff_info, pReal) != 0) {
pReal->CV_mode = old_cv_mode;
@@ -2406,7 +2411,13 @@ 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 protocol_config_changed;
rt_bool_t cc_changed;
rt_bool_t north_config_changed;
rt_bool_t protocol_switch_requested;
rt_bool_t old_is_pd;
rt_bool_t new_is_pd;
rt_bool_t need_cc_close;
struct vc_pd_t old_pd_set;
eSRC_PRO old_protocol = pReal->source.protocol;
rt_uint8_t old_cc_set = pReal->source.CC_Set;
@@ -2420,15 +2431,23 @@ static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
rt_uint16_t source_work = pShadow->source.Now_State;
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(&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;
/* 标准协议的电压电流直接交给功率板,不因pd_set数值变化重发0xD0。 */
protocol_config_changed = diff_info.protocol_changed ||
((pShadow->source.protocol != PRO_NONE) && diff_info.pd_changed);
cc_changed = diff_info.cc_changed;
north_config_changed = protocol_config_changed || cc_changed;
protocol_switch_requested = north_config_changed;
old_is_pd = (old_protocol == PRO_PD0PSS) ||
(old_protocol == PRO_PD31AVS);
new_is_pd = (pShadow->source.protocol == PRO_PD0PSS) ||
(pShadow->source.protocol == PRO_PD31AVS);
need_cc_close = (new_is_pd && north_config_changed) ||
(old_is_pd && !new_is_pd && diff_info.protocol_changed);
target_curr = pShadow->source.pd_set.mm.max_cur;
/* 影子区只拥有COM配置;北向解析产生的运行目标和回读数据继续由pReal持有。 */
@@ -2442,30 +2461,42 @@ static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
if (source_work == 0x01) {
if (pReal->source.On_Flag == 0) {
/* 新命令要求运行时直接置位,不依赖旧工作状态。 */
pReal->sub = IDX_SET_SOURCE_CC_SET;
/*
* 停止状态启动不需要先断开CC,直接从设置协议开始;
* 收到0xD0应答并等待保护时间后,再连接目标CC。
*/
chrg_roll_nor_source_switch_reset(ch);
pReal->sub = IDX_SET_SOURCE_PD;
pReal->source.On_Flag = 1;
pReal->source.work_mode = SOURCE_WORK_INIT;
return (chrg_source_work_state(ch, pReal) == 0) ?
MB_EX_NONE : MB_EX_SLAVE_BUSY;
if (chrg_source_work_state(ch, pReal) != 0) {
goto source_effect_submit_failed;
}
return MB_EX_NONE;
}
/* 协议、CC或任一pd_set字段变化时,重新执行北向配置链路。 */
/*
* PD配置或CC发生变化,以及从PD切换到非PD时,先断开CC;
* 其它协议配置变化直接从0xD0开始。
*/
if (north_config_changed) {
pReal->sub = IDX_SET_SOURCE_CC_SET;
chrg_roll_nor_source_switch_reset(ch);
if (need_cc_close) {
pReal->sub = IDX_SET_SOURCE_CC_CLOSE;
chrg_roll_nor_source_cc_close_register(ch);
} else {
pReal->sub = IDX_SET_SOURCE_PD;
}
}
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 (protocol_switch_requested) {
/*
* COM只进入协议切换状态;南向输出清零统一由Source状态函数执行。
* sub已在上方确定,北向轮询线程执行本状态并清零成功后,继续
* CC_CLOSE或协议设置,不需要COM直接发送任何清零命令。
*/
pReal->source.work_mode = SOURCE_WORK_PROTOCOL_SWITCH;
}
if (pReal->source.protocol != PRO_NONE) {
@@ -2474,11 +2505,11 @@ static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
}
/* 标准协议的模式和电流可直接提交,电压只能由RUNING状态写入。 */
if (diff_info.protocol_changed || diff_info.cv_changed) {
if (!protocol_switch_requested && diff_info.cv_changed) {
regs[count] = REG_MODE;
vals[count++] = pReal->CV_mode;
}
if (diff_info.protocol_changed || diff_info.current_changed) {
if (!protocol_switch_requested && diff_info.current_changed) {
regs[count] = REG_CURRENT_OUT + 1;
vals[count++] = target_curr;
}
@@ -2491,11 +2522,18 @@ static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
/* COM只更新标准协议配置并保持WAIT;下一帧北向解析负责比较
* pd_set目标与set_volt,再决定是否进入RUNING写电压。 */
pReal->source.set_curr = target_curr;
pReal->source.work_mode = SOURCE_WORK_WAIT;
if (!protocol_switch_requested) {
pReal->source.work_mode = SOURCE_WORK_WAIT;
}
return MB_EX_NONE;
}
pReal->source.On_Flag = 0;
if (pReal->source.On_Flag == 0) {
chrg_roll_nor_source_switch_reset(ch);
pReal->source.work_mode = SOURCE_WORK_WAIT;
return MB_EX_NONE;
}
chrg_roll_nor_source_switch_reset(ch);
pReal->source.work_mode = SOURCE_WORK_STOP;
return (chrg_source_work_state(ch, pReal) == 0) ?
MB_EX_NONE : MB_EX_SLAVE_BUSY;
@@ -2556,8 +2594,15 @@ static eMBException chrg_mb_write_sys(rt_uint8_t ch, rt_uint16_t reg_offset,
old_id = pReal->id;
new_id = pSW->id;
g_shadow_dirty[ch] = 0;
/*
* 使每次COM参数生效拥有独立版本。北向线程等待回码期间如果版本变化,
* 必须放弃旧请求,防止旧回码把新配置的sub错误推进到下一步。
*/
pNOR->config_version[ch]++;
if (old_id != new_id) {
/* 角色切换时清除旧Source协议切换的等待上下文。 */
chrg_roll_nor_source_switch_reset(ch);
rt_memcpy(pReal, pSW, sizeof(struct chrg_switch_t));
if (new_id == ID_SOURCE) {
pReal->source.work_mode = SOURCE_WORK_SWITCH;
+169 -71
View File
@@ -27,6 +27,7 @@
#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
/*****************************************************************
函数名称: chrg_source_apply_protocol_voltage_rule
@@ -121,37 +122,43 @@ static int chrg_source_voltage_control(eIDX_SOU_CH idx,
输入参数: frame_data:完整北向回码 idx:当前通道 pSW:通道控制结构体
pNOR:北向总控结构体
输出参数: 更新电压、电流、source.work_mode及电压重试计数
返回说明: -
返回说明: 0:回码正常 <0:参数错误、切换错误或功率板命令提交失败
其它说明: 标准协议使用COM配置目标参与实际电压校验;快充协议读取
frame_data[8~11]中的协商目标值,两个分支共用误差重试逻辑
frame_data[8~11]中的协商目标值;frame_data[14]的bit2表示
Source板协议/CC切换失败,错误回码不得驱动功率板输出
*****************************************************************/
void chrg_source_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
int chrg_source_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
{
rt_uint16_t volt_mv;
rt_uint16_t curr_ma;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return;
(void)pNOR;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL) ||
(frame_data == RT_NULL)) {
return -1;
}
pSW->source.qc_err = frame_data[14];
if ((pSW->source.qc_err & SOURCE_QC_SWITCH_ERROR) != 0U) {
return -2;
}
/* 标准协议不使用北向帧内目标值,但仍进入统一误差判断,确保INIT或
* COM下发的目标电压没有生效时能够自动重试。 */
if (pSW->source.protocol == PRO_NONE) {
(void)chrg_source_voltage_control(idx, pSW,
return chrg_source_voltage_control(idx, pSW,
pSW->source.pd_set.mm.max_vol,
pSW->source.pd_set.mm.max_cur);
return;
}
if ((frame_data == RT_NULL) || !pSW->source.On_Flag) {
return;
if (!pSW->source.On_Flag) {
return 0;
}
volt_mv = (frame_data[8] << 8) | frame_data[9];
curr_ma = (frame_data[10] << 8) | frame_data[11];
// /* 特殊协议规则只修正运行目标,不覆盖北向原始回码和COM协议配置。 */
// volt_mv = chrg_source_apply_protocol_voltage_rule(pSW, volt_mv);
(void)chrg_source_voltage_control(idx, pSW, volt_mv, curr_ma);
return chrg_source_voltage_control(idx, pSW, volt_mv, curr_ma);
}
/*****************************************************************
@@ -169,7 +176,7 @@ void chrg_source_setting(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
//执行输出调整
if (pSW->source.On_Flag == 1) {
/* 标准协议与快充协议都由解析入口选择各自目标值并统一校验。 */
chrg_source_parse(frame_data, idx, pSW, pNOR);
(void)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;
@@ -357,84 +364,163 @@ int chrg_north_send (rt_uint8_t *data, rt_size_t length)
}
/*****************************************************************
函数名称: chrg_north_frame_head
函数描述: 北向通道数据帧头提取
输入参数: *rb: 缓冲区 *head: 帧头 id:编号
函数名称: chrg_north_frame_shape_valid
函数描述: 检查北向回码命令和长度组合是否符合协议定义
输入参数: id:角色 length:帧长度 command:回码命令
输出参数: -
返回说明: <0: 错误 >0:帧长度
其它说明: 提取3字节帧头
返回说明: RT_TRUE:组合合法 RT_FALSE:组合非法
其它说明: 0x20量测回码固定21字节,设置命令回码固定5字节
*****************************************************************/
static int chrg_north_frame_head (struct rt_ringbuffer *rb, rt_uint8_t *head, eIDX_ID id)
static rt_bool_t chrg_north_frame_shape_valid(eIDX_ID id,
rt_uint8_t length, rt_uint8_t command)
{
int i = 0;
rt_uint8_t data = 0, stage = 0;
rt_size_t ret = 0;
rt_size_t len_rb = 0; // ring数据长度
rt_uint8_t len_frame = 0; // 帧长度
if (command == SRC_CMD_GET_VOLTAGE) {
return (length == 21U) ? RT_TRUE : RT_FALSE;
}
if (command == SRC_CMD_GET_PROTOCOL) {
return (length >= MIN_FRAME_LENGTH) ? RT_TRUE : RT_FALSE;
}
if (RT_NULL == rb) {
rt_kprintf("len_rb=NULL%d\r\n",len_rb);
if (id == ID_SOURCE) {
if ((command == SRC_CMD_SET_CC_LEVEL) ||
(command == SRC_CMD_SET_CC_CHOOSE) ||
(command == SRC_CMD_SET_PROTOCOL)) {
return (length == MIN_FRAME_LENGTH) ? RT_TRUE : RT_FALSE;
}
return RT_FALSE;
}
if (id == ID_SINK) {
switch (command) {
case SINK_CMD_SET_CC:
case SINK_CMD_SET_LEVEL:
case SINK_CMD_SET_QC20:
case SINK_CMD_SET_QC30:
case SINK_CMD_SET_FCP:
case SINK_CMD_SET_AFC:
case SINK_CMD_SET_SCP:
case SINK_CMD_SET_VIVO:
case SINK_CMD_SET_TFC:
case SINK_CMD_SET_PD0:
case SINK_CMD_SET_PPS:
case SINK_CMD_SET_PD3P1:
case SINK_CMD_SET_UFCS:
case SINK_CMD_SET_VOOC:
case SINK_CMD_SET_AVS:
case LOAD_PRO_NONE:
return (length == MIN_FRAME_LENGTH) ? RT_TRUE : RT_FALSE;
default:
return RT_FALSE;
}
}
return RT_FALSE;
}
/*****************************************************************
函数名称: chrg_north_ringbuffer_byte
函数描述: 不移动读指针地读取北向环形缓冲区指定偏移字节
输入参数: rb:环形缓冲区 offset:相对当前读指针偏移 data:输出字节
输出参数: data返回读取到的字节
返回说明: 0:成功 <0:参数或偏移错误
其它说明: 仅由北向接收线程调用,不与其它线程并发访问该环形缓冲区
*****************************************************************/
static int chrg_north_ringbuffer_byte(const struct rt_ringbuffer *rb,
rt_size_t offset, rt_uint8_t *data)
{
rt_size_t index;
if ((rb == RT_NULL) || (data == RT_NULL) ||
(offset >= rt_ringbuffer_data_len((struct rt_ringbuffer *)rb))) {
return -1;
}
index = rb->read_index + offset;
if (index >= rb->buffer_size) {
index -= rb->buffer_size;
}
*data = rb->buffer_ptr[index];
return 0;
}
/*****************************************************************
函数名称: chrg_north_ringbuffer_discard
函数描述: 从北向环形缓冲区丢弃指定数量字节
输入参数: rb:环形缓冲区 length:需要丢弃的字节数
输出参数: -
返回说明: -
其它说明: 用于移除帧头前噪声,同时保留末尾可能未接收完整的帧头
*****************************************************************/
static void chrg_north_ringbuffer_discard(struct rt_ringbuffer *rb,
rt_size_t length)
{
rt_uint8_t data;
while ((length > 0U) && (rt_ringbuffer_getchar(rb, &data) != 0U)) {
length--;
}
}
/*****************************************************************
函数名称: chrg_north_frame_head
函数描述: 在北向字节流中查找并提取合法四字节帧头
输入参数: rb:环形缓冲区 head:帧头输出区 id:当前通道角色
输出参数: head返回长度、固定字节、角色和命令
返回说明: >0:完整帧长度 0:暂未找到完整帧头 <0:参数或读取错误
其它说明: 未找到时保留末尾3字节,支持帧头跨多次UART接收
*****************************************************************/
static int chrg_north_frame_head(struct rt_ringbuffer *rb,
rt_uint8_t *head, eIDX_ID id)
{
rt_size_t i;
rt_size_t j;
rt_size_t len_rb;
rt_uint8_t length;
rt_uint8_t reserved;
rt_uint8_t frame_id;
rt_uint8_t command;
if ((rb == RT_NULL) || (head == RT_NULL)) {
return -1;
}
len_rb = rt_ringbuffer_data_len(rb);
if (len_rb < LEN_FRAME_HEAD) {
return 0;
}
for (i = 0; i < len_rb - 2; i++) { // 找头三个字节 [帧长, 0x00, id]
ret = rt_ringbuffer_getchar(rb, &data);
if (0 == ret) {
/* 使用非破坏读取逐字节滑动,查找完整的4字节候选帧头。 */
for (i = 0; (i + LEN_FRAME_HEAD) <= len_rb; i++) {
if ((chrg_north_ringbuffer_byte(rb, i, &length) != 0) ||
(chrg_north_ringbuffer_byte(rb, i + 1U, &reserved) != 0) ||
(chrg_north_ringbuffer_byte(rb, i + 2U, &frame_id) != 0) ||
(chrg_north_ringbuffer_byte(rb, i + 3U, &command) != 0)) {
break;
}
/* 长度、固定字节、角色或命令不匹配时继续搜索下一个候选位置。 */
if ((reserved != 0x00U) || (frame_id != (rt_uint8_t)id) ||
(length < MIN_FRAME_LENGTH) ||
!chrg_north_frame_shape_valid(id, length, command)) {
continue;
}
switch (stage) {
case 0:
head[0] = data;
len_frame = data;
if (data < MIN_FRAME_LENGTH) { // 长度不符
stage = 0;
continue;
/* 找到合法帧头后,仅丢弃它前面的噪声,再正式取出4字节帧头。 */
chrg_north_ringbuffer_discard(rb, i);
for (j = 0; j < LEN_FRAME_HEAD; j++) {
if (rt_ringbuffer_getchar(rb, &head[j]) == 0U) {
return -1;
}
stage = 1;
break;
case 1:
head[1] = data;
if (0x00 != data) {
stage = 0;
} else {
stage = 2;
}
break;
case 2:
head[2] = data;
if ((rt_uint8_t)id != data) {
stage = 0;
} else {
// if (id == ID_SINK) {
// rt_uint8_t ext1, ext2;
// // 主动读取并丢弃2字节扩展字段
// rt_ringbuffer_getchar(rb, &ext1);
// rt_ringbuffer_getchar(rb, &ext2);
// rt_kprintf("Sink:%02X %02X\r\n", ext1, ext2);
// }
return len_frame;
}
break;
default:
break;
}
return length;
}
return -1;
/* 保留末尾可能尚未接收完整的4字节帧头,等待下一批UART数据续接。 */
if (len_rb > (LEN_FRAME_HEAD - 1U)) {
chrg_north_ringbuffer_discard(rb,
len_rb - (LEN_FRAME_HEAD - 1U));
}
return 0;
}
/*****************************************************************
@@ -650,6 +736,7 @@ void chrg_north_thread_entry (void *data)
int len = 0;
rt_size_t put_len = 0;
eIDX_NOR_CH frame_idx = IDX_NOR_CH1; // 当前未完成帧所属的物理通道
struct chrg_tty_t *pTTY = RT_NULL;
struct chrg_north_t *pNOR = &chrgnorth;
//北向协议板线程体通道初始化为CH1,
@@ -690,6 +777,17 @@ void chrg_north_thread_entry (void *data)
continue;
}
/*
* 四个物理通道共用一个UART组帧器。切换通道后必须终止旧通道的未完成帧,
* 防止旧帧继续吞入新通道回码。本复位只在接收线程执行,避免发送线程与
* 接收线程同时操作环形缓冲区和组帧上下文。
*/
if (frame_idx != pNOR->idx) {
rt_ringbuffer_reset(pNOR->rb);
chrg_north_frame_reset(&pNOR->frame, RT_TRUE);
frame_idx = pNOR->idx;
}
put_len = rt_ringbuffer_put(pNOR->rb, pNOR->rx_buf, len);
if (put_len != (rt_size_t)len) {
LOG_E("north ringbuffer overflow: recv=%d put=%d", len, put_len);
+3 -3
View File
@@ -109,7 +109,7 @@ struct chrg_north_t {
struct chrg_tty_t *tty;
struct rt_ringbuffer *rb;
struct pickup_info_t frame;
rt_uint32_t config_version[TOTAL_NOR_CHS]; // COM config generation
rt_uint32_t config_version[TOTAL_NOR_CHS]; // COM配置版本,用于识别等待期间到达的旧回码
struct chrg_trim trim[TOTAL_NOR_CHS];
rt_uint8_t rx_buf[SIZE_BUF_TTY];
rt_uint8_t free_flag;
@@ -127,10 +127,10 @@ extern struct chrg_north_t chrgnorth;
输入参数: frame_data:已通过CRC校验的完整回码 idx:对应通道
pSW:通道控制结构体 pNOR:北向控制结构体
输出参数: 更新Source目标电压、电流和工作状态
返回说明: -
返回说明: 0:回码正常 <0:参数错误、Source切换错误或功率命令提交失败
其它说明: 仅由北向轮询线程在确认回码角色、命令和通道匹配后调用
*****************************************************************/
extern void chrg_source_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
extern int chrg_source_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR);
/*****************************************************************
+470 -89
View File
@@ -24,6 +24,15 @@
#include "chrg_wdt.h"
#define LOG_TAG "chrg.rollnor"
#include <rtdbg.h>
#define NORTH_RESPONSE_TIMEOUT_MS 50U // 单次北向请求等待匹配回码的总时间
#define NORTH_REQUEST_MAX_RETRIES 2U // 首次发送失败后最多重发2次,总发送次数为3次
#define NORTH_RETRY_INTERVAL_MS 5U // 相同请求两次发送之间的保护间隔
#define NORTH_POLL_INTERVAL_MS 10U // 一个通道事务结束后到下一通道的轮询间隔
#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 SOURCE_CHANNEL_BIT(ch) ((rt_uint8_t)(1U << (ch)))
const rt_base_t sw_uart[TOTAL_NOR_CHS] = {
IDX_GPO_UART_SW1,
IDX_GPO_UART_SW2,
@@ -37,6 +46,156 @@ struct chrg_rollnor_t chrgrollnor = {
// .Set_Role = ID_SINK,
// .group = 0,
};
/* 所有参与CC_CLOSE的通道共享300ms屏障;协议等待仍按通道独立计时。 */
static rt_uint8_t g_source_cc_close_pending_mask = 0U;
static rt_uint8_t g_source_cc_close_acked_mask = 0U;
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};
/*****************************************************************
函数名称: chrg_roll_nor_source_switch_reset
函数描述: 清理指定Source通道的CC_CLOSE及协议等待记录
输入参数: ch:北向通道编号
输出参数: 清除对应通道的屏障位和协议等待计时
返回说明: -
其它说明: 调用者必须持有北向轮询线程互斥锁
*****************************************************************/
void chrg_roll_nor_source_switch_reset(rt_uint8_t ch)
{
rt_uint8_t bit;
if (ch >= TOTAL_NOR_CHS) {
return;
}
bit = SOURCE_CHANNEL_BIT(ch);
g_source_cc_close_pending_mask &= (rt_uint8_t)~bit;
g_source_cc_close_acked_mask &= (rt_uint8_t)~bit;
g_source_protocol_wait_mask &= (rt_uint8_t)~bit;
g_source_protocol_wait_start[ch] = 0U;
if (g_source_cc_close_pending_mask == 0U) {
g_source_cc_close_wait_active = RT_FALSE;
g_source_cc_close_wait_start = 0U;
} else if (!g_source_cc_close_wait_active &&
((g_source_cc_close_acked_mask & g_source_cc_close_pending_mask) ==
g_source_cc_close_pending_mask)) {
g_source_cc_close_wait_active = RT_TRUE;
g_source_cc_close_wait_start = rt_tick_get();
}
}
/*****************************************************************
函数名称: chrg_roll_nor_source_cc_close_register
函数描述: 将Source通道登记到本轮共享CC_CLOSE等待屏障
输入参数: ch:北向通道编号
输出参数: 设置pending位并清除该通道旧ACK和等待状态
返回说明: -
其它说明: 新通道加入已经开始的屏障时重新等待,保证所有登记通道共享完整保护时间
*****************************************************************/
void chrg_roll_nor_source_cc_close_register(rt_uint8_t ch)
{
rt_uint8_t bit;
if (ch >= TOTAL_NOR_CHS) {
return;
}
bit = SOURCE_CHANNEL_BIT(ch);
g_source_cc_close_pending_mask |= bit;
g_source_cc_close_acked_mask &= (rt_uint8_t)~bit;
g_source_protocol_wait_mask &= (rt_uint8_t)~bit;
g_source_cc_close_wait_active = RT_FALSE;
g_source_cc_close_wait_start = 0U;
}
static void chrg_roll_nor_source_cc_close_ack(rt_uint8_t ch)
{
rt_uint8_t bit;
if (ch >= TOTAL_NOR_CHS) {
return;
}
bit = SOURCE_CHANNEL_BIT(ch);
if ((g_source_cc_close_pending_mask & bit) == 0U) {
return;
}
g_source_cc_close_acked_mask |= bit;
if (!g_source_cc_close_wait_active &&
((g_source_cc_close_acked_mask & g_source_cc_close_pending_mask) ==
g_source_cc_close_pending_mask)) {
g_source_cc_close_wait_active = RT_TRUE;
g_source_cc_close_wait_start = rt_tick_get();
}
}
static void chrg_roll_nor_source_protocol_wait_start(rt_uint8_t ch)
{
if (ch >= TOTAL_NOR_CHS) {
return;
}
g_source_protocol_wait_mask |= SOURCE_CHANNEL_BIT(ch);
g_source_protocol_wait_start[ch] = rt_tick_get();
}
/*****************************************************************
函数名称: chrg_roll_nor_source_wait_process
函数描述: 处理Source共享CC_CLOSE等待和每通道协议生效等待
输入参数: ch:北向通道编号 pSW:通道状态
输出参数: CC_CLOSE等待到期进入0xD0,协议等待到期进入目标CC设置
返回说明: RT_TRUE:本轮仍处于等待,不发送命令 RT_FALSE:可继续发送
其它说明: CC_CLOSE使用共享300ms屏障,协议ACK后每通道独立等待100ms
*****************************************************************/
static rt_bool_t chrg_roll_nor_source_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_SOURCE) || !pSW->source.On_Flag) {
chrg_roll_nor_source_switch_reset(ch);
return RT_FALSE;
}
if (pSW->sub == IDX_WAIT_SOURCE_CC_CLOSE) {
if ((g_source_cc_close_pending_mask & bit) == 0U) {
pSW->sub = IDX_SET_SOURCE_PD;
return RT_FALSE;
}
if (!g_source_cc_close_wait_active) {
return RT_TRUE;
}
elapsed = (rt_tick_t)(rt_tick_get() - g_source_cc_close_wait_start);
if (elapsed < rt_tick_from_millisecond(SOURCE_CC_CLOSE_WAIT_MS)) {
return RT_TRUE;
}
chrg_roll_nor_source_switch_reset(ch);
pSW->sub = IDX_SET_SOURCE_PD;
return RT_FALSE;
}
if (pSW->sub == IDX_WAIT_SOURCE_PROTOCOL) {
if ((g_source_protocol_wait_mask & bit) == 0U) {
pSW->sub = IDX_SET_SOURCE_CC_SET;
return RT_FALSE;
}
elapsed = (rt_tick_t)(rt_tick_get() -
g_source_protocol_wait_start[ch]);
if (elapsed < rt_tick_from_millisecond(SOURCE_PROTOCOL_WAIT_MS)) {
return RT_TRUE;
}
g_source_protocol_wait_mask &= (rt_uint8_t)~bit;
g_source_protocol_wait_start[ch] = 0U;
pSW->sub = IDX_SET_SOURCE_CC_SET;
return RT_FALSE;
}
return RT_FALSE;
}
/*****************************************************************
函数名称: sink_fill_pd_buf
函数描述: 根据快充协议类型填充PD指令的5字节数据缓冲区
@@ -108,7 +267,7 @@ int sink_fill_pd_buf(rt_uint8_t group,rt_uint8_t pd_val, rt_uint16_t pd_volt, rt
buf[1] = pd_volt & 0xFF; // 第7字节=电压低8位(mV)
buf[2] = (pd_curr >> 8) & 0xFF; // 第8字节=电流高8位(mA)
buf[3] = pd_curr & 0xFF; // 第9字节=电流低8位(mA)
buf[4] = 0;
buf[4] = 0x00;
#if NORTH_SINK_DEBUG
rt_kprintf("type: %02X, Voltage=%d mV, Current=%d mA\n", pd_val, pd_volt, pd_curr);
#endif
@@ -189,20 +348,38 @@ static void chrg_roll_nor_sw_uart (eIDX_NOR_CH ch, eIDX_ID id)
}
//设置负载协议
void chrg_roll_nor_sink_pro_set(struct chrg_switch_t *pSW)
/*****************************************************************
函数名称: chrg_roll_nor_sink_pro_set
函数描述: 根据当前负载协议参数组帧并下发负载协议设置命令
输入参数: pSW:当前通道控制结构体
输出参数: -
返回说明: >0:发送长度 <0:参数组帧或串口发送失败
其它说明: 本函数只负责发送,不修改pSW->sub,状态由有效回码确认后推进
*****************************************************************/
static int chrg_roll_nor_sink_pro_set(struct chrg_switch_t *pSW)
{
rt_uint8_t buf[6] = {0};
sink_fill_pd_buf(pSW->sink.Pro_Group,pSW->sink.protocol,pSW->sink.pro_loadv,pSW->sink.pro_loadc,buf);
sink_set_pd(pSW->sink.protocol,buf);
if (sink_fill_pd_buf(pSW->sink.Pro_Group, pSW->sink.protocol,
pSW->sink.pro_loadv, pSW->sink.pro_loadc, buf) != 0) {
return -1;
}
#if NORTH_SINK_DEBUG
rt_kprintf("set_pro\r\n");
#endif
#endif
return sink_set_pd(pSW->sink.protocol, buf);
}
//设置电源协议
void chrg_roll_nor_source_pro_set(struct chrg_switch_t *pSW){
source_set_pro(pSW->source.protocol,pSW);
/*****************************************************************
函数名称: chrg_roll_nor_source_pro_set
函数描述: 根据当前电源协议参数组帧并下发电源协议设置命令
输入参数: pSW:当前通道控制结构体
输出参数: -
返回说明: >0:发送长度 <0:参数组帧或串口发送失败
其它说明: 本函数只负责发送,不修改pSW->sub,状态由有效回码确认后推进
*****************************************************************/
static int chrg_roll_nor_source_pro_set(struct chrg_switch_t *pSW)
{
return source_set_pro(pSW->source.protocol, pSW);
}
/*****************************************************************
@@ -211,8 +388,8 @@ void chrg_roll_nor_source_pro_set(struct chrg_switch_t *pSW){
输入参数: pSW:当前通道控制结构体
输出参数: -
返回说明: 非0:期望命令码 0:当前状态没有可识别的北向请求
其它说明: 必须在chrg_roll_nor_set推进sub之前调用,确保设置命令的回码
能够与实际发送命令对应
其它说明: 必须在chrg_roll_nor_set发送命令前调用,确保保存的期望命令
与实际发送命令一致
*****************************************************************/
static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW)
{
@@ -228,6 +405,7 @@ static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW)
return SRC_CMD_GET_PROTOCOL;
case IDX_SET_SOURCE_PD:
return SRC_CMD_SET_PROTOCOL;
case IDX_SET_SOURCE_CC_CLOSE:
case IDX_SET_SOURCE_CC_SET:
return SRC_CMD_SET_CC_CHOOSE;
case IDX_SET_SOURCE_CCLVL:
@@ -258,20 +436,17 @@ static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW)
}
/*****************************************************************
函数名称: chrg_roll_nor_handle_response
函数描述: 校验北向回码与当前请求的通道角色和命令是否匹配并分发业务解析
函数名称: chrg_roll_nor_response_matches
函数描述: 校验北向回码与当前请求的通道角色、命令和固定长度是否匹配
输入参数: pMB:已通过north.c结构及CRC校验的回码 expected_idx:请求通道
expected_id:请求角色 expected_cmd:请求命令
输出参数: 匹配的0x20量测回码会更新对应Source/Sink状态机
返回说明: 0:回码匹配并处理完成 <0:参数错误或回码与请求不匹配
其它说明: 设置命令回码只用于确认请求完成;只有21字节0x20回码进入业务解析
输出参数: -
返回说明: 0:回码匹配 <0:参数错误或回码与请求不匹配
其它说明: 本函数只判断请求归属,业务解析在配置版本复核通过后执行
*****************************************************************/
static int chrg_roll_nor_handle_response(struct mb_msg_t *pMB,
static int chrg_roll_nor_response_matches(const struct mb_msg_t *pMB,
rt_uint8_t expected_idx, eIDX_ID expected_id, rt_uint8_t expected_cmd)
{
struct chrg_north_t *pNOR = &chrgnorth;
struct chrg_switch_t *pSW;
if ((pMB == RT_NULL) || (pMB->payload == RT_NULL) ||
(pMB->length < MIN_FRAME_LENGTH) ||
(expected_idx >= TOTAL_NOR_CHS) || (expected_cmd == 0U)) {
@@ -286,24 +461,110 @@ static int chrg_roll_nor_handle_response(struct mb_msg_t *pMB,
return -2;
}
if ((pMB->length != 21U) || (expected_cmd != SRC_CMD_GET_VOLTAGE)) {
return 0;
}
pSW = &pNOR->sw[expected_idx];
/* COM可能在等待回码期间切换角色;旧角色回码不得更新新角色状态。 */
if (pSW->id != expected_id) {
LOG_W("north response role changed: ch=%d old=%02x new=%02x",
expected_idx + 1U, expected_id, pSW->id);
if ((expected_cmd == SRC_CMD_GET_VOLTAGE) && (pMB->length != 21U)) {
return -3;
}
if (expected_id == ID_SOURCE) {
chrg_source_parse(pMB->payload, (eIDX_SOU_CH)expected_idx, pSW, pNOR);
} else if (expected_id == ID_SINK) {
chrg_sink_parse(pMB->payload, (eIDX_SOU_CH)expected_idx, pSW, pNOR);
if ((expected_cmd != SRC_CMD_GET_VOLTAGE) &&
(expected_cmd != SRC_CMD_GET_PROTOCOL) &&
(pMB->length != MIN_FRAME_LENGTH)) {
return -4;
}
return 0;
}
/*****************************************************************
函数名称: chrg_roll_nor_free_response
函数描述: 释放北向邮箱回码及其payload
输入参数: pMB:待释放邮箱消息
输出参数: -
返回说明: -
其它说明: 统一匹配成功、失配和手动模式下的消息释放路径
*****************************************************************/
static void chrg_roll_nor_free_response(struct mb_msg_t *pMB)
{
if (pMB == RT_NULL) {
return;
}
if (pMB->payload != RT_NULL) {
rt_free(pMB->payload);
}
rt_free(pMB);
}
/*****************************************************************
函数名称: chrg_roll_nor_drain_mailbox
函数描述: 非阻塞清理上一事务遗留在北向邮箱中的回码
输入参数: pTN:北向接收线程信息
输出参数: -
返回说明: -
其它说明: 必须在发送新请求前调用,避免旧回码被误认为本次请求回码
*****************************************************************/
static void chrg_roll_nor_drain_mailbox(struct chrg_thread_t *pTN)
{
struct mb_msg_t *pMB = RT_NULL;
if ((pTN == RT_NULL) || (pTN->mb == RT_NULL)) {
return;
}
while (rt_mb_recv(pTN->mb, (rt_uint32_t *)&pMB, 0) == RT_EOK) {
chrg_roll_nor_free_response(pMB);
pMB = RT_NULL;
}
}
/*****************************************************************
函数名称: chrg_roll_nor_wait_response
函数描述: 在统一截止时间内等待与当前角色和命令匹配的北向回码
输入参数: pTN:北向接收线程 expected_idx:请求通道 expected_id:请求角色
expected_cmd:请求命令 matched_response:匹配回码输出指针
输出参数: matched_response返回匹配消息,所有权交给调用者
返回说明: 0:收到匹配回码 <0:参数错误或等待超时
其它说明: 不匹配但CRC正确的旧回码会释放,并继续等待至本次总超时
*****************************************************************/
static int chrg_roll_nor_wait_response(struct chrg_thread_t *pTN,
rt_uint8_t expected_idx, eIDX_ID expected_id, rt_uint8_t expected_cmd,
struct mb_msg_t **matched_response)
{
rt_tick_t start_tick;
rt_tick_t timeout_tick;
rt_tick_t elapsed;
rt_tick_t remaining;
rt_err_t ret;
struct mb_msg_t *pMB = RT_NULL;
if ((pTN == RT_NULL) || (pTN->mb == RT_NULL) ||
(matched_response == RT_NULL)) {
return -1;
}
*matched_response = RT_NULL;
start_tick = rt_tick_get();
timeout_tick = rt_tick_from_millisecond(NORTH_RESPONSE_TIMEOUT_MS);
while (1) {
elapsed = (rt_tick_t)(rt_tick_get() - start_tick);
if (elapsed >= timeout_tick) {
return -1;
}
remaining = timeout_tick - elapsed;
ret = rt_mb_recv(pTN->mb, (rt_uint32_t *)&pMB, remaining);
if (ret != RT_EOK) {
return -1;
}
if (pMB == RT_NULL) {
continue;
}
if (chrg_roll_nor_response_matches(pMB, expected_idx,
expected_id, expected_cmd) == 0) {
*matched_response = pMB;
return 0;
}
/* CRC正确但不属于本次请求的旧回码不能结束当前等待。 */
chrg_roll_nor_free_response(pMB);
pMB = RT_NULL;
}
}
/*****************************************************************
函数名称: chrg_roll_nor_cmd
函数描述: 执行北向手动插入命令的入口检查
@@ -322,56 +583,172 @@ static int chrg_roll_nor_cmd (struct chrg_rollnor_t *pROLL)
/*****************************************************************
函数名称: chrg_roll_nor_set
函数描述: 按当前角色和sub状态发送一条北向协议命令
输入参数: ch:北向通道编号 pSW:通道控制结构体 enable:通道使能指针
输出参数: 某些发送步骤成功发起后推进pSW->sub
返回说明: -
其它说明: Source依次执行CC选择、协议设置、VC查询;
Sink依次执行CC线选择、协议设置、VC查询
输入参数: ch:北向通道编号 pSW:通道控制结构体 next_sub:候选下一状态输出指针
输出参数: next_sub保存正确回码后应进入的状态,不直接修改pSW->sub
返回说明: >0:发送长度 <0:发送失败或状态不支持
其它说明: Source按差异执行协议、CC或组合命令;涉及PD的运行中切换先发送CC_CLOSE;
停止状态启动和主动停止均不额外发送CC_CLOSE;
Sink依次执行CC线选择、协议设置、VC查询
*****************************************************************/
void chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,rt_uint8_t *enable)
static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,
rt_uint8_t *next_sub)
{
if ((pSW == RT_NULL) || (next_sub == RT_NULL)) {
return -1;
}
*next_sub = pSW->sub;
if(pSW->id==ID_SOURCE){ //电源
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);
pSW->sub = IDX_SET_SOURCE_PD;
break;
case IDX_SET_SOURCE_CC_CLOSE:
*next_sub = IDX_WAIT_SOURCE_CC_CLOSE;
return source_set_cc_choose(SOURCE_CC_CLOSE_VALUE);
case IDX_SET_SOURCE_PD:
chrg_roll_nor_source_pro_set(pSW);
pSW->sub = IDX_GET_SOURCE_VC;
break;
*next_sub = IDX_WAIT_SOURCE_PROTOCOL;
return chrg_roll_nor_source_pro_set(pSW);
case IDX_SET_SOURCE_CC_SET:
*next_sub = IDX_GET_SOURCE_VC;
return source_set_cc_choose(pSW->source.CC_Set);
case IDX_GET_SOURCE_VC:
source_get_vc(pSW->source.set_volt, pSW->source.set_curr);
//pSW->sub = IDX_GET_SOURCE_PD;
break;
return source_get_vc(pSW->source.set_volt,
pSW->source.set_curr);
case IDX_GET_SOURCE_PD:
source_get_pd();
pSW->sub = IDX_GET_SOURCE_VC;
*next_sub = IDX_GET_SOURCE_VC;
return source_get_pd();
default:
break;
return -1;
}
}
else{
else if (pSW->id == ID_SINK) {
chrg_roll_nor_sw_uart((eIDX_NOR_CH)ch, ID_SINK);
switch(pSW->sub){
case IDX_GET_SINK_VC: //查询
sink_get_vc(pSW->sink.pro_loadv,pSW->sink.pro_loadc);
break;
return sink_get_vc(pSW->sink.pro_loadv,
pSW->sink.pro_loadc);
case IDX_GET_SINK_PD:
*next_sub = IDX_GET_SINK_VC;
return sink_get_pd();
case IDX_SET_SINK_CCLINE: //CC线选择
sink_set_cc_line(pSW->sink.cc_set);
pSW->sub = IDX_SET_SINK_PD;
break;
*next_sub = IDX_SET_SINK_PD;
return sink_set_cc_line(pSW->sink.cc_set);
case IDX_SET_SINK_CCLVL: //CC电平
sink_set_cc_lvl(pSW->sink.cc_lvl);
break;
return sink_set_cc_lvl(pSW->sink.cc_lvl);
case IDX_SET_SINK_PD:
chrg_roll_nor_sink_pro_set(pSW);
pSW->sub = IDX_GET_SINK_VC;
break;
*next_sub = IDX_GET_SINK_VC;
return chrg_roll_nor_sink_pro_set(pSW);
default:
return -1;
}
}
return -1;
}
/*****************************************************************
函数名称: chrg_roll_nor_auto_request
函数描述: 执行一次自动北向请求事务,并在超时后有限次数重发
输入参数: idx:通道 pSW:通道状态 pTHR:轮询线程 pTN:北向接收线程
pNOR:北向共享状态
输出参数: -
返回说明: 0:请求成功 1:等待期间COM配置变化,旧事务已取消 <0:重试后失败
其它说明: 匹配回码有效且配置版本未变化时解析量测值并提交next_sub;
发送时不修改实时sub,CRC错误帧由north.c丢弃并表现为本次等待超时
*****************************************************************/
static int chrg_roll_nor_auto_request(rt_uint8_t idx,
struct chrg_switch_t *pSW, struct chrg_thread_t *pTHR,
struct chrg_thread_t *pTN, struct chrg_north_t *pNOR)
{
rt_uint8_t attempt; // 当前发送序号:0为首次,1~2为重发
rt_uint8_t request_sub = 0; // 本次请求实际使用的sub快照
rt_uint8_t next_sub = 0; // 正确回码后准备提交的候选sub
rt_uint8_t expected_cmd = 0; // 本次请求期望收到的命令码
eIDX_ID expected_id = ID_SINK; // 本次请求发送时的角色快照
rt_uint32_t request_version = 0; // 本次请求发送时的COM配置版本
int send_ret; // 当前命令的串口发送结果
int wait_ret; // 当前命令的回码等待结果
struct mb_msg_t *pMB = RT_NULL; // 已匹配回码,处理后统一释放
if ((idx >= TOTAL_NOR_CHS) || (pSW == RT_NULL) ||
(pTHR == RT_NULL) || (pTN == RT_NULL) || (pNOR == RT_NULL)) {
return -1;
}
for (attempt = 0; attempt <= NORTH_REQUEST_MAX_RETRIES; attempt++) {
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
if (attempt == 0U) {
/* 首次发送固定请求状态,后续重发必须继续使用同一份请求上下文。 */
request_sub = pSW->sub;
expected_id = pSW->id;
expected_cmd = chrg_roll_nor_expected_cmd(pSW);
request_version = pNOR->config_version[idx];
/* 新事务发送前清理已结束或已超时事务遗留的邮箱回码。 */
chrg_roll_nor_drain_mailbox(pTN);
} else if ((pNOR->config_version[idx] != request_version) ||
(pSW->id != expected_id) || (pSW->sub != request_sub)) {
/* COM或其它控制入口已修改配置,禁止继续重发旧命令。 */
rt_mutex_release(pTHR->mutex);
return 1;
}
if (expected_cmd == 0U) {
rt_mutex_release(pTHR->mutex);
return -1;
}
send_ret = chrg_roll_nor_set(idx, pSW, &next_sub);
rt_mutex_release(pTHR->mutex);
if (send_ret > 0) {
wait_ret = chrg_roll_nor_wait_response(pTN, idx,
expected_id, expected_cmd, &pMB);
if ((wait_ret == 0) && (pMB != RT_NULL)) {
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
if ((pNOR->config_version[idx] != request_version) ||
(pSW->id != expected_id) || (pSW->sub != request_sub)) {
/* 回码到达前配置已变化,该回码属于旧事务,只释放不解析。 */
rt_mutex_release(pTHR->mutex);
chrg_roll_nor_free_response(pMB);
return 1;
}
/* 只有固定21字节的0x20量测回码需要进入Source/Sink业务解析。 */
if ((expected_cmd == SRC_CMD_GET_VOLTAGE) &&
(pMB->length == 21U)) {
if (expected_id == ID_SOURCE) {
(void)chrg_source_parse(pMB->payload,
(eIDX_SOU_CH)idx, pSW, pNOR);
} else {
chrg_sink_parse(pMB->payload,
(eIDX_SOU_CH)idx, pSW, pNOR);
}
}
/* ID、命令、CRC和配置版本均确认有效后,才正式推进实时sub。 */
if ((expected_id == ID_SOURCE) &&
(request_sub == IDX_SET_SOURCE_CC_CLOSE)) {
/* 参与通道全部收到0x17=3应答后,共享一次300ms保护等待。 */
chrg_roll_nor_source_cc_close_ack(idx);
} else if ((expected_id == ID_SOURCE) &&
(request_sub == IDX_SET_SOURCE_PD)) {
/* 0xD0应答后每通道独立等待100ms,再连接目标CC。 */
chrg_roll_nor_source_protocol_wait_start(idx);
}
pSW->sub = next_sub;
rt_mutex_release(pTHR->mutex);
chrg_roll_nor_free_response(pMB);
return 0;
}
}
if (attempt < NORTH_REQUEST_MAX_RETRIES) {
/* 本次发送失败或等待超时,保持request_sub并准备重发同一命令。 */
rt_thread_mdelay(NORTH_RETRY_INTERVAL_MS);
}
}
LOG_W("north request failed: ch=%d id=%02x cmd=%02x sub=%d attempts=%d",
idx + 1U, expected_id, expected_cmd, request_sub,
NORTH_REQUEST_MAX_RETRIES + 1U);
return -1;
}
/*****************************************************************
@@ -392,9 +769,8 @@ void chrg_roll_nor_thread_entry (void *data)
rt_uint8_t index = 0, idx = 0, i = 0;
rt_uint8_t run = 0;
rt_uint8_t expected_idx = 0;
rt_uint8_t expected_cmd = 0;
eIDX_ID expected_id = ID_SINK;
rt_bool_t source_waiting = RT_FALSE;
int source_work_ret = 0;
rt_err_t ret = RT_EOK;
struct chrg_rollnor_t *pROLL = &chrgrollnor;
struct chrg_thread_t *pTN = &chrgthr[IDX_THR_NORTH];
@@ -406,7 +782,6 @@ void chrg_roll_nor_thread_entry (void *data)
}
while (1) {
chrg_wdt_feed();
expected_cmd = 0;
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
run = pROLL->run;
rt_mutex_release(pTHR->mutex);
@@ -414,6 +789,8 @@ void chrg_roll_nor_thread_entry (void *data)
case RUN_AUTO: {
idx = index%TOTAL_NOR_CHS;
pSW = &pNOR->sw[idx];
source_waiting = RT_FALSE;
source_work_ret = 0;
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
/*
* INIT/STOP必须在enable过滤前执行:STOP会把enable清零,而COM
@@ -423,13 +800,30 @@ void chrg_roll_nor_thread_entry (void *data)
*/
if ((pSW->id == ID_SOURCE) &&
((pSW->source.work_mode == SOURCE_WORK_INIT) ||
(pSW->source.work_mode == SOURCE_WORK_STOP))) {
chrg_source_work_state(idx, pSW);
(pSW->source.work_mode == SOURCE_WORK_STOP) ||
(pSW->source.work_mode == SOURCE_WORK_PROTOCOL_SWITCH))) {
source_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);
}
/*
* 协议切换等待只推进时间状态,不发送串口命令。该检查位于enable
* 过滤前,使停止或角色切换后的通道也能及时清理残留事务记录。
*/
source_waiting = chrg_roll_nor_source_wait_process(idx, pSW);
rt_mutex_release(pTHR->mutex);
if (source_work_ret != 0) {
/* 南向状态动作未成功入队时保留原状态,下轮重试且禁止北向先行。 */
index++;
rt_thread_mdelay(2);
continue;
}
if (source_waiting) {
index++;
rt_thread_mdelay(2);
continue;
}
if (0 == pNOR->enable[idx]) {
index++;
rt_thread_mdelay(2);
@@ -439,15 +833,12 @@ void chrg_roll_nor_thread_entry (void *data)
chrg_north_switch((eIDX_NOR_CH)idx);
chrgnorth.idx = idx;
pROLL->idx = idx;
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
/* sub会在发送函数中立即推进,因此必须在发送前保存请求上下文。 */
expected_idx = idx;
expected_id = pSW->id;
expected_cmd = chrg_roll_nor_expected_cmd(pSW);
chrg_roll_nor_set(pROLL->idx,pSW,&pNOR->enable[idx]);
rt_mutex_release(pTHR->mutex);
(void)chrg_roll_nor_auto_request(idx, pSW,
pTHR, pTN, pNOR);
}
index++;
rt_thread_mdelay(NORTH_POLL_INTERVAL_MS);
continue;
}
break;
@@ -478,20 +869,10 @@ void chrg_roll_nor_thread_entry (void *data)
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
pROLL->run = RUN_AUTO;
rt_mutex_release(pTHR->mutex);
} else {
(void)chrg_roll_nor_handle_response(pMB_R, expected_idx,
expected_id, expected_cmd);
}
if (RT_NULL != pMB_R->payload) {
rt_free(pMB_R->payload);
pMB_R->payload = RT_NULL;
}
if (RT_NULL != pMB_R) {
rt_free(pMB_R);
pMB_R = RT_NULL;
}
chrg_roll_nor_free_response(pMB_R);
pMB_R = RT_NULL;
rt_thread_mdelay(10); // 相邻北向请求间隔50ms
} else {
@@ -41,6 +41,11 @@ enum {
IDX_SET_SINK_WORK_STATE,
IDX_SET_SINK_STOP_TEST,//停止负载测试
IDX_SET_SINK_STORP_WAIT,
/* Source北向事务状态,追加在末尾以保持原有sub数值不变。 */
IDX_SET_SOURCE_CC_CLOSE, // 协议切换前发送0x17=0x03断开CC
IDX_WAIT_SOURCE_CC_CLOSE, // 等待参与切换的Source通道完成共享保护时间
IDX_WAIT_SOURCE_PROTOCOL, // 0xD0应答后的协议生效等待
};
struct chrg_rollnor_t {
@@ -81,6 +86,27 @@ extern void chrg_roll_nor_thread_entry (void *data);
extern int sink_fill_pd_buf(rt_uint8_t group,rt_uint8_t pd_val, rt_uint16_t pd_volt, rt_uint16_t pd_curr, rt_uint8_t *buf);
extern void roll_nor_set(void);
/*****************************************************************
函数名称: chrg_roll_nor_source_cc_close_register
函数描述: 将Source通道登记到本轮共享CC_CLOSE等待屏障
输入参数: ch:北向通道编号
输出参数: -
返回说明: -
其它说明: 仅需要在协议切换入口为IDX_SET_SOURCE_CC_CLOSE时调用
*****************************************************************/
extern void chrg_roll_nor_source_cc_close_register(rt_uint8_t ch);
/*****************************************************************
函数名称: chrg_roll_nor_source_switch_reset
函数描述: 清理指定Source通道的CC_CLOSE及协议等待记录
输入参数: ch:北向通道编号
输出参数: -
返回说明: -
其它说明: 停止、角色切换或新配置覆盖旧切换状态时调用
*****************************************************************/
extern void chrg_roll_nor_source_switch_reset(rt_uint8_t ch);
#define sink_time 3
#endif
+20 -8
View File
@@ -18,6 +18,9 @@
#include "chrg_regs.h"
#include "chrg_utils.h"
#include "chrg_south.h"
#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);
static rt_uint16_t ripple_base[TOTAL_SOU_CHS] = {0};
static rt_uint8_t ripple_cal_flag[TOTAL_SOU_CHS] = {0};
@@ -313,6 +316,20 @@ int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16
pLOAD->vpkn = swap_u16(pP1->Vpkn);
pLOAD->vpp = swap_u16(pP1->Vpp);
pLOAD->Ripple = swap_u16(pP1->Ripple);
/*
* 低于2V时直接清零量测结果。只清理量测字段,保留工作状态、
* 故障、模式及测试结果,避免影响通道状态判断。
*/
if (pLOAD->voltage < ELOAD_VALID_VOLTAGE_MIN_MV) {
pLOAD->voltage = 0;
pLOAD->current = 0;
pLOAD->vpkp = 0;
pLOAD->vpkn = 0;
pLOAD->vpp = 0;
pLOAD->Ripple = 0;
pSW->VZ = 0;
pSW->VF = 0;
}
pSW->Now_voltage = pLOAD->voltage;
pSW->Now_current = pLOAD->current;
#if LCD_OPEN
@@ -374,17 +391,12 @@ void Show_Curr(rt_uint8_t idx, struct chrg_eload_t *pLOAD, char *tmp, rt_uint8_t
}
void Show_Ripple(rt_uint8_t idx, struct chrg_eload_t *pLOAD, char *tmp, rt_uint8_t online,struct chrg_switch_t *pSW)
{
rt_int32_t ripple_rand = rand() % 11; // 生成0到10之间的随机数
if(online == 0){
if ((online == 0) ||
(pSW->Now_voltage < ELOAD_VALID_VOLTAGE_MIN_MV)) {
snprintf(tmp, 36, "status.CH%d_Ripple.val=0",idx+1);
return;
}else{
if(pSW->Now_voltage<200){
snprintf(tmp, 36, "status.CH%d_Ripple.val=%d",idx+1, ripple_rand);
}else{
snprintf(tmp, 36, "status.CH%d_Ripple.val=%d",idx+1, pLOAD->Ripple);
}
}
snprintf(tmp, 36, "status.CH%d_Ripple.val=%d",idx+1, pLOAD->Ripple);
}
void Show_DZF(rt_uint8_t idx, struct chrg_eload_t *pLOAD, char *tmp, rt_uint8_t online,rt_uint32_t vzf)
{
+25
View File
@@ -277,6 +277,28 @@ static int chrg_sink_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
return 0;
}
/*****************************************************************
函数名称: chrg_sink_work_protocol_unload
函数描述: 协议或CC线变化前的卸载状态:停止负载、电流归零、断开功率继电器
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: -
返回说明: 0:命令提交成功 <0:命令提交失败
其它说明: 不操作北向继电器,仅卸载功率板侧;后续由WAIT→北向线程重配
CC/协议,再由RUNING状态闭合继电器并恢复目标负载电流
*****************************************************************/
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};
if (chrg_sou_com_batch_submit(idx, regs, vals, 3) != 0) {
return -1;
}
pSW->sink.work_mode = SINK_WORK_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_sink_TEST_DYNA_ON
函数描述: 配置并启动动态负载测试
@@ -409,6 +431,9 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
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;
default:
break;
}
+1
View File
@@ -209,6 +209,7 @@ enum {
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过流保护测试
+55 -6
View File
@@ -84,6 +84,7 @@ static int chrg_source_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
}
pSW->source.On_Flag = 1;
pSW->source.qc_err = 0;
pNOR->enable[idx] = 1;
pSOU->online[idx] = 1;
chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SOURCE);
@@ -128,14 +129,58 @@ static int chrg_source_work_running(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
return chrg_sou_com_batch_submit(idx, &reg, &value, 1);
}
/*****************************************************************
函数名称: chrg_source_work_protocol_switch
函数描述: 执行电源协议或CC切换前的功率板输出清零
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 清零source.set_volt,并在命令入队成功后进入WAIT状态
返回说明: 0:清零命令提交成功 非0:状态或命令提交失败
其它说明: COM在进入本状态前必须将sub设置为协议、CC或组合事务入口;
本状态只管理南向清零,不修改北向sub,
标准协议同时按0V、模式、电流顺序入队,保证南向命令顺序固定
*****************************************************************/
static int chrg_source_work_protocol_switch(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW)
{
rt_uint16_t regs[3];
rt_uint16_t vals[3];
rt_uint8_t count = 0;
int ret;
if ((pSW->sub != IDX_SET_SOURCE_PD) &&
(pSW->sub != IDX_SET_SOURCE_CC_CLOSE)) {
return -1;
}
regs[count] = REG_VOLTAGE_OUT + 1;
vals[count++] = 0;
if (pSW->source.protocol == PRO_NONE) {
/* 标准协议切换时,模式和限流值必须排在0V命令之后生效。 */
regs[count] = REG_MODE;
vals[count++] = pSW->CV_mode;
regs[count] = REG_CURRENT_OUT + 1;
vals[count++] = pSW->source.pd_set.mm.max_cur;
}
/* 只有整批命令成功加入南向队列后,才允许北向继续协议切换。 */
ret = chrg_sou_com_batch_submit(idx, regs, vals, count);
if (ret != 0) {
return ret;
}
pSW->source.set_volt = 0;
pSW->source.work_mode = SOURCE_WORK_WAIT;
return 0;
}
/*****************************************************************
函数名称: chrg_source_work_stop
函数描述: 执行电源主动关机流程
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 清除目标电压、运行、使能和在线标志,关闭北向继电器
输出参数: 清除目标电压、运行标志、通道使能和在线状态
返回说明: 0:停止成功 非0:停止命令提交失败
其它说明: 依次关闭功率板两路继电器、清零电压并发送WORK_STOP;
只有整批命令入队成功后才清零set_volt
停止命令成功入队后立即关闭北向通道,不额外发送CC_CLOSE
*****************************************************************/
static int chrg_source_work_stop(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
@@ -154,9 +199,11 @@ static int chrg_source_work_stop(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
pSW->source.set_volt = 0;
pSW->source.On_Flag = 0;
pNOR->enable[idx] = 0;
pSW->source.qc_err = 0;
pNOR->enable[idx] = 0;
pSOU->online[idx] = 0;
chrg_nor_sw_rel((eIDX_NOR_CH)idx, 0);
pSW->sub = IDX_SET_SOURCE_STOP_STATE;
pSW->source.work_mode = SOURCE_WORK_WAIT;
return 0;
}
@@ -184,8 +231,8 @@ static int chrg_source_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
/* 停止成功入队后,清除旧Sink运行标志。 */
pSW->sink.On_work = 0;
pSW->source.set_volt = 0;
/* 首次进入Source时必须从CC线配置开始,不能沿用Sink的sub状态。 */
pSW->sub = IDX_SET_SOURCE_CC_SET;
/* 首次进入Source按“设置协议->连接CC”启动,不能沿用Sink的sub状态。 */
pSW->sub = IDX_SET_SOURCE_PD;
/* SWITCH只负责旧角色退出,后续启动统一交给Source INIT状态。 */
pSW->source.work_mode = SOURCE_WORK_INIT;
return 0;
@@ -197,7 +244,7 @@ static int chrg_source_work_switch(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
输入参数: idx:南向功率通道编号 pSW:当前通道控制结构体
输出参数: 根据source.work_mode执行对应状态动作
返回说明: 0:状态处理成功 <0:参数、状态或命令提交失败
其它说明: 支持INIT、WAIT、RUNING、LOW、STOP和SWITCH状态
其它说明: 支持INIT、WAIT、RUNING、STOP、SWITCH和PROTOCOL_SWITCH状态
*****************************************************************/
int chrg_source_work_state(rt_uint8_t idx, struct chrg_switch_t *pSW)
{
@@ -217,6 +264,8 @@ int chrg_source_work_state(rt_uint8_t idx, struct chrg_switch_t *pSW)
return chrg_source_work_stop(idx, pSW);
case SOURCE_WORK_SWITCH:
return chrg_source_work_switch(idx, pSW);
case SOURCE_WORK_PROTOCOL_SWITCH:
return chrg_source_work_protocol_switch(idx, pSW);
default:
return -1;
}
@@ -151,6 +151,7 @@ struct chrg_src_t {
rt_uint8_t LCD_Set_Pro_Flag; // LCD设置协议标志位
rt_uint8_t change_sink_Flag; // 从source切换到sink标志位
rt_uint8_t work_mode;
rt_uint8_t qc_err; // Source板0x20回码错误码,bit2表示协议/CC切换失败
rt_uint16_t set_volt; // 北向解析后准备写入功率板的目标电压(mV)
rt_uint16_t set_curr; // 北向解析后准备写入功率板的目标电流(mA)
struct src_vc_t vc; // 北向回读数据
@@ -165,6 +166,7 @@ enum {
SOURCE_WORK_LOW, // 电压过低,停止功率板输出
SOURCE_WORK_STOP, // 主动停止并关闭通道
SOURCE_WORK_SWITCH, // 停止旧负载后进入电源INIT
SOURCE_WORK_PROTOCOL_SWITCH, // 协议/CC切换前清零功率板输出
SOURCE_WORK_ON_TEST_DYNA,
SOURCE_WORK_ON_TEST_SHORT,
};
+5 -5
View File
@@ -443,16 +443,16 @@
<Group>
<GroupName>Applications/thread</GroupName>
<Files>
<File>
<FileName>chrg_north.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_north.c</FilePath>
</File>
<File>
<FileName>chrg_roll_nor.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_roll_nor.c</FilePath>
</File>
<File>
<FileName>chrg_north.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_north.c</FilePath>
</File>
<File>
<FileName>chrg_south.c</FileName>
<FileType>1</FileType>
+2 -14
View File
@@ -1,21 +1,9 @@
*** Using Compiler 'V6.16', folder: 'D:\keil mdk 2\ARM\ARMCLANG\Bin'
Build target 'chrg'
compiling chrg_vcom.c...
compiling chrg_north_pkg.c...
compiling chrg_thread.c...
compiling chrg_lcd.c...
compiling chrg_sink.c...
compiling chrg_rel.c...
compiling chrg_eload.c...
compiling chrg_south.c...
compiling chrg_roll_nor.c...
compiling chrg_north.c...
compiling chrg_source.c...
compiling chrg_roll_sou.c...
compiling chrg_comm.c...
linking...
Program Size: Code=142248 RO-data=15232 RW-data=9304 ZI-data=16016
Program Size: Code=144648 RO-data=15288 RW-data=9328 ZI-data=15824
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).
Build Time Elapsed: 00:00:01
Build Time Elapsed: 00:00:02