第三個實驗例子:FreeRTOS USART DMA 空閒中斷接收 隊列

第三個實驗例子:FreeRTOS USART DMA 空閒中斷接收 隊列

上一篇是串口中斷接收數據,然後通過消息隊列轉發。它實現的方法是每收到一個字節發送一次,這個做法用在串口轉發數據上實時性還是不錯的,但是在平時多數的串口應用中更多的是需要實際通訊。往往是單片機接收一幀數據,根據通訊協議實現某些功能,然後再回答。爲此我嘗試採用通過採用DMA方式,利用空閒中斷來接收一幀數據,然後發送數據到隊列,線程收到數據後處理數據。我採用modbus通訊協議做實驗。

CubeMx的FreeRTOS中創建Queue不能直接支持結構體或數組,爲了這個問題我反覆做了好幾種實驗。先總結一下:

方法一:

當空閒中斷產生,收到一幀數據時,通過xQueueSendToBackFromISR()函數發送幀數據的指針值,然後在線程中用osMessageGet()函數接收後,處理數據。這個做法可以實現功能,但是由於傳遞的是指針,所以在線程中處理數據時如果串口又有新的數據到達,就會發生問題。

方法二:

自己建一個FIFO的幀數據緩存(定義一幀數據的結構體,再做一個結構體的數組),息隊列傳遞當前一幀數據的指針,這個方法也可以能實現功能,但是我擔心自己定義FIFO幀數據緩存,如果代碼寫得不好是否有不可預測的問題。

方法三:

         第三種方法是我要重點介紹的:

 

  1. CubeMx設置

串口設置

FreeRTOS設置:

只新建二個Task,Queue之後在Keil手工創建

 

 

然後生成代碼。

 

  1. 程序代碼

串口部分參考我之前的博文:STM32的串口空閒中斷接收不定長數據

其中串口空閒中斷函數要增加消息隊列發送的代碼。

 

void Usart1Receive_IDLE(UART_HandleTypeDef *huart)

{

         uint32_t temp;

         BaseType_t xHigherPriorityTaskWoken = pdFALSE;        

        

         if(huart->Instance==USART1) //USART1

         {

                   if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))

                   {

                            __HAL_UART_CLEAR_IDLEFLAG(&huart1);

                            HAL_UART_DMAStop(&huart1);

                            temp = huart1.hdmarx->Instance->CNDTR;

                            ModbusReceiveData.rx_len =  RECEIVELEN - temp;

                            ModbusReceiveData.receive_flag=1;

                            //隊列

                            xQueueSendToBackFromISR(myQ02Handle,&ModbusReceiveData,&xHigherPriorityTaskWoken);

                            ModbusReceiveData.receive_flag=0;

                            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);       

                           

                            HAL_UART_Receive_DMA(&huart1,ModbusReceiveData.usartDMA_rxBuf,RECEIVELEN);

                   }

         }

}

 

在freertos.c中添加下列語句:

  1. 聲明

/* USER CODE BEGIN Variables */

extern UART_HandleTypeDef huart1;

osMessageQId myQ02Handle;        //聲明MessageQueue句柄

 

/* USER CODE END Variables */

  1. 創建MessageQueue

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

         myQ02Handle=xQueueCreate(3, sizeof(_USART_BUFF_TYPE));

  /* USER CODE END RTOS_QUEUES */

隊列深度可以根據實際情況,我嘗試設置爲1做測試,可以正常運行。前提是在上位機檢查到通訊超時之前單片機已經應答。爲了保險起見我最後設置了2 。

 

  1. 線程接收

/* USER CODE BEGIN Header_StartTask02 */

/**

* @brief Function implementing the myTask02 thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartTask02 */

void StartTask02(void const * argument)

{

  /* USER CODE BEGIN StartTask02 */

         _USART_BUFF_TYPE   p;

  /* Infinite loop */

  for(;;)

  {

                  

                   xQueueReceive(myQ02Handle,(void *)&p,osWaitForever);   //接收信息

                   HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET);

                   if((p).receive_flag)

                   {

                            HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_RESET);

                            checkComm0Modbus(&p)       ;        //Modbus數據解析

                            HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);

                   }

                   HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);

    osDelay(1);

 

  }

  /* USER CODE END StartTask02 */

}

 

 

 

實際效果:

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