我想用v3s解析gps数据。本来想得是可以读取到换行符停止。但是目前的现象是一串数据过来,比如45个字节,我串口接收到就变成长度32和13的两个数据。
例如我发送$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
接受到的信息如下
# ./uartRec
fcntl=0
isatty success!
fd->open=3
Receive 32
Receice 11
我的串口接收线程如下
static void *threadRead(void *arg)//串口读取线程
{
while(1)
{
bufflen=UART_Recv(uartfd,RecvBuff,150);
if(bufflen>0)
{
SendBuff=(char *)malloc(bufflen);
memcpy(SendBuff,RecvBuff,bufflen);
printf("Receive %d\n",bufflen);
free(SendBuff);
}
usleep(1000);
}
return NULL;
}
串口接受程序如下
int UART_Recv(int fd, char *rcv_buf,int data_len)
{
int len,fs_sel;
fd_set fs_read;
struct timeval time;
FD_ZERO(&fs_read);
FD_SET(fd,&fs_read);
time.tv_sec = 10;//等待时间 秒
time.tv_usec = 0;//等待时间 微妙
//使用select实现串口的多路通信
fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);
//printf("fs_sel = %d\n",fs_sel);
if(fs_sel)
{
len = read(fd,rcv_buf,data_len);
//printf("len = %d fs_sel = %d\n",len,fs_sel);
return len;
}
else
{
//printf("Sorry,I am wrong!");
return FALSE;
}
}
请大家给些指点,或者给个思路怎样在一次只接收32字节的情况下解析gps。。。
离线
参考链接: 在嵌入式C中实现循环/环形缓冲区
嵌入式软件通常涉及状态机,循环缓冲区和队列。在这篇文章中,将给你一个数据结构的概述,并引导你完成在低内存设备中实现循环/环形缓冲区所涉及的步骤。
对于那些不知道循环缓冲区是什么的人来说,这是一个数组结构,在数组结构中,数组被视为循环,并且在达到数组长度后,循环返回到0。这是通过有两个指针来完成的。一个指向“头”,另一个指向缓冲区的“尾部”。随着数据被添加到缓冲区,头指针向上移动,并且随着数据被移除(读取),尾指针向上移动。这与实施有关,因观点而异。所以,为了争辩,我们会同意的,你写在头上,从尾巴读。
这是一个很好的维基百科的 GIF ,
图片说明了一切。动画是非常快的,可能需要一些时间观察动画的迭代,然后才会注意到所有涉及的情况,但是花时间给出了对内存和指针的直观表示。
完整与空白
关于循环缓冲区的下一个重要的事情是没有“干净的方式”来区分缓冲区满空的情况。这是因为在这两种情况下,头等于尾巴。有很多方法/解决方法来处理这个问题,但其中大多数是不可读的。我提出了一个相当可读的方法。
在这种方法中,有两个关键的情况(边界条件)在实施循环缓冲区时必须考虑,
头等于尾巴 - >缓冲区是空的
(头+ 1)等于尾 - >缓冲区已满
其实质是,每当你试图推动,你检查is_buffer_full条件,每当有流行,你检查is_buffer_empty。
有了这些知识,我将着手定义数据类型!
typedef struct {
uint8_t * const buffer;
int head;
int tail;
const int maxLen;
} circBuf_t;
我们的主要结构是处理缓冲区及其指针。注意缓冲区是uint8_t * const buffer。const uint8_t *是一个指向常量元素的字节数组的指针,即被指向的值不能被改变,但指针本身可以被改变。另一方面uint8_t * const是指向一个字节数组的常量指针,其中被指向的值可以被改变,但指针不能被改变。
这样做是为了能够对缓冲区进行更改,但是不会意外地孤立指针。这是一个非常好的安全措施,我强烈建议你不要跳过那部分。
在push和pop例程中,我们将计算“next”偏移点到当前写入/读取将发生的位置。如前所述,如果下一个位置指向尾部指向的位置,那么我们知道缓冲区已满,并且不会将数据写入缓冲区(返回错误)。同样,当头等于尾时,我们知道缓冲区是空的,没有任何东西可以被读取。
将数据推入循环缓冲区
在大多数用例场景中,您将在ISR中调用此方法。因此,一个推送应该尽可能小,整个例程应该包含关键部分,以使其在多线程环境中同步。
在头指针递增之前,必须加载数据,以确保只有有效的数据被消费者线程(一个调用pop的函数)读取,见下文。
另外,如果您注意到,我们在缓冲区中保留一个字节作为保留空间。乍一看,它可能会被看作是一个一个,但如果你有这个事情,你会发现它的工程交易。如果我要使用这一个额外的字节,检测完整和空的情况就会变得稍微复杂一些,编写能够处理所有情况的代码是非常耗时的,而且很难调试。
因此,总而言之,对于小型和固定大小的数据单元,只要保留一个字节,而您仍然可以保持清醒。
int circBufPush(circBuf_t *c, uint8_t data)
{
// next is where head will point to after this write.
int next = c->head + 1;
if (next >= c->maxLen)
next = 0;
if (next == c->tail) // check if circular buffer is full
return -1; // and return with an error.
c->buffer[c->head] = data; // Load data and then move
c->head = next; // head to next data offset.
return 0; // return success to indicate successful push.
}
从循环缓冲区中弹出数据
Pop例程被应用程序调用来从缓冲区中取出数据。如果有多个线程正在读取这个缓冲区,这也必须包含在关键部分(虽然这不是通常的做法)
在这里,由于每个数据单元都是一个字节,所以在数据读取之前,尾部可以移动到下一个偏移量,当我们完全加载时,我们在缓冲区中保留一个字节。但是,在更先进的循环缓冲器实现方式中,数据单元并不需要是相同的大小。在这种情况下,我们不知道在读取数据之前需要移动多少尾部。
为了保持与这些实现的一致性,我将读取数据,然后移动尾指针。
int circBufPop(circBuf_t *c, uint8_t *data)
{
// if the head isn't ahead of the tail, we don't have any characters
if (c->head == c->tail) // check if circular buffer is empty
return -1; // and return with an error
// next is where tail will point to after this read.
int next = c->tail + 1;
if(next >= c->maxLen)
next = 0;
*data = c->buffer[c->tail]; // Read data and then move
c->tail = next; // tail to next data offset.
return 0; // return success to indicate successful push.
}
用法
我觉得很明显,你必须定义一个特定长度的缓冲区,然后创建一个实例,circBuf_t并将指针分配给缓冲区及其指针maxLen。
不言而喻,缓冲区必须是全局的,或者只要需要使用缓冲区就必须处于堆栈状态。
为了使维护变得容易一点,你可以使用这个宏,但会损害新用户的代码可读性。
#define CIRCBUF_DEF(x,y) \
uint8_t x##_dataSpace[y]; \
circBuf_t x = { \
.buffer = x##_dataSpace, \
.head = 0, \
.tail = 0, \
.maxLen = y \
}
例如,如果你需要一个长度为32字节的循环缓冲区,你可以在你的应用程序中这样做,
CIRCBUF_DEF(myDatBuf, 32);
void thisIsYourAppCode()
{
uint8_t outData, inData = 0x55;
if (circBufPush(&myDatBuf, inData)) {
DBG("Out of space in CB");
return;
}
if (circBufPop(&myDatBuf, &outData)) {
DBG("CB is empty");
return;
}
// here outData = inData = 0x55;
return data;
}
我希望这篇文章有助于理解循环缓冲区。我们会看到更多这样的数据结构,并且在将来的帖子中对这个循环缓冲区类型进行了高级扩展.
在线
你用一个环形缓冲区实现,一个生产者,一个消费者,
消费者只分析缓冲区就可以。
ok!谢谢指点!
离线
晕哥 说:你用一个环形缓冲区实现,一个生产者,一个消费者,
消费者只分析缓冲区就可以。ok!谢谢指点!
哈,不用谢。
想想还是C++轮子多,
直接new 出对象直接用就可以了。
http://www.boost.org/doc/libs/1_61_0/doc/html/circular_buffer.html
http://www.boost.org/doc/libs/1_61_0/doc/html/circular_buffer/example.html
// Create a circular buffer with a capacity for 3 integers.
boost::circular_buffer<int> cb(3);
在线
不错,写的非常好。
离线