当前位置: 首页 > news >正文

STM32F407学习记录(八)Cortex-M3/M4权威指南:第四章架构

前言:本文章相当于学习笔记,摘录总结。推荐自行观看相关书籍(权威指南),不足之处,请指点。

4.1架构简介

Cortex-M3和Cortex-M4都是基于ARMv7-M架构。Cortex-M4发布时,架构中又额外增加了新的指令和特性,改进后的架构有时也被称为ARMv7E-M.

嵌入式开发,只需要了解编程模型、异常(如中断)如何处理、存储器映射、如何使用外设以及如何使用微控制器供应商提供的软件驱动文件等。

4.2编程模型

4.2.1操作模式和状态

Cortex-M3/M4处理器有两种操作状态和两个模式,以及特权和非特权访问等级。

(1)操作状态

调试状态:当处理器被暂停后(例如,通过调试器或触发断点后),就会进入调试状态并停止指令执行。

Thumb状态:若处理器在执行程序代码(Thumb指令),它就会处于Thumb状态。Cortex-M处理器只有Thumb状态,因为它没有ARM指令集。

(2)操作模式

处理模式:执行中断服务程序(ISR)等异常处理。在处理模式下,处理器具有特权访问等级。

线程模式:在执行普通的应用程序代码时,处理器既可以处于特权访问等级,也可以处于非特权访问等级。

(3)访问等级

特权访问等级:可以访问处理器中的所有资源。NVIC寄存器只支持特权访问。

非特权访问等级:有些存储器区域不能访问,有些操作也是无法使用的。

访问等级由特殊寄存器(CONTROL)控制。

4.2.2寄存器

处理器中有多个用于执行数据处理和控制的寄存器,可以以寄存器组的形式对其进行分组。

每个数据处理指令都指定了所需的操作和源寄存器或目的寄存器

加载---存储架构:若要处理存储器中的数据,需要将数据从存储器中加载到寄存器里;处理器处理完数据后,如果有必要,还要将寄存器里的数据,写回存储器。

(1)R0~R12

R0~R7为低寄存器,16位的指令只能访问低寄存器。

R8~R12为高寄存器,可以用于32位指令和几个16位指令。

R0~R12的初始值是未定义的。

(2)R13:栈指针(SP)

R13为栈指针,可以通过PUSH和POP操作实现栈存储的访问。

在物理上存在两个栈指针:

主栈指针(MSP):为默认的栈指针,在复位后或处理器处于处理模式时,其会被处理器选择使用。

进程栈指针(PSP):其只能用于线程模式。

两个栈指针由特殊寄存器CONTROL决定,对于一般的程序,这两个寄存器只会有一个可见。

两个栈指针都是32位的,但是因为最低两位总是0,所以栈操作的地址必须对齐到32位的字边界上

大多数情况下,PSP没必要使用。一般用在OS中,OS的内核与任务的栈相互独立

PSP的初始值未定义,但是MSP的初始值则需要在复位流程中从存储器的第一个字中取出。

(3)R14:链接寄存器(LR)

用于函数或子程序调用时返回地址的保存。

在函数或子程序结束时,通过将LR的数值加载至PC(程序计数器)中返回到调用函数或者子程序处,继续执行。

当调用函数或者子程序后,LR的数值会自动更新。如果函数中又调用了另一个函数,那么需要先将LR的数值保存在栈中,否则返回地址将会被覆盖。

如果发生了异常,LR里的值会被自动更新为特殊的EXC_RETURN(异常返回)值,之后在处理完异常后,希望通过LR返回,会发现这是一个特殊值,则触发异常返回操作

PC地址永远是偶数的,因为指令必须对齐到半字或者字地址。但是LR的第0位,有一个解释:在进行指令跳转时,如果是经典的ARM处理器,第0位等于1,则表示跳转过去后,切换到Thumb状态;第0位等于0,则跳转过去后,切换到ARM状态。但是Cortex-M3/M4处理器只有Thumb状态,所以第0位的作用变成了,等于1表示正常跳转,等于0表示错误的跳转地址,触发异常。并且等于1的话,加载到PC的地址,会自动忽略第0位,强制改为0。

(4)R15(程序计数器PC)

可读可写,读操作返回当前指令地址+4(流水线特性)。写PC会引起跳转操作。

(5)程序中使用的寄存器名

对于多数汇编工具,寄存器有多种名称,也可以大小写混用。

4.2.3特殊寄存器

除了寄存器组中的寄存器,处理器中还有多个特殊寄存器。这些寄存器表示处理器状态、定义了操作状态和中断、异常屏蔽。一般来说用不到,但是开发嵌入式OS或需要高级中断屏蔽特性时,就需要访问它们。

