4. 开发工具链与工程模板
寄存器,说白了就是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,结果主程序跑飞了。
4.2 堆栈指针:MSP与PSP
Cortex-M有两个堆栈指针,这是个亮点。MSP(主堆栈指针)和PSP(进程堆栈指针),但同一时刻只有一个生效。
- MSP:复位后默认使用。Handler Mode下强制使用。
- PSP:Thread Mode下可选使用,常用于RTOS任务栈。
我记得第一次用FreeRTOS时,看到任务切换时SP在MSP和PSP之间跳来跳去,觉得特别神奇。其实原理很简单:中断服务用MSP,任务代码用PSP,互不干扰。
4.3 LR与PC:程序执行的核心
LR(R14)存的是函数返回地址。调用子程序时,硬件自动把下一条指令地址存入LR。执行BX LR就返回了。
PC(R15)指向当前正在执行的指令。注意,Cortex-M的PC最低位必须是1,表示Thumb状态。如果你不小心把PC的bit0清0了,硬件会触发异常。
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输出不受影响。
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的用户任务跑在非特权级,即使任务崩溃,也破坏不了内核数据结构。
4.7 知识结构图
下面这张图把寄存器组和操作模式的关系梳理清楚了。
4.8 实战要点总结
学完这一章,你至少得记住这几条:
- R0-R3 随便用,但函数返回时不保证值不变。
- R4-R11 用之前必须保存,返回前恢复。
- MSP和PSP 是独立的,中断用MSP,任务用PSP。
- PRIMASK 关总中断,BASEPRI 选择性关中断。
- Handler Mode 始终特权级,Thread Mode 可以是非特权级。