ucosIII任务切换学习笔记及简单的任务切换实现

一、ucos中任务切换

        一个内核要做的事情就是保持和回复寄存器,而ucos中保持与回复寄存器是在PendSV中进行的,也就是任务切换的过程在这里进行。

        启用ucos前,首先要初始化相关寄存器:

        切换过程总体分为四步:

        1.保存当前任务的状态寄存器和从计时器到当前任务堆栈

        2.将CPU当前堆栈指针(PSP)的当前值保存到将要停止运行的任务控制块OS_TCB中,即OSTCBCurPtr = SP

        3.将新任务的OS_TCB中保存的该任务的堆栈指针载入CPU的PSP中,该指针是上一次进行任务切换保存的,指向的是当时任务堆栈的栈顶,即SP = OSTCBHighRdyPtr->StkPtr

        4.从新的堆栈中回复CPU寄存器的值

PendSV_Handler
    CPSID   I       ; 关中断
    MRS     R0, PSP ; PSP 如果=0,则说明是第一次进行任务切换,因为OSStartHighRdy中已经将PSP初始化为0了,不用在进行入栈操作
                    ;任务刚创建的时候,任务堆栈已经按照ISR入栈的顺序将寄存器推入堆栈了
    CBZ     R0, PendSVHandler_nosave  ;跳转到PendSVHandler_nosave  

    SUBS    R0, R0, #0x20  ;能运行到此处,说明不是第一次进行任务切换(任务切换),因为只有在OS启动的时候PSP才有可能为0,
                           ;如调用了OSStartHighRdy,而任务在初始化的时候已经进行过堆栈的处理了,这里就不用再保存寄存器了。
    STM     R0, {R4-R11}   ; 保存R4-R11

    ; OSTCBCurPtr->OSTCBStkPtr = SP
    LDR     R1, =OSTCBCurPtr  ;R1 = & OSTCBCurPtr 
    LDR     R1, [R1]          ; R1 = *R1,即R1 = OSTCBCurPtr
    STR     R0, [R1]          ; *R1 = R0 其中OSTCBCurPtr首地址为StkPtr,即 OSTCBCurPtr->OSTCBStkPtr = SP (R0即PSP) 

PendSVHandler_nosave
    PUSH    {R14}               
    LDR     R0, =OSTaskSwHook   
    BLX     R0
    POP     {R14}
    
    ; 实现OSPrioCur   = OSPrioHighRdy;
    LDR     R0, =OSPrioCur       ;R0 = &OSPrioCur
    LDR     R1, =OSPrioHighRdy   ;R1 = &OSPrioHighRdy
    LDRB    R2, [R1]             ;R2 = *R1 即 R2 = OSPrioHighRdy
    STRB    R2, [R0]             ;*R0 = R2

    ; 实现OSTCBCurPtr = OSTCBHighRdyPtr
    LDR     R0, =OSTCBCurPtr      ;R0 = &OSTCBCurPtr
    LDR     R1, =OSTCBHighRdyPtr  ;R1 = &OSTCBHighRdyPtr
    LDR     R2, [R1]              ;R2 = *R1 即 R2 = OSTCBHighRdyPtr
    STR     R2, [R0]              ;*R0 = R2 即 OSTCBCurPtr = OSTCBHighRdyPtr
    ;将高优先级任务控制块及任务赋值给系统任务控制块    OSPrioHighRdy与 OSTCBHighRdyPtr在进行任务切换前已经更行,例如OSIntExit()  OSSched()
		
    LDR     R0, [R2]      ;R0 = *R2 即 R0 = OSTCBHighRdyPtr->OSTCBStkPtr
    LDM     R0, {R4-R11}  ;恢复R4-R11寄存器, 由于OSTCBHighRdyPtr首地址表示StkPtr(指向任务当前的栈顶),即将R4-R11保存到该位置
    ADDS    R0, R0, #0x20
    MSR     PSP, R0       ; PSP = R0
    ORR     LR, LR, #0x04 ; 赋值,确保LR位2是1,返回后使用PSP
    CPSIE   I             ;开中断
    BX      LR            ;中断返回
    END

