借助五一假期,写了一个串口通信协议基础库,虽然写着适用于单片机,但实际上并不限制具体的硬件平台。

特点如下:

  • 不涉及到具体硬件,libserial_protocol 纯软件协议,与具体硬件分离。
  • 内存空间占用可控,libserial_protocol 支持动静态内存,内存空间可控。
  • 接口简单容易复用,libserial_protocol 采用面向对象方式实现,提供大小数据量解码方式。

源码仓库:

gitee: libserial_protocol: 适用于单片机的串口通信协议基础库

github: https://github.com/lovemengx/libserial_protocol

一、接口定义

// 缓存数据结构(编解码不能同时使用同一块缓存)
typedef struct{
unsigned char *buf; // 缓存位置, 由用户指向一块可用的内存空间
unsigned int total; // 缓存大小, 标明该内存空间的总长度
}libserial_protocol_buf_t; /*---------------------------------------------------------------------
* 函数: libserial_protocol_create
* 功能: 使用接口内部申请指定可用大小的空间
* 参数: size: 申请可用缓冲区大小
* 返回: NULL: 申请内存空间失败 >0: 申请成功
* 备注: 接口内部会多申请内部数据结构所需的空间大小
*---------------------------------------------------------------------*/
libserial_protocol_buf_t *libserial_protocol_create(unsigned int size); /*---------------------------------------------------------------------
* 函数: libserial_protocol_release
* 功能: 释放接口内部申请的内存空间
* 参数: splbuf: 由 libserial_protocol_create() 创建的内存空间
* 返回: 0: 不满足最小长度要求 >0: 可供用户使用的大小
*---------------------------------------------------------------------*/
void libserial_protocol_release(libserial_protocol_buf_t *splbuf); /*---------------------------------------------------------------------
* 函数: libserial_protocol_internal_size
* 功能: 返回内部数据结构占用字节数
* 参数:
* 返回: 返回内部数据结构占用字节数
*---------------------------------------------------------------------*/
unsigned int libserial_protocol_internal_size(); /*---------------------------------------------------------------------
* 函数: libserial_protocol_init
* 功能: 使用用户提供的或创建接口的缓冲区, 初始化内部数据结构
* 参数: splbuf: 缓冲区 size: 缓冲区大小
* 返回: 0: 不满足最小长度要求 >0: 可供用户使用的大小
*---------------------------------------------------------------------*/
unsigned int libserial_protocol_init(libserial_protocol_buf_t *splbuf); /*---------------------------------------------------------------------
* 函数: libserial_protocol_reset
* 功能: 重置解码器
* 参数: splbuf: 缓冲区
* 返回: 无返回值
*---------------------------------------------------------------------*/
void libserial_protocol_decode_reset(libserial_protocol_buf_t *splbuf); /*---------------------------------------------------------------------
* 函数: libserial_protocol_decode
* 功能: 解码数据
* 参数: splbuf: 缓冲区 indata: 输入数据 dalen: 解成功的数据长度
* 返回: 0: 正在解码 1:解码成功 -1: 校验失败
*---------------------------------------------------------------------*/
int libserial_protocol_decode(libserial_protocol_buf_t *splbuf, unsigned char indata, unsigned int *dalen); /*---------------------------------------------------------------------
* 函数: libserial_protocol_decode_find
* 功能: 寻找数据区域(适合较大数据量)
* 参数: splbuf: 缓冲区 indata: 输入数据
* 返回: 0: 正在寻找 >0: 数据区域长度
*---------------------------------------------------------------------*/
unsigned int libserial_protocol_decode_find(libserial_protocol_buf_t *splbuf, unsigned char indata); /*---------------------------------------------------------------------
* 函数: libserial_protocol_decode_copy
* 功能: 拷贝数据区域(适合较大数据量)
* 参数: splbuf: 缓冲区 indata: 输入数据 dalen: 输入的数据长度
* 返回: 0: 完成拷贝 -1: 校验失败 -2: 数据长度或解码状态错误
*---------------------------------------------------------------------*/
int libserial_protocol_decode_copy(libserial_protocol_buf_t *splbuf, unsigned char *indata, unsigned int len); /*---------------------------------------------------------------------
* 函数: libserial_protocol_encode
* 功能: 数据编码
* 参数: splbuf: 缓冲区 indata: 输入数据 dalen: 输入的数据长度
* 返回: 0: 数据长度不合法 >0: 编码后的数据长度
*---------------------------------------------------------------------*/
unsigned int libserial_protocol_encode(libserial_protocol_buf_t *splbuf, const void *indata, unsigned int dalen);

二、示例代码

