第4章第3节 实时事件触发的实时抢…

来源:互联网 发布:中国程序员联合开发网 编辑:程序博客网 时间:2024/06/16 02:29

目前更新到5.3节,请在http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档

本节源代码请在http://dl.dbank.com/c0z9e2wbch下载

 

下面来看一下MDS_TaskDelay函数的代码:

00196  U32 MDS_TaskDelay(U32 uiDelayTick)

00197  {

00198      M_CHAIN* pstrChain;

00199      M_CHAIN* pstrNode;

00200      M_CHAIN* pstrDelayNode;

00201      M_TCBQUE* pstrTaskQue;

00202      M_PRIOFLAG* pstrPrioFlag;

00203      U8 ucTaskPrio;

00204  

00205      

00206      if(gpstrCurTcb == gpstrIdleTaskTcb)

00207      {

00208          return RTN_FAIL;

00209      }

00210  

00211      

00212      if(DELAYNOWAIT != uiDelayTick)

00213      {

00214          

00215          ucTaskPrio gpstrCurTcb->ucTaskPrio;

00216          pstrChain &gstrReadyTab.astrChain[ucTaskPrio];

00217          pstrPrioFlag &gstrReadyTab.strFlag;

00218  

00219          (void)MDS_IntLock();

00220  

00221          

00222          pstrNode MDS_TaskDelFromSchedTab(pstrChain, pstrPrioFlag, ucTaskPrio);

00223  

00224          

00225          gpstrCurTcb->strTaskOpt.ucTaskSta &= ~((U8)TASKREADY);

00226  

00227          

00228          gpstrCurTcb->strTaskOpt.uiDelayTick uiDelayTick;

00229  

00230          

00231          if(DELAYWAITFEV != uiDelayTick)

00232          {

00233              gpstrCurTcb->uiStillTick guiTick uiDelayTick;

00234  

00235              

00236              pstrTaskQue (M_TCBQUE*)pstrNode;

00237              pstrDelayNode &pstrTaskQue->pstrTcb->strDelayQue.strQueHead;

00238  

00239              

00240              MDS_TaskAddToDelayTab(pstrDelayNode);

00241  

00242              

00243              gpstrCurTcb->uiTaskFlag |= DELAYQUEFLAG;

00244          }

00245  

00246          

00247          gpstrCurTcb->strTaskOpt.ucTaskSta |= TASKDELAY;

00248  

00249          (void)MDS_IntUnlock();

00250      }

00251      else 

00252      {

00253          

00254          gpstrCurTcb->strTaskOpt.uiDelayTick RTN_SUCD;

00255      }

00256  

00257      

00258      MDS_TaskSwiSched();

00259  

00260      

00261      return gpstrCurTcb->strTaskOpt.uiDelayTick;

00262  }

00196行,函数返回值分4种,RTN_SUCD: 任务没有delay,仅发生任务切换,仅在入口参数为0时才会返回该值。RTN_FAIL: 任务delay失败,没有进入delay状态。RTN_TKDLTO: 任务已经进入过delay状态,并且又从delay状态返回到running状态,delay的时间已耗尽超时返回。RTN_TKDLBK: 任务delay状态被打断,被其它任务使用MDS_TaskWake函数唤醒。

入口参数uiDelayTick是需要delaytick数。

00206~00209行,idle任务不能延迟。

00212行,对任务delay的时间进行判断,不是delay tick的情况走下面分支。

00222行,当前任务需要切换到delay状态,从ready表拆除。

00225行,清除任务的ready状态。

00227行,更新当前任务的延迟时间。

00231行,对任务delay的时间进行判断,不是永久delay的情况走下面分支。

00233行,计算该任务需要delay到的tick数值,存入TCB中。

00236行,将当前任务的M_TCBQUEready节点指针强制转换为M_TCBQUE型指针。

00237行,从任务的ready节点找到delay节点指针。

00240行,将该任务加入delay表。

00243行,在该任务的TCB中的任务标志uiTaskFlag中设置标志,表明该任务已经处于delay表中。

00247行,增加任务的delay状态。

00251~00255行,delay tick时走此分支,任务只切换不延迟,将返回值保存在strTaskOpt中的uiDelayTick变量中。

00258行,任务相关变量、ready表、delay表的操作已经完成,调用MDS_TaskSwiSched函数触发软中断,开始任务调度。

