深入解析:单片机裸机和RTOS中断任务切换那些事~

目录

0、前置知识点(关键)

1.核心寄存器概览

2.详解R4-R11

(1)根本性质:通用寄存器

(2)关键特性:被调用者保存寄存器

一、单片机中断上下文切换

1.硬件自动压栈(进入中断瞬间)

2.软件手动压栈(ISR 开始)

3.中断处理逻辑

4.软件手动出栈(ISR 结束)

5.硬件自动出栈(执行 BX LR)

二、RTOS中断上下文切换

1.任务A运行中

2.中断发生

3.执行ISR

4.中断退出前触发 PendSV

5.PendSV Handler(FreeRTOS 内核关键函数)

6.任务B开始执行

三、RTOS中断错误切换

1. 寄存器污染(R4-R11 被提前改写)

2. 栈指针错配(把 MSP 当 PSP 用)

3. 错误的阻塞/让出(在 ISR 里企图阻塞)


0、前置知识点(关键)

1.核心寄存器概览

寄存器别名主要用途
R0-通用寄存器/参数/结果
R1-通用寄存器/参数
R2-通用寄存器/参数
R3-通用寄存器/参数
R4-通用寄存器(必须保护)
R5-通用寄存器(必须保护)
R6-通用寄存器(必须保护)
R7-通用寄存器(帧指针,在某些模式下)
R8-通用寄存器(必须保护)
R9-通用寄存器(必须保护)
R10-通用寄存器(必须保护)
R11-通用寄存器(必须保护)
R12-通用寄存器(临时寄存器,用于函数调用)
R13SP堆栈指针
R14LR链接寄存器(存放函数返回地址)
R15PC程序计数器(指向当前执行的指令)

2.详解R4-R11

(1)根本性质:通用寄存器

R4-R11的核心作用是临时存储数据和地址通过。它们能够被CPU的绝大多数指令(如加减乘除、逻辑运算、资料加载/存储)自由使用。程序员和编译器会用它们来存放计算中的中间变量、循环计数器、数组下标等。

(2)关键特性:被调用者保存寄存器

这是理解R4-R11行为的关键。在函数调用过程中,为了确保一个函数(调用者)不会因为调用另一个函数(被调用者)而丢失自己正在使用的资料,有一套严格的寄存器采用约定

  • R0-R3, R12:被称为调用者保存寄存器

    • 如果调用者(Caller)函数希望在这些寄存器中保留值,它必须在自己调用其他函数之前,手动将这些寄存器的值保存到堆栈上

    • 被调用者(Callee)可以随意修改这些寄存器。

  • R4-R11:被称为被调用者保存寄存器

    • 如果一个函数(被调用者)打算启用R4-R11中的任何一个寄存器,它必须在函数的开头将这些寄存器的值压入堆栈(PUSH)进行保存,并在函数结束返回前,再从堆栈中恢复(POP)这些值。

    • “受保护的”,可以放心地认为即使调用了子函数,这些寄存器里的值也不会改变。就是对于调用者来说,R4-R11的值就像

一、单片机中断上下文切换

实际执行流程(以 Cortex-M 为例):

1.硬件自动压栈(进入中断瞬间)

CPU 自动把 R0-R3、R12、LR、PC、xPSR 压到当前栈(MSP/PSP)。

2.软件手动压栈(ISR 开始)

ISR 开头编译器会生成 PUSH {R4-R11},保存高寄存器。

ISR_Handler:
PUSH {R4-R11}         ; 软件手动保存现场(ISR一开始)
; -------------------
; 中断处理逻辑
; -------------------
POP {R4-R11}          ; 软件恢复现场(ISR退出前)
BX LR                 ; 硬件自动恢复现场并返回

3.中断处理逻辑

ISR 内部自由使用寄存器(不怕覆盖主程序的值)。

4.软件手动出栈(ISR 结束)

ISR 退出前 POP {R4-R11},恢复保存的寄存器值。