特殊寄存器未经过存储器映射,需要通过MSR和MRS等特殊寄存器访问指令进行访问。CMSIS_Core也提供了几个用于访问特殊寄存器的C函数。

MRS <reg>,<special_reg>;将特殊寄存器读入寄存器 MSR <special_reg>,<reg>;写入特殊寄存器

(1)程序状态寄存器

程序状态寄存器包括三个状态寄存器:应用PSR(APSR)、执行PSR(EPSR)、中断PSR(IPSR)

这三个寄存器可以通过如下图所示的组合寄存器(xPSR)访问。

MRS R0, PSR ;读组合程序状态字 MSR PSR, R0 ;写组合程序状态字 ;可以单独访问每个PSR MRS r0, APSR ;将标志状态读入R0 MRS R0, IPSR ;读取异常/中断状态 MSR APSR, R0 ;写标志状态 ;软件代码无法直接使用MRS(读出为0)或MSR直接访问EPSR ;IPSR为只读的,可以从组合PSR(xPSR)中读出。

每个架构的PSR不太一样。

(2)PRIMASK、FAULTMASK、BASEPRI寄存器

这三个寄存器都用于异常或中断屏蔽,每个异常(包括中断)都有一个优先等级,数值越小优先级越高。这些特殊寄存器可基于优先等级屏蔽异常,只有在特权访问等级才可以对它们进行操作(非特权状态下的写操作会被忽略,而读出则会返回0)。它们默认全部为0,也就是屏蔽(禁止异常/中断)不起作用,也就是中断是打开的

PRIMASK寄存器为1位宽的中断屏蔽寄存器。在置位时,它会阻止不可屏蔽中断
(NMI)和HardFault异常之外的所有异常(包括中断)。实际上,它是将当前异常优先级提升
为0,这也是可编程异常/中断的最高优先级。PRIMASK最常见的用途为,在时间要求很严格的进程中禁止所有中断,在该进程完成后,需要将PRIMASK清除以重新使能中断。
FAULTMASK和 PRIMASK 非常类似,不过它还能屏蔽HardFault 异常,它实际上是将异常优先级提升到了-1。假设你进入了 HardFault 处理函数,原因是访问了非法内存地址(触发了 MemManage 错误)。如果你在错误处理函数里想去“清理现场”或“保存肇事数据”,而再次访问了那块非法地址,系统会立即再次触发 HardFault。由于 CPU 还在 HardFault 里没出来,新来的 HardFault 会直接导致Double Fault(双倍错误),系统直接锁死(进入 Lockup 状态)FAULTMASK 的作用就是进入错误处理时先戴上“防爆头盔”(开启 FAULTMASK),此时即使你在修复代码中访问了错误地址、触发了总线错误或 MPU 保护,CPU 也会选择“忽略”这些新错误,不让他们打断当前的处理流程。这样你就能安心地执行修复措施(比如强行读取危险地址的数据来备份,或者重置外设)。与PRIMASK不同,FAULTMASK在异常返回时会被自动清除
为使中断屏蔽更加灵活,ARMv7-M架构还支持BASEPRI,该寄存器会根据优先等级屏蔽异常或中断。BASEPRI的宽度取决于设计实际实现的优先级数量,这是由微控制器供应商决定的。大多数Cortex-M3或Cortex-M4微控制器都有8个(3位宽)或16个可编程的异常优先级,此时,BASEPRI的宽度就相应地为3位或4位。BASEPRI为0时就不会起作用,当被设置为非0数值时,它就会屏蔽具有相同或更低优先级的异常(包括中断),而更高优先级的则还可以被处理器接受。
CMSIS-Core提供了多C 函数个 C 函数用于访问 PRIAMSK,FAULTMASK 及 BASEPRI 寄存
器(注意,这些寄存器只能在特权等级下访问)。

X = _get_BASEPRI(); //读BASEPRI寄存器 X = _get_PRIMARK(); //读PRIMASK寄存器 X = _get_FAULTMASK(); //读FAULTMASK 寄存器 _set_BASEPRI(x); //设置BASEPRI的新数值 _set_PRIMASK(x); //设置PRIMASK的新数值 _set_FAULTMASK(x) ; //设置FAULTMASK的新数值 _disable_irq(); //设置PRIMASK,禁止 IRQ _enable_irq(); //清除 PRIMASK,使能IRQ ;还可以使用汇编代码访问这些异常屏蔽寄存器: MRS r0, BASEPRI ;将BASEPRI寄存器读入RO MRS r0, PRIMASK ;将PRIMASK寄存器读入RO MRS r0, FAULTMASK ;将FAULTMASK寄存器读入RO MSR BASEPRI, r0 ;将RO写入BASEPRI寄存器 MSR PRIMASK, r0 ;将RO写人PRIMASK寄存器; MSR FAULTMASK, r0 ;将RO写人FAULTMASK寄存器 另外,利用修改处理器状态(CPS)指令,可以很方便地设置或清除PRIMASK和FAULTMASK 的数值。 CPSIE i ;使能中断(清除PRIMASK) CPSID i ;禁止中断(设置PRIMASK) CPSIE f ;使能中断(清除FAULTMASK) CPSID f ;禁止中断(设置FAULTMASK)

