【裸机开发】中断系统 —— 中断向量表(设置中断向量偏移的原因)

news2024/11/16 9:34:57

之前的LED驱动不存在中断,也就不包含中断的初始化。如果程序包含了中断,我们应还需要初始化哪些内容?要解决这个问题,我们需要先了解一个中断系统包含了哪些内容。

中断向量表描述中断对应的中断服务函数,保存在程序开始运行的地方,默认是0x00000000

中断控制器(NVIC、GIC)中断系统的管理机构

中断使能:某个外设的中断使能(要使用某个外设的中断,要先使能这个外设的中断)

中断服务函数:当中断产生时,中断服务函数就会被调用(中断处理逻辑都在中断服务函数中)


目录

一、中断向量表

1、什么是中断向量表?

2、中断类型

二、为什么要设置中断向量表偏移 

1、原因分析

2、如何确定偏移量

3、如何设置

三、汇编编写中断向量表框架


一、中断向量表

1、什么是中断向量表?

中断向量表的作用是描述中断对应的中断服务函数。保存在程序开始运行的地方,默认是0x00000000,可以通过设置中断向量偏移,来改变中断向量表的位置。

Cortex-M 的中断向量表中列举出了所有的中断,每一个中断对应一个中断服务函数;而 Cortex-A 的中断向量表则是将中断分为了 7 类

  • 某个中断发生时,先判断属于哪一类
  • 然后,去中断向量表找对应类的中断服务函数
  • 随后,执行对应的中断服务函数
  • 最后,回到程序暂停的下一个位置

中断向量表所写的就是不同中断类型所对应的中断服务函数地址

2、中断类型

中断大致可以分为七种类型,不同的中断类型对应着一种 CPU 工作模式,当中断产生时,CPU 会先切换到对应的模式,然后再处理中断。其中我们最常用的是 Reset IRQ 中断。

  • ResetCPU 复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,比如初始化 SP 指针、DDR 等
  • Undefined Instruction:如果指令不能识别的话就会产生此中断
  • SWI:Linux 的系统调用会用 SWI 指令来引起软中断,通过软中断来陷入到内核空间
  • Prefetch Abort:预取指令的出错的时候会产生此中断
  • Data Abort:访问数据出错的时候会产生此中断
  • IRQ外设中断都会引起此中断的发生
  • FIQ:快速中断,如果需要快速处理中断的话就可以使用此中断
偏移地址中断类型中断模式
0x00复位中断(Reset)SVC
0x04未定义指令中断(Undefined Instruction)Undef
0x08软中断(Software Interrupt,SWI)SVC
0x0C指令预取中止中断(Prefetch Abort)Abort
0x10数据访问中止中断(Data Abort)Abort
0x14保留(Reserved)-
0x18IRQ 中断(IRQ Interrupt)IRQ
0x1CFIQ 中断(FIQ Interrupt)FIQ

注意:因为中断向量表是放在程序运行的起始位置,所以这里的偏移位置是相对于起始位置而言的

二、为什么要设置中断向量表偏移 

1、原因分析

中断向量表保存在程序运行的起始位置,默认是 0x00000000。根据参考手册中的内存映射表我们发现, 0x00000000 的位置保存了 boot rom,也就是设备上电启动的相关内容,为了不占用其他部分的内容,我们决定对中断向量表施加一个偏移。

中断控制器(GIC)可以配置中断向量表的偏移位置。裸机开发的环境下,我一般在Reset 中断服务函数中手动指定中断向量表的位置;在有 OS 的环境下,OS 会初始化中断控制器,并将中断向量表放置在一个没有被保留的地址空间中。

2、如何确定偏移量

考虑到 RAM 和 DDR 的范围,我们一般将程序保存在 DDR 中。

  • RAM:CPU内部的一段可用内存,imx6ull内部RAM的大小为128K(0X900000~0X91FFFF)
  • DDR:CPU外的存储器,封装在SOC 中,DDR的大小为 256M 或者 512M(虽然看着有2048,但是受到总线约束,CPU可访问的大小是256M)

DDR 的范围较广,考虑到后续的系统移植,所以使用的是DDR。假设中断向量表设为 0x83000000,其实就是在告诉CPU,中断发生时,到 0x83000000 的位置去找对应的中断服务函数。

其实我们也可以将中断向量表和存储地址都设置成 0x87800000

3、如何设置

通过汇编设置

如果是汇编形式,建议放在 Reset 中断服务函数中,因为设备上电就会触发 Reset 中断,然后去执行 Reset 中断服务函数。

/* 复位中断服务函数 */ 
Reset_Handler:
    /* ... */

    ldr r0, =0x87800000         /* 设置中断向量表的偏移 */
	dsb
	isb
	mcr p15, 0, r0, c12, c0, 0
	dsb
	isb

    /* ... */

调用 C 函数

