Porting kfifo based on Linux to STM32 (supporting mutually exclusive access of os)

Keywords: C github REST Linux Programming

Porting kfifo based on Linux to STM32 (supporting mutually exclusive access of os)

About kfifo

Kfifo is a First In First Out data structure in the kernel, which uses the data structure of circular queue to realize; it provides a boundless byte stream service, and the most important point is that it uses the parallel lock free programming technology, that is, when it is used in the field with only one queue in thread and one queue out thread, the two threads can operate concurrently without any need. The locking behavior can ensure the thread safety of kfifo.

What is ring buffer? Please see my previous article.

Explain

I will not introduce the related concepts of kfifo. If you are interested, you can see his related documents. I only transplant and rewrite the implementation process to the applicable stm32 development board, and rename it according to my personal habits. Ringbuff - > means ring buffer.

RingBuff_t

The structure member variable of the ring buffer. See the note for the specific meaning.
buffer: the cache used to store data
Size: size of buffer space
in, out: forms a circular queue with buffer. in points to the head of the buffer Squadron, and out points to the tail of the buffer squadron

typedef struct ringbuff 
{
    uint8_t *buffer;      /* Data region */
    uint32_t size;      /* Ring buffer size */
    uint32_t in;        /* Data entry pointer (in% size) */
    uint32_t out;       /* Data out% size */
#if USE_MUTEX
    MUTEX_T *mutex;       /* Supporting rtos mutual exclusion */
#endif
}RingBuff_t ;

Create_RingBuff

Create a ring buffer. The size of the ring buffer should be 2^n bytes in order to adapt to the subsequent efficient operation of buffer queuing.
If it is not this size, the system defaults to clipping to correspond to the buffer bytes.
Of course, it can also be optimized, but I haven't done so far. The idea is as follows: if the system supports dynamic memory allocation, align up to avoid wasting memory space. Otherwise, align down according to my default. The larger the memory, the more memory leaks will be caused by alignment. The function used for alignment is round out of two. If the system supports mutex, a mutex will also be created for mutex access to prevent data loss caused by simultaneous use of multiple threads.

/************************************************************
  * @brief   Create_RingBuff
  * @param   rb: Ring buffer handle
  *          buffer: Data area of ring buffer
  *          size: Size of ring buffer, buffer size should be 2^n
  * @return  err_t: ERR_OK Indicates creation success, others indicate failure
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Used to create a ring buffer
  ***********************************************************/
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);
    /* Buffer size must be 2^n bytes, system will cast,
         Otherwise, the pointer may access the illegal address.
         The larger the space is, the more memory will be lost during strong rotation */
    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    
  /* Failed to create semaphore */
  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: Length of data passed in
  * @return  size: Return the data length after processing
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Used to process data so that the data length must be 2^n
    *                     If not, convert and discard the extra parts, such as
    *                     roundup_pow_of_two(66) -> Return to 64
  ***********************************************************/
static unsigned long roundup_pow_of_two(unsigned long x)
{
    return (1 << (fls(x-1)-1));                //Alignment down
  //Return (1ul < FLS (x - 1)); / / align up and use dynamic memory.
}

Delete_RingBuff

Delete a ring buffer. After deletion, the real storage address of the buffer will not be changed (currently I use a custom array as the buffer), but after deletion, the buffer cannot be read or written. And if os is supported, the created mutex will be deleted.

/************************************************************
  * @brief   Delete_RingBuff
  * @param   rb: Ring buffer handle
  * @return  err_t: ERR_OK Success, other failures
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Delete a ring buffer
  ***********************************************************/
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

Writes the specified data to the ring buffer, and supports thread exclusive access. The length of the data that the user wants to write to the buffer is not necessarily the length of the actual queue, but also to see whether the return value is consistent with the length required by the user when it is finished.~
This function is very interesting and efficient. Copy the data in the specified area to the specified buffer. See the comments for the process.

/************************************************************
  * @brief   Write_RingBuff
  * @param   rb:Ring buffer handle
  * @param   wbuff:Data start address written
  * @param   len:Length of data written (bytes)
  * @return  len:Length of data actually written (bytes)
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    This function copies len byte length data from buff space to
             rb Free space in the ring buffer.
  ***********************************************************/
uint32_t Write_RingBuff(RingBuff_t *rb,
                        uint8_t *wbuff, 
                        uint32_t len)
{
  uint32_t l;
#if USE_MUTEX
  /* Request mutex, and access to ringbuff can only be done successfully */
  if(!request_mutex(rb->mutex))
  {
    PRINT_DEBUG("request mutex fail!");
    return 0;
  }
  else  /* Get mutex succeeded */
  {
#endif
    len = min(len, rb->size - rb->in + rb->out);

    /* Copy of the first part: write data from the ring buffer to the last address of the buffer */
    l = min(len, rb->size - (rb->in & (rb->size - 1)));
    memcpy(rb->buffer + (rb->in & (rb->size - 1)), wbuff, l);

    /* Write the rest of the buffer header if it overflows
       If it doesn't overflow, the code is invalid. */
    memcpy(rb->buffer, wbuff + l, len - l);

    rb->in += len;
    
    PRINT_DEBUG("write ringBuff len is %d!",len);
#if USE_MUTEX
  }
  /* Release mutex */
  release_mutex(rb->mutex);
#endif
  return len;
}

Read_RingBuff

Read the buffer data to the specified area, and the user specifies the read length. The length that the user wants to read is not necessarily the real read length. When the reading is completed, it is also necessary to see whether the return value is consistent with the length required by the user ~ it also supports multi-threaded exclusive access.
It is also an efficient operation of buffer queuing. See the code comments for the process.

/************************************************************
  * @brief   Read_RingBuff
  * @param   rb:Ring buffer handle
  * @param   wbuff:Read the starting address of data saving
  * @param   len:Length of data to be read (bytes)
  * @return  len:Length of data actually read (bytes)
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    This function copies len bytes from the data area in the rb ring buffer
             Length of data into the rbuff space.
  ***********************************************************/
uint32_t Read_RingBuff(RingBuff_t *rb,
                       uint8_t *rbuff, 
                       uint32_t len)
{
  uint32_t l;
#if USE_MUTEX
  /* Request mutex, and access to ringbuff can only be done successfully */
  if(!request_mutex(rb->mutex))
  {
    PRINT_DEBUG("request mutex fail!");
    return 0;
  }
  else
  {
#endif
    len = min(len, rb->in - rb->out);

    /* Copy of the first part: read data from the ring buffer to the last buffer */
    l = min(len, rb->size - (rb->out & (rb->size - 1)));
    memcpy(rbuff, rb->buffer + (rb->out & (rb->size - 1)), l);

    /* Read the rest of the buffer header if it overflows
       If it doesn't overflow, the code is invalid. */
    memcpy(rbuff + l, rb->buffer, len - l);

    rb->out += len;
    
    PRINT_DEBUG("read ringBuff len is %d!",len);
#if USE_MUTEX
  }
  /* Release mutex */
  release_mutex(rb->mutex);
#endif
  return len;
}

Get buffer information

It's easier to see how much data the buffer can read and write.

/************************************************************
  * @brief   CanRead_RingBuff
    * @param   rb:Ring buffer handle
    * @return  uint32:Readable data length 0 / len
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Readable data length
  ***********************************************************/
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:Ring buffer handle
    * @return  uint32:Writable data length 0 / len
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Writable data length
  ***********************************************************/
uint32_t CanWrite_RingBuff(RingBuff_t *rb)
{
    if(NULL == rb)
    {
        PRINT_ERR("ringbuff is null!");
        return 0;
    }

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

Incidental

The code here is for testing. I write it casually.

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

Support mutex operations of multiple os

This mimics the file system's mutex operation

#if USE_MUTEX
#Define mutex? Timeout 1000 / * timeout*/
#Define mutex? T mutex? T / * mutex control block*/
#endif

/*********************************** mutex **************************************************/
#if USE_MUTEX
/************************************************************
  * @brief   create_mutex
  * @param   mutex:Create semaphore handle
  * @return  Creation success is 1, 0 is unsuccessful.
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Create a mutex. Users can use ringbuff for mutex in os.
  *          Supported os include 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:Mutex handle
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Delete a mutex. Supported os include rtt, win32, ucos, FreeRTOS and 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:Mutex handle
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Request a mutex, and the thread that gets the mutex will be allowed to access the buffer.
  *          Supported os include 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:Mutex handle
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Release the mutex. When the thread finishes using the resource, it must release the mutex.
  *          Supported os include 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 **************************************************/

debug.h

Finally, send a simple source code for debug ging, because many times the previous article will call
PRINT_ERR
PRINT_DEBUG

#ifndef _DEBUG_H
#define _DEBUG_H
/************************************************************
  * @brief   debug.h
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    This file is used to print log information
  ***********************************************************/
/**
* @name Debug print 
* @{
*/
#Define print? Debug? Enable 1 / * print debug information*/
#Define print? Err? Enable 1 / * print error message*/
#Define print "info" enable 0 / * print personal information*/


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

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

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

/**@} */
    
//Calling different stdint.h files for different compilers
#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,                                /* error */
    ASSERT_SUCCESS = !ASSERT_ERR    /* Correct */
} Assert_ErrorStatus;

typedef enum 
{
    FALSE = 0,        /* false */
    TRUE = !FALSE    /* really */
}ResultStatus;

#endif /* __DEBUG_H */

Like to pay attention to me!

Relevant codes can be obtained in the public account background.
Welcome to the public account of "IoT development"

Posted by regiemon on Wed, 16 Oct 2019 09:58:49 -0700