Tasks

 

The main building block of your application is the task. The tasks should be created in the main function of the application before starting the os.

 

The number of created tasks must not exceed the defined value N_TASKS in os_defines.h.

 

A task is created this way:

 

taskId = task_create( taskProc, &taskData, prio, queue, Q_SIZE, msgSize );

 

Where:

The return value, taskId, above is just an uint8_t that can be used to identify the different tasks.

 

taskProc is a function pointer to the task procedure that is executed when the task is scheduled to run.

 

&taskData is a pointer to a user defined structure holding task data.

 

prio is the task priority in range 1-254, where 1 is highest priority. The kernel always lets the task with highest priority execute first. There can not be two tasks with same priority. 

 

queue is a pointer to a message queue for the task. If the specified size is 0, no message queue is allocated for the task, and the queue parameter is ignored.

 

Q_SIZE is the length of the message queue. Set to 0 if no queue should be used.

 

msgSize is the size of the message type held in the message queue. Ignored of no queue is used.

 

Task Procedures

The code section of a task procedure must begin and end with the task_open() and task_close() macros respectively. A very simple task is shown below:

void task(void) {
    task_open();
    ...
    task_close();
}

 

This task will execute only once. When the task_close() line is executed, the task is put in the killed state and will not be executed again. If the task has to synchronize with some external event, we can put a blocking call in the middle:

 

void task(void) {
    task_open();
    ...
    event_wait(evt);
    ...
    task_close();
}

 

This task will run to the event_wait() call and then block. The kernel will schedule other tasks to execute and eventually the event will be signalled which will put the task back to ready state again. Execution will be resumed after the event_wait() call. Once the task_close() statement is reached, the task will be killed and will not be executed again.

 

If we want a task to be excuted periodically we must put the code into an endless loop:

void task(void) {
    task_open();
    for (;;) {
        ...
        ...
        task_wait(TASK_DELAY_TICKS);
    }
    task_close();
}

 

Another example of a task waiting for characters to be received on the serial port:

void task(void) {
    uint8_t data;
    task_open();
    for (;;) {
        event_wait(rxEvt);
        uart_get(&data);
        ...
    }
    task_close();
}

 

The rxEvt may be signalled from the uart ISR using event_ISR_signal(rxEvt).

 

Execution order

It is important to understand the execution path through the task procedure:

 

- Statements between the opening bracket and the task_open() call is executed each time the task is executed. Also when the task is resumed after a wait operation.

 

- Statements between task_open() and the for(;;) statement, is executed only once: the very first time the task runs. Next time the task is run, code execution will jump from task_open() to the line where the task were suspended.

 

- Statements in the for loop are executed as expected.

 

This gives that the output from the following task will be: 1, 2, 3, 1, 4, 3, 1, 4, 3, 1, 4, 3, 1, 4, 3

 

void task(void) {
    printf("1\n");
    task_open();
    printf("2\n");
    for (;;) {
        printf("3\n");
        task_wait(500);
        printf("4\n");
    }
    task_close();
}


 

Below is another example that shows a bad example of variable initialization:

 

void task(void) {
    static uint8_t a;
    a = 0;
    task_open();
    for (;;) {
        task_wait(500);
        printf("a=%d\n", ++a);
    }
    task_close();
}


 

In this case the output would be: a=1, a=1, a=1. Even if the variable a is incremented for each loop, it is reset to 0 in the line before task_open(). This line is always executed.

 

The correct version is shown below:

 

void task(void) {
    static uint8_t a;
    task_open();
    a = 0;
    for (;;) {
        task_wait(500);
        printf("a=%d\n", ++a);
    }
    task_close();
}


The statements between task_open() and the for-loop is only executed once, making this area suitable for variable initialization.

 

Task API

The task API consists of the following functions and macros:
 - task_create()
 - task_kill()
 - task_open()
 - task_close()
 - task_suspend()
 - task_resume()
 - task_state_get()
 - task_id_get()
 - task_wait()

 

 

 

 

 

 

 

 cocoOS  2010

www.cocoos.net