Kuba Blog 2

 RTOS Code Examples

Example 1: Tasks

Original code for example 1:

#include <16f877.h>

#use delay(clock=20000000)

#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)

#use rtos(timer=1,minor_cycle=100ms)

#task(rate=1000ms,max=100ms)

// the function can be called anything that a standard function can be called

void The_first_rtos_task ( )

{

   printf("1\n\r");

}


#task(rate=500ms,max=100ms)

void The_second_rtos_task ( )

{

   printf("\t2!\n\r");

}


#task(rate=100ms,max=100ms)

void The_third_rtos_task ( )

{

   printf("\t\t3\n\r");   

}


#task(rate=100ms,max=100ms)

void The_fourth_rtos_task ( )

{

   printf("\t\t4\n\r");

}


/*#task(rate=100ms,max=100ms)

void The_fifth_rtos_task ( )

{

   printf("\t\t\t\t5\n\r");

}


#task(rate=100ms,max=100ms)

void The_sixth_rtos_task ( )

{

   printf("\t\t\t\t\t6\n\r");

}*/


// main is still the entry point for the program

void main ( )

{

   // rtos_run begins the loop which will call the task functions above at the

   // schedualed time

   rtos_run ( );

}

This code shows off the basic concept of tasks. The RTOS jumps between tasks 1 to 4 at different rates defined in the #task directive before each task function. The screenshot below shows the output of this code, showing how the rate of each task differs.


The most obvious modification to add to the code to see a difference in the behaviour is to add other tasks, and to make task 3 and 4 print on different lines to easier distinguish between them. The code snippet below shows the extra tasks that I added to the example code.

#task(rate=100ms,max=100ms)

void The_fourth_rtos_task ( )

{

   printf("\t\t\t4\n\r");

}


#task(rate=100ms,max=100ms)

void The_fifth_rtos_task ( )

{

   printf("\t\t\t\t5\n\r");

}


#task(rate=100ms,max=100ms)

void The_sixth_rtos_task ( )

{

   printf("\t\t\t\t\t6\n\r");

}

The screenshot below shows the output of this RTOS code, and we can see the extra tasks added in.


This first example is really simple but it shows the fundamental concept of RTOSs as we can see how it jumps from one task to another and after finishing loops back around.

Example 2: Termination

Below is the code given for example 2:

#include <16f877a.h>

#use delay(clock=20000000)

#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)

#use rtos(timer=1,minor_cycle=100ms)


// a counter will be kept

int8 counter;


#task(rate=1000ms,max=100ms)

void The_first_rtos_task ( )

{

   printf("1\n\r");

   // if the counter has reached the desired value, the rtos will terminate

   if(++counter==5)

      rtos_terminate ( );

}


#task(rate=500ms,max=100ms)

void The_second_rtos_task ( )

{

   printf("\t2!\n\r");

}


#task(rate=100ms,max=100ms)

void The_third_rtos_task ( )

{

   printf("\t\t3\n\r");

}


void main ( )

{

   // main is the best place to initialize resources the the rtos is dependent

   // upon

   counter = 0;

   rtos_run ( );

   // once the rtos_terminate function has been called, rtos_run will return

   // program control back to main

   printf("RTOS has been terminated\n\r");

}

This example operates in the same way as the first one, where the RTOS jumps between tasks 1 to 3. In this case the code is trying to show the terminate() function available for RTOS, which terminates the RTOS and stops it from running any further. In this case whenever the RTOS runs task 1 it increments a counter, and when the counter reaches 5, the RTOS is terminated. The picture below shows the output of this code, showing how the RTOS is terminated right after task 1 completes, since the count reached 5.


One of the things that came straight to mind is if it is possible to restart the RTOS after termination. After the class threw around ideas and tried different approaches we found a way to do this. The code snippet below shows a modification made to main(), which enables the RTOS to restart after termination.

