其他分享
首页 > 其他分享> > rtos 3 - 实现一个可以运行多任务的简易rtos

rtos 3 - 实现一个可以运行多任务的简易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_Handler\
                PROC
                IMPORT    osTick
                
                ;保存 LR = EXC_RETURN
                ;将LR作为参数输入osTick,否则中断程序的过程调用会将LR中的值破坏
                MOV R0, LR
                BL  osTick
                ENDP
void
osTick(uint32_t lr)
{
    if(!os_running) return;
    
    /** 保存现场 **/
    if(g_cur_task != NULL)
        pushCtxAsm(g_cur_task->stak_ptr);
    
    /** 获取下一个就绪任务 **/
    osGetNextReadyTask();
    
    /** 切换任务 **/
    popCtxAsm(g_cur_task->stak_ptr, lr);
}
pushCtxAsm        PROC
                
                ;保存R4-R11寄存器到堆栈
                STMDB R0!, {R4 - R11}
                BX LR
                
                ENDP

popCtxAsm        PROC
            
                ;将 R4-R11从堆栈中写入寄存器
                ;R0 :任务的栈顶地址
                ;R1 :LR = EXC_RETURN
                
                LDMIA R0!, {R4 - R11}
                MSR MSP, R0        ;跟新SP指针
                BX  R1             ;将 EXC_RETURN 写入PC,触发中断返回
                
                ENDP

  完整代码:https://gitee.com/ivan1024/os.git

标签:STAK,do,多任务,--,rtos,CPU,简易,ptr,stak
来源: https://www.cnblogs.com/ivan0512/p/16123325.html