shivency

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

0、前言

这节既然谈到时间管理,便需要一个度量,来衡量系统执行的时间。我们可以用时间片,也可以用现实生活中的分秒。

ucos中的时间片的具体设置与硬件环境有关,这里先不进行讨论。

然而在多任务情况下,每个时间片(也叫时间中断)都要执行任务的调度,这种调度称为任务级任务调度(上一节已学习了中断级任务调度)。

ucos在每个时间片都要进行任务调度。调度的结果或者是返回原来的任务继续执行,或者是因为找到了就绪的更高优先级的任务,而让任务运行。这个时间片可以是10ms或其他值。如果时间太长,高优先级的就绪任务可能等待时间过长,如果时间太短,花费在操作系统调度上的时间就显得过长,系统的吞吐量就变小。

有关任务级任务调度的具体学习将在下一节讨论,这一节主要学习时间管理。

 

1、思维导图

 

2、时间管理主要数据结构

1 #if OS_TIME_GET_SET_EN > 0
2 OS_EXT  volatile  INT32U  OSTime;                   /* Current value of system time (in ticks)         */
3 #endif

 

3、时间的获取和设置

源码:

时间的获得

 1 #if OS_TIME_GET_SET_EN > 0
 2 INT32U  OSTimeGet (void)
 3 {
 4     INT32U     ticks;
 5 #if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
 6     OS_CPU_SR  cpu_sr = 0;
 7 #endif
 8 
 9 
10 
11     OS_ENTER_CRITICAL();
12     ticks = OSTime;
13     OS_EXIT_CRITICAL();
14     return (ticks);
15 }
16 #endif

时间的设置

 1 #if OS_TIME_GET_SET_EN > 0
 2 void  OSTimeSet (INT32U ticks)
 3 {
 4 #if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
 5     OS_CPU_SR  cpu_sr = 0;
 6 #endif
 7 
 8 
 9 
10     OS_ENTER_CRITICAL();
11     OSTime = ticks;
12     OS_EXIT_CRITICAL();
13 }
14 #endif

 

4、任务延时函数OSTimeDly

源码:

 1 void  OSTimeDly (INT16U ticks)
 2 {
 3     INT8U      y;
 4 #if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
 5     OS_CPU_SR  cpu_sr = 0;
 6 #endif
 7 
 8 
 9 
10     if (OSIntNesting > 0) {                      /* See if trying to call from an ISR                  */
11         return;
12     }
13     if (ticks > 0) {                             /* 0 means no delay!                                  */
14         OS_ENTER_CRITICAL();
15         y            =  OSTCBCur->OSTCBY;        /* Delay current task                                 */
16         OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
17         if (OSRdyTbl[y] == 0) {
18             OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
19         }
20         OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */
21         OS_EXIT_CRITICAL();
22         OS_Sched();                              /* Find next task to run!                             */
23     }
24 }

line 10~line 12,表示如果有中断嵌套的话,则不延时;中断的存在便是为了实时处理任务。

不过书上还有一个条件判断,这儿没有。

1 if OSLockingNesting > 0u
2     return ;

line 13~line 19,在就绪表中取消当前任务的就绪标志,即对当前任务进行延时。

line 20,给任务块的OSTCBDly项赋值延时时间。操作系统在每个时间片都要对每个OSTCBDly大于0的任务块OSTCBDly项进行减1操作和进行调度,当OSTCBDly减到0时,就可以恢复至就绪态。

这里需要注意的是,如果需要延时一个时间片,最好调用两个OSTCBDly(2),具体解释如下,不爱看记住结论就好。

1 需要注意的是,如果将任务延时1个时间片,调用OSTimeDly(1),会不会产生正确的结果呢?
2 
3 回答是否定的。这是因为任务在调用时间延时函数的时候可能已经马上就要发生时间中断了,那么设置OSTCBDly的值为1,想延时10ms,然后系统切换到一个新的任务运行。在可能极短的时间,如0.5ms的时候就进入时钟中断服务程序,立刻将OSTCBDly的值减到0了。调度器在调度的时候就会恢复这个才延时了0.5ms的任务。可见,OSTimeDly的误差最大应该是1个时间片的长度,OSTCBDly(1)不会刚好延时10ms,
4 
5 如果真的需要延时一个时间片,最好调用OSTCBDly(2)。

 

流程图

 

 

5、任务按分秒延时函数OSTimeDlyHMSM()

这同样也是将任务延时(阻塞)一段时间,上一个函数是以时间片为单位,而这个函数是以小时、分、秒为单位。

