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