1. ARMv7 架构与 Cortex-M 系列处理器概述

做 RTOS 移植和任务调度,第一关就是吃透处理器架构。ARMv7 是个大家族,咱们课程主要关注 Cortex-M 系列——这是跑 RTOS 的主力。我个人习惯先把寄存器模型、特权模式和异常处理这三块啃透,后面写调度器心里才有底。

1.1 寄存器模型:你手头有多少“工具”

Cortex-M3/M4 有 16 个 32 位通用寄存器(R0-R15),外加几个特殊寄存器。我刚开始学的时候总觉得寄存器太多记不住,后来发现只要分三组理解就简单了:

  • R0-R12:通用寄存器,随便用。但注意 R4-R11 在函数调用里必须保存,这是 AAPCS 规定的。
  • R13 (SP):栈指针。Cortex-M 有两个栈——主栈(MSP)和进程栈(PSP)。RTOS 里任务跑在 PSP,异常处理用 MSP,这个切换是调度的关键。
  • R14 (LR):链接寄存器。异常返回时 LR 会被赋一个特殊值(EXC_RETURN),指示返回模式和栈选择。
  • R15 (PC):程序计数器。指向当前指令地址。

核心寄存器速查表

寄存器别名RTOS 中的关键作用
R0-R3参数/返回值函数调用传参,任务入口参数
R4-R11高组寄存器上下文切换时必须保存
R13SPMSP/PSP 切换,任务栈管理
R14LR异常返回模式控制
R15PC任务入口地址,恢复执行点

特殊寄存器里,xPSR(程序状态寄存器)存了条件标志、中断状态和处理器模式。我踩过一个坑:在 PendSV 里修改了 xPSR 的 T 位(Thumb 状态位),结果任务一恢复就 HardFault。嗯,ARMv7-M 强制 Thumb-2 指令集,T 位必须为 1,千万别手贱去改它。

1.2 特权模式与处理器模式

Cortex-M 只有两种模式:Handler 模式和Thread 模式。Handler 模式永远有特权,Thread 模式可以选特权或非特权。说白了,异常/中断处理跑在 Handler 模式,普通任务代码跑在 Thread 模式。

为什么 RTOS 要用非特权 Thread 模式?我做过一个产品,客户要求任务不能互相搞破坏。把任务放在非特权级,访问系统控制寄存器、操作 MPU 这些敏感操作必须通过 SVC 异常来“系统调用”,安全多了。

我的习惯:任务栈初始化时,把 CONTROL 寄存器的 nPRIV 位置 1,让任务跑在非特权级。内核代码(调度器、IPC)跑在特权级。这样任务再野也翻不了天。

1.3 异常处理与中断向量表

Cortex-M 的异常系统是硬件自动压栈的,这点比 ARM7TDMI 时代舒服太多。异常发生时,硬件自动把 R0-R3、R12、LR、PC、xPSR 压入当前栈,然后从向量表取出入口地址跳转。

向量表放在起始地址(默认 0x00000000),第一个字是 MSP 初始值,第二个字是 Reset_Handler 地址。后面依次是各种异常和中断。RTOS 里最常用的几个:

  • SVCall (11 号):系统服务调用,任务主动请求内核服务
  • PendSV (14 号):可挂起系统调用,上下文切换的主力
  • SysTick (15 号):系统滴答定时器,时间片轮转的节拍源

我曾经踩过的坑:在 STM32F4 上做移植时,忘了把向量表重定位到 SRAM。程序在 Flash 里跑得好好的,一开中断就 HardFault。查了两天才发现——中断向量表还在 0x00000000,而我把代码搬到了 0x08000000。用 SCB->VTOR 重定位一下就好了。

1.4 异常优先级与抢占行为

Cortex-M3/M4 支持最多 256 级中断优先级(实际芯片通常只实现 16 或 32 级)。优先级数值越小,优先级越高。RTOS 里通常把 PendSV 设为最低优先级,SysTick 设为中等优先级。为什么?

你想想看:SysTick 触发时如果正在处理一个高优先级中断,PendSV 因为优先级最低会被阻塞。等所有中断处理完了,PendSV 才执行上下文切换。这样中断响应延迟最小,不会因为调度而耽误紧急事件。

典型 RTOS 优先级分配

异常/中断优先级说明
PendSV最低(如 0xFF)所有中断处理完才切换
SysTick较低(如 0xF0)可被高优先级中断抢占
外设中断按需设置实时性要求高的设高优先级

1.5 知识体系总览

下面这张图把本章的核心知识点串起来了。我建议你把它存下来,后面写调度器代码时对照着看。

ARMv7-M 处理器核心知识体系 ARMv7-M 处理器核心知识体系 寄存器模型 特权模式 异常处理 R0-R15 + xPSR + CONTROL MSP / PSP 双栈机制 Handler 模式(特权) Thread 模式(特权/非特权) 硬件自动压栈 向量表 + VTOR 重定位 RTOS 三大关键异常 SVCall (11号) 系统服务调用 任务主动请求内核 PendSV (14号) 可挂起系统调用 上下文切换主力 SysTick (15号) 系统滴答定时器 时间片轮转节拍 优先级设计原则 PendSV 最低优先级 → 所有中断处理完才切换 SysTick 中等优先级 → 可被高优先级中断抢占

这张图把寄存器、模式、异常三大块串起来了。你注意看底部那个优先级设计原则——这是 RTOS 调度器能正确工作的基石。PendSV 优先级最低,意味着它永远在“空闲”时才执行切换;SysTick 中等优先级,保证时间片轮转不会被完全阻塞。

避坑指南:我曾经在一个项目里把 PendSV 优先级设得比某个外设中断还高,结果那个中断处理函数里调用了 RTOS 的延时函数,直接死锁。记住:PendSV 必须是最低优先级,任何中断里调用 RTOS API 都要小心。

好了,这一章的内容就是这些。寄存器模型是你写上下文切换的“工具箱”,特权模式决定了任务的安全边界,异常处理是调度器的“发动机”。这三样吃透了,后面写调度器代码就是水到渠成的事。