浅谈ARM Cortex-M系列架构——异常和中断(二)

浅谈ARM Cortex-M系列架构文章

浅谈ARM Cortex-M系列架构——指令集

浅谈ARM Cortex-M系列架构——架构篇

浅谈ARM Cortex-M系列架构——异常和中断(一)


目录

前言

一、什么是中断优先级?

 抢占优先级 & 响应优先级区别

二、向量表和向量表重定向

向量表重定位的应用

1.具有 Boot loader的设备

2.应用程序加载到RAM

3.动态修改向量表

最后


前言

上次我们讲了Cortex-M系列的异常和中断控制器(NVIC),以及谈到了中断向量表,今天这篇文章主要介绍中断优先级、向量表和向量表重定向。


一、什么是中断优先级?

  对于Cortex-M 处理器(包括ARMv6-M和ARMv7-M)异常是否能被处理器接受以及何时被处理器接受并执行异常处理,是由异常的优先级和处理器当前的优先级决定的。更高优先级的异常(优先级编号更小)可以抢占低优先级的异常(优先级编号更大),这就是异常/中断嵌套的情形。有些异常(复位、NMI和 HardFault)具有固定的优先级,其优先级由负数表示这样,它们的优先级就会比其他的异常高。其他异常则具有可编程的优先级,范围为 0~255。

  Cortex-M3和Cortex-M4处理器在设计上具有3个固定的最高优先级以及256个可编程优先级(具有最多 128 个抢占等级),可编程优先级的实际数量由芯片设计商决定。多数Cortex-M3或Cortex-M4 芯片支持的优先级较少,如8、16、32等。这是因为大量的优先级会增加NVIC 的复杂度,而且会增加功耗降低速度。多数情况下,应用程序只需少量的编程优先级。因此,芯片设计人员需要基于目标应用的优先级数量定制处理器设计。优先级的减少是通过去除优先级配置寄存器的最低位(LSB)实现的。上篇文章我也说到了例如F103芯片支持16个系统异常中断和60可屏蔽中断。

  这么一看是不是感觉很复杂,其实中断优先级的作用就相当于我们的程序在某一时刻触发了多种中断,但我们知道程序是顺序执行的,那么我们就要判断哪一个中断先执行。按照之前中断向量表的所定义的内容,肯定是中断编号越小的先执行。那当我们该如何让中断编号高的先执行呢?

这就使用到了我们的中断优先级,通过配置抢占优先级和响应优先级(子优先级)重新来实现中断执行的顺序。

下图是STM32F103ZETx芯片的优先级分组选择,我们可以看到有组0~4可以选择。可以对每个中断设置一个抢占优先级和一个响应优先级值。

浅谈ARM Cortex-M系列架构——异常和中断(二)_第1张图片

我们可以在HAL_Init中看到当前配置的优先级分组。

浅谈ARM Cortex-M系列架构——异常和中断(二)_第2张图片 当我们选择优先级分组为4时,我们可以看到Preemption Priority(抢占优先级)这一栏的设置最高是15,即2^4个抢占优先级设置。

浅谈ARM Cortex-M系列架构——异常和中断(二)_第3张图片

 由于我们设置优先级分组为4,这就导致我们中断的响应优先级(子优先级)只能配置为0。

 抢占优先级 & 响应优先级区别

  • 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的
  • 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级
  • 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行
  • 如果两个中断的抢占优先级和相应优先级都是一样的话,则看哪个中断先发生就先执行

NVIC中有一个专门用于配置中断优先级的寄存器NVIC_IPRx,IPR是一个8位的寄存器,理论上可配置的中断优先级为0~255,但是以我们F103芯片为例最大的中断优先级配置为0~16,这是因为我们大多数的单片机系统都会将中断优先级简化,降低NVIC的复杂度,降低成本。

F103只使用了该寄存器的高4位。

浅谈ARM Cortex-M系列架构——异常和中断(二)_第4张图片

浅谈ARM Cortex-M系列架构——异常和中断(二)_第5张图片

 因为F103只使用了Bit[7:4]位,所以将优先级分组3~7改为0~4,对应的抢占优先级和子优先级如下表。

