The programming model used to operate the RCX brick is that of a multithreaded environment. The use of threads is not required, however most problems that the brick will probably be used for will be real world and thus are very applicable to threading. Beware, the current release (legOS-0.1.7) doesn't support mutexes or any other pthread-like locking mechanisms. Each thread should play with it's own data, or you will run the risk of data corruption. This is supposed to be fixed RSN.

(all the code below needs unistd.h and tm.h)

Threads are created with the execi() call. It takes 3 arguments:

start function for the thread
thread priority
If NO_EQUAL_PRIORITIES is defined in legOS' include/config.h, make each priority different.
stack size
Normally this will be DEFAULT_STACK_SIZE (1Kb).
it returns a -1 if the thread cannot be created or a handle for the process if it could be started. The normal way that the threads are set up is:
pid_t t1...tn;
int stop_process(void);
 
int main(void) {
  t1=execi(&stop_process,1,DEFAULT_STACK_SIZE);  /* handle the "run" button
                                                    to stop the program */
  tn=execi(...)              /* all the other threads in the system */
  tm_start();                /* start the threads running */
  return 0;
}
The code within the stop_process() call tree would handle the shutdown of the other threads (more on this in a bit).

Threads should be nice to each other and release their timeslice when they are done with their processing. If a thread is waiting on an event there are ways to test the event and then quickly hand control to another thread. For front panel buttons:

#include "direct-button.h"

wakeup_t button_press_wakeup(wakeup_t data) {
  return(PRESSED(button_state(),data));
}

wakeup_t button_release_wakeup(wakeup_t data) {
  return(RELEASED(button_state(),data));
}

int stop_process(void) {
  wait_event(&button_release_wakeup,BUTTON_RUN); 
  msleep(250);  /* debounce the button */
  wait_event(&button_press_wakeup,BUTTON_RUN);  
  kill(t1);     /* stop the other threads */
  ...
  kill(tn);
  return(0);
}
For sensors:
#include "direct-sensor.h"
#include "direct-motor.h"

wakeup_t bumper_hit(wakeup_t data) {
  return (data<32768);
} 

int hit_something(void) {
  wait_event(&bumper_hit,SENSOR_1);
  motor_a_dir(off);
}
The wait_event call will poll the function until it returns non-zero. (this is actually handled within the thread scheduler, so is fairly efficient)

For threads that wish to release their timeslice directly there is the yield() call.

To stop a thread from within the thread a call to exit(return_code) is used. To kill a thread from another thread the kill(thread_handle) call is used. Remember that stuff like motor states are not associated with any thread and will only be automatically stopped when the main program calls exit().
Notes on floating point
The processor in the RCX brick has no floating point hardware. In order to use any floating point functions a floating point library has to be linked with the program that is being compiled for the brick. There are 3 choices:
Roll your own.
Use the floating point routines that come with GCC.
The problem with this is that the library will take around 6K of memory on the 32K brick. If you have the cross compiler, you will have the code to build this. Check the legOS-HOWTO for details.
Use the floating point emulation library in the librcx library.
The size of the floating point portion of this library is a whopping 1.1K. This is the route that I used. Build the libfloat.a portion of librcx and install it in /usr/local/mindstorms/legOS/lib. In the Makefile, add -lfloat to the LEGOS_OBJ line:
  LEGOS_OBJ=$(LEGOS_ROOT)kmain.o -llegOS -lfloat

Here is a test program for the floating point routines. It solves for the square root of 2 and prints it on the display.
#include "conio.h" 
#include "unistd.h"
#include "direct-button.h" 
#include "sys/tm.h"

pid_t t1,t2;

wakeup_t button_press_wakeup(wakeup_t data) {
  return PRESSED(button_state(),data);
}

wakeup_t button_release_wakeup(wakeup_t data) {
  return RELEASED(button_state(),data);
}

#define ABS(x) (((x)<0)?(-(x)):(x))
#define TOLERANCE 0.00001   /* display is only 4 digits wide anyway */

int root_solver(void) {
  float value=2.0;
  float x=value/2.0,delta=1.0;
  int count=0;

  /* use a Newtonian root solver to get sqrt(value) */
  while ((ABS(delta)>TOLERANCE) && (count++<100))
    x+=(delta=(value-x*x)/(2.0*x));

  lcd_number((int)(x*1000),sign,e_3);
  lcd_refresh();
  return(0);
}

int prog_stopper(void) {
  wait_event(&button_release_wakeup,BUTTON_RUN);
  msleep(200);
  wait_event(&button_press_wakeup,BUTTON_RUN);
  kill(t1);
  return(0);
}

int main(void) {
  lcd_clear();
  lcd_refresh();
  t1=execi(&root_solver ,1,DEFAULT_STACK_SIZE);
  t2=execi(&prog_stopper,2,DEFAULT_STACK_SIZE);
  tm_start();
  return 0;
}


Previous - Downloading the SREC Files to the Brick Main Next - Sensors
Monty Stein Dec 18, 1999
CLUG Home