串口通訊
阿新 • • 發佈:2018-01-14
字節 tar ood 我不 utc 串口 highlight tran 兩個
STM32串口通訊有3種形式:輪詢(阻塞式)、中斷、DMA。我不知道中斷方式的串口通訊有什麽適合的應用場景:每接收/發送一個字節,就要發生一次中斷,這對CPU反而是一種浪費。使用Cube HAL,輪詢式的串口通訊最簡單了,發送和接收數據分別有一個函數:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
Timeout 參數給 HAL_MAX_DELAY 常量,則無限超時,函數一直阻塞,直到發出/接收數據完成。
STM32F303RE 有3個 USART 和 2個 UART,而 USART 也可以工作在異步模式,相當於一個 UART。Nucleo 的 ST-LINK 部分也同時實現了 USB-TTL 轉換,與 MCU 的 USART2 連接。因此,將 C 的標準輸入輸出(<stdio.h>)重定向到 USART2 似乎是一個挺不錯的調試選項。
ARM GCC 使用的是 newlib-nano 標準 C 庫。要使用 newlib-nano,並將 stdio 重定向到串口,gcc 選項和重定向的實現可參考 ARM GCC 附帶的 retarget 示例,其路徑為 share\gcc-arm-none-eabi\samples\src\retarget。概括起來:
- 指定 C 編譯器選項: -fno-builtin
- 指定 linker(ld.exe)選項: --specs=nano.specs、--specs=nosys.specs
- 實現函數 _read()、_write() ,其原型如下。_read() 函數從串口讀取數據,_write() 將數據寫到串口。stdio 函數如 printf()、getchar() 等將最終調用這兩個函數實現輸入輸出
int _read (int fd, char *ptr, int len); int _write (int fd, char *ptr, int len);
在 main() 函數中,首先打印一個字符串,然後進入主循環,將接收到的字符原樣返回。如下:
... puts("Good judgments come from experiences. "); uint8_t buf[32]; while (1) { HAL_UART_Receive(&huart2, buf, 1, HAL_MAX_DELAY); putchar(buf[0]); fflush(stdout); }
我發現,在寫出數據(字符/字符串)時,直到 \n 字符時,數據才真正寫到串口。這應該是 stdio 的緩沖機制。puts() 自動在字符串末尾增加了一個 \n,因此都回立即寫出到串口。而 putchar() 則需要顯式 flush 一下。
_write() 重定向實現很簡單,直接調用 HAL_UART_Transmit():
#include "main.h" #include "stm32f3xx_hal.h" #include "usart.h" int _write (int fd, char *ptr, int len) { /* Write "len" of char from "ptr" to file id "fd" * Return number of char written. * Need implementing with UART here. */ HAL_UART_Transmit(&huart2, (uint8_t *) ptr, len, HAL_MAX_DELAY); return len; }
串口通訊