RT-Thread 中断管理学习(一)

中断管理

什么是中断?简单的解释就是系统正在处理某一个正常事件,忽然被另一个需要马上处理的紧急事件打断,系统转而处理这个紧急事件,待处理完毕,再恢复运行刚才被打断的事件。生活中,我们经常会遇到这样的场景:

当你正在专心看书的时候,忽然来了一个电话,于是记下书的页码,去接电话,接完电话后接着刚才的页码继续看书,这是一个典型的中断的过程。

电话是老师打过来的,让你赶快交作业,你判断交作业的优先级比看书高,于是电话挂断后先做作业,等交完作业后再接着刚才的页码继续看书,这是一个典型的在中断中进行任务调度的过程。

这些场景在嵌入式系统中也很常见,当CPU正在处理内部数据时,外界发生了紧急情况,要求CPU暂停当前的工作转去处理这个异步事件。处理完毕后,再回到原来被中断的地址,继续原来的工作,这样的过程称为中断。

实现这一功能的系统称为中断系统,申请CPU中断的请求源称为中断源

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

在这里插入图片描述
中断处理与CPU架构密切相关,所以,本章会先介绍 ARM Cortex-M 的 CPU 架构,然后结合 Cortex-M CPU 架构来介绍 RT-Thread 的中断管理机制,读完本章,大家将深入了解 RT-Thread 的中断处理过程,如何添加中断服务程序(ISR)以及相关的注意事项。

Cortex-M CPU架构基础

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-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寄存器。

在这里插入图片描述
当系统正在服务一个中断时,如果有一个更高优先级的中断触发,那么处理器同样会打断当前运行的中断服务程序,然后把这个中断服务程序的上下文寄存器自动保存到中断栈中。

PendSV系统调用

PendSV也称为可悬起的系统调用,它是一种异常,可以像普通的中断一样被挂起,专门用于辅助操作系统进行上下文切换。

PendSV异常会被初始化为最低优先级的异常
每次需要进行上下文切换时,会手动触发PendSV异常,在PendSV异常处理函数中进行上下文切换。

中断向量表

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

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

  __Vectors     DCD     __initial_sp             ; Top of StackDCD     Reset_Handler            ; Reset 处理函数DCD     NMI_Handler              ; NMI 处理函数DCD     HardFault_Handler        ; Hard Fault 处理函数DCD     MemManage_Handler        ; MPU Fault 处理函数DCD     BusFault_Handler         ; Bus Fault 处理函数DCD     UsageFault_Handler       ; Usage Fault 处理函数DCD     0                        ; 保留DCD     0                        ; 保留DCD     0                        ; 保留DCD     0                        ; 保留DCD     SVC_Handler              ; SVCall 处理函数DCD     DebugMon_Handler         ; Debug Monitor 处理函数DCD     0                        ; 保留DCD     PendSV_Handler           ; PendSV 处理函数DCD     SysTick_Handler          ; SysTick 处理函数… …NMI_Handler             PROCEXPORT NMI_Handler              [WEAK]B       .ENDP
HardFault_Handler       PROCEXPORT HardFault_Handler        [WEAK]B       .ENDP
… …

[WEAK]标识,是符号弱化标识,在[WEAK]前面的符号(如NMI_Handler、HardFault_Handler)将被执行弱化处理,如果整个代码在链接时遇到了名称相同的符号(如与NMI_Handler相同名称的函数),那么代码将使用未被弱化定义的符号(与 NMI_Handler 相同名称的函数),而与弱化符号相关的代码将被自动丢弃。

以SysTick中断为例,在系统启动代码上,需要填上SysTick_Handler中断入口函数,然后实现该函数即可对 SysTick 中断进行响应,中断处理函数示例程序如下所示:

void SysTick_Handler(void)
{/* enter interrupt */rt_interrupt_enter();rt_tick_increase();/* leave interrupt */rt_interrupt_leave();
}

中断处理过程

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

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

用户中断服务程序

在用户中断服务程序(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_enable(level);
}
  1. 恢复中断前的CPU上下文,如果在中断处理过程中未进行线程切换,那么恢复from线程的CPU上下文,如果在中断中进行了线程切换,那么恢复to线程的CPU上下文。
    在这里插入图片描述

中断嵌套

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

中断栈

在系统响应中断前,处理器需要把当前线程的上下文保存起来(通常保存在当前线程的线程栈中),再调用中断服务程序进行中断响应、处理。

在中断处理函数中很可能会有自己的局部变量,这些都需要相应的栈空间来保存,所以中断响应仍然需要一个栈空间来作为上下文,运行中断处理函数。

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/118382.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CS420 课程笔记 P2 - 内存编辑和基础的 GameHacking 尝试

文章目录 IntroductionOperating SystemToolsMemory ScanningMemory ScanExamples!Conclusion Introduction 本节将介绍操作系统的基础知识和内存扫描,这可以说是 game hacking 中最重要的技能,我们不会深入讨论操作系统,因为这本身就是一门…

988. 从叶结点开始的最小字符串

988. 从叶结点开始的最小字符串 C代码:DFS /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/// 叶子节点// 每一层用一个pathTop、遇到叶子节点就判断一次;…

Flutter 项目结构文件

1、Flutter项目的文件结构 先helloworld项目,看看它都包含哪些组成部分。首先,来看一下项目的文件结构,如下图所示。 2、介绍上图的内容。 -litb/main.dart文件:整个应用的入口文件,其中的main函数是整个Flutter应…

MATLAB中isequal函数转化为C语言

背景 有项目算法使用matlab中isequal函数进行运算,这里需要将转化为C语言,从而模拟算法运行,将算法移植到qt。 MATLAB中isequal简单介绍 语法 tf isequal(A,B) tf isequal(A1,A2,...,An) 说明 如果 A 和 B 等效,则 tf is…

