NVIC:Nest Vector Interrupt Controller,嵌套中斷向量控制器,是用來管理中斷嵌套的,核心任務在於其優先級的管理。NVIC給每個中斷賦予先佔優先級(搶佔優先級)和次佔優先級(響應優先級)。
CM3 內核支持256箇中斷,其中包含了16個內核中斷和240個外部中斷,並且具有256級的可編程中斷設置。但STM32並沒有使用CM3內核的全部東西,而是隻用了它的一部分,STM32有76 箇中斷,包括16 個內核中斷和60 個可屏蔽中斷,具有16級可編程的中斷優先級。而我們常用的就是這60個可屏蔽中斷。
STM32將CM3內核的中斷向量表進行了重新編排,將編號-3至6的中斷向量定義爲系統異常,編號爲負的內核異常不能被設置優先級,從編號7開始爲外部中斷,這些中斷優先級都是可以自行設置的。
EXTI:External Interrupt,外部中斷,通過GPIO檢查輸入脈衝,引起中斷時間,打斷原來的代碼執行流程,進入到中斷服務函數中進行處理,處理完後再返回中斷之前的代碼中執行。
STM32 的EXTI控制器支持19個外部中斷/事件請求。每個中斷設有狀態位,每個中斷/事件都有獨立的觸發和屏蔽設置。STM32的19 個外部中斷爲 :
線 0~15:對應外部IO口的輸入中斷。
線 16:連接到PVD輸出。
線 17:連接到RTC鬧鐘事件。
線 18:連接到USB喚醒事件。
EXTI中斷步驟:
① 配置端口爲輸入模式
② 開啓與該IO口相對應的線上中斷/事件,並設置觸發條件
③ 配置中斷分組(NVIC)並使能中斷
④ 中斷函數
一、配置端口爲輸入模式
void GPIOA_Init(void)
{
GPIO_DeInit(GPIOA);
RCC->APB2ENR|=1<<2; //使能PORTA時鐘
GPIOA->CRL&=0XFFFFFFF0;//PA0設置成上拉輸入
GPIOA->CRL|=0X00000008;
}
在對端口進行任何操作之前,必須打開對應的時鐘信號,其設置才能生效。這裏使
用了GPIOA.00 端口作爲中斷0輸入,作爲輸入時一般我們設置爲上拉輸入,如果要
設置成浮空輸入的話,外部一定要加上拉電阻,這樣對於過濾輸入波動很有益處(假
設在電壓在3.3-2.0之間進行波動,時間上沒嘗試按鍵操作,因爲一旦有按鍵,就應
該爲0,那麼接了上拉的話,除非產生了低電平,否則小波動都會被拉高過濾掉)。
二、開啓與該IO口相對應的線上中斷/事件,並設置觸發條件
這一步封裝在函數void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM)中,可以直接調用,例如:Ex_NVIC_Config(GPIO_A,0,RTIR); //設置PA(0)上升沿觸發
Ex_NVIC_Config(GPIO_A,13,FTIR);//設置PA(13)下降沿觸發
該函數爲 Ex_NVIC_Config,該函數有3個參數:GPIOx爲GPIOA~G(0~6),在sys.h裏面有定義。代表要配置的IO口。BITx則代表這個IO口的第幾位。TRIM爲觸發方式,低2位有效(0x01代表下降觸發;0x02代表上升沿觸發;0x03代表任意電平觸發)。其代碼如下:
/**************************
該函數只針對GPIOA~G;不包括PVD,RTC和USB喚醒這三個,參數:GPIOA~G(0~6),代表GPIOA~G;BITx:需要使能的位;TRIM:觸發模式,1,上升沿;2,下降沿;3,任意電平觸發;該函數一次只能配置1個IO 口,多個IO 口,需多次調用,該函數會自動開啓對應中斷,以及屏蔽線
***************************/
void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM)
{
u8 EXTADDR;
u8 EXTOFFSET;
EXTADDR=BITx/4;//得到中斷寄存器組的編號
EXTOFFSET=(BITx%4)*4;
RCC->APB2ENR|=0x01;//使能IO複用時鐘
AFIO->EXTICR[EXTADDR]|=GPIOx<<EXTOFFSET;//EXTI.BITx映射到GPIOx.BITx
//自動設置
EXTI->IMR|=1<<BITx;//開啓line BITx上的中斷
EXTI->EMR|=1<<BITx;//不屏蔽lineBITx上的事件
if(TRIM&0x01)EXTI->FTSR|=1<<BITx;//lineBITx 上事件下降沿觸發
if(TRIM&0x02)EXTI->RTSR|=1<<BITx;//lineBITx 上事件上升降沿觸發
}
EXTICR(External interrupt configuration register)即外部中斷配置寄存器。
因爲STM32 任何一個IO口都可以配置成中斷輸入口,但是IO口的數目遠大於中斷線數(16個),所以我們需要選擇哪一個中斷是經由那個IO口輸入的。於是STM32就這樣設計,GPIOA~GPIOG的[15:0]分別對應中斷線15~0。這樣每個中斷線對應了最多7個IO 口,以線0爲例:它對應了GPIOA.0、PIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0。而中斷線每次只能連接到1個IO 口上,這樣就需要EXTICR來決定對應的中斷線配置到哪個GPIO上了。寄存器EXTIR有四組,在書寫時要注意。具體對應關係如下:0000: PA[x] pin,0001: PB[x] pin,0010: PC[x] pin,0011: PD[x] pin0100: PE[x] pin,0101: PF[x] pin,0110: PG[x] pin。假設我們需要設定外部中斷0由GPIOA_0來控制,那麼我就可以寫成AFIO->EXTIR[0] |=0X00;
Ex_NVIC_Config,首先根據GPIOx的位得到中斷寄存器組的編號,即EXTICR的編號,在EXTICR裏面配置中斷線應該配置到GPIOx的哪個位。然後使能該位的中斷及事件,最後配置觸發方式。這樣就完成了外部中斷的的配置了。從代碼中可以看到該函數默認是開啓中斷和事件的。其次還要注意的一點就是該函數一次只能配置一個IO口,如果你有多個IO口需要配置,則多次調用這個函數就可以了。
三、配置中斷分組(NVIC)並使能中斷
這一步封裝在函數void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)裏面可以直接調用,例如
MY_NVIC_Init(2,2,EXTI0_IRQChannel,2); //搶佔2,子優先級2,組2
NVIC 設置函數MY_NVIC_Init,該函數有4個參數,分別爲:NVIC_PreemptionPriority、NVIC_SubPriority、NVIC_Channel、NVIC_Group。第一個參數NVIC_PreemptionPriority爲中斷搶佔優先級數值,第二個參數NVIC_SubPriority爲中斷子優先級數值,前兩個參數的值必須在規定範圍內,否則也可能產生意想不到的錯誤。第三個參數NVIC_Channel爲中斷的編號(範圍爲0~59),最後一個參數NVIC_Group爲中斷分組設置(範圍爲0~4)。
/********************設置NVIC****************
*NVIC_PreemptionPriority:搶佔優先級
*//NVIC_SubPriority :響應優先級
*NVIC_Channel :中斷編號
*NVIC_Group :中斷分組 0~4
*注意優先級不能超過設定的組的範圍!否則會有意想不到的錯誤
*組劃分:
*組0:0 位搶佔優先級,4 位響應優先級
*組1:1 位搶佔優先級,3 位響應優先級
*組2:2 位搶佔優先級,2 位響應優先級
*組3:3 位搶佔優先級,1 位響應優先級
*組4:4 位搶佔優先級,0 位響應優先級
*NVIC_SubPriority 和NVIC_PreemptionPriority的原則是,數值越小,越優先
************************************/
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8NVIC_Channel,u8 NVIC_Group)
{
u32 temp;
u8 IPRADDR=NVIC_Channel/4; //每組只能存4個,得到組地址
u8 IPROFFSET=NVIC_Channel%4;//在組內的偏移
IPROFFSET=IPROFFSET*8+4; //得到偏移的確切位置
MY_NVIC_PriorityGroupConfig(NVIC_Group);//設置分組
temp=NVIC_PreemptionPriority<<(4-NVIC_Group);
temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);
temp&=0xf;//取低四位
if(NVIC_Channel<32)NVIC->ISER[0]|=1<<NVIC_Channel;//使能中斷位(要清除的話,相反操作就OK)
else
NVIC->ISER[1]|=1<<(NVIC_Channel-32);
NVIC->IPR[IPRADDR]|=temp<<IPROFFSET;//設置響應優先級和搶斷優先級
}
MY_NVIC_PriorityGroupConfig爲NVIC的分組函數,該函數的參數NVIC_Group爲要設置的分組號,可選範圍爲0~4,總共5組:EXTI0、EXTI1、EXTI2、EXTI3、EXTI4爲Line0~Line4、EXTI15_10爲Line15~Line10、EXTI9_5爲Line9~Line5。如果參數非法,將可能導致不可預料的結果。
/***************設置NVIC分組*****************
*NVIC_Group:NVIC 分組 0~4總共5 組
************************************/
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)
{
u32 temp,temp1;
temp1=(~NVIC_Group)&0x07;//取後三位
temp1<<=8;
temp=SCB->AIRCR; //讀取先前的設置
temp&=0X0000F8FF; //清空先前分組
temp|=0X05FA0000; //寫入鑰匙
temp|=temp1;
SCB->AIRCR=temp; //設置分組
}
MY_NVIC_PriorityGroupConfig 函數設置中斷優先級分組的思路:STM32的5 個分組是通過設置SCB->AIRCR的BIT[10:8]來實現的,SCB->AIRCR的修改需要通過在高16位寫入0X05FA這個密鑰才能修改的,故在設置AIRCR之前,應該把密鑰加入到要寫入的內容的高16位,以保證能正常的寫入AIRCR。在修改AIRCR的時候,我們一般採用讀->改->寫的步驟,來實現不改變AIRCR原來的其他設置。MY_NVIC_PriorityGroupConfig分組函數在每個系統裏面只要設置一次就夠了,設置多次,則是以最後的那一次爲準。但是隻要多次設置的組號都是一樣,就沒事。否則前面設置的中斷會因爲後面組的變化優先級會發生改變,這點在使用的時候要特別注意!一個系統代碼裏面,所有的中斷分組都要統一,以上代碼對要配置的中斷號默認是開啓中斷的。也就是ISER中的值設置爲1了。
四、中斷函數
A. 中斷函數名的書寫有要求,否則會找不到中斷入口。
stm32f10x_it.c是專門用來存放中斷服務函數的。
在 3.5 庫函數之前,在stm32f10x_it.c中就預先寫好了個外部中斷的函數名稱,我們只要將對應的執行過程填充進去就可以。
在3.5庫函數中,文件中默認只有幾個福安與系統異常的中斷服務函數,而且都是空函數,在需要的時候自行編寫。但是中斷服務函數名是不可以自己定義,中斷服務函數的名字必須要與啓動文件startup_stm32f10x_hd.s中的中斷向量表中定義一致。
B. 在進入中斷後我們需要做的動作有 2 部,一次執行中斷過程,2 是清除中斷掛起標識,因爲執行完畢了。如果不清除中斷掛起標識,則無法再次進入中斷。
例程:
void EXTI0_IRQHandler(void)
{
delayMs(10);
if(((GPIOA->IDR)&0x01)==0)
{
while(((GPIOA->IDR)&0x01)==1);
GPIOA->ODR = ~(GPIOA->ODR&0X02);
EXTI->PR=1;
}
}
這裏 EXTI->PR=1,即想中斷掛起寄存器中寫1,清除中斷