NVIC与EXTI
中断
基本概念
中断源:触发中断条件
断点:中断执行完之后,返回该断点;
中断服务函数:中断处理;快进快出;
中断可嵌套
Cortex-M3的NVIC
NVIC是 内核的外设
Cortex-M3 支持 240 个优先级可动态配置的中断,每个中断的优先级有 256 个选择。 中断优先级的数目可配置为 1~8 位( 1~256 级)。
51单片机根据中断号,执行相应的中断服务;
NIVC:根据中断号,找对应的中断服务函数;(也就是将程序指针,指向对应 中断号 的 中断入口)
异常和中断
中断是一种我们可以操控的异常;
内核的异常:0~15是已经固化好的;16及以上是芯片厂商做的;
硬件优先级
制定软件优先级后,硬件优先级(可调整)无效;
如果两个或更多的中断指定了相同的优先级, 则由它们的硬件优先级来决定处理器对它们进行处理时的顺序。

内核的外部中断:是相对于内核来说的 “外部”
a. 该异常的优先级可修改,见系统处理器优先级寄存器的位分配。可调整的范围为 NVIC 的 0~N 优先级, N 为能够实现的最高优先级。在内部,用户可设置的最高优先级( 0)被看作 4。
b. 您可以使能或禁止该故障。见系统处理器控制和状态寄存器的位分配。
内核的NVIC寄存器配置
参考手册:Cortex-M3内核编程手册
优先级分组:
- 中断同时到来时先执行高优先级;
- 分配优先级的 占先 和 次级;其实就是分配 NVIC 的
IPR寄存器 8位数据 中 小数点 的位置;
Cortex-M3的中断优先级分组

