1. apps/chrg/applications/thread/chrg_north.h:调整北向调试宏默认配置 2. apps/chrg/applications/utils/chrg_source.h:新增Source全复位与初始化等待状态枚举 3. apps/chrg/applications/bsp/chrg_gpio.h:补充北向继电器引脚注释,明确功能对应关系 4. apps/chrg/applications/thread/chrg_comm.c:优化Source复位触发条件,仅非PD转PD执行全复位 5. apps/chrg/applications/utils/chrg_eload.c/h:新增南向板版本读取与解析接口,新增版本就绪校验逻辑 6. apps/boot/application/main.c:完善boot跳转APP逻辑,注释原循环运行代码 7. apps/chrg/applications/thread/chrg_roll_nor.c:新增INIT_WAIT状态处理,调整复位后流程与sub推进逻辑 8. apps/chrg/applications/thread/chrg_roll_sou.c:新增南向版本就绪标记,上电先读取版本再启动常规采集 9. apps/chrg/applications/utils/chrg_source.c:实现全复位流程与初始化等待状态处理函数
1267 lines
43 KiB
C
1267 lines
43 KiB
C
/****************************************************************************
|
||
文件名称 : chrg_roll_nor.c
|
||
完成日期 :
|
||
当前版本号 : V1.1.8
|
||
主要功能 : 实现北向通信轮巡请求命令。以独立线程处理。
|
||
版本历史 : 创建原始版本
|
||
说明 :
|
||
******************************************************************************/
|
||
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <rtthread.h>
|
||
|
||
#include "chrg_gpio.h"
|
||
#include "chrg_led.h"
|
||
#include "chrg_thread.h"
|
||
#include "chrg_sink.h"
|
||
#include "chrg_source.h"
|
||
#include "chrg_roll_nor.h"
|
||
#include "chrg_roll_sou.h"
|
||
#include "chrg_comm.h"
|
||
#include "chrg_north.h"
|
||
#include "chrg_switch.h"
|
||
#include "chrg_utils.h"
|
||
#include "chrg_rel.h"
|
||
#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_MEASURE_MAX_ATTEMPTS 1U // 0x20量测查询失败后直接留给下一轮轮询重发
|
||
#define NORTH_RETRY_INTERVAL_MS 5U // 相同请求两次发送之间的保护间隔
|
||
#define NORTH_POLL_INTERVAL_MS 10U // 一个通道事务结束后到下一通道的轮询间隔
|
||
#define SOURCE_PROTOCOL_WAIT_MS 100U // 每通道0xD0应答后的协议生效保护时间
|
||
#define SINK_CC_WAIT_MS 50U // Sink设置目标CC应答后的非阻塞保护时间
|
||
#define SINK_HANDSHAKE_FIRST_QUERY_DELAY_MS 50U // 协议/CC回码后首条0x20的等待时间
|
||
#define SINK_HANDSHAKE_QUERY_INTERVAL_MS 50U // 握手未完成时后续0x20查询间隔
|
||
#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,
|
||
IDX_GPO_UART_SW3,
|
||
IDX_GPO_UART_SW4
|
||
};
|
||
|
||
struct chrg_rollnor_t chrgrollnor = {
|
||
.run = RUN_CONSOLE, // RUN_AUTO,
|
||
.ch = IDX_NOR_CH1,
|
||
// .Set_Role = ID_SINK,
|
||
// .group = 0,
|
||
};
|
||
|
||
/* 所有参与CC_CLOSE的通道共享300ms屏障;协议等待仍按通道独立计时。 */
|
||
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};
|
||
/* Sink协议/CC握手期间的0x20查询节流记录,按通道独立计时。 */
|
||
static rt_bool_t g_sink_query_throttle[TOTAL_NOR_CHS] = {RT_FALSE};
|
||
static rt_bool_t g_sink_query_first_pending[TOTAL_NOR_CHS] = {RT_FALSE};
|
||
static rt_tick_t g_sink_query_tick[TOTAL_NOR_CHS] = {0};
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_sink_query_is_slow
|
||
函数描述: 判断当前Sink状态是否仍处于协议握手量测阶段
|
||
输入参数: pSW:当前负载通道控制结构体
|
||
输出参数: -
|
||
返回说明: RT_TRUE:0x20查询按握手节奏发送 RT_FALSE:使用正常轮询周期
|
||
其它说明: 只有协议/CC设置后等待协商电压的阶段限速;RUNING和低电流
|
||
待机不受影响,避免影响正常量测刷新速度
|
||
*****************************************************************/
|
||
static rt_bool_t chrg_roll_nor_sink_query_is_slow(
|
||
const struct chrg_switch_t *pSW)
|
||
{
|
||
if (pSW == RT_NULL) {
|
||
return RT_FALSE;
|
||
}
|
||
return (pSW->sink.work_mode == SINK_WORK_WAIT) ||
|
||
(pSW->sink.work_mode == SINK_WORK_INIT_AUX_WAIT) ||
|
||
(pSW->sink.work_mode == SINK_WORK_LOW_MONITOR);
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_sink_query_wait_start
|
||
函数描述: 启动Sink握手阶段的0x20查询节流计时
|
||
输入参数: ch:北向通道编号
|
||
输出参数: 记录当前tick并开启节流
|
||
返回说明: -
|
||
其它说明: 协议或CC回码成功后调用,避免下一轮立即查询协议板
|
||
*****************************************************************/
|
||
static void chrg_roll_nor_sink_query_wait_start(rt_uint8_t ch)
|
||
{
|
||
if (ch >= TOTAL_NOR_CHS) {
|
||
return;
|
||
}
|
||
g_sink_query_throttle[ch] = RT_TRUE;
|
||
g_sink_query_first_pending[ch] = RT_TRUE;
|
||
g_sink_query_tick[ch] = rt_tick_get();
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_source_switch_reset
|
||
函数描述: 清理指定Source通道的CC_CLOSE及协议等待记录
|
||
输入参数: ch:北向通道编号
|
||
输出参数: 清除对应通道的屏障位和协议等待计时
|
||
返回说明: -
|
||
其它说明: 调用者必须持有北向轮询线程互斥锁
|
||
*****************************************************************/
|
||
void chrg_roll_nor_source_switch_reset(rt_uint8_t ch)
|
||
{
|
||
if (ch >= TOTAL_NOR_CHS) {
|
||
return;
|
||
}
|
||
g_source_protocol_wait_mask &=
|
||
(rt_uint8_t)~SOURCE_CHANNEL_BIT(ch);
|
||
g_source_protocol_wait_start[ch] = 0U;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_source_cc_close_register
|
||
函数描述: 将Source通道登记到本轮共享CC_CLOSE等待屏障
|
||
输入参数: ch:北向通道编号
|
||
输出参数: 设置pending位并清除该通道旧ACK和等待状态
|
||
返回说明: -
|
||
其它说明: 新通道加入已经开始的屏障时重新等待,保证所有登记通道共享完整保护时间
|
||
*****************************************************************/
|
||
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->source.work_mode == SOURCE_WORK_INIT_WAIT) {
|
||
/* 南向初始化批次未完成时,禁止发送北向协议命令。 */
|
||
return RT_TRUE;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: 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;低电流和CC均满足后允许查询
|
||
返回说明: RT_TRUE:本轮仍需等待,不发送北向命令 RT_FALSE:可继续发送
|
||
其它说明: 完整重握手中,南向批次完成后协议/CC与低电流确认并行;
|
||
进入GET_SINK_VC后,必须等低电流确认完成才允许发送0x20
|
||
*****************************************************************/
|
||
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->sink.work_mode == SINK_WORK_STOP_WAIT) ||
|
||
(pSW->sink.work_mode == SINK_WORK_STOP_RELAY) ||
|
||
(pSW->sink.work_mode == SINK_WORK_STOP_RELAY_WAIT)) {
|
||
return RT_TRUE;
|
||
}
|
||
|
||
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;
|
||
g_sink_query_throttle[ch] = RT_FALSE;
|
||
g_sink_query_first_pending[ch] = RT_FALSE;
|
||
g_sink_query_tick[ch] = 0U;
|
||
return RT_FALSE;
|
||
}
|
||
/* 普通软卸载和低电流恢复仍禁止北向;INIT_AUX_WAIT允许南北向并行。 */
|
||
if ((pSW->sink.work_mode == SINK_WORK_UNLOAD_WAIT) ||
|
||
(pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT)) {
|
||
return RT_TRUE;
|
||
}
|
||
|
||
/* 协议或CC切换必须先完成低电流确认;该过程保持南向继电器状态不变。 */
|
||
if (pSW->sink.work_mode == SINK_WORK_HANDSHAKE_WAIT) {
|
||
return RT_TRUE;
|
||
}
|
||
|
||
/* D0回码确认后先经过一个非发送状态,禁止把协议回码当成量测回码。 */
|
||
if (pSW->sub == IDX_WAIT_SINK_PD_ACK) {
|
||
pSW->sub = IDX_GET_SINK_VC;
|
||
chrg_roll_nor_sink_query_wait_start(ch);
|
||
return RT_TRUE;
|
||
}
|
||
|
||
if ((pSW->sub != IDX_WAIT_SINK_CC) &&
|
||
(pSW->sub != IDX_WAIT_SINK_CC_BEFORE_PD)) {
|
||
/* COM在等待期间提交新配置时,清理旧事务留下的CC计时记录。 */
|
||
g_sink_cc_wait_mask &= (rt_uint8_t)~bit;
|
||
g_sink_cc_wait_start[ch] = 0U;
|
||
if (!chrg_roll_nor_sink_query_is_slow(pSW)) {
|
||
g_sink_query_throttle[ch] = RT_FALSE;
|
||
g_sink_query_first_pending[ch] = RT_FALSE;
|
||
g_sink_query_tick[ch] = 0U;
|
||
}
|
||
} else {
|
||
if ((g_sink_cc_wait_mask & bit) == 0U) {
|
||
if (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) {
|
||
pSW->sub = IDX_SET_SINK_PD;
|
||
} else {
|
||
pSW->sub = IDX_GET_SINK_VC;
|
||
chrg_roll_nor_sink_query_wait_start(ch);
|
||
}
|
||
return RT_TRUE;
|
||
}
|
||
elapsed = (rt_tick_t)(rt_tick_get() - g_sink_cc_wait_start[ch]);
|
||
if (elapsed < rt_tick_from_millisecond(SINK_CC_WAIT_MS)) {
|
||
return RT_TRUE;
|
||
}
|
||
|
||
if (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) {
|
||
/* CC先设置完成后进入协议设置,不立即查询电压。 */
|
||
g_sink_query_throttle[ch] = RT_FALSE;
|
||
g_sink_query_first_pending[ch] = RT_FALSE;
|
||
g_sink_query_tick[ch] = 0U;
|
||
g_sink_cc_wait_mask &= (rt_uint8_t)~bit;
|
||
g_sink_cc_wait_start[ch] = 0U;
|
||
pSW->sub = IDX_SET_SINK_PD;
|
||
return RT_TRUE;
|
||
}
|
||
|
||
/* 协议已先设置;CC应答并完成保护等待后进入电压查询。 */
|
||
g_sink_query_throttle[ch] = RT_TRUE;
|
||
g_sink_query_first_pending[ch] = RT_TRUE;
|
||
g_sink_query_tick[ch] = g_sink_cc_wait_start[ch];
|
||
g_sink_cc_wait_mask &= (rt_uint8_t)~bit;
|
||
g_sink_cc_wait_start[ch] = 0U;
|
||
pSW->sub = IDX_GET_SINK_VC;
|
||
return RT_TRUE;
|
||
}
|
||
|
||
/* 首条0x20等待200ms;握手未稳定时,后续查询间隔缩短为100ms。 */
|
||
if (chrg_roll_nor_sink_query_is_slow(pSW) &&
|
||
(pSW->sub == IDX_GET_SINK_VC)) {
|
||
rt_uint32_t query_interval_ms;
|
||
|
||
if (!g_sink_query_throttle[ch]) {
|
||
chrg_roll_nor_sink_query_wait_start(ch);
|
||
return RT_TRUE;
|
||
}
|
||
query_interval_ms = g_sink_query_first_pending[ch] ?
|
||
SINK_HANDSHAKE_FIRST_QUERY_DELAY_MS :
|
||
SINK_HANDSHAKE_QUERY_INTERVAL_MS;
|
||
elapsed = (rt_tick_t)(rt_tick_get() - g_sink_query_tick[ch]);
|
||
if (elapsed < rt_tick_from_millisecond(query_interval_ms)) {
|
||
return RT_TRUE;
|
||
}
|
||
g_sink_query_first_pending[ch] = RT_FALSE;
|
||
g_sink_query_tick[ch] = rt_tick_get();
|
||
}
|
||
return RT_FALSE;
|
||
}
|
||
/*****************************************************************
|
||
函数名称: sink_fill_pd_buf
|
||
函数描述: 根据快充协议类型填充PD指令的5字节数据缓冲区
|
||
输入参数:
|
||
pd_val: 快充协议类型(如0xC1/0xF8/0xFB等)
|
||
pd_volt: 电压值(mV,16位)
|
||
pd_curr: 电流值(mA,16位)
|
||
buf: 输出缓冲区
|
||
输出参数: buf: 填充后的5字节协议数据
|
||
返回说明: 0:成功 -1:参数错误
|
||
*****************************************************************/
|
||
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)
|
||
{
|
||
// 参数合法性校验
|
||
if (buf == RT_NULL) {
|
||
rt_kprintf("sink_fill_pd_buf: buf is a null pointer!\n");
|
||
return -1;
|
||
}
|
||
|
||
// 先清空缓冲区(默认全0)
|
||
rt_memset(buf, 0, 5);
|
||
|
||
// 按协议类型填充buf
|
||
switch(pd_val) {
|
||
// 标准/QC2.0/QC3.0/FCP(仅电压,第五位=电压)
|
||
case 0xFE: // 标准协议
|
||
case 0xF8: // QC2.0协议
|
||
case 0xF6: // QC3.0协议
|
||
case 0xF2: // FCP协议
|
||
buf[0] = pd_volt / 100; // 第5字节=电压(0.1V)
|
||
buf[1] = 0x00;//pd_curr / 100; // 第6字节=电流(0.1A)
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("type: %02X, Voltage(0.1V)=%d\n", pd_val, pd_volt/100);
|
||
rt_kprintf("type: %02x, Curr(0.1A)=%d\n",pd_curr,pd_curr/100);
|
||
#endif
|
||
break;
|
||
|
||
// PDO/PPS/PD3.1/UFCS/AVS(组号+电压+电流)
|
||
case 0xFC: // PPS手动/自动
|
||
case 0xE3: // AVS协议
|
||
buf[0] = group; // 第5字节=组号(默认0)
|
||
buf[1] = (pd_volt >> 8) & 0xFF; // 第6字节=电压高8位(mV)
|
||
buf[2] = pd_volt & 0xFF; // 第7字节=电压低8位(mV)
|
||
buf[3] = (pd_curr >> 8) & 0xFF; // 第8字节=电流高8位(mA)
|
||
buf[4] = pd_curr & 0xFF; // 第9字节=电流低8位(mA)
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("type: %02X, Group=0, Voltage=%d mV, Current=%d mA\n", pd_val, pd_volt, pd_curr);
|
||
#endif
|
||
break;
|
||
case 0xE0: // UFCS协议
|
||
case 0xE1: // PD3.1协议
|
||
case 0xFB: // PDO手动
|
||
|
||
buf[0] = 0x00; // 第5字节=组号(默认0)
|
||
buf[1] = (pd_volt >> 8) & 0xFF; // 第6字节=电压高8位(mV)
|
||
buf[2] = pd_volt & 0xFF; // 第7字节=电压低8位(mV)
|
||
buf[3] = (pd_curr >> 8) & 0xFF; // 第8字节=电流高8位(mA)
|
||
buf[4] = pd_curr & 0xFF; // 第9字节=电流低8位(mA)
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("type: %02X, Group=0, Voltage=%d mV, Current=%d mA\n", pd_val, pd_volt, pd_curr);
|
||
#endif
|
||
break;
|
||
|
||
// SCP/VIVO/传音(电压+电流)
|
||
case 0xF3: // SCP协议
|
||
case 0xFD: // VIVO协议
|
||
case 0xF7: // 传音/TFC协议
|
||
buf[0] = (pd_volt >> 8) & 0xFF; // 第6字节=电压高8位(mV)
|
||
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] = 0x00;
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("type: %02X, Voltage=%d mV, Current=%d mA\n", pd_val, pd_volt, pd_curr);
|
||
#endif
|
||
break;
|
||
|
||
// VOOC(无参数)
|
||
case 0xE2: // VOOC协议
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("type: VOOC(0xE2), No parameters\n");
|
||
#endif
|
||
break;
|
||
|
||
case 0xF9: // AFC协议
|
||
{
|
||
// 1. 电压转HEX
|
||
rt_uint8_t volt_hex = 0;
|
||
if (pd_volt >= 5000 && pd_volt <= 20000) { // 电压范围:5V(5000mV)~20V(20000mV)
|
||
rt_uint8_t volt_v = pd_volt / 1000; // 转成V(5000mV→5V)
|
||
volt_hex = volt_v - 5; // 5V对应0,20V对应15(0xF)
|
||
} else {
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("AFC VOLT ERROR VOLT=:%d mV Set 5000~20000\n", pd_volt);
|
||
#endif
|
||
return -1;
|
||
}
|
||
|
||
// 2. 电流转HEX
|
||
rt_uint8_t curr_hex = 0;
|
||
if (pd_curr >= 750 && pd_curr <= 3000) { // 电流范围:0.75A(750mA)~3.0A(3000mA)
|
||
rt_uint16_t curr_ma_step = (pd_curr - 750) / 150; // 步长150mA(0.75→1.5→...→3.0)
|
||
curr_hex = curr_ma_step; // 0.75A对应0,3.0A对应15(0xF)
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("curr_ma_step = %d",curr_ma_step);
|
||
#endif
|
||
} else {
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("AFC CURR ERROR%d mA SET 750~3000 \n", pd_curr);
|
||
#endif
|
||
return -1;
|
||
}
|
||
|
||
// 3. 合并电压Hex(高4位)和电流Hex(低4位)→ 填充第五字节
|
||
buf[0] = (volt_hex << 4) | curr_hex;
|
||
#if NORTH_SINK_DEBUG
|
||
rt_kprintf("type: AFC(0xF9), Voltage=%d V, Current=%.2f A, volaue=0x%X\n",
|
||
pd_volt/1000, pd_curr/1000.0, buf[0]);
|
||
#endif
|
||
}
|
||
break;
|
||
// 未知类型
|
||
default:
|
||
// rt_kprintf("Unknown fast charge type: %02X, Parameters cleared\n", pd_val);
|
||
break;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_sw_uart
|
||
函数描述: 北向通道切换 sink/source 串口
|
||
输入参数: ch: 通道 id: sink/source
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
static void chrg_roll_nor_sw_uart (eIDX_NOR_CH ch, eIDX_ID id)
|
||
{
|
||
if (ch >= TOTAL_NOR_CHS) {
|
||
return ;
|
||
}
|
||
|
||
if (ID_SOURCE == id) {
|
||
rt_pin_write(chrgios[sw_uart[ch]].base, PIN_HIGH);
|
||
} else if (ID_SINK == id) {
|
||
rt_pin_write(chrgios[sw_uart[ch]].base, PIN_LOW);
|
||
}
|
||
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: 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};
|
||
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
|
||
return sink_set_pd(pSW->sink.protocol, buf);
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: 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);
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_expected_cmd
|
||
函数描述: 根据当前角色和sub状态取得本次北向请求期望的回码命令
|
||
输入参数: pSW:当前通道控制结构体
|
||
输出参数: -
|
||
返回说明: 非0:期望命令码 0:当前状态没有可识别的北向请求
|
||
其它说明: 必须在chrg_roll_nor_set发送命令前调用,确保保存的期望命令
|
||
与实际发送命令一致
|
||
*****************************************************************/
|
||
static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW)
|
||
{
|
||
if (pSW == RT_NULL) {
|
||
return 0;
|
||
}
|
||
|
||
if (pSW->id == ID_SOURCE) {
|
||
switch (pSW->sub) {
|
||
case IDX_GET_SOURCE_VC:
|
||
return SRC_CMD_GET_VOLTAGE;
|
||
case IDX_GET_SOURCE_PD:
|
||
return SRC_CMD_GET_PROTOCOL;
|
||
case IDX_SET_SOURCE_PD:
|
||
return SRC_CMD_SET_PROTOCOL;
|
||
case IDX_SET_SOURCE_RESET:
|
||
return SRC_CMD_RESET;
|
||
case IDX_SET_SOURCE_CC_SET:
|
||
return SRC_CMD_SET_CC_CHOOSE;
|
||
case IDX_SET_SOURCE_CCLVL:
|
||
return SRC_CMD_SET_CC_LEVEL;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if (pSW->id == ID_SINK) {
|
||
switch (pSW->sub) {
|
||
case IDX_GET_SINK_VC:
|
||
return SINK_CMD_GET_VOLTAGE;
|
||
case IDX_GET_SINK_PD:
|
||
return SINK_CMD_GET_PD;
|
||
case IDX_SET_SINK_PD:
|
||
case IDX_SET_SINK_PD_ONLY:
|
||
#if 0
|
||
case IDX_SET_SINK_PD_BEFORE_CC:
|
||
#endif
|
||
return pSW->sink.protocol;
|
||
case IDX_SET_SINK_CCLINE:
|
||
case IDX_SET_SINK_CC_BEFORE_PD:
|
||
return SINK_CMD_SET_CC;
|
||
case IDX_SET_SINK_CCLVL:
|
||
return SINK_CMD_SET_LEVEL;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_response_matches
|
||
函数描述: 校验北向回码与当前请求的通道角色、命令和固定长度是否匹配
|
||
输入参数: pMB:已通过north.c结构及CRC校验的回码 expected_idx:请求通道
|
||
expected_id:请求角色 expected_cmd:请求命令
|
||
输出参数: -
|
||
返回说明: 0:回码匹配 <0:参数错误或回码与请求不匹配
|
||
其它说明: 本函数只判断请求归属,业务解析在配置版本复核通过后执行
|
||
*****************************************************************/
|
||
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)
|
||
{
|
||
if ((pMB == RT_NULL) || (pMB->payload == RT_NULL) ||
|
||
(pMB->length < MIN_FRAME_LENGTH) ||
|
||
(expected_idx >= TOTAL_NOR_CHS) || (expected_cmd == 0U)) {
|
||
return -1;
|
||
}
|
||
|
||
if ((pMB->payload[2] != (rt_uint8_t)expected_id) ||
|
||
(pMB->payload[3] != expected_cmd)) {
|
||
LOG_W("north response mismatch: ch=%d id=%02x/%02x cmd=%02x/%02x",
|
||
expected_idx + 1U, pMB->payload[2], expected_id,
|
||
pMB->payload[3], expected_cmd);
|
||
return -2;
|
||
}
|
||
|
||
if ((expected_cmd == SRC_CMD_GET_VOLTAGE) && (pMB->length != 21U)) {
|
||
return -3;
|
||
}
|
||
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
|
||
函数描述: 执行北向手动插入命令的入口检查
|
||
输入参数: pROLL:北向轮询控制结构体
|
||
输出参数: -
|
||
返回说明: 0:参数有效 <0:参数为空
|
||
其它说明: 当前仅保留入口,具体手动命令由后续功能扩展
|
||
*****************************************************************/
|
||
static int chrg_roll_nor_cmd (struct chrg_rollnor_t *pROLL)
|
||
{
|
||
if (RT_NULL == pROLL) {
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_set
|
||
函数描述: 按当前角色和sub状态发送一条北向协议命令
|
||
输入参数: ch:北向通道编号 pSW:通道控制结构体 next_sub:候选下一状态输出指针
|
||
输出参数: next_sub保存正确回码后应进入的状态,不直接修改pSW->sub
|
||
返回说明: >0:发送长度 <0:发送失败或状态不支持
|
||
其它说明: Source按差异执行协议、CC或组合命令;涉及PD的运行中切换先发送CC_CLOSE;
|
||
停止状态启动和主动停止均不额外发送CC_CLOSE;
|
||
Sink完整握手依次执行协议设置、目标CC线选择、稳定等待和VC查询
|
||
*****************************************************************/
|
||
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_RESET:
|
||
*next_sub = IDX_SET_SOURCE_PD;
|
||
return source_reset();
|
||
case IDX_SET_SOURCE_PD:
|
||
*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:
|
||
return source_get_vc(pSW->source.set_volt,
|
||
pSW->source.set_curr);
|
||
case IDX_GET_SOURCE_PD:
|
||
*next_sub = IDX_GET_SOURCE_VC;
|
||
return source_get_pd();
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
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: //查询
|
||
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线选择
|
||
*next_sub = IDX_WAIT_SINK_CC;
|
||
return sink_set_cc_line(pSW->sink.cc_set);
|
||
case IDX_SET_SINK_CC_BEFORE_PD:
|
||
*next_sub = IDX_WAIT_SINK_CC_BEFORE_PD;
|
||
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_WAIT_SINK_PD_ACK;
|
||
return chrg_roll_nor_sink_pro_set(pSW);
|
||
case IDX_SET_SINK_PD_ONLY:
|
||
*next_sub = IDX_WAIT_SINK_PD_ACK;
|
||
return chrg_roll_nor_sink_pro_set(pSW);
|
||
#if 0
|
||
case IDX_SET_SINK_PD_BEFORE_CC:
|
||
/* 协议回码确认后再设置CC,不提前查询电压。 */
|
||
*next_sub = IDX_SET_SINK_CCLINE;
|
||
return chrg_roll_nor_sink_pro_set(pSW);
|
||
#endif
|
||
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_bool_t advance_sub = RT_TRUE;
|
||
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; // 本次请求期望收到的命令码
|
||
rt_uint8_t max_attempts; // 本次请求允许的总发送次数
|
||
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;
|
||
}
|
||
|
||
/* 默认保留设置命令的3次发送;首轮取得实际命令码后,0x20会改为
|
||
* 单次发送并留给下一轮轮询重试,避免量测无回码时长期占用通道。 */
|
||
max_attempts = NORTH_REQUEST_MAX_RETRIES + 1U;
|
||
for (attempt = 0; attempt < max_attempts; 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);
|
||
if (expected_cmd == SRC_CMD_GET_VOLTAGE) {
|
||
max_attempts = NORTH_MEASURE_MAX_ATTEMPTS;
|
||
}
|
||
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_RESET)) {
|
||
/* 复位回码后重新执行Source INIT,初始化完成后再推进到PD。 */
|
||
pSW->source.work_mode = SOURCE_WORK_INIT;
|
||
advance_sub = RT_FALSE;
|
||
/* INIT完成前保持当前sub,禁止通用逻辑提前推进到PD。 */
|
||
} else if ((expected_id == ID_SOURCE) &&
|
||
(request_sub == IDX_SET_SOURCE_PD)) {
|
||
/* 0xD0应答后每通道独立等待100ms,再连接目标CC。 */
|
||
chrg_roll_nor_source_protocol_wait_start(idx);
|
||
chrg_comm_source_protocol_ack(idx,
|
||
(rt_uint8_t)pSW->source.protocol);
|
||
} else if ((expected_id == ID_SINK) &&
|
||
((request_sub == IDX_SET_SINK_CCLINE) ||
|
||
(request_sub == IDX_SET_SINK_CC_BEFORE_PD))) {
|
||
/* 北向Sink继电器保持接通,仅等待目标CC配置稳定。 */
|
||
chrg_roll_nor_sink_cc_wait_start(idx);
|
||
} else if ((expected_id == ID_SINK) &&
|
||
(request_sub == IDX_SET_SINK_PD_ONLY)) {
|
||
/* 仅重发协议时没有CC回码作为节流入口,直接启动200ms计时。 */
|
||
chrg_roll_nor_sink_query_wait_start(idx);
|
||
}
|
||
/* 业务解析可能主动切换sub,例如掉电恢复后要求重新设置协议。
|
||
* 只有sub仍属于本次请求时,才应用请求函数计算出的next_sub。 */
|
||
if (advance_sub && (pSW->sub == request_sub)) {
|
||
pSW->sub = next_sub;
|
||
}
|
||
rt_mutex_release(pTHR->mutex);
|
||
chrg_roll_nor_free_response(pMB);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if ((attempt + 1U) < max_attempts) {
|
||
/* 本次发送失败或等待超时,保持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,
|
||
max_attempts);
|
||
return -1;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_roll_nor_thread_entry
|
||
函数描述: 北向通道轮巡请求数据线程体
|
||
输入参数: *data: 本线程信息
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
void chrg_roll_nor_thread_entry (void *data)
|
||
{
|
||
struct chrg_thread_t *pTHR = (struct chrg_thread_t *)data;
|
||
|
||
if (RT_NULL == pTHR) {
|
||
return ;
|
||
}
|
||
|
||
rt_uint8_t index = 0, idx = 0, i = 0;
|
||
rt_uint8_t run = 0;
|
||
rt_bool_t source_waiting = RT_FALSE;
|
||
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];
|
||
struct chrg_north_t *pNOR = &chrgnorth;
|
||
struct chrg_switch_t *pSW = RT_NULL;
|
||
struct mb_msg_t *pMB_R = RT_NULL;
|
||
for (i = 0; i < TOTAL_NOR_CHS; i++) {
|
||
chrg_roll_nor_sw_uart((eIDX_NOR_CH)i, ID_SINK);
|
||
}
|
||
while (1) {
|
||
chrg_wdt_feed();
|
||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||
run = pROLL->run;
|
||
rt_mutex_release(pTHR->mutex);
|
||
switch (run) {
|
||
case RUN_AUTO: {
|
||
idx = index%TOTAL_NOR_CHS;
|
||
pSW = &pNOR->sw[idx];
|
||
source_waiting = RT_FALSE;
|
||
sink_waiting = RT_FALSE;
|
||
state_work_ret = 0;
|
||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||
/*
|
||
* INIT/STOP必须在enable过滤前执行:STOP会把enable清零,而COM
|
||
* 启动时只改变work_mode。若先判断enable,停机通道会直接continue,
|
||
* INIT永远没有机会把enable重新置1,也就无法恢复北向轮询。
|
||
* Source->Sink切换后的SINK_WORK_INIT同样从这里首次执行。
|
||
*/
|
||
if ((pSW->id == ID_SOURCE) &&
|
||
((pSW->source.work_mode == SOURCE_WORK_INIT) ||
|
||
(pSW->source.work_mode == SOURCE_WORK_STOP) ||
|
||
(pSW->source.work_mode == SOURCE_WORK_PROTOCOL_SWITCH) ||
|
||
(pSW->source.work_mode == SOURCE_WORK_FULL_RESET) ||
|
||
(pSW->source.work_mode == SOURCE_WORK_FULL_RESET_WAIT) ||
|
||
(pSW->source.work_mode == SOURCE_WORK_FULL_RESET_RELAY) ||
|
||
(pSW->source.work_mode == SOURCE_WORK_FULL_RESET_RELAY_WAIT) ||
|
||
(pSW->source.work_mode == SOURCE_WORK_INIT_WAIT))) {
|
||
state_work_ret = chrg_source_work_state(idx, pSW);
|
||
} else if ((pSW->id == ID_SINK) &&
|
||
((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) ||
|
||
(pSW->sink.work_mode == SINK_WORK_STOP_WAIT) ||
|
||
(pSW->sink.work_mode == SINK_WORK_STOP_RELAY) ||
|
||
(pSW->sink.work_mode == SINK_WORK_STOP_RELAY_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 (state_work_ret != 0) {
|
||
/* 南向状态动作未成功入队时保留原状态,下轮重试且禁止北向先行。 */
|
||
index++;
|
||
rt_thread_mdelay(2);
|
||
continue;
|
||
}
|
||
if (source_waiting || sink_waiting) {
|
||
index++;
|
||
rt_thread_mdelay(2);
|
||
continue;
|
||
}
|
||
if (0 == pNOR->enable[idx]) {
|
||
index++;
|
||
rt_thread_mdelay(2);
|
||
continue;
|
||
} else {
|
||
chrg_led_flashing(IDX_LED2, TIMES_ONE, PULSE_TIME*2, PULSE_TIME*2);
|
||
chrg_north_switch((eIDX_NOR_CH)idx);
|
||
chrgnorth.idx = idx;
|
||
pROLL->idx = idx;
|
||
(void)chrg_roll_nor_auto_request(idx, pSW,
|
||
pTHR, pTN, pNOR);
|
||
}
|
||
index++;
|
||
rt_thread_mdelay(NORTH_POLL_INTERVAL_MS);
|
||
continue;
|
||
}
|
||
break;
|
||
|
||
case RUN_LCD: {
|
||
chrg_roll_nor_cmd(pROLL);
|
||
}
|
||
break;
|
||
case RUN_CONSOLE: {
|
||
chrg_roll_nor_cmd(pROLL);
|
||
}
|
||
break;
|
||
case RUN_COM:
|
||
// {
|
||
// chrg_roll_nor_cmd(pROLL);
|
||
// }
|
||
break;
|
||
case RUN_NONE:
|
||
default:
|
||
rt_thread_mdelay(5);
|
||
continue;
|
||
break;
|
||
}
|
||
|
||
ret = rt_mb_recv(pTN->mb, (rt_uint32_t *)&pMB_R, 50); // 1kHz tick下超时约50ms
|
||
if ((RT_EOK == ret)&&(RT_NULL != pMB_R)) { // 存在返回数据
|
||
/* north.c只会向邮箱提交已经通过结构和CRC校验的完整帧。 */
|
||
if (RUN_AUTO != run) { // 恢复自动轮巡
|
||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||
pROLL->run = RUN_AUTO;
|
||
rt_mutex_release(pTHR->mutex);
|
||
}
|
||
|
||
chrg_roll_nor_free_response(pMB_R);
|
||
pMB_R = RT_NULL;
|
||
|
||
rt_thread_mdelay(10); // 相邻北向请求间隔50ms
|
||
} else {
|
||
if (RUN_AUTO != run) { // 非自动轮巡
|
||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||
//todo
|
||
pROLL->run = RUN_AUTO; // 切换会自动轮巡
|
||
rt_mutex_release(pTHR->mutex);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#if 1
|
||
|
||
|
||
static int sink (int argc, char **argv)
|
||
{
|
||
int ret = 0;
|
||
if (argc < 4) {
|
||
rt_kprintf("help : %s get|set [0-3] vc|pd|ccline|cclvl <data>\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
|
||
int gset = 0;
|
||
struct chrg_rollnor_t *pROLL = &chrgrollnor;
|
||
|
||
if (strcmp(argv[1], "get") == 0) {
|
||
gset = 0;
|
||
} else if (strcmp(argv[1], "set") == 0) {
|
||
gset = 1;
|
||
} else {
|
||
rt_kprintf("help : %s get|set [0-3] vc|pd|ccline|cclvl <data>\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
|
||
rt_uint8_t ch = (rt_uint8_t)atoi(argv[2]);
|
||
if ((ch < 0)||(ch >= TOTAL_NOR_CHS)) {
|
||
rt_kprintf("help : %s get|set [0-3] vc|pd|ccline|cclvl <data>\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
|
||
if (strcmp(argv[3], "vc") == 0) {
|
||
if (0 == gset) {
|
||
pROLL->sub = IDX_SET_SINK_WORK_STATE;
|
||
} else {
|
||
ret = -1;
|
||
rt_kprintf("sink vc can't set.\r\n");
|
||
return -1;
|
||
}
|
||
} else if (strcmp(argv[3], "pd") == 0) {
|
||
if (0 == gset) {
|
||
pROLL->sub = IDX_GET_SINK_PD;
|
||
} else {
|
||
//长度
|
||
if (argc != 5) {
|
||
rt_kprintf("help : %s set 0-3 ccline 0-3.\r\n", argv[0]);
|
||
return -1;
|
||
// sink_set_pd
|
||
} else { // sink set 0 pd 0
|
||
int type = atoi(argv[4]);
|
||
pROLL->pd = (rt_uint8_t)type;
|
||
pROLL->sub = IDX_SET_SINK_PD;
|
||
}
|
||
}
|
||
} else if (strcmp(argv[3], "ccline") == 0) {
|
||
if (1 == gset) {
|
||
if (argc != 5) {
|
||
rt_kprintf("help : %s set 0-3 ccline 0-3.\r\n", argv[0]);
|
||
return -1;
|
||
} else { // sink set 0 ccline 0-3
|
||
int line = atoi(argv[4]);
|
||
if ((line >= 0)&&(line <= 3)) {
|
||
pROLL->sub = IDX_SET_SINK_CCLINE;
|
||
pROLL->cc_line = (rt_uint8_t)line;
|
||
} else {
|
||
rt_kprintf("help : %s set 0-3 ccline 0-3.\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
}
|
||
} else {
|
||
ret = -1;
|
||
rt_kprintf("sink cc line can't get.\r\n");
|
||
}
|
||
} else if (strcmp(argv[3], "cclvl") == 0) {
|
||
if (1 == gset) {
|
||
if (argc != 5) {
|
||
rt_kprintf("help : %s set 0-3 cclvl 0-3.\r\n", argv[0]);
|
||
return -1;
|
||
} else { // sink set 0 cclvl 0-3
|
||
int line = atoi(argv[4]);
|
||
if ((line >= 0)&&(line <= 3)) {
|
||
pROLL->sub = IDX_SET_SINK_CCLVL;
|
||
pROLL->cc_line = (rt_uint8_t)line;
|
||
} else {
|
||
rt_kprintf("help : %s set 0-3 cclvl 0-3.\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
}
|
||
} else {
|
||
ret = -1;
|
||
rt_kprintf("sink cc level can't get.\r\n");
|
||
}
|
||
} else {
|
||
ret = -1;
|
||
}
|
||
|
||
if (0 == ret) {
|
||
pROLL->ch = ch;
|
||
pROLL->type = 0; // sink
|
||
pROLL->run = RUN_CONSOLE;
|
||
} else {
|
||
rt_kprintf("help : %s get|set [0-3] vc|pd|ccline|cclvl\r\n", argv[0]);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
MSH_CMD_EXPORT(sink, north sink test);
|
||
|
||
static int source (int argc, char **argv)
|
||
{
|
||
int ret = 0;
|
||
if (argc < 4) {
|
||
rt_kprintf("help : %s get|set [0-3] vc|pd|cclvl <data>\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
|
||
int gset = 0;
|
||
struct chrg_rollnor_t *pROLL = &chrgrollnor;
|
||
|
||
if (strcmp(argv[1], "get") == 0) {
|
||
gset = 0;
|
||
} else if (strcmp(argv[1], "set") == 0) {
|
||
gset = 1;
|
||
} else {
|
||
rt_kprintf("help : %s get|set [0-3] vc|pd|cclvl <data>\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
|
||
rt_uint8_t ch = (rt_uint8_t)atoi(argv[2]);
|
||
if ((ch < 0)||(ch >= TOTAL_NOR_CHS)) {
|
||
rt_kprintf("help : %s get|set [0-3] vc|pd|cclvl <data>\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
|
||
if (strcmp(argv[3], "vc") == 0) {
|
||
if (0 == gset) {
|
||
pROLL->sub = IDX_GET_SOURCE_VC;
|
||
} else {
|
||
ret = -1;
|
||
rt_kprintf("sink vc can't set.\r\n");
|
||
return -1;
|
||
}
|
||
} else if (strcmp(argv[3], "pd") == 0) {
|
||
if (0 == gset) {
|
||
pROLL->sub = IDX_GET_SOURCE_PD;
|
||
} else {
|
||
if (argc != 5) {
|
||
//if (argc != 5) {
|
||
rt_kprintf("help : %s set 0-3 pd 0-.\r\n", argv[0]);
|
||
return -1;
|
||
// sink_set_pd
|
||
} else { // sink set 0 pd 0
|
||
int type = atoi(argv[4]);
|
||
//协议
|
||
pROLL->pd = (rt_uint8_t)type;
|
||
//子命令
|
||
pROLL->sub = IDX_SET_SOURCE_PD;
|
||
}
|
||
}
|
||
} else if (strcmp(argv[3], "cclvl") == 0) {
|
||
if (1 == gset) {
|
||
if (argc != 5) {
|
||
rt_kprintf("help : %s set 0-3 cclvl 0-3.\r\n", argv[0]);
|
||
return -1;
|
||
} else { // sink set 0 cclvl 0-3
|
||
int line = atoi(argv[4]);
|
||
if ((line >= 0)&&(line <= 3)) {
|
||
pROLL->sub = IDX_SET_SOURCE_CCLVL;
|
||
pROLL->cc_line = (rt_uint8_t)line;
|
||
} else {
|
||
rt_kprintf("help : %s set 0-3 cclvl 0-3.\r\n", argv[0]);
|
||
return -1;
|
||
}
|
||
}
|
||
} else {
|
||
ret = -1;
|
||
rt_kprintf("sink cc level can't get.\r\n");
|
||
}
|
||
} else {
|
||
ret = -1;
|
||
}
|
||
|
||
if (0 == ret) {
|
||
pROLL->ch = ch;
|
||
pROLL->type = 1; // source
|
||
pROLL->run = RUN_CONSOLE;
|
||
} else {
|
||
rt_kprintf("help : %s get|set [0-3] vc|pd|cclvl\r\n", argv[0]);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
MSH_CMD_EXPORT(source, north source test);
|
||
|
||
#endif
|
||
|