00263行,将strTaskOpt中的uiDelayTick变量作为返回值返回给上级父函数。uiDelayTick变量中的返回值会在248行仅发生任务切换时存入,或者在任务调度函数MDS_TaskDelayTabScheddelay时间耗尽时存入,或者在MDS_TaskWake函数中由其它任务唤醒时存入。

261行与258行在代码编写上是连在一起的,但在执行时可能会有时间间隔,258行会发生任务调度,中间可能会插入其它任务的执行过程。

 

MDS_TaskWake函数正好与MDS_TaskDelay函数相反,它将处于delay状态的任务从delay表拆除,添加到ready表,并修改任务的一些相关状态变量,代码如下,不再详细介绍。

00270  U32 MDS_TaskWake(M_TCB* pstrTcb)

00271  {

00272      M_CHAIN* pstrChain;

00273      M_CHAIN* pstrNode;

00274      M_PRIOFLAG* pstrPrioFlag;

00275      U8 ucTaskPrio;

00276  

00277      

00278      if(NULL == pstrTcb)

00279      {

00280          return RTN_FAIL;

00281      }

00282  

00283      (void)MDS_IntLock();

00284  

00285      

00286      if(TASKDELAY != (TASKDELAY pstrTcb->strTaskOpt.ucTaskSta))

00287      {

00288          (void)MDS_IntUnlock();

00289  

00290          return RTN_FAIL;

00291      }

00292  

00293      pstrNode &pstrTcb->strDelayQue.strQueHead;

00294  

00295      

00296      if(DELAYWAITFEV != pstrTcb->strTaskOpt.uiDelayTick)

00297      {

00298          

00299          (void)MDS_ChainCurNodeDelete(&gstrDelayTab, pstrNode);

00300  

00301          

00302          pstrTcb->uiTaskFlag &= (~((U32)DELAYQUEFLAG));

00303      }

00304  

00305      

00306      pstrTcb->strTaskOpt.ucTaskSta &= ~((U8)TASKDELAY);

00307  

00308      

00309      pstrTcb->strTaskOpt.uiDelayTick RTN_TKDLBK;

00310  

00311      

00312      ucTaskPrio pstrTcb->ucTaskPrio;

00313      pstrChain &gstrReadyTab.astrChain[ucTaskPrio];

00314      pstrPrioFlag &gstrReadyTab.strFlag;

00315  

00316      

00317      MDS_TaskAddToSchedTab(pstrChain, pstrNode, pstrPrioFlag, ucTaskPrio);

00318  

00319      

00320      pstrTcb->strTaskOpt.ucTaskSta |= TASKREADY;

00321  

00322      (void)MDS_IntUnlock();

00323  

00324      

00325      MDS_TaskSwiSched();

00326  

00327      return RTN_SUCD;

00328  }

 

现在我们已经拥有了readydelay两种任务状态,那么在tick中断里就需要对这两个调度表进行操作。这个过程分为2步,先对delay表操作,将delay时间耗尽的任务从delay表拆除,挂入ready表,使之能参与到ready表的调度之中,然后再对ready表进行调度。

来看调度函数的代码:

00345  void MDS_TaskSched(void)

00346  {

00347      M_TCB* pstrTcb;

00348  

00349      

00350      MDS_TaskDelayTabSched();

00351  

00352      

00353      pstrTcb MDS_TaskReadyTabSched();

00354  

00355      

00356      MDS_TaskSwitch(pstrTcb);

00357  }

 

有关ready表调度的代码被封装到了353行的MDS_TaskReadyTabSched函数里面,本节新增的delay表调度代码被封装到350行的MDS_TaskDelayTabSched函数里面,下面来看看MDS_TaskDelayTabSched函数的代码:

00399  void MDS_TaskDelayTabSched(void)

