一、STM32 中斷概述
中斷,是指處理機處理程序運行中出現(xiàn)的緊急事件的整個過程。程序運行過程中,系統(tǒng)外部、系統(tǒng)內(nèi)部或者現(xiàn)行程序本身若出現(xiàn)緊急事件,處理機立即中止現(xiàn)行程序的運行,自動轉(zhuǎn)入相應(yīng)的處理程序(中斷服務(wù)程序),待處理完后,再返回原來的程序運行,這整個過程稱為程序中斷。當處理機接受中斷時,只需暫停一個或幾個周期而不執(zhí)行處理程序的中斷,稱為簡單中斷,中斷又可分為屏蔽中斷和非屏蔽中斷兩類。
STM32 中斷非常強大,每個外設(shè)都可以產(chǎn)生中斷,所以之后講的很多外設(shè)都可以使用中斷,在這里先總體講解一下中斷,之后講到相應(yīng)外設(shè)的時候再將每個外設(shè)的中斷使用方法;
F103 在內(nèi)核水平上搭載了一個異常響應(yīng)系統(tǒng),支持為數(shù)眾多的系統(tǒng)異常和外部中斷。其中系統(tǒng)異常有8 個(如果把Reset 和HardFault 也算上的話就是10 個),外部中斷有60 個。除了個別異常的優(yōu)先級被定死外,其它異常的優(yōu)先級都是可編程的。有關(guān)具體的系統(tǒng)異常和外部中斷可在HAL 庫文件stm32f1xx.h 這個頭文件查詢到,在IRQn_Type 這個結(jié)構(gòu)體里面包含了F103 系列全部的異常聲明。
F103 系統(tǒng)異常清單
優(yōu)先級 | 優(yōu)先級類型 | 名稱 | 說明 | 地址 |
---|---|---|---|---|
-3 | 固定 | Reset | 復(fù)位 | 0X0000 0004 |
-2 | 固定 | NMI | 不可屏蔽中斷。RCC 時鐘安全系統(tǒng)(CSS) 連接到NMI 向量 | 0X0000 0008 |
-1 | 固定 | HardFault | 所有類型的錯誤 | 0X0000 000C |
0 | 可編程 | MemManage | 存儲器管理 | 0X0000 0010 |
1 | 可編程 | BusFault | 預(yù)取指失敗,存儲器訪問失敗 | 0X0000 0014 |
2 | 可編程 | UsageFault | 未定義的指令或非法狀態(tài) | 0X0000 0018 |
3 | 可編程 | SVCall | 通過SWI 指令調(diào)用的系統(tǒng)服務(wù) | 0X0000 002C |
4 | 可編程 | Debug Monitor | 調(diào)試監(jiān)控器 | 0X0000 0030 |
5 | 可編程 | PendSV | 可掛起的系統(tǒng)服務(wù) | 0X0000 0038 |
6 | 可編程 | SysTick | 系統(tǒng)嘀嗒定時器 | 0X0000 003C |
F103 外部中斷清單
編號 | 優(yōu)先級 | 優(yōu)先級類型 | 名稱 | 說明 | 地址 |
---|---|---|---|---|---|
0 | 7 | 可編程 | WWDG | 窗口看門狗中斷 | 0X00000040 |
1 | 8 | 可編程 | PVD | 連到EXTI 的電源電壓檢測(PVD) 中斷 | 0X00000044 |
2 | 9 | 可編程 | TAMPER | 侵入檢測中斷 | 0X00000048 |
··· | |||||
57 | 64 | 可編程 | DMA2 通道2 DMA2 通道2 中斷 | 0X00000124 | |
58 | 65 | 可編程 | DMA2 通道3 DMA2 通道3z 中斷 | 0X0000 | |
59 | 66 | 可編程 | DMA2 通道4_5 DMA2 通道4 和通道5 中斷 | 0X0000012C |
二、NVIC 簡介
NVIC 是嵌套向量中斷控制器,控制著整個芯片中斷相關(guān)的功能,它跟內(nèi)核緊密耦合,是內(nèi)核里面的一個外設(shè)。但是各個芯片廠商在設(shè)計芯片的時候會對Cortex-M3 內(nèi)核里面的NVIC 進行裁剪,把不需要的部分去掉,所以說STM32的NVIC 是Cortex-M3 的NVIC 的一個子集。
NVIC 寄存器簡介
在固件庫中,NVIC 的結(jié)構(gòu)體定義給每個寄存器都預(yù)留了很多位。不過STM32F103 可用不了這么多,只是用了部分而已,具體使用了多少可參考《Cortex-M3 內(nèi)核編程手冊》NVIC 寄存器映射。
typedef struct {
__IOM uint32_t ISER[8U]; // 中斷使能寄存器
uint32_t RESERVED0[24U];
__IOM uint32_t ICER[8U]; // 中斷清除寄存器
uint32_t RSERVED1[24U];
__IOM uint32_t ISPR[8U]; // 中斷使能懸起寄存器
uint32_t RESERVED2[24U];
__IOM uint32_t ICPR[8U]; // 中斷清除懸起寄存器
uint32_t RESERVED3[24U];
__IOM uint32_t IABR[8U]; // 中斷有效位寄存器/
uint32_t RESERVED4[56U];
__IOM uint8_t IP[240U]; // 中斷優(yōu)先級寄存器(8Bit wide)
uint32_t RESERVED5[644U];
__OM uint32_t STIR; // 軟件觸發(fā)中斷寄存器
} NVIC_Type;
在配置中斷的時候我們一般只用ISER、ICER 和IP 這三個寄存器,ISER 用來使能中斷,ICER 用來失能中斷,IP 用來設(shè)置中斷優(yōu)先級。
中斷優(yōu)先級
在NVIC 有一個專門的寄存器:中斷優(yōu)先級寄存器NVIC_IPRx,用來配置外部中斷的優(yōu)先級,IPR寬度為8bit,原則上每個外部中斷可配置的優(yōu)先級為0~255,數(shù)值越小,優(yōu)先級越高。但是絕大多數(shù)CM3 芯片都會精簡設(shè)計,以致實際上支持的優(yōu)先級數(shù)減少,在F103 中,只使用了高4bit,
如下所示:
bit7 bit6 bit5 bit4 | bit3 bit2 bit1 bit0 |
---|---|
用于表達優(yōu)先級 | 未使用,讀回為0 |
用于表達優(yōu)先級的這4bit,又被分組成搶占優(yōu)先級和子優(yōu)先級;
如果有多個中斷同時響應(yīng),搶占優(yōu)先級高的就會搶占搶占優(yōu)先級低的優(yōu)先得到執(zhí)行,如果搶占優(yōu)先級相同,就比較子優(yōu)先級;
如果搶占優(yōu)先級和子優(yōu)先級都相同的話,就比較他們的硬件中斷編號,編號越小,優(yōu)先級越高。
優(yōu)先級的分組由內(nèi)核外設(shè)SCB 的應(yīng)用程序中斷及復(fù)位控制寄存器AIRCR 的PRIGROUP[10:8] 位決定,F(xiàn)103 分為了5 組;
設(shè)置優(yōu)先級分組可調(diào)用庫函數(shù)HAL_NVIC_SetPriority() 實現(xiàn),有關(guān)NVIC 中斷相關(guān)的庫函數(shù)都在庫文件stm32f1xx_hal_cortex.c 和stm32f1xx_hal_cortex.h 中。
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority,
uint32_t SubPriority)
{
uint32_t prioritygroup = 0x00U;
/* Check the parameters */
assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));
assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
prioritygroup = NVIC_GetPriorityGrouping();
NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup,
PreemptPriority, SubPriority));
}
NVIC 中斷配置固件庫
固件庫文件core_cm3.h 的最后,還提供了NVIC 的一些函數(shù),這些函數(shù)遵循CMSIS 規(guī)則,只要是Cortex-M3 的處理器都可以使用,具體如下:
void NVIC_EnableIRQ(IRQn_Type IRQn) //使能中斷
void NVIC_DisableIRQ(IRQn_Type IRQn) //失能中斷
void NVIC_SetPendingIRQ(IRQn_Type IRQn) //設(shè)置中斷懸起位
void NVIC_ClearPendingIRQ(IRQn_Type IRQn) //清除中斷懸起位
uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) //獲取懸起中斷編號
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) //設(shè)置中斷優(yōu)先級
uint32_t NVIC_GetPriority(IRQn_Type IRQn) //獲取中斷優(yōu)先級
void NVIC_SystemReset(void) //系統(tǒng)復(fù)位
中斷編程
在配置每個中斷的時候一般有3 個編程要點:
- 使能外設(shè)某個中斷,這個具體由每個外設(shè)的相關(guān)中斷使能位控制。比如串口有發(fā)送完成中斷,接收完成中斷,這兩個中斷都由串口控制寄存器的相關(guān)中斷使能位控制。
- 配置EXTI 中斷源、配置中斷優(yōu)先級。
- 編寫中斷服務(wù)函數(shù)。
會使用下面的函數(shù):
//NVIC 初始化結(jié)構(gòu)體
/* 配置EXTI 中斷源、配置中斷優(yōu)先級*/
HAL_NVIC_SetPriority(IRQn,PreemptPriority, SubPriority)
/*
1. IRQn:用來設(shè)置中斷源,不同的中斷中斷源不一樣,且不可寫錯,即使寫錯了程序也不會報錯,只會導(dǎo)致不響應(yīng)中斷。具體的成員配置可參考stm32f103xe.h 頭文件里面的IRQn_Type 結(jié)構(gòu)體定義,這個結(jié)構(gòu)體包含了所有的中斷源。
2. PreemptionPriority:搶占優(yōu)先級,具體的值要根據(jù)優(yōu)先級分組來確定,。
3. SubPriority:子優(yōu)先級,具體的值要根據(jù)優(yōu)先級分組來確定。
*/
在啟動文件startup_stm32f103xe.s 中我們預(yù)先為每個中斷都寫了一個中斷服務(wù)函數(shù),只是這些中斷函數(shù)都是為空,為的只是初始化中斷向量表。實際的中斷服務(wù)函數(shù)都需要我們重新編寫,為了方便管理我們把中斷服務(wù)函數(shù)統(tǒng)一寫在stm32f1xx_it.c 這個庫文件中。
關(guān)于中斷服務(wù)函數(shù)的函數(shù)名必須跟啟動文件里面預(yù)先設(shè)置的一樣,如果寫錯,系統(tǒng)就在中斷向量表中找不到中斷服務(wù)函數(shù)的入口,直接跳轉(zhuǎn)到啟動文件里面預(yù)先寫好的空函數(shù),并且在里面無限循環(huán),實現(xiàn)不了中斷。
注意:這個是中斷的底層原理部分,由于直接使用這些函數(shù)配置有點復(fù)雜,在HAL庫里面對于中斷函數(shù)的編程進行了簡寫,對于中斷使用會在之后的STM32CubeMx + Keil 里面詳細講述
三、外部中斷(EXTI)
EXTI 簡介
EXTI(External interrupt/event controller)—外部中斷/事件控制器,管理了控制器的20 個中斷/事件線。每個中斷/事件線都對應(yīng)有一個邊沿檢測器,可以實現(xiàn)輸入信號的上升沿檢測和下降沿的檢測。EXTI 可以實現(xiàn)對每個中斷/事件線進行單獨配置,可以單獨配置為中斷或者事件,以及觸發(fā)事件的屬性。
EXTI 功能框圖
EXTI 的功能框圖包含了EXTI 最核心內(nèi)容;
在上圖可以看到很多在信號線上打一個斜杠并標注“20”字樣,這個表示在控制器內(nèi)部類似的信號線路有20 個,這與EXTI 總共有20 個中斷/事件線是吻合的。
EXTI 可分為兩大部分功能,一個是產(chǎn)生中斷,另一個是產(chǎn)生事件,這兩個功能從硬件上就有所不同。
首先看上圖中紅色虛線指示的電路流程。它是一個產(chǎn)生中斷的線路,最終信號流入到NVIC 控制器內(nèi);
編號1 是輸入線,EXTI 控制器有19 個中斷/事件輸入線,這些輸入線可以通過寄存器設(shè)置為任意一個GPIO,也可以是一些外設(shè)的事件。輸入線一般是存在電平變化的信號;
編號2 是一個邊沿檢測電路,它會根據(jù)上升沿觸發(fā)選擇寄存器(EXTI_RTSR) 和下降沿觸發(fā)選擇寄存器(EXTI_FTSR) 對應(yīng)位的設(shè)置來控制信號觸發(fā);邊沿檢測電路以輸入線作為信號輸入端,如果檢測到有邊沿跳變就輸出有效信號1 給編號3 電路,否則輸出無效信號0。而EXTI_RTSR 和EXTI_FTSR 兩個寄存器可以控制器需要檢測哪些類型的電平跳變過程,可以是只有上升沿觸發(fā)、只有下降沿觸發(fā)或者上升沿和下降沿都觸發(fā);
編號3 電路實際就是一個或門電路,它一個輸入來自編號2 電路,另外一個輸入來自軟件中斷事件寄存器(EXTI_SWIER)。EXTI_SWIER 允許我們通過程序控制就可以啟動中斷/事件線,這在某些地方非常有用。我們知道或門的作用就是有1 就為1,所以這兩個輸入隨便一個有有效信號1
就可以輸出1 給編號4 和編號6 電路;
編號4 電路是一個與門電路,它一個輸入是編號3 電路,另外一個輸入來自中斷屏蔽寄存器(EXTI_IMR)。與門電路要求輸入都為1 才輸出1,導(dǎo)致的結(jié)果是如果EXTI_IMR 設(shè)置為0 時,那不管編號3 電路的輸出信號是1 還是0,最終編號4 電路輸出的信號都為0;如果EXTI_IMR設(shè)置為1 時,最終編號4 電路輸出的信號才由編號3 電路的輸出信號決定,這樣我們可以簡單的控制EXTI_IMR 來實現(xiàn)是否產(chǎn)生中斷的目的。編號4 電路輸出的信號會被保存到掛起寄存器(EXTI_PR) 內(nèi),如果確定編號4 電路輸出為1 就會把EXTI_PR 對應(yīng)位置1;
編號5 是將EXTI_PR 寄存器內(nèi)容輸出到NVIC 內(nèi),從而實現(xiàn)系統(tǒng)中斷事件控制。
接下來我們來看看綠色虛線指示的電路流程。它是一個產(chǎn)生事件的線路,最終輸出一個脈沖信號;產(chǎn)生事件線路是在編號3 電路之后與中斷線路有所不同,之前電路都是共用的。
編號6 電路是一個與門,它一個輸入來自編號3 電路,另外一個輸入來自事件屏蔽寄存器(EXTI_EMR)。如果EXTI_EMR 設(shè)置為0 時,那不管編號3 電路的輸出信號是1 還是0,最終編號6 電路輸出的信號都為0;如果EXTI_EMR 設(shè)置為1 時,最終編號6 電路輸出的信號才由編號3 電路的輸出信號決定,這樣我們可以簡單的控制EXTI_EMR 來實現(xiàn)是否產(chǎn)生事件的目的;
編號7 是一個脈沖發(fā)生器電路,當它的輸入端,即編號6 電路的輸出端,是一個有效信號1 時就會產(chǎn)生一個脈沖;如果輸入端是無效信號就不會輸出脈沖;
編號8 是一個脈沖信號,就是產(chǎn)生事件的線路最終的產(chǎn)物,這個脈沖信號可以給其他外設(shè)電路使用,比如定時器TIM、模擬數(shù)字轉(zhuǎn)換器ADC 等等,這樣的脈沖信號一般用來觸發(fā)TIM 或者ADC
開始轉(zhuǎn)換。
產(chǎn)生中斷線路目的是把輸入信號輸入到NVIC,進一步會運行中斷服務(wù)函數(shù),實現(xiàn)功能,這樣是軟件級的。而產(chǎn)生事件線路目的就是傳輸一個脈沖信號給其他外設(shè)使用,并且是電路級別的信號傳輸,屬于硬件級的。
另外,EXTI 是在APB2 總線上。
中斷/事件線
EXTI 有20 個中斷/事件線,每個GPIO 都可以被設(shè)置為輸入線,占用EXTI0 至EXTI15,還有另外七根用于特定的外設(shè)事件,4 根特定外設(shè)中斷/事件線由外設(shè)觸發(fā)。
中斷/事件線 | 輸入源 |
---|---|
EXTI0 | PX0(X 可為A,B,C,D,E,F,G,H,I) |
EXTI1 | PX1(X 可為A,B,C,D,E,F,G,H,I) |
EXTI2 | PX2(X 可為A,B,C,D,E,F,G,H,I) |
EXTI3 | PX3(X 可為A,B,C,D,E,F,G,H,I) |
EXTI4 | PX4(X 可為A,B,C,D,E,F,G,H,I) |
EXTI5 | PX5(X 可為A,B,C,D,E,F,G,H,I) |
EXTI6 | PX6(X 可為A,B,C,D,E,F,G,H,I) |
EXTI7 | PX7(X 可為A,B,C,D,E,F,G,H,I) |
EXTI8 | PX8(X 可為A,B,C,D,E,F,G,H,I) |
EXTI9 | PX9(X 可為A,B,C,D,E,F,G,H,I) |
EXTI10 | PX10(X 可為A,B,C,D,E,F,G,H,I) |
EXTI11 | PX11(X 可為A,B,C,D,E,F,G,H,I) |
EXTI12 | PX12(X 可為A,B,C,D,E,F,G,H,I) |
EXTI13 | PX13(X 可為A,B,C,D,E,F,G,H,I) |
EXTI14 | PX14(X 可為A,B,C,D,E,F,G,H,I) |
EXTI15 | PX15(X 可為A,B,C,D,E,F,G,H,I) |
EXTI16 | PVD 輸出 |
EXTI17 | RTC 鬧鐘事件 |
EXTI18 | USB 喚醒事件 |
EXTI19 | 以太網(wǎng)喚醒事件(只適用互聯(lián)型) |
EXTI0 至EXTI15 用于GPIO,通過編程控制可以實現(xiàn)任意一個GPIO 作為EXTI 的輸入源。由表可知,EXTI0 可以通過AFIO 的外部中斷配置寄存器1(AFIO_EXTICR1) 的EXTI0[3:0] 位選擇配置為PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0 或者PI0,見圖17_2。其他EXTI 線(EXTI中斷/事件線) 使用配置都是類似的。
四、STM32CubeMx 配置
為了試驗外部中斷,這里使用的示例是,使用按鍵控制LED燈的翻轉(zhuǎn);
將按鍵連接的引腳設(shè)置為外部中斷觸發(fā)模式,當按鍵按下,觸發(fā)外部中斷,在中斷處理函數(shù)或者中斷回調(diào)函數(shù)里面進行LED燈的翻轉(zhuǎn)操作;
下面進行STM32CubeMx的配置:
關(guān)于STM32CubeMx的基本配置,可以參考這篇博客:STM32 CubeMx教程 – 基礎(chǔ)知識及配置使用教程
配置RCC時鐘,這里選擇使用外部晶振模式(Crystal/Ceramic Resonator);
配置SYS,debug模式,由于我使用的是ST-Link,所以這里使用Serial Wire;
觀察原理圖,得以得到按鍵的引腳是PC1、PC13,LED燈的引腳是PA8、PD2;
將兩個LED燈(PA8、PD2)配置為輸出模式,
將按鍵(PC1)設(shè)置為外部中斷模式,PC13同理;
配置GPIO界面
先配置LED(PA8),初始電平都可以(反正后期只是實現(xiàn)電平翻轉(zhuǎn)功能)、輸出模式選擇推挽輸出、不使用內(nèi)部電阻、輸出速度選擇最大,PD2同理
然后配置GPIO中斷模式,由于按鍵另一頭接地,所以初始電平要設(shè)置為高電平,這樣按鍵按下時候,就是低電平,就可以被檢測到,所以這里模式選擇使用下降沿觸發(fā)模式、內(nèi)部電阻選擇上拉電阻,初始電平為高電平。PC1、PC13均為這樣設(shè)置。
GPIO mode:
上升沿觸發(fā)檢測的外部中斷模式(External Interrupt Mode with Rising edge trigger detection)
下降沿觸發(fā)檢測的外部中斷模式(External Interrupt Mode with Falling edge trigger detectiort)
上升/下降沿觸發(fā)檢測的外部中斷模式(External Interrupt Mode with Risinq/Falling edge trigger detection)
上升沿觸發(fā)檢測的外部事件模式(External Event Mode with Rising edge trigger detection)
l 下降沿觸發(fā)檢測的外部事件模式(External Event Mode with Falling edge trigger detection)
上升/下降沿觸發(fā)檢測的外部事件模式(External Event Mode with Rising/Falling edge trigger detectiont)
這里中斷觸發(fā)分為外部中斷觸發(fā)和內(nèi)部事件觸發(fā);
中斷和事件的區(qū)別:
中斷是當IO達到中斷條件后會向CPU產(chǎn)生中斷請求
事件是事先設(shè)置好的任務(wù),當單片機達到要求將通過硬件的方式處理事先設(shè)置好的任務(wù),而不向CPU請求中斷,比如DMA、AD轉(zhuǎn)換等
從外部激勵信號來看,中斷和事件的產(chǎn)生源都可以是一樣的,之所以分成2個部分,由于中斷是需要CPU參與的,需要軟件的中斷服務(wù)函數(shù)才能完成中斷后產(chǎn)生的結(jié)果。但是事件,是靠脈沖發(fā)生器產(chǎn)生一個脈沖,進而由硬件自動完成這個事件產(chǎn)生的結(jié)果,當然相應(yīng)的聯(lián)動部件需要先設(shè)置好,比如引起DMA操作,AD轉(zhuǎn)換等。
GPIO Pull-up/Pull-down :
No pull-up and no pull-down : 不使用內(nèi)部電阻
Pull-up :上拉電阻,初始化為高電平
Pull-down : 下拉電阻,初始化為低電平
配置NVIC
使能GPIO中斷;
可以直接點擊GPIO界面內(nèi)的NVIC來進入GPIO的中斷控制器或是System Core下面的NVIC,進入全局中斷控制器;
Priority Group:
STM32F103系列使用了4位中斷優(yōu)先級設(shè)置,一共可實現(xiàn)16個可編程的優(yōu)先等級。優(yōu)先級的配置需要使用NVIC,即嵌套向量中斷控制器。NVIC控制著整個芯片中斷相關(guān)的功能,跟內(nèi)核緊密耦合,是內(nèi)核里面的一個外設(shè)。
在具體配置時一般只用ISER、ICER和IP這3個寄存器,其中ISER用于使能中斷,ICER用于清除中斷,IP用于設(shè)置中斷優(yōu)先級。
用于表達優(yōu)先級分組的寄存器NVIC_IPRTx寬度為8位,但是f103只使用其中高4位。
這4位又被分為搶占優(yōu)先級和次優(yōu)先級兩組。多個中斷同時響應(yīng)時,搶占優(yōu)先級高的就會先于搶占優(yōu)先級低的執(zhí)行;如果搶占優(yōu)先級相同,就比較子優(yōu)先級;如果子優(yōu)先級也相同,就比較它們的硬件中斷編號,編號越小優(yōu)先級越高。
優(yōu)先級的分組由內(nèi)核外設(shè)SCB的應(yīng)用程序中斷及復(fù)位控制寄存器AIRCR的PRIGROUP[10:8]位決定。實際配置時程序中通過調(diào)用庫函數(shù)NVIC_PriorityGroupConfig()實現(xiàn)。
優(yōu)先級分組 | 主優(yōu)先級 | 次優(yōu)先級 | 描述 |
---|---|---|---|
NVIC_PriorityGroup_0 | 0 | 0~15 | 主-0bit,子-4bit |
NVIC_PriorityGroup_1 | 0~1 | 0~7 | 主-1bit,子-3bit |
NVIC_PriorityGroup_2 | 0~3 | 0~3 | 主-2bit,子-2bit |
NVIC_PriorityGroup_3 | 0~7 | 0~1 | 主-3bit,子-1bit |
NVIC_PriorityGroup_4 | 0~15 | 0 | 主-4bit,子-0bit |
Priority Group共有5個選項,與上面表格中的配置相對應(yīng),該選項配置完畢后,再在NVIC Interrupt Table中對各中斷優(yōu)先級分別進行配置。
0 bits for pre-emption priority 4 bits for subpriority
1 bits for pre-emption priority 3 bits for subpriority
2 bits for pre-emption priority 2 bits for subpriority
3 bits for pre-emption priority 1 bits for subpriority
4 bits for pre-emption priority 0 bits for subpriority
NVIC Interrupt Table:
F103系列在內(nèi)核上搭建了一個異常響應(yīng)系統(tǒng),支持10個系統(tǒng)異常和外部中斷,這部分一般不需要開發(fā)者進行配置。
Non maskable interrupt: 不可屏蔽中斷。RCC時鐘安全系統(tǒng)(CSS)連接到NMI向量
Hard fault interrupt: 所有類型的錯誤
Memory management fault: 存儲器管理
Prefetch fault, memory access fault: 預(yù)取指失敗,存儲器訪問失敗
Undefined instruction or illegal state: 未定義的指令或非法狀態(tài)
System service call via SWI instruction: 通過SWI指令調(diào)用的系統(tǒng)服務(wù)
Debug monitor: 調(diào)試監(jiān)控器
Pendable request for system service: 可掛起的系統(tǒng)服務(wù)
Time base: System tick timer: 系統(tǒng)嘀嗒定時器
EXIT line1 interrupt : 外部中斷
配置時鐘樹
五、代碼分析
實現(xiàn)按鍵觸發(fā)中斷,在中斷里面進行LED燈的翻轉(zhuǎn)操作。
生成代碼以后,進入程序;
首先,我們在左邊stm32f1xx_it.c 里面找到中斷處理函數(shù);
由于我們使用了兩個按鍵,所以會顯示倆個外部觸發(fā)源的中斷處理函數(shù),但是這兩個函數(shù)里面都引用了 中斷處理函數(shù)(HAL_GPIO_EXTI_IRQHandler());
// void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
// 功能: 外部中斷服務(wù)函數(shù),清除中斷標志位
/**
* @brief This function handles EXTI interrupt request.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
在這個函數(shù)里面主要執(zhí)行兩個操作,一個是清除中斷標志位,另一個是繼續(xù)調(diào)用了一個函數(shù),中斷回調(diào)函數(shù)(HAL_GPIO_EXTI_Callback(GPIO_Pin);)
// void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
// 功能: 中斷回調(diào)函數(shù),可以理解為中斷函數(shù)具體要響應(yīng)的動作。
/**
* @brief EXTI line detection callbacks.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
}
__weak 是一個弱化標識,帶有這個的函數(shù)就是一個弱化函數(shù),就是你可以在其他地方寫一個名稱和參數(shù)都一模一樣的函數(shù),編譯器就會忽略這一個函數(shù),而去執(zhí)行你寫的那個函數(shù);也就是重寫弱化函數(shù);
而UNUSED(GPIO_Pin) ,這就是一個防報錯的定義,當傳進來的GPIO端口號沒有做任何處理的時候,編譯器也不會報出警告。
這個回調(diào)函數(shù)還有一點非常便利的地方,就是當同時有多個中斷使能的時候,STM32CubeMX會自動地將幾個中斷的服務(wù)函數(shù)規(guī)整到一起并調(diào)用一個回調(diào)函數(shù),也就是無論幾個中斷,我們只需要重寫一個回調(diào)函數(shù)并判斷傳進來的端口號即可,還是非常方便的。
中斷執(zhí)行操作為先執(zhí)行中斷處理函數(shù),然后中斷處理函數(shù)調(diào)用中斷回調(diào)函數(shù);
意味著又多個中斷源時候,可以選擇把處理操作寫在中斷處理函數(shù)里面,也可以寫在中斷回調(diào)函數(shù)里面(這里會提供兩種情況的寫法)
如果我們想在函數(shù)里面加入延時程序(HAL_Delay),整個函數(shù)就會陷入卡死的狀況,具體分析在STM32 HAL庫 中斷處理中使用延時函數(shù)(HAL_Delay)
這個問題也有兩個解放辦法,一個是自己重寫一個延時函數(shù),另一個是更改系統(tǒng)滴答定時和外部中斷的優(yōu)先級;
這里采用修改優(yōu)先級的辦法
可以將處理寫在中斷處理函數(shù)(void EXTI1_IRQHandler(void))里面 + 中斷回調(diào)函數(shù)(void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin))里面,中斷處理函數(shù)處理針對于某一種外設(shè)要進行的操作,中斷回調(diào)函數(shù)里面處理所有中斷都會進行操作;
/**
* @brief This function handles EXTI line1 interrupt.
*/
void EXTI1_IRQHandler(void)
{
/* USER CODE BEGIN EXTI1_IRQn 0 */
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
/* USER CODE END EXTI1_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
/* USER CODE BEGIN EXTI1_IRQn 1 */
/* USER CODE END EXTI1_IRQn 1 */
}
/**
* @brief This function handles EXTI line[15:10] interrupts.
*/
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */
}
/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
}
/* USER CODE END 1 */
也可以將所有操作都寫在中斷回調(diào)函數(shù)里面,為了區(qū)分不同外設(shè)觸發(fā)的不同操作,可以根據(jù)觸發(fā)中斷不同IO口進行判斷文章來源:http://www.zghlxwxcb.cn/news/detail-715650.html
/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==GPIO_PIN_1)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_1)==GPIO_PIN_RESET)
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
}
}
if(GPIO_Pin==GPIO_PIN_13)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET)
{
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
}
}
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
}
/* USER CODE END 1 */
附錄
本節(jié)中的程序代碼:STM32 HAL庫 外部中斷文章來源地址http://www.zghlxwxcb.cn/news/detail-715650.html
到了這里,關(guān)于STM32 HAL庫 STM32CubeMx -- 外部中斷的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!