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
+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 */