你读懂了环形队列吗?如何去更好的实现环形队列?

偶尔在网络看到有人推荐了一个开源的环形队列的代码,总体思路没有问题,但是仔细读了一下,发现考虑得不是很严谨,有不少的bug,大家有兴趣都可以分析分析,对于学习很有帮助。
下面的文件代码中有详细中文注释,可以参考。
另外,推荐一个我实现的简单但是完整的环形队列实现的消息系统,更加实用和简单。
《一个用于嵌入式开发的简单可靠的消息系统-STOpen消息系统》
源代码和注释如下:

//**************************************************************************************
///	File Name	: \file zoBuffer.h 
///	Description	: \brief Ring byte buffer function library.
//	Created		: 21/01/2010
//	Target MCU	: all
//	Author		: Sissakis Giannis
//  email		: info@01mech.com
//
//  Copyright (C) 2010 Zero One Mechatronics LP
//
//  This program is free software: you can redistribute it and/or modify it under the 
//  terms of the GNU General Public License as published by the Free Software Foundation, 
//  version 3 of the License or any later version. This program is distributed in the hope
//  that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of 
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public 
//  License for more details: <http://www.gnu.org/licenses/>
//  
/// \ingroup soft 
/// \defgroup zoBuffer zoBuffer.h : Byte ring buffers.
/// 
/// \par Overview
/// This library provides byte buffering functionality. The buffer is protected from
/// data corruption that may occur by the interference of ISR code, thus it can be 
/// declared at local or global scope with no worries.
///   
/// \par Usage example 
/// \code
/// #include "zoTypes.h"
/// #include "zoBuffer.h"
///
/// ZO_BUFFER MyBuffer = ZO_BUFFER_DEFAULTS;    //declare a global buffer
///
/// int main(void)
/// {
///     u08 c;
///       
///     zoBufferInit(&MyBuffer,10); 		//initialize the buffer to a size of ten bytes
///        
///     zoBufferPut(&MyBuffer,'i'); 		//put the char i into the buffer
/// 
///     if(!zoBufferIsFull())   			//check if the buffer is full; if not:
///         zoBufferPut(&MyBuffer,23);		//put the number 23 into the buffer
///
///     if(!zoBufferIsEmpty())  			//check if the buffer is empty; if not:
///         c = zoBufferGet(&MyBuffer); 	//get the 'oldest' byte stored in the buffer
/// 									    //thus 'i' is assigned to c.
/// }
/// \endcode 
//****************************************************************************************
//@{

#ifndef ZO_BUFFER_H
#define ZO_BUFFER_H

#include "zoTypes.h"

//Types and defaults______________________________________________________________________
///Buffer type
typedef volatile struct {
	volatile u08 size;	///< Size of the ring buffer
	volatile u08 ctr;	///< how many bytes are currently in the buffer
	u08* pIn;			///< where to put next incoming byte
	u08* pOut;			///< from where to take next outgoing byte
	u08 *data;			///< pointer to byte storage
}ZO_BUFFER;

///Default ZO_BUFFER variable initializer.
#define ZO_BUFFER_DEFAULTS {0,0,0,0,0}

//Function declarations___________________________________________________________________
/*! \brief Initializes a ZO_BUFFER variable. Always initialize a buffer variable with this
	function before calling any other function. 
    \param *p A pointer to the buffer variable.
    \param  size  The maximum number of bytes the buffer can store.
    \return success of memory allocation for the buffer */
bool zoBufferInit(ZO_BUFFER *p,u08 size);           

/*! \brief Stores a byte in the buffer. Check for a space in the buffer with 
             zoBufferIsFull() function. 
    \param *p Pointer to the buffer variable.
    \param  data  The byte to store.*/
void zoBufferPut(ZO_BUFFER *p,const u08 data);

/*! \brief Gets a byte from the buffer. Check if there are any data in the buffer with 
    the zoBufferIsEmpty() function.
    \param *p Pointer to the buffer variable.
    \return the 'oldest' byte stored last in the buffer. If the buffer is empty the byte
            returned is garbage.*/
u08  zoBufferGet(ZO_BUFFER *p);

/*! \brief Reads a byte in the buffer at specific index. The zoBufferRead() function does 
    not take the byte out of he buffer like the zoBufferGet() function does.
    \param *p Pointer to the buffer variable.
    \param  index  Where to read the byte in the buffer.Index is relative to the 'oldest' 
    byte stored in the buffer. Thus index=0 means the 'oldest' byte in the buffer, index=1 
    the next and so on. If index is greater than the buffer size the return byte is 
    garbage.
    \return the byte read. */
u08  zoBufferRead(ZO_BUFFER *p,u08 index);

/*! \brief Checks if the buffer is empty. 
    \param *p Pointer to the buffer variable.
    \return true if the buffer is empty, false if it isn't. */
bool zoBufferIsEmpty(ZO_BUFFER *p);

/*! \brief Checks if the buffer is full. 
    \param *p Pointer to the buffer variable.
    \return true if the buffer is full, false if it isn't */
