一、STM32外部中斷
1、STM32外部中斷結構圖
如上圖所示:主要包括四個環節,GPIO、AFIO、EXTI、NVIC。以STM32F103VE(100腳)為例說明硬件模塊的數量:
GPIO: 80個
AFIO選擇通道: 16個
外部中斷線EXTI: 16 + 3 = 19個
NVIC : 1個
2、EXTI控制器結構圖
① 功能
- 中斷 申請中斷
- 事件 當事件發生的時候,將產生事件脈沖,可以用來喚醒內核
② 功能詳細說明
如果要產生中斷,必須事先配置好並使能中斷線。根據需要的邊沿檢測設置2個觸發寄存器,同時在中斷屏蔽寄存器的相應位寫’1’ 允許中斷請求。當外部中斷線上發生了需要的邊沿時,將產生一個中斷請求,對應的掛起位也隨之被置’1’ 。在掛起寄存器的對應位寫’1’,可以清除該中斷請求。
如果要為產生事件,必須事先配置好並使能事件線。根據需要的邊沿檢測通過設置2個觸發寄存器,同時在事件屏蔽寄存器的相應位寫’1’允許事件請求。當事件線上發生了需要的邊沿時,將產生一個事件請求脈沖,對應的掛起位不被置’1’。
通過在軟件中斷/事件寄存器寫’1’,也可以通過軟件產生中斷/事件請求。
3、STM32外部中斷軟件配置步驟
① 配置NVIC控制器
② 使能GPIO和AFIO時鍾
③ 配置GPIO (復用功能的輸入輸出配置)
④ 配置AFIO (選定要配置為EXTI的I/O口線)
⑤ EXTI控制器的配置
配置寄存器就好像是我們在更改STM32的硬件電路,讓它們處於一種新的工作狀態--外部中斷模式。
二、STM32外部中斷軟件配置細節
以PA0引腳的外部中斷為例進行介紹:
1、配置NVIC控制器
PA0引腳對應的外部中斷線是EXTI_Line0,而EXTI_Line0對應的中斷源是EXTI0_IRQn。
調用NVIC_Init函數設置中斷源EXTI0_IRQn的搶占優先級和響應優先級,並且使能中斷。
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
NVIC_IRQChannel
定義配置的對象,是哪一個中斷源,它不能使用 “EXTI0_IRQn|EXTI0_IRQn”方式調用一次NVIC_Init函數配置多個中斷源
2、使能GPIO和AFIO時鍾
GPIO用作EXTI外部中斷或使用重映射功能的時候,不僅需要開啟外設時鍾RCC_APB2Periph_GPIOA,還需要開啟AFIO時鍾RCC_APB2Periph_AFIO。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
3、配置GPIO(復用功能的輸入輸出配置)
配置PA0為上拉輸入。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉輸入 GPIO_Init(GPIOA, &GPIO_InitStructure);
4、配置AFIO(選定要配置為EXTI的I/O口線)
PA0引腳對應的外部中斷線是EXTI_Line0,但是PB0~PG0對應的也都是這個外部中斷線。所以,需要通過多路選擇器確定到底是哪一個引腳映射到EXTI_Line0。
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
5、EXTI控制器的配置
配置外部中斷線EXTI_Line0的工作模式,為中斷模式,下降沿觸發中斷,並且使能外部中斷線EXTI_Line0。
EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿中斷 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure);
三、中斷配置過程以及STM中斷結構解惑
1、為什么NVIC有使能中斷,EXTI控制器還有一個使能控制呢?
NVIC控制器是在ARM公司設計的Cortex-M3內核中,EXTI控制器是ST公司自己設計的外設。
在NVIC控制器中,有“Interrupt set-enable register&Interrupt clear-enable register”,它們來控制是否使能中斷源EXTI0_IRQn。
而EXTI控制器中,有“Interrupt mask register”來控制中斷線EXTI_Line0的中斷標志位是否傳送給NVIC控制器。
2、為什么NVIC控制器使能中斷源的寄存器要兩種?
中斷的使能與除能分別使用各自的寄存器來控制——這與傳統的,使用單一比特的兩個狀態來表達使能與除能是不同的。CM3 中可以有 240 對使能位/除能位(SETENA 位/CLRENA 位),每個中斷擁有一對。這 240 個對子分布在 8 對32位寄存器中(最后一對沒有用完)。欲使能一個中斷,我們需要寫1 到對應SETENA的位中;欲除能一個中斷,你需要寫 1 到對應的CLRENA位中。如果往它們中寫0,則不會有任何效果。
寫零無效是個很關鍵的設計理念:通過這種方式,使能/除能中斷時只需把“當事位”寫成 1,其它的位可以全部為零。再也不用像以前那樣,害怕有些位被寫入0 而破壞其對應的中斷設置(反正現在寫 0 沒有效果了),從而實現每個中斷都可以自顧地設置,而互不侵犯——只需單一的寫指令,不再需要讀-改-寫三步曲。(摘自《CM3權威指南》)
3、在清除中斷標志位的時候,為什么只需要清除EXTI的中斷標志位就行,而不需要清除NVIC的懸起寄存器SETPEND?
有些程序員在中斷服務程序結束的位置清除中斷源的時候,還調用了NVIC_ClearPendingIRQ()函數清除中斷源的懸起標志位,其實是不需要的。
如果中斷發生時,正在處理同級或高優先級異常,或者被掩蔽,則中斷不能立即得到響應。此時中斷被懸起。中斷的懸起狀態可以通過“中斷設置懸起寄存器(SETPEND) ”和“中斷懸起清除寄存器(CLRPEND) ”來讀取,還可以寫它們來手工懸起中斷。(這段文字摘自《CM3權威指南》)
由此我們可以得出結論:如果中斷服務程序得到執行(中斷得到響應),此時中斷就不會被懸起。所以我們的中斷服務程序不需要清除“懸起標志位”。
有人可能還會這么想:倘若一個中斷服務程序因為處理器正在執行同級中斷服務程序而不能被執行,中斷被懸起,那么當輪到這個中斷得到響應后,這個中斷服務程序需要清楚“懸起標志位”嗎?
我認為還是不需要,因為中斷標志位的懸起和解懸,硬件都是可以自動控制的。也就是說,當懸起的中斷被響應的時候,“懸起標志位”自動被硬件予以清除,不必手動清除。
參考資料:《STM32 庫開發實戰指南》
《CM3權威指南》
《STM32F10X芯片手冊》
《STM32F10xxx Cortex-M3 programming manual.pdf》