ucos任务调度函数 OSSched()函数分析 ,任务切换函数

OS_Sched()分析

         在uc/os中总是运行优先级最高的就绪任务,确定哪个任务优先级最高,该由哪个优先级人物运行了,这一工作是由任务调度器完成的,(而具体的任务切换,是任务调度器在调用其他函数来完成)。其中任务级的调度由函数OS_Sched()来完成,中断级的调度由OSIntExt()来完成的。

OS_Sched()函数分析

 

 

void OSSched (void)
{

 

  INT8U y;

  OS_ENTER_CRITICAL();

 

   if ((OSLockNesting | OSIntNesting) == 0)
   { //判断是否满足调度条件,在uc/os中任务级调度的调用不允许来自中断服务子程序(OSIntNesting) == 0),此外当调度器上锁时,任务调度函数将直接退出,不做任务调度

 

      y = OSUnMapTbl[OSRdyGrp];

       OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);//这两行代码是获得进入就绪态且优先级最高的任务

       if (OSPrioHighRdy != OSPrioCur) 

      { //检验优先级最高的任务是否是当前正在运行的任务。以避免不必要的的任务调度,毕竟任务调度是需要时间滴。

         OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//通过当前最高优先级OSPrioHighRdy,从任务控制块优先级表中OSTCBPrioTbl[]获得当前最高优优先级任务控制块

         OSCtxSwCtr++;//该全局变量(32位)用于记录任务切换的次数,

         OS_TASK_SW();最后调用OS_TASK_SW宏来完成实际上的任务切换,该宏是一个软中断

       }

 

    }

 

OS_EXIT_CRITICAL();
}

 

 

