1. 程式人生 > 實用技巧 >STM32之外部中斷

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