最近在搞了一块乐鑫的ESP826-12F模块,实现手机无线控制单片机,模块烧写的固件是机智云32M的,具体烧录过程就不说了,可参考这里http://club.gizwits.com/thread-3551-1-1.html,我这里解释一下源码吧。首先来介绍一下Utils/ ringbuffer,就是环形缓冲区,字面上理解,环形缓冲区,但实际并不是环形,只是模拟出来环形而已,因为内存地址是往一个方向增长的。
图1-内存增长图
先来看看ringbuffer.h
主要是用宏实现了一个求最小值的函数。
还有就是定义了一个环形缓冲区的结构体。
#define min(a, b) (a)<(b)?(a):(b) ///< Calculate the minimum value
typedef struct {
size_t rbCapacity;
uint8_t *rbHead;
uint8_t *rbTail;
uint8_t *rbBuff;
}rb_t;
rb_t是环形缓冲区的结构体,rbCapacity为环形缓冲区的容量,指针rbHead指向头部,指针rbTail指向尾部,指针rbBuff指向缓冲区首地址,在创建缓冲区的时候,这些都会初始化。
缓冲区的创建
int8_t ICACHE_FLASH_ATTR rbCreate(rb_t* rb)
{
if(NULL == rb)
{
return -1;
}
rb->rbHead = rb->rbBuff;
rb->rbTail = rb->rbBuff;
return 0;
}
这个是缓冲区的创建函数,这些开源的代码写的就是比较严谨,一进来首先判断指针是否为空,空的话直接返回(后面的函数也有,后面就不做解释了),这点值得我们学习,严谨,提高程序的健壮性,然后初始化头尾指针,均指向缓冲区首地址。初始化后的指向情况见下图
图二-初始化后指针指向
返回缓冲区的容量
int32_t ICACHE_FLASH_ATTR rbCapacity(rb_t *rb)
{
if(NULL == rb)
{
return -1;
}
return rb->rbCapacity;
}
这个很容易理解,直接返回rbCapacity。
读取环形缓冲区可读数据大小
int32_t ICACHE_FLASH_ATTR rbCanRead(rb_t *rb)
{
if(NULL == rb)
{
return -1;
}
if (rb->rbHead == rb->rbTail)
{
return 0;
}
if (rb->rbHead < rb->rbTail)
{
return rb->rbTail - rb->rbHead;
}
return rbCapacity(rb) - (rb->rbHead - rb->rbTail);
}
进来头尾指针是否相等,相等直接返回0,说明无数据可读,接着判断头指针是否小于尾指针,如果是,则直接返回rb->rbTail - rb->rbHead,头尾指针直接相减即为可读数据大小
图三-尾指针大于头指针结构图
如果都不满足,即返回rbCapacity(rb) - (rb->rbHead - rb->rbTail); 缓冲区的容量-(头指针-尾指针)下图实例:21-(7-2)=16
图四-头指针大于尾指针结构图
获取缓冲区可写数据区大小
int32_t ICACHE_FLASH_ATTR rbCanWrite(rb_t *rb)
{
if(NULL == rb)
{
return -1;
}
return rbCapacity(rb) - rbCanRead(rb); //总容量减去-可读数据区大小
}
读缓冲区数据
rb:准备读的缓冲区
data:读取数据的保存地址
count:要读取的个数
int32_t ICACHE_FLASH_ATTR rbRead(rb_t *rb, void *data, size_t count)
{
int32_t copySz = 0;
if(NULL == rb)
{
return -1;
}
if(NULL == data)
{
return -1;
}
if (rb->rbHead < rb->rbTail) //结合图三
{
copySz = min(count, rbCanRead(rb)); //取出要读的数据长度,这里min避免过渡读取,
memcpy(data, rb->rbHead, copySz); //读取数据到date
rb->rbHead += copySz; //移动头指针
return copySz; //返回读取的个数
}
else //结合图四
{
if (count < rbCapacity(rb)-(rb->rbHead - rb->rbBuff)) //判断count<21-(7-0)
{
copySz = count;
memcpy(data, rb->rbHead, copySz); //读取数据到date
rb->rbHead += copySz; //移动头指针
return copySz; //返回读取的个数
}
else // count>=20-(7-0)
{
copySz = rbCapacity(rb) - (rb->rbHead - rb->rbBuff); // copySz = 21-(7-0)
memcpy(data, rb->rbHead, copySz); //读取数据到date
rb->rbHead = rb->rbBuff; //移动头指针,指向缓冲区首地址,
copySz += rbRead(rb, (char*)data+copySz, count-copySz); //递归调用rbRead
return copySz;
}
}
}
这里我们假设缓冲区大小为21,方便讲解。
写缓冲区数据
rb:准备写的缓冲区
data:数据的保存地址
count:要写的个数
既然理解了读取数据,那么写数据写数据就是相反的过程,这里,交给你们自己解决,结合上面的图,一步一步啃,很简单的。
int32_t ICACHE_FLASH_ATTR rbWrite(rb_t *rb, const void *data, size_t count)
{
int32_t tailAvailSz = 0;
if((NULL == rb)||(NULL == data))
{
return -1;
}
if (count >= rbCanWrite(rb))
{
return -2;
}
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;
}
}
今天就先介绍环形缓冲区,有什么问题可以留言,或者发邮件[email protected]