<p><iframe name="ifd" src="https://mnifdv.cn/resource/cnblogs/单片机知识点总结/directory.html" frameborder="0" scrolling="auto" width="100%" height="1500"></iframe></p>
一看到名词就显得高大上了!!!
首先哈,对于做程序而言.一看到什么缓存什么队列,其实就是对数组进行操作.
话说以前有一个数组,这个数组假设是5个的
然后呢有人把这个数组交给了一套程序去管理
调用这套程序就可以往数组里存数据和取数据
但是呢,这套控制程序比较与众不同.
一开始调用控制程序往里面存一个字符A,A便会存储到数组的第一个位置
然后再调用控制程序往里面存一个字符B,B便会存储到数组的第二个位置
然后再调用控制程序往里面存两个字符C和D,C,D便会存储到数组的第三,四的位置
然后再调用控制程序让里面存储的时候,不能再存了,因为控制
程序默认已经满了.
然后调用控制程序从里面取一个数据
第一个存进去的 A 便会被取出来,然后第一个位置就代表可以再存数据了
然后调用控制程序再从里面取一个数据
第二个存进去的 B 便会被取出来,然后第二个位置就代表可以再存数据了
调用控制程序往里面存一个字符E,E便会存储到数组的第五个位置
注意前方高能!
然后再调用控制程序往里面存一个字符F,F便会存储到数组的第一个位置
然后因为现在控制程序又认为满了,我就再调用控制程序再从里面取一个数据
再调用控制程序往里面存一个字符G,G便会存储到数组的第二个位置
然后就是这样子循环.
1.环形队列管理程序
/*
V1.0.2:
1.屏蔽printf打印
2.设置不同的返回值,以确定具体错误
*/
#define LOOPLIST_C_
#include "LoopList.h"
#include <string.h>
#include <stdio.h>
//创建或者说初始化环形缓冲区
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 <stm32f10x.h>
#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.创建
3.通过环形队列函数往数组里面存数据
4.通过环形队列函数往数组里面存数据
5.取出来几个数据
咱存储数据的时候存储的顺序是 1,2,3,4,5,6依次存进去的.
取数据的时候也是先取1 然后取2 然后... 最后取6
其实就是先进先出的原则.
6.通过环形队列函数往数组里面存数据
咱再接着存的时候是不是形成了一个环形的结构了.转着圈的存数据.
注意上面的数组黄框位置,黄框位置咱已经调用了取数据函数把里面的数据读取了.
其实黄框位置在环形队列管理函数里面认为是空位置.
1,说明
首先环形队列适用于很多场合,尤其是一边存数据一边处理数据的场合.
2.使用环形队列缓存串口数据
主循环读取缓存的数据,并使用串口1发送出去
3.可能用户会想就这?
我的所有的项目都是使用的环形队列做数据处理.
更加典型的应该看下面的链接(里面的代码开源):单片机IAP升级程序
我使用环形队列接收程序文件,定义的数组只用了 5字节
也就是说就用了5字节大小的数组就完成了升级单片机程序
https://www.cnblogs.com/yangfengwu/p/14620102.html
4.用户只需要知道,环形队列就是一个缓存数据的方式
此节代码中还有使用中断发送数据,缓存也是使用的环形队列
其实就是把数据放到环形队列,然后打开中断发送,
然后在中断函数里面读取数据,发送出去
5.还有我使用环形队列再次封装的一套缓存
https://cloud.tencent.com/developer/article/1583664
结语
切莫眼高手低!!!!