00400  {

00401      M_TCB* pstrTcb;

00402      M_CHAIN* pstrChain;

00403      M_CHAIN* pstrNode;

00404      M_CHAIN* pstrDelayNode;

00405      M_CHAIN* pstrNextNode;

00406      M_PRIOFLAG* pstrPrioFlag;

00407      M_TCBQUE* pstrDelayQue;

00408      U32 uiTick;

00409      U8 ucTaskPrio;

00410  

00411      

00412      pstrDelayNode MDS_ChainEmpInq(&gstrDelayTab);

00413  

00414      

00415      if(NULL != pstrDelayNode)

00416      {

00417          

00418          while(1)

00419          {

00420              

00421              pstrDelayQue (M_TCBQUE*)pstrDelayNode;

00422              pstrTcb pstrDelayQue->pstrTcb;

00423              uiTick pstrTcb->uiStillTick;

00424  

00425              

00426              if(uiTick == guiTick)

00427              {

00428                  

00429                  pstrNextNode MDS_ChainCurNodeDelete(&gstrDelayTab, pstrDelayNode);

00430  

00431                  

00432                  pstrTcb->uiTaskFlag &= (~((U32)DELAYQUEFLAG));

00433  

00434                  

00435                  pstrTcb->strTaskOpt.ucTaskSta &= ~((U8)TASKDELAY);

00436  

00437                  

00438                  pstrTcb->strTaskOpt.uiDelayTick RTN_TKDLTO;

00439  

00440                  

00441                  pstrNode &pstrTcb->strTcbQue.strQueHead;

00442                  ucTaskPrio pstrTcb->ucTaskPrio;

00443                  pstrChain &gstrReadyTab.astrChain[ucTaskPrio];

00444                  pstrPrioFlag &gstrReadyTab.strFlag;

00445  

00446                  

00447                  MDS_TaskAddToSchedTab(pstrChain, pstrNode, pstrPrioFlag,

00448                                        ucTaskPrio);

00449  

00450                  

00451                  pstrTcb->strTaskOpt.ucTaskSta |= TASKREADY;

00452  

00453                  

00454                  if(NULL == pstrNextNode)

00455                  {

00456                      break;

00457                  }

00458                  else 

00459                  {

00460                      pstrDelayNode pstrNextNode;

00461                  }

00462              }

00463              else 

00464              {

00465                  break;

00466              }

00467          }

00468      }

00469  }

00412行,从delay表中获取第一个子节点pstrDelayNode

00415行,判断第一个子节点是否为空。

00418行,循环查询delay表中的任务。执行到此行说明delay链表不为空,说明有处于delay状态的任务。

000421行,将M_CHAIN型的指针pstrDelayNode强制转换为M_TCBQUE型的指针pstrDelayQue

00422行,通过pstrDelayQue获取TCB指针。

00423行,通过TCB指针获取到任务delay状态耗尽时的tick数值。

00426行,被查询的任务delay状态在当前tick已经耗尽,需要从delay状态转换为ready状态,走下面分支。

00429行,将该任务从delay表中拆除。

00432行,从TCB中的任务标志uiTaskFlag中去除任务处于delay表的标志。

00435行,去除任务的delay状态。

00438行,将delay耗尽的返回值存入strTaskOpt中的uiDelayTick变量,供MDS_TaskDelay函数返回时使用。

00441~00451行,将该任务添加到ready表中。

00454~00461行,若下个节点为空,说明delay表已经遍历完毕,退出调度函数。若下个节点不为空则继续查询。

00463~00466行,走到此分支说明当前节点delay时间还没有耗尽,因为delay表是按照delay耗尽时间从少到多的顺序排列的,因此后面节点的时间也不会耗尽,不需要再遍历,直接返回。

 

下面介绍一下与软中断相关的函数。

Mindows中使用MDS_TaskSwiSched函数触发软中断,MDS_TaskSwiSched函数里面使用MDS_RunInInt函数对芯片当前的状态做了一个判断,避免在中断中触发软中断造成异常,实际触发软中断的函数是MDS_TaskOccurSwi函数。

00137  void MDS_TaskSwiSched(void)

00138  {

00139      

00140      if(RTN_SUCD == MDS_RunInInt())

00141      {

00142          return;

00143      }

00144  

00145      

00146      MDS_TaskOccurSwi(SWI_TASKSCHED);

00147  }

 

MDS_RunInInt调用了汇编函数MDS_GetChipMode获取芯片的工作模式,若不是USR模式则表明当前是在中断中运行。

00155  U32 MDS_RunInInt(void)

00156  {

00157      

00158      if(MODE_USR != MDS_GetChipMode())

00159      {

00160          return RTN_SUCD;

00161      }

00162      else

00163      {

00164          return RTN_FAIL;

00165      }

00166  }

 

MDS_GetChipMode函数是一个汇编函数,CPSR寄存器中的bit4~bit0中保存的就是当前模式,它将CPSR寄存器的bit4~bit0作为函数返回值保存到R0中。

00114      .func MDS_GetChipMode

00115  MDS_GetChipMode:

00116  

00117      MRS    R0, CPSR

00118      AND    R0, R0, #0x1F

00119      BX     R14

00120  

00121      .endfunc

 

MDS_TaskOccurSwi函数也是一个汇编函数,它有一个入口参数,用来表明该软中断的功能,C语言的函数原型为

MDS_TaskOccurSwi(U32 uiSwiNo);

