历史上的今天
返回首页

历史上的今天

今天是:2024年12月14日(星期六)

2019年12月14日 | 基于Linux的kfifo移植到STM32(支持os的互斥访问)

2019-12-14 来源:eefocus

关于kfifo

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


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


说明

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


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    可写数据长度

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

uint32_t CanWrite_RingBuff(RingBuff_t *rb)

{

if(NULL == rb)

{

PRINT_ERR("ringbuff is null!");

return 0;

}


return (rb->size - CanRead_RingBuff(rb));

}


附带

这里的代码我是用于测试的,随便写的


RingBuff_t ringbuff_handle;

uint8_t rb[64];

uint8_t res[64];

Create_RingBuff(&ringbuff_handle, 

rb,

sizeof(rb));

Write_RingBuff(&ringbuff_handle,

                     res, 

                     datapack.data_length);

PRINT_DEBUG("CanRead_RingBuff = %d!",CanRead_RingBuff(&ringbuff_handle));

PRINT_DEBUG("CanWrite_RingBuff = %d!",CanWrite_RingBuff(&ringbuff_handle));

Read_RingBuff(&ringbuff_handle,

                     res, 

                     datapack.data_length);


支持多个os的互斥量操作

此处模仿了文件系统的互斥操作


#if USE_MUTEX

#define  MUTEX_TIMEOUT   1000     /* 超时时间 */

#define  MUTEX_T         mutex_t  /* 互斥量控制块 */

#endif


/*********************************** mutex **************************************************/

推荐阅读

史海拾趣

Fullywell Semiconductor Co Ltd公司的发展小趣事

Fullywell Semiconductor成立于2005年,由一群在半导体领域拥有多年经验的工程师和投资人共同创立。公司初期专注于研发高性能模拟集成电路,以解决当时市场上对于低功耗、高精度信号处理芯片的迫切需求。通过不懈的努力,Fullywell成功推出了其首款产品——一款针对移动通信设备的低功耗射频前端芯片,迅速获得了市场的认可。这一技术创新不仅为公司赢得了首批重要客户,也为后续的发展奠定了坚实的技术基础。

Genisco Electronics公司的发展小趣事
考虑当地的燃料供应情况和成本,选择适合的燃料类型(如柴油、燃气等)。
Anders DX公司的发展小趣事
对互投电路中的设备进行定期检查和维护,确保其处于良好状态。
冠图电子(GTL-POWER)公司的发展小趣事
负责检测电源状态并控制电源的切换。
bb-smartworx公司的发展小趣事
根据负载的功率、电压、电流等需求选择合适的发电机容量和类型。
Diamond SA公司的发展小趣事

在电子行业竞争日益激烈的背景下,Diamond SA公司开始寻求通过并购来实现规模扩张和资源整合。他们精心挑选了一些具有潜力的目标企业,通过谈判和交易成功完成了并购。这些并购不仅为Diamond SA公司带来了更多的技术资源和市场份额,还增强了公司的整体竞争力。

问答坊 | AI 解惑

Protel99出现“Access violation at address 40067324 in module 'Vcl50.bpl'.

使用Protel99打开PCB图的时候出现“Access violation at address 40067324 in module \'Vcl50.bpl\'. Write of address 0000014C” 请问怎么解决啊…

查看全部问答>

示波器开发总结

这段时间一直比较忙,没有时间来整理资料,幸亏有汤汤的提醒,做一些简单的总结和资料的发布。 示波器的性能指标: 最高60M采样; 具有上升边沿或下降边沿的电平触发功能; 采用2.8寸TFT显示,人机界面友好; 采用STM32控制器作为主控芯片,波 ...…

查看全部问答>

终结嵌入式时代的方案(原创)

这样设想,有一个家庭,他们有很多家用电器,包括电视机,空调,微波炉,冰箱等等,他们都需要有一个控制程序。现在一般是用嵌入式在家电里面进行编程是吧?这样有很多问题,一个是开发成本,因为要给每个家电做个小程序,用来控制它,一个是这个嵌 ...…

查看全部问答>

LPC2478能跑WinCE吗?

得到一块LPC2478的开发板,里面的资源太少了,只有些Demo程序,如果自己在上面移植个操作系统,ARM7TDMI-S,没MMU也能跑Linux、winCE吗? 请了解的人给点建议。…

查看全部问答>

串口通信的问题,高手来帮帮忙!

我们单位要给新买的AN2512C支流低电阻测量仪编一个软件用串口读取测量数据,vc++。这台仪器支持串口通信(说明书里也提供了通信协议)。可是我编出来之后“不灵”(也就是说那台仪器“没有反应”),求教高手!谢谢。 我用串口精灵也调过,按照协 ...…

查看全部问答>

何时可以买到STM32L的样品?

多处寻找STM32L样品未果,请版主提供一下线索,还有多久。 如果等待太长只好再选其他M3…

查看全部问答>

AD DA 芯片求推荐

主要是希望牛牛们推荐符合要求的主流片子。 AD/DA分辨率都为12bit以上, 多路(AD一共需要8路,DA一共需要16路,当然不一定一片搞定)。 速度是AD总转换时间小于5us,DA建立时间小于5us。  …

查看全部问答>

怎样让两个信号源发出的信号同步

我做的一个模电书中反向加法电路,想让两个信号源中发出的信号同步,比如全是100HZ,峰峰值是100mv,这两者之间没有相位差。这是我的两个信号源发生器,下图…

查看全部问答>

Xilinx ISE软件安装包

有木有给个Xilinx ise的安装包加破解包呢。。。小女子电脑是win764位机,几次下载安装都不正确。。而且Xilinx官网注册了但是老显示错误。。急求,谢谢。。。…

查看全部问答>