(3)CONTROL寄存器

栈指针的选择(主栈指针/进程栈指针)。
线程模式的访问等级(特权/非特权)。
对于具有浮点单元的Cortex-M4处理器,CONTROL寄存器中有一位表示当前上下文(正在执行的代码)是否使用浮点单元。

复位后,CONTROL寄存器默认为0,这也就意味着处理器此时处于线程模式、具有特权访问权限以及使用主栈指针。通过写CONTROL寄存器,特权线程模式的程序可以切换栈指针的选择或进人非特权访问等级。不过,nPRIV(CONTROL的第0位)置位后,运行在线程模式的程序就不能访问CONTROL寄存器了。

运行在非特权等级的程序无法再切换回特权访问等级;要将处理器在线程模式切换回特权访问等级,则需要使用异常机制。在异常处理期间,处理程序可以清除nPRIV位。再返回到线程模式后,处理器就会进入特权访问等级。

若使用嵌入式OS每次上下文切换时都可以重新编程CONTROL寄存器,以满足应用间不同的特权访问等级需要。

nPRIV和 SPSEL的设置共有4种组合方式,其中3种在实际应用中较为常见。


对于未使用嵌入式OS的多数简单应用,无须修改CONTROL寄存器的数值。整个应用可以运行在特权访问等级并且只使用MSP

要利用C语言访问CONTROL寄存器,可以使用符合CMSIS的设备驱动库提供的以下函数: x =_get_CONTROL(); //读取CONTROL寄存器的当前值 _set_CONTROL(x); //设置CONTROL寄存器的数值为x

在修改CONTROL寄存器的值时需要注意以下两点:

1.对于具有浮点单元(FPU)的Cortex-M4处理器,或具有FPU的ARMv7-M处理器,由于浮点单元的存在,FPCA位会自动置位。若程序中包含浮点运算但FPCA位被意外清除,而且接下来产生了一个中断,那么浮点单元寄存器中的数据将不能在异常人口流程保存,且可能会被中断处理覆盖。在这种情况下,继续执行被中断的任务时,程序能无法继续正确地处理

2.在修改了CONTROL寄存器后,从架构来看,应该使用指令同步屏障(ISB)指令(或符合CMSIS的设备驱动库中的----ISB()函数),以确保本次修改对接下来的代码能起到作用。 由于 Cortex-M3、Cortex-M4、Cortex-M0+、Cortex-M0 以及 Cortex-M1 的流水线非常简单,不使用ISB指令也不会引起什么问题。ISB(指令同步屏障)指令的作用是清空处理器流水线。它会确保在ISB执行完成前,其之前的所有指令都已执行完毕;而其后的所有指令,都必须在ISB完成后重新从内存或缓存中获取

为什么架构要求必须使用ISB

处理器为了提升效率,会采用流水线技术,在执行当前指令的同时,预先从内存中读取(预取)后续的多条指令。

问题就出在这里:CONTROL寄存器的修改会影响后续指令的执行环境(比如权限)。但如果在修改CONTROL时,流水线里已经预取了一些基于旧权限的指令,那么这些指令就可能被错误地执行。

ISB指令的作用,就是强制清空流水线中这些“过时”的预取指令,保证ISB之后的所有指令都使用更新后的CONTROL寄存器状态来执行。

;要用汇编访问CONTROL寄存器,可以使用MRS和MSR指令: MRS r0, CONTROL ;将 CONTROL寄存器读人RO MSR CONTROL, r0 ;将RO写人CONTROL寄存器
可以通过检查CONTROL和IPSR的数值来确定当前是否为特权等级。IPSR为正在处理中断编号。 int in_privileged(void) { if(_get_IPSR()!=0)return 1;//Ture else if((_get_CONTROL()&0X1)==0)return 1;//Ture else return 0;//False }

4.2.4浮点寄存器

Cortex-M4具有可选的浮点单元,其提供了浮点数据处理用的一些寄存器以及浮点状态
控制寄存器(FPSCR)

(1)S0~S31和 D0~D15

S0~S31(S)都为32位寄存器,而且每个都可以通过浮点指令访问,或者利用符号D0~D15(D代表双字/双精度)成对访问。例如,S0和S1成对组成D0,而S3和S2则成对组成D1。尽管Cortex-M4中的浮点单元不支持双精度浮点运算,在传输双精度数据时仍可使用浮点指令。

(2)浮点状态和控制寄存器(FPSCR)
FPSCR中包含多个位域:定义一些浮点运算动作;提供浮点运算结果的状态信息。

浮点控制默认被配置为符合IEEE754单精度运算。在普通应用中,浮点运算控制的设置也无须修改。

(3)经过存储器映射的浮点单元控制寄存器

除了浮点单元寄存器组和FPSCR,浮点单元还往系统中引人了一些经过存储器映射的寄存器,如用于使能和禁止浮点单元的协处理器访问控制寄存器(CPACR)。浮点单元默认被禁止以降低功耗,在使用任何浮点指令前,都必须通过编程CPACR寄存器,来使能浮点单元。

在使用符合CMSIS的设备驱动库的C编程环境中: SCB -> CPACR = 0xFK20; //使能对FPU的全访问
在汇编语言编程环境中,可以使用下面的代码: LDR R0, = 0xE000ED88 ;RO被设置为CPACR的地址 LDR R1,= 0x00F00000 ; R1 = 0xF<20 LDR R2 [R0] ;读取CPACR的当前值 ORRS R2, R2, R1 ;置位 STR R2,[R0] ;将修改后的数值写回CPACR

4.3应用程序状态寄存器(APSR)

APSR中包含下面的几组状态标志:
整数运算的状态标志(N-Z-C-V位)
饱和运算的状态标志(Q位)
SIMD运算的状态标志(GE位)

4.3.1整数状态标志

整数状态标志和其他许多处理器架构中的ALU状态标志类似,它们受普通数据处理指令的影响,在控制条件跳转和条件执行时非常有用。另外,APSR标志之一,也就是C(进位)位,也可用于加法和减法运算中。

对于ARMv7-MARMv7E-M架构,多数16位指令影响这4个ALU标志。对于多数32位指令指令编码中的一个位定义了是否应该更新APSR标志。注意,部分指令不会更新V标志和C标志。例如,MULS(乘法)指令只会修改N标志和Z标志。除条件跳转或条件执行代码,APSR的进位标志也可用于将加法和减法运算扩大为超过32位。例如,在将两个64位整数相加时,可以将低32位加法运算的进位标志作为高32位加法的一个输人:

//计算Z=X+Y,其中X、Y和Z都是64位的 Z[31:0] = x[31:0] + Y[31:0]; //低字相加,更新进位标志 Z[63:32] = X[63:32] + Y[63:32] + Carry; //高字相加

ARM处理器都具有N-Z-C-V标志,其中包括Cortex-M0处理器。

4.3.2Q状态标志

Q表示饱和算术运算饱和调整运算过程中产生了饱和,其存在于ARMv7-M(如Cortex-M3和 Cortex-M4 处理器),但在 ARMv6-M(如 Cortex-MO 处理器)中不可用。在该位被设置后,以及软件写APSR清除Q位之前,它会一直保持置位状态,饱和算术/调整运算不会清除该位。
作用:它的作用就是记录“自从上一次被清除以来,是否发生过饱和运算”

“饱和”(Saturation)是DSP(数字信号处理)运算中的一种特殊处理方式。

(1)普通算术(环绕/取模):普通+-指令在溢出时,结果会回绕(例如0x7FFFFFFF + 1 = 0x80000000)。这在音频或控制算法中会引发剧烈失真。

(2)饱和算术(钳位):专用的SSAT(有符号饱和)USAT(无符号饱和)QADD(饱和加法)等指令,在溢出时会将结果钳位到最大值或最小值(例如0x7FFFFFFF + 1 的结果依然是 0x7FFFFFFF)。

应用场景:自动增益控制(AGC):在音频或无线通信中,输入信号过大导致饱和。虽然输出还能听,但波形已被削平(失真)。主程序可以在中断或主循环中定期读取Q标志,如果发现Q=1,说明信号过载了,程序就会主动减小放大器的增益,然后手动清除Q标志,重新开始监控。

Q标志的作用就是:当上述这些“饱和指令”执行时,如果发生了钳位(即数据被截断在最大值/最小值),硬件就会自动将Q标志置为1

4.3.3GE位

在Cortex-M4中,“大于等于”(GE)位域在APSR中占用4 位,而 Cortex-M3处理器中则不存在。许多SIMD指令都会更新该标志,其中,多数情况下,每个位表示SIMD运算的每个字节为正或溢出。对于具有16位数据的SIMD指令,第0和1位由低半字的结果控制,第2和3位则由高半字的结果控制。

SIMDSingle Instruction, Multiple Data的缩写,即“单指令多数据流”。它允许处理器用一条指令,同时对多个数据执行相同的操作。

  • 核心思想:数据打包(Packing)
    在32位的ARM处理器上,一个寄存器可以存放4个8位的数据或2个16位的数据。SIMD指令就是把这些“打包”在一个寄存器里的多个数据,拿出来同时计算。

  • 主要优势:提升效率
    比如,要计算4对8位数据的加法。普通指令需要4步,而一条SIMD指令(如SADD8)就能一步完成。这在处理图像、音频等数据时能显著提升效率。

  • GE位Greater than or Equal的缩写,是应用程序状态寄存器(APSR)中的4个位(GE[3:0])。它的作用是记录SIMD指令的运算结果

  • 如何记录?
    当执行SADD8(有符号8位并行加法)这类SIMD指令时,硬件会自动设置GE位。规则如下:

    • GE[0]对应字节0[7:0])的运算结果。

    • GE[1]对应字节1[15:8])的运算结果。

    • GE[2]对应字节2[23:16])的运算结果。

    • GE[3]对应字节3[31:24])的运算结果。

    如果某个通道的运算结果大于或等于零,其对应的GE位就会被置为1;反之则被清零。

