忙着毕设,很久没有写文章了,终于答辩完了,得了个校优秀毕业设计。毕设做的是个智能接口模块,用一周时间入门了,MC9S12XEP100的开发,又用一周时间入门了uC/OS-II嵌入式操作系统,在做毕设的过程中学到了很多,现在把一些工具发上来分享。

这里分享一个自己用纯C实现的环形缓冲区。环形缓冲区有很多作用,比如嵌入式中的通信可以用环形缓冲区作为信道,一个线程往里放字节,一个线程取字节进行处理,只要保证取的速度大于读的速度,就可以保证通信顺畅进行,不丢一个字节。

简要介绍:

环形缓冲区其实就是一个队列,里头的元素是先入先出的,但是因为其(逻辑上)是环形的,所以不需要像很多队列的实现那样在内部元素变动的时候需要移动内部剩下的元素。这样就使元素出队入队的时间复杂度只有O(1)。具体实现一般有链表和数组两种方法,当不能确定需要的缓冲区大小时使用链表较好,能确定时使用数组可以节省很多动态分配内存的开销。

在嵌入式开发中,一般不动态分配内存,而是使用静态分配的数组。所以这里我使用数组实现了环形缓冲区,为了能够在不同的程序中复用代码,使用结构体模拟了面向对象编程,这样就可以用一套代码管理不同的缓冲区了。

废话不多说,直接上代码。以下是.h 文件:

/*

*********************************************************************************************************

*

*

* RingQueueStruct

* 环形队列结构

*

* File : RingQueue.h

* By : Lin Shijun(http://blog.csdn.net/lin_strong)

* Date : 2018/02/23

* version: V1.2

* NOTE(s): 这段程序用来对一个给定的缓冲区进行模拟环形队列的管理

* 程序本身不会自动分配缓冲区空间,用户需要自己负责分配空间,并且要保证不直接访问缓存区

* // 在某处分配内存空间

* RQTYPE buffer[BUFFER_SIZE];

* RING_QUEUE que,*ptr_que;

* unsigned char err;

* // 初始化

* ptr_que = RingQueueInit(&que,buffer,BUFFER_SIZE,&err);

* if(err == RQ_ERR_NONE ){

* // 初始化成功,使用其他函数

* }

* History : 2017/04/25 the original version of RingQueueStruct.

* 2017/10/16 put functions used frequently,RingQueueIn and RingQueueOut, in non-banked address;

* modify single line function RingQueueIsEmpty and RingQueueIsFull to marco function;

* to get better efficiency.

* 2018/02/23 1.add the marco(RQ_ARGUMENT_CHECK_EN) to controll argument check so user can save

* more code.

* 2.add the ADDRESSING MODE so the buffer can be defined in banked addressing area.

*********************************************************************************************************

*/

#ifndef RING_QUEUE_H

#define RING_QUEUE_H

/*

********************************************************************************************

* MISCELLANEOUS

********************************************************************************************

*/

#ifndef FALSE

#define FALSE 0

#endif

#ifndef TRUE

#define TRUE 1

#endif

/*

*********************************************************************************************************

* ADDRESSING MODE 寻址模式

*********************************************************************************************************

*/

// uncomment the corresponding line to select the addressing mode to the buffer of RingQueue module.

// if you don't understand. Just use the extended addressing mode

// 取消对应行的注释以选择环形缓冲区模块访问缓冲区时使用的寻址方式

// 如果你不知道这是什么意思的话,那就用扩展寻址就行了,这是默认的方式

// extended addressing mode 扩展区寻址(默认)

#define RQ_ADDRESSING_MODE

// banked RAM addressing mode RAM分页区寻址

//#define RQ_ADDRESSING_MODE __rptr

// global addressing mode 全局寻址

//#define RQ_ADDRESSING_MODE __far

/*

*********************************************************************************************************

* CONFIGURATION 配置

*********************************************************************************************************

*/

#define RQ_ARGUMENT_CHECK_EN TRUE // TRUE: arguments will be checked, however,this will

