• 沒有找到結果。

Call OSTaskStkChk() by specifying the priority of the task you desire to check

在文檔中 µC/OS-II Goals Preface (頁 116-128)

Task Management

4. Call OSTaskStkChk() by specifying the priority of the task you desire to check

OSTaskStkChk() computes the amount of free stack space by ‘walking’from the bottom of the stack and counting the number of zero entries on the stack until a non-zero value is found F4-2(5). Note that stack entries are checked using the data type of the stack (see OS_STK in OS_CPU.H ). In other words, if a stack entry is 32 -bit wide then the comparison for a zero value is done using 32 bits. The amount of stack space used F4-2(8) is obtained by subtracting the number of zero value entries F4-2(6) from the stack size you specified in OSTaskCreateExt().

OSTaskStkChk() actually places the number of bytes free and the number of bytes used in a data structure of type OS_STK_DATA (see uCOS_II.H). You should note that at any given time, the stack pointer for the task being checked may be pointing somewhere between the initial Top-Of-Stack (TOS) and the deepest stack growth F4 -2(7).

Also, every time you call OSTaskStkChk(), you may get a different value for the amount of free space on the stack until your task has reached its deepest growth F4-2(5).

You will need to run the application long enough and under your worst case conditions to get proper numbers. Once OSTaskStkChk() provides you with t he worst case stack requirement, you can go back and set the final size of your

stack. You should accommodate for system expansion so make sure you allocate between 10 and 25% more. What you should get from stack checking is a ‘ballpark’figure; you are not looking for an exact stack usage.

The code for OSTaskStkChk() is shown in listing 4.10. The data structure OS_STK_DATA (see uCOS_II.H) is used to hold information about the task stack. I decided to use a data structure for two reasons. First, I con sider OSTaskStkChk() to be a ‘query’ type function and I wanted to have all query functions work the same way – return data about the query in a data structure. Second, passing data in a data structure is both efficient and allows me to add additional fie lds in the future without changing the API (Application Programming Interface) of OSTaskStkChk().

For now, OS_STK_DATA only contains two fields: OSFree and OSUsed. As you can see, you invoke OSTaskStkChk() by specifying the priority of the task you desire to perform stack checking on. If you specify OS_PRIO_SELF L4.10(1) then it is assumed that you desire to know the stack information about the current task.

Obviously, the task must exist L4.10(2). To perform stack checking, you must have created the task using OSTaskCreateExt() and you must have passed the option OS_TASK_OPT_STK_CHK L4.10(3). If all the proper conditions are met, OSTaskStkChk() computes the free stack space as described above by ‘walking’from the bottom of stack until a non-zero stack entry is encountered L4.10(4). Finally, the information to store in OS_STK_DATA is computed L4.10(5). Note that the function computes the actual number of bytes free and the number of bytes used on the stack as opposed to the number of elements. Obvio usly, the actual stack size (in bytes) can be obtained by adding these two values.

INT8U OSTaskStkChk (INT8U prio, OS_STK_DATA *pdata) {

OS_TCB *ptcb;

OS_STK *pchk;

INT32U free;

INT32U size;

pdata->OSFree = 0;

pdata->OSUsed = 0;

if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { return (OS_PRIO_INVALID);

}

OS_ENTER_CRITICAL();

if (prio == OS_PRIO_SELF) { (1) prio = OSTCBCur->OSTCBPrio;

}

ptcb = OSTCBPrioTbl[prio];

if (ptcb == (OS_TCB *)0) { (2) OS_EXIT_CRITICAL();

return (OS_TASK_NOT_EXIST);

}

if ((ptcb->OSTCBOpt & OS_TASK_OPT_STK_CHK) == 0) { (3) OS_EXIT_CRITICAL();

return (OS_TASK_OPT_ERR);

}

free = 0; (4) size = ptcb->OSTCBStkSize;

pchk = ptcb->OSTCBStkBottom;

OS_EXIT_CRITICAL();

#if OS_STK_GROWTH == 1 while (*pchk++ == 0) { free++;

}

