建议使用以下浏览器,以获得最佳体验。 IE 9.0+以上版本 Chrome 31+ 谷歌浏览器 Firefox 30+ 火狐浏览器
请选择 进入手机版 | 继续访问电脑版
设置昵称

在此一键设置昵称,即可参与社区互动!

确定
我再想想
选择版块
直达楼层
标签
您还可以添加5个标签
  • 没有搜索到和“关键字”相关的标签
  • 云产品
  • 解决方案
  • 技术领域
  • 通用技术
  • 平台功能
取消

采纳成功

您已采纳当前回复为最佳回复

樊心昊

发帖: 172粉丝: 139

发消息 + 关注

发表于2020年05月13日 15:12:09 1226 1
直达本楼层的链接
楼主
显示全部楼层
[技术干货] LiteOS_Lab仓库组件杂谈---ring_buffer.c

摘要:本贴讲解ring_buffer.c文件,该文件中的函数用于在uart_at.c文件中管理串口接收缓存内存,是我们AT框架的底层,对于深度理解AT框架底层数据的运作起到了至关重要的作用。

tag_ring_buffer_t结构体


我们在使用ring_buffer.c文件中的函数之前,都需要通过该结构体创建一个其对应的结构体变量,并调用初始化函数,让我们需要管理的内存和这个结构体中的成员进行关联

 /*
 *                       dataoff
 * ring buffer map:       |  ----datalen-------
 *                        | /                   \
 *                        |/                     \
 *   ----------------------------------------------------------
 *   |     empty space    |//////valid data///////|           |
 *   ----------------------------------------------------------
 *   | \                                                      /
 *   |  \                                                    /
 *   |   \                                                  /
 *   |     ---------------buflen--------------------------
 *  buf
 */
 
typedef struct
{
    unsigned char   *buf;      ///< which means the buffer
    int              buflen;   ///< which means the buffer limit
    int              datalen;  ///< which means how many data in the buffer
    int              dataoff;  ///< which means the valid data offset in the buffer
}tag_ring_buffer_t;

image.png

读到这里,大家有没有想过这样做的好处是什么?我们通过特定函数对ring_buf清除或者把数据读走,实际上并没有真正的清除了其中的数据只是修改了dataoff变量和datalen变量,下面我们先来看看初始化函数。

tag_ring_buffer_t结构体初始化并和被管理的内存绑定


首先,我们需要申请一段内存,并创建一个tag_ring_buffer_t结构体类型的变量,通过调用ring_buffer_init函数,让tag_ring_buffer_t结构体类型的变量和我们申请的这段内存产生管理,从而能上面说的这种机制来管理这段内存。

先来看一个uart_at.c文件中的例子

image.png

image.png

uart_at.c文件中创建一个g_atio_cb结构体变量,成员包括了tag_ring_buffer_t创建的结构体变量rcvring,这个变量用于管理rcvringmem这段内存空间,通过ring_buffer_init函数将它们关联到一起。

int ring_buffer_init(tag_ring_buffer_t *ring,unsigned char *buf, int buflen,int offset,int datalen)
{
    int ret = -1;
    if((NULL == ring))
    {
        return ret;
    }
    ring->buf     = buf;        //将传入内存地址指针添加到结构体中
    ring->buflen  = buflen;     //设置内存大小
    ring->datalen = datalen;    //设置数据长度,初始值为0,因为还没有数据,在读写函数中会去修改该变量
    ring->dataoff = offset;     //设置有效数据起始位置,初始值为0,因为还没有数据,在读写函数中会去修改该变量
    ret = 0;
    return ret;
}

向ring_buffer中写数据


/*
 * ring结构体:我们想写入数据的ring_buffer,buf指针:指向要写入的数据的起始地址,len:要写入数据的长度
 * 返回值:实际写入数据的个数
 */
