精华内容
下载资源
问答
  • ARM NVIC GIC

    2021-08-14 11:18:02
    2. NVIC(内嵌向量中断控制器) 3. 中断使能 4. 中断服务函数 1、中断向量表 中断向量表是一个表,这个表里面存放的是中断向量。中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量,因此中断向量表是一...

    最近做Cortex-A53与M3集成研究,先将中断相关记录一下,文档主要参考的是正点原子 I.MX6U嵌入式Linux驱动开发指南V1.5.pdf

    NVIC

    STM32(Cortex-M)的中断控制器
    1. 中断向量表
    2. NVIC(内嵌向量中断控制器)
    3. 中断使能
    4. 中断服务函数

    1、中断向量表

    中断向量表是一个表,这个表里面存放的是中断向量。中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量,因此中断向量表是一系列中断服务程序入口地址组成的表。这些中断服务程序(函数)在中断向量表中的位置是由半导体厂商定好的,当某个中断被触发以后就会自动跳转到中断向量表中对应的中断服务程序(函数)入口地址处。中断向量表在整个程序的最前面,比如 STM32F103 的中断向量表如下所示:

    摘自startup_stm32f10x_hd.s

    __Vectors       DCD     __initial_sp               ; Top of Stack
                    DCD     Reset_Handler              ; Reset Handler
                    DCD     NMI_Handler                ; NMI Handler
                    DCD     HardFault_Handler          ; Hard Fault Handler
                    DCD     MemManage_Handler          ; MPU Fault Handler
                    DCD     BusFault_Handler           ; Bus Fault Handler
                    DCD     UsageFault_Handler         ; Usage Fault Handler
                    DCD     0                          ; Reserved
                    DCD     0                          ; Reserved
                    DCD     0                          ; Reserved
                    DCD     0                          ; Reserved
                    DCD     SVC_Handler                ; SVCall Handler
                    DCD     DebugMon_Handler           ; Debug Monitor Handler
                    DCD     0                          ; Reserved
                    DCD     PendSV_Handler             ; PendSV Handler
                    DCD     SysTick_Handler            ; SysTick Handler
    
                    ; External Interrupts
                    DCD     WWDG_IRQHandler            ; Window Watchdog
                    DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                    DCD     TAMPER_IRQHandler          ; Tamper
                    DCD     RTC_IRQHandler             ; RTC
                    DCD     FLASH_IRQHandler           ; Flash
                    DCD     RCC_IRQHandler             ; RCC
                    DCD     EXTI0_IRQHandler           ; EXTI Line 0
                    DCD     EXTI1_IRQHandler           ; EXTI Line 1
                    DCD     EXTI2_IRQHandler           ; EXTI Line 2
                    DCD     EXTI3_IRQHandler           ; EXTI Line 3
                    DCD     EXTI4_IRQHandler           ; EXTI Line 4
                    DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                    DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                    DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                    DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                    DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                    DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                    DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                    DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
                    DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                    DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                    DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                    DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                    DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                    DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                    DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                    DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                    DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
                    DCD     TIM2_IRQHandler            ; TIM2
                    DCD     TIM3_IRQHandler            ; TIM3
                    DCD     TIM4_IRQHandler            ; TIM4
                    DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                    DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                    DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                    DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                    DCD     SPI1_IRQHandler            ; SPI1
                    DCD     SPI2_IRQHandler            ; SPI2
                    DCD     USART1_IRQHandler          ; USART1
                    DCD     USART2_IRQHandler          ; USART2
                    DCD     USART3_IRQHandler          ; USART3
                    DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
                    DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                    DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
                    DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
                    DCD     TIM8_UP_IRQHandler         ; TIM8 Update
                    DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
                    DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
                    DCD     ADC3_IRQHandler            ; ADC3
                    DCD     FSMC_IRQHandler            ; FSMC
                    DCD     SDIO_IRQHandler            ; SDIO
                    DCD     TIM5_IRQHandler            ; TIM5
                    DCD     SPI3_IRQHandler            ; SPI3
                    DCD     UART4_IRQHandler           ; UART4
                    DCD     UART5_IRQHandler           ; UART5
                    DCD     TIM6_IRQHandler            ; TIM6
                    DCD     TIM7_IRQHandler            ; TIM7
                    DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
                    DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
                    DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
                    DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
    __Vectors_End
    

    中断向量表都是链接到代码的最前面,比如一般 ARM 处理器都是从地址 0X00000000 开始执行指令的,那么中断向量表就是从 0X00000000 开始存放的。上面第 1 行的“__initial_sp”就是第一条中断向量,存放的是栈顶指针,接下来是第 2 行复位中断复位函数 Reset_Handler 的入口地址,依次类推,直到第 27 行的最后一个中断服务函数 DMA2_Channel4_5_IRQHandler 的入口地址,这样 STM32F103 的中断向量表就建好了。
    我们说 ARM 处理器都是从地址 0X00000000 开始运行的,但是我们学习 STM32 的时候代码是下载到 0X8000000 开始的存储区域中。因此中断向量表是存放到 0X8000000 地址处的,而不是 0X00000000,这样不是就出错了吗?为了解决这个问题,Cortex-M 架构引入了一个新的概念——中断向量表偏移,通过中断向量表偏移就可以将中断向量表存放到任意地址处,中断向量表偏移配置在函数 SystemInit 中完成,通过向 SCB_VTOR 寄存器写入新的中断向量表首地址即可,

    void SystemInit (void)
    {
    	RCC->CR |= (uint32_t)0x00000001;
    
    	/* 省略其它代码 */
    #ifdef VECT_TAB_SRAM
    	SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
    #else
    	SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
    #endif
    }
    

    第 8 行和第 10 行就是设置中断向量表偏移,第 8 行是将中断向量表设置到 RAM 中,第10 行是将中断向量表设置到 ROM 中,基本都是将中断向量表设置到 ROM 中,也就是地址0X8000000 处。第 10 行用到了 FALSH_BASE 和 VECT_TAB_OFFSET,这两个都是宏,定义如下所示:

    #define FLASH_BASE ((uint32_t)0x08000000)
    #define VECT_TAB_OFFSET 0x0

    因此第 10 行的代码就是:SCB->VTOR=0X080000000,中断向量表偏移设置完成。通过上面的讲解我们了解了两个跟 STM32 中断有关的概念:中断向量表和中断向量表偏移,那么这个跟 I.MX6U 有什么关系呢?因为 I.MX6U 所使用的 Cortex-A7 内核也有中断向量表和中断向量表偏移,而且其含义和 STM32 是一模一样的!只是用到的寄存器不同而已,概念完全相同!

    2、NVIC(内嵌向量中断控制器)

    中断系统得有个管理机构,对于 STM32 这种 Cortex-M 3内核的单片机来说这个管理机构叫做 NVIC,全称叫做 Nested Vectored Interrupt Controller。关于 NVIC 本教程不作详细的讲解,既然 Cortex-M 内核有个中断系统的管理机构—NVIC,那么 I.MX6U 所使用的 Cortex-A7 内核是不是也有个中断系统管理机构?答案是肯定的,不过 Cortex-A 内核的中断管理机构不叫做NVIC,而是叫做 GIC,全称是 general interrupt controller,后面我们会详细的讲解 Cortex-A 内核的 GIC。

    3、中断使能

    要使用某个外设的中断,肯定要先使能这个外设的中断,以 STM32F103 的 USART1:

      	//Usart1 NVIC 配置
      	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//设置中断源,类型参考 enum IRQn,stm32f10x.h
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;	//抢占优先级3
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;			//子优先级3
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
    	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
    

    上述代码就是使能USART1中断,同理,如果要使用 I.MX6U 的某个中断的话也需要使能其对应的中断。

    4、中断服务函数

    我们使用中断的目的就是为了使用中断服务函数,当中断发生以后中断服务函数就会被调用,我们要处理的工作就可以放到中断服务函数中去完成。同样以 STM32F103 的 USART1 为例,其中断服务函数如下所示:

    void USART1_IRQHandler(void)                	//串口1中断服务程序
    {
    	u8 Res;
    #if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    	OSIntEnter();    
    #endif
    	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    	{
    		Res =USART_ReceiveData(USART1);	//读取接收到的数据
    		
    		if((USART_RX_STA&0x8000)==0)//接收未完成
    		{
    			if(USART_RX_STA&0x4000)//接收到了0x0d
    			{
    				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
    				else USART_RX_STA|=0x8000;	//接收完成了 
    			}
    			else //还没收到0X0D
    			{	
    				if(Res==0x0d)
    					USART_RX_STA|=0x4000;
    				else
    				{
    					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
    					USART_RX_STA++;
    					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
    				}		 
    			}
    		}   		 
         } 
    #if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    	OSIntExit();  											 
    #endif
    } 
    

    其中USART有如下具体中断,都跳转到同一个中断服务函数,然后再获取具体的中断源头USART_GetITStatus

    #define USART_IT_PE                          ((uint16_t)0x0028)
    #define USART_IT_TXE                         ((uint16_t)0x0727)
    #define USART_IT_TC                          ((uint16_t)0x0626)
    #define USART_IT_RXNE                        ((uint16_t)0x0525)
    #define USART_IT_IDLE                        ((uint16_t)0x0424)
    #define USART_IT_LBD                         ((uint16_t)0x0846)
    #define USART_IT_CTS                         ((uint16_t)0x096A)
    #define USART_IT_ERR                         ((uint16_t)0x0060)
    #define USART_IT_ORE                         ((uint16_t)0x0360)
    #define USART_IT_NE                          ((uint16_t)0x0260)
    #define USART_IT_FE                          ((uint16_t)0x0160)
    

    延伸IAP

    在这里插入图片描述Reset_Handler

    ; Reset handler
    Reset_Handler   PROC
                    EXPORT  Reset_Handler             [WEAK]
                    IMPORT  __main
                    IMPORT  SystemInit
                    LDR     R0, =SystemInit
                    BLX     R0               
                    LDR     R0, =__main
                    BX      R0
                    ENDP
    
    

    在这里插入图片描述代码: IAP bootloader.

    GICv2

    1、GIC 控制器总览

    GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器,类似 Cortex-M 内核中的
    NVIC。目前 GIC 有 4 个版本:V1~V4,V1 是最老的版本,已经被废弃了。V2~V4 目前正在大量的使用。GIC V2 是给 ARMv7-A 架构使用的,比如 Cortex-A7、Cortex-A9、Cortex-A15 等,V3 和 V4 是给 ARMv8-A/R 架构使用的,也就是 64 位芯片使用的。I.MX6U 是 Cortex-A 内核的,因此我们主要讲解 GIC V2。GIC V2 最多支持 8 个核。ARM 会根据 GIC 版本的不同研发出不同的 IP 核,那些半导体厂商直接购买对应的 IP 核即可,比如 ARM 针对 GIC V2 就开发出了 GIC400 这个中断控制器 IP 核。当 GIC 接收到外部中断信号以后就会报给 ARM 内核,但是ARM 内核只提供了四个信号给 GIC 来汇报中断情况:VFIQ、VIRQ、FIQ 和 IRQ,
    请添加图片描述GIC 接收众多的外部中断,然后对其进行处理,最终就只通过四个信号报给 ARM 内核,这四个信号的含义如下:

    VFIQ:虚拟快速 FIQ
    VIRQ:虚拟快速 IRQ
    FIQ:快速中断 IRQ
    IRQ:外部中断 IRQ

    VFIQ 和 VIRQ 是针对虚拟化的,我们讨论虚拟化,剩下的就是 FIQ 和 IRQ 了
    在这里插入图片描述

    左侧部分就是中断源,中间部分就是 GIC 控制器,最右侧就是中断控制器向处理器内核发送中断信息。我们重点要看的肯定是中间的 GIC 部分,GIC 将众多的中断源分为分为三类:
    1、SPI(Shared Peripheral Interrupt),共享中断,顾名思义,所有 Core 共享的中断,这个是最常见的,那些外部中断都属于 SPI 中断(注意!不是 SPI 总线那个中断) 。比如按键中断、串口中断等等,这些中断所有的 Core 都可以处理,不限定特定 Core。
    2、PPI(Private Peripheral Interrupt),私有中断,我们说了 GIC 是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断。
    3、SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信

    2、中断 ID

    中断源有很多,为了区分这些不同的中断源肯定要给他们分配一个唯一 ID,这些 ID 就是中断 ID。每一个 CPU 最多支持 1020 个中断 ID,中断 ID 号为 ID0~ID1019。这 1020个 ID 包含了 PPI、SPI 和 SGI,那么这三类中断是如何分配这 1020 个中断 ID 的呢?这 1020 个 ID 分配如下:
    ID0~ID15:这 16 个 ID 分配给 SGI。
    ID16~ID31:这 16 个 ID 分配给 PPI。
    ID32~ID1019:这 988 个 ID 分配给 SPI,像 GPIO 中断、串口中断等这些外部中断 ,至于具体到某个 ID 对应哪个中断那就由半导体厂商根据实际情况去定义了。比如 I.MX6U的总共使用了 128 个中断 ID,加上前面属于 PPI 和 SGI 的 32 个 ID, I.MX6U 的中断源共有 128+32=160个,这 128 个中断 ID 对应的中断在《I.MX6ULL 参考手册》的“3.2 Cortex A7 interrupts”小节NXP 官方 SDK中的文件 MCIMX6Y2C.h,在此文件中定义了一个枚举类型 IRQn_Type,此枚举类型就枚举出了 I.MX6U 的所有中断,代码如下所示:

    /** Interrupt Number Definitions */
    #define NUMBER_OF_INT_VECTORS 160                /**< Number of interrupts in the Vector table */
    
    typedef enum IRQn {
      /* Auxiliary constants */
      NotAvail_IRQn                = -128/**< Not available device specific interrupt */
    
      /* Core interrupts */
      Software0_IRQn               = 0/**< Cortex-A7 Software Generated Interrupt 0 */
      Software1_IRQn               = 1/**< Cortex-A7 Software Generated Interrupt 1 */
      Software2_IRQn               = 2/**< Cortex-A7 Software Generated Interrupt 2 */
      Software3_IRQn               = 3/**< Cortex-A7 Software Generated Interrupt 3 */
      Software4_IRQn               = 4/**< Cortex-A7 Software Generated Interrupt 4 */
      Software5_IRQn               = 5/**< Cortex-A7 Software Generated Interrupt 5 */
      Software6_IRQn               = 6/**< Cortex-A7 Software Generated Interrupt 6 */
      Software7_IRQn               = 7/**< Cortex-A7 Software Generated Interrupt 7 */
      Software8_IRQn               = 8/**< Cortex-A7 Software Generated Interrupt 8 */
      Software9_IRQn               = 9/**< Cortex-A7 Software Generated Interrupt 9 */
      Software10_IRQn              = 10/**< Cortex-A7 Software Generated Interrupt 10 */
      Software11_IRQn              = 11/**< Cortex-A7 Software Generated Interrupt 11 */
      Software12_IRQn              = 12/**< Cortex-A7 Software Generated Interrupt 12 */
      Software13_IRQn              = 13/**< Cortex-A7 Software Generated Interrupt 13 */
      Software14_IRQn              = 14/**< Cortex-A7 Software Generated Interrupt 14 */
      Software15_IRQn              = 15/**< Cortex-A7 Software Generated Interrupt 15 */
      VirtualMaintenance_IRQn      = 25/**< Cortex-A7 Virtual Maintenance Interrupt */
      HypervisorTimer_IRQn         = 26/**< Cortex-A7 Hypervisor Timer Interrupt */
      VirtualTimer_IRQn            = 27/**< Cortex-A7 Virtual Timer Interrupt */
      LegacyFastInt_IRQn           = 28/**< Cortex-A7 Legacy nFIQ signal Interrupt */
      SecurePhyTimer_IRQn          = 29/**< Cortex-A7 Secure Physical Timer Interrupt */
      NonSecurePhyTimer_IRQn       = 30/**< Cortex-A7 Non-secure Physical Timer Interrupt */
      LegacyIRQ_IRQn               = 31/**< Cortex-A7 Legacy nIRQ Interrupt */
    
      /* Device specific interrupts */
      IOMUXC_IRQn                  = 32/**< General Purpose Register 1 from IOMUXC. Used to notify cores on exception condition while boot. */
      DAP_IRQn                     = 33/**< Debug Access Port interrupt request. */
      SDMA_IRQn                    = 34/**< SDMA interrupt request from all channels. */
      TSC_IRQn                     = 35/**< TSC interrupt. */
      SNVS_IRQn                    = 36/**< Logic OR of SNVS_LP and SNVS_HP interrupts. */
      LCDIF_IRQn                   = 37/**< LCDIF sync interrupt. */
      RNGB_IRQn                    = 38/**< RNGB interrupt. */
      CSI_IRQn                     = 39/**< CMOS Sensor Interface interrupt request. */
      PXP_IRQ0_IRQn                = 40/**< PXP interrupt pxp_irq_0. */
      SCTR_IRQ0_IRQn               = 41/**< SCTR compare interrupt ipi_int[0]. */
      SCTR_IRQ1_IRQn               = 42/**< SCTR compare interrupt ipi_int[1]. */
      WDOG3_IRQn                   = 43/**< WDOG3 timer reset interrupt request. */
      Reserved44_IRQn              = 44/**< Reserved */
      APBH_IRQn                    = 45/**< DMA Logical OR of APBH DMA channels 0-3 completion and error interrupts. */
      WEIM_IRQn                    = 46/**< WEIM interrupt request. */
      RAWNAND_BCH_IRQn             = 47/**< BCH operation complete interrupt. */
      RAWNAND_GPMI_IRQn            = 48/**< GPMI operation timeout error interrupt. */
      UART6_IRQn                   = 49/**< UART6 interrupt request. */
      PXP_IRQ1_IRQn                = 50/**< PXP interrupt pxp_irq_1. */
      SNVS_Consolidated_IRQn       = 51/**< SNVS consolidated interrupt. */
      SNVS_Security_IRQn           = 52/**< SNVS security interrupt. */
      CSU_IRQn                     = 53/**< CSU interrupt request 1. Indicates to the processor that one or more alarm inputs were asserted. */
      USDHC1_IRQn                  = 54/**< USDHC1 (Enhanced SDHC) interrupt request. */
      USDHC2_IRQn                  = 55/**< USDHC2 (Enhanced SDHC) interrupt request. */
      SAI3_RX_IRQn                 = 56/**< SAI3 interrupt ipi_int_sai_rx. */
      SAI3_TX_IRQn                 = 57/**< SAI3 interrupt ipi_int_sai_tx. */
      UART1_IRQn                   = 58/**< UART1 interrupt request. */
      UART2_IRQn                   = 59/**< UART2 interrupt request. */
      UART3_IRQn                   = 60/**< UART3 interrupt request. */
      UART4_IRQn                   = 61/**< UART4 interrupt request. */
      UART5_IRQn                   = 62/**< UART5 interrupt request. */
      eCSPI1_IRQn                  = 63/**< eCSPI1 interrupt request. */
      eCSPI2_IRQn                  = 64/**< eCSPI2 interrupt request. */
      eCSPI3_IRQn                  = 65/**< eCSPI3 interrupt request. */
      eCSPI4_IRQn                  = 66/**< eCSPI4 interrupt request. */
      I2C4_IRQn                    = 67/**< I2C4 interrupt request. */
      I2C1_IRQn                    = 68/**< I2C1 interrupt request. */
      I2C2_IRQn                    = 69/**< I2C2 interrupt request. */
      I2C3_IRQn                    = 70/**< I2C3 interrupt request. */
      UART7_IRQn                   = 71/**< UART-7 ORed interrupt. */
      UART8_IRQn                   = 72/**< UART-8 ORed interrupt. */
      Reserved73_IRQn              = 73/**< Reserved */
      USB_OTG2_IRQn                = 74/**< USBO2 USB OTG2 */
      USB_OTG1_IRQn                = 75/**< USBO2 USB OTG1 */
      USB_PHY1_IRQn                = 76/**< UTMI0 interrupt request. */
      USB_PHY2_IRQn                = 77/**< UTMI1 interrupt request. */
      DCP_IRQ_IRQn                 = 78/**< DCP interrupt request dcp_irq. */
      DCP_VMI_IRQ_IRQn             = 79/**< DCP interrupt request dcp_vmi_irq. */
      DCP_SEC_IRQ_IRQn             = 80/**< DCP interrupt request secure_irq. */
      TEMPMON_IRQn                 = 81/**< Temperature Monitor Temperature Sensor (temperature greater than threshold) interrupt request. */
      ASRC_IRQn                    = 82/**< ASRC interrupt request. */
      ESAI_IRQn                    = 83/**< ESAI interrupt request. */
      SPDIF_IRQn                   = 84/**< SPDIF interrupt. */
      Reserved85_IRQn              = 85/**< Reserved */
      PMU_IRQ1_IRQn                = 86/**< Brown-out event on either the 1.1, 2.5 or 3.0 regulators. */
      GPT1_IRQn                    = 87/**< Logical OR of GPT1 rollover interrupt line, input capture 1 and 2 lines, output compare 1, 2, and 3 interrupt lines. */
      EPIT1_IRQn                   = 88/**< EPIT1 output compare interrupt. */
      EPIT2_IRQn                   = 89/**< EPIT2 output compare interrupt. */
      GPIO1_INT7_IRQn              = 90/**< INT7 interrupt request. */
      GPIO1_INT6_IRQn              = 91/**< INT6 interrupt request. */
      GPIO1_INT5_IRQn              = 92/**< INT5 interrupt request. */
      GPIO1_INT4_IRQn              = 93/**< INT4 interrupt request. */
      GPIO1_INT3_IRQn              = 94/**< INT3 interrupt request. */
      GPIO1_INT2_IRQn              = 95/**< INT2 interrupt request. */
      GPIO1_INT1_IRQn              = 96/**< INT1 interrupt request. */
      GPIO1_INT0_IRQn              = 97/**< INT0 interrupt request. */
      GPIO1_Combined_0_15_IRQn     = 98/**< Combined interrupt indication for GPIO1 signals 0 - 15. */
      GPIO1_Combined_16_31_IRQn    = 99/**< Combined interrupt indication for GPIO1 signals 16 - 31. */
      GPIO2_Combined_0_15_IRQn     = 100/**< Combined interrupt indication for GPIO2 signals 0 - 15. */
      GPIO2_Combined_16_31_IRQn    = 101/**< Combined interrupt indication for GPIO2 signals 16 - 31. */
      GPIO3_Combined_0_15_IRQn     = 102/**< Combined interrupt indication for GPIO3 signals 0 - 15. */
      GPIO3_Combined_16_31_IRQn    = 103/**< Combined interrupt indication for GPIO3 signals 16 - 31. */
      GPIO4_Combined_0_15_IRQn     = 104/**< Combined interrupt indication for GPIO4 signals 0 - 15. */
      GPIO4_Combined_16_31_IRQn    = 105/**< Combined interrupt indication for GPIO4 signals 16 - 31. */
      GPIO5_Combined_0_15_IRQn     = 106/**< Combined interrupt indication for GPIO5 signals 0 - 15. */
      GPIO5_Combined_16_31_IRQn    = 107/**< Combined interrupt indication for GPIO5 signals 16 - 31. */
      Reserved108_IRQn             = 108/**< Reserved */
      Reserved109_IRQn             = 109/**< Reserved */
      Reserved110_IRQn             = 110/**< Reserved */
      Reserved111_IRQn             = 111/**< Reserved */
      WDOG1_IRQn                   = 112/**< WDOG1 timer reset interrupt request. */
      WDOG2_IRQn                   = 113/**< WDOG2 timer reset interrupt request. */
      KPP_IRQn                     = 114/**< Key Pad interrupt request. */
      PWM1_IRQn                    = 115/**< hasRegInstance(`PWM1`)?`Cumulative interrupt line for PWM1. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */
      PWM2_IRQn                    = 116/**< hasRegInstance(`PWM2`)?`Cumulative interrupt line for PWM2. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */
      PWM3_IRQn                    = 117/**< hasRegInstance(`PWM3`)?`Cumulative interrupt line for PWM3. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */
      PWM4_IRQn                    = 118/**< hasRegInstance(`PWM4`)?`Cumulative interrupt line for PWM4. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */
      CCM_IRQ1_IRQn                = 119/**< CCM interrupt request ipi_int_1. */
      CCM_IRQ2_IRQn                = 120/**< CCM interrupt request ipi_int_2. */
      GPC_IRQn                     = 121/**< GPC interrupt request 1. */
      Reserved122_IRQn             = 122/**< Reserved */
      SRC_IRQn                     = 123/**< SRC interrupt request src_ipi_int_1. */
      Reserved124_IRQn             = 124/**< Reserved */
      Reserved125_IRQn             = 125/**< Reserved */
      CPU_PerformanceUnit_IRQn     = 126/**< Performance Unit interrupt ~ipi_pmu_irq_b. */
      CPU_CTI_Trigger_IRQn         = 127/**< CTI trigger outputs interrupt ~ipi_cti_irq_b. */
      SRC_Combined_IRQn            = 128/**< Combined CPU wdog interrupts (4x) out of SRC. */
      SAI1_IRQn                    = 129/**< SAI1 interrupt request. */
      SAI2_IRQn                    = 130/**< SAI2 interrupt request. */
      Reserved131_IRQn             = 131/**< Reserved */
      ADC1_IRQn                    = 132/**< ADC1 interrupt request. */
      ADC_5HC_IRQn                 = 133/**< ADC_5HC interrupt request. */
      Reserved134_IRQn             = 134/**< Reserved */
      Reserved135_IRQn             = 135/**< Reserved */
      SJC_IRQn                     = 136/**< SJC interrupt from General Purpose register. */
      CAAM_Job_Ring0_IRQn          = 137/**< CAAM job ring 0 interrupt ipi_caam_irq0. */
      CAAM_Job_Ring1_IRQn          = 138/**< CAAM job ring 1 interrupt ipi_caam_irq1. */
      QSPI_IRQn                    = 139/**< QSPI1 interrupt request ipi_int_ored. */
      TZASC_IRQn                   = 140/**< TZASC (PL380) interrupt request. */
      GPT2_IRQn                    = 141/**< Logical OR of GPT2 rollover interrupt line, input capture 1 and 2 lines, output compare 1, 2 and 3 interrupt lines. */
      CAN1_IRQn                    = 142/**< Combined interrupt of ini_int_busoff,ini_int_error,ipi_int_mbor,ipi_int_txwarning and ipi_int_waken */
      CAN2_IRQn                    = 143/**< Combined interrupt of ini_int_busoff,ini_int_error,ipi_int_mbor,ipi_int_txwarning and ipi_int_waken */
      Reserved144_IRQn             = 144/**< Reserved */
      Reserved145_IRQn             = 145/**< Reserved */
      PWM5_IRQn                    = 146/**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */
      PWM6_IRQn                    = 147/**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */
      PWM7_IRQn                    = 148/**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */
      PWM8_IRQn                    = 149/**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */
      ENET1_IRQn                   = 150/**< ENET1 interrupt */
      ENET1_1588_IRQn              = 151/**< ENET1 1588 Timer interrupt [synchronous] request. */
      ENET2_IRQn                   = 152/**< ENET2 interrupt */
      ENET2_1588_IRQn              = 153/**< MAC 0 1588 Timer interrupt [synchronous] request. */
      Reserved154_IRQn             = 154/**< Reserved */
      Reserved155_IRQn             = 155/**< Reserved */
      Reserved156_IRQn             = 156/**< Reserved */
      Reserved157_IRQn             = 157/**< Reserved */
      Reserved158_IRQn             = 158/**< Reserved */
      PMU_IRQ2_IRQn                = 159               /**< Brown-out event on either core, gpu or soc regulators. */
    } IRQn_Type;
    
    

    3、GIC 逻辑分块

    GIC 架构分为了两个逻辑块: Distributor 和 CPU Interface,也就是分发器端和 CPU 接口端。
    这两个逻辑块的含义如下:
    Distributor(分发器端):从上图可以看出,此逻辑块负责处理各个中断事件的分发问题,也就是中断事件应该发送到哪个 CPU Interface 上去。分发器收集所有的中断源,可以控制每个中断的优先级,它总是将优先级最高的中断事件发送到 CPU 接口端。分发器端要做的主要工作如下:
    1、全局中断使能控制。
    2、控制每一个中断的使能或者关闭。
    3、设置每个中断的优先级。
    4、设置每个中断的目标处理器列表。
    5、设置每个外部中断的触发模式:电平触发或边沿触发。
    6、设置每个中断属于组 0 还是组 1。
    CPU Interface(CPU 接口端):CPU 接口端听名字就知道是和 CPU Core 相连接的,因此在图 17.1.3.2 中每个 CPU Core 都可以在 GIC 中找到一个与之对应的 CPU Interface。CPU 接口端就是分发器和 CPU Core 之间的桥梁,CPU 接口端主要工作如下:
    1、使能或者关闭发送到 CPU Core 的中断请求信号。
    2、应答中断。
    3、通知中断处理完成。
    4、设置优先级掩码,通过掩码来设置哪些中断不需要上报给 CPU Core。
    5、定义抢占策略。
    6、当多个中断到来的时候,选择优先级最高的中断通知给 CPU Core。

    文件 core_ca7.h 定义了 GIC 结构体,此结构体里面的寄存器分为了分发器端和 CPU 接口端,寄存器定义如下所示:

    /*
     * GIC寄存器描述结构体,
     * GIC分为分发器端和CPU接口端
     */
    typedef struct
    {
            uint32_t RESERVED0[1024];
      __IOM uint32_t D_CTLR;                 /*!< Offset: 0x1000 (R/W) Distributor Control Register */
      __IM  uint32_t D_TYPER;                /*!< Offset: 0x1004 (R/ )  Interrupt Controller Type Register */
      __IM  uint32_t D_IIDR;                 /*!< Offset: 0x1008 (R/ )  Distributor Implementer Identification Register */
            uint32_t RESERVED1[29];
      __IOM uint32_t D_IGROUPR[16];          /*!< Offset: 0x1080 - 0x0BC (R/W) Interrupt Group Registers */
            uint32_t RESERVED2[16];
      __IOM uint32_t D_ISENABLER[16];        /*!< Offset: 0x1100 - 0x13C (R/W) Interrupt Set-Enable Registers */
            uint32_t RESERVED3[16];
      __IOM uint32_t D_ICENABLER[16];        /*!< Offset: 0x1180 - 0x1BC (R/W) Interrupt Clear-Enable Registers */
            uint32_t RESERVED4[16];
      __IOM uint32_t D_ISPENDR[16];          /*!< Offset: 0x1200 - 0x23C (R/W) Interrupt Set-Pending Registers */
            uint32_t RESERVED5[16];
      __IOM uint32_t D_ICPENDR[16];          /*!< Offset: 0x1280 - 0x2BC (R/W) Interrupt Clear-Pending Registers */
            uint32_t RESERVED6[16];
      __IOM uint32_t D_ISACTIVER[16];        /*!< Offset: 0x1300 - 0x33C (R/W) Interrupt Set-Active Registers */
            uint32_t RESERVED7[16];
      __IOM uint32_t D_ICACTIVER[16];        /*!< Offset: 0x1380 - 0x3BC (R/W) Interrupt Clear-Active Registers */
            uint32_t RESERVED8[16];
      __IOM uint8_t  D_IPRIORITYR[512];      /*!< Offset: 0x1400 - 0x5FC (R/W) Interrupt Priority Registers */
            uint32_t RESERVED9[128];
      __IOM uint8_t  D_ITARGETSR[512];       /*!< Offset: 0x1800 - 0x9FC (R/W) Interrupt Targets Registers */
            uint32_t RESERVED10[128];
      __IOM uint32_t D_ICFGR[32];            /*!< Offset: 0x1C00 - 0xC7C (R/W) Interrupt configuration registers */
            uint32_t RESERVED11[32];
      __IM  uint32_t D_PPISR;                /*!< Offset: 0x1D00 (R/ ) Private Peripheral Interrupt Status Register */
      __IM  uint32_t D_SPISR[15];            /*!< Offset: 0x1D04 - 0xD3C (R/ ) Shared Peripheral Interrupt Status Registers */
            uint32_t RESERVED12[112];
      __OM  uint32_t D_SGIR;                 /*!< Offset: 0x1F00 ( /W) Software Generated Interrupt Register */
            uint32_t RESERVED13[3];
      __IOM uint8_t  D_CPENDSGIR[16];        /*!< Offset: 0x1F10 - 0xF1C (R/W) SGI Clear-Pending Registers */
      __IOM uint8_t  D_SPENDSGIR[16];        /*!< Offset: 0x1F20 - 0xF2C (R/W) SGI Set-Pending Registers */
            uint32_t RESERVED14[40];
      __IM  uint32_t D_PIDR4;                /*!< Offset: 0x1FD0 (R/ ) Peripheral ID4 Register */
      __IM  uint32_t D_PIDR5;                /*!< Offset: 0x1FD4 (R/ ) Peripheral ID5 Register */
      __IM  uint32_t D_PIDR6;                /*!< Offset: 0x1FD8 (R/ ) Peripheral ID6 Register */
      __IM  uint32_t D_PIDR7;                /*!< Offset: 0x1FDC (R/ ) Peripheral ID7 Register */
      __IM  uint32_t D_PIDR0;                /*!< Offset: 0x1FE0 (R/ )文件 core_ca7.h 定义了 GIC 结构体,此结构体里面的寄存器分为了分
    发器端和 CPU 接口端,寄存器定义如下所示: Peripheral ID0 Register */
      __IM  uint32_t D_PIDR1;                /*!< Offset: 0x1FE4 (R/ ) Peripheral ID1 Register */
      __IM  uint32_t D_PIDR2;                /*!< Offset: 0x1FE8 (R/ ) Peripheral ID2 Register */
      __IM  uint32_t D_PIDR3;                /*!< Offset: 0x1FEC (R/ ) Peripheral ID3 Register */
      __IM  uint32_t D_CIDR0;                /*!< Offset: 0x1FF0 (R/ ) Component ID0 Register */
      __IM  uint32_t D_CIDR1;                /*!< Offset: 0x1FF4 (R/ ) Component ID1 Register */
      __IM  uint32_t D_CIDR2;                /*!< Offset: 0x1FF8 (R/ ) Component ID2 Register */
      __IM  uint32_t D_CIDR3;                /*!< Offset: 0x1FFC (R/ ) Component ID3 Register */
    
      __IOM uint32_t C_CTLR;                 /*!< Offset: 0x2000 (R/W) CPU Interface Control Register */
      __IOM uint32_t C_PMR;                  /*!< Offset: 0x2004 (R/W) Interrupt Priority Mask Register */
      __IOM uint32_t C_BPR;                  /*!< Offset: 0x2008 (R/W) Binary Point Register */
      __IM  uint32_t C_IAR;                  /*!< Offset: 0x200C (R/ ) Interrupt Acknowledge Register */
      __OM  uint32_t C_EOIR;                 /*!< Offset: 0x2010 ( /W) End Of Interrupt Register */
      __IM  uint32_t C_RPR;                  /*!< Offset: 0x2014 (R/ ) Running Priority Register */
      __IM  uint32_t C_HPPIR;                /*!< Offset: 0x2018 (R/ ) Highest Priority Pending Interrupt Register */
      __IOM uint32_t C_ABPR;                 /*!< Offset: 0x201C (R/W) Aliased Binary Point Register */
      __IM  uint32_t C_AIAR;                 /*!< Offset: 0x2020 (R/ ) Aliased Interrupt Acknowledge Register */
      __OM  uint32_t C_AEOIR;                /*!< Offset: 0x2024 ( /W) Aliased End Of Interrupt Register */
      __IM  uint32_t C_AHPPIR;               /*!< Offset: 0x2028 (R/ ) Aliased Highest Priority Pending Interrupt Register */
            uint32_t RESERVED15[41];
      __IOM uint32_t C_APR0;                 /*!< Offset: 0x20D0 (R/W) Active Priority Register */
            uint32_t RESERVED16[3];
      __IOM uint32_t C_NSAPR0;               /*!< Offset: 0x20E0 (R/W) Non-secure Active Priority Register */
            uint32_t RESERVED17[6];
      __IM  uint32_t C_IIDR;                 /*!< Offset: 0x20FC (R/ ) CPU Interface Identification Register */
            uint32_t RESERVED18[960];
      __OM  uint32_t C_DIR;                  /*!< Offset: 0x3000 ( /W) Deactivate Interrupt Register */
    } GIC_Type;
    

    结构体 GIC_Type 就是 GIC 控制器,列举出了 GIC 控制器的所有寄存器,可以通过结构体 GIC_Type 来访问 GIC 的所有寄存器。
    第 5 行是 GIC 的分发器端相关寄存器,其相对于 GIC 基地址偏移为 0X1000,因此我们获取到 GIC 基地址以后只需要加上 0X1000 即可访问 GIC 分发器端寄存器。
    第 51 行是 GIC 的 CPU 接口端相关寄存器,其相对于 GIC 基地址的偏移为 0X2000,同样的,获取到 GIC 基地址以后只需要加上 0X2000 即可访问 GIC 的 CPU 接口段寄存器。

    主要通过CP15协处理来操作GIC

    中断使能

    中断使能包括两部分,一个是 IRQ 或者 FIQ 总中断使能,另一个就是 ID0~ID1019 这 1020个中断源的使能。

    1、IRQ 和 FIQ 总中断使能

    IRQ 和 FIQ 分别是外部中断和快速中断的总开关,就类似家里买的进户总电闸,然后ID0~ID1019 这 1020 个中断源就类似家里面的各个电器开关。要想开电视,那肯定要保证进户总电闸是打开的,因此要想使用 I.MX6U 上的外设中断就必须先打开 IRQ 中断(本教程不使用FIQ)。在“6.3.2 程序状态寄存器”小节已经讲过了,寄存器 CPSR 的 I=1 禁止 IRQ,当 I=0 使能 IRQ;F=1 禁止 FIQ,F=0 使能 FIQ。我们还有更简单的指令来完成 IRQ 或者 FIQ 的使能和禁止:

    指令描述
    cpsid i禁止 IRQ 中断
    cpsie i使能 IRQ 中断
    cpsid f禁止 FIQ 中断
    cpsie f使能 FIQ 中断

    2、ID0~ID1019 中断使能和禁止

    GIC 寄存器 GICD_ISENABLERn 和 GICD_ ICENABLERn 用来完成外部中断的使能和禁止,对于 Cortex-A7 内核来说中断 ID 只使用了 512 个。一个 bit 控制一个中断 ID 的使能,那么就需要 512/32=16 个 GICD_ISENABLER 寄存器来完成中断的使能。同理,也需要 16 个GICD_ICENABLER 寄存器来完成中断的禁止。其中GICD_ISENABLER0 的 bit[15:0]对应ID15~0 的 SGI 中断,GICD_ISENABLER0 的 bit[31:16]对应 ID31~16 的 PPI 中断。剩下的GICD_ISENABLER1 ~ GICD_ISENABLER15 就是控制 SPI 中断的。

    中断优先级设置

    1、优先级数配置

    学过 STM32 都知道 Cortex-M 的中断优先级分为抢占优先级和子优先级,两者是可以配置的。同样的 Cortex-A7 的中断优先级也可以分为抢占优先级和子优先级,两者同样是可以配置的。Cortex-A7 最多可以支持 256 个优先级,数字越小,优先级越高!半导体厂商自行决定选择多少个优先级。I.MX6U 选择了 32 个优先级。在使用中断的时候需要初始化 GICC_PMR 寄存器,此寄存器用来决定使用几级优先级 ,寄存器结构如图 17.1.6.1 所示:
    请添加图片描述
    GICC_PMR 寄存器只有低 8 位有效,这 8 位最多可以设置 256 个优先级,其他优先级数设置如表
    请添加图片描述
    I.MX6U 支持 32 个优先级,所以 GICC_PMR 要设置为 0b11111000。

    2、抢占优先级和子优先级位数设置

    抢占优先级和子优先级各占多少位是由寄存器 GICC_BPR 来决定的
    请添加图片描述寄存器 GICC_BPR 只有低 3 位有效,其值不同,抢占优先级和子优先级占用的位数也不同,配置如表
    在这里插入图片描述为了简单起见,一般将所有的中断优先级位都配置为抢占优先级,比如 I.MX6U 的优先级位数为 5(32 个优先级),所以可以设置 Binary point 为 2,表示 5 个优先级位全部为抢占优先级。

    3、优先级设置

    前面已经设置好了 I.MX6U 一共有 32 个抢占优先级,数字越小优先级越高。具体要使用某个中断的时候就可以设置其优先级为0~31。某个中断 D的中断优先级设置由寄存器D_IPRIORITYR 来完成,前面说了 Cortex-A7 使用了 512 个中断 ID,每个中断 ID 配有一个优先级寄存器,所以一共有 512 个 D_IPRIORITYR 寄存器。如果优先级个数为32的话,使用寄存器 D_IPRIORITYR 的 bit7:4 来设置优先级,也就是说实际的优先级要左移 3 位。比如要设置ID40 中断的优先级为 5,示例代码如下:
    GICD_IPRIORITYR[40] = 5 << 3;
    有关优先级设置的内容就讲解到这里,优先级设置主要有三部分:

    1、设置寄存器 GICC_PMR,配置优先级个数,比如 I.MX6U 支持 32 级优先级。
    2、设置抢占优先级和子优先级位数,一般为了简单起见,会将所有的位数都设置为抢占 优先级。
    3、设置指定中断 ID 的优先级,也就是设置外设优先级。

    代码: link.

    参考
    代码: I.MX6U嵌入式Linux驱动开发指南V1.5.pdf.

    展开全文
  • 1 NVIC中断优先级分组 1.1NVIC的中断向量表 Cortex-M3和CM4内核都支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32F1并没有使用CM3内核的全部东西,...

    1 NVIC中断优先级分组

    1.1 NVIC的中断向量表

            Cortex-M3CM4内核都支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32F1并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32F1有84个中断,包括16个内核中断(异常)和68个可屏蔽中断,具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变,而可屏蔽中断只有60个(在107系列才有68个),本博文只介绍60个外部可屏蔽中断STM32F4也同样没有使用CM4内核的全部东西,而是只用了它的一部分,STM32F40xx/STM32F41xx总共有92个中断,STM32F42xx/STM32F43xx则总共有96个中断。STM32F40xx/STM32F41xx的92个中断里面,包括10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级,而我们常用的就是这82个可屏蔽中断。
            注意一下:CM3和CM4的外部中断和STM32的外部中断不是一个概念。CM3和CM4的外部中断:除了内核异常之外的都是外部中断;STM32的外部中断:外部中断EXTI只有6个。其实,内核中断也叫内核异常,注意中断和异常的区别

    • 中断是指系统停止当前正在运行的程序转而其他服务,可能是程序接收了比自身高优先级的请求,或者是人为设置中断,中断是属于正常现象。 
    • 异常是指由于cpu本身故障、程序故障或者请求服务等引起的错误,异常属于不正常现象。

            下面就简单介绍一下STM32F103系列的16个内核中断(异常)、60个中断:

             在中断向量表中从优先级7-66(中断号从0-59)代表着STM32F103的60个中断。优先级号越小,优先级越高。当表中的某处异常或中断被触发,程序计数器指针(PC)将跳转到该异常或中断的地址处执行,该地址处存放这一条跳转指令,跳转到该异常或中断的服务函数处执行相应的功能。因此,异常和中断向量表只能用汇编语言编写。
            在MDK中,有标准的异常和中断向量表文件可以使用(startup_stm32f10x_hd.s),在其中标明了中断处理函数的名称,不能随意定义。而中断通道NVIC_IRQChannel(即IRQn_Type类型)是在stm32f10x.h文件中进行了宏定义。
            什么是NVIC?即嵌套向量中断控制器(Nested Vectored Interrupt Controller)。CM3的中有一个强大而方便的NVIC,它是属于Cortex内核的器件,中断向量表中60个中断都由它来处理。NVIC是Cortex-M3核心的一部分,关于它的资料不在《STM32的技术参考手册》中,应查阅ARM公司的《Cortex-M3技术参考手册》。Cortex-M3的向量中断统一由NVIC管理。
            NVIC的核心功能是中断优先级分组、中断优先级的配置、读中断请求标志、清除中断请求标志、使能中断、清除中断等,它控制着STM32中断向量表中中断号为0-59的60个中断!!外部中断信号从核外发出,信号最终要传递到NVIC(嵌套向量中断控制器)。NVIC跟内核紧密耦合,它控制着整个芯片中断的相关功能。

     1.2 中断优先级分组寄存器

            这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组,分别为组0-4;同时,对每个中断设置一个抢占优先级和响应优先级。分组配置是由SCB->AIRCR寄存器的bit10-8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的,在文档《Cortex-M3权威指南(中文)》中能查找到。

            分组配置是在寄存器SCB->AIRCR中配置,具体的分配关系如下所示: 

    AIRCR[108]

    IP bit[74]分配情况

    分配结果

    0

    111

    04

    0位抢占优先级,4位响应优先级

    1

    110

    13

    1位抢占优先级,3位响应优先级

    2

    101

    22

    2位抢占优先级,2位响应优先级

    3

    100

    31

    3位抢占优先级,1位响应优先级

    4

    011

    40

    4位抢占优先级,0位响应优先级

             STM32有16个级别(4-bit)优先级可使用:

            其中AIRCR寄存器来确定是用哪种分组,IP寄存器是相对应于那种分组抢占优先级和响应优先级的分配比例。例如组设置成3,那么此时所有的60个中断优先寄存器高4位中的最高3位是抢占优先级,低1位为响应优先级。CM3中定义了8个Bit用于设置中断源的优先级,而STM32只选用其中的4个Bit。

            抢占优先级的级别高于响应优先级,而数值越小所代表的的优先级越高。 
            介绍一下抢占优先级、响应优先级的区别
                    1)高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
                    2)抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
                    3)抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
                    4)如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;

            除此之外有两点需要注意
                    1)打断的情况只会与抢占优先级有关, 和响应优先级无关!
                    2)一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
            优先级举例说明:假定设置中断优先级组为2,然后设置中断3(RTC中断)的抢占优先级为2,响应优先级为1。中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。那么这3个中断的优先级顺序为:中断7>中断3>中断6。

    1.3 中断优先级分组库函数

            CM3核的优先级分组方式,使用的设置函数NVIC_SetPriorityGrouping()接下来介绍STM32的中断优先级分组函数NVIC_PriorityGroupConfig(),用来进行中断分组设置的,此函数是在固件库下misc.c文件中(文件目录是:STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src\misc.c):

    void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
    {
      /* 检查参数 */
      assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
      
      /* 根据“NVIC_PriorityGroup”的值设置PRIGROUP[10:8]位 */
      SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
    }

            函数的参数的取值,是在同文件中进行宏定义的:

    #define NVIC_PriorityGroup_0         ((uint32_t)0x700)   /*!< 0 bits for pre-emption priority 4 bits for subpriority */
    #define NVIC_PriorityGroup_1         ((uint32_t)0x600)   /*!< 1 bits for pre-emption priority 3 bits for subpriority */
    #define NVIC_PriorityGroup_2         ((uint32_t)0x500)   /*!< 2 bits for pre-emption priority 2 bits for subpriority */
    #define NVIC_PriorityGroup_3         ((uint32_t)0x400)   /*!< 3 bits for pre-emption priority 1 bits for subpriority */
    #define NVIC_PriorityGroup_4         ((uint32_t)0x300)   /*!< 4 bits for pre-emption priority 0 bits for subpriority */

    2 中断优先级设置

    2.1 中断优先级设置寄存器

            分组设置好了之后,怎么设置单个中断的抢占优先级和响应优先级?
            MDK为与NVIC相关的寄存器定义了如下的结构体,控制着中断向量表中60个中断(由于与中断内核有关,定义在core_cm3.h文件中):

    /* cortex-m3内核分组方式(8组)结构体表达方式:*/
    typedef struct
    {
      __IO uint32_t ISER[8];                      /*!< Offset: 0x000  Interrupt Set Enable Register           */
           uint32_t RESERVED0[24];                                   
      __IO uint32_t ICER[8];                      /*!< Offset: 0x080  Interrupt Clear Enable Register         */
           uint32_t RSERVED1[24];                                    
      __IO uint32_t ISPR[8];                      /*!< Offset: 0x100  Interrupt Set Pending Register          */
           uint32_t RESERVED2[24];                                   
      __IO uint32_t ICPR[8];                      /*!< Offset: 0x180  Interrupt Clear Pending Register        */
           uint32_t RESERVED3[24];                                   
      __IO uint32_t IABR[8];                      /*!< Offset: 0x200  Interrupt Active bit Register           */
           uint32_t RESERVED4[56];                                   
      __IO uint8_t  IP[240];                      /*!< Offset: 0x300  Interrupt Priority Register (8Bit wide) */
           uint32_t RESERVED5[644];                                  
      __O  uint32_t STIR;                         /*!< Offset: 0xE00  Software Trigger Interrupt Register     */
    }  NVIC_Type;     
    /* STM32分组(5组)方式结构体表达方式*/
    /*******************************
    typedef struct
    {
      vu32 ISER[2];
      u32  RESERVED0[30];
      vu32 ICER[2];
      u32  RSERVED1[30];
      vu32 ISPR[2];
      u32  RESERVED2[30];
      vu32 ICPR[2];
      u32  RESERVED3[30];
      vu32 IABR[2];
      u32  RESERVED4[62];
      vu32 IPR[15];
    } NVIC_TypeDef; 
    *******************************/

            我们依次介绍一下这些寄存器:
            先介绍几个寄存器组长度为8,这些寄存器是32位寄存器。由于STM32只有60个可屏蔽中断,8个32位寄存器中只需要2个就有64位了,每1位控制一个中断。
                    1)ISER[8](Interrupt Set-Enable Registers)中断使能寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。其中只使用到了ISER[0]和ISER[1],ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59。要使能某个中断,就必须设置相应的ISER位为1,使该中断被使能(这仅仅是使能,还要配合中断分组、屏蔽、I/O口映射等设置才算完整)。具体每一位对应哪个中断参考stm32f103x.h里面第140行。
                    2)ICER[8](Interrupt Clear-Enable Registers)中断移除寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。该寄存器的作用于ISER相反。这里专门设置一个ICER来清除中断位,而不是向ISER位写0,是因为NVIC的寄存器写1有效,写0无效。
                    3)ISPR[8](Interrupt Set-Pending Registers)中断挂起控制寄存器--static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);。通过置1可以将正在进行的中断挂起,执行同级或者更高级别的中断。写0无效。
                    4)ICPR[8](Interrupt Clear-Pending Registers)中断解挂控制寄存器--static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn);。通过置1可以将正在挂起的中断解挂。写0无效。
                    5)IABR[8](Interrupt Active-Bit Registers)中断激活标志位寄存器--static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn);。这是一个只读寄存器,可以知道当前在执行的中断是哪一个(为1),在中断执行完后硬件自动清零。
            最后,介绍一个寄存器组长度为240,这个寄存器为8位寄存器240个8位寄存器,每个中断使用一个寄存器来确定优先级。由于CM3由240个外部中断,所以这个寄存器组的数目就是240(注意与上面寄存器的区别,一个是一个寄存器控制一个,一个是一位控制一个)。
                    1)IP[240](Interrupt Priority Registers)中断优先级控制的寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。这是用来控制每个中断的优先级。由于STM32F10x系列一共60个可屏蔽中断,故使用IP[59]~IP[0]。其中每个IP寄存器的高4位[7:4]用来设置抢占和响应优先级(根据分组),低4位没有用到。而两个优先级各占几个位又要由上面讲到的中断优先级分组决定。

            如何理解中断挂起与解挂的含义?
                    中断的挂起与解挂针对的是中断标志位,也可以叫做允许中断发生的位
            针对于中断标志位的操作有什么用?
                    1)一般来说单片机的中断发生有两个条件,一是中断标志位置位,二是中断允许,如果这两个条件都满足则进入中断,因为正常情况下中断一直是允许的,那么只能通过标志位来区分是否有中断挂起;
                    2)如果进入中断不清除标志位,那么这一中断服务程序结束后由于标志位还是置位的并且中断是允许的,那么还会再次进入该中断,就会发生一直在执行中断程序的情况。
            举例说明:在A中断中设置B中断的中断标志位为1,那么当B中断条件满足时,B中断可以发生,但是如果清除A中B中断标志位,那么无论是否满足触发B中断的条件,A中断中永远不可能执行B中断,因为中断发生的条件是:“中断标志位有效+中断条件满足”。

    2.2 中断优先级设置库函数

            接下来介绍如何使用库函数实现中断优先级管理,这里使用NVIC_Init()函数来进行对每个中断优先级的设置(misc.c文件中):

    void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
    {
      uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
      
      /* 检查参数 */
      assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
      assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));  
      assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
        
      if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
      {
        /* 计算相应的IRQ优先级 --------------------------------*/    
        tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
        tmppre = (0x4 - tmppriority);
        tmpsub = tmpsub >> tmppriority;
     
        tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
        tmppriority |=  NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
        tmppriority = tmppriority << 0x04;
            
        NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
        
        /* 启用已选IRQ通道 --------------------------------------*/
        NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
          (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
      }
      else
      {
        /* 禁用已选IRQ通道 -------------------------------------*/
        NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
          (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
      }
    }

            其中,NVIC_InitTypeDef为一个结构体,它的成员变量为:

    typedef struct
    {
      uint8_t NVIC_IRQChannel;                    /*!< 指定要启用或禁用的IRQ通道。该参数可以为@ref IRQn_Type  (有关完整的STM32设备IRQ通道列表,请参阅stm32f10x.h文件) */ 
      uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< 指定NVIC_IRQChannel中指定的IRQ通道的抢占优先级。取值范围为0 ~ 15,如@ref NVIC_Priority_Table所示 */
      uint8_t NVIC_IRQChannelSubPriority;         /*!< 指定NVIC_IRQChannel中指定的IRQ通道的子优先级。取值范围为0 ~ 15,如@ref NVIC_Priority_Table所示 */
      FunctionalState NVIC_IRQChannelCmd;         /*!< 指定是否启用或禁用NVIC_IRQChannel中定义的IRQ通道。可以设置为“启用”或“禁用” */   
    } NVIC_InitTypeDef;

            NVIC_InitTypeDef结构体有4个成员变量
                    1) NVIC_IRQChannel:定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,如USART1_IRQn;
                    2) NVIC_IRQChannelPreemptionPriority:定义此中断的抢占优先级别,具体的值要根据优先级分组来确定,具体参考表格 上图优先级分组真值表
                    3) NVIC_IRQChannelSubPriority:定义此中断的响应优先级别,具体的值要根据优先级分组来确定,具体参考表格 上图优先级分组真值表 
                    4) NVIC_IRQChannelCmd:该中断是否使能,操作的是 NVIC_ISER 和 NVIC_ICER 这两个寄存器。

            NVIC_IRQChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考 stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。IRQn_Type 结构体定义:

    typedef enum IRQn
    {
    /******  Cortex-M3 Processor Exceptions Numbers ***************************************************/
      NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                             */
      MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M3 Memory Management Interrupt              */
      BusFault_IRQn               = -11,    /*!< 5 Cortex-M3 Bus Fault Interrupt                      */
      UsageFault_IRQn             = -10,    /*!< 6 Cortex-M3 Usage Fault Interrupt                    */
      SVCall_IRQn                 = -5,     /*!< 11 Cortex-M3 SV Call Interrupt                       */
      DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M3 Debug Monitor Interrupt                 */
      PendSV_IRQn                 = -2,     /*!< 14 Cortex-M3 Pend SV Interrupt                       */
      SysTick_IRQn                = -1,     /*!< 15 Cortex-M3 System Tick Interrupt                   */
    
    /******  STM32 specific Interrupt Numbers *********************************************************/
      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                      */
    
    #ifdef STM32F10X_HD
      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        */
    #endif /* STM32F10X_HD */  
    }IRQn_Type;
    

            其实我们看NVIC_Init()函数内部使能中断,也是通过ISER寄存器配置的。这与我么之前的内容并不矛盾。函数内部使用NVIC->ISER,而NVIC是core_cm3.h的一个宏定义:

    #define NVIC_BASE           (SCS_BASE + 0x0100)                      /*!< NVIC Base Address                 */
    #define NVIC                ((NVIC_Type *)  NVIC_BASE)               /*!< NVIC configuration struct         */

            也就是直接操作结构体来实现操作ISER寄存器。具体原理可以看STM32-寄存器地址名称映射分析
            比如,使能串口1中断,抢占优先级为1,响应优先级为2,初始化的方法为:

    NVIC_InitTypeDef   NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;           //串口1中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;    // 抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;          // 子优先级位2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);   	         //根据上面指定的参数初始化NVIC寄存器

    3 总结与分析

            最后总结一下中断优先级设置的步骤:
                    1)系统运行后先设置中断优先级分组。调用函数:

    void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

            整个系统执行过程中,只设置一次中断分组;
                    2)针对每个中断,设置对应的抢占优先级和响应优先级:

    void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

                    3)如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。

            注意:在启动文件startup_stm32f10x_hd.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。

    展开全文
  • STM32 NVIC 知识点整理

    2021-05-10 00:11:46
    NVIC(nested vectored interrupt controller) NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 ...

    NVIC(nested vectored interrupt controller)
    NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC 是 Cortex-M 3 的 NVIC 的一个子集

    M3 的 NVIC 支持最多240 个 外部中断(即可屏蔽中断)和16个内核中断(M3 内核中断线),16个可编程优先等级(即每个中断有4bit 可以配置中断优先级,2^4=16)

    PS: F103 只有60个可屏蔽中断,F107 有68个

    NVIC_TYPE结构体如下

    typedef struct
    {
      __IO uint32_t ISER[8];                      /*!< Offset: 0x000  Interrupt Set Enable Register           */
           uint32_t RESERVED0[24];                                   
      __IO uint32_t ICER[8];                      /*!< Offset: 0x080  Interrupt Clear Enable Register         */
           uint32_t RSERVED1[24];                                    
      __IO uint32_t ISPR[8];                      /*!< Offset: 0x100  Interrupt Set Pending Register          */
           uint32_t RESERVED2[24];                                   
      __IO uint32_t ICPR[8];                      /*!< Offset: 0x180  Interrupt Clear Pending Register        */
           uint32_t RESERVED3[24];                                   
      __IO uint32_t IABR[8];                      /*!< Offset: 0x200  Interrupt Active bit Register           */
           uint32_t RESERVED4[56];                                   
      __IO uint8_t  IP[240];                      /*!< Offset: 0x300  Interrupt Priority Register (8Bit wide) */
           uint32_t RESERVED5[644];                                  
      __O  uint32_t STIR;                         /*!< Offset: 0xE00  Software Trigger Interrupt Register     */
    }  NVIC_Type;  
    
    

    ISER[8]:
    Interrupt Set-Enable Registers,这是一个中断使能寄存器组。上面说了 CM3 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断。但是STM32F103 的可屏蔽中断只有 60 个,所以对我们来说,有用的就是两个(ISER[0]和 ISER[1]),总共可以表示 64 个中断。而 STM32F103 只用了其中的前 60 位。ISER[0]的 bit0 ~ bit31 分别对应中断 0 ~ 31。ISER[1]的 bit0 ~ 27 对应中断 32~59;这样总共 60 个中断就分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。
    ICER[8]:
    Interrupt Clear-Enable Registers,是一个中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的。其对应位的功能,也和 ICER 一样。这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。
    ISPR[8]:
    Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。
    ICPR[8]:
    Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断接挂。写 0 无效。
    IABR[8]:
    Interrupt Active Bit Registers,是一个中断激活标志位寄存器组。对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。
    IP[240]:
    Interrupt Priority Registers,是一个中断优先级控制的寄存器组。这个寄存器组相当重要!STM32 的中断分组与这个寄存器组密切相关。IP 寄存器组由 240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。而 STM32 只用到了其中的前 60 个。IP[59] ~ IP[0]分别对应中断 59 ~ 0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是 只用了高 4 位。这 4 位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。

    SCB->AIRCR的中断分组决定了如何去理解IP[x]寄存器的高4bit。
    高4bit 按照如下方式拆分为,抢占优先级和子优先级
    在这里插入图片描述
    中断的执行原则为
    具体优先级的确定和嵌套规则。ARM cortex_m3(STM32)规定
    a/ 只能高抢先优先级的中断可以打断低抢先优先级的中断服务,构成中断嵌套。
    b/ 当 2(n)个相同抢先优先级的中断出现,它们之间不能构成中断嵌套,但 STM32 首先响应子优先级高的中断。
    c/ 当 2(n)个相同抢先优先级和相同子优先级的中断出现,STM32 首先响应中断通道所对应的中断向量地址低的那个中断(见 ROM0008,表 52)。
    具体一点:
    0 号抢先优先级的中断,可以打断任何中断抢先优先级为非 0 号的中断;1 号抢先优先级的中断,可以打断任何中断抢先优先级为 2、3、4 号的中断;……;构成中断嵌套。如果两个中断的抢先优先级相同,谁先出现,就先响应谁,不构成嵌套。如果一起出现(或挂在那里等待),就看它们 2 个谁的子优先级高了,如果子优先级也相同,就看它们的中断向量位置了。

    下图为F103 的10个内部中断和60个外部中断,如图可知,每一个中断通道(包括内核中断和外部中断)都有对应的编号,在stm32f10x.h里面描述(ARM 不清楚外设,没办法定义外部中断)优先级在ST的reference mannul 描述,作为每个中断通道的默认优先级;地址即执行中断要跳转的地址(未确认)。

    在这里插入图片描述
    在这里插入图片描述

    可屏蔽中断(外部中断来源)和外设强相关,最为常见的就是外部IO 的输入中断,外部IO的输入中断由EXIF 管理
    EXTI接入 的通路,共20个,其中0~15 为一般IO 用途,其余四个对应指定用途)
    在这里插入图片描述

    EXIF 的框图如下,

    CM3 中断逻辑
    上图可以看到外部输入是如何通过EXTI(External interrupt/event controller)产生中断和事件的。

    信号线上打一个斜杠并标注“20”字样,这个表示在控制器
    内部类似的信号线路有 20 个。

    编号 1 是输入线, EXTI 控制器有 20 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,这部分内容我们将在后面专门讲解。输入线一般是存在电平变化的信号。

    编号 2 是一个边沿检测电路,它会根据上升沿触发选择寄存器 (EXTI_RTSR) 和下降沿触发选择寄存器 (EXTI_FTSR) 对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 给编号 3 电路,否则输出无效信号 0。而 EXTI_RTSR 和EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。

    编号 3 电路实际就是一个或门电路,它一个输入来自编号 2 电路,另外一个输入来自软件中断事件寄存器 (EXTI_SWIER)。 EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线,这在某些地方非常有用。我们知道或门的作用就是有 1 就为 1,所以这两个输入随便一个有有效信号 1就可以输出 1 给编号 4 和编号 6 电路。

    编号 4 电路是一个与门电路,它一个输入是编号 3 电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为 1 才输出 1,导致的结果是如果 EXTI_IMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 4 电路输出的信号都为 0;如果 EXTI_IMR设置为 1 时,最终编号 4 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_IMR 来实现是否产生中断的目的。编号 4 电路输出的信号会被保存到挂起寄存器(EXTI_PR) 内,如果确定编号 4 电路输出为 1 就会把 EXTI_PR 对应位置 1。

    编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。接下来我们来看看绿色虚线指示的电路流程。它是一个产生事件的线路,最终输出一个脉冲信号。产生事件线路是在编号 3 电路之后与中断线路有所不同,之前电路都是共用的。

    编号 6 电路是一个与门,它一个输入来自编号 3 电路,另外一个输入来自事件屏蔽寄存器 (EXTI_EMR)。如果EXTI_EMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 6 电路输出的信号都为 0;如果 EXTI_EMR 设置为 1 时,最终编号 6 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_EMR 来实现是否产生事件的目的。

    编号 7 是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。

    编号 8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换。

    产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。

    PS:之所以外部信号有在EXTI两条通路,是因为并非所有输入信号都有必要报到NVIC 去执行软件中断,等待内核来处理,实际上,很多操作可以完全交给硬件电路来处理。无需内核调度(譬如DMA搬运,CNT 计数),此时外部信号就可以当做事件来处理,无需向NVIC 提交中断。

    这就是中断和事件的区别。

    如果外部输入被配置为中断,交给NVIC 来处理,则NVIC 需要对外部中断的优先级做一个判断。

    在这里插入图片描述
    NVIC的寄存器中最常用的就是ISER/ICER和IP,注意图中存在问题, IP(Interrupt priority) 这个寄存器名称发生过变更,截止2021.5.10日查询,ARM CM3.h 定义为IP[240], 而老的版本中此处定义为IPR[15](Interrupt priority register),见下图。部分指导文档中存在混用,仍会出现NVIC_IPR,见野火描述优先级时的表述,ST 的programming mannul 同样存在类似问题。
    PS:部分文档中还把IP 高4bit 成为 PRI_N 控制字。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    如果需要调整此外部中断的响应优先级,需要在STM32103.h 找到该中断通道对应的编号,然后对应调整该中断通道的抢占优先级和子优先级,务必注意,抢占优先级和子优先级应当在SCB->AIRCR 生效的范围内配置,否则会导致异常状态。
    调整方式见下图,EXIF 初始化和配置可参考野火的教材
    在这里插入图片描述

    在这里插入图片描述

    参考资料:
    STM32 中断优先级相关概念与使用笔记

    展开全文
  • STM32学习笔记(4)——NVIC中断优先级管理和外部中断EXTI一、NVIC中断优先级管理1. 中断简介2. 中断向量表3. 嵌套向量中断控制器(NVIC)4. NVIC的定义以及库函数 一、NVIC中断优先级管理 1. 中断简介 在Cortex-M3...

    一、NVIC中断优先级管理

    1. 中断简介

    在Cortex-M3(CM3)内核中,每个中断的优先级都是用寄存器中的8位来设置的,这样就有2^8 =256级中断,意味着可以支持256个中断,这其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。但许多芯片厂商并没有使用CM3内核的全部东西,而是只用了它的一部分,而多余的部分应该是设计者考虑到后续应用发展而冗余设计的。

    实际情况中,芯片厂商根据自己生产的芯片做出了调整。比如ST(意法半导体)公司的STM32F1xx和F4xx系列只使用了这个(寄存器NVIC->IPR,如图所示)8位中的高四位[7:4],低四位取零,这样2^4=16,只能表示16级中断嵌套。

    在这里插入图片描述

    STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。我们使用的是STM32F103系列,只有60个可屏蔽中断,而在107系列有68个。

    2. 中断向量表

    中断向量表为每个外设作了硬件编号,可以把它理解为默认顺序。如果有两个外设工作顺序发生冲突(一般在NVIC设置好后就很少发生这种情况)时,就按照这个表来分执行先后。

    STM32的中断向量表如下(可对照STM32中文参考手册9.1.2节中断和异常向量中的表):
    在这里插入图片描述
    在这里插入图片描述

    其中灰色的部分(图片未显示)为异常向量(均为负数),其余白色部分为中断向量。

    在头文件stm32f10x.h的163行开始中定义了各中断向量的顺序编号,现摘录该定义(IRQ = Interrupt Request):

    /******  Cortex-M3 Processor Exceptions Numbers 内核处理器异常编号(用户一般不使用,说白就是不用管) ***************************************************/
      NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                             */
     ······
    
    /******  STM32 specific Interrupt Numbers STM32特定中断号 *********************************************************/
      WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                            */
      PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt            */
      TAMPER_IRQn                 = 2,      /*!< Tamper Interrupt                                     */
      ······
      
    //省略号表示下面还有很多很多,而且使用的是条件编译,因为不同型号对应不同中断号。
    

    在头文件core_cm3.h中可以看到配置与中断相关的寄存器。实际上ST芯片用不到这么大的寄存器,因此我们在网上借鉴了一段代码,反映了ST芯片真实使用到的寄存器大小。其余未使用到的空间均为保留。

    /*
    cortex-m3内核分组方式(8组)结构体表达方式:
    */
    typedef struct
    {
      __IO uint32_t ISER[8];             //中断使能设置寄存器,作用:用来使能中断
      //32位寄存器,每个位控制一个中断的使能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ISER[0]和ISER[1]。
      //ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59;
              /*!< 偏移量: 0x000  Interrupt Set Enable Register           */
           uint32_t RESERVED0[24];      //这些保留的不用看,也不要使用                             
      __IO uint32_t ICER[8];              //中断清除使能寄存器,作用:用来失能中断
      //32位寄存器,每个位控制一个中断的失能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ICER[0]和ICER[1]。
      //ICER[0]的bit0~bit31分别对应中断0~31。ICER[1]的bit0~27对应中断32~59,下面都差不多
            /*!<偏移量: 0x080  Interrupt Clear Enable Register        */
           uint32_t RSERVED1[24];                                    
      __IO uint32_t ISPR[8];              //中断挂起设置寄存器,作用:用来挂起中断,就是我之前关掉这个中断,但现在我想打开它了!
            /*!< 偏移量: 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];               //中断激活状态位寄存器,作用:只读,通过它可以知道当前在执行的中断是哪一个
      //问题:既然只读的话为何不声明 __I ?没搞懂
           /*!< 偏移量: 0x200  Interrupt Active bit Register           */
           uint32_t RESERVED4[56];                                   
      __IO uint8_t  IP[240];               //中断优先级寄存器 
      //240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F10x系列一共60个可屏蔽中断,使用IP[59]~IP[0]。
      //之前已经讲过,每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。
      /*!< 偏移量: 0x300  Interrupt Priority Register (8Bit wide) */
           uint32_t RESERVED5[644];        //软件触发方式寄存器                          
      __O  uint32_t STIR;                         /*!< 偏移量: 0xE00  Software Trigger Interrupt Register     */
    }  NVIC_Type;  
    
    
    /*
    实际STM32分组(5组)方式结构体表达方式
    */
    typedef struct
    {
      vu32 ISER[2]; //v表示volatile,这个关键字很重要,以后有时间去研究研究
      u32  RESERVED0[30];
      vu32 ICER[2];
      u32  RSERVED1[30];
      vu32 ISPR[2];
      u32  RESERVED2[30];
      vu32 ICPR[2];
      u32  RESERVED3[30];
      vu32 IABR[2];
      u32  RESERVED4[62];
      vu32 IPR[15]; //Interrupt Priority Registers,这里可以与上面的IP对应
      //大家可以算算,每个寄存器仅占用4位,15*4=60是不是正好。
    } NVIC_TypeDef;
    
    

    这么多的中断,该怎样管理呢?NVIC这时要出场了。

    3. 嵌套向量中断控制器(NVIC)

    NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。

    需要注意一点:NVIC是Cortex-M3核心的一部分,因此就不要在STM32中文参考手册里面找了(STM32:关我鸟事?),应查阅ARM的Cortex-M3技术参考手册。

    NVIC一个很重要的概念是优先级分组。和51单片机不同,NVIC将优先级分为两个:抢占优先级(PreemptionPriority)和响应优先级(SubPriority)。从英文就知道了,抢占优先级比响应优先级要高。同一个优先级上,数字越小,优先级越高。工作原理如下:

    【情况一】外设B工作时遇到中断请求,外设A需要工作,因为A的抢占优先级高,这时外设B会立刻停止,外设A抢占B开始工作。如果遇到多个中断请求,还会进入中断嵌套,形成“套娃”。所以,抢占优先级是可以嵌套的

    如果是A和B同时到来,因为A的抢占优先级高,会先执行A,后执行B。

    外设抢占优先级响应优先级中断向量号
    A123
    B214

    【情况二】外设B工作时遇到中断请求,外设A需要工作,因为两者抢占优先级相同,此时只能实行先到先得,B弄完后A再来了。所以,响应优先级是不能嵌套的

    如果是A和B同时到来,因为两者抢占优先级相同,此时继续比较响应优先级,B的响应优先级高,会先执行B,后执行A。

    外设抢占优先级响应优先级中断向量号
    A123
    B114

    【情况三】外设B工作时遇到中断请求,外设A需要工作,因为两者抢占优先级和响应优先级相同,所以实行的是先到先得的办法,谁先执行就让谁了。

    如果是A和B同时到来,因为A的中断向量号小,会先执行A,后执行B。

    外设抢占优先级响应优先级中断向量号
    A113
    B114

    4. NVIC的定义以及库函数

    在STM32中,优先级编号不是你想多少就多少的,是有规定的。首先,NVIC对STM32中断进行分组,一共5个组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。分组配置是在寄存器SCB->AIRCR(可在头文件core_cm3.h找到)中配置的。

    如下表:
    在这里插入图片描述

    对应:

    • 第0组:所有4位(仅能用0-15设置优先级别,下同)用于指定响应优先级
    • 第1组:最高1位(0-1)用于指定抢占式优先级,最低3位(0-7)用于指定响应优先级
    • 第2组:最高2位(0-3)用于指定抢占式优先级,最低2位(0-3)用于指定响应优先级
    • 第3组:最高3位(0-7)用于指定抢占式优先级,最低1位(0-1)用于指定响应优先级
    • 第4组:所有4位(0-15)用于指定抢占式优先级

    NVIC的定义(位于头文件misc.h,80行还有一个和上面一样的表格)如下:

    typedef struct
    {
      uint8_t NVIC_IRQChannel;      //指定是哪个外设需要中断,各外设向量号在stm32f10x.h可以看到
    
      uint8_t NVIC_IRQChannelPreemptionPriority;   //设置抢占优先级编号(至于最大可以多少参照上表)
    
      uint8_t NVIC_IRQChannelSubPriority;      //设置响应优先级编号(至于最大可以多少参照上表)
    
      FunctionalState NVIC_IRQChannelCmd;       //使能中断通道
    } NVIC_InitTypeDef;
    

    NVIC的库函数定义(位于头文件misc.h)如下:

    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);
    
    //与挂起和解挂有关的函数:
    static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn)static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn);
    
    //与中断标志激活位有关的函数(作用就是看这个中断有没挂起,别想多了):
    static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn);
    

    使用NVIC的流程如下:

    NVIC_InitTypeDef NVIC_InitStructure;
    // 1.选择优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    // 2.选择需要产生中断的外设
    NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
    // 3.设置抢占优先级级别
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    // 4.设置响应优先级级别
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    // 5.使能中断
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    // 6.初始化中断
    NVIC_Init(&NVIC_InitStructure);
    

    二、外部中断EXTI

    EXTI(External interrupt / event controller)又叫外部中断/事件控制器,EXTI是ST公司在其STM32产品上扩展的外中断控制。它负责管理映射到GPIO引脚上的外中断和片内几个集成外设的中断(PVD,RTC闹钟,USB唤醒,以太网),以及软件中断。其输出最终被映射到NVIC的相应通道。因此,配置EXTI中断的过程必然包含对NVIC的配置。(不严谨理解:NVIC包含EXTI)

    在头文件stm32f10x.h中,我们用的是STM32F10X_HD型,所以有关EXTI的中断向量号(尤其需要注意最后两个)分别如下:

      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, 
      EXTI9_5_IRQn                = 23,     /*!< External Line[9:5] Interrupts                        */
      EXTI15_10_IRQn              = 40,     /*!< External Line[15:10] Interrupts                      */
    

    EXTI由19个(互联型为20个)产生事件/中断请求的边沿检测器组成,每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。

    1. EXTI功能框图

    (该图来自火哥PPT)
    在这里插入图片描述

    对照图中编号顺序来讲讲各个元素(以下寄存器配置对照STM32中文参考手册9.3EXTI寄存器描述):

    (0)信号线

    图中箭头为信号线,可以看到箭头所指方向为信号传输方向,双箭头表示信号可双向传导。上面的“/20”表示在控制器内部类似的信号线路有20个,这里是省略了其余19个信号线(这里显示的是互联型)。这里详细说一下哪些可以作为输入:每个IO都可以作为外部中断输入,中断控制器支持19个外部中断/事件请求。具体如下:

    • 线(EXTI_Linex,下同)0-15:对应外部IO口的输入中断(PX0、PX1、···、PX15,X = A、B、C、D、E、F、G、H、I)
    • 线16:连接到PVD输出
    • 线17:连接到RTC闹钟事件
    • 线18:连接到USB唤醒事件
    • 线19(只适用于互联型):连接到以太网唤醒事件

    下图显示的是在AFIO_EXTICR寄存器的EXTIx位(AFIO是复用GPIO,以后会讲到):
    在这里插入图片描述

    (1)输入线

    EXTI有19个中断/事件输入线,中断输入可以来自GPIO,也可以来自外设。

    (2)边沿检测电路

    可以通过寄存器设置电路是上升沿触发(EXTI_RTSR)或下降沿触发(EXTI_FTSR)或两者皆触发。若检测到有效信号则输出高电平(1),否则低电平(0)。

    (3)或门

    输入分别是来自边沿检测电路和软件中断寄存器(EXTI_SWIER),只要有一个输入为高电平,输出即为高电平。

    (4)与门

    输入分别来自中断屏蔽寄存器(EXTI_IMR)和请求挂起寄存器(EXTI_PR),需要两个输入为高电平,输出才为高电平。

    (5)输出至NVIC

    将EXTI_PR寄存器内容输出到NVIC内,从而实现系统中断事件控制。以上均为NVIC路线。

    (6)与门

    输入分别来自事件屏蔽寄存器(EXTI_EMR)和或门输出,需要两个输入为高电平,输出才为高电平。

    (7)脉冲发生器

    当输入端,即与门(6)的输出端,是一个高电平就会产生一个脉冲;如果输入端是低电平就不会输出脉冲。

    (8)产生事件

    脉冲发生器产生的脉冲信号,是事件线路最终的产物,这个脉冲信号可以给其他外设电路使用。

    2. EXTI的定义以及库函数

    在头文件stm32f10x_exti.h中定义了结构体EXTI:

    typedef struct
    {
      uint32_t EXTI_Line;   
      // EXTI中断/事件线选择,参数为EXTI_Linex 
      EXTIMode_TypeDef EXTI_Mode;       
      // EXTI模式选择:中断(EXTI_Mode_Interrupt)触发或者事件(EXTI_Mode_Event)触发    
      EXTITrigger_TypeDef EXTI_Trigger;
      // EXTI边沿触发类型选择:上升沿触发(EXTI_Trigger_Rising)、下降沿触发(EXTI_Trigger_Falling) 
      //或者上升沿和下降沿都触发(EXTI_Trigger_Rising_Falling)
      FunctionalState EXTI_LineCmd;     
      // 使能EXTI
    }EXTI_InitTypeDef;
    

    同时,也定义了库函数(参考原子PPT):

    void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
    //设置IO口与中断线的映射关系
    
       exp:  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
    //初始化中断线:触发方式等
    
    ③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
    //判断中断线中断状态,是否发生void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
    //清除中断线上的中断标志位
    
    

    在设置好EXTI后,还要指定中断后程序要干什么,这就需要用户自己定义中断服务函数,但是函数名不能乱起,在startup_stm32f10x_hd.s文件中已经写好了中断向量表的顺序(从62行__Vectors开始),这些一大堆下来都是中断函数名,你只能用这些。至于怎么写,待会举个例子就好了。

    使用EXTI的流程较繁琐,如下:

    // 0.先配置想要产生中断的GPIO,用GPIO配置的方法配置
    EXTI_InitTypeDef EXTI_InitStructure;
    // 1.开启(使能)GPIO复用时钟(关键一步)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    // 2.选择IO口与中断线的映射关系
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
    // 3.EXTI中断/事件线选择	
    EXTI_InitStructure.EXTI_Line = EXTI_Line3;
    // 4.EXTI边沿触发类型选择
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    // 5.EXTI模式选择
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    // 6.使能EXTI
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    // 7.初始化EXTI
    EXTI_Init(&EXTI_InitStructure);
    // 8.编写中断服务函数,中断后你想干嘛都写在这里面
    EXTIx_IRQHandler(){···}
    // 9.别忘了在中断服务函数里面清除中断标志位
    EXTI_ClearITPendingBit();
    
    

    三、一个简单的例程

    功能:按键KEY1按下,实现一次LED0反转。初始状态时LED0灭。用NVIC和EXTI中断实现。

    部分程序(省略led部分)如下:

    // main.c
    #include  "stm32f10x.h"
    #include  "led.h"
    #include  "delay.h"
    #include  "exti_config.h"
    
    int main(void)
    { 
    	LED_Init();
    	delay_init();
    	EXTI_Key_Config();
    	EXTI_NVIC_Config();
    	
    	while(1){}
    		
    }
    
    
    //exti_config.h
    #ifndef __EXTI_CONFIG_H
    #define __EXTI_CONFIG_H
    
    #include "stm32f10x.h"
    #include "led.h"
    #include "delay.h"
    
    #define KEY1 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)
    
    void EXTI_Key_Config(void);
    void EXTI_NVIC_Config(void);
    
    #endif /* __EXTI_CONFIG_H */
    
    
    //exti_config.c
    #include "exti_config.h"
    
    void EXTI_NVIC_Config(void)
    {
    	NVIC_InitTypeDef NVIC_InitStructure;
    	
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // NVIC组别为0
    	
    	NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级为1
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级为1
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStructure);
    }
    
    // KEY1: PE3
    void EXTI_Key_Config(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	EXTI_InitTypeDef EXTI_InitStructure;
    	
    	//初始化GPIOE.3 
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    	GPIO_Init(GPIOE, &GPIO_InitStructure);
    	
    	//为GPIOE.3配置EXTI
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
    	
    	EXTI_InitStructure.EXTI_Line = EXTI_Line3;
    	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    	EXTI_Init(&EXTI_InitStructure);
    }
    
    //中断服务函数
    void EXTI3_IRQHandler(void)
    {
    	delay_ms(10); //按键延时判断
    	if(KEY1 == 0)
    	{
    		LED0 = !LED0; 
    	}
    	EXTI_ClearITPendingBit(EXTI_Line3); //记得最后清除中断标志位,为下一次中断做准备
    }
    
    展开全文
  • 系列文章总目录 【STM32】实验解析目录 目录 一、中断介绍 1.1 中断概念 1.2 NVIC介绍 1.3 中断优先级 二、中断配置步骤 三、结构体解读 3.1 程序样例 3.2 程序解读 1、创建NVIC结构体变量 2、NVIC结构体 1、NVIC...
  • NVIC的使用

    千次阅读 2021-05-05 21:19:00
    NVIC相关内容 中断编程的四个部分—— 1.使能中断请求; 2.配置中断优先级分组(一个项目中只需要配置一次) 3.配置NVIC寄存器,初始化NVIC_InitTypeDef; 4.编写中断服务函数。 中断过程的三个部分—— 1.配置NVIC_...
  • 文章目录 抢占优先级和响应优先级 抢占 响应 NVIC 的优先级组 中断寄存器 ISER[8] ICER[8] ISPR[8] ICPR[8] IABR[8] IP[240] 中断优先级设置步骤总结 Ref. 抢占优先级和响应优先级 STM32 的中断向量具有两个属性,一...
  • STM32 NVIC与中断控制

    2021-12-16 14:23:39
    STM32 NVIC与中断控制学习 NVIC全称向量中断控制器,NVIC 共支持 1 至 240 个外部中断输入(通常外部中断写作 IRQs)。NVIC 还支持一个 “永垂不朽”的不可屏蔽中断(NMI)输入。NVIC 的访问地址是 0xE000_E000。...
  • STM32复习笔记(八) —— EXTI(NVIC与外部中断) MDK版本: MDK 5.35.0.0 芯片型号: STM32F103C6 固件库版本: STM32F10x_StdPeriph_Lib_V3.5.0 1.芯片资源对比 (STM32F103C8 and STM32F103C6) 主要...
  • NVIC与外部中断

    2021-04-16 19:11:09
    RCC,NVIC,GPIO,USART,EXIT配置 GPIOA.0 为按键 GPIO用作EXTI外部中断时,需要打开AFIO #ifdef VECT_TAB_RAM NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0000000); #else NVIC_SetVectorTable(NVIC_VectTab_FLASH,...
  • STM32 CubeIDE(六)NVIC与EXTI NVIC NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。NVIC控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在...
  • STM32-中断优先级管理NVIC 1.NVIC中断优先级分组 NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。STM32F中文参考手册中搜索向量表可以找到相应的中断说明。 CM4/CM7 内核支持256个中断,...
  • void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) { /* Check the parameters */ assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); /* Set the PRIGROUP[10:8] bits according to NVIC_
  • STM32学习笔记——NVIC

    2021-06-27 16:37:12
    STM32学习笔记——NVIC 1、设置中断分组 HAL_NVIC_SetPriorityGrouping void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { /* Check the parameters */ assert_param(IS_NVIC_PRIORITY_GROUP...
  • STM32F1----NVIC

    2021-04-11 16:29:25
    1.NVIC初始化流程 <1>配置中断优先级分组 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); <2>建立NVIC初始化结构体 NVIC_InitTypeDef NVIC_InitStructure; <3>配置NVIC结构体并...
  • 1. 什么是NVIC? 注:本博文只是对正点原子课程的又一次重复,总结正点的教程、写一些自己的感悟,并非以原创之名做抄袭之事。 一、STM32F4中断的介绍 1. STM32F4的中断个数、类型 STM32F4的内核是ARM家的Cortex-M4...
  • 实现原理图 systick定时器进行工作时,先将LOAD的值置入VAL寄存器中,VAL开始倒数减一,每一个减一经过一个Systick定时器设置的时钟频率 NVIC NVIC,提供中断控制器,用于总体管理异常,称之为“内嵌向量中断控制器...
  • NVIC中断管理---STM32

    2021-11-10 16:03:15
    NVIC:嵌套向量中断控制器 在了解中断之前,我们先了解抢占优先级和响应优先级 抢占优先级和响应优先级 抢占优先级决定能不能打断,高优先级的抢占可以中断低优先级的抢占 响应优先级在抢占优先级相同时,高响应...
  • 无论学习哪款单片机,都会涉及中断,包括在之后学习FreeRTOS操作系统,也会...NVIC 的寄存器以存储器映射的方式来访问,除了包含控制寄存器和中断处理的控制逻辑之外,NVIC 还包含了 MPU的控制寄存器、SysTick 定时器以
  • 【STM32】NVIC的基本含义及基本配置

    千次阅读 2021-03-19 16:04:00
    目录 抢占式优先级(占先式优先级)和响应优先级(子优先级...但是各个芯片厂商在设计芯片的时候会对 Cortex-M4内核里面的 NVIC进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC 是 Cortex-M4的 NVIC 的一个子集,只是
  • STM32的NVIC中断分组

    2021-11-15 14:41:24
    PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种: NVIC_PriorityGroup_0 => 选择第0组 NVIC_PriorityGroup_1 => 选择第1组 NVIC_PriorityGroup_2 => 选择第2组 NVIC_PriorityGroup_3 =>...
  • NVIC中断优先级管理

    2021-05-25 18:27:25
    //设置系统中断优先级分组为2 2、给每个中断进行抢占优先级和响应优先级的设置 使用函数: NIVC_Init(NVIC_InitTypeDef* NVIC_InitStruct) 其中这里和我们初始化GPIO一样有一个结构体参数需要我们传入,结构体定义...
  • 目录 1. 跑马灯 1.1 跑马灯硬件连接 1.2 跑马灯实验讲解 1.2.1 实验步骤 1.2.2 代码及注释 2. 蜂鸣器 2.1 蜂鸣器硬件连接 2.2 蜂鸣器实验讲解 2.2.1 实验步骤 ...4. NVIC中断优先级管理 4.1 N...
  • stm32之外部中断EXTI和NVIC 中断如何配置步骤如下 一、初始化用来中断的GPIO口 二、初始化EXTI(外部中断) 三、配置NVIC(中断优先级)中断控制器 四、编写中断函数 一、初始化用来中断的GPIO口 以GPIOA1为例: ...
  • 文章目录系列文章目录前言一、STM32 NVIC 中断优先级管理二、STM32 的中断分组 前言 使用中断前,需了解中断优先级管理, CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256 级...
  • Cortex-M3中断NVIC 概览

    2021-06-12 16:13:56
    中断控制器,简称 NVIC,是 Cortex-M3 不可分离的一部分,它与 CM3 内核的逻辑紧密耦合,有一部分甚至水乳交融在一起。 NVIC 与 CM3 内核同声相应,同气相求,相辅相成,里应外合,共同完成对中断的响应。 NVIC 的...
  • 相关链接:stm32学习笔记——中断组别和优先级配置_最弱勇者哀嚎地-CSDN博客中断组别配置,NVIC中断函数在misc.c文件中NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);NVIC_PriorityGroup有5个有效参数,...
  • 中断的类型、触发方式 5、配置NVIC_Init() //中断优先级 6、编写中断服务函数 3、外部中断的主要配置说明 1)NVIC 外部出现满足中断条件,开始执行中断服务函数,如同时出现多个中断,则根据NVIC设定的优先级开始...
  • NVIC_IRQChannelPreemptionPriority; //先占优先级 NVIC_IRQChannelSubPriority; //从优先级 1、两者之间的逻辑关系: 具有高先占式优先级的中断可以在具有低先占式优先级的中断处理过程中被响应,即中断嵌套, ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,863
精华内容 10,745
关键字:

NVIC