[撰寫外部中斷步驟]
瞭解設定優先順序 NVIC 及外部中斷 EXTI 兩個重要的功能後,接下來以一個範例來說明,如何撰寫外部中斷程式,有以下幾個步驟:(1) 啟用 AFIO 複用時鐘功能
使用 RCC_APB2PeriphClockCmd() 函式,將 RCC_APB2Periph_AFIO 啟用。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
(2) 設定觸發中斷的 GPIO Port
要將 GPIO Port 作為觸發中斷的來源,需將 GPIO 設定為輸入模式,有以下 4 種:
- GPIO_Mode_AIN 模擬輸入(ADC模擬輸入,或者低功耗下省電)
- GPIO_Mode_IN_FLOATING 浮空輸入
- GPIO_Mode_IPD 下拉輸入
- GPIO_Mode_IPU 上拉輸入
GPIO_InitTypeDef GPIO_InitStructure; //定義結構體 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //設定成上拉輸入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //設定 PB14 當作I/O GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOE,&GPIO_InitStructure); //使用結構體進行初始化(3) 將 GPIO Port 與中斷來源連接起來
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);(4) 定義初始化中斷設定,並建立 EXTI_InitStructure
EXTI_InitTypeDef EXTI_InitStructure; //定義初始化結構體 EXTI_InitStructure.EXTI_Line = EXTI_Line14; //設定 Line14 為中斷線的標號 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中斷模式:中斷 EXTI_Mode_Interrupt 或 事件 EXTI_Mode_Event。 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //觸發方式:上升/下降或上下沿觸發 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); //根據結構體進行初始化(5) 設定中斷優先順序分組
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);(6) 中斷優先順序配置:
NVIC_InitTypeDef NVIC_InitStructure; //定義結構體 NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //Enable 外部中斷的通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //設定主要優先順序 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //設定次要優先順序 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //Enable外部中斷通道 NVIC_Init(&NVIC_InitStructure); //根據結構體進行初始化(7) 編寫外部中斷服務函式的
外部中斷函式分別為以下七種之一,中斷線 0-4 每個中斷線對應一箇中斷函式,中斷線 5-9 共用中斷函式 EXTI9_5_IRQHandler,中斷線 10-15 共用中斷函式 EXTI15_10_IRQHandler。
- EXPORT EXTI0_IRQHandler
- EXPORT EXTI1_IRQHandler
- EXPORT EXTI2_IRQHandler
- EXPORT EXTI3_IRQHandler
- EXPORT EXTI4_IRQHandler
- EXPORT EXTI9_5_IRQHandler
- EXPORT EXTI15_10_IRQHandler
void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line14) == SET) //判斷 Line14 的中斷是否發生 { // 中斷執行的程式... Button_Count ++; delay_ms(300); EXTI_ClearITPendingBit(EXTI_Line14); //清除 LINE 上的中斷標誌位 } }
以下就用一個按鍵的範例來說明如何觸發外部中斷,然後將計數器加 1 ,顯示在 OLED 上。
[材料]
- STM32F103C8T6 主板 x 1
- OLED SSD1306 顯示器 x 1
- 麵包板 x 1
- 按鍵 x 1 個
- STLINK V2 模擬下載器 x 1
- 連接線 x N 條
[接線與電路圖]
按鍵的一個接腳接在 GND,另一腳接在 STM32F103x PB14,與 SSD1306 連接的方式如下:STM32F103x | SSD1306 OLED | 按鍵 |
---|---|---|
3.3v | VDD | - |
GND | GND | 其中一個 Pin |
B8 | SCK/SCL | - |
B9 | SDA | - |
B14 | - | 另一個 Pin |
[程式]
主程式一開始先對 OLED 進行初始化,再設定 PB14 為按鍵的其中一個引腳,進入迴圈循環後,就等中斷產生,主程式 main.c 如下:/********************************************************** * @file main.c * @date 2022/3/27 * @brief traffic light with OLED * VDD - 3.3V * GND - GND * SCL/SCK - PB8 // IIC時鐘信號 * SDA - PB9 // IIC資料信號 **********************************************************/ #include "stm32f10x.h" #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" #include "delay.h" #include "sys.h" #include "oled.h" #include "bmp.h" #include "button.h" // GPIO_InitTypeDef GPIO_InitStruct; // 定義一個GPIO_InitTypeDef類型的結構體 int main (void) { delay_init(); //延時函數初始化 NVIC_Configuration(); //設置 NVIC 中斷分組2:2位搶佔優先順序,2位回應優先順序 OLED_Init(); //初始化OLED Button_Init(); OLED_Clear(0); //清除螢幕 OLED_ShowString(2, 2, "Count:",16); while (1) { OLED_ShowNum(60, 2, Button_Get(),3, 16); } }
以下 Button.c 內有一段產生中斷服務程式:EXTI15_10_IRQHandler(void) ,這段程式使用 delay_ms(300); 原本沒有加上這一行時,按一下按鍵會隨機跳好幾個數字,原因是按下按鍵的瞬間產生好幾個中斷,讓數字連續跳好幾個號碼。加上 delay 後,這個問題就解決了。
#include "stm32f10x.h" #include "delay.h" uint16_t Button_Count; void Button_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //設定 RCC 時鐘 GPIO_InitTypeDef GPIO_InitStructure; //設定 GPIOB Pin14 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); //設定外部中斷 EXTI_Line14 EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line14; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設定 NVIC 優先順序 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //使用 EXTI15_10_IRQn 通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } uint16_t Button_Get(void) { return Button_Count; // 傳回累計按鍵數 } void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line14) == SET) { Button_Count ++; // 將計數 +1 delay_ms(300); // 暫停 300ms EXTI_ClearITPendingBit(EXTI_Line14); } }
Button.h
#ifndef __BUTTON_H #define __BUTTON_H void Button_Init(void); uint16_t Button_Get(void); #endif完整的程式請參考 Github:4. Push button control led using Interrupt
張貼留言