<p><iframe name="ifd" src="https://mnifdv.cn/resource/cnblogs/单片机知识点总结/directory.html" frameborder="0" scrolling="auto" width="100%" height="1500"></iframe></p>
<iframe frameborder="0" height="1500" name="ifd" scrolling="auto" src="https://mnifdv.cn/resource/cnblogs/单片机知识点总结/directory.html" width="100%"></iframe>
环形队列是啥?
一看到名词就显得高大上了!!!
首先哈,对于做程序而言.一看到什么缓存什么队列,其实就是对数组进行操作.
话说以前有一个数组,这个数组假设是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://www.cnblogs.com/yangfengwu/p/12228402.html
结语
切莫眼高手低!!!!