DHorse v1.3.2 发布,基于 k8s 的发布平台

版本说明 新增特性 构建版本、部署应用时的线程池可配置化; 优化特性 构建版本跳过单元测试; 解决问题 解决Vue应用详情页面报错的问题;解决Linux环境下脚本运行失败的问题;解决下载Maven安装文件失败的问题; 升…

分布式集群框架——有关zookeeper的面试考点

3.掌握Zookeeper的概念 当涉及到大规模分布式系统的协调和管理时,Zookeeper是一个非常重要的工具。 1. 分布式协调服务:Zookeeper是一个分布式协调服务,它提供了一个高可用和高性能的环境,用于协调和同步分布式系统中的各个节点…

操作系统中一些零散的知识点

第三章 内存管理 在虚拟内存系统中,虚拟内存的最大容量是由计算机的地址结构(CPU寻址范围)确定的,而虚拟内存的实际容量是受到“内存大小磁盘空间大小”、“地址线位数”共同制约,取二者最小值实现虚拟内存管理必须有…

【Apollo学习笔记】——规划模块TASK之RULE_BASED_STOP_DECIDER

文章目录 前言RULE_BASED_STOP_DECIDER相关配置RULE_BASED_STOP_DECIDER总体流程StopOnSidePassCheckClearDoneCheckSidePassStopIsPerceptionBlockedIsClearToChangeLaneCheckSidePassStopBuildStopDecisionELSE:涉及到的一些其他函数NormalizeAngleSelfRotate CheckLaneChang…

【包过滤防火墙——firewalld动态防火墙】的简单使用

文章目录 firewald与iptables区别firewalld九个区域firewalld配置方法firewalld参数和命令firewalld两种模式firewalld使用实验 firewalld不要与iptables混用 firewald与iptables区别 iptables 主要是基于接口,来设置规则,从而判断网络的安全性。firewa…

UE4 春节鞭炮

先搞个基类,一个鞭炮的 搞个鞭炮类,存多个鞭炮 在构造函数的位置先生成对应的鞭炮数 将鞭炮绑定到绳子上,随绳子摆动而一起摆动 在基类里面写爆炸事件 最后用Timer去调用

docker-compose 部署 Seata整合nacos,Postgresql 为DB存储

docker-compose 部署 Seata整合nacos,Postgresql 为DB存储 环境 详情环境可参考 https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 我这里 <spring.cloud.alibaba-version>2021.1</spring.cloud.alibaba-version>所…

linux的make和makefile学习

linux的make和makefile学习 准备工作使用GNU链接库链接到math库编写复利程序 创建自己的库链接到主目录 不同的C标准系统调用write()获取头文件信息功能测试宏 准备工作 安装GCC和Make工具 安装中文输入法 参考&#xff1a;http://t.csdn.cn/eH0Ow sudo apt-get update sudo…

springboot使用logback配置彩色日志

springboot使用logback配置彩色日志 前言一、logback文件二、效果 前言 应该有很多同学发现&#xff0c;使用了logback以后&#xff0c;我们的控制台日志都变成灰色了&#xff0c;网络上搜到的logback配置大多数没有进行配色&#xff0c;所以会把springboot的默认配色方案给覆盖…

【python爬虫】10.指挥浏览器自动工作(selenium)

文章目录 前言selenium是什么怎么用设置浏览器引擎获取数据解析与提取数据自动操作浏览器 实操运用确认目标分析过程代码实现 本关总结 前言 上一关&#xff0c;我们认识了cookies和session。 分别学习了它们的用法&#xff0c;以及区别。 还做了一个项目&#xff1a;带着小…

数据结构 -作用及基本概念

为什么要使用数据结构 学习数据结构是计算机科学和软件工程领域中非常重要的一门课程。以下是学习数据结构的几个重要原因&#xff1a; 组织和管理数据&#xff1a;数据结构提供了一种组织和管理数据的方式。通过学习不同的数据结构&#xff0c;你可以了解如何有效地存储和操作…

Lesson6---案例:人脸案例

学习目标 了解opencv进行人脸检测的流程了解Haar特征分类器的内容 1 基础 我们使用机器学习的方法完成人脸检测&#xff0c;首先需要大量的正样本图像&#xff08;面部图像&#xff09;和负样本图像&#xff08;不含面部的图像&#xff09;来训练分类器。我们需要从其中提取特…

C++学习笔记总结练习:多态与虚函数

1 多态 多态分类 静态多态&#xff0c;是只在编译期间确定的多态。静态多态在编译期间&#xff0c;根据函数参数的个数和类型推断出调用的函数。静态多态有两种实现的方式 重载。&#xff08;函数重载&#xff09;模板。 动态多态&#xff0c;是运行时多态。通过虚函数机制实…

详细介绍如何基于ESP32实现低功耗的电子纸天气显示器--附完整源码

实现界面展示 这是一款天气显示器,由支持 wifi 的 ESP32 微控制器和 7.5 英寸电子纸(又名电子墨水)显示器供电。当前和预测的天气数据是从 OpenWeatherMap API 获取的。传感器为显示屏提供准确的室内温度和湿度。 该项目在睡眠时消耗约 14μA,在约 10 秒的清醒期…

MATLAB制图代码【第二版】

MATLAB制图代码【第二版】 文档描述 Code describtion: This code is version 2 used for processing the data from the simulation and experiment. Time : 2023.9.3 Author: PEZHANG 这是在第一版基础上&#xff0c;迭代出的第二版MATLAB制图代码&#xff0c;第二版的特点是…

不同路径 II【动态规划】

不同路径 II 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上…