串列通訊是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使用。
依照下圖官方的電路圖來看,Nucleo開發板的 USART2 已經連接到 ST-LINK 燒錄器,而ST-LINK的STM32F103C8T6透過USB傳送 UART 的訊號,因此只要開啟 UART2 並插上USB就可以與電腦連線。
這邊推薦電腦的終端機軟體可以使用RealTerm,可以讀取USB Com Port的訊號,切換成不同資料格式進行顯示,與 UART 的相關通訊設定也可以一一調整,在開發上是相當好用的工具之一。
主要可以使用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 配合更多的週邊電路進行控制,達到簡易的人機介面控制。也歡迎把自己的實驗結果一起交流、共同學習。