STM32的中斷系統
STM32具有十分強大的中斷系統,將中斷分為了兩個類型:核心例外狀況和外部中斷。並將所有中斷通過一個表編排起來,下面是stm32中斷向量表的部分內容:
-3到6這個地區被標黑了,這個地區就是核心例外狀況。核心例外狀況不能夠被打斷,不能被設定優先權(也就是說優先順序是淩駕於外部中斷之上的)。常見的核心例外狀況有以下幾種:複位(reset),不可屏蔽中斷(NMI),硬性錯誤(Hardfault),其他的也可以在表上找到。
從第7個開始,後面所有的中斷都是外部中斷。外部中斷是我們必須學習掌握的知識,包含線中斷,定時器中斷,IIC,SPI等所有的外設中斷,可配置優先順序。外部中斷的優先順序分為兩種:搶佔優先順序和響應優先順序。
什麼是搶佔優先順序?
搶佔優先順序比較霸道,一言不和就插隊。搶佔優先順序高的,能夠打斷優先順序低的任務,等優先順序較高的任務執行完畢後,再回來繼續執行之前的任務。所以當存在多個搶佔優先順序不同的任務時,很有可能會產生任務的嵌套。
什麼是響應優先順序?
響應優先順序則稍微謙遜些,比較有禮貌。響應優先順序又被稱為次優先順序,若兩個任務的搶佔式優先順序一樣,那麼響應優先順序較高的任務則先執行,且在執行的同時不能被下一個響應優先順序更高的任務打斷,所以我說它比較有有禮貌。。
中斷控制器(NVIC)
因為stm32的中斷系統比較複雜,所以在核心中有一個專門管理中斷的控制器:NVIC.
NVIC負責除了SYSTICK之外的所有中斷的控制,十分重要!
在標準庫中,提供了一套通過NVIC來控制中斷的API,我們首先來看NVIC_Init()函數,這套函數首先要定義並填充一個結構體:NVIC_InitTypeDef 該結構體的定義如下:
NVIC_IRQChannel 需要配置的中斷向量
NVIC_IRQChannelCmd 使能或者關閉相應中斷向量的中斷響應
NVIC_IRQChannelPreemptionPriority 配置相應中斷向量的搶佔優先順序
NVIC_IRQChannelSubPriority 配置相應中斷的響應優先順序
結構體的四個成員都比較好理解,這裡就不再累述了。
不過要注意一點的是,NVIC只可以配置16種中斷向量的優先順序,其搶佔優先順序和響應優先順序都用一個4位的數字來決定。在庫函數中,將其分為了5中不同的分配方式:
第0組:所有的4位都有來表示響應優先順序,能夠配置16種不同的響應優先順序。中斷優先順序則都相同。
第1組:最高一位用來配置搶佔優先順序,剩餘三位用來表示響應優先順序。那麼就有兩種不同的搶佔優先順序(0和1)和8種不同的響應優先順序(0~7)。
第2組:高兩位用來配置搶佔優先順序,低位用來配置響應優先順序。那麼兩種優先順序就各有4種。
第3組:高三位用來配置搶佔優先順序,低位用來配置響應優先順序。有8種搶佔優先順序和2種相應優先順序。
第4組:所有位都用來配置搶佔優先順序,即有16種搶佔優先順序,沒有響應屬性。
這5種不同的分配方式,根據項目的實際需求來配置。
配置的API如下:
NVIC_PriorityGroupConfig();
其中括弧內可以輸入以下一個參數,代表不同的分配方式:
NVIC_PriorityGroup_0
NVIC_PriorityGroup_1
NVIC_PriorityGroup_2
NVIC_PriorityGroup_3
NVIC_PriorityGroup_4
EXTI外部中斷
STM32的所有GPIO都引入到了EXTI外部中斷線上,也就是說,所有的IO口經過配置後都能夠觸發中斷。就是GPIO和EXTI的串連方式:
從我們可以看出,一共有16個中斷線: EXTI0到EXTI15.
每個中斷線都對應了從PAx到PGx一共7個GPIO。也就是說,在同一時刻每個中斷線只能相應一個GPIO連接埠的中斷,不能夠同時相應所有連接埠的中斷事件,但是可以分時複用,在程式執行過程中,這個不需要我們太多的去關心。我們關心最多的是中斷觸發的方式:
在EXTI中,有三種觸發中斷的方式:上升沿觸發,下降沿觸發,雙邊沿觸發。根據不同的電路,我們選擇不同的觸發方式,以確保中斷能夠被正常觸發。
執行個體
為了便於理解,這裡我們將中斷配置代碼貼上來。
void NVIC_Configuration(void){ NVIC_InitTypeDef NVIC_InitStructure; /* 配置NVIC為優先順序組1 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* 配置中斷源:按鍵1 */ NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //配置為EXTI0通道 /* 配置搶佔優先順序 */ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 配置子優先順序 */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 使能中斷通道 */ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //將上述配置參數傳入中斷初始化函數}
除了中斷線的配置,我們還要配置對應引腳
void EXTI_Key_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; /*開啟按鍵GPIO口的時鐘*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE); /* 配置 NVIC 中斷*/ NVIC_Configuration();/*--------------------------KEY1配置-----------------------------*/ /* 選擇按鍵用到的GPIO */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /* 配置為浮空輸入 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure); /* 選擇EXTI的訊號源 */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; /* EXTI為中斷模式 */ EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; /* 上升沿中斷 */ EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; /* 使能中斷 */ EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure);}
至此,中斷的配置完畢。相信你已經看出來,上述代碼是將PA0配置為上升沿中斷。不過,現在只能夠說該中斷已經配置完畢,但我們還不能使用它。我們還缺少一個中斷的執行函數。
當中斷被觸發後,程式要馬上跳轉到中斷函數去執行中斷操作,這個函數在工程建立時預設時沒有的。需要你自己去添加。而且需要注意的是,中斷函數的名稱必須是由標準庫提供的,否則無法識別。
我們開啟startup_stm32f10x_hd.s這個檔案,在裡面能找到這麼一段代碼:
; External Interrupts DCD WWDG_IRQHandler ; Window Watchdog DCD PVD_IRQHandler ; PVD through EXTI Line detect DCD TAMPER_IRQHandler ; Tamper DCD RTC_IRQHandler ; RTC DCD FLASH_IRQHandler ; Flash DCD RCC_IRQHandler ; RCC DCD EXTI0_IRQHandler ; EXTI Line 0 DCD EXTI1_IRQHandler ; EXTI Line 1 DCD EXTI2_IRQHandler ; EXTI Line 2 DCD EXTI3_IRQHandler ; EXTI Line 3 ... ... ...
不難看出,EXTI0_IRQHandler 就是中斷線0的中斷函數,所以,我們把這個函數添加到工程中即可。最好添加到stm32f10x_it.c 這個檔案中,方便管理。
可以在這個函數中添加你想要的功能,代碼如下:
void EXTI0_IRQHandler(void){ //確保是否產生了EXTI Line中斷 if(EXTI_GetITStatus(EXTI_Line0) != RESET) { /******/ //LED閃爍相關代碼 /******/ //清除中斷標誌位 EXTI_ClearITPendingBit(EXTI_Line0); } }
至此,整個中斷的流程梳理完畢,如果有什麼紕漏的話,歡迎探討!