嵌入式系统中串口通信粘包问题的解决方案(C语言)

文章目录

    • 0. 引言
    • 1. 什么是粘包问题?
    • 2. 粘包问题的影响
    • 3. 处理粘包问题的思路
    • 4. 不同处理方法的优缺点分析
    • 5. 实现方案
      • 5.1 数据包格式
      • 5.2 代码实现

0. 引言

在嵌入式系统中,串口通信是一种常见且重要的数据传输方式。然而,由于硬件和软件的限制,串口通信过程中常会出现数据包粘连(即粘包)问题。这种问题会导致接收端无法正确解析数据。本文将介绍粘包问题的成因,结合实际代码示例,详细说明如何实现解决方案。

1. 什么是粘包问题?

粘包问题是指在串口通信中,多个独立的数据包在传输过程中被接收端视为一个连续的数据流,导致数据包之间的边界不明确,从而使解析过程变得困难:

  • 发送端数据发送频率高,接收端处理速度较慢。
  • 数据包长度变化较大,接收端难以准确确定数据包边界。
  • 硬件限制导致的数据包边界模糊。

2. 粘包问题的影响

  1. 数据丢失:由于粘包问题,接收端可能会错误地丢弃某些数据包,导致数据丢失。
  2. 数据错误:粘包可能导致数据包被错误解析,从而导致数据内容出现错误。
  3. 数据重传:为了确保数据完整性,系统可能需要重传数据包,增加了通信负担。

3. 处理粘包问题的思路

为了解决粘包问题,可以采取以下措施:

  1. 使用特殊分隔符:在每个数据包的开始或结束添加特殊字符,明确标记数据包的边界。
  2. 固定数据包长度:确保每个数据包的长度一致,使接收端可以通过固定长度来解析数据包。
  3. 设计协议:在数据包中包含长度信息和校验信息,以确保接收端能够准确解析和校验数据包的完整性。

4. 不同处理方法的优缺点分析

方法优点缺点
特殊分隔符实现简单,易于检测数据边界需要处理转义字符,性能略有影响
固定数据包长度简单高效,解析速度快不适用于变长数据,浪费带宽
设计协议灵活性高,适应多种数据格式实现复杂度高,需要额外开销

5. 实现方案

以下是一个具体的实现方案,通过在数据包中添加头部标识符和长度信息,确保接收端能够正确解析数据包。

5.1 数据包格式

我们假设数据包的格式如下:

  • 头部标识符(2字节):用于标识数据包的开始。
  • 数据长度(1字节):表示数据包的长度。
  • 数据内容(可变长度):实际的数据内容。
  • 校验码(1字节):用于校验数据包的完整性。

5.2 代码实现

