1.NVIC是什么
NVIC 是嵌套向量中斷控制器,控制着整個芯片中斷相關的功能,它跟內核緊密耦合,是內核里面的一個外設。但是各個芯片廠商在設計芯片的時候會對 Cortex-M3 內核里面的NVIC 進行裁剪,把不需要的部分去掉,所以說 STM32 的 NVIC 是 Cortex-M3 的NVIC 的一個子集。普通外設都在標准庫中以stmf10x_xxx.c中。NVIC屬於內核中的外設,相關的函數存放在misc.c中。
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset); void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState); void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);
說一下NVIC的設置中斷分組和設置優先級問題。
在 NVIC 有一個專門的寄存器:中斷優先級寄存器 NVIC_IPRx, 用來配置外部中斷的優先級, IPR 寬度為 8bit,原則上每個外部中斷可配置的優先級為 0~255,數值越小,優先級越高。但是絕大多數 CM3 芯片都會精簡設計,以致實際上支持的優先級數減少,在F103 中,只使用了高 4bit,如下所示:
圖 1 中斷優先級寄存器NVIC_IPRx
用於表達優先級的這 4bit(bit4-bit7),又被分組成搶占優先級和子優先級。總共可以分成5個分組:在用到NVIC時,需要先設置分組,再對某個中斷設置搶占優先級和子優先級。
主優先級占4位,子優先級占0位 //這種情況,如果有2個中斷,他們的的主優先級可以設置2^4種,0-15,而子優先級只能設置成一樣的 主優先級占3位,子優先級占1位 //這種情況,如果有2個中斷,他們的的主優先級可以設置2^3種,0-8,而子優先級可以設置成2^1種,0-1
主優先級占2位,子優先級占2位 //這種情況,如果有2個中斷,他們的的主優先級可以設置2^2種,0-3,而子優先級可以設置成2^2種,0-3
主優先級占1位,子優先級占3位 //這種情況,如果有2個中斷,他們的的主優先級可以設置2^1種,0-1,而子優先級可以設置成2^3種,0-7
主優先級占0位,子優先級占4位 //這種情況,如果有2個中斷,他們的的主優先級可以設置2^0種,0,而子優先級可以設置2^4種。0-15
圖2 主優先級和子優先級的分配
----------------------------------------------------------------分割線------------------------------------------------------------------------------------------------------------------------------------
1 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按鍵WK_UP所在的外部中斷通道 2 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //搶占優先級2, 3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子優先級3 4 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
上面這4句代碼是配置某個中斷的NVIC初始化。第一行中 NVIC_IRQChannel 必須選定一個中斷源,既然用到了NVIC肯定用到了中斷,那中斷源從哪里來,必須指定出來。那中斷源從哪里找,比如,我要是設置定時器中斷的優先級,NVIC_IRQChannel怎么賦值?
從stm32f10x.h中找即可。(該文件中170行開始)
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ TAMPER_IRQn = 2, /*!< Tamper Interrupt */ RTC_IRQn = 3, /*!< RTC global Interrupt */ FLASH_IRQn = 4, /*!< FLASH global Interrupt */ RCC_IRQn = 5, /*!< RCC global Interrupt */ EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 global Interrupt */ DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 global Interrupt */ DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 global Interrupt */ DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 global Interrupt */ DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 global Interrupt */ DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 global Interrupt */ DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 global Interrupt */ ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */ USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */ CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */ TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */ TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */ TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ USART1_IRQn = 37, /*!< USART1 global Interrupt */ USART2_IRQn = 38, /*!< USART2 global Interrupt */ USART3_IRQn = 39, /*!< USART3 global Interrupt */ EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ USBWakeUp_IRQn = 42, /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */ TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */ TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */ TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */ TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ ADC3_IRQn = 47, /*!< ADC3 global Interrupt */ FSMC_IRQn = 48, /*!< FSMC global Interrupt */ SDIO_IRQn = 49, /*!< SDIO global Interrupt */ TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ UART4_IRQn = 52, /*!< UART4 global Interrupt */ UART5_IRQn = 53, /*!< UART5 global Interrupt */ TIM6_IRQn = 54, /*!< TIM6 global Interrupt */ TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ DMA2_Channel4_5_IRQn = 59 /*!< DMA2 Channel 4 and Channel 5 global Interrupt */
中斷的處理函數在startup_stm32f10x_hd.s中(啟動文件),這些函數可以實現在其他任意文件中。啟動文件中只是定義了這些函數名字。
WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
ADC3_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler
2.如何比較中斷的優先級?
如果有多個中斷同時響應,搶占優先級高的就會 搶占 搶占優先級低的優先得到執行,如果搶占優先級相同,就比較子優先級。如果搶占優先級和子優先級都相同的話,就比較他們的硬件中斷編號,編號越小,優先級越高。
3.EXTI是什么?
普通的GPIO作為輸入輸出,EXTI則是把普通的GPIO復用作為檢測口,根據EXTI寄存器配置是中斷模式還是事件模式。來控制引腳的輸入信號是到NVIC中去,還是到脈沖發生器中去。如果配置為中斷,則引腳的輸入信號會通過下降沿觸發選擇寄存器、上升沿觸發選擇寄存器、軟件中斷事件寄存器、請求掛起寄存器、中斷屏蔽寄存器共同設置最后達到NVIC中。CPU則會響應這個中斷,去處理它的中斷服務函數。事件的話,最后到達脈沖發生器,不需要CPU響應。
圖3 EXTI 框圖
每個GPIO引腳都可以配置外部中斷。每個端口有16個引腳,EXTI0-EXTI15總共16個外部中斷線。分配方法是,把每個端口的0號引腳連接到EXTI0上,一次類推,可以看下面的圖。
圖4 EXTI中斷線的連接方式
具體配置方法和疑問
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能復用功能時鍾 //GPIOE.2 中斷線以及中斷初始化配置 GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2); EXTI_InitStructure.EXTI_Line=EXTI_Line2; //KEY2 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿觸發
EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); //根據EXTI_InitStruct中指定的參數初始化外設EXTI寄存
EXTI使用方法是。對照上面的代碼
1.把引腳連接到外部中斷線上
2.關閉中斷屏蔽
3.觸發模式(中斷還是事件)
4.觸發方式(上升還是下降還是全部)
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2); EXTI_InitStructure.EXTI_Line=EXTI_Line2; //KEY2
通過代碼中發現這兩行代碼好像是重復了,都是配置中斷線的,通過查看這兩個函數的實現,發現GPIO_EXTILineConfig內部操作的是8.4.3 外部中斷配置寄存器(AFIO_EXTICRx),就是把具體引腳連接到對應的中斷線上,(總共有4個寄存器,共同配置EXTI0-EXTI15)
圖5 外部中斷配置寄存器AFIO_EXTICRx
而 EXTI_InitStructure.EXTI_Line=EXTI_Line2; 這句代碼,通過EXTI_Init函數內部查找發現,這個是操作的9.3.1 中斷屏蔽寄存器(EXTI_IMR),是用來屏蔽還是開放這個線上的中斷的。主要作用是是否開發中斷。對應圖3中的中斷屏蔽寄存器。
圖6 中斷屏蔽寄存器(EXTI_IMR)
問題0:EXTI->IMR &= ~EXTI_InitStruct->EXTI_Line;的疑惑,解答地址https://bbs.21ic.com/icview-245974-1-1.html
問題1:外部中斷5-9 外部中斷10-15共用同一個中斷處理函數,怎么設置5-9的中斷優先級?
問題2:如果PA0,PB0,PC0同時連接到了EXTI0,如何判斷是哪個引腳的外部中斷?
https://bbs.21ic.com/icview-561604-1-1.html