Skip to content

STM32 LibOpenCM3:USART 發送

發佈

前言

USART 是最常用且基本的通訊方式之一,我通常會用 USART 來讓 MCU 與電腦進行溝通,在進行設定或開發除錯時很好用。不過實際上這篇要介紹的只是 UART 而非 USART,不過我還是統一用 USART。

這一篇的目標是讓 STM32 持續透過 USART 來發送資料到電腦,並且可以使用 printf() 函式。

正文

一樣先以 Nucleo-F446RE 做示範。

首先建立一個 PIO 的專案,選擇 Framework 為「libopencm3」,並在 src/ 資料夾中新增並開啓 main.c 檔案。

完整程式

/**
 * @file   main.c
 * @brief  USART with printf() function for STM32 Nucleo-F446RE.
 */
 
#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) /* Arduino-D1. */
#define GPIO_USART_AF (GPIO_AF7)  /* Table-11 in DS10693 */
 
static void delay(uint32_t value)
{
  for (uint32_t i = 0; i < value; i++)
  {
    __asm__("nop"); /* Do nothing. */
  }
}
 
static void rcc_setup(void)
{
  rcc_periph_clock_enable(RCC_USART_TX_GPIO);
  rcc_periph_clock_enable(RCC_USART2);
}
 
static void usart_setup(void)
{
  /* Set USART-Tx pin to alternate function. */
  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);
 
  /* Config USART params. */
  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); /* Tx-Only mode. */
 
  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;
}
 
/* For printf(). */
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

#include <stdio.h>
#include <errno.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>

除了 LibOpenCM3 的 rcc.hgpio.husart.h外,因為我們還需要實現 printf() 函式,所以還需要 stdio.herrno.h

RCC

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 選擇

#define RCC_USART_TX_GPIO (RCC_GPIOA)
#define GPIO_USART_TX_PORT (GPIOA)
#define GPIO_USART_TX_PIN (GPIO2) /* Arduino-D1. */
#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 設定

static void usart_setup(void)
{
  /* Set USART-Tx pin to alternate function. */
  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);
 
  /* Congif USART params. */
  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); /* Tx-Only mode. */
 
  usart_enable(USART2);
}

首先要設定好 GPIO。我們需要將 USART Tx 設定為 Alternate Function。

設定好 GPIO 後就是設定 USART 本身,也就是鮑率、資料位元、停止位元那些,這部分就照實際需求設定。

由於本例只有用到傳送的部分,不需要接收,所以 usart_set_mode() 設定為 USART_MODE_TX

printf()

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

static void usart_setup(void)
{
  /* Set USART-Tx pin to alternate function. */
#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 鐵人賽


STM32 LibOpenCM3:USART 接收
STM32 LibOpenCM3:EXTI 外部中斷

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