内核-内核移植

目录

1、简介

2、CPU 架构移植

2.1、libcpu抽象层

2.1.1、CPU 架构移植需要实现的接口和变量

2.2、实现全局中断开关

2.2.1、rt_hw_interrupt_disable()函数

2.2.2、rt_hw_interrupt_enable()函数

2.3、实现线程栈初始化

2.4、实现上下文切换

2.4.1、rt_hw_context_switch_to()函数

2.4.2、rt_hw_context_switch()函数

2.4.3、rt_hw_context_switch_interrupt ()函数

2.5、实现时钟节拍

3、BSP移植

3.1、BSP 抽象层

4、Cortex-M 架构移植示例

4.1、中断

4.1.1、中断向量表

4.2、全局中断开关实现

4.2.1、关闭全局中断

4.2.2、打开全局中断

4.3、线程栈初始化实现

4.3.1、寄存器

4.3.2、堆栈指针

4.3.3、rt_hw_stack_init()函数实现

4.3.4、向下生长的栈结构

4.4、上下文切换实现

4.4.1、操作模式和特权级别

4.4.2、线程之间的上下文切换

4.4.3、中断到线程的上下文切换

4.4.4、实现 rt_hw_context_switch_to()函数

4.4.5、实现 rt_hw_context_switch()/ rt_hw_context_switch_interrupt()函数

4.5、实现 PendSV 中断

4.6、实现时钟节拍

4.7、栈切换结构图示

4.7.1、运行第一个线程

 4.7.2、线程间切换


1、简介

内核移植就是指将 RT-Thread 内核在不同的芯片架构、不同的板卡上运行起来,能够具备线程管理和调度,内存管理,线程间同步和通信、定时器管理等功能。移植可分为CPU架构移植和BSP移植两部分:

1)CPU架构移植:此部分不需要用户移植(芯片原厂移植)。

2)BSP移植:官方会提供开发板的BSP,开发时可以参考官方提供开发板的BSP。

2、CPU 架构移植

在嵌入式领域有多种不同 CPU 架构,例如 Cortex-M、ARM920T、MIPS32、RISC-V 等等。为了使 RT-Thread 能够在不同 CPU 架构的芯片上运行,RT-Thread 提供了一个 libcpu 抽象层来适配不同的 CPU 架构。

2.1、libcpu抽象层

1)RT-Thread 的 libcpu 抽象层向上对内核提供统一的接口,包括全局中断的开关,线程栈的初始化,上下文切换等。

2)RT-Thread 的 libcpu 抽象层向下提供了一套统一的 CPU 架构移植接口,这部分接口包含了全局中断开关函数、线程上下文切换函数、时钟节拍的配置和中断函数、Cache 等等内容。

2.1.1、CPU 架构移植需要实现的接口和变量

函数和变量 描述
rt_base_t rt_hw_interrupt_disable(void); 关闭全局中断
void rt_hw_interrupt_enable(rt_base_t level); 打开全局中断
rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, rt_uint8_t *stack_addr, void *texit); 线程栈的初始化。内核在线程创建和线程初始化里面会调用这个函数
void rt_hw_context_switch_to(rt_uint32_t to); 没有来源线程的上下文切换。在调度器启动第一个线程的时候调用,以及在signal里面会调用
void rt_hw_context_switch(rt_uint32_t from, rt_uint32_t to); 从 from 线程切换到 to 线程,用于线程和线程之间的切换
void rt_hw_context_switch_interrupt(rt_uint32_t from, rt_uint32_t to); 从 from 线程切换到 to 线程,用于中断里面进行切换的时候使用
rt_uint32_t rt_thread_switch_interrupt_flag; 表示需要在中断里进行切换的标志
rt_uint32_t rt_interrupt_from_thread, rt_interrupt_to_thread; 在线程进行上下文切换时候,用来保存 from 和 to 线程的sp

2.2、实现全局中断开关

无论内核代码还是用户的代码,都可能存在一些变量,需要在多个线程或者中断里面使用,如果没有相应的保护机制,那就可能导致临界区问题。

RT-Thread 里为了解决这个问题,提供了一系列的线程间同步和通信机制来解决。但是这些机制都需要用到 libcpu 里提供的全局中断开关函数。

/* 关闭全局中断 */
rt_base_t rt_hw_interrupt_disable(void);

