STM32 串口傳輸最佳處理方式 FreeRTOS+隊列+DMA+IDLE (二)

緊接着上一篇文章,如何合理處理多個串口接收大量數據。此種方法,很厲害,很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來實現數據的處理,是的更加的通用性。

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