Seasky协议V2

[!NOTE|style:callout] 串口通信协议,包含帧头CRC8校验以及数据的整包校验,可以实现不定长收发数据(例如RoboMaster中可用于和视觉双方通信,以及其他自研模块)

1. 接口协议说明

以下所有低位在前发送

1.1. 通信协议格式

帧头
设备类型
设备ID
数据ID
帧尾
protocol_header(4-byte) equipment_type(2-byte) equipment_id (2-byte) data_id(2-byte) frame_tail(2-byte,CRC16,整包校验)

1.2. 帧头详细定义

帧头
偏移位置
字节大小
内容
sof(CMD) 0 1 数据帧起始字节,固定值为 0xA5
data_length 1 2 数据帧中 data 的长度
crc_check 3 1 帧头CRC校验
数据包长度 = data_length*4+(10+2);

1.3. equipment_type 设备类型-说明(字节偏移 4,字节大小 2)

命令码
数据段长度
功能说明
0x0001(可修改) 1 KmdFoc
0x0002 2 ...

1.4. equipment_id 设备ID-说明(字节偏移 6,字节大小 2)

1.5. data_id 数据ID-说明(字节偏移 8,字节大小 2)

1.6. 数据段 data (字节偏移 10, n-byte )

1.7. frame_tail ( CRC16 ,整包校验)

1.8. 准备使用

/// <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);
}

1.9. 使用待发送数据更新获取发送数据帧

/// <summary>
/// 生成带发送的数据内容
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
int make_protocol(protocol_struct* pProtocol);

[!TIP|style:callout] 生成发送数据前请准备好数据源,并传入数据源有效数据长度,你可以使用以下方式将自己的数据备份到解析缓冲区

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;
}

1.10. 接收数据

/// <summary>
/// 解析接收到的数据
/// </summary>
/// <param name="pProtocol"></param>
/// <returns></returns>
int parse_protocol(protocol_struct* pProtocol,uint16_t parseDataLen);

[!TIP|style:callout] 解析数据前请准备好数据源,并传入数据源有效数据长度,你可以使用以下方式将自己的数据备份到解析缓冲区

memcpy(pRxBuffer,pData,uLen);                   //整体待解析数据长度
memcpy(pRxProtocol.message_st.pData,pData,uLen);//整体待解析数据长度,对于隐式初始化分配内存可用

[!TIP|style:callout] 使用说明,首先将接收数据存储于 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;
}

1.11. 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

1.12. 通信协议依赖文件(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

results matching ""

    No results matching ""