Table of Contents

OSA : Tasks

Intro

A task in OSA is a C-function. This function must contain an infinite loop containing at least one service that switches task context. A simple task can look like this:

void SimpleTask (void)
{
    for (;;) {      // Infinite loop
        OS_Yield(); // Unconditional context switching
    }
}

When using mikroC PRO you must tell the linker that the function will be called indirectly by adding a #pragma directive, e.g.:

#pragma funcall main SimpleTask

Each task can be in one of five states:

Every task has a priority. There are eight priority levels from 0 (highest) to 7 (lowest). Priority can be changed at run time.(More about priorities and states).

To use tasks OSA reserves RAM for task descriptors. The number of tasks which can be active at one time is set by the constant OS_TASKS in OSAcfg.h.

Pointer to TCB

Most operations on a task are made through a pointer to its descriptor called a TCB (Task Control Block). Task descriptors can not be accessed directly. But you can define a global variable of type OST_TASK_POINTER which will point to the task descriptor.

OST_TASK_POINTER tp_MyTask;

To control the task via this variable we must initialize it by the service OS_Task_GetCur. Inside a task this service returns a pointer to the current task's descriptor. It is recommended that you call this service at the beginning of the task-function.

void MyTask (void)
{
    tp_MyTask = OS_GetCurTask();
    for (;;) {
        /*...*/
    }
}

It is possible for a task to control itself. In this case you can use the system macro this_task (or the service OS_Task_GetCur) as the parameter.

void MyTask (void)
{
    for (;;) {
        /*...*/
        OS_Task_SetPriority(this_task, 0);     // set highest priority for current task
        /*...*/
    }
}

Task creation

A task is created by the service OS_Task_Create.

For example:

#include <osa.h>
 
void Task1 (void)
{
    for (;;)
    {
        OS_Yield();
    }
}
 
...
 
void main (void)
{
    OS_Init();
    OS_Task_Create(7, Task1);
    ...
    for (;;) OS_Sched();
}

In this example we created a task to run the C function Task1() with the lowest priority. After this service completes, the operating system knows that function Task1() is a task and will give control to it when it is ready.

We must provide the service OS_Task_Create with a priority even if the priority mode is disabled (constant OS_DISABLE_PRIORITY is defined in OSAcfg.h).

After OS_Task_Create() executes you can get a pointer to the descriptor of the created task with the service OS_Task_GetCreated (this is useful when one task will control another task):

    OST_TASK_POINTER  tp;
 
    OS_Task_Create(0, MyTask);
    if (!OS_IsError) tp = OS_Task_GetCreated();

When using CCS all function-tasks should be defined in main() by the service OS_Task_Define

Task creation error

If before calling the service OS_Task_Create there is no free task descriptor, then the task will not be created and the system flag OS_IsError will be set.

    OS_Task_Create(7, Task1);
    if (OS_IsError()) ...	;	//

If OS_Task_Create() exits with an error then you need to increase the value of constant OS_TASKS in OSAcfg.h and then re-build the project. If there should be a free descriptor but isn't, then you need to look for an error in the program (e.g. some task should have been stopped but is still running).

Task deletion

To delete a task (to delete it from the list of active tasks and to free its descriptor) you can use the service OS_Task_Delete. Before deleting a task you should be sure that the task has freed all its resources. It is recommended that a task should only delete itself (but OSA services allow deletion of a task from anywhere in the program).

Be sure that the task being deleted frees all resources.

After calling OS_Task_Delete the task descriptor will be free. If this service was called to delete the current task (with the this_task parameter) then the system automatically switches context. After OS_Task_Delete(this_task) no further instructions will be executed by the task.

    ...
    OS_Task_Delete(this_task);
    Counter++;
    ...

In this example, the variable Counter will not be increased.

To stop the current task and to run another, first we must create a new task and then delete the current task. Otherwise the new task will not be created.

    . . .
    OS_Task_Create(1, Task_NewTask);
    OS_Task_Delete(this_task);
    . . .

But this method has a defect: we must have at least one free task descriptor at all times. For example, a program for a dictaphone consists of several tasks: buttons, indication, recording, playing, battery checking. Here we have 2 tasks that cannot be active at the same time: recording and playing. Thus at any one time only 4 tasks can be active. When we switch from the recording task to the playing task, we must first create the playing task and than stop the recording. Thus we must have 5 task descriptors for 4 active tasks.

void Task_Play (void)
{
    . . .
    OS_Task_Create(1, Task_Record);
    OS_Task_Delete(this_task);
    . . .
}

In this case several bytes of RAM (used by fifth task descriptor) are unused most of the time. To avoid this we can use the service OS_Task_Replace:

This service will stop the current task and create a new task in the just-released descriptor.

    . . .
    OS_Task_Replace(1, Task_Record);
    . . .

Task priority change

We can change the priority of tasks in real-time. There are two services available for use with task priority: OS_Task_GetPriority and OS_Task_SetPriority.

Task local variables

All active (created) tasks are parallel processes, and you should remember that their local variables may intersect. Thus a local variable's value may be lost (overwritten by a parallel task) after context switching. To avoid loss of a variable's value, it must be declared as static. For example:

void Task1 (void)
{
  static char s_cCounter;  // This variable will remain unchanged after
                           // context switching.
  char i, j;               // These variables are temporary. They can
                           // be used only within one task session
                           // (from getting control to context switching)
  . . .
}

Services

Service Arguments Description Properties
Creating/Deleting
OS_Task_Define (TaskName) For CCS and HT-PICC PRO: Tell compiler that function TaskName is a task and it will be called indirectly. This service is called from main() only main
OS_Task_Create (priority, TaskName) Create task and add it to the list of active tasks Not allowed in interrupt
OS_Task_Replace (priority, TaskName) Stop and delete current task and create a new task Allowed only in task
OS_Task_Delete (tp) Delete task Switches context
Management
OST_TASK_POINTER
OS_Task_GetCur
Get pointer to current task's descriptor Allowed only in task
OST_TASK_POINTER
OS_Task_GetCreated
Get pointer to just created task's descriptor
OS_Task_Pause (tp) Pause task Switches context
OS_Task_Continue (tp) Continue paused task
char
OS_Task_GetPriority
(tp) Get priority of task
OS_Task_SetPriority (tp, priority) Change task's priority
Checking
OS_Task_IsPaused (tp) Check if task is paused