在STM32中執(zhí)行中斷主要分三部分:
==1.配置NVIC_Config()函數(shù)
2.配置EXTI_Config()函數(shù)
3.編寫中斷服務函數(shù)==
(注:本文章所用代碼為中斷按鍵代碼刽严,實現(xiàn)了按鍵進入中斷從而控制LED亮滅)
==配置NVIC_Config()函數(shù)==
NVIC 是嵌套向量中斷控制器揣非,控制著整個芯片中斷相關的功能哄芜,它跟內核緊密耦合德频,是內核里面的一個外設。
NVIC_Config()函數(shù)代碼如下:
staticvoidNVIC_Config(void)/* 主要是配置中斷源的優(yōu)先級與打開使能中斷通道 */{NVIC_InitTypeDef NVIC_InitStruct;/* 配置中斷優(yōu)先級分組(設置搶占優(yōu)先級和子優(yōu)先級的分配)盆耽,在函數(shù)在misc.c */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);/* 配置初始化結構體 在misc.h中 *//* 配置中斷源 在stm32f10x.h中 */NVIC_InitStruct.NVIC_IRQChannel=KEY1_EXTI_IRQN;/* 配置搶占優(yōu)先級 */NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;/* 配置子優(yōu)先級 */NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;/* 使能中斷通道 */NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;/* 調用初始化函數(shù) */NVIC_Init(&NVIC_InitStruct);/* 對key2執(zhí)行相同操作 */NVIC_InitStruct.NVIC_IRQChannel=KEY2_EXTI_IRQN;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_Init(&NVIC_InitStruct);}
配置NVIC_Config()的目的是選擇中斷源的優(yōu)先級以及打開中斷通道祝懂,主要功能通過配置NVIC初始化結構體==NVIC_InitStruct==來完成。通俗的講亥揖,STM32中有很多中斷,而當有多個中斷同時發(fā)生時就涉及到中斷執(zhí)行的先后問題了圣勒,所以引入了中斷優(yōu)先級的概念费变,中斷優(yōu)先級越高中斷就越先執(zhí)行。在這里我們只討論外部中斷的優(yōu)先級圣贸,在 NVIC 有一個專門的寄存器:中斷優(yōu)先級寄存器 NVIC_IPRx挚歧,用來配置外部中斷的優(yōu)先級。優(yōu)先級高低的比較包括搶占優(yōu)先級和子優(yōu)先級吁峻,先比較搶占優(yōu)先級滑负,如果搶占優(yōu)先級相同就比較子優(yōu)先級,從而得出中斷之間的優(yōu)先級高低用含。==NVIC的主要任務就是給對應的中斷源分配中斷優(yōu)先級矮慕。== 中斷優(yōu)先級分配的原理繁雜,但固件庫編程的好處就是化繁為簡啄骇,我們只需要按照NVIC_InitStruct()中的內容進行配置就行痴鳄。
接下來簡單講解一下NVIC_Config()函數(shù)的內容:
==1.首先設置中斷優(yōu)先級分組==
中斷優(yōu)先級分組其實是確立一個大綱,中斷優(yōu)先級寄存器 NVIC_IPRx中有4個位用來確定優(yōu)先級缸夹,中斷優(yōu)先級的分組就是把這4個位分配在搶占優(yōu)先級和子優(yōu)先級中痪寻。比如設定一個位配置搶占優(yōu)先級,其余三個位配置子優(yōu)先級虽惭。通過函數(shù)==NVIC_PriorityGroupConfig() ;== 實現(xiàn)分組橡类,詳細代碼如下:
1/**
2 * 配置中斷優(yōu)先級分組:搶占優(yōu)先級和子優(yōu)先級
3 * 形參如下:
4 * @arg NVIC_PriorityGroup_0: 0bit for 搶占優(yōu)先級
5 *? ? ? ? ? ? ? ? ? ? ? ? ? ? 4 bits for 子優(yōu)先級
6 * @arg NVIC_PriorityGroup_1: 1 bit for 搶占優(yōu)先級
7 *? ? ? ? ? ? ? ? ? ? ? ? ? ? 3 bits for 子優(yōu)先級
8 * @arg NVIC_PriorityGroup_2: 2 bit for
9 *? ? ? ? ? ? ? ? ? ? ? ? ? ? 2 bits for 子優(yōu)先級
10 * @arg NVIC_PriorityGroup_3: 3 bit for 搶占優(yōu)先級
11 *? ? ? ? ? ? ? ? ? ? ? ? ? 1 bits for 子優(yōu)先級
12 * @arg NVIC_PriorityGroup_4: 4 bit for 搶占優(yōu)先級
13 *? ? ? ? ? ? ? ? ? ? ? ? ? 0 bits for 子優(yōu)先級
14 * @注意 如果優(yōu)先級分組為 0,則搶占優(yōu)先級就不存在芽唇,優(yōu)先級就全部由子優(yōu)先級控制
15 */16voidNVIC_PriorityGroupConfig(uint32_tNVIC_PriorityGroup)17{18// 設置優(yōu)先級分組19SCB->AIRCR=AIRCR_VECTKEY_MASK|NVIC_PriorityGroup;20}
2.優(yōu)先級分組完畢后猫态,是==配置NVIC初始化結構體==
typedefstruct{2uint8_tNVIC_IRQChannel;// 中斷源3uint8_tNVIC_IRQChannelPreemptionPriority;// 搶占優(yōu)先級4uint8_tNVIC_IRQChannelSubPriority;// 子優(yōu)先級5FunctionalState NVIC_IRQChannelCmd;// 中斷使能或者失能6}NVIC_InitTypeDef;
初始化結構體的作用是,收集中斷源的信息(包括配置的是哪一個中斷源披摄、中斷源的搶占優(yōu)先級是多少、中斷源的子優(yōu)先級是多少勇凭、中斷源的使能是否開啟)疚膊。
NVIC_IROChannel:用來設置中斷源,不同的中斷中斷源不一樣虾标,且不可寫錯寓盗,即使寫錯了程序也不會報錯,只會導致不響應中斷。 ==stm32f10x.h 頭文件里面的 IRQn_Type 結構體定義傀蚌,這個結構體包含了所有的中斷源基显。==
NVIC_IRQChannelPreemptionPriority和NVIC_IRQChannelSubPriority 分別設置搶占優(yōu)先級和子優(yōu)先級,具體的值要根據(jù)中斷優(yōu)先級分組來確定善炫。
NVIC_IRQChannelCmd:設置中斷使能(ENABLE)或者失能(DISABLE)撩幽,相當于一個電源總開關。
3.最后借助NVIC初始化函數(shù)將NVIC初始化結構體中的信息寫入相應的寄存器中(體現(xiàn)了固件庫編程的優(yōu)點箩艺,不需要我們深入到寄存器層次去窜醉,只需要掌握相應函數(shù)的配置即可)
==配置EXTI_Config()函數(shù)==
EXTI(External interrupt/event controller):外部中斷/事件控制器,管理了控制器的 20個中斷/事件線艺谆。每個中斷/事件線都對應有一個邊沿檢測器榨惰,可以實現(xiàn)輸入信號的上升沿檢測和下降沿的檢測。 EXTI 可以實現(xiàn)對每個中斷/事件線進行單獨配置静汤,可以單獨配置為中斷或者事件琅催,以及觸發(fā)事件的屬性。
按我的理解虫给,EXTI是一個有著多達20個接口的控制器藤抡,它可以為每一個接入接口的信號源配置中斷(或事件)線、設置信號的檢測方式狰右、設置觸發(fā)事件的性質杰捂,也就是說,==傳入EXTI的僅僅是一個信號棋蚌,EXTI的功能就是根據(jù)信號傳入的“線”對信號做出相應的處理嫁佳,然后將處理后的信號轉向NVIC。== 就像一個分揀機器谷暮,傳入的東西經過篩選處理被送往不同的地方蒿往,只是EXTI分揀的是信號罷了。 ==如果說NVIC是配置中斷源湿弦,那么EXTI就是向NVIC傳送中斷信號瓤漏。==
EXTI功能框圖:
在這里插入圖片描述
EXTI 可分為兩大部分功能,一個是產生中斷颊埃,另一個是產生事件蔬充,線路1-2-4-5是產生中斷的流程,20/代表著有20條相同的線路班利。
接下來講解一下EXTI_Config()函數(shù)代碼:
voidEXTI_Config()/* 主要是連接EXTI與GPIO */{GPIO_InitTypeDef GPIO_InitStruct;EXTI_InitTypeDef EXTI_InitStruct;NVIC_Config();/* 初始化要與EXTI連接的GPIO *//* 開啟GPIOA與GPIOC的時鐘 */RCC_APB2PeriphClockCmd(KEY1_EXTI_GPIO_CLK|KEY2_EXTI_GPIO_CLK,ENABLE);GPIO_InitStruct.GPIO_Pin=KEY1_EXTI_GPIO_PIN;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;GPIO_Init(KEY1_EXTI_GPIO_PORT,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin=KEY2_EXTI_GPIO_PIN;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;GPIO_Init(KEY2_EXTI_GPIO_PORT,&GPIO_InitStruct);/* 初始化EXTI外設 *//* EXTI的時鐘要設置AFIO寄存器 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);/* 選擇作為EXTI線的GPIO引腳 */GPIO_EXTILineConfig(KEY1_GPIO_PORTSOURCE,KEY1_GPIO_PINSOURCE);/* 配置中斷or事件線 */EXTI_InitStruct.EXTI_Line=KEY1_EXTI_LINE;/* 使能EXTI線 */EXTI_InitStruct.EXTI_LineCmd=ENABLE;/* 配置模式:中斷or事件 */EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;/* 配置邊沿觸發(fā) 上升or下降 */EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising;EXTI_Init(&EXTI_InitStruct);GPIO_EXTILineConfig(KEY2_GPIO_PORTSOURCE,KEY2_GPIO_PINSOURCE);EXTI_InitStruct.EXTI_Line=KEY2_EXTI_LINE;EXTI_InitStruct.EXTI_LineCmd=ENABLE;EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;EXTI_Init(&EXTI_InitStruct);}
代碼可大體分為三部分:
配置GPIO相應引腳饥漫、配置EXTI并連接GPIO引腳、傳入NVIC_Config()
==1.配置GPIO相應引腳==
該代碼是通過按鍵產生一個電平信號罗标,然后經EXTI處理傳入NVIC產生中斷的庸队,所以要配置連接按鍵的GPIO引腳积蜻,主要是設置相應的引腳模式為浮空輸入。老規(guī)矩彻消,先開啟相應GPIO的時鐘竿拆,然后配置引腳初始化結構體,再利用初始化函數(shù)將初始化結構體寫入寄存器中宾尚。
==2.配置EXTI并連接GPIO引腳==
要操作外設丙笋,首先要打開相關的時鐘,==EXTI掛載在APB2總線上央勒,并且開啟時鐘時要操作AFIO寄存器== 不见,準備工作就緒后連接GPIO相應的引腳到EXTI中,前面說了EXTI有20個接口崔步,所以特定的引腳有特定的接口稳吮,所以要根據(jù)GPIO_EXTILineConfig();函數(shù)選擇用作EXTI線的GPIO引腳井濒,函數(shù)說明如下
/**
? * @brief? Selects the GPIO pin used as EXTI Line.
? * @param? GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines.
? *? This parameter can be GPIO_PortSourceGPIOx where x can be (A..G).
? * @param? GPIO_PinSource: specifies the EXTI line to be configured.
? *? This parameter can be GPIO_PinSourcex where x can be (0..15).
? * @retval None
? */voidGPIO_EXTILineConfig(uint8_tGPIO_PortSource,uint8_tGPIO_PinSource){uint32_ttmp=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)));}
其實對應的==EXTI線就對應GPIO引腳號==灶似,這樣看起來還比較直觀。
連接好GPIO引腳與EXTI后就該配置EXTI的初始化結構體了瑞你,結構體如下:
typedefstruct{uint32_tEXTI_Line;// 中斷/事件線EXTIMode_TypeDef EXTI_Mode;// EXTI 模式EXTITrigger_TypeDef EXTI_Trigger;// 觸發(fā)類型FunctionalState EXTI_LineCmd;// EXTI 使能}EXTI_InitTypeDef;
配置此結構體主要是:選擇相應的==EXTI線== 酪惭、選擇觸發(fā)模式、選擇產生的結果(中斷還是事件)者甲、是否使能EXTI線春感。
EXTI_Line:中斷線選擇,可選 EXTI_0 至 EXTI_19(一共20個)虏缸。既然剛才配置好了與GPIO引腳對應的EXTI線鲫懒,所以初始化結構體中的EXTI線就是與GPIO連接的那個線。
EXTI_Mode: EXTI 模式選擇刽辙,可選為產生中斷或者產生事件窥岩。就是決定信號的發(fā)展方向,是產生中斷呢宰缤?還是產生事件呢颂翼?此處是中斷。
EXTI_Trigger: EXTI 邊沿觸發(fā)模式慨灭,可選上升沿觸發(fā)朦乏、下降 沿 觸 發(fā) 或 者 上 升 沿 和 下 降 沿 都 觸 發(fā)。觸發(fā)信號氧骤。
EXTI_LineCmd:控制是否使能 EXTI 線呻疹,可選使能 EXTI 線或禁用。
初始化結構體配置完畢后交由初始化函數(shù)寫入相應的寄存器中语淘。
==3.傳入NVIC_Config()==
之后就自動傳入NVIC中了诲宇。。惶翻。
==編寫中斷服務函數(shù)==
到這里就萬事俱備只欠東風了姑蓝,中斷的觸發(fā)與處理及優(yōu)先級定義都已經安排上了,最后一步就是編寫中斷函數(shù)的內容了吕粗,只要進入中斷就會執(zhí)行中斷函數(shù)中的代碼纺荧,所以這是收尾工作。STM32的中斷服務函數(shù)不同于51單片機中的中斷服務函數(shù)颅筋,STM32的所有中斷函數(shù)都被偷偷安排了宙暇,==每個中斷都有其固定的名字,只有找到這個名字议泵,在這個固定的函數(shù)名下編寫中斷服務函數(shù)才是有效的==占贫,所有中斷函數(shù)的編寫都要在==stm32f10x_it.c== 中,如示:
在這里插入圖片描述
從所給的信息可得知外設的中斷服務函數(shù)的名字都存放在==startup_stm32f10x_xx.s== 中先口,而且是由匯編語言編寫型奥,如示:
在這里插入圖片描述
可知EXTI線0到EXTI線4線都是單獨的中斷函數(shù)名、EXTI線5到EXTI線9共用一個中斷函數(shù)名碉京、EXTI線10線到EXTI線15線共用一個中斷函數(shù)名厢汹。
我們要做的就是==以相應的EXTI線的中斷函數(shù)名字在stm32f10x_it.c中編寫中斷函數(shù)== 如下:
voidEXTI0_IRQHandler(void){if(EXTI_GetITStatus(KEY1_EXTI_LINE)!=RESET){LED1_TOGGLE;//LED1的亮滅狀態(tài)反轉}EXTI_ClearITPendingBit(KEY1_EXTI_LINE);}voidEXTI15_10_IRQHandler(void){if(EXTI_GetITStatus(KEY2_EXTI_LINE)!=RESET){LED2_TOGGLE;//LED2的亮滅狀態(tài)反轉}EXTI_ClearITPendingBit(KEY2_EXTI_LINE);}
每次進入中斷函數(shù)后,==靠ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)讀取中斷是否執(zhí)行== 谐宙,執(zhí)行完之后要利用==void EXTI_ClearITPendingBit(uint32_t EXTI_Line)清除清除中斷標志位烫葬,以免不斷進入中斷==
==大功告成==
到此完整的中斷系統(tǒng)就已經完成,主函數(shù)只需調用即可7豺摺4钭邸!
(附上主函數(shù)及倆個頭文件)
==希望可以一起交流學習
qq:2723808286==
#include"stm32f10x.h"#include"bsp_led.h"#include"bsp_key.h"intmain(void){LED_GPIO_Config();EXTI_Config();while(1){}}
#ifndef__BSP_KEY_H#define__BSP_KEY_H#include"stm32f10x.h"#defineKEY1_EXTI_GPIO_CLK? ? ? RCC_APB2Periph_GPIOA#defineKEY1_EXTI_GPIO_PORT? ? GPIOA#defineKEY1_EXTI_GPIO_PIN? ? ? GPIO_Pin_0#defineKEY1_EXTI_IRQN? ? ? ? ? EXTI0_IRQn/* 對應著引腳號 */#defineKEY1_EXTI_LINE? ? ? ? ? EXTI_Line0/* 中斷咽瓷、事件線對應引腳號 */#defineKEY1_GPIO_PORTSOURCE? ? GPIO_PortSourceGPIOA#defineKEY1_GPIO_PINSOURCE? ? GPIO_PinSource0#defineKEY1_EXTI_IRQHANDLER? ? ? EXTI0_IRQHandler#defineKEY2_EXTI_GPIO_CLK? ? ? RCC_APB2Periph_GPIOC#defineKEY2_EXTI_GPIO_PORT? ? GPIOC#defineKEY2_EXTI_GPIO_PIN? ? ? GPIO_Pin_13#defineKEY2_EXTI_IRQN? ? ? ? ? EXTI15_10_IRQn#defineKEY2_EXTI_LINE? ? ? ? ? EXTI_Line13#defineKEY2_GPIO_PORTSOURCE? ? GPIO_PortSourceGPIOC#defineKEY2_GPIO_PINSOURCE? ? GPIO_PinSource13#defineKEY2_EXTI_IRQHANDLER? ? ? EXTI15_10_IRQHandlervoidEXTI_Config(void);#endif
#ifndef__BSP_LED_H#define__BSP_LED_H#include"stm32f10x.h"#defineLED1_GPIO_CLK? RCC_APB2Periph_GPIOC/*時鐘*/#defineLED1_GPIO_PORT? GPIOC/*端口*/#defineLED1_GPIO_PIN? GPIO_Pin_2/*引腳*/#defineLED2_GPIO_PIN? GPIO_Pin_3#defineLED2_GPIO_CLK? RCC_APB2Periph_GPIOC#defineLED2_GPIO_PORT? GPIOC#definedigitalTOGGLE(p,i)? ? {p->ODR ^=i;}#defineLED1_TOGGLE? ? ? ? ? ? digitalTOGGLE(LED1_GPIO_PORT,LED1_GPIO_PIN)#defineLED2_TOGGLE? ? ? ? ? ? digitalTOGGLE(LED2_GPIO_PORT,LED2_GPIO_PIN)/* LED狀態(tài)反轉 */voidLED_GPIO_Config(void);#endif?
想了解更多加1090492304