// cost a little code volume.

/*

*********************************************************************************************************

* CONSTANTS 常量

*********************************************************************************************************

*/

#define RQ_ERR_NONE 0u

#define RQ_ERR_POINTER_NULL 1u

#define RQ_ERR_SIZE_ZERO 2u

#define RQ_ERR_BUFFER_FULL 3u

#define RQ_ERR_BUFFER_EMPTY 4u

#define RQ_OPTION_WHEN_FULL_DISCARD_FIRST 0u // discard the first element when ring buffer is full

#define RQ_OPTION_WHEN_FULL_DONT_IN 1u // discard new element when ring buffer is full

/*

*********************************************************************************************************

* DATA TYPE 数据类型

*********************************************************************************************************

*/

// define the data type that stores in the RingQueue. 定义存在环形缓冲区内的数据的类型

typedef unsigned char RQTYPE;

typedef RQTYPE *RQ_ADDRESSING_MODE pRQTYPE;

typedef struct {

unsigned short RingBufCtr; /* Number of characters in the ring buffer */

unsigned short RingBufSize; /* Ring buffer Size */

pRQTYPE RingBufInPtr; /* Pointer to where next character will be inserted */

pRQTYPE RingBufOutPtr; /* Pointer from where next character will be extracted */

pRQTYPE RingBuf; /* Ring buffer array */

pRQTYPE RingBufEnd; /* Point to the end of the buffer */

} RING_QUEUE;

/*

*********************************************************************************************************

* FUNCTION PROTOTYPES 函数原型

*********************************************************************************************************

*/

RING_QUEUE *RingQueueInit(RING_QUEUE *pQueue,pRQTYPE pbuf,unsigned short bufSize,unsigned char *perr);

#pragma CODE_SEG __NEAR_SEG NON_BANKED

unsigned short RingQueueIn(RING_QUEUE *pQueue,RQTYPE data,unsigned char option,unsigned char *perr);

RQTYPE RingQueueOut(RING_QUEUE *pQueue,unsigned char *perr);

#pragma CODE_SEG DEFAULT

short RingQueueMatch(RING_QUEUE *pQueue,pRQTYPE pbuf,unsigned short len);

void RingQueueClear(RING_QUEUE *pQueue);

/*

*********************************************************************************************************

* RingQueueIsEmpty()

*

* Description : whether the RingQueue is empty. 环形队列是否为空

*

* Arguments : pQueue pointer to the ring queue control block; 指向环形队列控制块的指针

*

* Return : TRUE the RingQueue is empty.

* FALSE the RingQueue is not empty.

* Note(s) :

*********************************************************************************************************

*/

#define RingQueueIsEmpty(pQueue) ((pQueue)->RingBufCtr == 0)

/*

*********************************************************************************************************

* RingQueueIsFull()

*

* Description : whether the RingQueue is full. 环形队列是否为空

*

* Arguments : pQueue pointer to the ring queue control block; 指向环形队列控制块的指针

*

* Return : TRUE the RingQueue is full.

* FALSE the RingQueue is not full.

* Note(s) :

*********************************************************************************************************

*/

#define RingQueueIsFull(pQueue) ((pQueue)->RingBufCtr >= (pQueue)->RingBufSize)

#endif

然后下面是.c文件。

/*

*********************************************************************************************************

*

*

* RingQueueStruct

* 环形队列结构

*

* File : RingQueue.c

* By : Lin Shijun(http://blog.csdn.net/lin_strong)

* Date : 2018/02/23

* version: V1.2

* NOTE(s):

*

* History : 2017/04/25 the original version of RingQueueStruct.

* 2017/10/16 put functions used frequently,RingQueueIn and RingQueueOut, in non-banked address;

* modify single line function RingQueueIsEmpty and RingQueueIsFull to marco function;

* to get better efficiency.

* 2018/02/23 1.add the marco(RQ_ARGUMENT_CHECK_EN) to controll argument check so user can save

* more code.

* 2.add the ADDRESSING MODE so the buffer can be defined in banked addressing area.

*********************************************************************************************************

*/

