参考:https://blog.csdn.net/kouxi1/article/details/123650688
RTOS本质就是切换线程栈,栈换了环境就换了,一个重要的结构tcb(linux叫PCB或thread_info):
struct tcb{
int32_t *sp; // 重要的sp指针,其它寄存器保存在栈中
struct tcb *next; // 下一个tcb
};
采用Valvano教授的RoundRobin_4C123源码一张图解释线程调度
两个重要文件os.c osasm.s
os.c
void StartOS(void);
#define NUMTHREADS 3 // maximum number of threads
#define STACKSIZE 100 // number of 32-bit words in stack
void SetInitialStack(int i){
tcbs[i].sp = &Stacks[i][STACKSIZE-16]; // thread stack pointer
//r0-r3 r12,LR XPSR发生中断时自动压栈
Stacks[i][STACKSIZE-1] = 0x01000000; // thumb bit xPSR
Stacks[i][STACKSIZE-3] = 0x14141414; // R14 LR
Stacks[i][STACKSIZE-4] = 0x12121212; // R12 ip
Stacks[i][STACKSIZE-5] = 0x03030303; // R3
Stacks[i][STACKSIZE-6] = 0x02020202; // R2
Stacks[i][STACKSIZE-7] = 0x01010101; // R1
Stacks[i][STACKSIZE-8] = 0x00000000; // R0
//r4-r11要求程序处理
Stacks[i][STACKSIZE-9] = 0x11111111; // R11
Stacks[i][STACKSIZE-10] = 0x10101010; // R10
Stacks[i][STACKSIZE-11] = 0x09090909; // R9
Stacks[i][STACKSIZE-12] = 0x08080808; // R8
Stacks[i][STACKSIZE-13] = 0x07070707; // R7
Stacks[i][STACKSIZE-14] = 0x06060606; // R6
Stacks[i][STACKSIZE-15] = 0x05050505; // R5
Stacks[i][STACKSIZE-16] = 0x04040404; // R4
}
void OS_Launch(uint32_t theTimeSlice){
STCTRL = 0; // disable SysTick during setup
STCURRENT = 0; // any write to current clears it
SYSPRI3 =(SYSPRI3&0x00FFFFFF)|0xE0000000; // priority 7
STRELOAD = theTimeSlice - 1; // reload value
STCTRL = 0x00000007; // enable, core clock and interrupt arm
StartOS(); // 第一次启动一个线程
}
void Scheduler(void){
RunPt = RunPt->next; // Round Robin
}
osasm.s
SysTick_Handler ; 自动保存 R0-R3,R12,LR,PC,PSR
CPSID I ; Prevent interrupt during switch
PUSH {R4-R11} ; 保存 r4-r11在马上成为旧线程的sp中
LDR R0, =RunPt ; R0=pointer to RunPt, old thread
LDR R1, [R0] ; R1 = 当前线程,也马上成为旧线程
STR SP, [R1] ; 保存马上成为旧线程的sp到TCB
PUSH {R0,LR} ; Scheduler会改变r0, LR,所以先保存(发生中断时LR=0xfffffff9,以 后要通过它触发中断返回)
BL Scheduler 找到下一个新线程
POP {R0,LR} 恢复r0, LR,
LDR R1, [R0] ; R1 = 新线程
LDR SP, [R1] ; 新的线程的 SP = RunPt->sp;
POP {R4-R11} ; 恢复r4-r11从新的线程的sp中
CPSIE I ; 9) tasks run with interrupts enabled
BX LR ; 10) LR=0xfffffff9触发中断返回,恢复 R0-R3,R12,LR,PC,PSR
StartOS
//R0=pointer to RunPt, old thread伪指令,调试时发现r0=0x0000044
//runPt=0x0000054 而且0x0000054中的内容为tcb[0]
LDR R0, =RunPt ; currently running thread
LDR R2, [R0] ; R2 = value of RunPt
LDR SP, [R2] ; new thread SP; SP = RunPt->stackPointer;
POP {R4-R11} ; 恢复 regs r4-11
POP {R0-R3} ; 恢复 regs r0-3
POP {R12}
ADD SP,SP,#4 ; 第一次,不需要返回,丢弃LR
POP {LR} ; 把task()函数地址推送给LR
ADD SP,SP,#4 ;第一次,丢弃 PSR
CPSIE I ; Enable interrupts at processor level
BX LR ; start first thread
ALIGN
END