C语言实现循环缓冲区(环形缓冲区)

嵌入式开发中,由于资源有限,经常会用到循环缓冲区,可实现数据流的异步处理,一般使用的是共享内存:只需一定长度的共享内存就可以循环传输大量数据进行通信。
如果使用链表,会涉及到内存释放的问题。

基于C语言实现如下:
ringBuffer.h

#ifndef RINGBUFFER_H_
#define RINGBUFFER_H_
typedef unsigned char u8;
typedef unsigned int u32;

void initRingbuffer(void);
int wirteRingbuffer(u8* buffer,u32 len);
int readRingbuffer(u8* buffer,u32 len);
u32 getRingbufferValidLen(void);
void releaseRingbuffer(void);

#endif /* RINGBUFFER_H_ */

ringBuffer.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "ringBuffer.h"
#define BUFFER_SIZE  512   //缓冲区的长度,可以修改

static u32 validLen;//已使用的数据长度
static u8* pHead = NULL;//环形存储区的首地址
static u8* pTail = NULL;//环形存储区的结尾地址
static u8* pValid = NULL;//已使用的缓冲区的首地址
static u8* pValidTail = NULL;//已使用的缓冲区的尾地址

/*
 * 初始化环形缓冲区
 * 环形缓冲区这里可以是malloc申请的内存,也可以是Flash存储介质
 * */
void initRingbuffer(void)
{
	if(pHead == NULL)
	{
		pHead = (u8*) malloc(BUFFER_SIZE);
	}
	pValid = pValidTail = pHead;
	pTail = pHead + BUFFER_SIZE;
	validLen = 0;
}

/*
 * function:向缓冲区中写入数据
 * param:@buffer 写入的数据指针
 * 		 @addLen 写入的数据长度
 * return:-1:写入长度过大
 * 		  -2:缓冲区没有初始化
 * */
int wirteRingbuffer(u8* buffer,u32 addLen)
{
	if(addLen > BUFFER_SIZE) return -2;
	if(pHead==NULL) return -1;
	assert(buffer);

	//将要存入的数据copy到pValidTail处
	if(pValidTail + addLen > pTail)//需要分成两段copy
	{
		int len1 = pTail - pValidTail;
		int len2 = addLen - len1;
		memcpy( pValidTail, buffer, len1);
		memcpy( pHead, buffer + len1, len2);
		pValidTail = pHead + len2;//新的有效数据区结尾指针
	}else
	{
		memcpy( pValidTail, buffer, addLen);
		pValidTail += addLen;//新的有效数据区结尾指针
	}

	//需重新计算已使用区的起始位置
	if(validLen + addLen > BUFFER_SIZE)
	{
		int moveLen = validLen + addLen - BUFFER_SIZE;//有效指针将要移动的长度
		if(pValid + moveLen > pTail)//需要分成两段计算
		{
			int len1 = pTail - pValid;
			int len2 = moveLen - len1;
			pValid = pHead + len2;
		}else
		{
			pValid = pValid + moveLen;
		}
		validLen = BUFFER_SIZE;
	}else
	{
		validLen += addLen;
	}

	return 0;
}

/*
 * function:从缓冲区内取出数据
 * param   :@buffer:接受读取数据的buffer
 *		    @len:将要读取的数据的长度
 * return  :-1:没有初始化
 *	 	    >0:实际读取的长度
 * */
int readRingbuffer(u8* buffer,u32 len)
{
	if(pHead==NULL) return -1;

	assert(buffer);

	if(validLen ==0) return 0;

	if( len > validLen) len = validLen;

	if(pValid + len > pTail)//需要分成两段copy
	{
		int len1 = pTail - pValid;
		int len2 = len - len1;
		memcpy( buffer, pValid, len1);//第一段
		memcpy( buffer+len1, pHead, len2);//第二段,绕到整个存储区的开头
		pValid = pHead + len2;//更新已使用缓冲区的起始
	}else
	{
		memcpy( buffer, pValid, len);
		pValid = pValid +len;//更新已使用缓冲区的起始
	}
	validLen -= len;//更新已使用缓冲区的长度

	return len;
}

