偶尔在网络看到有人推荐了一个开源的环形队列的代码,总体思路没有问题,但是仔细读了一下,发现考虑得不是很严谨,有不少的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;
}