8.4 中断的悬起与解悬
- 首页
- 演化计算
- 程序设计基础
- 现代优化计算方法
- 《Borland传奇》在线阅读。作者:李维
- VC++程序设计基础
- 用于最优化的计算智能
- Windows API 编程
- 操作系统教程
- VBScript语言
- VB开发工具
- VB参考
- VB语法
- 计算机网络
- 嵌入式系统
- Cortex M3开发
- ARM开发详解
- IBM S390 系统管理
- 电子计算机组成原理
- Oracle编程艺术
- 计算机代数系统
- VFP语法、实例、函数和类库参考手册
- Windows 2003服务器网络管理与故障解决手册
- /a/
- IT名企
- 开发工具史
- 教育
- 英文文档
- 程序设计经验
- 计算机硬件发展史
- IT名人堂
- 编程库
- linux 技巧
- 计算机网络历史
- 散论
- 操作系统开发
- 操作系统历史
- 编程语言历史
- 编程技巧
- 网站运营
- 智能手机
- 网页设计
- windows 技巧
再上一篇: 8.2 中断配置基础 上一篇: 8.3 中断的使能与除能 主页 下一篇: 8.5 中断系统设置全过程的演示 再下一篇: 8.6 软件中断 文章列表
8.4 中断的悬起与解悬
《Cortex-M3 权威指南》,嵌入式处理器开发教程。
如果中断发生时,正在处理同级或高优先级异常,或者被掩蔽,则中断不能立即得到响应。此 时中断被悬起。中断的悬起状态可以通过“中断设置悬起寄存器(SETPEND)”和“中断悬起清除寄 存器(CLRPEND)”来读取,还可以写它们来手工悬起中断。
悬起寄存器和“解悬”寄存器也可以有 8 对,其用法和用量都与前面介绍的使能/除能寄存器
完全相同,见表 8.2。
表 8.2 SETPEND/CLRPEND 寄存器族 (此表参考官方技术参考手册作了些改编——译者注)
SETPENDs:0xE000_E200 – 0xE000_E21C ; CLRPENDs:0xE000E280 - 0xE000_E29C
名称 | 类型 | 地址 | 复位值 | 描述 |
SETPEND0 | R/W | 0xE000_E200 | 0 | 中断 0-31的悬起寄存器,共 32个悬起位 位[n],中断#n悬起(异常号 16+n) |
SETPEND1 | R/W | 0xE000_E204 | 0 | 中断 32-63的悬起寄存器,共 32个悬起位 |
… | … | … | … | … |
SETPEND7 | R/W | 0xE000_E21C | 0 | 中断 224-239的悬起寄存器,共 16个悬起位 |
CLRPEND0 | R/W | 0xE000_E280 | 0 | 中断 0-31的解悬寄存器,共 32个解悬位 位[n],中断#n解悬(异常号 16+n) |
CLRPEND1 | R/W | 0xE000_E284 | 0 | 中断 32-63的解悬寄存器,共 32个解悬位 |
… | … | … | … | … |
CLRPEND7 | R/W | 0xE000_E29C | 0 | 中断 224-239的解悬寄存器,共 16个解悬位 |
8.4.1 优先级
每个外部中断都有一个对应的优先级寄存器,每个寄存器占用 8位,但是 CM3允许在最“粗线 条”的情况下,只使用最高 3位。4个相临的优先级寄存器拼成一个 32位寄存器。如前所述,根据 优先级组的设置,优先级可以被分为高低两个位段,分别是抢占优先级和亚优先级。优先级寄存器 都可以按字节访问,当然也可以按半字/字来访问。有意义的优先级寄存器数目由芯片厂商实现的 中断数目决定,优先级配置寄存器的详细信息在附录D中给出(表 D.18)。
表 8.3 中断优先级寄存器阵列 0xE000_E400 – 0xE000_E4EF
名称 | 类型 | 地址 | 复位值 | 描述 |
PRI_0 | R/W | 0xE000_E400 | 0(8位) | 外中断#0的优先级 |
PRI_1 | R/W | 0xE000_E401 | 0(8位) | 外中断#1的优先级 |
… | … | … | … | … |
PRI_239 | R/W | 0xE000_E4EF | 0(8位) | 外中断#239的优先级 |
表8.3B 系统异常优先级寄存器阵列 0xE000_ED18 - 0xE000_ED23
地址 | 名称 | 类型 | 复位值 | 描述 |
0xE000_ED18 | PRI_4 | 存储器管理 fault的优先级 | ||
0xE000_ED19 | PRI_5 | 总线 fault的优先级 | ||
0xE000_ED1A | PRI_6 | 用法 fault的优先级 | ||
0xE000_ED1B | - | - | - | - |
0xE000_ED1C | - | - | - | - |
0xE000_ED1D | - | - | - | - |
0xE000_ED1E | - | - | - | - |
0xE000_ED1F | PRI_11 | SVC优先级 | ||
0xE000_ED20 | PRI_12 | 调试监视器的优先级 | ||
0xE000_ED21 | - | - | - | - |
0xE000_ED22 | PRI_14 | PendSV的优先级 | ||
0xE000_ED23 | PRI_15 | SysTick的优先级 |
8.4.2 活动状态
每个外部中断都有一个活动状态位。在处理器执行了其 ISR的第一条指令后,它的活动位就被 置 1,并且直到 ISR返回时才硬件清零。由于支持嵌套,允许高优先级异常抢占某个 ISR。然而, 哪怕中断被抢占,其活动状态也依然为 1(请仔细琢磨前文讲到的“直到 ISR返回时才清零)。活动 状态寄存器的定义,与前面讲的使能/除能和悬起/解悬寄存器相同,只是不再成对出现。它们也能 按字/半字/字节访问,但他们是只读的,如表 8.4所示。
表 8.4 ACTIVE 寄存器族 0xE000_E300_0xE000_E31C(此表参考官方技术参考手册作了些改编——
译者注)
名称 | 类型 | 地址 | 复位值 | 描述 |
ACTIVE0 | RO | 0xE000_E300 | 0 | 中断0-31的活动状态寄存器,共32个状态 位 位[n],中断 #n活动状态(异常号16+n) |
ACTIVE1 | RO | 0xE000_E304 | 0 | 中断 32-63 的活动状态寄存器,共 32 个状 态位 |
… | … | … | … | … |
ACTIVE7 RO 0xE000_E31C 0 中断 224-239 的活动状态寄存器,共 16 个
状态位
8.4.3 特殊功能寄存器 PRIMASK与 FAULTMASK
PRIMASK用于除能在 NMI和硬 fault之外的所有异常,它有效地把当前优先级改为 0(可编程 优先级中的最高优先级)。该寄存器可以通过 MRS和MSR以下例方式访问:
1. 关中断
MOV | R0, | #1 |
MSR 2. 开中断 MOV | PRIMASK, R0, | R0 #0 |
MSR | PRIMASK, | R0 |
此外,还可以通过CPS指令快速完成上述功能:
CPSID i ;关中断
CPSIE i ;开中断
FAULTMASK更绝,它把当前优先级改为-1。这么一来,连硬fault都被掩蔽了。使用方案与
PRIMASK的相似。但要注意的是,FAULTMASK会在异常退出时自动清零。 掩蔽寄存器虽然能一手遮天,却都动不了NMI,因为NMI是用在最危急的情况下的。因此系统为
它开出单行道,无需挂号只是不要迟到。当NMI激活时,“谁都是省略号,唯独是你不得了,第一优 先谁比你重要”!试想,如果NMI被连接到系统的掉电报警线上,且系统是体外循环机的电源管理 器……如果因为中断被除能就视而不见,则会使体外循环机因断电而失能,体外循环序列可以被意 外终止,病人的生命也将丢失。
8.4.4 BASEPRI寄存器
在更精巧的设计中,需要对中断掩蔽进行更细腻的控制——只掩蔽优先级低于某一阈值的中断
——它们的优先级在数字上大于等于某个数。那么这个数存储在哪里?就存储在BASEPRI中。不过, 如果往BASEPRI中写0,则另当别论——BASEPRI将停止掩蔽任何中断。例如,如果我们需要掩蔽所 有优先级不高于0x60的中断,则可以如下编程:
MOV R0, #0x60
MSR BASEPRI, R0
如果需要取消 BASEPRI对中断的掩蔽,则示例代码如下:
MOV R0, #0
MSR BASEPRI, R0
另外,我们还可以使用BASEPRI_MAX这个名字来访问BASEPRI寄存器,它俩其实是同一个寄存 器。但是当我们使用这个名字时,会使用一个条件写操作。个中原因如下:尽管它俩在硬件水平上 是同一个寄存器,但是生成的机器码不一样,从而硬件的行为也不同:使用BASEPRI时,可以任意 设置新的优先级阈值;但是使用BASEPRI_MAX时则“许进不许出”——只允许新的优先级阈值比原 来的那个在数值上更小,也就是说,只能一次次地扩大掩蔽范围,反之则不行。就好像绳子打了死 结,只会越拉越紧。举例来说,检视下面的程序片断:
MSR | BASEPRI_MAX, R0 | ;本次设置被忽略,因为0xf0比0x60的优先级低 |
MOV MSR | R0, #0x40 BASEPRI_MAX, R0 | ;Ok。扩大掩蔽范围到优先级不高于0x40的中断 |
为了把掩蔽阈值降低,或者解除掩蔽,需要使用“BASEPRI”这个名字。上例中,把设置阈值
为0xf0的那条指令改用BASEPRI,则可以操作成功。显然,在用户级下是不得更改BASEPRI寄存器 的。与其它和优先级有关的寄存器一样,系统中表达优先级的位数,也同样影响BASEPRI中有意义 的位数。如果系统中只使用3个位来表达优先级,则BASEPRI有意义的值仅为0x00, 0x20, 0x40,
0x60, 0x80, 0xA0, 0xC0以及0xE0。
8.4.5 其它异常的配置寄存器
用法fault,总线fault以及存储器管理fault都是特殊的异常,因此给它们开了小灶。其中, 它们的使能控制是通过“系统Handler控制及状态寄存器(SHCSR)”(地址:0xE000_ED24)来实现 的。各种faults的悬起状态和大多数系统异常的活动状态也都在该寄存器中,如表8.5所示。
表8.5 系统Handler控制及状态寄存器SHCSR(地址:0xE000_ED24)
位段 | 名称 | 类型 | 复位值 | 描述 |
18 | USGFAULTENA | R/W | 0 | 用法 fault服务例程使能位 |
17 | BUSFAULTENA | R/W | 0 | 总线 fault服务例程使能位 |
16 | MEMFAULTENA | R/W | 0 | 存储器管理 fault服务例程使能位 |
15 | SVCALLPENDED | R/W | 0 | SVC 悬起中。本来已经要 SVC服务例程,但 是却被更高优先级异常取代 |
14 | BUSFAULTPENDED | R/W | 0 | 总线 fault悬起中,细节同上。 |
13 | MEMFAULTPENDED | R/W | 0 | 存储器管理 fault悬起中,细节同上 |
12 | USGFAULTPENDED | R/W | 0 | 用法 fault悬起中,细节同上 |
11 | SYSTICKACT | R/W | 0 | SysTick异常活动中 |
10 | PENDSVACT | R/W | 0 | PendSV异常活动中 |
9 | - | - | - | - |
8 | MONITORACT | R/W | 0 | Monitor异常活动中 |
7 | SVCALLACT | R/W | 0 | SVC异常活动中 |
6:4 | - | - | - | - |
3 | USGFAULTACT | R/W | 0 | 用法 fault异常活动中 |
2 | - | - | - | - |
1 | BUSFAULTACT | R/W | 0 | 总线 fault异常活动中 |
0 | MEMFAULTACT | R/W | 0 | 存储器管理 fault异常活动中 |
写这些寄存器时要小心,必须确保对活动位的修改是经过深思熟虑的,决不能粗心修改。否则,
如果某个异常的活动位被意外地清零了,其服务例程却不知晓,仍然执行异常返回指令,那么CM3
将视之为无理取闹——在异常服务例程以外做异常返回,从而产生一个fault。
译注:下段文字改编自《Cortex-M3 Technical Reference Manual》, pg8-29,是给那些骨灰级玩家们看的,因为修改这 些位还有更深层次的背景和特效。译文为:上表中的活动位虽然也是可写的,但是改动时必须予以极度的小心,否 则这是玩火行为——设置或者清零这些位,会改变处理器中对异常活动的记录,却不会对应地修复堆栈中的数据(不 会为了此改动而特意执行一次自动入栈或自动出栈操作),于是埋下了破坏堆栈内容而引起程序跑飞的隐患;另外, 其它一些重要的数据结构也得不到清除,后患无穷。事实上,只有操作系统在特殊场合下才会修改它们。例如:在
任务执行系统调用的过程中执行上下文切换(大幅提升实时性),或者在使用软件模拟未定义指令的功能期间(在用 法fault服务例程中),以及软件模拟协处理器的功能期间,执行上下文切换,同样大幅提升实时性。
下面开始讲中断控制及状态寄存器ICSR。对于NMI、SysTick定时器以及PendSV,可以通过此寄存器手工悬起它
们。另外,在该寄存器中,有好多位段都用于调试目的。在大多数情况下,它们对于应用软件都没有什么用处,只 有悬起位对应用程序常常比较有参考价值,如表8.6所示。
表8.5 中断控制及状态寄存器ICSR(地址:0xE000_ED04)
位段 | 名称 | 类型 | 复位值 | 描述 |
31 | NMIPENDSET | R/W | 0 | 写 1以悬起 NMI。因为 NMI的优先级最高且从不 掩蔽,在置位此位后将立即进入 NMI服务例程。 |
28 | PENDSVSET | R/W | 0 | 写 1 以悬起 PendSV。读取它则返回 PendSV 的 状态 |
27 | PENDSVCLR | W | 0 | 写 1以清除 PendSV悬起状态 |
26 | PENDSTSET | R/W | 0 | 写 1以悬起 SysTick。读取它则返回 PendSV的 状态 |
25 | PENDSTCLR | W | 0 | 写 1以清除 SysTick悬起状态 |
23 | ISRPREEMPT | R | 0 | =1 时,则表示一个悬起的中断将在下一步时进 入活动状态(用于单步执行时的调试目的) |
22 | ISRPENDING | R | 0 | 1=当前正有外部中断被悬起(不包括 NMI) |
21:12 | VECTPENDING | R | 0 | 悬起的 ISR 的编号。如果不止一个中断悬起, 则它的值是这次中断中,优先级最高的那一个。 |
11 | RETTOBASE | R | 0 | 如果异常返回后将回到基级(base level),并 且没有其它异常悬起时,此位为 1。若是在线程 模式下,在某个服务例程中,有不止一级的异常 处于活动状态,或者在异常没有活动时执行了异 常服务例程(此时执行返回指令将产生 fault。 此乃高危行为,大虾也需慎用),则此位为 0 |
9:0 | VECTACTIVE | R | 0 | 当前活动的ISR编号,该位段指出当前运行中的 ISR是哪个中断的(提供异常序号),包括NMI和 硬fault。如果多个异常共享一个服务例程,该例程可 根据本位段的值来判定是哪一个异常的响应导致它的执 行。把本位段的值减去16,就得到了外中断的编号,并 可以用此编号来操作外中断相关的使能/除能等寄存器。 |
更多推荐
8.4 中断的悬起与解悬
发布评论