在嵌入式系統開發領域,STM32 系列微控制器憑借其強大的性能和豐富的資源被廣泛應用。中斷系統作為 STM32 的關鍵特性之一,能夠極大地提升系統的實時響應能力和多任務處理效率。本文將基于 STM32F4 系列芯片,深入剖析中斷與外設中斷的原理、配置及應用,幫助開發者全面掌握這一核心技術。
一、中斷簡介
中斷是指當 CPU 執行程序時,由于外部或內部隨機事件的發生,導致 CPU 暫時停止正在運行的程序,轉而執行一段特殊的服務程序(中斷服務子程序或中斷處理程序)。待事件處理完畢后,CPU 再返回被中斷的程序繼續執行,引發中斷的事件源被稱為中斷源。
STM32 的中斷功能極為強大,每個外設都具備產生中斷的能力。其中,可屏蔽中斷通道多達 82 個(外部),系統異常有 10 個,并且擁有 16 個可編程優先級(使用 4 位中斷優先級) 。除個別異常優先級固定外,其他異常優先級均可編程。在標準庫文件?stm32f4xx.h
?的?IRQn_Type
?結構體中,包含了 F4 系列全部的異常聲明。以 STM32F405xx/07xx 和 STM32F415xx/17xx 為例,其向量表如下:
序號 | 優先級類型 | 名稱 | 說明 | 地址 | |
---|---|---|---|---|---|
保留 | 0x00000000 | ||||
3 | 固定 | Reset | 復位 | 0x00000004 | |
W | 固定 | NMI | 不可屏蔽中斷,RCC 時鐘安全系統(CSS)連接到此向量 | 0x00000008 | |
-1 0 | 固定 可設置 | HardFault MemManage | 所有類型的錯誤 存儲器管理 | 0x0000000C 0x00000010 | |
1 | 可設置 | BusFault | 預取指失敗,存儲器訪問失敗 | 0x00000014 | |
2 | 可設置 | UsageFault | 未定義的指令或非法狀態 | 0x00000018 | |
保留 | 0x0000001C - 0x0000002B | ||||
3 | 可設置 | SVCall | 通過 SWI 指令調用的系統服務 | 0x0000002C | |
4 | 可設置 | DebugMonitor | 調試監控器 | 0x00000030 | |
保留 | 0x00000034 | ||||
5 | 可設置 | Pendsv | 可掛起的系統服務 | 0x00000038 | |
0 | 7 6 | 可設置 可設置 | WWDG SysTick | 窗口看門狗中斷 系統滴答定時器 | 0x00000040 0x0000003C |
1 | 8 | 可設置 | PVD | 連接到 EXTI 線的可編程電壓檢測 | 0x00000044 |
2 | 9 | TAMPSTAMP | (PVD)中斷 | 0x00000048 | |
3 | 10 | 可設置 可設置 | RTC_WKUP | 連接到 EXTI 線的入侵和時間蔽中斷 連接到 EXTI 線的 RTC 喚醒中斷 | 0x0000004C |
4 5 6 7 | 11 12 13 14 | 可設置 可設置 可設置 | FLASH RCC EXTI0 EXTI1 | FLASH 全局中斷 RCC 全局中斷 EXTI 線 0 中斷 | 0x00000050 0x00000054 0x00000058 0x0000005C |
8 9 10 | 15 16 17 | 可設置 可設置 可設置 | EXTI2 EXTI3 EXTI4 | EXTI 線 1 中斷 EXTI 線 2 中斷 EXTI 線 3 中斷 EXTI 線 4 中斷 | 0x00000060 0x00000064 0x00000068 |
... | ... | ... | ... | ... | |
80 81 87 88 | 可設置 可設置 | 哈希和隨機數發生器全局中斷 FPU 全局中斷 | 0x00000180 0x00000184 |
二、NVIC(Nested vectored interrupt controller)
NVIC 即嵌套向量中斷控制器,在中斷向量表中,優先級 7 - 88(中斷號從 0 - 82)代表著 STM32F103 的 91 個中斷 ,優先級號越小,優先級越高。當異常或中斷被觸發時,程序計數器指針(PC)會跳轉到對應地址執行,該地址存放跳轉指令,進而跳轉到服務函數執行相應功能。
在 MDK 開發環境中,可使用標準的異常和中斷向量表文件?startup_stm32f40_41xxx.s
?,其中明確規定了中斷處理函數的名稱,開發者不可隨意定義。
在 STM32 中,中斷優先級由搶占式優先級和響應優先級共同決定,二者組成 4 位控制字,數值越小優先級越高。通過不同的優先級分組,可靈活配置搶占式優先級和子優先級的位數:
優先級分組 | 搶占式優先級 | 子優先級 | 高 4 位使用情況描述 |
---|---|---|---|
NVICPriorityGroup_0 | 0 級搶占優先級 | 0 - 15 級子優先級 | 4bit 全用于子優先級 |
NVICPriorityGroup_1 | 0 - 1 級搶占優先級 | 0 - 7 級子優先級 | 1bit 用于搶占優先級,3bit 用于子優先級 |
NVICPriorityGroup_2 | 0 - 3 級搶占優先級 | 0 - 3 級子優先級 | 2bit 用于搶占優先級,2bit 用于子優先級 |
NVICPriorityGroup_3 | 0 - 7 級搶占優先級 | 0 - 1 級子優先級 | 3bit 用于搶占優先級,1bit 用于子優先級 |
NVICPriorityGroup_4 | 0 - 15 級搶占優先級 | 0 級子優先級 | 4bit 全用于搶占優先級 |
具有高搶占式優先級的中斷能夠在低搶占式優先級中斷服務程序執行過程中被響應,即支持中斷嵌套;而在搶占式優先級相同的情況下,若多個子優先級不同的中斷同時到來,高子優先級的中斷優先被響應 。不過,子優先級不支持中斷嵌套,當低子優先級中斷正在執行時,高子優先級中斷需等待其執行結束才能得到響應。此外,Reset、NMI、Hard Fault 的優先級為負數,高于普通中斷優先級且不可配置。
NVIC 相關的庫函數位于?misc.c
?中:
-
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
:用于設置搶占式優先級的位數。 -
NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
:初始化配置函數,其中?NVIC_InitTypeDef
?結構體定義如下:
typedef struct {uint8_t NVIC_IRQChannel; // 指定IRQ通道,可從IRQn_Type枚舉中選擇uint8_t NVIC_IRQChannelPreemptionPriority; // 搶占式優先級,取值0 - 15,值越小優先級越高uint8_t NVIC_IRQChannelSubPriority; // 子優先級,取值0 - 15,值越小優先級越高FunctionalState NVIC_IRQChannelCmd; // 使能或禁用IRQ通道,可設置為ENABLE或DISABLE
} NVIC_InitTypeDef;
三、外部中斷
3.1 外部中斷簡介
外部中斷 / 事件控制器(EXTI)負責管理控制器的 23 個中斷 / 事件線 。每個中斷 / 事件線都配備邊沿檢測器,可實現對輸入信號的上升沿和下降沿檢測。EXTI 能夠對每條中斷 / 事件線進行單獨配置,可設置為中斷或事件,并定義觸發事件的屬性。中斷線路將輸入信號傳輸至 NVIC,進而運行中斷服務函數實現軟件層面的功能;而事件線路則是向其他外設傳輸脈沖信號,屬于硬件級別的電路信號傳輸。
STM32F4 的中斷控制器支持 22 個外部中斷 / 事件請求(中斷線) 。對于每條中斷線,開發者可設置相應的觸發方式(上升沿觸發、下降沿觸發、邊沿觸發)并使能。各中斷線的映射關系如下:
-
EXTI 線 0 - 15:對應外部 IO 口的輸入中斷。
-
EXTI 線 16:連接到 PVD 輸出。
-
EXTI 線 17:連接到 RTC 鬧鐘事件。
-
EXTI 線 18:連接到 USB OTG FS 喚醒事件。
-
EXTI 線 19:連接到以太網喚醒事件。
-
EXTI 線 20:連接到 USB OTG HS(在 FS 中配置)喚醒事件。
-
EXTI 線 21:連接到 RTC 入侵和時間戳事件。
-
EXTI 線 22:連接到 RTC 喚醒事件。
系統配置控制器(SYSCFG)主要用于管理可執行代碼存儲區域的地址重映射、選擇以太網 PHY 接口以及管理 GPIO 的外部中斷線連接 。例如,SYSCFGEXTICR1 寄存器中的 EXTI0 [3:0] 位和 EXTI1 [3:0] 位分別用于映射 PA0 - PI0 和 PA1 - PI1 到相應的 EXTI 線。
3.2 配置流程
-
使能 GPIO 外設時鐘:為使用的 GPIO 端口提供時鐘,確保其正常工作。例如,若使用 PA0 和 PF9 端口,則需使能 GPIOA 和 GPIOF 的時鐘:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOF, ENABLE);
-
配置外部中斷占用的 GPIO:將用于外部中斷的 GPIO 端口配置為輸入模式,無需設置復用功能。例如,配置 PA0 為按鍵輸入端口:
GPIO_InitTypeDef gpio_struct; gpio_struct.GPIO_Pin = GPIO_Pin_0; gpio_struct.GPIO_Mode = GPIO_Mode_IN; gpio_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &gpio_struct);
-
使能 SYSCFG 外設時鐘:SYSCFG 用于處理外部中斷映射,因此需要使能其時鐘:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
-
設置中斷線與 IO 的映射關系:通過?
SYSCFG_EXTILineConfig()
?函數設置 GPIO 端口與 EXTI 中斷線的映射。例如,將 PA0 映射到 EXTI0:SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
-
配置外部中斷參數:使用?
EXTI_InitTypeDef
?結構體配置外部中斷的相關參數,如中斷線、模式、觸發方式和使能狀態等。例如,配置 EXTI0 為上升沿觸發的中斷:EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure);
-
設置中斷優先級(NVIC):通過?
NVIC_InitTypeDef
?結構體設置外部中斷的優先級,包括搶占式優先級和子優先級,并使能中斷通道。例如,設置 EXTI0 的優先級:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitTypeDef mynvic; mynvic.NVIC_IRQChannel = EXTI0_IRQn; mynvic.NVIC_IRQChannelPreemptionPriority = 0; mynvic.NVIC_IRQChannelSubPriority = 3; mynvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&mynvic);
-
編寫中斷處理函數:中斷處理函數的名稱必須與啟動代碼向量表中定義的一致。在函數中,需檢測中斷標志位,處理完中斷后清除標志位。例如,EXTI0 的中斷處理函數:
void EXTI0_IRQHandler() {if(EXTI_GetFlagStatus(EXTI_Line0) == SET) {// 處理中斷事件,如設置標志位led_flag = 1;EXTI_ClearFlag(EXTI_Line0);}
}
3.3 實戰案例:按鍵控制 LED 閃爍
以按下按鍵使 D1 燈閃爍五次為例,完整代碼如下:
#include "stm32f4xx.h"uint8_t led_flag = 0;// 配置GPIO端口
void config_port() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOF, ENABLE);// 配置LED1端口為推挽低速輸出GPIO_InitTypeDef gpio_struct;gpio_struct.GPIO_Pin = GPIO_Pin_9;gpio_struct.GPIO_Mode = GPIO_Mode_OUT;gpio_struct.GPIO_OType = GPIO_OType_PP;gpio_struct.GPIO_PuPd = GPIO_PuPd_NOPULL;gpio_struct.GPIO_Speed = GPIO_Low_Speed;GPIO_Init(GPIOF, &gpio_struct);// 按鍵端口配置gpio_struct.GPIO_Pin = GPIO_Pin_0;gpio_struct.GPIO_Mode = GPIO_Mode_IN;gpio_struct.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA, &gpio_struct);
}// 設置外部中斷(EXTI與syscfg)
void config_exti() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line0;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);
}// 配置NVIC
void config_nvic() {NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitTypeDef mynvic;mynvic.NVIC_IRQChannel = EXTI0_IRQn;mynvic.NVIC_IRQChannelPreemptionPriority = 0; mynvic.NVIC_IRQChannelSubPriority = 3;mynvic.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&mynvic);
}// 中斷函數
void EXTI0_IRQHandler() {if(EXTI_GetFlagStatus(EXTI_Line0) == SET) {led_flag = 1;EXTI_ClearFlag(EXTI_Line0);}
}// 簡單延時函數
void delay() {int i = 1000000;while(i--);
}int main() {config_port();config_exti();config_nvic();while(1) {if(led_flag == 1) {for(int i = 0; i < 5; i++) {GPIO_SetBits(GPIOF, GPIO_Pin_9);delay();delay();GPIO_ResetBits(GPIOF, GPIO_Pin_9);delay();delay();}led_flag = 0;}}
}
在上述代碼中,首先通過?config_port()
?函數配置了按鍵輸入端口(PA0)和 LED 輸出端口(PF9);接著在?config_exti()
?函數中完成了外部中斷的配置,將 PA0 映射到 EXTI0 并設置為上升沿觸發;config_nvic()
?函數則設置了 EXTI0 的中斷優先級;最后在主函數中,根據按鍵觸發的中斷標志?led_flag
?控制 LED 閃爍五次。
四、總結
本文全面深入地介紹了 STM32 中斷系統中的中斷基本概念、NVIC 的工作機制以及外部中斷的詳細原理和配置方法,并通過實際案例展示了外部中斷在按鍵控制 LED 閃爍中的應用。掌握這些知識對于開發高效、穩定的 STM32 嵌入式系統至關重要。在實際項目中,開發者可根據具體需求靈活配置中斷優先級、觸發方式等參數,充分發揮 STM32 中斷系統的強大功能,提升系統的性能和響應速度。希望本文能為廣大嵌入式開發者在 STM32 中斷開發方面提供有益的參考和幫助。