GE位本身不直接参与运算,它的价值在于配合SEL(选择)指令

SEL指令会根据GE[3:0]的值,从两个源寄存器RnRm中逐字节选择数据,拼成最终结果。

4.4存储器系统

4.4.1存储器系统特性

(1)4GB线性地址空间。通过32位寻址,ARM处理器可以访问多达4GB的存储器空间。尽管许多嵌入式系统需要的存储器都不超过1MB,32位的寻址能力可以确保将来升级和扩展的可能。Cortex-M3和 Cortex-M4处理器用AHB LITE总线协议提供了32位总线,利用合适的存储器接口控制器,可以将32/16/8位处理器连接到总线上。

(2)架构定义的存储器映射。4GB的存储器空间被划分为多个区域,用于预定义的存储器
和外设,以优化处理器设计的性能。例如,Cortex-M3和Cortex-M4处理器具有多个总线接口,允许对程序代码用的CODE区域的访问和对SRAM或外设区域的数据操作同时进行。

(3)支持小端和大端的存储器系统。Cortex-M3和Cortex-M4处理器可以使用小端或大
端的存储器系统。实际上,微控制器产品一般会被设计成只具有一种端配置。
(4)位段访问(可选)。当包含位段特性时(由微控制器/片上系统供应商决定),存储器映
射中的两个1MB区域可以通过两个位段区域进行位寻址,这样可以实现对SRAM或外设地址空间中单独位的原子操作。
(5)写缓冲。若对可缓冲存储器区域的写传输需要花费几个周期,Cortex-M3和Cortex-
M4处理器内的写缓冲可能会将本次传输缓存起来,处理器可以继续执行下一条指令,如果可能的话,这样可以提高程序的执行速度。
(6)存储器保护单元(MPU)。MPU定义了各存储器区域的访问权限,且为可编程的。
Cortex-M3和 Cortex-M4处理器中的 MPU支持8个可编程区域,可在嵌人式OS中提高系统的健壮性。

(7)非对齐传输支持。ARMv7-M架构的所有处理器(包括Cortex-M3和Cortex-M4处理器)都支持非对齐传输。

Cortex-M处理器的总线接口为通用总线接口,可通过不同的存储器控制器被连接至不同类型和大小的存储器。微控制器存储器系统中的存储器一般为两种或更多:程序代码用的Flash存储器、数据用的静态RAM(SRAM),有时还会有电可擦除只读存储器(EEPROM)

4.4.2存储器影射

Cortex-M处理器的4GB地址空间被分为了多个存储器区域。区域根据
各自典型用法进行划分,它们主要用于:

(1)程序代码访问(如CODE区域)

(2)数据访问(如SRAM区域)

(3)外设(如外设区域)

(4)处理器的内部控制和调试部件(如私有外设总线)

程序既可以在CODE区域执行,也可以在SRAM区域执行,而且微控制器也可以在CODE区域加人SRAM块。

不同的微控制器具有不同的存储器大小和外设地址,这些信息一般会在微控制器供应商提供的数据手册中有所描述。

所有Cortex-M处理器的存储器映射处理都是一样的。例如,PPB地址区域中存在嵌套向量中断控制器(NVIC)的寄存器、处理器配置寄存器以及调试部件的寄存器等。

4.4.3栈存储

Cortex-M处理器在运行时需要栈存储和栈指针(R13)。在栈这种存储器使用机制中,存储器的一部分可被用作后进先出的数据存储缓冲。ARM处理器将系统主存储器用于栈空间操作,且使用PUSH指令往栈中存储数据以及POP指令从栈中提取数据。每次PUSH和POP操作后,当前使用的栈指针都会自动调整

