The RL language is a subset of C with additions for state machines and instrinsics for controlling the system environment.
Each RL program is contained complete in a single file and contains 2 sections - a declarations section followed by a main program section.
This compiler does not have a C-style preprocessor built in, if you want this functionality grab a copy of the GNU cccp C-preprocessor and use it as a first pass over your source prior to compilation.
\\ a single backslash \n a new line \t a tab \f a form feed \b a back spaceString constants are surrounded by double quotes - (ie ") they can contain any number of characters (including the backquoted ones mentioned above and represent a null terminated string in memory.
<name> = <constant expression> ;
char 1 byte int(short) 2 bytes long 4 bytes unsigned char 1 byte unsigned int(short) 2 bytes unsigned long 4 bytesIn addition to these types you can create pointers to them, and arrays of them and pointers to them. Pointers are 2 bytes in size. Here are some examples of declarations:
int a, // a 2 byte integer *b, // a 2 byte pointer to an integer c[6], // and array of 6 2-byte values *d[5]; // 5 pointers to 2 byte objects char x, *y; // a 1 byte char and a pointer to a char long j, k; // 2 4-byte longs
void ex1(int a, char b) { int x, y; x = a+b; y = a-b; if (y == 0) return; pval(x/y); } int fact(int y) { if (y <= 1) return(1); return(y*fact(y-1)); }
The basic syntax for a state declaration is:
state <STATE_NAME>: <some optional statements to executed once on entry to state STATE_NAME> on <event a>: <some statements to be executed on event a> on <event b>: <some statements to be executed on event b> etcOnce a state has been entered the program executes the optional entry code, it also starts a timer for the timeout event.
It then looks for the events specified in order, if it finds none it goes back and looks for them again untill one of the events has happened. Valid events are:
on timeout <number>: - trigger when <number> milliseconds have passed once a timer has triggered it does not get rearmed without passing through the beginning of a state again - if you want a state to periodically trigger its timer you should complete the 'on timeout' event with a 'next STATE' for the same state. on output: - trigger when a character may be output by the pchr() intrinsic without stalling on input <name>: - if a serial character is available on the serial input return it and put it in the variable <name> on launch: - trigger when a launch even is detected on log_full: - trigger when the log is full on interrupt <num>: - trigger when interrupt <num> has occured (this is a hardware specific thing - check with your implementation's specifications) on <exp>: - trigger when expression <exp> is non 0 on idle: - this is the default - it always triggers, put this at the end because no other event statements in this state that follow an 'on idle' will be recognized.You can transition from one state to another using a 'next <STATE NAME>' statement - this enters a new state from the start. Statements attached to an event description are executed in order untill a 'next' statement is executed (statements following a 'next' are ignored). If all the statements are executed then the program will go back to the start of the list of events looking for new events that have triggered.
Below is C-like pseudo code for a state declaration showing how it works:
state fred: <start code> on timeout 100: <timeout statements>; // note no next on interrupt 1: <int 1 statements>; next fred; on interrupt 2: <int 2 statements>; next joe; on launch: <launch statements>; next mike; on idle: <idle statements>; is equivalent to: fred_entry: timer = current_time; timer_running = 1; <start code> fred_loop: if launch detected) launch_flag = 1; time = current_time; // for 'time' variable check to see if any logging needs to be done if (timer_running && (current_time-timer) > 100) { timer_running = 0; <timeout statements>; goto fred_loop; // no next } if (interrupt 1 happened) { <int 1 statements>; goto fred_entry; } if (interrupt 2 happened) { <int 2 statements>; goto joe_entry; } if (launch_flag) { launch_flag = 0; <int 2 statements>; goto mike_entry; } <idle statements>; goto fred_loop;Each program must have a state called 'start' which is the first state entered when the program starts.
empty statement - ; compound statements - { <statement list > } assignments - a = <exp>; *a = <exp>; a[<exp>] = <exp>; '=' above can be replaced with any of += -= *= /= %= ^= |= &= >>= <<= (note a full range of asignment left hand sides have not yet been implemented) auto inc/decrements - name++; name--; --name; ++name; subroutine calls - subr(a, b, c); intrinsics - halt; - stops the program fire(<exp>); - fires a pyro charge arm(<exp>); - arms pyro charges safe(<exp>); - safes pyro charges set(<exp>, <exp>); - sets some external pin or implementation specific object to a particular value beep(<exp>); - sets the beeper state eesave(<exp>, <exp>, <exp>); - saves to eeprom eeload(<exp>, <exp>, <exp>); - loads from eeprom log(<exp>, <exp>); - logs a value to the log log_ctl(<exp>, <exp>);- sets log functions pchr(<exp>); - puts a character to the serial port pstr(<exp>); - puts a 0 terminated string to the serial port phex(<exp>); - puts a value as hexadecimal to the serial port pval(<exp>); - puts a value as decimal to the serial port see the Intrinsics section below for more information about each of these return - return; return(<exp>); returns a value from a subroutine if - if (<exp>) <statement> if (<exp>) <statement> else <statemt> while - while (<exp>) <statement> do - do <statement> while (<exp>) for - for ( <assign> ; <exp> ; <assign> ) <statement> break - break; leaves the enclosing for, do or while statement continue - continue; goes back to the start of the enclosing for, do or while statement switch - switch (<exp>) { case <exp>: .... }
Expressions follow the normal C expression forms with the expected operators:
?: - ( <exp1> ? <exp2> : <exp3> ) selection - if <exp1> is true return <exp2> otherwise <exp3> || - <exp1> || <exp2> guarded or - return true if <exp1> or <exp2> are true, if <exp1> is true <exp2> is not evaluated && - <exp1> && <exp2> guarded and - return true if <exp1> and <exp2> are true, if <exp1> is false <exp2> is not evaluated < - <exp1> < <exp2> less than - true if <exp1> is less than <exp2> <= - <exp1> <= <exp2> less than or equal - true if <exp1> is less than or equal to <exp2> > - <exp1> > <exp2> greater than - true if <exp1> is greater than <exp2> >= - <exp1> >= <exp2> greater than or equal - true if <exp1> is greater than or equal to <exp2> == - <exp1> == <exp2> equal - true if <exp1> is equal to <exp2> != - <exp1> != <exp2> not equal - true if <exp1> not equal to <exp2> | - <exp1> | <exp2> bitwise or - each bit in <exp1> is logically ored to the same bit in <exp2> & - <exp1> & <exp2> bitwise and - each bit in <exp1> is logically anded to the same bit in <exp2> ^ - <exp1> ^ <exp2> bitwise xor - each bit in <exp1> is logically xored to the same bit in <exp2> << - <exp1> << <exp2> shift left - <exp1> shifted left by <exp2> >> - <exp1> >> <exp2> shift right - <exp1> shifted right by <exp2> + - <exp1> + <exp2> add - <exp1> is added to <exp2> - - <exp1> - <exp2> subtract - <exp2> is subtracted from <exp1> * - <exp1> * <exp2> multiply - <exp1> is multiplyed by <exp2> / - <exp1> / <exp2> divide - <exp1> is divided by <exp2> (if <exp2> is 0 the result is undefined) % - <exp1> % <exp2> mod - <exp1> is moded by <exp2> (arithmetic remainder) (if <exp2> is 0 the result is undefined) call - subr( <exp>, ....) call a subroutine and return a value () - (<exp>) variable - name name++ name-- ++name --name array index - name[<exp>] number - 1232 0x1234 'a' string - "abcd" unary - - -<exp> return 0-<exp> unary + - +<exp> return <exp> unary * - *<exp> return the value <exp> points to ~ - ~<exp> the bitwise complement of the value <exp> ! - !<exp> true if <exp> is false, false if it's true unary & - &name &name[<exp>] return the address of an object cast - (char)<exp> (int)<exp> (long)<exp> (char *)<exp> (int *)<exp> (long *)<exp> change the type of a value size - sizeof(type) sizeof(<exp>) get the size of an object intrinsics - time - time in milliseconds we last passed through the state polling ltime - time in milliseconds since last launch event rtime - current time in milliseconds stime - time when the last state was entered in milliseconds get(<exp>) - return value of a system object (an analog channel, a pin state etc) (see the section below on intrinsics)Operator priority is the same as C - in order from lowest to highest they are:
?: || && < > <= >= == != | & ^ << >> + - * / % call name unary_ops cast ~ !
Executing halt may destroy the contents of the log.
arm(0); // arm everything fire(1); // fire channel 1 fire(2); // fire channel 2 fire(4); // fire channel 3 fire(8); // fire channel 4 fire(9); // fire channel 1 and 4 fire(1<<(N-1)); // fire channel N fire(0); // stop firing safe(0); // save all channelsbeep(code)
Optional - if not supported calling this causes nothing to happen. This call activates the on board beeper, not all codes are supported - if the beeper is supported the first two are manadatory:Code Action 0 turns beeper silent 1 turns beeper on 10+N sends the beep code for N >=256 implementation specificlog_ctl(option, value)
Support of a logging function is optional - a log is a buffer of recorded events which can be played back after flight. The internal details of how the log works are system specific, but the following is true of all logs:
Option Value Comment 0 0 turn the logger off 1 empty the log and start the logger 2 pause the logger 3 continue the logger 4 empty the log 5 unfreeze the log 6 enable automatic logging to EEPROM (system specific) 7 save log in EEPROM 8 load log from EEPROM 9 erase EEPROM log 10 enable logging of fire/arm/safe calls 11 disable logging of fire/arm/safe calls 12 enable logging of launch events 13 disable logging of launch events 14 enable logging of set() calls 15 disable logging of set() calls 16 log data is printed in decimal (default) 17 log data is printed in hex 18 enable logging of log_ctl() events 19 disable logging of log_ctl() calls 1 N freeze the log start at N milliseconds prior to the current time 2 addr set EE address for log save 3 count copy first count bytes of log information to eeprom address saved by log_ctl(2) 4 count empty log then copy first count bytes of log information from eeprom address saved by log_ctl(2). 5 count output (to serial port) first count entries from log. '0' means all the log entries. 6 time set periodic time for automatic logging (time in milliseconds) 7 N enable automatic logging for channel N 8 N disable automatic logging of channel NThe logger is started by a log_ctl(0, 1) call - once running it starts to collect log records - when it sees a log_ctl(1, *) call the starting point of the log is marked at the current time less N milliseconds and the log continues to fill untill no more space is available - at this time it stops collecting log data and goes into the 'frozen' state. This ability to freeze at an earlier time is usefull because the launch detection event actually occurs some time after launch (this is because it needs to see thrust for some period of time so as not to be triggered by casual bumping) by freezing at a fixed point prior to the launch detection all of the launch can be logged.
A 'frozen' log can be unfrozen by means of a log_ctl(0, 5) call which means that the start of the log starts to be overwritten again.
If log_ctl(7,*)/log_ctl(8,*) is called then the value specified by log_ctl(6,*) is used to trigger a period logging If EEPROM is supported there are three ways to save data into the log, which one(s) are supported are system specific:
The entry types are:
If your rocket's going to be flying for more than an hour get in touch I'm sure we can work something interesting out :-)
0x00 undefined 0x01-0x3f sensor channels - writing is undefined by convention 0x01 - accelerometer 0x02 - pressure sensor 0x40-0x7f reserved for R future assignment