/*

*********************************************************************************************************

* INCLUDES

*********************************************************************************************************

*/

#include "RingQueue.h"

/*

*********************************************************************************************************

* LOCAL FUNCTION DECLARATION

*********************************************************************************************************

*/

#if(RQ_ARGUMENT_CHECK_EN == TRUE)

#define argCheck(cond,err,rVal) if(cond) { *perr = (err); return (rVal); }

#else

#define argCheck(cond,err,rVal)

#endif // of (SPI_ARGUMENT_CHECK_EN == TRUE)

/*

*********************************************************************************************************

* LOCAL FUNCTION DECLARE

*********************************************************************************************************

*/

#pragma CODE_SEG __NEAR_SEG NON_BANKED

// 内部使用,给定将给定指针在环形缓冲区内向前移动一步(到尾了会移回头)

static void _forwardPointer(RING_QUEUE *pQueue,pRQTYPE* pPointer);

#pragma CODE_SEG DEFAULT

/*

*********************************************************************************************************

* RingQueueInit()

*

* Description : To initialize the ring queue. 初始化环形队列

*

* Arguments : pQueue pointer to the ring queue control block; 指向环形队列控制块的指针

* pbuf pointer to the buffer(an array); 指向自定义的缓冲区(实际就是个数组)

* bufSize the Size of the buffer; 缓冲区的大小;

* perr a pointer to a variable containing an error message which will be set by this

* function to either:

*

* RQ_ERR_NONE

* RQ_ERR_SIZE_ZERO

* RQ_ERR_POINTER_NULL

*

* Return : the pointer to the ring queue control block; 返回指向环形队列控制块的指针

* 0x00 if any error; 如果出错了则返回NULL

*

*Note(s):

*********************************************************************************************************

*/

RING_QUEUE* RingQueueInit(RING_QUEUE *pQueue,pRQTYPE pbuf,unsigned short bufSize,unsigned char *perr){

argCheck(pQueue == 0x00 || pbuf == 0x00,RQ_ERR_POINTER_NULL,0x00);

argCheck(bufSize == 0,RQ_ERR_SIZE_ZERO,0x00);

pQueue->RingBufCtr = 0;

pQueue->RingBuf = pbuf;

pQueue->RingBufInPtr = pbuf;

pQueue->RingBufOutPtr = pbuf;

pQueue->RingBufSize = bufSize;

pQueue->RingBufEnd = pbuf + bufSize;

*perr = RQ_ERR_NONE;

return pQueue;

}

/*

*********************************************************************************************************

* RingQueueIn()

*

* Description : Enqueue an element. 入队一个元素

*

* Arguments : pQueue pointer to the ring queue control block; 指向环形队列控制块的指针

* data the data to enqueue; 要入队的数据

* option option when queue is full ,you can choose: 当队列满的时候的选项,你可以选择:

* RQ_OPTION_WHEN_FULL_DISCARD_FIRST 抛弃队头的元素来填进去新的元素

* RQ_OPTION_WHEN_FULL_DONT_IN 不入队新给的元素

* perr a pointer to a variable containing an error message which will be set by this

* function to either:

*

* RQ_ERR_NONE if no err happen

* RQ_ERR_POINTER_NULL if pointer is 0x00

* RQ_ERR_BUFFER_FULL if buffer is full

*

* Return : the Elements Count after enqueue the element

* 调用函数后队列中的元素个数

*Note(s) :

*********************************************************************************************************

*/

#pragma CODE_SEG __NEAR_SEG NON_BANKED

