[撰寫外部中斷步驟]
瞭解設定優先順序 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

張貼留言