Files

801 lines
31 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 */