我们可以直接调用 C 函数来设置中断向量表,只要在中断发生之前设置即可。一般放在中断初始化的函数中。

/* 设置中断向量表偏移 */
__set_VBAR((uint32_t)0x87800000);

三、汇编编写中断向量表框架

1、局部流程

中断向量表保存在程序运行的起始位置,默认是 0x00000000。其实就是在告诉内核,每一个中断对应的中断服务函数位置在哪。假设我们要设置 Reset 中断的服务函数地址。

.global _start
 
_start:
    /* 把 Reset_Handler 的地址保存到 pc 指向的位置 */ 
    ldr pc, =Reset_Handler          


/* 复位中断服务函数 */ 
Reset_Handler:
    b Reset_Handler      @ 暂时先死循环,后面再修改              

pc 是程序计数器,用于保存下一次要执行的指令地址。默认情况下,程序从 0x00000000 开始执行,即 pc 最开始拿到的地址就是 0x00000000,这里其实就把 Reset_Handler 的地址保存到了 0x00000000 的位置。

随后,pc 会指向下一个地址,即 0x00000004。

2、整体框架

其他中断也是类似的,我们借助pc寄存器的地址自动递增的特性,逐个设置各个中断服务函数的地址。至于具体实现,这里有 Reset 中断 和 IRQ 中断的汇编部分。

Reset中断:仅包含汇编部分,因为一般只有在复位或者刚上电的时候才会触发,没有需要特意实现的逻辑。Reset 中断服务函数(汇编部分)

② IRQ 中断:包含汇编部分和 C代码部分。

  • 汇编部分用于初始化环境
  • C 代码部分用于逻辑实现
_start:
    ldr pc, =Reset_Handler          /* 复位中断 */ 
    ldr pc, =Undefined_Handler      /* 未定义指令中断 */
    ldr pc, =SVC_Handler            /* SVC(Supervisor)中断*/
    ldr pc, =PrefAbort_Handler      /* 预取终止中断 */
    ldr pc, =DataAbort_Handler      /* 数据终止中断 */
    ldr pc, =NotUsed_Handler        /* 保留中断 */
    ldr pc, =IRQ_Handler            /* IRQ 中断 */
    ldr pc, =FIQ_Handler            /* FIQ(快速中断) */

/* 复位中断 */ 
Reset_Handler:
    b Reset_Handler

/* 未定义指令中断 */ 
Undefined_Handler:
    b Undefined_Handler

/* SVC */ 
SVC_Handler:
    b SVC_Handler

/* 预取终止中断 */ 
PrefAbort_Handler:
    b PrefAbort_Handler

/* 数据终止中断 */ 
DataAbort_Handler:
    b DataAbort_Handler    

/* 保留中断 */ 
NotUsed_Handler:
    b NotUsed_Handler 

/* IRQ 中断 */ 
IRQ_Handler:
    b IRQ_Handler   

/* FIQ(快速中断) */ 
FIQ_Handler:
    b FIQ_Handler  

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++笔记之stack、queue、priority_queue、deque

code review! C笔记之stack、queue、priority_queue、deque 文章目录 C笔记之stack、queue、priority_queue、deque一.cppreference介绍1.stack2.queue3.priority_queue4.deque 二.容器适配器三.stack详解1.创建一个不包含任何元素的 stack 适配器,并采用默认的 de…

Debian 12中再次安装R软件