抢占优先级位数 子优先级位数
0 0 16
1 2 8
2 4 4
3 8 2
4 16 0

 需要注意的是,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后我们一般不会再次改变分组。因为随意改变分组会导致中断管理混乱,使得程序出现奇奇怪怪的执行结果。

二、向量表和向量表重定向

当Cortex-M处理器接受了某异常请求后,处理器需要确定该异常处理(若为中断则是ISR)的起始地址。该信息位于存储器内的向量表中,向量表默认从地址0开始,向量地址则为异常编号乘 4,为什么向量地址要乘于4呢?我们可以这样理解,因为我们STM32内部的寄存器是32位的,而32位刚好对应4个字节,所以要乘于4.

如图下图所示。向量表一般被定义在微控制器供应商提供的启动代码中。从表中我们可以看出Cortex-M处理器的向量表以异常处理作为向量表的起始地址。

浅谈ARM Cortex-M系列架构——异常和中断(二)_第6张图片

  一般来说,起始地址(0x00000000)处应为启动存储器,它可以为 Flash 存储器或ROM设备,而且在运行时不能对它们进行修改。不过,有些应用可能需要在运行时修改或定义向量表。为了进行这种处理,Cortex-M3和Cortex-M4处理器实现了一种名为向量表重定位的特性。
  向量表重定位特性提供了一个名为向量表偏移寄存器(VTOR)的可编程寄存器。该寄存器将正在使用的存储器的起始地址定义为向量表。

注意,该寄存器在 CortexM3的版本r2p0和r2p1间稍微有此不同。

浅谈ARM Cortex-M系列架构——异常和中断(二)_第7张图片

对Cortex-M3 r2p0或之前版本向量表只能位于CODE和SRAM区域而这个限制在 Cortex-M3r2pl和Cortex-M4中已经不存在了。

VTOR的复位值为0若使用符合CMSIS 的设备驱动库进行应用编程,可以通过SCB->VTOR访问该寄存器。要将向量表重定位到 SRAM区域的开头外,可以使用下面的代码

在使用 VTOR时,需要将向量表大小扩展为下一个 2的整数次方,且新向量表的基地址必须要对齐到这个数值。

举个例子,假设微控制器有60个中断源

    向量表大小为(60(用于中断)十16(用于系统异常空间))X4(每个向量的字节数)=304(0x103)。将其扩大为下一个2的整数次方就得到 512 字节,因此,向量表的地址可被设置为0x00000000、0x00000200以及0x00000400等。

需要注意的是,由于中断的最小数量为1,最小的向量表对齐为 128字节,因此,VTOR的最低7位保留,且被强制置为0。

向量表重定位的应用

1.具有 Boot loader的设备

   有些微控制器具有多个程序存储器:启动 ROM 和用户Flash 存储器。微控制器生产商般会将 Boot loader 预先写到启动 ROM 中,这样在微控制器启动时,启动 ROM 中的 Bootloader 就会首先执行,而且在跳转到用户 Flash 的应用程序前,VTOR会被设置为指向用户Flash存储器的开始处,因此会使用用户Flash中的向量表。

如图所示

浅谈ARM Cortex-M系列架构——异常和中断(二)_第8张图片

2.应用程序加载到RAM

  有些情况下,应用程序可能会被从外部设备加载到 RAM 中执行,它可能会位于SD卡中,或者甚至需要通过网络传输。在这种情况下,存储在片上存储器中用于启动的程序需要初始化一些硬件、复制位于外部设备中的应用程序到 RAM、更新 VTOR后执行存储在外部的程序。

如图所示

浅谈ARM Cortex-M系列架构——异常和中断(二)_第9张图片

3.动态修改向量表

  有些情况下,ROM中可能会有一个中断的多个处理实例,可能需要在应用的不同阶段在它们之间进行切换。在这种情况下,可以将向量表从程序存储器复制到 SRAM,并且设置VTOR指向 SRAM中的向量表。由于SRAM中的内容可在任意时间修改,因此可以轻易地在应用的不同阶段修改中断量。

向量表最少也要提供 MSP 的初始值以及用于系统启动的复位向量。另外,对于一些应用若设备在启动时有触发NMI的可能,也许还需要加入NMI向量和用于错误处理的HardFault向量。


最后

感谢各位的阅读,最近事情比较多,更新会比较慢。

你可能感兴趣的:(stm32,架构)