学习ARM开发20-21

 /***********************************
 *作者:蔡军生
 *出处:http://blog.youkuaiyun.com/caimouse/
 ************************************/
 
  学习ARM开发(20)
OS的任务切换
有了前面的Tick中断,那么基本的任务切换条件已经是“万事俱备,只欠东风”了。不过,这个“东风”也是很难搞得懂的,只有不断地通过实践才会找到合适的方法。现在我就需要去找这个东风了,就是解决不同的任务切换的问题。从简单到复杂,这是任何事物的认识过程,也是行之有效的方法。绝对不要一上来就搞一个很复杂的,因为人的理解能力还是有限的。最简单的任务切换,就是我需要实现的:只需要实现两个任务不断地来回切换,就已经说明可行了。那我先把这两个任务设置为最简单的,因此,就把任务的栈定下来,因为每个任务的栈是肯定不同的,所以我选择了固定地设置栈地址。比如第一个任务的栈地址是0x0c700000,第二个任务的栈地址是0x0c700200。接着就需要把任务这两个栈初始化成中断返回的方式,就是需要保存R0到R12,LR,PC,CPSR等寄存器的值。

这时就需要理解ARM的几种工作模式了,目前我使用到的只有两种模式:IRQ和SVC模式。在这两种模式下,它的寄存器是有一些不同的。就是SP,LR,SPCR的寄存器不同样,并且LR与SPCR在两种模式中是有关联的。当从SVC模式转换到IRQ模式时,它的LR,就是在SVC下的执行的下一条指令地址减4;SPSR就是SVC模式下的CPSR寄存器的值。因此,在IRQ中断之后,一定要想办法把这两个寄存器保存下来,否则就返回不到先前的任务了。由于SVC与IRQ的SP是不一样的,并且在SVC下的LR也没有办法保存,那么就一定要切换回到SVC模式下,才能访问这个任务的栈了,并且那时才能保存LR的值。因此,特殊的要求就在这里了。

  由于我的OS是采用时间片轮转算法,那么当时间片到时,TICK中断就会中断任务的运行,并且要切换到新的任务运行。具体过程是这样的:启动时第一个任务运行,当时间片使用完了,那么TICK中断就发生,接着就保存IRQ模式下的LR,SPCR寄存器到内存某个位置,并且把R0到R12的所有寄存器恢复,接着切换回到SVC模式。接着保存一个寄存器的值,然后用这个寄存器从内存读回来在IRQ方式保存的LR值。然后把这个LR值压入栈,这个LR值就是任务再回来时要运行的PC值。因此把它放到最先栈里,到时出栈才方便。接着保存SVC模式下的LR值,保存R0到R12的值,然后保存SPCR值。到这里,就把所有任务恢复时所需要的寄存器保存了。

  到后面,接着再写一段可以恢复任务的汇编程序就可以了,这个就是上面的压栈反向过程。通过这样的方法,就可以不断来回地切换任务了。

  学习ARM开发(21)
OS任务切换源程序分析

       先要声明任务指针,因为后面需要使用。

       //任务指针.

volatile TASK_TCB* volatile g_pCurrentTask = NULL;

volatile TASK_TCB* volatile g_pCurrentTask1 = NULL;

volatile TASK_TCB* volatile g_pCurrentTask2 = NULL;

接着就需要初始化这些任务栈,用下面的代码进行初始化,为了简单,全部使用内存地址操作的方式,当然后面会改成动态地分配内存的方式。代码如下:

///////////////////////////////////////////////////////////////////////////////
//函数名称:   TaskInitStack
//函数功能:   分配任务的栈。
//输入参数:
//输出参数:
//返 回 值:   
//开发人员:   蔡军生
//时    间:   2006/02/26
//修改说明:  
//
///////////////////////////////////////////////////////////////////////////////

void TaskInitStack(void)

{

       g_pCurrentTask1 = (PTASK_TCB)0x0c700000;

       g_pCurrentTask1->pStackStart = (UINT*)(0x0c700000+0x200);

       g_pCurrentTask1->pStackTop = g_pCurrentTask1->pStackStart + 0x100;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       g_pCurrentTask2 = (PTASK_TCB)(0x0c700000 + 0x400);

       g_pCurrentTask2->pStackStart = (UINT*)(0x0c700000+0x400 + 0x200);

       g_pCurrentTask2->pStackTop = g_pCurrentTask2->pStackStart + 0x100;

}

<!--[if !supportEmptyParas]--> <!--[endif]-->

接着再创建两个简单的任务,它们都是输出一行字符串,就等待一会,代码如下:

//

void TaskTest1(void)

{    
       for(;;)

       {
              Lock();
              puts("TaskTest1/n");
              UnLock();

<!--[if !supportEmptyParas]--> <!--[endif]-->

              SoftDelay(100);
       }

}
//

void TaskTest2(void)

{    
       for(;;)

       {
              Lock();
              puts("TaskTest2/n");
              UnLock();

<!--[if !supportEmptyParas]--> <!--[endif]-->

              SoftDelay(100);
       }
}

<!--[if !supportEmptyParas]--> <!--[endif]-->

然后再初始化任务栈,代码如下:

void TaskStart(void)