void main ( )
{
   // main is the best place to initialize resources the the rtos is dependent
   // upon
   while(1){
   counter=0;
   rtos_run();
   printf("RTOS restarting\n\r");
   
   }

The screenshot below shows the output of this modified script, where we can see the RTOS restarting after termination.

Example 2 showcased the RTOS directive terminate(), which allows for the termination of the RTOS when desired.

Example 3: Enable and Disable
This example shows off the enable() and disable() functions available for RTOS. The disable function disables the specified task until it is enabled. Below is the example 3 code:

#include <16f877a.h>
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#use rtos(timer=1,minor_cycle=100ms)

int8 counter;

// now that task names will be passed as parameters, it is best
// to declare function prototypes so that there are no undefined
// identifier errors from the compiler
#task(rate=1000ms,max=100ms)
void The_first_rtos_task ( );

#task(rate=500ms,max=100ms)
void The_second_rtos_task ( );

#task(rate=100ms,max=100ms)
void The_third_rtos_task ( );

void The_first_rtos_task ( ) {
   printf("1\n\r");
   if(counter==3)
   {
      // to disable a task, simply pass the task name
      // into the rtos_disable function
      rtos_disable(The_third_rtos_task);
   }
}

void The_second_rtos_task ( ) {
   printf("\t2!\n\r");
   if(++counter==10) {
      counter=0;
      // enabling tasks is similar to disabling them
      rtos_enable(The_third_rtos_task);
   }
}

void The_third_rtos_task ( ) {
   printf("\t\t3\n\r");
}

void main ( ) {
   counter = 0;
   rtos_run ( );
}

This code disables task 3 after count increments to 3 and enables it ounce count reaches 10. THen count is reset back to 0. I added a task 4 to show how multiple tasks can be enabled or disabled simultaneously. The tasks being enabled and disables is shown below:





Example 4: Messages
This example shows the message sending functions of RTOS. Below is the code:
#include <16f877a.h>
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#use rtos(timer=1,minor_cycle=100ms)

int8 count;

// each task will now be given a two byte queue
#task(rate=1000ms,max=100ms,queue=2)
void The_first_rtos_task ( );

#task(rate=500ms,max=100ms,queue=2)
void The_second_rtos_task ( );

void The_first_rtos_task ( ) {
   // the function rtos_msg_poll will return the number of messages in the
   // current tasks queue
   // always make sure to check that their is a message or else the read
   // function will hang
   if(rtos_msg_poll ( )>0){
      // the function rtos_msg_read, reads the first value in the queue
      printf("messages recieved by task1 : %i\n\r",rtos_msg_read ( ));
      // the funciton rtos_msg_send, sends the value given as the
      // second parameter to the function given as the first
      rtos_msg_send(The_second_rtos_task,count);
      count++;
   }
}

void The_second_rtos_task ( ) {
   rtos_msg_send(The_first_rtos_task,count);
   if(rtos_msg_poll ( )>0){
      printf("messages recieved by task2 : %i\n\r",rtos_msg_read ( ));
      count++;
   }
}

void main ( ) {
   count=0;
   rtos_run();
}

The function rtos_msg_send() allows one task to send a message to another. The function rtos_msg_poll() allows a task to check if it has any messages in its queue, and finally the rtos_msg_read() task allows a task to read the first message in its queue. The example code above shows two tasks sending messages to each other, where each task increments a count and sends it back to the other task. The output of this is shown in the screenshot below.

I added the following task which sends a message to tasks 1 and 2., showing how a task may receive messages from multiple different tasks.

void The_third_rtos_task ( ) {
   rtos_msg_send(The_first_rtos_task,33);
   rtos_msg_send(The_second_rtos_task,33);

Tasks 1 and 2 receive the number 33 as a message from task 3, as well as the count as shown below:

These message functions are useful and can be used to synchronize events and actions between tasks potentially.

Example 5: Yield
The code for this example is shown below:

#include <16f877a.h>
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#use rtos(timer=1,minor_cycle=100ms)

#task(rate=1000ms,max=100ms,queue=2)
void The_first_rtos_task ( );

#task(rate=500ms,max=100ms,queue=2)
void The_second_rtos_task ( );

void The_first_rtos_task ( ) {
   int count=0;
   // rtos_yield allows the user to break out of a task at a given point
   // and return to the same ponit when the task comes back into context
   while(TRUE){
      count++;
      rtos_msg_send(The_second_rtos_task,count);
      rtos_yield ( );
   }
}

void The_second_rtos_task ( ) {
   if(rtos_msg_poll( ))
   {
      printf("count is : %i\n\r",rtos_msg_read ( ));
   }
}

void main ( ) {
   rtos_run();
}

This code introduces the yield task. This task essentially stops a task from operating any further until another task performs any action concerning the yielded task. In this case task 1 yields after sending a message. The task then continues operation once task 2 reads the message sent by task 1. Since the rate of task 2 is 500ms which is faster than task 1, we can see in the video below that task 1 doesn't really get affected by the yield:




I modified the rate of task 2 to 5 seconds, and the video below shows that task 1 is yielded for longer:




This yield function will be useful when a specific task is required to be paused when certain conditions are met.

Example 6: Semaphores
The code for example 6 is shown below:
#include <16f877a.h>
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#use rtos(timer=1,minor_cycle=100ms)

#include <stdio.h>
#include <stdlib.h>
#include "BENGLCD420.C"

// a semaphore is simply a shared system resource
// in the case of this example, the semaphore will be the red LED
int8 sem;
#define RED PIN_B4

#task(rate=1000ms,max=100ms,queue=2)
void The_first_rtos_task ( );

#task(rate=1000ms,max=100ms,queue=2)
void The_second_rtos_task ( );

void The_first_rtos_task ( ) {
   int i;

   // this will decrement the semaphore variable to zero which signals
   // that no more user may use the resource
   rtos_wait(sem);
   for(i=0;i<5;i++){
      
      //output_low(RED); delay_ms(1000); output_high(RED); delay_ms(1000);
      
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"task1 red on  ");
      //clear lcd display 
      delay_ms(1000);
      //lcd_putc('\f');
      
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"task1 red off");
      //clear lcd display 
      delay_ms(1000);
      //lcd_putc('\f');
      
      
      rtos_yield ( );
   }
   // this will inrement the semaphore variable to zero which then signals
   // that the resource is available for use
   rtos_signal(sem);
}

