STM32:NVIC
1 NVIC
NVIC全称nested vectored interrupt controller,嵌套 向量 中断控制器;cm3的所有中断机制都由NVIC管理;
1.1 "向量"
cm3自动初始化了中断响应事件与中断服务程序入口地址相对应的向量表,如下表所示;
cm3中断响应后,cm3通过nvic直接控制程序在内核层跳转至中断服务程序入口地址执行,速度快效率高;
启动文件为每个中断事件都预先写了一个中断空函数,目的是初始化向量表地址;中断函数可统一重新编写在stm32f10x_it.c中;
编号 | 类型 | 优先级 | 默认地址 | 功能 |
0 |
\
|
\
|
0x0000 0000
|
无异常运行,当前地址数据为MSP初始值;
|
1 | Reset_Handler | -3(max) | 0x0000 0004 | 上电复位 |
2 | NMI | -2 | 0x0000 0008 | 不可屏蔽中断(外部NMI引脚输入) |
3 | hard fault | -1 | 0x0000 000C | 使能所有被除能的fault中断为hard fault |
4 | MemManage fault | 可编程 | 0x0000 0010 |
存储器管理fault,MPU 访问犯规以及访问非法位置
|
5 |
bus fault
|
0x0000 0014 |
总线错误(预取流产(Abort)或数据流产)
|
|
6 |
usage fault
|
0x0000 0018 |
程序错误的异常 ,通常为指令无效或非法状态转换
|
|
7-10 |
\
|
\
|
\
|
|
11 |
SVCall
|
可编程
|
0x0000 002C
|
系统服务调用
|
12 | debug monitor | 0x0000 0030 |
调试监视器 (断点,数据观察点,或者是外部调试请求)
|
|
13 |
\
|
\
|
0x0000 0034
|
\
|
14 |
PendSV
|
可编程 | 0x0000 0038 |
为系统设备而设的“可悬挂请求”(pendable request)
用来实现中断嵌套时,应把PendSV的优先级编程为最低;
|
15 |
systick
|
0x0000 003C |
cm3自带的systick中断
|
|
16 |
IRQ_0 (WWDG)
|
0x0000 0040 |
外设中断0
|
|
17 |
IRQ_1 (PVD)
|
0x0000 0044 |
外设中断1
|
|
... | ... | ... | ... | ... |
255 |
IRQ_239
|
可编程 | 0x0000 03FF |
外设中断239
|
不同芯片interrupts支持 由芯片厂商裁剪;STM32F10X_HD型号支持60+8(core_periph) interrupts; | ||||
cm3在exception entry时,自动压栈了 R0‐R3, R12, LR, PSR 和 PC,结束异常后自动弹回,无需指令执行;
|
||||
向量表地址可以通过修改向量表偏移量寄存器(0xE000_ED08)重定向至flash或rom中; | ||||
本表格中断编号、优先级与cm3不一样;具体看代码struct IRQn_Type; |
1.1.1 NMI中断
NMI全称Non Maskable Interrupt,不可屏蔽中断;
大概作用相当于判断中断信号是否有效、是否抖动无效;配置程序是否由threa模式进入handler模式;
1.1.2 内核中断
除了NMI之外还有好几个内核中断的功能作用,这些内核中断具体干什么和nvic关系不大我就不看了;
1.2 “嵌套”
cm3使用一个8bit的"优先级配置寄存器ip_reg[7:0]"来配置优先级,只支持16个优先级,可管理256个中断优先级;
cm3使用的"ip_reg[3:0]" \ 始终读为0,cm3只可以配置"ip_reg[7:4]"所以只支持16个中断优先级;
高优先级的中断响应可以打断低优先级的中断函数执行,将低优先级的中断函数悬起,等待之后弹回继续执行;
1.2.1 优先级
cm3将优先级配置寄存器的bit位分为两部分,命名为抢占优先级和子优先级;
优先级配置寄存器地址MSB对齐,目的是防止程序移植后[MSB]丢失,导致高优先级中断变成低优先级中断;
(1) 抢占优先级pre-emption priority
包含[ MSB ]所在bit的优先级配置寄存器的地址区域;抢占优先级最多7bit,最多可管理128个中断抢占优先级;
(2) 子优先级subpriority
包含[ LSB ]所在bit的优先级配置寄存器的地址区域;子优先级最少1bit,最少可管理1个中断子优先级;
1.2.2 优先级 “嵌套”
如果中断函数执行时,cm3检测到抢占优先级高的中断响应,那么cm3就会pending当前中断响应,转而去执行新的中断响应函数;
如果中断函数执行时,cm3检测到新的响应和当前响应抢占优先级相同时,那么就通过子优先级去判断是否pending当前中断响应;
2 NVIC寄存器
NVIC一共有7个寄存器,其中STIR寄存器不理解就被我跳过了;nvic初始化函数主要配置IPR、ISER寄存器;
如果外设使能了中断函数,但是该外设对应的nvic_ISER_bit未使能,那么cm3不会跳转执行中断函数;
内核外设不需要配置nvic_ISER,通过SCB配置优先级以及使能中断函数,就可以跳转执行中断函数了;或者说内核外设也没有nvic_ISER可配置;先放着;
nvic_ICER寄存器的写bit、读bit相同数值的功能是相反的,这个rc_w1有点离谱;
ip_reg[7:0]只支持16个优先级的意思是虽然最多有81个中断事件,但是中断优先级只能配置16个;
2.1 中断管理
启动文件中中断地址顺序初始化,所以每个中断的偏移地址始终不变,也就是中断编号始终不变,而中断优先级可编程;
这样的话给每个中断都来一个IP寄存器显然更容易记,更便捷查找;
图片来自pm0056_cm3编程手册,4.3节;每个寄存器里都放了81个配置,这里中断编号只有68个大约是打错了。
表44放这里的目的,是为了表达81个IPR寄存器的地址分布;
3 标准库函数
/****misc.h***/ typedef struct { uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled. This parameter can be a value of @ref IRQn_Type (For the complete STM32 Devices IRQ Channels list, please refer to stm32f10x.h file) */ uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel specified in NVIC_IRQChannel. This parameter can be a value between 0 and 15 as described in the table @ref NVIC_Priority_Table */ uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified in NVIC_IRQChannel. This parameter can be a value between 0 and 15 as described in the table @ref NVIC_Priority_Table */ FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel will be enabled or disabled. This parameter can be set either to ENABLE or DISABLE */ } NVIC_InitTypeDef; /** @defgroup NVIC_Priority_Table @code The table below gives the allowed values of the pre-emption priority and subpriority according to the Priority Grouping configuration performed by NVIC_PriorityGroupConfig function ============================================================================================================================ NVIC_PriorityGroup | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority | Description ============================================================================================================================ NVIC_PriorityGroup_0 | 0 | 0-15 | 0 bits for pre-emption priority | | | 4 bits for subpriority ---------------------------------------------------------------------------------------------------------------------------- NVIC_PriorityGroup_1 | 0-1 | 0-7 | 1 bits for pre-emption priority | | | 3 bits for subpriority ---------------------------------------------------------------------------------------------------------------------------- NVIC_PriorityGroup_2 | 0-3 | 0-3 | 2 bits for pre-emption priority | | | 2 bits for subpriority ---------------------------------------------------------------------------------------------------------------------------- NVIC_PriorityGroup_3 | 0-7 | 0-1 | 3 bits for pre-emption priority | | | 1 bits for subpriority ---------------------------------------------------------------------------------------------------------------------------- NVIC_PriorityGroup_4 | 0-15 | 0 | 4 bits for pre-emption priority | | | 0 bits for subpriority ============================================================================================================================ @endcode */
/***stm32f10x.h***/ 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;
/***misc.c misc.h***/ #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 */ #define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \ ((GROUP) == NVIC_PriorityGroup_1) || \ ((GROUP) == NVIC_PriorityGroup_2) || \ ((GROUP) == NVIC_PriorityGroup_3) || \ ((GROUP) == NVIC_PriorityGroup_4)) 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_PriorityGroup value */ SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; }
/***misc.c misc.h***/ #define IS_NVIC_PREEMPTION_PRIORITY(PRIORITY) ((PRIORITY) < 0x10) #define IS_NVIC_SUB_PRIORITY(PRIORITY) ((PRIORITY) < 0x10) #define IS_NVIC_OFFSET(OFFSET) ((OFFSET) < 0x000FFFFF) void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) { uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F; /* Check the parameters */ 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) { /* Compute the Corresponding IRQ Priority --------------------------------*/ 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; /* Enable the Selected IRQ Channels --------------------------------------*/ NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F); } else { /* Disable the Selected IRQ Channels -------------------------------------*/ NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F); } }
//先设置优先级组,只需设置一遍; //然后设置抢占优先级,不同外设可以配置不同的抢占优先级; //然后响应的时候,如果当前中断的抢占优先级高于之前的中断,那就直接响应当前中断;不需要比较子优先级; // 如果当前中断的优先级和之前的中断优先级一样,那再比较子优先级; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
4 小结
博客教程看来看去完全看不出框架头绪,建好框架之后再看别人的博客教程又很容易捋思绪;
还是老老实实看中文参考手册、官方编程手册、数据手册然后加上源码才是正道;
可能自己有一点强迫症,自己写的博客如果不按照自己心中既定的思路写,按着别人的思路走基本记不住;
可是悲伤的是自己的心中常常没有既定的思路,最耗费时间精力的就是这个搭思路了,唉;
nvic的SCB system control block系统控制单元,EXTI外部中断事件控制器的相关部分也应该捋一捋,只能先占个坑了;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)