栈可用于:

(1)当正在执行的函数需要使用寄存器(寄存器组中)进行数据处理时,临时存储数据的初
始值。这些数据在函数结束时可以被恢复出来,以免调用函数的程序丢失数据。

(2)往函数或子程序中的信息传递。

(3)用于存储局部变量

(4)在中断等异常产生时保存处理器状态和寄存器数值。

Cortex-M处理器使用的栈模型被称作“满递减”处理器启动后SP被设置为栈存储空
间最后的位置。对于每次PUSH操作,处理器首先减小SP的值,然后将数据存储在SP指向
的存储器位置。
在操作期间,SP指向上一次数据被存储在栈中的位置。

寄存器中的数值稍后还会被主程序用到,因此,它们就被PUSH保存到栈中,且在functionl结束时被POP恢复。这样,调用函数的程序代码不会丢失任何数据而且可以继续执行。注意,对于每次PUSH操作(保存到存储器),都会有一个对应的POP(从存储器中读取),而且POP操作的地址应该和 PUSH的一致。

每个PUSH和POP指令都可以往/从栈空间传输多个数据。由于寄存器组中的寄存器都是32位的,每个由栈PUSH和POP生成的存储器传输都会访问至少一个字(4字节)的数据,而且地址总是对齐到4字节边界上,SP的最低两位也总是为0。


可以将函数返回和POP操作结合起来,首先,把LR(R14)的数值压到栈存储中,在子程序/函数结束时将其恢复到PC(R15)。

Cortex-M处理器在物理上存在两个栈指针。它们为:

主栈指针(MSP)。复位后默认使用的栈指针,用于所有的异常处理。

进程栈指针(PSP)。只能用于线程模式的栈指针,通常用于运行嵌入式OS的嵌入式系统中的应用任务。

MSP和PSP的选择由 CONTROL寄存器的第2位SPSEL的数值决定,若该位为0,则线程模式在栈操作时使用MSP,否则线程模式使用PSP。另外,在从处理模式到线程模式的异常返回期间,栈指针的选择可由EXC_RETURN(异常返回)数值决定,这样处理器硬件会相应地自动更新SPSEL的数值。

在异常返回时,选择哪个栈指针(MSP主栈指针 或 PSP进程栈指针)不再是软件通过代码设置的,而是由硬件根据EXC_RETURN的值自动决定的。硬件会直接修改CONTROL寄存器中的SPSEL(栈指针选择)位,程序员不需要(也不应该)在此时手动写代码去改CONTROL寄存器。EXC_RETURN本质上是一个特殊的“魔法”数值(在0xFFFFFFFx范围内),它并不是一个真正的返回地址,而是一个携带了返回上下文信息的状态值

当CPU执行异常返回指令(如BX LR,且LR中装载的是EXC_RETURN值)时,硬件会执行一个原子性的出栈(Pop)和恢复序列。在这个序列中,它会执行以下操作:

1.读取EXC_RETURN:判断Bit[2]的值。

2.选择出栈用的SP:如果Bit[2]==1,硬件就使用PSP来弹出(恢复)之前压入的寄存器;如果Bit[2]==0,则使用MSP弹出。软件(RTOS内核)在切换任务时,把当前要运行的任务的栈顶地址,手动写进了PSP寄存器里。

3.同步更新CONTROL.SPSEL:在弹出PC(程序计数器)并跳转回线程模式之前,硬件自动CONTROL寄存器中的SPSEL位设置为与Bit[2]一致的值。

4.切换模式:硬件将处理器模式从处理模式切换回线程模式。

对于不具有OS的简单应用,线程模式和处理模式都可以只使用MSP。在异常事件产生后,处理器在进入中断服务程序(ISR)前会首先将多个寄存器压人栈中,这种寄存器状态保存操作被称作“压栈”,而在ISR结束时,这些寄存器又会被恢复到寄存器组中,这种操作则被称作“出栈”

若嵌人式系统中包含嵌入式OS,它们通常会将应用任务内核所用的栈空间分离开来,因此,PSP就会被用到,而且在异常人口和异常退出时会发生SP切换。利用这种分离的栈结构,栈或应用任务的错误不会影响OS使用的栈,同时也简化了OS设计,提高了上下文切换的速度

尽管同一时间内只有一个SP可见(当使用SP或R13访问时),也可以直接读/写MSP以及PSP,无须考虑使用的是哪个SP/R13。