unsigned short RingQueueIn(RING_QUEUE *pQueue,RQTYPE data,unsigned char option,unsigned char *perr){

argCheck(pQueue == 0x00,RQ_ERR_POINTER_NULL,0x00);

if(pQueue->RingBufCtr >= pQueue->RingBufSize){

*perr = RQ_ERR_BUFFER_FULL;

if(option == RQ_OPTION_WHEN_FULL_DISCARD_FIRST){

_forwardPointer(pQueue,&pQueue->RingBufOutPtr); /* Wrap OUT pointer */

}else{ // option == RQ_OPTION_WHEN_FULL_DONT_IN

return pQueue->RingBufCtr;

}

}else{

pQueue->RingBufCtr++; /* No, increment character count */

*perr = RQ_ERR_NONE;

}

*pQueue->RingBufInPtr = data; /* Put character into buffer */

_forwardPointer(pQueue,&pQueue->RingBufInPtr); /* Wrap IN pointer */

return pQueue->RingBufCtr;

}

/*

*********************************************************************************************************

* RingQueueOut()

*

* Description : Dequeue an element. 出队一个元素

*

* Arguments : pQueue pointer to the ring queue control block; 指向环形队列控制块的指针

* perr a pointer to a variable containing an error message which will be set by this

* function to either:

*

* RQ_ERR_NONE if no err happen

* RQ_ERR_POINTER_NULL if pointer is 0x00

* RQ_ERR_BUFFER_EMPTY if buffer is empty

*

* Return : 0 if any error or the data is 0;

* others the data

*

*Note(s):

*********************************************************************************************************

*/

RQTYPE RingQueueOut(RING_QUEUE *pQueue,unsigned char *perr){

RQTYPE data;

argCheck(pQueue == 0x00,RQ_ERR_POINTER_NULL,0x00);

if(pQueue->RingBufCtr == 0){

*perr = RQ_ERR_BUFFER_EMPTY;

return 0;

}

pQueue->RingBufCtr--; /* decrement character count */

data = *pQueue->RingBufOutPtr; /* Get character from buffer */

_forwardPointer(pQueue,&pQueue->RingBufOutPtr); /* Wrap OUT pointer */

*perr = RQ_ERR_NONE;

return data;

}

#pragma CODE_SEG DEFAULT

/*

*********************************************************************************************************

* RingQueueMatch()

*

* Description : Match the given buffer in RingQueue 在环形队列中匹配给定缓冲区

*

* Arguments : pQueue pointer to the ring queue control block; 指向环形队列控制块的指针

* pbuf pointer to the chars need to match;

* len the length of the chars

* Return : -1 Don't match -1 则没有匹配到

* >= 0 match >= 0 则匹配到了

*

*Note(s):

*********************************************************************************************************

*/

short RingQueueMatch(RING_QUEUE *pQueue,pRQTYPE pbuf,unsigned short len){

pRQTYPE pPosQ,pCurQ,pCurB,pEndB;

unsigned short rLen,Cnt;

if(len > pQueue->RingBufCtr)

return -1;

pPosQ = pQueue->RingBufOutPtr;

pEndB = pbuf + len;

Cnt = 0;

rLen = pQueue ->RingBufCtr;

while(rLen-- >= len){ // if remian length of queue bigger than buffer. continue

pCurQ = pPosQ;

pCurB = pbuf;

while(pCurB != pEndB && *pCurQ == *pCurB) { // compare one by one,until match all(pCurB==pEndB) or some one don't match

_forwardPointer(pQueue,&pCurQ);

pCurB++;

}

if(pCurB == pEndB) // if match all

return Cnt;

Cnt++;

_forwardPointer(pQueue,&pPosQ);

}

return -1;

}

/*

*********************************************************************************************************

* RingQueueClear()

*

* Description: Clear the RingQueue. 清空环形队列

*

* Arguments : pQueue pointer to the ring queue control block; 指向环形队列控制块的指针

*

* Return:

*

* Note(s):

*********************************************************************************************************

*/

void RingQueueClear(RING_QUEUE *pQueue){

#if(RQ_ARGUMENT_CHECK_EN == TRUE)

if(pQueue == 0x00)

return;

#endif

pQueue->RingBufCtr = 0;

pQueue->RingBufInPtr = pQueue -> RingBufOutPtr;

}