OS_TASK_SW()函数分析
任务切换的内容其实是很简单的:将被挂起任务的寄存器压入堆栈,然后将高优先级的寄存器从栈中恢复到CPU的寄存器中。该过程使用软中断来实现。
SoftwareInterrupt
  LDR SP, StackSvc ; 重新设置堆栈指针,在LPC2200开发板中软中断触发后,开发板处于管理模式
  STMFD SP!, {R0-R3, R12, LR} //压栈,在下面过程中使用到了以下以下的寄存器,故需要将这些寄存器压入管理模式堆栈,来实现原始寄存器的保存。
  MOV R1, SP //R1指向参数存储位置

  MRS R3, SPSR /在ARM处理器中,只有MRS指令可以读取状态寄存器
  TST R3, #T_bit //中断前是否是Thumb状态
  LDRNEH R0, [LR,#-2] //是: 取得Thumb状态SWI号
  BICNE R0, R0, #0xff00
  LDREQ R0, [LR,#-4] //否: 取得arm状态SWI号
  BICEQ R0, R0, #0xFF000000
  // r0 = SWI号,R1指向参数存储位置
  CMP R0, #1//比较软中断号,确立软中断的服务程序
  LDRLO PC, =OSIntCtxSw//0号软中断用于任务级切换
  LDREQ PC, =__OSStartHighRdy ; SWI 0x01为第一次任务切换

  BL SWI_Exception//跳转到软中断服务子程序

  LDMFD SP!, {R0-R3, R12, PC}^//SWI异常中断返回
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
对于SWI中断来说,其格式是SWI{cond} immed_24;后面的24位数字就是软中断号,故在SWI异常中断出路程序中,取出SWI中断号的方法是首先确定软中断SWI指令是ARM指令还是THUMB指令(通过访问SPSR获得),然后取得该指令的地址,可通过LR获得,接着读出指令,分解出立即数,该立即数就是软中断号。
在软中断触发后,处理器应进入管理模式,故需要从新设置堆栈指针。
在软中断触发后,SoftwareInterrupt相当于是一个判断程序,只是简单的取出中断号,具体的实现是调用其他函数完成的。
在SoftwareInterrupt中,进行了一次压栈,是因为在获得软中断号的过程中使用了R0,R1,R2,R3故需要将这些寄存器压栈,而使用的SP是管理模式下的寄存器,其于用户模式下的SP寄存器不是一个寄存器,故不需要进行压栈。

 

 该函数调用所需要的时间是常量,于实际的任务数无 

 

 

任务级调度 OSIntCtxsw
OSIntCtxSw
  //下面为保存任务环境
  LDR R2, [SP, #20] //此时处理器仍处于管理模式,从管理模式堆栈中获得PC保存到R2中
  LDR R12, [SP, #16] //从堆栈中获取R12,更新CPU寄存器
  MRS R0, CPSR //保存当前状态寄存器,用于模式之间的切换

  MSR CPSR_c, #(NoInt | SYS32Mode)//进入系统模式,且关中断
  MOV R1, LR //获得系统模式下的LR保存到R1中,该LR就是原始任务的LR
  STMFD SP!, {R1-R2} //此时处理器处于系统模式,故SP是原始任务的堆栈指针,故原始任务的LR,PC压入原始任务堆栈
  STMFD SP!, {R4-R12} //由于R4-R11寄存器未改变,故其内容是原始任务的内容,直接压入原始任务堆栈即可。

  MSR CPSR_c, R0 //此时切换回管理模式
  LDMFD SP!, {R4-R7} //从管理模式堆栈中获得前面保存的原是任务的RO-R3,保存到CPU的R4-R7
  ADD SP, SP, #8 //忽略掉R12,PC,使堆栈指针回到初始位置,使得下次继续使用

  MSR CPSR_c, #(NoInt | SYS32Mode)//进入系统模式
  STMFD SP!, {R4-R7} //原始任务的R0-R3入栈保存

  LDR R1, =OsEnterSum //获取OsEnterSum
  LDR R2, [R1]
  STMFD SP!, {R2, R3} //从上面知道R3的内容为原始任务的CPSR,故该指令保存CPSR,OsEnterSum

  LDR R1, =OSTCBCur //获得原始任务的TCB地址
  LDR R1, [R1]//该地址的内容是原始任务的SP值
  STR SP, [R1] //保存原始任务堆栈指针到原始任务的TCB

  BL OSTaskSwHook //调用钩子函数
  ;OSPrioCur <= OSPrioHighRdy将当前优先级切换为最高优先级(优先级的切换)
  LDR R4, =OSPrioCur//当前TCB结构体首地址
  LDR R5, =OSPrioHighRdy//最高优先级TCB结构体首地址
  LDRB R6, [R5]
  STRB R6, [R4]//更新当前TCB指向最高优先级TCB
  ;OSTCBCur <= OSTCBHighRdy将当前TCB切换为最高优先级的TCB(TCB的切换)
  LDR R6, =OSTCBHighRdy
  LDR R6, [R6]
  LDR R4, =OSTCBCur
  STR R6, [R4]
OSIntCtxSw_1
  ;获取新任务堆栈指针
  LDR R4, [R6]//R6为新任务的TCB地址,其内容是新任务的堆栈指针
  ADD SP, R4, #68 //;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP,移动新任务堆栈的SP,指向栈底
  LDR LR, [SP, #-8]//新任务LR进入CPU寄存器
  MSR CPSR_c, #(NoInt | SVC32Mode) //进入管理模式
  MOV SP, R4 //设置堆栈指针

  LDMFD SP!, {R4, R5} //CPSR,OsEnterSum
  //恢复新任务的OsEnterSum
  LDR R3, =OsEnterSum
  STR R4, [R3]

  MSR SPSR_cxsf, R5 //恢复CPSR
  LDMFD SP!, {R0-R12, LR, PC }^ ;新任务RO-R12,LR,PC出栈,运行新任务

  该函数执行前的数据结构:OSTCBCur指向原始任务(低优先级任务)、CPU的SP指向原始任务的栈顶、OSTCBHighRdy指向新任务的TCB
  【转】msr cpsr_cxsf,r1 ;这里的cxsf表示从低到高分别占用的4个8bit的数据域
  指令中有时还有出现cpsr_cf, cpsr_all, cpsr_c等,这里:

 

c 指 CPSR中的control field ( PSR[7:0])
f 指 flag field (PSR[31:24])
x 指 extend field (PSR[15:8])
s 指 status field ( PSR[23:16])
其中cpsr的位表示为:
31 30 29 28 --- 7 6 - 4 3 2 1 0
N Z C V I F M4 M3 M2 M1 M0

 

0 0 0 0 0 User26 模式
0 0 0 0 1 FIQ26 模式
0 0 0 1 0 IRQ26 模式
0 0 0 1 1 SVC26 模式
1 0 0 0 0 User 模式
1 0 0 0 1 FIQ 模式
1 0 0 1 0 IRQ 模式
1 0 0 1 1 SVC 模式
1 0 1 1 1 ABT 模式
1 1 0 1 1 UND 模式
深入分析:
对于MSR(寄存器到状态寄存器)的指令,
MSR CPSR, r0
MSR CPSR_all, r0
MSR CPSR_flg, r0
都是已经过时的表示方法。
对于MRS(状态寄存器到寄存器)的指令,
MRS R0, CPSR 等同于MRS R0, CPSR_cxsf
MRS R0, CPSR_all 会有waring
MRS R0, CPSR_flg 会有错误
在ADS中使用c,f,x,s表示cpsr的各个部分是推荐的。从指令来说:
MSR CPSR_f, r0机器码为0xe128f000
MSR CPSR_c, r0机器码为0xe121f000
MSR CPSR_x, r0机器码为0xe122f000
MSR CPSR_s, r0机器码为0xe124f000
可见机器码中用bit[29:16]4bit表示是f,c,x,s的。所以能够在机器执行的时候,
给予不同的执行结果。为了代码向后兼容性,建议使用f,c,x,s尾缀

 

posted on 2017-11-28 16:45  Red_Point  阅读(3867)  评论(0编辑  收藏  举报

导航