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