#else

while (*pchk-- == 0) { free++;

}

#endif

pdata->OSFree = free * sizeof(OS_STK); (5) pdata->OSUsed = (size - free) * sizeof(OS_STK);

return (OS_NO_ERR);

}

Listing 4.10, Stack checking function

4.04 Deleting a Task, OSTaskDel()

It is sometimes necessary to delete a task. Deleting a task means that the task will be returned to the DORMANT state (see Section 3.02, Task States) and does not mean that the code for the task will be deleted. The task code is simply no longer scheduled by µC/OS -II. You delete a task by calling OSTaskDel() and the code for this function is shown in listing 4.11. Here, we start by making sure that you are not attempting to delete the idle task because this is not allowed L4.11(1). You are, however, allowed to delete the statistic task L4.11(2). OSTaskDel() then checks to make sure you are not attempting to delete a task from within an ISR which is again not allowed L4.11(3). The caller can delete itself by specifying OS_PRIO_SELF as the argument L4.11(4). We then verify that the task to delete does in fact exist L4.11(5). This test will obviously pass if you specified OS_PRIO_SELF. I didn’t want to create a separate case for this situation because it would have increased code size and thus execution time.

Once all conditions are satisfied, the OS_TCB is removed from all the possible µC/OS -II data structures.

OSTaskDel() does this in two parts to reduce interrupt latency. First, if the task is in the re ady list, it is removed L4.11(6). We then check to see if the task is in a list waiting for a mailbox, a queue or a semaphore and if so, the task is removed from that list L4.11(7). Next, we force the delay count to zero to make sure that the tick ISR wi ll not ready this task once we re-enable interrupts L4.11(8). Finally, we set the task’s .OSTCBStat flag to OS_STAT_RDY.

Note that we are not trying to make the task ready, we are simply preventing another task or an ISR from resuming this task (i.e. in case the other task or ISR called OSTaskResume() L4.11(9)). This situation could occur because we will be re -enabling interrupts L4.11(11) so, an ISR can make a higher priority task ready which could resume the task we are trying to delete. Instead of setting the task’s .OSTCBStat flag to OS_STAT_RDY, I could have simply cleared the OS_STAT_SUSPEND bit (which would have been clearer) but, this takes slightly more processing time.

At this point, the task to delete cannot be made ready to run by another task or an ISR because, it’s been removed from the ready list, it’s not waiting for an event to occur, it’s not waiting for time to expire and cannot be resumed. In other words the task is DORMANT for all intents and purposes. Because of this, I must prevent the scheduler L4.11(10) from switching to another task because, if the current task is almost deleted then it would not be able to be rescheduled!

At this point, we want to re -enable interrupts in order to reduce interrupt latency L4.11(11). We could thus service an interrupt but because we incremented OSLockNesting the ISR would return to the interrupted task. You should note that we are still not done with the deletion process because we need to unlink the OS_TCB from the TCB chain and return the OS_TCB to the free OS_TCB list.

Note also that I call a ‘dummy’ function (OSDummy() ) immediately after calling OS_EXIT_CRITICAL() L4.11(12). I do this because I want to make sure that the processor will execute at least one instruction with interrupts enabled. On many processors, executing an interrupt enable instruction forces the CPU to have interrupts disabled until the end of the next instruction! The Intel 80x86 and Zilog Z-80 processors actually work like this. Enabling and immediately disabling interrupts would behave just as if I didn’t enable interrupts. This would of course increase interrupt latency. Calling OSDummy() thus ensures that I will execute a call and a return instruction before re-disabling interrupts. You could certainly replace OSDummy() with a macro that executes a ‘no-operation’

instruction and thus slightly reduce the execution time of OSTaskDel(). I didn’t think it was worth the effort of creating yet another macro that would require porting.