/* 打开全局中断 */
void rt_hw_interrupt_enable(rt_base_t level);

2.2.1、rt_hw_interrupt_disable()函数

在 rt_hw_interrupt_disable() 函数里面需要依序完成的功能是:

1)保存当前的全局中断状态,并把状态作为函数的返回值。

2)关闭全局中断。

2.2.2、rt_hw_interrupt_enable()函数

在 rt_hw_interrupt_enable(rt_base_t level) 里,将变量 level 作为需要恢复的状态,覆盖芯片的全局中断状态。

2.3、实现线程栈初始化

在动态创建线程和初始化线程的时候,调用栈初始化函数 rt_hw_stack_init(),手动构造一个上下文内容,这个上下文内容将被作为每个线程第一次执行的初始值。

2.4、实现上下文切换

在不同的 CPU 架构里,线程之间的上下文切换和中断到线程的上下文切换,上下文的寄存器部分可能是有差异的,也可能是一样的。

在线程环境下进行切换和在中断环境进行切换是存在差异的。线程环境下,如果调用 rt_hw_context_switch() 函数,那么可以马上进行上下文切换;而在中断环境下,需要等待中断处理函数完成之后才能进行切换。

在中断处理程序里如果触发了线程的调度,调度函数里会调用 rt_hw_context_switch_interrupt() 触发上下文切换。中断处理程序里处理完中断事务之后,中断退出之前,检查 rt_thread_switch_interrupt_flag 变量,如果该变量的值为 1,则根据 rt_interrupt_from_thread 变量和 rt_interrupt_to_thread 变量,完成线程的上下文切换。

2.4.1、rt_hw_context_switch_to()函数

没有来源线程,切换到目标线程。在调度器启动第一个线程的时候被调用。

void rt_hw_context_switch_to(rt_ubase_t to, struct rt_thread *to_thread);

2.4.2、rt_hw_context_switch()函数

在线程环境下,从当前线程切换到目标线程。

2.4.3、rt_hw_context_switch_interrupt ()函数

在中断环境下,从当前线程切换到目标线程。

2.5、实现时钟节拍

有了开关全局中断和上下文切换功能的基础,RTOS 就可以进行线程的创建、运行、调度等功能了。有了时钟节拍支持,RT-Thread 可以实现对相同优先级的线程采用时间片轮转的方式来调度,实现定时器功能,实现 rt_thread_delay() 延时函数等等。

注:libcpu的移植需要完成的工作,就是确保 rt_tick_increase()函数会在时钟节拍的中断里被周期性的调用,调用周期取决于 rtconfig.h 的宏 RT_TICK_PER_SECOND 的值。

3、BSP移植

相同的CPU架构在实际项目中,不同的板卡上可能使用相同的 CPU 架构,搭载不同的外设资源,完成不同的产品,所以也需要针对板卡做适配工作。

3.1、BSP 抽象层

RT-Thread 提供了 BSP 抽象层来适配常见的板卡。如果希望在一个板卡上使用 RT-Thread 内核,除了需要有相应的芯片架构的移植,还需要有针对板卡的移植,也就是实现一个基本的 BSP。主要任务是建立让操作系统运行的基本环境,需要完成的主要工作是:

1)初始化 CPU 内部寄存器,设定 RAM 工作时序。

2)实现时钟驱动及中断控制器驱动,完善中断管理。

3)实现串口和 GPIO 驱动。

4)初始化动态内存堆,实现动态堆内存管理。

4、Cortex-M 架构移植示例

4.1、中断

4.1.1、中断向量表

中断向量表是所有中断处理程序的入口,如下图所示是 Cortex-M 系列的中断处理过程:把一个函数(用户中断服务程序)同一个虚拟中断向量表中的中断向量联系在一起。当中断向量对应中断发生的时候,被挂接的用户中断服务程序就会被调用执行。

内核-内核移植_第1张图片

