FreeRTOS_RingBuff 環形緩存數組的使用

前面我博客寫了一篇《STM32 串口傳輸最佳處理方式 FreeRTOS+隊列+DMA+IDLE(一)》就是利用RingBuff環形緩存數組來存數據,大家可以看着那邊代碼來看。
詳細描述一下里面的原理:看一下入隊函數

static uint16_t ps_tbwr_Blue=0;//緩存所在指針

//入隊的結構體
typedef struct 
{
    uint16_t start_addr;  //入隊開始位置
    uint16_t len;    // 入隊長度    
}BufferLoopData_Typedef

   void USART1_SendData(uint8_t *ps,uint16_t len)
{
    uint16_t i;		
    BufferLoopData_Typedef buffer_loop;
    buffer_loop.start_addr = ps_tbwr_Blue;
    buffer_loop.len = len;

    for (i=0;i<len;i++)
    {
        if (ps_tbwr_Blue >= MAX_FRAME_COMM_BUFFER_SIZE)
        {
            ps_tbwr_Blue = 0;
        }        
        txbuf_Blue_DMA[ps_tbwr_Blue] = *(ps+i);//txbuf_Blue_DMA爲
        ps_tbwr_Blue++;        
    }
    xQueueSendToBack(xQueue_Blue_tx,(void *)&buffer_loop,portMAX_DELAY);   
}

大家注意了,buffer_loop.start_addr 是循環數組入隊地址,buffer_loop.start_addr是在ps_tbwr_Blue累加之前的被賦值。所以,它每次入隊都是上一次緩存完的位置。即出隊時,數據取出的位置。ps_tbwr_Blue是一個靜態變量,會一直遞增過去。

出隊函數如下:

void USART1_SendTask(void const * argument)
{
    uint16_t i;
    BufferLoopData_Typedef buffer_loop;
    extern uint8_t txbuf_Blue_tmp[MAX_FRAME_COMM_LEN];   
      for (;;)
    {
        xQueueReceive(xQueue_Blue_tx,&buffer_loop,portMAX_DELAY);  
		//SCB_CleanDCache();
        for (i=0;i<buffer_loop.len;i++)
        {
            if (buffer_loop.start_addr >= MAX_FRAME_COMM_BUFFER_SIZE)
            {
                buffer_loop.start_addr = buffer_loop.start_addr - MAX_FRAME_COMM_BUFFER_SIZE;
            }
            txbuf_Blue_tmp[i] = txbuf_Blue_DMA[buffer_loop.start_addr];
            
            buffer_loop.start_addr += 1;
            
        }   
        //SCB_CleanInvalidateDCache();
        Uart1_DMASend_Start(&txbuf_Blue_tmp[0],buffer_loop.len);//通過DMA發送
        xSemaphoreTake(BinarySem_UART1_tx_finish_Handle,portMAX_DELAY); //等待DMA發送成功
        taskYIELD();  
    } 
}

工作如下:

在這裏插入圖片描述
第一次入隊時,addr1,lenth1。
此時addr=0;ps=lenth1
第二次入隊時addr2,lenth2。
此時addr2=ps=lenth1; ps=lenth1+lenth2
第三次入隊時addr3,lenth3。
此時addr3=ps=lenth1+len2; ps=lenth1+lenth2+lenth2.
……
所以說,就想上面說的。addr是在ps累加之前被賦值了。它就是這樣一個循環的過程。
上面是環形緩存數組的一種方法,有一點點拗口,但是非常的簡介,實用。
出來上面方法還有一種利用鏈表的方法。具體如下

方法二:
利用鏈表`

/** 環形緩存區數據結構 */
typedef struct {
    size_tt rbCapacity;//空間大小
    uint8_t  *rbHead; //頭
    uint8_t  *rbTail; //尾
    uint8_t  *rbBuff; //數據首地址
}rb_t;

//第一步,創建環形隊列,Wifi_rb_tx就是上面環形結構體
rbCreate(&Wifi_rb_tx,Wifi_RingBuff,Wifi_TX_FRAME_MAX_SIZE);

//源碼:
void rbCreate(rb_t* rb,uint8_t *Buff,uint32_t BuffLen)
{
    if(NULL == rb)
    {
        printf("ERROR: input rb is NULL\n");
        return;
    }
    rb->rbCapacity = BuffLen;
		rb->rbBuff = Buff;
    rb->rbHead = rb->rbBuff;
    rb->rbTail = rb->rbBuff;
//第二步,向環形隊列寫入數據
rbWrite(&Wifi_rb_tx, data+1024*idx, (size_tt) 1024);
//源碼:
int32_t rbWrite(rb_t *rb, const void *data, size_tt count)
{
    int tailAvailSz = 0;
    if(NULL == rb)
    {
        printf("ERROR: rb is empty \n");
        return -1;
    }
    if(NULL == data)
    {
        printf("ERROR: data is empty \n");
        return -1;
    }
    if (count >= rbCanWrite(rb))
    {
        printf("ERROR: no memory \n");
        return -1;
    }
    if (rb->rbHead <= rb->rbTail)
    {
        tailAvailSz = rbCapacity(rb) - (rb->rbTail - rb->rbBuff);
        if (count <= tailAvailSz)
        {
            memcpy(rb->rbTail, data, count);
            rb->rbTail += count;
            if (rb->rbTail == rb->rbBuff+rbCapacity(rb))
            {
                rb->rbTail = rb->rbBuff;
            }
            return count;
        }
        else
        {
            memcpy(rb->rbTail, data, tailAvailSz);
            rb->rbTail = rb->rbBuff;        
            return tailAvailSz + rbWrite(rb, (char*)data+tailAvailSz, count-tailAvailSz);
        }
    }
    else 
    {
      memcpy(rb->rbTail, data, count);
      rb->rbTail += count;
      return count;
    }
}
}

//第三步驟:從環形隊列讀出數據
rbRead((rb_t*)&Wifi_rb_tx,Wifi_TxRing_Temp,buffer_loop.len);
//源碼
int32_t rbRead(rb_t *rb, void *data, size_tt count)
{
    int copySz = 0;

    if(NULL == rb)
    {
        printf("ERROR: input rb is NULL\n");
        return -1;
    }
    if(NULL == data)
    {
        printf("ERROR: input data is NULL\n");
        return -1;
    }
    if (rb->rbHead < rb->rbTail)
    {
        copySz = min(count, rbCanRead(rb));
        memcpy(data, rb->rbHead, copySz);
        rb->rbHead += copySz;
        return copySz;
    }
    else 
    {
        if (count < rbCapacity(rb)-(rb->rbHead - rb->rbBuff))
        {
            copySz = count;
            memcpy(data, rb->rbHead, copySz);//
            rb->rbHead += copySz;
            return copySz;
        }
        else
        {
            copySz = rbCapacity(rb) - (rb->rbHead - rb->rbBuff);
            memcpy(data, rb->rbHead, copySz);
            rb->rbHead = rb->rbBuff;
            copySz += rbRead(rb, (char*)data+copySz, count-copySz);
            return copySz;
        }
    }
}

配合FreeRTOS隊列操作,只需將長度入隊,出隊即可。 上面方法模塊性強,更爲複雜,但邏輯上更爲簡單,可作爲模塊化調用。

好了,總上所述。上面兩種RingBuff各有所長。蘿蔔青菜各有所愛;兩個都愛,一起拿走。

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