我使用环形缓冲区来跟踪嵌入式c++应用程序下载的字节。在这个小例子中,当环形缓冲区中有5个字节时,我希望‘空’缓冲区。在实际的应用程序中,我将处理要写入闪存的字节。我的问题是,我一直跳过第五个字节。这是我的代码:
#include <stdio.h>
#include <stdint.h>
uint8_t outData;
uint8_t myDatBuf[32];
typedef struct {
uint8_t * buffer;
int head;
int tail;
int maxLen;
} ring_buffer_t;
ring_buffer_t rb;
int ring_buffer_get(ring_buffer_t *c, uint8_t *data);
int ring_buffer_put(ring_buffer_t *c, uint8_t data);
int ring_buffer_full(ring_buffer_t *c);
int ring_buffer_has_data(ring_buffer_t *c);
int main()
{
uint8_t a = 0;
rb.buffer = myDatBuf;
rb.head = 0;
rb.tail = 0;
rb.maxLen = 5;
for (uint8_t i = 0; i < 12; i++) {
if (ring_buffer_put(&rb, i) == -1) { // rb is full
printf("Ring Buffer is full! Lets empty it\n\r");
for (int x = 0; x < 5; x++) {
if (ring_buffer_get(&rb, &outData) == - 1) {
printf("Buffer is Empty\n\r");
} else {
printf("val: %d\n\r", outData);
}
}
}
}
printf("Empty the remaining bytes\n\r");
while (ring_buffer_has_data(&rb)) {
if (ring_buffer_get(&rb, &outData) == -1) {
printf("Buffer is Empty\n\r");
}
else {
printf("Rest: %d\n\r", outData);
}
}
return 0;
}
int ring_buffer_put(ring_buffer_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.
}
int ring_buffer_get(ring_buffer_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;
uint8_t t = c->tail;
*data = c->buffer[t]; // Read data and then move
c->tail = next; // tail to next data offset.
return 0; // return success to indicate successful pop.
}
int ring_buffer_full(ring_buffer_t *c) {
return c->head == c->maxLen ? 1 : 0;
}
int ring_buffer_has_data(ring_buffer_t *c) {
return (c->head != c->tail) ? 1 : 0;
}这是输出-正如您可以看到的,数组中的第5个元素被跳过。任何帮助都是非常感谢的。
Ring Buffer is full! Lets empty it
val: 0
val: 1
val: 2
val: 3
Buffer is Empty
Ring Buffer is full! Lets empty it
val: 5
val: 6
val: 7
val: 8
Buffer is Empty
Empty the remaining bytes
Rest: 10
Rest: 11发布于 2018-05-29 17:52:22
正如其他答案所述,您必须区分缓冲区已满和缓冲区空,这样做的一种方法是比缓冲区大小小一个。但是这使得缓冲区维护变得更加困难,因为它是一个环形缓冲区,而且您不能在没有篡改的情况下进行直接比较。
因此,我通常的方法是为缓冲区中的项目数设置另一个变量,这样编写者就可以很容易地判断它是否已满,而读者则可以判断它是否为空。变量需要定义为volatile,例如volatile int bufcount,这样它就不会受到编译器优化的影响。
它还应该是atomic,以确保其值的更改不会被中断,方法是在单个操作中使用增量或递减指令,而不是读-修改-写入。
发布于 2018-05-29 16:07:20
问题是,head == tail是一个模棱两可的情况。它是否意味着缓冲区是空的,还是意味着它已经满了?你不能两全其美。您似乎已经意识到了这一点,因为ring_buffer_put在head变得等同于tail之前返回错误,从而避免了歧义。ring_buffer_get还假定head == tail意味着空缓冲区。因此,实际上,这样一个带有maxLen == 5的环形缓冲区只能包含4个元素!
那么,为什么4值似乎丢失了呢?原因很简单,如果ring_buffer_put返回-1,缓冲区将被刷新,但不会再次尝试将该值放入缓冲区。所以问题不在于环形缓冲区实现本身(在我看来它很好),而在于驱动它的main代码。
您可以通过让ring_buffer_put执行put操作来修复这个问题,然后检查缓冲区是否已满。或者,您可以添加一个单独的ring_buffer_is_full函数,在尝试写入缓冲区之前调用该函数。
发布于 2018-05-29 16:08:25
现在是学习如何使用调试器的时候了。循环缓冲区当前具有以下约定:
这意味着您只能在其中存储maxlen -1字符!
无论如何,如果您想要区分缓冲区满和缓冲区空,那么就没有什么可做的了。
(*)目前ring_buffer_full的实现是错误的.
https://stackoverflow.com/questions/50588299
复制相似问题