To LUGNET HomepageTo LUGNET News HomepageTo LUGNET Guide Homepage
 Help on Searching
 
Post new message to lugnet.robotics.rcx.nqcOpen lugnet.robotics.rcx.nqc in your NNTP NewsreaderTo LUGNET News Traffic PageSign In (Members)
 Robotics / RCX / NQC / 828
827  |  829
Subject: 
"True" Interrupts
Newsgroups: 
lugnet.robotics.rcx.nqc
Date: 
Wed, 11 Oct 2000 09:16:47 GMT
Viewed: 
2005 times
  
When I did some work with the new control structures, I found out that they are
quite nice (monitor and acquire). However, you could do the same in the past
but you needed to split your program into several tasks which are controlled of
a task taking care of all events.

A thing that would be nice but isn't implemented in RIS2.0 are "true
interrupts", i.e. a program can be interrupted by an event and will continue
with the statement where it has been interrupted.

I tried to simulate such a behaviour. The objects needed for this (like with
processors/systems that have a true ionterrupt scheme) are:

task main_prog the essential program
prog_count program counter for "statements" of main program
i_flag         interrupt flag; set when an interrupt occurs (e.g. by a sensor
                event)
i_status interrupt status; if "enabled", interrupt command sequence
                is carried out
int. sequence   commands that will be carried out, if an interrupt occurs
                (i.e. i_flag = 1 & i_status = 1)

The scheme is:
· There is a main program that may be interrupted at any statement and if the
interrupt is allowed at this statement.
· If the interrupt is allowed and occurs, "program flow control" switches to
the interrupt routine.
· The status of motors and timers are saved on a stack.
· The interrupt sequence is carried out.
· Status of motors and timers are restored.
· Program flow control switches back to the interrupted statement of the main
program and continues.

The way I implemented the scheme is, by having a SWITCH statement in the main
program. Each case is a "statement".
The true NQC commands 1 to n have to be clustered into statements, each cluster
beginning with an Enable_Interrupt or Disable_Interrupt and ending with
incrementing the program counter.

Disable_interrupt
        Command 1
        ...            Statement  1
        Command i
Inc_prog_count
---
Enable_interrupt
        Command i+1    Statement  2
Inc_prog_count
---
Disable_interrupt
        Command i+2
        ...            Statement  3
        Command j
Inc_prog_count
---
Enable_interrupt
        Command j+1    Statement  4
Inc_prog_count
---
        etc.

The following sample program makes a robot move forward for 3 seconds, move
backwards for 3 seconds, turn around for 5 seconds and count and display to
1000. An interrupt can be generated by pressing a touch sensor (see complete
program below):

OnFwd (OUT_A + OUT_C);
ClearTimer (0);
---
until (Timer(0) == 30);
OnRev (OUT_A + OUT_C);
ClearTimer (0);
---
until (Timer(0) == 30);
---
OnFwd (OUT_A);
OnRev (OUT_C);
ClearTimer (0);
---
until (Timer(0) == 50);
Off (OUT_A + OUT_C);
---
SetUserDisplay (Counter (0), 0);
ClearCounter (0);
---
while (Counter (0) <= 999)
{
  IncCounter (0);
}
---
StopAllTasks ();
---

The "clustered" program with "statements":

switch (prog_count)
  {
/* statement 1 */ case 1 :
      {
      disable_interrupt;
         OnFwd (OUT_A + OUT_C);
         ClearTimer (0);
      inc_prog_count;
      }
/* statement 2 */ case 2 :
      {
      enable_interrupt;
         until (Timer(0) == 30);
      inc_prog_count;
      }
/* statusment 3 */ case 3 :
      {
      disable_interrupt;
         OnRev (OUT_A + OUT_C);
         ClearTimer (0);
      inc_prog_count;
      }
/* statement 4 */ case 4 : /
      {
      enable_interrupt;
         until (Timer(0) == 30);
      inc_prog_count;
      }
/* statement 5 */ case 5 :
      {
      disable_interrupt;
         OnFwd (OUT_A);
         OnRev (OUT_C);
         ClearTimer (0);
      inc_prog_count;
      }
/* statement 6 */ case 6
      {
      enable_interrupt;
         until (Timer(0) == 50);
         Off (OUT_A + OUT_C);
      inc_prog_count;
      }
/* statement 7 */ case 7
      {
      disable_interrupt;
         SetUserDisplay (Counter (0), 0);
         ClearCounter (0);
      inc_prog_count;
      }
/* statement 8 */ case 8 :
      {
      enable_interrupt;
         while (Counter (0) <= 999)
         {
           IncCounter (0);
         }
      inc_prog_count;
      }
    }
StopAllTasks ();


The above complete sample program:

#pragma reserve 0

#define enabled  1
#define disabled 0
#define mask_dir  0x08
#define mask_mode 0x80
#define inc_prog_count i_status = 0; prog_count = prog_count +1;
#define enable_interrupt i_status = enabled;
#define disable_interrupt i_status = disabled;

int i_status = disabled;
int i_flag = 0;
int stack [7];
int prog_count = 1;


task main ()                       /* main task only starts the other tasks */
{
  SetSensor (SENSOR_1, SENSOR_TOUCH);
  ClearSensor (SENSOR_1);

  start generate_interrupt;
  start interrupt_routine;
  start main_program;
}


