STM32之外部中斷
本文介紹如何使用STM32標準外設庫設定並使用中斷,微控制器中斷系統的目的是為了讓MCU對內部或外部的突發事件及時地作出響應,並執行相應的程式,最簡單直觀的為外部中斷,可以用作檢測按鍵的按下和彈起,本例程設定GPIOA的Pin0作為外部中斷。
本文適合對微控制器及C語言有一定基礎的開發人員閱讀,MCU使用STM32F103VE系列。
中斷分為兩部分,初始化和處理。
1. 初始化
初始化分為三步,包括設定優先順序分組、結構體初始化和初始化庫函式呼叫。
1.1.1. 優先順序分組
共5組,一般選擇NVIC_PriorityGroup_2,即2位搶佔優先順序、2位子優先順序。
呼叫庫函式
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
1.1.2. 結構體初始化
typedef struct { uint8_t NVIC_IRQChannel; uint8_t NVIC_IRQChannelPreemptionPriority; uint8_t NVIC_IRQChannelSubPriority; FunctionalState NVIC_IRQChannelCmd; } NVIC_InitTypeDef;
- 中斷源:不同的中斷設定不同的中斷源,具體的成員配置可參考 stm32f10x.h 標頭檔案裡面的 IRQn_Type 結構體定義,這個結構體包含了所有的中斷源。
/* 配置中斷源:外部中斷0 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
- 搶佔優先順序,根據中斷的優先順序程度設定不同的優先順序,優先順序高的中斷設定高的優先順序,數值越小優先順序越高,優先順序分組為NVIC_PriorityGroup_2時,取值範圍為0-3,優先順序從高到低分別為0、1、2、3。
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
- 子優先順序,根據中斷的優先順序程度設定不同的優先順序,優先順序高的中斷設定高的優先順序,數值越小優先順序越高,優先順序分組為NVIC_PriorityGroup_2時,取值範圍為0-3,優先順序從高到低分別為0、1、2、3。
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
- 使能
直接設定為ENABLE即可。
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
1.1.3. 庫函式
呼叫庫函式:
NVIC_Init(&NVIC_InitStructure);
2. 處理
2.1. 中斷服務函式
可以把中斷服務函式統一寫在 stm32f10x_it.c 這個庫檔案中,也可以寫在其他檔案中,中斷服務函式的函式名必須跟啟動檔案(startup_stm32f10x_hd.s)裡面預先設定的一樣,比如外部中斷0的中斷服務函式名稱為EXTI0_IRQHandler ()。
void EXTI0_IRQHandler(void)
2.2. 中斷處理
中斷服務函式進入之後首先判定中斷標誌位是否置位,如果未置位則直接退出,如果已置位則執行相應的處理,退出中斷之前需要清除中斷標誌位。有些操作如讀串列埠資料會自動清除中斷讀資料暫存器非空標誌位,因此無需手動清除。
外部中斷分為兩部分,初始化和處理。
1. 外部中斷初始化專案
分三部分:GPIO、通用中斷、EXTI(External interrupt/event controller)
1.1. GPIO:時鐘、埠、引腳、輸入模式
- 時鐘:需要同時使能GPIO時鐘
- 引腳:需要選擇指定的引腳
- 輸入模式:一般選擇浮空輸入
1.2. 通用中斷:優先順序分組、中斷源、優先順序、使能
- 優先順序分組
- 中斷源:選擇指定的EXTI中斷源
- 優先順序:
- 使能:呼叫庫函式即可
1.3. EXTI:時鐘、訊號源、線選、EXTI模式、觸發型別、使能
結構體:
typedef struct { uint32_t EXTI_Line; EXTIMode_TypeDef EXTI_Mode; EXTITrigger_TypeDef EXTI_Trigger; FunctionalState EXTI_LineCmd; }EXTI_InitTypeDef;
- 時鐘:需要使能AFIO時鐘
/*開啟AFIO時鐘*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
- 訊號源:需要配置指定引腳的訊號源
直接呼叫庫函式GPIO_EXTILineConfig()即可
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
- 線選:初始化需要配置的線
/* EXTI中斷線選擇Line0 */ EXTI_InitStructure.EXTI_Line = EXTI_Line0;
- EXTI模式:一般選擇中斷模式
/* EXTI為中斷模式 */ EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
- 觸發型別:可選上升沿或者下降沿
/* 上升沿中斷 */ EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
- 使能:
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
- 初始化庫函式
EXTI_Init(&EXTI_InitStructure);
2. 處理
2.1. 中斷服務函式
外部中斷0的中斷服務函式名稱為EXTI0_IRQHandler ()。
void EXTI0_IRQHandler(void);
2.2. 中斷處理
中斷服務函式中呼叫EXTI_GetITStatus()函式判定中斷標誌位狀態以確定中斷是否發生,呼叫EXTI_ClearITPendingBit()函式清除中斷標誌位。
if(EXTI_GetITStatus(EXTI_Line0) != RESET) { EXTI_ClearITPendingBit(EXTI_Line0); }
完整程式碼(僅自己編寫的部分)
1 void GPIO_Config(void) 2 { 3 /*定義一個GPIO_InitTypeDef型別的結構體*/ 4 GPIO_InitTypeDef GPIO_InitStructure; 5 6 /*開啟指定埠的GPIO外設時鐘*/ 7 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 8 9 /*選擇要控制的GPIO引腳*/ 10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 11 12 /*設定引腳模式為浮空輸入*/ 13 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 14 15 /*呼叫庫函式,初始化GPIO*/ 16 GPIO_Init(GPIOA, &GPIO_InitStructure); 17 } 18 19 void NVIC_Config(void) 20 { 21 NVIC_InitTypeDef NVIC_InitStructure; 22 23 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 24 25 /* 配置中斷源:外部中斷0 */ 26 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; 27 /* 配置搶佔優先順序 */ 28 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 29 /* 配置子優先順序 */ 30 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 31 /* 使能中斷通道 */ 32 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 33 34 NVIC_Init(&NVIC_InitStructure); 35 } 36 37 void EXTI0_Config(void) 38 { 39 EXTI_InitTypeDef EXTI_InitStructure; 40 41 /*開啟AFIO時鐘*/ 42 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 43 44 /*配置訊號源*/ 45 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); 46 47 /* EXTI中斷線選擇Line0 */ 48 EXTI_InitStructure.EXTI_Line = EXTI_Line0; 49 /* EXTI為中斷模式 */ 50 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 51 /* 上升沿中斷 */ 52 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; 53 /* 使能中斷 */ 54 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 55 56 EXTI_Init(&EXTI_InitStructure); 57 } 58 59 void EXTI0_init(void) 60 { 61 GPIO_Config(); 62 NVIC_Config(); 63 EXTI0_Config(); 64 } 65 66 //中斷服務函式,待完善 67 void EXTI0_IRQHandler(void) 68 { 69 if(EXTI_GetITStatus(EXTI_Line0) != RESET) 70 { 71 EXTI_ClearITPendingBit(EXTI_Line0); 72 } 73 } 74 75 76 int main(void) 77 { 78 // 外部中斷0中斷初始化 79 EXTI0_init(); 80 81 while (1) 82 { 83 } 84 }
模擬結果
程式編譯成功後,點選開始模擬,開啟選單欄Peripherals→General Purpose I/O→GPIOA,如果中斷觸發型別為上升沿,那麼觸發條件為指定埠先低電平,再設定為高電平;反之如果觸發型別為下降沿,那麼觸發條件為指定埠先高電平,再設定為低電平。
可在中斷服務函式內發生中斷後設定斷點,程式開始執行,當滿足中斷觸發條件後,程式在斷點處暫停,表明中斷成功。
原始碼下載:(不包括工程檔案和庫檔案)
https://files.cnblogs.com/files/greatpumpkin/EXTI_int.zip