• STM32 & FreeRTOS & KFIFO (巧夺天工)


    巧夺天工 的 KFIFO ,用STM32实现。

    实现源文件如下:

    /**********************************************************
      *
      *  文件名:    kfifo.c
      *
      *  文件描述:    该文件包含的kfifo的处理函数
      *
      *  创建人:       GXP
      *
      *   创建日期:     2016年8月9日16:13:06
      *
      *  版本号:    1.0
      *
      *  修改记录:    无
        *   
        *   本文参考博客地址: http://blog.csdn.net/linyt/article/details/5764312
        *                     http://blog.csdn.net/chen19870707/article/details/39899743
      *
    ***********************************************************/
    
    
    #include "kfifo.h"
    
    #include "FreeRTOS.h"
    #include "task.h"
    
    
    #define min(a, b)                (((a) < (b)) ? (a) : (b))
    
    
    
    //找出最接近 最大2的指数次幂
    unsigned int roundup_pow_of_two(unsigned int date_roundup_pow_of_two )
    {            
        /* 这里采用 STM32 硬件提供的计算前导零指令 CLZ
         * 举个例子,假如变量date_roundup_pow_of_two 0x09
         *(二进制为:0000 0000 0000 0000 0000 0000 0000 1001), 即bit3和bit0为1
         * 则__clz( (date_roundup_pow_of_two)的值为28,即最高位1 前面有28个0,32-28 =3 代表最高位1 的 位置
         * 31UL 表示 无符号 int 数字 31,否则默认为 有符号 int 数字 31
         * 这里参考  FreeRTOS 的 寻找高级优先级任务 的写法,详细解释到朱工博客
         * 博客地址: http://blog.csdn.net/zhzht19861011/article/details/51418383
         */
    
        return ( 1UL << ( 32UL - ( unsigned int ) __clz( (date_roundup_pow_of_two) ) ) );
    
    }
    
    
    /* 
     * 每次调用这个函数都会产生 两个内存块,一个内存块指向struct KFIFO,一个指向 KFIFO.buff
     * 因此 如果这两个内存块不在使用请释放掉!GXP,2016年8月17日12:55:54
     */
    
    struct KFIFO *kfifo_alloc(unsigned int size) 
    {   
        unsigned char *buffer;
        
        struct KFIFO *ret;
        
            ret=(struct KFIFO *) pvPortMalloc(sizeof (struct KFIFO));
    
        /*  
         * round up to the next power of 2, since our 'let the indices  
         * wrap' tachnique works only in this case.  
             * 如果size 是2的 次幂圆整,则 size & (size - 1)  =0
         */
      
        if (size & (size - 1)) 
            {   
                //        BUG_ON(size > 0x80000000);  
                
                //如果你要申请的buffer 不是 2的 次幂圆整,就要把 size 变成 2的次幂圆整 ,方便下面计算
            size = roundup_pow_of_two(size);
        }
            
            //这里使用 FreeRTOS的 分配内存的 API
        buffer = (unsigned char*) pvPortMalloc(size);
            
        if (!buffer)   //如果返回的值为NULL,这说明分配内存失败
            return 0UL;
      
            //    ret = kfifo_init(buffer, size, lock);   
            
            ret->buffer=buffer;
            ret->size  =size;
            ret->in  = 0;
            ret->out = 0;
            
        if (!ret) //如果ret的值为NULL,这说明分配内存失败
            vPortFree(buffer); //释放之前分配的 内存空间
      
        return ret;
            
    }
    
    
    unsigned int __kfifo_put(struct KFIFO *fifo, unsigned char *buffer, unsigned int len)   
    {
        unsigned int L;
        
            //环形缓冲区的剩余容量为fifo->size - fifo->in + fifo->out,让写入的长度取len和剩余容量中较小的,避免写越界;
        len = min( len , fifo->size - fifo->in + fifo->out );
      
        /*  
         * Ensure that we sample the fifo->out index -before- we  
         * start putting bytes into the kfifo.  
         */   
                    //多处理器 处理内存 的 屏障,STM32不需要这个
                    //    smp_mb(); 
      
        /* first put the data starting from fifo->in to buffer end */
                    /* 首先将数据从fifo.in 所在的位置开始写,写之前,首先要看一下fifo->in到 buffer 末尾的大小 是不是 比 len 大*/
        
                    /*
                     * 前面讲到fifo->size已经2的次幂圆整,主要是方便这里计算,提升效率
                 * 在对10进行求余的时候,我们发现,余数总是整数中的个位上的数字,而不用管其他位是什么;
                     * 所以,kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1),效率会提升
                     * 所以fifo->size - (fifo->in & (fifo->size - L)) 即位 fifo->in 到 buffer末尾所剩余的长度,
                     * L取len和剩余长度的最小值,即为需要拷贝L 字节到fifo->buffer + fifo->in的位置上。
                     */ 
        L = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
        
        memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, L);   
      
        /* then put the rest (if any) at the beginning of the buffer */ 
    
        memcpy(fifo->buffer, buffer + L, len - L);
      
        /*  
         * Ensure that we add the bytes to the kfifo -before-  
         * we update the fifo->in index.  
         */   
      
          // smp_wmb();   //多处理器 处理内存 的 屏障,STM32不需要这个    
    
                /* 
                 * 注意这里 只是用了 fifo->in +=  len而未取模,
                 * 这就是kfifo的设计精妙之处,这里用到了unsigned int的溢出性质,
                 * 当in 持续增加到溢出时又会被置为0,这样就节省了每次in向前增加都要取模的性能,
                 * 锱铢必较,精益求精,让人不得不佩服。
                 */
      
        fifo->in += len; 
            
          /*返回值 代表  写入数据的个数 ,这样 就可以根据返回值 判断缓冲区是否写满*/
        return len;   
    }  
      
    unsigned int __kfifo_get(struct KFIFO *fifo, unsigned char *buffer, unsigned int len)   
    {
        unsigned int L;   
      
        len = min(len, fifo->in - fifo->out);   
      
        /*  
         * Ensure that we sample the fifo->in index -before- we  
         * start removing bytes from the kfifo.  
         */   
      
        //smp_rmb();    //多处理器 处理内存 的 屏障,STM32不需要这个
      
        /* first get the data from fifo->out until the end of the buffer */   
        L = min(len, fifo->size - (fifo->out & (fifo->size - 1)));   
        memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), L);   
      
        /* then get the rest (if any) from the beginning of the buffer */   
        memcpy(buffer + L, fifo->buffer, len - L);   
      
        /*  
         * Ensure that we remove the bytes from the kfifo -before-  
         * we update the fifo->out index.  
         */   
      
        //smp_mb();   //多处理器 处理内存 的 屏障,STM32不需要这个
                
                /*
               * 注意这里 只是用了 fifo->out +=  len 也未取模运算,
                 * 同样unsigned int的溢出性质,当out 持续增加到溢出时又会被置为0,
                 * 如果in先溢出,出现 in  < out 的情况,那么 in – out 为负数(又将溢出),
                 * in – out 的值还是为buffer中数据的长度。
                 */
    
        fifo->out += len;
      
        return len;  
    }

    头文件如下:

    #ifndef _KFIFO_H_
    #define _KFIFO_H
    
    
    //声明 一个 结构体 kfifo
    
    struct KFIFO
    {   
        unsigned char *buffer;    /* the buffer holding the data */   
        unsigned int size;            /* the size of the allocated buffer */   
        unsigned int in;                /* data is added at offset (in % size) */   
        unsigned int out;                /* data is extracted from off. (out % size) */   
            /*STM32 只有一个核心,同一时刻只能写或者读,因此不需要*/
            //    volatile unsigned int *lock; /* protects concurrent modifications */  
    };
    
    unsigned int roundup_pow_of_two( unsigned int date_roundup_pow_of_two );
    
    struct KFIFO *kfifo_alloc(unsigned int size);
    
    unsigned int __kfifo_put(struct KFIFO *fifo, unsigned char *buffer, unsigned int len);
    
    unsigned int __kfifo_get(struct KFIFO *fifo, unsigned char *buffer, unsigned int len);
    
    #endif

    上面  实现 找出  最接近 最大2的指数次幂 的是通过 STM32 一个特殊的 寄存器实现的 ,可以百度 找 C 语言 实现的方式。

    以及内存 的 分配 是由  FreeRTOS提供  的 内存分配和释放  实现的 。

    使用 方式如下:

    //创建一个 KFIFO 的结构体 指针
    
    struct KFIFO *test_kifo_buffer=NULL;
            //kfifo 测试 第一步: 创建 一个 1024字节的 fifo buff
        
                //首先定义一个 在全局变量中定义一个 KFIFO 结构体 test_kifo_buffer
                
                //接着 给这个 KFIFO 结构体 test_kifo_buffer 申请 一个 1024字节的 内存空间
        
        //你如果分配468,会分配512字节的空间,你如果写 668,就会分配1024字节的 空间,1025就分配 2048
        //因为这样 会方便计算
        
        test_kifo_buffer=kfifo_alloc( 668);//这里写入668,也分配的是 1024 字节的 空间
        
        if( !test_kifo_buffer )
                printf("
     KFIFO 结构体 test_kifo_buffer 没有创建成功!
    ");
        //kfifo 测试 第二步: 发送10个字节写入到 KFIFO 结构体 test_kifo_buffer 中
            write_counter=__kfifo_put(test_kifo_buffer, test_fifo_write_buff, 10 );
            
                if(write_counter!=10)
                    printf("
    发送10个字节写入到KFIFO结构体test_kifo_buffer失败,写入个数是:%d.
    ",write_counter);
            read_counter=read_counter=__kfifo_get(test_kifo_buffer, test_fifo_read_buff,5);
            
            printf("从test_kifo_buffer缓冲区读取的数据是%s,读出的个数是%d.
    ",test_fifo_read_buff,read_counter);
            
            printf("test_kifo_buffer->size:%d,test_kifo_buffer->out:%d,test_kifo_buffer->in:%d.
    ",test_kifo_buffer->size,test_kifo_buffer->out,test_kifo_buffer->in);
  • 相关阅读:
    点分治 / 点分树题目集
    HNOI2019 游记
    WC2019 题目集
    SA / SAM 题目集
    Min_25 筛小结
    NOIP2018 差点退役记
    Atcoder 乱做
    DP及其优化
    计数与概率期望小结
    分库分表之后全局id咋生成?
  • 原文地址:https://www.cnblogs.com/suozhang/p/6373510.html
Copyright © 2020-2023  润新知