Sailor 通信协议
大约 14 分钟Sailor 调试助手
Sailor通信协议
Note
串口通信协议,包含帧头CRC8校验以及数据的整包校验,可以实现不定长收发数据(例如RoboMaster中可用于和视觉双方通信,以及其他自研模块)
接口协议说明
以下所有低位在前发送
通信协议格式
帧头 | 设备类型 | 设备ID | 数据ID | 帧尾 |
---|---|---|---|---|
protocol_header(4-byte) | equipment_type(2-byte) | equipment_id (2-byte) | data_id(2-byte) | frame_tail(2-byte,CRC16,整包校验) |
帧头详细定义
帧头 | 偏移位置 | 字节大小 | 内容 |
---|---|---|---|
sof(CMD) | 0 | 1 | 数据帧起始字节,固定值为 0xA5 |
data_length | 1 | 2 | 数据帧中 data 的长度 |
crc_check | 3 | 1 | 帧头CRC校验 |
数据包长度 = data_length*4+(10+2);
equipment_type
设备类型-说明(字节偏移 4,字节大小 2)
命令码 | 数据段长度 | 功能说明 |
---|---|---|
0x0001(可修改) | 1 | KmdFoc |
0x0002 | 2 | ... |
equipment_id
设备ID-说明(字节偏移 6,字节大小 2)
data_id
数据ID-说明(字节偏移 8,字节大小 2)
数据段 data
(字节偏移 10, n-byte
)
frame_tail
( CRC16
,整包校验)
准备使用
/// <summary>
/// 方式一 初始化,并初始化内存
/// </summary>
/// <param name="pProtocol"></param>
/// <param name="uLen"></param>
void init_protocol(protocol_struct* pProtocol, uint16_t uLen);
/// <summary>
/// 方式二 外部预先分配好内存空间
/// </summary>
/// <param name="pProtocol"></param>
/// <param name="pFrameSt">uint32_t*</param>
/// <param name="pMessageSt">uint8_t*</param>
/// <param name="uLen"></param>
/// <returns></returns>
int init_protocol_pointer(protocol_struct* pProtocol, void* pFrameSt, void* pMessageSt, uint16_t uLen);
初始化,并初始化内存,内部分配
typedef struct
{
protocol_struct pTxProtocol;// 发送数据帧协议解析或生成
protocol_struct pRxProtocol;// 接收数据帧协议解析或生成
}protocol_struct;
protocol_struct protocol_t;
void bsp_protocol_init()
{
init_protocol(&protocol_t.pTxProtocol,24);
init_protocol(&protocol_t.pRxProtocol,24);
}
外部预先分配好内存空间方式
typedef struct
{
protocol_struct pTxProtocol;// 发送数据帧协议解析或生成
protocol_struct pRxProtocol;// 接收数据帧协议解析或生成
}protocol_struct;
protocol_struct protocol_t;
// 外部预先分配好内存空间
uint32_t pTxData[PROTOCOL_MAX_U32_LEN];
uint8_t pTxBuffer[PROTOCOL_MAX_U32_LEN * 4 + PROTOCOL_DATA_OFFSET + 2];
uint32_t pRxData[64];
uint8_t pRxBuffer[PROTOCOL_MAX_U32_LEN * 4 + PROTOCOL_DATA_OFFSET + 2];
void bsp_protocol_init()
{
protocol_t.pTxProtocol.frame_st.frame_user.cmd_data.pData = NULL;
protocol_t.pTxProtocol.message_st.pData = NULL;
init_protocol_pointer(&protocol_t.pTxProtocol, pTxData, pTxBuffer, PROTOCOL_MAX_U32_LEN);
protocol_t.pRxProtocol.frame_st.frame_user.cmd_data.pData = NULL;
protocol_t.pRxProtocol.message_st.pData = NULL;
init_protocol_pointer(&protocol_t.pRxProtocol, pRxData, pRxBuffer, PROTOCOL_MAX_U32_LEN);
}
使用待发送数据更新获取发送数据帧
/// <summary>
/// 生成带发送的数据内容
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
int make_protocol(protocol_struct* pProtocol);
Tip
生成发送数据前请准备好数据源,并传入数据源有效数据长度,你可以使用以下方式将自己的数据备份到解析缓冲区。
memcpy(pTxData,pData,uLen); //扩展帧数据长度
memcpy(pRxProtocol.frame_st.frame_user.pData,pData,uLen);//扩展帧数据长度,对于隐式初始化分配内存可用
典型案例->PC端 QT上位机协议发送
void vSeaskyPort::vSeaskyTxSlot(void)
{
if(this->vTxSeasky.vDataLen>this->vTxNum)
{
return;//超出长度,错误
}
if(this->vProtocol.pTxProtocol->message_st.pData!=nullptr)
{
if(vDataAutoTxEnable==true)
{
this->vProtocol.pTxProtocol->frame_st.frame_user.equipment_type = this->vTxSeasky.vEquipmentType;
this->vProtocol.pTxProtocol->frame_st.frame_user.equipment_id = this->vTxSeasky.vEquipmentId;
this->vProtocol.pTxProtocol->frame_st.frame_user.data_id = this->vTxSeasky.vDataId;
this->vProtocol.pTxProtocol->frame_st.frame_user.cmd_data.data_len = this->vTxSeasky.vDataLen;
make_protocol(this->vProtocol.pTxProtocol);
vSeaskyTxBuff=QByteArray(reinterpret_cast<const char*>(this->vProtocol.pTxProtocol->message_st.pData),
this->vProtocol.pTxProtocol->message_st.data_len);
emit vSerialTx(vSeaskyTxBuff);
}
else
{
if(vLineEditTxCtr())
{
vDataAutoTxLines(currIndex);
}
}
}
}
典型案例->MCU实时系统 数据发送
/**
* @description: USB和串口共用发送协议数据处理函数
* @param {BSP_KMD_COM_TYPE} tKmdCom_type
* @return {*}
*/
bool bsp_kmd_transmit(hal_frame_struct *tx_frame,BSP_KMD_COM_TYPE tKmdCom_type)
{
const uint8_t dataLen = 8;
// 设置设备类型为BSP_EQ_TYPE_KMD_FOC
kmd_usb_t.pTxProtocol.frame_st.frame_user.equipment_type = BSP_EQ_TYPE_KMD_FOC;
// 设置设备ID为 can_id
kmd_usb_t.pTxProtocol.frame_st.frame_user.equipment_id = tx_frame->can_id;
// 数据ID 对于Kmdfoc 而言 不使用
kmd_usb_t.pTxProtocol.frame_st.frame_user.data_id = 0;
// 设置 数据长度,uint32_t
kmd_usb_t.pTxProtocol.frame_st.frame_user.cmd_data.data_len = dataLen/4;
// 赋值待发送数据到协议解析区
memcpy((char*)(kmd_usb_t.pTxProtocol.frame_st.frame_user.cmd_data.pData),(char*)tx_frame->data,dataLen);
// 根据SEASKY协议生成发送缓冲
make_protocol(&kmd_usb_t.pTxProtocol);
switch(tKmdCom_type)
{
case BSP_MSG_FRAME_UART:
{
// 串口方式发送消息
return bsp_uart_transmit((uint8_t*)(kmd_usb_t.pTxProtocol.message_st.pData),kmd_usb_t.pTxProtocol.message_st.data_len);
}break;
#ifdef KMD_USB_USER
case BSP_MSG_FRAME_USB:
{
// USB方式发送消息
return bsp_kmd_usb_transmit((uint8_t*)(kmd_usb_t.pTxProtocol.message_st.pData),kmd_usb_t.pTxProtocol.message_st.data_len);
}break;
#endif
default:break;
}
return false;
}
接收数据
/// <summary>
/// 解析接收到的数据
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
int parse_protocol(protocol_struct* pProtocol,uint16_t parseDataLen);
Alerts tip
解析数据前请准备好数据源,并传入数据源有效数据长度,你可以使用以下方式将自己的数据备份到解析缓冲区。
memcpy(pRxBuffer,pData,uLen); //整体待解析数据长度
memcpy(pRxProtocol.message_st.pData,pData,uLen);//整体待解析数据长度,对于隐式初始化分配内存可用
Tip
使用说明,首先将接收数据存储于 rx_buf[],每接收一个数据 *rx_pos 的值会加一,以便于存储下一个数据,原始数据缓冲的存储由中断方式完成,在linux或win上可能有所区别, 调用此函数,函数中包含crc校验,数据解读等相关处理,最后会更新 flags_register 的值即为收到的16位寄存器的值, rx_data[] 即为收到的 float 数据;
典型案例->端 QT上位机协议解析
void vSeaskyPort::vSeaskyRxIRQ(const QByteArray &str)
{
int Ret = -1;
static uint32_t total_read_len; //总读取长度
static uint32_t reality_read_len; //实际读取长度
static uint32_t ready_parse_len; //预解析数据长度
//检验数据帧头,帧头固定为(0XA5),同时务必确认帧头与上一帧数据有时差
vRxBuff.append(str);
//缓冲区总数据长度
total_read_len = vRxBuff.length();
//准备解析的数据长度
reality_read_len = total_read_len;
while(reality_read_len>0)
{
//准备解析的数据长度
if((reality_read_len)>this->vProtocol.pRxProtocol->message_st.max_data_len)
{
ready_parse_len = this->vProtocol.pRxProtocol->message_st.max_data_len;
}
else
{
ready_parse_len = (reality_read_len);
}
//协议处理,如果现有数据大于数据帧长度
memcpy(&this->vProtocol.pRxProtocol->message_st.pData[0],&((uint8_t*)(vRxBuff.data()))[0],ready_parse_len);
Ret = parse_protocol(this->vProtocol.pRxProtocol,ready_parse_len);
if(Ret != PROTOCOL_RESULT_OK)
{
if(vRxBuff.at(0)!=char(PROTOCOL_HEAD_ID))
{
vRxBuff.clear();
}
break;
}
else
{
//解析数据成功
QString timeString;
timeString = QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss.zzz]\n");
/*获取CMDID*/
/*加入显示*/
vUpdateShowBuff(timeString);
/*加入示波器*/
this->vRxdata.clear();
for(qint8 i=0;i<this->vProtocol.pRxProtocol->frame_st.frame_user.cmd_data.data_len;i++)
{
this->vRxdata.append(((float*)this->vProtocol.pRxProtocol->frame_st.frame_user.cmd_data.pData)[i]);
}
//删除已使用
vRxBuff.remove(0,this->vProtocol.pRxProtocol->message_st.data_len);
//待接收长度清零
this->vProtocol.pRxProtocol->message_st.data_len = 0;
}
reality_read_len = vRxBuff.length();
}
}
典型案例->MCU实时系统 数据接收
/**
* @description: USB和串口共用接收数据处理函数
* @param {uint8_t} *pData
* @param {uint16_t} uLen
* @param {BSP_KMD_COM_TYPE} pMsgType
* @return {*}
*/
bool bsp_kmd_callback(uint8_t *pData,uint16_t uLen,BSP_KMD_COM_TYPE pMsgType)
{
uint16_t uPos;
if((uLen>0)&&(uLen<=kmd_usb_t.pRxProtocol.message_st.max_data_len))
{
memcpy(pRxBuffer,pData,uLen);
// 进行数据解析
parse_protocol(&kmd_usb_t.pRxProtocol,uLen);
switch (kmd_usb_t.pRxProtocol.frame_st.frame_user.equipment_type)
{
case BSP_EQ_TYPE_NULL:
;
break;
case BSP_EQ_TYPE_KMD_FOC:
{
return bsp_kmd_foc_callback(&kmd_usb_t.pRxProtocol,pMsgType);
}
break;
default:
;
break;
}
}
return false;
}
Seasky
串口通信协议
#include <stdio.h>
#ifndef __MICROLIB
#include <malloc.h >
#include <memory.h>
#endif
#include "bsp_protocol.h"
#include "./crc/bsp_crc8.h"
#include "./crc/bsp_crc16.h"
#include "bsp_protocol_class.h"
#ifdef PROTOCOL_CPP_CLR_DEBUG
#elif PROTOCOL_C_DEBUG
#define bsp_debug_c(Format, ...) printf(Format, ##__VA_ARGS__)
#else
#define bsp_debug_c(...)
#endif // !CPP_DEBUG
#define PROTOCOL_DEBUG_PRINTF(Format,...) bsp_debug_c(LOG_LEVEL_DEBUG,Format, ##__VA_ARGS__)
#define PROTOCOL_INFO__PRINTF(Format,...) bsp_debug_c(LOG_LEVEL_INFO_,Format, ##__VA_ARGS__)
#define PROTOCOL_WARN__PRINTF(Format,...) bsp_debug_c(LOG_LEVEL_WARN_,Format, ##__VA_ARGS__)
#define PROTOCOL_ERROR_PRINTF(Format,...) bsp_debug_c(LOG_LEVEL_ERROR,Format, ##__VA_ARGS__)
#define PROTOCOL_FATAL_PRINTF(Format,...) bsp_debug_c(LOG_LEVEL_FATAL,Format, ##__VA_ARGS__)
/// <summary>
/// 获取CRC8校验码
/// </summary>
/// <param name="pchMessage"></param>
/// <param name="dwLength"></param>
/// <returns></returns>
static uint8_t Get_CRC8_Check(uint8_t *pchMessage,uint16_t dwLength)
{
return crc_8(pchMessage,dwLength);
}
/// <summary>
/// 检验CRC8数据段
/// </summary>
/// <param name="pchMessage"></param>
/// <param name="dwLength"></param>
/// <returns></returns>
static uint8_t CRC8_Check_Sum(uint8_t *pchMessage,uint16_t dwLength)
{
uint8_t ucExpected = 0;
if ((pchMessage == 0) || (dwLength <= 2)) return 0;
ucExpected = Get_CRC8_Check(pchMessage, dwLength-1);
return (ucExpected == pchMessage[dwLength-1] );
}
/// <summary>
/// 获取CRC16校验码
/// </summary>
/// <param name="pchMessage"></param>
/// <param name="dwLength"></param>
/// <returns></returns>
static uint16_t Get_CRC16_Check(uint8_t *pchMessage,uint32_t dwLength)
{
return crc_16(pchMessage,dwLength);
}
/// <summary>
/// 检验CRC16数据段
/// </summary>
/// <param name="pchMessage"></param>
/// <param name="dwLength"></param>
/// <returns></returns>
static uint16_t CRC16_Check_Sum(uint8_t *pchMessage, uint32_t dwLength)
{
uint16_t wExpected = 0;
if ((pchMessage == 0) || (dwLength <= 2))
{
return 0;
}
wExpected = Get_CRC16_Check ( pchMessage, dwLength - 2);
return (((wExpected & 0xff) == pchMessage[dwLength - 2] )&& (((wExpected > 8) & 0xff) == pchMessage[dwLength - 1]));
}
/// <summary>
/// 检查帧头
/// </summary>
/// <param name="pData"></param>
/// <returns></returns>
static int check_protocol_heade(uint8_t *pData)
{
if(pData[0] == PROTOCOL_HEAD_ID)
{
if(CRC8_Check_Sum(&pData[0],4))
{
return PROTOCOL_RESULT_OK;
}
}
return PROTOCOL_RESULT_CHECK_HEAD_ERR;
}
/// <summary>
/// 方式一->初始化frame_struct 自动分配地址
/// </summary>
/// <param name="pFrameStruct"></param>
/// <param name="uLen"></param>
/// <returns></returns>
static int init_frame_struct(frame_struct* pFrameStruct,uint16_t uLen)
{
user_data_struct *pUserDataStruct = &pFrameStruct->frame_user;
if ((pUserDataStruct->cmd_data.pData == NULL))
{
if (uLen <= MAX_BUFFER_SIZE)
{
pUserDataStruct->cmd_data.max_data_len = uLen;
pUserDataStruct->cmd_data.pData = (uint32_t*) malloc(pUserDataStruct->cmd_data.max_data_len * (sizeof(uint32_t)));
memset(pUserDataStruct->cmd_data.pData, 0, pUserDataStruct->cmd_data.max_data_len * (sizeof(data_union)));
return PROTOCOL_RESULT_OK;
}
else
{
return PROTOCOL_RESULT_OUT_OF_LEN;
}
}
else
{
return PROTOCOL_RESULT_ERR;
}
}
/// <summary>
/// 方式二->初始化frame_struct 外部分配地址 该方式更方便修改数据
/// </summary>
/// <param name="pFrameStruct"></param>
/// <param name="pFrameSt"></param>
/// <param name="uLen"></param>
/// <returns></returns>
static int init_frame_pointer(frame_struct* pFrameStruct,void *pFrameSt,uint16_t uLen)
{
user_data_struct *pUserDataStruct = &pFrameStruct->frame_user;
if ((pUserDataStruct->cmd_data.pData == NULL))
{
if (uLen <= MAX_BUFFER_SIZE)
{
pUserDataStruct->cmd_data.max_data_len = uLen;
pUserDataStruct->cmd_data.pData = (uint32_t *)pFrameSt;
// pUserDataStruct->cmd_data.pData = (uint32_t*) malloc(pUserDataStruct->cmd_data.max_data_len * (sizeof(uint32_t)));
memset(pUserDataStruct->cmd_data.pData, 0, pUserDataStruct->cmd_data.max_data_len * (sizeof(data_union)));
return PROTOCOL_RESULT_OK;
}
else
{
return PROTOCOL_RESULT_OUT_OF_LEN;
}
}
else
{
return PROTOCOL_RESULT_ERR;
}
}
/// <summary>
/// 方式一 ->初始化message_struct
/// </summary>
/// <param name="pMessageStruct"></param>
/// <param name="uLen"></param>
/// <returns></returns>
static int init_message_struct(message_struct* pMessageStruct,uint16_t uLen)
{
if ((pMessageStruct->pData == NULL))
{
if (uLen <= get_protocol_size(MAX_BUFFER_SIZE))
{
pMessageStruct->max_data_len = uLen;
pMessageStruct->pData = (uint8_t *)malloc(pMessageStruct->max_data_len * (sizeof(uint8_t)));
memset(pMessageStruct->pData, 0, pMessageStruct->max_data_len * (sizeof(uint8_t)));
return PROTOCOL_RESULT_OK;
}
else
{
return PROTOCOL_RESULT_OUT_OF_LEN;
}
}
else
{
return PROTOCOL_RESULT_ERR;
}
}
/// <summary>
/// 方式二 ->初始化message_struct
/// </summary>
/// <param name="pMessageStruct"></param>
/// <param name="pMessageSt"></param>
/// <param name="uLen"></param>
/// <returns></returns>
static int init_message_pointer(message_struct* pMessageStruct,void *pMessageSt,uint16_t uLen)
{
if ((pMessageStruct->pData == NULL))
{
if (uLen <= get_protocol_size(MAX_BUFFER_SIZE))
{
pMessageStruct->max_data_len = uLen;
pMessageStruct->pData = (uint8_t*)pMessageSt;
// pMessageStruct->pData = (uint8_t *)malloc(pMessageStruct->max_data_len * (sizeof(uint8_t)));
memset(pMessageStruct->pData, 0, pMessageStruct->max_data_len * (sizeof(uint8_t)));
return PROTOCOL_RESULT_OK;
}
else
{
return PROTOCOL_RESULT_OUT_OF_LEN;
}
}
else
{
return PROTOCOL_RESULT_ERR;
}
}
/// <summary>
/// 根据data_union长度计算数据帧长度,用于分配内存
/// </summary>
/// <param name="uLen"></param>
/// <returns></returns>
uint16_t get_protocol_size(uint16_t uLen)
{
return (uLen * sizeof(uint32_t) + PROTOCOL_DATA_OFFSET + 2);
}
/// <summary>
/// 方式一 初始化,并初始化内存
/// </summary>
/// <param name="pProtocol"></param>
/// <param name="uLen"></param>
void init_protocol(protocol_struct* pProtocol, uint16_t uLen)
{
/*初始化crc*/
init_crc16_tab();
/*分配内存空间*/
if (init_frame_struct(&pProtocol->frame_st, uLen) != 0)
{
PROTOCOL_ERROR_PRINTF("pProtocol init_frame_struct err!\n");
}
PROTOCOL_DEBUG_PRINTF("pProtocol init_frame_struct ok!\n");
if (init_message_struct(&pProtocol->message_st, get_protocol_size(uLen)) != 0)
{
PROTOCOL_ERROR_PRINTF("pProtocol init_message_struct err!\n");
}
PROTOCOL_DEBUG_PRINTF("pProtocol init_message_struct ok!\n");
}
/// <summary>
/// 方式二 外部预先分配好内存空间
/// </summary>
/// <param name="pProtocol"></param>
/// <param name="pFrameSt"></param>
/// <param name="pMessageSt"></param>
/// <param name="uLen"></param>
/// <returns></returns>
int init_protocol_pointer(protocol_struct* pProtocol, void *pFrameSt,void *pMessageSt,uint16_t uLen)
{
/*初始化crc*/
init_crc16_tab();
/*分配内存空间*/
if (init_frame_pointer(&pProtocol->frame_st,pFrameSt, uLen) != 0)
{
PROTOCOL_ERROR_PRINTF("pProtocol init_frame_struct err!\n");
return PROTOCOL_RESULT_ERR;
}
PROTOCOL_DEBUG_PRINTF("pProtocol init_frame_struct ok!\n");
if (init_message_pointer(&pProtocol->message_st,pMessageSt,get_protocol_size(uLen)) != 0)
{
PROTOCOL_ERROR_PRINTF("pProtocol init_message_struct err!\n");
return PROTOCOL_RESULT_ERR;
}
PROTOCOL_DEBUG_PRINTF("pProtocol init_message_struct ok!\n");
return PROTOCOL_RESULT_OK;
}
/// <summary>
/// 生成带发送的数据内容
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
int make_protocol(protocol_struct* pProtocol)
{
int ret = -1;
uint16_t pos_offset;
frame_struct *pFrameStruct = &pProtocol->frame_st;
message_struct *pMessageStruct = &pProtocol->message_st;
//更新头部数据
pFrameStruct->header.sof = PROTOCOL_HEAD_ID;
pFrameStruct->header.data_length = pFrameStruct->frame_user.cmd_data.data_len*sizeof(data_union);
pMessageStruct->pData[0] = pFrameStruct->header.sof;
pMessageStruct->pData[1] = (pFrameStruct->header.data_length)&0xff;
pMessageStruct->pData[2] = (pFrameStruct->header.data_length>8)&0xff;
pFrameStruct->header.crc_check = Get_CRC8_Check(&pMessageStruct->pData[0],3);
pMessageStruct->pData[3] = pFrameStruct->header.crc_check;
//更新设备类型
pMessageStruct->pData[4] = (pFrameStruct->frame_user.equipment_type)&0xff;
pMessageStruct->pData[5] = (pFrameStruct->frame_user.equipment_type>8)&0xff;
//更新设备ID
pMessageStruct->pData[6] = (pFrameStruct->frame_user.equipment_id)&0xff;
pMessageStruct->pData[7] = (pFrameStruct->frame_user.equipment_id >8)&0xff;
//更新设备ID
pMessageStruct->pData[8] = (pFrameStruct->frame_user.data_id) & 0xff;
pMessageStruct->pData[9] = (pFrameStruct->frame_user.data_id > 8) & 0xff;
if (pFrameStruct->frame_user.cmd_data.data_len <= pFrameStruct->frame_user.cmd_data.max_data_len)
{
pos_offset = pFrameStruct->frame_user.cmd_data.data_len * sizeof(data_union);
//复制内存
ret = (int)memcpy(&pMessageStruct->pData[PROTOCOL_DATA_OFFSET],&pFrameStruct->frame_user.cmd_data.pData[0], pos_offset);
pos_offset = pFrameStruct->frame_user.cmd_data.data_len * sizeof(data_union)+PROTOCOL_DATA_OFFSET;
//帧尾校验值
pFrameStruct->frame_tail = Get_CRC16_Check(&pMessageStruct->pData[0], pos_offset);
pMessageStruct->pData[pos_offset] = (pFrameStruct->frame_tail)&0XFF;
pMessageStruct->pData[pos_offset+1] = (pFrameStruct->frame_tail>8) & 0XFF;
pMessageStruct->data_len = pos_offset + 2;
PROTOCOL_DEBUG_PRINTF("make_protocol->data_len[%d] > max_data_len[%d]!\n",
pFrameStruct->frame_user.cmd_data.data_len,
pFrameStruct->frame_user.cmd_data.max_data_len);
return PROTOCOL_RESULT_OK;
}
else
{
PROTOCOL_ERROR_PRINTF("make_protocol->data_len[%d] > max_data_len[%d]!\n",
pFrameStruct->frame_user.cmd_data.data_len,
pFrameStruct->frame_user.cmd_data.max_data_len);
return PROTOCOL_RESULT_OUT_OF_LEN;
}
PROTOCOL_DEBUG_PRINTF("make_protocol->data_len[%d],max_data_len[%d]!\n",
pFrameStruct->frame_user.cmd_data.data_len,
pFrameStruct->frame_user.cmd_data.max_data_len);
return PROTOCOL_RESULT_ERR;
}
/// <summary>
/// 解析接收到的数据
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
int parse_protocol(protocol_struct* pProtocol,uint16_t parseDataLen)
{
//解析数据,使用前需提前缓冲 pProtocol->message_st.pData
int ret = -1;
uint16_t pos_offset;
frame_struct* pFrameStruct = &pProtocol->frame_st;
message_struct* pMessageStruct = &pProtocol->message_st;
if (check_protocol_heade(pMessageStruct->pData) == PROTOCOL_RESULT_OK)
{
//更新帧头数据
pFrameStruct->header.sof = pMessageStruct->pData[0];
//获取data段的数据长度
pFrameStruct->header.data_length = (pMessageStruct->pData[2] << 8) | (pMessageStruct->pData[1]);
pFrameStruct->header.crc_check = pMessageStruct->pData[3];
//获取此次数据包长度
pMessageStruct->data_len = pFrameStruct->header.data_length + PROTOCOL_DATA_OFFSET + 2;
//计算解析后得到的 data_union 数据长度
pFrameStruct->frame_user.cmd_data.data_len = (pFrameStruct->header.data_length) / sizeof(data_union);
if(pMessageStruct->data_len<=parseDataLen)
{
if (pMessageStruct->data_len <= pMessageStruct->max_data_len)
{
if(CRC16_Check_Sum(&pMessageStruct->pData[0], pMessageStruct->data_len) != 0)
{
pFrameStruct->frame_user.equipment_type = (pMessageStruct->pData[5]<<8) | (pMessageStruct->pData[4]);
pFrameStruct->frame_user.equipment_id = (pMessageStruct->pData[7] << 8) | (pMessageStruct->pData[6]);
pFrameStruct->frame_user.data_id = (pMessageStruct->pData[9] << 8) | (pMessageStruct->pData[8]);
//拷贝 data段 指定长度数据
ret = (int)memcpy(&pFrameStruct->frame_user.cmd_data.pData[0], &pMessageStruct->pData[PROTOCOL_DATA_OFFSET], pFrameStruct->header.data_length);
pos_offset = pFrameStruct->header.data_length + PROTOCOL_DATA_OFFSET;
pFrameStruct->frame_tail = (pMessageStruct->pData[pos_offset+1] << 8) | (pMessageStruct->pData[pos_offset]);
return PROTOCOL_RESULT_OK;
}
else
{
//待解析BUFF超过预定解析数据容量,避免内存越界
PROTOCOL_ERROR_PRINTF("parse_protocol->CRC16_Check_Sum err!\n");
return PROTOCOL_RESULT_CHECK_FRAME_ERR;
}
}
else
{
//待解析BUFF超过预定解析数据容量,避免内存越界
PROTOCOL_ERROR_PRINTF("parse_protocol->data_len[%d] > max_data_len[%d]!\n",
pMessageStruct->data_len,
pMessageStruct->max_data_len);
return PROTOCOL_RESULT_OUT_OF_LEN;
}
}
else
{
//通过包头计算,还未收到完整的数据包
// PROTOCOL_ERROR_PRINTF("parse_protocol->data_len[%d] > max_data_len[%d]!\n",
// pMessageStruct->data_len,
// pMessageStruct->max_data_len);
return PROTOCOL_RESULT_OUT_OF_LEN;
}
}
else
{
//待解析BUFF超过预定解析数据容量,避免内存越界
PROTOCOL_ERROR_PRINTF("parse_protocol->check_protocol_heade err!\n");
return PROTOCOL_RESULT_CHECK_HEAD_ERR;
}
PROTOCOL_DEBUG_PRINTF("parse_protocol->check_protocol_heade ok!\n");
return PROTOCOL_RESULT_ERR;
}
/// <summary>
/// 反初始化,释放内存,如果是方式二创建然后反初始化,请注意规避野指针
/// </summary>
/// <param name="pProtocol"></param>
void deinit_protocol(protocol_struct* pProtocol)
{
//指针为空不需要判断
free(pProtocol->frame_st.frame_user.cmd_data.pData);
free(pProtocol->message_st.pData);
//memset(pProtocol, 0, sizeof(protocol_struct));
pProtocol->frame_st.frame_user.cmd_data.pData = NULL;
pProtocol->message_st.pData = NULL;
}
/// <summary>
/// 提供给用户,直接操作数据,但是请注意不要超过数据长度,避免内存越界
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
const user_data_struct * get_user_data_point(protocol_struct* pProtocol)
{
return &pProtocol->frame_st.frame_user;
}
/// <summary>
/// 提供给用户,直接操作数据,但是请注意不要超过数据长度,避免内存越界
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
const message_struct * get_message_point(protocol_struct* pProtocol)
{
return &pProtocol->message_st;
}
#ifndef _BSP_PROTOCOL_H_
#define _BSP_PROTOCOL_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define PROTOCOL_HEAD_ID 0XA5
#define PROTOCOL_HEAD_LEN 4
#define PROTOCOL_DATA_OFFSET 10
#define MAX_BUFFER_SIZE 128
#define LOG_LEVEL_DEBUG (1<<0)
#define LOG_LEVEL_INFO_ (1<<1)
#define LOG_LEVEL_WARN_ (1<<2)
#define LOG_LEVEL_ERROR (1<<3)
#define LOG_LEVEL_FATAL (1<<4)
typedef enum
{
PROTOCOL_RESULT_OK = 0,
PROTOCOL_RESULT_ERR = 1,
PROTOCOL_RESULT_CHECK_HEAD_ERR = 2,
PROTOCOL_RESULT_CHECK_FRAME_ERR = 3,
PROTOCOL_RESULT_OUT_OF_LEN = 4,
}protocol_result;
//32位
typedef union
{
char data_char[4];
uint8_t data_u8[4];
int16_t data_int16[2];
uint16_t data_u16[2];
int32_t data_int32;
uint32_t data_u32;
float data_float;
}data_union;
typedef struct
{
/*字节偏移 4 --> 字节大小 2*/
uint16_t equipment_type; //设备类型
/*字节偏移 6 --> 字节大小 2*/
uint16_t equipment_id; //设备ID
/*字节偏移 8 --> 字节大小 data_len+2 */
uint16_t data_id; //数据ID
struct
{
uint16_t data_len; //data_union数据长度
uint16_t max_data_len; //数据最长长度
uint32_t* pData; //数据(data_union),推荐使用 data_union * pDataUnion = &pData[];
}cmd_data;
} user_data_struct;//数据内容
typedef struct
{
uint8_t* pData; //数据
uint16_t data_len; //数据总长度
uint16_t max_data_len; //数据最长长度
} message_struct;//消息数据
typedef struct
{
/*字节偏移 0 --> 字节大小 4*/
struct
{
uint8_t sof; //固定帧头
uint16_t data_length; //数据长度
uint8_t crc_check; //帧头CRC校验
} header; //数据帧头
user_data_struct frame_user;//用户数据
uint16_t frame_tail;//帧尾CRC校验
} frame_struct;
typedef struct
{
frame_struct frame_st; //原始数据或解析后的数据
message_struct message_st; //有待解析或生成的数据
} protocol_struct;
/// <summary>
/// 根据data_union长度计算数据帧长度,用于分配内存
/// </summary>
/// <param name="uLen"></param>
/// <returns></returns>
uint16_t get_protocol_size(uint16_t uLen);
/// <summary>
/// 方式一 初始化,并初始化内存
/// </summary>
/// <param name="pProtocol"></param>
/// <param name="uLen"></param>
void init_protocol(protocol_struct* pProtocol, uint16_t uLen);
/// <summary>
/// 方式二 外部预先分配好内存空间
/// </summary>
/// <param name="pProtocol"></param>
/// <param name="pFrameSt"></param>
/// <param name="pMessageSt"></param>
/// <param name="uLen"></param>
/// <returns></returns>
int init_protocol_pointer(protocol_struct* pProtocol, void* pFrameSt, void* pMessageSt, uint16_t uLen);
/// <summary>
/// 生成带发送的数据内容
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
int make_protocol(protocol_struct* pProtocol);
/// <summary>
/// 解析接收到的数据
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
int parse_protocol(protocol_struct* pProtocol,uint16_t parseDataLen);
/// <summary>
/// 反初始化,释放内存,如果是方式二创建然后反初始化,请注意规避野指针
/// </summary>
/// <param name="pProtocol"></param>
void deinit_protocol(protocol_struct* pProtocol);
/// <summary>
/// 提供给用户,直接操作数据,但是请注意不要超过数据长度,避免内存越界
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
const user_data_struct* get_user_data_point(protocol_struct* pProtocol);
/// <summary>
/// 提供给用户,直接操作数据,但是请注意不要超过数据长度,避免内存越界
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
const message_struct* get_message_point(protocol_struct* pProtocol);
#ifdef __cplusplus
}
#endif
#endif
通信协议依赖文件(CRC8,CRC16算法)(适用于C、C++)
bsp_crc16.h
bsp_crc16.c 可改为 bsp_crc16.cpp
bsp_crc8.h
bsp_crc8.c 可改为 bsp_crc8.cpp
bsp_protocol.h
bsp_protocol.c 可改为 bsp_protocol.cpp
bsp_protocol_class.h
bsp_protocol_class.cpp