RT-Thread 中断管理(学习)

news/2025/3/15 22:40:12/

中断是一种异常,异常是导致处理器脱离正常运行转向执行特殊代码的任何事件,如果不及时进行处理,轻则系统出错,重则会导致系统毁灭性地瘫痪。所以正确地处理异常,避免错误的发生是提高软件鲁棒性(稳定性)非常重要的一环。

中断处理与CPU架构密切相关

Cortex-M CPU架构基础

不同于老的经典 ARM 处理器(例如:ARM7, ARM9),ARM Cortex-M 处理器有一个非常不同的架构,Cortex-M 是一个家族系列,其中包括 Cortex M0/M3/M4/M7 多个不同型号,每个型号之间会有些区别,例如 Cortex-M4 比 Cortex-M3 多了浮点计算功能等,但它们的编程模型基本是一致的。

Cortex-M系列CPU的寄存器组里有R0~R15共16个通用寄存器组和若干特殊功能寄存器。

通用寄存器组里的R13作为堆栈指针寄存器(Stack Pointer,SP);
R14作为连接寄存器(Link Register,LR),用于在调用子程序时,存储返回地址;R15作为程序计数器(Program Counter,PC)。

其中堆栈指针寄存器可以是主堆栈指针(MSP),也可以是进程堆栈指针(PSP)。

在这里插入图片描述
特殊功能寄存器包括程序状态字寄存器(PSRs)、中断屏蔽寄存器组(PRIMASK、FAULTMASK,BASEPRI)、控制寄存器(CONTROL)。
可以通过MSR/MRS指令访问特殊功能寄存器。

MRS R0,CONTROL;读取CONTROL到R0中
MSR CONTROL,R0;写入R0到CONTROL寄存器中

程序状态字寄存器里保存算术与逻辑标志,例如负数标志,零结果标志,溢出标志等等。
中断屏蔽寄存器组控制Cortex-M的中断除能。
控制寄存器用来定义特权级别和当前使用哪个堆栈指针。

如果是具有浮点单元的Cortex-M4或者Cortex-M7,控制寄存器也用来指示浮点单元当前是否在使用,浮点单元包含了32个浮点通用寄存器S0~S31和特殊FPSCR寄存器。

操作模式和特权级别
Cortex-M引入了操作模式和特权级别的概念,分别为线程模式和处理模式,如果进入异常或中断处理进入处理模式,其它情况则为线程模式
在这里插入图片描述
Cortex-M有两个运行级别,分别为特权级和用户级,线程模式可以工作在特权级或者用户级,而处理模式工作在特权级,可通过CONTROL特殊寄存器控制。

Cortex-M的堆栈寄存器SP对应两个物理寄存器MSP和PSP,MSP为主堆栈,PSP为进程堆栈,处理模式总是使用MSP作为堆栈,线程模式可以选择使用MSP或PSP作为堆栈,同样通过CONTROL特殊寄存器控制。
复位后,Cortex-M默认进入线程模式、特权级、使用MSP堆栈。

嵌套向量中断控制器

Cortex-M中断控制器名为NVIC(嵌套向量中断控制器),支持中断嵌套功能。
当一个中断触发并且系统进行响应时,处理器硬件会将当前运行位置的上下文寄存器自动压入中断栈中,这部分的寄存器包括PSR、PC、LR、R12、R3-R0寄存器。

在这里插入图片描述

当系统正在服务一个中断时,如果有一个更高优先级的中断触发,那么处理器同样会打断当前运行的中断服务程序,然后把这个中断服务程序上下文的PSR、PC、LR、R12、R3-R0寄存器自动保存到中断栈中。

PendSV系统调用

PendSV也称为可悬起的系统调用,它是一种异常,可以像普通的中断一样被挂起,它是专门用来辅助操作系统进行上下文切换的。
PendSV异常会被初始化为最低优先级的异常。每次需要进行上下文切换的时候,会手动触发PendSV异常,在PendSV异常处理函数中进行上下文切换。

中断向量表

中断向量表是所有中断处理程序的入口,把用户中断服务程序同一个虚拟中断向量表中的中断向量联系在一起。
当中断向量对应中断发生的时候,被挂接的用户中断服务程序就会被调用执行。

