4. Cortex-M内核编程模型:寄存器组与操作模式

寄存器,说白了就是CPU内部的高速存储单元。Cortex-M内核的寄存器设计非常精妙,我当年刚接触时也被绕晕过。今天咱们把这套东西彻底理清楚。

4.1 通用寄存器组:R0-R15

Cortex-M有16个32位通用寄存器,编号R0到R15。它们各有分工,不是所有都能随便用。

寄存器 别名 用途说明
R0-R3 参数寄存器 函数调用时传参,调用者保存
R4-R11 通用变量 被调用者保存,中断服务里要小心
R12 IP 内部过程调用暂存
R13 SP 堆栈指针,分MSP和PSP
R14 LR 链接寄存器,存返回地址
R15 PC 程序计数器,指向当前指令

R0-R3在函数调用时用来传参。我习惯用R0传第一个参数,R1传第二个,以此类推。如果参数超过4个,就得压栈了。

R4-R11是“被调用者保存”的。什么意思?你写一个函数,如果要用R4,必须先把它原来的值压栈保存,返回前再恢复。这个坑我踩过——早期写中断服务程序时忘了保存R4,结果主程序跑飞了。

小技巧: 写中断服务函数时,尽量只用R0-R3。这样编译器会自动处理保存恢复,省心。

4.2 堆栈指针:MSP与PSP

Cortex-M有两个堆栈指针,这是个亮点。MSP(主堆栈指针)和PSP(进程堆栈指针),但同一时刻只有一个生效。

  • MSP:复位后默认使用。Handler Mode下强制使用。
  • PSP:Thread Mode下可选使用,常用于RTOS任务栈。

我记得第一次用FreeRTOS时,看到任务切换时SP在MSP和PSP之间跳来跳去,觉得特别神奇。其实原理很简单:中断服务用MSP,任务代码用PSP,互不干扰。

关键点: 当前使用的是哪个SP,由CONTROL寄存器的bit[1]决定。0选MSP,1选PSP。

4.3 LR与PC:程序执行的核心

LR(R14)存的是函数返回地址。调用子程序时,硬件自动把下一条指令地址存入LR。执行BX LR就返回了。

PC(R15)指向当前正在执行的指令。注意,Cortex-M的PC最低位必须是1,表示Thumb状态。如果你不小心把PC的bit0清0了,硬件会触发异常。

避坑指南: 我曾经在Bootloader里直接给PC赋值,忘了置位bit0,结果系统直接HardFault。后来养成了习惯:PC赋值时一定用 OR 1 操作。

4.4 特殊寄存器:xPSR、PRIMASK、FAULTMASK、BASEPRI

特殊寄存器不直接参与数据运算,但控制着CPU的行为模式。

xPSR:程序状态寄存器

xPSR其实是三个寄存器的组合:APSR(算术标志)、IPSR(异常编号)、EPSR(执行状态)。

  • APSR:N、Z、C、V标志位,条件跳转全靠它。
  • IPSR:当前正在处理的中断号,调试时很有用。
  • EPSR:T位必须为1,表示Thumb状态。

读xPSR用MRS指令,写用MSR。我调试时经常用MRS R0, xPSR来查看当前状态。

PRIMASK、FAULTMASK、BASEPRI

这三个是中断屏蔽寄存器,用来控制中断响应。

寄存器 作用 典型用法
PRIMASK 屏蔽所有可屏蔽中断 临界区保护
FAULTMASK 屏蔽所有异常,包括HardFault 系统级紧急处理
BASEPRI 屏蔽优先级低于某值的中断 选择性关中断

PRIMASK置1相当于关总中断。BASEPRI更灵活——你可以只屏蔽优先级低于某个阈值的中断,高优先级的还能响应。我在做电机控制时就用BASEPRI,只屏蔽低优先级中断,保证PWM输出不受影响。

经验之谈: 关中断时间不要超过10微秒,否则实时性会出问题。用BASEPRI比PRIMASK更优雅。

4.5 操作模式:Thread Mode vs Handler Mode

Cortex-M有两种操作模式,理解这个对写RTOS至关重要。

  • Thread Mode:普通程序运行模式。可以特权级或非特权级。
  • Handler Mode:异常/中断处理模式。始终是特权级。

从Thread Mode进入Handler Mode的唯一途径是发生异常或中断。退出Handler Mode用异常返回指令(BX LR时LR的值特殊处理)。

我刚开始做RTOS移植时,一直搞不懂为什么任务切换时要修改LR的bit[2]。后来才明白:bit[2]决定返回后使用MSP还是PSP。bit[2]=0用MSP,bit[2]=1用PSP。

4.6 特权级与非特权级

特权级和非特权级是Cortex-M的访问控制机制。

特性 特权级 非特权级
访问系统寄存器 允许 禁止
修改CONTROL 允许 禁止
执行MRS/MSR 全部 受限
典型场景 内核、驱动 用户任务

非特权级下,你不能修改CONTROL寄存器,也不能访问系统控制块(SCB)。这有什么用?做安全隔离。比如RTOS的用户任务跑在非特权级,即使任务崩溃,也破坏不了内核数据结构。

注意: 复位后默认是特权级Thread Mode。要切换到非特权级,需要先设置CONTROL寄存器的bit[0]=1,然后执行异常返回。

4.7 知识结构图

下面这张图把寄存器组和操作模式的关系梳理清楚了。

Cortex-M编程模型结构图 Cortex-M内核编程模型 通用寄存器组 (R0-R15) R0-R3 (参数) R4-R11 (变量) R12 (IP) R13 (SP) R14 (LR) R15 (PC) 特殊寄存器 xPSR PRIMASK FAULTMASK BASEPRI CONTROL (模式控制) 操作模式 Thread Mode 特权级 / 非特权级 Handler Mode 始终特权级 异常/中断 异常返回 MSP / PSP 由CONTROL选择 关键:R13(SP)在Thread Mode下可选MSP或PSP,Handler Mode下强制MSP 特权级可访问所有寄存器,非特权级受限

4.8 实战要点总结

学完这一章,你至少得记住这几条:

  1. R0-R3 随便用,但函数返回时不保证值不变。
  2. R4-R11 用之前必须保存,返回前恢复。
  3. MSP和PSP 是独立的,中断用MSP,任务用PSP。
  4. PRIMASK 关总中断,BASEPRI 选择性关中断。
  5. Handler Mode 始终特权级,Thread Mode 可以是非特权级。