| PICOS18 > Tutorial > First |
|
Now you have successfully compiled the PICos18 project under MPLAB® it's time to create and simulate your first task. You will learn how to write a such task and what is the system tick at 1ms. |
![]() |
| > Requirements |
First of all it is necessary to create a new project under MPLAB® from the PICos18 sources.
Then You have to install the Microchip C18 compiler and to setup the software (previous chapter).
| > State of one task |
In our application there is only one task diefined in the "tsk_task0.c" file :
TASK(TASK0)
{
while(1);
}
We understand easily the goal of the state is to create a infinite loop.
Under MPLAB® place a breakpoint on the line with the "while(1);"
statement then start the simulation (F9).
You can notice the simulation stops on the breakpoint. But how the kernel knows
the presence of the task, to execute its code upto the infinite loop ... without
being fully blocked by this instruction ?
Actually the task has been declared to the kernel in the "taskdesc.c" file.
/********************************************************************** * ----------------------------- task 0 ------------------------------ **********************************************************************/ rom_desc_tsk rom_desc_task0 = { TASK0_PRIO, /* prioinit from 0 to 15 */ stack0, /* stack address (16 bits) */ TASK0, /* start address (16 bits) */ READY, /* state at init phase */ TASK0_ID, /* id_tsk from 1 to 15 */ sizeof(stack0) /* stack size (16 bits) */ };
On the bold line you can specify the state of the task at the start time. In
fact when a task exists in the project (it has beeen compiled by the compiler)
it is not necessarily enabled by the kernel.
With PICos18, as specified by the OSEK standard, a task has 4 possible states
:
![]() |
SUSPENDED : the task is present in the application but is not taken into account by the kernel. READY : the task is available to be executed by the kernel then is taken into account by the scheduler. WAITING : the task is sleeping and so is temporarily SUSPENDED and will be READY as soon as an event occurs. RUNNING : there is only one task running at a certain time : this is the task in a REAYD state with the highest priority. |
Speaking of our application the task is in a READY state. While in this
state the kernel will be allowed to execute it...
Replace READY by SUSPENDED in the taskdesc.c file then rebuild
and restart the simulation : you can notice the simulation does not stop anymore
on the breakpoint.
| > Task description |
Like the state of the task a lot of features are described in the taskdesc.c file. Actually everything related to your application are located in this file as an example : the ID of the task, its priority, its software stack, the alarms used by the project, ...
We find at first the alarms of the application:
AlarmObject Alarm_list[] =
{
/*******************************************************************
* -------------------------- First task ---------------------------
*******************************************************************/
{
OFF, /* State */
0, /* AlarmValue */
0, /* Cycle */
&Counter_kernel, /* ptrCounter */
TASK0_ID, /* TaskID2Activate */
ALARM_EVENT, /* EventToPost */
0 /* CallBack */
},
};
This table is the list of the alarms, each one is represented by a structure. An alarm is a kind of TIMER object managed by software by the kernel. In fact PICos18 has a timer reference of 1 ms, built from hardware and software drivers. So it is possible to create a software timer based on this 1 ms tick: the alarms.
An alarm is connected to a coounter (here Counter_kernel which is the 1ms reference)
and a task (with the TASK0_ID identifier). When the alarm reachs a certain value
it can post an event to this task (ALARM_EVENT in our example) or call a C function
(CallBack mecanism).
We find then the list of resources of the application:
/**********************************************************************
* --------------------- COUNTER & ALARM DEFINITION -------------------
**********************************************************************/
Resource Resource_list[] =
{
{
10, /* priority */
0, /* Task prio */
0, /* lock */
}
};
A resource is another object managed by the kernel. A resources is in general
a peripheral shared by different tasks, for instance a 8 bits port of the PIC18.
It has its own priority (priority field) and can be locked (lock field) by a
taks (Task prio field). Then when a task want to grant access to the 8 bits
port it can have a secure access through the resource and lock the other tasks
to an access during a short time. The resource management will be introduced
in the multi-task chapter.
Now we find the software stack list:
There are 2 kind of variables : the static ones and the automatic ones...but to be more understandable it should be said there are global variables and local variables, even if it is not totaly correct. The global variables (it means the variable declared out a C function) are placed in RAM at an absolute address, an address that never change.
The local variables (declared in a C function) are compiled by C18 so that they are stored in a specific memory buffer called the software stack. A software stack is a dynamic memory area, something alive from a certain point of view. When you enter in a C function the PIC18 starts to push the local variables and when it leaves this C function it pops (removes) the area reserved to local variables then freeing memeory. This mecanism lets you spare a lot of memory when you need a lot of C function, and the addtionnal code necessary is not significant.
PICos18 is compliant with the C18 software stack management. Each task has its own software stack so that each task of the application can work as alone in its own private space without disturbing the other processing. Moreover PICos18 has an automatic stack overflow detection that occurs when a software stack becomes bigger than expected (kernelpanic flag).
/********************************************************************** * ----------------------- TASK & STACK DEFINITION -------------------- **********************************************************************/ #define DEFAULT_STACK_SIZE 128 DeclareTask(TASK0); volatile unsigned char stack0[DEFAULT_STACK_SIZE];
Everytime you add a task to your application do not forget to add a software stack by copying the line above and changing the name of the stack (by stack1 for example).
Only the 3 above avlues are correct. Do not try to setup a different value,
the C18 compiler cannot manage a such value. Moreover a software stack have
to be always in a same bank and not on 2 pieces of bank. If you hesitate let
all the stack size to 128.
On trouve ensuite la liste des tâches de l'application:
/**********************************************************************
* ----------------------------- task 0 ------------------------------
**********************************************************************/
rom_desc_tsk rom_desc_task0 = {
TASK0_PRIO, /* prioinit from 0 to 15 */
stack0, /* stack address (16 bits) */
TASK0, /* start address (16 bits) */
READY, /* state at init phase */
TASK0_ID, /* id_tsk from 1 to 15 */
sizeof(stack0) /* stack size (16 bits) */
};
You have already seen this task descriptor but here is some additionnal details :
| > 1ms interrupt |
Even if your application does not need to manage interrupts you will probably need the internal software timer. To do so the "int.c" file has some definitions and one is a kind of software IT vector table.
In fact the the function is very ligth and lets you manage IT without reading first the heavy PIC18 datasheet :
#pragma code _INTERRUPT_VECTORL = 0x003000
#pragma interruptlow InterruptVectorL save=section(".tmpdata"), PROD
void InterruptVectorL(void)
{
EnterISR();
if (INTCONbits.TMR0IF == 1)
AddOneTick();
/*Here is the next interrupts you want to manage */
/*if (INTCONbits.RBIF == 1) */
/* MyOwnISR(); */
LeaveISR();
}
#pragma code
As you can imagine when the TIMER0 overflow occurs (TMR0IF flag) the AddOneTick function is called to add 1 ms to every alarms present in the application. When an alarm reachs a critical value it sends some actions to the dedicated task with the kernel help, the only one entity to be able to activate a task.
You can notice then the kernel is always enable and that no task can stop it even with an infinite "while(1)" loop.
To verify it setup your simulation to 40MHZ (Debugger/Settings..), place a breakpoint face to the AddOneTick() call, and display the MPLAB stopwatch (Debbuger/Stopwatch) :