在这里插入图片描述

当一个中断触发

  .word _estack.word Reset_Handler.word NMI_Handler.word HardFault_Handler.word MemManage_Handler.word BusFault_Handler.word UsageFault_Handler.word 0.word 0.word 0.word 0.word SVC_Handler.word DebugMon_Handler.word 0.word PendSV_Handler.word SysTick_Handler

在Cortex-M内核上,当一个中断触发时,处理器将直接判定是哪个中断源,然后直接跳转到相应的固定位置进行处理,每个中断服务程序必须排列在统一的地址上(这个地址必须要设置到NVIC的中断向量偏移寄存器中)。中断向量表一般由一个数组定义或在起始代码中给出。

中断处理过程

将中断处理程序分为中断前导程序、用户中断服务程序、中断后续程序三部分。
在这里插入图片描述

中断前导程序

中断前导程序主要工作如下:

  1. 保存CPU中断现场,这部分跟CPU架构相关,不同CPU架构的实现方式有差异。
    对于Cortex-M来说,该工作由硬件自动完成。当一个中断触发并且系统进行响应时,处理器硬件会将当前运行部分的上下文寄存器自动压入中断栈中,这部分的寄存器包括PSR、PC、LR、R12、R0-R3寄存器。
  2. 通知内核进入中断状态,调用rt_interrupt_enter()函数,作用是把全局变量rt_interrupt_nest加1,用它来记录中断嵌套的层数。
void rt_interrupt_enter(void)
{rt_base_t level;level = rt_hw_interrupt_disable();rt_interrupt_nest++;rt_hw_interrupt_anble(level);
}

用户中断服务程序

在用户中断服务程序(ISR)中,分为两种情况,第一种情况是不进行线程切换,这种情况下用户中断服务程序和中断后续程序运行完毕后退出中断模式,返回被中断的线程。

另一种情况是,在中断处理过程中需要进行线程切换,这种情况会调用rt_hw_context_switch_interrupt()函数进行上下文切换,该函数跟CPU架构相关,不同CPU架构实现方式有差异。

在 Cortex-M 架构中,rt_hw_context_switch_interrupt() 的函数实现流程如下图所示,它将设置需要切换的线程rt_interrupt_to_thread变量,然后触发PendSV异常(PendSV异常是专门用来辅助上下文切换的,且被初始化为最低优先级的异常)。
PendSV异常被触发后,不会立即进行PendSV异常中断处理程序,因为此时还在中断处理中,只有当中断后续程序运行完毕,真正退出中断处理后,才进入PendSV异常中断处理程序。

在这里插入图片描述

中断后续程序

  1. 通知内核立刻中断状态,通过调用rt_interrupt_leave()函数,将全局变量rt_interrupt_nest减1。
void rt_interrupt_leave(void)
{rt_base_t level;level = rt_hw_interrupt_disable();rt_interrupt_nest--;rt_hw_interrupt_anble(level);
}
  1. 恢复中断前的CPU上下文,如果在中断处理过程中未进行线程切换,那么恢复form线程的CPU上下文,如果在中断中进行了线程切换,那么恢复to线程的CPU上下文。
    在这里插入图片描述

中断嵌套

在允许中断嵌套的情况下,在执行中断服务程序的过程中,如果出现高优先级的中断,当前中断服务程序的执行将被打断,以执行高优先级的中断服务程序,当高优先级中断处理完成后,被打断的中断服务程序才又得到继续执行,如果需要进行线程切换,线程的上下文切换将在所有中断处理程序都运行结束时才发生。
在这里插入图片描述

中断栈

软件代码(或处理器)需要把当前线程的上下文保存下来(通常保存在当前线程的线程栈中),再调用中断处理程序进行中断响应、处理。

在进行中断处理时(实质是调用用户的中断服务程序函数),中断处理函数中很可能会有自己的局部变量,这些都需要相应的栈空间来保存,所以中断响应依然需要一个栈空间来作为上下文,运行中断处理函数。

中断栈可以保存在打断线程的栈中,当从中断中退出时,返回相应的线程继续执行。

