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各有所长。萝卜青菜各有所爱;两个都爱,一起拿走。

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