1.STM32中斷概述
中斷優(yōu)先級(jí)
在使用中斷式按鍵之前,我們先去了解一下STM32的中斷。
關(guān)于這方面可以參考《STM32中斷優(yōu)先級(jí)和開關(guān)總中斷》這篇文檔。
STM32外部中斷有兩個(gè)優(yōu)先級(jí),搶占式優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí)(副優(yōu)先級(jí))。高搶占式優(yōu)先級(jí)的中斷可以在低搶占式優(yōu)先級(jí)中斷的處理過(guò)程中被響應(yīng),這被稱為中斷嵌套。

如圖所示,A中斷的搶占式優(yōu)先級(jí)更高,因此它可以嵌套在B中斷中執(zhí)行。B中斷必須等待A中斷的執(zhí)行完成才能繼續(xù)執(zhí)行。優(yōu)先級(jí)屬性編號(hào)越小表明優(yōu)先級(jí)別越高。
但是當(dāng)兩個(gè)中斷的搶占式優(yōu)先級(jí)相同時(shí),它們無(wú)法進(jìn)行中斷嵌套。后到來(lái)的中斷必須等之前的中斷結(jié)束才能被響應(yīng)。
如果兩個(gè)搶占式優(yōu)先級(jí)相同的中斷同時(shí)達(dá)到,則系統(tǒng)會(huì)根據(jù)副優(yōu)先級(jí)來(lái)決定先處理哪一個(gè)。如果副優(yōu)先級(jí)也相同,則根據(jù)中斷表中的順序決定先處理哪一個(gè)。
STM32使用的是Cortex-M3內(nèi)核,Cortex-M3中定義了8個(gè)比特位用于設(shè)置中斷源的優(yōu)先級(jí),這8個(gè)比特位可以有8種分配方式。
同時(shí)Cortex-M3允許具有較少中斷源時(shí)使用較少的寄存器位指定中斷源的優(yōu)先級(jí)。
因此STM32把指定中斷優(yōu)先級(jí)的寄存器位減少到4位,這4個(gè)寄存器位的分組方式如下:
第0組:所有4位用于指定響應(yīng)優(yōu)先級(jí)
第1組:最高1位用于指定搶占式優(yōu)先級(jí),最低3位用于指定響應(yīng)優(yōu)先級(jí)
第2組:最高2位用于指定搶占式優(yōu)先級(jí),最低2位用于指定響應(yīng)優(yōu)先級(jí)
第3組:最高3位用于指定搶占式優(yōu)先級(jí),最低1位用于指定響應(yīng)優(yōu)先級(jí)
第4組:所有4位用于指定搶占式優(yōu)先級(jí)
可以通過(guò)調(diào)用STM32的固件庫(kù)中的函數(shù) NVIC_PriorityGroupConfig( ) 選擇使用哪種優(yōu)先級(jí)分組方式,這個(gè)函數(shù)的參數(shù)有下列5種:
NVIC_PriorityGroup_0 => 選擇第0組
NVIC_PriorityGroup_1 => 選擇第1組
NVIC_PriorityGroup_2 => 選擇第2組
NVIC_PriorityGroup_3 => 選擇第3組
NVIC_PriorityGroup_4 => 選擇第4組
下面以一個(gè)簡(jiǎn)單的例子說(shuō)明如何指定中斷源的搶占式優(yōu)先級(jí)和響應(yīng)優(yōu)先級(jí):
//選擇使用優(yōu)先級(jí)分組第1組
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1 );
//使能EXTI0中斷
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;
//指定搶占式優(yōu)先級(jí)別1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//指定響應(yīng)優(yōu)先級(jí)別0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
//使能EXTI9_5中斷
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;
//指定搶占式優(yōu)先級(jí)別0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//指定響應(yīng)優(yōu)先級(jí)別1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
開關(guān)總中斷
在STM32/Cortex-M3中是通過(guò)改變CPU的當(dāng)前優(yōu)先級(jí)來(lái)允許或禁止中斷。
PRIMASK位:只允許NMI和hard fault異常,其他中斷/ 異常都被屏蔽(當(dāng)前CPU優(yōu)先級(jí)=0)。
FAULTMASK位:只允許NMI,其他所有中斷/異常都被屏蔽(當(dāng)前CPU優(yōu)先級(jí)=-1)。
在STM32固件庫(kù)中(stm32f10x_nvic.c和stm32f10x_nvic.h) 定義了四個(gè)函數(shù)操作PRIMASK位和FAULTMASK位,改變CPU的當(dāng)前優(yōu)先級(jí),從而達(dá)到控制所有中斷的目的。
下面兩個(gè)函數(shù)等效于關(guān)閉總中斷:
void NVIC_SETPRIMASK( void );
void NVIC_SETFAULTMASK( void );
下面兩個(gè)函數(shù)等效于開放總中斷:
void NVIC_RESETPRIMASK( void );
void NVIC_RESETFAULTMASK( void );
上面兩組函數(shù)要成對(duì)使用,不能交叉使用。
第一種方法:
NVIC_SETPRIMASK( ); //關(guān)閉總中斷
NVIC_RESETPRIMASK( );//開放總中斷
第二種方法:
NVIC_SETFAULTMASK( ); //關(guān)閉總中斷
NVIC_RESETFAULTMASK( );//開放總中斷
通常使用
NVIC_SETPRIMASK( ); //Disable Interrupts
NVIC_RESETPRIMASK( );//Enable Interrupts
在3.0的庫(kù)中已經(jīng)沒有第一種方法
NVIC_SETPRIMASK(); //關(guān)閉總中斷
NVIC_RESETPRIMASK();//開放總中斷
第二種方法:
NVIC_SETFAULTMASK( ); //關(guān)閉總中斷
NVIC_RESETFAULTMASK( );//開放總中斷
也可以用宏定義
#define CLI( ) __set_PRIMASK( 1 )
#define SEI( ) __set_PRIMASK( 0 )
2.程序代碼
按鍵的電路在之前已經(jīng)給出,可參照上一節(jié)。
程序目的:使用中斷式按鍵控制LED燈的開關(guān)。
exti.h源代碼:
#ifndef __EXTI_H
#define __EXTI_H
#include "stm32f10x.h"
void EXTI_PE4_Init(void);
#endif
exti.c源代碼:
#include "stm32f10x.h"
#include "exti.h"
/************************************************************************
PAx ~ PGx 端口的中斷事件都連接到了EXTIx,即同一時(shí)刻EXTIx只能響應(yīng)一個(gè)端口
多個(gè) GPIO 口的時(shí)間無(wú)法同一時(shí)間響應(yīng),但是可以分時(shí)復(fù)用
EXTI最普通的應(yīng)用就是接上一個(gè)按鍵,設(shè)置為下降沿觸發(fā),用中斷來(lái)檢測(cè)按鍵
************************************************************************/
static void NVIC_Configuration( void )
{
NVIC_InitTypeDef NVIC_InitStructure;
//將優(yōu)先級(jí)組配置成第1組
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1 );
/*****************************************************************************
NVIC_IRQChannel指需要配置的中斷向量。因使用PE4口的按鍵,所以配置在4通道
如果使用GPIO_PIN_5~GPIO_PIN9的任意一個(gè), 則配置通道為EXTI9_5_IRQn
如果使用GPIO_PIN_10~GPIO_PIN15的任意一個(gè),則配置通道為EXTI15_10_IRQn
******************************************************************************/
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
}
void EXTI_PE4_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
//打開GPIOE時(shí)鐘和AFIO時(shí)鐘
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );
NVIC_Configuration();
// PE4外部中斷配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉輸入
GPIO_Init( GPIOE, &GPIO_InitStructure );
GPIO_EXTILineConfig( GPIO_PortSourceGPIOE, GPIO_PinSource4 );
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 );
}
中斷服務(wù)函數(shù):
/********************************************************
中斷服務(wù)函數(shù)寫在stm32f10x_it_c中,新定義一個(gè)函數(shù)即可。
它的名字必須要與啟動(dòng)文件startup中的中斷向量表定義一致。
中斷函數(shù)入口的函數(shù)名只有下面兩種寫法:
(1)
EXTI0_IRQHandler ;EXTI Line 0
EXTI1_IRQHandler ;EXTI Line 1
EXTI2_IRQHandler ;EXTI Line 2
EXTI3_IRQHandler ;EXTI Line 3
EXTI0_IRQHandler ;EXTI Line 4
(2)
EXTI9_5_IRQHandler ;EXTI Line 5~9
EXTI15_10_IRQHandler ;EXTI Line 10~15
********************************************************/
void EXTI4_IRQHandler( void )
{
//確保產(chǎn)生了 EXTI Line 中斷
if( EXTI_GetITStatus( EXTI_Line4 ) != RESET )
{
LED2_REV;
//清除中斷標(biāo)志位
EXTI_ClearITPendingBit( EXTI_Line4 );
}
}
主函數(shù)main.c:
#include "stm32f10x.h"
#include "led.h"
#include "exti.h"
int main(void)
{
LED_Init();
LED2(ON);
EXTI_PE4_Init();
while(1)
{
//主函數(shù)不執(zhí)行任何動(dòng)作,只需要等待中斷
}
}