汇编语言的源代码为:

00127      .func MDS_TaskOccurSwi

00128  MDS_TaskOccurSwi:

00129  

00130      SWI    0

00131      BX     R14

00132  

00133      .endfunc

00130行,执行“SWI”汇编指令,该指令会触发软中断,将PC指针跳转到软中断向量表。在startup.s文件的软中断向量表里面使用MDS_SwiContextSwitch函数作为软中断的服务函数,它的功能与tick中断的MDS_TickContextSwitch函数功能非常相似,都是用来调度任务的,但还是有一些不同的地方。

00054      .func MDS_SwiContextSwitch

00055  MDS_SwiContextSwitch:

00056  

00057      @保存接口寄存器

00058      ADD    R14, R14, #4

00059      STMDB  R13!, {R0 R3, R12, R14}

00060  

00061      @调用C语言SWI中断处理函数

00062      LDR    R3, =MDS_SwiIsr

00063      MOV    R14, PC

00064      BX     R3

00065  

00066      @保存当前任务的堆栈信息

00067      LDR    R0, =gpstrCurTaskSpAddr

00068      LDR    R14, [R0]

00069      MRS    R0, SPSR

00070      STMIA  R14!, {R0}

00071      LDMIA  R13!, {R0 R3, R12}

00072      STMIA  R14, {R0 R14}^

00073      ADD    R14, R14, #0x3C

00074      LDMIA  R13!, {R0}

00075      STMIA  R14, {R0}

00076  

00077      @任务调度完毕恢复将要运行任务现场

00078      LDR    R0, =gpstrNextTaskSpAddr

00079      LDR    R14, [R0]

00080      LDMIA  R14, {R0}

00081      MSR    SPSR, R0

00082      LDMIB  R14, {R0 R14}^

00083      NOP

00084      ADD    R14, R14, #0x40

00085      LDMIA  R14, {R14}

00086      SUB    R14, R14, #4

00087      MOVS   PC, R14

00088  

00089      .endfunc

00058行,将返回的LR寄存器地址多加4字节,这是因为在进入MDS_TickContextSwitch函数时硬件会自动多加4字节,为了使MDS_SwiContextSwitch函数与MDS_TickContextSwitch函数的栈格式相同,这里先多加4字节,在MDS_SwiContextSwitch函数返回前会再多减去4字节。

00062~00064行,调用软中断处理函数MDS_SwiIsr。注意,这里不能使用R0寄存器,因为此时R0中保存的是MDS_TaskOccurSwi函数传递进来的入口参数。

00067~00085行,寄存器组备份还原过程,与MDS_TickContextSwitch函数完全一致。

00086行,函数返回前减去前面多加的4字节。

00087行,软中断调度完成,使用MOVS指令返回,这点与tick中断所使用的ISR中断不同。

 

MDS_SwiIsr函数才是真正的软中断处理过程,使用C语言编写,所有的软中断处理过程都在这个函数内实现。

00094  void MDS_SwiIsr(U32 uiSwiNo)

00095  {

00096      

00097      if(SWI_TASKSCHED == (SWI_TASKSCHED uiSwiNo))

00098      {

00099          

00100          MDS_TaskReadySched();

00101      }

00102      else 

00103      {

00104          

00105          MDS_TaskSwitch(gpstrCurTcb);

00106  

00107          switch(uiSwiNo)

00108          {

00109              case SWI_INTENABLE:     

00110  

00111                  MDS_IntEnable();

00112  

00113                  break;

00114  

00115              case SWI_INTDISABLE:    

00116  

00117                  MDS_IntDisable();

00118  

00119                  break;

00120  

00121              

00122  

00123              default:

00124  

00125                  break;

00126  

00127              

00128          }

00129      }

00130  }

00094行,入口参数uiSwiNoMDS_TaskOccurSwi函数开始,被保存在R0中,经历了MDS_SwiContextSwitch函数传递给MDS_SwiIsr函数。

00097行,软中断调度的中断走此分支。

00100行,在软中断中调度任务,只需要调度ready表。delay表的调度是以tick为周期的,在tick中断中才能调度。

00102行,非软中断调度产生的软中断走此分支。

00105行,更新调度中所使用的全局变量。尽管是非软中断调度,但由于该函数的上级函数MDS_SwiContextSwitch会对寄存器组进行备份、恢复,因此此处也需要更新一下调度中所使用的全局变量。

00107行,选择不同的软中断命令分支。