/*

*********************************************************************************************************

* LOCAL FUNCTION

*********************************************************************************************************

*/

#pragma CODE_SEG __NEAR_SEG NON_BANKED

static void _forwardPointer(RING_QUEUE *pQueue,pRQTYPE* pPointer){

if (++*pPointer == pQueue->RingBufEnd)

*pPointer = pQueue->RingBuf; /* Wrap OUT pointer */

}

#pragma CODE_SEG DEFAULT

简单解释下。在.h文件中定义了一个环形缓冲区的控制块,当然也可以当其为一个环形缓冲区对象,用户需要为每个环形缓冲区分配一个控制块和其缓冲区(也就是一个数组)。理想情况下,虽然用户知道控制块的结构,但也不应该直接访问内部字段,而应该通过提供的函数来访问。

队列中默认的元素是无符号字符,如果要改成缓存其他类型的话改下.h文件中的

typedef unsigned char RQTYPE;

这行就行了。

使用示例:

#include "RingQueue.h"

#define RX_BUF_MAX_SIZE 200 // 定义缓冲区的最大大小为200

static unsigned char RxBuffer[RX_BUF_MAX_SIZE]; // 定义缓冲区

static RING_QUEUE RxRingQ; // 定义环形缓冲区的控制块

void main(){

unsigned char err;

// 初始化缓冲区

RingQueueInit(&RxRingQ,RxBuffer,RX_BUF_MAX_SIZE,&err);

if(err != RQ_ERR_NONE){

//初始化缓冲区失败的处理

}

……

}

然后调用所有方法都需要传递环形缓冲区控制块的指针。如入队就像:

// 往RxRingQ缓冲区内入队一个元素c,如果满的话丢弃第一个元素

RingQueueIn(&RxRingQ,c,RQ_OPTION_WHEN_FULL_DISCARD_FIRST,&err);

出队就像:

// 从RxRingQ缓冲区内提取一个字符

c = RingQueueOut(&RxRingQ,&err);

其他就不一 一举例了。要特别说明下的是RingQueueMatch()这个方法并不是队列应该有的方法,这是为了比如我需要在缓冲区中匹配到某一串字符后做某些事情而特别加上的,不需要的话删掉即可。比如我需要一旦出现“abc”就做某些事情,那我代码可以类似这么写:

static const unsigned char* StringsWait = "abc";

……

while(true){

//比如从某处获得了下一个字符c

……

// 将字符c入队

RingQueueIn(&RxRingQ,c,RQ_OPTION_WHEN_FULL_DISCARD_FIRST,&err);

if(RingQueueMatch(&RxRingQ,StringsWait,3) >= 0){ // 如果在缓冲区内找到"abc"

// RingQueueClear(&RxRingQ); // 可能需要清空缓冲区

// 做想要做的事

……

}

}

有什么建议或意见请留言,谢谢!

更改历史:

2017/10/27 更新到 V1.1

2018/02/23 更新到 V1.2

2019/03/06 更改缓冲区实现方式,此版本弃用。