| > Application start |
Finally your applicaiton has a file called "main.c" needed by the
compiler.
According to the OSEK standart this main file is not a part of the OS but from
the applicaiton itself, and the job of this main file is to start the kernel
(because it is possible to start and to stop the kernel from the main file).
In the main file you will find an Init() function that lets you setup your application frequency without open the PIC18 datasheet :
void Init(void)
{
FSR0H = 0;
FSR0L = 0;
/* User setting : actual PIC frequency */
Tmr0.lt = _40MHZ;
/* Timer OFF - Enabled by Kernel */
T0CON = 0x08;
TMR0H = Tmr0.bt[1];
TMR0L = Tmr0.bt[0];
}
The bold line is necessary to setup the INTERNAL frequency of the PIC28 chip. Indeed the PIC18 has got a PLL by 4 you can enable or not. A typical PIC18 application should need an external 10MHz crystal with two 15 pF capacitors and with the PLL enable in order to run to 40 MHz.
The internal pipeline of the processor is at level 4, then the worst case of
the PIC18 is 10 MIPS, it means 10 millions of instructions par secondes (see
the PIC18 datasheet). So the PIC18 is a small 8 bits microcontroler cheap and
very powerfull with a lot of peripheral for your applications.
If you wish to use a different frequency have a look at the "define.h" file to get the available frequenciess.