STM32 LibOpenCM3:USART 發送

系列:簡單入門 LibOpenCM3 STM32 嵌入式系統開發 Posted on 2022-09-22

前言

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.hgpio.husart.h外,因爲我們還需要實現 printf() 函式,所以還需要 stdio.herrno.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

▲ AF 對照表,取自 DS10693。

▲ AF 對照表,取自 DS10693。

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 是很基本且常用的功能,如果運作起來不正常的話還是先再次確定通訊的設定是否正確。

參考資料

本文的程式也有放在 GitHub 上。
本文同步發表於 iT 邦幫忙-2022 iThome 鐵人賽



留言可能不會立即顯示。若過了幾天仍未出現,請 Email 聯繫:)

comments powered by Disqus