上篇博客(地址:https://blog.csdn.net/my1114/article/details/131347147?spm1001.2014.3001.5501)中所述的,在Debian12中按默认方式编译安装R软件,有一定的局限性。 如下图所示: 因此,本…

Redis入门(2)-字符串

String是Redis最基础、最常见的类型,string类型的value中可存放任意数据,包括数值型、二进制的图片、音频、视频、序列化对象等。一个String类型的value最大是512M. 1.getset k v 若key存在返回之前的值,若不存在返回nil 2.strlen key 返…

【面试】标准库相关题型(三)

文章目录 1. unordered_map底层实现原理1.1 散列表1.2 STL 中的 unordered_map 的实现1.3 unordered_map 2. 迭代器底层实现原理及种类2.1 主要作用2.2 底层原理2.3 迭代器类型属性 3. 迭代器失效3.1 容器类别3.2 失效情况3.3 C11容器类别 4. STL容器的线程安全4.1 背景知识4.2…

Git分支使用方法

目录 前言 一、查看可用分支 二、创建新分支 三、切换到新分支 四、在新分支上进行工作 五、提交更改 六、切换回主分支 前言 分支是指在同一个代码仓库中的不同版本线。它们可以被用来同时开展不同的开发任务、修复bug或实现新功能,而不会影响到主要的代码…

【数据结构】栈和队列的应用

🎇[数据结构]栈和队列的应用🎇 🌟 正式开始学习数据结构啦~此专栏作为学习过程中的记录🌟 文章目录 🎇[数据结构]栈和队列的应用🎇🍰一.栈在括号表达式中的应用🚀1.原理&#x1f680…

Linux1.基础指令(上)

1.Linux系统可创建多个用户。 2.创建用户:adduser 用户名 设置密码:passwd 用户名 (系统会提示再次输入密码,注意密码不回显)。 3.删除用户首先要在root权限下,输入指令:userdel -r 用户名。 4.ls指令 ls -a(显示所有文件,包括隐藏文件) :…

【软件设计师暴击考点】计算机组成原理与体系结构高频考点暴击系列【二】

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:软件…

【P3】kali 最强渗透工具 - metasploit(安装配置及使用教程详解)

文章目录 一、metasploit 是什么?二、metasploit 攻击 windows 操作系统三、metasploit 攻击永恒之蓝全流程四、metasploit 攻击成功后能做什么4.1、操作步骤4.2、攻击示例 五、msfvenom 绕过杀毒软件技巧5.1、捆绑木马5.2、加壳:压缩壳、加密壳5.2.1、T…

分片和一致性哈希

在设计大规模分布式系统时,你可能会遇到两个概念——分片(sharding)和一致性哈希(consistent hashing)。虽然我在网上找到了很多关于这些术语的解释,但它们让我感到有些困惑。我觉得分片和一致性哈希本质上…

Web网页制作-知识点(2)——常用文本标签、列表标签、表格标签、Form表单、块元素与行内元素(内联元素)

目录 常用文本标签 列表标签 有序列表 无序列表 定义列表 表格标签 表格组成与特点 表格标签 表格属性 ​​​合并表格单元格 Form表单 属性说明 表单元素 文本框 密码框 提交按钮 块元素与行内元素(内联元素) 内联元素和块级元素…

Flink JdbcSink.sink源码解析及常见问题

文章目录 源码入口我们看下flush方法干了什么flush方法至此走完了,但是什么时机写入的数据呐?补充总结: 常见问题1. 为什么会出现JdbcSink.sink方法插入Mysql无数据的情况?2. JdbcSink.sink写Phoenix无数据问题 参考 基于Flink 1.…

设计模式之组合模式笔记

设计模式之组合模式笔记 说明Composite(组合)目录组合模式示例类图菜单组件抽象类菜单类菜单项类测试类 说明 记录下学习设计模式-组合模式的写法。JDK使用版本为1.8版本。 Composite(组合) 意图:将对象组合成树型结构以表示“部分-整体”的层次结构。Composite使得用户对单…

Linux网络-网络层IP协议

目录 IP协议 计算机网络分层 IP协议头格式 IP数据报 - 数据分片 数据报为什么要分片? 数据报分片是什么? 如何做到IP数据报分片? 分片demo示例 并不推荐分片,能不分片则不分片。 网段划分 前置了解 网络号和主机号 为…

如何监测和优化阿里云服务器的性能?有哪些性能分析工具和指标?

如何监测和优化阿里云服务器的性能?有哪些性能分析工具和指标?   阿里云服务器性能监测与优化是云计算服务中一个非常重要的环节。为了确保服务器稳定、高效地运行,我们需要对其性能进行监测,并在监测的基础上进行优化。本文将为…

Packet Tracer - 综合技能练习(配置 VLAN、中继、DHCP 服务器、DHCP 中继代理,并将路由器配置为 DHCP 客户端)

Packet Tracer - 综合技能练习 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 R1 G0/0.10 172.31.10.1 255.255.255.224 不适用 G0/0.20 172.31.20.1 255.255.255.240 不适用 G0/0.30 172.31.30.1 255.255.255.128 不适用 G0/0.40 172.31.40.1 255.255…

MySQL权限控制及日志管理

MySQL权限控制及日志管理 用户权限管理 创建用户 CREATE USER 用户名IP地址 [ IDENTIFIED BY 密码 ];GRANT SELECT ON *.* TO 用户名’IP地址’ IDENTIFIED BY "密码";--创建一个用户名为Usr1 密码为 Usr1.mysql的用户 并授权 CREATE USER…

无忧行:突破网络封锁、跨境访问国外的网站和应用程序(安装注册及使用教程详解)

文章目录 步骤一:注册微软账号步骤二:修改账号的国家/地区步骤三:在Edge Dev浏览器中安装无忧行插件步骤四:创建 无忧行 账户步骤五:无忧行使用教程 包括注册微软账号、在Edge Dev浏览器中安装无忧行插件、创建 无忧行…

Python基础篇(六):组织管理代码—模块和包

组织管理代码—模块和包 前言模块(Module)创建模块使用模块 包(Package)创建包使用包 前言 在Python中,模块和包是组织和管理代码的重要概念。模块是一个包含 Python 定义和语句的文件,而包则是一组相关模块的目录。它们是组织和管理代码的强大工具&…

【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 文章目录 系列文章目录前言一、所有权(Ownership)1.1.、所有权(Ow…