基于Linux的kfifo移植到STM32

发布时间:2024-02-03  

关于kfifo

kfifo是内核里面的一个First In First Out数据结构,它采用环形循环队列的数据结构来实现;它提供一个无边界的字节流服务,最重要的一点是,它使用并行无锁编程技术,即当它用于只有一个入队线程和一个出队线程的场情时,两个线程可以并发操作,而不需要任何加锁行为,就可以保证kfifo的线程安全。


具体什么是环形缓冲区,请看我以前的文章



说明

关于kfifo的相关概念我不会介绍,有兴趣可以看他的相关文档,我只将其实现过程移植重写,移植到适用stm32开发板上,并且按照我个人习惯重新命名, RingBuff ->意为环形缓冲区


往期关于环形缓冲区的文章:


杰杰带你解读【机智云】环形缓冲区源码


STM32进阶之串口环形缓冲区实现


RingBuff_t

环形缓冲区的结构体成员变量,具体含义看注释。


buffer: 用于存放数据的缓存


size: buffer空间的大小


in, out: 和buffer一起构成一个循环队列。 in指向buffer中队头,而且out指向buffer中的队尾


typedef struct ringbuff 

{

    uint8_t *buffer;    /* 数据区域 */

    uint32_t size;      /* 环形缓冲区大小 */

    uint32_t in;        /* 数据入队指针 (in % size) */

    uint32_t out;       /* 数据出队指针 (out % size) */

#if USE_MUTEX

    MUTEX_T *mutex;       /* 支持rtos的互斥 */

#endif

}RingBuff_t ;

Create_RingBuff

创建一个环形缓冲区,为了适应后续对缓冲区入队出队的高效操作,环形缓冲区的大小应为2^n字节,


如果不是这个大小,则系统默认裁剪以对应缓冲区字节。


当然还可以优化,不过我目前并未做,思路如下:如果系统支持动态分配内存,则向上对齐,避免浪费内存空间,否则就按照我默认的向下对齐,当内存越大,对齐导致内存泄漏则会越多。对齐采用的函数是roundup_pow_of_two。如果系统支持互斥量,那么还将创建一个互斥量用来做互斥访问,防止多线程同时使用导致数据丢失。


/************************************************************

  * @brief   Create_RingBuff

  * @param   rb:环形缓冲区句柄

  *          buffer:环形缓冲区的数据区域

  *          size:环形缓冲区的大小,缓冲区大小要为2^n

  * @return  err_t:ERR_OK表示创建成功,其他表示失败

  * @author  jiejie

  * @github  https://github.com/jiejieTop

  * @date    2018-xx-xx

  * @version v1.0

  * @note    用于创建一个环形缓冲区

  ***********************************************************/

err_t Create_RingBuff(RingBuff_t* rb, 

                      uint8_t *buffer,

                      uint32_t size

                                )

{

    if((rb == NULL)||(buffer == NULL)||(size == 0))

    {

        PRINT_ERR("data is null!");

        return ERR_NULL;

    }


    PRINT_DEBUG("ringbuff size is %d!",size);

    /* 缓冲区大小必须为2^n字节,系统会强制转换,

         否则可能会导致指针访问非法地址。

         空间大小越大,强转时丢失内存越多 */

    if(size&(size - 1))

    {

        size = roundup_pow_of_two(size);

        PRINT_DEBUG("change ringbuff size is %d!",size);

    }


    rb->buffer = buffer;

    rb->size = size;

    rb->in = rb->out = 0;

#if USE_MUTEX    

  /* 创建信号量不成功 */

  if(!create_mutex(rb->mutex))

  {

    PRINT_ERR("create mutex fail!");

    ASSERT(ASSERT_ERR);

    return ERR_NOK;

  }

#endif

    PRINT_DEBUG("create ringBuff ok!");

    return ERR_OK;

}

roundup_pow_of_two

/************************************************************

  * @brief   roundup_pow_of_two

  * @param   size:传递进来的数据长度

  * @return  size:返回处理之后的数据长度

  * @author  jiejie

  * @github  https://github.com/jiejieTop

  * @date    2018-xx-xx

  * @version v1.0

  * @note    用于处理数据,使数据长度必须为 2^n

    *                    如果不是,则转换,丢弃多余部分,如

    *                    roundup_pow_of_two(66) -> 返回 64

  ***********************************************************/

static unsigned long roundup_pow_of_two(unsigned long x)

{

    return (1 << (fls(x-1)-1));             //向下对齐

  //return (1UL << fls(x - 1));            //向上对齐,用动态内存可用使用

}

Delete_RingBuff

删除一个环形缓冲区,删除之后,缓冲区真正存储地址是不会被改变的(目前我是使用自定义数组做缓冲区的),但是删除之后,就无法对缓冲区进行读写操作。并且如果支持os的话,创建的互斥量会被删除。


/************************************************************

  * @brief   Delete_RingBuff

  * @param   rb:环形缓冲区句柄

  * @return  err_t:ERR_OK表示成功,其他表示失败

  * @author  jiejie

  * @github  https://github.com/jiejieTop

  * @date    2018-xx-xx

  * @version v1.0

  * @note    删除一个环形缓冲区

  ***********************************************************/

err_t Delete_RingBuff(RingBuff_t *rb)

