# ProtocolLib **Repository Path**: veis/protocol-lib ## Basic Information - **Project Name**: ProtocolLib - **Description**: 用于串口字节流协议解析 - **Primary Language**: Unknown - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2024-10-16 - **Last Updated**: 2024-10-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 通信协议解析库说明 ## 一、概述 用于上位机通讯协议解析,协议格式:AA len type id data 校验 | 帧头(1byte) | 长度(1byte) | 协议类型(1byte) | 命令ID(1byte) | 数据(xbyte) | 校验和(1byte) | | ----------- | ----------- | --------------- | ------------- | ----------- | ------------- | | AA | x | x | x | x | 异或校验和 | > 固定帧头:0xAA > > 校验和:从AA到校验和之前的所有字节进行异或校验 ## 二、头文件 ```c++ #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 * * Example: * #include * #include "./protocol.h" * #include * * 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; * } * ***************************************************************************************** */ #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 ``` ## 三、调用时序图 ![image-20241012195003893](./assets/image-20241012195003893.png) ## 四、库编译说明 - 安装Qt Creator,创建动态库工程 - 添加vpprotocollib.cpp和vpprotocollib.h文件到工程 - 使用**qmake**选择released和debug分别编译项目,生成库文件(编译输出的文件如下图所示) ![image-20241012195026416](./assets/image-20241012195026416.png) ## 五、调用示例 ```c++ #include "vpprotocollib.h" #include #include 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; } ```