void The_second_rtos_task ( ) {
   int i;
   rtos_wait(sem);
   for(i=0;i<5;i++){
      //output_high(RED); delay_ms(500); output_low(RED);  delay_ms(500);
      
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"task 2 red on  ");
      //clear lcd display 
      delay_ms(200);
      //lcd_putc('\f');
      
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"task 2 red off");
      //clear lcd display 
      delay_ms(200);
      //lcd_putc('\f');
   }
   rtos_signal(sem);
}

void main ( ) {
   // sem is initialized to the number of users allowed by the resource
   // in the case of the LED and most other resources that limit is one
   sem=1;
      //init lcd
   lcd_init();
   
   rtos_run();
}

This code introduces the concept of semaphores. In the context of RTOS semaphores are variables that manage coordination and resource usage between tasks. Essentially they are shared resources that can be used by X amount of tasks simultaneously. The code above sets the semaphore to 1, meaning only one task at a time can use the resource, and since both tasks rely on the semaphore for their internal code, only one task's code runs at a time. When a task uses a semaphore it decrements the semaphore by 1, in this case to 0, and when a semaphore is at 0 no other task can use it. The video below shows how only one tasks code runs at once.




After adding a third task :

void The_third_rtos_task ( ) {
   int i;
   rtos_wait(sem);
   for(i=0;i<5;i++){
      //output_high(RED); delay_ms(500); output_low(RED);  delay_ms(500);
      
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"/n/ntask 3 red on  ");
      //clear lcd display 
      delay_ms(400);
      //lcd_putc('\f');
      
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"/n/ntask 3 red off");
      //clear lcd display 
      delay_ms(400);
      //lcd_putc('\f');
   }
   rtos_signal(sem);
}
 
, we see how 3 tasks an run, but not at the same time, only the one that holds the semaphore at the time, as seen in the video:




Example 7: Await
This example introduces the await function. The code for this example is :

#include <16f877a.h>
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#use rtos(timer=1,minor_cycle=100ms)

#include <stdio.h>
#include <stdlib.h>
#include "BENGLCD420.C"

#define RED PIN_B4
#define GREEN PIN_B5

int8 count;

#task(rate=1000ms,max=100ms,queue=2)
void The_first_rtos_task ( );

#task(rate=1000ms,max=100ms,queue=2)
void The_second_rtos_task ( );