假定当前处于特权等级,则可以利用下面的CMSIS 函数问 MSP 和 PSP: x =_get_MSP(); //读取MSP的数值 _set_MSP(x); //设置MSP的数值 x =_get_PSP(); //读取PSP的数值 _set_PSP(x); //设置PSP的数值
要在汇编代码中访问MSP和PSP,可以使用MSR和 MRS指令: MRS R0, MSP ;将主栈指针读人R0 MSR MSP, R0 ;将R0写人主栈指针 MRS R0, PSP ;将进程栈指针读人R0 MSR PSP,R0 ;将R0写人进程栈指针

MSP和PSP一般用于嵌入式 OS。例如,通过MRS指令读取PSP的数值后,OS可以从应用任务API调用的栈中读出压入的数据(如SVC执行前的寄存器内容),而且OS的上下文切换代码会在上下文切换期间更新PSP的数值。
上电后,处理器硬件在读取向量表后会自动初始化MSP。PSP则不会被自动初始化,需要在使用前由软件初始化

4.4.4存储器保护单元(MPU)

MPU在Cortex-M3和 Cortex-M4处理器中是可选的,因此并不是所有的Cortex-M3或Cortex-M4微控制器都有MPU特性。多数应用不会用到MPU,因此可以忽略。在需要高可靠性的嵌入式系统中,MPU可以通过定义特权和非特权访问权限,来保护存储器区域。
MPU是可编程的,而且 Cortex-M3和 Cortex-M4处理器中的MPU支持8 个可编程区域。MPU可以有多种用法。有些情况下,MPU由嵌人式OS控制,每个任务都被配置了存储器访问权限;而对于其他情况,MPU被配置为只保护某一特定存储器区域,如将某存储器区域设置为只读。

4.5异常和中断

4.5.1什么是异常

打断运行的程序,去执行异常处理函数,执行完后,返回被打断的地方继续运行。中断也是异常,它可以由外设或者软件触发,触发后去执行中断服务程序(ISR)。

(1)NVIC处理异常。NVIC可以处理多个中断请求(IRQ)一个不可屏蔽中断(NMI)请求,IRQ一般由片上外设或外部中断输入通过I/O端口产生,NMI可用于看门狗定时器或掉电检测(一种电压监视单元,在电压低到一定程度时会给处理器产生警告)。处理器内部也有名为SysTick的定时器,它可以产生周期性的定时中断请求,可用于嵌人式OS计时或没有OS的应用中的简单定时控制

(2)处理器自身也是一个异常事件源,其中包括表示系统错误状态的错误事件以及软件
产生
支持嵌人式OS操作的异常

每个异常源都有一个异常编号,编号1~15被归为系统异常,16号及其之上的则用于中断。Cortex-M3和Cortex-M处理器在设计上支持最多240个中断输人,实际一般在16~100之间,这样可以减小硅片面积,同时也会降低功耗。
异常编号在多个寄存器中都有所体现,其中包括用于确定异常向量地址的IPSR。异常向
量存储在向量表中,在异常入口流程中,处理器会读取这个表格以确定异常处理的起始地址。
注意,异常编号的定义和CMSIS的设备驱动库中的中断编号定义不同。在CMSIS的设备驱
动库
中,中断编号从0开始,系统异常编号则为负值。
与ARM7TDMI等经典ARM处理器相比,Cortex-M处理器中没有FIQ(快速中断)。不过,
Cortex-M3和Cortex-M4的中断等待非常小,只有12个周期,因此这也不会引起什么问题。
复位是一种特殊的异常,当处理器从复位中退出时,就会在线程模式(而不是其他异常时
的处理模式)下执行复位处理。IPSR中的异常编号读出也是0。

4.5.2嵌套向量中断控制器(NVIC)

NVIC是可编程的,寄存器位于存储器映射的系统控制空间(SCS)。NVIC处理异常和中断配置、优先级以及中断屏蔽。

1.灵活的中断和异常管理

除了NMI,其他都可以通过软件使能、禁止、挂起、清除。

2.嵌套向量/中断支持

有抢占优先级和响应优先级。抢占优先级高的,可以打断抢占优先级低的。响应优先级高的可以优先响应,但是不能抢占。如果优先级都一样,则按照异常编号进行响应。

3.向量化的异常/中断入口

异常发生,处理器会从存储器的中断向量表中,自动定位异常处理入口。

4.中断屏蔽

提供了多个中断屏蔽寄存器,如 PRIMASK特殊寄存器。利用PRIMASK寄存器,可以禁止除HardFault 和NMI外的所有异常。这种屏蔽对不应被中断的操作非常有用,如时序关键控制任务。使用BASEPRI寄存器来选择屏蔽低于特定优先级的异常或中断

4.5.3向量表

向量表是系统存储器内的字数据数组,每个元素都是一个异常的入口函数地址。向量表可以由NVIC中的向量表偏移寄存器(VTOR)进行重定位复位后,VTOR默认为0

