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

特点如下:

  • 不涉及到具体硬件,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. JVM堆内存转储

    在发生内存溢出错误 java.lang.OutOfMemoryError 时, JVM自动执行堆内存转储,以方便事后进行排查和分析. JVM提供了一个命令行启动参数 HeapDumpOnOutOfMe ...

  2. C#使用不安全指针来操作bitmap

    C#允许通过不安全指针实现像C++一样操作指针,这个速度是最快的.下面这个例子是修改一幅RGB图像的每个像素值,速度很快,测试一张2592*1944的彩色图像,只需要几ms就能够全部遍历. /// & ...

  3. elasticsearch多字段聚合实现方式

    目录 1.背景 2.实现多字段聚合的思路 3.需求 4.数据准备 4.1 创建索引 4.2 准备数据 5.实现方式 5.1 multi_terms实现 5.1.1 dsl 5.1.2 java 代码 ...

  4. K8s如何启用cgroup2支持?

    什么是 cgroup ️Reference: control groups(控制组),通常被称为cgroup,是Linux内核的一项功能.它允许将进程组织成分层的组,然后限制和监控各种资源的使用. 内 ...

  5. Go语言核心36讲43-----io包中接口的好处与优势

    上一篇文章中,我主要讲到了io.Reader的扩展接口和实现类型.当然,io代码包中的核心接口不止io.Reader一个. 我们基于它引出的一条主线,只是io包类型体系中的一部分.我们很有必要再从另一 ...

  6. Window使用PowerShell改文件时间戳

    We cross infinity with every step; we meet eternity in every second. 我们每一步都跨过无穷,每一秒都遇见永恒. Window使用Po ...

  7. Spring Cloud Circuit Breaker 使用示例

    Spring Cloud Circuit Breaker 使用示例 作者: Grey 原文地址: 博客园:Spring Cloud Circuit Breaker 使用示例 CSDN:Spring C ...

  8. Android Studio打开时报错if you already hava 64-bit JDK installed,define a JAVA_HOME

    出现这个问题不知道改了什么导致的,卸载了重新安装也是不行. 以及到高级设置中进行配置jdk也是无效, 解决方法为需要在路径 C:\Users\你自己的用户名\AppData\Roaming\Googl ...

  9. 将现有源码添加进repo管理

    将现有源码添加进repo管理 适用于大型项内无源码管理(git/repo)的源码 前言 ​ 公司在进行一些项目的开发时,从供应商原厂给的code内没有包含任何源码管理的文件.需要多人协同开发,但由于项 ...

  10. -webkit-box-orient:vertical 编译报错之autoprefixer问题

    由于各大浏览器的兼容问题,autoprefixer 插件 就可以帮我们自动补齐前缀.它和 less.scss 这样的预处理器不同,它属于后置处理器. 预处理器:在打包之前进行处理 后置处理器:在代码打 ...