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
|
||||
Reference in New Issue
Block a user