【开源】libserial_protocol:适用于单片机的串口通信协议基础库
借助五一假期,写了一个串口通信协议基础库,虽然写着适用于单片机,但实际上并不限制具体的硬件平台。
特点如下:
- 不涉及到具体硬件,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:适用于单片机的串口通信协议基础库的更多相关文章
- 单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等(转)
源:单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等 //modebus_rtu.c /***************************************** ...
- 基于STM32之UART串口通信协议(一)详解
一.前言 1.简介 写的这篇博客,是为了简单讲解一下UART通信协议,以及UART能够实现的一些功能,还有有关使用STM32CubeMX来配置芯片的一些操作,在后面我会以我使用的STM32F429开发 ...
- 基于STM32之UART串口通信协议(三)接收
一.前言 1.简介 回顾上一篇UART发送当中,已经讲解了如何实现UART的发送操作了,接下来这一篇将会继续讲解如何实现UART的接收操作. 2.UART简介 嵌入式开发中,UART串口通信协议是我们 ...
- [51单片机] HC-SR04超声波测距仪 基础代码
>_<:超声波测距仪模块: >_<:51单片机,11.0592MHz晶振,将采集数据发送到串口的基础例子: >_<:代码: /******************* ...
- 自制单片机之十七……PC与单片机RS-232串口的通讯和控制
这次我们来试着一步步的去掌握PC与单片机通过RS-232进行通讯和控制. 先说说我硬件的情况.我用的PC是个二手的IBM240小本本,十寸屏,赛扬400,机子很老了.但也有它的优点:1.串口,并口,P ...
- UART串口协议基础1
Louis kaly.liu@163.com 串口协议基础 1 串口概述 串口由收发器组成.发送器是通过TxD引脚发送串行数据,接收器是通过RxD引脚接收串行数据. 发送器和接收器都利用了一个移位寄存 ...
- 基于STM32之UART串口通信协议(四)Printf发送
一.前言 1.简介 前面在UART发送中已经讲解过如何调用HAL库的HAL_UART_Transmit函数来实现串口发送,而在调用这个函数来实现串口发送的话,但是在发送数据或者字符的时候,需要将数据或 ...
- 基于STM32之UART串口通信协议(二)发送
一.前言 1.简介 在上一篇UART详解中,已经有了关于UART的详细介绍了,也有关于如何使用STM32CubeMX来配置UART的操作了,而在该篇博客,主要会讲解一下如何实现UART串口的发送功能. ...
- YARN底层基础库
YARN基础库是其他一切模块的基础,它的设计直接决定了YARN的稳定性和扩展性,YARN借用了MRV1的一些底层基础库,比如RPC库等,但因为引入了很多新的软件设计方式,所以它的基础库更多,包括直 ...
- ESP8266开发之旅 网络篇⑥ ESP8266WiFiGeneric——基础库
1. 前言 在前面的博文中,博主介绍到ESP8266WiFi库是包含了很多功能的一个超级库.ESP8266WiFi库不仅仅局限于 ESP8266WiFi.h 和 ESP8266WiFi.cpp ...
随机推荐
- 【lwip】09-IPv4协议&超全源码实现分析
目录 前言 9.1 IP协议简述 9.2 IP地址分类 9.2.1 私有地址 9.2.2 受限广播地址 9.2.3 直接广播地址 9.2.4 多播地址 9.2.5 环回地址 9.2.6 本地链路地址 ...
- Redis系列10:HyperLogLog实现海量数据基数统计
Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...
- 【iOS逆向】某车之家sign签名分析
阅读此文档的过程中遇到任何问题,请关注公众号[移动端Android和iOS开发技术分享]或加QQ群[309580013] 1.目标 分析某车之家sign签名算法的实现 2.操作环境 frida mac ...
- VS 生成后事件中自动修改文件名插入当前时间
目录 rename 指令 获取当前时间 将当前时间插入名字 rename 指令 VS 生成后事件中使用的是CMD 的语法 我们重命名使用的是Rename 简单用法如下: RENAME (REN) [d ...
- i春秋phone number
点开题目是一个普普通通的登录注册界面,随便注册一个点进去有两个功能,一个是查看电话和你相同的用户,一个是登出. 点击查询就可以看到用户数 这里有访问数据库的操作应该,所以就应该用数据库注入来解题. 又 ...
- kettel
下载教程:(目前最高版本7.1) 1.网址:https://community.hitachivantara.com/docs/DOC-1009855 2.
- java 分别获取当前时间的年月日以及当前时间所在周的周一周末日期
以前也经常用date去截取,但是病史所有场景都适合,或者说效率满足不了,或者说拼接格外麻烦.能用java本省的的方法去实现其实更爽.因为中西方的文化的差异有时候在简单的方法上我们不得不去加一些其他的去 ...
- SpringCloud Alibaba(三) - GateWay网关
1.基本环境搭建 1.1 依赖 <!-- Gatway 网关会和springMvc冲突,不能添加web依赖 --> <dependency> <groupId>or ...
- SpringMVC01:入门、请求参数绑定、自定义类型转换器、常见注解
一.介绍--三层架构和MVC 1.三层架构介绍和MVC设计模型介绍 开发架构一般都是基于两种形式,一种是 C/S 架构,也就是客户端/服务器,另一种是 B/S 架构,也就是浏览器/服务器.在 Java ...
- 【ubuntu】解决无法打开终端:gnome-terminal找不到路径问题
因为之前安装pyton的时候把电脑本身的python路径给改了,所以出现了这样的问题:图形化启动系统自带终端时,报错找不到gnome-terminal的路径 后来找到解决方法: 先桌面右键-终端(E) ...