二、简化的任务切换

        由之前的介绍可知,ucos要完成4个步骤,而我自己做任务切换也大致可分为4个过程:

        1.任务的创建(初始化)

           任务的创建(初始化)其实就是模拟一个中断,并按照ISR将寄存器入栈的顺序把寄存器推入堆栈中,

void Task_Create(void)
{
	U16 i;
	//初始化堆栈
    	for(i=0; i<TAST_SIZE; i++)	
    	{
    		Task_Stk_1[i] = 0;              // 初始化堆栈
    		Task_Stk_2[i] = 0;
    	}
	StkPtr_1 = &Task_Stk_1[TAST_SIZE];                
    *--StkPtr_1 = (U32)0x01000000u;                        /* xPSR              */
    *--StkPtr_1 = (U32)*Task1;                             /* Entry Point       */
    *--StkPtr_1 = (U32)0x14141414u;                        /* R14 (LR)          */
    *--StkPtr_1 = (U32)0x12121212u;                        /* R12               */
    *--StkPtr_1 = (U32)0x03030303u;                        /* R3                */
    *--StkPtr_1 = (U32)0x02020202u;                        /* R2                */
    *--StkPtr_1 = (U32)0x10101010u;                        /* R1                */
    *--StkPtr_1 = (U32)0x00000000u;                        /* R0 :              */
    *--StkPtr_1 = (U32)0x11111111u;                        /* R11               */
    *--StkPtr_1 = (U32)0x10101010u;                        /* R10               */
    *--StkPtr_1 = (U32)0x09090909u;                        /* R9                */
    *--StkPtr_1 = (U32)0x08080808u;                        /* R8                */
    *--StkPtr_1 = (U32)0x07070707u;                        /* R7                */
    *--StkPtr_1 = (U32)0x06060606u;                        /* R6                */
    *--StkPtr_1 = (U32)0x05050505u;                        /* R5                */
    *--StkPtr_1 = (U32)0x04040404u;                        /* R4                */

    StkPtr_2 = &Task_Stk_2[TAST_SIZE];                  
    ......
}

2.设置PendSV优先级(为最低)

void PendSVPriSet(void)
{
  #define NVIT_SYSPRI		*((U32 *)0xE000ED22)        //优先级寄存器
  #define NVIT_PENDSV_PRI	0xff                        //设置PendSV优先级为0xFF
	
  NVIT_SYSPRI = NVIT_PENDSV_PRI;	
}

3.初始化PSP

__ASM void InitPSP(void)
{
	 MOVS	R0,#0
	 MSR	PSP,R0
}

4.编写PendSV程序

PendSV_Handler
	CPSID	I                         ; 关中断
    MRS		R0, PSP                   ; PSP 如果=0,则说明是第一次进行任务切换,不用在进行入栈操作
    CBZ		R0, PendSVHandler_nosave  ;跳转到PendSVHandler_nosave  

    SUBS	R0, R0, #0x20  
    STM		R0, {R4-R11}              ; 保存R4-R11
	
    LDR     R1, =StkPtr_Cur               ; R1 = & StkPtr_Cur ,
    STR     R0, [R1]                      ; *R1 = R0,  *StkPtr_Cur = PSP
PendSVHandler_nosave
    LDR		R0, =StkPtr_Next          ;R0 = &StkPtr_Next
    LDR		R0, [R0]                  ;R0=*R0
	
    LDM     R0, {R4-R11}                  ;恢复R4-R11寄存器, 由于OSTCBHighRdyPtr首地址表示StkPtr(指向任务当前的栈顶),即将R4-R11保存到该位置
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                       ; PSP = R0
    ORR     LR, LR, #0x04                 ; 赋值,确保LR位2是1,返回后使用PSP
    CPSIE   I                             ;开中断
    BX      LR                            ;中断返回
    END

5.开始任务切换

void Tast_SW(void)
{
	#define NVIT_CTRL       *((U32 *)0xE000ED04)    //中断控制寄存器
	#define NVIC_PENDSVSET  0x10000000              //向中断控制寄存器28位写1,出发PendSV中断	
	
	NVIT_CTRL = NVIC_PENDSVSET;			
}

至此,任务已经可以正常运行。具体程序在附件中

  • 11
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值