xqueue:基于C语言实现的循环队列缓冲区模块

原创 电子电路开发学习 2023-04-09 20:56

1. 为什么需要FIFO

FIFO 是First-In First-Out的缩写,它是一个具有先入先出特点的缓冲区。

可以理解成一个大的水池,水对应数据,注水速度对应数据输入的频率,放水速度对应数据处理的速度,当注水速度和放水速度相同时,我们不需要使用水池来缓冲,但是当注水速度大于放水速度,或者注水速度突然变大时(突发),为了保证水池不溢出(数据不丢失),就需要水池(缓冲区)来处理这种突发情况,并设置合理大小的水池空间(FIFO的深度)。

或者为了降低CPU负担,提高数据处理效率,可以在积累到一定的数据量之后,再一次性处理。

在FPGA中,FIFO一般是使用RAM存储器作为缓冲区,可以分为同步FIFO或异步FIO,一般用于数据缓冲,或者不同时钟域之间的数据传递。

在单片机中,一般是基于一维数组和结构体实现的循环队列(Queue),或者叫环形队列。

FIFO的使用,既可以保证数据的完整性,还可以让数据被及时的处理。

本文介绍,基于C语言的循环队列缓冲区原理、设计与实现

2. FIFO的存取顺序

定义一个一维数组当作存储区,数组长度为6,再定义两个读写指针变量。

初始化时,FIFO为空,读写指针相等,并都置为0。

写入一个数据1之后,写指针递增,读指针不变:

再写两个数据2和3,写指针递增,读指针不变:

写了三个数据之后,我们读出一个数据1,写指针不变,读指针递增:

读出一个数据2,再写两个数据4和5,读写指针变化:

再写一个数据6,此时超过数组长度,但是数组头部还有空间,所以写指针回到数组起始地址0:

再写一个数据7,此时判断FIFO满:

可能会有朋友疑惑,不是还有一个空位置可以存放数据吗?

如果再存入一个数据之后,读写指针相等,此时可以判断是满状态吗?

显然是不能,因为当FIFO为空时,也是读写指针相等,所以这种情况就无法判断满和空。

这里就涉及到FIFO设计中,最重要的满和空的判断条件,需要遵循FIFO读写的两个规则:

  • FIFO为空时,不能执行读操作

  • FIFO为满时,不能执行写操作

为了避免这种情况发生,我们空出一个元素位置,写指针指向的位置永远为空,这样就会有两种满的情况:

  • rd < wr

  • rd > wr

对于第一种情况,当(wr + 1) % FIFO_SIZE == rd时,可以认为FIFO满,FIFO_SIZE是指数组长度;

对于第二种情况,当wr + 1 == rd时,可以认为FIFO满。

以上两种情况可以合并为一种,即(wr + 1) % FIFO_SIZE == rd时,判断FIFO满。

所以这种判断方式,会牺牲一个存储位置,实际可以存储的元素个数为FIFO_SIZE-1。

同理,获取当前FIFO内元素的个数,也可以分为两种情况:

当wr > rd时, count = wr - rd

当wr < rd时,count = wr + FIFO_SIZE - rd

3. FIFO的代码实现

根据以上FIFO存取逻辑,我们可以使用一维数组来构造一个环形缓冲区,读写地址循环递增,分别实现FIFO初始化、读写操作、判断空满、获取元素个数等函数,并封装成模块。

xqueue.h

/*
 * Copyright(C), 2010-2023, CSDN @ whik1194
 * Time       : 2023年4月9日
 * Author     : https://blog.csdn.net/whik1194
 * GitHub     : https://gitee.com/whik/xqueue
 */

#ifndef __XQUEUE_H__
#define __XQUEUE_H__

#include "stdint.h"

/* FIFO数据的类型,可以是结构体类型 */
#define qdata_t uint8_t

/* FIFO长度,实际存放的数据=FIFO_SIZE-1 */
#define FIFO_SIZE 6

