欢迎来到我的博客https://www.cnblogs.com/veis/

https://www.cnblogs.com/veis/p/14182037.html

串口HEX字节流交互协议解析库分享

通信协议解析库说明

一、概述

用于上位机串口通讯协议解析,协议格式:AA len type id data 校验

帧头(1byte) 长度(1byte) 协议类型(1byte) 命令ID(1byte) 数据(xbyte) 校验和(1byte)
AA x x x x 异或校验和

固定帧头:0xAA

校验和:从AA到校验和之前的所有字节进行异或校验

二、头文件

#ifndef VPPROTOCOLLIB_H
#define VPPROTOCOLLIB_H

/**
 ****************************************************************************************
 *
 * @file protocol.h
 *
 * @brief Attribute Protocol
 *
 ****************************************************************************************
 * @attention
  #####Copyright (c) 2024 veis
  All rights reserved.
 *****************************************************************************************
 *************************************Instructions for use*********************************
    * Step 1: Call Protocol_Init to Initialization the lib.
    * Step 2: Define a sMsgType_t variable to bind the message and the callback function.
    * Step 3: Use the Protocol_RegisterCmdCB function to register the message and the callback function.
    * Step 4: Timely invocation of Protocol_SetPacket for data packet grouping and parsing in tasks
    *         receiving byte streams.
    * PS:
    *       (1)Can use the Protocol_GetVersion to get the lib version
    *       (2)If use dynamic memory, should use Protocol_FreeMsgPool to free memory
    *
 *****************************************************************************************
 */

#ifdef __cplusplus
extern "C" {
#endif

// 使用静态内存池
#define DYNAMIC_MEM_POOL_CONFIG 0x01
// 使用静态内存池
#define STATIC_MEM_POOL_CONFIG  0x02

// 内存池类型
#define MEM_POOL_TYPE DYNAMIC_MEM_POOL_CONFIG
// 消息内存池大小
#define MSG_POOL_SIZE   (500)

#if (MEM_POOL_TYPE != STATIC_MEM_POOL_CONFIG) && (MEM_POOL_TYPE != DYNAMIC_MEM_POOL_CONFIG)
#error "please set the MEM_POOL_TYPE into a vaild vaule!"
#endif

/**
 *  协议通用部分
 */
#define FRAME_HEAD              0xAA // 帧头
#define HEAD_INX                0    // 帧头数组下标偏移
#define DATA_LEN_IDX            1    // 长度数组下标偏移
#define PROTOCOL_ID_IDX         2    // 协议标识数组下标偏移
#define FUNC_ID_IDX             3    // 功能字数组下标偏移
#define MIN_FRAME_LEN           5    // 最小帧长度

// 消息处理回调函数类型
typedef void (*pMsgFuncHandle)(void *param, unsigned int len);

// 消息池状态码
typedef enum
{
    MSG_POOL_OK = 0,       //正常
    MSG_POOL_ERROR,        //错误
    MSG_POOL_OVERLOAD,     //已满
    MSG_POOL_REPEAT,       //重复
} MsgPoolStatus_t;

// 数据包类型
typedef struct __attribute__((aligned(4))) // 4字节对齐,加速32位操作系统下的访问
{
    unsigned char m_head;       // 协议头
    unsigned char m_len;        // 数据长度
    unsigned char m_cmdType;    // 协议标识
    unsigned char m_cmdID;      // 命令ID
    unsigned char *m_pbuf;      // 数据缓冲区地址
    unsigned char m_checksum;   // 校验和
} sDataPacketType_t;

// 消息类型
typedef struct
{
    unsigned char m_cmdType;    // 协议标识
    unsigned char m_cmdID;      // 命令ID
    pMsgFuncHandle m_cb;        // 回调函数
} sMsgType_t;


/**
 * @brief 初始化协议解析需要用到的资源
 * @return 0:操作成功,-1:操作失败
 */
int Protocol_Init(void);

/**
 * @brief 绑定注册命令和回调函数
 * @param pcb 控制块
 * @return 见@MsgPoolStatus_t
 */
int Protocol_RegisterCmdCB(sMsgType_t *pcb);

/**
 * @brief 从命令池里面删除命令,需要通过协议标识+命令ID来索引移除
 * @param cmdType 需要移除的协议标识
 * @param cmdID 需要移除的命令ID
 * @return
 */
int Protocol_RemoveCmd(unsigned char cmdType, unsigned char cmdID);

/**
 * @brief 把串口字节流转为数据包压入队列
 * @param pbuf 字节流缓冲区地址
 * @param len 字节个数
 * @return 0:操作成功,-1:操作失败
 */
int Protocol_SetPacket(unsigned char *pbuf, unsigned int len);

/**
 * @brief 释放内存池
 */
void Protocol_FreeMsgPool(void);

/**
 * @brief 获取版本
 * @return 返回版本的字符串
 */
char *Protocol_GetVersion(void);

#ifdef __cplusplus
}
#endif


#endif // VPPROTOCOLLIB_H

三、调用时序图

四、库编译说明

  • 安装Qt Creator,创建动态库工程

  • 添加vpprotocollib.cpp和vpprotocollib.h文件到工程

  • 使用qmake选择released和debug分别编译项目,生成库文件(编译输出的文件如下图所示)

五、调用示例

#include "vpprotocollib.h"
#include <windows.h>
#include <iostream>

static void test_cb(void *param, unsigned int len)
{
   std::cout << "testcb be call!" << std::endl;
}

static void test_cb1(void *param, unsigned int len)
{
   unsigned char *pdata = (unsigned char *)param;
   int i;

   for(i = 0; i < len; i++)
   {
       printf("0x%02x ", pdata[i]);
   }
   std::cout << std::endl;
   std::cout << "testcb1 be call!" << std::endl;
}

int main(int argc, char *argv[])
{
    unsigned char test_buf[] = "\xAA\x02\x9F\x80\xB7\x55\xAA\x04\x9F\x01\x02\x03\x31"; // TEST BUFFER
    sMsgType_t msg = {.m_cmdType = 0x9f, .m_cmdID = 0x80, .m_cb = test_cb};
    sMsgType_t msg2 = {.m_cmdType = 0x9f, .m_cmdID = 0x01, .m_cb = test_cb1};

    Protocol_Init();
    Protocol_RegisterCmdCB(&msg);
    Protocol_RegisterCmdCB(&msg2);

    std::cout << Protocol_GetVersion() << std::endl;

    while(true)
    {
        Protocol_SetPacket(test_buf, sizeof(test_buf));
        Sleep(1000);
    }
    return 0;
}

库源码:https://gitee.com/veis/protocol-lib

posted @ 2024-10-16 11:47  veis  阅读(38)  评论(0编辑  收藏  举报