Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2012 / uCOS / uCOSII_ebook.pdf
Скачиваний:
70
Добавлен:
10.02.2015
Размер:
1.08 Mб
Скачать

latency. When called, OSTaskCreateHook() receives a pointer to the OS_TCB of the task being created. This means that the hook function can access all members of the OS_TCB data structure.

Finally, if OSTaskCreate() was called from a task (i.e. OSRunning is set to TRUE L4.1(10)) then the scheduler

is called L4.1(11) to determine whether the created task has a higherpriority than its creator. Creating a higher priority task will result in a context switch to the new task. If the task was created before multitasking has started (i.e. you did not call OSStart() yet) then the scheduler is not called.

4.01 Creating a Task, OSTaskCreateExt()

Creating a task using OSTaskCreateExt() offers you more flexibility but, at the expense of additional overhead. The code for OSTaskCreateExt() is shown in listing 4.3.

As can be seen, OSTaskCreateExt() requires nine (9) arguments! The first four arguments (task, pdata, ptos and prio) are exactly the same as with OSTaskCreate() and also, they are located in the same order. I did that to make it easier to migrate your code to use OSTaskCreateExt().

The id establishes a unique identifier for the task being created. This argument has been added for future expansion

and is otherwise unused by µC/OS -II. This identifier will allow me to extend µC/OS-II beyond its limit of 64 tasks. For now, simply set the task’s ID to the same value as the task’s priority.

pbos is a pointer to the task’s bottom-of-stack and this argument is used to perform stack checking.

stk_size specifies the size of the stack in number of elements. This means that if a stack entry is 4 bytes wide then, a stk_size of 1000 means that the stack will have 4000 bytes. Again, this argument is used for stack checking.

