ibcadmin 发表于 2019-10-17 09:45:09

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

<h2 id="基于linux的kfifo移植到stm32支持os的互斥访问">基于Linux的kfifo移植到STM32(支持os的互斥访问)</h2>
<h3 id="关于kfifo">关于kfifo</h3>
<p>kfifo是内核里面的一个First In First Out数据布局,它接纳环形循环队列的数据布局来实现;它提供一个无边界的字节省服务,最重要的一点是,它使用并行无锁编程技术,即当它用于只有一个入队线程和一个出队线程的场情时,两个线程可以并发操作,而不必要任何加锁活动,就可以包管kfifo的线程安全。</p>
<blockquote>
<p>具体什么是环形缓冲区,请看我以前的文章</p>
</blockquote>
<h3 id="说明">说明</h3>
<blockquote>
<p>关于kfifo的相干概念我不会介绍,有爱好可以看他的相干文档,我只将其实现过程移植重写,移植到实用stm32开辟板上,而且按照我个人习惯重新定名,<strong>RingBuff</strong>->意为环形缓冲区</p>
</blockquote>
<h3 id="ringbuff_t">RingBuff_t</h3>
<p>环形缓冲区的布局体成员变量,具体寄义看注释。<br />
buffer: 用于存放数据的缓存<br />
size: buffer空间的巨细<br />
in, out: 和buffer一起构成一个循环队列。 in指向buffer中队头,而且out指向buffer中的队尾</p>
<code>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 ;</code>
<h3 id="create_ringbuff">Create_RingBuff</h3>
<p>创建一个环形缓冲区,为了顺应后续对缓冲区入队出队的高效操作,环形缓冲区的巨细应为2^n字节,<br />
如果不是这个巨细,则体系默认裁剪以对应缓冲区字节。<br />
当然还可以优化,不外我现在并未做,思绪如下:如果体系支持动态分配内存,则向上对齐,制止浪费内存空间,否则就按照我默认的向下对齐,当内存越大,对齐导致内存走漏则会越多。对齐接纳的函数是<code>roundup_pow_of_two</code>。如果体系支持互斥量,那么还将创建一个互斥量用来做互斥访问,防止多线程同时使用导致数据丢失。</p>
<code>/************************************************************
* @brief   Create_RingBuff
* @param   rb:环形缓冲区句柄
*          buffer:环形缓冲区的数据区域
*          size:环形缓冲区的巨细,缓冲区巨细要为2^n
* @returnerr_t:ERR_OK表现创建成功,其他表现失败
* @authorjiejie
* @githubhttps://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;
}</code>
<h3 id="roundup_pow_of_two">roundup_pow_of_two</h3>
<code>/************************************************************
* @brief   roundup_pow_of_two
* @param   size:通报进来的数据长度
* @returnsize:返回处理惩罚之后的数据长度
* @authorjiejie
* @githubhttps://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));         //向上对齐,用动态内存可用使用
}</code>
<h3 id="delete_ringbuff">Delete_RingBuff</h3>
<p>删除一个环形缓冲区,删除之后,缓冲区真正存储地点是不会被改变的(现在我是使用自定义数组做缓冲区的),但是删除之后,就无法对缓冲区举行读写操作。而且如果支持os的话,创建的互斥量会被删除。</p>
<code>/************************************************************
* @brief   Delete_RingBuff
* @param   rb:环形缓冲区句柄
* @returnerr_t:ERR_OK表现成功,其他表现失败
* @authorjiejie
* @githubhttps://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;
}</code>
<h3 id="write_ringbuff">Write_RingBuff</h3>
<p>向环形缓冲区写入指定命据,支持线程互斥访问。用户想要写入缓冲区的数据长度不肯定是真正入队的长度,在完成的时间还要看看返回值是否与用户必要的长度同等~<br />
这个函数很故意思,也是比力高效的入队操作,将指定区域的数据拷贝到指定的缓冲区中,过程看注释即可</p>
<code>/************************************************************
* @brief   Write_RingBuff
* @param   rb:环形缓冲区句柄
* @param   wbuff:写入的数据起始地点
* @param   len:写入数据的长度(字节)
* @returnlen:实际写入数据的长度(字节)
* @authorjiejie
* @githubhttps://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;
}</code>
<h3 id="read_ringbuff">Read_RingBuff</h3>
<p>读取缓冲区数据到指定区域,用户指定读取长度,用户想要读取的长度不肯定是真正读取的长度,在读取完成的时间还要看看返回值是否与用户必要的长度同等~也支持多线程互斥访问。<br />
也是缓冲区出队的高效操作。过程看代码注释即可</p>
<code>/************************************************************
* @brief   Read_RingBuff
* @param   rb:环形缓冲区句柄
* @param   wbuff:读取数据生存的起始地点
* @param   len:想要读取数据的长度(字节)
* @returnlen:实际读取数据的长度(字节)
* @authorjiejie
* @githubhttps://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;
}</code>
<h3 id="获取缓冲区信息">获取缓冲区信息</h3>
<p>这些就比力简单了,看看缓冲区可读可写的数据有多少</p>
<code>/************************************************************
* @brief   CanRead_RingBuff
    * @param   rb:环形缓冲区句柄
    * @returnuint32:可读数据长度 0 / len
* @authorjiejie
* @githubhttps://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:环形缓冲区句柄
    * @returnuint32:可写数据长度 0 / len
* @authorjiejie
* @githubhttps://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));
}</code>
<h3 id="附带">附带</h3>
<p>这里的代码我是用于测试的,随便写的</p>
<code>    RingBuff_t ringbuff_handle;
   
    uint8_t rb;
    uint8_t res;
    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);</code>
<h3 id="支持多个os的互斥量操作">支持多个os的互斥量操作</h3>
<p>此处模仿了文件体系的互斥操作</p>
<code>#if USE_MUTEX
#defineMUTEX_TIMEOUT   1000   /* 超时时间 */
#defineMUTEX_T         mutex_t/* 互斥量控制块 */
#endif