int ring_buffer_write(tag_ring_buffer_t *ring,unsigned char *buf, int len)
{
    int ret = -1;
    int cpylen;  //the current time we should move
    int lenleft;  //and how many data still left to move
    int offset;
    unsigned char *src;
    unsigned char *dst;
    if((NULL == ring)||(NULL == buf)||(0 == len))    //传入的参数不对
    {
        return ret;//which means parameters error
    }
    
    /* 如果ring结构体中的数据长度等于内存长度,那说明满了,写不下了 */
    if((ring->datalen == ring->buflen)|| (ring->buflen <= 0))
    {
        ret = 0;
        return  ret;//which means you could copy nothing here
    }
    
    /* 如果我们要写的数据长度大于剩余空间的长度,那么只能写一部分进去(剩余空间的大小),反之,可以全部写进去 */
    ret = len > (ring->buflen-ring->datalen)?(ring->buflen-ring->datalen):len;
    
    /* 这里的意思是我们写数据, 从empty space2的起始位置开始写,如果数据长度大于empty space2的大小,就需要回到empty space1的位置接着写
        ,是不是有点ring的味道了?
      ----------------------------------------------------------------------------------------------
      ||     empty space1    ||//////valid data///////||     empty space2    ||
      ----------------------------------------------------------------------------------------------
    */
    //now let us think the method to fill the data,take care of the roll back
    lenleft = ret;    //需要写的数据大小
    src = buf;        //需要写的数据的起始位置
    
    
    /* 如果有效数据起始位置加上有效数据长度大于内存长度,说明这段内存中包含了一份roll back的数据,长这样 */
    /*
      ----------------------------------------------------------------------------------------------
      ||     valid data2    ||//////empty space///////||     valid data1    ||
      ----------------------------------------------------------------------------------------------    
      其中valid data1和valid data2是同一帧数据,该数据由valid data1开始到valid data2结束,所以我们只能从valid data2结束位置开始写新数据
    */
    if((ring->dataoff+ring->datalen)>ring->buflen) //which means the data has roll back
    {
        offset = (ring->dataoff+ring->datalen)%ring->buflen; //we could move it one time 计算一下内存开始位置的数据大小
        cpylen = lenleft;    //需要写的数据的大小
        dst = ring->buf + offset;    //设置dst指针指向valid data2结束位置
        if(cpylen > 0)
        {
            (void) memcpy(dst,src,cpylen);    //开始写数据
            ring->datalen += cpylen;       //更新datalen计数,增加上我们写入数据的大小
            lenleft -= cpylen;          //计算还有多少数据没写
        }
    }
    /* 如果效数据起始位置加上需要写的数据长度大于内存大小,我们就需要写到内存末尾转回到内存起始位置接
        着写,假设old valid data是之前已经存在的数据,valid data是我们写的一帧数据,那么写完之后就和下图一样
           ----------------------------------------------------------------------------------------------
              ||     valid data   ||//////empty space/////// || old valid data ||     valid data   ||
            ----------------------------------------------------------------------------------------------        
    */
    else if((ring->dataoff+ring->datalen + lenleft)>ring->buflen) //which means the data will be roll back
    {
        //which means roll back,we should copy some here to the tail
        offset = ring->dataoff + ring->datalen;    //计算老的数据的结束位置
        cpylen = ring->buflen - offset;            //计算新的数据有多少可以写到尾部
        dst = ring->buf + offset;                  //将老的数据的结束位置地址赋值给dst
        (void) memcpy(dst,src,cpylen);                //开始写尾部的数据
        src += cpylen;                               //移动指针,指向剩余的数据
        ring->datalen += cpylen;                     //更新数据长度
        lenleft -= cpylen;                           //计算还有多少数据没写
    }
    //here means we could move it by one time
    if(lenleft > 0)    //如果有数据还没写完
    {
        offset = (ring->dataoff+ring->datalen)%ring->buflen; //we could move it one time 计算下次写的位置
        cpylen = lenleft;                //将剩余为写入的数据个数写到cpylen中
        dst = ring->buf + offset;      //设置下次写的位置
        (void) memcpy(dst,src,cpylen);    //开始写
        ring->datalen += cpylen;        //更新数据长度计数
    }
    return ret;
}

向ring_buffer中读数据


/*
 * ring结构体:我们从ring_buffer中读取数据,buf:传入一块内存的地址,将读到的数据存到这块内存中,len:这块内存的大小
 * 返回值:返回实际读到的内存大小
 */
