04-1-数据处理思想和程序架构: 关于环形队列

资料源码:https://gitee.com/yang456/OpenProgrammingModuleForMCU.git

点击加入群聊【单片机,物联网,上位机】:

说明1:知识从未如此性感。 烂程序员关心的是代码,好程序员关心的是数据结构和它们之间的关系!

说明2:学的是思想,而非程序!此代码思路适用于所有的单片机。

说明3:学会以后,下面的代码可能会跟你一辈子!

说明4:这一系列文章是为大幅度裁剪本人博客文章!使博客文章更有条理。便于推其它教程!
 

环形队列是啥?

一看到名词就显得高大上了!!!

首先哈,对于做程序而言.一看到什么缓存什么队列,其实就是对数组进行操作.

话说以前有一个数组,这个数组假设是5个的

04-1-数据处理思想和程序架构: 关于环形队列_第1张图片

 

 

 

然后呢有人把这个数组交给了一套程序去管理

调用这套程序就可以往数组里存数据和取数据

但是呢,这套控制程序比较与众不同.

一开始调用控制程序往里面存一个字符A,A便会存储到数组的第一个位置

 

 

 

然后再调用控制程序往里面存一个字符B,B便会存储到数组的第二个位置

 

 

 然后再调用控制程序往里面存两个字符C和D,C,D便会存储到数组的第三,四的位置

 

 

 

然后再调用控制程序让里面存储的时候,不能再存了,因为控制

程序默认已经满了.

-------------------------------------------------------

然后调用控制程序从里面取一个数据

第一个存进去的 A 便会被取出来,然后第一个位置就代表可以再存数据了

04-1-数据处理思想和程序架构: 关于环形队列_第2张图片

 

 

 

然后调用控制程序再从里面取一个数据

第二个存进去的 B 便会被取出来,然后第二个位置就代表可以再存数据了

04-1-数据处理思想和程序架构: 关于环形队列_第3张图片

 

 

 

调用控制程序往里面存一个字符E,E便会存储到数组的第五个位置

04-1-数据处理思想和程序架构: 关于环形队列_第4张图片

 

 

注意前方高能!

然后再调用控制程序往里面存一个字符F,F便会存储到数组的第一个位置

04-1-数据处理思想和程序架构: 关于环形队列_第5张图片

 

 

然后因为现在控制程序又认为满了,我就再调用控制程序再从里面取一个数据

04-1-数据处理思想和程序架构: 关于环形队列_第6张图片

 

 

 

再调用控制程序往里面存一个字符G,G便会存储到数组的第二个位置

04-1-数据处理思想和程序架构: 关于环形队列_第7张图片

 

 

然后就是这样子循环.

 

现在看看实际的

1.环形队列管理程序

04-1-数据处理思想和程序架构: 关于环形队列_第8张图片

 

 

复制代码

/*
V1.0.2:
1.屏蔽printf打印
2.设置不同的返回值,以确定具体错误
*/

#define LOOPLIST_C_
#include "LoopList.h"
#include 
#include 

//创建或者说初始化环形缓冲区
void rbCreate(rb_t* rb,void *Buff,uint32_t BuffLen)
{
    if(NULL == rb)
    {
//            printf("ERROR: input rb is NULL\n");
            return;
    }
    rb->rbCapacity = BuffLen;
    rb->rbBuff = Buff;
    rb->rbHead = rb->rbBuff;//头指向数组首地址
    rb->rbTail = rb->rbBuff;//尾指向数组首地址
}

//删除一个环形缓冲区
void rbDelete(rb_t* rb)
{
    if(NULL == rb)
    {
//        printf("ERROR: input rb is NULL\n");
        return;
    }

    rb->rbBuff = NULL;//地址赋值为空
    rb->rbHead = NULL;//头地址为空
    rb->rbTail = NULL;//尾地址尾空
    rb->rbCapacity = 0;//长度为空
}

//获取链表的长度
int32_t rbCapacity(rb_t *rb)
{
    if(NULL == rb)
    {
//        printf("ERROR: input rb is NULL\n");
        return -51;
    }

    return rb->rbCapacity;
}

//返回能读的空间
int32_t rbCanRead(rb_t *rb)
{
    if(NULL == rb)
    {
//        printf("ERROR: input rb is NULL\n");
        return -31;
    }

    if (rb->rbHead == rb->rbTail)//头与尾相遇
    {
        return 0;
    }

    if (rb->rbHead < rb->rbTail)//尾大于头
    {
        return rb->rbTail - rb->rbHead;
    }

    return rbCapacity(rb) - (rb->rbHead - rb->rbTail);//头大于尾
}

