PICOS18 > Tutorial > Application

PICos18 : Application example

The tutorial is terminated with this last chapter about PICos18 development. The sources of this typical application are located in the Project/Tutorial directory of PICos18. This example is here to show you how easy it is to develop a complex application with PICos18.


> Multiple interrupts

If you start the application now and you push one of the both buttons you will see pretty soon there is a problem : the PIC18 seams to be like frozen after a while, and it occurs when you press a button quite fast !

Stop the application if you're using a Microchip ICD2 in debug mode or stop the simulation if you're using the MPLAB simulator and have a look at the following registers :

You can notice the "kernelPanic" variable is not null. In our case its value is 0x53.
Actually this variable indicates if there is a stack overflow. When a stack overlaps another one the kernel detects it automaticaly and stop the task at the origin of the problem. This allow to save your application and also to debug with PICos18 in order to find out what appends.

kernelPanic value is 0x53.

The right nibble ("3") means the task with the ID 3 has a stack overflow.
The left nibble ("5") means the task with the ID 5 has a stack trashed by an oberflow.

The kernel has then decided to stop the both tasks because the behavior is no more safe for both of them. This safety mecanism lets the PIC18 runs even with a stack overflow and lets you fix the bug.

The task 2 in charge of the button processing runs anormaly and its software stack is overload and has trashed the following one, the one of the task 4.

But why a such overflow ?
2 cases can explain this :

We're using push buttons and we have seen in the previous chapters these buttons have some rebounces when they are pressed. Then many interrupts occur and overload the PIC18. At every interrupt the current task needs to save its context but the IT are so close we are finally in an infinite loop in the InterruptVectorL() upto the stack overflow....

The behavior occurs quite often even if you don't use a real-time kernel. The only one difference here is that PICos18 inspects the application in "real-time" to see if a such overflow occured and stop some task if necessary. Without an OS the PIC18 will crash after a while... In fact it is not a architecture default but a coding trouble we'll fix in the next chapter.

> Bug fixing

The problem occurs because we always enable the interrupts from the RB1 and RB2 ports while we didn't start to treate the first interrupt. Detect every button interrupts is not usefull : no need to know that the button has 50 rebounces before stabilizing the signal. We just need to know it has been pressed..

Modify the tasks TASK2 and TASK3 :

TASK(TASK2) 
{
  unsigned int i;

  hour++;
  if (hour == 24)
    hour = 0;
  INTCON3bits.INT1IF = 0;
  INTCON3bits.INT1IE = 1;
  TerminateTask();
}

...

TASK(TASK3) 
{
  unsigned int i;

  min++;
  if (min == 60)
    min = 0;
  INTCON3bits.INT2IF = 0;
  INTCON3bits.INT2IE = 1;
  TerminateTask();
}

And modify your interrupt ISR :

void MyOwnISR(void)
{
  TaskStateType State;

  if (INTCON3bits.INT1IF)
  {
    INTCON3bits.INT1IE = 0;
    GetTaskState(TASK2_ID, &State);
    if (State == SUSPENDED) 
      ActivateTask(TASK2_ID);
  }
  if (INTCON3bits.INT2IF)
  {
    INTCON3bits.INT2IE = 0;
    GetTaskState(TASK3_ID, &State);
    if (State == SUSPENDED) 
      ActivateTask(TASK3_ID);
  }
}

...

void InterruptVectorL(void)
{
  EnterISR();

  if (INTCONbits.TMR0IF == 1)
    AddOneTick();
/*Here is the next interrupts you want to manage */
  if ((INTCON3bits.INT1IF & INTCON3bits.INT1IE)|| 
      (INTCON3bits.INT2IF & INTCON3bits.INT2IE))
    MyOwnISR();
  if ((PIR1bits.RCIF)&(PIE1bits.RCIE))
    RS_RX_INT();
  if ((PIR1bits.TXIF)&(PIE1bits.TXIE))
    RS_TX_INT();

  LeaveISR();
}
You can see MyOwnISR() disable the interrupts (INTxIE = 0 means Interrupt Enable disabled) and the task has to manage the interrupt enable flag once the interrupt processing is completed.

You can now program your PIC18 to test the application : you will be able to update the software swtach by pushing the both buttons. But there is still a problem : if you press many times a button very fast the new value is not taken into account immediatly but every seconds when the display is updated. To fix that we need the real-time capability of PICos18 !


> Real-time updates

To update the display each time you press a button you have to inform the display each time there is a new value.... but without disturb the display that runs every seconds it means without breaking our software swatch behavior.

To do so we need to post an event to the task TASK4 :

TASK(TASK2) 
{
  unsigned int i;

  hour++;
  if (hour == 24)
    hour = 0;
  SetEvent(TASK4_ID, UPDATE_EVENT);
  INTCON3bits.INT1IF = 0;
  INTCON3bits.INT1IE = 1;
  TerminateTask();
}

...

TASK(TASK3) 
{
  unsigned int i;

  min++;
  if (min == 60)
    min = 0;
  SetEvent(TASK4_ID, UPDATE_EVENT);
  INTCON3bits.INT2IF = 0;
  INTCON3bits.INT2IE = 1;
  TerminateTask();
}

Then the task TASK4 needs to wait for this new event :

...


  while(1)
  {
    WaitEvent(ALARM_EVENT | UPDATE_EVENT);
    GetEvent(id_tsk_run, &Time_event);

    if (Time_event & ALARM_EVENT)
      ClearEvent(ALARM_EVENT); 
    if (Time_event & UPDATE_EVENT)
      ClearEvent(UPDATE_EVENT); 

    Printf("%02d : %02d : %02d\r", (int)hour, (int)min, (int)sec);
  }

...

Do not forget to add the variable "EventMaskType Time_event" as a global or a local variable.

The UPDATE_EVENT definition is unknown in our application then update the define.h file :
...

/***********************************************************************
 * ----------------------------- Events --------------------------------
 **********************************************************************/
#define ALARM_EVENT       0x80
#define TASK1_EVENT       0x10
#define UPDATE_EVENT      0x02

#define RS_NEW_MSG        0x10
#define RS_RCV_MSG        0x20
#define RS_QUEUE_EMPTY    0x10
#define RS_QUEUE_FULL     0x20

...


> Final application

Now you can rebuild the application, load it into the PIC18F452 and test it, you will see the software swatch to be displayed in HyperTerminal and you will be able to update it in real-time with the push buttons.

Of course you can simplify the application. It has not been done to let the tutorial has simple as possible. You can also to add your own code and to use the PICos18 drivers : for instance you can add a RTC (Real Time Clock) on a I2C bus (then use the I2C master driver) ad update it from HyperTerminal...