int ring_buffer_read(tag_ring_buffer_t *ring,unsigned char *buf, int len)
{
    int ret = -1;
    int cpylen;  //the current time we should move
    int lenleft;  //and how many data still left to move
    int offset;
    unsigned char *src;
    unsigned char *dst;
    if((NULL == ring)||(NULL == buf)||(0 == len))
    {
        return ret;//which means parameters error
    }
    
    /* 如果ring_buffer为空 */
    if((ring->datalen == 0) || (ring->buflen <= 0))
    {
        ret = 0;
        return  ret;//which means you could copy nothing here
    }
    
    /* 计算实际读到的数据大小 */
    ret = len > ring->datalen?ring->datalen:len;
    
    //now let us think the method to fill the data,take care of the roll back
    lenleft = ret;
    dst = buf;
    
    /* 如果数据的偏移位置大于等于内存大小减去需要读取的数据大小,情况如下图,从old valid data开始读,
        读到vaild data2结束 */
    /*
     ----------------------------------------------------------------------------------------------
       ||     valid data2   ||//////empty space/////// || old valid data ||     valid data1   ||
     ----------------------------------------------------------------------------------------------   
    */
    if(ring->dataoff >= (ring->buflen - lenleft)) //which means the data has roll back
    {
        offset =ring->dataoff; //we cpy part    从offset位置开始读
        cpylen = ring->buflen - ring->dataoff;  //先读到内存末尾
        src = ring->buf + offset;        //设置开始读的地址

        if(cpylen > 0)
        {
            (void) memcpy(dst,src,cpylen);
            ring->dataoff = (ring->dataoff + cpylen)%ring->buflen;    //将dataoff移动到内存末尾
            ring->datalen -= cpylen;                                  //ring中的数据计数器减去已经读了的数据
            lenleft -= cpylen;                                        //计算剩余需要读取的数据
            dst += cpylen;                                            //移动dst指针指向用于存储数据的内存的为空的位置
        }
    }
    
    //here means we could move it by one time
    /* 从头部开始读 */
    if(lenleft > 0)
    {
        offset =ring->dataoff; //we cpy part
        cpylen = lenleft;
        src = ring->buf + offset;
        (void) memcpy(dst,src,cpylen);
        ring->dataoff = ring->dataoff + cpylen;
        ring->datalen -= cpylen;
    }
    return ret;
}

剩余函数


剩余函数比较简单,可以略过

/*    获取ring_buffrt中数据的长度
 *
 */
int ring_buffer_datalen(tag_ring_buffer_t *ring)
{
    int ret = -1;
    if(NULL != ring)
    {
        ret = ring->datalen;
    }
    return ret;
}

/*    获取ring_buffrt中剩余空间
 *
 */
int ring_buffer_freespace(tag_ring_buffer_t *ring)
{
    int ret = -1;
    if(NULL != ring)
    {
        ret = ring->buflen-ring->datalen;
    }
    return ret;
}

/*    重置ring_buffer,本质就是修改两个计数器即可
 *
 */
int ring_buffer_reset(tag_ring_buffer_t *ring)
{
    int ret = -1;
    if(NULL != ring)
    {
        ring->datalen = 0;
        ring->dataoff = 0;
        ret = 0;
    }
    return ret;
}

/*    ring_buffrt反初始化
 *
 */
int ring_buffer_deinit(tag_ring_buffer_t *ring)
{
    int ret = -1;
    if(NULL != ring)
    {
        (void) memset(ring,0,sizeof(tag_ring_buffer_t));
        ret = 0;
    }
    return ret;
}


举报
分享

分享文章到朋友圈

分享文章到微博

采纳成功

您已采纳当前回复为最佳回复

我是卤蛋

发帖: 121粉丝: 292

级别 : 版主,版块专家

发消息 + 关注

发表于2020年06月26日 11:12:17
直达本楼层的链接
沙发
显示全部楼层

感谢分享,持续学习~

点赞 评论 引用 举报

游客

富文本
Markdown
您需要登录后才可以回帖 登录 | 立即注册

结贴

您对问题的回复是否满意?
满意度
非常满意 满意 一般 不满意
我要反馈
0/200