Blog 3 | RTOS | Kuba

 These last two weeks started with us becoming familiar with how the example9 (basic kernal) code from the previous two weeks works. This included looking at the serial interrupt task, which waits for input to be typed in the terminal. This code also includes two other tasks: "The_first_rtos_task" and "The second_rtos_task". Both of these tasks toggle an LED at different rates, and print a message "Task X LED On/Off". The final task is the kernal itself. This task waits for one of the four possible inputs : "enable1", "enable2", "disable1", and "disable2". All of these inputs either enable or disable one of the two previously mentioned tasks.

The first aim of the work to be done this week was to work with our partner, Tao in my case, to iterate on this kernal example and add input and output tasks as well as a state machine task that controls the outputs depending on the inputs and the current state. State machines were explained more in the first blog. The first step in this process was the creation of a state machine state diagram to show what functionality we want to implement. We decided on two inputs, a button, as well as a counter which when it reaches 10 acts as an input. At this point we weren't aware of using the ADC input from the potentiometer as an input, which would replace the counter input looking back now. The state diagram for the state machine is shown below.

The next step was deciding the structure of the code, in terms of the number of tasks and how they interact. It was chosen to have a kernal task that can independently enable or disable the two outputs. There are also two input tasks, which send messages when the input is activated. There are two output tasks which make the outputs happen when they receive a message. The heart of the code is the FSM task. This task jumps between states S0, S1, and S2. The inputs are messages in its queue from the input tasks. Depending on the state and the input the FSM then jumps to a different state and chooses an output. The output is a message sent to the output tasks. A diagram of the code is shown below.


The implementation began with me working on the FSM and Tao working on the output tasks. The FSM task first polls to see if it has any messages and if so reads it into the state_input variable. What follows is an if - else if - else ladder for each state, with another if - else if inside each state for each input. Then the FSM sends messages to the output tasks depending on the state and inputs. I also then implemented the input tasks. Initially the tasks were two seperate counters that sent an input when reaching a certain number, 10 and 15 respectively in this case. After we received example code on how to use the button as input, one of the counter inputs was changed to a button input. As mentioned earlier we didn't notice the ADC input option, so that could be a future improvement for the other input. Tao wrote the two output tasks. One of the tasks wrote "LED On" to the LCD, which happened after the counter reached 10, and the other wrote "LED Off", after the button was pressed. The FSM also printed messages to the terminal indicating state transitions to have a visual confirmation of states changing. As well as that we are printing the count as it increments and a message once it reaches 10, to see if the LCD changing matches up with the count as intended. The final code 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"


#define BTN PIN_A4


#define RED PIN_B5

#define GREEN PIN_A5

#define s0 1

#define s1 2

#define s2 3

#define input1 1

#define input2 2

#define output1 1

#define output2 2


#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;


int8 count1;

int8 state_input = 0;

int8 state;

// different commands

char en1 [ ] = "enable1";

char en2 [ ] = "enable2";

char dis1 [ ] = "disable1";

char dis2 [ ] = "disable2";


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

void input1_task();


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

void input2_task();


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

void output_task1();


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

void output_task2();


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

void FSM_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 input1_task(){

   if(count1 == 10){

      printf("Count reached 10\n\r");

      count1 = 0;

      rtos_msg_send(FSM_task, input1);

   }

   else{

      count1 = count1 + 1;

      printf("Count = %i \n\r", count1);

      rtos_msg_send(FSM_task, 0);

   }

}


void input2_task(){

   if(!input(PIN_A4)){

      rtos_msg_send(FSM_task, input2);

   }

   else{

      rtos_msg_send(FSM_task, 0);

   }

}


void output_task1(){

   if(rtos_msg_poll()>0){

      if(rtos_msg_read() == output1){

         lcd_gotoxy(1,2);

         printf(lcd_putc, "LED ON (cnt=10) ");

      }

   }

}


void output_task2(){

   if(rtos_msg_poll()>0){

      if(rtos_msg_read() == output2){

         lcd_gotoxy(1,2);

         printf(lcd_putc, "LED OFF (button)");

      }

   }

}