中断栈也可以与线程栈完全分离开来,即每次进入中断时,在保存完打断线程上下文后,切换到新的中断栈中独立运行。在中断退出时,再做相应的上下文恢复。使用独立中断栈相对来说更容易实现,并且对于线程栈使用情况也比较容易了解和掌握(否则必须要为中断栈预留空间,如果系统支持中断嵌套,还需要考虑应该为嵌套中断预留多大的空间)。

中断发生时,中断的前期处理程序会将用户的栈指针更换到系统事先留出的中断栈空间中,等中断退出时再恢复用户的栈指针。这样中断就不会占用线程的栈空间,从而提高了内存空间的利用率,且随着线程的增加,这种减少内存占用的效果也越明显。

在Cortex-M处理器内核里有两个堆栈指针,一个是主堆栈指针(MSP),是默认的堆栈指针,在运行第一个线程之前和在中断和异常服务程序里使用;另一个是线程堆栈指针(PSP),在线程里使用。

在中断和异常服务程序退出时,修改LR寄存器的第2位的值为1,线程的SP就由MSP切换到PSP。


http://www.ppmy.cn/news/1172920.html

相关文章

MATLAB算法实战应用案例精讲-【人工智能】ROS机器人

目录 ROS机器人操作系统底层原理 1、序列化 1.1 什么是序列化? 1.2 ROS中的序列化实现

c进阶测试题

选择题 1.请问该程序的输出是多少&#xff08;C&#xff09; #include<stdio.h> int main(){unsigned char i 7;int j 0;for(;i > 0;i - 3){ j;} printf("%d\n", j);return 0; }A. 2 B. 死循环 C. 173 D. 172 首先unsigned char型是不会为负数&#xff…

【2021集创赛】Digilent杯二等奖:基于FPGA的动态视觉感知融合的运动目标检测系统

杯赛题目&#xff1a;Diligent杯&#xff1a;基于FPGA开源软核的硬件加速智能平台 参赛组别&#xff1a;A组 设计任务&#xff1a; 利用业界主流软核处理器(仅限于Cortex-M系列及 RISC-V系列)在限定的DIGILENT官方FPGA平台上构建SoC片上系统&#xff0c;在 SoC中添加面向智能应…

Leetcode 454 四数相加II(哈希表 + getOrDefault方法用于获取Map中指定键的值,如果键不存在,则返回一个默认值)

Leetcode 454 四数相加II&#xff08;哈希表&#xff09; 解法1 HashMap getOrDefault方法 解法1 HashMap getOrDefault方法 【HashMap】 【⭐️HashMap常用操作】 创建HashMap&#xff1a;HashMap<Integer, Integer> hash new HashMap<>(); 向HashMap添加元素…

【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割2(基础数据流篇)

构建pytorch训练模型读取的数据,是有模版可以参考的,是有套路的,这点相信使用过的人都知道。我也会给出一个套路的模版,方便学习和查询。 同时,也可以先去参考学习之前的一篇较为简单的3D分类任务的数据构建方法,链接在这里:【3D图像分类】基于Pytorch的3D立体图像分类…

Promise笔记-同步回调-异步回调-JS中的异常error处理-Promis的理解和使用-基本使用-链式调用-七个关键问题

Promise笔记 1. 预备知识1.1 实例对象与函数对象1.2 两种类型的回调函数1. 同步回调2. 异步回调 1.3 JS中的异常error处理1. 错误的类型2. 错误处理&#xff08;捕获与抛出&#xff09;3. 错误对象 2.Promise的理解和使用2.1 Promise是什么1.理解Promise2.Promise 的状态3. Pro…

git简介和指令

git是一个开源的的分布式版本控制系统&#xff0c;用于高效的管理各种大小项目和文件 用途&#xff1a;防止代码丢失&#xff0c;做备份 项目的版本管理和控制&#xff0c;可以通过设置节点进行跳转 建立各自的开发环境分支&#xff0c;互不影响&#xff0c;方便合并 在多终端开…

字符串逆序(递归实现)

字符串逆序&#xff08;递归实现&#xff09; 编写一个函数 reverse_string(char* string)&#xff08;递归实现&#xff09; 实现&#xff1a;将参数字符串中的字符反向排列&#xff0c;不是逆序打印。 要求&#xff1a;不能使用C函数库中的字符串操作函数。 比如 : char arr[…