操作系统开发:(5) 异常模型、伪造栈帧、SVC 与 PendSV异常处理(实现 port.c)
本文详细介绍了ARMv7-M架构的异常处理机制及其在RTOS中的实现。主要内容包括:1) ARMv7-M异常分类与特性,涵盖4大类异常及其优先级机制;2) 任务栈初始化原理,通过伪造中断栈帧实现任务首次调度;3) SVC异常处理流程,完成系统从启动模式到任务模式的切换;4) PendSV异常处理机制,实现高效的任务上下文切换;5) SysTick定时器中断配置,为系统提供时间基准。文章深入分析了硬
1. ARMv7-M 异常模型
在 操作系统开发:(4) 系统级程序员模型、模式、特权、栈、异常与函数序言(实现 portable.h )中,我们讲了 ARMv7-M 对异常的定义、支持的4大类异常、异常的3种状态、异常的优先级和异常返回,这里详细介绍 ARMv7-M 的各个异常。

Armv7-M配置文件与其他Armv7配置文件的不同之处在于,在异常进入和退出时使用硬件保存和恢复关键上下文状态,并使用向量表来指示异常入口点。此外,Armv7-M配置文件中的异常分类与其他Armv7配置文件不同。


|
异常类型 |
触发条件 |
优先级 |
特性 |
主要用途 |
|---|---|---|---|---|
|
Reset |
电源开启或复位信号 |
固定 -3 |
有两种复位级别: |
系统启动和恢复 |
|
NMI |
硬件生成或软件设置 |
固定 -2 |
不可屏蔽中断 |
紧急情况处理 |
|
HardFault |
无法被其他异常处理的通用故障 |
固定 -1 |
用于不可恢复的系统故障; |
故障处理 |
|
MemManage |
MPU 检测到内存保护故障 |
可配置 (0=最高) |
处理指令/数据内存保护故障; |
内存保护 |
|
BusFault |
内存相关的总线错误 |
可配置 (0=最高) |
检测总线错误; |
总线错误处理 |
|
UsageFault |
非内存相关的指令执行错误 |
可配置 (0=最高) |
触发条件: |
指令错误处理 |
|
DebugMonitor |
调试事件发生 |
可配置 (0=最高) |
调试事件处理; |
调试支持 |
|
SVCall |
SVC #n 指令执行 |
固定 -1 |
由 SVC 指令显式触发; |
系统调用 |
|
PendSV |
软件设置 |
可配置 (0=最高) |
用于软件生成的系统调用; |
任务切换 |
|
SysTick |
SysTick 定时器 |
可配置 (0=最高) |
系统滴答定时器; |
系统定时 |
|
Interrupts |
外部事件 |
可配置 (0=最高) |
最多 496 个外部中断; |
外部事件处理 |
如表所示:

|
特性 |
SVCall |
PendSV |
|---|---|---|
|
触发方式 |
执行 SVC #n 指令(同步) |
设置 ICSR.PENDSVSET 位(异步) |
|
触发时机 |
立即执行(下一条指令就是异常入口) |
延迟执行(等所有高优先级中断处理完才执行) |
|
优先级 |
固定 -1(高优先级) |
通常配置为 最低优先级(如 255) |
|
核心用途 |
用户程序主动请求系统服务 |
操作系统被动调度(如任务切换) |
2. 异常处理与伪造栈帧 port_pu32InitStack
2.1 异常处理(伪造栈帧的原理)
Cortex-M 处理器在进入异常时,会自动将 8 个寄存器的值压入当前栈(称为“自动保存”):
高地址
| xPSR | ← 栈顶初始位置
| PC |
| LR |
| R12 |
| R3 |
| R2 |
| R1 |
| R0 |
低地址
当执行 BX LR 时,硬件检测到 LR → 触发返回流程。
- 如果 LR 是一个普通地址(如 0x20001235),则正常跳转。
- 但如果 LR 是 EXC_RETURN 值(如 0xFFFFFFF9),处理器就知道:“这不是普通返回,这是异常返回。”
于是,处理器会自动从栈中弹出之前压入的那 8 个寄存器(R0-R3, R12, LR, PC, xPSR),把栈里保存的寄存器值,原封不动地放回对应的寄存器,从而恢复异常发生前的上下文,并跳转回原来的执行位置。
Cortex-M 的硬件强制约束:只有异常返回能切换栈指针和模式。
// 伪代码:BX LR 指令执行时,硬件内部逻辑
if ((LR & 0xF0000000) == 0xF0000000) { // 高 4 位 = 0xF(即高 28 位全 1)
// 这是 EXC_RETURN!执行异常返回流程:
1. 根据 LR 低 4 位决定:用 MSP 还是 PSP?返回 Thread 还是 Handler?
2. 从栈中自动弹出 8 个寄存器(R0-R3, R12, LR, PC, xPSR)
3. 恢复模式(Thread/Handler)和特权级别
} else {
// 普通跳转:仅设置 PC = LR
PC = LR;
}
本代码中:
// 57. 初始化任务栈函数定义,头文件 64
// 在创建任务时,手动模拟一个中断返回时的栈帧结构,以便任务第一次被调度运行时,能像从中断返回一样正确地跳转到任务入口函数
StackType_t* port_pu32InitStack(StackType_t* pu32TopOfStack, TaskFunc_t pfCode, void* pvParameters ){
pu32TopOfStack--;
*pu32TopOfStack = portINITIAL_XPSR; /* xPSR */
pu32TopOfStack--;
*pu32TopOfStack = ( ( StackType_t ) pfCode ) & portSTART_ADDRESS_MASK; /* PC */
pu32TopOfStack--;
*pu32TopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */
pu32TopOfStack -= 5; /* R12, R3, R2 and R1. */
*pu32TopOfStack = ( StackType_t ) pvParameters; /* R0 */
pu32TopOfStack--;
*pu32TopOfStack = portINITIAL_EXC_RETURN;
pu32TopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pu32TopOfStack;
}
实现了:
上电
↓
CPU 读 0x0 / 0x4 → 设置 SP 和 PC
↓
执行 Reset_Handler → 调用 main()
↓
main() 中创建任务 → 分配栈 + 调用 port_pu32InitStack 伪造栈帧
↓
启动调度器
↓
触发 PendSV 中断
↓
PendSV 设置 PSP = 之前伪造栈地址,然后执行 BX LR(LR 被设置为了特殊值,CPU识别其为异常返回指令)
↓
CPU 切换栈指针为 PSP,自动弹出寄存器之前设置的8个寄存器 → 设置 PC 为伪造的 PC 即任务函数地址
↓
任务开始执行!
|
阶段 |
执行者 |
堆栈状态 |
代码作用 |
|---|---|---|---|
|
1. 任务创建时 |
调度器/主线程 |
使用的是 MSP(主栈)或当前任务的 PSP |
port_pu32InitStack 只是向新任务的私有堆栈内存块中写入一些初始值。 |
|
2. 任务首次运行时 |
Cortex-M 硬件在 PendSV 异常返回时 |
CPU 将 新任务的堆栈指针(PSP)指向这块内存 |
CPU 自动从这块内存弹出 8 个字,并将其解释为: |
2.2 任务返回地址
本代码中:
*pu32TopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */
// 42. 任务返回地址定义(默认)
#define portTASK_RETURN_ADDRESS port_vTaskExitError
这里把返回地址设置为 port_vTaskExitError 函数,是因为任务函数绝对不能 return。
因为任务函数的调用方式不是普通函数调用!
- 任务是通过 伪造中断栈帧 + 异常返回 启动的
- CPU 是跳转到任务函数的,没有正常的调用栈
- 任务函数的调用者根本不存在
所以:
- CPU 会尝试从 LR(Link Register) 跳回调用者
- 但 LR 被初始化为 0xFFFFFFFF(一个非法地址)
- 尝试跳转到 0xFFFFFFFF → 触发 HardFault(硬件异常)
- 系统崩溃
故将 LR(链接寄存器)设置为 port_vTaskExitError 函数的地址(错误处理函数)进行死循环。
3. SVC 异常处理 port_vSVCHandler
port_vSVCHandler 负责将系统从 单线程启动模式 切换到 多任务运行模式。将 CPU 从 Handler mode(特权)无缝切换到第一个任务的 Thread mode(非特权 + PSP)
// 59. SVC异常处理函数定义,把系统从“启动模式”切换到“任务运行模式”,让第一个任务开始干活
// msr psp, r0 把栈指针切换到任务专用的栈
void port_vSVCHandler( void ){
__asm volatile (
" ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */
" ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
" ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" ldmia r0!, {r4-r11, r14} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */
" msr psp, r0 \n" /* Restore the task stack pointer. */
" isb \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" bx r14 \n"
" \n"
" .align 4 \n"
"pxCurrentTCBConst2: .word pxCurrentTCB \n"
);
}
3.1 SVC 异常触发时的状态
当执行 svc #0 指令后,硬件自动完成以下操作:
|
硬件自动行为 |
说明 |
寄存器/栈状态 |
|---|---|---|
|
1. 保存 8 个寄存器 |
压入当前栈(MSP) |
栈中:[xPSR, PC, LR, R12, R3, R2, R1, R0] |
|
2. 切换到 Handler mode |
进入异常处理模式 |
CONTROL[0]=0(特权) |
|
3. 设置 LR = 0xFFFFFFF9 |
标记"返回时用 MSP" |
LR = 0xFFFFFFF9 |
|
4. 跳转到 SVC 向量 |
执行 port_vSVCHandler |
PC = port_vSVCHandler |
此时 CPU 仍在 MSP 栈上运行,但我们需要切换到 任务专用的 PSP 栈。
3.2 执行的操作
|
步骤 |
汇编指令 |
操作说明 |
寄存器/内存变化 |
硬件状态 |
设计目的 |
|---|---|---|---|---|---|
|
0 |
SVC 异常触发前 |
执行 |
|
Thread mode |
触发异常的起点 |
|
1 |
硬件自动压栈 |
硬件自动保存 8 个寄存器 |
栈内容 (MSP 栈): |
切换到 Handler mode |
为异常处理保存现场 |
|
2 |
|
加载 |
|
无变化 |
获取任务控制块指针地址 |
|
3 |
|
读取 TCB 指针值 |
|
无变化 |
获取当前任务的 TCB 地址 |
|
4 |
|
读取任务栈顶指针 |
|
无变化 |
关键:TCB 首成员 = 栈顶指针 |
|
5 |
|
从任务栈恢复 9 个值 |
加载顺序: |
无变化 |
核心魔法: |
|
6 |
|
设置 PSP 为任务栈指针 |
|
无变化 |
栈指针切换准备: |
|
7 |
|
指令同步屏障 |
无寄存器变化 |
确保 |
防止流水线导致后续指令使用旧 PSP 值 |
|
8 |
|
清除 BASEPRI 屏蔽 |
|
允许所有优先级中断响应 |
解除中断屏蔽: |
|
9 |
|
跳转到 r14 指向地址 |
|
硬件自动行为: |
模式/栈切换完成: |
|
10 |
任务开始运行 |
硬件跳转到任务入口 |
|
Thread mode |
第一个任务正式启动 |
- 硬件压栈:svc #0 触发后,硬件自动保存 8 个寄存器到 MSP 栈
- 软件恢复:从任务栈恢复 R4-R11 + EXC_RETURN (0xFFFFFFFD) 到 r14
- 硬件弹栈:bx r14 触发异常返回,硬件自动从 PSP 弹出 8 个寄存器并切换模式/栈
4. 启动第一个任务 port_vStartFirstTask
// 60. 启动第一个任务函数定义,完成最后的系统初始化,然后按下 SVC 0 按钮,触发之前的 port_vSVCHandler,让第一个任务正式开始运行
static void port_vStartFirstTask( void ){
__asm volatile (
" ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */
" ldr r0, [r0] \n"
" ldr r0, [r0] \n"
" msr msp, r0 \n" /* Set the msp back to the start of the stack. */
" mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */
" msr control, r0 \n"
" cpsie i \n" /* Globally enable interrupts. */
" cpsie f \n"
" dsb \n"
" isb \n"
" svc 0 \n" /* System call to start first task. */
" nop \n"
" .ltorg \n"
);
}
|
步骤 |
汇编指令 |
操作说明 |
寄存器/内存变化 |
硬件状态 |
设计目的 |
|---|---|---|---|---|---|
|
0 |
函数调用前 |
|
|
Thread mode |
进入启动第一个任务的准备阶段 |
|
1 |
|
加载 VTOR 寄存器地址 |
|
无变化 |
获取向量表基地址寄存器地址 |
|
2 |
|
读取 VTOR 寄存器值 |
|
无变化 |
获取中断向量表在内存中的实际位置 |
|
3 |
|
读取向量表第 0 项(MSP 初始值) |
|
无变化 |
关键:向量表第 0 项 = 复位后的 MSP 初始值 |
|
4 |
|
设置 MSP 为初始栈顶 |
|
无变化 |
恢复主栈: |
|
5 |
|
清除 CONTROL 寄存器 |
|
无变化 |
强制特权 + MSP: |
|
6 |
|
使能全局中断 |
|
允许所有可屏蔽中断 |
开启中断系统: |
|
7 |
|
使能浮点异常(若支持) |
|
允许浮点异常 |
确保 FPU 异常可被处理 |
|
8 |
|
数据同步屏障 |
无寄存器变化 |
确保之前所有内存操作完成 |
防止指令重排序导致状态不一致 |
|
9 |
|
指令同步屏障 |
无寄存器变化 |
刷新流水线 |
确保 |
|
10 |
|
触发 SVC 异常 |
|
硬件自动行为: |
模式切换: |
|
11 |
|
空操作(永不执行) |
无变化 |
无变化 |
占位符: |
|
12 |
SVC 处理开始 |
硬件跳转到 |
|
Handler mode |
调度器启动的临界点: |
- 恢复主栈:从向量表读取 MSP 初始值,确保中断有合法栈空间
- 强制特权:清零 CONTROL 寄存器,保证后续操作在特权模式下执行
- 触发异常:
svc 0是唯一能安全启动任务的途径,利用硬件异常机制完成上下文切换
5. 启动调度器 port_lStartScheduler
// 61. 启动调度器函数定义
BaseType_t port_lStartScheduler( void ){
// 1. CPU 版本检查
cfgASSERT( portCPUID != portCORTEX_M7_r0p1_ID );
cfgASSERT( portCPUID != portCORTEX_M7_r0p0_ID );
// 2. 中断向量表验证(防配置错误)
#if ( cfgCHECK_HANDLER_INSTALLATION == 1 )
{
const portISR_t * const ppfVectorTable = portSCB_VTOR_REG;
cfgASSERT( ppfVectorTable[ portVECTOR_INDEX_SVC ] == port_vSVCHandler );
cfgASSERT( ppfVectorTable[ portVECTOR_INDEX_PENDSV ] == port_vPendSVHandler );
}
#endif
#if ( cfgASSERT_DEFINED == 1 )
{
volatile uint8_t u8OriginalPriority;
volatile uint32_t u32ImplementedPrioBits = 0;
volatile uint8_t * const pu8FirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
volatile uint8_t u8MaxPriorityValue;
u8OriginalPriority = *pu8FirstUserPriorityRegister;
*pu8FirstUserPriorityRegister = portMAX_8_BIT_VALUE;
u8MaxPriorityValue = *pu8FirstUserPriorityRegister;
u8MaxSysCallPriority = cfgMAX_SYSCALL_INTERRUPT_PRIORITY & u8MaxPriorityValue;
cfgASSERT( u8MaxSysCallPriority );
cfgASSERT( ( cfgMAX_SYSCALL_INTERRUPT_PRIORITY & ( ~u8MaxPriorityValue ) ) == 0U );
while( ( u8MaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ){
u32ImplementedPrioBits++;
u8MaxPriorityValue <<= ( uint8_t ) 0x01;
}
if( u32ImplementedPrioBits == 8 ){
cfgASSERT( ( cfgMAX_SYSCALL_INTERRUPT_PRIORITY & 0x1U ) == 0U );
u32MaxPRIGROUPValue = 0;
}else{
u32MaxPRIGROUPValue = portMAX_PRIGROUP_BITS - u32ImplementedPrioBits;
}
u32MaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
u32MaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;
*pu8FirstUserPriorityRegister = u8OriginalPriority;
}
#endif
// 3. 优先级配置
portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
portNVIC_SHPR2_REG = 0;
port_vSetupTimerInterrupt();
ulCriticalNesting = 0;
port_vEnableVFP();
*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;
port_vStartFirstTask();
task_vSwitchContext();
port_vTaskExitError();
return 0;
}
|
阶段 |
关键操作 |
硬件行为 |
软件责任 |
|---|---|---|---|
|
1. 安全校验 |
CPU 版本/向量表检查 |
无 |
防止硬件缺陷和配置错误 |
|
2. 优先级配置 |
设置 SVC/PendSV/SysTick 优先级 |
无 |
构建实时性保障的优先级金字塔 |
|
3. 时钟初始化 |
配置 SysTick 定时器 |
无 |
提供任务调度的时间基准 |
|
4. FPU 初始化 |
启用 FPU + 懒惰保存 |
无 |
优化浮点任务切换性能 |
|
5. 任务启动 |
|
硬件自动: |
通过异常机制完成首次任务切换 |
6. PendSV 异常处理 port_vPendSVHandler
void port_vPendSVHandler( void )
{
__asm volatile
(
" mrs r0, psp \n"
" isb \n"
" \n"
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
" ldr r2, [r3] \n"
" \n"
" tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */
" it eq \n"
" vstmdbeq r0!, {s16-s31} \n"
" \n"
" stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */
" str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */
" \n"
" stmdb sp!, {r0, r3} \n"
" mov r0, %0 \n"
" msr basepri, r0 \n"
" dsb \n"
" isb \n"
" bl task_vSwitchContext \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" ldmia sp!, {r0, r3} \n"
" \n"
" ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" ldr r0, [r1] \n"
" \n"
" ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */
" \n"
" tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */
" it eq \n"
" vldmiaeq r0!, {s16-s31} \n"
" \n"
" msr psp, r0 \n"
" isb \n"
" \n"
#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
#if WORKAROUND_PMU_CM001 == 1
" push { r14 } \n"
" pop { pc } \n"
#endif
#endif
" \n"
" bx r14 \n"
" \n"
" .align 4 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
::"i" ( cfgMAX_SYSCALL_INTERRUPT_PRIORITY )
);
}
|
步骤 |
汇编指令 |
操作说明 |
寄存器/内存变化 |
硬件状态 |
设计目的 |
|---|---|---|---|---|---|
|
0 |
PendSV 触发前 |
高优先级中断执行完毕 |
|
Handler mode |
PendSV 挂起位被置位,等待执行 |
|
1 |
|
读取当前任务栈指针 |
|
无变化 |
获取任务栈顶: |
|
2 |
|
指令同步屏障 |
无寄存器变化 |
确保 |
防止流水线导致读取到旧的 PSP 值 |
|
3 |
|
加载 TCB 指针地址 |
|
无变化 |
获取当前任务控制块地址 |
|
4 |
|
读取当前 TCB 指针 |
|
无变化 |
指向当前运行任务的 TCB |
|
5 |
|
条件保存 FPU 寄存器 |
检查 |
无变化 |
懒惰保存优化: |
|
6 |
|
保存核心寄存器 |
栈操作: |
无变化 |
保存硬件未自动保存的寄存器: |
|
7 |
|
保存更新后的栈顶到 TCB |
|
无变化 |
关键:TCB 首成员 = 栈顶指针, |
|
8 |
|
保存临时寄存器到 MSP 栈 |
|
无变化 |
保护寄存器: |
|
9 |
|
屏蔽低优先级中断 |
|
屏蔽优先级 ≤48 的中断 |
临界区保护: |
|
10 |
|
数据/指令同步屏障 |
无寄存器变化 |
确保 BASEPRI 生效 |
防止指令重排序导致保护失效 |
|
11 |
|
调用 C 函数切换任务 |
1. 保存当前任务状态到 TCB |
无变化 |
决策层: |
|
12 |
|
恢复中断 |
|
允许所有中断响应 |
退出临界区 |
|
13 |
|
恢复临时寄存器 |
|
无变化 |
恢复被 C 函数破坏的寄存器 |
|
14 |
|
读取新任务 TCB 指针 |
|
无变化 |
指向新任务的 TCB |
|
15 |
|
读取新任务栈顶 |
|
无变化 |
获取新任务的上下文位置 |
|
16 |
|
恢复核心寄存器 |
栈操作: |
无变化 |
恢复 R4-R11 + EXC_RETURN |
|
17 |
|
条件恢复 FPU 寄存器 |
检查 |
无变化 |
懒惰恢复:仅当需要时恢复 FPU 状态 |
|
18 |
|
设置 PSP 为新任务栈 |
|
无变化 |
栈指针切换: |
|
19 |
|
指令同步屏障 |
无寄存器变化 |
确保 PSP 更新生效 |
防止后续 |
|
20 |
|
触发异常返回 |
|
硬件自动行为: |
任务切换完成: |
|
21 |
新任务开始运行 |
硬件跳转到 task2 入口 |
|
Thread mode |
task2 正式运行 |
|
阶段 |
关键操作 |
硬件行为 |
软件责任 |
|---|---|---|---|
|
1. 保存旧任务 |
从 PSP 读取栈指针 → 保存 R4-R11/FPU → 更新 TCB |
无 |
精确捕获任务状态 |
|
2. 任务决策 |
调用 |
无 |
选择下一个运行任务(仅切换 TCB 指针) |
|
3. 恢复新任务 |
从 TCB 读取栈指针 → 恢复 R4-R11/FPU → 更新 PSP |
无 |
准备新任务运行环境 |
|
4. 异常返回 |
|
硬件自动: |
触发硬件状态机切换 |
7. Systick 异常处理 port_vSysTickHandler
// 66. SysTick中断处理函数定义
void port_vSysTickHandler( void ){
portDISABLE_INTERRUPTS();
traceISR_ENTER();
{
if( task_ulIncrementTick() != pdFALSE ){
traceISR_EXIT_TO_SCHEDULER();
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}else{
traceISR_EXIT();
}
}
portENABLE_INTERRUPTS();
}
|
步骤 |
代码/操作 |
操作说明 |
寄存器/内存变化 |
硬件状态 |
设计目的 |
|---|---|---|---|---|---|
|
0 |
SysTick 触发前 |
定时器计数到 0 |
|
Thread mode |
周期性时间事件发生(默认 1ms) |
|
1 |
硬件自动压栈 |
硬件自动保存 8 个寄存器 |
栈内容 (PSP 栈): |
切换到 Handler mode |
为中断处理保存现场 |
|
2 |
|
禁用所有可屏蔽中断 |
|
屏蔽所有优先级 ≤255 的中断 |
临界区保护: |
|
3 |
|
调试跟踪(可选) |
无变化(若未启用跟踪) |
无变化 |
记录中断进入时间(用于性能分析) |
|
4 |
|
核心:更新系统时钟 |
1. |
无变化 |
调度决策点: |
|
5 |
条件分支判断 |
根据返回值决定是否切换 |
|
无变化 |
最小化中断延迟: |
|
6 |
|
调试跟踪(需切换) |
无变化 |
无变化 |
记录"将触发调度"事件 |
|
7 |
|
挂起 PendSV 异常 |
|
硬件标记 PendSV 为 Pending 状态 |
关键设计: |
|
8 |
|
调试跟踪(无需切换) |
无变化 |
无变化 |
记录中断正常退出 |
|
9 |
|
恢复中断使能 |
|
允许所有可屏蔽中断响应 |
快速退出中断: |
|
10 |
硬件自动弹栈 |
硬件自动恢复 8 个寄存器 |
从 PSP 弹出: |
切换回 Thread mode |
恢复被中断的任务现场 |
|
11 |
任务继续运行 |
返回被中断的指令 |
|
Thread mode |
任务无缝继续执行 |
|
12 |
PendSV 触发 |
所有高优先级中断完成后 |
硬件自动触发 PendSV 异常 |
切换到 Handler mode |
安全切换点: |
|
阶段 |
关键操作 |
硬件行为 |
软件责任 |
|---|---|---|---|
|
1. 保存旧任务 |
• 从 PSP 读取栈指针 → 保存 R4-R11/FPU |
无 |
精确捕获任务状态: |
|
2. 任务决策 |
• 调用 |
无 |
仅切换 TCB 指针: |
|
3. 恢复新任务 |
• 从 TCB 读取新栈指针 → 恢复 R4-R11/FPU |
无 |
准备新任务环境: |
|
4. 异常返回 |
• 执行 |
硬件自动: |
触发硬件状态机: |
8. 定时器中断设置异常处理 port_vSetupTimerInterrupt
// 68. 定时器中断设置函数定义
__attribute__( ( weak ) ) void port_vSetupTimerInterrupt( void ){
#if ( cfgUSE_TICKLESS_IDLE == 1 )
{
ulTimerCountsForOneTick = ( cfgSYSTICK_CLOCK_HZ / cfgTICK_RATE_HZ );
xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( cfgCPU_CLOCK_HZ / cfgSYSTICK_CLOCK_HZ );
}
#endif
portNVIC_SYSTICK_CTRL_REG = 0UL;
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
portNVIC_SYSTICK_LOAD_REG = ( cfgSYSTICK_CLOCK_HZ / cfgTICK_RATE_HZ ) - 1UL;
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}
|
步骤 |
代码/操作 |
操作说明 |
寄存器/内存变化 |
硬件状态 |
设计目的 |
|---|---|---|---|---|---|
|
0 |
函数调用前 |
|
|
Thread mode |
调度器初始化阶段,中断已使能 |
|
1 |
条件编译 |
检查是否启用低功耗模式 |
无变化 |
无变化 |
仅当配置低功耗时计算补偿参数 |
|
2 |
|
计算单个滴答的计数值 |
示例 (168MHz/1kHz): |
无变化 |
滴答精度基础: |
|
3 |
|
计算最大可抑制滴答数 |
|
无变化 |
低功耗安全边界: |
|
4 |
|
计算定时器停止补偿值 |
示例: |
无变化 |
补偿时钟源切换延迟: |
|
5 |
|
禁用 SysTick 定时器 |
|
定时器停止计数 |
安全重配置: |
|
6 |
|
清除当前计数值 |
|
计数器清零 |
确保精确起始点: |
|
7 |
|
设置重载值 |
示例 (168MHz/1kHz): |
无变化 |
滴答周期定义: |
|
8 |
|
使能定时器 |
|
定时器开始工作: |
系统时钟源启动: |
|
阶段 |
关键操作 |
硬件行为 |
软件责任 |
设计目的 |
|---|---|---|---|---|
|
1. 低功耗参数计算 |
• 计算 |
无 |
预计算低功耗参数: |
为低功耗模式提供精确的时间补偿参数,避免睡眠唤醒后时间漂移 |
|
2. 定时器禁用 |
|
硬件行为: |
安全重配置前提: |
防止在重配置过程中产生意外中断,保证配置原子性 |
|
3. 计数器清零 |
|
硬件行为: |
精确起始点: |
确保首次滴答从精确的 0 点开始,避免时间累积误差 |
|
4. 重载值配置 |
|
无即时行为 |
定义滴答周期: |
建立系统时间基准,决定任务调度粒度(默认1ms) |
|
5. 定时器使能 |
|
硬件自动行为: |
启动系统心跳: |
激活系统时钟源,为 |
9. port_vEnableVFP
#if ( cfgASSERT_DEFINED == 1 )
// 69. 验证中断优先级函数定义
void port_vValidateInterruptPriority( void ){
uint32_t u32CurInter;
uint8_t u8CurPrio;
__asm volatile ( "mrs %0, ipsr" : "=r" ( u32CurInter )::"memory" );
if( u32CurInter >= portFIRST_USER_INTERRUPT_NUMBER ){
u8CurPrio = pu8InterPrioRegisters[ u32CurInter ];
cfgASSERT( u8CurPrio >= u8MaxSysCallPriority );
}
cfgASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= u32MaxPRIGROUPValue );
}
#endif
|
步骤 |
汇编指令 |
操作说明 |
寄存器/内存变化 |
硬件状态 |
设计目的 |
|---|---|---|---|---|---|
|
0 |
函数调用前 |
|
|
Thread mode |
调度器初始化阶段,准备启用 FPU |
|
1 |
|
加载 CPACR 寄存器地址 |
|
无变化 |
定位协处理器控制寄存器: |
|
2 |
|
读取 CPACR 当前值 |
|
无变化 |
获取当前配置: |
|
3 |
|
设置 FPU 访问权限位 |
|
无变化 |
启用 FPU 完全访问: |
|
4 |
|
写回修改后的 CPACR 值 |
|
硬件行为: |
激活 FPU 硬件: |
|
5 |
|
返回调用者 |
|
无变化 |
裸函数返回: |
|
阶段 |
关键操作 |
硬件行为 |
软件责任 |
设计目的 |
|---|---|---|---|---|
|
1. 定位控制寄存器 |
• 加载 CPACR 地址 (0xE000ED88) |
无 |
精确寻址: |
为安全修改做准备,避免覆盖其他协处理器配置 |
|
2. 权限位设置 |
• 按位或操作设置 |
无 |
最小化修改: |
遵循"最小权限原则",仅启用必需功能,避免意外启用其他协处理器 |
|
3. 激活 FPU 硬件 |
• 写回修改后的 CPACR 值 |
硬件立即生效: |
原子操作: |
消除浮点指令陷阱: |
|
4. 安全返回 |
• |
无 |
裸函数规范: |
无缝集成到调度器启动流程,无额外开销 |
10. 流程总结图