{

    if(rb == NULL)

    {

        PRINT_ERR("ringbuff is null!");

        return ERR_NULL;

    }


    rb->buffer = NULL;

    rb->size = 0;

    rb->in = rb->out = 0;

#if USE_MUTEX    

  if(!deleta_mutex(rb->mutex))

  {

    PRINT_DEBUG("deleta mutex is fail!");

    return ERR_NOK;

  }

#endif

    return ERR_OK;

}

Write_RingBuff

向环形缓冲区写入指定数据,支持线程互斥访问。用户想要写入缓冲区的数据长度不一定是真正入队的长度,在完成的时候还要看看返回值是否与用户需要的长度一致~


这个函数很有意思,也是比较高效的入队操作,将指定区域的数据拷贝到指定的缓冲区中,过程看注释即可


/************************************************************

  * @brief   Write_RingBuff

  * @param   rb:环形缓冲区句柄

  * @param   wbuff:写入的数据起始地址

  * @param   len:写入数据的长度(字节)

  * @return  len:实际写入数据的长度(字节)

  * @author  jiejie

  * @github  https://github.com/jiejieTop

  * @date    2018-xx-xx

  * @version v1.0

  * @note    这个函数会从buff空间拷贝len字节长度的数据到

             rb环形缓冲区中的空闲空间。

  ***********************************************************/

uint32_t Write_RingBuff(RingBuff_t *rb,

                        uint8_t *wbuff, 

                        uint32_t len)

{

  uint32_t l;

#if USE_MUTEX

  /* 请求互斥量,成功才能进行ringbuff的访问 */

  if(!request_mutex(rb->mutex))

  {

    PRINT_DEBUG("request mutex fail!");

    return 0;

  }

  else  /* 获取互斥量成功 */

  {

#endif

    len = min(len, rb->size - rb->in + rb->out);


    /* 第一部分的拷贝:从环形缓冲区写入数据直至缓冲区最后一个地址 */

    l = min(len, rb->size - (rb->in & (rb->size - 1)));

    memcpy(rb->buffer + (rb->in & (rb->size - 1)), wbuff, l);


    /* 如果溢出则在缓冲区头写入剩余的部分

       如果没溢出这句代码相当于无效 */

    memcpy(rb->buffer, wbuff + l, len - l);


    rb->in += len;


    PRINT_DEBUG("write ringBuff len is %d!",len);

#if USE_MUTEX

  }

  /* 释放互斥量 */

  release_mutex(rb->mutex);

#endif

  return len;

}

Read_RingBuff

读取缓冲区数据到指定区域,用户指定读取长度,用户想要读取的长度不一定是真正读取的长度,在读取完成的时候还要看看返回值是否与用户需要的长度一致~也支持多线程互斥访问。


也是缓冲区出队的高效操作。过程看代码注释即可


/************************************************************

  * @brief   Read_RingBuff

  * @param   rb:环形缓冲区句柄

  * @param   wbuff:读取数据保存的起始地址

  * @param   len:想要读取数据的长度(字节)

  * @return  len:实际读取数据的长度(字节)

  * @author  jiejie

  * @github  https://github.com/jiejieTop

  * @date    2018-xx-xx

  * @version v1.0

  * @note    这个函数会从rb环形缓冲区中的数据区域拷贝len字节

             长度的数据到rbuff空间。

  ***********************************************************/

uint32_t Read_RingBuff(RingBuff_t *rb,

                       uint8_t *rbuff, 

                       uint32_t len)

{

  uint32_t l;

#if USE_MUTEX

  /* 请求互斥量,成功才能进行ringbuff的访问 */

  if(!request_mutex(rb->mutex))

  {

    PRINT_DEBUG("request mutex fail!");

    return 0;

  }

  else

  {

#endif

    len = min(len, rb->in - rb->out);


    /* 第一部分的拷贝:从环形缓冲区读取数据直至缓冲区最后一个 */

    l = min(len, rb->size - (rb->out & (rb->size - 1)));

    memcpy(rbuff, rb->buffer + (rb->out & (rb->size - 1)), l);


    /* 如果溢出则在缓冲区头读取剩余的部分

       如果没溢出这句代码相当于无效 */

    memcpy(rbuff + l, rb->buffer, len - l);


    rb->out += len;


    PRINT_DEBUG("read ringBuff len is %d!",len);

#if USE_MUTEX

  }

  /* 释放互斥量 */

  release_mutex(rb->mutex);

#endif

  return len;

}

获取缓冲区信息

这些就比较简单了,看看缓冲区可读可写的数据有多少


/************************************************************

  * @brief   CanRead_RingBuff

    * @param   rb:环形缓冲区句柄

    * @return  uint32:可读数据长度 0 / len

  * @author  jiejie

  * @github  https://github.com/jiejieTop

  * @date    2018-xx-xx

  * @version v1.0

  * @note    可读数据长度

  ***********************************************************/

uint32_t CanRead_RingBuff(RingBuff_t *rb)

{

    if(NULL == rb)

    {

        PRINT_ERR("ringbuff is null!");

        return 0;

    }

    if(rb->in == rb->out)

        return 0;


    if(rb->in > rb->out)

        return (rb->in - rb->out);


    return (rb->size - (rb->out - rb->in));

}


/************************************************************

  * @brief   CanRead_RingBuff

    * @param   rb:环形缓冲区句柄

    * @return  uint32:可写数据长度 0 / len

  * @author  jiejie

  * @github  https://github.com/jiejieTop

  * @date    2018-xx-xx

  * @version v1.0

  * @note    可写数据长度

  ***********************************************************/

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>