We can now continue with the deletion process of the task. After we re -disable interrupts, re -enable scheduling by decrementing the lock nesting counter L4.11(13). We then call the user definable task delete hook, OSTaskDelHook() L4.11(14). This allows user defined TCB extensions to be relinquished. Next, we decrement the task counter to indicate that there is one less task being managed by µC/OS -II. We remove the OS_TCB from the priority table by simply replacing the link to the OS_TCB of the task being deleted with a NULL pointer L4.11(15).

We then remove the OS_TCB of the task to delete from the doubly -linked list of OS_TCBs that starts at OSTCBList L4.11(16). You should note that there is no need to check for the case where ptcb->OSTCBNext == 0 because we cannot delete the idle task which happens to always be at the end of the chain. The OS_TCB is returned to the free list of OS_TCBs to allow another task to be created L4.11(17). Last, but not least, the scheduler L4.11(18) is called to see if a higher priority task has been made ready to run by an ISR that would have occurred when we re-enabled interrupts at step L4.11(11).

INT8U OSTaskDel (INT8U prio) {

OS_TCB *ptcb;

OS_EVENT *pevent;

if (prio == OS_IDLE_PRIO) { (1) return (OS_TASK_DEL_IDLE);

}

if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (2) return (OS_PRIO_INVALID);

}

OS_ENTER_CRITICAL();

if (OSIntNesting > 0) { (3)

OS_EXIT_CRITICAL();

return (OS_TASK_DEL_ISR);

}

if (prio == OS_PRIO_SELF) { (4) prio = OSTCBCur->OSTCBPrio;

}

if ((ptcb = OSTCBPrioTbl[prio]) != (OS_TCB *)0) { (5) if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { (6) OSRdyGrp &= ~ptcb->OSTCBBitY;

}

if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) { (7) if ((pevent->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { pevent->OSEventGrp &= ~ptcb->OSTCBBitY;

} }

ptcb->OSTCBDly = 0; (8) ptcb->OSTCBStat = OS_STAT_RDY; (9) OSLockNesting++; (10) OS_EXIT_CRITICAL(); (11) OSDummy(); (12) OS_ENTER_CRITICAL();

OSLockNesting--; (13) OSTaskDelHook(ptcb); (14) OSTaskCtr--;

OSTCBPrioTbl[prio] = (OS_TCB *)0; (15) if (ptcb->OSTCBPrev == (OS_TCB *)0) { (16) ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0;

OSTCBList = ptcb->OSTCBNext;

} else {

ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext;

ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev;

}

ptcb->OSTCBNext = OSTCBFreeList; (17) OSTCBFreeList = ptcb;

OS_EXIT_CRITICAL();

OSSched(); (18) return (OS_NO_ERR);

} else {

OS_EXIT_CRITICAL();

return (OS_TASK_DEL_ERR);

} }

Listing 4.11, Task Delete

4.05 Requesting to delete a task, OSTaskDelReq()

Sometimes, a task may own resources such as a memory buffers or a semaphore. If another task attempts to delete this task then, the resource would not be freed and thus would be lost. In this type of situation, you would need to somehow signal the task that owns these resources to tell it to delete itself when it’s done with the resources. You can accomplish this with the OSTaskDelReq() function.

Both the requestor and the task to be deleted need to call OSTaskDelReq(). The requestor code would look as shown in listing 4.12. The task that makes the request needs to determine what condition(s) will cause a request for the task to be deleted L4.12(1). In other words, your application will determine what condition(s) will lead to this decision.

If the task needs to be deleted then you call OSTaskDelReq() by passing the priority of the task to be deleted L4.12(2). If the task to delete does not exist then OSTaskDelReq() returns OS_TASK_NOT_EXIST. You would get this if the task to delete has already been deleted or, it has not been created yet. If the return value is OS_NO_ERR then, the request has been accepted but the task has not been deleted yet. You may want to wait until the task to be

deleted does in fact delete itself. You can do this by delaying the requestor for a certain amount of time like I did in L4.12(3). I decided to delay for one tick but you can certainly wait longer if needed. When the requested task eventually deletes itself, the return value in L4.12(2) would be OS_TASK_NOT_EXIST and the loop would exit L4.12(4).