例如,复位异常(编号为1),那么地址为1*4(每个字为4字节)=0x00000004。地址0x00000000存放的是MSP的初始值

4.5.4错误处理

检测到未定义指令总线错误存储器访问返回错误等错误,则会触发错误异常。

定义指令、总线错误、存储器访问返回错误默认禁止,可以单独使能,进行处理,当触发错误后都会触发HardFault异常(HardFault异常总是使能)。

4.6系统控制块(SCB)

SCB为处理器的一部分,位于NVIC中。SCB包含寄存器,用于:

控制处理器配置(如低功耗模式)

提供错误状态信息(错误状态寄存器)

向量表重定位(VTOR)。
SCB经过了存储器映射与NVIC寄存器类似,SCB寄存器可以从系统控制空间(SCS)访问。

4.7调试

处理器中包含了多种经过精心设计的调试特性,其中包括暂停和步进等程序执行控制、指令断点、数据监视点、寄存器和存储器访问、概况及跟踪等。

Cortex-M处理器提供了两种接口:调试和跟踪
调试接口,调试适配器可以连接到Cortex-M微控制器上以控制调试特性和访问片上的存储器空间。有JTAG和SWD两种协议。

跟踪接口用于运行时收集处理器的信息,如数据、事件、概况信息或者程序执行的完整细节。SWV和多引脚两种协议。

实时跟踪需要嵌入式跟踪宏单元(EMT),该单元可选,如果没有该单元的MCU则不具备程序/指令跟踪。

处理器内还存在其他多个调试部件。例如,利用指令跟踪宏单元(ITM),运行在微控制器上的程序代码可以产生通过跟踪接口输出的数据,这些数据可以在调试器窗口中显示。

4.8复位和复位流程

上电复位、系统复位、处理器复位。

SCB中的寄存器可以进行系统复位或处理器复位。

复位后以及处理器开始执行程序前,会读取栈地址和复位异常向量赋值给MSP和PC。

http://www.jsqmd.com/news/1256696/

相关文章:

  • 《最优化模型与方法》全套课件PDF
  • Appium 3.x实战:Python后台切换与关闭APP新写法
  • 魔兽争霸III终极优化方案:5分钟让你的经典游戏焕发新生
  • Nintendo Switch大气层系统:3个关键步骤解锁完整游戏体验
  • 南京市防水补漏攻略|(2026 新)阳台漏水返潮原因与微创维修方案 - 资讯速览
  • LinkSwift:九大网盘直链解析的终极技术实现指南
  • 合肥市蜀山区外墙防水科普|2026 高层窗边渗水原因与规范修复方法 - 资讯速览
  • Linux入门攻坚——83、kvm虚拟化-3
  • 怎样在5分钟内掌握QKeyMapper:终极免费按键映射解决方案
  • FigmaCN终极中文翻译指南:3步让Figma界面全中文化,设计师效率提升50%
  • Figma中文翻译插件终极指南:3分钟实现Figma界面全中文化,设计师效率翻倍
  • GBLM-Pruner 论文精读:预训练完成后,梯度还能帮助我们剪枝吗?
  • 字体管理优化指南:三个关键问题与解决方案
  • 2026福州宠物眼科全行业服务生态梳理白皮书 - 招财兔数字员工
  • 终极键盘连击修复方案:KeyboardChatterBlocker完整使用指南
  • 2026草本酱酒深度测评:五大源头厂家综合排行,国台缘何领跑? - 资讯快报
  • 【关注可白嫖源码】--课程设计--毕业设计--NodeJS旅游网站[编号:project68414](案件分析)
  • HarmonyOS7 工具栏设计:MenuBar + Toolbar 搭建高效操作区
  • 同研究生谈科技文献阅读
  • 2026年余姚黄金回收商家实测|走访如意奢侈品黄金回收变现全流程深度体验含联系方式 - 微城市网络
  • TDD-LTE小区资源配置失败与通道异常故障排查案例解析
  • AI编写代码,谁来保证质量?
  • 杭州江南水乡屋面梅雨防潮防水全攻略:2026 各区县施工标准与正规品牌参考 - 资讯速览
  • 【Jetson开发】Jetson Orin NX Super基础开发组件安装
  • 学工一体化平台怎么选?不同学校规模下的选型思路梳理
  • Topit:在Mac上实现窗口强制置顶的终极免费指南
  • 宁波汽车养护服务GEO城市合伙人选型推荐哪家靠谱:代理方如何找到真正值得长期合作的技术源头? - 小随科技
  • Windows Defender完全移除指南:windows-defender-remover工具终极解决方案
  • AI 智能定价数据分析:价格弹性模型的训练与业务落地
  • 题解:洛谷 B4501 [GESP202603 四级] 山之谷