void The_first_rtos_task ( ) {
   // rtos_await simply waits for the given expression to be true
   // if it is not true, it acts like an rtos_yield and passes the system
   // to the next task
   rtos_await(count==10);
   //output_low(GREEN); delay_ms(200); output_high(GREEN); delay_ms(200);
         
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"task1 red on  ");
      //clear lcd display 
      delay_ms(500);
      //lcd_putc('\f');
      
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"task1 red off");
      //clear lcd display 
      delay_ms(500);
      //lcd_putc('\f');
   count=0;
}

void The_second_rtos_task ( ) {
   //output_low(RED); delay_ms(200); output_high(RED);delay_ms(200);
        lcd_gotoxy( 1,1);
      printf(lcd_putc,"task2 red on  ");
      //clear lcd display 
      delay_ms(200);
      //lcd_putc('\f');
      
      lcd_gotoxy( 1,1);
      printf(lcd_putc,"task2 red off");
      //clear lcd display 
      delay_ms(200);
      //lcd_putc('\f');
   
   count++;
}

void main ( ) {

   count=0;
   
   //init lcd
   lcd_init();
   
   rtos_run();
}

This code introduces rtos_await(). This function essentially waits for the condition inside the brackets to be true before proceeding with the task. In this code task 2 switches between led on and led off, and every time it does it increments a count. Task 1 contains rtos_await(count == 10), so it waits until count is 10 before executing it's code, and resetting count to 0. This means that task 2 executes 10 times for every one time task 1 executes. This is shown in the video below:




I added a third task which awaits for the count to be 4 or 8 to execute its code:

void The_third_rtos_task ( ) {
   // rtos_await simply waits for the given expression to be true
   // if it is not true, it acts like an rtos_yield and passes the system
   // to the next task
   rtos_await(count==4 || count==8);
   //output_low(GREEN); delay_ms(200); output_high(GREEN); delay_ms(200);
         
      lcd_gotoxy( 1,3);
      printf(lcd_putc,"task3 red on  ");
      //clear lcd display 
      delay_ms(500);
      //lcd_putc('\f');
      
      lcd_gotoxy( 1,3);
      printf(lcd_putc,"task3 red off");
      //clear lcd display 
      delay_ms(500);
      //lcd_putc('\f');
}

The output is shown in the video below:






Example 8: Statistics
The code for example 8 is shown below:

#include <16f877a.h>
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#use rtos(timer=1,minor_cycle=100ms,statistics)

// This structure must be defined inorder to retrieve the statistical
// information
struct rtos_stats {
   int32 task_total_ticks;       // number of ticks the task has used
   int16 task_min_ticks;         // the minimum number of ticks used
   int16 task_max_ticks;         // the maximum number of ticks ueed
   int16 hns_per_tick;           // us = (ticks*hns_per_tic)/10
};

#task(rate=1000ms,max=100ms)
void The_first_rtos_task ( );

#task(rate=1000ms,max=100ms)
void The_second_rtos_task ( );

void The_first_rtos_task ( ) {
   struct rtos_stats stats;
   rtos_stats(The_second_rtos_task,&stats);
   printf ( "\n\r" );
   printf ( "task_total_ticks : %Lius\n\r" ,
            (int32)(stats.task_total_ticks)*stats.hns_per_tick );
   printf ( "task_min_ticks   : %Lius\n\r" ,
            (int32)(stats.task_min_ticks)*stats.hns_per_tick );
   printf ( "task_max_ticks   : %Lius\n\r" ,
            (int32)(stats.task_max_ticks)*stats.hns_per_tick );
   printf ("\n\r");
}

void The_second_rtos_task ( ) {
   int i, count = 0;

   while(TRUE) {
      if(rtos_overrun(the_second_rtos_task)) {
         printf("The Second Task has Overrun\n\r\n\r");
         count=0;
      }
      else
        count++;

      for(i=0;i<count;i++)
         delay_ms(50);

      rtos_yield();
   }
}

void main ( ) {
   rtos_run ( );
}


This code introduces the statistical functions available with RTOS. These include: task_total_ticks() which counts the number of ticks a task has used, task_min_ticks() which returns the minimum number of ticks a task has used, and task_max_ticks() which returns the maximum number of ticks a task has used. To use these functions the structure "rtos_stats" must be defined at the beginning of a program. The code given obtains these statistics for task 2 every time it is executed, using task 1, while task 2 prints a message every time task 2 overruns. This is shown below.



