EXTI 簡介
????????EXTI(External interrupt/event controller)—外部中斷/事件控制器,管理了控制器的 20 個中斷/事件線。每個輸入線可以獨立地配置輸入類型(脈沖 或掛起)和對應(yīng)的觸發(fā)事件(上升沿或下降沿或者雙邊沿都觸發(fā))。EXTI 可以實現(xiàn)對每個中斷/事件線進(jìn)行單獨配置,可以單獨配置為中斷或者事件,以及觸發(fā)事件的屬性。每個輸入線都可以獨立地被屏 蔽。掛起寄存器保持著狀態(tài)線的中斷請求。
?????? EXTI控制器的主要特性如下:
????????● 每個中斷/事件都有獨立的觸發(fā)和屏蔽
????????● 每個中斷線都有專用的狀態(tài)位
????????● 支持多達(dá)20個軟件的中斷/事件請求
????????● 檢測脈沖寬度低于APB2時鐘寬度的外部信號。
EXTI 功能框圖
????????EXTI 的功能框圖包含了 EXTI 最核心內(nèi)容,掌握了功能框圖,對 EXTI 就有一個整體的把握,在編程時思路就非常清晰。
????????在圖 EXTI 功能框圖中可以看到很多在信號線上打一個斜杠并標(biāo)注“20”字樣(“/20”),這個表示在EXTI控制器內(nèi)部類似的信號線路有 20 個,這與 EXTI 總共有 20 個中斷/事件線時一樣的。
?
?
????????EXTI控制器的功能可分為兩大部分,一個功能是產(chǎn)生中斷,另一個功能是產(chǎn)生事件,這兩個功能從硬件上就有所不同。
框圖中不同的功能概述
????????
?
產(chǎn)生中斷線路
?????? 紅色虛線指示的電路流程就是一個產(chǎn)生中斷的線路,最終產(chǎn)生的信號是流入到 NVIC 控制器內(nèi)。
?????? 編號 1 是輸入線,STM32F103系列EXTI 控制器有 19 個中斷/事件輸入線,這些輸入線可以通過寄存器設(shè)置為任意一個 GPIO,也可以是一些外設(shè)的事件,輸入線一般是存在電平變化的信號。
?????? 編號 2 是一個邊沿檢測電路,它會根據(jù)上升沿觸發(fā)選擇寄存器 (EXTI_RTSR) 和下降沿觸發(fā)選擇寄存器 (EXTI_FTSR) 對應(yīng)位的設(shè)置來控制信號觸發(fā)。邊沿檢測電路以輸入線作為信號輸入端,如果檢測到有邊沿跳變就輸出有效信號 1 給編號 3 電路,否則輸出無效信號 0。而 EXTI_RTSR 和EXTI_FTSR 兩個寄存器可以控制器需要檢測哪些類型的電平跳變過程,可以是只有上升沿觸發(fā)、只有下降沿觸發(fā)或者上升沿和下降沿都觸發(fā)。
?????? 編號 3 電路實際就是一個或門電路,它一個輸入來自編號 2 電路,另外一個輸入來自軟件中斷事件寄存器 (EXTI_SWIER)。EXTI_SWIER 允許我們通過程序控制就可以啟動中斷/事件線,這在某些地方非常有用。我們知道或門的作用就是有 “1“ 就為 ”1“,所以這兩個輸入隨便一個是有效信號 ”1“就可以輸出 ”1“ 給編號 4 和編號 6 電路。
????????編號 4 電路是一個與門電路,它一個輸入是編號 3 電路,另外一個輸入來自中斷屏蔽寄存器(EXTI_IMR)。與門電路要求輸入都為 1 才輸出 1,導(dǎo)致的結(jié)果是如果 EXTI_IMR 設(shè)置為 0 時,那不管編號 3 電路的輸出信號是 1 還是 0,最終編號 4 電路輸出的信號都為 0;如果 EXTI_IMR設(shè)置為 1 時,最終編號 4 電路輸出的信號才由編號 3 電路的輸出信號決定,這樣我們可以簡單的控制 EXTI_IMR 來實現(xiàn)是否產(chǎn)生中斷的目的。編號 4 電路輸出的信號會被保存到掛起寄存器(EXTI_PR) 內(nèi),如果確定編號 4 電路輸出為 1 就會把 EXTI_PR 對應(yīng)位置 1。
????????編號 5 是將 EXTI_PR 寄存器內(nèi)容輸出到 NVIC 內(nèi),從而實現(xiàn)系統(tǒng)中斷事件控制。
產(chǎn)生事件的線路
????????接下來我們來看看綠色虛線指示的電路流程。它是一個產(chǎn)生事件的線路,最終輸出一個脈沖信號。產(chǎn)生事件線路是在編號 3 電路之后與中斷線路有所不同,之前電路都是共用的。
????????編號 6 電路是一個與門,它一個輸入來自編號 3 電路,另外一個輸入來自事件屏蔽寄存器 (EXTI_EMR)。如果EXTI_EMR 設(shè)置為 0 時,那不管編號 3 電路的輸出信號是 1 還是 0,最終編號 6 電路輸出的信號都為 0;如果 EXTI_EMR 設(shè)置為 1 時,最終編號 6 電路輸出的信號才由編號 3 電路的輸出信號決定,這樣我們可以簡單的控制 EXTI_EMR 來實現(xiàn)是否產(chǎn)生事件的目的。
????????編號 7 是一個脈沖發(fā)生器電路,當(dāng)它的輸入端,即編號 6 電路的輸出端,是一個有效信號 1 時就會產(chǎn)生一個脈沖;如果輸入端是無效信號就不會輸出脈沖。
????????編號 8 是一個脈沖信號,就是產(chǎn)生事件的線路最終的產(chǎn)物,這個脈沖信號可以給其他外設(shè)電路使用,比如定時器 TIM、模擬數(shù)字轉(zhuǎn)換器 ADC 等等,這樣的脈沖信號一般用來觸發(fā) TIM 或者 ADC開始轉(zhuǎn)換。
????????產(chǎn)生中斷線路目的是把輸入信號輸入到 NVIC,進(jìn)一步會運行中斷服務(wù)函數(shù),實現(xiàn)功能,這樣是軟件級的。而產(chǎn)生事件線路目的就是傳輸一個脈沖信號給其他外設(shè)使用,并且是電路級別的信號傳輸,屬于硬件級的。
EXTI功能框圖總結(jié):
?????? 如果需要產(chǎn)生中斷,必須先配置好并使能中斷線。根據(jù)需要的邊沿檢測設(shè)置2個觸發(fā)寄存器,同時在中斷屏蔽寄存器的相應(yīng)位寫’1’允許中斷請求。當(dāng)外部中斷線上發(fā)生了需要觸發(fā)的邊沿時,將產(chǎn)生一個中斷請求,對應(yīng)的掛起位也隨之被置’1’。在掛起寄存器的對應(yīng)位寫’1’,將清除該中斷請求。通過在軟件中斷寄存器寫’1’,也可以通過軟件產(chǎn)生中斷請求。
????????如果需要產(chǎn)生事件,必須先配置好并使能事件線。根據(jù)需要的邊沿檢測設(shè)置2個觸發(fā)寄存器,同時在事件屏蔽寄存器的相應(yīng)位寫’1’允許事件請求。當(dāng)事件線上發(fā)生了需要觸發(fā)的邊沿時,將產(chǎn)生一個事件請求脈沖,對應(yīng)的掛起位不被置’1’。通過在軟件事件寄存器寫’1’,也可以通過軟件產(chǎn)生事件請求。
硬件中斷選擇
????????通過下面的過程來配置20個線路做為中斷源:
????????● 配置20個中斷線的屏蔽位(EXTI_IMR)
????????● 配置所選中斷線的觸發(fā)選擇位(EXTI_RTSR和EXTI_FTSR);
????????● 配置對應(yīng)到外部中斷控制器(EXTI)的NVIC中斷通道的使能和屏蔽位,使得20個中斷線中的請求可以被正確地響應(yīng)。
硬件事件選擇
????????通過下面的過程,可以配置20個線路為事件源
????????● 配置20個事件線的屏蔽位(EXTI_EMR)
????????● 配置事件線的觸發(fā)選擇位(EXTI_RTSR和EXTI_FTSR)
軟件中斷/事件的選擇
????????20個線路可以被配置成軟件中斷/事件線。下面是產(chǎn)生軟件中斷的過程:
????????● 配置20個中斷/事件線屏蔽位(EXTI_IMR, EXTI_EMR)
????????● 設(shè)置軟件中斷寄存器的請求位(EXTI_SWIER)
外部中斷/事件線路映像
?
?
?????????EXTI0 至 EXTI15 用于 GPIO,通過編程控制可以實現(xiàn)任意一個 GPIO 作為 EXTI 的輸入源。
????????另外四個EXTI線的連接方式如下:
????????● EXTI線16連接到PVD輸出
????????● EXTI線17連接到RTC鬧鐘事件
????????● EXTI線18連接到USB喚醒事件
????????● EXTI線19連接到以太網(wǎng)喚醒事件(只適用于互聯(lián)型產(chǎn)品)
????????通過AFIO_EXTICRx配置GPIO線上的外部中斷/事件,必須先使能AFIO時鐘。
EXTI 寄存器描述
?
?
?
?外部中斷/事件寄存器映像
?
?
EXTI 初始化結(jié)構(gòu)體詳解
????????標(biāo)準(zhǔn)庫函數(shù)對每個外設(shè)都建立了一個初始化結(jié)構(gòu)體,比如 EXTI_InitTypeDef,結(jié)構(gòu)體成員用于設(shè)置外設(shè)工作參數(shù),并由外設(shè)初始化配置函數(shù),比如 EXTI_Init() 調(diào)用,這些設(shè)定參數(shù)將會設(shè)置外設(shè)相應(yīng)的寄存器,達(dá)到配置外設(shè)工作環(huán)境的目的。
????????初始化結(jié)構(gòu)體定義在 stm32f4xx_exti.h 文件中,初始化庫函數(shù)定義在 stm32f4xx_exti.c 文件中。
/**@brief EXTI 初始化結(jié)構(gòu)體定義 */
typedef struct
{
uint32_t EXTI_Line; /*!< 中斷/事件線 */
EXTIMode_TypeDef EXTI_Mode; /*!< EXTI 模式 */
EXTITrigger_TypeDef EXTI_Trigger; /*!< 觸發(fā)類型f */
FunctionalState EXTI_LineCmd; /*!< EXTI 使能 */
}EXTI_InitTypeDef;
結(jié)構(gòu)體每個成員參數(shù):
????????1) EXTI_Line:EXTI 中斷/事件線選擇。
????????????????可選 EXTI0 至 EXTI19。
????????2) EXTI_Mode:EXTI 模式選擇。
????????????????可選為產(chǎn)生中斷 (EXTI_Mode_Interrupt) 或者產(chǎn)生事件(EXTI_Mode_Event)。
????????3) EXTI_Trigger:EXTI 邊沿觸發(fā)事件。
????????????????可選上升沿觸發(fā) (EXTI_Trigger_Rising)、下降沿觸發(fā) (EXTI_Trigger_Falling) 或者上升沿和下降沿都觸發(fā) ( EXTI_Trigger_Rising_Falling)。
????????4) EXTI_LineCmd:控制是否使能 EXTI 線。
? ? ? ? ? ? ? ? ?可選使能 EXTI 線 (ENABLE) 或禁用 (DISABLE)。
固件庫函數(shù)及編程步驟
?????? 在庫函數(shù)中,配置 GPIO 與中斷線的映射關(guān)系的函數(shù) GPIO_EXTILineConfig()來實現(xiàn)的:
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
?????????該函數(shù)將 GPIO 端口與中斷線映射起來,使用范例是:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
????????將中斷線 2 與 GPIOE 映射起來。設(shè)置好中斷線映射之后,接下來我們就要設(shè)置該中斷線上中斷的初始化參數(shù)了。
????????中斷線上中斷的初始化是通過函數(shù) EXTI_Init()實現(xiàn)的。EXTI_Init()函數(shù)的定義是:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根據(jù) EXTI_InitStruct 中指定的
//參數(shù)初始化外設(shè) EXTI 寄存器
????????上面的例子實現(xiàn)的功能是設(shè)置中斷線 4 上的中斷為下降沿觸發(fā)。
????????我們設(shè)置好中斷線和 GPIO 映射關(guān)系,然后又設(shè)置好了中斷的觸發(fā)模式等初始化參數(shù)。既然是外部中斷,涉及到中斷我們當(dāng)然還要設(shè)置 NVIC 中斷優(yōu)先級。設(shè)置NVIC中斷優(yōu)先級的知識已經(jīng)在上一篇文章詳細(xì)的介紹了,不太了解的可以先去看看上一篇文章。
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //使能按鍵外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優(yōu)先級 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子優(yōu)先級 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
NVIC_Init(&NVIC_InitStructure); //中斷優(yōu)先級分組初始化
????????我們配置完中斷優(yōu)先級之后,接著我們要做的就是編寫中斷服務(wù)函數(shù)。中斷服務(wù)函數(shù)的名 字是在 MDK 中事先有定義的。這里需要說明一下,STM32 的 IO 口外部中斷函數(shù)只有 6 個,分別為:
EXPORT EXTI0_IRQHandler
EXPORT EXTI1_IRQHandler
EXPORT EXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORT EXTI4_IRQHandler
EXPORT EXTI9_5_IRQHandler
EXPORT EXTI15_10_IRQHandler
???????中斷線 0-4 每個中斷線對應(yīng)一個中斷函數(shù),中斷線 5-9 共用中斷函數(shù) EXTI9_5_IRQHandler,中斷線 10-15 共用中斷函數(shù) EXTI15_10_IRQHandler。在編寫中斷服務(wù)函數(shù)的時候會經(jīng)常使用到兩個函數(shù),第一個函數(shù)是判斷某個中斷線上的中斷是否發(fā)生(標(biāo)志位是否置位):
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
????????這個函數(shù)一般使用在中斷服務(wù)函數(shù)的開頭判斷中斷是否發(fā)生。另一個函數(shù)是清除某個中斷線上的中斷標(biāo)志位:
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
????????這個函數(shù)一般應(yīng)用在中斷服務(wù)函數(shù)結(jié)束之前,清除中斷標(biāo)志位。
????????常用的中斷服務(wù)函數(shù)格式為:
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判斷某個線上的中斷是否發(fā)生
{
中斷邏輯…
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中斷標(biāo)志位
}
}
????????使用 IO 口外部中斷的一般步驟:
????????1)初始化 IO 口為輸入。
????????2)開啟 AFIO 時鐘
????????3)設(shè)置 IO 口與中斷線的映射關(guān)系。
????????4)初始化線上中斷,設(shè)置觸發(fā)條件等。
????????5)配置中斷分組(NVIC),并使能中斷。
????????6)編寫中斷服務(wù)函數(shù)。
外部中斷實驗
??? 硬件設(shè)計
?????? 本實驗用到的硬件資源有:
????????1) 指示燈 DS0、DS1
????????2) 蜂鳴器
????????3) 3 個按鍵:KEY0、KEY1 和 KEY_UP。
????????DS0、DS1 以及蜂鳴器和 STM32F1 的連接在之前的博文都已經(jīng)分別介紹了,在精英 STM32F103 上的按鍵 KEY0 連接在 PE4 上、KEY1 連接在 PE3 上、KEY_UP 連接在 PA0 上。如圖所示:
?
?
????????這里需要注意的是:KEY0 和 KEY1 是低電平有效的,而 KEY_UP 是高電平有效的,并且外部都沒有上下拉電阻,所以,需要在 STM32F1 內(nèi)部設(shè)置上下拉。
軟件設(shè)計
?????? 在工程文件目錄下面新建兩個文件夾分別為:exti.c和exti.h。并在把源文件添加到工程,和添加頭文件的路徑。
?????? exit.c 文件將要編寫4 個函數(shù)。
????????一個是外部中斷初始化函數(shù):
void EXTIX_Init(void);
????????三個都是中斷服務(wù)函數(shù):
void EXTI0_IRQHandler(void);//是外部中斷 0 的服務(wù)函數(shù),負(fù)責(zé) WK_UP 按鍵的中斷檢測;
void EXTI3_IRQHandler(void);//是外部中斷 3 的服務(wù)函數(shù),負(fù)責(zé) KEY1 按鍵的中斷檢測;
void EXTI4_IRQHandler(void);//是外部中斷 4 的服務(wù)函數(shù),負(fù)責(zé) KEY0 按鍵的中斷檢測;
exit.c中的代碼:
#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#include "beep.h"
//外部中斷初始化函數(shù)
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
KEY_Init(); // 按鍵端口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能復(fù)用功能時鐘
//GPIOE.3 中斷線以及中斷初始化配置 下降沿觸發(fā) //KEY1
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
EXTI_InitStructure.EXTI_Line=EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure); //根據(jù)EXTI_InitStruct中指定的參數(shù)初始化外設(shè)EXTI寄存器
//GPIOE.4 中斷線以及中斷初始化配置 下降沿觸發(fā) //KEY0
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_Init(&EXTI_InitStructure); //根據(jù)EXTI_InitStruct中指定的參數(shù)初始化外設(shè)EXTI寄存器
//GPIOA.0 中斷線以及中斷初始化配置 上升沿觸發(fā) PA0 WK_UP
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure); //根據(jù)EXTI_InitStruct中指定的參數(shù)初始化外設(shè)EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按鍵WK_UP所在的外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優(yōu)先級2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子優(yōu)先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //使能按鍵KEY1所在的外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優(yōu)先級2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子優(yōu)先級1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
NVIC_Init(&NVIC_InitStructure); //根據(jù)NVIC_InitStruct中指定的參數(shù)初始化外設(shè)NVIC寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //使能按鍵KEY0所在的外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優(yōu)先級2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //子優(yōu)先級0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
NVIC_Init(&NVIC_InitStructure); //根據(jù)NVIC_InitStruct中指定的參數(shù)初始化外設(shè)NVIC寄存器
}
//外部中斷0服務(wù)程序
void EXTI0_IRQHandler(void)
{
delay_ms(10);//消抖
if(WK_UP==1) //WK_UP按鍵
{
BEEP=!BEEP;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中斷標(biāo)志位
}
//外部中斷3服務(wù)程序
void EXTI3_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY1==0) //按鍵KEY1
{
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中斷標(biāo)志位
}
//外部中斷4服務(wù)程序
void EXTI4_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY0==0) //按鍵KEY0
{
LED0=!LED0;
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中斷標(biāo)志位
}
exit.h中的代碼:
#ifndef __EXTI_H
#define __EXIT_H
#include "sys.h"
void EXTIX_Init(void);//外部中斷初始化
#endif
main.c中的代碼
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "exti.h"
#include "beep.h"
int main(void)
{
delay_init(); //延時函數(shù)初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設(shè)置NVIC中斷分組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
uart_init(115200); //串口初始化為115200
LED_Init(); //初始化與LED連接的硬件接口
BEEP_Init(); //初始化蜂鳴器IO
EXTIX_Init(); //初始化外部中斷輸入
LED0=0; //先點亮紅燈
while(1)
{
printf("OK\r\n");
delay_ms(1000);
}
}
實驗現(xiàn)象:
???????當(dāng)WK_UP 按下時,蜂鳴器的狀態(tài)反轉(zhuǎn)(即響變?yōu)椴豁?,不響變成響)?/p>
?????? 當(dāng)KEY_0按下時, LED1的狀態(tài)反轉(zhuǎn)(即亮變成不亮,不亮變成亮);文章來源:http://www.zghlxwxcb.cn/news/detail-744113.html
???????當(dāng)KEY_0按下時,LED0和LED1的狀態(tài)同時反轉(zhuǎn)(即亮變成不亮,不亮變成亮);文章來源地址http://www.zghlxwxcb.cn/news/detail-744113.html
到了這里,關(guān)于【STM32學(xué)習(xí)筆記】(13)——外部中斷詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!