STM32

您所在的位置:网站首页 stm32f103中断优先级 STM32

STM32

2023-08-01 03:46| 来源: 网络整理| 查看: 265

1 NVIC中断优先级分组 1.1 NVIC的中断向量表

        Cortex-M3和CM4内核都支持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[10:8]

IP bit[7:4]分配情况

分配结果

0

111

0:4

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

1

110

1:3

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

2

101

2:2

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

3

100

3:1

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

4

011

4:0

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 NVIC_IRQChannelSubPriority & tmpsub; tmppriority = tmppriority IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority; /* 启用已选IRQ通道 --------------------------------------*/ NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = (uint32_t)0x01 NVIC_IRQChannel & (uint8_t)0x1F); } else { /* 禁用已选IRQ通道 -------------------------------------*/ NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = (uint32_t)0x01 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 这个库文件中。关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3