#include <stdio.h>
#include "libserial_protocol.h" #define iprintf(format,...) printf("[inf]%s():%05d " format , __func__, __LINE__,##__VA_ARGS__) /*---------------------------------------------------------------------
* 函数: static_libserial_protocol
* 功能: 演示采用静态内存方式进行两种方法解码
*---------------------------------------------------------------------*/
void static_libserial_protocol(const char *data, unsigned int length)
{
int result = 0;
unsigned int i = 0;
unsigned char buf1[512], buf2[512];
libserial_protocol_buf_t splbuf1, splbuf2; // 使用静态内存
splbuf1.buf = buf1;
splbuf2.buf = buf2;
splbuf1.total = sizeof(buf1);
splbuf2.total = sizeof(buf2); // 初始化内部数据结构
unsigned int avail1 = libserial_protocol_init(&splbuf1);
unsigned int avail2 = libserial_protocol_init(&splbuf2);
iprintf("avail1:%d avail2:%d\n", avail1, avail2); // 对数据进行编码
unsigned int enbyte = libserial_protocol_encode(&splbuf1, data, length);
iprintf("enbyte:%d datalen:%d\n", enbyte, length); // 使用最简单的方式解码, 适合小数据量
unsigned int debyte = 0x00;
for (i = 0; i < enbyte; i++) {
if ((result = libserial_protocol_decode(&splbuf2, splbuf1.buf[i], &debyte)) == 1) {
iprintf("simple mode: decode success debyte:%d data:[%s]\n", debyte, splbuf2.buf);
break;
}
} // 使用较高性能方式解码, 适合大数据量
for (i = 0; i < enbyte; i++){
if ((debyte = libserial_protocol_decode_find(&splbuf2, splbuf1.buf[i])) > 0) {
if (libserial_protocol_decode_copy(&splbuf2, splbuf1.buf + i + 1, debyte) == 0) {
iprintf("comple mode: decode success debyte:%d data:[%s]\n", debyte, splbuf2.buf);
}
break;
}
} iprintf("static run done...\n\n");
return;
} /*---------------------------------------------------------------------
* 函数: static_libserial_protocol
* 功能: 演示采用动态内存方式进行两种方法解码
*---------------------------------------------------------------------*/
void dynamic_libserial_protocol(const char* data, unsigned int length)
{
int result = 0;
unsigned int i = 0;
libserial_protocol_buf_t* splbuf1, * splbuf2; // 使用动态内存
splbuf1 = libserial_protocol_create(512);
splbuf2 = libserial_protocol_create(512);
if (!splbuf1 || !splbuf2) {
iprintf("create dynamic failed.\n");
libserial_protocol_release(splbuf1);
libserial_protocol_release(splbuf2);
return ;
} // 初始化内部数据结构
unsigned int avail1 = libserial_protocol_init(splbuf1);
unsigned int avail2 = libserial_protocol_init(splbuf2);
iprintf("avail1:%d avail2:%d\n", avail1, avail2); // 对数据进行编码
unsigned int enbyte = libserial_protocol_encode(splbuf1, data, length);
iprintf("enbyte:%d datalen:%d\n", enbyte, length); // 使用最简单的方式解码, 适合小数据量
unsigned int debyte = 0x00;
for (i = 0; i < enbyte; i++) {
if ((result = libserial_protocol_decode(splbuf2, splbuf1->buf[i], &debyte)) == 1) {
iprintf("simple mode: decode success debyte:%d data:[%s]\n", debyte, splbuf2->buf);
break;
}
} // 使用较高性能方式解码, 适合大数据量
for (i = 0; i < enbyte; i++) {
if ((debyte = libserial_protocol_decode_find(splbuf2, splbuf1->buf[i])) > 0) {
if (libserial_protocol_decode_copy(splbuf2, splbuf1->buf + i + 1, debyte) == 0) {
iprintf("comple mode: decode success debyte:%d data:[%s]\n", debyte, splbuf2->buf);
}
break;
}
} // 释放内存空间
libserial_protocol_release(splbuf1);
libserial_protocol_release(splbuf2); iprintf("dynamic run done...\n\n");
return;
} int main(int argc, char* argv[])
{
char data[256] = { 0 }; // 填充数据, 最后一字节存储 '\0'
for (unsigned int i = 0, j = 0; i < sizeof(data) - 1; i++) {
data[i] = '0' + j;
j = '9' == data[i] ? 0 : j + 1;
} static_libserial_protocol(data, sizeof(data));
dynamic_libserial_protocol(data, sizeof(data)); return 0;
}

三、代码运行结果

