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();
}
+246
View File
@@ -0,0 +1,246 @@
#ifndef PCM_COMM_H
#define PCM_COMM_H
#ifdef __cplusplus
extern "C" {
#endif
/*------ Exported includes(头文件) ----------------------*/
#include "publicdata/publicdata.h"
/*------ Exported macro(宏定义) -------------------------*/
/* PCM UDP 网络参数 */
#define PCM_UDP_SRC_PORT 502 /* 本机(终端)UDP socket绑定的源端口 */
#define PCM_UDP_DST_PORT 502 /* 目标(主机)UDP端口 */
/* 终端IP地址基址:本机(终端)从192.168.1.100开始 */
#define PCM_TERM_IP_BASE0 192
#define PCM_TERM_IP_BASE1 168
#define PCM_TERM_IP_BASE2 1
#define PCM_TERM_IP_BASE3 100
/* 主机IP地址基址:1号主机从192.168.1.200开始 */
#define PCM_HOST_IP_BASE0 192
#define PCM_HOST_IP_BASE1 168
#define PCM_HOST_IP_BASE2 1
#define PCM_HOST_IP_BASE3 200
/* 每台主机占用的终端IP地址偏移量(10个地址) */
#define PCM_TERM_IP_OFFSET 10
/* 帧结构定义 */
#define PCM_FRAME_HEAD 0x5A99 /* 帧头 */
#define PCM_FRAME_TAIL 0xA5 /* 帧尾 */
/* 帧功能码 */
#define PCM_CMD_TERM_HEARTBEAT 0x10 /* 分桩心跳帧:终端->主机 */
#define PCM_CMD_HOST_HEARTBEAT 0x11 /* 主机心跳帧:主机->终端 */
#define PCM_CMD_TERM_REQUEST 0x12 /* 分桩请求帧:终端->主机 */
#define PCM_CMD_HOST_RESPONSE 0x13 /* 分桩请求应答帧:主机->终端 */
/* 帧最小长度:帧头(2) + 功能码(1) + 长度(1) + CRC(1) + 帧尾(1) = 6 */
#define PCM_FRAME_MIN_LEN 6
/* 帧最大数据长度 */
#define PCM_FRAME_MAX_DATA_LEN 128
/* 帧最大总长度 */
#define PCM_FRAME_MAX_LEN (PCM_FRAME_MIN_LEN + PCM_FRAME_MAX_DATA_LEN)
/* 通信周期(ms */
#define PCM_HEARTBEAT_PERIOD_MS 1000 /* 心跳周期 */
#define PCM_REQUEST_PERIOD_MS 200 /* 请求/应答周期 */
/* 通信超时时间(ms */
#define PCM_COMM_TIMEOUT_MS 5000 /* 5秒无通信判定超时 */
/* 终端枪工作状态 */
#define PCM_GUN_STATE_IDLE 0 /* 空闲 */
#define PCM_GUN_STATE_PLUG 1 /* 插枪 */
#define PCM_GUN_STATE_INSU_START 2 /* 绝缘启动 */
#define PCM_GUN_STATE_INSU_STOP 3 /* 绝缘停止 */
#define PCM_GUN_STATE_PRE_CHARGE 4 /* 预充启动 */
#define PCM_GUN_STATE_CHARGING 5 /* 充电中 */
#define PCM_GUN_STATE_CHARGE_STOP 6 /* 充电停止 */
#define PCM_GUN_STATE_CHARGE_STOPPED 7 /* 充电停止完成 */
/* 接触器状态 */
#define PCM_CONTACTOR_OPEN 0 /* 断开 */
#define PCM_CONTACTOR_CLOSE 1 /* 闭合 */
/* 模块启停状态 */
#define PCM_MODULE_STANDBY 0 /* 待机 */
#define PCM_MODULE_WORK 1 /* 工作 */
/* 故障状态 */
#define PCM_FAULT_NONE 0 /* 无故障 */
#define PCM_FAULT_EXIST 1 /* 故障 */
/*------ Exported struct(结构体定义) --------------------*/
/**
* @brief PCM 通用帧头结构
*/
typedef struct {
U16_T u16_head; /* 帧头:0x5A99 */
U8_T u8_cmd; /* 帧功能码 */
U8_T u8_len; /* 数据长度 */
}__attribute__((packed)) S_PCM_FRAME_HEAD;
/**
* @brief PCM 通用帧结构(完整帧)
*/
typedef struct {
U16_T u16_head; /* 帧头:0x5A99 */
U8_T u8_cmd; /* 帧功能码 */
U8_T u8_len; /* 数据长度 */
U8_T u8_data[PCM_FRAME_MAX_DATA_LEN]; /* 帧数据 */
U8_T u8_crc; /* CRC校验 */
U8_T u8_tail; /* 帧尾:0xA5 */
}__attribute__((packed)) S_PCM_FRAME;
/**
* @brief 单组终端枪数据(用于终端发送请求帧)
* 对应协议中:终端枪地址 + 工作状态 + 接触器状态x2 + 需求电压 + 需求电流 + 故障状态 + 故障原因
* 每组 11 字节
*/
typedef struct {
U8_T u8_gun_addr; /* 终端枪地址 */
U8_T u8_work_state; /* 终端枪工作状态 */
U8_T u8_contactor_dc; /* 直流输出接触器状态 */
U8_T u8_contactor_discharge; /* 直流输出泄放接触器状态 */
U16_T u16_need_vol; /* 需求电压 BE 0.1V */
U16_T u16_need_cur; /* 需求电流 BE 0.1A */
U8_T u8_fault_state; /* 终端枪故障状态 */
U16_T u16_fault_reason; /* 终端枪故障原因 BE */
}__attribute__((packed)) S_PCM_GUN_TERM_DATA;
/**
* @brief 单组主机应答数据(用于解析主机应答帧)
* 对应协议中:终端枪地址 + 模块启停 + 输出电压 + 输出电流 + 主机故障状态 + 主机故障原因
* 每组 9 字节
*/
typedef struct {
U8_T u8_gun_addr; /* 终端枪地址 */
U8_T u8_module_state; /* 模块启停状态 */
U16_T u16_out_vol; /* 输出电压 BE 0.1V */
U16_T u16_out_cur; /* 输出电流 BE 0.1A */
U8_T u8_host_fault; /* 主机故障状态 */
U16_T u16_host_fault_reason; /* 主机故障原因 BE */
}__attribute__((packed)) S_PCM_GUN_HOST_DATA;
/**
* @brief PCM 主机通信状态(收到的对端信息)
*/
typedef struct {
U32_T u32_host_ip; /* 主机IP地址(网络字节序) */
U16_T u16_host_port; /* 主机端口(网络字节序) */
/* 主机下发的应答数据 */
U8_T u8_gun_num; /* 主机回复的枪数量 */
S_PCM_GUN_HOST_DATA s_gun_data[GUN_MAX_CNT]; /* 各枪应答数据 */
/* 通信状态 */
U32_T u32_last_recv_time; /* 最后接收时间(ms */
U8_T u8_comm_ok; /* 通信状态:0超时 1正常 */
}__attribute__((packed)) S_PCM_HOST_STATE;
/**
* @brief PCM 终端通信数据
*/
typedef struct {
U8_T u8_term_no; /* 终端编号(1开始,对应主机编号) */
U32_T u32_term_ip; /* 本机(终端)IP地址(网络字节序) */
U16_T u16_term_port; /* 本机(终端)端口(网络字节序) */
/* 主机信息 */
U32_T u32_host_ip; /* 目标主机IP地址(网络字节序) */
U16_T u16_host_port; /* 目标主机端口(网络字节序) */
S_PCM_HOST_STATE s_host; /* 主机通信状态 */
/* 本机(终端)枪数据(待发送给主机) */
U8_T u8_gun_num; /* 本机终端枪数量 */
S_PCM_GUN_TERM_DATA s_gun_data[GUN_MAX_CNT]; /* 各枪数据 */
/* 定时器 */
U32_T u32_heartbeat_tick; /* 心跳发送计时 */
U32_T u32_request_tick; /* 请求发送计时 */
}__attribute__((packed)) S_PCM_COMM_DATA;
/*------ Exported variables(变量定义) -------------------*/
/*------ Exported function prototypes (函数声明) -------*/
/**
* @brief 初始化 PCM UDP 通信(终端角色)
*
* @param u8_term_no 终端编号(1开始,对应主机编号)
* @return S8_T 0成功 -1失败
*/
S8_T s8_pcm_udp_init(void);
/**
* @brief PCM UDP 周期处理函数
*
* @param u16_sleep_time_ms 任务周期时间(ms
*/
void v_pcm_udp_periodic_handler(U16_T u16_sleep_time_ms);
/**
* @brief PCM UDP 非阻塞接收处理
*
* @return U8_T 0无数据 1收到有效数据
*/
U8_T u8_pcm_udp_recv_process(void);
/**
* @brief PCM 通信主处理函数(由 mdu_comm_task 调用)
*
* @param u16_sleep_time_ms 任务周期时间(ms
*/
void v_pcm_comm_gw_action(U16_T u16_sleep_time_ms);
/**
* @brief 发送分桩心跳帧(功能码0x10)
*
* @return S8_T 0成功 -1失败
*/
S8_T s8_pcm_send_term_heartbeat(void);
/**
* @brief 发送分桩请求帧(功能码0x12)
*
* @return S8_T 0成功 -1失败
*/
S8_T s8_pcm_send_term_request(void);
/**
* @brief 获取主机通信状态
*
* @return U8_T 0超时 1正常
*/
U8_T u8_pcm_get_host_comm_status(void);
/**
* @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);
/**
* @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);
#ifdef __cplusplus
}
#endif
#endif /* PCM_COMM_H */
File diff suppressed because it is too large Load Diff
+214
View File
@@ -0,0 +1,214 @@
#ifndef MDU_COMM_TASK_H
#define MDU_COMM_TASK_H
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
extern "C" {
#endif
/*------ Exported includes(头文件) ----------------------*/
#include "mdu_comm/mdu_interface.h"
#include "bsp_include.h"
#if MDU_UULN_EN
#include "mdu_comm/mdu_uuln_comm.h"
#endif
#include "mylog/mylog.h"
/*------ Exported macro(宏定义) -------------------------*/
#define MDU_CAN_PRINT_FLAG (0) //模块报文 调试打印标志
#define MODULE_BROADCAST_ADDR 0x9F //广播地址
#define MODULE_PILE_ADDR 0xA0 //主机地址
#define MODULE_MDU_FIRST_ADDR 0x80 //充电模块首地址
#define MODULE_CMD_HEARTBEAT 0x40 /* 模块心跳命令 */
#define MODULE_CMD_SETTING 0x80 /* 定值设置命令 */
#define MODULE_CMD_GETTING 0x82 /* 定值查询命令 */
#define MODULE_CMD_REMOTE_CONTROL 0x05 /* 遥控命令 */
#define MODULE_CMD_GROUP_SET 0x03 /* 组号设置命令 */
#define MODULE_CMD_HEARTBEAT_ACK 0x41 //心跳应答
#define MODULE_CMD_SETTING_ACK 0x81 //定值设置应答
#define MODULE_CMD_GETTING_ACK 0x83 //定值查询应答
#define MODULE_CMD_YXYC_20 0x20 //遥信遥测20帧
#define MODULE_CMD_YXYC_21 0x21 //遥信遥测21帧
#define MODULE_CMD_TEMP 0xE6 //温度查询帧
#define MODULE_TEMP_OFFSET (50) //模块温度偏移量,50
#define MODULE_ALL (0xFF) //模块接收数据 全部清空
/*------ Exported struct(结构体定义) --------------------*/
#define Mdu_Comm_LOG(fmt, ...) \
MYLOG_MSG(TASK_ID_MduComm,fmt, ##__VA_ARGS__) //任务打印
typedef enum {E_MDU_IDLE = 0, E_MDU_WORK = !E_MDU_IDLE} E_MDU_WORK_STATE;
//模块状态 枚举
typedef enum
{
E_MDU_STATE_NONE=0, //未通信
E_MDU_STATE_NORMAL, //正常
E_MDU_STATE_FAULT, //故障
}E_MDU_STATE;
//模块故障告警数据
typedef union
{
U8_T u8_mdu_alarm;
struct
{
U8_T b0_other:1; //其他故障,0正常,1故障
U8_T b1_relief:1; //泄放故障
U8_T b2_fan:1; //风扇故障
U8_T b3_DC_short:1; //直流输出短路
U8_T b4_overTemp:1; //过温
U8_T b5_DC_UV:1; //直流输出欠压
U8_T b6_DC_OV:1; //直流输出过压
U8_T b7_AC_IN:1; //交流输入故障
}bit_mdu_alarm;
}U_MDU_ALARM_DATA;
typedef union {
uint8_t raw; // 原始字节值
struct {
uint8_t operationCommand : 4; // Bit3~Bit0: 操作指令
/*0x01 --- 快速开机(绝缘检测阶段使用)
0x02 --- 停止充电(保留分组信息)
0x03 --- 软起开机(预启动阶段使用)
0x04 --- 显示地址(显示模块通信地址)
0x05 --- 参数修改
0x06 --- 停止充电(自动清除分组信息)
*/
uint8_t voltageRange : 1; // Bit4: 电压输出范围选择
uint8_t powerContactor : 1; // Bit5: 功率分配回路接触器状态
uint8_t mainContactor : 1; // Bit6: 充电回路主接触器状态
uint8_t reserved : 1; // Bit7: 预留
} bits;
}U_MDU_YK_DEVICE_STATUS;
typedef struct
{
U_MDU_YK_DEVICE_STATUS u_dev_state; //设备状态 包含功率分配回路接触器状态、充电回路主接触器状态等数据
U16_T u16_needVol; //需求电压 精度0.1
U16_T u16_oneMdu_needCur; //单模块需求电流 精度0.1
U16_T u16_battVol; //电池电压 精度0.1
}S_MDU_RemoteCtlrData;
typedef enum {
SEND_MODE_UNICAST, ///< 单点发送:命令发送给特定目标设备
SEND_MODE_BROADCAST ///< 广播发送:命令发送给所有设备
}E_CommandSendMode;
//充电模块接收状态 结构体
typedef struct
{
E_MDU_STATE e_state; //模块状态 未通讯、通信正常、故障
E_MDU_WORK_STATE e_work_state; //模块工作状态 空闲、工作
U8_T u8_groupNo; //模块组号
U8_T u8_GrpMode; //分组模式:【遥测分组模式:0固定分组,1动态分组】
U16_T u16_outVol; //输出电压 精度0.1
U16_T u16_outCur; //输出电流 精度0.1
U16_T u16_outtimecnt; //通信超时计数
U_MDU_ALARM_DATA u_alarm_data; //故障告警数据 0x20 遥信遥测帧上送
#if MDU_UULN_EN
U_UULN_MDU_STATUS u_uuln_state; //优优绿能模块状态保存
E_TrueHiLo_Status e_TrueHiLo_Status; //高低压状态读取 1: 1000V 2: 500V
#endif
//模块温度读取使能
#if( MDU_TEMP_READ_EN == 1)
S8_T s8_mdutemp_1; //模块温度1 进风口温度 单位摄氏度 注意有正负号 范围是 -127 ~ 127
S8_T s8_mduPara_2; //模块温度2 模块PFC温度 单位摄氏度 注意有正负号 范围是 -127 ~ 127
#endif
}S_MDU_STATE;
//充电模块控制命令 结构体
typedef struct
{
E_CMD e_mdu_start_flag; //模块启停状态控制 ON / OFF
U8_T u8_mdu_in_pile; //模块归属枪号 0未分配 1-N分配枪号
U8_T u8_swh_flag; //模块对应输出回路继电器 闭合标志
U8_T u8_mdu_fault_num; //模块故障次数 区分不充电正常,一充电就报错 直接就不能用得了-暂未使用
}S_MDU_CTRL;
typedef struct
{
U8_T u8_mdu_addr; //模块地址 0x80/0x60
S_MDU_CTRL s_ctrl; //模块控制
S_MDU_STATE s_state; //模块状态
}S_MDU_ONE_DATA; //单模块数据
typedef struct
{
U8_T u8_on_group_num; //模块组内模块数量 已分组模块数量
U8_T u8_work_state; //模块组工作状态 0空闲 1工作 --组内有一个模块输出 则是工作
U_MDU_YK_DEVICE_STATUS u_yk_dev_state; //遥控命令状态
U16_T u16_needVol; //需求电压 0.1V
U16_T u16_oneMdu_needCur; //单模块需求电流 0.1A 流程控制计算号后直接使用
U16_T u16_battVol; //电池电压 0.1V
U16_T u16_outVol; //模块组内输出电压0.1V-- 指已投入充电回路的模块
U16_T u16_outCur; //模块组内输出电流0.1A
}S_MDU_GROUP_DATA;
typedef struct
{
U8_T u8_mdu_type; //模块类型
U8_T u8_mdu_num; //模块数量
U8_T u8_mdu_group_num; //模块组数量-- 枪数量
U8_T u8_mdu_usable_num; //可用模块数量 所有通信正常 且无故障模块
U8_T u8_mdu_using_num; //在用模块的数量 所有以分配模块
U8_T u8_can_send_cnt; //can报文发送数量 用于报文发送延时
U16_T u16_timer; //周期计数值
U32_T u32_runing_time; //任务运行时间 刚插枪延时5秒再判断通信超时
U8_T u8_recv_most_pack[20]; //多包数据接收 最多2包,2*7=14个字节数据
//模块参数读取使能 -- 默认同一主机使用的模块参数一致 - 查询第一个可通信的模块参数
#if( MDU_PARA_READ_EN == 1)
U16_T u16_mduPara_MaxOutVol; //模块参数 最大输出电压 精度0.1
U16_T u16_mduPara_MinOutVol; //模块参数 最小输出电压 精度0.1
U16_T u16_mduPara_MaxOutCur; //模块参数 最大输出电流 精度0.1
U16_T u16_mduPara_MinOutCur; //模块参数 最小输出电流 精度0.1
#endif
S_MDU_ONE_DATA s_mdu[MDU_MAX_NUM]; //模块状态
S_MDU_GROUP_DATA s_mdu_group[GUN_MAX_CNT]; //模块组状态
}S_MDU_DATA;
/*------ Exported variables(变量定义) -------------------*/
/*------ Exported function prototypes (函数声明) -------*/
void v_MduCanMsg_send(CAN_DATA* can_data);
U8_T u8_MduCanMsg_recv(CAN_DATA *rx_can_data);
void v_mdu_clear_recv_data(U8_T u8_id);
void v_mdu_data_summ(void);
/* STM32 状态 CAN 数据获取接口 */
U8_T u8_stm_status_get_gun_plug(U8_T u8_gunNo); // 0:A枪 1:B枪 返回0未插枪 1插枪
U8_T u8_stm_status_get_di(U8_T u8_index); // 0:DI1 1:DI2 返回0/1
void v_Mdu_Comm_task(void *argument);
#ifdef __cplusplus
}
#endif
#endif /* MDU_COMM_TASK_H */
+113
View File
@@ -0,0 +1,113 @@
# mdu_comm 模块说明
> 路径:`app/mdu_comm/`
> 返回主说明:[README](../../README.md)
## 1. 模块职责
- 实现 MDU 模块通信任务与报文处理;
- 维护模块在线/离线、通信状态与超时判断;
- 提供 MDU 对接系统数据的接口层;
- **可选**:当配置为群充「终端(分桩)」角色时,通过 **PCM UDP** 与主机交互(见 `PCM_comm.c`)。
## 2. 关键文件
- `mdu_comm_task.c/h`MDU 任务主循环;根据 `u8_mdu_type` 分支调用 CAN 网关逻辑或 PCM UDP 逻辑。
- `mdu_interface.c/h`MDU 数据与控制接口层。
- **`PCM_comm.c` / `PCM_comm.h`**:群充 **PCM 终端 ↔ 主机****UDP** 协议栈(lwIP `socket`/`sendto`/`recvfrom`),详见下文第 6 节。
## 3. 功能说明
- 处理模块 CAN 报文收发和任务周期调度;
- 对外暴露 MDU 数据读取与控制动作接口;
- 当前包含部分可配置/可屏蔽的系统取数接口(便于分阶段集成)。
## 4. 调度与对外接口
该模块由 FreeRTOS 任务 `MduComm` 周期运行(单位:ms),周期来自 `app/app_init/app_init.c`
- `MduComm``50ms`(任务入口:`v_Mdu_Comm_task(void *argument)`
任务内根据 **`s_mdu_data.u8_mdu_type`** 选择路径:
- **`0`**:充电模块 **CAN** 通信(`v_mdu_comm_gw_action` 等)。
- **`0x02`**PCM **UDP** 终端通信(`v_pcm_comm_gw_action`,实现位于 `PCM_comm.c`)。
对外接口(来自 `mdu_comm_task.h`):
- `void v_MduCanMsg_send(CAN_DATA* can_data)`:发送 MDU 报文(封装)
- `U8_T u8_MduCanMsg_recv(CAN_DATA *rx_can_data)`:接收 MDU 报文
- `void v_mdu_clear_recv_data(U8_T u8_id)`:清空指定接收缓存
- `void v_mdu_data_summ(void)`:汇总/刷新统计数据
并提供与 STM/PLC 相关的状态查询接口(根据配置启用):
- `U8_T u8_stm_status_get_gun_plug(U8_T u8_gunNo)`
- `U8_T u8_stm_status_get_di(U8_T u8_index)`
## 5. 维护建议
- 新增命令类型时建议先扩展接口层,再接入任务主循环;
- 保持超时与故障上报路径一致,避免重复告警;
- PCM 与 CAN 共用 `MduComm` 任务时,注意 **`u8_mdu_type`** 与网络初始化顺序(lwIP 就绪后再 `bind`)。
## 6. PCM 群充 UDP`PCM_comm.c` / `PCM_comm.h`
### 6.1 定位与引用协议
- **本机角色**:群充 **终端(分桩)**,通过 **UDP****主机** 通信。
- **代码注释约定**:协议细节可参考工程内 **`主机终端通信协议.md`**(与 `PCM_comm.c` 文件头说明一致)。
- **接入点**:由 `mdu_comm_task.c`**`u8_mdu_type == 0x02`** 时每周期调用 **`v_pcm_comm_gw_action(sleep_time_ms)`**;首次调用内完成 **`s8_pcm_udp_init()`**(失败则下周期重试)。
- **日志**:使用 `TASK_ID_MduComm`,宏 `PCM_COMM_LOG`
### 6.2 传输与地址
- **Socket**UDP`AF_INET`;本地 **`bind`** 端口 **`PCM_UDP_SRC_PORT`502**;发往主机 **`PCM_UDP_DST_PORT`502**。
- **本机 IP**:取自共享配置 **`g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip`**(写入 `S_PCM_COMM_DATA` 中的终端 IP 字段)。
- **主机 IP**:按终端 IP 末字节推算——**`192.168.1.(200 + ((终端末字节-100)/10)*10)`**(与 `PCM_HOST_IP_BASE3``PCM_TERM_IP_OFFSET` 等宏对应;用于群充多终端分段寻址)。
### 6.3 帧格式摘要
| 字段顺序 | 长度 | 说明 |
| --- | --- | --- |
| 帧头 | 2 B | 固定 **`0x5A 0x99`**`PCM_FRAME_HEAD` |
| 功能码 | 1 B | 见下表 |
| 数据长度 | 1 B | `N`,数据域字节数,`N ≤ 128` |
| 数据 | N B | 依功能码变化 |
| CRC | 1 B | **CRC8**,多项式 **`x^8+x^2+x+1`0x07**,计算范围为 **帧头+功能码+长度+数据**(与组帧顺序一致) |
| 帧尾 | 1 B | 固定 **`0xA5`** |
最小帧长 **6 字节**(无数据域时);接收路径校验帧头、帧尾、长度、`CRC` 后按功能码分发。
### 6.4 功能码与周期行为(终端侧)
| 功能码 | 方向 | 含义 |
| --- | --- | --- |
| `0x10` | 终端→主机 | **分桩心跳**:数据含枪数量 + 各枪终端地址(配置 `u8_terminal[]` |
| `0x11` | 主机→终端 | **主机心跳**:解析后刷新主机通信「存活」与时间戳 |
| `0x12` | 终端→主机 | **分桩请求**:按枪拼接多组 **11 字节** 数据(枪地址、流程状态、直流/泄放接触器、需求电压电流 BE、故障状态与原因 BE 等),数据来自 **`flow_interface`**、**`u8_get_gun_yx_data`**、**`u8_get_fault_sum` / `u16_flow_get_sys_data`** |
| `0x13` | 主机→终端 | **请求应答**:解析多组 **9 字节**(枪地址、模块启停、输出电压电流 BE、主机故障及原因 BE),写入 **`S_PCM_HOST_STATE`** |
**定时**(在 **`v_pcm_udp_periodic_handler`** 中累加任务周期 `sleep_time_ms`):
- **`PCM_HEARTBEAT_PERIOD_MS`1000 ms**:发送 **`0x10`**
- **`PCM_REQUEST_PERIOD_MS`200 ms**:发送 **`0x12`**
- **`PCM_COMM_TIMEOUT_MS`(5000 ms)**:若距上次有效接收超时,置 **`u8_comm_ok = 0`**(主机通信异常)。
接收:**`u8_pcm_udp_recv_process`** 使用 **`MSG_DONTWAIT`** 非阻塞 `recvfrom`,仅处理 **`0x11` / `0x13`**。
### 6.5 对外可查接口(节选)
- **`u8_pcm_get_host_comm_status()`**:主机链路是否正常(`0` 超时 / `1` 正常)。
- **`ps_pcm_get_host_gun_data(u8_gun_index)`**:最近一次 **`0x13`** 解析得到的某枪应答(索引非法或超出主机回复枪数则返回 `NULL`)。
- **`v_pcm_update_term_gun_data(...)`**:可选写入本地缓存的终端枪数据(当前 **`0x12`** 组帧仍以实时采集接口为准,该接口便于后续扩展)。
### 6.6 与 CAN MDU 路径的关系
- **互斥分支**`u8_mdu_type == 0` 走 CAN**`== 0x02`** 走 PCM UDP,二者在同一任务周期内不同时执行主通路。
- **喂狗、锁、睡眠**:仍由 `mdu_comm_task` 统一调度;PCM 仅作为分支内的网络协议处理。
## 7. 关联文档
- 主索引:[README](../../README.md)
- 群充 PCM`主机终端通信协议.md`(与 `PCM_comm.c` 注释引用一致;若文档路径调整请在工程中同步)
+553
View File
@@ -0,0 +1,553 @@
#include "mdu_comm/mdu_interface.h"
#include "mdu_comm/mdu_comm_task.h"
#include "publicdata/publicdata.h"
#include "fault_cheak/fault_interface.h"
#include "collect_ctrl/collect_task.h"
#include "flowctrl/flow_interface.h"
#include "mdu_comm/PCM_comm.h"
extern S_MDU_DATA s_mdu_data;
extern S_PCM_COMM_DATA s_pcm_comm;
#define TEST_MDU_CTRL_FLAG (0) //模块控制 测试标志
/* 0: 屏蔽系统取数(流程/采样/故障联动), 1: 使能系统取数 */
#define MDU_SYS_FETCH_ENABLE (1)
#if TEST_MDU_CTRL_FLAG
typedef struct
{
U8_T u8_start_flag; //模块任务执行标志 0 不执行 1执行
U8_T u8_cmd; //模块控制命令 0无 1快速启机 2快速启机停(不清空分组) 3软启开机 4充电停止(清空分组)
U16_T u16_need_vol; //单模块需求电压 0.1V
U16_T u16_one_mdu_need_cur; //单模块需求电压 0.1V
}TEST_MDU_TRL_DATA;
TEST_MDU_TRL_DATA test_mdu_ctrl[GUN_MAX_CNT]; //按枪控制
#endif
#if 1 //外部参数获取
/**
* ************************************************************************
* @brief - 流程控制任务 对外输出数据
* 0 空闲 1插枪 2绝缘启动 3绝缘停止 4预充启动 5充电中 6充电停止 7充电停止完成
* @param[in] u8_gunNo - 枪号 0-N 对于不分区枪号的数据,无意义
*
* @return U8_T - 数据返回值
*
* @author jiankalka
* @date 2025-09-03
* ************************************************************************
*/
U8_T u8_mdu_put_data(U8_T u8_gunNo)
{
U8_T u8_ret = 0;
U8_T u8_gunlink = u8_get_gun_yx_data(YX_GUN_LINK,u8_gunNo);
U16_T u16_gun_real_state = u16_flow_put_data(u8_gunNo, E_FLOW_PUT_DATA_GUN_REAL_STATE, 0);
if(u8_gunlink == 0 && u16_gun_real_state == 0)
{
u8_ret = 0; //枪未连接且状态为0空闲
}
else if(u8_gunlink == 1 && (u16_gun_real_state == 0 || u16_gun_real_state == 1))
{
u8_ret = 1; //枪已连接且状态为0空闲
}
else if(u8_gunlink == 1 && u16_gun_real_state == 2 )
{
u8_ret = 2; //枪已连接且状态为2绝缘启动
}
else if(u8_gunlink == 1 && (u16_gun_real_state == 4 || u16_gun_real_state == 3 || u16_gun_real_state == 8))
{
u8_ret = 3; //枪已连接且状态为4绝缘检测模块停止
}
else if(u8_gunlink == 1 && (u16_gun_real_state == 5 || u16_gun_real_state == 6))
{
u8_ret = 4; //枪已连接且状态为5开始预充
}
else if(u8_gunlink == 1 && u16_gun_real_state == 7)
{
u8_ret = 5; //枪已连接且状态为7充电中
}
else if(u8_gunlink == 1 && u16_gun_real_state == 9 )
{
u8_ret = 6; //枪已连接且状态为9充电停止
}
else if(u8_gunlink == 1 && u16_gun_real_state == 10)
{
u8_ret = 7; //枪已连接且状态为10充电停止完成
}
return u8_ret;
}
/**
* ************************************************************************
* @brief -获取充电枪 充电需求
*
* @param[in] u8_id -id号 当作为枪号时,0-N; 当作为模块地址号时,0-N
* @param[in] type - 数据类型
* E_MDU_GET_DATA_GUN_CMD = 0, //枪命令状态 0无 1快速启机 2快速启机停(不清空分组) 3软启开机 4充电停止(清空分组)
E_MDU_GET_DATA_MAIN_KY, //主回路接触器状态
E_MDU_GET_DATA_POWER_KY, //功率回路接触器状
E_MDU_GET_DATA_NEED_VOL, //需求电压 精度 0.1
E_MDU_GET_DATA_NEED_CUR, //需求电流 精度 0.1
E_MDU_GET_DATA_BAAT_VOL, //电池电压 精度 0.1
* @return U16_T - 对应数据值
*
* @author jiankalka
* @date 2025-07-04
* ************************************************************************
*/
U16_T u16_mdu_get_charge_need(U8_T u8_id, E_MDU_GET_DATA_TYPE type)
{
U16_T u16_res = 0;
switch (type)
{
case E_MDU_GET_DATA_GUN_CMD: //枪命令状态 0无 1快速启机 2快速启机停(不清空分组) 3软启开机 4充电停止(清空分组)
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = u16_flow_put_data(u8_id , E_FLOW_PUT_DATA_MDU_YK_CMD,0); //模块遥控命令 0无 1快速启机 2快速启机停(不清空分组) 3软启开机 4充电停止(清空分组)
#else
u16_res = 0;
#endif
#if TEST_MDU_CTRL_FLAG
u16_res = test_mdu_ctrl[u8_id].u8_cmd;
#endif
break;
}
case E_MDU_GET_DATA_MAIN_KY: //主回路接触器状态
{
u16_res = 1;
break;
}
case E_MDU_GET_DATA_POWER_KY: //功率回路接触器状
{
u16_res = 1;
break;
}
case E_MDU_GET_DATA_NEED_VOL: //需求电压 精度 0.1
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = u16_flow_put_data(u8_id , E_FLOW_PUT_DATA_MDU_NEED_VOL,0); //需求电压 精度 0.1
#else
u16_res = 0;
#endif
#if TEST_MDU_CTRL_FLAG
u16_res = test_mdu_ctrl[u8_id].u16_need_vol;
#endif
break;
}
case E_MDU_GET_DATA_NEED_CUR_ONE: //单模块需求电流 精度 0.1 -- 流程控制计算好
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = u16_flow_put_data(u8_id , E_FLOW_PUT_DATA_MDU_ONE_NEED_CUR,0); //单模块需求电流值 精度0.1
#else
u16_res = 0;
#endif
#if TEST_MDU_CTRL_FLAG
u16_res = test_mdu_ctrl[u8_id].u16_one_mdu_need_cur;
#endif
break;
}
case E_MDU_GET_DATA_BAAT_VOL: //电池电压 精度 0.1
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = (U16_T)((f32_get_gun_yc_data(ADC_GUN_VOLT_PLUS,u8_id) + f32_get_gun_yc_data(ADC_GUN_VOLT_MINUS,u8_id))*10);
#else
u16_res = 0;
#endif
#if TEST_MDU_CTRL_FLAG
u16_res = test_mdu_ctrl[u8_id].u16_need_vol;
#endif
break;
}
case E_MDU_GET_DATA_START_FALG: //模块启停标志
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = u16_flow_put_data(u8_id ,E_FLOW_PUT_DATA_MDU_STAR_FLAG, 0);
#else
u16_res = 0;
#endif
break;
}
case E_MDU_GET_DATA_SHW_ON_FLAG: //模块对应开关闭合标志
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = 1;
// u16_res = u16_flow_put_data(u8_id , E_FLOW_PUT_DATA_MDU_SHW_ON_FLAG, 0);
#else
u16_res = 1;
#endif
break;
}
case E_MDU_GET_DATA_IN_PILE_ID: //模块归属终端号
{
if(g_t_share_data.t_sys_fix_cfg.u8_gunCnt == 1 ||
u8_id < ((s_mdu_data.u8_mdu_num+1) / 2)) // 前半部分为1枪 后半部分为2枪 模块数量为奇数时,1枪多一个
u16_res = 1;
else
u16_res = 2;
break;
}
case E_MDU_GET_DATA_MDU_NUM: //充电模块数量
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = g_t_share_data.t_sys_fix_cfg.u8_mdu_num;
#else
u16_res = s_mdu_data.u8_mdu_num;
#endif
break;
}
case E_MDU_GET_DATA_GUN_NUM: //充电枪数量
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = g_t_share_data.t_sys_fix_cfg.u8_gunCnt;
#else
u16_res = s_mdu_data.u8_mdu_group_num;
#endif
break;
}
case E_MDU_GET_DATA_RUNING_FLAG: //模块任务运行标志 0模块任务不运行并清空模块数据 1模块任务运行-执行报文收发和超时判断
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = u16_flow_put_data(u8_id , E_FLOW_PUT_DATA_MDU_STAR_FLAG,0); //模块运行开始标志 0未开始 1开始
#else
u16_res = 0;
#endif
#if TEST_MDU_CTRL_FLAG
u16_res = test_mdu_ctrl[u8_id].u8_start_flag;
#endif
break;
}
case E_MDU_GET_DATA_MDU_TYPE:
{
#if (MDU_SYS_FETCH_ENABLE == 1)
u16_res = g_t_share_data.t_sys_fix_cfg.u8_MduType;
#else
u16_res = s_mdu_data.u8_mdu_type;
#endif
break;
}
default:
break;
}
return u16_res;
}
#endif
#if 1 //对外数据提供
/**
* ************************************************************************
* @brief - 获取模块组内运行模块的数量 模块工作状态为 工作
*
* @param[in] u8_gunNo - 抢号序号 0-N
* @param[in] type - type 数据类型 0归属本枪模块数量 1归属本枪且正在运行中模块数量
*
* @return U8_T -
*
* @author jiankalka
* @date 2025-08-28
* ************************************************************************
*/
static U8_T u8_mdu_swh__get_runing_num(U8_T u8_gunNo ,U8_T type)
{
U8_T i;
U8_T u8_inPile_num = 0 , u8_runing_num = 0;
for ( i = 0; i < s_mdu_data.u8_mdu_num; i++) //遍历所有模块
{
if(s_mdu_data.s_mdu[i].s_ctrl.u8_mdu_in_pile == u8_gunNo+1)
{
u8_inPile_num ++;
if(s_mdu_data.s_mdu[i].s_state.e_work_state == E_MDU_WORK)
u8_runing_num ++;
}
}
if(type == 0)
return u8_inPile_num;
else
return u8_runing_num;
}
/**
* ************************************************************************
* @brief - 获取模块温度值
* 模块采集两个温度,进风口温度,内部元器件温度;咱们之前PT1000采集的是出风口温度。
* 之前不调速的风机方案:出风口温度取值为各个模块进风口温度与内部元器件温度的平均值的最大值。
*
* @return U16_T - 温度最大值
*
* @author jiankalka
* @date 2025-09-16
* ************************************************************************
*/
static U16_T u16_mdu_get_temp()
{
U8_T i;
S8_T s8_temp_1,s8_temp_2,s8_temp_3,s8_temp_max = 0;
for (i = 0; i < s_mdu_data.u8_mdu_num; i++) //遍历所有模块
{
if(s_mdu_data.s_mdu[i].s_state.e_state == E_MDU_STATE_NORMAL) //模块状态正常
{
s8_temp_1 = s_mdu_data.s_mdu[i].s_state.s8_mdutemp_1;
s8_temp_2 = s_mdu_data.s_mdu[i].s_state.s8_mduPara_2;
if(s8_temp_1 != 0 && s8_temp_2 != 0) //数据不为0
{
s8_temp_3 = (s8_temp_1+s8_temp_2)/2;
s8_temp_max = (s8_temp_max > s8_temp_3)?s8_temp_max:s8_temp_3; //获取最大值
}
}
}
if(s8_temp_max < 0) s8_temp_max = 0; //最小为0
return s8_temp_max;
}
U16_T u16_mdu_put_data(U8_T u8_id , E_MDU_PUT_DATA_TYPE e_type)
{
U16_T u16_res;
// 根据桩类型判断通讯方式:一体机(0/1)使用CAN模块通讯,分体机(2/3)使用PCM通讯
if(u16_flow_get_sys_data(0, E_FLOW_GET_DATA_PILE_TYPE,0) < 2)
{
switch (e_type)
{
case E_MDU_PUT_DATA_usable_num: //可用模块数量
{
u16_res = s_mdu_data.u8_mdu_usable_num;
break;
}
case E_MDU_PUT_DATA_using_num: //在用模块的数量
{
u16_res = s_mdu_data.u8_mdu_using_num;
break;
}
case E_MDU_PUT_DATA_ID_commFlag: //模块通信状态 0 未通信 1通信
{
u16_res = (s_mdu_data.s_mdu[u8_id].s_state.e_state == E_MDU_STATE_NONE)?0:1;
break;
}
case E_MDU_PUT_DATA_ID_workSate: //模块工作状态 0空闲 1工作
{
u16_res = s_mdu_data.s_mdu[u8_id].s_state.e_work_state;
break;
}
case E_MDU_PUT_DATA_ID_faultFlag: //模块故障标志 0正常 1故障
{
u16_res = (s_mdu_data.s_mdu[u8_id].s_state.e_state == E_MDU_STATE_FAULT)?1:0;
break;
}
case E_MDU_PUT_DATA_ID_gropNum: //模块组号 0-N
{
u16_res = s_mdu_data.s_mdu[u8_id].s_state.u8_groupNo;
break;
}
case E_MDU_PUT_DATA_ID_putVol: //模块输出电压 精度 0.1V
{
u16_res = s_mdu_data.s_mdu[u8_id].s_state.u16_outVol;
break;
}
case E_MDU_PUT_DATA_ID_putCur: //模块输出电流 精度 0.1A
{
u16_res = s_mdu_data.s_mdu[u8_id].s_state.u16_outCur;
break;
}
case E_MDU_PUT_DATA_GROUP_WORK_STATE:
{
u16_res = s_mdu_data.s_mdu_group[u8_id].u8_work_state; //模块组工作状态 0空闲 1工作
break;
}
case E_MDU_PUT_DATA_GROUP_putVol: //模块组输出电压 精度0.1V
{
u16_res = s_mdu_data.s_mdu_group[u8_id].u16_outVol;
break;
}
case E_MDU_PUT_DATA_GROUP_putCur: //模块组输出电流 精度0.1A
{
u16_res = s_mdu_data.s_mdu_group[u8_id].u16_outCur;
break;
}
case E_MDU_PUT_DATA_GROUP_mduNum: //模块组分配模块数量
{
u16_res = s_mdu_data.s_mdu_group[u8_id].u8_on_group_num;
break;
}
case E_MDU_PUT_DATA_GROUP_RUNING_mduNum: //模块组运行模块数量
{
u16_res = u8_mdu_swh__get_runing_num(u8_id,1);
break;
}
case E_MDU_PUT_DATA_MDU_TEMP: //获取模块上送温度值- 单位 度,范围0-100,注意最低为0度
{
u16_res = u16_mdu_get_temp();
break;
}
default:
break;
}
}
else // 分体机:使用网线PCM通讯
{
switch (e_type)
{
case E_MDU_PUT_DATA_usable_num: //可用模块数量
{
u16_res = s_mdu_data.u8_mdu_usable_num;
break;
}
case E_MDU_PUT_DATA_using_num: //在用模块的数量
{
u16_res = s_mdu_data.u8_mdu_using_num;
break;
}
case E_MDU_PUT_DATA_ID_commFlag: //模块通信状态 0 未通信 1通信
{
u16_res = (s_mdu_data.s_mdu[u8_id].s_state.e_state == E_MDU_STATE_NONE)?0:1;
break;
}
case E_MDU_PUT_DATA_ID_workSate: //模块工作状态 0空闲 1工作
{
u16_res = s_pcm_comm.s_host.s_gun_data[u8_id].u8_module_state;
break;
}
case E_MDU_PUT_DATA_ID_faultFlag: //模块故障标志 0正常 1故障
{
u16_res = (s_mdu_data.s_mdu[u8_id].s_state.e_state == E_MDU_STATE_FAULT)?1:0;
break;
}
case E_MDU_PUT_DATA_ID_gropNum: //模块组号 0-N
{
u16_res = s_mdu_data.s_mdu[u8_id].s_state.u8_groupNo;
break;
}
case E_MDU_PUT_DATA_ID_putVol: //模块输出电压 精度 0.1V
{
u16_res = s_pcm_comm.s_host.s_gun_data[u8_id].u16_out_vol;
break;
}
case E_MDU_PUT_DATA_ID_putCur: //模块输出电流 精度 0.1A
{
u16_res = s_pcm_comm.s_host.s_gun_data[u8_id].u16_out_cur;
break;
}
case E_MDU_PUT_DATA_GROUP_WORK_STATE:
{
u16_res = s_pcm_comm.s_host.s_gun_data[u8_id].u8_module_state; //模块组工作状态 0空闲 1工作
break;
}
case E_MDU_PUT_DATA_GROUP_putVol: //模块组输出电压 精度0.1V
{
u16_res = s_pcm_comm.s_host.s_gun_data[u8_id].u16_out_vol;
break;
}
case E_MDU_PUT_DATA_GROUP_putCur: //模块组输出电流 精度0.1A
{
u16_res = s_pcm_comm.s_host.s_gun_data[u8_id].u16_out_cur;
break;
}
case E_MDU_PUT_DATA_GROUP_mduNum: //模块组分配模块数量
{
u16_res = s_mdu_data.s_mdu_group[u8_id].u8_on_group_num;
break;
}
case E_MDU_PUT_DATA_GROUP_RUNING_mduNum: //模块组运行模块数量
{
u16_res = u8_mdu_swh__get_runing_num(u8_id,1);
break;
}
case E_MDU_PUT_DATA_MDU_TEMP: //获取模块上送温度值- 单位 度,范围0-100,注意最低为0度
{
u16_res = u16_mdu_get_temp();
break;
}
default:
break;
}
}
return u16_res;
}
#endif
#if 1 //模块故障告警控制
/**
* ************************************************************************
* @brief - 模块通信故障 设置
*
* @param[in] u8_id - 模块地址 0-N ,单模块通信超时 为告警; 值为0xFF时 表示所有模块通信超时,为故障
* @param[in] u8_flag - 0 回复 1超时
*
*
* @author jiankalka
* @date 2025-09-17
* ************************************************************************
*/
void v_mdu_set_comm_fault_info(U8_T u8_id,U8_T u8_flag)
{
#if (MDU_SYS_FETCH_ENABLE == 1)
U8_T u8_fault_flag;
E_FAULT_SITE e_fite;
E_FAULT_TYPE e_type;
if(u32_safe_seconds_since(s_mdu_data.u32_runing_time) < NUM_10) //模块上电10秒内 不进行故障判断
return;
if(u8_id == MODULE_ALL) //所有模块通信故障 动作
{
e_fite = E_WHOLE_HOST; //故障位置为整机
e_type = E_FAULT; //故障
}
else //单个模块 故障动作
{
e_fite = (E_FAULT_SITE)(E_CHAR_MDU+u8_id); //故障位置为模块
e_type = E_WARN; //告警
}
u8_fault_flag = u8_cheak_fault_flag(E_FAULT_14019,e_fite); //获取故障标志
if(u8_fault_flag == 0 && u8_flag == 1)
v_set_fault_data(e_fite,e_type,E_FAULT_14019,0,0,0);
else if(u8_fault_flag != 0 && u8_flag == 0)
v_set_fault_data(e_fite,E_RES,E_FAULT_14019,0,0,0);
#else
(void)u8_id;
(void)u8_flag;
#endif
}
/**
* ************************************************************************
* @brief - 模块故障告警动作
*
* @param[in] u8_id - 模块地址 0-N ,单模块通信超时 为告警; 值为0xFF时 表示所有模块通信超时,为故障
* @param[in] u8_flag - 0 回复 1超时
* @param[in] u_alarm_data - 故障信息
*
*
* @author jiankalka
* @date 2025-09-17
* ************************************************************************
*/
void v_mdu_set_alarm_info(U8_T u8_id,U8_T u8_flag, U_MDU_ALARM_DATA *u_alarm_data)
{
}
#endif
+74
View File
@@ -0,0 +1,74 @@
#ifndef MDU_INTERFACE_H
#define MDU_INTERFACE_H
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
extern "C" {
#endif
/*------ Exported includes(头文件) ----------------------*/
#include "publicdata/publicdata.h"
/*------ Exported macro(宏定义) -------------------------*/
/*------ Exported struct(结构体定义) --------------------*/
typedef enum
{
E_MDU_GET_DATA_GUN_CMD = 0, //枪命令状态 0无 1快速启机 2快速启机停(不清空分组) 3软启开机 4充电停止(清空分组)
E_MDU_GET_DATA_MAIN_KY, //主回路接触器状态
E_MDU_GET_DATA_POWER_KY, //功率回路接触器状
E_MDU_GET_DATA_NEED_VOL, //需求电压 精度 0.1
E_MDU_GET_DATA_NEED_CUR_ONE, //单模块需求电流 精度 0.1 -- 流程控制计算好
E_MDU_GET_DATA_BAAT_VOL, //电池电压 精度 0.1
E_MDU_GET_DATA_START_FALG, //模块启停标志
E_MDU_GET_DATA_SHW_ON_FLAG, //模块对应开关闭合标志
E_MDU_GET_DATA_IN_PILE_ID, //模块归属终端号
E_MDU_GET_DATA_MDU_NUM, //充电模块数量
E_MDU_GET_DATA_GUN_NUM, //充电枪数量
E_MDU_GET_DATA_MDU_TYPE, //模块类型 0 三统一 1优优绿能
E_MDU_GET_DATA_RUNING_FLAG, //模块任务运行标志 0模块任务不运行并清空模块数据 1模块任务运行
}E_MDU_GET_DATA_TYPE; //枪需求数据 类型
typedef enum
{
E_MDU_PUT_DATA_usable_num=0, //可用模块数量
E_MDU_PUT_DATA_using_num, //在用模块的数量
E_MDU_PUT_DATA_ID_commFlag, //模块通信状态 0 未通信 1通信
E_MDU_PUT_DATA_ID_workSate, //模块工作状态 0空闲 1工作
E_MDU_PUT_DATA_ID_faultFlag, //模块故障标志 0正常 1故障
E_MDU_PUT_DATA_ID_gropNum, //模块组号 0-N
E_MDU_PUT_DATA_ID_putVol, //模块输出电压 精度 0.1V
E_MDU_PUT_DATA_ID_putCur, //模块输出电流 精度 0.1A
E_MDU_PUT_DATA_GROUP_WORK_STATE, //模块组工作状态 0空闲 1工作
E_MDU_PUT_DATA_GROUP_putVol, //模块组输出电压 精度0.1V
E_MDU_PUT_DATA_GROUP_putCur, //模块组输出电流 精度0.1A
E_MDU_PUT_DATA_GROUP_mduNum, //模块组分配模块数量 已分组模块数量
E_MDU_PUT_DATA_GROUP_RUNING_mduNum, //模块组运行模块数量
E_MDU_PUT_DATA_MDU_TEMP, //获取模块上送温度值- 单位 度,范围0-100,注意最低为0度
}E_MDU_PUT_DATA_TYPE; //模块信息对外输出数据类型
//1快速开机(绝缘检测阶段使用) 2停止充电(保留分组信息) 3软启开机(预启动阶段使用) 4显示地址(显示模块通信地址) 5参数修改 6停止充电(自动清除分组信息)
typedef enum //遥控命令
{
MDU_YK_FASTBOOT=1, //快速开机(绝缘检测阶段使用)
MDU_YK_STOP_SUSPEND, //停止充电(保留分组信息)
MDU_YK_SOFTSTART, //软启开机(预启动阶段使用)
MDU_YK_SHOWADDR, //显示地址(显示模块通信地址)
MDU_YK_SETPARA, //参数修改
MDU_YK_STOP, //停止充电(自动清除分组信息)
}E_MDU_YK_TYPE;
/*------ Exported variables(变量定义) -------------------*/
/*------ Exported function prototypes (函数声明) -------*/
U16_T u16_mdu_get_charge_need(U8_T gunNo, E_MDU_GET_DATA_TYPE type);
U16_T u16_mdu_put_data(U8_T u8_id , E_MDU_PUT_DATA_TYPE e_type);
U8_T u8_mdu_put_data(U8_T u8_gunNo);
void v_mdu_set_comm_fault_info(U8_T u8_id,U8_T u8_flag);
#ifdef __cplusplus
}
#endif
#endif /* MDU_INTERFACE_H */
+125
View File
@@ -0,0 +1,125 @@
#include "mdu_comm/mdu_stm_status_comm.h"
#include "mdu_comm/mdu_comm_task.h"
#include "collect_ctrl/yk_ctrl.h"
#if (GUN_LINK_COMM_EN)
/* STM32072 板状态 CAN 帧:扩展帧 ID 0x1801AABB
* data[0] = A枪插枪(0=未插枪 1=插枪)
* data[1] = B枪插枪
* data[2] = DI1
* data[3] = DI2
*/
#define STM_STATUS_CAN_ID ((U32_T)0x1801AABB)
/* STM32072 板控制 CAN 帧:扩展帧 ID 0x1802BBAA
* data[0] = DO0(本板 DO1,对应辅源1
* data[1] = DO1(本板 DO2,对应辅源2
*/
#define STM_CTRL_CAN_ID ((U32_T)0x1802BBAA)
typedef struct
{
U8_T u8_gun_plug[2]; // 0:A枪 1:B枪
U8_T u8_di[2]; // 0:DI1 1:DI2
U8_T u8_aux[2]; // 0:AUX_A 1:AUX_B
} S_STM_STATUS_CAN;
static S_STM_STATUS_CAN s_stm_status_can;
U8_T u8_stm_status_can_rx(CAN_DATA *rxMsg)
{
if (rxMsg->ID != STM_STATUS_CAN_ID)
{
return 0;
}
s_stm_status_can.u8_gun_plug[0] = (rxMsg->Data[0] ? 1U : 0U);
s_stm_status_can.u8_gun_plug[1] = (rxMsg->Data[1] ? 1U : 0U);
s_stm_status_can.u8_di[0] = (rxMsg->Data[2] ? 1U : 0U);
s_stm_status_can.u8_di[1] = (rxMsg->Data[3] ? 1U : 0U);
s_stm_status_can.u8_aux[0] = (rxMsg->Data[4] ? 1U : 0U); // AUX_PADET_A
s_stm_status_can.u8_aux[1] = (rxMsg->Data[5] ? 1U : 0U); // AUX_PADET_B
return 1;
}
void v_stm_status_can_send_aux_frame(void)
{
CAN_DATA can_data;
memset(&can_data , 0 , sizeof(CAN_DATA));
can_data.ID = STM_CTRL_CAN_ID; /* 与 STM32 侧 APP_CTRL_CAN_ID 保持一致 */
can_data.Len = 8;
/* 辅源1/2 控制:E_CMD 为 0/1,直接作为 DO0/DO1 的控制位 */
can_data.Data[0] = (U8_T)e_yk_get_ctrl_cmd(E_KY_TYPE_AUX_POWER1);
can_data.Data[1] = (U8_T)e_yk_get_ctrl_cmd(E_KY_TYPE_AUX_POWER2);
v_MduCanMsg_send(&can_data);
}
U8_T u8_stm_status_get_gun_plug(U8_T u8_gunNo)
{
if (u8_gunNo < 2)
{
return s_stm_status_can.u8_gun_plug[u8_gunNo];
}
return 0;
}
U8_T u8_stm_status_get_di(U8_T u8_index)
{
if (u8_index < 2)
{
return s_stm_status_can.u8_di[u8_index];
}
return 0;
}
U8_T u8_stm_status_get_aux(U8_T u8_index)
{
if (u8_index < 2)
{
return s_stm_status_can.u8_aux[u8_index];
}
return 0;
}
// 判断是否有国标枪,若有则启用国标相关功能
U8_T u8_stm_gb_enable(void)
{
return (g_t_share_data.t_sys_fix_cfg.u8_gun_type[0] == GUN_TYPE_GB ||
g_t_share_data.t_sys_fix_cfg.u8_gun_type[1] == GUN_TYPE_GB);
}
#else /* GUN_LINK_COMM_EN == 0 */
U8_T u8_stm_status_can_rx(CAN_DATA *rxMsg)
{
(void)rxMsg;
return 0;
}
void v_stm_status_can_send_aux_frame(void)
{
}
U8_T u8_stm_status_get_gun_plug(U8_T u8_gunNo)
{
(void)u8_gunNo;
return 0;
}
U8_T u8_stm_status_get_di(U8_T u8_index)
{
(void)u8_index;
return 0;
}
// 判断是否有国标枪,若有则启用国标相关功能
U8_T u8_stm_gb_enable(void)
{
return 0;
}
#endif /* GUN_LINK_COMM_EN */
+57
View File
@@ -0,0 +1,57 @@
#ifndef MDU_STM_STATUS_COMM_H
#define MDU_STM_STATUS_COMM_H
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
extern "C" {
#endif
/*------ Exported includes(头文件) ----------------------*/
#include "main.h"
#include "bsp_include.h"
/*------ Exported function prototypes (函数声明) -------*/
/**
* @brief 解析 STM32072 状态帧(ID 0x1801AABB
* @param rxMsg - 接收的 CAN 帧
* @return U8_T - 1:已处理该帧并更新内部状态 0:非本模块帧
*/
U8_T u8_stm_status_can_rx(CAN_DATA *rxMsg);
/**
* @brief 发送辅源/DO 控制帧(ID 0x1802BBAA)到 STM32072
*/
void v_stm_status_can_send_aux_frame(void);
/**
* @brief 获取 STM32 状态 CAN 中的枪插枪状态
* @param u8_gunNo - 枪号 0:A枪 1:B枪
* @return U8_T - 0未插枪 1插枪
*/
U8_T u8_stm_status_get_gun_plug(U8_T u8_gunNo);
/**
* @brief 获取 STM32 状态 CAN 中的 DI 状态
* @param u8_index - 0:DI1 1:DI2
* @return U8_T - 0/1
*/
U8_T u8_stm_status_get_di(U8_T u8_index);
/**
* @brief 获取 STM32 状态 CAN 中的 AUX 状态
* @param u8_index - 0:AUX_A 1:AUX_B
* @return U8_T - 0/1
*/
U8_T u8_stm_status_get_aux(U8_T u8_index);
/**
* @brief 判断是否启用国标相关功能(是否有国标枪)
* @return U8_T - 0:不启用 1:启用
*/
U8_T u8_stm_gb_enable(void);
#ifdef __cplusplus
}
#endif
#endif /* MDU_STM_STATUS_COMM_H */
+801
View File
@@ -0,0 +1,801 @@
#include "mdu_comm/mdu_uuln_comm.h"
#if MDU_UULN_EN
#include "mdu_comm/mdu_interface.h"
#include "mdu_comm/mdu_comm_task.h"
#include "mdu_comm/mdu_stm_status_comm.h"
#include "bsp_include.h"
extern S_MDU_DATA s_mdu_data;
/*
本协议使用优优绿能自定义协议,此协议与常规的CAN协议内容理解不一样 现做以下说明:
ID 域报文格式定义如下:
| 28:25 | 24:21 | 20:14 | 13:9 | 8:0 |
|----------------|---------------------------|---------------------------|-------------------|-----------------------|
| Protocol | MonitorAddress | ModuleAddress | ProductionDay | SerialNumberLowPart |
| (协议号) | (监控地址) | (模块地址) | (生产日期) | (序列号低位) |
|----------------|---------------------------|---------------------------|-------------------|-----------------------|
| 0x01 | Monitor - (0x01~0x0F) | Module - (0x01~0x7F) | 1~31 (可选) | 0 (可选) |
| | BroadCast - 0 | BroadCast - 0 | | |
详细内容说明详见协议文档,或者下方变量定义 U_UULN_CAN_ID_TYPE。
DATA 域规定内容如下:
字节0 : 字节 0 数据由两部分组成,高 4 位为组地址,低 4 位为信息类型; 变量定义为 U_UULN_DATA1_TYPE
字节1 : 命令信息类型。表示数据信息对应的命令类型。 变量定义为 E_UULN_CMD_TYPE
字节2 预留
字节3 预留
字节4 - 7 : 数据部分 详看协议文档
控制流程关键点(与厂家技术人员沟通所得):
1、正常充电,根据电池电压来设置模块高低压模式,然后设置需求电压,需求电流,开机
2、500v是切换点,如果跨过500v,先关机,切换高低压模式,然后设置电压电流开机
3、绝缘检测是根据绝缘电压先设定模块高低压模式,然后设置电压开机
*/
#if 1 //发送组帧
/**
* ************************************************************************
* @brief - 读取所有模块的状态标志位
*
* @param[in] u8_grp_on - 读取组号 0为广播 1-2
* @param[in] e_cmd_type - 读取数据类型 详看枚举 E_UULN_CMD_TYPE
*
* send 2200000, 22 08 00 00 00 00 00 00
* recv 2209c2e, 23 08 00 00 00 00 00 00
* recv 2205c1c, 23 08 00 00 00 00 00 00
*
* @author jiankalka
* @date 2025-11-13
* ************************************************************************
*/
static void v_uuln_send_read_all_data(U8_T u8_grp_on , E_UULN_CMD_TYPE e_cmd_type)
{
CAN_DATA can_data;
U_UULN_CAN_ID_TYPE u_ID;
U_UULN_DATA1_TYPE u_Data1;
memset(&can_data , 0 , sizeof(CAN_DATA));
//帧ID赋值
u_ID.u32_ID = 0;
u_ID.bit.Protocol = D_MDU_UULN_VER ; // 协议号 0x01 表示整流模块与监控之间的通讯协议号
u_ID.bit.MonitorAddress = 1 ; // 监控地址 地址设定在 0x010x0F,默认 0x01 0x00:代表广播通信
u_ID.bit.ModuleAddres = 0 ; // 整流模块地址 为整流模块地址,地址确定在 0x01-0x7F。 0x00:代表广播通信
u_ID.bit.ProductionDay = 0 ; // 产品生产日期(01-31)。
u_ID.bit.SerialNumberLowPart = 0 ; // 产品序列号低位(默认填 0)。
//DATA1 赋值 包括组地址和信息类型
u_Data1.u8_data = 0;
u_Data1.bit.GroupAddress = u8_grp_on; // 0 广播
u_Data1.bit.MessageType = E_MAG_TYPE_READ_DATA; // 0x02 读模块信息
//组帧 CAN
can_data.ID = u_ID.u32_ID; //组帧ID
can_data.Len = 8;
//组帧DATA
can_data.Data[0] = u_Data1.u8_data;
can_data.Data[1] = e_cmd_type ; //读取数据命令码
v_MduCanMsg_send(&can_data);
}
/**
* ************************************************************************
* @brief - 模块参数设置
*
* @param[in] u8_grp_on - 设置组号 1/2 不能设置0广播,控制分组控
* @param[in] e_cmd_type - 数据命令码
* @param[in] u32_data - 数据内容 注意精度转换 其中 电压电流精度为0.0001
*
*
* @author jiankalka
* @date 2025-11-13
* ************************************************************************
*/
static void v_uuln_send_set_all_data(U8_T u8_grp_on , E_UULN_CMD_TYPE e_cmd_type , U32_T u32_data)
{
CAN_DATA can_data;
U_UULN_CAN_ID_TYPE u_ID;
U_UULN_DATA1_TYPE u_Data1;
if(u8_grp_on == 0) //组号为0 不做处理
return;
memset(&can_data , 0 , sizeof(CAN_DATA));
//帧ID赋值
u_ID.u32_ID = 0;
u_ID.bit.Protocol = D_MDU_UULN_VER ; // 协议号 0x01 表示整流模块与监控之间的通讯协议号
u_ID.bit.MonitorAddress = 1 ; // 监控地址 地址设定在 0x010x0F,默认 0x01 0x00:代表广播通信
u_ID.bit.ModuleAddres = 0 ; // 整流模块地址 为整流模块地址,地址确定在 0x01-0x7F。 0x00:代表广播通信
u_ID.bit.ProductionDay = 0 ; // 产品生产日期(01-31)。
u_ID.bit.SerialNumberLowPart = 0 ; // 产品序列号低位(默认填 0)。
//DATA1 赋值 包括组地址和信息类型
u_Data1.u8_data = 0;
u_Data1.bit.GroupAddress = u8_grp_on; // 0 广播
u_Data1.bit.MessageType = E_MAG_TYPE_SET_PARA; // 0x00 设置模块参数
//组帧 CAN
can_data.ID = u_ID.u32_ID; //组帧ID
can_data.Len = 8;
//组帧DATA
can_data.Data[0] = u_Data1.u8_data;
can_data.Data[1] = e_cmd_type ; //读取数据命令码
can_data.Data[2] = 0;
can_data.Data[3] = 0;
can_data.Data[4] = GetHigh24Bits(u32_data); //赋值数据
can_data.Data[5] = GetHigh16Bits(u32_data);
can_data.Data[6] = GetHigh8Bits(u32_data);
can_data.Data[7] = u32_data&0xFF;
v_MduCanMsg_send(&can_data);
}
/**
* ************************************************************************
* @brief - 单个模块参数设置
*
* @param[in] u8_addr - 设置模块号 1-N 不能设置0广播
* @param[in] e_cmd_type - 数据命令码
* @param[in] u32_data - 数据内容 注意精度转换 其中 电压电流精度为0.0001
*
*
* @author jiankalka
* @date 2025-11-13
* ************************************************************************
*/
static void v_uuln_send_set_one_data(U8_T u8_addr , E_UULN_CMD_TYPE e_cmd_type , U32_T u32_data)
{
CAN_DATA can_data;
U_UULN_CAN_ID_TYPE u_ID;
U_UULN_DATA1_TYPE u_Data1;
S_MDU_STATE *s_mdu_state = NULL; //模块状态
if(u8_addr == 0 || u8_addr > MDU_MAX_NUM) //模块号为0 或超过最大数量 不做处理
return;
s_mdu_state = &s_mdu_data.s_mdu[u8_addr-1].s_state; //i号模块 状态数据结构体
memset(&can_data , 0 , sizeof(CAN_DATA));
//帧ID赋值
u_ID.u32_ID = 0;
u_ID.bit.Protocol = D_MDU_UULN_VER ; // 协议号 0x01 表示整流模块与监控之间的通讯协议号
u_ID.bit.MonitorAddress = 1 ; // 监控地址 地址设定在 0x010x0F,默认 0x01 0x00:代表广播通信
u_ID.bit.ModuleAddres = u8_addr ; // 整流模块地址 为整流模块地址,地址确定在 0x01-0x7F。 0x00:代表广播通信
u_ID.bit.ProductionDay = 0 ; // 产品生产日期(01-31)。
u_ID.bit.SerialNumberLowPart = 0 ; // 产品序列号低位(默认填 0)。
//DATA1 赋值 包括组地址和信息类型
u_Data1.u8_data = 0;
u_Data1.bit.GroupAddress = s_mdu_state->u8_groupNo; // 模块组号
u_Data1.bit.MessageType = E_MAG_TYPE_SET_PARA; // 0x00 设置模块参数
//组帧 CAN
can_data.ID = u_ID.u32_ID; //组帧ID
can_data.Len = 8;
//组帧DATA
can_data.Data[0] = u_Data1.u8_data;
can_data.Data[1] = e_cmd_type ; //读取数据命令码
can_data.Data[2] = 0;
can_data.Data[3] = 0;
can_data.Data[4] = GetHigh24Bits(u32_data); //赋值数据
can_data.Data[5] = GetHigh16Bits(u32_data);
can_data.Data[6] = GetHigh8Bits(u32_data);
can_data.Data[7] = u32_data&0xFF;
v_MduCanMsg_send(&can_data);
}
/**
* ************************************************************************
* @brief - 设置模块1的组地址
*
* @param[in] u8_addr -
* @param[in] u8_grp_on -
*
*
* @author jiankalka
* @date 2025-11-12
* ************************************************************************
*/
static void v_uuln_send_set_grp_no(U8_T u8_addr , U8_T u8_grp_on)
{
CAN_DATA can_data;
U_UULN_CAN_ID_TYPE u_ID;
U_UULN_DATA1_TYPE u_Data1;
if(u8_addr >= MDU_MAX_NUM)
return;
memset(&can_data , 0 , sizeof(CAN_DATA));
//帧ID赋值
u_ID.u32_ID = 0;
u_ID.bit.Protocol = D_MDU_UULN_VER ; // 协议号 0x01 表示整流模块与监控之间的通讯协议号
u_ID.bit.MonitorAddress = 1 ; // 监控地址 地址设定在 0x010x0F,默认 0x01 0x00:代表广播通信
u_ID.bit.ModuleAddres = u8_addr ; // 整流模块地址 为整流模块地址,地址确定在 0x01-0x7F。 0x00:代表广播通信
u_ID.bit.ProductionDay = 0 ; // 产品生产日期(01-31)。
u_ID.bit.SerialNumberLowPart = 0 ; // 产品序列号低位(默认填 0)。
//DATA1 赋值 包括组地址和信息类型
u_Data1.u8_data = 0;
u_Data1.bit.GroupAddress = 0; // 0 广播
u_Data1.bit.MessageType = E_MAG_TYPE_SET_PARA; // 0x00 设置模块参数
//组帧 CAN
can_data.ID = u_ID.u32_ID; //组帧ID
can_data.Len = 8;
//组帧DATA
can_data.Data[0] = u_Data1.u8_data;
can_data.Data[1] = E_GROUP_ADDRESS ; // 0x59 组地址 R/W Y
can_data.Data[7] = u8_grp_on; //设置组号
v_MduCanMsg_send(&can_data);
}
#endif
#if 1 //发送
/**
* ************************************************************************
* @brief - 设置模块高低压状态
*
* @param[in] u8_grp_on - 组号 1/2 0无意义
* @param[in] u8_flag - 11000V 2500V
*
* @return U8_T - 是否执行切换步骤 1是 0否
* 执行切换步骤,包含控制模块停机、设置高低压状态等操作 执行时不许再控制模块启动
*
* @author jiankalka
* @date 2025-11-13
* ************************************************************************
*/
static U8_T u8_uuln_set_grp_mdu_hlvol(U8_T u8_grp_on , E_TrueHiLo_Status e_flag)
{
U8_T i,u8_ret = 0;
S_MDU_STATE *s_mdu_state = NULL; //模块状态
if(u8_grp_on == 0) return 0; //组号为0 无意义
for (i = 0; i < s_mdu_data.u8_mdu_num; i++) //遍历所有模块 检查模块状态和分组情况
{
s_mdu_state = &s_mdu_data.s_mdu[i].s_state; //i号模块 状态数据结构体
if(s_mdu_state->e_state != E_MDU_STATE_NORMAL || //模块正常 且组号一致
s_mdu_state->u8_groupNo != u8_grp_on)
continue;
if(e_flag != s_mdu_state->e_TrueHiLo_Status) //状态不一致 则设置状态
{
u8_ret = 1;
if(s_mdu_state->e_work_state == E_MDU_WORK) //停模块
{
v_uuln_send_set_one_data(i+1,E_DCDC_MODULE_POWER_CONTROL,1); // 0x04 DCDC模块开关机 W N 模块开关机(0 开机,1 关机)
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d](0-N) set hl vol state ,ctrl mdu off\r\n",i);
#endif
}
else
{
v_uuln_send_set_one_data(i+1,E_MODULE_HIGH_LOW_VOLTAGE_MODE,e_flag); // 0x5F 模块高低压模式 W Y 11000V 2500V
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d](0-N) set hl vol state [%d] -> [%d]\r\n",i,s_mdu_state->e_TrueHiLo_Status,e_flag );
#endif
}
}
}
return u8_ret;
}
/**
* ************************************************************************
* @brief - 充电模块启停控制 包含需求电压电流下发和启停控制等 内部包含模块高低压状态切换功能
*
*
*
* @author jiankalka
* @date 2025-11-13
* ************************************************************************
*/
static void v_uuln_mdu_set_ctrl()
{
/* 周期发送
*/
U8_T gunNo,u8_flag,u8_gunState;
S_MDU_GROUP_DATA *s_mdu_group = NULL; //模块组数据
for (gunNo = 0; gunNo < s_mdu_data.u8_mdu_group_num; gunNo++) //遍历模块组数量
{
s_mdu_group = &s_mdu_data.s_mdu_group[gunNo]; //获取i模块组数据结构体
//模块 高低压状态检查 500V
if(s_mdu_group->u16_needVol != 0) //需求电压为0时 无意义 不用做检查
{
if(s_mdu_group->u16_needVol > 5000) //需求电压大于500V 则设置为高压状态
{
//返回值1 则代表存在设置过程 其中包含停机操作 因此不必执行下方的控制命令
if(u8_uuln_set_grp_mdu_hlvol(gunNo+1 , E_TrueHiLo_H)) //设置模块高低压状态 1000V
{
continue;
}
}
else //需求电压小于500V 则设置为底压状态
{
//返回值1 则代表存在设置过程 其中包含停机操作 因此不必执行下方的控制命令
if(u8_uuln_set_grp_mdu_hlvol(gunNo+1 , E_TrueHiLo_L)) //设置模块高低压状态 500V
{
continue;
}
}
}
#if( MDU_PARA_READ_EN == 1) //模块参数读取使能
if(s_mdu_data.u16_mduPara_MaxOutVol != 0 && s_mdu_group->u16_needVol > s_mdu_data.u16_mduPara_MaxOutVol)
s_mdu_group->u16_needVol = s_mdu_data.u16_mduPara_MaxOutVol;
else if(s_mdu_group->u16_needVol < s_mdu_data.u16_mduPara_MinOutVol)
s_mdu_group->u16_needVol = s_mdu_data.u16_mduPara_MinOutVol;
if(s_mdu_data.u16_mduPara_MaxOutCur != 0 && s_mdu_group->u16_oneMdu_needCur > s_mdu_data.u16_mduPara_MaxOutCur)
s_mdu_group->u16_oneMdu_needCur = s_mdu_data.u16_mduPara_MaxOutCur;
else if(s_mdu_group->u16_oneMdu_needCur < s_mdu_data.u16_mduPara_MinOutCur)
s_mdu_group->u16_oneMdu_needCur = s_mdu_data.u16_mduPara_MinOutCur;
#endif
v_uuln_send_set_all_data(gunNo+1,E_MODULE_VOLTAGE_SETPOINT,s_mdu_group->u16_needVol*100); // 0x02 模块输出电压设定值 R/W N 数据精度 0.0001V mV
v_uuln_send_set_all_data(gunNo+1,E_MODULE_CURRENT_LIMIT_SETPOINT,s_mdu_group->u16_oneMdu_needCur *100); // 0x03 模块输出限流点设定值 R/W N 数据精度 0.0001V mV
u8_gunState = u16_mdu_get_charge_need(gunNo, E_MDU_GET_DATA_GUN_CMD) ; //枪状态 0无 1快速启机 2快速启机停(不清空分组) 3软启开机 4充电停止(清空分组)
/*0无 1快速启机 2快速启机停(不清空分组) 3软启开机 4充电停止(清空分组)
*/
if(u8_gunState == 0x01 || u8_gunState== 0x03)
{
u8_flag = 0; //开机
}
else
{
u8_flag = 1; //关机
}
v_uuln_send_set_all_data(gunNo+1,E_DCDC_MODULE_POWER_CONTROL,u8_flag); // 0x04 DCDC模块开关机 W N DCDC 模块开关机(0 开机,1 关机)
}
}
/**
* ************************************************************************
* @brief - 模块组号设置检查 设置模块数量除以2 前半部分设置组号为1 后半部分设置组号为2
*
*
*
* @author jiankalka
* @date 2025-11-12
* ************************************************************************
*/
static void v_uuln_mdu_grp_cheak()
{
U8_T i,u8_mdu_grp;
S_MDU_CTRL *s_mdu_ctrl = NULL; //模块控制
S_MDU_STATE *s_mdu_state = NULL; //模块状态
for ( i = 0; i < s_mdu_data.u8_mdu_num; i++) //遍历所有模块
{
s_mdu_ctrl = &s_mdu_data.s_mdu[i].s_ctrl; //i号模块 控制数据结构体
s_mdu_state = &s_mdu_data.s_mdu[i].s_state; //i号模块 状态数据结构体
if(s_mdu_state->e_state == E_MDU_STATE_NONE)
continue;
// if(i < ((s_mdu_data.u8_mdu_num+1) / 2)) // 前半部分为1枪 后半部分为2枪 模块数量为奇数时,1枪多一个
// u8_mdu_grp = 1;
// else
// u8_mdu_grp = 2;
u8_mdu_grp = s_mdu_ctrl->u8_mdu_in_pile;
if(s_mdu_state->u8_groupNo != u8_mdu_grp) //模块组号不正确 设置模块组号
{
v_uuln_send_set_grp_no(i+1,u8_mdu_grp);
}
}
}
/**
* ************************************************************************
* @brief -模块通信发送控制函数
*
*
*
* @author jiankalka
* @date 2025-07-03
* ************************************************************************
*/
static void v_uuln_mdu_comm_send_ctrl(U16_T u16_sleepTime)
{
if(s_mdu_data.u16_timer % NUM_250 == 0) //250ms
{
v_mdu_data_summ(); //模块组信息更新
v_uuln_mdu_set_ctrl(); //模块设置控制
}
if(s_mdu_data.u16_timer % NUM_1000 == 0) //1秒
{
v_uuln_send_read_all_data(0,E_MODULE_STATUS_FLAGS); //读取所有模块的状态标志位 组1 组2
}
else if(s_mdu_data.u16_timer % NUM_1000 == u16_sleepTime) //1秒 与上一帧间隔 一个周期
{
v_uuln_send_read_all_data(0,E_MODULE_OUTPUT_VOLTAGE); //0x00 模块输出电压 R N
}
else if(s_mdu_data.u16_timer % NUM_1000 == u16_sleepTime*2) //1秒 与上一帧间隔 一个周期
{
v_uuln_send_read_all_data(0,E_MODULE_OUTPUT_CURRENT); //0x00 0x01 模块输出电流 R N
if(s_mdu_data.u16_mduPara_MaxOutVol == 0xFF ||
s_mdu_data.u16_mduPara_MaxOutCur == 0xFF)
v_uuln_send_read_all_data(0,E_MODULE_CURRENT_AND_CAPABILITY); // 0x72 读取模块输出电流和输出电流能力 R N 单位:0.1A(高十六位:模块的实际输出电流,低十六位:实际的电流能力)
}
else if(s_mdu_data.u16_timer % NUM_1000 == u16_sleepTime*3) //1秒 与上一帧间隔 一个周期
{
v_uuln_send_read_all_data(0,E_MODULE_HIGH_LOW_VOLTAGE_STATUS); // 0x60 读取模块高低压模式状态 R N
}
else if(s_mdu_data.u16_timer % NUM_1000 == u16_sleepTime*4) //1秒 与上一帧间隔 一个周期
{
v_uuln_send_read_all_data(0,E_INLET_AIR_TEMPERATURE); // 0x1E 进风口温度 R N
}
if(s_mdu_data.u16_timer % NUM_5000 == 0) //5秒
{
v_uuln_mdu_grp_cheak(); //模块组号设置检查
}
}
#endif
#if 1 //接收
static void v_uuln_mdu_rx_unpack(CAN_DATA *rxMsg)
{
U_UULN_CAN_ID_TYPE u_ID;
E_UULN_CMD_TYPE e_cmd_type;
U_UULN_DATA1_TYPE u_Data1;
U8_T u8_addr;
U32_T u32_recv_data;
S_MDU_STATE *s_mdu_state = NULL;
u_ID.u32_ID = rxMsg->ID; //获取ID
u8_addr = u_ID.bit.ModuleAddres-1; //模块地址
e_cmd_type = rxMsg->Data[1]; //数据类型
u_Data1.u8_data = rxMsg->Data[0]; //信息类型 和 组地址
/* 抢连接/DI 状态:交给 STM 状态通信模块解析(ID 0x1801AABB */
#if (GUN_LINK_COMM_EN)
if (u8_stm_gb_enable() && u8_stm_status_can_rx(rxMsg))
return;
#endif
if(u_ID.bit.Protocol != D_MDU_UULN_VER) //协议号 不一致
return;
if(u_Data1.bit.MessageType != E_MAG_TYPE_SET_ACK &&
u_Data1.bit.MessageType != E_MAG_TYPE_READ_ACK )//信息类型 不正确
return;
if(u8_addr >= MDU_MAX_NUM)
return;
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d](0-N) cmd is [%d]\r\n",u8_addr,e_cmd_type );
#endif
s_mdu_state = &s_mdu_data.s_mdu[u8_addr].s_state; //获取模块状态
s_mdu_state->u16_outtimecnt = 0; //超时计数 清零
//获取4-7字节数据内容 转换为整形
u32_recv_data = (rxMsg->Data[4] << 24) | (rxMsg->Data[5] << 16) | (rxMsg->Data[6] << 8) | rxMsg->Data[7];
switch (e_cmd_type)
{
case E_MODULE_STATUS_FLAGS : // 0x08 模块状态标志位 R N
{
U_UULN_MDU_STATUS u_state;
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d](0-N) grp is [%d],work state is [%d]\r\n",u8_addr,u_Data1.bit.GroupAddress,1-u_state.bit.DcdcNotRun );
#endif
u_state.all = u32_recv_data;
s_mdu_state->u8_GrpMode = 1; //默认都是动态分组 模块分组模式 0固定分组 1动态分组
s_mdu_state->u8_groupNo = u_Data1.bit.GroupAddress; //组号
if (u_state.bit.DcdcNotRun == 0) //工作状态,1待机,2工作
s_mdu_state->e_work_state = E_MDU_WORK;
else
s_mdu_state->e_work_state = E_MDU_IDLE;
// 故障判断:如果存在任何故障点位或预留点位非零则判为故障,否则正常
if (u_state.all & FAULT_MASK) { // 存在故障,进行故障处理
if(s_mdu_state->e_state != E_MDU_STATE_NONE)
s_mdu_state->e_state = E_MDU_STATE_FAULT; //故障状态
s_mdu_state->u_alarm_data.bit_mdu_alarm.b0_other = 1; //其他故障,0正常,1故障
// s_mdu_state->u_alarm_data.bit_mdu_alarm.b1_relief = ISBITSET(rxMsg->Data[1],1); //泄放故障
// s_mdu_state->u_alarm_data.bit_mdu_alarm.b2_fan = ISBITSET(rxMsg->Data[1],2); //风扇故障
// s_mdu_state->u_alarm_data.bit_mdu_alarm.b3_DC_short = ISBITSET(rxMsg->Data[1],3); //直流输出短路
// s_mdu_state->u_alarm_data.bit_mdu_alarm.b4_overTemp = ISBITSET(rxMsg->Data[1],4); //过温
// s_mdu_state->u_alarm_data.bit_mdu_alarm.b5_DC_UV = ISBITSET(rxMsg->Data[1],5); //直流输出欠压
// s_mdu_state->u_alarm_data.bit_mdu_alarm.b6_DC_OV = ISBITSET(rxMsg->Data[1],6); //直流输出过压
// s_mdu_state->u_alarm_data.bit_mdu_alarm.b7_AC_IN = ISBITSET(rxMsg->Data[1],7); //交流输入故障
} else {
if(s_mdu_state->e_state == E_MDU_STATE_FAULT)
s_mdu_state->e_state = E_MDU_STATE_NONE;
s_mdu_state->u_alarm_data.u8_mdu_alarm = 0;
}
s_mdu_state->u_uuln_state.all = u_state.all;
break;
}
case E_GROUP_ADDRESS : // 0x59 组地址 R/W Y
{
break;
}
case E_MODULE_OUTPUT_VOLTAGE: // 0x00 模块输出电压 R N
{
//模块输出电压 mV
s_mdu_state->u16_outVol = u32_recv_data / 100; //直流输出电压 0.1V
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d](0-N) grp is [%d],put vol is [%d](0.1V)\r\n",u8_addr,u_Data1.bit.GroupAddress,s_mdu_state->u16_outVol );
#endif
break;
}
case E_MODULE_OUTPUT_CURRENT: // 0x01 模块输出电流 R N
{
//模块输出电流 mA
s_mdu_state->u16_outCur = u32_recv_data/100; //直流输出电流 0.1A
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d] grp is [%d],put cur is [%d](0.1A)\r\n",u8_addr,u_Data1.bit.GroupAddress,s_mdu_state->u16_outCur );
#endif
break;
}
case E_MODULE_HIGH_LOW_VOLTAGE_STATUS: // 0x60 读取模块高低压模式状态 R N
{
//读取模块实际的高低压档位状态(1:1000V2:500V
s_mdu_state->e_TrueHiLo_Status = (E_TrueHiLo_Status)u32_recv_data; //高低压状态读取 1: 1000V 2: 500V
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d](0-N) grp is [%d],高低压模式状态 is [%d](1:1000V 2:500V)\r\n",u8_addr,u_Data1.bit.GroupAddress,s_mdu_state->e_TrueHiLo_Status );
#endif
}
case E_INLET_AIR_TEMPERATURE: // 0x1E 进风口温度 R N 毫度
{
s_mdu_state->s8_mdutemp_1 = u32_recv_data / 1000; //毫度
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d](0-N) grp is [%d], in fan temp is [%d](1:1000V 2:500V)\r\n",u8_addr,u_Data1.bit.GroupAddress,s_mdu_state->s8_mdutemp_1 );
#endif
break;
}
case E_MODULE_CURRENT_AND_CAPABILITY: // 0x72 读取模块输出电流和输出电流能力 R N 单位:0.1A(高十六位:模块的实际输出电流,低十六位:实际的电流能力)
{
//模块输出电流 mA
s_mdu_state->u16_outCur = ((u32_recv_data >> 16)&0xFFFF); //直流输出电流 0.1A
if(s_mdu_data.u16_mduPara_MaxOutCur == 0xFF) //未赋值 则赋值模块最大输出电流值
{
s_mdu_data.u16_mduPara_MaxOutVol = 1000*10; //默认1000V
s_mdu_data.u16_mduPara_MinOutVol = 0;
s_mdu_data.u16_mduPara_MaxOutCur = (u32_recv_data & 0xFFFF)/10;
s_mdu_data.u16_mduPara_MinOutCur = 0;
}
#if MDU_CAN_PRINT_FLAG
printf("mdu[%d] grp is [%d],put cur is [%d][%d](0.1A)\r\n",u8_addr,u_Data1.bit.GroupAddress,s_mdu_state->u16_outCur ,s_mdu_data.u16_mduPara_MaxOutCur);
#endif
break;
}
default:
break;
}
}
/**
* ************************************************************************
* @brief -模块通信接收解包函数
*
*
*
* @author jiankalka
* @date 2025-07-03
* ************************************************************************
*/
static void v_uuln_mdu_comm_recv_unpack()
{
U8_T i, canRcvCnt = 0; //接收can报文数目
CAN_DATA rx_can_data[CAN_RECV_BUF_SIZE] = {0};
canRcvCnt= u8_MduCanMsg_recv(&rx_can_data[0]);
//DEBUG_LOG("fdcan1 recv num = %d\n",ret);
for (i = 0; i < canRcvCnt; i++)
{
v_uuln_mdu_rx_unpack(&rx_can_data[i]);
}
}
#endif
#if 1 //通信超时
/**
* ************************************************************************
* @brief -模块控制 报文接收超时检查
*
*
*
* @author jiankalka
* @date 2025-07-03
* ************************************************************************
*/
static void v_uuln_mdu_comm_outtime_cheak(U16_T u16_sleepTime)
{
U8_T i,u8_mdu_num = s_mdu_data.u8_mdu_num; //模块数量
U8_T u8_mdu_comm_err_num = 0; //通信超时模块数量
S_MDU_STATE *s_mdu_state = NULL;
for(i=0;i<u8_mdu_num;i++)
{
s_mdu_state = &s_mdu_data.s_mdu[i].s_state; //获取模块状态
if(s_mdu_state->e_state == E_MDU_STATE_NONE) //模块未通信 判断通信
{
u8_mdu_comm_err_num ++;
if(s_mdu_state->u16_outtimecnt < 10*NUM_1000) //10秒超时
{
s_mdu_state->e_state = E_MDU_STATE_NORMAL; //通信正常
Mdu_Comm_LOG("(%d)号充电模块通讯正常\n",i);
}
v_mdu_set_comm_fault_info(i,1); //故障动作触发
}
else{ //模块已通信 判断超时
s_mdu_state->u16_outtimecnt += u16_sleepTime; //超时计数递增
if(s_mdu_state->u16_outtimecnt > 20*NUM_1000) //10秒超时
{
s_mdu_state->e_state = E_MDU_STATE_NONE;
v_mdu_clear_recv_data(i);
Mdu_Comm_LOG("(%d)号充电模块通讯超时\n",i);
}
else if(s_mdu_state->e_state == E_MDU_STATE_NORMAL &&
s_mdu_state->u16_outtimecnt > 10*NUM_1000 &&
s_mdu_state->u16_outtimecnt < 20*NUM_1000)
{
/* 10s无报文:按需可强制重初始化,这里默认强制 */
uint8_t act = CANSPI_HealthCheckAndRecover(1U);
if (act == 0U) {
printf("time=%d, 模块(%d) 10s无报文,但总线/控制器自检正常,暂不复位\r\n",
get_current_seconds(), i);
}
s_mdu_state->u16_outtimecnt = 0;
}
else
{
}
v_mdu_set_comm_fault_info(i,0); //故障恢复触发
}
}
if(u8_mdu_comm_err_num == u8_mdu_num) //所有模块通信故障
{
v_mdu_set_comm_fault_info(MODULE_ALL,1); //故障恢复触发
}
else
{
v_mdu_set_comm_fault_info(MODULE_ALL,0); //故障恢复触发
}
}
/**
* ************************************************************************
* @brief - 枪未连接 充电模块通信超时恢复
*
*
*
* @author jiankalka
* @date 2025-11-20
* ************************************************************************
*/
static void v_uuln_mdu_gun_no_link_outtime_claer()
{
U8_T i,u8_mdu_num = s_mdu_data.u8_mdu_num; //模块数量
v_mdu_set_comm_fault_info(MODULE_ALL,0); //故障恢复触发
for(i=0;i<u8_mdu_num;i++)
{
v_mdu_set_comm_fault_info(i,0); //故障恢复触发
}
}
#endif
/**
* ************************************************************************
* @brief - 优优绿能协议控制动作
*
*
*
* @author jiankalka
* @date 2025-11-12
* ************************************************************************
*/
void v_mdu_comm_uuln_action(U16_T u16_leep_time)
{
U16_T u16_runing_flag;
static U8_T u8_spican_init_flag = 0;
//获取模块任务运行标志 0模块任务不运行并清空模块数据 1模块任务运行-执行报文收发和超时判断
u16_runing_flag = u16_mdu_get_charge_need(0, E_MDU_GET_DATA_RUNING_FLAG);
v_uuln_mdu_comm_recv_unpack();
if(u16_runing_flag == 1)
{
v_uuln_mdu_comm_send_ctrl(u16_leep_time);
v_uuln_mdu_comm_outtime_cheak(u16_leep_time);
if(u8_spican_init_flag == 0) //模块任务运行后第一次进入 则进行SPI-CAN初始化
{
CANSPI_HealthCheckAndRecover(1U); //强制进行一次SPI-CAN健康检查和恢复,确保通信正常
u8_spican_init_flag = 1;
}
}
else
{
u8_spican_init_flag = 0;
s_mdu_data.u32_runing_time = get_current_seconds();
v_mdu_clear_recv_data(MODULE_ALL); //清空所有模块接收数据
v_uuln_mdu_gun_no_link_outtime_claer();
}
if(s_mdu_data.u16_timer < 0xFF00) //因为在
s_mdu_data.u16_timer += u16_leep_time; //计数周期累加
else
s_mdu_data.u16_timer = 0;
/* 辅源控制 CAN 帧:利用 s_mdu_data.u16_timer,每 1000ms 发送一次 */
#if (GUN_LINK_COMM_EN)
if (u8_stm_gb_enable() && (s_mdu_data.u16_timer % NUM_1000 == 0))
{
v_stm_status_can_send_aux_frame();
}
#endif
/* 周期性 SPI-CAN 健康检查:独立于模块通信超时运行 */
if (s_mdu_data.u16_timer % NUM_1000 == 0)
{
(void)CANSPI_HealthCheckAndRecover(0U);
}
}
#endif /* MDU_UULN_EN */
+122
View File
@@ -0,0 +1,122 @@
#ifndef MDU_YYLN_COMM_H
#define MDU_YYLN_COMM_H
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
extern "C" {
#endif
/*------ Exported includes(头文件) ----------------------*/
#include "main.h"
#include "publicdata/public_define.h"
/*------ Exported macro(宏定义) -------------------------*/
#define D_MDU_UULN_VER (0x01) //协议号 0x01 表示整流模块与监控之间的通讯协议号
#define FAULT_MASK 0xB035FBFF // 定义故障掩码:所有故障点位为1,非故障点位为0
/*------ Exported struct(结构体定义) --------------------*/
typedef enum
{
E_MAG_TYPE_SET_PARA = 0, // 0x00 设置模块参数
E_MAG_TYPE_SET_ACK, // 0x01 模块设置应答
E_MAG_TYPE_READ_DATA, // 0x02 读模块信息
E_MAG_TYPE_READ_ACK, // 0x03 模块读信息应答
E_MAG_TYPE_READ_SERIAL, // 0x04 模块读序列号应答
}E_UULN_MAG_TYPE;
typedef enum
{
E_MODULE_OUTPUT_VOLTAGE = 0, // 0x00 模块输出电压 R N
E_MODULE_OUTPUT_CURRENT, // 0x01 模块输出电流 R N
E_MODULE_VOLTAGE_SETPOINT, // 0x02 模块输出电压设定值 R/W N
E_MODULE_CURRENT_LIMIT_SETPOINT, // 0x03 模块输出限流点设定值 R/W N
E_DCDC_MODULE_POWER_CONTROL, // 0x04 DCDC模块开关机 W N 模块开关机(0 开机,1 关机)
E_MODULE_SERIAL_NUMBER, // 0x05 读取模块序列号 R Y
E_MODULE_STATUS_FLAGS = 8, // 0x08 模块状态标志位 R N
E_LINE_VOLTAGE_AB = 20, // 0x14 线电压AB R N
E_LINE_VOLTAGE_BC, // 0x15 线电压BC R N
E_LINE_VOLTAGE_CA, // 0x16 线电压CA R N
E_INLET_AIR_TEMPERATURE = 30, // 0x1E 进风口温度 R N
E_GROUP_ADDRESS = 89, // 0x59 组地址 R/W Y
E_MODULE_HIGH_LOW_VOLTAGE_MODE = 95,// 0x5F 模块高低压模式 W Y 11000V 2500V
E_MODULE_HIGH_LOW_VOLTAGE_STATUS, // 0x60 读取模块高低压模式状态 R N
E_MODULE_OUTPUT_CURRENT_CAPABILITY = 104, // 0x68 读取模块输出电流能力 R N
E_MODULE_CURRENT_AND_CAPABILITY = 114, // 0x72 读取模块输出电流和输出电流能力 R N 单位:0.1A(高十六位:模块的实际输出电流,低十六位:实际的电流能力)
}E_UULN_CMD_TYPE; //type 1
typedef enum {
E_TrueHiLo_H = 1, //模块高低压模式 W Y 11000V )
E_TrueHiLo_L //模块高低压模式 W Y (2:500V )
}E_TrueHiLo_Status;
typedef union
{
U32_T u32_ID;
struct
{
U32_T SerialNumberLowPart : 9; // 产品序列号低位(默认填 0)。
U32_T ProductionDay : 5; // 产品生产日期(01-31)。
U32_T ModuleAddres : 7; // 整流模块地址 为整流模块地址,地址确定在 0x01-0x7F。 0x00:代表广播通信
U32_T MonitorAddress : 4; // 监控地址 地址设定在 0x010x0F,默认 0x01 0x00:代表广播通信
U32_T Protocol : 4; // 协议号 0x01 表示整流模块与监控之间的通讯协议号
U32_T res : 3;
}bit;
}U_UULN_CAN_ID_TYPE;
typedef union
{
U8_T u8_data;
struct
{
U8_T MessageType : 4; // 信息类型 详看枚举 E_UULN_MAG_TYPE
U8_T GroupAddress : 4; // 组地址 组地址范围为 0x01~0x0F,0x00 为广播通信;
}bit;
}U_UULN_DATA1_TYPE;
typedef union {
U32_T all;
struct MODULE_STATUS_BITS {
U32_T AcOV:1; // 0 交流输入过压
U32_T AcUV:1; // 1 交流输入欠压
U32_T AcOVDisconnected:1; // 2 交流输入过压脱离
U32_T PfcOV:1; // 3 PFC母线过压
U32_T PfcUV:1; // 4 PFC母线欠压
U32_T PfcUnbalance:1; // 5 PFC母线不平衡
U32_T DcOV:1; // 6 直流输出过压
U32_T DcOVShutDown:1; // 7 直流输出过压关机
U32_T DcUV:1; // 8 直流输出欠压
U32_T FanNotRun:1; // 9 风扇故障
U32_T Reserved_10:1; // 10 保留
U32_T FanDriverFail:1; // 11 风扇电压驱动电路损坏
U32_T AmbientOT:1; // 12 环境温度过温保护
U32_T AmbientUT:1; // 13 环境温度过低
U32_T Pfc1_OT:1; // 14 PFC过温保护1
U32_T OutputRelayFault:1; // 15 输出继电器故障
U32_T Dcdc1_OT:1; // 16 DC过温保护1
U32_T Reserved_17:1; // 17 保留
U32_T SciIsNotOK:1; // 18 原副边通信故障
U32_T Reserved_19:1; // 19 保留
U32_T PfcFail:1; // 20 原边故障
U32_T DcdcFail:1; // 21 副边故障
U32_T Reserved_22:1; // 22 保留
U32_T Reserved_23:1; // 23 保留
U32_T Reserved_24:1; // 24 保留
U32_T DcdcNotRun:1; // 25 开关机状态
U32_T OutputMode:2; // 26:27 输出环路状态
U32_T DcVoltageUnbalance:1; // 28 DC输出电压不平衡
U32_T GetSameSN:1; // 29 发现相同序列号的模块
U32_T Reserved_30:1; // 30 保留
U32_T BleederNotWork:1; // 31 泄放电路损坏
}bit;
}U_UULN_MDU_STATUS;
/*------ Exported variables(变量定义) -------------------*/
/*------ Exported function prototypes (函数声明) -------*/
void v_mdu_comm_uuln_action(U16_T u16_leep_time);
#ifdef __cplusplus
}
#endif
#endif /* MDU_YYLN_COMM_H */