Example 9: A basic kernel

THe code for example 9 is :


#include <16f877a.h>

#use delay(clock=20000000)

#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)

#use rtos(timer=1,minor_cycle=100ms)


#include <stdio.h>

#include <stdlib.h>

#include "BENGLCD420.C"


#define RED PIN_B5

#define GREEN PIN_A5


#include <string.h>


// this character array will be used to take input from the prompt

char input [ 30 ];

// this will hold the current position in the array

int index;

// this will signal to the kernal that input is ready to be processed

int1 input_ready;

// different commands

char en1 [ ] = "enable1";

char en2 [ ] = "enable2";

char dis1 [ ] = "disable1";

char dis2 [ ] = "disable2";


#task(rate=1000ms,max=100ms)

void The_first_rtos_task ( );


#task(rate=1000ms,max=100ms)

void The_second_rtos_task ( );


#task(rate=500ms,max=100ms)

void The_kernal ( );


// serial interupt

#int_rda

void serial_interrupt ( )

{

   if(index<29) {

      input [ index ] = getc ( );   // get the value in the serial recieve reg

      putc ( input [ index ] );     // display it on the screen

      if(input[index]==0x0d){       // if the input was enter

         putc('\n');

         input [ index ] = '\0';    // add the null character

         input_ready=TRUE;          // set the input read variable to true

         index=0;                   // and reset the index

      }

      else if (input[index]==0x08){

         if ( index > 1 ) {

            putc(' ');

            putc(0x08);

            index-=2;

         }

      }

      index++;

   }

   else {

      putc ( '\n' );

      putc ( '\r' );

      input [ index ] = '\0';

      index = 0;

      input_ready = TRUE;

   }

}


void The_first_rtos_task ( ) {

   //output_low(RED); delay_ms(50); output_high(RED);

           lcd_gotoxy( 1,2);

      printf(lcd_putc,"task1 red on  ");

      //clear lcd display 

      delay_ms(200);

      //lcd_putc('\f');

      

      lcd_gotoxy( 1,2);

      printf(lcd_putc,"task1 red off");

      //clear lcd display 

      delay_ms(200);

      //lcd_putc('\f');

}


void The_second_rtos_task ( ) {

   //output_low(GREEN); delay_ms(20); output_high(GREEN);

           lcd_gotoxy( 1,1);

      printf(lcd_putc,"task2 red on  ");

      //clear lcd display 

      delay_ms(200);

      //lcd_putc('\f');

      

      lcd_gotoxy( 1,1);

      printf(lcd_putc,"task2 red off");

      //clear lcd display 

      delay_ms(200);

      //lcd_putc('\f');

}


void The_kernal ( ) {

   while ( TRUE ) {

      printf ( "INPUT:> " );

      while(!input_ready)

         rtos_yield ( );


      printf ( "%S\n\r%S\n\r", input , en1 );


      if ( !strcmp( input , en1 ) )

         rtos_enable ( The_first_rtos_task );

      else if ( !strcmp( input , en2 ) )

         rtos_enable ( The_second_rtos_task );

      else if ( !strcmp( input , dis1 ) )

         rtos_disable ( The_first_rtos_task );

      else if ( !strcmp ( input , dis2 ) )

         rtos_disable ( The_second_rtos_task );

      else

         printf ( "Error: unknown command\n\r" );


      input_ready=FALSE;

      index=0;

   }

}


void main ( ) {

   // initialize input variables

   index=0;

   input_ready=FALSE;

   // initialize interrupts

   enable_interrupts(int_rda);

   enable_interrupts(global);

   

      //init lcd

   lcd_init();

   rtos_run();

}


This code builds a basic kernel, comprised of multiple functions and task. The two tasks are similar to the program that introduced "yield", where each task switches on and off an LED. The code also contains a serial_interrupt() function, which reads command line commands. The 4 commands are "enable1", "enable2", "disable1" and "disable2". The kernel runs the task while waiting for these commands. The commands enable or disable tasks 1 or 2. The operation in the command line is shown below:




Comments

Popular posts from this blog

Blog 1 - Igor Kapusniak