本文转载自: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(使用前将#替换为@)