c语言数组实现环形缓冲区,[嵌入式开发模块]环形缓冲区/循环队列 C语言实现相关推荐

  1. 关于嵌入式开发,到底需要会多少门语言,做好准备了吗

    我觉得在嵌入式环境是linux,arm体系架构的情况下 一个健康的完整的 嵌入式 工程应包含以下语言: (C++自学了大概总时长2-3个月,熟悉对象编程和语法,没有对C++11库的具体的进行学习) ( ...

  2. [嵌入式开发模块]GY25倾斜角度传感器 驱动模块

    文章目录 前言 GY25简介 概述 通信协议 驱动文件 GY25Driver.h GY25Recver.c GY25Cmder.c 依赖 使用示例 更新历史 前言 干活中用到了GY25传感器模块,写了 ...

  3. 循环队列–C语言实现–数据结构

    循环队列–C语言实现–数据结构 目录 循环队列C语言实现数据结构 目录 一 要求 二 循环队列 三 循环队列的算法设计 1 建立循环队列 2 置空队列 3 入队 4 出队 5 打印队 四 程序 1 程 ...

  4. 7-219 sdut-C语言实验-A+B for Input-Output Practice (不确定次数循环)7-220 sdut-C语言实验——两个数比较

    目录 7-219 sdut-C语言实验-A+B for Input-Output Practice (不确定次数循环) 7-220 sdut-C语言实验--两个数比较 7-219 sdut-C语言实验 ...

  5. LeetCode622循环队列C语言实现

    LeetCode622循环队列C语言实现 c语言一般是10表示true false,所以有一点点不习惯. 这道题需要注意的点.开始的时候front=1,back=0. 这样enqueue和dequeu ...

  6. c语言改变cmd 字体大小_嵌入式开发中常见3个的C语言技巧

    原文:https://www.cnblogs.com/CrazyCatJack/p/6080266.html 1.指向函数的指针 指针不光能指向变量.字符串.数组,还能够指向函数.在C语言中允许将函数 ...

  7. 循环队列c语言的实现,循环队列的C语言实现

    生活中有很多队列的影子,比如打饭排队,买火车票排队问题等,可以说与时间相关的问题,一般都会涉及到队列问题:从生活中,可以抽象出队列的概念,队列就是一个能够实现"先进先出"的存储结构 ...

  8. [嵌入式开发模块]通用接收状态机模块

    文章目录 前言 模块相关概念和逻辑 接收机状态 标志字符序列 flush 接收机类 过滤器 onGetHeader事件 接收机运行逻辑 边际条件 相关代码 接收机代码 RxMac.h RxMacPri ...

  9. c语言 数组、字符串的形参格式_华中师范大学计算机考研874C语言笔记(一)

    运算符优先级 自右向左记忆口诀 条件赋值自增减(?: =/+=/-= ++ -- ) 指针取址按位反 (* & ~) 内存非负强制转 (sizeof ! - (type)) define 前面 ...

最新文章

  1. JavaScript闭包函数箭头函数调用与执行
  2. 网警信息安全!_只愿与一人十指紧扣_新浪博客
  3. 任务五十二:王牌特工
  4. TF之AE:AE实现TF自带数据集AE的encoder之后decoder之前的非监督学习分类
  5. 【数据结构与算法】动画:什么是 BF 算法 ?
  6. 使用jQuery操作DOM元素
  7. 2012.02.17
  8. 命令行参数python sys模块_python教程--sys模块--命令行参数
  9. [Fedora 20] 设置Terminal快捷键 + 设置桌面快捷方式 + Terminal透明解决方案
  10. 数源思维完成目标设定
  11. 一行代码让纯文本编辑器秒变 Markdown 编辑器
  12. Transcad学习记
  13. 51单片机之矩阵键盘
  14. 基于三极管的电平转换电路
  15. PS2汉化2 - 自制程序的运行与调试
  16. 源自神话的写作要义之英雄之旅
  17. 电脑启动快捷键一览表
  18. 产品经理自学资料/UI交互设计/产品相关电子书/求职招聘/案例资料免费领取
  19. Proxy(代理) ARP作用及原理
  20. html浏览器兼容性问题总结,常见的浏览器兼容性问题(小结)

热门文章

  1. 报道丨香港科技大学今年增设金融科技专业!
  2. 7月30日螺纹、橡胶交易日记
  3. WireMock的应用
  4. Android 7.0 分屏拖拽文字和图片的研究
  5. CSS的概述、选择器
  6. linux pureftpd mysql_虚拟主机与PureFTPd和MySQL(包括配额和带宽管理)在CentOS 5.0
  7. OAuth2实现单点登录SSO完整教程,其实不难!
  8. conda安装onnx
  9. 白帽子们是如何看待挖洞的?
  10. 阿里云 mysql升级_如何升级阿里云MySQL实例的版本?