是内核支持的优先级分组;
STM32 只用了 4个位 来分配优先级分组;
只有 16个 不同的 优先级分组
如果使用小于 8 的位来配置处理器的优先级,则寄存器的低位始终为 0。例如,如果使用 4 个位来配置优先级,则 PRI_N[7:4]用来配置优先级,而 PRI_N[3:0]为 4`b0000。
STM32的内核中断优先级分组
[内核参考手册]
是由
这个寄存器的
三个位控制的;
从图上可以看出:STM32设置分组的时候:写入的数据大小是
3-7
内核提供的 “优先级分组” 函数:
void NVIC_SetPriorityGrouping(uint32_t priority_grouping);传参为 3~7
设置优先级
优先级的小数点位数是由 “优先级分组” 的时候就规定好的;
设置优先级:
- 抢占(占先)
- 次级
NIVC 的 IPR 寄存器的设置
翻译:
- 可以有0-255个优先级,值越小,优先级越高;
- STM32中 使用了 高4位 作为优先级的设置;第四位默认为0;
- 优先级设置的值范围:0~15;这个优先级包含了 “抢占” 和 “次级”
内核提供的 “设置优先级” 函数:
void NVIC_SetPriority (IRQn_t IRQn, uint32_t priority) 传入终端号;写入对应的 优先级
0~15
STM32的中断

STM32的中断向量表
硬件的中断顺序


.....................
“位置“ 就是 “中断号“
启动程序中的中断向量表
启动程序的中断向量表:就是 “中断服务函数名”
”函数名就是一个地址“
写 “中端服务函数” 的时候,直接去
.s文件中找 “函数名”
中断向量表
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__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
EXTI中断
控制方式
- “外部触发” 和 “软件触发” 是 “或” 关系;其中一个成立,就会将中断挂起;
- “请求挂起” 和 “中断屏蔽” 是 “与” 关系;两者同时成立,才会向NVIC申请中断请求;
EXTI中断号
EXTI0 EXTI1 EXTI2 EXTI3 EXTI4 EXTI9-5 EXTI15-10
配置步骤
- 开
AFIO时钟 - IO口 映射 到 EXTI线 上
- 配置触发方式
- 清除中断标志
- 开启 EXTI 中断
- 配置 优先级
- 使能NVIC
外部中断的映射
映射:就是把 IO 口 接到 EXTI0 的中断源;
梯形:是一个选择开关;
每个 EXTI 中断源,只能选择一个;
IO 的 EXTI中断的选择 在 AFIO_EXTICR1 寄存器中配置;
选择 EXTI 的中断源
将寄存器的 位 设置成相应的 值 就行了;
EXTI寄存器
- 中断屏蔽寄存器--使能EXTI中断
- 上升/下降沿 沿触发寄存器--设置EXTI中断源的触发方式
- 挂起寄存器--中断的状态
中断屏蔽寄存器
中断开启
中断触发条件
上升沿触发 / 下降沿触发
中断挂起
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 EXTI9_5_IRQHandler ; EXTI Line 9..5像 5~9 的 外部中断 需要 在 “中断同一个服务函数” 中判断具体的 “状态寄存器”;
为了方便,每个中断都可以检查一下对应的 “状态位”;
一个 “服务函数” 对应 多个 “中断源” 的时候,要判断中断源对应的 “状态”
EXTI的寄存器配置
选择中断源
配置 PA0 为 EXTI0 的中断源
配置寄存器的 4个位 为 0000

EXTICR在 结构体 中使用 数组来定义的;
EXTI中断方式
上升沿触发、下降沿触发、上升沿和下降沿触发
EXTI->RTSR |= (1<<0);
打开EXTI中断
PA0 对应中断屏蔽位 置1
EXTI->IMR |= (1<<0);
中断优先级分组
只需要配置一次,在主函数开头使用
传参 3 ~ 7 => 5
NVIC_SetPriorityGrouping(5);
优先级配置
0~16;
如果中断分组为 5;
中断优先级为 “4”; // 4 = 01 00 (抢占:1;次级:0)
NVIC_SetPriority(EXTI0_IRQn, 3);
使能NVIC中对应的中断
内核提供了函数:
void NVIC_EnableIRQ(IRQn_t IRQn)
void NVIC_DisableIRQ(IRQn_t IRQn)
中断服务函数
中断服务函数名
查看
.s文件的 向量表;
判断中断源
有些 “中断服务函数” 对应着 ”多个中断源“ ;
所以都判断中断源;
清除中断标志
有些中断标志被清除,是通过读取DR寄存器;不需要写寄存器清除中断标志;
- 寄存器方式清中断:查看手册,找到清除中断标志的方式;
- 库函数:直接调用一个函数就行了;
中断服务函数内容
在中断中想要干的事;
//EXTI5 6的中断服务函数
void EXTI9_5_IRQHandler(void)
{
//EXTI5
if((EXTI->PR & (1<<5)) != 0)
{
//清标志
EXTI->PR |= (1<<5);
BEEP_Toggle();
}
//EXTI6
if((EXTI->PR & (1<<6)) != 0)
{
//清标志
EXTI->PR |= (1<<6);
Relay_Toggle();
}
}
库函数的获取标志
获取标志位
标志位 = 中断标志 + 其他标志
常用在 “非中断” 时 获取 标志位;
获取中断标志
常用在 “中断” 时, 获取中断标志
中断标志状态
1或者0
参考手册,找到对应的状态寄存器,查看对应的 状态
NVIC代码
NVIC分组代码
/* ########################## NVIC functions #################################### */
/**
* @brief Set the Priority Grouping in NVIC Interrupt Controller
*
* @param PriorityGroup is priority grouping field
*
* Set the priority grouping field using the required unlock sequence.
* The parameter priority_grouping is assigned to the field
* SCB->AIRCR [10:8] PRIGROUP field. Only values from 0..7 are used.
* In case of a conflict between priority grouping and available
* priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set.
*/
static __INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
//只取出 “低三位”
uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */
//读 寄存器
reg_value = SCB->AIRCR; /* read old register configuration */
//清零 ” &=~ “
reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */
//写内容 “ |= ”
reg_value = (reg_value |
(0x5FA << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << 8)); /* Insert write key and priorty group */
//写入寄存器
SCB->AIRCR = reg_value;
}

浙公网安备 33010602011771号