void FSM_task(){

   if(rtos_msg_poll()>0){

      state_input = rtos_msg_read();

   }

   if(state == s0){

      lcd_gotoxy(1,2);

      printf(lcd_putc, "In IDLE state");

      if(state_input == input1){

         rtos_msg_send(output_task1, output1);

         printf("s0 -> s1\n\r");

         state = s1;

      }

      else if(state_input == input2){

         rtos_msg_send(output_task2, output2);

         printf("s0 -> s2\n\r");

         state = s2;

      }

   }

   else if(state == s1){

      if(state_input == input1){

         rtos_msg_send(output_task1, output1);

         printf("s1 -> s1\n\r");

         state = s1;

      }

      else if(state_input == input2){

         rtos_msg_send(output_task2, output2);

         printf("s1 -> s2\n\r");

         state = s2;

      } 

   }

   else if(state == s2){

      if(state_input == input1){

         rtos_msg_send(output_task1, output1);

         printf("s2 -> s1\n\r");

         state = s1;

      }

      else if(state_input == input2){

         rtos_msg_send(output_task2, output2);

         printf("s2 -> s2\n\r");

         state = s2;

      }   

   }

}


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 ( output_task1 );

      else if ( !strcmp( input , en2 ) )

         rtos_enable ( output_task2 );

      else if ( !strcmp( input , dis1 ) )

         rtos_disable ( output_task1 );

      else if ( !strcmp ( input , dis2 ) )

         rtos_disable ( output_task2 );

      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();

   state = s0;

   count1 = 0;

   count2 = 0;

   

   SET_TRIS_B(0x00);

   SET_TRIS_a(0xDF);

   setup_adc(ADC_CLOCK_INTERNAL);

   setup_adc_ports(AN0);

   set_adc_channel(0);

   

   rtos_run();

}


The video below shows both the LCD and the terminal, where you can see the count incrementing, state transitions, LED Off when button is pressed, and then also LED on after the count reaches 10 again.


Comparing to example
This part of the blog will compare our implementation of an FSM in an RTOS context to that provided later by Jason. Both of our implementations have an input tasks which waits for a button press, however the implementation is a bit different. While mine and Tao's implementation waits for a button press while the task runs, the example code has this in a while loop. Essentially our code requires keeping the button pressed until that task runs, while the example code accepts a button input at any time. As mentioned earlier, our second input task is just a simple counter, whereas the example code has an input task which takes a measurement from the ADC from the potentiometer, where the input sends a message to the state machine if the value read from the ADC is larger than 100. Both input tasks also print to the LCD when the inputs are toggled.

Both output tasks in the example code toggle two different messages on the LCD. This is slightly different to our implementation as the outputs are different, where as our code essentially "toggled" the same LED depending on the output. Essentially our outputs controlled one external resource while the example code outputs affect two different external resources.

Comparing the state machine we instantly see that the example code has a few more states, and while our output decisions based on input were nested within the states, the example code contains the state and input decision within one if statement. Our code also went to state S0 at startup and then jumped between states S1 and S2 based on state and input. The example code cycles always from states 1 to 2 to 3 to 4 and back to 1, depending on inputs. In the example code only the button input seems to have any control, where each state transitions to the next after a button press. Including the ADC input shows that it is an option, but it remains unused in the code.

The overall implementation of the FSM and output tasks is pretty similar between our code and the example code, but the input code is implemented a lot more efficiently and smartly in the example code.


These two weeks working on the state machine rtos implementation I feel like served as a good exercise for the whole class in preparation for working on the binbot project. Each groups ideas and implementation of the FSM and RTOS implementation was different, and when comparing to each other and against the example code we can see how differently everyone approaches the goal. This just goes to show how multiple people can look at the same exact same information and tasks and come out with completely different thoughts, plans, and approaches. This can help in coming up with diverse and different ideas on a project such as the BinBot.

Comments

Popular posts from this blog

Blog 1 - Igor Kapusniak