bool zoBufferIsFull(ZO_BUFFER *p);

/*! \brief Flushes the buffer. All data in the buffer are discarded.
    \param *p Pointer to the buffer variable. */
void zoBufferFlush(ZO_BUFFER *p);

//!Inline unprotected version of zoBufferPut() for use in ISRs
void zoBufferPutIsr(ZO_BUFFER *p,const u08 data);

//!Inline unprotected version of zoBufferGet() for use in ISRs
u08  zoBufferGetIsr(ZO_BUFFER *p);

//!Inline unprotected version of zoBufferRead() for use in ISRs
u08	 zoBufferReadIsr(ZO_BUFFER *p,u08 index);

#endif //ZO_BUFFER_H
//@}
#include "zoBuffer.h"
#include <stdlib.h>
#include <string.h>
#include "zoMcu.h"

//判断buffer是否为空
inline bool zoBufferIsEmpty(ZO_BUFFER *p)
{
	return (p->ctr > 0)?FALSE:TRUE;	//直接判断计数器
}
//判断buffer是否满
inline bool zoBufferIsFull(ZO_BUFFER *p)
{
	return (p->ctr < p->size)?FALSE:TRUE;
}
//清空队列
void zoBufferFlush(ZO_BUFFER *p)
{
	enterCritical();
	p->pIn = p->data;		
	p->pOut = p->data;	//输入输出的位置都设置为初始位置
	p->ctr = 0;					//初始计数为0
	exitCritical();
}
//
//函数采用calloc方式初始化,在一般小系统中没有必要去实现这个调用,费内存不安全
//可以把这个函数接口修改为zoBufferInit(ZO_BUFFER *p,u08 *pBuffer,08 size)
//这样就更具有灵活性,可以从外面传递一个buffer指针来初始化
bool zoBufferInit(ZO_BUFFER *p,u08 size)
{
	bool success = FALSE;
	enterCritical();

	p->ctr = 0;						//initialize counter to 0
	p->size = size;					//store size
	p->data = (u08*)calloc(size,1);		//allocate memory for data
	
	if( p->data != NULL )				//if allocation was successfull
	{
		p->pIn = p->data;	//initialize in pointer to start of data
		p->pOut = p->data;	//initialize out pinter to start of data
		success = TRUE;
	}

	exitCritical();

	return success;
}
//在中断里面读取数据
inline u08  zoBufferGetIsr(ZO_BUFFER *p)
{
	u08 c;
//1.这里有一个bug,如果用户没有判断buffer是否有数据,进来p->ctr为0的情况,会出错。
//所以就要去用户调用前先判断。
	p->ctr--;							//decrement count of bytes in buffer
	c = *p->pOut++;					//get one piece of data
	if(p->pOut == &p->data[p->size])	//wrap pout pointer
		p->pOut = &p->data[0];
	
	return c;
}
//在任务或者外循环读取数据
u08  zoBufferGet(ZO_BUFFER *p)
{
	u08 c;
	
	enterCritical();
	c= zoBufferGetIsr(p);
	exitCritical();
	return c;	
}
//在中断里面放数据
//这个函数也有一个bug。如果一直没有读取数据,一直在放数据,数据满后重新
//覆盖了原来的位置,导致实际数据量和计数的数据量不一致。
//比如buffer总计100,无论你如何放数据,因为是循环放数据,所以最大数据量
//只有100,但是p->ctr却在一直增加。所以你读数据无论如何也不可能读到超过
//100个数据,其中很多数据就变的重复了。
//所以要保证读到的是新的数据,要同步移动out指针位置。
inline void zoBufferPutIsr(ZO_BUFFER *p,const u08 data)
{
	p->ctr++;
	*p->pIn++ = data;
	if(p->pIn == &p->data[p->size])	//wrap the pIn ptr
		p->pIn = &p->data[0];
/*
//放入的数据回绕超过了取数据的位置,出队的数据位置要调整到下一个,
//不然就会取到的是最新的数据,而不是先进先出的数据
		if((p->pIn > p->pOut) && (p->ctr > p->pIn - &p->data[0]))
		{
			p->pOut++;
			if(p->pOut == &p->data[p->size])	//wrap pout pointer
			p->pOut = &p->data[0];		
		}
	}
*/		
}
//在任务或者主循环放数据
void zoBufferPut(ZO_BUFFER *p,const u08 data)
{
	enterCritical();
	zoBufferPutIsr(p,data);
	exitCritical();
}
//中断函数内调用读buffer接口
//这里如果index的数值大于p->size也是有一个bug
inline u08 zoBufferReadIsr(ZO_BUFFER *p,u08 index)
{
	u08* c;
	
	c = p->pOut + index;
	if( c > &p->data[p->size] )
		c = p->pOut - p->size + index;
	
	return *c;
}
//在任务或者主循环调用读buffer的接口
u08  zoBufferRead(ZO_BUFFER *p,u08 index)
{
	u08 c;
	enterCritical();
	c = zoBufferReadIsr(p,index);
	exitCritical();
	return c;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值