/*********************************** mutex **************************************************/
#if USE_MUTEX
/************************************************************
* @brief   create_mutex
* @param   mutex:创建信号量句柄
* @return创建成功为1,0为不成功。
* @authorjiejie
* @githubhttps://github.com/jiejieTop
* @date    2018-xx-xx
* @version v1.0
* @note    创建一个互斥量,用户在os中互斥使用ringbuff,
*          支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
***********************************************************/
static err_t create_mutex(MUTEX_T *mutex)
{
err_t ret = 0;

//*mutex = rt_mutex_create("test_mux",RT_IPC_FLAG_PRIO); /* rtt */
//ret = (err_t)(*mutex != RT_NULL);
   
//*mutex = CreateMutex(NULL, FALSE, NULL);      /* Win32 */
//ret = (err_t)(*mutex != INVALID_HANDLE_VALUE);

//*mutex = OSMutexCreate(0, &err);      /* uC/OS-II */
//ret = (err_t)(err == OS_NO_ERR);

//*mutex = xSemaphoreCreateMutex();   /* FreeRTOS */
//ret = (err_t)(*mutex != NULL);

//ret = LOS_MuxCreate(&mutex);/* LiteOS */
//ret = (err_t)(ret != LOS_OK);
return ret;
}
/************************************************************
* @brief   deleta_mutex
* @param   mutex:互斥量句柄
* @returnNULL
* @authorjiejie
* @githubhttps://github.com/jiejieTop
* @date    2018-xx-xx
* @version v1.0
* @note    删除一个互斥量,支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
***********************************************************/
static err_t deleta_mutex(MUTEX_T *mutex)
{
    err_t ret;
   
//ret = rt_mutex_delete(mutex);   /* rtt */
   
//ret = CloseHandle(mutex);   /* Win32 */

//OSMutexDel(mutex, OS_DEL_ALWAYS, &err); /* uC/OS-II */
//ret = (err_t)(err == OS_NO_ERR);

//vSemaphoreDelete(mutex);      /* FreeRTOS */
//ret = 1;

//ret = LOS_MuxDelete(&mutex);/* LiteOS */
//ret = (err_t)(ret != LOS_OK);

    return ret;
}
/************************************************************
* @brief   request_mutex
* @param   mutex:互斥量句柄
* @returnNULL
* @authorjiejie
* @githubhttps://github.com/jiejieTop
* @date    2018-xx-xx
* @version v1.0
* @note    请求一个互斥量,得到互斥量的线程才答应举行访问缓冲区
*          支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
***********************************************************/
static err_t request_mutex(MUTEX_T *mutex)
{
    err_t ret;

//ret = (err_t)(rt_mutex_take(mutex, MUTEX_TIMEOUT) == RT_EOK);/* rtt */
   
//ret = (err_t)(WaitForSingleObject(mutex, MUTEX_TIMEOUT) == WAIT_OBJECT_0);/* Win32 */

//OSMutexPend(mutex, MUTEX_TIMEOUT, &err));       /* uC/OS-II */
//ret = (err_t)(err == OS_NO_ERR);

//ret = (err_t)(xSemaphoreTake(mutex, MUTEX_TIMEOUT) == pdTRUE);/* FreeRTOS */

//ret = (err_t)(LOS_MuxPend(mutex,MUTEX_TIMEOUT) == LOS_OK);          /* LiteOS */

    return ret;
}
/************************************************************
* @brief   release_mutex
* @param   mutex:互斥量句柄
* @returnNULL
* @authorjiejie
* @githubhttps://github.com/jiejieTop
* @date    2018-xx-xx
* @version v1.0
* @note    开释互斥量,当线程使用完资源必须开释互斥量
*          支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
***********************************************************/
static void release_mutex(MUTEX_T *mutex)
{
//rt_mutex_release(mutex);/* rtt */
   
//ReleaseMutex(mutex);      /* Win32 */

//OSMutexPost(mutex);   /* uC/OS-II */

//xSemaphoreGive(mutex);/* FreeRTOS */

//LOS_MuxPost(mutex);   /* LiteOS */
}
#endif
/*********************************** mutex **************************************************/</code>
<h3 id="debug.h">debug.h</h3>
<p>末了送一份debug的简便操作源码,由于前文许多时间会调用<br />
<code>PRINT_ERR</code><br />
<code>PRINT_DEBUG</code></p>
<code>#ifndef _DEBUG_H
#define _DEBUG_H
/************************************************************
* @brief   debug.h
* @authorjiejie
* @githubhttps://github.com/jiejieTop
* @date    2018-xx-xx
* @version v1.0
* @note    此文件用于打印日记信息
***********************************************************/
/**
* @name Debug print
* @{
*/
#define PRINT_DEBUG_ENABLE      1       /* 打印调试信息 */
#define PRINT_ERR_ENABLE            1   /* 打印错误信息 */
#define PRINT_INFO_ENABLE         0       /* 打印个人信息 */


#if PRINT_DEBUG_ENABLE
#define PRINT_DEBUG(fmt, args...)    do{(printf("\n >> "), printf(fmt, ##args));}while(0)   
#else
#define PRINT_DEBUG(fmt, args...)      
#endif

#if PRINT_ERR_ENABLE
#define PRINT_ERR(fmt, args...)      do{(printf("\n >> "), printf(fmt, ##args));}while(0)   
#else
#define PRINT_ERR(fmt, args...)      
#endif

#if PRINT_INFO_ENABLE
#define PRINT_INFO(fmt, args...)   do{(printf("\n >> "), printf(fmt, ##args));}while(0)   
#else
#define PRINT_INFO(fmt, args...)         
#endif

/**@} */
   
//针对差别的编译器调用差别的stdint.h文件
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
    #include <stdint.h>
#endif

/* 断言 Assert */
#define AssertCalled(char,int)printf("\nError:%s,%d\r\n",char,int)
#define ASSERT(x)   if((x)==0)AssertCalled(__FILE__,__LINE__)

typedef enum
{
    ASSERT_ERR = 0,                           /* 错误 */
    ASSERT_SUCCESS = !ASSERT_ERR    /* 精确 */
} Assert_ErrorStatus;

typedef enum
{
    FALSE = 0,      /* 假 */
    TRUE = !FALSE   /* 真 */
}ResultStatus;

#endif /* __DEBUG_H */</code>
<h1 id="喜好就关注我吧">喜好就关注我吧!</h1>
<p><div align="center"></div></p>
<p>相干代码可以在公众号背景获取。<br />
更多资料欢迎关注“物联网IoT开辟”公众号!</p><br><br/><br/><br/><br/><br/>来源:<a href="https://www.cnblogs.com/iot-dev/p/11688794.html" target="_blank">https://www.cnblogs.com/iot-dev/p/11688794.html</a>
页: [1]
查看完整版本: 基于Linux的kfifo移植到STM32(支持os的互斥访问)