外部中斷/事件控制器(EXTI)管理了控制器的 23 個中斷/事件線。每個中斷/事件線都對
應有一個邊沿檢測器,可以實現輸入信號的上升沿檢測和下降沿的檢測。 EXTI 可以實現對
每個中斷/事件線進行單獨配置,可以單獨配置為中斷或者事件,以及觸發事件的屬性。
EXTI功能框圖
EXTI 的功能框圖包含了 EXTI 最核心內容,掌握了功能框圖,對 EXTI 就有一個整體
的把握,在編程時就思路就非常清晰。 EXTI 功能框圖見圖 17-1。
在圖 17-1 可以看到很多在信號線上打一個斜杠並標注“ 23”字樣,這個表示在控制器
內部類似的信號線路有 23 個,這與 EXTI 總共有 23 個中斷/事件線是吻合的。所以我們只
要明白其中一個的原理,那其他 22 個線路原理也就知道了。
EXTI 可分為兩大部分功能,一個是產生中斷,另一個是產生事件,這兩個功能從硬件
上就有所不同。
產生中斷線路目的是把輸入信號輸入到 NVIC,進一步會運行中斷服務函數,實現功
能,這樣是軟件級的。而產生事件線路目的就是傳輸一個脈沖信號給其他外設使用,並且
是電路級別的信號傳輸,屬於硬件級的。
另外, EXTI 是在 APB2 總線上的,在編程時候需要注意到這點。
EXTI 有 23 個中斷/事件線,每個 GPIO 都可以被設置為輸入線,占用 EXTI0 至
EXTI15,還有另外七根用於特定的外設事件,見表 17-1。
七根特定外設中斷/事件線由外設觸發,具體用法參考《 STM32F4xx 中文參考手冊》
中對外設的具體說明。
EXTI0 可以通過 SYSCFG 外部中斷配置寄存器
1(SYSCFG_EXTICR1)的 EXTI0[3:0]位選擇配置為 PA0、 PB0、 PC0、 PD0、 PE0、 PF0、
PG0、 PH0 或者 PI0,
EXTI初始化結構體詳解
標准庫函數對每個外設都建立了一個初始化結構體,比如 EXTI_InitTypeDef,結構體
成員用於設置外設工作參數,並由外設初始化配置函數,比如 EXTI_Init()調用,這些設定
參數將會設置外設相應的寄存器,達到配置外設工作環境的目的。
初始化結構體和初始化庫函數配合使用是標准庫精髓所在,理解了初始化結構體每個
成員意義基本上就可以對該外設運用自如了。初始化結構體定義在 stm32f4xx_exti.h 文件中,
初始化庫函數定義在 stm32f4xx_exti.c 文件中,編程時我們可以結合這兩個文件內注釋使用。
下面說一下編程框架:
使用 GPIO 之前必須開啟 GPIO 端口的時鍾;用到 EXTI 必須開啟 SYSCFG 時鍾。
作為中斷/時間輸入線把 GPIO 配置為輸入模式,這里不使用上拉或下拉,由外部電路
完全決定引腳的狀態。
SYSCFG_EXTILineConfig 函數用來指定中斷/事件線的輸入源,它實際是設定
SYSCFG 外部中斷配置寄存器的值,該函數接收兩個參數,第一個參數指定 GPIO 端口源,
第二個參數為選擇對應 GPIO 引腳源編號。
1 int main(void) 2 { 3 /* LED 端口初始化 */ 4 LED_GPIO_Config(); 5 6 /* 初始化EXTI中斷,按下按鍵會觸發中斷, 7 * 觸發中斷會進入stm32f4xx_it.c文件中的函數 8 * KEY1_IRQHandler和KEY2_IRQHandler,處理中斷,反轉LED燈。 9 */ 10 EXTI_Key_Config(); 11 12 /* 等待中斷,由於使用中斷方式,CPU不用輪詢按鍵 */ 13 while(1) 14 { 15 } 16 }
主函數結構,先初始化硬件LED,用於按鍵中斷控制的效果展示。
然后是 EXTI_Key_Config(); 需要留意一點的函數或者配置加以紅色標注:
1 /** 2 * @brief 配置嵌套向量中斷控制器NVIC 3 * @param 無 4 * @retval 無 5 */ 6 static void NVIC_Configuration(void) 7 { 8 NVIC_InitTypeDef NVIC_InitStructure; 9 10 /* 配置NVIC為優先級組1 */ 11 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 12 13 /* 配置中斷源:按鍵1 */ 14 NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ; 15 /* 配置搶占優先級:1 */ 16 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 17 /* 配置子優先級:1 */ 18 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 19 /* 使能中斷通道 */ 20 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 21 NVIC_Init(&NVIC_InitStructure); 22 23 /* 配置中斷源:按鍵2,其他使用上面相關配置 */ 24 NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ; 25 NVIC_Init(&NVIC_InitStructure); 26 } 27 28 /** 29 * @brief 配置 PA0 為線中斷口,並設置中斷優先級 30 * @param 無 31 * @retval 無 32 */ 33 void EXTI_Key_Config(void) 34 { 35 GPIO_InitTypeDef GPIO_InitStructure; 36 EXTI_InitTypeDef EXTI_InitStructure; 37 38 /*開啟按鍵GPIO口的時鍾*/ 39 RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK|KEY2_INT_GPIO_CLK ,ENABLE); 40 41 /* 使能 SYSCFG 時鍾 ,使用GPIO外部中斷時必須使能SYSCFG時鍾*/ 42 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); 43 44 /* 配置 NVIC */ 45 NVIC_Configuration(); 46 47 /* 選擇按鍵1的引腳 */ 48 GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN; 49 /* 設置引腳為輸入模式 */ 50 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; 51 /* 設置引腳不上拉也不下拉 */ 52 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 53 /* 使用上面的結構體初始化按鍵 */ 54 GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure); 55 56 /* 連接 EXTI 中斷源 到key1引腳 */ 57 SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,KEY1_INT_EXTI_PINSOURCE); 58 59 /* 選擇 EXTI 中斷源 */ 60 EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE; 61 /* 中斷模式 */ 62 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 63 /* 下降沿觸發 */ 64 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; 65 /* 使能中斷/事件線 */ 66 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 67 EXTI_Init(&EXTI_InitStructure); 68 69 /* 選擇按鍵2的引腳 */ 70 GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN; 71 /* 其他配置與上面相同 */ 72 GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure); 73 74 /* 連接 EXTI 中斷源 到key2 引腳 */ 75 SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE,KEY2_INT_EXTI_PINSOURCE); 76 77 /* 選擇 EXTI 中斷源 */ 78 EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE; 79 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 80 /* 上升沿觸發 */ 81 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 82 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 83 EXTI_Init(&EXTI_InitStructure); 84 }
在stm32f4xx_it.c中編寫中斷服務函數,其中中斷服務函數名要對應啟動文件的中斷向量表,為了可讀性可以用宏給中斷向量表取一個適合中斷目的的名字。
還有一個需要注意的:需要使能 SYSCFG 時鍾 ,使用GPIO外部中斷時必須使能SYSCFG時鍾。
上面是固件庫的函數定義內部,這個是固件庫編程的套路,都是這樣定義某個功能的結構體,然后初始化這個結果體,最后調用一個init函數完成。
固件庫這樣的套路明白之后,編寫應用程序會輕松愉快地多。至於固件庫內部到底是如何實現的,在達到高手水平了再去,畢竟做項目是以速度和質量說話的,固件庫肯定快於寄存器版的,但是想自己寫出庫函數,又不得不去學習寄存器編程,所以都是相輔相成的,先學好固件庫,然后學寄存器版,然后達到自己可以編寫庫,就達到學習目的了。