01嵌套向量中斷控制器——NVIC
NVIC的全稱是Nested vectoredinterrupt controller,即嵌套向量中斷控制器。控制著整個芯片中斷相關的功能,通過對NVIC寄存器進行配置可以實現對內核和片上外設的中斷的控制。但是各個芯片廠商在設計芯片的時候會對 Cortex-M4內核里面的 NVIC進行裁剪,把不需要的部分去掉,所以說 STM32的 NVIC 是 Cortex-M4的 NVIC 的一個子集,只是用到了NVIC的一部分功能,其余的保留以后備用。
搶占式優先級(占先式優先級)和響應優先級(子優先級)
搶占優先級(占先式優先級)
搶占,是指打斷其他中斷的屬性,即因為具有這個屬性會出現嵌套中斷(在執行中斷服務函數A 的過程中被中斷B 打斷,執行完中斷服務函數B 再繼續執行中斷服務函數A)。
通俗的講,完成某一件事正常是有順序,先完成事件 A ,再完成事件 B,假如,張三現在在做事件 A ,突然李四叫張三去做事件 B ,那么現在就有一個問題,張三是先完成事件 A ?還是去做事件 B ?這里就需要看看那個事件的優先級了,倘若事件 A ,比事件 B 比較重要,那么先完成事件 A 后再完成事件 B ,假如是事件 B, 比事件 A 比較重要,則張三需要先完成事件 B ,再回來繼續完成事件 A ,不管事件 A,有沒有完成。
在片內中設置好事件執行的優先級之后,總是高優先級先執行完然后再執行低優先級的。編號越低優先級越高。即 “0”的優先級最高。
響應優先級(子優先級)
響應屬性則應用在搶占屬性相同的情況下,當兩個中斷向量的搶占優先級相同時,如果兩個中斷同時到達, 則先處理響應優先級高的中斷。
通俗的講,同時有事件 A 、事件 B 、事件 C ,其中事件 A 的搶占優先級要高于事件 B 、事件 C ,其次事件 B 、事件 C 的搶占優先級一樣,在完成事件 A 之后,假如事件 B 、事件 C 這兩件事同時“到達”,那這個時候響應屬性的作用就開始發揮了,假如事件 B 的響應屬性高于事件 C ,則優先完成事件B。
響應和搶占優先級,有種搶占優先級里面包含著響應優先級的感覺,只不過搶占優先級強調的事,我正在做某一件事,有另外一件事來打斷我現在在做的這件。響應優先級則強調的是,同時“到達”,我先處理哪一件事的問題。
NVIC 的定義
在 NVIC 有一個專門的寄存器:中斷優先級寄存器 NVIC_IPRx,用來配置外部中斷的優先級,IPR寬度為 8bit,原則上每個外部中斷可配置的優先級為 0~255,數值越小,優先級越高。但是絕大多數 CM3 芯片都會精簡設計,以致實際上支持的優先級數減少,在 F103 中,只使用了高 4bit,如下所示:
在僅剩的4位中,又包含搶占優先級和響應優先級。理論是會有16個中斷源,但是STM32又進行了分組,共分為5組,如下:
在庫文件misc.c和misc.h中分別用宏定義定義了 "NVIC_PtiorityGroup"這五組分組源。
#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /* 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /* 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /* 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /* 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /* 4 bits for pre-emption priority
0 bits for subpriority */
通過misc.c文件中定義的 “ **NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) ** ” 函數:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB- >AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
配置響應優先級及應用舉例
例如配置以下一位搶占優先級,三位子優先級
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//中斷優先級分組:2位的搶占,2位的子優先級
在選擇完組源之后,就需要對該組的中斷源、搶占優先級和響應優先級、配置通道的開啟進行配置 。
通過在 misc.h 中的結構體,進行配置
typedef struct
{
uint8_t NVIC_IRQChannel;//中斷源
uint8_t NVIC_IRQChannelPreemptionPriority;//搶占優先級
uint8_t NVIC_IRQChannelSubPriority;//響應優先級
FunctionalState NVIC_IRQChannelCmd;//是否使能
} NVIC_InitTypeDef;
針對每個中斷,設置對應的搶占優先級和響應優先級,下面以中斷源為 "USART2_IRQn",搶占優先級為1,響應優先級為0,的例子,優先級分組為一。
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 嵌套向量中斷控制器組選擇 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ; /* 配置USART為中斷源 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; /* 搶斷優先級*/
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; /* 子優先級 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /* 使能中斷 */
NVIC_Init(&NVIC_InitStructure); /* 初始化配置NVIC */
}
其中中斷源通過在 "stm32f10x.h"中的結構體
typedef enum IRQn
{
.
.
.
.
.
}IRQn_Type;
至此,關于NCIV的簡單配置就完成了
02外部中斷/事件控制器——EXTI
EXTI簡介
EXTI(External interrupt/event controller)—外部中斷/事件控制器,管理了控制器的 20 個中斷/事件線。每個中斷/事件線都對應有一個邊沿檢測器,可以實現輸入信號的上升沿檢測和下降沿的檢測。EXTI 可以實現對每個中斷/事件線進行單獨配置,可以單獨配置為中斷或者事件,以及觸發事件的屬性。
中斷/事件線
EXTI 有 20 個中斷/事件線,每個 GPIO 都可以被設置為輸入線,占用 EXTI0 至 EXTI15,還有另外七根用于特定的外設事件。這里得并不是以PA的整個系列作為一個中斷/事件線,而是以PA0,PB0,PC0 …… PG0作為一個中斷源,如下圖所示。
16個中斷線的不是每個中斷都有獨立的中斷服務函數,IO口外部中斷在中斷向量表中只分配了7個中斷向量,也就是只能使用7個中斷服務函數。
從表可以看出外部中斷EXTI5_9共用一個服務函數,外部中斷EXTI15_10共用一個服務函數,對應的中斷函數在啟動文件里面
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
因為這里是通過STM32 的引腳,通過映射的方法,讓GPIO具有了中斷的功能,所以在使用某一個GPIO作為中斷的引腳的時候,就需要調動映射服務函數。( “ stm32f10x_gpio.c” 中的 “ GPIO_EXTILineConfig”)
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{
uint32_t tmp = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));
tmp = ((uint32_t)0x0F) < < (0x04 * (GPIO_PinSource & (uint8_t)0x03));
AFIO- >EXTICR[GPIO_PinSource > > 0x02] &= ~tmp;
AFIO- >EXTICR[GPIO_PinSource > > 0x02] |= (((uint32_t)GPIO_PortSource) < < (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
}
其中,GPIO_PortSource,通過在 stm32f10x_gpio.h 中的查找響應的指令,如下所示
#define GPIO_PortSourceGPIOA ((uint8_t)0x00)
#define GPIO_PortSourceGPIOB ((uint8_t)0x01)
#define GPIO_PortSourceGPIOC ((uint8_t)0x02)
#define GPIO_PortSourceGPIOD ((uint8_t)0x03)
#define GPIO_PortSourceGPIOE ((uint8_t)0x04)
#define GPIO_PortSourceGPIOF ((uint8_t)0x05)
#define GPIO_PortSourceGPIOG ((uint8_t)0x06)
同理,GPIO_PinSource ,通過在 stm32f10x_gpio.h 中的查找響應的指令,如下所示
#define GPIO_PinSource0 ((uint8_t)0x00)
#define GPIO_PinSource1 ((uint8_t)0x01)
#define GPIO_PinSource2 ((uint8_t)0x02)
#define GPIO_PinSource3 ((uint8_t)0x03)
#define GPIO_PinSource4 ((uint8_t)0x04)
#define GPIO_PinSource5 ((uint8_t)0x05)
#define GPIO_PinSource6 ((uint8_t)0x06)
#define GPIO_PinSource7 ((uint8_t)0x07)
#define GPIO_PinSource8 ((uint8_t)0x08)
#define GPIO_PinSource9 ((uint8_t)0x09)
#define GPIO_PinSource10 ((uint8_t)0x0A)
#define GPIO_PinSource11 ((uint8_t)0x0B)
#define GPIO_PinSource12 ((uint8_t)0x0C)
#define GPIO_PinSource13 ((uint8_t)0x0D)
#define GPIO_PinSource14 ((uint8_t)0x0E)
#define GPIO_PinSource15 ((uint8_t)0x0F)
EXTI功能框圖
輸入線通過邊沿檢測電路,檢測是上升沿還是下降沿,至于哪一種觸發方式,通過上升沿觸發選擇寄存器(EXTI_RTSR)和下降沿觸發選擇寄存器(EXTI_FTSR)進行相應位置置 “1”。
通過在 “stm32f10x_exti.h”中的結構體進行選擇
typedef enum
{
EXTI_Trigger_Rising = 0x08,//上升沿
EXTI_Trigger_Falling = 0x0C, //下降沿
EXTI_Trigger_Rising_Falling = 0x10//雙觸發
}EXTITrigger_TypeDef;
在通過或門,分別有兩個信號來源,一個是軟件中斷事件寄存器,允許我們通過程序控制就可以啟動中斷/事件線,另一個是邊沿檢測電路,假如我設置的是是上升沿觸發,當外部輸入輸入線是上升沿的時,則邊沿檢測電路則會輸出“1”至或門,再通過或門,有 “1”則輸出是 “1”,或門的輸出端分別有兩個信號源去向,一個是請求掛起寄存器,一個事件屏蔽寄存器。
中斷屏蔽寄存器和請求掛起寄存器的與門邏輯運算至NVIC中斷寄存器,再去判斷優先級等等。
事件屏蔽寄存器和事件屏蔽寄存器的與門邏輯運算至脈沖發生器,這個脈沖信號可以給其他外設電路使用,比如定時器 TIM、模擬數字轉換器 ADC 等等,這樣的脈沖信號一般用來觸發 TIM 或者 ADC開始轉換。
事件:是表示檢測到某一動作(電平邊沿)觸發事件發生了。
中斷:有某個事件發生并產生中斷,并跳轉到對應的中斷處理程序中。
選擇中斷線與EXTI 初始化結構體詳解
首先選擇某一個GPIO作為中斷的輸入引腳,第一步使能起對應的時鐘線,其次是配置起響應的GPIO的功能,如下
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//配置相應的Pin腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; /* 配置為浮空輸入 */
GPIO_Init(GPIOA, &GPIO_InitStructure);//配置相應的GPIO
"激活"該引腳之后,就需要將中斷的功能賦于這個引腳,即把響應的映射功能使能,同時對該中斷的詳細內容進行配置.如下
首先找到以下函數進行映射使能
"RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState) "
找到"RCC_APB2Periph_AFIO"
碼源:
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO ,ENABLE);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
/* EXTI為中斷模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 上升沿中斷 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/* 使能中斷 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
下面是對應查找的位置
通過查找EXTI相應的設置結構體
EXTI_Line :——>stm32f10x_exti.h
"EXTI為中斷模式":
EXTI_Trigger :
最后一步則是選擇EXTI的信號源,通過下面該函數,進行配置
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
以上中斷服務函數的初始化就完成了,接下來則是編寫中斷服務函數
void EXTI0_IRQHandler (void)
{
//要執行的內容
EXTI_ClearITPendingBit( 相應的中斷線);//查詢相應中斷線和EXTI_Line 一樣
}
" EXTI0_IRQHandler "在啟動文件中查找
void EXTI_ClearITPendingBit( 相應的中斷線):
其作用就是清除進入中斷時對寄存器某個位置 "1",進行清 "0"
理論完成,進入喜聞樂見的實驗環節
以按鍵觸發中斷為例子,觸發引腳為:PA1
簡單的邏輯過程
EXIT.c
#include "exti.h"
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中斷控制器組選擇 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART為中斷源 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
/* 搶斷優先級*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
/* 子優先級 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
/* 使能中斷 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
void EXIT_config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_IN_FLOATING ;
GPIO_InitStruct.GPIO_Pin= GPIO_Pin_1 ;
GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz ;
GPIO_Init(GPIOA,&GPIO_InitStruct);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line=EXTI_Line1 ;
EXTI_InitStruct.EXTI_LineCmd=ENABLE ;
EXTI_InitStruct.EXTI_Mode= EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling;
NVIC_Configuration();
EXTI_Init(&EXTI_InitStruct);
}
void EXTI1_IRQHandler (void)
{
SysTick_Config_delay_ms(10);
GPIO_ResetBits(GPIOC, GPIO_Pin_13 );
EXTI_ClearITPendingBit( EXTI_Line1 );//查詢相應中斷線和EXTI_Line 一樣
}
EXIT.h
#ifndef _EXIT_H
#define _EXIT_H
#include "misc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x.h"
#include "led.h"
#include "systick.h"
#include "stm32f10x_exti.h"
void EXIT_config(void);
static void NVIC_Configuration(void);
#endif
main.c
#include "led.h"
#include "stm32f10x.h"
#include "exti.h"
#include "systick.h"
int main(void)
{
SysTick_Init();
LED_GPIO_Config() ;
EXIT_config( );
while(1)
{
}
}
-
單片機
+關注
關注
6056文章
44800瀏覽量
643658 -
寄存器
+關注
關注
31文章
5396瀏覽量
122481 -
中斷
+關注
關注
5文章
902瀏覽量
42303 -
NVIC
+關注
關注
0文章
35瀏覽量
11859 -
EXTI
+關注
關注
0文章
27瀏覽量
3829
發布評論請先 登錄
相關推薦
《振南電子STM32視頻教程》第六講:EXTI和NVIC的配置和使用
51單片機NVIC中斷控制設計
STM32單片機中斷及NVIC的詳細資料概述

MCU_關于STM32Fxxx中斷EXTI產生時多次(兩次)進入中斷的原因

STM32學習筆記(4)——NVIC中斷優先級管理和外部中斷EXTI

NVIC與外部中斷

STM32_EXTI外部中斷學習筆記

評論