紧接着上一篇文章,如何合理处理多个串口接收大量数据。此种方法,很厉害,很NB,首先,利用DMA 可节省大量CUP资源。其次,利用IDLE空闲中断来接收位置个数的数据。最后利用串口DMA环形数据的偏移量、长度,入队,出队处理数据。保证了任务的流畅处理。
串口接收数据:
中心思想
1、开启DMA 环形接收数据模式。
2、触发接收数据中断。
3、假如数据有进来,将上面环形数据的偏移量与长度 入队。
4、出队函数,处理数据。
直接上代码
两个任务完成
#define MAX_FRAME_DISP_LEN 125
#define MAX_FRAME_DISP_ITEM 5
#define MaxSize_FRAME_DISP (MAX_FRAME_DISP_LEN*MAX_FRAME_DISP_ITEM)
static void RecUart1Temp_Task(void* parameter)
{
uint16_t len;
uint16_t ndtr_last;//上次剩余个数
BufferLoopData_Typedef buffer_loop;
BaseType_t xReturn = pdPASS;
buffer_loop.start_addr = 0;
buffer_loop.len = 0;
ndtr_last = MaxSize_FRAME_DISP;
while (1)
{
//等待有数据消息
xReturn = xSemaphoreTake(BinarySem_Handle, portMAX_DELAY);
len = Reg_Usart1_CHANNEL_CNDTR;//DMA剩余个数
//环形数组
if (ndtr_last != len)//上次与这次不同,表示有新数据
{
buffer_loop.start_addr = buffer_loop.start_addr + buffer_loop.len;//环形数据地址偏移量
if (buffer_loop.start_addr >= MaxSize_FRAME_DISP)
{
buffer_loop.start_addr = buffer_loop.start_addr - MaxSize_FRAME_DISP;
}
if (ndtr_last > len)
{
buffer_loop.len = ndtr_last - len;//接收数据长度=上次长度-这次长度
}
else
{
buffer_loop.len = MaxSize_FRAME_DISP - len+ ndtr_last;//环形数据到头后,总数-这次剩余+上次剩余
}
ndtr_last = len;
if ((buffer_loop.len > 0)||(buffer_loop.len < MAX_FRAME_DISP_LEN))
{
xQueueSendToBack(xQueue_Smrj_rx,(void *)&buffer_loop,0);
}
}
taskYIELD();
}
}
任务出队,处理函数
extern uint8_t rxbuf_Uart1_DMA[MaxSize_FRAME_DISP];
static void RecUart1DealTask(void* pvParameters)
{
uint16_t i;
BufferLoopData_Typedef buffer_loop;
for (;;)
{
xQueueReceive(xQueue_Smrj_rx,&buffer_loop,portMAX_DELAY);
if ((buffer_loop.len > 0)&&(buffer_loop.len < MAX_FRAME_DISP_LEN))
{
for (i=0;i<buffer_loop.len;i++)
{
if (buffer_loop.start_addr >= MaxSize_FRAME_DISP)
{
buffer_loop.start_addr = buffer_loop.start_addr - MaxSize_FRAME_DISP;
}
rxbuf_Uart1_tmp[i] = rxbuf_Uart1_DMA[buffer_loop.start_addr];
buffer_loop.start_addr += 1;
}
__nop();
/*此处处理接收的数据*/
//Smrj_RecUserHandle(&rxbuf_Uart1_tmp[0],buffer_loop.len);
//SMRJ_Data_Dec(&rxbuf_Smrj_tmp[0],buffer_loop.len);
}
taskYIELD();
}
}
中断处理函数:
void USART1_IRQHandler(void)
{
uint32_t ulReturn;
ulReturn = taskENTER_CRITICAL_FROM_ISR();
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE)!=RESET)
{
Uart_DMA_Rx_Data();
USART_ReceiveData(DEBUG_USARTx);
taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}
}
void Uart_DMA_Rx_Data(void)
{
BaseType_t pxHigherPriorityTaskWoken;
DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE);
DMA_ClearFlag( DMA1_FLAG_TC5 );
Reg_Usart1_CHANNEL_CNDTR= USART_RX_DMA_CHANNEL->CNDTR;//循环数组里剩余个数
DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE);
xSemaphoreGiveFromISR(BinarySem_Handle,&pxHigherPriorityTaskWoken);
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}
串口初始化见上面一篇文章
总结,从上面代码我们可以看到,我们是巧妙的利用的串口DMA环形模式,利用队列来处理数据。
下一篇,我们直接利用环形缓存buf来实现数据的处理,是的更加的通用性。