pext is a pointer to a user supplied data area that can be used to extend the OS_TCB of the task. For example, you can add a name to a task (see Example #3), storage for the contents of floating-point registers during a context switch, port address to trigger an oscilloscope during a context switch and more.

Finally, opt specifies options to OSTaskCreateExt() to specify whether stack checking is allowed, whether the stack will be cleared, whether floating-point operations are performed by the task, etc. uCOS_II.H contains a list of available options (OS_TASK_OPT_STK_CHK, OS_TASK_OPT_STK_CLR, and OS_TASK_OPT_SAVE_FP).

Each option consists of a bit. The option is selected when the bit is set (you would simply OR the above

OS_TASK_OPT_??? constants).

INT8U OSTaskCreateExt (void

(*task)(void *pd),

 

 

void

*pdata,

 

 

OS_STK

*ptos,

 

 

INT8U

prio,

 

 

INT16U

id,

 

 

OS_STK

*pbos,

 

 

INT32U

stk_size,

 

 

void

*pext,

 

{

INT16U

opt)

 

 

 

 

void

*psp;

 

 

INT8U

err;

 

 

INT16U

i;

 

 

OS_STK

*pfill;

 

 

if (prio > OS_LOWEST_PRIO) {

(1)

return (OS_PRIO_INVALID);

 

}

OS_ENTER_CRITICAL();

if (OSTCBPrioTbl[prio] == (OS_TCB *)0) {

(2)

OSTCBPrioTbl[prio] = (OS_TCB *)1;

(3)

OS_EXIT_CRITICAL();

(4)

if (opt & OS_TASK_OPT_STK_CHK) {

(5)

if (opt & OS_TASK_OPT_STK_CLR) {

 

pfill = pbos;

 

for (i = 0; i < stk_size; i++) {

 

#if OS_STK_GROWTH == 1

 

*pfill++ = (OS_STK)0;

 

#else

 

*pfill-- = (OS_STK)0;

 

#endif

 

}

 

}

 

}

 

psp = (void *)OSTaskStkInit(task, pdata, ptos, opt);

(6)

err = OSTCBInit(prio, psp, pbos, id, stk_size, pext, opt);

(7)

if (err == OS_NO_ERR) {

(8)

OS_ENTER_CRITICAL;

 

OSTaskCtr++;

(9)

OSTaskCreateHook(OSTCBPrioTbl[prio]);

(10)

OS_EXIT_CRITICAL();

 

if (OSRunning) {

(11)

OSSched();

(12)

}

 

} else {

 

OSTCBPrioTbl[prio] = (OS_TCB *)0;

(13)

}

 

return (err);

 

} else {

 

OS_EXIT_CRITICAL();

 

return (OS_PRIO_EXIST);

}

}

Listing 4.3, OSTaskCreateExt()

OSTaskCreateExt() starts by checking that the task priority is valid L4.3(1). The priority of a task must be a number between 0 and OS_LOWEST_PRIO, inclusively. Next, OSTaskCreateExt() makes sure that a task has

not already been created at the desired priority L4.3(2). With µC/OS-II, all tasks must have a unique priority. If the desired priority is free then µC/OS-II ‘reserves’the priority by placing a non-NULL pointer in OSTCBPrioTbl[] L4.3(3). This allows OSTaskCreateExt() to re-enable interrupts L4.3(4) while it sets up the rest of the data structures for the task.

In order to perform stack checking (see section 4.03, Stack Checking ) on a task, you must set the OS_TASK_OPT_STK_CHK flag in the opt argument. Also, stack checking requires that the stack contain zeros (i.e.

it needs to be cleared) when the task is created. To specify that a task gets cleared when it is created, you would also set OS_TASK_OPT_STK_CLR in the opt argument. When both of these flags are set, OSTaskCreateExt()

clears the stack L4.3(5).

OSTaskCreateExt() then calls OSTaskStkInit() L4.3(6) which is responsible for setting up the task stack. This function is processor specific and is found inOS_CPU_C.C. Refer to Chapter 8, Porting µC/OS-II for details on how to implement OSTaskStkInit(). If you already have a port of µC/OS-II for the processor you are intending to use then, you don’t need to be concerned about implementation details. OSTaskStkInit() returns the new top-of-stack (psp) which will be saved in the task’s OS_TCB.

µC/OS-II supports processors that have stacks that grow from either high memory to low memory or from low memory to high memory (see section 4.02, Task Stacks). When you call OSTaskCreateExt(), you must know

how the stack grows (see OS_CPU.H of the processor you are using) because you must pass the task’s top -of-stack to OSTaskCreateExt() which can either be the lowest memory location of the stack (when OS_STK_GROWTH is 0) or the highest memory location of the stack (when OS_STK_GROWTH is 1).

Once OSTaskStkInit() has completed setting up the stack, OSTaskCreateExt() calls OSTCBInit()

L4.3(7) to obtain and initialize an OS_TCB from the pool of free OS_TCBs. The code for OSTCBInit() is shown and described with OSTaskCreate() (see section 4.00) Upon return from OSTCBInit(), OSTaskCreateExt() checks the return code L4.3(8) and upon success, increments the counter of the number of tasks created, OSTaskCtr L4.3(9). If OSTCBInit() failed, the priority level is relinquished by setting the entry in

OSTCBPrioTbl[prio] to 0 L4.3(13). OSTaskCreateExt() then calls OSTaskCreateHook() L4.3(10) which is a user specified function that allows you to extend the functionality of OSTaskCreateExt(). OSTaskCreateHook() can be declared either in OS_CPU_C.C (if OS_CPU_HOOKS_EN is set to 1) or elsewhere (if OS_CPU_HOOKS_EN is set to 0). Note that interrupts are disabled when OSTaskCreateExt() calls OSTaskCreateHook(). Because of this, you should keep the code in this function to a minimum because it can directly impact interrupt latency. When called, OSTaskCreateHook() receives a pointer to the OS_TCB of the task being created. This means that the hook function can access all members of the OS_TCB data structure.

Finally, if OSTaskCreateExt() was called from a task (i.e. OSRunning is set to TRUE L4.3(11)) then the scheduler is called L4.3(12) to determine whether the created task has a higher priority than its creator. Creating a higher priority task will result in a context switch to the new task. If the task was created before multitasking has started (i.e. you did not call OSStart() yet) then the scheduler is not called.

4.02 Task Stacks

Each task MUST have its own stack space. A stack MUST be declared as being of type OS_STK and MUST consist

of contiguous memory locations You can either allocate stack space ‘statically’(i.e. compile -time) or ‘dynamically’ (run-time). A static stack declaration looks as shown below. Both of these declarations are made outside a function.

static OS_STK MyTaskStack[stack_size];

Listing 4.4, Static Stack

or,

OS_STK MyTaskStack[stack_size];

Listing 4.5, Static Stack

You can allocate stack space dynamically by using the C compiler’s malloc() function as shown in listing 4.6. However, you must be careful with fragmentation. Specifically, if you create and delete tasks then, eventually, your memory allocator may not be able to return a stack for your task(s) because the heap gets fragmented.

OS_STK *pstk;

pstk = (OS_STK *)malloc(stack_size);

 

if (pstk != (OS_STK *)0) {

/* Make sure malloc() had enough space */

Create the task;

 

}

 

Listing 4.6, Using ‘malloc()’to allocate stack space for a task

Figure 4-1 illustrates a heap containing 3 Kbytes or available memory that can be allocated with malloc() F4-1(1). For sake of discussion, you create 3 tasks (task A, B and C) each requiring 1K. We will also assume that the first 1

Kbytes is given to task A, the second to task B and the third to C F4-1(2). Your application then deletes task A and task B and, relinquishes the memory back to the heap using free() F4-1(3). Your heap now has 2 Kbytes of

memory free but, it’s not contiguous. This means that you could not create another task (i.e. task D) that required 2 Kbytes. Your heap is thus fragmented. If, however, you never delete a task then using malloc() is perfectly

acceptable.

 

 

A

 

1K

 

 

(1K)

 

 

 

 

 

 

 

 

 

 

3K

 

B

 

B

 

 

(1K)

 

(1K)

 

 

C

 

1K

 

 

(1K)

 

 

 

 

 

(1)

(2)

(3)

Figure 4-1, Fragmentation.

µC/OS-II supports processors that have stacks that grow from either high memory to low memory or from low memory to high memory. When you call either OSTaskCreate() or OSTaskCreateExt(), you must know

how the stack grows because you need to pass the task’s top-of-stack to the task creation function. When OS_STK_GROWTH is set to 0 in OS_CPU.H, you need to pass the lowest memory location of the stack to the task

create function as shown in listing 4.7.

Соседние файлы в папке uCOS