STM32CubeMX->FreeRTOS+USART接收不定長數據
由於本人做的一個項目功能相對複雜,要求使用操作系統,且項目工程中有很多需要串口操作的外設,所以需要對串口設計不定長的收發功能,裸機跑慣了的孩子就是比較野,一天瞎吉爾弄,現在是不是GG,這裏先批評一下自己。爲了解決這一問題並做個筆記,就有了這篇博客。
實現思路:
使用串口、定時器記錄接收到的數據,每5ms進入一次處理函數(一條數據處理完畢)
使用一個計數位、一個標誌位、一個暫存buff
標誌位置1則認爲數據處理完成,將數據打印出來
使用硬件功能:串口3及中斷,定時器7及中斷
使用操作系統元素:一個互斥量
使能RTOS後會RTOS會強制調用系統時鐘,所以在使能了操作系統之後需要將系統時鐘換爲定時器TIM1
操作步驟
-
啓動STM32CubeMX並對芯片選型
主要設置- 串口3及中斷使能,115200,8,None,1
- 定時器7及中斷使能,預分頻9000-1,自動重載50-1(時鐘樹APB1、2爲90MHz)
- SYS時鐘源設爲定時器1
- 使能FreeRTOS,創建1個串口任務、創建1個互斥量
- 使能兩個GPIO輸出驅動LED
以上都清楚的大佬轉到代碼功能實現繼續
-
使能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生成工程
- 打開項目,先編譯沒有錯誤之後再進行修改
代碼功能實現
-
首先在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 */
-
定義串口使用的變量
主要包括串口數據存儲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 */
-
串口中斷回調函數,在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); //重新使能中斷 } } /********************************************************************************/
-
定時器週期回調函數,由於在設置中系統時鐘使用了定時器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 */
-
對freeRTOS添加任務
首先在freertos.c文件的/* USER CODE BEGIN Includes */ 框架下添加串口使用的頭文件
/* USER CODE BEGIN Includes */ /********************************************************************************/ #include "usart.h" #include "stdio.h" /********************************************************************************/ /* USER CODE END Includes */
-
在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 */ }
-
在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燈就會反轉一次。這個效果難以展示,你們自己試試就好!
如果本博客對您有幫助,希望點贊關注走一波,今後會看心情更新!哈哈哈哈哈哈哈哈哈