NVIC中斷工作原理
cortex-m3支持256個中斷大溜,其中包含了16個內(nèi)核中斷,240個外部中斷估脆。stm32中斷是cortex-m3中斷子集钦奋。對于103系列有60個可屏蔽中斷。
STM32F103xC強型產(chǎn)品內(nèi)置嵌套的向量式中斷控制器,能夠處理多達60個可屏蔽中斷通道(不包括16個Cortex?-M3的中斷線)和16個優(yōu)先級付材。
/*
cortex-m3內(nèi)核分組方式(8組)結(jié)構(gòu)體表達方式:
*/
typedef struct
{
__IO uint32_t ISER[8]; 中斷使能設(shè)置寄存器 /*!< 偏移量: 0x000 Interrupt Set Enable Register */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; 中斷清除使能寄存器 /*!<偏移量: 0x080 Interrupt Clear Enable Register */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; 中斷掛起設(shè)置寄存器 /*!< 偏移量: 0x100 Interrupt Set Pending Register */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; 中斷清除掛起寄存器 /*!<偏移量: 0x180 Interrupt Clear Pending Register */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; 中斷激活狀態(tài)位寄存器 /*!< 偏移量: 0x200 Interrupt Active bit Register */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; 中斷優(yōu)先級寄存器 /*!< 偏移量: 0x300 Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644]; 軟件觸發(fā)方式寄存器
__O uint32_t STIR; /*!< 偏移量: 0xE00 Software Trigger Interrupt Register */
} NVIC_Type;
ISER[2]:ISER全稱是:Interrupt Set-Enable Registers朦拖,這是一個中斷使能寄存器組。103系列可屏蔽中斷有60個厌衔,這里用了2個32位寄存器璧帝,總共可以表示64個中斷,STM32F103只用了其中的前60位富寿;ISER[0]的bit0-bit31分別對應(yīng)中斷0-31睬隶。ISER[1]的bit0-27對應(yīng)中斷32-59;這樣页徐,要使能某個中斷理疙,必須設(shè)置相應(yīng)的ISER位為1,使該中斷被使能(這里僅是使能泞坦,還要配合中斷分組窖贤、屏蔽、IO口映射等設(shè)置才算一個完整的中斷設(shè)置)贰锁。
ICER[2]:全稱是Interrupt Clear-Enable Registers赃梧,是一個清除中斷使能寄存器組,和ISER寄存器功能相反豌熄。這里專門設(shè)置一個ICER寄存器來清除中斷位授嘀,而不是向ISER寫0來擦除,是因為NVIC的這些寄存器都是寫1有效的锣险,寫0是無效的蹄皱。
ISPR[2]:全程是Interrupt Set-Pending Registers巷折,是一個中斷掛起控制寄存器組锻拘。每個位對應(yīng)的中斷和ISER是一樣的署拟,通過置1推穷,可將正在進行中的中斷掛起类咧,而執(zhí)行同級或更高級別的中斷,寫0無效骗露。
ICPR[2]:Interrupt Clear-Pending Registers萧锉,解除中斷掛起柿隙。寫1有效禀崖,寫0無效。
IABR[2]:Interrupt Active Bit Registers艺晴,中斷激活標(biāo)志位寄存器組封寞,只讀狈究,可以讀取當(dāng)前正在執(zhí)行的中斷是哪一個抖锥。在中斷執(zhí)行完成后由硬件自動清零磅废。對應(yīng)位所代表的中斷和ISER相同魂莫,如果為1耙考,表示該位所對應(yīng)的中斷正在執(zhí)行倦始。
IPR[15]:Interrupt Priority Registers鞋邑,中斷優(yōu)先級控制寄存器組。STM32的中斷分組與這個寄存器密切相關(guān)逾一。因為STM32的中斷多達60多個遵堵,所以STM32采用中斷分組的辦法來確定中斷的優(yōu)先級陌宿。IPR寄存器由15個32bit的寄存器組成壳坪,每個可屏蔽中斷占8bit爽蝴,這樣總共可以表示15x4=60個可屏蔽中斷霜瘪。IPR[0]的[31-24]惧磺,[23-16]磨隘,[15-8]番捂,[7-0]分別對應(yīng)中斷3-0设预,總共對應(yīng)60個外部中斷。而每個可屏蔽中斷占用的8bit并沒有全部使用魄梯,只用了高4位酿秸。這4位又分為搶占優(yōu)先級和子優(yōu)先級辣苏。這兩個優(yōu)先級要根據(jù)SCB->AIRCR中中斷分組的設(shè)置來決定。
typedef struct
{
__IO uint32_t EVCR;
__IO uint32_t MAPR;
__IO uint32_t EXTICR[4];
} AFIO_TypeDef;
STM32 任何一個 IO 口都可以配置成中斷輸入口煌张,但是 IO 口的數(shù)目遠大于中斷線數(shù)(16 個)退客。于是 STM32 就這樣設(shè)計井辜,GPIOA~GPIOG 的[15:0]分別對應(yīng)中斷線 15~0粥脚。這樣每個中斷線對應(yīng)了最多 7 個 IO 口刷允,以線 0為例:它對應(yīng)了 GPIOA.0、GPIOB.0纤怒、GPIOC.0泊窘、GPIOD.0烘豹、GPIOE.0携悯、GPIOF.0筷笨、GPIOG.0胃夏。中斷線每次只能連接到1個IO口上构订,這樣就需要EXTICR來決定對應(yīng)的中斷線配置到哪個GPIO上了悼瘾。EXTICR 寄存器組,總共有 4 個卸勺,因為編譯器的寄存器組都是從 0 開始編號的曙求,所以EXTICR[0]~ EXTICR[3]悟狱,每個EXTICR 只用了其低 16 位挤渐。比如如我要設(shè)置 GPIOB.1 映射到中斷線 1浴麻,則只要設(shè)置 EXTICR[0]的 bit[7:4]為 0001 即可软免。默認(rèn)都是 0000 即映射到 GPIOA膏萧。
typedef struct
{
__IO uint32_t IMR;
__IO uint32_t EMR;
__IO uint32_t RTSR;
__IO uint32_t FTSR;
__IO uint32_t SWIER;
__IO uint32_t PR;
} EXTI_TypeDef;
通過這些寄存器的設(shè)置榛泛,就可以對外部中斷進行詳細設(shè)置了胚委。IMR:中斷屏蔽寄存器亩冬。這是一個 32 寄存器硅急。但是只有前 19 位有效营袜。當(dāng)位 x 設(shè)置為 1 時,則開啟這個線上的中斷凤壁,否則關(guān)閉該線上的中斷。
EMR:事件屏蔽寄存器煤搜,同 IMR擦盾,只是該寄存器是針對事件的屏蔽和開啟迹卢。
RTSR:上升沿觸發(fā)選擇寄存器徒仓。該寄存器同 IMR蓬衡,也是一個 32 為的寄存器狰晚,只有前 19位有效。位 x 對應(yīng)線 x 上的上升沿觸發(fā)瓷们,如果設(shè)置為 1谬晕,則是允許上升沿觸發(fā)中斷/事件携取。否則雷滋,不允許晤斩。
FTSR:下降沿觸發(fā)選擇寄存器。同 RTSR实愚,不過這個寄存器是設(shè)置下降沿的腊敲。下降沿和上升沿可以被同時設(shè)置兔仰,這樣就變成了任意電平觸發(fā)了蕉鸳。
SWIER:軟件中斷事件寄存器潮尝。通過向該寄存器的位 x 寫入 1勉失,在未設(shè)置 IMR 和 EMR 的時候,將設(shè)置 PR 中相應(yīng)位掛起顽素。如果設(shè)置了 IMR 和 EMR 時將產(chǎn)生一次中斷胁出。被設(shè)置的 SWIER位全蝶,將會在 PR 中的對應(yīng)位清除后清除抑淫。
PR:掛起寄存器始苇。當(dāng)外部中斷線上發(fā)生了選擇的邊沿事件催式,該寄存器的對應(yīng)位會被置為 1疏唾。0槐脏,表示對應(yīng)線上沒有發(fā)生觸發(fā)請求。通過向該寄存器的對應(yīng)位寫入 1 可以清除該位堂氯。在中斷服務(wù)函數(shù)里面經(jīng)常會要向該寄存器的對應(yīng)位寫 1 來清除中斷請求啤握。
GPIO外部輸入中斷實驗
與按鍵輸入實驗相同晶框,通過中斷實現(xiàn)。KEY0 控制 DS0蹲蒲,按一次亮届搁,再按一次卡睦,就滅表锻。KEY1 控制 DS1辽旋,效果同 KEY0补胚。WK_UP 按鍵則同時控制 DS0 和 DS1溶其,按一次,他們的狀態(tài)就翻轉(zhuǎn)一次束铭。KEY0為PC5KEY1為PA15WK_UP位PA0。
1 ) 初始化 IO 口為輸入昔汉。
這一步設(shè)置你要作為外部中斷輸入的 IO 口的狀態(tài),可以設(shè)置為上拉/下拉輸入口予,也可以設(shè)
置為浮空輸入沪停,但浮空的時候外部一定要帶上拉木张,或者下拉電阻窟哺。否則可能導(dǎo)致中斷不停的觸
發(fā)。在干擾較大的地方虚婿,就算使用了上拉/下拉然痊,也建議使用外部上拉/下拉電阻屉符,這樣可以一
定程度防止外部干擾帶來的影響矗钟。
2 ) 開啟 IO 口復(fù)用時鐘,設(shè)置 IO 口與中斷線的映射關(guān)系吨艇。
STM32 的 IO 口與中斷線的對應(yīng)關(guān)系需要配置外部中斷配置寄存器 EXTICR东涡,這樣我們要
先開啟復(fù)用時鐘疮跑,然后配置 IO 口與中斷線的對應(yīng)關(guān)系。才能把外部中斷與中斷線連接起來祖娘。
3 ) 開啟與該 IO 口相對的線上中斷/ 事件失尖,設(shè)置觸發(fā)條件。
這一步,我們要配置中斷產(chǎn)生的條件雹仿,STM32 可以配置成上升沿觸發(fā)增热,下降沿觸發(fā),或者
任意電平變化觸發(fā)胧辽,但是不能配置成高電平觸發(fā)和低電平觸發(fā)峻仇。這里根據(jù)自己的實際情況來配
置,同時要開啟中斷線上的中斷邑商。這里需要注意的是:如果使用外部中斷摄咆,并設(shè)置該中斷的 EMR
位的話吭从,會引起軟件仿真不能跳到中斷暇仲,而硬件上是可以的。而不設(shè)置 EMR,軟件仿真就可以
進入中斷服務(wù)函數(shù),并且硬件上也是可以的一喘。建議不要配置 EMR 位萎战。
4 ) 配置中斷分組(NVIC )虫啥,并使能中斷。
這一步,我們就是配置中斷的分組,以及使能,對 STM32 的中斷來說,只有配置了 NVIC
的設(shè)置,并開啟才能被執(zhí)行,否則是不會執(zhí)行到中斷服務(wù)函數(shù)里面去的。關(guān)于 NVIC 的詳細介
紹。
5 ) 編寫中斷服務(wù)函數(shù)。
這是中斷設(shè)置的最后一步歼冰,中斷服務(wù)函數(shù)腮恩,是必不可少的缸榛,如果在代碼里面開啟了中斷均澳,
但是沒編寫中斷服務(wù)函數(shù),就可能引起硬件錯誤槽惫,從而導(dǎo)致程序崩潰各薇!所以在開啟了某個中斷
后啥箭,一定要記得為該中斷編寫服務(wù)函數(shù)急侥。在中斷服務(wù)函數(shù)里面編寫你要執(zhí)行的中斷后的操作。
寄存器
1)初始化IO口
void KEYInit(void)
{
RCC->APB2ENR|=1<<2; //使能 PORTA 時鐘
RCC->APB2ENR|=1<<4; //使能 PORTC 時鐘
JTAG_Set(SWD_ENABLE); //關(guān)閉 JTAG,開啟 SWD
GPIOA->CRL&=0XFFFFFFF0; //PA0 設(shè)置成輸入
GPIOA->CRL|=0X00000008;
GPIOA->CRH&=0X0FFFFFFF; //PA15 設(shè)置成輸入
GPIOA->CRH|=0X80000000;
GPIOA->ODR|=1<<15; //PA15 上拉,PA0 默認(rèn)下拉
GPIOC->CRL&=0XFF0FFFFF; //PC5 設(shè)置成輸入
GPIOC->CRL|=0X00800000;
GPIOC->ODR|=1<<5; //PC5 上拉
}
2)開啟IO復(fù)用時鐘,配置IO口和中斷線的映射關(guān)系琴锭,以及觸發(fā)條件
void EXTtInit(u8 GPIOx, u8 BITx, u8 TRIM)
{
U8 EXTADDR;
u8 EXTOFFSET;
EXTADDR=BITx/4; //得到中斷寄存器組的編號
EXTOFFSET=(BITx%4)*4;
RCC->APB2ENR|=0x01; //使能 io 復(fù)用時鐘
AFIO->EXTICR[EXTADDR]&=~(0x000F<<EXTOFFSET);//清除原來設(shè)置B渚!靠瞎!
AFIO->EXTICR[EXTADDR]|=GPIOx<<EXTOFFSET;//EXTI.BITx 映射到 GPIOx.BITx
//自動設(shè)置
EXTI->IMR|=1<<BITx; //開啟 line BITx 上的中斷
if(TRIM&0x01)EXTI->FTSR|=1<<BITx; //line BITx 上事件下降沿觸發(fā)
if(TRIM&0x02)EXTI->RTSR|=1<<BITx; //line BITx 上事件上升降沿觸發(fā)
}
3)配置中斷分組,設(shè)置中斷優(yōu)先級,使能中斷
設(shè)置中斷分組
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)
{
u32 temp坡慌,temp1;
temp1=(~NVIC_Group)&0x07;//取后三位
temp1<<=8;
temp=SCB->AIRCR; //讀取先前的設(shè)置
temp&=0X0000F8FF; //清空先前分組
temp|=0X05FA0000; //寫入鑰匙
temp|=temp1;
SCB->AIRCR=temp; //設(shè)置分組
}
配置中斷優(yōu)先級(包括搶占優(yōu)先級和子優(yōu)先級)
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority藻雪,u8 NVIC_Channel枢纠,
u8 NVIC_Group)
{
u32 temp;
MY_NVIC_PriorityGroupConfig(NVIC_Group);//設(shè)置分組
temp=NVIC_PreemptionPriority<<(4-NVIC_Group);
temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);
temp&=0xf; //取低四位
NVIC->ISER[NVIC_Channel/32]|=(1<<NVIC_Channel%32);
//使能中斷位(要清除的話,相反操作就 OK)
NVIC->IP[NVIC_Channel]|=temp<<4; //設(shè)置響應(yīng)優(yōu)先級和搶斷優(yōu)先級
}
4)寫中斷服務(wù)函數(shù)
void EXTI0_IRQHandler(void)
{
delay_ms(10); //消抖
if(WK_UP==1) //WK_UP 按鍵
{
LED0=!LED0;
LED1=!LED1;
}
EXTI->PR=1<<0; //清除 LINE0 上的中斷標(biāo)志位
}
//外部中斷 9~5 服務(wù)程序
void EXTI9_5_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0) LED0=!LED0; //按鍵 0
EXTI->PR=1<<5; //清除 LINE5 上的中斷標(biāo)志位
}
//外部中斷 15~10 服務(wù)程序
void EXTI15_10_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY1==0) LED1=!LED1; //按鍵 1
EXTI->PR=1<<15; //清除 LINE15 上的中斷標(biāo)志位
}
庫函數(shù)
1)配置IO口
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,
ENABLE);//使能 PORTA,PORTC 時鐘
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
//關(guān)閉 jtag叼丑,使能 SWD绰垂,可以用 SWD 模式調(diào)試
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PA15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //設(shè)置成上拉輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 GPIOA15
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PC5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //設(shè)置成上拉輸入
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化 GPIOC5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 設(shè)置成輸入,默認(rèn)下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 GPIOA.0
2)開啟IO復(fù)用時鐘,配置IO口和中斷線的映射關(guān)系顿膨,以及觸發(fā)條件
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)//GPIO與中斷線的映射
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)//中斷線上的參數(shù)配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中斷,需要使能 AFIO 時鐘
//GPIOC.5 中斷線以及中斷初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line5;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿觸發(fā)
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);//根據(jù) EXTI_InitStruct 中指定的參數(shù)初始化外設(shè) EXTI 寄存器
//GPIOA.15 中斷線以及中斷初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);
EXTI_InitStructure.EXTI_Line=EXTI_Line15;
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 寄存器
//GPIOA.0 中斷線以及中斷初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
//根據(jù) EXTI_InitStruct 中指定的參數(shù)初始化外設(shè) EXTI 寄存器
3)設(shè)置中斷分組枢希,設(shè)置中斷優(yōu)先級
void NVIC_Init(NVIC_InitTypeDef * NVIC_InitStructure)//中斷優(yōu)先級配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
//使能按鍵所在的外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優(yōu)先級 2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子優(yōu)先級 1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
NVIC_Init(&NVIC_InitStructure);//根據(jù) NVIC_InitStruct 中指定的參數(shù)初始化外設(shè) NVIC 寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
//使能按鍵所在的外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優(yōu)先級 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子優(yōu)先級 1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
//使能按鍵所在的外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優(yōu)先級 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子優(yōu)先級 1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
NVIC_Init(&NVIC_InitStructure);
4)中斷服務(wù)函數(shù)
void EXTI0_IRQHandler(void)
{
delay_ms(10); //消抖
if(WK_UP==1)
{
LED0=!LED0;
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除 EXTI0 線路掛起位
}
void EXTI9_5_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0) {
LED0=!LED0;
}
EXTI_ClearITPendingBit(EXTI_Line5); //清除 LINE5 上的中斷標(biāo)志位
}
void EXTI15_10_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY1==0) {
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line15); //清除 LINE15 線路掛起位
}
常用中斷服務(wù)函數(shù)格式為:
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)!=RESET)//判斷某個線上的中斷是否發(fā)生
{
中斷邏輯…
EXTI_ClearITPendingBit(EXTI_Line2); //清除 LINE 上的中斷標(biāo)志位
}
}
STM32Cube
配置端口后,軟件自動生成配置函數(shù),只需要寫回調(diào)函數(shù)即可畦韭。(清除中斷標(biāo)志位在void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);函數(shù)中)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin & WK_UP_Pin){
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
}
if (GPIO_Pin & KEY0_Pin){
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
}
if (GPIO_Pin & KEY1_Pin){
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
}
}