RTT 内核移植接口
CortexM系列的内核移植
1.关闭中断
;/*
; * rt_base_t rt_hw_interrupt_disable(void);
; */
rt_hw_interrupt_disable PROC ;PROC 伪指令定义函数EXPORT rt_hw_interrupt_disable ;EXPORT 输出定义的函数,类似于 C 语言 externMRS r0, PRIMASK ; 读取 PRIMASK 寄存器的值到 r0 寄存器CPSID I ; 关闭全局中断BX LR ; 函数返回ENDP ;ENDP 函数结束
2.打开中断
;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable PROC ; PROC 伪指令定义函数EXPORT rt_hw_interrupt_enable ; EXPORT 输出定义的函数,类似于 C 语言 externMSR PRIMASK, r0 ; 将 r0 寄存器的值写入到 PRIMASK 寄存器BX LR ; 函数返回ENDP ; ENDP 函数结束
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;
}
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 PROCEXPORT rt_hw_context_switch_to; r0 的值是一个指针,该指针指向 to 线程的线程控制块的 SP 成员; 将 r0 寄存器的值保存到 rt_interrupt_to_thread 变量里LDR r1, =rt_interrupt_to_threadSTR r0, [r1]; 设置 from 线程为空,表示不需要从保存 from 的上下文LDR r1, =rt_interrupt_from_threadMOV r0, #0x0STR r0, [r1]; 设置标志为 1,表示需要切换,这个变量将在 PendSV 异常处理函数里切换的时被清零LDR r1, =rt_thread_switch_interrupt_flagMOV r0, #1STR r0, [r1]; 设置 PendSV 异常优先级为最低优先级LDR r0, =NVIC_SYSPRI2LDR r1, =NVIC_PENDSV_PRILDR.W r2, [r0,#0x00] ; readORR r1,r1,r2 ; modifySTR r1, [r0] ; write-back; 触发 PendSV 异常 (将执行 PendSV 异常处理程序)LDR r0, =NVIC_INT_CTRLLDR r1, =NVIC_PENDSVSETSTR r1, [r0]; 放弃芯片启动到第一次上下文切换之前的栈内容,将 MSP 设置启动时的值LDR r0, =SCB_VTORLDR r0, [r0]LDR r0, [r0]MSR msp, r0; 使能全局中断和全局异常,使能之后将进入 PendSV 异常处理函数CPSIE FCPSIE I; 不会执行到这里ENDP
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_interruptEXPORT rt_hw_context_switch_interrupt
rt_hw_context_switch PROCEXPORT rt_hw_context_switch; 检查 rt_thread_switch_interrupt_flag 变量是否为 1; 如果变量为 1 就跳过更新 from 线程的内容LDR r2, =rt_thread_switch_interrupt_flagLDR r3, [r2]CMP r3, #1BEQ _reswitch; 设置 rt_thread_switch_interrupt_flag 变量为 1MOV r3, #1STR r3, [r2]; 从参数 r0 里更新 rt_interrupt_from_thread 变量LDR r2, =rt_interrupt_from_threadSTR r0, [r2]_reswitch; 从参数 r1 里更新 rt_interrupt_to_thread 变量LDR r2, =rt_interrupt_to_threadSTR r1, [r2]; 触发 PendSV 异常,将进入 PendSV 异常处理函数里完成上下文切换LDR r0, =NVIC_INT_CTRLLDR r1, =NVIC_PENDSVSETSTR r1, [r0]BX LR
4.实现时钟节拍
void SysTick_Handler(void)
{/* enter interrupt */rt_interrupt_enter();rt_tick_increase();/* leave interrupt */rt_interrupt_leave();
}