【嵌入式Linux】i.MX6ULL IRQ中断服务函数的编写

文章目录

  • IRQ中断服务函数流程解释
    • 0. 基本流程步骤
    • 1. 入口部分
    • 2. 读取中断号
    • 3. 切换模式并调用C语言处理函数
    • 4. 清理和恢复环境
    • 5. 完整代码

本文章结合了正点原子的 i.mx6u嵌入式Linux开发指南和笔者的理解。

IRQ中断服务函数流程解释

IRQ Interrupt Request 外部中断

0. 基本流程步骤

整个IRQ中断服务函数的流程可以简洁地概括为以下几个关键步骤:

  1. 保存环境:

    • 保存当前任务的关键寄存器(r0-r3, r12, lr)和程序状态寄存器(SPSR)以保护执行环境。
  2. 获取中断源:

    • 从Generic Interrupt Controller (GIC)读取当前触发的中断号,这是识别和处理特定中断的关键步骤。
  3. 模式切换:

    • 切换到SVC(Supervisor Call)模式以允许中断嵌套,即在处理当前中断时允许其他中断的处理。
  4. 执行中断处理:

    • 调用C语言编写的中断处理函数system_irqhandler,处理具体的中断逻辑。
  5. 中断结束处理:

    • 将处理完成的中断号写入GICC_EOIR寄存器,通知GIC中断已被处理。
  6. 恢复环境并退出:

    • 恢复之前保存的各寄存器和程序状态,通过更新程序计数器完成从中断返回到主程序的流程。

这些步骤确保了系统在响应中断时能够保持稳定和可预测,同时保护了任务的执行状态不受中断影响。

1. 入口部分

  1. 保存寄存器状态:
    push {lr}                   /* 保存链接寄存器(lr)的值,即返回地址 */
    push {r0-r3, r12}           /* 保存通用寄存器r0到r3和r12 */
    
    这些步骤是中断服务例程的标准做法,用于保护执行环境,使得中断处理过程不会影响当前执行的任务。
  • R0-R3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
  • R4-R11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
  • R12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复
    r12。
  • R13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
  • R14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
  • R15 是程序计数器 PC。它不能用于任何其它用途。
  • 注意:在中断程序中,所有的寄存器都必须保护,编译器会自动保护R4~R11
  1. 保存程序状态寄存器:
    mrs r0, spsr                /* 读取保存程序状态寄存器(SPSR)到r0 */
    push {r0}                   /* 将SPSR的值压栈保存 */
    
    SPSR保存了中断发生时的处理器状态,需要在中断处理后恢复。
  • CPSR: Current Program Status Register 当前程序状态寄存器
  • SPSR: Saved Program Status Register 程序状态保存寄存器 当特定的异常中断发生时,SPSR用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后,可以用 SPSR 中保存的值来恢复 CPSR。

