plsbackup 发表于 2024-11-27 22:00

嵌入式常用算法:环形缓冲

C文件:

view plaincopy
/**
******************************************************************************
* @file    cx_ringbuff.c
* @authorCX
* @version V1.0.0.2
* @date    2016-07-13
* @brief   1.0.0.2 期望帧逻辑优化
             修改匹配期望帧任务的条件
             增加匹配期望帧函数
             1.0.0.1 优化耦合性
             优化读取逻辑
             增加多缓冲区支持
         1.0.0.0 主体架构搭建,完成读写环形结构化
******************************************************************************
* @Attention
*
* 项目   :None
* 官网   : None
* 实验室 :None
*
******************************************************************************
*/

#include "cx_ringbuff.h"


/* 除了必须的缓冲区外没有向外部模块公开一个变量,实现高内聚,低耦合 */
RingBuff_TypedefRingBuffStruct;


/**
* @brief   缓冲区实例化
* @param   rb_ptr 缓冲区结构体地址
* @retvalNone
* @noticeNone
*/
void RingBuff_New(RingBuff_Typedef* rb_ptr)
{
    rb_ptr->Readpos = rb_ptr->RxBuff;
    rb_ptr->Writepos = rb_ptr->RxBuff;
    rb_ptr->ReadRetStateStruct.Readstate = Fail;
    rb_ptr->ReadRetStateStruct.ptr = NULL;
    rb_ptr->RingbufCount = 0;
    memset(rb_ptr->RxBuff, 0, MaxBuffSize);
}


/**
* @brief   定位函数
* @param   rb_ptr 缓冲区结构体地址,addr 当前读写地址
* @retvaladdr+1 or 0   
* @noticeNone
*/
unsigned char* NextDataAddrHandle(RingBuff_Typedef* rb_ptr, unsigned char* addr)
{
    return (addr + 1) == (rb_ptr->RxBuff + MaxBuffSize) ? rb_ptr->RxBuff : (addr + 1);
}


/**
* @brief   读字节函数
* @param   rb_ptr 缓冲区结构体地址
* @retvaldata
* @noticeNone
*/
ReadRetState_Typedef* ReadDataFromRingbuff(RingBuff_Typedef* rb_ptr)
{
    if (rb_ptr->RingbufCount > 0)
    {
      rb_ptr->ReadRetStateStruct.ptr = rb_ptr->Readpos;
      rb_ptr->ReadRetStateStruct.Readstate = Success;
      rb_ptr->Readpos = NextDataAddrHandle(rb_ptr, rb_ptr->Readpos);
      rb_ptr->RingbufCount--;
      return &rb_ptr->ReadRetStateStruct;
    }
    rb_ptr->ReadRetStateStruct.Readstate = Fail;
    rb_ptr->ReadRetStateStruct.ptr = NULL;
    return &rb_ptr->ReadRetStateStruct;
}


/**
* @brief   写字节函数
* @param   rb_ptr 缓冲区结构体地址,data 写入数据
* @retvalNone
* @noticeNone
*/
void WriteDataToRingbuff(RingBuff_Typedef* rb_ptr, unsigned char data)
{
    *(rb_ptr->Writepos) = data;
    rb_ptr->Writepos = NextDataAddrHandle(rb_ptr,rb_ptr->Writepos);
    rb_ptr->RingbufCount++;
}


/**
* @brief   读指定包长函数
* @param   rb_ptr 缓冲区结构体地址,head 包头,length 包长
* @retval读取状态及数据包地址
* @noticelength为整个数据包长即头置尾
*/
ReadRetState_Typedef* ReadEfectiveFrameFixLength(RingBuff_Typedef* rb_ptr, unsigned char head, unsigned char length)
{
    unsigned char count = rb_ptr->RingbufCount;            //标记触发前有效数据大小
    if (count < length)
    {
      rb_ptr->ReadRetStateStruct.Readstate = Fail;
      rb_ptr->ReadRetStateStruct.ptr = NULL;
      return &rb_ptr->ReadRetStateStruct;
    }
    while (count >= length)
    {
      rb_ptr->ReadRetStateStruct = *ReadDataFromRingbuff(rb_ptr);
      count--;
      if (rb_ptr->ReadRetStateStruct.Readstate == Success && *rb_ptr->ReadRetStateStruct.ptr == head)
      {
            return &rb_ptr->ReadRetStateStruct;
      }
    }
    rb_ptr->ReadRetStateStruct.Readstate = Fail;
    rb_ptr->ReadRetStateStruct.ptr = NULL;
    return &rb_ptr->ReadRetStateStruct;
}


