PICOS18 > Tutorial > Multi-task

PICos18 : A multi-task application

This chapter introduces the synchro mecanisms between the tasks in PICos18. A third task comes to complete the application and post an event to another task in order to activate it. We will learn to use the events posted from a task or an alarm.


> Building an idle task

In the previous chapters we have designed an application composed by 2 tasks that manage a kind of software swatch with 3 variables : hour, min et sec.

The PICos18 multi-task does not only let you split your project into separated task to reduce the complexity of the application but also to code different independant sub-system. For instance we gonna create 2 additionnal tasks to inspect the RB0 and RB1 port permanetly like 2 push buttons.

A push button is not a typical signal that go from the 0 state to the 1 state and stay at 1 upto the next change.

When you press a push button (something mecanic) there are some rebounces during few milliseconds before getting a stable signal at 1. Then we have to write a code that wait for this stable state before reading the push button state.

First of all we start to create a first task that inspects the RB0 port where is located the BNT0 push button.

1) Under MPLAB® save the "tsk_task0.c" file to "tsk_task2.c"

2) Modify the definition related to the new task in the taskdesc.c file :

#define DEFAULT_STACK_SIZE      128
DeclareTask(TASK0);
DeclareTask(TASK1);
DeclareTask(TASK2);

volatile unsigned char stack0[DEFAULT_STACK_SIZE];
volatile unsigned char stack1[DEFAULT_STACK_SIZE];
volatile unsigned char stack2[DEFAULT_STACK_SIZE];

/**********************************************************************
 * ---------------------- TASK DESCRIPTOR SECTION ---------------------
 **********************************************************************/
#pragma		romdata		DESC_ROM
...

/**********************************************************************
 * -----------------------------  task 2 ------------------------------
 **********************************************************************/
rom_desc_tsk rom_desc_task2 = {
	TASK2_PRIO,                       /* prioinit from 0 to 15       */
	stack2,                           /* stack address (16 bits)     */
	TASK2,                            /* start address (16 bits)     */
	READY,                            /* state at init phase         */
	TASK2_ID,                         /* id_tsk from 1 to 15         */
	sizeof(stack2)                    /* stack size    (16 bits)     */
};

3) Then change the the task 2 core as follows :

extern unsigned char hour;

/**********************************************************************
 * ------------------------------ TASK2 -------------------------------
 *
 * Third task of the tutorial.
 *
 **********************************************************************/
TASK(TASK2) 
{
  unsigned int i;

  while(1)
  {
    while(PORTBbits.RB0 == 0);
    for (i = 0; i < 10000; i++)
      Nop();
    if (PORTBbits.RB0 == 1)
      hour++;
  }
}

The task has an infinite loop whithout waiting for an event with a WaitEvent function. So the task can block the rest of the application , more precisely the task with a lowest priority... Then its priority has to be the smallest one of the application : we say the task id the idle task.

It starts to inspect the RB0 port and stay in an infinite loop as long as the BTN0 button is not pressed. As soon as the button is pressed the task waits for the end of the rebounce thanks to a FOR loop. Then if the RB0 is ON the "hour" variable is incremented. It can update the software swtach for instance.

4) Now you need to update the definition related to the new task in the "define.h" file.

/***********************************************************************
 * ----------------------------- Task ID -------------------------------
 **********************************************************************/
#define TASK0_ID             1
#define TASK1_ID             2
#define TASK2_ID             3

#define TASK0_PRIO           7
#define TASK1_PRIO           10
#define TASK2_PRIO           1

5) Finally you have to add the new task to the project ("Project/Add files to Project...").

You can rebuild and start the simulation (F10).

 

> Round robin

Now we can create a second idle task. Indeed PICos18 lets you design many different tasks with the same priority. In our application we wonder which one is able to run... Actually the PICos18 scheduler uses a 'Round Robin" algorithm.

With a such algorithm the idl tasks can share the PIC18 processor during a time slice of 1ms. Because the task have a very low priority they run only when not any task needs to run and during a maximum time of 1 ms. After this time another task with the same priority will run during the same time slice.

Deisgn a task 3 with the same code (Except that the port inspected is the port RB1) and with the same priority :

/***********************************************************************
 * ----------------------------- Task ID -------------------------------
 **********************************************************************/
#define TASK0_ID             1
#define TASK1_ID             2
#define TASK2_ID             3
#define TASK3_ID             4

#define TASK0_PRIO           7
#define TASK1_PRIO           10
#define TASK2_PRIO           1
#define TASK3_PRIO           1
Donot forget to add the new file to the project and rebuild (F10).


Place some breakpoints in front of the "while(PORTBbits.RBx == 0);" instructions and start the simulation. You will see the pointer to be stopped in the task2. Disable the breakpoint where the pointer is and run the simulation : the pointer stops now in the task 3 after 1ms ...

If now you place breakpoints in task 0 and 1 you will see the pointer to be stopped after 1 seconde in one of the 2 tasks : there is preemption because of the highest priorities of the both tasks.

 

> Simulation

To simulate the push button is ON you can use the Watch window :

Start the simulation until the pointer stops on the first breakpoint of the task2. Click on the Watch window to set the PORTB at 0x01. Then start the simulation and check the breakpoint in the task 3 is still enabled.

You will see the pointer stops at first in the task 3 before reaching the second breakpoint in the task 2. In fact after 1 ms, hte scheduler has decided to run the other task...