以下代码展示了如何在接收端处理粘包问题:

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <unistd.h>// 使用 const 定义常量
static const uint8_t UART_FIRST_HEADER_CHAR = 0xAA;
static const uint8_t UART_SECOND_HEADER_CHAR = 0xBB;typedef struct {uint8_t msg_type;uint8_t len;uint8_t msg_buff[255];
} uart_frame_t;static int uart_read(int fd, uint8_t *buff, int len, int timeout_us) {int ret;struct timeval tv;fd_set rfds;memset(buff, 0, len);FD_ZERO(&rfds);FD_SET(fd, &rfds);memset(&tv, 0, sizeof(tv));tv.tv_sec = timeout_us / 1000000;tv.tv_usec = timeout_us % 1000000;ret = select(fd + 1, &rfds, NULL, NULL, &tv);if (ret == -1) {printf("select() failed. ret %d, %s\n", ret, strerror(errno));return -1;  // Select itself failed} else if (ret == 0) {// Timeout occurred without any file descriptor becoming readyprintf("select() timed out.\n");return 0;  // Indicate timeout}if (FD_ISSET(fd, &rfds)) {ssize_t bytesRead = read(fd, buff, len);if (bytesRead == -1) {printf("read() failed. errno %d, %s\n", errno, strerror(errno));return -1;  // Error during read} else if (bytesRead == 0) {// EOF reached or no data availablereturn 0;  // Can be considered as no data or end of stream}return bytesRead;  // Return actual bytes read} else {// This branch should not be reachable given the checks abovereturn 0;  // No data ready within the timeout}
}static long get_time_ns(void) {// 模拟获取当前时间return 0;
}static void dump_prefix(const char *prefix, const uint8_t *data, int len) {// 打印调试信息printf("%s:", prefix);for (int i = 0; i < len; i++) {printf(" %02X", data[i]);}printf("\n");
}static uint8_t new_frame_crc(const uint8_t *data) {// 模拟计算 CRCreturn 0;
}static uint8_t frame_crc(const uint8_t *data) {// 模拟计算 CRCreturn 0;
}static int recv_frame(uint8_t *recv_buf, int len, int timeout_us) {int ret = 0;int i = 0;long start_time = get_time_ns();uint8_t buff[1024] = {0};uart_frame_t uart_frame;int data_offset = 0;int total_len = 0;start_time = get_time_ns();do {ret = uart_read(0, buff + data_offset, sizeof(buff) - data_offset, timeout_us);if (ret < 0) {if (errno == EINTR) {continue;} else {printf("read uart data failed. %s\n", strerror(errno));return -1;}}total_len = data_offset + ret;dump_prefix("UART2 RECV", buff, total_len);for (i = 0; i < total_len; i++) {if (buff[i] == UART_FIRST_HEADER_CHAR) {if (i + 1 < total_len && buff[i + 1] == UART_SECOND_HEADER_CHAR) {// 处理新协议帧if (i + 2 < total_len && 2 + buff[i + 2] <= total_len - i) {if (new_frame_crc(buff + i) == buff[i + buff[i + 2] + 2]) {// 校验通过,处理帧数据i += buff[i + 2] + 2;  // 跳过帧头和CRC字节} else {// 校验失败,跳过可能的帧开始++i;}} else {// 帧不完整,停止处理data_offset = total_len - i;memmove(buff, buff + i, data_offset);break;}} else {// 处理旧协议帧if (i + 1 < total_len && 1 + buff[i + 1] <= total_len - i) {if (frame_crc(buff + i) == buff[i + buff[i + 1]]) {dump_prefix("FRAME", buff + i, 1 + buff[i + 1]);memcpy(&uart_frame, buff + i, buff[i + 1] + 1);if (uart_frame.msg_type == 1 || (uart_frame.msg_type == 4 && uart_frame.msg_buff[0] == 0xFD)) {int copy_len = uart_frame.len - 9 > len ? len : uart_frame.len - 9;memcpy(recv_buf, uart_frame.msg_buff, copy_len);return copy_len;}i += buff[i + 1] + 1;  // 跳过帧} else {// 校验失败,跳过可能的帧开始++i;}} else {// 帧不完整,停止处理data_offset = total_len - i;memmove(buff, buff + i, data_offset);break;}}}}if (i == total_len) {data_offset = 0;  // 数据已处理完}} while (get_time_ns() - start_time < ((long)timeout_us * 1000));printf("timeout\n");return 0;
}int main(void) {uint8_t recv_buf[255];int len = recv_frame(recv_buf, sizeof(recv_buf), 1000000);if (len > 0) {printf("Received %d bytes: ", len);for (int i = 0; i < len; i++) {printf("%02X ", recv_buf[i]);}printf("\n");} else {printf("No data received or an error occurred.\n");}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/34502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

闪迪sd卡视频格式化数据恢复方法,你了解吗

咨询&#xff1a;“我不小心将闪迪SD卡格式化了&#xff0c;里面的重要视频文件全都不见了。我感到非常焦虑&#xff0c;因为这些视频对我来说意义非凡。现在急需找到方法来恢复&#xff01;&#xff01;” 在数字时代&#xff0c;SD卡已成为我们日常生活中不可或缺的数据存储设…

python中数据的作用域

一、命名空间 在 Python 中&#xff0c;命名空间是一个系统&#xff0c;它用于确保名字的唯一性&#xff0c;并防止命名冲突。命名空间是一个存储变量名称&#xff08;或者更广泛地说&#xff0c;标识符&#xff09;与对象之间映射的抽象概念。每个变量名你在程序中创建&#x…

本篇内容:ArkTS开发系列之事件(2.8.1触屏、键鼠、焦点事件)

上篇回顾&#xff1a; ArkTS开发系列之导航 (2.7动画&#xff09; 本篇内容&#xff1a;ArkTS开发系列之事件&#xff08;2.8.1触屏、键鼠、焦点事件&#xff09; 一、知识储备 1. 触屏事件&#xff1a;包括点击事件、拖拽事件、触摸事件。 点击事件 Button()....onClick(…

msvcp120.dll丢失怎么办,找不到msvcp120.dll的多种解决方法

最近&#xff0c;我在运行一个程序时遇到了一个错误&#xff0c;系统提示找不到msvcp120.dll文件&#xff0c;无法继续执行代码。这让我感到非常困扰&#xff0c;因为这个问题导致我无法正常运行这个程序。经过一番搜索和尝试&#xff0c;我找到了几种修复这个问题的方法&#…

如何开发、使用 Starter

开发 第一步&#xff1a;创建starter工程hello-spring-boot-starter并配置pom.xml文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchem…

SpringBoot优点达项目实战:项目初始化(一)

SpringBoot优点达项目实战&#xff1a;项目初始化&#xff08;一&#xff09; 文章目录 SpringBoot优点达项目实战&#xff1a;项目初始化&#xff08;一&#xff09;1、项目介绍2、项目搭建3、依赖导入4、数据准备 1、项目介绍 技术框架 SpringbootmybatisPlusvueknife 2、项目…

什么是生成式AI?

生成式AI&#xff08;Generative AI&#xff09;是一类利用机器学习和人工智能技术来生成内容的系统。这些系统可以创建文本、图像、音乐、视频等各种类型的内容。生成式AI通过学习大量的数据来理解和模仿人类的创作过程&#xff0c;从而生成新的、原创的内容。以下是生成式AI的…

创建App

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Django项目中&#xff0c;推荐使用App来完成不同模块的任务&#xff0c;通过执行如下命令可以启用一个应用程序。 python manage.py startapp app…

RT-Thread的Finsh实现学习

学习原因 工作中&#xff0c;使用同事开发的调试软件&#xff0c;输入参数打印的函数名就可以打印参数&#xff0c;但看不到代码实现&#xff0c;只能用自己微薄的知识积累去猜一下&#xff0c;之前尝试过&#xff0c;专门写一个函数&#xff0c;去解析编译生成的map文件&#…

名侦探李先生第一话:谁是真正的凶手(只出现一次的数字相关题解(力扣)+位操作符回忆)

引子&#xff1a;我们在之前的案子中破解过基础的单身狗问题&#xff0c;那面对更有挑战的案子&#xff0c;且看李先生如何破局&#xff0c;那下凶手&#xff01; 复习&#xff1a; 1&#xff0c;位操作符&#xff1a; 正整数原&#xff0c;反&#xff0c;补码都相同 首位是…

RocketMQ如何添加JVM监控

这里是小奏,觉得文章不错可以关注公众号小奏技术 JVM监控选型 本次JVM监控我们采用prometheus官方提供的jmx_exporter来实现 RocketMQJVM开发 整体目录 1. 新增agent目录 我们在distribution目录新增一个agent模块&#xff0c;然后添加两个文件 jmx_prometheus_javaagent-…

NtripShare2024年第二季度主要技术进展

NtripShare Cloud GNSS解算云平台方面 1、解算引擎增加根据卫星多路径效应自动剔除卫星的算法。 2、解算引擎增加解算时间段限制&#xff08;发现贵州某地在晚12点周期性效果变差&#xff09;。 3、增加2000坐标至地方坐标系转换的支持(七参数、四参数、TGO高程拟合&#x…

高性能STL库 EASTL 、高性能JSON库

GitHub - electronicarts/EASTL: EASTL stands for Electronic Arts Standard Template Library. It is an extensive and robust implementation that has an emphasis on high performance. 兄弟们&#xff0c;对STL要求性能高的可以试试这个EASTL库&#xff01;&#xff01…

原神数据库实训存储过程

原神数据库存储过程 user表存储过程 drop procedure if exists insert_user; DELIMITER $$ CREATE PROCEDURE insert_user(in userName VARCHAR(255), IN weapon_type INT,IN dendro VARCHAR(255), in rarity int,in user_level int ) BEGIN if user_level>90 OR user_lev…

[数据集][目标检测]棉花检测数据集VOC+YOLO格式389张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;389 标注数量(xml文件个数)&#xff1a;389 标注数量(txt文件个数)&#xff1a;389 标注类别…

Python Selenium 定位有空格的元素

参考&#xff1a; seleniumpython 自动化——class定位&#xff1a;class属性有空格 https://blog.csdn.net/yxxxiao/article/details/96282145seleniumpython 定位带空格的class属性 https://blog.csdn.net/sinat_34209942/article/details/81127486css定位 1.css来定位class属…

办理北京公司注册地址异常变更要求和流程

在北京注册公司时选择注册地址是非常重要的一环&#xff0c;注册地址不仅体现在营业执照上&#xff0c;在网上也有公示信息&#xff0c;一般选用的是商用地址和商住两用地址&#xff0c;在公司经营过程中&#xff0c;因为经营需要变更注册地址&#xff0c;也要依法变更&#xf…

差分GPS原理

双差RTK&#xff08;Real-Time Kinematic&#xff09;算法是基于差分全球卫星导航系统&#xff08;GNSS&#xff09;技术的一种高精度定位方法。它利用至少两个接收机&#xff08;一个为基站&#xff0c;其他为移动站&#xff09;接收自同一组卫星的信号来实现精确测量。双差处…

低价可转债崩盘,发生了什么?

下跌不在于“出库”&#xff0c;甚至不在于“风险”。问题更多在于交易层面&#xff0c;何时能积聚更多的左侧资金并成功过渡至右侧。 低价券怎么了&#xff1f; 如果说6月初主要是小微盘品种的退市风险&#xff0c;后来是一些评级下调的品种&#xff0c;到本周&#xff0c;已…

ONLYOFFICE 桌面编辑器 8.1重磅来袭:全新功能提升您的办公效率

文章目录 前言ONLYOFFICE 桌面编辑器8.1一、PDF编辑&#xff1a;告别“头痛”时刻二、幻灯片版式&#xff1a;秒变“设计大师”三、无缝切换&#xff1a;办公界的“快速通道”四、语言支持&#xff1a;全球通吃的“翻译官”五、 隐藏“连接到云”板块&#xff1a;摆脱“云”的束…