【凌鸥LKS32】基础篇(六)·NVIC中断理论补充

目录
1. 中断简介
嵌套向量中断控制器位于CPU核内部。当中断事件发生时,通知CPU暂停主程序执行,按照优先级设定跳转进入中断服务函数。
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行

在此时的断点部分,若是此时主程序还在进行例如加减乘除运算,但是突然有个更加紧急的的事情需要处理,可以在此时进行中断,优先处理更加紧急的事情,等事情处理完后,在返回主程序继续处理。
在中断这里,我们在断点处离开,编译器会自动帮我们保护现场(当前程序),等我们回来(中断完成后),继续工作。
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
例如:两个人同时就医,急诊和门诊。
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回


2. 内核基础
LKS32MC07x 基于 ARM Cortex-M0+ 内核,NVIC 是该内核的标准嵌套向量中断控制器。这从启动文件的结构可确认,仅有 5 个系统异常(Reset、NMI、HardFault、SVCall、PendSV、SysTick),没有 Cortex-M3/M4 才有的 BusFault、UsageFault、MemManage。
LKS32MC07x系列芯片共有25个外部中断源。
| 类别 | 数量 | 说明 |
|---|---|---|
| 系统异常 | 6 个 | Reset、NMI、HardFault、SVCall、PendSV、SysTick |
| 外设中断 | 25 个 | 共 28 个向量槽位(0~27),其中 3 个保留未用 |
| 中断向量表总大小 | 32 个条目 | 1 SP + 15 系统 + 16 外部(含保留槽位) |

3. 关键特性
3.1 优先级位宽
Cortex-M0+ 的 NVIC 支持 2 位优先级位(__NVIC_PRIO_BITS = 2),即可配置 0~3 共 4 个优先级级别。从代码中可以看到 NVIC_SetPriority(PWRDN_IRQn, 0) 将电源跌落中断设为最高优先级 0。

抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队:

对于响应优先级,相当于“插队”,此时“1”在看病(程序在进行中),“4”要是比较严重,“4”可以进行插队到“2”前面,但是要等“1”看完(进程走完)

对于抢占优先级(中断嵌套),相当于“1”在看病(程序在进行中),“4”不等“1”看完直接冲到屋内,把“1”推到一边,“4”先看病(进行),“4”先看完,在进行“1”,再依次进行后续的操作。

3.2 中断使能/禁止寄存器
ISER(Interrupt Set-Enable Register):NVIC->ISER[0] 仅 1 个 32 位寄存器,控制 32 个 IRQ(实际使用 25 个)
ICER(Interrupt Clear-Enable Register):NVIC->ICER[0] 对应的禁止寄存器在睡眠管理代码中可以看到对这两个寄存器的直接操作:
在睡眠管理代码中可以看到对这两个寄存器的直接操作:

3.3 中断挂起清除
支持通过 NVIC_ClearPendingIRQ() 清除挂起中断,在代码中多处用于清除 PWRDN(电源跌落检测)中断标志,防止误触发。
3.4 全局中断控制
使用 Cortex-M 标准指令:
__disable_irq() / __enable_irq():操作 PRIMASK 寄存器
__get_PRIMASK() / __set_PRIMASK(flg):保存/恢复中断状态
我们之前使用就是:

4. 函数介绍
主要使用的几个中断函数如下:
| 函数 | 功能说明 |
|---|---|
NVIC_EnableIRQ(GPIO_IRQn); | 中断使能,打开该路外部中断,NVIC 可以响应这个中断请求 |
NVIC_DisableIRQ(GPIO_IRQn); | 中断失能,关闭该路外部中断,即使有中断信号,NVIC 也不会响应 |
NVIC_GetPendingIRQ(GPIO_IRQn); | 读取中断挂起 (Pending) 状态;返回 1 代表中断请求挂起待处理,0 代表无挂起;可软件查询是否存在未处理中断请求 |
NVIC_ClearPendingIRQ(GPIO_IRQn); | 清除中断挂起标志,中断解挂;把待处理的中断请求清除,放弃本次中断 |
NVIC_SetPriority(GPIO_IRQn, 0); | 设置中断优先级;示例设置为 0(最高优先级) |
#ifndef CMSIS_NVIC_VIRTUAL
/*#define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping not available for Cortex-M0 */
/*#define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping not available for Cortex-M0 */
#define NVIC_EnableIRQ __NVIC_EnableIRQ
#define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ
#define NVIC_DisableIRQ __NVIC_DisableIRQ
#define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ
#define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ
#define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ
/*#define NVIC_GetActive __NVIC_GetActive not available for Cortex-M0 */
#define NVIC_SetPriority __NVIC_SetPriority
#define NVIC_GetPriority __NVIC_GetPriority
#endif /* CMSIS_NVIC_VIRTUAL */
4.1 中断使能
CMSIS 底层内联函数,使能 NVIC 外部中断,上层NVIC_EnableIRQ()就是直接调用该函数:
/**
\brief Enable Interrupt
\details Enables a device specific interrupt in the NVIC interrupt controller.
\param [in] IRQn Device specific interrupt number.
\note IRQn must not be negative.
*/
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
}
__NVIC_EnableIRQ底层用来使能外部中断,仅支持 IRQn≥0 外设中断;操作 NVIC 的 ISER 置使能寄存器,写 1 开启对应中断;ISER 写 0 无效,关闭中断需要使用 ICER 寄存器。内核系统异常不能使用该函数。
只能开中断,不能关中断。
4.2 中断失能
CMSIS 底层内联函数,关闭外部中断,上层 NVIC_DisableIRQ() 直接调用该函数:
/**
\brief Disable Interrupt
\details Disables a device specific interrupt in the NVIC interrupt controller.
\param [in] IRQn Device specific interrupt number.
\note IRQn must not be negative.
*/
__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
__DSB();
__ISB();
}
}
__NVIC_DisableIRQ用于关闭外部中断,仅接受 IRQn≥0 的外设中断。操作 NVIC 的 ICER 寄存器,写 1 失能对应中断;之后执行__DSB()、__ISB()内存屏障,保证寄存器写入完成、流水线刷新,确保关闭中断立即生效。内核异常不能调用该函数。
只能关中断,不能开中断。
4.3 优先级
CMSIS‑Core 底层内联函数,上层 NVIC_SetPriority() 直接调用本函数,用来配置外设外部中断和内核系统异常的优先级。
/**
\brief Set Interrupt Priority
\details Sets the priority of a device specific interrupt or a processor exception.
The interrupt number can be positive to specify a device specific interrupt,
or negative to specify a processor exception.
\param [in] IRQn Interrupt number.
\param [in] priority Priority to set.
\note The priority cannot be set for every processor exception.
*/
__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) |
(((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn)));
}
else
{
SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) |
(((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn)));
}
}
__NVIC_SetPriority用来设置外设中断或内核异常的优先级。中断号大于等于 0 操作 NVIC‑IP 寄存器;小于 0 操作 SCB‑SHP 寄存器。采用读改写方式仅修改目标中断;依据__NVIC_PRIO_BITS进行移位对齐。部分内核异常优先级硬件固定,软件不可修改。


更多推荐
所有评论(0)