在 Cortex-M 内核上,所有中断都采用中断向量表的方式进行处理,即当一个中断触发时,处理器将直接判定是哪个中断源,然后直接跳转到相应的固定位置进行处理,每个中断服务程序必须排列在一起放在统一的地址上(这个地址必须要设置到 NVIC 的中断向量偏移寄存器中)。中断向量表一般由一个数组定义或在起始代码中给出,如: 

  __Vectors     DCD     __initial_sp             ; Top of Stack
                DCD     Reset_Handler            ; Reset 处理函数
                DCD     NMI_Handler              ; NMI 处理函数
                DCD     HardFault_Handler        ; Hard Fault 处理函数
                DCD     MemManage_Handler        ; MPU Fault 处理函数
                DCD     BusFault_Handler         ; Bus Fault 处理函数
                DCD     UsageFault_Handler       ; Usage Fault 处理函数
                DCD     0                        ; 保留
                DCD     0                        ; 保留
                DCD     0                        ; 保留
                DCD     0                        ; 保留
                DCD     SVC_Handler              ; SVCall 处理函数
                DCD     DebugMon_Handler         ; Debug Monitor 处理函数
                DCD     0                        ; 保留
                DCD     PendSV_Handler           ; PendSV 处理函数
                DCD     SysTick_Handler          ; SysTick 处理函数

… …

NMI_Handler             PROC
                EXPORT NMI_Handler              [WEAK]
                B       .
                ENDP
HardFault_Handler       PROC
                EXPORT HardFault_Handler        [WEAK]
                B       .
                ENDP
… …

[WEAK] 标识弱符号。在 [WEAK] 前面的符号(如 NMI_Handler)将被执行弱化处理,如果整个代码在链接时遇到了名称相同的符号,那么代码将使用未被弱化定义的符号,而与弱符号相关的代码将被自动丢弃。

注:向量表存放的第一个元素为栈顶地址!

4.2、全局中断开关实现

Cortex-M 为了快速开关中断,实现了 CPS 指令。

CPSID I ;PRIMASK=1, ; 关中断
CPSIE I ;PRIMASK=0, ; 开中断

注:PRIMASK:1 bit寄存器。当该bit置位时,允许NMI和硬件故障异常;所有其他中断和异常都被屏蔽。默认值是0,这意味着没有设置屏蔽。

4.2.1、关闭全局中断

基于 MDK,在 Cortex-M 内核上实现关闭全局中断,代码如下所示:

;/*
; * rt_base_t rt_hw_interrupt_disable(void);
; */
rt_hw_interrupt_disable    PROC      ;PROC 伪指令定义函数
    EXPORT  rt_hw_interrupt_disable  ;EXPORT 输出定义的函数,类似于 C 语言 extern
    MRS     r0, PRIMASK              ; 读取 PRIMASK 寄存器的值到 r0 寄存器
    CPSID   I                        ; 关闭全局中断
    BX      LR                       ; 函数返回
    ENDP                             ;ENDP 函数结束

1)使用 MRS 指令将 PRIMASK 寄存器的值保存到 r0 寄存器里。

2)使用 “CPSID I” 指令关闭全局中断。

3)使用 BX 指令返回(r0 存储的数据就是函数的返回值)。

注:关于寄存器在函数调用的时候和在中断处理程序里是如何管理的,不同的 CPU 架构有不同的约定。在 ARM 官方手册《Procedure Call Standard for the ARM ® Architecture》里可以找到关于 Cortex-M 的更详细的介绍寄存器使用的约定。

4.2.2、打开全局中断

基于 MDK,在 Cortex-M 内核上的实现打开全局中断,代码如下所示:

;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable    PROC      ; PROC 伪指令定义函数
    EXPORT  rt_hw_interrupt_enable  ; EXPORT 输出定义的函数,类似于 C 语言 extern
    MSR     PRIMASK, r0             ; 将 r0 寄存器的值写入到 PRIMASK 寄存器
    BX      LR                      ; 函数返回
    ENDP                            ; ENDP 函数结束

1)使用 MSR 指令将 r0 的值寄存器写入到 PRIMASK 寄存器,从而恢复之前的中断状态。

2)使用 BX 指令返回

4.3、线程栈初始化实现

4.3.1、寄存器

Cortex-M 系列 CPU 的寄存器组里有 R0~R15 共 16 个通用寄存器组和若干特殊功能寄存器。

1)通用寄存器组里的 R13 作为堆栈指针寄存器 (Stack Pointer,SP);R14 作为连接寄存器 (Link Register,LR),用于在调用子程序时,存储返回地址;R15 作为程序计数器 (Program Counter,PC),其中堆栈指针寄存器可以是主堆栈指针(MSP),也可以是进程堆栈指针(PSP)。

