STM32 LibOpenCM3:GPIO 輸出

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

前言

終於要開始實際寫程式了,接續上一篇的內容,這次要教最基本的 LibOpenCM3 的 GPIO 輸出用法,會控制一個 LED 燈使其閃爍。

正文

先以 Nucleo-F446RE 做示範。

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

完整程式

先把完整的程式打出來:

 1/**
 2 * @file   main.c
 3 * @brief  Blinking LED example for STM32 Nucleo-F446RE.
 4 */
 5
 6#include <libopencm3/stm32/rcc.h>
 7#include <libopencm3/stm32/gpio.h>
 8
 9/* User LED (LD2) connected to Arduino-D13 pin. */
10#define RCC_LED_GPIO (RCC_GPIOA)
11#define GPIO_LED_PORT (GPIOA)
12#define GPIO_LED_PIN (GPIO5)
13
14static void delay(uint32_t value)
15{
16  for (uint32_t i = 0; i < value; i++)
17  {
18    __asm__("nop"); /* Do nothing. */
19  }
20}
21
22int main(void)
23{
24  /* Enable clock. */
25  rcc_periph_clock_enable(RCC_LED_GPIO);
26
27  /* Set LED pin to output push-pull. */
28  gpio_mode_setup(GPIO_LED_PORT,
29                  GPIO_MODE_OUTPUT,
30                  GPIO_PUPD_NONE,
31                  GPIO_LED_PIN);
32
33  gpio_set_output_options(GPIO_LED_PORT,
34                          GPIO_OTYPE_PP,
35                          GPIO_OSPEED_2MHZ,
36                          GPIO_LED_PIN);
37
38  /* Start blinking. */
39  while (1)
40  {
41    gpio_toggle(GPIO_LED_PORT, GPIO_LED_PIN); /* LED on/off. */
42    delay(500000);
43  }
44
45  return 0;
46}

分段說明

Include

1#include <libopencm3/stm32/rcc.h>
2#include <libopencm3/stm32/gpio.h>

如同其它的程式,首先要將需要用到的功能 Include 進來。本例中有兩個檔案要引入:

  • rcc.h:RCC 是 Reset and Clock Controller 的意思,由於基本上所有的 STM32 功能都需要 Clock,因此 RCC 通常是一定會用到的。
  • gpio.h:如如同它的名字,這就是包含了 GPIO 的各種功能。

LibOpenCM3 的這些檔案 PIO 都會幫我們處理好,所以不用另外下載或設定路徑,直接 #include 就可以了。

定義腳位

1/* User LED (LD2) connected to Arduino-D13 pin. */
2#define RCC_LED_GPIO (RCC_GPIOA)
3#define GPIO_LED_PORT (GPIOA)
4#define GPIO_LED_PIN (GPIO5)

因爲每個 STM32 Nucleo 開發板的 LED 腳位可能不同,因此使用 #define 來定義腳位比較方便程式的撰寫與修改。

根據資料手冊 UM1724,Nucleo-F446RE 的板載 LED(LD2)所在的腳位是 PA5(對應 Arduino 的 D13 腳位),也就是 GPIO Port-A 的 5 號腳,因此我們定義 GPIO_LED_PORTGPIOAGPIO_LED_PINGPIO5

此外 RCC 也會需要依照 GPIO Port 進行設定,所以也定義一個 RCC_LED_GPIORCC_GPIOA

Delay 函式

1static void delay(uint32_t value)
2{
3  for (uint32_t i = 0; i < value; i++)
4  {
5    __asm__("nop"); /* Do nothing. */
6  }
7}

這是一個最簡單暴力的 Delay 寫法,幾乎無精度可言,但目前我們只要這樣就夠了。

其中的 __asm__("nop") 代表嵌入組合語言的「nop」指令,也就是無操作(No operation)。

主程式

 1int main(void)
 2{
 3  /* Enable clock. */
 4  rcc_periph_clock_enable(RCC_LED_GPIO);
 5
 6  /* Set LED pin to output push-pull. */
 7  gpio_mode_setup(GPIO_LED_PORT,
 8                  GPIO_MODE_OUTPUT,
 9                  GPIO_PUPD_NONE,
10                  GPIO_LED_PIN);
11
12  gpio_set_output_options(GPIO_LED_PORT,
13                          GPIO_OTYPE_PP,
14                          GPIO_OSPEED_2MHZ,
15                          GPIO_LED_PIN);
16
17  /* Start blinking. */
18  while (1)
19  {
20    gpio_toggle(GPIO_LED_PORT, GPIO_LED_PIN); /* LED on/off. */
21    delay(500000);
22  }
23
24  return 0;
25}
  • rcc_periph_clock_enable():這個函式會致能指定功能的 Clock。在這裡我們要啓用 LED 所在的 GPIO Port 的 Clock。

  • gpio_mode_setup():爲指定的 GPIO 設定模式。

    • GPIO_LED_PORT:要設定的 GPIO Port。
    • GPIO_MODE_OUTPUT:設定爲「General Purpose Output」 模式。
    • GPIO_PUPD_NONE:設定爲不使用上下拉電阻。
    • GPIO_LED_PIN:要設定的 GPIO Pin,若要在同一個 Port 中設定多個 Pin,各個 Pin 可以用 | 來複選。
  • gpio_set_output_options():爲指定的 GPIO 設定輸出選項。

    • GPIO_LED_PORT:要設定的 GPIO Port。
    • GPIO_OTYPE_PP:設定輸出電路組態爲「Push-Pull(推挽)」 。
    • GPIO_OSPEED_2MHZ:設定速度。
    • GPIO_LED_PIN:要設定的 GPIO Pin,若要在同一個 Port 中設定多個 Pin,各個 Pin 可以用 | 來複選。
  • gpio_toggle():反轉該 GPIO 的輸出值。如果目前是輸出 High,那就變成輸出 Low,反之亦然。