typedef enum {
    QUEUE_OK,
    QUEUE_FULL,
    QUEUE_EMPTY
}qstatus_t;

typedef struct {
    uint16_t addr_wr;        /* 写地址 */
    uint16_t addr_rd;        /* 读地址 */
    uint16_t length;         /* FIFO长度,实际存放的数据=length-1 */
    qdata_t fifo[FIFO_SIZE];
}queue_t;

qstatus_t queue_reset(queue_t *q);
qstatus_t queue_read(queue_t *q, qdata_t *pdata);
qstatus_t queue_write(queue_t *q, qdata_t data);
int queue_isFull(queue_t *q);
int queue_isEmpty(queue_t *q);
int queue_print(queue_t *q);

#endif

xqueue.c文件

/*
 * Copyright(C), 2010-2023, CSDN @ whik1194
 * Time       : 2023年4月9日
 * Author     : https://blog.csdn.net/whik1194
 * GitHub     : https://gitee.com/whik/xqueue
 */

#include "xqueue.h"
#include "stdio.h"

/* FIFO复位 */
qstatus_t queue_reset(queue_t *q)
{
    int i = 0;

    q->addr_wr = 0;
    q->addr_rd = 0;
    q->length = FIFO_SIZE;
    for(i = 0; i < q->length; i++)
        q->fifo[i] = 0;

    return QUEUE_OK;
}

/* FIFO写入数据 */
qstatus_t queue_write(queue_t *q, qdata_t data)
{
    if(queue_isFull(q))
    {
        printf("Write failed(%d), queue is full\n", data);
        return QUEUE_FULL;
    }

    q->fifo[q->addr_wr] = data;
    q->addr_wr = (q->addr_wr + 1) % q->length;
    printf("write success: %02d\n", data);
    queue_print(q);

    return QUEUE_OK;
}

/* FIFO读出数据 */
qstatus_t queue_read(queue_t *q, qdata_t *pdata)
{
    if(queue_isEmpty(q))
    {
        printf("Read failed, queue is empty\n");
        return QUEUE_EMPTY;
    }

    *pdata = q->fifo[q->addr_rd];
    q->addr_rd = (q->addr_rd + 1) % q->length;

    printf("read success: %02d\n", *pdata);
    queue_print(q);

    return QUEUE_OK;
}

/* FIFO是否为空 */
int queue_isEmpty(queue_t *q)
{
    return (q->addr_wr == q->addr_rd);
}

/* FIFO是否为满 */
int queue_isFull(queue_t *q)
{
    return ((q->addr_wr + 1) % q->length == q->addr_rd);
}

/* FIFO内数据的个数 */
int queue_count(queue_t *q)
{
    if(q->addr_rd <= q->addr_wr)
        return (q->addr_wr - q->addr_rd);
    //addr_rd > addr_wr;
    return (q->length + q->addr_wr - q->addr_rd);
}

/* 打印当前FIFO内的数据和读写指针的位置 */
int queue_print(queue_t *q)
{
    int i = 0;
    int j = 0;

    for(i = 0; i < q->addr_rd; i++)
        printf("     ");

    printf("rd=%d", q->addr_rd);
    printf("\n");

    for(i = 0; i < q->length; i++)
    {
        if(q->addr_wr > q->addr_rd)
        {
            if(i >= q->addr_rd && i < q->addr_wr)
                printf("[%02d] ", q->fifo[i]);
            else
                printf("[  ] ");
        }
        else//addr_rd > addr_wr
        {
            if(i < q->addr_wr || i >= q->addr_rd)
                printf("[%02d] ", q->fifo[i]);
            else
                printf("[  ] ");
        }
    }
    printf("------count = %d\n", queue_count(q));

    for(i = 0; i < q->addr_wr; i++)
        printf("     ");

    printf("wr=%d", q->addr_wr);
    printf("\n");

    return QUEUE_OK;
}

