• RTT学习笔记8-RTT内核移植


    RTT 内核移植接口

    在这里插入图片描述

    CortexM系列的内核移植

    1.关闭中断

    ;/*
    ; * 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.打开中断

    ;/*
    ; * 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.线程栈初始化

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    3. 实现上下文切换

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

    2) rt_hw_context_switch():在线程环境下,从当前线程切换到目标线程。

    3) rt_hw_context_switch_interrupt ():在中断环境下,从当前线程切换到目标线程。

    • 在线程环境下,可以马上进行上下文切换;
    • 而在中断环境下,需要等待中断处理函数完成之后才能进行切换。

    3.1在 Cortex-M 上下文切换

    线程A运行过程中产生PendSV中断,进入中断函数之前硬件保存当前线程的 PSR、PC、LR、R12、R3-R0 ,进入中断函数后软件/手动保存 R11~R4 寄存器,接下来恢复线程B的R11~R4 寄存器,退出中断,硬件完成线程B的 R0~R3、R12、LR、PC、PSR 寄存器
    在这里插入图片描述

    3.2只有目标线程,没有来源线程的线程切换

    rt_hw_context_switch_to()
    在这里插入图片描述

    ;/*
    ; * 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 设置启动时的值
        LDR     r0, =SCB_VTOR
        LDR     r0, [r0]
        LDR     r0, [r0]
        MSR     msp, r0
    
        ; 使能全局中断和全局异常,使能之后将进入 PendSV 异常处理函数
        CPSIE   F
        CPSIE   I
    
        ; 不会执行到这里
        ENDP
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    3.2 从一个线程到另外一个线程的切换和中断内部的线程切换

    rt_hw_context_switch()/ rt_hw_context_switch_interrupt()

    ;/*
    ; * 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    4.实现时钟节拍

    void SysTick_Handler(void)
    {
        /* enter interrupt */
        rt_interrupt_enter();
    
        rt_tick_increase();
    
        /* leave interrupt */
        rt_interrupt_leave();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    5种常用格式的数据输出,手把手教你用Pandas实现
    Office文件在线预览大全-Word文档在线预览的实现方法-OFD文档在线预览-WPS文件在线预览
    Flutter-无限循环滚动标签
    「废话少说,放码过来」:博客园2024夏季T恤上架预售
    Java 基础 - 面向对象
    汉服新闻查询易语言代码
    1-2Java程序运行机制以及运行过程
    一文搞懂JavaScript数组的特性
    从0到一开发微信小程序(3)—小程序框架配置
    杰理之立体声输入【篇】
  • 原文地址:https://blog.csdn.net/u010261063/article/details/126193160