Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,777 @@
|
||||
#include "sub_comm/sub_comm_task.h"
|
||||
|
||||
#include "app_init/app_init.h"
|
||||
#include "app_init/task_lock.h"
|
||||
#include "mylog/mylog.h"
|
||||
#include "wdt_task/wdt_task.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "collect_ctrl/collect_task.h"
|
||||
#include "collect_ctrl/led_ctrl.h"
|
||||
#include "flowctrl/flow_interface.h"
|
||||
#include "fault_cheak/fault_interface.h"
|
||||
#include "app_rtc/app_rtc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "usart.h"
|
||||
|
||||
#define SUBCOMM_TASK_ID (TASK_ID_SubComm)
|
||||
#define SUBCOMM_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(SUBCOMM_TASK_ID))
|
||||
#define SUBCOMM_LOCK_INIT() (u8_task_lock_ctrl(SUBCOMM_TASK_ID, TASK_LOCK_OP_INIT))
|
||||
#define SUBCOMM_LOCK_TAKE() (u8_task_lock_ctrl(SUBCOMM_TASK_ID, TASK_LOCK_OP_TAKE))
|
||||
#define SUBCOMM_LOCK_GIVE() (u8_task_lock_ctrl(SUBCOMM_TASK_ID, TASK_LOCK_OP_GIVE))
|
||||
#define SUBCOMM_FEED_DOG() (v_wdt_setFlag(SUBCOMM_TASK_ID))
|
||||
#define SUBCOMM_LOG(fmt, ...) MYLOG_MSG(SUBCOMM_TASK_ID, (fmt), ##__VA_ARGS__)
|
||||
|
||||
/* ======================== 接收缓存(处理粘包/残包) ======================== */
|
||||
/* 说明:
|
||||
* - USART0 中断已把数据写入 FIFO,本模块周期从 FIFO 读出并追加到该缓存;
|
||||
* - 缓存中可能存在:
|
||||
* 1) 粘包:一次收到多帧 -> 解析循环连续处理
|
||||
* 2) 残包:一次只到半帧 -> 保留到下次继续拼
|
||||
* 3) 杂散字节:找不到帧头/校验失败 -> 丢弃直到重新同步 */
|
||||
#define SUBCOMM_RX_BUF_MAX (128U)
|
||||
|
||||
/* ======================== SubComm 模块上下文(模块私有) ======================== */
|
||||
typedef struct
|
||||
{
|
||||
/* 副板遥控/遥信缓存 */
|
||||
U_SUB_YK_DATA sub_yk;
|
||||
U_SUB_YX_DATA sub_yx;
|
||||
|
||||
/* 接收缓存(处理粘包/残包) */
|
||||
U8_T rx_buf[SUBCOMM_RX_BUF_MAX];
|
||||
U16_T rx_len;
|
||||
|
||||
/* 最近一次收到有效 DI 状态帧(0x01)的时间戳(ms)。用于离线超时判断。 */
|
||||
uint64_t last_di_ms;
|
||||
} S_SUBCOMM_CTX;
|
||||
|
||||
static S_SUBCOMM_CTX g_subcomm_ctx; /* SubComm 模块运行时上下文(缓存+状态) */
|
||||
|
||||
|
||||
|
||||
/* ======================== 副板串口协议(USART0) ======================== */
|
||||
/* 帧格式:
|
||||
* [0] 0x5A 帧头
|
||||
* [1] cmd 命令码
|
||||
* [2] len 数据长度 N(0~64)
|
||||
* [3] data[0..N-1] 数据域(可选)
|
||||
* [3+N] sum 校验:从帧头到数据域的累加和(8-bit 截断)
|
||||
* [4+N] 0xA5 帧尾 */
|
||||
#define SUBCOMM_UART_ID (E_USART_0)
|
||||
#define SUBCOMM_FRAME_HEAD (0x5AU)
|
||||
#define SUBCOMM_FRAME_TAIL (0xA5U)
|
||||
#define SUBCOMM_DATA_MAX (64U)
|
||||
|
||||
/* 命令码定义 */
|
||||
#define SUBCOMM_CMD_DI_STATUS (0x01U) /* 辅板DI状态:辅板 -> 主板 */
|
||||
#define SUBCOMM_CMD_DO_CTRL (0x02U) /* 控制继电器:主板 -> 辅板 */
|
||||
#define SUBCOMM_CMD_SYS_INFO (0x03U) /* 系统信息:主板 -> 辅板 */
|
||||
#define SUBCOMM_CMD_GUN_INFO (0x04U) /* 枪信息:主板 -> 辅板 */
|
||||
|
||||
/* DI 超时:超过该时间未收到 0x01,则认为副板离线(清零采集缓存)。 */
|
||||
#define SUBCOMM_DI_TIMEOUT_MS (5000U)
|
||||
|
||||
/* 接收缓存已纳入 g_subcomm_ctx 统一管理。 */
|
||||
|
||||
#if 1 // 初始化/数据清理
|
||||
/**
|
||||
* @brief SubComm 模块数据初始化。
|
||||
*
|
||||
* - 清空 YK/YX 缓存(默认全 0);
|
||||
* - 重置接收状态机到“等待帧头”。
|
||||
*/
|
||||
static void v_sub_comm_data_init(void)
|
||||
{
|
||||
(void)memset(&g_subcomm_ctx.sub_yk, 0, sizeof(g_subcomm_ctx.sub_yk));
|
||||
(void)memset(&g_subcomm_ctx.sub_yx, 0, sizeof(g_subcomm_ctx.sub_yx));
|
||||
|
||||
(void)memset(g_subcomm_ctx.rx_buf, 0, sizeof(g_subcomm_ctx.rx_buf));
|
||||
g_subcomm_ctx.rx_len = 0U;
|
||||
|
||||
/* 置 0 表示“尚未收到任何 DI 帧”。 */
|
||||
g_subcomm_ctx.last_di_ms = 0U;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 // bit 操作辅助
|
||||
/* ======================== bit 操作辅助 ======================== */
|
||||
/**
|
||||
* @brief 按 bit 写入 16bit 点位缓存。
|
||||
*
|
||||
* @param bits 目标 bit-map 指针
|
||||
* @param bit_idx bit 下标(0~15)
|
||||
* @param val01 0 或 1
|
||||
*
|
||||
* @note 本模块约定:点位枚举值 == bit 下标(与 sub_comm_task.h 的枚举顺序一致)。
|
||||
*/
|
||||
static void v_sub_comm_bit_set_u16(uint16_t *bits, U8_T bit_idx, U8_T val01)
|
||||
{
|
||||
uint16_t mask; /* 当前点位掩码 */
|
||||
if ((bits == NULL) || (bit_idx >= 16U))
|
||||
{
|
||||
return;
|
||||
}
|
||||
mask = (uint16_t)(1U << bit_idx);
|
||||
if (val01 == 0U)
|
||||
{
|
||||
*bits = (uint16_t)(*bits & (uint16_t)(~mask));
|
||||
}
|
||||
else
|
||||
{
|
||||
*bits = (uint16_t)(*bits | mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取 16bit 点位缓存的某一 bit。
|
||||
* @return 0/1
|
||||
*/
|
||||
static U8_T u8_sub_comm_bit_get_u16(uint16_t bits, U8_T bit_idx)
|
||||
{
|
||||
if (bit_idx >= 16U)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
return (U8_T)((bits >> bit_idx) & 0x01U);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取 32bit 点位缓存的某一 bit。
|
||||
* @return 0/1
|
||||
*/
|
||||
static U8_T u8_sub_comm_bit_get_u32(uint32_t bits, U8_T bit_idx)
|
||||
{
|
||||
if (bit_idx >= 32U)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
return (U8_T)((bits >> bit_idx) & 0x01U);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 // DI 数据域解析/命令分发
|
||||
/**
|
||||
* @brief 将副板 DI 状态数据域解码进 g_subcomm_ctx.sub_yx。
|
||||
*
|
||||
* 数据域编码(本工程默认 bit-map 方案,便于紧凑传输):
|
||||
* - len >= 2 时使用 data[0..1] 的 16 个 bit:
|
||||
* bit0 -> YX13
|
||||
* bit1 -> YX14
|
||||
* ...
|
||||
* bit9 -> YX22
|
||||
* bit10 -> DIP1
|
||||
* ...
|
||||
* bit15 -> DIP6
|
||||
*
|
||||
* 说明:
|
||||
* - 若现场协议改为“每点 1 字节”或“更多字节”,只需在此函数内替换映射。
|
||||
*/
|
||||
static void v_sub_comm_apply_di_payload(const U8_T *data, U8_T len)
|
||||
{
|
||||
if ((data == NULL) || (len < 2U))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 当前 0x01 数据域仅承载低 16bit(YX13~YX22 + DIP1~DIP6),预留 8bit 默认 0。 */
|
||||
g_subcomm_ctx.sub_yx.u32_bits = (uint32_t)data[0] | ((uint32_t)data[1] << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析一帧完整有效的副板协议帧,并更新内部缓存。
|
||||
* @param cmd 命令码
|
||||
* @param data 数据域
|
||||
* @param len 数据长度
|
||||
*/
|
||||
static void v_sub_comm_handle_frame(U8_T cmd, const U8_T *data, U8_T len)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case SUBCOMM_CMD_DI_STATUS:
|
||||
v_sub_comm_apply_di_payload(data, len);
|
||||
g_subcomm_ctx.last_di_ms = u64_get_current_millis();
|
||||
break;
|
||||
|
||||
default:
|
||||
/* 其它命令暂不处理 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 // RX 缓存解帧(处理粘包/残包)
|
||||
/**
|
||||
* @brief 从接收缓存中尝试解析一帧完整协议帧。
|
||||
*
|
||||
* @return U8_T 1=成功解析并消费掉该帧;0=缓存中暂时没有完整有效帧(可能是残包/需继续收数)
|
||||
*/
|
||||
static U8_T u8_sub_comm_try_parse_one_frame(void)
|
||||
{
|
||||
U16_T i; /* 遍历下标 */
|
||||
U16_T frame_start = 0U; /* 帧头位置 */
|
||||
U16_T total_len; /* 当前帧总长度 */
|
||||
U8_T cmd; /* 命令码 */
|
||||
U8_T len; /* 数据域长度 */
|
||||
U8_T checksum; /* 计算得到的校验值 */
|
||||
|
||||
/* 最小帧长度:head+cmd+len+sum+tail = 5 */
|
||||
if (g_subcomm_ctx.rx_len < 5U)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/* 1) 查找帧头 */
|
||||
frame_start = 0xFFFFU;
|
||||
for (i = 0U; i < g_subcomm_ctx.rx_len; i++)
|
||||
{
|
||||
if (g_subcomm_ctx.rx_buf[i] == SUBCOMM_FRAME_HEAD)
|
||||
{
|
||||
frame_start = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_start == 0xFFFFU)
|
||||
{
|
||||
/* 缓存里没有帧头,直接清空 */
|
||||
g_subcomm_ctx.rx_len = 0U;
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/* 丢弃帧头之前的杂散数据 */
|
||||
if (frame_start > 0U)
|
||||
{
|
||||
g_subcomm_ctx.rx_len = (U16_T)(g_subcomm_ctx.rx_len - frame_start);
|
||||
(void)memmove(g_subcomm_ctx.rx_buf, &g_subcomm_ctx.rx_buf[frame_start], g_subcomm_ctx.rx_len);
|
||||
}
|
||||
|
||||
/* 2) 至少要有 head/cmd/len */
|
||||
if (g_subcomm_ctx.rx_len < 3U)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
|
||||
cmd = g_subcomm_ctx.rx_buf[1];
|
||||
len = g_subcomm_ctx.rx_buf[2];
|
||||
if (len > (U8_T)SUBCOMM_DATA_MAX)
|
||||
{
|
||||
/* 长度非法,丢弃 1 字节重新同步 */
|
||||
g_subcomm_ctx.rx_len--;
|
||||
(void)memmove(g_subcomm_ctx.rx_buf, &g_subcomm_ctx.rx_buf[1], g_subcomm_ctx.rx_len);
|
||||
return 1U; /* 已消费 1 字节,让外层继续循环 */
|
||||
}
|
||||
|
||||
total_len = (U16_T)(5U + (U16_T)len);
|
||||
if (g_subcomm_ctx.rx_len < total_len)
|
||||
{
|
||||
/* 残包:等待后续数据 */
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/* 3) 校验帧尾 */
|
||||
if (g_subcomm_ctx.rx_buf[total_len - 1U] != SUBCOMM_FRAME_TAIL)
|
||||
{
|
||||
/* 帧尾不匹配,丢弃 1 字节重新同步 */
|
||||
g_subcomm_ctx.rx_len--;
|
||||
(void)memmove(g_subcomm_ctx.rx_buf, &g_subcomm_ctx.rx_buf[1], g_subcomm_ctx.rx_len);
|
||||
return 1U;
|
||||
}
|
||||
|
||||
/* 4) 校验累加和:从 head 到 data(3+len 字节),对比 sum 字节 */
|
||||
checksum = (U8_T)u16_crc_checksum(&g_subcomm_ctx.rx_buf[0], (U16_T)(3U + (U16_T)len));
|
||||
if (checksum != g_subcomm_ctx.rx_buf[3U + (U16_T)len])
|
||||
{
|
||||
/* 校验失败,丢弃 1 字节重新同步 */
|
||||
g_subcomm_ctx.rx_len--;
|
||||
(void)memmove(g_subcomm_ctx.rx_buf, &g_subcomm_ctx.rx_buf[1], g_subcomm_ctx.rx_len);
|
||||
return 1U;
|
||||
}
|
||||
|
||||
/* 5) 处理帧 */
|
||||
v_sub_comm_handle_frame(cmd, &g_subcomm_ctx.rx_buf[3], len);
|
||||
|
||||
/* 6) 移除已处理数据(支持粘包:后面可能还有帧) */
|
||||
g_subcomm_ctx.rx_len = (U16_T)(g_subcomm_ctx.rx_len - total_len);
|
||||
if (g_subcomm_ctx.rx_len > 0U)
|
||||
{
|
||||
(void)memmove(g_subcomm_ctx.rx_buf, &g_subcomm_ctx.rx_buf[total_len], g_subcomm_ctx.rx_len);
|
||||
}
|
||||
return 1U;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 // 系统接口
|
||||
/* ======================== 系统接口(03/04数据来源与编码) ======================== */
|
||||
#define SUBCOMM_SYS_TIME_LEN (12U)
|
||||
#define SUBCOMM_SYS_RSVD_LEN (6U)
|
||||
#define SUBCOMM_SYS_INFO_LEN (22U)
|
||||
#define SUBCOMM_GUN_INFO_LEN (14U)
|
||||
#define SUBCOMM_GUN_RSVD_LEN (8U)
|
||||
|
||||
/**
|
||||
* @brief 获取系统枪数量(并限制到工程上限)。
|
||||
* @return U8_T 枪数量,范围 0~GUN_MAX_CNT
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_gun_cnt_if(void)
|
||||
{
|
||||
U8_T gun_cnt = g_t_share_data.t_sys_fix_cfg.u8_gunCnt; /* 配置中的枪数量 */
|
||||
if (gun_cnt > (U8_T)GUN_MAX_CNT) {
|
||||
gun_cnt = (U8_T)GUN_MAX_CNT;
|
||||
}
|
||||
return gun_cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取灯板类型(透传系统固定配置)。
|
||||
* @return U8_T 灯板类型编码
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_led_board_type_if(void)
|
||||
{
|
||||
return (U8_T)g_t_share_data.t_sys_fix_cfg.e_ledType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取液冷使能标志(0/1)。
|
||||
* @return U8_T 0/1(取最低位)
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_liquid_cooling_enable_if(void)
|
||||
{
|
||||
return (U8_T)(g_t_share_data.t_sys_fix_cfg.u_enable.bit_enable.bit_YeLenEn & 0x01U);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取液冷类型。
|
||||
* @return U8_T 当前未对接任何系统接口,固定为 0。
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_liquid_cooling_type_if(void)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定枪连接状态。
|
||||
* @param gun_no 枪号(0-based)
|
||||
* @return U8_T 0=未连接,1=已连接
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_gun_link_if(U8_T gun_no)
|
||||
{
|
||||
return (U8_T)((u8_get_gun_yx_data(YX_GUN_LINK, gun_no) == 0U) ? 0U : 1U);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定枪对外充电状态(flow 实时状态)。
|
||||
* @param gun_no 枪号(0-based)
|
||||
* @return U8_T 充电状态枚举值低8位
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_charge_state_if(U8_T gun_no)
|
||||
{
|
||||
U16_T flow_state = u16_flow_put_data(gun_no, E_FLOW_PUT_DATA_GUN_REAL_STATE, 0U); /* 流程状态原值 */
|
||||
return (U8_T)(flow_state & 0xFFU);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定枪故障/告警标志。
|
||||
* @param gun_no 枪号(0-based)
|
||||
* @return U8_T 0=无,1=故障,2=告警
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_fault_alarm_flag_if(U8_T gun_no)
|
||||
{
|
||||
U8_T pile_fault = u8_get_fault_sum(0U, 0U); /* 整桩故障数量 */
|
||||
U8_T gun_fault = u8_get_fault_sum((U8_T)(gun_no + 1U), 0U); /* 该枪故障数量 */
|
||||
U8_T pile_alarm = u8_get_fault_sum(0U, 1U); /* 整桩告警数量 */
|
||||
U8_T gun_alarm = u8_get_fault_sum((U8_T)(gun_no + 1U), 1U); /* 该枪告警数量 */
|
||||
|
||||
if ((pile_fault != 0U) || (gun_fault != 0U)) {
|
||||
return 1U;
|
||||
}
|
||||
if ((pile_alarm != 0U) || (gun_alarm != 0U)) {
|
||||
return 2U;
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定枪 SOC(0~100)。
|
||||
* @param gun_no 枪号(0-based)
|
||||
* @return U8_T SOC 百分比
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_soc_if(U8_T gun_no)
|
||||
{
|
||||
U16_T soc = u16_flow_get_sys_data(gun_no, E_FLOW_GET_DATA_BMS_SOC, 0U); /* BMS 上报 SOC */
|
||||
if (soc > 100U) {
|
||||
soc = 100U;
|
||||
}
|
||||
return (U8_T)soc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算指定枪对应的指示灯状态编码。
|
||||
* @param gun_no 枪号(0-based)
|
||||
* @return U8_T 指示灯状态(与 yk_ctrl 约定一致)
|
||||
*/
|
||||
static U8_T u8_sub_comm_get_led_state_if(U8_T gun_no)
|
||||
{
|
||||
/* 统一由 led_ctrl 模块计算并缓存两把枪状态,sub_comm 直接读取缓存。 */
|
||||
return u8_led_state_get(gun_no);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 生成 12 字节时间 ASCII(YYMMDDhhmmss)。
|
||||
* @param out 输出缓存,长度至少 12 字节
|
||||
*/
|
||||
static void v_sub_comm_fill_time_ascii12(U8_T *out)
|
||||
{
|
||||
Comm_Time now; /* RTC 当前时间结构体 */
|
||||
char time_buf[13]; /* 临时字符串,包含结尾 '\0' */
|
||||
|
||||
if (out == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
GetCurrentTime(&now);
|
||||
(void)snprintf(time_buf, sizeof(time_buf), "%02u%02u%02u%02u%02u%02u",
|
||||
(unsigned int)(now.iYear % 100),
|
||||
(unsigned int)now.ucMonth,
|
||||
(unsigned int)now.ucDay,
|
||||
(unsigned int)now.ucHour,
|
||||
(unsigned int)now.ucMin,
|
||||
(unsigned int)now.ucSec);
|
||||
(void)memcpy(out, time_buf, SUBCOMM_SYS_TIME_LEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 //发送函数
|
||||
/**
|
||||
* @brief 组帧并通过 USART0 发送一条副板协议命令。
|
||||
*
|
||||
* @param cmd 命令码
|
||||
* @param data 数据域指针(允许 NULL)
|
||||
* @param len 数据长度(0~64)
|
||||
*
|
||||
* @return U8_T 1=发送成功(至少发出一帧);0=参数不合法或发送失败
|
||||
*/
|
||||
static U8_T u8_sub_comm_send_cmd(U8_T cmd, const U8_T *data, U8_T len)
|
||||
{
|
||||
U8_T buf[4U + SUBCOMM_DATA_MAX + 1U]; /* 最大发送帧缓存:head+cmd+len+data+sum+tail */
|
||||
U8_T sum; /* 8位累加和 */
|
||||
uint16_t total_len; /* 实际总帧长 */
|
||||
|
||||
if (len > (U8_T)SUBCOMM_DATA_MAX)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
|
||||
buf[0] = SUBCOMM_FRAME_HEAD;
|
||||
buf[1] = cmd;
|
||||
buf[2] = len;
|
||||
if ((len > 0U) && (data != NULL))
|
||||
{
|
||||
(void)memcpy(&buf[3], data, len);
|
||||
}
|
||||
|
||||
/* 累加和:从帧头到数据域的累加和(取低 8 位) */
|
||||
sum = (U8_T)u16_crc_checksum(&buf[0], (U16_T)(3U + len));
|
||||
buf[3U + len] = sum;
|
||||
buf[4U + len] = SUBCOMM_FRAME_TAIL;
|
||||
|
||||
total_len = (uint16_t)(5U + len);
|
||||
if (u16_usart_send(SUBCOMM_UART_ID, buf, total_len) != total_len)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
return 1U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将副板继电器控制(DO11~DO16)编码为数据域并发送 0x02。
|
||||
*
|
||||
* 数据域编码(本工程默认 bit-map 方案):
|
||||
* - 2 字节(低字节在前):
|
||||
* - bit0~bit5:K1~K6(即 DO11~DO16)
|
||||
* - bit6~bit11:预留遥控点位(RSVD1~RSVD6)
|
||||
* - bit12~bit15:保留(对齐)
|
||||
* bit0 -> DO11(K1)
|
||||
* bit1 -> DO12(K2)
|
||||
* ...
|
||||
* bit5 -> DO16(K6)
|
||||
*/
|
||||
static void v_sub_comm_send_do_ctrl(void)
|
||||
{
|
||||
/* 下发 2 字节,包含 DO11~DO16 + 预留位 */
|
||||
U8_T payload[2]; /* 0x02 数据域 */
|
||||
payload[0] = g_subcomm_ctx.sub_yk.u8_bytes[0];
|
||||
payload[1] = g_subcomm_ctx.sub_yk.u8_bytes[1];
|
||||
(void)u8_sub_comm_send_cmd(SUBCOMM_CMD_DO_CTRL, payload, 2U);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送 0x03 系统信息帧。
|
||||
*
|
||||
* 数据域格式:
|
||||
* - [0..11] 时间字符串 YYMMDDhhmmss
|
||||
* - [12] 枪数量
|
||||
* - [13] 灯板类型
|
||||
* - [14] 液冷使能(0/1)
|
||||
* - [15] 液冷类型(默认0)
|
||||
* - [16..21] 预留
|
||||
*/
|
||||
static void v_sub_comm_send_sys_info(void)
|
||||
{
|
||||
U8_T payload[SUBCOMM_SYS_INFO_LEN] = {0}; /* 0x03 数据域 */
|
||||
|
||||
v_sub_comm_fill_time_ascii12(&payload[0]);
|
||||
payload[12] = u8_sub_comm_get_gun_cnt_if();
|
||||
payload[13] = u8_sub_comm_get_led_board_type_if();
|
||||
payload[14] = u8_sub_comm_get_liquid_cooling_enable_if(); /* liquid_cooling_enable */
|
||||
payload[15] = u8_sub_comm_get_liquid_cooling_type_if(); /* liquid_cooling_type */
|
||||
/* payload[16..21] 为预留,默认0 */
|
||||
|
||||
(void)u8_sub_comm_send_cmd(SUBCOMM_CMD_SYS_INFO, payload, (U8_T)sizeof(payload));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送 0x04 充电枪信息帧。
|
||||
* @param gun_no 枪号(0-based)
|
||||
*
|
||||
* 数据域格式:
|
||||
* - [0] 枪号(1-based) [1]连接状态 [2]充电状态 [3]灯状态
|
||||
* - [4]故障告警标志 [5]SOC [6..13]预留
|
||||
*/
|
||||
static void v_sub_comm_send_gun_info(U8_T gun_no)
|
||||
{
|
||||
U8_T payload[SUBCOMM_GUN_INFO_LEN] = {0}; /* 0x04 数据域 */
|
||||
|
||||
payload[0] = (U8_T)(gun_no + 1U); /* 枪号:1/2 */
|
||||
payload[1] = u8_sub_comm_get_gun_link_if(gun_no); /* 枪连接状态 */
|
||||
payload[2] = u8_sub_comm_get_charge_state_if(gun_no); /* 充电状态 */
|
||||
payload[3] = u8_sub_comm_get_led_state_if(gun_no); /* 指示灯状态 */
|
||||
payload[4] = u8_sub_comm_get_fault_alarm_flag_if(gun_no);/* 故障告警标志 */
|
||||
payload[5] = u8_sub_comm_get_soc_if(gun_no); /* 充电SOC */
|
||||
/* payload[6..13] 为预留,默认0 */
|
||||
|
||||
(void)u8_sub_comm_send_cmd(SUBCOMM_CMD_GUN_INFO, payload, (U8_T)sizeof(payload));
|
||||
}
|
||||
/**
|
||||
* @brief 发送处理:按调用计数轮询发送 0x02/0x03/0x04。
|
||||
* @param period_ms 本次调用周期(ms,当前方案未使用)
|
||||
*
|
||||
* 轮询策略:
|
||||
* - 偶数轮:固定发送 0x02(保证与上次 0x02 至少间隔一个执行周期);
|
||||
* - 奇数轮:按状态发送信息帧(0=0x03,1=1枪0x04,2=2枪0x04)。
|
||||
*/
|
||||
static void v_sub_comm_tx_process(uint16_t period_ms)
|
||||
{
|
||||
static U8_T s_send_turn = 0U; /* 发送轮次计数:偶数发0x02,奇数发信息帧 */
|
||||
static U8_T s_info_slot = 0U; /* 信息帧状态:0=0x03,1=1枪0x04,2=2枪0x04 */
|
||||
U8_T gun_cnt; /* 当前枪数量 */
|
||||
|
||||
(void)period_ms;
|
||||
|
||||
if ((s_send_turn & 0x01U) == 0U)
|
||||
{
|
||||
v_sub_comm_send_do_ctrl();
|
||||
s_send_turn++;
|
||||
return;
|
||||
}
|
||||
|
||||
gun_cnt = u8_sub_comm_get_gun_cnt_if();
|
||||
|
||||
switch (s_info_slot)
|
||||
{
|
||||
case 0U:
|
||||
v_sub_comm_send_sys_info();
|
||||
s_info_slot = 1U;
|
||||
break;
|
||||
|
||||
case 1U:
|
||||
if (gun_cnt >= 1U)
|
||||
{
|
||||
v_sub_comm_send_gun_info(0U); /* 发送1枪数据 */
|
||||
}
|
||||
/* 在状态1判断是否双枪:双枪进状态2,否则回状态0。 */
|
||||
s_info_slot = (gun_cnt >= 2U) ? 2U : 0U;
|
||||
break;
|
||||
|
||||
case 2U:
|
||||
if (gun_cnt >= 2U)
|
||||
{
|
||||
v_sub_comm_send_gun_info(1U); /* 发送2枪数据 */
|
||||
}
|
||||
s_info_slot = 0U;
|
||||
break;
|
||||
|
||||
default:
|
||||
s_info_slot = 0U;
|
||||
break;
|
||||
}
|
||||
|
||||
s_send_turn++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 // 接收函数(USART FIFO -> RX缓存 -> 解帧)
|
||||
/**
|
||||
* @brief 接收处理:从 USART0 FIFO 读取并解析所有已到达数据。
|
||||
*/
|
||||
static void v_sub_comm_rx_process(void)
|
||||
{
|
||||
/* 接收:尽可能把 FIFO 中的数据取出并追加到接收缓存 */
|
||||
uint8_t rx_buf[64]; /* USART 临时读缓存 */
|
||||
uint16_t left; /* 本轮读到的字节数 */
|
||||
uint16_t free_space;/* 全局接收缓存剩余空间 */
|
||||
uint16_t copy_len; /* 本次可复制字节数 */
|
||||
|
||||
left = u16_usart_recv(SUBCOMM_UART_ID, rx_buf, (uint16_t)sizeof(rx_buf));
|
||||
while (left > 0U)
|
||||
{
|
||||
/* 追加到缓存,满了则丢弃最老的数据(保留末尾 1/2,类似 meter 的处理) */
|
||||
free_space = (g_subcomm_ctx.rx_len < (U16_T)SUBCOMM_RX_BUF_MAX) ? (U16_T)(SUBCOMM_RX_BUF_MAX - g_subcomm_ctx.rx_len) : 0U;
|
||||
copy_len = (left > free_space) ? free_space : left;
|
||||
|
||||
if (copy_len > 0U)
|
||||
{
|
||||
(void)memcpy(&g_subcomm_ctx.rx_buf[g_subcomm_ctx.rx_len], rx_buf, copy_len);
|
||||
g_subcomm_ctx.rx_len = (U16_T)(g_subcomm_ctx.rx_len + copy_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 缓冲区无空间:保留最后 1/2 数据 */
|
||||
if (g_subcomm_ctx.rx_len > (U16_T)(SUBCOMM_RX_BUF_MAX / 2U))
|
||||
{
|
||||
(void)memmove(g_subcomm_ctx.rx_buf,
|
||||
&g_subcomm_ctx.rx_buf[g_subcomm_ctx.rx_len - (U16_T)(SUBCOMM_RX_BUF_MAX / 2U)],
|
||||
(U16_T)(SUBCOMM_RX_BUF_MAX / 2U));
|
||||
g_subcomm_ctx.rx_len = (U16_T)(SUBCOMM_RX_BUF_MAX / 2U);
|
||||
}
|
||||
}
|
||||
|
||||
/* 若一次读取的数据超出剩余空间,剩余部分丢弃(避免阻塞/反复搬运)。 */
|
||||
left = u16_usart_recv(SUBCOMM_UART_ID, rx_buf, (uint16_t)sizeof(rx_buf));
|
||||
}
|
||||
|
||||
/* 解析:循环消费缓存中的完整帧(处理粘包) */
|
||||
while (u8_sub_comm_try_parse_one_frame() != 0U) {}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if 1 // 超时处理函数
|
||||
/**
|
||||
* @brief 超时处理:若 DI 状态帧(0x01)长时间未更新,则清空 YX/DIP 缓存。
|
||||
*
|
||||
* 策略:
|
||||
* - 若从未收到过 DI 帧(last_di_ms==0),不处理;
|
||||
* - 若超过 SUBCOMM_DI_TIMEOUT_MS 未更新,清空 g_subcomm_ctx.sub_yx(全部置 0)。
|
||||
*/
|
||||
static void v_sub_comm_timeout_process(void)
|
||||
{
|
||||
uint64_t now_ms; /* 当前时间戳(ms),用于调试保留 */
|
||||
uint64_t delta_ms; /* 距离上次 DI 更新时间 */
|
||||
|
||||
if (g_subcomm_ctx.last_di_ms == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
now_ms = u64_get_current_millis();
|
||||
delta_ms = u64_safe_millis_since(g_subcomm_ctx.last_di_ms);
|
||||
if (delta_ms >= (uint64_t)SUBCOMM_DI_TIMEOUT_MS)
|
||||
{
|
||||
(void)memset(&g_subcomm_ctx.sub_yx, 0, sizeof(g_subcomm_ctx.sub_yx));
|
||||
/* 避免每个周期都重复清零:重置 last_di_ms,等待下次有效帧重新置位。 */
|
||||
g_subcomm_ctx.last_di_ms = 0U;
|
||||
}
|
||||
(void)now_ms;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 // 对外接口函数(缓存读写)
|
||||
/**
|
||||
* @brief 写入副板遥控(YK)命令缓存。
|
||||
*
|
||||
* @param e_type 点位(DO11~DO16 或预留位)
|
||||
* @param e_cmd CMD_OFF/CMD_ON
|
||||
*
|
||||
* @note 仅更新缓存;串口下发由 v_sub_comm_poll() 周期执行。
|
||||
*/
|
||||
void v_sub_comm_yk_set(E_SUB_YK_TYPE e_type, E_CMD e_cmd)
|
||||
{
|
||||
if ((U8_T)e_type >= (U8_T)E_SUB_YK_NUM)
|
||||
{
|
||||
return;
|
||||
}
|
||||
v_sub_comm_bit_set_u16(&g_subcomm_ctx.sub_yk.u16_bits, (U8_T)e_type, (U8_T)((e_cmd == CMD_OFF) ? 0U : 1U));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取副板遥控(YK)命令缓存。
|
||||
* @return CMD_OFF/CMD_ON
|
||||
*/
|
||||
E_CMD e_sub_comm_yk_get(E_SUB_YK_TYPE e_type)
|
||||
{
|
||||
if ((U8_T)e_type >= (U8_T)E_SUB_YK_NUM)
|
||||
{
|
||||
return CMD_OFF;
|
||||
}
|
||||
return (u8_sub_comm_bit_get_u16(g_subcomm_ctx.sub_yk.u16_bits, (U8_T)e_type) == 0U) ? CMD_OFF : CMD_ON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取副板遥信(YX)/拨码(DIP)/预留位采集缓存。
|
||||
* @return 0/1
|
||||
*/
|
||||
U8_T u8_sub_comm_yx_get(E_SUB_YX_TYPE e_type)
|
||||
{
|
||||
if ((U8_T)e_type >= (U8_T)E_SUB_YX_NUM)
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
return u8_sub_comm_bit_get_u32(g_subcomm_ctx.sub_yx.u32_bits, (U8_T)e_type);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#if 1 // SubComm 任务入口
|
||||
/**
|
||||
* @brief 副板通信任务(USART0)。
|
||||
*
|
||||
* 职责:
|
||||
* - 解析副板上报帧(0x01),刷新副板 DI/DIP 状态;
|
||||
* - 周期下发副板继电器控制(0x02),数据来源为 g_subcomm_ctx.sub_yk 缓存;
|
||||
* - 任务喂狗与运行状态打印。
|
||||
*/
|
||||
void v_sub_comm_task(void *arg)
|
||||
{
|
||||
uint16_t sleep_time_ms; /* 任务轮询周期(ms) */
|
||||
uint32_t last_send_time;/* 上次运行日志打印时间(s) */
|
||||
uint32_t current_time; /* 当前秒计时 */
|
||||
|
||||
(void)arg;
|
||||
(void)SUBCOMM_LOCK_INIT();
|
||||
sleep_time_ms = SUBCOMM_SLEEP_TIME_MS();
|
||||
last_send_time = get_current_seconds();
|
||||
|
||||
v_sub_comm_data_init();
|
||||
|
||||
for (;;) {
|
||||
SUBCOMM_FEED_DOG();
|
||||
current_time = get_current_seconds();
|
||||
if ((TASK_RUN_PRINT_TIME != 0U) && (u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) {
|
||||
if (SUBCOMM_LOCK_TAKE() == 1U) {
|
||||
SUBCOMM_LOG("sub comm task is runing, sleep time is %ums", sleep_time_ms);
|
||||
(void)SUBCOMM_LOCK_GIVE();
|
||||
}
|
||||
last_send_time = current_time;
|
||||
}
|
||||
|
||||
/* 副板通信:接收 / 发送 / 超时 三部分独立执行 */
|
||||
v_sub_comm_rx_process();
|
||||
v_sub_comm_tx_process(sleep_time_ms);
|
||||
v_sub_comm_timeout_process();
|
||||
|
||||
mSleep(sleep_time_ms);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
#ifndef SUB_COMM_TASK_H
|
||||
#define SUB_COMM_TASK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "publicdata/type.h"
|
||||
|
||||
/* ======================== 副板遥控(YK) 点位定义 ======================== */
|
||||
/* 说明:
|
||||
* - 副板 YK 代表“主板下发到副板的输出控制命令”;
|
||||
* - 当前定义的 DO11~DO16 对应副板继电器 K1~K6(协议命令 0x02);
|
||||
* - 预留点位用于后续扩展,不影响现有协议与数据结构布局。 */
|
||||
typedef enum
|
||||
{
|
||||
/* 实际点位:DO11 ~ DO16(副板输出) */
|
||||
E_SUB_YK_DO11 = 0,
|
||||
E_SUB_YK_DO12,
|
||||
E_SUB_YK_DO13,
|
||||
E_SUB_YK_DO14,
|
||||
E_SUB_YK_DO15,
|
||||
E_SUB_YK_DO16,
|
||||
|
||||
/* 预留点位:便于后续扩展(副板输出) */
|
||||
E_SUB_YK_RSVD1,
|
||||
E_SUB_YK_RSVD2,
|
||||
E_SUB_YK_RSVD3,
|
||||
E_SUB_YK_RSVD4,
|
||||
E_SUB_YK_RSVD5,
|
||||
E_SUB_YK_RSVD6,
|
||||
|
||||
E_SUB_YK_NUM
|
||||
} E_SUB_YK_TYPE;
|
||||
|
||||
typedef union
|
||||
{
|
||||
/* 按 bit 存储的命令缓存(节省空间):
|
||||
* - 低 12 bit 对应 E_SUB_YK_TYPE 的 12 个点位;
|
||||
* - bit 取值:0=CMD_OFF,1=CMD_ON;
|
||||
* - u8_bytes 便于协议层直接取字节序列(低字节在前)。 */
|
||||
uint16_t u16_bits;
|
||||
U8_T u8_bytes[2];
|
||||
struct
|
||||
{
|
||||
/* bit-field 视图(与枚举顺序一一对应) */
|
||||
uint16_t do11 : 1; /* DO11 / K1 */
|
||||
uint16_t do12 : 1; /* DO12 / K2 */
|
||||
uint16_t do13 : 1; /* DO13 / K3 */
|
||||
uint16_t do14 : 1; /* DO14 / K4 */
|
||||
uint16_t do15 : 1; /* DO15 / K5 */
|
||||
uint16_t do16 : 1; /* DO16 / K6 */
|
||||
uint16_t rsvd1 : 1; /* 预留1 */
|
||||
uint16_t rsvd2 : 1; /* 预留2 */
|
||||
uint16_t rsvd3 : 1; /* 预留3 */
|
||||
uint16_t rsvd4 : 1; /* 预留4 */
|
||||
uint16_t rsvd5 : 1; /* 预留5 */
|
||||
uint16_t rsvd6 : 1; /* 预留6 */
|
||||
uint16_t _pad : 4; /* 对齐到 16bit */
|
||||
} b_yk;
|
||||
} U_SUB_YK_DATA;
|
||||
|
||||
/* ======================== 副板遥信(YX) 点位定义 ======================== */
|
||||
/* 说明:
|
||||
* - 副板 YX 代表“从副板采集回来的输入状态”;
|
||||
* - 当前包含:
|
||||
* 1) YX13~YX22:10 路 DI 输入检测
|
||||
* 2) DIP1~DIP6:6 路拨码开关位(用于副板地址/模式等)
|
||||
* - 具体编码由协议命令 0x01 的数据域决定(在 sub_comm_task.c 中实现映射)。 */
|
||||
typedef enum
|
||||
{
|
||||
/* 实际点位:YX13 ~ YX22(副板输入) */
|
||||
E_SUB_YX_13 = 0,
|
||||
E_SUB_YX_14,
|
||||
E_SUB_YX_15,
|
||||
E_SUB_YX_16,
|
||||
E_SUB_YX_17,
|
||||
E_SUB_YX_18,
|
||||
E_SUB_YX_19,
|
||||
E_SUB_YX_20,
|
||||
E_SUB_YX_21,
|
||||
E_SUB_YX_22,
|
||||
|
||||
/* 拨码位:6 位 */
|
||||
E_SUB_DIP_1,
|
||||
E_SUB_DIP_2,
|
||||
E_SUB_DIP_3,
|
||||
E_SUB_DIP_4,
|
||||
E_SUB_DIP_5,
|
||||
E_SUB_DIP_6,
|
||||
|
||||
/* 预留位:8 bit(用于后续扩展副板DI/状态位) */
|
||||
E_SUB_YX_RSVD1,
|
||||
E_SUB_YX_RSVD2,
|
||||
E_SUB_YX_RSVD3,
|
||||
E_SUB_YX_RSVD4,
|
||||
E_SUB_YX_RSVD5,
|
||||
E_SUB_YX_RSVD6,
|
||||
E_SUB_YX_RSVD7,
|
||||
E_SUB_YX_RSVD8,
|
||||
|
||||
E_SUB_YX_NUM
|
||||
} E_SUB_YX_TYPE;
|
||||
|
||||
typedef union
|
||||
{
|
||||
/* 按 bit 存储的采集缓存(节省空间):
|
||||
* - 低 24 bit 对应 E_SUB_YX_TYPE 的点位(16个有效 + 8个预留);
|
||||
* - bit 取值:0/1;
|
||||
* - u8_bytes 便于协议层直接取字节序列(低字节在前)。 */
|
||||
uint32_t u32_bits;
|
||||
U8_T u8_bytes[4];
|
||||
struct
|
||||
{
|
||||
/* bit-field 视图(与枚举顺序一一对应) */
|
||||
uint16_t yx13 : 1;
|
||||
uint16_t yx14 : 1;
|
||||
uint16_t yx15 : 1;
|
||||
uint16_t yx16 : 1;
|
||||
uint16_t yx17 : 1;
|
||||
uint16_t yx18 : 1;
|
||||
uint16_t yx19 : 1;
|
||||
uint16_t yx20 : 1;
|
||||
uint16_t yx21 : 1;
|
||||
uint16_t yx22 : 1;
|
||||
uint16_t dip1 : 1;
|
||||
uint16_t dip2 : 1;
|
||||
uint16_t dip3 : 1;
|
||||
uint16_t dip4 : 1;
|
||||
uint16_t dip5 : 1;
|
||||
uint16_t dip6 : 1;
|
||||
uint16_t rsvd1 : 1;
|
||||
uint16_t rsvd2 : 1;
|
||||
uint16_t rsvd3 : 1;
|
||||
uint16_t rsvd4 : 1;
|
||||
uint16_t rsvd5 : 1;
|
||||
uint16_t rsvd6 : 1;
|
||||
uint16_t rsvd7 : 1;
|
||||
uint16_t rsvd8 : 1;
|
||||
uint16_t _pad : 8; /* 对齐到 32bit */
|
||||
} b_yx;
|
||||
} U_SUB_YX_DATA;
|
||||
|
||||
/* 缓存访问接口:
|
||||
* - YK:上层写入命令缓存;副板通信线程负责周期下发(协议实现后接入)
|
||||
* - YX:副板通信线程刷新采集缓存;上层读取缓存即可 */
|
||||
/**
|
||||
* @brief 写入副板遥控(YK)命令缓存。
|
||||
* @note 该函数只更新缓存;真正的串口下发由 SubComm 线程周期执行。
|
||||
*/
|
||||
void v_sub_comm_yk_set(E_SUB_YK_TYPE e_type, E_CMD e_cmd);
|
||||
|
||||
/**
|
||||
* @brief 读取副板遥控(YK)命令缓存(用于调试或回显)。
|
||||
*/
|
||||
E_CMD e_sub_comm_yk_get(E_SUB_YK_TYPE e_type);
|
||||
|
||||
/**
|
||||
* @brief 读取副板遥信(YX)/拨码(DIP)采集缓存。
|
||||
* @return 0/1 状态值
|
||||
*/
|
||||
U8_T u8_sub_comm_yx_get(E_SUB_YX_TYPE e_type);
|
||||
|
||||
void v_sub_comm_task(void *arg);
|
||||
U8_T u8_sub_comm_get_gun_num(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
# sub_comm 模块说明
|
||||
|
||||
> 路径:`app/sub_comm/`
|
||||
> 返回主说明:[README](../../README.md)
|
||||
|
||||
## 1. 模块职责
|
||||
|
||||
- 主板侧 **USART0** 与副板(CCU621-S)通信:`0x01` 接收 DI/DIP,`0x02/0x03/0x04` 下发 DO 与系统/枪信息;
|
||||
- 维护副板 YK/YX 缓存,供其它模块读写;
|
||||
- FreeRTOS 周期任务:接收拼帧、发送轮询、DI 超时清零遥信缓存。
|
||||
|
||||
## 2. 关键文件
|
||||
|
||||
- `sub_comm_task.c`
|
||||
- `sub_comm_task.h`
|
||||
|
||||
## 3. 功能说明
|
||||
|
||||
- 任务模型对齐工程其它通信任务(初始化、周期循环、任务锁、喂狗、日志);
|
||||
- **执行周期 `50ms`**(`app_init.c` 中 `TASK_SLEEP_MS_50`);
|
||||
- 对外接口:
|
||||
- `void v_sub_comm_task(void *arg)`:SubComm 线程入口(FreeRTOS)
|
||||
- `void v_sub_comm_yk_set(E_SUB_YK_TYPE e_type, E_CMD e_cmd)`:写入待下发的 DO(YK)缓存;实际下发见 `v_sub_comm_tx_process()` 轮询
|
||||
- `E_CMD e_sub_comm_yk_get(E_SUB_YK_TYPE e_type)`:读取 YK 缓存
|
||||
- `U8_T u8_sub_comm_yx_get(E_SUB_YX_TYPE e_type)`:读取最近一次有效 `0x01` 刷新后的 YX/DIP 缓存
|
||||
|
||||
## 4. 维护建议
|
||||
|
||||
- 校验字段实现上使用 `u16_crc_checksum()`,函数名为历史命名,实质为 **字节累加和**(见 `public_func.c`),与副板 `subcomm_checksum8` 语义一致(结果取低 8 位);
|
||||
- 可增加链路统计(超时次数、校验失败计数)便于现场诊断;
|
||||
- 大报文时可评估环形缓冲;当前 `SUBCOMM_RX_BUF_MAX=128` 与副板一致。
|
||||
|
||||
## 5. 关联文档
|
||||
|
||||
- [plat_comm模块说明](../plat_comm/plat_comm模块说明.md)
|
||||
- 主索引:[README](../../README.md)
|
||||
|
||||
---
|
||||
|
||||
## 6. 协议格式说明(副板:USART0,主板<->副板)
|
||||
|
||||
本模块使用自定义短帧协议,帧头/帧尾固定,命令码区分 DI 上报与 DO 控制。
|
||||
|
||||
### 6.1 帧结构(通用)
|
||||
|
||||
单帧总长度:`total_len = 5 + len`(其中 `len` 为数据域长度)
|
||||
|
||||
| 字节序号 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `[0]` | `HEAD` | 固定帧头:`0x5A` |
|
||||
| `[1]` | `cmd` | 命令码(DI/DO) |
|
||||
| `[2]` | `len` | 数据域长度:`0~64` |
|
||||
| `[3..3+len-1]` | `data[]` | 数据域(按 `cmd` 不同含义不同) |
|
||||
| `[3+len]` | `sum` | 校验:对帧头到数据域的校验结果取低 8 位 |
|
||||
| `[4+len]` | `TAIL` | 固定帧尾:`0xA5` |
|
||||
|
||||
### 6.2 校验字段 `sum`
|
||||
|
||||
- 校验计算(发送与接收一致):
|
||||
- `sum = (uint8_t)u16_crc_checksum(&buf[0], 3 + len)`
|
||||
- 即对 `[0]~[2+len]`(帧头+cmd+len+data)做 `u16_crc_checksum`,再取低 8 位作为 `sum`
|
||||
|
||||
### 6.3 命令码定义
|
||||
|
||||
- `0x01`:`SUBCOMM_CMD_DI_STATUS`
|
||||
- 方向:副板 -> 主板(上报 DI 状态)
|
||||
- 数据域长度:工程约定 `len >= 2`(低 16bit 有效)
|
||||
- `0x02`:`SUBCOMM_CMD_DO_CTRL`
|
||||
- 方向:主板 -> 副板(控制副板继电器 DO11~DO16)
|
||||
- 数据域长度:固定 `len = 2`(两个字节编码 DO11~DO16 + 预留位)
|
||||
- `0x03`:`SUBCOMM_CMD_SYS_INFO`
|
||||
- 方向:主板 -> 副板(下发系统信息)
|
||||
- 数据域长度:固定 `len = 22`
|
||||
- `0x04`:`SUBCOMM_CMD_GUN_INFO`
|
||||
- 方向:主板 -> 副板(下发充电枪信息)
|
||||
- 数据域长度:固定 `len = 14`
|
||||
|
||||
### 6.4 DI 状态帧(`cmd=0x01`)数据域映射
|
||||
|
||||
通信方向:**副板 -> 主板**(副板上报 DI/DIP 状态)。
|
||||
|
||||
数据域低 16bit(`data[0]` 为低字节,`data[1]` 为高字节)用于承载:
|
||||
|
||||
- `bit0 -> YX13`
|
||||
- `bit1 -> YX14`
|
||||
- `...`
|
||||
- `bit9 -> YX22`
|
||||
- `bit10 -> DIP1`
|
||||
- `...`
|
||||
- `bit15 -> DIP6`
|
||||
|
||||
当接收未超时(见 6.5)时,上述位值被写入副板缓存 `g_subcomm_ctx.sub_yx.u32_bits`。
|
||||
|
||||
### 6.5 DO 控制帧(`cmd=0x02`)数据域映射
|
||||
|
||||
通信方向:**主板 -> 副板**(主板下发 DO11~DO16 控制命令)。
|
||||
|
||||
数据域固定 2 字节(`payload[0]` 为低字节,`payload[1]` 为高字节),承载:
|
||||
|
||||
- `bit0 -> DO11(K1)`
|
||||
- `bit1 -> DO12(K2)`
|
||||
- `bit2 -> DO13(K3)`
|
||||
- `bit3 -> DO14(K4)`
|
||||
- `bit4 -> DO15(K5)`
|
||||
- `bit5 -> DO16(K6)`
|
||||
- `bit6 -> RSVD1`(预留遥控位,不参与当前业务)
|
||||
- `bit7 -> RSVD2`
|
||||
- `...`
|
||||
- `bit11 -> RSVD6`
|
||||
- `bit12~bit15`:保留对齐
|
||||
|
||||
### 6.6 系统信息帧(`cmd=0x03`)数据域映射(新增)
|
||||
|
||||
通信方向:**主板 -> 副板**(主板周期/按需下发系统状态信息)。
|
||||
|
||||
数据域固定 22 字节,字段定义如下:
|
||||
|
||||
| 偏移 | 长度 | 字段 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `0` | `12` | `time_str` | 当前时间,ASCII 格式 `YYMMDDhhmmss`,示例:`260101120000` |
|
||||
| `12` | `1` | `gun_cnt` | 枪数量,单字节 `0~N` |
|
||||
| `13` | `1` | `led_board_type` | 灯板类型,单字节 `0~N` |
|
||||
| `14` | `1` | `liquid_cooling_enable` | 液冷使能标志,`0/1` |
|
||||
| `15` | `1` | `liquid_cooling_type` | 液冷类型;本工程未提供数据来源,默认 `0x00` |
|
||||
| `16` | `6` | `rsvd` | 预留 6 字节,当前固定 `0x00` |
|
||||
|
||||
说明:
|
||||
|
||||
- 时间字段为纯 ASCII 数字,不带结束符 `\0`;
|
||||
- `gun_cnt` 建议来自主板当前配置枪数;
|
||||
- `led_board_type` 建议在后续实现时由板型参数或配置项给出。
|
||||
- `liquid_cooling_enable` 取值来源:主板系统配置 `u_enable.bit_enable.bit_YeLenEn` 的最低位(约定 `0/1`);
|
||||
- `liquid_cooling_type` 当前未对接任何系统接口,固定为 `0`(预留给后续协议扩展)。
|
||||
|
||||
### 6.7 充电枪信息帧(`cmd=0x04`)数据域映射(新增)
|
||||
|
||||
通信方向:**主板 -> 副板**(按枪号下发枪状态信息)。
|
||||
|
||||
数据域固定 14 字节,字段定义如下:
|
||||
|
||||
| 偏移 | 长度 | 字段 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `0` | `1` | `gun_no` | 充电枪号,单字节,取值 `1/2` |
|
||||
| `1` | `1` | `gun_link` | 枪连接状态,`0` 未连接,`1` 已连接 |
|
||||
| `2` | `1` | `charge_state` | 充电状态,取 `flow_interface.c` 的 `e_flow_real_state`(`u16_flow_put_data(...E_FLOW_PUT_DATA_GUN_REAL_STATE...)`) |
|
||||
| `3` | `1` | `led_state` | 指示灯状态,枚举参考 `E_LED_STATE_TYPE`:`0`绿常亮、`1`绿闪、`2`红常亮、`3`红闪、`4`黄常亮、`5`黄闪 |
|
||||
| `4` | `1` | `fault_alarm_flag` | 故障告警标志:`0`无、`1`故障、`2`告警 |
|
||||
| `5` | `1` | `soc` | 充电 SOC,单字节 `0~100` |
|
||||
| `6` | `8` | `rsvd` | 预留 8 字节,当前固定 `0x00` |
|
||||
|
||||
### 6.8 发送节拍与接收超时
|
||||
|
||||
> 说明:当前 `sub_comm_task.c` 中 DO/信息帧发送节拍由 SubComm 线程 `50ms` 周期的轮询位移决定,而不是由某个固定 `SUBCOMM_CMD_PERIOD_MS` 常量直接控制。
|
||||
|
||||
- DO 控制帧 `0x02`:
|
||||
- 在 `v_sub_comm_tx_process()` 中通过 `s_send_turn` 轮询发送;
|
||||
- 当 SubComm 任务周期为 `50ms` 时,`0x02` 下发约为 `100ms/次`(偶数执行周期发送)。
|
||||
- 系统信息 `0x03` / 枪信息 `0x04`:
|
||||
- 与 `0x02` 轮流发送;
|
||||
- 轮询顺序为:`0x03`(系统)-> `0x04`(枪1)-> `0x04`(枪2,若配置存在)-> 下一轮。
|
||||
- DI 接收超时(离线判断):
|
||||
- `SUBCOMM_DI_TIMEOUT_MS = 5000ms`
|
||||
- 若超过该时间未收到有效 DI 状态帧(`cmd=0x01`),则清零副板 YX/DIP 缓存,避免使用过期状态。
|
||||
|
||||
### 6.9 收发处理流程(概览)
|
||||
|
||||
- 接收端:
|
||||
- 从 USART0 FIFO 尽可能读取数据追加到 `rx_buf`(支持粘包/残包)。
|
||||
- 在 `while (u8_sub_comm_try_parse_one_frame() != 0) {}` 中循环解帧,校验失败则丢弃 1 字节重新同步。
|
||||
- 处理策略:当前接收侧仅对 `cmd=0x01` 的 DI 状态帧刷新 YX/DIP 缓存,其它 `cmd` 默认不落业务缓存。
|
||||
- 发送端:
|
||||
- 上层通过 `v_sub_comm_yk_set()` 仅修改副板 YK 缓存位图;串口下发由副板通信任务周期性执行。
|
||||
Reference in New Issue
Block a user