实际应用:

/*
 * Copyright(C), 2010-2023, CSDN @ whik1194
 * Time       : 2023年4月9日
 * Author     : https://blog.csdn.net/whik1194
 * GitHub     : https://github.com/whik/xqueue
 */

#include 
#include 

#include "xqueue.h"

int main(int argc, char *argv[])
{
    queue_t queue;
    qdata_t data;

    queue_reset(&queue);
    queue_write(&queue1);
    queue_write(&queue2);
    queue_write(&queue3);
    queue_read(&queue, &data);
    queue_read(&queue, &data);
    queue_write(&queue4);
    queue_write(&queue5);
    queue_write(&queue6);
    queue_write(&queue7);
    queue_read(&queue, &data);
    queue_read(&queue, &data);
    queue_read(&queue, &data);
    queue_write(&queue8);
    queue_write(&queue9);
    queue_write(&queue10);
    queue_read(&queue, &data);

    system("pause");
    return 0;
}

运行结果:

循环队列元素的数据类型,可以根据需要指定,也可以是结构体类型。

后台回复【xqueue】,获取下载地址,或者到以下开源地址获取资料。

4. 开源地址

  • xqueue

    https://gitee.com/whik/xqueue

精选

  • 永不消逝的电波——带你走进占全国人口万分之一的神秘群体!
  • Xilinx FPGA Multiboot设计与实现(Spartan-6和Kintex-7示例)
  • Xilinx FPGA独立的下载和调试工具LabTools下载、安装、使用教程
  • 基于Xilinx XC7A100T的ARM Cortex-M3软核设计与实现