/**
* @brief   读期望帧
* @param   rb_ptr 缓冲区结构体地址,str 期望帧
* @retval读取状态
* @noticeNone
*/
ReadRetState_Enum ReadEfectiveFrame(RingBuff_Typedef* rb_ptr, const char* str)
{
    unsigned char count = rb_ptr->RingbufCount;               //标记触发前有效数据大小                  
    unsigned char length = strlen(str);
    unsigned char i = 0;
    if (count < length)
    {
      return Fail;
    }
    while (count >= length)
    {
      rb_ptr->ReadRetStateStruct = *ReadDataFromRingbuff(rb_ptr);
      count--;
      if (rb_ptr->ReadRetStateStruct.Readstate == Success && *rb_ptr->ReadRetStateStruct.ptr == *(str + i))
      {
            count++;
            i++;
      }
      else
      {
            i = 0;
      }
      if (i == length)
      {
            return Success;
      }
    }
    return Fail;
}


/**
* @brief   匹配期望帧
* @param   rb_ptr 缓冲区结构体地址,str 期望帧所在注册表地址, ExpectFrameCount 期望帧注册表的个数
* @retval读取状态
* @noticeNone
*/
ReadRetState_Typedef* MatchExpectFrame(RingBuff_Typedef* rb_ptr, const char** str, unsigned char ExpectFrameCount)
{
    unsigned char Retcount = rb_ptr->RingbufCount;               
    unsigned char* RetPos = rb_ptr->Readpos;

    unsigned char i = 0;
    for (i = 0; i < ExpectFrameCount; i++)
    {
      if (ReadEfectiveFrame(rb_ptr, *(str + i)) == Success)
      {
            rb_ptr->ReadRetStateStruct.pos = i;
            rb_ptr->ReadRetStateStruct.Readstate = Success;
            return &rb_ptr->ReadRetStateStruct;
      }
      else
      {
            rb_ptr->RingbufCount = Retcount;
            rb_ptr->Readpos = RetPos;
      }
    }
    rb_ptr->ReadRetStateStruct.Readstate = Fail;
    return &rb_ptr->ReadRetStateStruct;
}


/******************** (C)COPYRIGHT(2016) YFTCEND OF FILE **********************/
H文件
view plaincopy
#ifndef __CX_RINGBUFF_H
#define __CX_RINGBUFF_H


#include "stdlib.h"
#include "string.h"


#define         MaxBuffSize      256      //缓冲区大小可任意设置,但最好为2^X


typedef enum
{
    Success = 1,
    Fail = !Success,
}ReadRetState_Enum;


typedef struct
{
    ReadRetState_Enum Readstate;                //读状态
    unsigned char* ptr;                         //读取字节地址
    unsigned charpos;                         //期望帧在注册表中位置
}ReadRetState_Typedef;



typedef struct
{
    unsigned char* Writepos;                  //写入地址
    unsigned char* Readpos;                     //读取地址
    unsigned char RingbufCount;               //有效未读数据大小
    ReadRetState_Typedef ReadRetStateStruct;    //读缓冲相关
    unsigned char RxBuff;          //数据缓存区
}RingBuff_Typedef;


void RingBuff_New(RingBuff_Typedef* rb_ptr);
unsigned char* NextDataAddrHandle(RingBuff_Typedef* rb_ptr,unsigned char* addr);
ReadRetState_Typedef* ReadDataFromRingbuff(RingBuff_Typedef* rb_ptr);
void WriteDataToRingbuff(RingBuff_Typedef* rb_ptr,unsigned char data);
ReadRetState_Typedef* ReadEfectiveFrameFixLength(RingBuff_Typedef* rb_ptr, unsigned char head, unsigned char length);
ReadRetState_Enum ReadEfectiveFrame(RingBuff_Typedef* rb_ptr, const char* str);
ReadRetState_Typedef* MatchExpectFrame(RingBuff_Typedef* rb_ptr, const char** str, unsigned char ExpectFrameCount);


#endif

tpgf 发表于 2024-12-2 15:42

在嵌入式开发中,环形缓冲区常用于设备通信,以确保数据在读取速度大于写入速度时不会丢失任何字节

pentruman 发表于 2024-12-2 21:29

环形缓冲区(Ring Buffer)是一种固定大小的数据结构,适用于嵌入式系统中的缓冲管理,特别是在处理流数据时。

mickit 发表于 2024-12-2 22:28

环形缓冲区的实现可以通过数组或链表来完成。数组实现方式简单且高效,适用于已知缓冲区大小的场景。链表实现方式则更加灵活,适用于需要动态扩展缓冲区大小的场景