void RequestorTask (void *pdata) {

INT8U err;

pdata = pdata;

for (;;) {

/* Application code */

if (‘TaskToBeDeleted()’ needs to be deleted) { (1) while (OSTaskDelReq(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIST) { (2) OSTimeDly(1); (3) }

}

/* Application code */ (4) }

}

Listing 4.12, Requesting a task to delete itself.

The code for the task that needs to delete itself is shown in listing 4.13. This task basically needs to ‘poll’ a flag that resides inside the task’s OS_TCB. The value of this flag is obtained by calling OSTaskDelReq(OS_PRIO_SELF).

When OSTaskDelReq() returns OS_TASK_DEL_REQ L4.13(1) to its caller, it indicates that another task has requested that this task needs to be deleted. In this case, the task to be deleted releases any resources owned L4.13(2) and calls OSTaskDel(OS_PRIO_SELF) to delete itself L4.13(3). As previously mentioned, the code for the task is not actually deleted. Instead, µC/OS-II will simply not schedule the task for execution. In other word, the task code will no longer run. You can, however, re -create the task by calling either OSTaskCreate() or OSTaskCreateExt().

void TaskToBeDeleted (void *pdata) {

INT8U err;

pdata = pdata;

for (;;) {

/* Application code */

if (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { (1) Release any owned resources; (2) De-allocate any dynamic memory;

OSTaskDel(OS_PRIO_SELF); (3) } else {

/* Application code */

} } }

Listing 4.13, Requesting a task to delete itself.

The code for OSTaskDelReq() is shown in listing 4.14. As usual, we need to check for boundary conditions. First, we notify the caller in case he requests to delete the idle task L4.14(1). Next, we must ensure that the caller is not trying to request to delete an invalid priority L4.14(2). If the caller is the task to be deleted, the flag stored in the OS_TCB will be returned L4.14(3). If you specified a task with a priority other than OS_PRIO_SELF then, if the task

exist L4.14(4), we set the internal flag for that task L4.14(5). If the task does not exist, we return OS_TASK_NOT_EXIST to indicate that the task must have deleted itself L4.14(6).

INT8U OSTaskDelReq (INT8U prio) {

BOOLEAN stat;

INT8U err;

OS_TCB *ptcb;

if (prio == OS_IDLE_PRIO) { (1) return (OS_TASK_DEL_IDLE);

}

if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (2) return (OS_PRIO_INVALID);

}

if (prio == OS_PRIO_SELF) { (3) OS_ENTER_CRITICAL();

stat = OSTCBCur->OSTCBDelReq;

OS_EXIT_CRITICAL();

return (stat);

} else {

OS_ENTER_CRITICAL();

if ((ptcb = OSTCBPrioTbl[prio]) != (OS_TCB *)0) { (4) ptcb->OSTCBDelReq = OS_TASK_DEL_REQ; (5) err = OS_NO_ERR;

} else {

err = OS_TASK_NOT_EXIST; (6) }

OS_EXIT_CRITICAL();

return (err);

} }

Listing 4.14, OSTaskDelReq().

4.06 Changing a Task’s Priority, OSTaskChangePrio()

When you create a task, you assign the task a priority. At run-time, you can change the priority of any task by calling OSTaskChangePrio(). In other words, µC/OS-II allows you to change priorities dynamically.

The code for OSTaskChangePrio() is shown in listing 4.15. You cannot change the priority of the idle task L4.15(1). You can either change the priority of the calling task or another task. To change the priority of the calling task you can must either specify the ‘old’ priority of that task or specify OS_PRIO_SELF and OSTaskChangePrio() will determine what the priority of the calling task is for you. You must also specify the

‘new’(i.e. desired) priority. Because µC/OS -II cannot have multiple tasks running at the same priority, we need to check that the desired priority is available L4.15(2). If the desired priority is available, µC/OS-II reserves the priority by loading ‘something’in the OSTCBPrioTbl[] thus reserving that entry L4.15(3). This allows us to re-enable interrupts and know that no other task can either create a task at the desired priority or have another task call OSTaskChangePrio() by specifying the same ‘new’priority. This is done so that we can pre -compute some values that will be stored in the task’s OS_TCB L4.15(4). These values are used to put or remove the task in or from the ready list (s ee section 3.04, Ready List).

We then check to see if the current task is attempting to change its priority L4.15(5). Next, we need to see if the task for which we are trying to change the priority exist L4.15(6). Obviously, if it’s the current task then this test will succeed. However, if we are trying to change the priority of a task that doesn’t exist then, we must relinquish the

‘reserved’priority back to the priority table, OSTCBPrioTbl[] L4.15(17), and return an error code to the caller.

We now remove the pointer to the OS_TCB of the task from the priority table by inserting a NULL pointer L4.15(7).

This will make the ‘old’priority available for reuse. Then, we check to see if the task for which we are changing the priority is ready-to-run L4. 15(8). If it is, it must be removed from the ready list at the current priority L4.15(9) and inserted back in the ready list at the new priority L4.15(10). Note here that we use our pre -computed values L4.15(4) to insert the task in the ready list.

If t he task is ready then, it could be waiting on a semaphore, a mailbox or a queue. We know that the task is waiting for one of these events if the OSTCBEventPtr is non-NULL L4.15(11). If the task is waiting for an event, we must remove the task from the wa it list (at the old priority) of the event control block (see section 6.00, Event Control Block) and insert the task back in the wait list but this time at the new priority L4.15(12). The task could be waiting for time to expire (see chapter 5, Time Management) or the task could be suspended (see section 4.07, Suspending a Task, OSTaskSuspend()). In these cases, items L4.15(8) through L4.15(12) would be skipped.

Next, we store a pointer to the task’s OS_TCB in the priority table, OSTCBPrioTbl[] L4.15(13). The new priority is saved in the OS_TCB L4.15(14) and the pre -computed values are also saved in the OS_TCB L4.15(15). After we exit the critical section, the scheduler is called in case the new priority is higher than the old priority or the priority of the calling task L4.15(16).

INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio) {

OS_TCB *ptcb;

OS_EVENT *pevent;

INT8U x;

INT8U y;

INT8U bitx;

INT8U bity;

if ((oldprio >= OS_LOWEST_PRIO && oldprio != OS_PRIO_SELF) || (1) newprio >= OS_LOWEST_PRIO) {

return (OS_PRIO_INVALID);

}

OS_ENTER_CRITICAL();

if (OSTCBPrioTbl[newprio] != (OS_TCB *)0) { (2) OS_EXIT_CRITICAL();

return (OS_PRIO_EXIST);

} else {

OSTCBPrioTbl[newprio] = (OS_TCB *)1; (3) OS_EXIT_CRITICAL();

y = newprio >> 3; (4) bity = OSMapTbl[y];

x = newprio & 0x07;

bitx = OSMapTbl[x];

OS_ENTER_CRITICAL();

if (oldprio == OS_PRIO_SELF) { (5) oldprio = OSTCBCur->OSTCBPrio;

}

if ((ptcb = OSTCBPrioTbl[oldprio]) != (OS_TCB *)0) { (6) OSTCBPrioTbl[oldprio] = (OS_TCB *)0; (7) if (OSRdyTbl[ptcb->OSTCBY] & ptcb->OSTCBBitX) { (8) if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { (9) OSRdyGrp &= ~ptcb->OSTCBBitY;

}

OSRdyGrp |= bity; (10) OSRdyTbl[y] |= bitx;

} else {

if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) { (11)

if ((pevent->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { pevent->OSEventGrp &= ~ptcb->OSTCBBitY;

}

pevent->OSEventGrp |= bity; (12) pevent->OSEventTbl[y] |= bitx;

} }

OSTCBPrioTbl[newprio] = ptcb; (13) ptcb->OSTCBPrio = newprio; (14) ptcb->OSTCBY = y; (15) ptcb->OSTCBX = x;

ptcb->OSTCBBitY = bity;

ptcb->OSTCBBitX = bitx;

OS_EXIT_CRITICAL();

OSSched(); (16) return (OS_NO_ERR);

} else {

OSTCBPrioTbl[newprio] = (OS_TCB *)0; (17) OS_EXIT_CRITICAL();

return (OS_PRIO_ERR);

} } }

Listing 4.15, OSTaskChangePrio().

4.07 Suspending a Task, OSTaskSuspend()

It is sometimes useful to explicitly suspend the execution of a task. This is accomplished with the OSTaskSuspend() function call. A suspended task can only be resumed by calling the OSTaskResume() function call. Task suspension is additive. This means that if the task being suspended is also waiting for time to expire then the suspension needs to be removed and the time needs to expire in order for the task to be ready-to-run. A task can either suspend itself or another task.

The code for OSTaskSuspend() is shown in listing 4.16. As usual, we check boundary conditions. First, we must ensure that your application is not attempting to suspend the idle task L4.16(1). Next, you must specify a valid priority L4.16(2). Remember than the highest valid priority number (i.e. lowest priority) is OS_LOWEST_PRIO. Note that you can suspend the statistic task. You may have noticed that the first test L4.16(1) is replicated in L4.16(2). I did this to be backward compatible with µC/OS. The first test could be removed to save a little bit of processing time but, this is really insignificant so I decided to leave it.

Next, we check to see if you specified to suspend the calling task L4.16(3) by specifying OS_PRIO_SELF. You could also decided to suspend the calling task by specifying its priority L4.16(4). In both of these cases, the scheduler will need to be called. This is why I created the local variable self which will be examined at the appropriate time.

If we are not suspending the calling task then we will not need to run the scheduler because the calling task is suspending a lower priority task.

We then check to see that the task to suspend exist L4.16(5). If the task to suspend exist, we remove it from the ready list L4.16(6). Note that the task to suspend may not be in the ready list because it’s waiting for an event or for time to expire. In this case, the corresponding bit for the task t o suspend in OSRdyTbl[] would already be cleared (i.e. 0).

Clearing it again is faster than checking to see if it’s clear and then clearing it if it’s not. We then set the OS_STAT_SUSPEND flag in the task’s OS_TCB to indicate that the task is now suspended L4.16(7). Finally, we call the scheduler only if the task being suspended is the calling task L4.16(8).

INT8U OSTaskSuspend (INT8U prio) {

BOOLEAN self;

OS_TCB *ptcb;

if (prio == OS_IDLE_PRIO) { (1) return (OS_TASK_SUSPEND_IDLE);

}

if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (2) return (OS_PRIO_INVALID);

}

OS_ENTER_CRITICAL();

if (prio == OS_PRIO_SELF) { (3) prio = OSTCBCur->OSTCBPrio;

self = TRUE;

} else if (prio == OSTCBCur->OSTCBPrio) { (4) self = TRUE;

} else {

self = FALSE;

}

if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { (5) OS_EXIT_CRITICAL();

return (OS_TASK_SUSPEND_PRIO);

} else {

if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { (6) OSRdyGrp &= ~ptcb->OSTCBBitY;

}

ptcb->OSTCBStat |= OS_STAT_SUSPEND; (7) OS_EXIT_CRITICAL();

if (self == TRUE) { (8) OSSched();

}

return (OS_NO_ERR);

} }

