满时清空环形缓冲区

时间:2018-05-29 15:46:13

标签: c arrays

我正在使用环形缓冲区来跟踪嵌入式c应用程序的下载字节数。在这个小例子中,当环形缓冲区中有5个字节时,我想要“清空”缓冲区。在实际应用程序中,我将处理要写入闪存的字节。我的问题是,我一直在跳过第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

3 个答案:

答案 0 :(得分:3)

问题是head == tail是一个模棱两可的情况。这是否意味着缓冲区是空的,还是意味着它已满?你不可能两种方式。您似乎意识到了这一点,因为ring_buffer_puthead等于tail之前返回错误,从而避免了歧义。 ring_buffer_get还假设head == tail表示空缓冲区。事实上,这样一个maxLen == 5的环形缓冲区只能包含4个元素!

那么为什么价值4似乎丢失了?只是因为如果ring_buffer_put返回-1,则刷新缓冲区,但不会再尝试将值放入缓冲区。所以问题不在于环形缓冲区实现本身(对我来说看起来很好),但main中的代码驱动它。

您可以通过让ring_buffer_put执行put操作来解决这个问题,然后然后检查缓冲区是否已满。或者,您可以在尝试写入缓冲区之前添加一个单独的ring_buffer_is_full函数。

答案 1 :(得分:2)

现在是学习如何使用调试器的时候了。您的循环缓冲区当前具有以下约定:

  • 尾巴:最古老的角色(当头!=尾巴)
  • head:下一个角色将去哪里
  • tail == head =&gt;缓冲空
  • head =(tail - 1)modulo maxlen =&gt;缓冲区满(*)

这意味着您只能存储maxlen - 1个字符!

无论如何,如果你想区分缓冲区满和缓冲区为空,还有更多的事情要做。

(*)ring_buffer_full的当前实现是错误的......

答案 2 :(得分:1)

正如其他答案所说,你必须区分缓冲区满和缓冲区空,并且一种方法是保持一个小于缓冲区大小。但这会使缓冲区维护更加困难,因为它是一个环形缓冲区,如果没有摆弄,你就无法进行直接比较。

所以我通常的方法是为缓冲区中的项目数量设置另一个变量,这样可以让作者轻松判断它是否已满,并且读者可以判断它是否为空。该变量需要定义为volatile,例如volatile int bufcount,因此它不会受编译器优化的影响。

它也应该是atomic,以保证在单个操作中使用递增或递减指令而不是读取 - 修改 - 写入来改变其值不会被中断。