2)程序状态字寄存器里保存算术与逻辑标志,例如负数标志,零结果标志,溢出标志等等。

3)中断屏蔽寄存器组控制 Cortex-M 的中断除能。

4)控制寄存器用来定义特权级别和当前使用哪个堆栈指针。

5)如果是具有浮点单元的 Cortex-M4 或者 Cortex-M7,控制寄存器也用来指示浮点单元当前是否在使用,浮点单元包含了 32 个浮点通用寄存器 S0~S31 和特殊 FPSCR 寄存器(Floating point status and control register)。

内核-内核移植_第2张图片

4.3.2、堆栈指针

在 Cortex-M 处理器内核里有两个堆栈指针:

1)一个是主堆栈指针(MSP),是默认的堆栈指针,在运行第一个线程之前和在中断和异常服务程序里使用;

2)另一个是线程堆栈指针(PSP),在线程里使用。在中断和异常服务程序退出时,修改 LR 寄存器的第 2 位的值为 1,线程的 SP 就由 MSP 切换到 PSP。

4.3.3、rt_hw_stack_init()函数实现

rt_uint8_t *rt_hw_stack_init(void       *tentry,
                             void       *parameter,
                             rt_uint8_t *stack_addr,
                             void       *texit)
{
    struct stack_frame *stack_frame;
    rt_uint8_t         *stk;
    unsigned long       i;

    /* 对传入的栈指针做对齐处理 */
    stk  = stack_addr + sizeof(rt_uint32_t);
    stk  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
    stk -= sizeof(struct stack_frame);

    /* 得到上下文的栈帧的指针 */
    stack_frame = (struct stack_frame *)stk;

    /* 把所有寄存器的默认值设置为 0xdeadbeef */
    for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
    {
        ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
    }

    /* 根据 ARM  APCS 调用标准,将第一个参数保存在 r0 寄存器 */
    stack_frame->exception_stack_frame.r0  = (unsigned long)parameter;
    /* 将剩下的参数寄存器都设置为 0 */
    stack_frame->exception_stack_frame.r1  = 0;                 /* r1 寄存器 */
    stack_frame->exception_stack_frame.r2  = 0;                 /* r2 寄存器 */
    stack_frame->exception_stack_frame.r3  = 0;                 /* r3 寄存器 */
    /* 将 IP(Intra-Procedure-call scratch register.) 设置为 0 */
    stack_frame->exception_stack_frame.r12 = 0;                 /* r12 寄存器 */
    /* 将线程退出函数的地址保存在 lr 寄存器 */
    stack_frame->exception_stack_frame.lr  = (unsigned long)texit;
    /* 将线程入口函数的地址保存在 pc 寄存器 */
    stack_frame->exception_stack_frame.pc  = (unsigned long)tentry;
    /* 设置 psr 的值为 0x01000000L,表示默认切换过去是 Thumb 模式 */
    stack_frame->exception_stack_frame.psr = 0x01000000L;

    /* 返回当前线程的栈地址       */
    return stk;
}

4.3.4、向下生长的栈结构

struct exception_stack_frame
{
    rt_uint32_t r0;
    rt_uint32_t r1;
    rt_uint32_t r2;
    rt_uint32_t r3;
    rt_uint32_t r12;
    rt_uint32_t lr;
    rt_uint32_t pc;
    rt_uint32_t psr;
};

struct stack_frame
{
    /* r4 ~ r11 register */
    rt_uint32_t r4;
    rt_uint32_t r5;
    rt_uint32_t r6;
    rt_uint32_t r7;
    rt_uint32_t r8;
    rt_uint32_t r9;
    rt_uint32_t r10;
    rt_uint32_t r11;

    struct exception_stack_frame exception_stack_frame;
};

 内核-内核移植_第3张图片

 

注:CPU架构小端,故PSR位于栈顶而非R4!

4.4、上下文切换实现

在Cortex-M 里面上下文切换都是统一使用 PendSV 异常来完成,切换部分并没有差异(线程之间的上下文切换和中断到线程的上下文切换)。