5.硬件自动出栈(执行 BX LR

CPU 自动从栈里弹出 R0-R3、R12、LR、PC、xPSR,恢复到中断前的执行点。

二、RTOS中断上下文切换

1.任务A运行中

PSP 指向任务A的栈。

2.中断发生

  • 硬件自动压栈:R0-R3, R12, LR, PC, xPSR → PSP

  • 编译器压栈:R4-R11 → MSP(当前使用的栈,此时已切换到中断),压入栈的是任务A的寄存器原值,此时中断写R4-R11不会影响任务A的现场。

3.执行ISR

  • 如果调用了 FreeRTOS 的 ...FromISR() API,并且解锁了一个高优先级任务 → 标记需要任务切换。

4.中断退出前触发 PendSV

  • FreeRTOS 在 ISR 里设置 PendSV 异常挂起。

  • ISR 结束后,CPU 自动跳到 PendSV Handler,执行这个函数的时候就会把R4-R11恢复到任务A的PSP中。

5.PendSV Handler(FreeRTOS 内核关键函数)

  • 保存任务A的栈指针(PSP)到任务控制块(TCB)。

  • 从调度器里选出下一个任务(任务B)。

  • 加载任务B的栈指针(PSP)。

  • 从任务B的栈恢复寄存器(POP R4-R11 + 硬件自动恢复 R0-R3,PC 等)。

6.任务B开始执行

“从中断直接跳到任务B”,但实际上是就是好像PendSV 完成了上下文切换

三、RTOS中断错误切换

下面给你看三个最常见、也是最致命的污染路径(逐条按时间线解释)。

1. 寄存器污染(R4-R11 被提前改写)

时间线:

  1. 任务A在 PSP 上运行,寄存器(含 R4-R11)里有它自己的值。

  2. 中断发生:硬件把 R0-R3,R12,LR,PC,xPSR 压到 PSPR4-R11 还在寄存器里(还没保存)。处理器切到 Handler 模式,改用 MSP

  3. 在 ISR 里你错误地调用了任务 API(非 FromISR),这是一个“普通 C 函数调用”,编译器会很自然地用到 R4-R11(callee-saved)。

    • 注意:此时 R4-R11 依然是“任务A的寄存器值”,但你在 ISR 里把它们当成可用临时寄存器用了,也会被函数序言/内部逻辑修改。

  4. ISR 结束前,FreeRTOS 可能因为你该 API 的动作而触发了 PendSV做任务切换。

    • PendSV(naked) 入口的第一件事是:MRS r0, psp 取到任务A的 PSP,然后 STMDB r0!, {r4-r11}当前寄存器里的 R4-R11 压到 任务A 的 PSP,并写回到 TCB 的 pxTopOfStack

  5. 这些 R4-R11 已经不是“任务A被打断时的原始值”了(被你在 ISR 里误用的任务 API 改过)。

  6. 等将来切回任务A时,“被污染后的 R4-R11”就是恢复出来的——任务A用这些错误的寄存器继续跑,各种诡异挑战就来了:

    • 局部变量/静态链表指针错乱、返回地址拼不起来、最终HardFault/跑飞

精髓:PendSV 期望在保存 R4-R11 之前,没人动过它们。ISR 里调用“普通任务 API”正好打破了这个前提。


2. 栈指针错配(把 MSP 当 PSP 用)

时间线:

  1. 中断发生后,被打断任务的硬件堆栈帧在 PSP,此时 ISR 在 MSP 上运行

  2. 你在 ISR 里调用了任务 API。这类 API 在设计上默认自己运行在任务上下文(PSP),很多路径会**基于“当前 SP 就是任务栈”**去做事(保存/恢复局部上下文、计算可用栈、甚至有的端口会采样 SP 进 pxTopOfStack 等)。

  3. 但现在 SP=MSP(因为在 Handler 模式),导致这些内部操作基于错误的栈

    • 可能把 MSP 的值(中断栈指针)当成任务栈指针去保存到 TCB;

    • 可能在 MSP上做出“以为自己在 PSP 上做”的入栈/出栈;

    • 结果是 TCB 里的 pxTopOfStack 与实际 PSP 脱节,或直接把中断栈内容写进任务栈,两边都乱套。

  4. 后果:下一次上下文切换或异常返回时,PSP/TCB 指针对不上,取回来的栈顶不是它自己 →恢复失败/崩溃

精髓:任务 API 假设 SP=PSP,但在 ISR 中 SP=MSP。错用导致“把 A 的事写到了 B 的地方”。


3. 错误的阻塞/让出(在 ISR 里企图阻塞)

时间线:

  1. 某些任务 API 允许阻塞(如 xQueueSend()portMAX_DELAY),内部会走“加入事件队列、调用 portYIELD_WITHIN_API() 或触发调度”的路径。

  2. ISR 不能阻塞:它正处于 异常嵌套的“半保存状态”(被中断的任务的硬件堆栈帧还在 PSP 上、尚未出栈)。

  3. 要是你在 ISR 里走到“阻塞/切换”的路径:

    • 内核会试图 切换到别的任务,但当前上下文不是任务,而是异常上下文;

    • 异常返回序列(依据 LR=EXC_RETURN 自动出栈 PSP 的硬件帧)被你“半路换车”:TCB/PSP 可能已被改到另一个任务上;

    • 于是 异常返回恢复不了(EXC_RETURN 指向旧帧,PSP 指到新任务),直接HardFault / UsageFault

精髓:异常返回是一套严格的硬件流程;ISR 里发起“像任务那样的阻塞/切换”会把这套流程半途打断,硬件和内核对“不该在此时发生的切换”没有共同语义,必炸。

posted @ 2025-09-16 18:17  yjbjingcha  阅读(18)  评论(0)    收藏  举报