關(guān)于STM32外部中斷
以下總結(jié)參考原子STM32開發(fā)指南
STM32F103 的中斷控制器支持 19 個(gè)外部中斷/事件請(qǐng)求。STM32F103 的19 個(gè)外部中斷為:
線 0~15:對(duì)應(yīng)外部 IO 口的輸入中斷。
線 16:連接到 PVD 輸出。
線 17:連接到 RTC 鬧鐘事件。
線 18:連接到 USB 喚醒事件。
每個(gè)中斷線對(duì)應(yīng)了最多 7 個(gè) IO 口,以線 0 為例:它對(duì)應(yīng)了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、 GPIOF.0、 GPIOG.0。
在庫函數(shù)中,配置 GPIO 與中斷線的映射關(guān)系的函數(shù) GPIO_EXTILineConfig()來實(shí)現(xiàn)的:
voidGPIO_EXTILineConfig(uint8_tGPIO_PortSource,uint8_tGPIO_PinSource);
然后看一下這個(gè)函數(shù)的使用范例:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);//將中斷線2與GPIOE映射起來
中斷線上中斷的初始化是通過函數(shù) EXTI_Init()實(shí)現(xiàn)的。EXTI_Init()函數(shù)的定義是:
voidEXTI_Init(EXTI_InitTypeDef*EXTI_InitStruct);
我們來看看結(jié)構(gòu)體 EXTI_InitTypeDef 的成員變量:
typedefstruct
{
uint32_tEXTI_Line;
EXTIMode_TypeDefEXTI_Mode;
EXTITrigger_TypeDefEXTI_Trigger;
FunctionalStateEXTI_LineCmd;
}EXTI_InitTypeDef;
第一個(gè)參數(shù)是中斷線的標(biāo)號(hào),取值范圍為EXTI_Line0~EXTI_Line15。
第二個(gè)參數(shù)是中斷模式,可選值為中斷 EXTI_Mode_Interrupt 和事件 EXTI_Mode_Event。
第三個(gè)參數(shù)是觸發(fā)方式,可以是下降沿觸發(fā) EXTI_Trigger_Falling,上升沿觸發(fā) EXTI_Trigger_Rising,或者任意電平(上升沿和下降沿)觸發(fā)EXTI_Trigger_Rising_Falling。
最后一個(gè)參數(shù)就是使能中斷線。
下面我們用一個(gè)使用范例來說明這個(gè)函數(shù)的使用:
EXTI_InitTypeDefEXTI_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寄存器
這樣就設(shè)置好中斷線和 GPIO 映射關(guān)系,然后又設(shè)置好了中斷的觸發(fā)模式等初始化參數(shù)。既然是外部中斷,我們還需要設(shè)置NVIC中斷優(yōu)先級(jí)。
下面介紹一下NVIC
首先要講解的是中斷優(yōu)先級(jí)分組函數(shù) NVIC_PriorityGroupConfig,其函數(shù)申明如下:
voidNVIC_PriorityGroupConfig(uint32_tNVIC_PriorityGroup);
這個(gè)函數(shù)的作用是對(duì)中斷的優(yōu)先級(jí)進(jìn)行分組,這個(gè)函數(shù)在系統(tǒng)中只能被調(diào)用一次,一旦分組確定就最好不要更改。
比如我們?cè)O(shè)置整個(gè)系統(tǒng)的中斷優(yōu)先級(jí)分組值為 2,那么方法是:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
這樣就確定了一共為“2 位搶占優(yōu)先級(jí),2 位響應(yīng)優(yōu)先級(jí)”。
下面我們講解一個(gè)重要的函數(shù)為中斷初始化函數(shù) NVIC_Init,其函數(shù)申明為:
voidNVIC_Init(NVIC_InitTypeDef*NVIC_InitStruct);
其中 NVIC_InitTypeDef 是一個(gè)結(jié)構(gòu)體,我們可以看看結(jié)構(gòu)體的成員變量:
typedefstruct
{
uint8_tNVIC_IRQChannel;
uint8_tNVIC_IRQChannelPreemptionPriority;
uint8_tNVIC_IRQChannelSubPriority;
FunctionalStateNVIC_IRQChannelCmd;
}NVIC_InitTypeDef;
NVIC_InitTypeDef 結(jié)構(gòu)體中間有三個(gè)成員變量,這三個(gè)成員變量的作用是:
NVIC_IRQChannel:定義初始化的是哪個(gè)中斷,這個(gè)我們可以在 stm32f10x.h 中找到每個(gè)中斷對(duì)應(yīng)的名字。例如 USART1_IRQn。
NVIC_IRQChannelPreemptionPriority:定義這個(gè)中斷的搶占優(yōu)先級(jí)別。
NVIC_IRQChannelSubPriority:定義這個(gè)中斷的子優(yōu)先級(jí)別。
NVIC_IRQChannelCmd:該中斷是否使能。
搶占優(yōu)先級(jí)的級(jí)別高于響應(yīng)優(yōu)先級(jí)。而數(shù)值越小所代表的優(yōu)先級(jí)就越高。
這里需要注意兩點(diǎn):
第一,如果兩個(gè)中斷的搶占優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)都是一樣的話,則看哪個(gè)中斷先發(fā)生就先執(zhí)行;第二,高優(yōu)先級(jí)的搶占優(yōu)先級(jí)是可以打斷正在進(jìn)行的低搶占優(yōu)先級(jí)中斷的。而搶占優(yōu)先級(jí)相同的中斷,高優(yōu)先級(jí)的響應(yīng)優(yōu)先級(jí)不可以打斷低響應(yīng)優(yōu)先級(jí)的中斷。
結(jié)合實(shí)例說明一下:假定設(shè)置中斷優(yōu)先級(jí)組為 2,然后設(shè)置中斷 3(RTC 中斷)的搶占優(yōu)先級(jí)為 2,響應(yīng)優(yōu)先級(jí)為 1。中斷 6(外部中斷 0)的搶占優(yōu)先級(jí)為 3,響應(yīng)優(yōu)先級(jí)為 0。中斷 7(外部中斷 1)的搶占優(yōu)先級(jí)為 2,響應(yīng)優(yōu)先級(jí)為 0。那么這 3 個(gè)中斷的優(yōu)先級(jí)順序?yàn)椋褐袛?7>中斷 3>中斷 6。
上面例子中的中斷 3 和中斷 7 都可以打斷中斷 6 的中斷。而中斷 7 和中斷 3 卻不可以相互打斷!
我們現(xiàn)在就可以設(shè)置中斷線2的中斷優(yōu)先級(jí):
NVIC_InitTypeDefNVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;//使能按鍵外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x02;//搶占優(yōu)先級(jí)2
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02;//子優(yōu)先級(jí)2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能外部中斷通道
NVIC_Init(&NVIC_InitStructure);//中斷優(yōu)先級(jí)分組初始化
我們配置完中斷優(yōu)先級(jí)之后,接著我們要做的就是編寫中斷服務(wù)函數(shù)。中斷服務(wù)函數(shù)的名字是在 MDK 中事先有定義的。這里需要說明一下,STM32 的 IO 口外部中斷函數(shù)只有 6 個(gè),分別為:
EXPORTEXTI0_IRQHandler
EXPORTEXTI1_IRQHandler
EXPORTEXTI2_IRQHandler
EXPORTEXTI3_IRQHandler
EXPORTEXTI4_IRQHandler
EXPORTEXTI9_5_IRQHandler
EXPORTEXTI15_10_IRQHandler
中斷線 0-4 每個(gè)中斷線對(duì)應(yīng)一個(gè)中斷函數(shù),中斷線 5-9 共用中斷函數(shù) EXTI9_5_IRQHandler,中斷線 10-15 共用中斷函數(shù) EXTI15_10_IRQHandler。 在編寫中斷服務(wù)函數(shù)的時(shí)候會(huì)經(jīng)常使用到兩個(gè)函數(shù),第一個(gè)函數(shù)是判斷某個(gè)中斷線上的中斷是否發(fā)生(標(biāo)志位是否置位) :
ITStatusEXTI_GetITStatus(uint32_tEXTI_Line);
這個(gè)函數(shù)一般使用在中斷服務(wù)函數(shù)的開頭判斷中斷是否發(fā)生。另一個(gè)函數(shù)是清除某個(gè)中斷線上的中斷標(biāo)志位:
voidEXTI_ClearITPendingBit(uint32_tEXTI_Line);
這個(gè)函數(shù)一般應(yīng)用在中斷服務(wù)函數(shù)結(jié)束之前,清除中斷標(biāo)志位。
常用的中斷服務(wù)函數(shù)格式為:
voidEXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判斷某個(gè)線上的中斷是否發(fā)生
{
中斷邏輯…
EXTI_ClearITPendingBit(EXTI_Line3);//清除LINE上的中斷標(biāo)志位
}
}
固件庫還提供了兩個(gè)函數(shù)用來判斷外部中斷狀態(tài)以及清除外部狀態(tài)標(biāo)志位的函數(shù) EXTI_GetFlagStatus 和 EXTI_ClearFlag,他們的作用和前面兩個(gè)函數(shù)的作用類似。只是在 EXTI_GetITStatus 函數(shù)中會(huì)先判斷這種中斷是否使能,使能了才去判斷中斷標(biāo)志位,而EXTI_GetFlagStatus 直接用來判斷狀態(tài)標(biāo)志位。
下面我們?cè)倏偨Y(jié)一下使用 IO 口外部中斷的一般步驟:
1)初始化 IO 口為輸入。
2)開啟 IO 口復(fù)用時(shí)鐘,設(shè)置 IO 口與中斷線的映射關(guān)系。
3)初始化線上中斷,設(shè)置觸發(fā)條件等。
4)配置中斷分組(NVIC),并使能中斷。
5)編寫中斷服務(wù)函數(shù)。
通過以上幾個(gè)步驟的設(shè)置,我們就可以正常使用外部中斷了。
注意使用STM32外部中斷需要開啟AFIO時(shí)鐘,開啟方式為:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);