00109~00113行,解锁中断命令,调用MDS_IntEnable函数解锁中断。MDS_IntEnable函数是汇编函数,直接修改CPSR中的全局中断控制位允许中断产生。

00115~00119行,锁中断命令,调用MDS_IntDisable函数锁中断。MDS_IntDisable函数是汇编函数,直接修改CPSR中的全局中断控制位禁止中断产生。

00121~00127行,用户可以将自定义的软中断命令放在此处执行。

 

MDS_IntEnableMDS_IntDisable函数直接操作的对象是SVC模式下SPSRSVC寄存器,当程序从SVC模式返回到USR模式时,SPSRSVC寄存器就会被恢复到CPSR中,达到修改CPSR寄存器控制中断产生的目的。

00139      .func MDS_IntEnable

00140  MDS_IntEnable:

00141  

00142      MRS    R0, SPSR

00143      LDR    R1, =(CPSR_IRQENABLE CPSR_FIQENABLE)

00144      AND    R0, R0, R1

00145      MSR    SPSR, R0

00146      BX     R14

00147  

00148  .endfunc

00154      .func MDS_IntDisable

00155  MDS_IntDisable:

00156  

00157      MRS    R0, SPSR

00158      LDR    R1, =(CPSR_IRQDISABLE CPSR_FIQDISABLE)

00159      ORR    R0, R0, R1

00160      MSR    SPSR, R0

00161      BX     R14

00162  

00163      .endfunc

 

提供给用户使用的锁中断函数是MDS_IntLock,解锁中断函数是MDS_IntUnlock,它们同软中断调度函数一样,都是通过调用MDS_TaskOccurSwi函数触发软中断,通过入口参数区分功能,在软中断服务函数MDS_SwiIsr里实现自己的功能。

00650  U32 MDS_IntLock(void)

00651  {

00652      

00653      if(NULL == gpstrCurTcb)

00654      {

00655          return RTN_SUCD;

00656      }

00657  

00658      

00659      if(RTN_SUCD == MDS_RunInInt())

00660      {

00661          return RTN_SUCD;

00662      }

00663  

00664      

00665      if(0 == guiIntLockCounter)

00666      {

00667          MDS_TaskOccurSwi(SWI_INTDISABLE);

00668  

00669          guiIntLockCounter++;

00670  

00671          return RTN_SUCD;

00672      }

00673      

00674      else if(guiIntLockCounter 0xFFFFFFFF)

00675      {

00676          guiIntLockCounter++;

00677  

00678          return RTN_SUCD;

00679      }

00680      else 

00681      {

00682          return RTN_FAIL;

00683      }

00684  }

00653~00656行,在还没有进入操作系统状态前不能执行此函数。

00659~00662行,在中断中不能执行此函数。

00665~00672行,guiIntLockCounter变量里存放的是锁中断、解锁中断的次数,初始化为0,当guiIntLockCounter0时,说明是第一次执行该函数,只有第一次执行该函数时才操作硬件寄存器,执行锁中断动作。

需要注意一点,669guiIntLockCounter变量自加需要在667MDS_TaskOccurSwi函数之后执行,因为MDS_IntLock函数可能会重入,这样做在函数重入时,不会使guiIntLockCounter变量计数产生错误。

00674~00679行,guiIntLockCounter变量没有溢出则做自加操作,返回锁中断成功,是一个虚假的锁中断过程。

00680~00683行,guiIntLockCounter变量溢出,返回成功。

 

00693  U32 MDS_IntUnlock(void)

00694  {

00695      

00696      if(NULL == gpstrCurTcb)

00697      {

00698          return RTN_SUCD;

00699      }

00700  

00701      

00702      if(RTN_SUCD == MDS_RunInInt())

00703      {

00704          return RTN_SUCD;

00705      }

00706  

00707      

00708      if(guiIntLockCounter 1)

00709      {

00710          guiIntLockCounter--;

00711  

00712          return RTN_SUCD;

00713      }

00714      

00715      else if(1 == guiIntLockCounter)

00716      {

00717          guiIntLockCounter--;

00718  

00719          MDS_TaskOccurSwi(SWI_INTENABLE);

00720  

00721          return RTN_SUCD;

00722      }

00723      else 

00724      {

00725          return RTN_FAIL;

00726      }

00727  }

MDS_IntUnlock函数与MDS_IntLock函数,非常相似,只有当guiIntLockCounter变量为1时才操作硬件寄存器,执行解锁中断动作。

本节操作系统部分的内容就介绍到这里。

阅读全文
0 0
原创粉丝点击