# zh_info_tool **Repository Path**: Createtree/zh_info_tool ## Basic Information - **Project Name**: zh_info_tool - **Description**: 信息工具箱,不定期维护 - **Primary Language**: C - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-06-29 - **Last Updated**: 2022-07-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # zh_info_tool > `zh_info_tool` 是一个信息处理库,提供了有关字符处理和数据处理的API。 **目录** - [API手册](#API手册) - [一个例子](#一个例子) - [另一个例子](#另一个例子) > `TEST.c` 是一个测试文件,包含main函数 > 将`zh_info_tool.c`和`zh_info_tool.h`移植到你的工程即可使用 ## API手册 ```c char Format_Buffer[128]; /** * @brief 字符参数格式化(用法和printf一样,返回格式化的字符串) * @param 包含格式的字符串 * @param 要格式化的数据 * @note 设置合适的Format_Buffer大小 * @retval 格式化后的字符指针 **/ char* FormatToStr(const char* fmt,...) ``` ```c /** * @brief 将一个缓存中的字符串分割为多个子串 * @param pSt : Sentence_t句柄 * @param str : 要分割的字符串缓存 * @param size: 字符串缓存的尺寸 * @note 请注意该函数会读写字符串缓存,不能传入const char* * @note 请先使用St_CHECK(pSt)检查再调用该函数 * @retval 解析出的数据个数 */ uint8_t St_Split(Sentence_t *pSt, uint8_t *str, uint8_t Size) ``` ```c /** * @brief 设置待分析数据的地址 * @param Sentence句柄 * @param 首地址 * @param 尺寸 * @note 需要检测用Split解析多个地址时使用该函数更 * 改分析的地址。切换后当前结果丢失需要重新分析 * @retval None **/ void St_SetAddress(Sentence_t *pSt, uint8_t* Address, uint8_t Size) ``` ```c /** * @brief 在缓存中查找数据段的位置 * @param pdata 查找的缓存 * @param Size 缓存长度 * @param AimData 搜索的字节串目标 * @param AimSize 目标的长度 * @retval [>=0:帧头偏移值][-1:不存在][-2:参数错误] **/ int St_MemSearch(uint8_t* pdata, uint8_t Size, uint8_t* AimData, uint8_t AimSize) ``` ### 以下宏配合Split 使用 ```c# /** * @brief 获取分割后某段的数据长度 * @param Sentence句柄 * @param 下标 * @retval 帧数据长度 **/ #define St_GetLen(pSt,index) /** * @brief 获取分割后的十六进制数据 * @param Sentence句柄 * @param 下标 * @retval 字节指针 **/ #define St_GetHex(pSt,index) /** * @brief 获取分割后的字符 * @param Sentence句柄 * @param 下标 * @retval 字符指针 **/ #define St_GetStr(pSt,index) /** * @brief 获取分割后的整型数据 * @param Sentence句柄 * @param 下标 * @retval 整型数据 **/ #define St_GetInt(pSt,index) /** * @brief 获取分割后的浮点型数据 * @param Sentence句柄 * @param 下标 * @retval 浮点数据 **/ #define St_GetDob(pSt,index) ``` ### 以下宏是解析数据的快捷方式 ```c# #define St_ISHEAD(pSt,str) /* 判断str指向的数据是否为帧头 */ #define St_FIND_HEAD(pSt) /* 在一大串数据中寻找帧头 */ #define St_FIND_TAIL(pSt) /* 在一大串数据中寻找帧尾 */ #define St_CHECK(pSt) /* 检查St对象中是否出现了完整帧数据*/ #define St_SPLIT(pSt) /* 检查是否有帧数据,并且将St对象中的帧数据分割为多个数据段 */ ``` ## 一个例子 > 假设要解析一帧数据,格式为 \[PM:][ data0 ],...\[datan][..] ``` "PM:1,a.." 表示让推杆1前进 "PM:2,b.." 表示让推杆2后退 "PM:1,s.." 表示让推杆1停止 "PM:2,ta,1000.." 表设定推杆2的前进时间 "PM:1,tb,1000.." 表设定推杆1的后退时间 ``` 根据上面命令的形式可以知道 `PM:` 为帧头,`..` 为帧尾,`,`为分隔符,`[data0~datan]`为数据 其中`[data0]` 为推杆电机编号,`[data1]` 为命令名称,`[data2]` 表示命令的参数(可以没有) ### 1.创建一个Sentence_t对象 ```c //1-1定义全局变量 char UART1_RBuf[128];//串口接收缓存 char PMSt_Buf[10];//解析器的缓存,Split分割后的数据的引索存储到这里 Sentence_t PMSt;//全局的St对象 //1-2初始化函数 void Sentence_Init(void) { StInit(&PMSt,"PM:", ',', "..", PMSt_Buf, UART1_RBuf); //使用前请在zh_info_tool.h中定义malloc函数分配内存 } ``` 如果使用C99编译器可以用更简单的方式来初始化 ```c //静态创建一个全局变量并初始化,不再需要动态内存分配 char UART1_RBuf[128];//串口接收缓存 char PMSt_Buf[10];//解析器的缓存,Split分割后的数据的引索存储到这里 Sentence_t pPMSt = StCreate("PM:", ',', "..", PMSt_Buf, UART1_RBuf); ``` ### 2.根据需求编写命令应用代码 ```c void PushMotorCMD(pushMotor_t* p)//pushMotor_t为推杆的句柄 { uint8_t* pstr = St_GetStr(&PMSt,1); switch (*pstr) { case 'a'://前进 PM_GOAhead(p); break; case 'b'://后退 PM_GOBack(p); break; case 's': PM_Stop(p); case 't'://ta/tb if(*(pstr+1) == 'a')//前进时间 { PM_SET_AheadTime(p,St_GetInt(&PMSt,2)); } else if(*(pstr+1) == 'b')//后退时间 { PM_SET_BackTime(p,St_GetInt(&PMSt,2)); } break; } } ``` ### 3.在串口处理任务中添加 ```c void UART1_Handle() { if(UART1_RFlag == 1)//在串口接收中断中置位接收标志,然后在任务中处理 { UART1_RFlag = 0;//清空标志位 if(St_SPLIT(&PMSt))//检测是否有帧头帧尾,有则自动解析 { switch(St_GetInt(&PMSt,0))//第一个参数用来判断控制的对象 { case 1: PushMotorCMD(&PM1);Beep(50);break;//推杆1,解析时蜂鸣器叫一声 case 2: PushMotorCMD(&PM2);Beep(50);break;//推杆2 //由此可见将命令函数单独写添加更多推杆电机将会更简单 } } } } ``` ## 另一个例子 解析下面的命令: ```C /*--------------------------------- 有四个PID_t对象,分别是pidx{x=1,2,3,4} 设置PID值的API为PID_SET_X(PID_t*, float){X=P,I,D} 访问方式为pidx.p,pidx.i,pidx.d ----------------------------------*/ "PID:1,P,2.1.." //设置PID1的P值 "PID:2,I,0.03.."//设置PID2的I值 "PID:3,D,0.2.." //设置PID3的D值 "PID:4,a,2.1,0.03,0.2.."//同时设置PID4的P、I、D值 "PID:1,GetPID.." //获取当前的PID1的P、I、D值 "PID:2,GetOut.." //获取当前PID2的输出值 ``` ### 定义变量 ```C //定义全局变量 char UART1_RBuf[128] = {0};//串口接收缓存 char PIDSt_Buf[10] = {0};//解析器的缓存,Split分割后的数据的引索存储到这里 //创建全局的St对象并初始化 Sentence_t PIDSt = StCreate("PID:", ',', "..", PIDSt_Buf, UART1_RBuf); ``` ### 命令解析程序 ```C void PID_Ctrl_CMD(PID_t* pid)//PID_t为PID的句柄 { uint8_t* pstr = St_GetStr(&PIDSt,1); switch (*pstr) { case 'p'://设置P的值 PID_SET_P(pid, St_GetDob(&PIDSt,2)); break; case 'i'://设置I的值 PID_SET_I(pid, St_GetDob(&PIDSt,2)); break; case 'd'://设置D的值 PID_SET_D(pid, St_GetDob(&PIDSt,2)); break; case 'a'://同时设置PID PID_SET_P(pid, St_GetDob(&PIDSt,2)); PID_SET_I(pid, St_GetDob(&PIDSt,3)); PID_SET_D(pid, St_GetDob(&PIDSt,4)); break; case 'G': if(strcmp(pstr,"GetPID") == 0)//获取当前P、I、D值 printf("pid:%f,%f,%f\n\r", pid.p, pid.i, pid.d); if(strcmp(pstr,"GetOut") == 0)//获取PID输出值 printf("pid:%f\n\r", pid.out); break; } } ``` ### 串口数据处理 ```C void UART1_Handle() { if(UART1_RFlag == 1)//在串口接收中断中置位接收标志,然后在任务中处理 { UART1_RFlag = 0;//清空标志位 if(St_SPLIT(&PIDSt))//检测是否有帧头帧尾,有则自动解析 { switch(St_GetInt(&PIDSt,0))//第一个参数用来判断控制的对象 { case 1: PID_Ctrl_CMD(&pid1);Beep(50);break; case 2: PID_Ctrl_CMD(&pid2);Beep(50);break; case 3: PID_Ctrl_CMD(&pid3);Beep(50);break; case 4: PID_Ctrl_CMD(&pid4);Beep(50);break; } } } } ```