电子电路开发学习 单片机点灯小能手,电子行业从业者。开发板评测、嵌入式开源项目分享、学习笔记记录。可能不会经常更新,但每一篇都是精心编写。
评论
  • 概述 说明(三)探讨的是比较器一般带有滞回(Hysteresis)功能,为了解决输入信号转换速率不够的问题。前文还提到,即便使能滞回(Hysteresis)功能,还是无法解决SiPM读出测试系统需要解决的问题。本文在说明(三)的基础上,继续探讨为SiPM读出测试系统寻求合适的模拟脉冲检出方案。前四代SiPM使用的高速比较器指标缺陷 由于前端模拟信号属于典型的指数脉冲,所以下降沿转换速率(Slew Rate)过慢,导致比较器检出出现不必要的问题。尽管比较器可以使能滞回(Hysteresis)模块功
    coyoo 2024-12-03 12:20 108浏览
  • 戴上XR眼镜去“追龙”是种什么体验?2024年11月30日,由上海自然博物馆(上海科技馆分馆)与三湘印象联合出品、三湘印象旗下观印象艺术发展有限公司(下简称“观印象”)承制的《又见恐龙》XR嘉年华在上海自然博物馆重磅开幕。该体验项目将于12月1日正式对公众开放,持续至2025年3月30日。双向奔赴,恐龙IP撞上元宇宙不久前,上海市经济和信息化委员会等部门联合印发了《上海市超高清视听产业发展行动方案》,特别提到“支持博物馆、主题乐园等场所推动超高清视听技术应用,丰富线下文旅消费体验”。作为上海自然
    电子与消费 2024-11-30 22:03 98浏览
  • 当前,智能汽车产业迎来重大变局,随着人工智能、5G、大数据等新一代信息技术的迅猛发展,智能网联汽车正呈现强劲发展势头。11月26日,在2024紫光展锐全球合作伙伴大会汽车电子生态论坛上,紫光展锐与上汽海外出行联合发布搭载紫光展锐A7870的上汽海外MG量产车型,并发布A7710系列UWB数字钥匙解决方案平台,可应用于数字钥匙、活体检测、脚踢雷达、自动泊车等多种智能汽车场景。 联合发布量产车型,推动汽车智能化出海紫光展锐与上汽海外出行达成战略合作,联合发布搭载紫光展锐A7870的量产车型
    紫光展锐 2024-12-03 11:38 101浏览
  • RDDI-DAP错误通常与调试接口相关,特别是在使用CMSIS-DAP协议进行嵌入式系统开发时。以下是一些可能的原因和解决方法: 1. 硬件连接问题:     检查调试器(如ST-Link)与目标板之间的连接是否牢固。     确保所有必要的引脚都已正确连接,没有松动或短路。 2. 电源问题:     确保目标板和调试器都有足够的电源供应。     检查电源电压是否符合目标板的规格要求。 3. 固件问题: &n
    丙丁先生 2024-12-01 17:37 100浏览
  • TOF多区传感器: ND06   ND06是一款微型多区高集成度ToF测距传感器,其支持24个区域(6 x 4)同步测距,测距范围远达5m,具有测距范围广、精度高、测距稳定等特点。适用于投影仪的无感自动对焦和梯形校正、AIoT、手势识别、智能面板和智能灯具等多种场景。                 如果用ND06进行手势识别,只需要经过三个步骤: 第一步&
    esad0 2024-12-04 11:20 50浏览
  • 最近几年,新能源汽车愈发受到消费者的青睐,其销量也是一路走高。据中汽协公布的数据显示,2024年10月,新能源汽车产销分别完成146.3万辆和143万辆,同比分别增长48%和49.6%。而结合各家新能源车企所公布的销量数据来看,比亚迪再度夺得了销冠宝座,其10月新能源汽车销量达到了502657辆,同比增长66.53%。众所周知,比亚迪是新能源汽车领域的重要参与者,其一举一动向来为外界所关注。日前,比亚迪汽车旗下品牌方程豹汽车推出了新车方程豹豹8,该款车型一上市就迅速吸引了消费者的目光,成为SUV
    刘旷 2024-12-02 09:32 119浏览
  • 11-29学习笔记11-29学习笔记习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-02 23:58 71浏览
  • 作为优秀工程师的你,已身经百战、阅板无数!请先醒醒,新的项目来了,这是一个既要、又要、还要的产品需求,ARM核心板中一个处理器怎么能实现这么丰富的外围接口?踌躇之际,你偶阅此文。于是,“潘多拉”的魔盒打开了!没错,USB资源就是你打开新世界得钥匙,它能做哪些扩展呢?1.1  USB扩网口通用ARM处理器大多带两路网口,如果项目中有多路网路接口的需求,一般会选择在主板外部加交换机/路由器。当然,出于成本考虑,也可以将Switch芯片集成到ARM核心板或底板上,如KSZ9897、
    万象奥科 2024-12-03 10:24 68浏览
  • 遇到部分串口工具不支持1500000波特率,这时候就需要进行修改,本文以触觉智能RK3562开发板修改系统波特率为115200为例,介绍瑞芯微方案主板Linux修改系统串口波特率教程。温馨提示:瑞芯微方案主板/开发板串口波特率只支持115200或1500000。修改Loader打印波特率查看对应芯片的MINIALL.ini确定要修改的bin文件#查看对应芯片的MINIALL.ini cat rkbin/RKBOOT/RK3562MINIALL.ini修改uart baudrate参数修改以下目
    Industio_触觉智能 2024-12-03 11:28 84浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2024-12-02 10:40 120浏览
  •         温度传感器的精度受哪些因素影响,要先看所用的温度传感器输出哪种信号,不同信号输出的温度传感器影响精度的因素也不同。        现在常用的温度传感器输出信号有以下几种:电阻信号、电流信号、电压信号、数字信号等。以输出电阻信号的温度传感器为例,还细分为正温度系数温度传感器和负温度系数温度传感器,常用的铂电阻PT100/1000温度传感器就是正温度系数,就是说随着温度的升高,输出的电阻值会增大。对于输出
    锦正茂科技 2024-12-03 11:50 106浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