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
+853
View File
@@ -0,0 +1,853 @@
#include "meter_calculate/meter_coll_task.h"
#include "app_init/app_init.h"
#include "app_init/task_lock.h"
#include "mylog/mylog.h"
#include "publicdata/publicdata.h"
#include "wdt_task/wdt_task.h"
#include "usart.h"
#include "fault_cheak/fault_interface.h"
#define METERCOLL_TASK_ID (TASK_ID_MeterColl)
#define METERCOLL_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(METERCOLL_TASK_ID))
#define METERCOLL_LOCK_INIT() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_INIT))
#define METERCOLL_LOCK_TAKE() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_TAKE))
#define METERCOLL_LOCK_GIVE() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_GIVE))
#define METERCOLL_FEED_DOG() (v_wdt_setFlag(METERCOLL_TASK_ID))
#define METERCOLL_LOG(fmt, ...) MYLOG_MSG(METERCOLL_TASK_ID, (fmt), ##__VA_ARGS__)
#define D_Print_flag (0) //printf 打印标志
#define D_YaDa_Flag (1) //雅达电表 使用高精度指令不一样 其组合有功电量获取为 00 0E 00 00 XXXXX.XXX
#define D_FrameStart 0x68 /* 帧头 */
#define D_FrameEnd 0x16 /* 帧尾 */
//645协议
#define D_645_FCReadData 0x11 /* 读实时数据 */
#define D_645_FCReadDataResp 0x91 /* 读实时数据正常应答 */
//645协议标准精度与高精度度数据标识
#define DATA_645_ENERGY 0x00000000 //电量(标准)
#define DATA_645_VOL 0x00010102 //电压(标准)
#define DATA_645_CUR 0x00010202 //电流(标准)
#define DATA_645_HIGH_CUR 0x00011102 //电流(扩展)
#define DATA_645_HIGH_VOL 0x00011002 //电压(扩展帧)
#if (D_YaDa_Flag == 0)
#define DATA_645_HIGH_ENERGY 0x00006200 //电量(高精度)
#else
#define DATA_645_HIGH_ENERGY 0x00006000 //电量(高精度) 使用正向有功电量获取
#endif
#define DATE_645_DATA_NUM (6) //645协议标准内容数量
static const Meter_Data_Item st_MeterDataSheet[DATE_645_DATA_NUM] =
{
//数据标识 字节数 系数
{DATA_645_ENERGY, 4, 100, }, //组合电量 (标准) XXXXXX.XX kWh
{DATA_645_VOL, 2, 10, }, //电压 (标准) XXX.X V
{DATA_645_CUR, 3, 1000, }, //电流(标准)XXX.XXX A
{DATA_645_HIGH_ENERGY, 5, 10000, }, //组合电量(扩展帧)xxxxxx.xxxx kWh
{DATA_645_HIGH_VOL, 3, 100, }, //电压(扩展帧) xxxx.xx V
{DATA_645_HIGH_CUR, 4, 10000, }, //电流(扩展帧) xxxx.xxxx A
};
S_METER_CTRL_DATA_DATA s_memter_ctrl; //电表控制任务初始化
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_6,E_USART_5}; //双枪对应通信口
#elif SYSTEM_TYPE == SYSTEM_TYPE_CCU621_M
const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_3,E_USART_4}; //双枪对应通信口
#else
const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_3,E_USART_4}; //双枪对应通信口
#endif
/****************************************************************************系统集成改动部分-开始******************************************************/
#if 1 //接口函数
/**
* ************************************************************************
* @brief - 电表数据发送接口函数 发送485数据
*
* @param[in] u8_gunNo - 枪号 0-1 函数内部自行匹配串口号
* @param[in] u8_sendBuf - 发送数据地址
* @param[in] u16_len - 发送数据长度
*
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
static void v_com_meterSend(U8_T u8_gunNo , U8_T *u8_sendBuf , U16_T u16_len)
{
if(u8_gunNo >= METER_COMM_MAX_CNT) return;
u16_usart_send(u8_commPort[u8_gunNo],u8_sendBuf, u16_len);
#if D_Print_flag //调试打印
if(u8_gunNo == D_Print_flag - 1) {
printf("[%d] GUN meter send,len is [%d]\r\n",u8_gunNo , u16_len);
printf_hex("--",u8_sendBuf,u16_len);
}
#endif
}
/**
* ************************************************************************
* @brief - 电表数据接收接口函数 接收485数据
*
* @param[in] u8_gunNo - 枪号 0-1 函数内部自行匹配串口号
* @param[in] u8_recvBuf - 接收数据地址
* @param[in] u16_len - 预期接收数据长度
*
* @return U16_T - 已接收数据长度
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
static U16_T u16_com_meterRecv(U8_T u8_gunNo , U8_T *u8_recvBuf ,U16_T u16_len)
{
U16_T recv_len = 0;
if(u8_gunNo >= METER_COMM_MAX_CNT) return 0;
recv_len = u16_usart_recv(u8_commPort[u8_gunNo],u8_recvBuf,u16_len);
#if D_Print_flag //调试打印
if(u8_gunNo == D_Print_flag - 1) {
if(recv_len == 0) return 0;
printf("[%d] GUN meter recv,len is [%d]\r\n",u8_gunNo , recv_len);
printf_hex("--",u8_recvBuf,recv_len);
}
#endif
return recv_len;
}
#endif
#if 1 //对外数据提供 --
/**2
* ************************************************************************
* @brief - 外部获取电表数据
*
* @param[in] u8_gunNo - 抢号 0-1
* @param[in] u8_id - 该枪的电表号 默认0为直流表 1为交流表
* @param[in] u8_type - 数据类型 0电量 1电压 2电流
*
* @return U64_T - 返回值 电压、电流、电量 -精度都为0.0001 4位小数
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
U64_T u64_get_meter_data(U8_T u8_gunNo ,U8_T u8_id , E_METER_DATA_TYPE e_type)
{
U64_T u64_ret = 0;
S_METER_DATA *s_meter_data =NULL;
//数据合法性判断
if(u8_gunNo >= GUN_MAX_CNT || u8_id >= METER_COMM_MAX_CNT || e_type >= METER_COMM_TYPE_CNT)
return u64_ret;
METERCOLL_LOCK_TAKE(); //数据上锁
s_meter_data = &s_memter_ctrl.s_meters[u8_gunNo][u8_id]; //获取电表数据地址
switch (e_type)
{
case E_METER_DATA_POWER:
u64_ret = s_meter_data->u64_energy;
break;
case E_METER_DATA_VOL:
u64_ret = s_meter_data->u32_voltage;
break;
case E_METER_DATA_CUR:
u64_ret = s_meter_data->u32_current;
break;
default:
break;
}
METERCOLL_LOCK_GIVE(); //数据解锁
return u64_ret;
}
/**
* ************************************************************************
* @brief - 获取电表号
*
* @param[in] u8_gunNo - 枪号 0-N
* @param[in] u8_id - 该枪的电表号 默认0为直流表 1为交流表
*
* @return U8_T - 0未获取 1获取
*
* @author jiankalka
* @date 2025-11-27
* ************************************************************************
*/
U8_T u8_get_meter_id_str(U8_T u8_gunNo ,U8_T u8_id ,U8_T *u8_getData , U8_T u8_len)
{
if(u8_gunNo >= GUN_MAX_CNT || u8_id >= METER_COMM_MAX_CNT)
return 0;
memset(u8_getData, 0, u8_len);
strncpy((char *)u8_getData , (char *)&s_memter_ctrl.s_meters[u8_gunNo][u8_id].u8_meterNumber[0] , u8_len);
return 1;
}
#endif
#if 1 //外部数据获取
/**
* ************************************************************************
* @brief - 获取电表地址
*
* @param[in] s_memter_data 电表数据
*
*
* @author jiankalka
* @date 2025-08-06
* ************************************************************************
*/
static void v_meter_get_para(S_METER_CTRL_DATA_DATA *s_memter_data)
{
U8_T i, k, cnt;
S_MEMET_PARA *s_para = &g_t_share_data.t_sys_fix_cfg.s_memter;
U8_T gun_cnt = g_t_share_data.t_sys_fix_cfg.u8_gunCnt;
if (s_memter_data == NULL) {
return;
}
if ((gun_cnt == 0U) || (gun_cnt > GUN_MAX_CNT)) {
gun_cnt = GUN_MAX_CNT;
}
s_memter_data->u8_gunCnt = gun_cnt; // 枪数量
for (i = 0; i < s_memter_data->u8_gunCnt; i++) {
cnt = 0;
for (k = 0; k < s_para->u8_meter_num; k++) { //遍历所有电表
if (s_para->s_data[k].u8_inGun == (U8_T)(i + 1U)) { //参数设置电表 为本枪 则保存数据
if (cnt >= METER_COMM_MAX_CNT) {
break; // 单枪电表位已满
}
s_memter_data->s_meters[i][cnt].u8_enFlag = 1; //使能标志 0不使能 1使能 用于区分该位置是否存在电表
s_memter_data->s_meters[i][cnt].u8_isAC = s_para->s_data[k].u8_isAC; // 是否为交流电表标识 (1:交流 0:直流)
s_memter_data->s_meters[i][cnt].u8_highPrec = s_para->s_data[k].u8_highPrec; //是否支持高精度 0 不支持 1支持
memcpy((char *)&s_memter_data->s_meters[i][cnt].u8_meterNumber[0],
(char *)&s_para->s_data[k].u8_meterAddr[0],
6);
cnt++;
}
}
}
}
/**
* ************************************************************************
* @brief - 设置电表通信超时状态 用于外部触发故障告警
*
* @param[in] u8_gunNo - 枪号 0-1
* @param[in] u8_id - 该枪电表序号 默认0直流表 1交流表
* @param[in] u8_flag - 故障动作 0通信恢复 1 通信超时故障
*
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
static void v_meter_set_comm_timeout(U8_T u8_gunNo , U8_T u8_id , U8_T u8_flag)
{
if(u8_flag == 1)
{
v_set_fault_data((E_FAULT_SITE)(u8_gunNo + E_GUN), E_FAULT, E_FAULT_13071, 0, u8_id, 0); //电表通讯故障
}
else
v_set_fault_data((E_FAULT_SITE)(u8_gunNo + E_GUN), E_RES, E_FAULT_13071, 0, u8_id, 0); //电表通讯故障
return;
}
#endif
/****************************************************************************系统集成改动部分-结束******************************************************/
/****************************************************************************内部逻辑-开始******************************************************/
#if 1 //发送控制
/*************************************************************
函数名称: u8_645_SendFrame(U8_T u8_frm_type, MeterAddress meteraddr, U32_T datesign, U8_T* u8_sendBuf)
函数功能: 打包组帧发送645协议命令帧
输入参数: u8_frm_type 控制码 meteraddr 电表地址 datesign 数据标识 u8_sendBuf 需要发送的数据
输出参数:
**************************************************************/
static void v_645_SendFrame(S_METER_FRAME *s_meterFrame) //组帧发送
{
U8_T u8_cnt = 0;
U8_T CheckSum;
U8_T u8_sendBuf[NUM_64] = {0};
u8_sendBuf[u8_cnt++] = D_FrameStart; //帧起始域
memcpy(&u8_sendBuf[u8_cnt], &s_meterFrame->address[0], 6);
u8_cnt += 6;
u8_sendBuf[u8_cnt++] = D_FrameStart; //帧起始域
u8_sendBuf[u8_cnt++] = s_meterFrame->control_code; //控制码 0x11 读数据
u8_sendBuf[u8_cnt++] = 0x04; //数据长度
// 数据标识(拆分为高字节和低字节)
u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 24) & 0xFF) + 0x33;
u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 16) & 0xFF) + 0x33;
u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 8) & 0xFF) + 0x33;
u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign) & 0xFF) + 0x33;
/* 打包校验和与帧尾 */
CheckSum = u16_crc_checksum(u8_sendBuf, u8_cnt);
u8_sendBuf[u8_cnt++] = CheckSum;
u8_sendBuf[u8_cnt++] = D_FrameEnd;
v_com_meterSend(s_meterFrame->u8_gunNo,u8_sendBuf, u8_cnt);
}
/**
* ************************************************************************
* @brief - 电表数据采集发送组帧函数
*
* @param[in] u8_gunNo - 枪地址索引号 0-1
* @param[in] u8_type - 0 电量 1电压 2电流
* @param[in] s_meter_data - 电表数据
*
*
* @author jiankalka
* @date 2025-08-06
* ************************************************************************
*/
static void v_memter_SendFrame(U8_T u8_gunNo, U8_T u8_type , S_METER_DATA *s_meter_data)
{
S_METER_FRAME s_meterFrame;
memset(&s_meterFrame , 0, sizeof(S_METER_FRAME));
s_meterFrame.u8_gunNo = u8_gunNo;
s_meterFrame.control_code = D_645_FCReadData; //控制码 0x11 读数据
memcpy(&s_meterFrame.address[0], s_meter_data->u8_meterNumber, 6);
if(u8_type % 3 == 0){ //电量
if(s_meter_data->u8_highPrec == 1) //高精度
s_meterFrame.u32_datesign = DATA_645_HIGH_ENERGY;
else
s_meterFrame.u32_datesign = DATA_645_ENERGY;
}
else if(u8_type % 3 == 1){ //电压
if(s_meter_data->u8_highPrec == 1) //高精度
s_meterFrame.u32_datesign = DATA_645_HIGH_VOL;
else
s_meterFrame.u32_datesign = DATA_645_VOL;
}
else if(u8_type % 3 == 2){ //电流
if(s_meter_data->u8_highPrec == 1) //高精度
s_meterFrame.u32_datesign = DATA_645_HIGH_CUR;
else
s_meterFrame.u32_datesign = DATA_645_CUR;
}
//printf("send ctrl , gun(%d) id(0x%08x) \n",s_meterFrame.u8_gunNo ,s_meterFrame.u32_datesign );
v_645_SendFrame(&s_meterFrame);
}
/**
* ************************************************************************
* @brief - 电表数据采集发送控制函数 用于对两路电表分别进行电压、电流、电量的数据采集发送--
*
* @param[in] u16_sleepTime - 任务睡眠时间
*
*
* @author jiankalka
* @date 2025-08-06
* ************************************************************************
*/
static void v_meter_send_ctrl(U16_T u16_sleepTime)
{
U8_T i;
S_METER_DATA *s_meter_data =NULL;
S_METER_COMM_CTRL *s_comm_ctrl = NULL;
for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量
{
s_comm_ctrl = &s_memter_ctrl.s_commCtrl[i]; //获取通信控制内容
s_meter_data = &s_memter_ctrl.s_meters[i][s_comm_ctrl->u8_send_type/METER_COMM_TYPE_CNT]; //获取该枪电表首地址 当send type 大于3时,则为2枪数据
if(s_meter_data->u8_enFlag == 0) //不存在电表 则不执行; 当电表2不存在时,send type从0开始执行
{
s_comm_ctrl->u8_send_type = 0;
continue;
}
if(s_comm_ctrl->u8_recv_flag == 0) //未存在 等待接收数据
{
v_memter_SendFrame(i,s_comm_ctrl->u8_send_type, s_meter_data); //控制数据发送
if(++s_comm_ctrl->u8_send_type >= METER_COMM_TYPE_CNT*2) //发送数据类型 递增
{
s_comm_ctrl->u8_send_type = 0;
}
s_comm_ctrl->u8_recv_flag = 1; //发送后等待数据接收
s_comm_ctrl->u16_recv_timeout_cnt = 0; //超时计数清空
}
else //等待接收状态 判断超时
{
s_comm_ctrl->u16_recv_timeout_cnt += u16_sleepTime;
if(s_comm_ctrl->u16_recv_timeout_cnt >= METER_RECV_TIMEOUT_MS) //数据接收超时时间 600ms ,则清空标识 重新进行发送
{
s_comm_ctrl->u8_recv_flag = 0;
s_comm_ctrl->u16_recv_timeout_cnt = 0; //超时计数清空
}
}
}
}
#endif
#if 1 //接收解析
/**
* ************************************************************************
* @brief - 电表数据内容解析 并存储; 查找接收数据对应的枪号、表号、数据内容等
*
* @param[in] s_meterFrame - 接收报文解析后的数据包-默认可用数据为正确数据
*
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
static void v_meter_unpack(S_METER_FRAME *s_meterFrame)
{
U8_T i,id;
U64_T u64_unpackData = 0,u64_recvData=0;
S_METER_DATA *s_meter = NULL;
//printf_hex("解析成功////data",s_meterFrame->data,s_meterFrame->data_len);
//前4个字节 转换成 数据标识
s_meterFrame->u32_datesign = 0;
for (i = 0; i < NUM_4; i++)
{
s_meterFrame->u32_datesign += ((s_meterFrame->data[3-i]- 0x33) << i*8);
}
//遍历所有可解析数据标识 查询对应标识
for (id = 0; id < DATE_645_DATA_NUM; id++)
{
if(s_meterFrame->u32_datesign == st_MeterDataSheet[id].iFlagCode)
break;
}
//查找失败,则退出
if(id >= DATE_645_DATA_NUM){
// printf("find fail , 0x%08x\n",s_meterFrame->u32_datesign);
return;
}
//长度一致性检查
if(st_MeterDataSheet[id].iLenth != s_meterFrame->data_len-4){
// printf("len fail , 0x%08x\n",s_meterFrame->u32_datesign);
return;
}
//将报文数据 -0x33 BCD转int 组成整形数据
for (i = 0; i < st_MeterDataSheet[id].iLenth; i++)
{
U8_T u8_data = (s_meterFrame->data[i+4]- 0x33);
u64_recvData = (u64_recvData << 8);
u64_recvData += u8_data;
}
//将BCD码 转换成 十进制 -- u64 8字节
for (i = 0; i < st_MeterDataSheet[id].iLenth; i++)
{
// 提取当前BCD数字(4位)
U8_T u8_data = (u64_recvData >> (8 * i))&0xFF;
U8_T digit = bcd_to_dec(u8_data);
// 累积结果(每次乘以10并加上新数字)
u64_unpackData = u64_unpackData * 100 + digit;
}
//电表号赋值 -- 一路单电表时,可以不设置电表号 --当一路多电表使用时,电表号为已经设置好-因此此处仅对0地址电表进行赋值
s_meter = &s_memter_ctrl.s_meters[s_meterFrame->u8_gunNo][0];
if(s_meter->u8_meterNumber[0] == 0xAA && s_meter->u8_meterNumber[1] == 0xAA){
memcpy(&s_meter->u8_meterNumber[0] , &s_meterFrame->address[0] , 6);
//凯弦欣能电表特殊处理 //打印
if(s_meterFrame->u8_gunNo == 1)
{
if(s_meter->u8_meterNumber[0] == 0x01 &&
s_meter->u8_meterNumber[1] == 0x00 &&
s_meter->u8_meterNumber[2] == 0x00 &&
s_meter->u8_meterNumber[3] == 0x00 &&
s_meter->u8_meterNumber[4] == 0x00 &&
s_meter->u8_meterNumber[5] == 0x00
)
{
s_meter->u8_meterNumber[0] = 2;
}
}
}
//printf("u64_unpackData is %lld\n", u64_unpackData);
METERCOLL_LOCK_TAKE(); // 数据上锁
//查找电表号一致的电表 - 赋值对应内容
for (i = 0; i < METER_COMM_MAX_CNT; i++)
{
s_meter = &s_memter_ctrl.s_meters[s_meterFrame->u8_gunNo][i];
//if(strncmp((char *)s_meter->u8_meterNumber , (char *)s_meterFrame->address , 6) == 0) //电表号相等
if (memcmp(s_meter->u8_meterNumber, s_meterFrame->address, 6) == 0)
{
// printf("find ok , gun(%d) id(0x%08x) data(%lld)\n",s_meterFrame->u8_gunNo ,s_meterFrame->u32_datesign , u64_unpackData);
//存储电压、电流、电量值的精度 都是0.0001 四位小数,因此根据数据系数做统一转换
switch (st_MeterDataSheet[id].iFlagCode)
{
case DATA_645_ENERGY : //0x00000000 //电量(标准) XXXXXX.XX kWh
case DATA_645_HIGH_ENERGY : //0x00006200 //电量(高精度) xxxxxx.xxxx kWh
{
if(st_MeterDataSheet[id].iCoefficient != 0)
s_meter->u64_energy = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电量值(单位:0.0001 kWh
s_meter->u16_timeout_cnt[0] = 0;
break;
}
case DATA_645_VOL : //0x00010102 //电压(标准) XXX.X V
case DATA_645_HIGH_VOL : //0x00011002 //电压(扩展帧 xxxx.xx V
{
if(st_MeterDataSheet[id].iCoefficient != 0)
s_meter->u32_voltage = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电压值(单位:0.0001V
s_meter->u16_timeout_cnt[1] = 0;
break;
}
case DATA_645_CUR : //0x00010202 //电流(标准) XXX.XXX A
case DATA_645_HIGH_CUR : //0x00011102 //电流(扩展) xxxx.xxxx A
{
if(st_MeterDataSheet[id].iCoefficient != 0)
s_meter->u32_current = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电流值(单位:0.0001 A
s_meter->u16_timeout_cnt[2] = 0;
break;
}
default:
break;
}
break;
}
}
METERCOLL_LOCK_GIVE(); //数据解锁
//接收标识清空
s_memter_ctrl.s_commCtrl[s_meterFrame->u8_gunNo].u8_recv_flag = 0; //数据接收标识 已清空
// if(s_meterFrame->u8_gunNo == 1)
// printf("unpack is ok , gun(%d) id(0x%08x) data(%lld)\n",s_meterFrame->u8_gunNo ,s_meterFrame->u32_datesign , u64_unpackData);
}
/**
* ************************************************************************
* @brief - 接收电表数据解帧函数 - 内部包括多包、残包等异常数据内容的处理方法
*
* @param[in] new_data - 新接收数据内容
* @param[in] new_len - 新接收数据内容长度
* @param[in] s_frame - 电表数据帧结构体指针 用于存储解析后的电表数据
*
* @return U8_T - 0解析失败 1解析成功
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
static U8_T u8_meter_recvFrame(const U8_T *new_data, U8_T new_len,S_METER_FRAME *s_frame)
{
S_METER_COMM_CTRL *s_ctrl = &s_memter_ctrl.s_commCtrl[s_frame->u8_gunNo];
// 1. 检查输入有效性
if (!new_data || new_len <= 0 || !s_frame) {
return 0;
}
// 2. 添加新数据到缓冲区
int free_space = MAX_BUFFER_SIZE - s_ctrl->u8_buffer_len;
int copy_len = (new_len > free_space) ? free_space : new_len;
if (copy_len > 0) {
memcpy(s_ctrl->u8_buffer + s_ctrl->u8_buffer_len, new_data, copy_len);
s_ctrl->u8_buffer_len += copy_len;
}
// 3. 查找有效帧(最小帧长为12字节)
if (s_ctrl->u8_buffer_len < 12) {
return 0; // 数据不足
}
// 4. 尝试查找帧起始位置
int frame_start = -1;
int frame_end = -1;
for (int i = 0; i <= s_ctrl->u8_buffer_len - 12; i++) {
// 检查帧起始标识(跳过前导符)
int start_idx = i;
if (s_ctrl->u8_buffer[start_idx] != D_FrameStart) continue;
// 检查地址后的帧起始符(位于start_idx+7
if (start_idx + 7 >= s_ctrl->u8_buffer_len) continue;
if (s_ctrl->u8_buffer[start_idx + 7] != D_FrameStart) continue;
// 检查数据长度域(位于start_idx+9
if (start_idx + 9 >= s_ctrl->u8_buffer_len) continue;
// 检查帧完整长度
frame_end = start_idx + 11 + s_ctrl->u8_buffer[start_idx + 9]; // 包含结束符的位置
if (frame_end >= s_ctrl->u8_buffer_len) continue;
// 检查帧结束符(0x16
if (s_ctrl->u8_buffer[frame_end] != D_FrameEnd) continue;
frame_start = start_idx;
break;
}
// 5. 没有找到完整帧
if (frame_start < 0) {
// 缓冲区已满但无有效帧,清除部分数据
if (s_ctrl->u8_buffer_len >= MAX_BUFFER_SIZE / 2) {
// 保留最后1/4的数据
int keep_len = MAX_BUFFER_SIZE / 4;
if (s_ctrl->u8_buffer_len > keep_len) {
memmove(s_ctrl->u8_buffer, s_ctrl->u8_buffer + (s_ctrl->u8_buffer_len - keep_len), keep_len);
s_ctrl->u8_buffer_len = keep_len;
}
}
return 0;
}
// 6. 计算校验和
uint8_t calc_checksum = u16_crc_checksum(&s_ctrl->u8_buffer[frame_start] , frame_end - frame_start - 1);
if (calc_checksum != s_ctrl->u8_buffer[frame_end - 1]) {
return 0;
}
// 8. 解包帧数据
// 地址域(小端格式)
memcpy(s_frame->address, s_ctrl->u8_buffer + frame_start + 1, 6);
// 控制码
s_frame->control_code = s_ctrl->u8_buffer[frame_start + 8];
// 数据域长度和内容
s_frame->data_len = s_ctrl->u8_buffer[frame_start + 9];
s_frame->data_len = (s_frame->data_len > 0xFF) ? 0xFF : s_frame->data_len; // 安全限制
if (s_frame->data_len > 0) {
memcpy(s_frame->data, s_ctrl->u8_buffer + frame_start + 10, s_frame->data_len);
}
// // 计算完整帧长度(包括前导符)
// s_frame->data_len = (frame_end - frame_start + 1);
// 9. 移除已处理数据
int process_len = (frame_end + 1);
s_ctrl->u8_buffer_len -= process_len;
if (s_ctrl->u8_buffer_len > 0) {
memmove(s_ctrl->u8_buffer, s_ctrl->u8_buffer + process_len, s_ctrl->u8_buffer_len);
}
else
memset(s_ctrl->u8_buffer, 0 , MAX_BUFFER_SIZE);
return s_frame->data_len; // 返回处理的字节数
}
/**
* ************************************************************************
* @brief - 电表数据接收函数 遍历接收两个枪号的电表数据
*
*
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
static void v_memter_recv_ctrl()
{
U8_T i,u8_readLen,u8_recvBuf[NUM_128];
S_METER_FRAME s_meterFrame;
for (i = 0; i < METER_COMM_MAX_CNT; i++)
{
memset(u8_recvBuf , 0 , NUM_128);
u8_readLen = u16_com_meterRecv(i,u8_recvBuf , NUM_128);
if(u8_readLen > 0)
{
memset(&s_meterFrame , 0, sizeof(S_METER_FRAME));
s_meterFrame.u8_gunNo = i;
if(u8_meter_recvFrame(u8_recvBuf , u8_readLen, &s_meterFrame ) != 0)
{
//printf("gun[%d] data_len is %d\n", i, s_meterFrame.data_len);
if(s_meterFrame.data_len >= 6) //数据有效 否则可能为错误数据 最小长度为7
{
v_meter_unpack(&s_meterFrame);
}
}
}
}
}
#endif
#if 1 //通信超时
/**
* ************************************************************************
* @brief - 通信超时检查函数 超时计数累加,接收数据时会清空计数,由此判断接受超时
*
* @param[in] u16_sleepTime - 任务执行周期 ms
*
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
static void v_meter_timeout_cheak(U16_T u16_sleepTime)
{
U8_T i,k;
U16_T u16_max_timeout_cnt;
S_METER_DATA *s_meter_data =NULL;
for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量
{
for (k = 0; k < METER_COMM_MAX_CNT; k++) //遍历所有电表位置
{
s_meter_data = &s_memter_ctrl.s_meters[i][k]; //获取该枪电表首地址
if(s_meter_data->u8_enFlag == 0) //电表不存在 则不判断超时
continue;
if (s_meter_data->u16_timeout_cnt[0] >= s_meter_data->u16_timeout_cnt[1] &&
s_meter_data->u16_timeout_cnt[0] >= s_meter_data->u16_timeout_cnt[2]) {
u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[0];
} else if (s_meter_data->u16_timeout_cnt[1] >= s_meter_data->u16_timeout_cnt[0] &&
s_meter_data->u16_timeout_cnt[1] >= s_meter_data->u16_timeout_cnt[2]) {
u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[1];
} else {
u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[2];
}
if(s_meter_data->u8_comm_flag == 0) //未通信 判通信恢复
{
if(u16_max_timeout_cnt < METER_TIMEOUT_CNT)
{
s_meter_data->u8_comm_flag = 1;
v_meter_set_comm_timeout(i,k,0); //触发电表通信恢复
METERCOLL_LOG("gun[%d] id[%d], comm is ok\n", i,k);
// printf("gun[%d] id[%d], comm is ok\n", i,k);
}
}
else
{
if(u16_max_timeout_cnt < METER_TIMEOUT_CNT)
{
s_meter_data->u16_timeout_cnt[0] += u16_sleepTime;
s_meter_data->u16_timeout_cnt[1] += u16_sleepTime;
s_meter_data->u16_timeout_cnt[2] += u16_sleepTime;
}
else
{
s_meter_data->u8_comm_flag = 0;
//memset(s_meter_data, 0 ,sizeof(S_METER_DATA)); //不能全部清空 仅情况采集值即可
s_meter_data->u32_voltage =0;
s_meter_data->u32_current =0;
s_meter_data->u64_energy =0;
v_meter_set_comm_timeout(i,k,1); //触发电表通信超时故障
METERCOLL_LOG("gun[%d] id[%d], comm is fail\n", i,k);
// printf("gun[%d] id[%d], comm is fail\n", i,k);
}
}
}
}
}
#endif
/**
* ************************************************************************
* @brief - 电表数据初始化 初始化数据,并获取外部数据
*
*
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
static void v_memter_data_init()
{
U8_T i,k;
S_METER_DATA *s_meter_data =NULL;
memset(&s_memter_ctrl , 0 ,sizeof(S_METER_CTRL_DATA_DATA));
v_meter_get_para(&s_memter_ctrl);
for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量
{
for (k = 0; k < METER_COMM_MAX_CNT; k++) //遍历所有电表位置
{
s_meter_data = &s_memter_ctrl.s_meters[i][k]; //获取该枪电表首地址
if(s_meter_data->u8_enFlag == 0) //电表不存在 则不判断超时
continue;
s_meter_data->u8_comm_flag = 1;
s_meter_data->u16_timeout_cnt[0] = 0;//0xFFFF; //电表默认通信超时
s_meter_data->u16_timeout_cnt[1] = 0;
s_meter_data->u16_timeout_cnt[2] = 0;
}
}
}
/**
* ************************************************************************
* @brief - 电表数据采集函数 - 循环执行数据发生、数据接收、超时判断函数
*
* @param[in] p -
*
*
* @author jiankalka
* @date 2025-08-07
* ************************************************************************
*/
void v_Meter_Coll_task(void *argument)
{
uint16_t sleep_time_ms;
uint32_t last_send_time;
uint32_t current_time;
(void)argument;
(void)METERCOLL_LOCK_INIT();
sleep_time_ms = METERCOLL_SLEEP_TIME_MS();
last_send_time = get_current_seconds();
v_memter_data_init();
for (;;) {
METERCOLL_FEED_DOG();
current_time = get_current_seconds();
if ((TASK_RUN_PRINT_TIME != 0U) && (u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) {
if (METERCOLL_LOCK_TAKE() == 1U) {
METERCOLL_LOG("meter coll task is runing, sleep time is %ums", sleep_time_ms);
(void)METERCOLL_LOCK_GIVE();
}
last_send_time = current_time;
}
v_memter_recv_ctrl(); //电表数据接收
v_meter_send_ctrl(sleep_time_ms); //电表数据发生控制
v_meter_timeout_cheak(sleep_time_ms); //电表通信超时检查
mSleep(sleep_time_ms);
}
}