1. 程式人生 > >串口通訊

串口通訊

字節 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;
}

串口通訊