串口网口数据帧解析(支持连包、断传、错误数据过滤)

2023-05-16

 本文转载自:https://blog.csdn.net/hwb_1988/article/details/45872379

嵌入式系统中,关于数据接受部分确实思考了很多,下面总结下个人经验。

        关于串口传输,个人觉得采用modbus协议来接受数据是比较合理的,采用3.5char字符的超时机制,接受的时候如果判断超时,就当作一帧数据进行处理,所以这种情况,帧格式没有那么讲解,发送和超时机制弄好就行。

        第二种网口用的比较多,串口也用的上,什么情况下用的上呢,当发送的数据没有固定的格式和长度,而且发送的时间也无特别的讲究。一般用的比较普遍的格式为:

        帧头 + 长度 + 数据 + 校验 + 帧尾,这种格式可以算的上一种万能的数据处理格式啦,几乎都可以套用。

        贴出来的代码主要有以下几个功能:

        一是处理没用的数据格式,假如帧头为0xa5,如果发送不是该0xa5的字符,统统过滤,如果不过滤的话,循环队列的缓冲会被浪费。

        二是处理了断续传输,假如一帧数据在传输过程中,一部分先达到,一部分后达到,我采用的机制就是一帧数据没有接受完,那就不处理。

        三是当多帧数据一起接受时,此时就有粘包了,这个时候需要做的是把接受到的所有数据按照帧格式分析提取,处理完后的数据就是有效的单帧数据。我这里面采用的回调机制,你只要把你的帧格式处理函数注册一下,当数据处理完毕后,会自动调用你注册函数。

        四是只能处理单包数据不超过6K的ram,如果长度标志超过6K,这些数据数据会被过滤,当然这个不是固定的你可以修改,程序没有采用宏定义方式。

        采用C++写的,当然你只要稍微的修改下,就可以支持C语言了,只要一个头文件即可

        新建Queue.h文件

​#ifndef QUEUE_H
#define QUEUE_H
#define MAXSIZE	10240								//10K的RAM作为缓冲区
#define START_CHAR	0xa5
#define END_CHAR	0x5a
typedef unsigned char uint8_t;
typedef unsigned long uint32_t;
typedef enum {
	RES_OK = 0,
	RES_ERROR
} STATUS;
typedef enum {
	RECEIVED_START,
	NO_RECEIVED_START
} RECV_FSM;
typedef void (*AnalysisFun)(uint8_t *, int len);	//回调函数类型
class Queue {
private:
	uint32_t front;
	uint32_t rear;
	uint8_t data[MAXSIZE];
	AnalysisFun anaFun;
public:
	Queue(AnalysisFun cb)
	{
		front = 0;
		rear = 0;
		anaFun = cb;
	}
	~Queue()
	{
	}
	STATUS EnQueue(uint8_t e)
	{
		if ((rear + 1) % MAXSIZE == front)
		{
			return RES_ERROR;
		}
		data[rear] = e;
		rear = (rear + 1) % MAXSIZE;
		return RES_OK;  
	}
	int QueueLength()
	{
		return (rear - front + MAXSIZE) % MAXSIZE;
	}
	uint8_t GetQueue(int index)
	{
		return data[(front + index) % MAXSIZE];
	}
	void HandleData()
	{
		uint32_t frame_len, frame_startpos = 0;
		uint8_t frame_check, c;
		RECV_FSM frame_state = NO_RECEIVED_START;
		uint32_t len = (rear - front + MAXSIZE) % MAXSIZE;	//循环队列中数据长度
		for (uint32_t i = 0; i < len; i++)
		{
			if (frame_state == NO_RECEIVED_START)
			{
				if (GetQueue(i) == START_CHAR)				//接受到0xa5
				{
					frame_check = 0;
					frame_state = RECEIVED_START;			//切换,进而处理帧数据分析
				}
				else
				{
					frame_startpos = i + 1;					//标记缓冲队列需要释放的位置
				}
			}
			else if (frame_state == RECEIVED_START)
			{
				if (i + 4 <= len)							//长度4个字节
				{
					frame_len = ((uint32_t)GetQueue(i++));
					frame_len |= ((uint32_t)GetQueue(i++)) << 8;
					frame_len |= ((uint32_t)GetQueue(i++)) << 16;
					frame_len |= ((uint32_t)GetQueue(i++)) << 24;
					if (frame_len > 6137)					//不支持单包数据超过6K
					{
						frame_state = NO_RECEIVED_START;
						frame_startpos = i;					//标志需要释放的位置
						continue;
					}
					if (i + frame_len + 2 <= len)			//数据长度+校验+帧尾
					{
						uint8_t *p = new uint8_t[frame_len + 2];//分配空间,把循环队列数据转移到新分配的空间
						if (!p)
						{
							return;
						}
						for (uint32_t k = 0; k < frame_len; k++)
						{
							c = GetQueue(i++);				//取出循环队列一个数据
							p[k] = c;						//转移
							frame_check += c;				//校验求和			
						}
						c = GetQueue(i++);					//取出校验码和求和的内容进行对比
						if (c == frame_check && GetQueue(i) == END_CHAR)	//如何比对内容一致且帧尾也无误
						{
							anaFun(p, frame_len);			//回调处理
						}
						frame_state = NO_RECEIVED_START;	//切换到重新等待0xa5状态
						frame_startpos = i + 1;				//重新标记缓冲队列需要释放的位置
						delete[] p;							//释放空间
					}
					else
					{
						break;
					}
				}
				else
				{
					break;
				}
			}
		}
		front = (front + frame_startpos) % MAXSIZE;			//释放缓冲区
	}
};
#endif​

 

新建main.cpp,我这个工程是在VS2010新建的:

// Thread.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include "Queue.h"
using namespace std;
void Print(uint8_t *str, int len)									//回调函数处理的内容,这里只是打印
{
	for (int i = 0; i < len; i++)
	{
		printf("%d=%02x\r\n", i, str[i]);
	}
}
int _tmain(int argc, _TCHAR* argv[])
{
	Queue queue(Print);				
	uint8_t t[] = {
		0xa5, 0x02, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x5a,		//过滤掉了,数据超过了6K
		0xa5, 0x02, 0x00, 0x00, 0x00, 0x02, 0x03, 0x05, 0x5a,		//打印
		0xa5, 0x02, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x5a,		//校验错了,过滤
		0xaa, 0xa1, 0x33,											//过滤,这些数据没有出现0xa5
		0xa5, 0x03, 0x00,											//有0xa5,等待续传,不能过滤
	};
	for (int i = 0; i < sizeof(t); i++)
	{
		if (queue.EnQueue(t[i]))
		{
			cout << "error"<< endl;
		}
	}
	queue.HandleData();
	printf("len = %d\r\n", queue.QueueLength());
	getchar();
	return 0;
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

串口网口数据帧解析(支持连包、断传、错误数据过滤) 的相关文章

  • strstr函数的精彩之处

    在C的库函数里面有很多精彩的代码 xff0c 仔细分析会体会到这些写法和思路的巧妙之处 写strstr函数的时候要判断两个字符换str1和str2什么时候开始匹配 xff0c 什么时候结束 xff0c 这两点都是比较容易做到的 xff0c
  • RISC与CISC

    作者 xff1a Austin Wang 链接 xff1a http www zhihu com question 20783321 answer 72598657 这里简单来谈一下 xff0c ARM和X86之间为什么不太具有可比性的问题
  • NandFlash和NorFlash的区别

    一 xff0e NAND和NOR的比较 NOR和NAND是现在市场上两种主要的非易失闪存技术 Intel于1988年首先开发出NOR flash技术 xff0c 彻底改变了原先由EPROM 和EEPROM一统天下的局面 紧接着 xff0c
  • 安装 CORD 之前需要了解的术语

    CORD Central Office Re Architected as a Data Center 是ONF组织推动的开源的边缘计算的项目 CORD 项目的官方资料包括源代码 xff0c 安装指导等 本文对官方安装指导涉及的一些术语进行
  • SoC嵌入式软件架构设计之一:系统内存需求评估

    博主 xff1a 吴跃前 转载自 xff1a http www cnblogs com yueqian scut p 4015046 html SoC嵌入式软件架构设计之一 xff1a 系统内存需求评估 内存是SoC System on C
  • S5PV210启动过程分析

    刚刚接触 S5PV210 xff0c 看了官方的数据手册感觉跟以前用的 STM32 单片机根本不在一个层面上 xff0c 当然两者的用途不同 xff0c 不具备可比性 可能之前习惯使用 STM32 等单片机 xff0c 现在接触 S5PV2
  • ARM支持的4种堆栈工作方式

    1 满递增堆栈 xff1a 堆栈指针指向最后压入堆栈的数据 xff0c 且由低地址向高地址生成 2 满递减堆栈 xff1a 堆栈指针指向最后压入堆栈的数据 xff0c 且由高地址向低地址生成 3 空递增堆栈 xff1a 堆栈指针指向下一个要
  • 前变址、回写前变址、后变址

    根据数据传输传输的时机以及在指令执行后基址寄存器是否被更新 xff0c 寄存器变址有前变址 回写前变址和后变址暗中方式 执行指令的时候 xff0c 如果先进行变址运算 xff0c 后传递数据 xff0c 那么这种方式就是前变址方式 如图1所
  • ubuntu 配置 tftp 服务器

    注意 xff1a 1 tftpboot为根目录 2 已经是root权限不用再加sudo 临时获取权限 3 使用netstat au grep tftp xff0c 保证tftp是启动的 出现 udp 0 0 tftp 则说明tftp服务已经
  • STM32启动文件详解

    先在RAM中分配系统使用的栈 xff0c RAM的起始地址为0x2000 0000 然后在RAM中分配变量使用的堆 然后在CODE区 flash 分配中断向量表 xff0c flash的起始地址为0x0800 0000 xff0c 该中断向
  • C语言细节之定义与声明

    什么是定义什么是声明 xff0c 似乎这俩个东西一样但是又有所区别 xff0c 思考一下 xff0c 如果你不能直接说出这二者的根本区别 xff0c 那么本文对你还是有点用处的 首先什么是定义 xff1f C 43 43 Primer 第四
  • 关于数据存储大小端模式的C语言题目

    int main int a 5 61 1 2 3 4 5 int ptr1 61 int amp a 43 1 int ptr2 61 int int a 43 1 printf 34 x x 34 ptr1 1 ptr2 return
  • 窥探引用和指针

    C语言面试题里面经常会出现一道题目 xff1a 向某个指定的内存地址写入特定的数据 xff0c 譬如向0x1234写入100 xff0c 做法如下 int 0x1234 61 100 解释一下就是将0x1234利用 int 强制转换为地址
  • STM32F101xxT6中VBAT 管脚上的怪现象

    转自 xff1a http bbs elecfans com forum php mod 61 viewthread amp tid 61 458808 amp extra 61 问题 xff1a 问题由某 UPS 制造公司提出 其工程师在
  • ST-LINK接口定义

    ST LINK接口定义 https www cnblogs com jieruishu p 9980350 html ST LINKIII管脚定义及接法 xff1a ST LINK IIILED灯三种状态含义 xff1a 常亮 xff1a
  • 罗德里格斯公式推导

    罗德里格斯公式推导 第一部分 向量 v 61 v x v
  • Ubuntu中的vi

    Windows中的常用编辑器 xff0c 如自带的notepad 比较好用的notepad 43 43 xff0c UltraEditor xff0c SlickEditor Linux中常用编辑器 xff0c 自带的最古老的vi 比较好用
  • 关于C++中的友元函数的总结

    转载自 xff1a http www cnblogs com BeyondAnyTime archive 2012 06 04 2535305 html 关于C 43 43 中的友元函数的总结 1 友元函数的简单介绍 1 1为什么要使用友元
  • 拷贝构造函数

    拷贝构造函数是重载构造函数的一种重要形式 xff0c 它的功能是使用一种已经存在的对象去初始一个新创建的同类对象 xff0c 它可以将一个已有对象的数据成员的值拷贝给正在创建的另一个同类的对象 拷贝构造函数与类同名 xff0c 没有返回值

随机推荐

  • cp: omitting directory ‘sp/’

    在linux中使用命令复制文件夹的时候出现的错误 xff1a cp omitting directory sp 解决办法 xff1a 使用递归拷贝 xff0c 在cp命令后面加上 r参数 xff0c 形如 xff1a cp r sp ica
  • LDR和ADR

    转自 xff1a https www douban com note 331036776 ARM汇编有ldr指令以及ldr adr伪指令 xff0c 他门都可以将标号表达式作为操作数 xff0c 下面通过分析一段代码以及对应的反汇编结果来说
  • 在显示屏上显示的数字的方法

    在现实屏上显示的数字的时候经常需要对数据进行分解 xff0c 得到数据 的每一位再依次显示 xff0c 方法有很多种 xff0c 下面介绍经常用到的3种方法 第一种方法 xff1a 不断的利用取整运算与取余运算得到每一位 void disp
  • 可变参数列表的简单实现

    使用可变参数列表实现一个sum函数 include lt stdio h gt define AUPBND sizeof int 1 define ADNBND sizeof int 1 define bnd X bnd sizeof X
  • 函数指针实例

    include lt stdio h gt void fun1 void printf 34 I 96 m fun1 n 34 void fun2 void printf 34 I 96 m fun2 n 34 void fun3 void
  • makefile小试牛刀

    使用make编译两个 c文件 div span style font size 14px a c void fun1 void int main fun1 return 0 span div div span style font size
  • 线程,进程,程序的区别

    转载自 xff1a https mp weixin qq com s O6 cZ9t8aJUMyeb7TbcbTQ 任务是代码运行的一个映象 xff0c 从系统的角度看 xff0c 任务是竞争系统资源的最小运行单元 任务可以使用或等待CPU
  • ubuntu14.04 samba服务器安装配置

    Data 2016 12 19 Author cjh Theme samba install andconfiguration 在介绍如何安装配置samba之前 xff0c 先简单介绍linux中两个命令的区别分别是update和upgra
  • 在数组中进行图形绘制

    include lt stdio h gt define k 2 43 1 define 2 define s 0 static unsigned short stop 61 s k k k k k k k s k k k k k k k
  • socket函数

    转自 xff1a http blog csdn net guoping16 article details 6584382 1 基本套接字函数 xff08 1 xff09 socket函数原型 socket 建立一个socket文件描述符
  • 进程与线程

    第一章 xff1a 计算机系统漫游 进程 xff1a 进程是操作系统对一个正在运行的程序的一种抽象 xff0c 一个系统可以同时运行多个进程 xff0c 看上去是CPU并发的执行多个程序 xff0c 实际上是通过进程切换来实现的 xff0c
  • GDI GDI+ 的区别

    转载自 xff1a http www cnblogs com lidabo p 3701252 html GDI 43 是GDI的下一个版本 xff0c 它进行了很好的改进 xff0c 并且易用性更好 GDI的一个好处就是你不必知道任何关于
  • GDI GDI+ 的区别

    转载自 xff1a http www cnblogs com lidabo p 3701252 html GDI 43 是GDI的下一个版本 xff0c 它进行了很好的改进 xff0c 并且易用性更好 GDI的一个好处就是你不必知道任何关于
  • 使用的DockPanel的心得

    使用这个控件的时候后一定要先Show this DockPlan1 才能使用DockTo this DockPlan1 DockStyle Bottom 固定位置 xff0c 否则会抛出异常
  • C#使用双缓冲解决绘图闪屏的问题

    最近在工作需要使用C 绘制图形 xff0c 看了一下绘制的函数觉得很简单就开始着手工作了 xff0c 但是在实际应用的时候发现鼠标进行绘制的时候会闪屏 xff0c 原因是图元重绘的时间不一致 xff0c 百度一下有很多更详细的 xff0c
  • 关于捕获键盘信息的processDialogkey方法

    转载自 xff1a http blog csdn net lucifinil s article details 6318189 在一些控件里的keydown方法 xff0c 没有办法捕获所有的按键消息 比如自己写一个窗体控件库 xff0c
  • C#泛型委托

    因为项目原因最近要使用C 进行编程 xff0c 于是每天现学现卖一点一点的进行开发 之前很长的时间一直使用C和C 43 43 进行编程 xff0c 于是转到C 一开始开始不是特别适应的 xff0c 特别是C 这门语言没有了指针 xff0c
  • 基于windows的Ubuntu双系统安装

    Data 2016 12 19 Author cjh Theme Ubuntu dual system installation 前期准备 1 Ubuntu相关版本镜像ios 2 UltraISO 用于制作U盘启动盘 3 EasyBCD 2
  • 汇编文件.S和.s的区别

    c C 原始程序 xff1b 预处理 编译 汇编 C C 43 43 原始程序 xff1b 预处理 编译 汇编 ccC 43 43 原始程序 xff1b 预处理 编译 汇编 cxx C 43 43 原始程序 xff1b 预处理 编译 汇编
  • 串口网口数据帧解析(支持连包、断传、错误数据过滤)

    本文转载自 xff1a https blog csdn net hwb 1988 article details 45872379 嵌入式系统中 xff0c 关于数据接受部分确实思考了很多 xff0c 下面总结下个人经验 关于串口传输 xf