開始學習外部中斷的基本知識
中斷優先級分組管理
CortexM3內部支持256箇中斷,其中包含了 16 個內核中斷和 240 個外部中斷,並且具有 256
級的可編程中斷設置。STM32F1系列有 84 箇中斷,包括 16 個內核中斷和 68 個可屏蔽中斷,具有 16 級可編程的中斷優先級。而我們常用的就是這 68 個可屏蔽中斷, 但是 STM32 的 68 個可屏蔽中斷,在 STM32F103 系列
上面,可屏蔽中斷又只有 60 個(在 107 系列纔有 68 個)。
中斷管理辦法:
首先,對STM32中斷進行分組,分成5個組:組0~4,該分組的設置是由 SCB->AIRCR 寄存器的 bit10~8 來定義的。 同時,對每個中斷設置一個搶佔優先級和一個響應優先級。
有4個位來分配搶佔優先級和響應優先級。
下面是AIRCR 中斷分組設置表
組 | AIRCR[10: 8] | bit[7: 4]分配情況 | 分配結果 |
---|---|---|---|
0 | 111 | 0: 4 | 0 位搶佔優先級, 4 位響應優先級 |
1 | 110 | 1: 3 | 1 位搶佔優先級, 3 位響應優先級 |
2 | 101 | 2: 2 | 2 位搶佔優先級, 2 位響應優先級 |
3 | 100 | 3: 1 | 3 位搶佔優先級, 1 位響應優先級 |
4 | 011 | 4: 0 | 4 位搶佔優先級, 0 位響應優先級 |
每個中斷,可以設置搶佔優先級爲 0~7,響應優先級爲 1 或 0。搶佔優先級的級別高於響應優先級。而數值越小所代表的優先級就越高。搶佔優先級相同的中斷,當兩個中斷同時發生,哪個響應優先級高,哪個先執行。
這裏需要注意兩點:第一,如果兩個中斷的搶佔優先級和響應優先級都是一樣的話,則看哪個中斷先發生就先執行;第二,高優先級的搶佔優先級是可以打斷正在進行的低搶佔優先級中斷的。而搶佔優先級相同的中斷,高優先級的響應優先級不可以打斷低響應優先級的中斷。
中斷優先級分組設置函數NVIC_PriorityGroupConfig
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
分組可以在下面選
(((GROUP) == NVIC_PriorityGroup_0) ||
((GROUP) == NVIC_PriorityGroup_1) || \
((GROUP) == NVIC_PriorityGroup_2) || \
((GROUP) == NVIC_PriorityGroup_3) || \
((GROUP) == NVIC_PriorityGroup_4))
設置好了系統中斷分組,那麼對於每個中斷我們又怎麼確定他的搶佔優先級和響應優先級呢?通過中斷初始化函數void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
解析一下上式用到的結構體NVIC_InitTypeDef
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
成員變量的含義
NVIC_InitTypeDef 結構體中間有三個成員變量,這三個成員變量的作用是:
NVIC_IRQChannel:定義初始化的是哪個中斷,這個我們可以在 stm32f10x.h 中找到每個中斷對應的名字。例如 USART1_IRQn。
NVIC_IRQChannelPreemptionPriority:定義這個中斷的搶佔優先級別。
NVIC_IRQChannelSubPriority:定義這個中斷的子優先級別。
NVIC_IRQChannelCmd:該中斷是否使能。
比如我們要使能串口 1 的中斷,同時設置搶佔優先級爲 1,子優先級位 2,初始化的方法是
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 搶佔優先級爲 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子優先級位 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根據上面指定的參數初始化 NVIC 寄存器
NVIC 寄存器結構體NVIC_Type
typedef struct
{
__IO uint32_t ISER[8]; /*!< Interrupt Set Enable Register */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*!< Interrupt Clear Enable Register */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*!< Interrupt Set Pending Register */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*!< Interrupt Clear Pending Register */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*!< Interrupt Active bit Register */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*!< Interrupt Priority Register, 8Bit wide */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*!< Software Trigger Interrupt Register */
} NVIC_Type;
外部中斷
STM32每一個IO口都可以作爲外部中斷輸入。
STM32的中斷控制器支持19個外部中斷/事件請求。
線0~15:對應外部IO口的輸入中斷
每個外部中斷線可以獨立配置觸發方式(上升沿,下降沿或者雙邊沿觸發),觸發/屏蔽,專用的狀態位。
GPIO的管腳和中斷線有一個對應關係,每個中斷線最多對應7個IO口。以線0爲例,它對應了GPIOA.0,GPIOB.0,GPIOC.0,GPIOD.0,GPIOE.0,GPIOF.0,GPIOG.0。中斷線每次只能連接到一個IO口,所以需要在7個IO口中進行選擇。
映射關係
圖片來源:正點原子
外部中斷的一般配置步驟
初始化IO口爲輸入GPIO_Init()
開啓IO口複用時鐘RCC_APB2PeriphClockCmd(RCC_APB2Perith_AFIO,ENABLE);
設置IO口與中斷線的映射關係void GPIO_EXTILineConfig()
初始化線上中斷,設置觸發條件等EXTI_Init()
配置中斷分組(NVIC),並使能中斷NVIC_Init()
編寫中斷服務程序EXTIx_IRQHandler()
清除中斷標誌位EXTI_ClearTPendingBit()
IO口和中斷線的映射
在庫函數中,配置 GPIO 與中斷線的映射關係的函數 GPIO_EXTILineConfig()來實現的:
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
該函數將 GPIO 端口與中斷線映射起來,使用範例是:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
將中斷線 2 與 GPIOE 映射起來,即GPIOE.2 與 EXTI2 中斷線連接了。我們要是用的是PE4(對應的是KEY0),所以這裏是
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
然後我們對中斷進行初始化
//初始化中斷
EXTI_InitStructure.EXTI_Line=EXTI_Line4;//線號
EXTI_InitStructure.EXTI_LineCmd=ENABLE;//使能
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//中斷還是事件
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿觸發
EXTI_Init(&EXTI_InitStructure);
這裏筆者有個問題?這裏爲什麼選擇下降沿觸發呢?原來是因爲KEY0接的是低電平,所以讓它有效的話,必須是到低電平。
配置中斷分組NVIC
//NVIC中斷分組的設置
NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE:
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStructure);
前五步的初始化函數如下
void EXITX_Init(){
EXTI_InitTypeDef EXTI_InitStructure;//結構體變量
NVIC_InitTypeDef NVIC_InitStructure;
KEY_Init();//初始化GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//key0對應PE4
//PE4映射到中斷線4
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4 );
//初始化中斷
EXTI_InitStructure.EXTI_Line=EXTI_Line4;//線號
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//中斷
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿觸發
EXTI_Init(&EXTI_InitStructure);
//NVIC中斷分組的設置
NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE:
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStructure);
}