STM32CubeMX Lesson 9 ─ 透過UART/USART與PC通訊

  串列通訊是MCU與周邊電路或IC與IC傳輸資料的工具之一,許多人對於 UART 與 USART 不理解,UART全名是Universal Asynchronous Receiver/Transmitter,而U「S」ART的「S」則是同步Synchronous的意思。

  早期的8250、16550就是很廣為人知的 UART 通訊IC,目前 UART 已是MCU不可或缺的通訊界面之一。UART包含了RS232、RS449、RS423、RS422和RS485等接口標準規範和匯流排標準規範,依照NRZ工業非同步資料傳輸格式進行資料交換,並提供不同通訊速率的Baud Rate使用。

Datasheet Block Diagram 詳細可參考Reference manual

依照下圖官方的電路圖來看,Nucleo開發板的 USART2 已經連接到 ST-LINK 燒錄器,而ST-LINK的STM32F103C8T6透過USB傳送 UART 的訊號,因此只要開啟 UART2 並插上USB就可以與電腦連線。

這邊推薦電腦的終端機軟體可以使用RealTerm,可以讀取USB Com Port的訊號,切換成不同資料格式進行顯示,與 UART 的相關通訊設定也可以一一調整,在開發上是相當好用的工具之一。

RealTerm

主要可以使用Polling(輪詢法)、Interrupt(中斷)、DMA(直接記憶體存取)的方式進行通訊,該文章以「Polling」為主,未來會針對中斷配合Ring-Buffer的資料結構實作不等長資料傳接。

因為Nucleo已經預設了 USART2 ,Baud Rate為115200,所以就不用特別進行設定與調整,完成Clock的設置就可以產生程式碼囉!

在C語言的學習過程當中,一開始就是使用printf進行Hello World!的輸出,因此透過printf是較方便的方式。否則 UART 的傳送需要使用HAL_UART_Transmit傳入4個參數才可以呼叫,若將printf重新定義為 UART 的傳送,則Coding時會簡潔很多。

因此在main.c的USER CODE BEGIN Includes需引用stdio.h,stdio.h是C語言為輸入輸出提供的標準庫頭文件。

同時在USER CODE BEGIN 0重新定義printf為HAL_UART_Transmit,因此需新增以下程式碼。

#ifdef __GNUC__
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
	
PUTCHAR_PROTOTYPE
{
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}

接著到while的地方,如同剛開始學C語言一樣,先print一個Hello World到PC的終端機吧!記得使用HAL_Delay,否則終端機會瘋狂收到Hello World的訊息!

 while (1)
  {
		printf("Hello World!\r\n");
   HAL_Delay(3000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

而RealTerm的Display設定建議選擇下方的Ascii,因為選擇第一個Ascii會出現CRLF的換行符號。

在Port的設定選擇\USER000,Baud Rate為當初在CubeMX設定的115200,以下的同位元及資料長度的設定皆與CubeMX設定一致。

按下Change後,回到Keil進行燒錄,每3秒就會收到一次Hello World!。

接著我們回到Keil,來學習怎麼接收。將我們從電腦傳送的字串回傳回來,假設我們從電腦傳送123,則會看到MCU回傳123。

使用HAL_UART_Receive接收,並將接收到的Data直接printf回終端機,接著清除Data,但長度是有限制的,不等長的資料通訊,需要使用資料結構Ring-Buffer,未來會再其餘文章提及。

uint8_t Data[16]; 
while (1)
  {
		HAL_UART_Receive(&huart2,Data,sizeof(Data),0xff);
		printf("%s",Data);
		Data[0]='\0';
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

燒錄後,到Send的介面,傳送123456789,按下Send ASCII,則會收到MCU回傳的123456789。

接著我們可以嘗試針對收到的資料進行簡單的判斷,可以學習很基礎的人機介面通訊,當我們收到A,回傳CASE A,進行A的功能;收到B,則回傳CASE B,進行B的功能;其餘的回傳DEFAULT。

可以搭配LED或是週邊電路,達到用電腦終端機控制週邊電路的效果。若可以搭配C#或Python等語言寫桌面應用程式,則可以做到按軟體的按鈕,硬體做相對的變化的效果。

uint8_t Data[16]="";
  while (1)
  {
		HAL_UART_Receive(&huart2,Data,sizeof(Data),0xff);
		switch(Data[0])
		{
			case 'A':
				printf("CASE A\r\n");
				break;
			case 'B':
				printf("CASE B\r\n");
				break;
			case '\0':
				break;
			default:
				printf("DEFAULT\r\n");
				break;
		}
		Data[0]='\0';
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

燒錄後的效果如下。 當我們收到A,回傳CASE A,進行A的功能;收到B,則回傳CASE B,進行B的功能;其餘的回傳DEFAULT。

可以使用 UART 配合更多的週邊電路進行控制,達到簡易的人機介面控制。也歡迎把自己的實驗結果一起交流、共同學習。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *