1. 系统级术语和操作概述

下载并查看内核参考手册:DDI0403E_e_armv7m_arm.pdf,查看系统级程序员模型(System Level Programmers’ Model),以下讲解相关基础知识:

1.1 模式、特权和栈

1.1.1 模式

ARMv7-M 仅支持 两种操作模式:

模式

定义

触发条件

关键特性

Thread mode

系统正常运行模式

1. 复位后首次进入
2. 异常返回(如中断处理结束)

- 可以是 特权非特权
- 用于执行用户程序或操作系统内核

Handler mode

异常处理模式

1. 发生异常(如中断、系统调用)
2. 执行异常返回指令(

- 必须 用此模式触发异常返回
- 总是特权执行

  • 异常处理流程:Thread mode → (异常触发) → Handler mode → (异常返回) → Thread mode

1.1.2 特权

特权级别控制 代码对系统资源的访问权限

特权级别

定义

访问权限

关键规则

Privileged(特权)

具有完全系统访问权

可访问 所有资源(如控制寄存器、内存保护单元)

- Handler mode 总是特权
- Thread 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

- 异常处理
- 特权 Thread mode

- Handler mode 必须使用 MSP
- 特权 Thread mode 可选

PSP

Process Stack Pointer

- 非特权 Thread mode(用户任务)
- 多任务栈隔离

- 仅 Thread mode 可用
- 非特权任务专用

  • 异常处理稳定性:Handler mode 使用 MSP,确保中断处理时栈空间独立,避免用户任务栈溢出影响异常处理。
  • 多任务支持:每个用户任务可分配独立的 PSP,实现栈隔离(类似操作系统中的任务栈)。

模式

栈指针选择

说明

Handler mode

强制使用 MSP

异常处理必须有统一栈空间

Thread mode

可选 MSP 或 PSP

- 内核任务 → MSP
- 用户任务 → PSP

典型流程

  1. 复位 → Thread mode(特权)执行启动代码 → 使用 MSP
  2. 切换到用户任务 → Thread mode(非特权)执行 main → 切换到 PSP
  3. 中断发生 → Handler mode(默认特权) → 自动切换回 MSP
  4. 中断结束 → Thread mode(非特权)返回用户任务 main → 切换回 PSP

1.2 异常  Exceptions

1.2.1 异常的定义

异常是 改变程序正常执行流的事件,ARMv7-M 把异常分成 两个不可分割的阶段:

阶段

何时发生

发生了什么

为什么重要

1. 异常生成(Exception generation)

异常事件发生时
(例如:按下按钮触发中断)

- 事件被“提交”给处理器
- 系统标记异常为 Pending(挂起)

硬件自动完成
(你无法控制)
→ 确保异常不被忽略

2. 异常处理/激活(Exception processing)

处理器决定处理异常时
(例如:红灯变绿后开始处理)

- 1. 保存当前程序状态(自动压栈)
- 2. 切换到 Handler mode
- 3. 执行异常处理函数
- 4. 异常返回(恢复原程序)

软件可干预
(你可以写处理函数)
→ 决定

中断属于异常的一部分。

  • 从 生成 → 处理 的过渡是 瞬间的(硬件自动触发)。
  • 你只能控制 处理阶段(写异常处理函数),但不能控制 生成阶段(硬件决定何时发生)。

1.2.2 ARMv7-M 支持的 4 类异常

异常类型

触发方式

典型场景

详解

Reset

复位信号(如上电)

系统启动

- 特殊异常:强制终止当前执行
- 取消复位后:从固定地址(如 0x0000_0000)重新开始

Supervisor Call (SVC)

执行 SVC #n 指令

用户程序调用操作系统

- 软件显式触发(例如:SVC #0 请求分配内存)
- 用途:让非特权代码安全访问特权资源

Fault

指令执行出错

内存访问错误、除零错误

- 同步异常:与指令执行直接相关
- 分类
• BusFault:总线错误(如访问无效地址)
• UsageFault:指令错误(如未对齐访问)
- 同步 vs 异步
• 同步 Fault:精确知道出错指令
• 异步 Fault:无法精确定位(如 DebugMonitor)

Interrupt

外部信号(如定时器中断)

按键中断、串口接收数据

- 异步异常:与当前指令流无关
- 典型:PendSV(用于任务切换)、SysTick(系统滴答定时器)

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)发生。
  • 硬件自动执行
    1. 保存低优先级异常的现场(压栈)
    2. 切换到高优先级异常处理程序
    3. 处理完高优先级异常后,自动恢复低优先级异常
  • 结论:
  1. 高优先级异常永远能抢占低优先级异常,
  2. 但 低优先级不能抢占高优先级(这是硬件强制的!)。

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)

❌ 无

汇编指令:
MOV, ADD, LDR/ST(仅用于数据传输)

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 等

控制中断屏蔽:
• PRIMASK:关全局中断
• FAULTMASK:关所有异常(除 NMI/Reset)
• BASEPRI:按优先级屏蔽

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、栈指针调整就是编译器自动生成的上下文管理代码。

    底层系统编程场景中,我们不希望编译器动堆栈或寄存器,因为:

    1. 我们自己完全控制上下文(如中断服务例程 ISR、任务切换代码);
    2. 函数入口/出口必须严格符合硬件要求(如异常返回前不能破坏寄存器);
    3. 需要直接写纯汇编逻辑,不能被编译器污染。

    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
    
    Logo

    openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

    更多推荐