操作系统开发:(4) 系统级程序员模型、模式、特权、栈、异常与函数序言(实现 portable.h )
本文详细介绍了ARMv7-M架构的系统级编程模型,包括操作模式、特权级别、栈机制和异常处理等核心概念。主要内容包括:1. 两种操作模式(Thread和Handler模式)及其转换机制;2. 特权级别对系统资源访问的控制;3. 双栈指针(MSP和PSP)的设计原理与应用场景;4. 异常处理流程(生成、挂起、激活)和4类异常类型;5. 优先级抢占规则和异常状态管理;6. 内核寄存器功能说明及函数调用流
1. 系统级术语和操作概述
下载并查看内核参考手册:DDI0403E_e_armv7m_arm.pdf,查看系统级程序员模型(System Level Programmers’ Model),以下讲解相关基础知识:
1.1 模式、特权和栈
1.1.1 模式

ARMv7-M 仅支持 两种操作模式:
|
模式 |
定义 |
触发条件 |
关键特性 |
|---|---|---|---|
|
Thread mode |
系统正常运行模式 |
1. 复位后首次进入 |
- 可以是 特权 或 非特权 |
|
Handler mode |
异常处理模式 |
1. 发生异常(如中断、系统调用) |
- 必须 用此模式触发异常返回 |
- 异常处理流程:Thread mode → (异常触发) → Handler mode → (异常返回) → Thread mode
1.1.2 特权
特权级别控制 代码对系统资源的访问权限:
|
特权级别 |
定义 |
访问权限 |
关键规则 |
|---|---|---|---|
|
Privileged(特权) |
具有完全系统访问权 |
可访问 所有资源(如控制寄存器、内存保护单元) |
- Handler mode 总是特权 |
|
Unprivileged(非特权) |
受限访问权限 |
禁止访问 关键资源(如系统寄存器),防止用户程序破坏系统 |
- 仅在 Thread mode 中可用 |
- 安全隔离:操作系统内核运行在 特权 Thread mode,用户程序运行在 非特权 Thread mode,避免恶意代码破坏系统。
- 异常处理强制特权:Handler mode 必须是特权(因为中断处理需要访问所有硬件资源)。
示例场景:
- 复位后:进入 特权 Thread mode(执行启动代码)。
- 用户程序运行:切换到 非特权 Thread mode(防止直接操作硬件)。
- 中断发生:自动进入 特权 Handler mode(处理中断)。
- 中断结束:返回到 非特权 Thread mode(继续执行用户程序)。
1.1.3 栈指针
ARMv7-M 实现 双栈指针机制,支持灵活的任务切换和异常处理:
|
栈指针 |
全称 |
用途 |
模式关联 |
|---|---|---|---|
|
MSP |
Main Stack Pointer |
- 异常处理 |
- Handler mode 必须使用 MSP |
|
PSP |
Process Stack Pointer |
- 非特权 Thread mode(用户任务) |
- 仅 Thread mode 可用 |
- 异常处理稳定性:Handler mode 使用 MSP,确保中断处理时栈空间独立,避免用户任务栈溢出影响异常处理。
- 多任务支持:每个用户任务可分配独立的 PSP,实现栈隔离(类似操作系统中的任务栈)。
|
模式 |
栈指针选择 |
说明 |
|---|---|---|
|
Handler mode |
强制使用 MSP |
异常处理必须有统一栈空间 |
|
Thread mode |
可选 MSP 或 PSP |
- 内核任务 → MSP |
典型流程:
- 复位 → Thread mode(特权)执行启动代码 → 使用 MSP
- 切换到用户任务 → Thread mode(非特权)执行 main → 切换到 PSP
- 中断发生 → Handler mode(默认特权) → 自动切换回 MSP
- 中断结束 → Thread mode(非特权)返回用户任务 main → 切换回 PSP
1.2 异常 Exceptions
1.2.1 异常的定义

异常是 改变程序正常执行流的事件,ARMv7-M 把异常分成 两个不可分割的阶段:
|
阶段 |
何时发生 |
发生了什么 |
为什么重要 |
|---|---|---|---|
|
1. 异常生成(Exception generation) |
异常事件发生时 |
- 事件被“提交”给处理器 |
硬件自动完成 |
|
2. 异常处理/激活(Exception processing) |
处理器决定处理异常时 |
- 1. 保存当前程序状态(自动压栈) |
软件可干预 |
中断属于异常的一部分。
- 从 生成 → 处理 的过渡是 瞬间的(硬件自动触发)。
- 你只能控制 处理阶段(写异常处理函数),但不能控制 生成阶段(硬件决定何时发生)。
1.2.2 ARMv7-M 支持的 4 类异常

|
异常类型 |
触发方式 |
典型场景 |
详解 |
|---|---|---|---|
|
Reset |
复位信号(如上电) |
系统启动 |
- 特殊异常:强制终止当前执行 |
|
Supervisor Call (SVC) |
执行 SVC #n 指令 |
用户程序调用操作系统 |
- 软件显式触发(例如:SVC #0 请求分配内存) |
|
Fault |
指令执行出错 |
内存访问错误、除零错误 |
- 同步异常:与指令执行直接相关 |
|
Interrupt |
外部信号(如定时器中断) |
按键中断、串口接收数据 |
- 异步异常:与当前指令流无关 |
1.2.3 异常的 3 种状态

|
状态 |
定义 |
何时发生 |
举例 |
|---|---|---|---|
|
Inactive |
异常未发生、未挂起 |
- 系统刚启动 |
按键未按下时,按键中断处于 Inactive |
|
Pending |
异常已生成,但尚未处理 |
- 事件发生后 → 处理器开始处理前 |
按键按下 → 但 CPU 正在处理高优先级任务(如电机控制) |
|
Active |
处理器正在执行异常处理程序 |
- 从异常入口 → 异常返回前 |
CPU 正在执行 USART_IRQHandler() 函数 |

1.2.4 优先级

|
概念 |
定义 |
作用 |
|---|---|---|
|
Exception Priority |
异常自身的优先级值(0=最高,255=最低) |
决定 谁先被处理 |
|
Execution Priority |
CPU 当前正在执行的优先级 |
类似“当前车道的车速” |
|
Base Priority |
Thread 模式下的基础优先级阈值 |
仅 Thread mode 有效 |
抢占规则:
- 场景:CPU 正在处理 低优先级异常(如 UART 中断),此时 高优先级异常(如 PendSV)发生。
- 硬件自动执行:
- 保存低优先级异常的现场(压栈)
- 切换到高优先级异常处理程序
- 处理完高优先级异常后,自动恢复低优先级异常
- 结论:
- 高优先级异常永远能抢占低优先级异常,
- 但 低优先级不能抢占高优先级(这是硬件强制的!)。
Thread 模式下的特殊规则:
- 当 CPU 在 Thread mode(用户程序)运行时:
- 如果发生异常,必须切换到 Handler mode
- 例外:如果异常优先级 低于 BASEPRI(基础优先级寄存器),则 不触发异常(相当于“屏蔽”)
1.2.5 异常返回


1.3 执行状态(Execution State) 与 调试状态(Debug State)

2. 内核寄存器
2.1 内核寄存器介绍
在第 0 讲中,我们通过查阅开发板参考手册得知:可知,ARM Cortex-M 采用统一编址,即所有东西(代码、内存、外设)都映射到一个 4GB 的线性地址空间(硬件电路硬编码实现)。
在开发板参考手册中所描述的寄存器,都是内存映射寄存器(Memory-Mapped Registers),有真实地址,可以用 C 指针读写。
但是在开发操作系统时,我们需要使用到一些与 CPU 紧密耦合的寄存器,这些寄存器无地址,不能用 取地址;只能用汇编指令如 MOV R1, #5 操作,即 CPU 内核寄存器。
开发板参考手册不会给出这些寄存器的信息,因为其属于应用级程序员模型,我们需要下载并查看内核参考手册:DDI0403E_e_armv7m_arm.pdf,查看系统级程序员模型(System Level Programmers’ Model),如图所示:

内容总结如下表:
|
寄存器名称 |
类型 |
是否有地址? |
访问方式 |
功能 |
|---|---|---|---|---|
|
R0 – R12 |
通用寄存器(General-purpose) |
❌ 无 |
汇编指令: |
CPU 内部硬件寄存器,存放运算数据、函数参数、返回值等 |
|
SP_main /SP_process (R13) |
栈指针 (Stack Pointer) |
❌ 无 |
PUSH/POP、MSR SP, Rn |
分别用于 Handler 模式(中断)和 Thread 模式(任务)的栈顶指针 |
|
LR(R14) |
链接寄存器(Link Register) |
❌ 无 |
BL, BX LR, MOV LR, PC |
函数调用或异常进入时自动写入,保存子程序/异常返回地址 |
|
PC(R15) |
程序计数器 (Program Counter) |
❌ 无 |
B, BL, BX, 异常返回自动加载 |
指向下一条要执行的指令地址( +2) |
|
xPSR( APSR/IPSR/EPSR) |
状态寄存器(Status Register) |
❌ 无 |
MRS R0, xPSR, MSR xPSR, R0 |
为处理器提供关于当前执行状态、条件标志和异常信息的完整视图 |
|
PRIMASK / FAULTMASK / BASEPRI |
屏蔽寄存器(Mask Registers) |
❌ 无 |
MSR PRIMASK, R0 等 |
控制中断屏蔽: |
|
CONTROL |
控制寄存器 |
❌ 无 |
MSR CONTROL, R0 |
决定当前运行模式与栈选择 |
|
其他所有寄存器 (NVIC, SCB, GPIO, USART 等) |
内存映射寄存器(Memory-Mapped Registers) |
✅ 有(位于 0x40000000 ~ 0xE000EFFF) |
C语言指针或 LDR/STR:*0x40020000 = 1; |
外设配置、系统控制、中断管理等 |
2.2 SP、LR 和 PC调用流程
例:
int add(int a, int b) {
return a + b;
}
int main() {
int x = add(5, 6);
return 0;
}
假设内存布局:
- main 函数地址:0x0800_0200
- add 函数地址:0x0800_0300
其汇编为:
; main 函数(位于 Flash 0x0800_0200 起始)
0x0800_0200: MOV R0, #5 ; 2字节指令
0x0800_0202: MOV R1, #6 ; 2字节指令
0x0800_0204: BL add ; 4字节指令(占 0x0800_0204 ~ 0x0800_0207)
0x0800_0208: BX LR ; 2字节指令
; add 函数(位于 Flash 0x0800_0300 起始)
0x0800_0300: ADD R0, R0, R1 ; 2字节指令
0x0800_0302: BX LR ; 2字节指令
执行 BL add(地址 0x0800_0204):
|
时刻 |
寄存器 |
值 |
说明 |
|---|---|---|---|
|
执行前 |
PC |
0x0800_0204 |
指向 BL 指令本身 |
|
LR |
旧值 |
旧值 | |
|
执行后 |
PC |
0x0800_0300 |
✅ 已跳转到 add 函数 |
|
LR |
0x0800_0208 |
✅ 记住返回地址 |
由以上流程可得 PC 和 LR 的作用。
但是如果 add 不是叶子函数:
int helper(int x) { return x * 2; }
int add(int a, int b) {
int tmp = helper(a); // 要调用 helper!
return tmp + b;
}
则 add 的汇编:
add:
PUSH {LR} ; 保存返回地址
BL helper
ADD R0, R0, R1
POP {PC} ; 恢复并返回
在 helper 函数 中还会再保存需要返回的指令 ADD R0, R0, R1 的地址到 LR 中,这就覆盖了 add 需要返回的地址(即 main 函数中的 return 0 对应的地址),故在跳转到 helper 前需要先记录 add 自己要返回的 LR 到 内存 ram 中,这一段内存即 栈 ,由链接脚本预先分配的 ram 的一部分,SP 则记录栈顶地址,每次压入数据后 SP 指针下移用来给下次数据压入。
2.3 间接寻址(Indirect Addressing)
Flash 中的指令 寄存器(桥梁) RAM 中的数据
┌──────────────┐ ┌──────────────────┐ ┌────────────────────┐
│ LDR R1, [R0] │ → │ R0 = 0x2000_0000 │ → │[0x2000_0000] = 123 │
└──────────────┘ └──────────────────┘ └────────────────────┘
指令本身在Flash 寄存器存地址 实际数据在RAM
[R0] 中的方括号 [ ] 表示"间接访问" —— 不是用 R0 的值,而是用 R0 指向的地址里的值。
|
寻址方式 |
汇编示例 |
寄存器作用 |
实际访问位置 |
|---|---|---|---|
|
立即数寻址 |
MOV R0, #5 |
R0 直接存值 5 |
无内存访问(5 编码在指令中) |
|
寄存器寻址 |
ADD R0, R1, R2 |
R0/R1/R2 存值 |
无内存访问(纯寄存器运算) |
|
间接寻址 |
LDR R1, [R0] |
R0 存地址,R1 存值 |
✅ 访问 RAM(R0 指向的位置) |
链接脚本与内存布局规定在 操作系统开发:(1) 启动文件与链接脚本 中有详细介绍。
3. 指针算术
在 C 语言中,对指针做 ++ 或 -- 操作时,移动的不是 1 字节,而是 sizeof(所指类型) 字节。
如:
uint32_t arr[10];
uint32_t* p = &arr[5]; // p 指向第 5 个元素(每个元素 4 字节)
p--; // p 现在指向 arr[4]
虽然代码写的是 p--(减 1),但实际地址减少了 4 字节!
这是因为编译器知道 p 是 uint32_t*,所以:
p-- 等价于 p = (uint32_t*)((char*)p - sizeof(uint32_t))
4. 满递减栈(Full Descending Stack)
ARM Cortex-M 使用 满递减栈,含义是:
- “递减”:栈向低地址方向增长(每次 push,SP 减小)
- “满”:SP 始终指向栈顶最后一个有效数据(不是下一个空位置)
例(假设初始栈顶地址是 0x2000_1000):
|
操作 |
SP 值(地址) |
说明 |
|---|---|---|
|
初始 |
0x2000_1000 |
栈顶(最高地址) |
|
push R0 |
0x2000_0FFC |
先减 4,再写入 R0 → SP 指向 R0 |
|
push R1 |
0x2000_0FF8 |
再减 4,写入 R1 → SP 指向 R1 |
所以,压栈顺序是:先移动指针,再写数据。
本代码中 port_pu32InitStack 函数中:
pu32TopOfStack--; // 先移动到下一个空位置(低地址)
*pu32TopOfStack = value; // 再写入
完全模拟了硬件的压栈行为。
5. 函数序言:__attribute__((naked))
__attribute__((naked)) 用于告诉编译器:不要为这个函数生成任何标准的函数序言。
在普通 C 函数中,编译器会自动生成一些汇编代码来管理调用约定(ABI),例如:
void normal_function(void) {
int a = 1;
// ...
}
编译后可能变成:
normal_function:
PUSH {R7, LR} ; ← 序言:保存帧指针和返回地址
MOV R7, SP ; 设置帧指针
SUB SP, SP, #4 ; 为局部变量分配栈空间
MOV [SP], #1 ; a = 1
ADD SP, SP, #4 ; ← 尾声:释放局部变量
POP {R7, PC} ; 恢复并返回(PC = LR)
这些 PUSH/POP、栈指针调整就是编译器自动生成的上下文管理代码。
在底层系统编程场景中,我们不希望编译器动堆栈或寄存器,因为:
- 我们自己完全控制上下文(如中断服务例程 ISR、任务切换代码);
- 函数入口/出口必须严格符合硬件要求(如异常返回前不能破坏寄存器);
- 需要直接写纯汇编逻辑,不能被编译器污染。
6. 完整代码 port.h
// 源码文件架构:
// FreeRTOSConfig.h (用户配置文件)
// FreeRTOS.h
// ├── projdefs.h (任务类型、错误码定义)
// └── portable.h (根据平台定义定义系统宏定义)
// └── portmacro.h (平台硬件特有宏定义)
// └── port.c (平台硬件特有实现)
//
// 这里把硬件移植相关分别放在 port.h 和 port.c中
#ifndef PORT_H
#define PORT_H
#ifdef __cplusplus
extern "C" {
#endif
// ------- 原 portmacro.h 中的定义 begin -------
// 01. 字符类型定义
#define portCHAR char
// 02. 浮点类型定义
#define portFLOAT float
// 03. 双精度浮点类型定义
#define portDOUBLE double
// 04. 长整型定义
#define portLONG long
// 05. 短整型定义
#define portSHORT short
// 06. 栈类型定义
#define portSTACK_TYPE uint32_t
// 07. 基础类型定义
#define portBASE_TYPE long
// 08. 栈类型别名定义
typedef portSTACK_TYPE StackType_t;
// 09. 基础类型别名定义
typedef long BaseType_t;
// 10. 无符号基础类型别名定义
typedef unsigned long UBaseType_t;
// 11 - 18. 时钟节拍类型与最大延时定义
#if ( cfgTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS )
// 11. 时钟节拍类型定义 - 16位
typedef uint16_t TickType_t;
// 12. 最大延时定义 - 16位
#define portMAX_DELAY ( TickType_t ) 0xffff
#elif ( cfgTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS )
// 13. 时钟节拍类型定义 - 32位
typedef uint32_t TickType_t;
// 14. 最大延时定义 - 32位
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
// 15. 时钟节拍原子操作标志
#define portTICK_TYPE_IS_ATOMIC 1
#elif ( cfgTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS )
// 16. 时钟节拍类型定义 - 64位
typedef uint64_t TickType_t;
// 17. 最大延时定义 - 64位
#define portMAX_DELAY ( TickType_t ) 0xffffffffffffffffULL
#else
// 18. 时钟节拍类型错误处理
#error cfgTICK_TYPE_WIDTH_IN_BITS set to unsupported tick type width.
#endif
// 19. 栈增长方向定义,定义栈的增长方向,-1表示栈向下增长(从高地址向低地址)
#define portSTACK_GROWTH ( -1 )
// 20. 时钟节拍周期定义,定义时钟节拍的周期(以毫秒为单位)
// 21. 字节对齐定义,定义内存对齐的字节数
#define portBYTE_ALIGNMENT 8
// 22. 不丢弃属性定义,确保编译器不会优化掉未使用的变量或函数
#define portDONT_DISCARD __attribute__( ( used ) )
// 23. 任务切换宏定义,触发任务切换,通过设置PendSV中断来请求上下文切换
/***************
** portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT:向 NVIC 的中断控制状态寄存器(0xe000ed04)写入 1 << 28,设置 PendSV 挂起位,请求异常。
** dsb(Data Synchronization Barrier):确保所有之前的内存访问(如寄存器写入)已完成,避免因乱序执行导致写入未生效。
** __asm volatile ("指令" : 输出 : 输入 : 破坏列表);
** __asm volatile (
** "dsb" // ← 汇编指令:Data Synchronization Barrier
** : // ← 输出操作数:空(无冒号后内容表示无输出)
** : // ← 输入操作数:空
** : "memory" // ← 破坏列表:告知编译器“内存状态被改变”
** );
** isb(Instruction Synchronization Barrier):刷新处理器流水线,确保后续指令(包括异常响应)基于最新状态执行。
***************/
#define portYIELD() \
{ \
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
__asm volatile ( "dsb" ::: "memory" ); \
__asm volatile ( "isb" ); \
}
// 24. NVIC中断控制寄存器定义,定义NVIC中断控制寄存器ICSR的地址
#define portNVIC_INT_CTRL_REG ( *( ( volatile uint32_t* ) 0xe000ed04 ) )
// 25. PendSV设置位定义,用于触发 PendSV 异常的位掩码,在 ICSR 寄存器中,bit 28 为 PENDSVSET 位,写 1 即可挂起 PendSV 异常
#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
// 26. 中断结束切换宏定义,在 ISR 结束时,根据参数决定是否发起任务切换
#define portEND_SWITCHING_ISR( SwitchRequired ) \
do \
{ \
if( SwitchRequired != pdFALSE ) \
{ \
traceISR_EXIT_TO_SCHEDULER(); \
portYIELD(); \
} \
else \
{ \
traceISR_EXIT(); \
} \
} while( 0 )
// 27. 中断中任务切换宏定义,从中断中触发任务切换的宏,是portEND_SWITCHING_ISR的别名
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
// 28. 进入临界区函数声明,进入临界区,禁用中断
extern void port_vEnterCritical( void );
// 29. 退出临界区函数声明:退出临界区,重新启用中断
extern void port_vExitCritical( void );
// 30. 中断中设置中断掩码宏定义,从中断中设置中断掩码,返回原始的BASEPRI值
#define portSET_INTERRUPT_MASK_FROM_ISR() port_u32RaiseBASEPRI()
// 31. 中断中清除中断掩码宏定义,从中断中清除中断掩码,恢复到指定的BASEPRI值
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) port_vSetBASEPRI( x )
// 32. 禁用中断宏定义,禁用中断,通过提高BASEPRI来实现
#define portDISABLE_INTERRUPTS() port_vRaiseBASEPRI()
// 33. 启用中断宏定义,启用中断,通过设置BASEPRI为0来实现
#define portENABLE_INTERRUPTS() port_vSetBASEPRI( 0 )
// 34. 进入临界区宏定义,进入临界区,通过调用port_vEnterCritical()函数来实现
#define portENTER_CRITICAL() port_vEnterCritical()
// 35. 退出临界区宏定义,退出临界区,通过调用port_vExitCritical()函数来实现
#define portEXIT_CRITICAL() port_vExitCritical()
// 36. 任务函数原型类型宏定义,定义任务函数的原型,用于声明任务函数,用于在tasks.c中实现
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters )
// 37. 任务函数类型宏定义,定义任务函数的实现,用于实现任务函数,用于在tasks.c中实现
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters )
// 38. 无节拍空闲/低功耗功能,定义无节拍空闲/低功耗功能的宏和函数
#ifndef portSUPPRESS_TICKS_AND_SLEEP
// 39. 无节拍空闲/低功耗函数声明,实现无节拍空闲/低功耗功能的函数
extern void port_vSuppressTicksAndSleep(TickType_t xExpectedIdleTime);
// 40. 无节拍空闲/低功耗宏定义,调用port_vSuppressTicksAndSleep函数,实现无节拍空闲/低功耗功能
#define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) port_vSuppressTicksAndSleep( xExpectedIdleTime )
#endif
// 41. 定义是否使用优化任务选择算法,如果未定义,默认为1,表示使用端口优化的任务选择算法
#ifndef cfgUSE_PORT_OPTIMISED_TASK_SELECTION
#define cfgUSE_PORT_OPTIMISED_TASK_SELECTION 1
#endif
#if cfgUSE_PORT_OPTIMISED_TASK_SELECTION == 1
// 42. 前导零计数函数,计算32位无符号整数中前导零的个数,使用汇编指令clz来实现,提高性能
__attribute__( ( always_inline ) ) static inline uint8_t port_u8CountLeadingZeros( uint32_t ulBitmap ){
uint8_t u8Return;
__asm volatile ( "clz %0, %1" : "=r" ( u8Return ) : "r" ( ulBitmap ) : "memory" );
return u8Return;
}
// 43. 配置检查,检查配置是否正确,确保cfgMAX_PRIO不大于32
#if ( cfgMAX_PRIO > 32 )
#error cfgUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when cfgMAX_PRIO is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
#endif
// 44. 记录就绪优先级宏定义,在位图中记录任务的就绪优先级
#define portRECORD_READY_PRIORITY(ulPrio, ulReadyPrio) ( ulReadyPrio ) |= ( 1UL << ( ulPrio ) )
// 45. 重置就绪优先级宏定义,在位图中重置任务的就绪优先级
#define portRESET_READY_PRIORITY(ulPrio, ulReadyPrio) ( ulReadyPrio ) &= ~( 1UL << ( ulPrio ) )
// 46. 获取最高优先级宏定义,从就绪优先级位图中获取最高优先级
#define portGET_HIGHEST_PRIORITY(ulTopPrio, ulReadyPrio) ulTopPrio = ( 31UL - ( uint32_t ) port_u8CountLeadingZeros( ( ulReadyPrio ) ) )
#endif
#ifdef cfgASSERT
// 47. 中断优先级验证函数声明,验证中断优先级是否有效
void port_vValidateInterruptPriority( void );
// 48. 中断优先级无效断言宏定义,当中断优先级无效时进行断言
#define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() port_vValidateInterruptPriority()
#endif
// 49. 空操作宏定义,定义空操作宏,不需要实际的NOP操作
#define portNOP()
// 50. 内联函数宏定义,定义内联函数的宏
#define portINLINE __inline
// 51. 强制内联函数宏定义,定义强制内联函数的宏
#ifndef portFORCE_INLINE
#define portFORCE_INLINE inline __attribute__( ( always_inline ) )
#endif
// 52. 中断状态检查函数,检查当前是否在中断上下文中执行,通过读取IPSR寄存器的值来判断,如果为0表示不在中断中,否则表示在中断中
portFORCE_INLINE static BaseType_t port_lIsInsideInterrupt( void ){
uint32_t u32CurInter;
BaseType_t lReturn;
__asm volatile ( "mrs %0, ipsr" : "=r" ( u32CurInter )::"memory" ); // 将系统寄存器IPSR的值读入通用寄存器 %0(u32CurInter)
// 只要 IPSR ≠ 0,就说明 CPU 正在异常/中断处理程序中
if( u32CurInter == 0 ){
lReturn = pdFALSE;
}else{
lReturn = pdTRUE;
}
return lReturn;
}
// 53. 提高BASEPRI寄存器的值,禁用低于cfgMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断
portFORCE_INLINE static void port_vRaiseBASEPRI( void ){
uint32_t u32NewBASEPRI;
__asm volatile
(
" mov %0, %1 \n" \
" msr basepri, %0 \n" \
" isb \n" \
" dsb \n" \
: "=r" ( u32NewBASEPRI )
: "i" ( cfgMAX_SYSCALL_INTERRUPT_PRIORITY )
: "memory"
);
}
// 54. 提高BASEPRI寄存器的值,禁用低于cfgMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断,并返回BASEPRI的原始值
portFORCE_INLINE static uint32_t port_u32RaiseBASEPRI( void ){
uint32_t u32OriginalBASEPRI, u32NewBASEPRI;
__asm volatile
(
" mrs %0, basepri \n" \
" mov %1, %2 \n" \
" msr basepri, %1 \n" \
" isb \n" \
" dsb \n" \
: "=r" ( u32OriginalBASEPRI ), "=r" ( u32NewBASEPRI )
: "i" ( cfgMAX_SYSCALL_INTERRUPT_PRIORITY )
: "memory"
);
return u32OriginalBASEPRI;
}
// 55. 设置BASEPRI寄存器的值,用于控制中断优先级,当设置为0时,启用所有中断
portFORCE_INLINE static void port_vSetBASEPRI( uint32_t u32NewMaskValue ){
__asm volatile
(
" msr basepri, %0 " ::"r" ( u32NewMaskValue ) : "memory"
);
}
// 56. 内存屏障宏定义,定义内存屏障,确保编译器不会重排内存操作
#define portMEMORY_BARRIER() __asm volatile ( "" ::: "memory" )
// ------- 原 portmacro.h 中的定义 end -------
// ------- 原 portable.h 中的定义 begin -------
// 57. 字节对齐掩码定义,根据portBYTE_ALIGNMENT的值,定义字节对齐掩码
#if portBYTE_ALIGNMENT == 32
#define portBYTE_ALIGNMENT_MASK ( 0x001f )
#elif portBYTE_ALIGNMENT == 16
#define portBYTE_ALIGNMENT_MASK ( 0x000f )
#elif portBYTE_ALIGNMENT == 8
#define portBYTE_ALIGNMENT_MASK ( 0x0007 )
#elif portBYTE_ALIGNMENT == 4
#define portBYTE_ALIGNMENT_MASK ( 0x0003 )
#elif portBYTE_ALIGNMENT == 2
#define portBYTE_ALIGNMENT_MASK ( 0x0001 )
#elif portBYTE_ALIGNMENT == 1
#define portBYTE_ALIGNMENT_MASK ( 0x0000 )
#else
#error "Invalid portBYTE_ALIGNMENT definition"
#endif
// 58. MPU包装器使用标志
#ifndef portUSING_MPU_WRAPPERS
#define portUSING_MPU_WRAPPERS 0
#endif
// 59. 定义可配置的MPU(内存保护单元)区域数量
#ifndef portNUM_CONFIGURABLE_REGIONS
#define portNUM_CONFIGURABLE_REGIONS 1
#endif
// 60. 栈溢出检查标志,定义是否启用栈溢出检查功能
#ifndef portHAS_STACK_OVERFLOW_CHECKING
#define portHAS_STACK_OVERFLOW_CHECKING 0
#endif
// 61. 架构名称定义
#ifndef portARCH_NAME
#define portARCH_NAME NULL
#endif
// 62. 栈深度类型定义
#ifndef cfgSTACK_DEPTH_TYPE
#define cfgSTACK_DEPTH_TYPE StackType_t
#endif
// 63. 栈分配标志,定义是否从独立的堆中分配栈内存
#ifndef cfgSTACK_ALLOCATION_FROM_SEPARATE_HEAP
#define cfgSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0
#endif
// 64. 栈初始化函数声明,设置新任务的栈,使其准备好被调度器控制
#if ( portUSING_MPU_WRAPPERS == 1 )
#else
#if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
StackType_t * port_pu32InitStack(StackType_t * pu32TopOfStack,
StackType_t * pu32EndOfStack,
TaskFunc_t pfCode,
void * pvParameters );
#else
StackType_t * port_pu32InitStack(StackType_t * pu32TopOfStack,
TaskFunc_t pfCode,
void * pvParameters );
#endif
#endif
// 65. 堆区域结构体定义,用于heap_5.c定义构成FreeRTOS堆空间的每个内存区域的起始地址和大小
// 66. 堆统计信息结构体定义,用于从port_vGetHeapStats()传递堆的信息。
typedef struct xHeapStats{
size_t sAvailableHeapSpaceInBytes; /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */
size_t sSizeOfLargestFreeBlockInBytes; /* The maximum size, in bytes, of all the free blocks within the heap at the time port_vGetHeapStats() is called. */
size_t sSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time port_vGetHeapStats() is called. */
size_t sNumberOfFreeBlocks; /* The number of free memory blocks within the heap at the time port_vGetHeapStats() is called. */
size_t sMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */
size_t sNumberOfSuccessfulAllocations; /* The number of calls to port_pvMalloc() that have returned a valid memory block. */
size_t sNumberOfSuccessfulFrees; /* The number of calls to vPortFree() that has successfully freed a block of memory. */
} HeapStats_t;
// 67. 定义堆区域函数声明,用于heap_5.c定义多个堆区域
// 68. 获取堆统计信息函数声明,返回一个填充了当前堆状态信息的HeapStats_t结构体
void port_vGetHeapStats( HeapStats_t * pxHeapStats );
// 69. 内存分配函数声明,分配指定大小的内存块,返回指向分配内存的指针
void* port_pvMalloc( size_t sWantedSize );
// 70. 内存分配并清零函数声明:分配指定数量和大小的内存块,并将内存清零,返回指向分配内存的指针
void* port_pvCalloc( size_t sNum, size_t sSize );
// 71. 内存释放函数声明,释放之前分配的内存块,将其返回给堆
void port_vFree( void* pv );
// 72. 块初始化函数声明,初始化堆块
void port_vInitBlocks( void );
// 73. 获取空闲堆大小函数声明,返回堆中当前可用的空闲内存大小(字节)
size_t port_sGetFreeHeapSize( void );
// 74. 获取最小空闲堆大小函数声明,返回自系统启动以来堆中可用的最小空闲内存大小(字节)
size_t port_sGetMinEverFreeHeapSize( void );
#if ( cfgSTACK_ALLOCATION_FROM_SEPARATE_HEAP == 1 )
// 75. 栈内存分配函数声明,从独立的栈内存堆中分配指定大小的内存块
void * port_pvMallocStack( size_t sSize );
// 76. 栈内存释放函数声明,释放之前从独立栈内存堆中分配的内存块
void port_vFreeStack( void * pv );
#else
// 77. 栈内存分配宏定义,将栈内存分配函数映射到普通内存分配函数
#define port_pvMallocStack port_pvMalloc
// 78. 栈内存释放宏定义,将栈内存释放函数映射到普通内存释放函数
#define port_vFreeStack port_vFree
#endif
// 79. 堆状态重置函数声明,重置堆模块的内部状态,必须在重启调度器之前由应用程序调用
void port_vHeapResetState( void );
// 80. 内存分配失败钩子函数声明
#if ( cfgUSE_MALLOC_FAILED_HOOK == 1 )
#endif
// 81. 启动调度器函数声明,设置硬件,使调度器能够接管控制
BaseType_t port_lStartScheduler( void );
// 82. 停止调度器函数声明,撤销port_lStartScheduler()执行的任何硬件/ISR设置,使硬件在调度器停止执行后保持其原始状态
void port_vEndScheduler( void );
// 83. MPU任务设置函数声明
#if ( portUSING_MPU_WRAPPERS == 1 )
#endif
// 84. 缓冲区访问权限检查函数声明,检查调用任务是否有权访问给定的缓冲区
#if ( portUSING_MPU_WRAPPERS == 1 )
#endif
// 86. 内核对象访问权限检查函数声明,检查调用任务是否有权访问给定的内核对象
#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( cfgUSE_MPU_WRAPPERS_V1 == 0 ) )
#endif
// ------- 原 portable.h 中的定义 end -------
#ifdef __cplusplus
}
#endif
#endif // PORT_H
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)