2. 读取中断号

  1. 获取中断号:
    mrc p15, 4, r1, c15, c0, 0  /* 从CP15协处理器中获取GIC基地址 */
    add r1, r1, #0x2000         /* 计算GIC的CPU接口端基地址 */
    ldr r0, [r1, #0xC]          /* 从GICC_IAR寄存器读取当前的中断号 */
    push {r0, r1}               /* 保存中断号和GIC基地址 */
    
    这部分代码是从GIC(Generic Interrupt Controller)中获取当前触发的中断号,这是处理中断的关键步骤,用于确定具体的中断处理函数。
  • c15寄存器
    Cortex-A7 Technical ReferenceManua.pdf P68
    在这里插入图片描述
  • mrc p15, 4, r1, c15, c0, 0 通过c15寄存器读取了CBAR寄存器
    Cortex-A7 Technical ReferenceManua.pdf P138
    在这里插入图片描述
    在这里插入图片描述
  • 获取GIC块在基地址上的偏移大小
    Cortex-A7 Technical ReferenceManua.pdf P138
    在这里插入图片描述
  • 获取GICC_IAR寄存器的在CPU interface上的偏移地址(记录了中断号)为0x000C
    Cortex-A7 Technical ReferenceManua.pdfP188
    在这里插入图片描述

3. 切换模式并调用C语言处理函数

  1. 切换到SVC模式以允许中断嵌套:
    cps #0x13                   /* 切换到SVC(Supervisor Call)模式 */
    push {lr}                   /* 保存SVC模式的链接寄存器 */
    
    这允许在处理当前中断的过程中再次接受其他中断,实现中断嵌套。
  • CPS 是 Change Processor State 的缩写 [6],它可以改变 CPSR 中的 A、I、F 中断屏蔽位以及 M 模式位,而不会改变其他 CPSR 位 [6]。
  • cps #0x13 就是改变了M模式位(处理器模式控制位)
    【正点原子】LMX6U嵌入式Linux驱动开发指南V1.81.pdfP294
    在这里插入图片描述
    ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdfP1139
    在这里插入图片描述
  1. 调用C语言中断处理函数:
    ldr r2, =system_irqhandler  /* 将C语言编写的中断处理函数地址加载到r2 */
    blx r2                      /* 使用链接寄存器跳转到该函数执行 */
    
    这里,system_irqhandler 是实际处理中断的C语言函数,可以根据r0中的中断号执行相应的操作。
  • blx 的全称是 Branch and Link with eXchange。它是一个 ARM 指令,用于跳转到一个新的地址并保存当前的 PC 值到 LR 寄存器。
  • blx 指令可以改变处理器状态,从 ARM 状态切换到 Thumb 状态,或者从 Thumb 状态切换到 ARM 状态。这取决于目标地址的最低位。
  • 如果目标地址的最低位为 0,则处理器状态将切换到 ARM 状态。如果目标地址的最低位为 1,则处理器状态将切换到 Thumb 状态。

4. 清理和恢复环境

  1. 恢复IRQ模式并完成中断处理:
    pop {lr}                    /* 恢复SVC模式的lr寄存器 */
    cps #0x12                   /* 切回IRQ模式 */
    pop {r0, r1}                /* 恢复r0, r1寄存器值 */
    str r0, [r1, #0x10]         /* 向GICC_EOIR寄存器写入中断结束信号 */
    
    这些步骤标志着中断处理的结束,通过向GICC_EOIR寄存器写入中断号来通知GIC中断已被处理。
  • pop {lr} 指令用于从堆栈中取出(pop)一个值并将其存入到链接寄存器(Link Register, 简称 LR)中

  • 为什么中断发生时,要进入SVC模式后进行中断函数的执行,执行完成后又进入IRQ模式

    1. 中断函数执行完后需要从 SVC 模式变回 IRQ 模式。 这是因为 SVC 模式用于处理系统调用,而 IRQ 模式用于处理外部中断。
    2. 中断发生时,处理器会从当前模式切换到 IRQ 模式,并执行相应的中断服务函数。
    3. 中断服务函数可能需要调用系统调用来完成某些操作。 例如读取设备寄存器或发送数据。
    4. 为了执行系统调用,处理器需要切换到 SVC 模式。
    5. 系统调用执行完毕后,处理器需要返回到 IRQ 模式,继续处理中断。
    6. 因此,中断函数执行完后需要从 SVC 模式变回 IRQ 模式,以确保中断处理流程的完整性。
  • 写结束信号

    • 获取GICC_EOIR寄存器的在CPU interface上的偏移地址(记录了中断号)为0x00010
      Cortex-A7 Technical ReferenceManua.pdfP189
      在这里插入图片描述
    • GICC_EOIR寄存器结构
      ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdfP214
      在这里插入图片描述
  1. 恢复处理器状态并退出中断:
    pop {r0}                    /* 恢复保存的SPSR */
    msr spsr_cxsf, r0           /* 将SPSR的值恢复到处理器状态寄存器 */
    pop {r0-r3, r12}            /* 恢复通用寄存器 */
    pop {lr}                    /* 恢复链接寄存器 */
    subs pc, lr, #4             /* 通过链接寄存器设置程序计数器,完成中断返回 */
    
    这最后的步骤将处理器状态恢复到中断发生前的状态,并通过subs pc, lr, #4指令安全地返回到中断点。
  • 为什么这里要将lr-4 然后赋给 pc 呢?而不是直接将 lr 赋值给 pc?

核心原因: ARM 处理器采用流水线执行指令,当前执行的指令地址和下一条将要执行的指令地址并不一致。
具体解释:

  1. ARM 处理器流水线: ARM 处理器采用三级流水线,分别为取指、译指和执行。这意味着处理器会同时进行取指、译指和执行三个步骤,而不是顺序执行。
  2. PC 指针: PC 寄存器指向的是正在取值的指令地址,而不是正在执行的指令地址。
  3. 中断发生时: 当中断发生时,处理器会保存当前 PC 指针的值到 LR 寄存器。此时,LR 寄存器保存的是下一条将要执行的指令地址,而不是当前正在执行的指令地址。
  4. 中断返回: 中断处理完成后,需要返回到中断发生的位置继续执行。如果直接将 LR 赋值给 PC,那么处理器会跳过当前正在执行的指令,导致程序执行错误。
  5. lr - 4 赋值给 pc 为了确保程序正常执行,需要将 LR 寄存器的值减去 4,然后赋值给 PC 寄存器。这样,PC 指针就会指向中断发生时正在执行的指令的下一条指令,从而保证程序能够正常执行。

示例:

0X2000 MOV R1, R0 ;执行
0X2004 MOV R2, R3 ;译指
0X2008 MOV R4, R5 ;取值 PC
  • 中断发生时,LR 寄存器保存的是 0X2008,因为 PC 指针指向的是下一条将要执行的指令地址。
  • 如果直接将 LR 赋值给 PC,处理器会跳过 0X2004 处的指令 MOV R2, R3,导致程序执行错误。
  • lr - 4 赋值给 PC,即 PC = 0X2004,处理器会从 0X2004 处的指令 MOV R2, R3 开始执行,保证程序正常执行。

总结:

lr - 4 赋值给 pc 是为了解决 ARM 处理器流水线执行指令带来的问题,确保中断处理完成后能够正常返回到中断发生的位置继续执行程序。

5. 完整代码

@ IRQ中断服务函数
IRQ_Handler:
  push {lr}				    /* 保存lr地址,Link Register, R14 寄存器*/
  push {r0-r3, r12}			/* 保存r0-r3,r12寄存器 */

  mrs r0, spsr				/* 读取spsr寄存器 */
  push {r0}					/* 保存spsr寄存器 */

  mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
  							* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
  							* Cortex-A7 Technical ReferenceManua.pdf P68 P138
  							*/							
  add r1, r1, #0X2000			/* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
  ldr r0, [r1, #0XC]			/* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
  							 * GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
  							 * 这个中断号来绝对调用哪个中断服务函数
  							 */
  push {r0, r1}				/* 保存r0,r1 */
  
  cps #0x13					/* 进入SVC模式,允许其他中断再次进去 */
  
  push {lr}					/* 保存SVC模式的lr寄存器 */
  ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/
  blx r2						/* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */

  pop {lr}					/* 执行完C语言中断服务函数,lr出栈 */
  cps #0x12					/* 进入IRQ模式 */
  pop {r0, r1}				
  str r0, [r1, #0X10]			/* 中断执行完成,写EOIR */

  pop {r0}						
  msr spsr_cxsf, r0			/* 恢复spsr */

  pop {r0-r3, r12}			/* r0-r3,r12出栈 */
  pop {lr}					/* lr出栈 */
  subs pc, lr, #4				/* 将lr-4赋给pc */

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度解析:开关电源(DC/DC)与线性电源(LDO)的技术特性与应用差异

若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/139955493 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…

VS Code 使用 Makefile 运行 CPP项目

Installing the MinGW-w64 toolchainCMake Toolsmakelist.txt报错 1报错 2报错 3生成了 Makefile ,如何使用 make 命令 Installing the MinGW-w64 toolchain 参见文档 将 GCC 与 MinGW 结合使用 CMake Tools 参见文档 Linux 上的 CMake 工具入门 CMake 的使用 …

Excel 宏录制与VBA编程 —— 14、使用VBA处理Excel事件

简介 若希望特定事件处理程序在触发特定事件时运行,可以为 Application 对象编写事件处理程序。 Application 对象的事件处理程序是全局的,这意味着只要 Microsoft Excel 处于打开状态,事件处理程序将在发生相应的事件时运行,而不…

AI降痕工具:论文AI率的智能解决方案

告诉大家一个非常残忍的答案,以后所有论文都会被查ai率的。 学术界不仅关注传统的抄袭问题,还增加了一项名为“AIGC检测”的指标。例如知网、维普等平台都能检测论文AI率。 用GPT写论文虽然重复率基本不用担心,但是AI率基本都较高&#xff…

vue3组件通讯-介绍

简介 Vue 3 引入了多种强大的功能和改进,其中包括增强的组件通信机制。了解这些机制对于构建复杂、可维护的应用程序至关重要。下面,我们将介绍在 Vue 3 中组件通信的几种方法。 通讯类型 父子组件通信上下级通信(不仅父子级)兄…

通用大模型VS垂直大模型——最后还是要双赢

大模型的江湖争霸:通用与垂直,谁会先“拿下一城”? 哎呀,在人工智能这片神奇的沃土上,大模型(咱们说的可是那些超级聪明的“大脑”哦)正上演着一场别开生面的“武林大会”。一方是全能型选手—…

Java字符串处理深度解析:String、StringBuffer与StringBuilder的奥秘

摘要: 本文将深入探讨Java语言中处理字符串的基础构件:String、StringBuffer和StringBuilder。我们将详细讲解它们的内部原理、适用场景、性能对比以及在现代开发实践中的使用策略。同时,结合当下编程行业的热点技术,如微服务架构…

80、443端口不能开放也能为IP地址申请SSL证书!

IP地址证书作为一种特定的证书,不同于传统的域名验证证书,IP地址证书是通过验证IP地址来确保安全连接。在证书申请过程中,往往要求短暂开放80或者443端口,如果不能开放,IP地址证书则不能签发。 JoySSL提供的IP地址证书…

来聊聊Redis所实现的Reactor模型

写在文章开头 我们都知道解决C10k问题的最好方案就是通过在IO多路复用的基础上通过reactor模型实现高性能的网络并发程序,借助这个设计,redis的主线程也是基于IO多路复用以reactor模型的思路实现了一个高性能的单线程内存数据,本文将带领读者…

使用JAVA代码实现发送订阅消息以及模板消息

今天写了一个商品到货提醒的job任务,具体效果如下 这里用到了微信的发送订阅消息,主要代码是这一块的,最后我把发送了消息的订单存到表里,因为是定时任务,大家可不存 发送订阅消息 | 微信开放文档 /*** 微信平台-商品…

vue+canvas画布实现网页签名效果

1、签名自定义组件代码示例&#xff1a; qianMing.vue <template><!-- 容器&#xff0c;包含画布和清除按钮 --><div class"signature-pad-container"><!-- 画布元素&#xff0c;用于用户签名 --><canvasref"canvas" <!--…

领克杀入纯电赛道:年轻人想要一台什么样的大电轿?

‍作者 |老缅 编辑 |德新 6月12日&#xff0c;领克旗下首款纯电动车型在瑞典进行了全球首秀&#xff0c;该车正式定名为Z10。 Z10的字母「Z」&#xff0c;源自ZERO。 Zeal-激情&#xff0c;Enjoy-享受&#xff0c;Responsibility-责任&#xff0c;Original-原创&#xff0c;…

动态规划数字三角形模型——AcWing 275. 传纸条

动态规划数字三角形模型 定义 动态规划数字三角形模型是在一个三角形的数阵中&#xff0c;通过一定规则找到从顶部到底部的最优路径或最优值。 运用情况 通常用于解决具有递推关系、需要在不同路径中做出选择以达到最优结果的问题。比如计算最短路径、最大和等。 计算其他…

中国高分辨率土壤侵蚀因子K

土壤可蚀性因子&#xff08;K&#xff09;数据&#xff0c;基于多种土壤属性数据计算&#xff0c;所用数据包括土壤黏粒含量&#xff08;%&#xff09;、粉粒含量&#xff08;%&#xff09;、砂粒含量&#xff08;%&#xff09;、土壤有机碳含量&#xff08;g/kg&#xff09;、…

【新版本来袭】ONLYOFFICE桌面编辑器8.1 —— 重塑办公效率与体验

文章目录 一、功能完善的PDF编辑器&#xff1a;重塑文档处理体验编辑文本插入和修改各种对象&#xff0c;如表格、形状、文本框、图像、艺术字、超链接、方程式等添加、旋转和删除页面添加文本注释和标注 二、幻灯片版式设计&#xff1a;创意展示的无限舞台三、改进从右至左显示…

规则引擎-Aviator 表达式校验是否成立

目录 介绍特性使用更多文献支持 介绍 Aviator是一个轻量级、高性能的Java表达式执行引擎&#xff0c;它动态地将表达式编译成字节码并运行。 特性 支持绝大多数运算操作符&#xff0c;包括算术操作符、关系运算符、逻辑操作符、位运算符、正则匹配操作符(~)、三元表达式(?:…

接口防篡改+防重放攻击

接口防止重放攻击&#xff1a;重放攻击是指攻击者截获了一次有效请求(如交易请求),并在之后的时间里多次发送相同的请求&#xff0c;从而达到欺骗系统的目的。为了防止重放攻击&#xff0c;通常需要在系统中引入一种机制&#xff0c;使得每个请求都有一个唯一的标识符(如时间戳…

Go 如何使用指针灵活操作内存

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

华为的开发语言有2中,分别是ArkTS和仓颉,他们的区别是什么?

华为的开发语言有2中&#xff0c;分别是ArkTS和仓颉&#xff0c;他们的区别在哪呢&#xff1f; ArkTS和仓颉&#xff08;cangjie&#xff09;他们的区别是什么&#xff1f; 华为的仓颉和 ArkTS 是两种不同的编程语言&#xff0c;它们有以下区别&#xff1a; 设计目的&#xff1…

emoji控必备:制作一个emoji面板插件

说在前面 &#x1f4bb;在数字时代&#xff0c;emoji表情符号已成为很多人沟通的重要工具&#xff0c;但是输入法中的emoji表情包可能不太够用&#xff0c;所以很多时候我会到在线的网站去复制emoji&#xff0c;然后再回来粘贴&#xff0c;这样操作感觉有点繁琐&#xff0c;所以…