編譯與燒錄/上傳

打完程式後,可以在 VS Code 左下方找到編譯(Build)和燒錄(Upload)的按鈕。也可以用快捷鍵「Ctrl+Alt+B」、「Ctrl+Alt+U」。


▲ PIO 的 Build 與 Upload 按鈕在 VS Code 的左下。

▲ PIO 的 Build 與 Upload 按鈕在 VS Code 的左下。

編譯完成後 PIO 會顯示佔用的資源:

1RAM: 0.0% (used 0 bytes from 131072 bytes)
2Flash: 0.1% (used 764 bytes from 524288 bytes)

記得燒錄前要用 USB 線接上 Nucleo,並安裝 ST-Link 的驅動程式,否則會報錯。

▲ 成果。

▲ 成果。

F103RB

STM32F1 系列的部分程式寫法不一樣,所以在此也提供 Nucleo-F103RB 的程式範例。主要差異只有 GPIO 的設定函式不同,STM32F1 用的是 gpio_set_mode(),而非 gpio_mode_setup()gpio_set_output_options()

 1/**
 2 * @file   main.c
 3 * @brief  Blinking LED example for STM32 Nucleo-F103RB.
 4 */
 5
 6#include <libopencm3/stm32/rcc.h>
 7#include <libopencm3/stm32/gpio.h>
 8
 9/* User LED (LD2) connected to Arduino-D13 pin. */
10#define RCC_LED_GPIO (RCC_GPIOA)
11#define GPIO_LED_PORT (GPIOA)
12#define GPIO_LED_PIN (GPIO5)
13
14static void delay(uint32_t value)
15{
16  for (uint32_t i = 0; i < value; i++)
17  {
18    __asm__("nop"); /* Do nothing. */
19  }
20}
21
22int main(void)
23{
24  /* Enable clock. */
25  rcc_periph_clock_enable(RCC_LED_GPIO);
26
27  /* Set LED pin to output push-pull. */
28  gpio_set_mode(GPIO_LED_PORT,
29                GPIO_MODE_OUTPUT_2_MHZ,
30                GPIO_CNF_OUTPUT_PUSHPULL,
31                GPIO_LED_PIN);
32
33  /* Start blinking. */
34  while (1)
35  {
36    gpio_toggle(GPIO_LED_PORT, GPIO_LED_PIN); /* LED on/off. */
37    delay(500000);
38  }
39
40  return 0;
41}

PIO 環境

如果你的程式會需要在 F1 或 F4 等其它 STM32 系列上運作,那每次用 F1 時 GPIO 的寫法不同,或是有 Pin 腳不同的情況會很麻煩,所以這裡簡單介紹如何用 PIO 設定多個專案環境,方便切換。

▲ 設定好的環境可以在 VS Code 下方進行切換。

▲ 設定好的環境可以在 VS Code 下方進行切換。

主程式

 1/**
 2 * @file   main.c
 3 * @brief  Blinking LED example for STM32 based on LibOpenCM3.
 4 */
 5
 6#include <libopencm3/stm32/rcc.h>
 7#include <libopencm3/stm32/gpio.h>
 8
 9/* User LED (LD2) connected to Arduino-D13 pin. */
10#if defined(NUCLEO_F103RB) || defined(NUCLEO_F446RE)
11  #define RCC_LED_GPIO (RCC_GPIOA)
12  #define GPIO_LED_PORT (GPIOA)
13  #define GPIO_LED_PIN (GPIO5)
14#else
15  #error "STM32 board not defined."
16#endif
17
18static void delay(uint32_t value)
19{
20  for (uint32_t i = 0; i < value; i++)
21  {
22    __asm__("nop"); /* Do nothing. */
23  }
24}
25
26int main(void)
27{
28  /* Enable clock. */
29  rcc_periph_clock_enable(RCC_LED_GPIO);
30
31  /* Set LED pin to output push-pull. */
32#if defined(STM32F1)
33  gpio_set_mode(GPIO_LED_PORT,
34                GPIO_MODE_OUTPUT_2_MHZ,
35                GPIO_CNF_OUTPUT_PUSHPULL,
36                GPIO_LED_PIN);
37#else
38  gpio_mode_setup(GPIO_LED_PORT,
39                  GPIO_MODE_OUTPUT,
40                  GPIO_PUPD_NONE,
41                  GPIO_LED_PIN);
42
43  gpio_set_output_options(GPIO_LED_PORT,
44                          GPIO_OTYPE_PP,
45                          GPIO_OSPEED_2MHZ,
46                          GPIO_LED_PIN);
47#endif
48
49  /* Start blinking. */
50  while (1)
51  {
52    gpio_toggle(GPIO_LED_PORT, GPIO_LED_PIN); /* LED on/off. */
53    delay(500000);
54  }
55
56  return 0;
57}

PIO 專案設定檔 platformio.ini

 1[platformio]
 2default_envs = nucleo_f103rb
 3
 4; Set/Override default options for each [env:XXX]
 5[env]
 6platform = ststm32
 7framework = libopencm3
 8
 9[env:nucleo_f103rb]
10board = nucleo_f103rb
11build_flags = -D NUCLEO_F103RB
12
13[env:nucleo_f446re]
14board = nucleo_f446re
15build_flags = -D NUCLEO_F446RE

小結

這次簡單介紹了 LibOpenCM3 的 GPIO 輸出用法,這部分只要有搞懂 STM32 的 GPIO 模式應該不會太難。

參考資料

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



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

comments powered by Disqus