Skip to content

[LibOpenCM3 × STM32教學-2] 按鈕觸發外部中斷 EXTI

發佈

我在 2022 年 9 月重新寫了與本文內容相近的文章,建議可以觀看新文章:

前言

LibOpenCM3 是一個 Open-Source 的 ARM Cortex-M3 微控制器底層硬體函式庫,支援包含 STM32 在內的多種微控制器。

本文將以 STM32F103RB(Nucleo F103RB)作為示範,介紹如何使用 LibOpenCM3 寫出 STM32 的外部中斷(External interrupt,EXTI)。

正文

外部中斷最基本的應用就是按鈕。雖然可以使用輪詢的方式來感測按鈕是否有觸發,但這種做法不但消耗資源,也不保險(觸發當下可能剛好錯過輪詢),而使用外部中斷就不會有這樣的問題。

本文示範一個以按鈕觸發的外部中斷,每次按下按鈕時就會觸發指定的外部中斷,讓 LED 進行一次開或關。

程式全文

/**
 * @file   main.c
 * @brief  Basic button external interrupt(EXTI).
 */
 
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/cm3/nvic.h>
 
void led_setup(void)
{
  rcc_periph_clock_enable(RCC_GPIOA);
  gpio_set_mode(GPIOA,
                GPIO_MODE_OUTPUT_2_MHZ,
                GPIO_CNF_OUTPUT_PUSHPULL,
                GPIO5);
}
 
 
void button_setup(void)
{
  rcc_periph_clock_enable(RCC_GPIOC);
  rcc_periph_clock_enable(RCC_AFIO);
 
  nvic_enable_irq(NVIC_EXTI15_10_IRQ);
 
  gpio_set_mode(GPIOC,
                GPIO_MODE_INPUT,
                GPIO_CNF_INPUT_FLOAT,
                GPIO13);
 
  exti_select_source(EXTI13, GPIOC);
  exti_set_trigger(EXTI13, EXTI_TRIGGER_FALLING);
  exti_enable_request(EXTI13);
}
 
/**
 * @brief EXTI15~10 Interrupt service routine.
 */
void exti15_10_isr(void)
{
  exti_reset_request(EXTI13);
  gpio_toggle(GPIOA, GPIO5);
}
 
int main(void)
{
  led_setup();
  button_setup();
 
  while (1)
  {
    __asm__("nop");
  }
 
  return 0;
}

程式說明

引入函式庫

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/cm3/nvic.h>

除了基本的「RCC」與「GPIO」函式庫外,要使用外部中斷還需要「EXTI」與「NVIC」這兩個函式庫。

其中「EXTI」包含了外部中斷(External interrupt)的相關功能。「NVIC」是「Nested vectored interrupt controller」的意思,是 ARM Cortex-M3 中負責管理所有中斷的核心功能,包含了中斷優先級、中斷請求(Interrupt request,IRQ)、中斷旗標等。

注意是 libopencm3/cm3/nvic.h 而非 libopencm3/stm32/nvic.h

設定 LED

void led_setup(void)
{
  rcc_periph_clock_enable(RCC_GPIOA);
  gpio_set_mode(GPIOA,
                GPIO_MODE_OUTPUT_2_MHZ,
                GPIO_CNF_OUTPUT_PUSHPULL,
                GPIO5);
}

函數 led_setup() 負責設定 LED。

設定按鈕及 EXTI

void button_setup(void)
{
  rcc_periph_clock_enable(RCC_GPIOC);
  rcc_periph_clock_enable(RCC_AFIO);
 
  nvic_enable_irq(NVIC_EXTI15_10_IRQ);
 
  gpio_set_mode(GPIOC,
                GPIO_MODE_INPUT,
                GPIO_CNF_INPUT_FLOAT,
                GPIO13);
 
  exti_select_source(EXTI13, GPIOC);
  exti_set_trigger(EXTI13, EXTI_TRIGGER_FALLING);
  exti_enable_request(EXTI13);
}

函數 button_setup() 負責按鈕的相關設定。

中斷服務程序 ISR

/**
 * @brief EXTI15~10 Interrupt service routine.
 */
void exti15_10_isr(void)
{
  exti_reset_request(EXTI13);
  gpio_toggle(GPIOA, GPIO5);
}

函數 exti15_10_isr() 是「EXTI-10 到 15」的中斷服務程序(Interrupt service routine,ISR)。當「EXTI-10 到 15」發生中斷時就會執行這裡的程式。此函數名稱是規定好的,不能打錯。

主程式

int main(void)
{
  led_setup();
  button_setup();
 
  while (1)
  {
    __asm__("nop");
  }
 
  return 0;
}

主程式先依序呼叫 led_setup()button_setup() 來完成設定,隨後就進入一個無限空迴圈,等待中斷的觸發。

__asm__("nop") 會嵌入組合語言的「nop(無操作)」指令。

結語

本次文章內介紹的程式我也有放在 GitHub 上,可以直接載下來並使用 PlatformIO 開始專案。

相關文章


無線分離式人體工學鍵盤Mitosis的介紹與分析
如果幸福有顏色

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