【开源】libserial_protocol:适用于单片机的串口通信协议基础库的更多相关文章

  1. 单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等(转)

    源:单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等 //modebus_rtu.c /***************************************** ...

  2. 基于STM32之UART串口通信协议(一)详解

    一.前言 1.简介 写的这篇博客,是为了简单讲解一下UART通信协议,以及UART能够实现的一些功能,还有有关使用STM32CubeMX来配置芯片的一些操作,在后面我会以我使用的STM32F429开发 ...

  3. 基于STM32之UART串口通信协议(三)接收

    一.前言 1.简介 回顾上一篇UART发送当中,已经讲解了如何实现UART的发送操作了,接下来这一篇将会继续讲解如何实现UART的接收操作. 2.UART简介 嵌入式开发中,UART串口通信协议是我们 ...

  4. [51单片机] HC-SR04超声波测距仪 基础代码

    >_<:超声波测距仪模块: >_<:51单片机,11.0592MHz晶振,将采集数据发送到串口的基础例子: >_<:代码: /******************* ...

  5. 自制单片机之十七……PC与单片机RS-232串口的通讯和控制

    这次我们来试着一步步的去掌握PC与单片机通过RS-232进行通讯和控制. 先说说我硬件的情况.我用的PC是个二手的IBM240小本本,十寸屏,赛扬400,机子很老了.但也有它的优点:1.串口,并口,P ...

  6. UART串口协议基础1

    Louis kaly.liu@163.com 串口协议基础 1 串口概述 串口由收发器组成.发送器是通过TxD引脚发送串行数据,接收器是通过RxD引脚接收串行数据. 发送器和接收器都利用了一个移位寄存 ...

  7. 基于STM32之UART串口通信协议(四)Printf发送

    一.前言 1.简介 前面在UART发送中已经讲解过如何调用HAL库的HAL_UART_Transmit函数来实现串口发送,而在调用这个函数来实现串口发送的话,但是在发送数据或者字符的时候,需要将数据或 ...

  8. 基于STM32之UART串口通信协议(二)发送

    一.前言 1.简介 在上一篇UART详解中,已经有了关于UART的详细介绍了,也有关于如何使用STM32CubeMX来配置UART的操作了,而在该篇博客,主要会讲解一下如何实现UART串口的发送功能. ...

  9. YARN底层基础库

      YARN基础库是其他一切模块的基础,它的设计直接决定了YARN的稳定性和扩展性,YARN借用了MRV1的一些底层基础库,比如RPC库等,但因为引入了很多新的软件设计方式,所以它的基础库更多,包括直 ...

  10. ESP8266开发之旅 网络篇⑥ ESP8266WiFiGeneric——基础库

    1. 前言     在前面的博文中,博主介绍到ESP8266WiFi库是包含了很多功能的一个超级库.ESP8266WiFi库不仅仅局限于 ESP8266WiFi.h 和 ESP8266WiFi.cpp ...

随机推荐

  1. Spring Boot 应用的热部署配置

    前言 所谓热部署,简单来说,就是代码修改后不需重启项目就可自动加载出新的内容. 注意:热部署在 debug 调试模式下才生效! IDEA 配置 在 IDE(IDEA)中开启相关项目自动构建选项 开启编 ...

  2. ES6 学习笔记(二)解构赋值

    一.数组的解构赋值 1.基本用法 ES6允许按照一定模式从数组和对象中提取值,然后对变量进行赋值,该操作即为解构 如: let [a,b,c]=[1,2,3]; console.log(a,b,c) ...

  3. Archlinux安装Picgo配置Typora

    Typora堪称为markdown界的老大哥,其大名我们多有耳闻,所见即所的就是他的特点.但是在日常使用中,也经常会碰到一些特别的需求,比如:希望图片能够上传到云端. 怎么将markdown即时粘贴的 ...

  4. Java自定义排序:继承Comparable接口,重写compareTo方法(排序规则)

    代码: 1 import java.util.*; 2 3 /** 4 * 学习自定义排序:继承Comparable接口,重写compareTo方法(排序规则). 5 * TreeMap容器的Key是 ...

  5. 【题解】CF1013B And

    题面传送门 解决思路 首先我们可以得出,$ a $ \(\&\) $ x $ \(=\) $ a $ \(\&\) $ x $ \(\&\) $ x $.由此得知,同一个 \( ...

  6. bootstrap-table参数

    table.bootstrapTable({ url:'/Home/geurl', //请求后台的URL(*) method:'get', //请求方式(*) toolbar:'#toolbar', ...

  7. gdb不能使用mac

    先说问题:1.gdb不能使用,重新用homebrew install 了gdb 2.brew装的gdb可以用了,但是等start调试的时候报这些错误:        dyld: Library not ...

  8. C++ undered_map哈希表用法——leetcode两数之和

    undered_map 头文件:#include<undered_map> 创建表undered_map<key,value> Map_name; 插入元素 a[key]=va ...

  9. c# 使用委托子窗体改变父窗体控件

    首先创建两个窗体,在窗体1和窗体2放上对应的控件 在窗体1的代码如下 using System; using System.Collections.Generic; using System.Comp ...

  10. pycharm恢复删除文档与查询修改前数据

    1.pycharm恢复删除文档 第一步: 第二步: 2.pycharm查询修改前文档数据 第一步: 第二步: