嵌入式開發|高效緩衝器設計(C語言)

一、前言

        嵌入式開發中我們要時刻保持代碼的高效與整潔。在嵌入式的開發中緩衝器是非常常用的,比如串口的數據,MCU處理數據的時候,只能先處理先來的,那麼處理完後呢,就會把數據釋放掉,再處理下一個。已經處理的數據的內存就會被浪費掉。因爲後來的數據只能往後排隊,如果要將剩餘的數據都往前移動一次,那麼效率就會低下了,肯定不現實,所以,環形隊列就出現了。

                                                 

       環形緩衝區通常有一個讀指針和一個寫指針。讀指針指向環形緩衝區中可讀的數據,寫指針指向環形緩衝區中可寫的緩衝區。通過移動讀指針和寫指針就可以實現緩衝區的數據讀取和寫入。在通常情況下,環形緩衝區的讀用戶僅僅會影響讀指針,而寫用戶僅僅會影響寫指針。如果僅僅有一個讀用戶和一個寫用戶,那麼不需要添加互斥保護機制就可以保證數據的正確性。如果有多個讀寫用戶訪問環形緩衝區,那麼必須添加互斥保護機制來確保多個用戶互斥訪問環形緩衝區。

二、代碼

   經典的環形緩衝器源代碼可以參考linux內核中的kernel/kfifo.c,巧奪天工的kfifo:Linux Kernel中的無鎖環形緩衝,下面代碼是移植的nordic ble SDK中的,相對比較簡單,使用方便。

#include "cola_fifo.h"
#include "utils.h"
static __inline uint32_t fifo_length(cola_fifo_t * p_fifo)
{
    uint32_t tmp = p_fifo->read_pos;
    return p_fifo->write_pos - tmp;
}


#define FIFO_LENGTH() fifo_length(p_fifo)  /**< Macro for calculating the FIFO length. */


/**@brief Put one byte to the FIFO. */
static __inline void fifo_put(cola_fifo_t * p_fifo, uint8_t byte)
{
    p_fifo->p_buf[p_fifo->write_pos & p_fifo->buf_size_mask] = byte;
    p_fifo->write_pos++;
}


/**@brief Look at one byte in the FIFO. */
static __inline void fifo_peek(cola_fifo_t * p_fifo, uint16_t index, uint8_t * p_byte)
{
    *p_byte = p_fifo->p_buf[(p_fifo->read_pos + index) & p_fifo->buf_size_mask];
}


/**@brief Get one byte from the FIFO. */
static __inline void fifo_get(cola_fifo_t * p_fifo, uint8_t * p_byte)
{
    fifo_peek(p_fifo, 0, p_byte);
    p_fifo->read_pos++;
}


uint32_t cola_fifo_init(cola_fifo_t * p_fifo, uint8_t * p_buf, uint16_t buf_size)
{
    // Check buffer for null pointer.
    if (p_buf == NULL)
    {
        return 0;
    }

    // Check that the buffer size is a power of two.
    if (!IS_POWER_OF_TWO(buf_size))
    {
        return 0;
    }

    p_fifo->p_buf         = p_buf;
    p_fifo->buf_size_mask = buf_size - 1;
    p_fifo->read_pos      = 0;
    p_fifo->write_pos     = 0;

    return 1;
}


uint32_t cola_fifo_put(cola_fifo_t * p_fifo, uint8_t byte)
{
    if (FIFO_LENGTH() <= p_fifo->buf_size_mask)
    {
        fifo_put(p_fifo, byte);
        return 1;
    }

    return 0;
}


uint32_t cola_fifo_get(cola_fifo_t * p_fifo, uint8_t * p_byte)
{
    if (FIFO_LENGTH() != 0)
    {
        fifo_get(p_fifo, p_byte);
        return 1;
    }

    return 0;

}


uint32_t cola_fifo_peek(cola_fifo_t * p_fifo, uint16_t index, uint8_t * p_byte)
{
    if (FIFO_LENGTH() > index)
    {
        fifo_peek(p_fifo, index, p_byte);
        return 1;
    }

    return 0;
}


uint32_t cola_fifo_flush(cola_fifo_t * p_fifo)
{
    p_fifo->read_pos = p_fifo->write_pos;
    return 1;
}


uint32_t cola_fifo_read(cola_fifo_t * p_fifo, uint8_t * p_byte_array, uint32_t  p_size)
{
    //VERIFY_PARAM_NOT_NULL(p_fifo);
    //VERIFY_PARAM_NOT_NULL(p_size);
    if(p_fifo == NULL)
    {
        return 0;
    }
    if(p_size == 0)
    {
        return 0;
    }
    const uint32_t byte_count    = fifo_length(p_fifo);
    const uint32_t requested_len = (p_size);
    uint32_t       index         = 0;
    uint32_t       read_size     = MIN(requested_len, byte_count);

    (p_size) = byte_count;

    // Check if the FIFO is empty.
    if (byte_count == 0)
    {
        return 0;
    }

    // Check if application has requested only the size.
    if (p_byte_array == NULL)
    {
        return 0;
    }

    // Fetch bytes from the FIFO.
    while (index < read_size)
    {
        fifo_get(p_fifo, &p_byte_array[index++]);
    }

    (p_size) = read_size;

    return p_size;
}


uint32_t cola_fifo_write(cola_fifo_t * p_fifo, uint8_t const * p_byte_array, uint32_t  p_size)
{
    //VERIFY_PARAM_NOT_NULL(p_fifo);
    //VERIFY_PARAM_NOT_NULL(p_size);
    if(p_fifo == NULL)
    {
        return 0;
    }
    if(p_size == 0)
    {
        return 0;
    }
    const uint32_t available_count = p_fifo->buf_size_mask - fifo_length(p_fifo) + 1;
    const uint32_t requested_len   = (p_size);
    uint32_t       index           = 0;
    uint32_t       write_size      = MIN(requested_len, available_count);

    (p_size) = available_count;

    // Check if the FIFO is FULL.
    if (available_count == 0)
    {
        return 0;
    }

    // Check if application has requested only the size.
    if (p_byte_array == NULL)
    {
        return 0;
    }

    //Fetch bytes from the FIFO.
    while (index < write_size)
    {
        fifo_put(p_fifo, p_byte_array[index++]);
    }

    (p_size) = write_size;

    return p_size;
}

三、引申

        環形串口緩衝器的缺點是同一塊buffer中只能有一個緩衝器工作,如果當前申請的緩衝區沒有在使用那麼也會佔用內存,因此在這將緩衝器的設計思想做一下引申,一個可以同時多個緩衝器共用同一個buffer的例子。

實現原理:

1.申請一塊固定長度的數組用於緩衝區。如:uint8_t buffer[2048],申請2K的總緩衝區

2.將總的緩衝區分成2的整數冪字節大小的塊。如每塊128字節,可分成16塊。

3.使用位圖方式管理每塊,uint32_t bitmap,32位整形最大可管理32塊內存塊。

4.0-127字節標號塊0,128-255字節標號塊2.......,以此類推標號。

5.每次可申請一塊的大小,每次申請空閒且標號最小的塊,這樣可保證每次都是用低位空間。

6.每塊最高位字節記錄下一塊標號,這樣可將多個塊連城一個大塊

7.實現多通道,每個通道包括,最大字節長度,當前字節說,當前寫入的塊號,當前寫入處於某個塊的位置,當前讀出塊號,當前讀出塊位置。

缺點:多個緩衝器公用同一個buffer,在讀寫數據時需要加鎖。

                     

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章