前面我博客写了一篇《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各有所长。萝卜青菜各有所爱;两个都爱,一起拿走。