11. port.c 实现
// 硬件平台相关函数实现
#include "RTOS.h"
#include "task.h"
// 00. 检查是否启用硬件浮点支持
#ifndef __VFP_FP__
#error This port can only be used when the project options are configured to enable hardware floating point support.
#endif
// 01. 中断服务程序(ISR)函数指针类型定义
typedef void ( * portISR_t )( void );
// 02. SysTick控制寄存器地址定义
#define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) )
// 03. SysTick重载值寄存器地址定义
#define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) )
// 04. SysTick当前值寄存器地址定义
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) )
// 05. 系统优先级寄存器2地址定义
#define portNVIC_SHPR2_REG ( *( ( volatile uint32_t * ) 0xe000ed1c ) )
// 06. 系统优先级寄存器3地址定义
#define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) )
// 07. SysTick时钟位定义
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
// 08. SysTick中断位定义
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
// 09. SysTick使能位定义
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
// 10. SysTick计数标志位定义
#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
// 11. PendSV清除位定义
#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL )
// 12. SysTick挂起设置位定义
#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL )
// 13. SysTick挂起清除位定义
#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL )
// 14. CPU ID寄存器地址定义
#define portCPUID ( *( ( volatile uint32_t * ) 0xE000ed00 ) )
// 15. Cortex-M7 r0p1核心ID定义
#define portCORTEX_M7_r0p1_ID ( 0x410FC271UL )
// 16. Cortex-M7 r0p0核心ID定义
#define portCORTEX_M7_r0p0_ID ( 0x410FC270UL )
// 17. 最小中断优先级定义
#define portMIN_INTERRUPT_PRIORITY ( 255UL )
// 18. PendSV中断优先级定义
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) portMIN_INTERRUPT_PRIORITY ) << 16UL )
// 19. SysTick中断优先级定义
#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) portMIN_INTERRUPT_PRIORITY ) << 24UL )
// 20. SCB向量表偏移寄存器地址定义
#define portSCB_VTOR_REG ( *( ( portISR_t ** ) 0xE000ED08 ) )
// 21. SVC中断向量索引定义
#define portVECTOR_INDEX_SVC ( 11 )
// 22. PendSV中断向量索引定义
#define portVECTOR_INDEX_PENDSV ( 14 )
// 23. 第一个用户中断号定义
#define portFIRST_USER_INTERRUPT_NUMBER ( 16 )
// 24. NVIC_IP寄存器偏移量定义
#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 )
// 25. AIRCR寄存器地址定义
#define portAIRCR_REG ( *( ( volatile uint32_t * ) 0xE000ED0C ) )
// 26. 最大8位值定义
#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff )
// 27. 字节最高位定义
#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 )
// 28. 最大优先级组位数定义
#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 )
// 29. 优先级组掩码定义
#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL )
// 30. 优先级组移位定义
#define portPRIGROUP_SHIFT ( 8UL )
// 31. 向量激活掩码定义
#define portVECTACTIVE_MASK ( 0xFFUL )
// 32. VFP上下文控制寄存器地址定义
#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 )
// 33. VFP ASPEN和LSPEN位定义
#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL )
// 34. 初始XPSR值定义
#define portINITIAL_XPSR ( 0x01000000 )
// 35. 初始异常返回值定义
#define portINITIAL_EXC_RETURN ( 0xfffffffd )
// 36. 最大24位数值定义
#define portMAX_24_BIT_NUMBER ( 0xffffffUL )
// 37. 起始地址掩码定义
#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL )
// 38. 错过计数因子定义
#define portMISSED_COUNTS_FACTOR ( 94UL )
#ifndef cfgSYSTICK_CLOCK_HZ
// 39. SysTick时钟频率定义
#define cfgSYSTICK_CLOCK_HZ ( cfgCPU_CLOCK_HZ )
// 40. SysTick时钟位配置定义
#define portNVIC_SYSTICK_CLK_BIT_CONFIG ( portNVIC_SYSTICK_CLK_BIT )
#else
#define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 )
#endif
#ifdef cfgTASK_RETURN_ADDRESS
// 42. 任务返回地址定义(用户覆盖)
#define portTASK_RETURN_ADDRESS cfgTASK_RETURN_ADDRESS
#else
// 42. 任务返回地址定义(默认)
#define portTASK_RETURN_ADDRESS port_vTaskExitError
#endif
// 43. 定时器中断设置函数声明
void port_vSetupTimerInterrupt( void );
// 44. PendSV异常处理函数声明
void port_vPendSVHandler( void ) __attribute__( ( naked ) );
// 45. SysTick中断处理函数声明
void port_vSysTickHandler( void );
// 46. SVC异常处理函数声明
void port_vSVCHandler( void ) __attribute__( ( naked ) );
// 47. 启动第一个任务函数声明
static void port_vStartFirstTask( void ) __attribute__( ( naked ) );
// 48. 启用VFP函数声明
static void port_vEnableVFP( void ) __attribute__( ( naked ) );
// 49. 任务退出错误处理函数声明
static void port_vTaskExitError( void );
// 50. 临界区嵌套计数变量定义
static UBaseType_t ulCriticalNesting = 0xaaaaaaaa;
#if ( cfgUSE_TICKLESS_IDLE == 1 )
// 51. 一个 tick 周期的 SysTick 计数定义
static uint32_t ulTimerCountsForOneTick = 0;
// 52. 最大可抑制的 tick 周期数定义
static uint32_t xMaximumPossibleSuppressedTicks = 0;
// 53. 停止计时器补偿值定义
static uint32_t ulStoppedTimerCompensation = 0;
#endif
#if ( cfgASSERT_DEFINED == 1 )
// 54. 最大系统调用优先级变量定义
static uint8_t u8MaxSysCallPriority = 0;
// 55. 最大PRIGROUP值变量定义
static uint32_t u32MaxPRIGROUPValue = 0;
// 56. 中断优先级寄存器指针定义
static const volatile uint8_t* const pu8InterPrioRegisters = (const volatile uint8_t* const)portNVIC_IP_REGISTERS_OFFSET_16;
#endif
// ------- 函数定义 begin -------
// 57. 初始化任务栈函数定义,头文件 64
// 在创建任务时,手动模拟一个中断返回时的栈帧结构,以便任务第一次被调度运行时,能像从中断返回一样正确地跳转到任务入口函数
StackType_t* port_pu32InitStack(StackType_t* pu32TopOfStack, TaskFunc_t pfCode, void* pvParameters ){
pu32TopOfStack--;
*pu32TopOfStack = portINITIAL_XPSR; /* xPSR */
pu32TopOfStack--;
*pu32TopOfStack = ( ( StackType_t ) pfCode ) & portSTART_ADDRESS_MASK; /* PC */
pu32TopOfStack--;
*pu32TopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */
pu32TopOfStack -= 5; /* R12, R3, R2 and R1. */
*pu32TopOfStack = ( StackType_t ) pvParameters; /* R0 */
pu32TopOfStack--;
*pu32TopOfStack = portINITIAL_EXC_RETURN;
pu32TopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pu32TopOfStack;
}
// 58. 任务退出错误处理函数定义,当任务函数意外返回(return)时,立即触发错误并死循环,防止系统进入未知状态
static void port_vTaskExitError( void ){
volatile uint32_t u32Dummy = 0;
cfgASSERT( ulCriticalNesting == ~0UL );
portDISABLE_INTERRUPTS();
while( u32Dummy == 0 ){}
}
// 59. SVC异常处理函数定义,把系统从“启动模式”切换到“任务运行模式”,让第一个任务开始干活
// msr psp, r0 把栈指针切换到任务专用的栈
void port_vSVCHandler( void ){
__asm volatile (
" ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */
" ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
" ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" ldmia r0!, {r4-r11, r14} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */
" msr psp, r0 \n" /* Restore the task stack pointer. */
" isb \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" bx r14 \n"
" \n"
" .align 4 \n"
"pxCurrentTCBConst2: .word pxCurrentTCB \n"
);
}
// 60. 启动第一个任务函数定义,完成最后的系统初始化,然后按下 SVC 0 按钮,触发之前的 port_vSVCHandler,让第一个任务正式开始运行
static void port_vStartFirstTask( void ){
__asm volatile (
" ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */
" ldr r0, [r0] \n"
" ldr r0, [r0] \n"
" msr msp, r0 \n" /* Set the msp back to the start of the stack. */
" mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */
" msr control, r0 \n"
" cpsie i \n" /* Globally enable interrupts. */
" cpsie f \n"
" dsb \n"
" isb \n"
" svc 0 \n" /* System call to start first task. */
" nop \n"
" .ltorg \n"
);
}
// 61. 启动调度器函数定义
BaseType_t port_lStartScheduler( void ){
// 1. CPU 版本检查
cfgASSERT( portCPUID != portCORTEX_M7_r0p1_ID );
cfgASSERT( portCPUID != portCORTEX_M7_r0p0_ID );
// 2. 中断向量表验证(防配置错误)
#if ( cfgCHECK_HANDLER_INSTALLATION == 1 )
{
const portISR_t * const ppfVectorTable = portSCB_VTOR_REG;
cfgASSERT( ppfVectorTable[ portVECTOR_INDEX_SVC ] == port_vSVCHandler );
cfgASSERT( ppfVectorTable[ portVECTOR_INDEX_PENDSV ] == port_vPendSVHandler );
}
#endif
#if ( cfgASSERT_DEFINED == 1 )
{
volatile uint8_t u8OriginalPriority;
volatile uint32_t u32ImplementedPrioBits = 0;
volatile uint8_t * const pu8FirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
volatile uint8_t u8MaxPriorityValue;
u8OriginalPriority = *pu8FirstUserPriorityRegister;
*pu8FirstUserPriorityRegister = portMAX_8_BIT_VALUE;
u8MaxPriorityValue = *pu8FirstUserPriorityRegister;
u8MaxSysCallPriority = cfgMAX_SYSCALL_INTERRUPT_PRIORITY & u8MaxPriorityValue;
cfgASSERT( u8MaxSysCallPriority );
cfgASSERT( ( cfgMAX_SYSCALL_INTERRUPT_PRIORITY & ( ~u8MaxPriorityValue ) ) == 0U );
while( ( u8MaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ){
u32ImplementedPrioBits++;
u8MaxPriorityValue <<= ( uint8_t ) 0x01;
}
if( u32ImplementedPrioBits == 8 ){
cfgASSERT( ( cfgMAX_SYSCALL_INTERRUPT_PRIORITY & 0x1U ) == 0U );
u32MaxPRIGROUPValue = 0;
}else{
u32MaxPRIGROUPValue = portMAX_PRIGROUP_BITS - u32ImplementedPrioBits;
}
u32MaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
u32MaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;
*pu8FirstUserPriorityRegister = u8OriginalPriority;
}
#endif
// 3. 优先级配置
portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
portNVIC_SHPR2_REG = 0;
port_vSetupTimerInterrupt();
ulCriticalNesting = 0;
port_vEnableVFP();
*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;
port_vStartFirstTask();
task_vSwitchContext();
port_vTaskExitError();
return 0;
}
// 62. 结束调度器函数定义
void port_vEndScheduler( void ){
cfgASSERT( ulCriticalNesting == 1000UL );
}
// 63. 进入临界区函数定义
void port_vEnterCritical( void ){
portDISABLE_INTERRUPTS();
ulCriticalNesting++;
if( ulCriticalNesting == 1 ){
cfgASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}
// 64. 退出临界区函数定义
void port_vExitCritical( void ){
cfgASSERT( ulCriticalNesting );
ulCriticalNesting--;
if( ulCriticalNesting == 0 ){
portENABLE_INTERRUPTS();
}
}
// 65. PendSV异常处理函数定义
void port_vPendSVHandler( void )
{
__asm volatile
(
" mrs r0, psp \n"
" isb \n"
" \n"
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
" ldr r2, [r3] \n"
" \n"
" tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */
" it eq \n"
" vstmdbeq r0!, {s16-s31} \n"
" \n"
" stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */
" str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */
" \n"
" stmdb sp!, {r0, r3} \n"
" mov r0, %0 \n"
" msr basepri, r0 \n"
" dsb \n"
" isb \n"
" bl task_vSwitchContext \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" ldmia sp!, {r0, r3} \n"
" \n"
" ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" ldr r0, [r1] \n"
" \n"
" ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */
" \n"
" tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */
" it eq \n"
" vldmiaeq r0!, {s16-s31} \n"
" \n"
" msr psp, r0 \n"
" isb \n"
" \n"
#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
#if WORKAROUND_PMU_CM001 == 1
" push { r14 } \n"
" pop { pc } \n"
#endif
#endif
" \n"
" bx r14 \n"
" \n"
" .align 4 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
::"i" ( cfgMAX_SYSCALL_INTERRUPT_PRIORITY )
);
}
// 66. SysTick中断处理函数定义
void port_vSysTickHandler( void ){
portDISABLE_INTERRUPTS();
traceISR_ENTER();
{
if( task_ulIncrementTick() != pdFALSE ){
traceISR_EXIT_TO_SCHEDULER();
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}else{
traceISR_EXIT();
}
}
portENABLE_INTERRUPTS();
}
#if ( cfgUSE_TICKLESS_IDLE == 1 )
// 67. 抑制 tick 并睡眠函数定义
__attribute__( ( weak ) ) void port_vSuppressTicksAndSleep( TickType_t u32ExpectedIdleTime ){
uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft;
TickType_t xModifiableIdleTime;
if( u32ExpectedIdleTime > xMaximumPossibleSuppressedTicks ){
u32ExpectedIdleTime = xMaximumPossibleSuppressedTicks;
}
__asm volatile ( "cpsid i" ::: "memory" );
__asm volatile ( "dsb" );
__asm volatile ( "isb" );
if( task_eConfirmSleepModeStatus() == eAbortSleep ){
__asm volatile ( "cpsie i" ::: "memory" );
}else{
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG;
if( ulSysTickDecrementsLeft == 0 ){
ulSysTickDecrementsLeft = ulTimerCountsForOneTick;
}
ulReloadValue = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( u32ExpectedIdleTime - 1UL ) );
if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ){
portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT;
ulReloadValue -= ulTimerCountsForOneTick;
}
if( ulReloadValue > ulStoppedTimerCompensation ){
ulReloadValue -= ulStoppedTimerCompensation;
}
portNVIC_SYSTICK_LOAD_REG = ulReloadValue;
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
xModifiableIdleTime = u32ExpectedIdleTime;
cfgPRE_SLEEP_PROCESSING( xModifiableIdleTime );
if( xModifiableIdleTime > 0 ){
__asm volatile ( "dsb" ::: "memory" );
__asm volatile ( "wfi" );
__asm volatile ( "isb" );
}
cfgPOST_SLEEP_PROCESSING( u32ExpectedIdleTime );
__asm volatile ( "cpsie i" ::: "memory" );
__asm volatile ( "dsb" );
__asm volatile ( "isb" );
__asm volatile ( "cpsid i" ::: "memory" );
__asm volatile ( "dsb" );
__asm volatile ( "isb" );
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ){
uint32_t u32CalculatedLoadValue;
u32CalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );
if( ( u32CalculatedLoadValue <= ulStoppedTimerCompensation ) || ( u32CalculatedLoadValue > ulTimerCountsForOneTick ) ){
u32CalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
}
portNVIC_SYSTICK_LOAD_REG = u32CalculatedLoadValue;
ulCompleteTickPeriods = u32ExpectedIdleTime - 1UL;
}else{
ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG;
#if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT )
{
if( ulSysTickDecrementsLeft == 0 ){
ulSysTickDecrementsLeft = ulReloadValue;
}
}
#endif
ulCompletedSysTickDecrements = ( u32ExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft;
ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;
portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
}
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
#if ( portNVIC_SYSTICK_CLK_BIT_CONFIG == portNVIC_SYSTICK_CLK_BIT )
{
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
}
#else
{
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT;
if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ){
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0;
}
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
}
#endif
task_vStepTick( ulCompleteTickPeriods );
__asm volatile ( "cpsie i" ::: "memory" );
}
}
#endif
// 68. 定时器中断设置函数定义
__attribute__( ( weak ) ) void port_vSetupTimerInterrupt( void ){
#if ( cfgUSE_TICKLESS_IDLE == 1 )
{
ulTimerCountsForOneTick = ( cfgSYSTICK_CLOCK_HZ / cfgTICK_RATE_HZ );
xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( cfgCPU_CLOCK_HZ / cfgSYSTICK_CLOCK_HZ );
}
#endif
portNVIC_SYSTICK_CTRL_REG = 0UL;
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
portNVIC_SYSTICK_LOAD_REG = ( cfgSYSTICK_CLOCK_HZ / cfgTICK_RATE_HZ ) - 1UL;
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}
static void port_vEnableVFP( void ){
__asm volatile
(
" ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */
" ldr r1, [r0] \n"
" \n"
" orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */
" str r1, [r0] \n"
" bx r14 \n"
" .ltorg \n"
);
}
#if ( cfgASSERT_DEFINED == 1 )
// 69. 验证中断优先级函数定义
void port_vValidateInterruptPriority( void ){
uint32_t u32CurInter;
uint8_t u8CurPrio;
__asm volatile ( "mrs %0, ipsr" : "=r" ( u32CurInter )::"memory" );
if( u32CurInter >= portFIRST_USER_INTERRUPT_NUMBER ){
u8CurPrio = pu8InterPrioRegisters[ u32CurInter ];
cfgASSERT( u8CurPrio >= u8MaxSysCallPriority );
}
cfgASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= u32MaxPRIGROUPValue );
}
#endif
// ------- 函数定义 end -------
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)