task main_program ()                /* the essential program, consisting of
statements 1 ... n */
{
  switch (prog_count)
  {
/* statement 1 */ case 1 : /* set motors forward*/
      {
      disable_interrupt;
         OnFwd (OUT_A + OUT_C);
         ClearTimer (0);
      inc_prog_count;
      }
/* statement 2 */ case 2 : /* move forward 3 seconds */
      {
      enable_interrupt;
         until (Timer(0) == 30);
      inc_prog_count;
      }
/* statusment 3 */ case 3 : /* set motors reverse*/
      {
      disable_interrupt;  /* redundant, "syntactic suggar" */
         OnRev (OUT_A + OUT_C);
         ClearTimer (0);
      inc_prog_count;
      }
/* statement 4 */ case 4 : /* move backwards 3 seconds */
      {
      enable_interrupt;
         until (Timer(0) == 30);
      inc_prog_count;
      }
/* statement 5 */ case 5 : /* set motors for turning*/
      {
      disable_interrupt;
         OnFwd (OUT_A);
         OnRev (OUT_C);
         ClearTimer (0);
      inc_prog_count;
      }
/* statement 6 */ case 6 : /* turn around for 5 seconds */
      {
      enable_interrupt;
         until (Timer(0) == 50);
         Off (OUT_A + OUT_C);
      inc_prog_count;
      }
/* statement 7 */ case 7 : /* set display and prepare loop */
      {
      disable_interrupt;
         SetUserDisplay (Counter (0), 0);
         ClearCounter (0);
      inc_prog_count;
      }
/* statement 8 */ case 8 : /* count and display to 1000*/
      {
      enable_interrupt;
         while (Counter (0) <= 999)
         {
           IncCounter (0);
         }
      inc_prog_count;
      }
// statement n    case n :
//    {
//    en-/disable_interrupt;
//        ...
//    inc_prog_count;
//    }
      default :
      {
      }
  }
  StopAllTasks ();
}


task interrupt_routine ()
{
  while (true)
  {
    if ((i_status == enabled) && (i_flag == 1)) /* check whether interrupt flag
set and interrupt enabled */
    {
      stop main_program;                        /* interrupt active task */
//-- interrupt save sequence ---------------
      stack [0] = Timer (0);
      stack [1] = Timer (1);
      stack [2] = Timer (2);
      stack [3] = Timer (3);
      stack [4] = OutputStatus (0);
      stack [5] = OutputStatus (1);
      stack [6] = OutputStatus (2);
//-------------------------------------------
      Off (OUT_A + OUT_B + OUT_C);              /* all motors off */
//-- interrupt command sequence ------------
      PlaySound (SOUND_UP);
      Wait (200);
      // ...
//-- interrupt restore sequence ------------
      if ((stack [6] & mask_dir) == mask_dir) SetDirection (OUT_C, OUT_FWD);
        else SetDirection (OUT_C, OUT_REV);
      if ((stack [5] & mask_dir) == mask_dir) SetDirection (OUT_B, OUT_FWD);
        else SetDirection (OUT_B, OUT_REV);
      if ((stack [4] & mask_dir) == mask_dir) SetDirection (OUT_A, OUT_FWD);
        else SetDirection (OUT_A, OUT_REV);
      if ((stack [6] & mask_mode) == mask_mode) SetOutput (OUT_C, OUT_ON);
        else SetOutput (OUT_C, OUT_OFF);
      if ((stack [5] & mask_mode) == mask_mode) SetOutput (OUT_B, OUT_ON);
        else SetOutput (OUT_B, OUT_OFF);
      if ((stack [4] & mask_mode) == mask_mode) SetOutput (OUT_A, OUT_ON);
        else SetOutput (OUT_A, OUT_OFF);
      SetTimer (3, stack [3]);
      SetTimer (2, stack [2]);
      SetTimer (1, stack [1]);
      SetTimer (0, stack [0]);
//------------------------------------------
      i_flag = 0;                               /* clear interrupt flag */
      start main_program;                       /* return control to
interrupted program */
    }
  }
}


task generate_interrupt ()                     /* simple task that generates
interrupt if SENSOR_1 pressed */
{
  while (true)
  {
    if (SENSOR_1 == 1) i_flag = 1;
  }
}

If an interrupt occurs e.g. during moving forward at 1.2 seconds, the interrupt
sequence is carried out (play a sound and wait). After the interrupt sequence
is completed, the robot completes the interrupted statement, i.e. moving
forward for 1.8 seconds. The same holds for turning and counting.

I know that the above approach is more or less of an "academic nature".
Especially, if you have REPEAT loops and other control structures, the above
scheme does not work without "manual work" (perhaps, it won't work at all, due
to very complicated structures).

The next question is, if it is really necessary to take over control completely
in an interrupt situation.

However, if there are situation where a movement has to be interrupted and
completed afterwards, the above scheme satisfies this need.
Nevertheless, a "true interrupt behaviour" of the RCX would be fine and would
give me the feeling of "total control" over the unit.



1 Message in This Thread:

Entire Thread on One Page:
Nested:  All | Brief | Compact | Dots
Linear:  All | Brief | Compact
    

Custom Search

©2005 LUGNET. All rights reserved. - hosted by steinbruch.info GbR