heimaojingzhang 发表于 2024-12-3 09:40

环形缓冲算法是一种用于管理固定大小循环数组的数据结构,它允许数据以先进先出(FIFO)的方式存储和访问

keaibukelian 发表于 2024-12-3 13:43

环形缓冲区通常由一个固定大小的数组和一个写指针(tail)组成,读指针(head)指向缓冲区中的第一个数据元素;另一个是写指针(tail),指向下一个可写入数据的位置

paotangsan 发表于 2024-12-3 18:20

环形缓冲区可以高效地利用内存空间,避免了动态内存分配带来的开销和碎片问题

renzheshengui 发表于 2024-12-3 20:03

在实际应用中,环形缓冲区被广泛用于需要高效数据传输的场景,如实时数据处理、网络通信等

wowu 发表于 2024-12-3 21:59

环形缓冲区还可以与生产者-消费者模型结合使用,实现多线程或多任务之间的数据同步和通信

loutin 发表于 2024-12-6 19:36

环形缓冲区的读写操作可以通过简单的索引计算来实现,无需复杂的指针操作。

adolphcocker 发表于 2024-12-6 22:41

由于环形缓冲区的大小是固定的,因此可以预先分配一块连续的内存空间,避免了动态内存分配带来的开销。

mickit 发表于 2024-12-7 01:43

#define BUFFER_SIZE 1024

typedef struct {
    int buffer;
    int head;
    int tail;
    int count;
} RingBuffer;

void RingBuffer_Init(RingBuffer *rb) {
    rb->head = 0;
    rb->tail = 0;
    rb->count = 0;
}

int RingBuffer_IsFull(RingBuffer *rb) {
    return rb->count == BUFFER_SIZE;
}

int RingBuffer_IsEmpty(RingBuffer *rb) {
    return rb->count == 0;
}

int RingBuffer_Put(RingBuffer *rb, int data) {
    if (RingBuffer_IsFull(rb)) {
      return -1; // Buffer is full
    }
    rb->buffer = data;
    rb->head = (rb->head + 1) % BUFFER_SIZE;
    rb->count++;
    return 0;
}

int RingBuffer_Get(RingBuffer *rb, int *data) {
    if (RingBuffer_IsEmpty(rb)) {
      return -1; // Buffer is empty
    }
    *data = rb->buffer;
    rb->tail = (rb->tail + 1) % BUFFER_SIZE;
    rb->count--;
    return 0;
}

ingramward 发表于 2024-12-7 04:46

通常用于在生成数据和消费数据之间提供平滑的数据流,特别是在数据生成速率和消费速率不一致的情况下。

sesefadou 发表于 2024-12-7 07:48

头指针(Head):指向下一个数据将要写入的位置。
尾指针(Tail):指向下一个数据将要被读取的位置。
空状态:当头指针和尾指针指向同一个位置时,缓冲区为空。
满状态:当头指针紧随尾指针时(考虑缓冲区大小),缓冲区为满。

jtracy3 发表于 2024-12-9 11:32

环形缓冲区是一种先进先出(FIFO)的存储空间,其特点是首尾相连形成了闭环。这种结构允许数据在达到缓冲区末尾时自动回绕到起始位置,从而实现数据的循环存储和管理。

rosemoore 发表于 2024-12-9 14:52

环形缓冲区是嵌入式系统中一个非常实用且高效的数据结构,适用于各种需要缓冲区的场景,如信号处理、通信协议处理等。

AdaMaYun 发表于 2024-12-10 08:17

环形缓冲的优势是什么?

ingramward 发表于 2024-12-10 16:58

环形缓冲区的特点是其两端相连,形成一个闭环,当缓冲区填满后,新的数据会覆盖旧的数据。这种数据结构非常适合于需要缓存连续数据流的场景,如音频处理、网络通信和实时数据采集等。

deliahouse887 发表于 2024-12-10 17:54

环形缓冲区可以存储一定数量的数据,并在数据满后通过覆盖最早的数据来继续存储新数据,实现“先进先出”(FIFO)的数据管理方式。

sesefadou 发表于 2024-12-10 18:23

环形缓冲的实现相对简单,主要涉及到读指针和写指针的操作。与其他复杂的缓冲区管理方式相比,它的代码量较少,易于理解和维护。在嵌入式软件开发中,简单的代码结构可以降低系统的复杂度,减少出错的可能性。
页: [1] 2
查看完整版本: 嵌入式常用算法:环形缓冲