Initial commit: CCU621_M firmware project with BLE debug link support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:28:36 +08:00
commit 9ceb218f80
1597 changed files with 724159 additions and 0 deletions
+834
View File
@@ -0,0 +1,834 @@
#include "PCM_comm.h"
#include "lwip/sockets.h"
#include "mdu_comm/mdu_comm_task.h"
#include "mdu_comm/mdu_interface.h"
#include "app_init/app_init.h"
#include "mylog/mylog.h"
#include "publicdata/publicdata.h"
#include "wdt_task/wdt_task.h"
#include "app_init/app_init.h"
#include "app_init/app_test.h"
#include "app_init/task_lock.h"
#include "flowctrl/flowctrl_task.h"
#include "flowctrl/flow_interface.h"
#include "collect_ctrl/collect_task.h"
#include <string.h>
#include <stdio.h>
/*============================================================================
* PCM UDP 通信模块(终端角色)
*
* 功能:实现群充终端(分桩)与主机之间的UDP通信协议
* 协议定义参考:主机终端通信协议.md
*
* 本机角色:终端(分桩)
* - 主动发送:分桩心跳帧(0x10) 周期1000ms
* - 主动发送:分桩请求帧(0x12) 周期200ms
* - 接收处理:主机心跳帧(0x11)
* - 接收处理:分桩请求应答帧(0x13)
*
* 帧结构:
* 5A 99 - 帧头(2B)
* xx - 帧功能码(1B)
* xx - 数据长度(1B)
* data - 帧数据(NB)
* xx - CRC校验(1B)
* A5 - 帧尾(1B)
*============================================================================*/
/*============================================================================
* 局部宏定义
*============================================================================*/
#define PCM_COMM_TASK_ID (TASK_ID_MduComm)
#define PCM_COMM_LOG(fmt, ...) MYLOG_MSG(PCM_COMM_TASK_ID, (fmt), ##__VA_ARGS__)
/* 分桩请求帧:每组枪数据长度(11字节) */
#define PCM_TERM_REQUEST_GUN_DATA_LEN 11
/* 分桩请求应答帧:每组枪数据长度(9字节) */
#define PCM_HOST_RESPONSE_GUN_DATA_LEN 9
/* 0x12 分桩请求帧测试开关:
* 0: 使用系统实时数据
* 1: 使用本文件内置测试结构体数据(脱离系统数据源联调) */
#ifndef PCM_TERM_REQUEST_TEST_EN
#define PCM_TERM_REQUEST_TEST_EN 0
#endif
/* UDP收发内容打印开关(默认关闭,避免刷屏/占用CPU)
* 0: 关闭
* 1: 打印 TX/RX 数据(十六进制,截断到最大字节数) */
#ifndef PCM_UDP_PRINT_EN
#define PCM_UDP_PRINT_EN 0
#endif
#ifndef PCM_UDP_PRINT_MAX_BYTES
#define PCM_UDP_PRINT_MAX_BYTES 64U
#endif
/*============================================================================
* 局部变量
*============================================================================*/
/* UDP 套接字句柄 */
static int g_pcm_udp_sock = -1;
/* UDP 目标地址结构体(主机地址) */
static struct sockaddr_in g_pcm_peer_addr;
/* PCM 通信数据 */
S_PCM_COMM_DATA s_pcm_comm;
/* 接收缓冲区 */
static U8_T g_u8_recv_buf[PCM_FRAME_MAX_LEN];
#if (PCM_UDP_PRINT_EN != 0)
static void v_pcm_log_hex(const char *tag, const U8_T *data, U16_T len)
{
char line[PCM_UDP_PRINT_MAX_BYTES * 3U + 16U];
U16_T n = len;
U16_T i;
int pos = 0;
if ((tag == NULL) || (data == NULL)) {
return;
}
if (n > (U16_T)PCM_UDP_PRINT_MAX_BYTES) {
n = (U16_T)PCM_UDP_PRINT_MAX_BYTES;
}
for (i = 0U; i < n; i++) {
int w = snprintf(&line[pos], sizeof(line) - (size_t)pos, "%02X ", (unsigned)data[i]);
if (w <= 0) {
break;
}
pos += w;
if (pos >= (int)sizeof(line) - 1) {
break;
}
}
line[(pos < (int)sizeof(line)) ? pos : ((int)sizeof(line) - 1)] = '\0';
if (len > n) {
printf("%s len=%u show=%u: %s...\r\n", tag, (unsigned)len, (unsigned)n, line);
} else {
printf("%s len=%u: %s\r\n", tag, (unsigned)len, line);
}
}
#endif
#if (PCM_UDP_PRINT_EN != 0)
static void v_pcm_log_addr_port(const char *tag, const struct sockaddr_in *addr)
{
U32_T ip_host;
U16_T port_host;
if ((tag == NULL) || (addr == NULL)) {
return;
}
ip_host = PP_NTOHL(addr->sin_addr.s_addr);
port_host = PP_NTOHS(addr->sin_port);
printf("%s %u.%u.%u.%u:%u\r\n",
tag,
(unsigned)((ip_host >> 24) & 0xFFU),
(unsigned)((ip_host >> 16) & 0xFFU),
(unsigned)((ip_host >> 8) & 0xFFU),
(unsigned)(ip_host & 0xFFU),
(unsigned)port_host);
}
#endif
#if (PCM_TERM_REQUEST_TEST_EN != 0)
typedef struct
{
U8_T u8_gun_num;
S_PCM_GUN_TERM_DATA s_gun_data[GUN_MAX_CNT];
} S_PCM_TERM_REQUEST_TEST_DATA;
/* 0x12 测试固定数据(仅在 PCM_TERM_REQUEST_TEST_EN=1 时生效) */
static S_PCM_TERM_REQUEST_TEST_DATA s_pcm_term_req_test_data = {
2U,
{
/* gun_addr, work_state, contactor_dc, contactor_discharge, need_vol(0.1V), need_cur(0.1A), fault_state, fault_reason */
{1U, 0U, 0U, 0U, 0U, 0U, 0U, 0U},
{2U, 0U, 0U, 0U, 0U, 0U, 0U, 0U}
}
};
#endif
/*============================================================================
* 局部函数声明
*============================================================================*/
static void v_pcm_udp_socket_init(void);
//static U8_T u8_pcm_verify_frame(const S_PCM_FRAME *ps_frame);
static S8_T s8_pcm_send_frame(U8_T u8_cmd, const U8_T *pu8_data, U8_T u8_len);
static void v_pcm_parse_host_heartbeat(const S_PCM_FRAME *ps_frame);
static void v_pcm_parse_host_response(const S_PCM_FRAME *ps_frame);
static void v_pcm_check_comm_timeout(U16_T u16_sleep_time_ms);
/*============================================================================
* 函数实现
*============================================================================*/
/**
* @brief 初始化 PCM UDP socket
*
* 参照 mylog.c 中 v_mylog_udp_init 的实现方式:
* - 创建 UDP socket
* - 绑定本地端口
* - 失败时关闭socket并重试
*/
static void v_pcm_udp_socket_init(void)
{
struct sockaddr_in local_addr;
if (g_pcm_udp_sock >= 0) {
return;
}
g_pcm_udp_sock = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if (g_pcm_udp_sock < 0) {
PCM_COMM_LOG("pcm udp socket create fail");
return;
}
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = PP_HTONS(PCM_UDP_SRC_PORT);
local_addr.sin_addr.s_addr = PP_HTONL(INADDR_ANY);
if (lwip_bind(g_pcm_udp_sock, (const struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) {
PCM_COMM_LOG("pcm udp bind fail, port=%d", PCM_UDP_SRC_PORT);
lwip_close(g_pcm_udp_sock);
g_pcm_udp_sock = -1;
return;
}
PCM_COMM_LOG("pcm udp socket init ok, port=%d", PCM_UDP_SRC_PORT);
}
///**
// * @brief 校验接收帧的合法性
// *
// * @param ps_frame 帧指针
// * @return U8_T 0无效 1有效
// */
//static U8_T u8_pcm_verify_frame(const S_PCM_FRAME *ps_frame)
//{
// U8_T u8_calc_crc;
// /* 校验帧头 */
// if (ps_frame->u16_head != PCM_FRAME_HEAD) {
// return 0;
// }
// /* 校验帧尾 */
// if (ps_frame->u8_tail != PCM_FRAME_TAIL) {
// return 0;
// }
// /* 校验数据长度合法性 */
// if (ps_frame->u8_len > PCM_FRAME_MAX_DATA_LEN) {
// return 0;
// }
// /* 校验CRC:计算范围 = 帧头(2) + 功能码(1) + 长度(1) + 数据(u8_len) */
// u8_calc_crc = u8_pcm_calc_crc((const U8_T *)&ps_frame->u16_head,
// (U16_T)(sizeof(U16_T) + sizeof(U8_T) + sizeof(U8_T) + ps_frame->u8_len));
// if (u8_calc_crc != ps_frame->u8_crc) {
// return 0;
// }
// return 1;
//}
/**
* @brief 发送PCM帧
*
* @param u8_cmd 帧功能码
* @param pu8_data 数据指针
* @param u8_len 数据长度
* @return S8_T 0成功 -1失败
*/
static S8_T s8_pcm_send_frame(U8_T u8_cmd, const U8_T *pu8_data, U8_T u8_len)
{
U8_T au8_send_buf[PCM_FRAME_MIN_LEN + PCM_FRAME_MAX_DATA_LEN];
U8_T u8_send_len;
U8_T u8_crc;
S8_T s8_ret;
if (g_pcm_udp_sock < 0) {
v_pcm_udp_socket_init();
}
if (g_pcm_udp_sock < 0) {
return -1;
}
if (u8_len > PCM_FRAME_MAX_DATA_LEN) {
return -1;
}
/* 手动组帧:紧凑排列,无空洞 */
u8_send_len = 0;
/* 帧头(2B) */
au8_send_buf[u8_send_len++] = (U8_T)(PCM_FRAME_HEAD >> 8);
au8_send_buf[u8_send_len++] = (U8_T)(PCM_FRAME_HEAD & 0xFF);
/* 功能码(1B) */
au8_send_buf[u8_send_len++] = u8_cmd;
/* 数据长度(1B) */
au8_send_buf[u8_send_len++] = u8_len;
/* 数据(NB) */
if (pu8_data != NULL && u8_len > 0) {
memcpy(&au8_send_buf[u8_send_len], pu8_data, u8_len);
u8_send_len += u8_len;
}
/* CRC校验(1B):计算范围 = 帧头 + 功能码 + 长度 + 数据 */
u8_crc = u16_crc_checksum(&au8_send_buf[4], u8_len);
au8_send_buf[u8_send_len++] = u8_crc;
/* 帧尾(1B) */
au8_send_buf[u8_send_len++] = PCM_FRAME_TAIL;
#if (PCM_UDP_PRINT_EN != 0)
v_pcm_log_addr_port("[PCM][UDP][TX][DST]", &g_pcm_peer_addr);
v_pcm_log_hex("[PCM][UDP][TX]", au8_send_buf, (U16_T)u8_send_len);
#endif
/* 发送 */
s8_ret = (S8_T)lwip_sendto(g_pcm_udp_sock,
(const void *)au8_send_buf,
(size_t)u8_send_len,
0,
(const struct sockaddr *)&g_pcm_peer_addr,
sizeof(g_pcm_peer_addr));
if (s8_ret < 0) {
return -1;
}
return 0;
}
/**
* @brief 解析主机心跳帧(功能码0x11)
*
* 数据格式:
* - 终端枪数量(1B)
* - 终端枪地址(1B) x N
*/
static void v_pcm_parse_host_heartbeat(const S_PCM_FRAME *ps_frame)
{
U8_T u8_gun_num;
if (ps_frame->u8_len < 1) {
return;
}
u8_gun_num = ps_frame->u8_data[0];
if (u8_gun_num == 0 || u8_gun_num > GUN_MAX_CNT) {
return;
}
/* 校验数据长度:1(枪数量) + u8_gun_num(枪地址) */
if (ps_frame->u8_len < (U8_T)(1 + u8_gun_num)) {
return;
}
/* 更新主机通信状态 */
s_pcm_comm.s_host.u32_last_recv_time = (U32_T)u64_get_current_millis();
s_pcm_comm.s_host.u8_comm_ok = 1;
PCM_COMM_LOG("recv host heartbeat, gun_num=%d", u8_gun_num);
}
/**
* @brief 解析分桩请求应答帧(功能码0x13)
*
* 数据格式(每组枪数据):
* - 终端枪地址(1B)
* - 模块启停状态(1B)
* - 输出电压(2B) BE 0.1V
* - 输出电流(2B) BE 0.1A
* - 主机故障状态(1B)
* - 主机故障原因(2B) BE
* 每组共 9 字节
*/
static void v_pcm_parse_host_response(const S_PCM_FRAME *ps_frame)
{
U8_T u8_gun_num;
U8_T i;
U8_T u8_offset;
U8_T u8_expected_len;
if (ps_frame->u8_len < 1) {
return;
}
u8_gun_num = ps_frame->u8_data[0];
if (u8_gun_num == 0 || u8_gun_num > GUN_MAX_CNT) {
return;
}
/* 校验数据长度:1(枪数量) + u8_gun_num * 9(每组枪数据) */
u8_expected_len = (U8_T)(1 + u8_gun_num * PCM_HOST_RESPONSE_GUN_DATA_LEN);
if (ps_frame->u8_len < u8_expected_len) {
return;
}
/* 更新主机通信状态 */
// s_pcm_comm.s_host.u32_last_recv_time = (U32_T)u64_get_current_millis();
s_pcm_comm.s_host.u8_comm_ok = 1;
s_pcm_comm.s_host.u8_gun_num = u8_gun_num;
u8_offset = 1;
for (i = 0; i < u8_gun_num && i < GUN_MAX_CNT; i++) {
S_PCM_GUN_HOST_DATA *ps_gun = &s_pcm_comm.s_host.s_gun_data[i];
U16_T u16_value;
ps_gun->u8_gun_addr = ps_frame->u8_data[u8_offset++];
ps_gun->u8_module_state = ps_frame->u8_data[u8_offset++];
/* 输出电压 BE: 高字节在前 */
u16_value = (U16_T)ps_frame->u8_data[u8_offset++] << 8;
u16_value |= (U16_T)ps_frame->u8_data[u8_offset++];
ps_gun->u16_out_vol = u16_value;
/* 输出电流 BE: 高字节在前 */
u16_value = (U16_T)ps_frame->u8_data[u8_offset++] << 8;
u16_value |= (U16_T)ps_frame->u8_data[u8_offset++];
ps_gun->u16_out_cur = u16_value;
ps_gun->u8_host_fault = ps_frame->u8_data[u8_offset++];
/* 主机故障原因 BE: 高字节在前 */
u16_value = (U16_T)ps_frame->u8_data[u8_offset++] << 8;
u16_value |= (U16_T)ps_frame->u8_data[u8_offset++];
ps_gun->u16_host_fault_reason = u16_value;
if(ps_gun->u16_out_vol > 2000 && ps_gun->u8_module_state == 0)
ps_gun->u8_module_state = 1;
// u8_offset += PCM_HOST_RESPONSE_GUN_DATA_LEN;
// PCM_COMM_LOG("recv host response, gun_addr=%d, module_state=%d, out_vol=%d, out_cur=%d, host_fault=%d, host_fault_reason=%d",
// ps_gun->u8_gun_addr, ps_gun->u8_module_state, ps_gun->u16_out_vol, ps_gun->u16_out_cur, ps_gun->u8_host_fault, ps_gun->u16_host_fault_reason);
}
}
/**
* @brief 检查主机通信超时
*
* @param u16_sleep_time_ms 任务周期时间(ms
*/
static void v_pcm_check_comm_timeout(U16_T u16_sleep_time_ms)
{ U64_T u64_now_ms __attribute__((unused));
U64_T u64_elapsed;
// (void)u16_sleep_time_ms;
if (s_pcm_comm.s_host.u32_last_recv_time == 0) {
return;
}
u64_now_ms = u64_get_current_millis();
u64_elapsed = u64_safe_millis_since(s_pcm_comm.s_host.u32_last_recv_time);
if (u64_elapsed >= PCM_COMM_TIMEOUT_MS) {
if (s_pcm_comm.s_host.u8_comm_ok != 0) {
s_pcm_comm.s_host.u8_comm_ok = 0;
PCM_COMM_LOG("pcm host comm timeout");
}
}
}
#if 1 //获取外部数据接口
#endif
/*============================================================================
* 全局函数实现
*============================================================================*/
/**
* @brief 初始化 PCM UDP 通信(终端角色)
*
* @param u8_term_no 终端编号(1开始,对应主机编号)
* @return S8_T 0成功 -1失败
*/
S8_T s8_pcm_udp_init(void)
{
/* 初始化socket */
v_pcm_udp_socket_init();
if (g_pcm_udp_sock < 0) {
return -1;
}
/* 初始化通信数据 */
memset(&s_pcm_comm, 0, sizeof(S_PCM_COMM_DATA));
//s_pcm_comm.u8_term_no = u8_term_no;
/* 构建本机(终端)IP地址:192.168.1.100 + (终端编号-1)*10 */
{
s_pcm_comm.u32_term_ip = PP_HTONL(LWIP_MAKEU32(g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[0],
g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[1],
g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[2],
g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[3]));
}
s_pcm_comm.u16_term_port = PP_HTONS(PCM_UDP_SRC_PORT);
/* 构建目标主机IP地址:192.168.1.200 + (终端编号-1) */
{
U8_T u8_ip3 = (U8_T)(PCM_HOST_IP_BASE3 + ((g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[3] - 100)/PCM_TERM_IP_OFFSET)*PCM_TERM_IP_OFFSET);
s_pcm_comm.u32_host_ip = PP_HTONL(LWIP_MAKEU32(PCM_HOST_IP_BASE0,
PCM_HOST_IP_BASE1,
PCM_HOST_IP_BASE2,
u8_ip3));
}
s_pcm_comm.u16_host_port = PP_HTONS(PCM_UDP_DST_PORT);
/* 设置目标地址(主机地址) */
memset(&g_pcm_peer_addr, 0, sizeof(g_pcm_peer_addr));
g_pcm_peer_addr.sin_family = AF_INET;
g_pcm_peer_addr.sin_port = s_pcm_comm.u16_host_port;
g_pcm_peer_addr.sin_addr.s_addr = s_pcm_comm.u32_host_ip;
/* 初始化定时器 */
s_pcm_comm.u32_heartbeat_tick = 0;
s_pcm_comm.u32_request_tick = 0;
return 0;
}
/**
* @brief 发送分桩心跳帧(功能码0x10)
*
* 数据格式:
* - 终端枪数量(1B)
* - 终端枪地址(1B) x N
*
* @return S8_T 0成功 -1失败
*/
S8_T s8_pcm_send_term_heartbeat()
{
SYS_FIX_CFG_T *t_sys_fix_cfg = &g_t_share_data.t_sys_fix_cfg;
U8_T au8_data[1 + GUN_MAX_CNT];
U8_T u8_len = 0;
au8_data[u8_len++] = u16_flow_get_sys_data(0,E_FLOW_GET_DATA_GUN_CNT,0); //获取枪数量
au8_data[u8_len++] = t_sys_fix_cfg->u8_terminal[0]; //枪1地址
au8_data[u8_len++] = t_sys_fix_cfg->u8_terminal[1]; //枪2地址
return s8_pcm_send_frame(PCM_CMD_TERM_HEARTBEAT, au8_data, u8_len);
}
/**
* @brief 发送分桩请求帧(功能码0x12)
*
* 数据格式(每组枪数据):
* - 终端枪地址(1B)
* - 终端枪工作状态(1B)
* - 直流输出接触器状态(1B)
* - 直流输出泄放接触器状态(1B)
* - 需求电压(2B) BE 0.1V
* - 需求电流(2B) BE 0.1A
* - 终端枪故障状态(1B)
* - 终端枪故障原因(2B) BE
* 每组共 11 字节
*
* @return S8_T 0成功 -1失败
*/
S8_T s8_pcm_send_term_request(void)
{
U8_T au8_data[1 + GUN_MAX_CNT * PCM_TERM_REQUEST_GUN_DATA_LEN];
U8_T u8_len = 0;
U8_T i;
U8_T u8_gun_num;
U16_T u16_data;
SYS_FIX_CFG_T *t_sys_fix_cfg = &g_t_share_data.t_sys_fix_cfg;
#if (PCM_TERM_REQUEST_TEST_EN != 0)
u8_gun_num = s_pcm_term_req_test_data.u8_gun_num;
if (u8_gun_num > GUN_MAX_CNT) {
u8_gun_num = GUN_MAX_CNT;
}
#else
u8_gun_num = u16_flow_get_sys_data(0, E_FLOW_GET_DATA_GUN_CNT, 0); //获取枪数量
au8_data[u8_len++] = u8_gun_num; /* 枪数量 */
#endif
// /* 枪数量 */
for (i = 0; i < u8_gun_num ; i++) {
#if (PCM_TERM_REQUEST_TEST_EN != 0)
const S_PCM_GUN_TERM_DATA *ps_test = &s_pcm_term_req_test_data.s_gun_data[i];
au8_data[u8_len++] = u16_flow_get_sys_data(0, E_FLOW_GET_DATA_GUN_CNT, 0);
au8_data[u8_len++] = ps_test->u8_gun_addr; /* 枪地址 */
au8_data[u8_len++] = ps_test->u8_work_state; /* 工作状态 */
au8_data[u8_len++] = ps_test->u8_contactor_dc; /* 直流接触器 */
au8_data[u8_len++] = ps_test->u8_contactor_discharge; /* 泄放接触器 */
/* 需求电压 BE */
u16_data = ps_test->u16_need_vol;
au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF);
au8_data[u8_len++] = (U8_T)(u16_data & 0xFF);
/* 需求电流 BE */
u16_data = ps_test->u16_need_cur;
au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF);
au8_data[u8_len++] = (U8_T)(u16_data & 0xFF);
au8_data[u8_len++] = ps_test->u8_fault_state; /* 故障状态 */
/* 故障原因 BE */
u16_data = ps_test->u16_fault_reason;
au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF);
au8_data[u8_len++] = (U8_T)(u16_data & 0xFF);
#else
au8_data[u8_len++] = t_sys_fix_cfg->u8_terminal[i]; /* 枪地址 */
au8_data[u8_len++] = u8_mdu_put_data(i); /* 工作状态 */
au8_data[u8_len++] = (U8_T)u8_get_gun_yx_data(YX_GUN_DC_KM,i); /* 直流接触器 */
au8_data[u8_len++] = (U8_T)u8_get_gun_yx_data(YX_GUN_RELIF,i); /* 泄放接触器 */
/* 需求电压 BE */
u16_data = (U16_T)u16_flow_put_data(i, E_FLOW_PUT_DATA_GUN_NEED_VOL, 0);
au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF);
au8_data[u8_len++] = (U8_T)(u16_data & 0xFF);
/* 需求电流 BE */
u16_data = (U16_T)u16_flow_put_data(i, E_FLOW_PUT_DATA_GUN_NEED_CUR, 0);
au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF);
au8_data[u8_len++] = (U8_T)(u16_data & 0xFF);
/* 故障状态 */
if(u8_get_fault_sum(i,0) > 0)
{
au8_data[u8_len++] = 1;
}
else
{
au8_data[u8_len++] = 0;
}
/* 故障原因 BE */
u16_data = u16_flow_get_sys_data(i, E_FLOW_GET_DATA_GUN_FAULT_INFO, 0);
au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF);
au8_data[u8_len++] = (U8_T)(u16_data & 0xFF);
#endif
}
return s8_pcm_send_frame(PCM_CMD_TERM_REQUEST, au8_data, u8_len);
}
/**
* @brief PCM UDP 非阻塞接收处理
*
* 参照 mylog.c 中 v_mylog_udp_recv_echo 的实现方式:
* - 使用 MSG_DONTWAIT 非阻塞接收
* - 解析接收到的帧
*
* @return U8_T 0无数据 1收到有效数据
*/
U8_T u8_pcm_udp_recv_process(void)
{
int recv_len;
socklen_t peer_len;
struct sockaddr_in peer_addr;
S_PCM_FRAME s_frame;
S_PCM_FRAME *ps_frame = &s_frame;
if (g_pcm_udp_sock < 0) {
return 0;
}
peer_len = (socklen_t)sizeof(peer_addr);
recv_len = lwip_recvfrom(g_pcm_udp_sock,
(void *)g_u8_recv_buf,
sizeof(g_u8_recv_buf),
MSG_DONTWAIT,
(struct sockaddr *)&peer_addr,
&peer_len);
if (recv_len <= 0) {
return 0;
}
#if (PCM_UDP_PRINT_EN != 0)
v_pcm_log_addr_port("[PCM][UDP][RX][SRC]", &peer_addr);
v_pcm_log_hex("[PCM][UDP][RX]", g_u8_recv_buf, (U16_T)recv_len);
#endif
/* 检查最小帧长度 */
if (recv_len < PCM_FRAME_MIN_LEN) {
return 0;
}
memset(&s_frame, 0, sizeof(S_PCM_FRAME));
ps_frame->u16_head = (U16_T)((g_u8_recv_buf[0] << 8) | g_u8_recv_buf[1]);
ps_frame->u8_cmd = g_u8_recv_buf[2];
ps_frame->u8_len = g_u8_recv_buf[3];
// if (ps_frame->u8_len > PCM_FRAME_MAX_DATA_LEN) {
// return 0;
// }
memcpy(ps_frame->u8_data, &g_u8_recv_buf[4], ps_frame->u8_len);
ps_frame->u8_crc = g_u8_recv_buf[4 + ps_frame->u8_len];
ps_frame->u8_tail = g_u8_recv_buf[4 + ps_frame->u8_len + 1];
/* 校验帧合法性 */
// if (u8_pcm_verify_frame(ps_frame) == 0) {
// return 0;
// }
/* 根据功能码分发处理 */
switch (ps_frame->u8_cmd) {
case PCM_CMD_HOST_HEARTBEAT: /* 0x11 主机心跳帧 */
v_pcm_parse_host_heartbeat(ps_frame);
break;
case PCM_CMD_HOST_RESPONSE: /* 0x13 分桩请求应答帧 */
v_pcm_parse_host_response(ps_frame);
break;
default:
/* 未知功能码,忽略 */
break;
}
return 1;
}
/**
* @brief PCM UDP 周期处理函数(终端角色)
*
* 处理逻辑:
* - 每1000ms发送分桩心跳帧(0x10)
* - 每200ms发送分桩请求帧(0x12)
* - 每周期检查通信超时
*
* @param u16_sleep_time_ms 任务周期时间(ms
*/
void v_pcm_udp_periodic_handler(U16_T u16_sleep_time_ms)
{
if (g_pcm_udp_sock < 0) {
return;
}
/* 更新定时器 */
s_pcm_comm.u32_heartbeat_tick += u16_sleep_time_ms;
s_pcm_comm.u32_request_tick += u16_sleep_time_ms;
/* 每1000ms发送分桩心跳帧(0x10) */
if (s_pcm_comm.u32_heartbeat_tick >= PCM_HEARTBEAT_PERIOD_MS) {
s_pcm_comm.u32_heartbeat_tick = 0;
(void)s8_pcm_send_term_heartbeat();
}
/* 每200ms发送分桩请求帧(0x12) */
if (s_pcm_comm.u32_request_tick >= PCM_REQUEST_PERIOD_MS) {
s_pcm_comm.u32_request_tick = 0;
s8_pcm_send_term_request();
}
/* 检查通信超时 */
v_pcm_check_comm_timeout(u16_sleep_time_ms);
}
/**
* @brief 获取主机通信状态
*
* @return U8_T 0超时 1正常
*/
U8_T u8_pcm_get_host_comm_status(void)
{
return s_pcm_comm.s_host.u8_comm_ok;
}
/**
* @brief 获取主机应答枪数据
*
* @param u8_gun_index 枪索引
* @return S_PCM_GUN_HOST_DATA* 主机应答数据指针,失败返回NULL
*/
S_PCM_GUN_HOST_DATA* ps_pcm_get_host_gun_data(U8_T u8_gun_index)
{
if (u8_gun_index >= GUN_MAX_CNT) {
return NULL;
}
if (u8_gun_index >= s_pcm_comm.s_host.u8_gun_num) {
return NULL;
}
return &s_pcm_comm.s_host.s_gun_data[u8_gun_index];
}
/**
* @brief 更新本机(终端)枪数据(由外部模块调用)
*
* @param u8_gun_index 枪索引
* @param ps_data 终端枪数据指针
*/
void v_pcm_update_term_gun_data(U8_T u8_gun_index, const S_PCM_GUN_TERM_DATA *ps_data)
{
if (u8_gun_index >= GUN_MAX_CNT || ps_data == NULL) {
return;
}
memcpy(&s_pcm_comm.s_gun_data[u8_gun_index], ps_data, sizeof(S_PCM_GUN_TERM_DATA));
}
/**
* @brief PCM 通信主处理函数(由 mdu_comm_task 调用)
*
* 该函数在 u8_mdu_type == 0x02 时被 mdu_comm_task 主循环调用。
* 负责 PCM UDP 通信的初始化、周期收发和接收处理。
*
* @param u16_sleep_time_ms 任务周期时间(ms
*/
void v_pcm_comm_gw_action(U16_T u16_sleep_time_ms)
{
static U8_T u8_pcm_init_done = 0;
/* 首次运行时初始化 PCM UDP */
if (u8_pcm_init_done == 0) {
S8_T s8_ret = s8_pcm_udp_init();
if (s8_ret == 0) {
u8_pcm_init_done = 1;
PCM_COMM_LOG("pcm udp init success");
} else {
/* 初始化失败(可能lwIP网络协议栈未就绪),下次循环继续重试 */
return;
}
}
/* PCM UDP 周期处理:发送心跳(1000ms)、发送请求(200ms)、超时检测(5000ms) */
v_pcm_udp_periodic_handler(u16_sleep_time_ms);
/* PCM UDP 非阻塞接收处理 */
(void)u8_pcm_udp_recv_process();
}