源码

 1 #if OS_TIME_DLY_HMSM_EN > 0
 2 INT8U  OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U ms)
 3 {
 4     INT32U ticks;
 5     INT16U loops;
 6 
 7 
 8     if (OSIntNesting > 0) {                      /* See if trying to call from an ISR                  */
 9         return (OS_ERR_TIME_DLY_ISR);
10     }
11 #if OS_ARG_CHK_EN > 0
12     if (hours == 0) {
13         if (minutes == 0) {
14             if (seconds == 0) {
15                 if (ms == 0) {
16                     return (OS_ERR_TIME_ZERO_DLY);
17                 }
18             }
19         }
20     }
21     if (minutes > 59) {
22         return (OS_ERR_TIME_INVALID_MINUTES);    /* Validate arguments to be within range              */
23     }
24     if (seconds > 59) {
25         return (OS_ERR_TIME_INVALID_SECONDS);
26     }
27     if (ms > 999) {
28         return (OS_ERR_TIME_INVALID_MS);
29     }
30 #endif
31                                                  /* Compute the total number of clock ticks required.. */
32                                                  /* .. (rounded to the nearest tick)                   */
33     ticks = ((INT32U)hours * 3600L + (INT32U)minutes * 60L + (INT32U)seconds) * OS_TICKS_PER_SEC
34           + OS_TICKS_PER_SEC * ((INT32U)ms + 500L / OS_TICKS_PER_SEC) / 1000L;
35     loops = (INT16U)(ticks >> 16);               /* Compute the integral number of 65536 tick delays   */
36     ticks = ticks & 0xFFFFL;                     /* Obtain  the fractional number of ticks             */
37     OSTimeDly((INT16U)ticks);
38     while (loops > 0) {
39         OSTimeDly((INT16U)32768u);
40         OSTimeDly((INT16U)32768u);
41         loops--;
42     }
43     return (OS_ERR_NONE);
44 }
45 #endif

由于两者功能是一样的,所以结构也很类似。

line 8~ line 30负责检查参数,而后将形参转变成时间片,再进行延时。

 

6、延时恢复函数OSTimeDlyResume()

任务在延时之后,进入阻塞态。当延时时间到了就从阻塞态恢复到就绪态,可以被操作系统调度执行。但是,并非回到就绪态就只有这么一种方法,同样可以通过调用延时恢复函数OSTimeDlyResume()恢复该任务到就绪态。

源码

 1 #if OS_TIME_DLY_RESUME_EN > 0
 2 INT8U  OSTimeDlyResume (INT8U prio)
 3 {
 4     OS_TCB    *ptcb;
 5 #if OS_CRITICAL_METHOD == 3                                    /* Storage for CPU status register      */
 6     OS_CPU_SR  cpu_sr = 0;
 7 #endif
 8 
 9 
10 
11     if (prio >= OS_LOWEST_PRIO) {
12         return (OS_ERR_PRIO_INVALID);
13     }
14     OS_ENTER_CRITICAL();
15     ptcb = OSTCBPrioTbl[prio];                                 /* Make sure that task exist            */
16     if (ptcb == (OS_TCB *)0) {
17         OS_EXIT_CRITICAL();
18         return (OS_ERR_TASK_NOT_EXIST);                        /* The task does not exist              */
19     }
20     if (ptcb == OS_TCB_RESERVED) {
21         OS_EXIT_CRITICAL();
22         return (OS_ERR_TASK_NOT_EXIST);                        /* The task does not exist              */
23     }
24     if (ptcb->OSTCBDly == 0) {                                 /* See if task is delayed               */
25         OS_EXIT_CRITICAL();
26         return (OS_ERR_TIME_NOT_DLY);                          /* Indicate that task was not delayed   */
27     }
28 
29     ptcb->OSTCBDly = 0;                                        /* Clear the time delay                 */
30     if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
31         ptcb->OSTCBStat     &= ~OS_STAT_PEND_ANY;              /* Yes, Clear status flag               */
32         ptcb->OSTCBStatPend  =  OS_STAT_PEND_TO;               /* Indicate PEND timeout                */
33     } else {
34         ptcb->OSTCBStatPend  =  OS_STAT_PEND_OK;
35     }
36     if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {  /* Is task suspended?                   */
37         OSRdyGrp               |= ptcb->OSTCBBitY;             /* No,  Make ready                      */
38         OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
39         OS_EXIT_CRITICAL();
40         OS_Sched();                                            /* See if this is new highest priority  */
41     } else {
42         OS_EXIT_CRITICAL();                                    /* Task may be suspended                */
43     }
44     return (OS_ERR_NONE);
45 }
46 #endif

事实上OSTimeDlyResume()能处理的情况很多,一来可以恢复使用延时的任务(取消其延时,立刻就绪),二来能恢复设置了超时的函数,但对于采用OSTaskSuspend挂起的任务,则不允许使用这个函数恢复。因此,这个函数的代码有些复杂。

函数形参为被恢复任务的优先级。

line11~line 27为参数检查,避免恢复那些既没有延时也没有超时的函数。

之后多个条件判断中都出现了OSTCBStat这个数据结构,其主要作用是判断该任务块的状态——延时、超时,或者被挂起。然后针对不同情况,以宏清任务块的数据结构。

 

流程图

 

 

7、参考

基于µC/OS-II的时间片调度法设计

uC/OS-II中的时间

 

下一节将切回第二章,讨论任务的调度和多任务启动的内容。

 

posted on 2013-09-10 13:11  shivency  阅读(456)  评论(0编辑  收藏  举报