/*
 * function:获取已使用缓冲区的长度
 * return  :已使用的buffer长度
 * */
u32 getRingbufferValidLen(void)
{
	return validLen;
}

/*
 * function:释放环形缓冲区
 * */
void releaseRingbuffer(void)
{
	if(pHead!=NULL) free(pHead);
	pHead = NULL;
}

main.c

/*
-------循环缓冲区----------
2023/1/11
*/
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include "ringBuffer.h"


#define N 256
int num = 0;
int readLen;
u8 iData[N];
u8 readBuffer[N];
u8 pData[25*N];
int rcnt=0;


FILE *fp = NULL;

u32 validLen = 0;

void getRandNum(u8 data[])
{
	for (int i = 0; i < N; i++)
	{
		int tmp=rand()% N;//产生0-255的随机数(uint8)
		data[i] = tmp;
		//printf("data[%d]=%d\n",i,tmp);
	}
}

void *func1(void *ptr)
{
	while (1)
	{
		printf("func1\n");
		//向缓冲区写入数据
		getRandNum(iData);

		wirteRingbuffer((u8 *)&iData, N);
		sleep(1);
	}
}

void *func2(void *ptr)
{
	while (1)
	{
		printf("func2\n");
		//从缓冲区读取数据
		readLen = readRingbuffer(readBuffer,N);
		printf("*****readLen=%d\n", readLen);

		for(int i=0;i<readLen;i++)
		{
			pData[rcnt*readLen+i]=readBuffer[i];
			//printf("readBuffer[%d]=%d\n", i,readBuffer[i]);
		}
		rcnt++;
		if(rcnt==25)
		{
			printf("rcnt=%d\n", rcnt);
			rcnt=0;
		}
		sleep(1);
	}
}

int main()
{
	//fp = fopen("saveData.txt", "wb");
	pthread_t tid1, tid2;
	initRingbuffer();//初始化缓冲区
	//创建两个线程
	pthread_create(&tid1, NULL, func1, NULL);//线程:向缓冲区中写数据
	pthread_create(&tid2, NULL, func2, NULL);//线程:从缓冲区中读数据
	// 等待两个线程结束
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);

	while (1)
	{
		printf("%d:main\n", ++num);
		sleep(1);
	}
	fclose(fp);
	return 0;
}

运行结果:
在这里插入图片描述
工程实现

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以使用数组来实现环形缓冲区。下面是一个简单的示例代码: ```c #define BUFFER_SIZE 10 typedef struct { int buffer[BUFFER_SIZE]; int head; // 缓冲区头部索引 int tail; // 缓冲区尾部索引 } CircularBuffer; // 初始化环形缓冲区 void initCircularBuffer(CircularBuffer* cb) { cb->head = 0; cb->tail = 0; } // 向环形缓冲区中写入数据 void writeCircularBuffer(CircularBuffer* cb, int data) { cb->buffer[cb->tail] = data; cb->tail = (cb->tail + 1) % BUFFER_SIZE; } // 从环形缓冲区中读取数据 int readCircularBuffer(CircularBuffer* cb) { int data = cb->buffer[cb->head]; cb->head = (cb->head + 1) % BUFFER_SIZE; return data; } ``` 使用示例: ```c int main() { CircularBuffer cb; initCircularBuffer(&cb); writeCircularBuffer(&cb, 10); writeCircularBuffer(&cb, 20); writeCircularBuffer(&cb, 30); int data1 = readCircularBuffer(&cb); int data2 = readCircularBuffer(&cb); printf("%d\n", data1); // 输出: 10 printf("%d\n", data2); // 输出: 20 return 0; } ``` 这个示例中,我们使用数组 `buffer` 来存储数据,使用 `head` 和 `tail` 分别表示缓冲区的头部和尾部索引。在写入数据时,将数据存储在 `tail` 所指向的位置,并将 `tail` 的索引递增一位。在读取数据时,从 `head` 所指向的位置读取数据,并将 `head` 的索引递增一位。当 `tail` 或 `head` 达到缓冲区的末尾时,我们使用取余运算将其重新定位到缓冲区的开头,实现循环利用缓冲区的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linda Fan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值