注:PendSV 也称为可悬起的系统调用,它是一种异常,可以像普通的中断一样被挂起,它是专门用来辅助操作系统进行上下文切换的。PendSV 异常会被初始化为最低优先级的异常。每次需要进行上下文切换的时候,会手动触发 PendSV 异常,在 PendSV 异常处理函数中进行上下文切换。

4.4.1、操作模式和特权级别

Cortex-M 引入了操作模式和特权级别的概念。

1)如果进入异常或中断处理则进入处理模式,其他情况则为线程模式。

2)Cortex-M 有两个运行级别,分别为特权级和用户级。线程模式可以工作在特权级或者用户级,而处理模式总工作在特权级,可通过 CONTROL 特殊寄存器控制。

特权级 用户级
线程模式
处理模式 /

Cortex-M 的堆栈寄存器 SP 对应两个物理寄存器 MSP 和 PSP,MSP 为主堆栈,PSP 为进程堆栈,处理模式总是使用 MSP 作为堆栈,线程模式可以选择使用 MSP 或 PSP 作为堆栈,同样通过 CONTROL 特殊寄存器控制。

内核-内核移植_第4张图片

注:复位后,Cortex-M 默认进入线程模式、特权级、使用 MSP 堆栈。

4.4.2、线程之间的上下文切换

硬件在进入 PendSV 中断之前自动保存了 from 线程的 PSR、PC、LR、R12、R3-R0 寄存器,然后 PendSV 里保存 from 线程的 R11~R4 寄存器,以及恢复 to 线程的 R4~R11 寄存器,最后硬件在退出 PendSV 中断之后,自动恢复 to 线程的 R0~R3、R12、LR、PC、PSR 寄存器。

内核-内核移植_第5张图片

4.4.3、中断到线程的上下文切换

硬件在进入中断之前自动保存了 from 线程的 PSR、PC、LR、R12、R3-R0 寄存器,然后触发了 PendSV 异常。在 PendSV 异常处理函数里保存 from 线程的 R11~R4 寄存器,以及恢复 to 线程的 R4~R11 寄存器,最后硬件在退出 PendSV 中断之后,自动恢复 to 线程的 R0~R3、R12、PSR、PC、LR 寄存器。

内核-内核移植_第6张图片

4.4.4、实现 rt_hw_context_switch_to()函数

内核-内核移植_第7张图片

在 Cortex-M3 内核上的 rt_hw_context_switch_to() 实现(基于 MDK),代码如下所示:

;/*
; * void rt_hw_context_switch_to(rt_uint32_t to);
; * r0 --> to
; * this fucntion is used to perform the first thread switch
; */
rt_hw_context_switch_to    PROC
    EXPORT rt_hw_context_switch_to
    ; r0 的值是一个指针,该指针指向 to 线程的线程控制块的 SP 成员
    ; 将 r0 寄存器的值保存到 rt_interrupt_to_thread 变量里
    LDR     r1, =rt_interrupt_to_thread
    STR     r0, [r1]

    ; 设置 from 线程为空,表示不需要从保存 from 的上下文
    LDR     r1, =rt_interrupt_from_thread
    MOV     r0, #0x0
    STR     r0, [r1]

    ; 设置标志为 1,表示需要切换,这个变量将在 PendSV 异常处理函数里切换的时被清零
    LDR     r1, =rt_thread_switch_interrupt_flag
    MOV     r0, #1
    STR     r0, [r1]

    ; 设置 PendSV 异常优先级为最低优先级
    LDR     r0, =NVIC_SYSPRI2
    LDR     r1, =NVIC_PENDSV_PRI
    LDR.W   r2, [r0,#0x00]       ; read
    ORR     r1,r1,r2             ; modify
    STR     r1, [r0]             ; write-back

    ; 触发 PendSV 异常 (将执行 PendSV 异常处理程序)
    LDR     r0, =NVIC_INT_CTRL
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]

    ; 将 MSP 设置启动时的值(向量表放的第一个元素是sp)
    LDR     r0, =SCB_VTOR
    LDR     r0, [r0]
    LDR     r0, [r0]
    MSR     msp, r0

    ; 使能全局中断和全局异常,使能之后将进入 PendSV 异常处理函数
    CPSIE   F
    CPSIE   I

    ; 不会执行到这里
    ENDP

注:此汇编代码会设置 PendSV 异常优先级为最低优先级,并将MSP设置为SCB_VTOR(向量表放的第一个元素是sp)

4.4.5、实现 rt_hw_context_switch()/ rt_hw_context_switch_interrupt()函数

内核-内核移植_第8张图片

在 Cortex-M3 内核上的 rt_hw_context_switch() 和 rt_hw_context_switch_interrupt() 实现(基于 MDK),代码如下所示:

;/*
; * void rt_hw_context_switch(rt_uint32_t from, rt_uint32_t to);
; * r0 --> from
; * r1 --> to
; */
rt_hw_context_switch_interrupt
    EXPORT rt_hw_context_switch_interrupt
rt_hw_context_switch    PROC
    EXPORT rt_hw_context_switch

    ; 检查 rt_thread_switch_interrupt_flag 变量是否为 1
    ; 如果变量为 1 就跳过更新 from 线程的内容
    LDR     r2, =rt_thread_switch_interrupt_flag
    LDR     r3, [r2]
    CMP     r3, #1
    BEQ     _reswitch
    ; 设置 rt_thread_switch_interrupt_flag 变量为 1
    MOV     r3, #1
    STR     r3, [r2]

    ; 从参数 r0 里更新 rt_interrupt_from_thread 变量
    LDR     r2, =rt_interrupt_from_thread
    STR     r0, [r2]

_reswitch
    ; 从参数 r1 里更新 rt_interrupt_to_thread 变量
    LDR     r2, =rt_interrupt_to_thread
    STR     r1, [r2]

    ; 触发 PendSV 异常,将进入 PendSV 异常处理函数里完成上下文切换
    LDR     r0, =NVIC_INT_CTRL
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]
    BX      LR

4.5、实现 PendSV 中断

在 Cortex-M3 里,PendSV 中断处理函数是 PendSV_Handler()。在 PendSV_Handler() 里完成线程切换的实际工作,下图是具体的流程图:

内核-内核移植_第9张图片

; r0 --> switch from thread stack
; r1 --> switch to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
PendSV_Handler   PROC
    EXPORT PendSV_Handler

    ; 关闭全局中断
    MRS     r2, PRIMASK
    CPSID   I

    ; 检查 rt_thread_switch_interrupt_flag 变量是否为 0
    ; 如果为零就跳转到 pendsv_exit
    LDR     r0, =rt_thread_switch_interrupt_flag
    LDR     r1, [r0]
    CBZ     r1, pendsv_exit         ; pendsv already handled

    ; 清零 rt_thread_switch_interrupt_flag 变量
    MOV     r1, #0x00
    STR     r1, [r0]

    ; 检查 rt_thread_switch_interrupt_flag 变量
    ; 如果为 0,就不进行 from 线程的上下文保存
    LDR     r0, =rt_interrupt_from_thread
    LDR     r1, [r0]
    CBZ     r1, switch_to_thread

    ; 保存 from 线程的上下文
    MRS     r1, psp                 ; 获取 from 线程的栈指针
    STMFD   r1!, {r4 - r11}       ; 将 r4~r11 保存到线程的栈里
    LDR     r0, [r0]
    STR     r1, [r0]                ; 更新线程的控制块的 SP 指针

switch_to_thread
    LDR     r1, =rt_interrupt_to_thread
    LDR     r1, [r1]
    LDR     r1, [r1]                ; 获取 to 线程的栈指针

    LDMFD   r1!, {r4 - r11}       ; 从 to 线程的栈里恢复 to 线程的寄存器值
    MSR     psp, r1                 ; 更新 r1 的值到 psp

pendsv_exit
    ; 恢复全局中断状态
    MSR     PRIMASK, r2

    ; 修改 lr 寄存器的 bit2,确保进程使用 PSP 堆栈指针
    ORR     lr, lr, #0x04
    ; 退出中断函数
    BX      lr
    ENDP

注:进入PendSV中断时使用的是MSP,在退出时要切换回PSP。

4.6、实现时钟节拍

在 Cortex M 中,实现 SysTick 的中断处理函数即可实现时钟节拍功能。

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

4.7、栈切换结构图示

4.7.1、运行第一个线程

内核-内核移植_第10张图片

 4.7.2、线程间切换

 内核-内核移植_第11张图片

 

你可能感兴趣的:(RT-Thread,rtt,内核,移植)