{     
       //

       UINT* pTemp = g_pCurrentTask1->pStackTop;

       //

       *g_pCurrentTask1->pStackTop =  (UINT)TaskTest1;

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x14141414; /* R14 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

//       *g_pCurrentTask1->pStackTop = (UINT)pTemp;       /* R13 */

//       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x12121212; /* R12 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x11111111;   /* R11 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x10101010; /* R10 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x09090909; /* R9 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x08080808; /* R8 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x07070707; /* R7 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x06060606; /* R6 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x05050505; /* R5 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x04040404; /* R4 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x03030303; /* R3 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x02020202; /* R2 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x01010101; /* R1 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0;       /* R0 */

       g_pCurrentTask1->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask1->pStackTop = (UINT)0x13;  /* SPSR */

<!--[if !supportEmptyParas]--> <!--[endif]-->

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //
       //
       //

       pTemp = g_pCurrentTask2->pStackTop;

       //

       *g_pCurrentTask2->pStackTop =  (UINT)TaskTest2;

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x14141414; /* R14 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

//       *g_pCurrentTask2->pStackTop = (UINT)pTemp;       /* R13 */

//       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x12121212; /* R12 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x11111111;   /* R11 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x10101010; /* R10 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x09090909; /* R9 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x08080808; /* R8 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x07070707; /* R7 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x06060606; /* R6 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x05050505; /* R5 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x04040404; /* R4 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x03030303; /* R3 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x02020202; /* R2 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x01010101; /* R1 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0;       /* R0 */

       g_pCurrentTask2->pStackTop--;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       *g_pCurrentTask2->pStackTop = (UINT)0x13;  /* SPSR */

      

       //设置首先运行的任务是1.

       g_pCurrentTask = g_pCurrentTask1;

}

代码初始化了两个任务栈后,接着设置第一个任务为优先运行的任务。

<!--[if !supportEmptyParas]--> <!--[endif]-->

最后就需要进行中断任务调度任务进行运行了,代码如下:

///////////////////////////////////////////////////////////////////////////////
//函数名称:   EIntTickIsr
//函数功能:   时钟中断函数。
//输入参数:
//输出参数:
//返 回 值:   
//开发人员:   蔡军生
//时    间:   2006/02/26
//修改说明:  
//
///////////////////////////////////////////////////////////////////////////////

void EIntTickIsr(void) __attribute__((naked));

void EIntTickIsr(void)

{

       //保存R0-R12寄存器到栈里.

       asm volatile(" STMDB  SP!, {R0-R12} ");

      

       //关闭TICK中断.

       INTMSK |= BIT_GLOBAL|BIT_TICK;

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //取得保存IRQ的LR地址,并保存LR.

       asm volatile (" LDR     R0,=g_dwIRQLR" );

       asm volatile (" SUBS    LR,LR,#4 ");

       asm volatile (" STR           LR,[R0] ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //取回前面保存的R0-R12寄存的值.

       asm volatile (" LDMIA       SP!, {R0-R12} ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //取得最后返回的SPSR,就是SVC模式下的CPSR.

       //从IRQ模式切换回到SVC模式.

       asm volatile (" MRS          LR,SPSR ");

       asm volatile (" ORR           LR,#0xc0 ");

       asm volatile (" MSR          CPSR,LR ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //现在已经转换回到SVC模式,取回IRQ模式下保存的LR.

       //把R0的值保存到栈中第二个位置.

       asm volatile (" STR     R0,[SP,#-8] ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //从保存位置取回IRQ模式下保存的LR值.

       asm volatile (" LDR     R0,=g_dwIRQLR" );

       asm volatile (" LDR     R0,[R0] ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //把IRQ模式下的LR保存到栈里,就是出栈时的PC值.

       asm volatile (" STMDB   SP!,{R0} ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //从栈里第二个位置取回先前保存的R0值.

       asm volatile (" SUBS    SP,SP,#4 ");

       asm volatile (" LDMIA   SP!,{R0} ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //保存R0-R12,LR到栈里,入栈顺序是刚好相反的.

       asm volatile (" STMDB   SP!,{R0-R12,LR} ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //保存CPSR的值到栈里,就是SPSR的位置.

       asm volatile (" MRS     R4,CPSR ");

       asm volatile (" BIC     R4,#0xc0 ");

       asm volatile (" STMDB   SP!,{R4} ");

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //保存栈顶到任务指针里.

       asm volatile ( "LDR           R0, %0" : : "m" (g_pCurrentTask) );

       asm volatile ( "STR           SP, [R0]" );

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //增加时钟计数.

       g_dwTickCount++;  

       //    
       printf("g_dwTickCount = (%d)/n",g_dwTickCount);

       //    
       //清除屏蔽位.

       I_ISPC = BIT_TICK;           

       INTMSK &= ~(BIT_GLOBAL|BIT_TICK);

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //
       if (g_dwTickCount < 3)

       {
              g_pCurrentTask = g_pCurrentTask1;
       }

       else if (g_dwTickCount < 4)

       {
              g_pCurrentTask = g_pCurrentTask2;
       }
       else

       {
              g_dwTickCount = 0;
       }

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //取得新任务指针

       asm volatile ( "LDR           R0, %0" : : "m" (g_pCurrentTask) );  

       asm volatile ( "LDR           SP, [R0]" );

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //取回SPSR值.

       asm volatile ( "LDMIA       SP!, {R0}" );

       asm volatile ( "MSR          SPSR, R0" );

<!--[if !supportEmptyParas]--> <!--[endif]-->

       //取回R0-R12,LR,PC值,并运行最后的任务.

       asm volatile ( "LDMIA       SP!, {R0-R12,LR,PC}^" );

}

<!--[if !supportEmptyParas]--> <!--[endif]-->

写完上面的代码,就可以真正地实现了任务调度了。有了以上的代码,写其它复杂的任务调试都变得很容易了,这些代码得来是不太容易的,我经历了好几个星期的调试才通过的。

这些都是在我的S3C44B0开发板上调试通过的,如果你没有开发板,可以跟我购买。联系方法ccaimouse#gmail.com(请把#换成@)。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值