前言
USART 是最常用且基本的通訊方式之一,我通常會用 USART 來讓 MCU 與電腦進行溝通,在進行設定或開發除錯時很好用。不過實際上這篇要介紹的只是 UART 而非 USART,不過我還是統一用 USART。
這一篇的目標是讓 STM32 持續透過 USART 來發送資料到電腦,並且可以使用 printf()
函式。
正文
一樣先以 Nucleo-F446RE 做示範。
首先建立一個 PIO 的專案,選擇 Framework 為「libopencm3」,並在 src/
資料夾中新增並開啓 main.c
檔案。
完整程式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
#include <stdio.h> #include <errno.h> #include <libopencm3/stm32/rcc.h> #include <libopencm3/stm32/gpio.h> #include <libopencm3/stm32/usart.h>
#define USART_BAUDRATE (9600)
#define RCC_USART_TX_GPIO (RCC_GPIOA) #define GPIO_USART_TX_PORT (GPIOA) #define GPIO_USART_TX_PIN (GPIO2) #define GPIO_USART_AF (GPIO_AF7)
static void delay(uint32_t value) { for (uint32_t i = 0; i < value; i++) { __asm__("nop"); } }
static void rcc_setup(void) { rcc_periph_clock_enable(RCC_USART_TX_GPIO); rcc_periph_clock_enable(RCC_USART2); }
static void usart_setup(void) { gpio_mode_setup(GPIO_USART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_USART_TX_PIN);
gpio_set_af(GPIO_USART_TX_PORT, GPIO_USART_AF, GPIO_USART_TX_PIN);
usart_set_baudrate(USART2, USART_BAUDRATE); usart_set_databits(USART2, 8); usart_set_stopbits(USART2, USART_STOPBITS_1); usart_set_parity(USART2, USART_PARITY_NONE); usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE); usart_set_mode(USART2, USART_MODE_TX);
usart_enable(USART2); }
int main(void) { rcc_setup(); usart_setup();
int i = 0; while (1) { printf("Hello World! %i\r\n", i++); delay(500000); }
return 0; }
int _write(int file, char *ptr, int len) { int i;
if (file == 1) { for (i = 0; i < len; i++) { usart_send_blocking(USART2, ptr[i]); } return i; }
errno = EIO; return -1; }
|
分段說明
Include
1 2 3 4 5
| #include <stdio.h> #include <errno.h> #include <libopencm3/stm32/rcc.h> #include <libopencm3/stm32/gpio.h> #include <libopencm3/stm32/usart.h>
|
除了 LibOpenCM3 的 rcc.h
、gpio.h
和 usart.h
外,因為我們還需要實現 printf()
函式,所以還需要 stdio.h
與 errno.h
。
RCC
1 2 3 4 5
| static void rcc_setup(void) { rcc_periph_clock_enable(RCC_USART_TX_GPIO); rcc_periph_clock_enable(RCC_USART2); }
|
除了要致能 USART Tx 接腳所在的 GPIO Port 外,還要致能 USART 本身。
USART 選擇
1 2 3 4
| #define RCC_USART_TX_GPIO (RCC_GPIOA) #define GPIO_USART_TX_PORT (GPIOA) #define GPIO_USART_TX_PIN (GPIO2) #define GPIO_USART_AF (GPIO_AF7)
|
一個 STM32 MCU 中通常不會只有一個 USART,且各個 USART 的詳細規格可能不同,因此我們要選擇到底該使用哪一個 USART。
STM32 Nucleo 開發板上其實已經設計 USART 的硬體線路好了,以我們使用的 Nucleo-64 (參考 UM1724)來說,USART2 已經連接到 ST-Link 了,也就是程式燒錄和 USART 都可以透過板載的 ST-Link 完成,只需要連接一條 USB 線就好,不需要額外的 USB-to-TTL 模組,因此使用 USART2 是最方便的選擇。而 USART2 的 Tx 腳位為 PA2。
記得除了 STM32F1 系列外,AF 功能還要設定是「AF 幾?」。根據 F446RE Datasheet (DS10693) 的「Table 11. Alternate function」我們可以知道我們所使用的「USART2」是 「AF7」,因此使用 GPIO_USART_AF
指定要使用的是 GPIO_AF7
。
USART 設定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| static void usart_setup(void) { gpio_mode_setup(GPIO_USART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_USART_TX_PIN);
gpio_set_af(GPIO_USART_TX_PORT, GPIO_USART_AF, GPIO_USART_TX_PIN);
usart_set_baudrate(USART2, USART_BAUDRATE); usart_set_databits(USART2, 8); usart_set_stopbits(USART2, USART_STOPBITS_1); usart_set_parity(USART2, USART_PARITY_NONE); usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE); usart_set_mode(USART2, USART_MODE_TX);
usart_enable(USART2); }
|
首先要設定好 GPIO。我們需要將 USART Tx 設定為 Alternate Function。
設定好 GPIO 後就是設定 USART 本身,也就是鮑率、資料位元、停止位元那些,這部分就照實際需求設定。
由於本例只有用到傳送的部分,不需要接收,所以 usart_set_mode()
設定為 USART_MODE_TX
。
printf()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int _write(int file, char *ptr, int len) { int i;
if (file == 1) { for (i = 0; i < len; i++) { usart_send_blocking(USART2, ptr[i]); } return i; }
errno = EIO; return -1; }
|
透過實作 _write()
,我們就可以透過 USART 來使用 printf()
函式了。usart_send_blocking()
用來透過 USART 發送資料。
這部分的程式參考自 libopencm3-example。
多環境程式(F446RE + F103RB)
由於 STM32F1 的部分函式不同,所以 F103RB 沒辦法直接使用上面的 F446RE 的程式。
以下列出主要的差異部分,也就是 GPIO 的部分。完整的程式請看 GitHub repo。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| static void usart_setup(void) { #if defined(STM32F1) gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO2); #else gpio_mode_setup(GPIO_USART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_USART_TX_PIN);
gpio_set_af(GPIO_USART_TX_PORT, GPIO_USART_AF, GPIO_USART_TX_PIN); #endif
}
|
成果
可以使用 PIO 內建的 Serial Monitor 查看。
小結
這次介紹了 USART 的發送功能寫法,還一併實現了透過 printf()
來使用 USART。USART 是很基本且常用的功能,如果運作起來不正常的話還是先再次確定通訊的設定是否正確。
參考資料
本文的程式也有放在 GitHub 上。
本文同步發表於 iT 邦幫忙-2022 iThome 鐵人賽。