设计一个简易的多任务 rtos 4 - 实现一个可以运行多任务的简易rtos
1.创建任务
创建任务的主要工作是对任务的stack进行初始化,也就是伪造一个现场。
/********************************************************************************** 创建任务 **********************************************************************************/ int osTaskCreate(OsTcb *tcb_ptr, TaskFunc task_ptr, char *name, void *param, CPU_STAK *stak_base, uint32_t stak_size) { if(tcb_ptr == NULL) return -1; if(task_ptr == NULL) return -1; if(stak_base == NULL) return -1; /** 初始化堆栈,伪造现场 **/ tcb_ptr->stak_ptr = _osTaskStakInit(task_ptr, param, stak_base, stak_size); /** 将任务加入就序列表 **/ osTaskAddReadyList(tcb_ptr); return 0; } /********************************************************************************** 堆栈初始化操作完任务栈空间的现场伪造,保证任务第一次执行时能正常运行。 **********************************************************************************/ static inline CPU_STAK * _osTaskStakInit(TaskFunc task_ptr, void *param, CPU_STAK *stak_base, uint32_t stak_size) { CPU_STAK * stak_ptr = &stak_base[stak_size]; stak_ptr = (CPU_STAK *)((CPU_STAK)stak_ptr & 0xFFFFFFF8); * --stak_ptr = (CPU_STAK)0x01000000; /** xPSR, bit24 = 1, use thumb instruction **/ * --stak_ptr = (CPU_STAK)task_ptr; /** R15/PC, enter point **/ * --stak_ptr = (CPU_STAK)_osTaskReturn; /** R14/LR, task return function **/ * --stak_ptr = (CPU_STAK)0x12121212; /** R12, do not care **/ * --stak_ptr = (CPU_STAK)0x03030303; /** R3, do not care **/ * --stak_ptr = (CPU_STAK)0x02020202; /** R2, do not care **/ * --stak_ptr = (CPU_STAK)0x01010101; /** R1, do not care **/ * --stak_ptr = (CPU_STAK)param; /** R0, parameter **/ * --stak_ptr = (CPU_STAK)0x11111111; /** R11, do not care **/ * --stak_ptr = (CPU_STAK)0x10101010; /** R10, do not care **/ * --stak_ptr = (CPU_STAK)0x09090909; /** R9, do not care **/ * --stak_ptr = (CPU_STAK)0x08080808; /** R8, do not care **/ * --stak_ptr = (CPU_STAK)0x07070707; /** R7, do not care **/ * --stak_ptr = (CPU_STAK)0x06060606; /** R6, do not care **/ * --stak_ptr = (CPU_STAK)0x05050505; /** R5, do not care **/ * --stak_ptr = (CPU_STAK)0x04040404; /** R4, do not care **/ return stak_ptr; }
2. 任务切换
任务切换在 SysTick_Handler 中执行,每次中断都进行一次任务切换。这里强调一点,由于任务切换是在systick中断中执行,因此在恢复完成新任务的现场之后,需要通过将进入中断时写入LR寄存器中的EXC_RETURN数据来触发中断返回,从而恢复现场,也即由硬件将R0 - R3、R12、LR、PC、xPSR寄存器的值从栈中恢复,而在中断执行过程中,又有函数调用会将LR中的值破坏掉,因此在进入 SysTick_Handler 时要第一时间把 EXC_RETURN 数据保存起来,已备触发中断返回时使用。
SysTick_HandlerAsm PROC IMPORT os_running IMPORT g_cur_task IMPORT osTick ;进入中断时,硬件完成 xPSR, PC, LR, R12, R3-R0的入栈保存操作 ;并在 LR 中写入 EXC_RETURN ;判断是否需要保存现场 LDR R1, =g_cur_task LDR R1, [R1] CBZ R1, NO_SAVE ;如果为空,说明是第一次切换任务,无需保存现场 ;保存现场 MRS R0, MSP STMDB R0!, {R4 - R11} ;保存R4-R11寄存器到堆栈 ;保存当前任务的SP指针 LDR R1, =g_cur_task LDR R1, [R1] STR R0, [R1] ;g_cur_task->stak_ptr = R0 MSR MSP, R0 ;MSP = R0 NO_SAVE ;执行osTick PUSH {LR} ;备份LR,以便osTick退出后触发中断返回 BL osTick ;在osTick中切换任务 POP {LR} ;恢复LR ;获取新任务的SP指针 LDR R1, =g_cur_task LDR R1, [R1] LDR R0, [R1] ;R0 = g_cur_task->stak_ptr ;恢复现场 LDMIA R0!, {R4 - R11} ;将 R4-R11从堆栈中写入寄存器 MSR MSP, R0 ;跟新SP指针 BX LR ;将 EXC_RETURN 写入PC,触发中断返回 ENDP
完整代码:https://gitee.com/ivan1024/os.git
                    
                
                
            
        
浙公网安备 33010602011771号