前面我博客寫了一篇《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各有所長。蘿蔔青菜各有所愛;兩個都愛,一起拿走。