基于Linux的kfifo移植到STM32(支持os的互斥访问)

2019-12-14来源: eefocus关键字:Linux  kfifo  移植  STM32  互斥访问

关于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);


/* 如果溢出则

[1] [2]
关键字:Linux  kfifo  移植  STM32  互斥访问 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic482872.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:一种Cortex-M内核中的精确延时方法
下一篇:【干货】老外的GitHub整理的stm32f4驱动库

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

ARMv8 Linux内核源码分析:__flush_dcache_all()
1.1 /* *  __flush_dcache_all()*  Flush the wholeD-cache. * Corrupted registers: x0-x7, x9-x11 */ENTRY(__flush_dcache_all)//保证之前的访存指令的顺序    dsb sy                 //读cache level id register    mrs x0, clidr_el1    &n
发表于 2020-01-19
ARMv8 Linux内核源码分析:__flush_dcache_all()
ARM linux内核在内存中的布局
Kernel Memory Layout on ARM Linux Russell King <rmk@arm.linux.org.uk>      November 17, 2005 (2.6.15)This document describes the virtual memory layout which the Linuxkernel uses for ARM processors.  It indicates which regions arefree for platforms to use, and which are used by generic
发表于 2020-01-19
单片机成长之路(51基础篇) - 006 在Linux下搭建开发烧写环境
在Linux下没有像keli那样好用的IDE来开发51单片机,开发环境只能自己搭建了。 第一步:安装交叉编译工具 a) 安装SDCC sudo apt-get install sdcc b)测试SDCC是否可用,这是个网上找的简单的流水灯代码 test.c, 用来测试 1 #include "8051.h" 2  3 #define uint unsigned int  4 #define uchar unsigned char  5 uchar tab[8] = {0x01
发表于 2020-01-16
单片机成长之路(51基础篇) - 006 在Linux下搭建开发烧写环境
在ARM Linux 使用 Valgrind
Linux valgrind 移植到ARM-Linux 一、Cross-Compile/交叉编译(1)下载及解压Valgrind-3.11(2)修改confirure  将armv7*)修改为armv7*|arm*)(3)执行configure./configure CC=arm-linux-gcc CPP=arm-linux-cpp CXX=arm-linux-g++  --host=arm-linux --prefix=/opt/valgrind/lib注意:CC=arm-linux-gcc,之所以没有像有些博客上写的、用了绝对路径,是因为「我已经将arm-linux-gcc 软链接/soft-linke
发表于 2020-01-13
ARM-Linux移植之二
平台:mini2440  交叉工具链:arm-linux-gcc-4.3.2 一、内核移植基本知识移植内核也叫构建BSP(boardsupprot packet)。BSP的作用有两个:一是为内核运行提供底层支持,二是屏蔽与板相关的细节。BSP的构建分三个层次1、体系结构层次对一些体系结提供linux内核支持,比如说ARM,X86等芯片。这一类工作一般在arc/xxx/下面额除了palt-xxx和mach-xxx目录的其他目录完成。2、SOC层次对一些公司提供的SOC微处理器提供linux内核支持,比如说三星公司的   S3C2440。这一类工作一般在arch/xxx
发表于 2020-01-12
arm-linux-ld 命令详解
本文转自《S3C2410完全开发手册》在开始后续实验之前,我们得了解一下arm-linux-ld连接命令的使用。在上述实验中,我们一直使用类似如下的命令进行连接:arm-linux-ld -Ttext 0x00000000 crt0.o led_on_c.o -o led_on_c_tmp.o我们看看它是什么意思:-o选项设置输出文件的名字为led_on_c_tmp.o;“--Ttext 0x00000000”设置代码段的起始地址为0x00000000;这条指令的作用就是将crt0.o和led_on_c.o连接成led_on_c_mp.o可执行文件,此可执行文件的代码段起始地址为0x00000000(即从这里开始执行)。我们感兴趣
发表于 2020-01-12
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved