STM32CubeMX在FreeRTOS下使用串口進行數據收發(不定長度)

STM32CubeMX->FreeRTOS+USART接收不定長數據

由於本人做的一個項目功能相對複雜,要求使用操作系統,且項目工程中有很多需要串口操作的外設,所以需要對串口設計不定長的收發功能,裸機跑慣了的孩子就是比較野,一天瞎吉爾弄,現在是不是GG,這裏先批評一下自己。爲了解決這一問題並做個筆記,就有了這篇博客。

實現思路:
​ 使用串口、定時器記錄接收到的數據,每5ms進入一次處理函數(一條數據處理完畢)
​ 使用一個計數位、一個標誌位、一個暫存buff
​ 標誌位置1則認爲數據處理完成,將數據打印出來
使用硬件功能:串口3及中斷,定時器7及中斷
使用操作系統元素:一個互斥量
使能RTOS後會RTOS會強制調用系統時鐘,所以在使能了操作系統之後需要將系統時鐘換爲定時器TIM1

操作步驟

  1. 啓動STM32CubeMX並對芯片選型
    主要設置

    1. 串口3及中斷使能,115200,8,None,1
    2. 定時器7及中斷使能,預分頻9000-1,自動重載50-1(時鐘樹APB1、2爲90MHz)
    3. SYS時鐘源設爲定時器1
    4. 使能FreeRTOS,創建1個串口任務、創建1個互斥量
    5. 使能兩個GPIO輸出驅動LED
      以上都清楚的大佬轉到代碼功能實現繼續
  2. 使能RCC時鐘源爲外部時鐘


3. 設置SYS調試方式爲串口,時鐘源改爲定時器1


4. 設置時鐘樹,是APB1、2爲90MHz,或者自行設定,在後面定時器工作會用到

5. 使能串口3,並啓用串口3中斷,串口3設置爲115200,8Bit,None,1(不知道什麼意思的就沒有必要看下去了)

在這裏插入圖片描述
6. 使能定時器7,並使能中斷,定時器預分頻係數與之前的90MHz相關,需要通過預分頻係數和自動重載值將定時時間設爲5ms【200Hz】,不會算的看我定時器博客(這裏我用了一個不常用的定時器,用哪個都可以,只要注意好定時器隸屬哪根時鐘線就行)

在這裏插入圖片描述
7. 使能FreeRTOS並創建一個串口任務和一個互斥量(互斥量用於串口數據接收完成後進入串口任務)

在這裏插入圖片描述
在這裏插入圖片描述
8. 設置兩個IO輸出作爲一個可視化依據

在這裏插入圖片描述
9. 項目命名、選擇使用的程序編輯軟件我的是MDK_ARM V5、分離.c.h文件,點擊右上角GENERATE CODE生成工程
在這裏插入圖片描述

  1. 打開項目,先編譯沒有錯誤之後再進行修改

代碼功能實現

  1. 首先在usart.c文件對串口3進行printf函數重定義,使串口3可用printf函數輸出

    內容加入到/* USER CODE BEGIN 0 */框架內

    /* USER CODE BEGIN 0 */
    /********************************************************************************/
    #include "stdio.h"
    //加入以下代碼,支持printf函數,而不需要選擇use MicroLIB	  
    //#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)	
    #if 1
    #pragma import(__use_no_semihosting)             
    //標準庫需要的支持函數                 
    struct __FILE 
    { 
    	int handle; 
    }; 
    
    FILE __stdout;       
    //定義_sys_exit()以避免使用半主機模式    
    void _sys_exit(int x) 
    { 
    	x = x; 
    } 
    //重定義fputc函數 
    int fputc(int ch, FILE *f)
    { 	
    	while((USART3->SR&0X40)==0);//循環發送,直到發送完畢   
    	USART3->DR = (uint8_t) ch;      
    	return ch;
    }
    #endif 
    /********************************************************************************/
    /* USER CODE END 0 */
    
  2. 定義串口使用的變量

    主要包括串口數據存儲BUFF,串口數據長度計數BUFF,串口接收成功標誌位(此處借鑑正點原子的串口處理方法)

    在main.c文件下的定義/* USER CODE BEGIN PV */框架內定義

    /* USER CODE BEGIN PV */
    /********************************************************************************/
    uint8_t RxBuffer[MAX_REC_LENGTH] = {0};		//串口數據存儲BUFF		長度2048
    uint8_t RxFlag = 0;							//串口接收完成標誌符
    uint16_t RxCounter = 0;						//串口長度計數
    uint8_t RxTemp[REC_LENGTH] = {0};			//串口數據接收暫存BUFF	長度1
    
    extern osSemaphoreId myBinarySem01Handle;	//操作系統定義的互斥量
    /********************************************************************************/
    /* USER CODE END PV */
    

    並在usart.h文件下的/* USER CODE BEGIN Private defines */框架下定義長度和鏈接串口用變量

    /* USER CODE BEGIN Private defines */
    /********************************************************************************/
    #define REC_LENGTH 1
    #define MAX_REC_LENGTH 2048
    
    extern uint8_t RxBuffer[MAX_REC_LENGTH];
    extern uint8_t RxFlag;
    extern uint16_t RxCounter;
    extern uint8_t RxTemp[REC_LENGTH];
    /********************************************************************************/	 
    /* USER CODE END Private defines */
    
  3. 串口中斷回調函數,在main.c的/* USER CODE BEGIN 4 */框架下添加串口回調函數

    /********************************************************************************/
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口3接收完成回調函數
    {
    	if(huart->Instance == USART3)
    	{
    		__HAL_TIM_SET_COUNTER(&htim7,0);							//清除定時器7計數值	
    		if(0 == RxCounter)											//如果是首字符(每幀數據開頭)則開啓定時器
    		{
    			__HAL_TIM_CLEAR_FLAG(&htim7, TIM_FLAG_UPDATE);			//清除中斷標誌位
    			HAL_TIM_Base_Start_IT(&htim7);							//開啓基本定時器
    		}
    		RxBuffer[RxCounter] = RxTemp[0];							//緩存數據放入接收數組
    		RxCounter++;												//計數器加1
    		HAL_UART_Receive_IT(&huart3,(uint8_t *)RxTemp, REC_LENGTH);	//重新使能中斷
    	}
    }
    /********************************************************************************/
    
  4. 定時器週期回調函數,由於在設置中系統時鐘使用了定時器1,所以在main.c文件存在

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    

    在函數 /* USER CODE BEGIN Callback 1 */框架下添加定時器7的回調函數

      /* USER CODE BEGIN Callback 1 */
    	/********************************************************************************/
    	if(htim->Instance == TIM7)
    	{
    		HAL_GPIO_TogglePin(DS1_GPIO_Port, DS1_Pin);	//LED反轉一次
    		RxFlag = 1;																	//接收標誌位置1
    		HAL_TIM_Base_Stop_IT(&htim7);								//關閉定時器
    		osSemaphoreRelease(myBinarySem01Handle);		//釋放二值信號量,進入串口任務
    	}
    	/********************************************************************************/
      /* USER CODE END Callback 1 */
    
  5. 對freeRTOS添加任務

    首先在freertos.c文件的/* USER CODE BEGIN Includes */ 框架下添加串口使用的頭文件

    /* USER CODE BEGIN Includes */     
    /********************************************************************************/
    #include "usart.h"
    #include "stdio.h"
    /********************************************************************************/
    /* USER CODE END Includes */
    
  6. 在freertos主任務中啓動串口中斷以及添加LED閃爍任務

    /* USER CODE END Header_StartDefaultTask */
    void StartDefaultTask(void const * argument)
    {
      /* USER CODE BEGIN StartDefaultTask */
    	HAL_UART_Receive_IT(&huart3,(uint8_t *)RxTemp, REC_LENGTH);
      /* Infinite loop */
      for(;;)
      {
    	HAL_GPIO_TogglePin(DS0_GPIO_Port,DS0_Pin);
        osDelay(200);
      }
      /* USER CODE END StartDefaultTask */
    }
    
  7. 在freertos串口子任務中添加串口數據打印功能

    /* USER CODE END Header_Start_U3_Task */
    void Start_U3_Task(void const * argument)
    {
      /* USER CODE BEGIN Start_U3_Task */
      /* Infinite loop */
      for(;;)
      {
    	/********************************************************************************/  
    	osSemaphoreWait(myBinarySem01Handle,100);						//等待二值信號量
    	if(RxFlag == 1)													//數據接收完成
    	{
    		for(int i = 0; i<RxCounter; i++)							//打印接收數組存儲的內容
    			printf("%c",RxBuffer[i]);	
             printf("\r\n");											//打印完成換行
    		RxFlag = 0;													//接收標誌清零
    		RxCounter = 0;												//接收計數清零
    		memset(RxBuffer ,0, MAX_REC_LENGTH);						//清空接收數組
    	}
    	/********************************************************************************/
        osDelay(1);
      }
      /* USER CODE END Start_U3_Task */
    }
    

實驗結果

編譯下載程序,打開串口調試助手,用串口轉USB模塊將串口3接入電腦,使用串口調試助手測試
在這裏插入圖片描述
上圖爲測試結果,發送ASWaterbenben後回覆相同內容,每幀數據用換行符隔開,至此,實驗成功!

DS0燈200ms反轉一次,每次接收到一幀完整數據DS1燈就會反轉一次。這個效果難以展示,你們自己試試就好!

如果本博客對您有幫助,希望點贊關注走一波,今後會看心情更新!哈哈哈哈哈哈哈哈哈

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