Listing 4.16, OSTaskSuspend().

4.08 Resuming a Task, OSTaskResume()

As mentioned in the previous section, a suspended task can only be resumed by calling OSTaskResume(). The code for OSTaskResume() is shown in listing 4.17. Because we cannot suspend the idle task we must verify that your application is not attempting to resume this task L4.17(1). You will note that this test also ensures that you are not trying to resume OS_PRIO_SELF (OS_PRIO_SELF is #defined to 0xFF which is always greater than OS_LOWEST_PRIO) because this wouldn’t make sense.

The task to resume must exist because we will be manipulating its OS_TCB L4.17(2) and, it must also have been suspended L4.17(3). We remove the suspension by clearing the OS_STAT_SUSPEND bit in the OSTCBStat field L4.17(4). For the task to be ready-to-run, the OSTCBDly field must be zero L4.17(5) because there are no flags in OSTCBStat to indicate that a task is waiting for time to expire. The task is made ready -to-run only when both conditions are satisfied L4.16(6). Finally, the scheduler is called to see if the resumed task has a higher priority than the calling task L4.17(7).

INT8U OSTaskResume (INT8U prio) {

OS_TCB *ptcb;

if (prio >= OS_LOWEST_PRIO) { (1) return (OS_PRIO_INVALID);

}

OS_ENTER_CRITICAL();

if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { (2) OS_EXIT_CRITICAL();

return (OS_TASK_RESUME_PRIO);

} else {

if (ptcb->OSTCBStat & OS_STAT_SUSPEND) { (3) if (((ptcb->OSTCBStat &= ~OS_STAT_SUSPEND) == OS_STAT_RDY) && (4) (ptcb->OSTCBDly == 0)) { (5) OSRdyGrp |= ptcb->OSTCBBitY; (6) OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

OS_EXIT_CRITICAL();

OSSched(); (7) } else {

OS_EXIT_CRITICAL();

}

return (OS_NO_ERR);

} else {

OS_EXIT_CRITICAL();

return (OS_TASK_NOT_SUSPENDED);

} } }

4.09 Getting Information about a Task, OSTaskQuery()

Your application can obtain information about itself or other application tasks by calling OSTaskQuery(). In fact, OSTaskQuery() obtains a copy of the contents of the desired task’s OS_TCB. The fields that are available to you in the OS_TCB depend on the configuration of your application (see OS_CFG.H). Indeed, because µC/OS -II is scalable, it only includes the features that your application requires.

To call OSTaskQuery() , your application must allocate storage for an OS_TCB as shown in listing 4.18. This OS_TCB is in a totally different data space as the OS_TCBs allocated by µC/OS-II. After calling OSTaskQuery(), this OS_TCB will contain a snapshot of the OS_TCB for the desired task. You need to be careful with the links to other OS_TCBs (i.e. OSTCBNext and OSTCBPrev); you don’t want to change what these links are pointing to! In general, you would only use this function to see what a task is doing – a great tool for debugging.

OS_TCB MyTaskData;

void MyTask (void *pdata) {

pdata = pdata;

for (;;) {

/* User code */

err = OSTaskQuery(10, &MyTaskData);

/* Examine error code .. */

/* User code */

} }

Listing 4.18, Obtaining information about a task.

The code for OSTaskQuery() is shown in listing 4.19. You should note that I now allow you to examine ALL the tasks, including the idle task L4.19(1). You need to be especially careful NOT to change what OSTCBNext and OSTCBPrev are pointing to. As usual, we check to see if you want information about the current task L4.19(2) and also, the task must have been created in order to obtain information about it L4.19(3). All fields are copied using the assignment shown instead of field by field L4.19(4). This is much faster because the compiler will most likely generate memory copy instructions.

INT8U OSTaskQuery (INT8U prio, OS_TCB *pdata) {

OS_TCB *ptcb;

if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (1) return (OS_PRIO_INVALID);

}

OS_ENTER_CRITICAL();

if (prio == OS_PRIO_SELF) { (2) prio = OSTCBCur->OSTCBPrio;

}

if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { (3) OS_EXIT_CRITICAL();

return (OS_PRIO_ERR);

}

*pdata = *ptcb; (4) OS_EXIT_CRITICAL();

return (OS_NO_ERR);

}

Listing 4.19, OSTaskQuery()

Chapter 5

在文檔中 µC/OS-II Goals Preface (頁 116-128)