//返回能写入的空间
int32_t rbCanWrite(rb_t *rb)
{
    if(NULL == rb)
    {
//        printf("ERROR: input rb is NULL\n");
        return -41;
    }

    return rbCapacity(rb) - rbCanRead(rb);//总的减去已经写入的空间
}

/*   
  rb--要读的环形链表
  data--读出的数据
  count--读的个数
*/
int32_t rbRead(rb_t *rb, void *data, uint32_t count)
{
    int copySz = 0;

    if(NULL == rb)//        printf("ERROR: input rb is NULL\n");
    {
        return -21;
    }

    if(NULL == data)//        printf("ERROR: input data is NULL\n");
    {
        return -22;
    }
        
    if (rb->rbHead < rb->rbTail)//尾大于头
    {
        copySz = min(count, rbCanRead(rb));//查看能读的个数
        memcpy(data, rb->rbHead, copySz);//读出数据到data
        rb->rbHead += copySz;//头指针加上读取的个数
        return copySz;//返回读取的个数
    }
    else //头大于等于了尾
    {
        if (count < rbCapacity(rb)-(rb->rbHead - rb->rbBuff))//读的个数小于头上面的数据量
        {
            copySz = count;//读出的个数
            memcpy(data, rb->rbHead, copySz);//
            rb->rbHead += copySz;
            return copySz;
        }
        else//读的个数大于头上面的数据量
        {
            copySz = rbCapacity(rb) - (rb->rbHead - rb->rbBuff);//先读出来头上面的数据
            memcpy(data, rb->rbHead, copySz);
            rb->rbHead = rb->rbBuff;//头指针指向数组的首地址
                                                               //还要读的个数
            copySz += rbRead(rb, (char*)data+copySz, count-copySz);//接着读剩余要读的个数
            return copySz;
        }
    }
}

int32_t rbWrite(rb_t *rb, const void *data, uint32_t count)
{
    int tailAvailSz = 0;

    if(NULL == rb)
    {
//        printf("ERROR: rb is empty \n");
        return -11;
    }

    if(NULL == data)
    {
//        printf("ERROR: data is empty \n");
        return -12;
    }

    if (count >= rbCanWrite(rb))//如果剩余的空间不够
    {
//        printf("ERROR: no memory \n");
        return -13;
    }

    if (rb->rbHead <= rb->rbTail)//头小于等于尾
    {
        tailAvailSz = rbCapacity(rb) - (rb->rbTail - rb->rbBuff);//查看尾上面剩余的空间
        if (count <= tailAvailSz)//个数小于等于尾上面剩余的空间
        {
            memcpy(rb->rbTail, data, count);//拷贝数据到环形数组
            rb->rbTail += count;//尾指针加上数据个数
            if (rb->rbTail == rb->rbBuff+rbCapacity(rb))//正好写到最后
            {
                rb->rbTail = rb->rbBuff;//尾指向数组的首地址
            }
            return count;//返回写入的数据个数
        }
        else
        {
            memcpy(rb->rbTail, data, tailAvailSz);//填入尾上面剩余的空间
            rb->rbTail = rb->rbBuff;//尾指针指向数组首地址
                   //剩余空间                   剩余数据的首地址       剩余数据的个数
            return tailAvailSz + rbWrite(rb, (char*)data+tailAvailSz, count-tailAvailSz);//接着写剩余的数据
        }
    }
    else //头大于尾
    {
      memcpy(rb->rbTail, data, count);
      rb->rbTail += count;
      return count;
    }
}
/**@} */

/**
* @brief   往环形队列里面写入数据
* @param   rb      环形队列管理变量 
* @param   USARTx  控制打开某个串口发送中断  
* @param   EnabledUsart 控制打开中断
* @param   buf     发送的数据
* @param   len     数据长度
* @retval  负数:错误   正数:写入的数据长度
* @warning
* @example 
**/
int32_t PutData(rb_t *rb ,void *buf, uint32_t len)
{
    int32_t count = 0;

    if(NULL == buf)
    {
//        printf("ERROR: gizPutData buf is empty \n");
        return -1;
    }
    
    count = rbWrite(rb, buf, len);
    if(count != len)
    {
        //printf("ERROR: Failed to rbWrite \n");
        return -2;
    }
    return count;
}

复制代码

 

复制代码

#ifndef LOOPLIST_H_
#define LOOPLIST_H_

