首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >满时清空环形缓冲器

满时清空环形缓冲器
EN

Stack Overflow用户
提问于 2018-05-29 15:46:13
回答 3查看 1.8K关注 0票数 2

我使用环形缓冲区来跟踪嵌入式c++应用程序下载的字节。在这个小例子中,当环形缓冲区中有5个字节时,我希望‘空’缓冲区。在实际的应用程序中,我将处理要写入闪存的字节。我的问题是,我一直跳过第五个字节。这是我的代码:

代码语言:javascript
复制
#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个元素被跳过。任何帮助都是非常感谢的。

代码语言:javascript
复制
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
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-05-29 17:52:22

正如其他答案所述,您必须区分缓冲区已满和缓冲区空,这样做的一种方法是比缓冲区大小小一个。但是这使得缓冲区维护变得更加困难,因为它是一个环形缓冲区,而且您不能在没有篡改的情况下进行直接比较。

因此,我通常的方法是为缓冲区中的项目数设置另一个变量,这样编写者就可以很容易地判断它是否已满,而读者则可以判断它是否为空。变量需要定义为volatile,例如volatile int bufcount,这样它就不会受到编译器优化的影响。

它还应该是atomic,以确保其值的更改不会被中断,方法是在单个操作中使用增量或递减指令,而不是读-修改-写入。

票数 1
EN

Stack Overflow用户

发布于 2018-05-29 16:07:20

问题是,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函数,在尝试写入缓冲区之前调用该函数。

票数 3
EN

Stack Overflow用户

发布于 2018-05-29 16:08:25

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

  • 尾:最老的字符(当头!=尾)
  • 标题:下一个角色将去哪里?
  • 尾==头=>缓冲器空
  • head = (tail - 1)模最大=>缓冲区满(*)

这意味着您只能在其中存储maxlen -1字符!

无论如何,如果您想要区分缓冲区满和缓冲区空,那么就没有什么可做的了。

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50588299

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档