#ifndef LOOPLIST_C_
#define LOOPLIST_Ex_ extern
#else
#define LOOPLIST_Ex_
#endif

#include 
    

#define min(a, b) (a)<(b)?(a):(b)                   ///< 获取最小值

/** 环形缓冲区数据结构 */
typedef struct {
    uint32_t  rbCapacity;//空间大小
    char  *rbHead; //头
    char  *rbTail; //尾
    char  *rbBuff; //数组的首地址
}rb_t;


void rbCreate(rb_t *rb,void *Buff,uint32_t BuffLen);//创建或者说初始化环形缓冲区
void rbDelete(rb_t* rb);
int32_t rbCapacity(rb_t *rb);//得到环形大小
int32_t rbCanRead(rb_t *rb);//能读出数据的个数
int32_t rbCanWrite(rb_t *rb);//还剩余的空间
int32_t rbRead(rb_t *rb, void *data, uint32_t count);//读取数据
int32_t rbWrite(rb_t *rb, const void *data, uint32_t count);
int32_t PutData(rb_t *rb ,void *buf, uint32_t len);


#endif

复制代码

 

 

2.创建

04-1-数据处理思想和程序架构: 关于环形队列_第9张图片

 

 

 

 

 

3.通过环形队列函数往数组里面存数据

04-1-数据处理思想和程序架构: 关于环形队列_第10张图片

 

 

 

 

 

 04-1-数据处理思想和程序架构: 关于环形队列_第11张图片

 

 

04-1-数据处理思想和程序架构: 关于环形队列_第12张图片

 

 

 

 

4.通过环形队列函数往数组里面存数据

04-1-数据处理思想和程序架构: 关于环形队列_第13张图片

 

 

 

 

5.取出来几个数据

04-1-数据处理思想和程序架构: 关于环形队列_第14张图片

 

 

 

04-1-数据处理思想和程序架构: 关于环形队列_第15张图片

 

 

 

 

咱存储数据的时候存储的顺序是 1,2,3,4,5,6依次存进去的.

取数据的时候也是先取1 然后取2 然后...  最后取6

其实就是先进先出的原则.

 

 

6.通过环形队列函数往数组里面存数据

 

 04-1-数据处理思想和程序架构: 关于环形队列_第16张图片

 

 

 

04-1-数据处理思想和程序架构: 关于环形队列_第17张图片

 

 

 

咱再接着存的时候是不是形成了一个环形的结构了.转着圈的存数据.

注意上面的数组黄框位置,黄框位置咱已经调用了取数据函数把里面的数据读取了.

其实黄框位置在环形队列管理函数里面认为是空位置.

 

现在看典型应用

1,说明

首先环形队列适用于很多场合,尤其是一边存数据一边处理数据的场合.

 

2.使用环形队列缓存串口数据

04-1-数据处理思想和程序架构: 关于环形队列_第18张图片

 

 

 

04-1-数据处理思想和程序架构: 关于环形队列_第19张图片

 

 

 

 

04-1-数据处理思想和程序架构: 关于环形队列_第20张图片

 

 

 

 

主循环读取缓存的数据,并使用串口1发送出去

04-1-数据处理思想和程序架构: 关于环形队列_第21张图片

 

 

 

04-1-数据处理思想和程序架构: 关于环形队列_第22张图片

 

 

 

 

3.可能用户会想就这?

我的所有的项目都是使用的环形队列做数据处理.

更加典型的应该看下面的链接(里面的代码开源):单片机IAP升级程序

我使用环形队列接收程序文件,定义的数组只用了 5字节

也就是说就用了5字节大小的数组就完成了升级单片机程序

https://www.cnblogs.com/yangfengwu/p/14620102.html

 

 

4.用户只需要知道,环形队列就是一个缓存数据的方式

此节代码中还有使用中断发送数据,缓存也是使用的环形队列

其实就是把数据放到环形队列,然后打开中断发送,

04-1-数据处理思想和程序架构: 关于环形队列_第23张图片

 

 

 

然后在中断函数里面读取数据,发送出去

04-1-数据处理思想和程序架构: 关于环形队列_第24张图片

 

 

 

 

5.还有我使用环形队列再次封装的一套缓存

https://www.cnblogs.com/yangfengwu/p/12228402.html

04-1-数据处理思想和程序架构: 关于环形队列_第25张图片

 

 

 

 

结语

切莫眼高手低!!!!

 

你可能感兴趣的:(数据处理思想和程序架构)