StickOS™ BASIC User's Guide v1.70

StickOS BASIC is an entirely MCU-resident interactive programming environment, which includes an easy-to-use editor, transparent line-by-line compiler, interactive debugger, performance profiler, and flash filesystem, all controlled thru an interactive command-line user interface.
In StickOS, external MCU pins may be mapped to BASIC “pin variables” for manipulation or examination, and internal MCU peripherals may be managed by BASIC control statements and interrupt handlers.
A StickOS-capable MCU may be connected to a host computer via a variety of transports and may then be controlled by any terminal emulator program, with no additional software or hardware required on the host computer.
Additionally, when coupled with an MC13201 ZigFlea Wireless Transceiver, the MCU may be remotely controlled by another MCU, via a telnet/rlogin-like interface, eliminating the need for a direct connection to the host computer altogether. Also, BASIC programs may trivially remotely access variables on other MCUs, enabling the use of “remote pin variables” or other forms of inter-MCU communication.
On selected MCUs, the USB interface can optionally be configured into USB Host Mode, creating a trivial data logger to an external USB flash drive.
Once program development is complete, the MCU may be disconnected from the host computer and configured to autorun its resident BASIC program autonomously.
By its very nature, StickOS supports in-circuit emulation when it is running -- all you need is a transport connecting the MCU to a host computer, and you have full control over the target embedded system, just as if you were using an in-circuit emulator! Alternatively, you can use the 2.4GHz zigflea wireless transport and have full control over the target embedded system with no connected transport at all!!!
The StickOS BASIC programming environment includes the following features:
o BASIC line editor
o ansi or vt100'ish terminal support
BASIC compiler
compiles to a fast and safe
intermediate bytecode
transparent line-by-line
compilation is invisible to the user
interactive BASIC debugger,
supporting:
breakpoints, assertions, and
watchpoints
live variable (and pin)
manipulation and examination
execution tracing and
single-stepping
edit-and-continue!
BASIC performance profiler
trivially see where your
program spends its time!
BASIC file system
load and store multiple BASIC
programs in flash
USB Host Mode (on selected
MCUs)
log StickOS "print"
statements to external USB flash drive
2.4GHz zigflea wireless
transport
remote control via a
telnet/rlogin-like interface
remote variable access in
BASIC
external control of MCU I/O
pins, implicit thru "pin variables"
o digital input or output
o analog input or output (PWM actually)
o servo output
o frequency output
o uart input or output
o qspi master input and output
internal peripheral control
interrupts delivered to BASIC
handlers!
interval timers, dma timers,
ADC, PWM, uarts, qspi, etc.
direct MCU register access
from BASIC for low-level control
internal flash memory control
save programs and parameters
to flash for standalone operation
prolong flash lifetime by
storing incremental updates in RAM
clone one MCU’s flash
directly to another
upgrade StickOS firmware via
terminal emulator!
no external flash programmers
needed!
Note that for the purposes of examples in this User’s Guide, we’ll be running StickOS primarily on an MCF52221; other MCUs are similar.
Table of Contents
3 Wireless Embedded Systems Made Just as Easy!
5.1 First Boot & Pin Assignments
5.3.4 Loading and Storing Programs
5.4.8 Looping Conditional Statements. 55
6 2.4GHz ZigFlea Wireless Operation
11.1 StickOS Command Reference
11.2 BASIC Program Statement Reference
11.2.6 Pins (MCF52221 example)
11.2.7 ZigFlea (MCF52221 example)
A simple embedded system, like a toaster oven temperature profile controller, can be brought online in record time!

It’s as easy as...
The entire toaster oven temperature profile controller BASIC control program is shown below:

Line 10 declares two simple
RAM variables named “target”
and “secs”
for use in the program, and initializes them to 0.
Line 20 declares an analog
input "pin variable" named “thermocouple” that is bound to pin an0,
to read the thermocouple voltage, in millivolts
Line 30 declares a digital
output "pin variable" named “relay” that is bound to pin an1, to control the solid
state relay.
Line 40 declares the
temperature target and delay time pairs for our temperature profile ramp.
Lines 50 and 60 configure a
timer interrupt to call the "adjust" subroutine asynchronously, every second, while the
program runs.
Lines 70 thru 100 set the
target temperature profile while the program runs.
Lines 110 and 120 end the
program with the solid state relay control turned off.
Lines 130 thru 190 use the
declared pin variables to simply turn the solid state relay control off if the
target temperature has been achieved, or on otherwise.
Then:
“save” saves the program to
non-volatile flash memory.
“autorun on” sets the program
to run automatically when the MCU is powered up.
Finally, “reset” resets the
MCU as if it was just powered up.
Note that if terse code were our goal, lines 60 and 130 thru 190 could have all been replaced with the single statement:
> 60 on timer 0 do let relay = thermocouple<target
With the aid of an MC13201 ZigFlea Wireless Transceiver, a simple wireless embedded system, like a remote LED dimmer, can be brought online just as easily as a local embedded system!

It’s as easy as...
The entire debugging session, including the writing and running of both MCU’s BASIC control programs, is shown below:

Note that all of this debugging session is occurring on the Hyper Terminal connected to the USB interface on MCU #1!
First we write the program on MCU #1.
Notice in line 10 that we
declare a local pin variable named “potentiometer” to read the value of the
potentiometer, through analog input pin an0, in millivolts.
Then, in line 20, we declare
a remote pin variable to control the LED on MCU #2 (through MCU #2’s
local pin variable!); the “as remote on nodeid 2” indicates that the real
variable declaration is found on MCU #2.
Then we simply enter an
infinite loop reading the value of the potentiometer (again, in millivolts)
every 100ms, and writing it to the LED on MCU #2.
We then save the program to flash memory on MCU #1 and configure it to run automatically when the MCU powers up.
Then we remotely connect to MCU #2 and write its program.
Notice in line 10 that we
declare a local pin variable named “led” to control the LED, through analog output pin dtin0,
in millivolts.
Then we simply enter an
infinite loop, waiting for our local pin variable to be written remotely from
MCU #1 every 100ms!
We then save the program to flash memory on MCU #2 and configure it to run automatically when the MCU powers up.
Finally, we run the program on MCU #2, disconnect from MCU #2 by pressing <Ctrl-D>, and run the program on MCU #1.
At this point, adjusting the potentiometer on MCU #1 causes the LED brightness on MCU #2 to be correspondingly adjusted, after a 100ms delay!!!
When the StickOS is running the “heartbeat” LED will blink slowly; when the BASIC program in the MCU is running, the “heartbeat” LED will blink quickly.
Holding the “autorun disable” switch depressed during power-on prevents autorun of the BASIC program and overrides static IP address assignment in favor of DHCP.
Use the help pins command to see the list of MCU pin names, and the pins command to see their LED and switch assignments.
All MCU external pins support general purpose digital input or output.
In addition, certain external pins can support analog input, analog output (PWM actually), frequency output, UART input, UART output, and/or QSPI master input/output.
Use the help pins command to see the list of MCU pin names and their capabilities.
StickOS is controlled via a terminal emulator program, such as Windows Hyper Terminal (typically found under Start -> All Programs -> Accessories -> Communications -> Hyper Terminal), thru one of the following command-line transports:
· USB, via a Virtual COM port
· Ethernet, via a raw socket on port 1234
· UART, via a physical COM port
When using Hyper Terminal, if the USB or Ethernet connection is lost (such as when you unplug and re-plug in the MCU), press the “Disconnect” button followed by the “Call” button, to reconnect Hyper Terminal.
Note that if you do not have Hyper Terminal, my favorite terminal emulator program is “putty”, available free from http://www.putty.org/ (the latest version supports serial port connections). On Linux minicom also works from http://alioth.debian.org/projects/minicom/; On Mac you can also just use the screen command under Terminal.
When the MCU is connected to a USB host computer (including Windows, Linux, and Mac), it will present an FTDI Serial Port function to the host computer. An appropriate driver will be loaded automatically from microsoft.com, if needed, or you can manually install the VCP driver from http://www.ftdichip.com/FTDrivers.htm.
Once the driver is loaded, a new virtual COM port (VCP) will be present on your system. This virtual COM port will be visible in Device Manager:

At this point you can use Hyper Terminal to connect to the new virtual COM port. Specify a new connection name, such as “cpustick”, and then select the new virtual COM port under Connect Using; the baud rate and data characteristics in Port Settings are ignored.
Press <Enter> when you are connected and you should see the command prompt:

You are now ready to enter StickOS commands and/or BASIC program statements!
If a UART Transport is used, the USB interface on selected MCUs can be configured into host mode to create a trivial USB data logging mechanism to an external USB flash drive.
The state of USB Host mode can be displayed (along with whether a USB flash drive is attached or not), turned on, or turned off with the commands:
usbhost
usbhost on
usbhost off
When USB host mode is turned on and an external USB flash drive is attached and supplied with appropriate VBUS power, all StickOS “print” statement output will be appended to the file x:\stickos.log in the USB flash drive, where x: is your drive letter.
The write-back cache is flushed every second, so you must wait one second after the last “print” statement before disconnecting the external USB flash drive.
The MCU will acquire an IP address from DHCP (query your DHCP server to figure out which IP address it got).
At this point you can use Hyper Terminal to connect to the new IP address on TCP port 1234. Specify a new connection name, such as “52233”, and then specify “TCP/IP” under Connect Using; then specify the new IP address as Host address and 1234 as Port number.
Press <Enter> when you are connected and you should see the command prompt:

You are now ready to enter StickOS commands and/or BASIC program statements!
You can subsequently use the "ipaddress" command to set a static IP address; you can override the static IP address and revert to DHCP by holding the “autorun disable” switch depressed during power-on.
Find the physical COM port in Device Manager:

At this point you can use Hyper Terminal to connect to the physical COM port. Specify a new connection name, such as “cpustick”, and then select the physical COM port under Connect Using; set the baud rate and data characteristics in Port Settings to:
Bits per second: 9600
Data bits: 8
Parity: None
Stop bits: 2
Flow control: Xon/Xoff
Press <Enter> when you are connected and you should see the command prompt:

You are now ready to enter StickOS commands and/or BASIC
program statements!
StickOS supports a BASIC programming environment, where external pins are mapped to special “pin variables” for manipulation or examination.
External pins can be dynamically configured as one of:
o digital input or output,
o analog input or output (PWM actually),
o servo output,
o frequency output,
o uart input or output, or
o qspi master input/output
BASIC programs as well as “persistent parameters” can be stored in non-volatile flash memory; volatile variables as well as recent code edits (up to the next “save” command) are stored in RAM.
When StickOS first boots, certain pin assignments default to “standard” board layouts. Since StickOS runs on any MCU, independent of its board layout, you may need to customize these pin assignments when you first log in if your board is different.
The following pin assignments are supported:
|
assign |
function |
|
heartbeat |
indicates the position of the pin attached to the “heartbeat” LED (digital output). |
|
safemode* |
indicates the position of the safemode pin attached to the “autorun disable” switch (digital input). |
|
qspi_cs* |
indicates the position of the cs* pin used for QSPI transfers for clone and zigflea operations. |
|
clone_rst* |
indicates the position of the rst* pin used when cloning firmware to another MCU via EzPort (digital output) |
|
zigflea_rst* |
indicates the position of the rst* pin used to reset the MC1320x ZigFlea Transceiver (digital output) |
|
zigflea_attn* |
indicates the position of the attn* pin used to wake the MC1320x ZigFlea Transceiver (digital output)
N.B. this signal is only needed if the MC1320x circuit uses it; StickOS does not need it |
|
zigflea_rxtxen |
indicates the position of the rxtxen pin used to activate the MC1320x ZigFlea Transceiver (digital output) |
The default pin assignments may be displayed with the command:
pins
An individual pin assignment may be displayed with the command:
pins assign
An individual pin may be reassigned persistently in flash with the command:
pins assign none
pins assign pinname
Use the following command to see a lit of pin names for the MCU:
help pins
> help pins
pin names:
0 1 2 3 4 5 6 7
-------- --------- --------- -------- ----- -------- -------- ------+
an0 an1 an2 an3 an4 an5 an6 an7 | AN
scl sda | AS
irq1* irq4* irq7* | NQ
qspi_dout qspi_din qspi_clk qspi_cs0 qspi_cs2 qspi_cs3 | QS
dtin0 dtin1 dtin2 dtin3 | TC
utxd0 urxd0 urts0* ucts0* | UA
utxd1 urxd1 urts1* ucts1* | UB
all pins support general purpose digital input/output
an? = potential analog input pins (mV)
dtin? = potential analog output (PWM) pins (mV)
dtin? = potential servo output (PWM) pins (cms)
dtin? = potential frequency output pins (Hz)
urxd? = potential uart input pins (received byte)
utxd? = potential uart output pins (transmit byte)
> pins
heartbeat dtin3
safemode* irq1*
qspi_cs* scl
clone_rst* qspi_cs0
zigflea_rst* an2
zigflea_attn* an3
zigflea_rxtxen an5
> pins heartbeat dtin2
> pins heartbeat
dtin2
> _
In the command and statement specifications that follow, the following nomenclatures are used:
|
bold |
literal text; enter exactly as shown |
|
italics |
parameterized text; enter actual parameter value |
|
(alternate1| alternate2| ...) |
alternated text; enter exactly one alternate value |
|
regular |
displayed by StickOS |
|
<key> |
press this key |
To avoid confusion with array indices (specified by [...]), optional text will always be called out explicitly, either by example or by text, rather than nomenclated with the traditional [...].
When StickOS is controlled with an ansi or vt100'ish terminal emulator, command-line editing is enabled via the terminal keys, as follows:
|
key |
function |
|
← |
move cursor left |
|
→ |
move cursor right |
|
↑ |
recall previous history line |
|
↓ |
recall next history line |
|
<Home> |
move cursor to start of line |
|
<End> |
move cursor to end of line |
|
<Backspace> |
delete character before cursor |
|
<Delete> |
delete character at cursor |
|
<Ctrl-C> |
clear line |
|
<Ctrl-D> |
disconnect from remote node (zigflea) |
|
<Enter> |
enter line to StickOS |
If you enter a command or statement in error, StickOS will indicate the position of the error, such as:
> print i forgot to use quotes
error - ^
> _
As a simple example, the following BASIC program generates a 1 Hz square wave on the “dtin0” pin:
> 10 dim square as pin dtin0 for digital output
> 20 while 1 do
> 30 let square = !square
> 40 sleep 500 ms
> 50 endwhile
> run
<Ctrl-C>
STOP at line 40!
> _
Press <Ctrl-C> to stop the program.
Line 10 configures the “dtin0” pin for digital output, and creates a variable named “square” whose updates are reflected at that pin. Line 20 starts an infinite loop (typically MCU programs run forever). Line 30 inverts the state of the dtin0 pin from its previous state -- note that you can examine as well as manipulate the (digital or analog or servo or frequency) output pins. Line 40 just delays the program execution for one half second. And finally line 50 ends the infinite loop.
If we want to run the program in a slightly more demonstrative way, we can use the “trace on” command to show every variable update as it occurs:
> trace on
> run
30 let square = 1
30 let square = 0
30 let square = 1
30 let square = 0
<Ctrl-C>
STOP at line 40!
> _
Again, press <Ctrl-C> to stop the program.
Note that almost all commands that can be run in a program can also be run in “immediate” mode, at the command prompt. For example, after having run the above program, the “square” variable (and dtin0 pin) remain configured, so you can type:
> print "square is now", square
square is now 0
> let square = !square
> print "square is now", square
square is now 1
> _
This also demonstrates how you can examine or manipulate variables (or pins!) at the command prompt during program debug.
The MCU can perform analog I/O as simply as digital I/O.
The following BASIC program takes a single measurement of an analog input at pin “an0” and displays it:
> new
> 10 dim potentiometer as pin an0 for analog input
> 20 print "potentiometer is", potentiometer
> run
potentiometer is 2026
> _
Note that analog inputs and outputs are represented by integers in units of millivolts (mV).
Note that almost all commands that can be run in a program can also be run in “immediate” mode, at the command prompt. For example, after having run the above program, the “potentiometer” variable (and an0 pin) remain configured, so you can type:
> print "potentiometer is now", potentiometer
potentiometer is now 2027
> _
This also demonstrates how you can examine variables (or pins!) at the command prompt during program debug.
The MCU can perform servo I/O as simply as digital or analog I/O.
The following program moves a servo on pin “dtin1” from one extreme (assumed calibrated to a 0.5ms pulse) to the other (assumed calibrated to a 2.5ms pulse) over the period of a second, using the default servo frequency of 45Hz:
> new
> servo
45
> 10 dim servo as pin dtin1 for servo output
> 20 for servo = 50 to 250 step 10
> 30 sleep 50 ms
> 40 next
> run
> _
Note that servo outputs are represented by integers in units of centi-milliseconds (1/100th of a millisecond), so we’re generating pulses at 45Hz, and they start at 0.5ms and increase to 2.5ms.
Note that almost all commands that can be run in a program can also be run in “immediate” mode, at the command prompt. For example, after having run the above program, the “servo” variable (and dtin1 pin) remain configured, so you can type the following to return the servo to the other extreme position:
> let servo = 50
> _
The MCU can perform frequency I/O as simply as digital or analog I/O.
The following BASIC program generates a 1kHz square wave on a frequency output pin “dtin0” for 1 second:
> new
> 10 dim audio as pin dtin0 for frequency output
> 20 let audio = 1000
> 30 sleep 1 s
> 40 let audio = 0
> run
> _
Note that frequency outputs are represented by integers in units of hertz (Hz).
Note that almost all commands that can be run in a program can also be run in “immediate” mode, at the command prompt. For example, after having run the above program, the “audio” variable (and dtin0 pin) remain configured, so you can type:
> print "audio is now", audio
audio is now 0
> let audio = 2000
> print "audio is now", audio
audio is now 2000
> _
This also demonstrates how you can examine or manipulate variables (or pins!) at the command prompt during program debug.
The MCU can perform serial uart I/O as simply as digital or analog I/O.
The following BASIC program configures a uart for loopback mode, transmits two characters and then asserts it receives them correctly:
> new
> 10 configure uart 0 for 9600 baud 7 data even parity loopback
> 20 dim tx as pin utxd0 for uart output
> 30 dim rx as pin urxd0 for uart input
> 40 let tx = 48
> 50 let tx = 49
> 60 while tx do
> 70 endwhile
> 80 assert rx==48
> 90 assert rx==49
> 100 assert rx==0
> 110 print "ok!"
> run
ok!
> _
Line 10 configures uart 0 for 9600 baud loopback operation. Lines 20 and 30 configure the “utxd0” and “urxd0” pins for uart output and input, and creates two variable named “tx” and “rx” bound to those pins. Line 40 sends a character (‘0’, ascii 48) out the uart and line 50 sends another (‘1’, ascii 49). Line 60 waits until all characters are sent (when “tx” reads back 0). Line 80 and 90 then receive two characters from the uart and assert they are what we sent. Line 100 then asserts there are no more characters received (“rx” reads back 0).
The uart can also be controlled using interrupts rather than polling. The following program shows this:
> trace off
> 10 configure uart 0 for 9600 baud 7 data even parity loopback
> 20 dim tx as pin utxd0 for uart output
> 30 dim rx as pin urxd0 for uart input
> 40 on uart 0 input do gosub receive
> 50 let tx = 48
> 60 let tx = 49
> 70 sleep 1 s
> 80 end
> 90 sub receive
> 100 print "received", rx
> 110 endsub
> run
received 48
received 49
> _
The MCU can perform serial QSPI master I/O as simply as digital or analog I/O.
The following BASIC program configures QSPI to talk to the EzPort of another MCU via QSPI, assuming a clone cable is attached. It enables flash memory writes and then queries the status register:
> list
10 dim nrsti as pin scl for digital output
20 dim ncs as pin qspi_cs0 for digital output
30 dim cmd as byte, status as byte
40 rem pulse rsti* low with cs*
50 let ncs = 0, nrsti = 0, nrsti = 1
60 sleep 100 ms
70 let ncs = 1
80 rem send write enable command
90 let cmd = 0x6
100 let ncs = 0
110 qspi cmd
120 let ncs = 1
130 rem send read status register command
140 let cmd = 0x5
150 let ncs = 0
160 qspi cmd, status
170 let ncs = 1
180 print hex status
end
> run
0x2
> _
Line 10 configures a digital output pin to reset the target MCU. Line 20 configures a digital output pin to drive the MCU chip select, for use with EzPort. Line 30 just dimensions two byte sized variables for use below; note that QSPI transfers are sized by the variables specified in the qspi statement. Lines 40 thru 70 reset the target MCU. Lines 80 thru 120 send a one byte “write enable” command, with chip select. Lines 130 thru 170 send a one byte “read status register” command and receive a one byte status, with chip select. Line 180 prints the status, which shows writes are enabled but the configuration register is not yet loaded.
A more complicated program can be used to control the IOStick’s 2.4GHz zigflea transceiver, when it is not otherwise being used by native StickOS services.
StickOS commands are used to control the StickOS BASIC program. Unlike BASIC program statements, StickOS commands cannot be entered into the StickOS BASIC program with a line number.
The help command displays the top level list of help topics:
help
To get help on a subtopic, use the command:
help subtopic
> help
for more information:
help about
help commands
help modes
help statements
help blocks
help devices
help expressions
help variables
help pins
help board
help clone
help zigflea
see also:
http://www.cpustick.com
> help commands
auto <line> -- automatically number program lines
clear [flash] -- clear ram [and flash] variables
clone [run] -- clone flash to slave MCU [and run]
cls -- clear terminal screen
cont [<line>] -- continue program from stop
delete [<line>][-][<line>] -- delete program lines or <subname>
download <slave Hz> -- download for relay to QSPI to EzPort
dir -- list saved programs
edit <line> -- edit program line
help [<topic>] -- online help
list [<line>][-][<line>] -- list program lines or <subname>
load <name> -- load saved program
memory -- print memory usage
new -- erase code ram and flash memories
profile [<line>][-][<line>] -- like list, but display profile info
purge <name> -- purge saved program
renumber [<line>] -- renumber program lines (and save)
reset -- reset the MCU!
run [<line>] -- run program
save [<name>] -- save code ram to flash memory
undo -- undo code changes since last save
upgrade -- upgrade StickOS firmware!
uptime -- print time since last reset
for more information:
help modes
> _
To enter a statement into the BASIC program, precede it with a line number identifying its position in the program:
line statement
If the specified line already exists in the BASIC program, it is overwritten.
To delete a statement from the BASIC program, enter just its line number:
line
Note that statements are initially entered into a RAM buffer to avoid excessive writes to flash memory, and therefore can be lost if the MCU is reset or loses power before the program has been saved. When a program is run, the (newly edited) statements in RAM are seamlessly merged with the (previously saved) statements in flash memory, to give the appearance of a single “current program”, at a slight performance penalty. When the newly edited program is subsequently saved again, the merged program is re-written to flash and the RAM buffer is cleared, resulting in maximum program performance. If the RAM buffer fills during program entry, an “auto save” is performed to accelerate the merging process.
To automatically number program lines as you enter them, use the command:
auto
auto line
Enter two blank lines to terminate automatic line numbering.
Note that you can edit a BASIC program in a text editor, without line numbers, and then paste it into the terminal emulator window with automatic line numbering, and then enter two blank lines to terminate automatic line numbering.
To list the BASIC program, or a range of lines from the BASIC program, use the command:
list
list line
list -line
list line-
list line-line
Alternately, you can list an entire subroutine by name with the command:
list subname
To set the listing indent mode, use the command:
indent (on|off)
To display the listing indent mode, use the command:
indent
If the listing indent mode is on, nested statements within a block will be indented by two characters, to improve program readability.
To set the line numbering mode, use the command:
numbers (on|off)
To display the line numbering mode, use the command:
numbers
Note that unnumbered listings are useful to paste back in to the “auto” command which automatically supplies line numbers to program statements.
To delete a range of lines from the BASIC program, use the command:
delete line
delete -line
delete line-
delete line-line
Alternately, you can delete an entire subroutine by name with the command:
delete subname
To edit an existing line of the BASIC program via command-line editing, use the command:
edit line
A copy of the unchanged line is also stored in the history buffer.
To undo changes to the BASIC program since it was last saved (or renumbered, or new'd, or loaded), use the command:
undo
To save the BASIC program permanently to flash memory, use the command:
save
Note that any unsaved changes to the BASIC program will be lost if the MCU is reset or loses power.
To renumber the BASIC program by 10's and save the BASIC program permanently to flash memory, use the command:
renumber
To delete all lines from the BASIC program, use the command:
new
> 10 dim a
> 20 for a = 1 to 10
> auto 30
> 30 print a
> 40 next
> 50
> 60
> save
> list 20-40
20 for a = 1 to 10
30 print a
40 next
end
> delete 20-40
> list
10 dim a
end
> undo
> list
10 dim a
20 for a = 1 to 10
30 print a
40 next
end
> 1 rem this is a comment
> list
1 rem this is a comment
10 dim a
20 for a = 1 to 10
30 print a
40 next
end
> renumber
> list
10 rem this is a comment
20 dim a
30 for a = 1 to 10
40 print a
50 next
end
> new
> list
end
> _
To run the BASIC program, use the command:
run
Alternately, to run the program starting at a specific line number, use the command:
run line
To stop a running BASIC program, press:
<Ctrl-C>
To continue a stopped BASIC program, use the command:
cont
Alternately, to continue a stopped BASIC program from a specific line number, use the command:
cont line
To set the autorun mode for the saved BASIC program, use the command:
autorun (on|off)
To display the autorun mode for the saved BASIC program, use the command:
autorun
If the autorun mode is on, when the MCU is reset, it will start running the saved BASIC program automatically.
Note that any unsaved changes to the BASIC program will be lost if the MCU is reset or loses power.
> 10 dim a
> 20 while 1 do
> 30 let a = a+1
> 40 endwhile
> save
> run
<Ctrl-C>
STOP at line 40!
> print a
5272
> cont
<Ctrl-C>
STOP at line 30!
> print a
11546
> autorun
off
> autorun on
> _
The “current program” has no name and is saved and run by default. In addition to the current program, StickOS can load and store a number of named BASIC programs in a flash filesystem. Named programs are simply copies of the current program that can be retrieved at a later time, but are otherwise unaffected by all other StickOS commands than these.
To display the list of currently stored named programs, use the command:
dir
To store the current program under the specified name, use the command:
save name
To load a named stored program to become the current program, use the command:
load name
To purge (erase) a stored program, use the command:
purge name
> 10 dim a
> 20 while 1 do
> 30 let a = a+1
> 40 endwhile
> dir
> save spinme
> dir
spinme
> new
> list
end
> load spinme
> list
10 dim a
20 while 1 do
30 let a = a+1
40 endwhile
end
> purge spinme
> dir
> _
There are a number of techniques you can use for debugging StickOS BASIC programs.
The simplest debugging technique is simply to insert print statements in the program at strategic locations, and display the values of variables.
A more powerful debugging technique is to insert one or more breakpoints in the program, with the following statement:
line stop
When program execution reaches line, the program will stop and then you can use immediate mode to display or modify the values of any and all variables.
To continue a stopped BASIC program, use the command:
cont
cont line
An even more powerful debugging technique is to insert one or more conditional breakpoints in the program, with the following statement:
line assert expression
When the program execution reaches line, expression is evaluated, and if it is false (i.e., 0), the program will stop and you can use immediate mode to display or modify the values of any and all variables.
Again, to continue a stopped BASIC program, use the command:
cont
cont line
The most powerful debugging technique, though also the most expensive in terms of program performance, is to insert a watchpoint expression in the program, with the following statement
line on expression do statement
The watchpoint expression is re-evaluated before every line of the program is executed; if the expression transitions from false to true, the watchpoint statement handler runs.
When debugging, the statement handler is typically a “stop” statement, such as:
line on expression do stop
This will cause the program to stop as soon as the specified expression becomes true, such as when a variable or pin takes on an incorrect value.
To set the smart watchpoint mode, which dramatically reduces watchpoint overhead at a slight delay of input pin sensitivity, use the command:
watchsmart (on|off)
To display the smart watchpoint mode, use the command:
watchsmart
At any time when a program is stopped, you can enter BASIC program statements at the command-line with no line number and they will be executed immediately; this is called "immediate mode". This allows you to display the values of variables, with an immediate mode statement like:
print expression
It also allow you to modify the value of variables, with an immediate mode statement like:
let variable = expression
Note that if an immediate mode statement references a pin variable, the live MCU pin is examined or manipulated, providing a very powerful debugging technique for the embedded system itself!
Thanks to StickOS’s transparent line-by-line compilation, you can also edit a stopped BASIC program and then continue it, either from where you left off or from another program location!
When the techniques discussed above are insufficient for debugging, two additional techniques exist -- single-stepping and tracing.
To set the single-step mode for the BASIC program, use the command:
step (on|off)
To display the single-step mode for the BASIC program, use the command:
step
While single-step mode is on, the program will stop execution after every statement, as if a stop statement was inserted after every line.
Additionally, while single-step mode is on, pressing <Enter> (essentially entering what would otherwise be a blank command) is the same as the cont command.
To set the trace mode for the BASIC program, use the command:
trace (on|off)
To display the trace mode for the BASIC program, use the command:
trace
While trace mode is on, the program will display all variable modifications while running.
> 10 dim a, sum
> 20 for a = 1 to 10000
> 30 let sum = sum+a
> 40 next
> 50 print sum
> run
50005000
> 25 stop
> run
STOP at line 25!
> print a, sum
1 0
> cont
STOP at line 25!
> print a, sum
2 1
> 25 assert a != 5000
> cont
assertion failed
STOP at line 25!
> print a, sum
5000 12497500
> cont
50005000
> delete 25
> trace
off
> step
off
> trace on
> step on
> list
10 dim a, sum
20 for a = 1 to 10000
30 let sum = sum+a
40 next
50 print sum
end
> run
STOP at line 10!
> cont
20 let a = 1
STOP at line 20!
> <Enter>
30 let sum = 1
STOP at line 30!
> <Enter>
40 let a = 2
STOP at line 40!
> <Enter>
30 let sum = 3
STOP at line 30!
> _
To clear BASIC program variables, and reset all pins to digital input mode, use the command:
clear
This command is also used after a stopped program has defined program variables and before redefining program variables in “immediate” mode, to avoid duplicate definition errors without having to erase the program with a “new” command.
To clear BASIC program variables, including flash parameters, use the command:
clear flash
To display the StickOS memory usage, use the command:
memory
To reset the MCU as if it was just powered up, use the command:
Note that the reset command inherently breaks the USB or Ethernet connection between the MCU and host computer; press the “Disconnect” button followed by the “Call” button, to reconnect Hyper Terminal.
To clear the terminal screen, use the command:
cls
To display the time since the MCU was last reset, use the command:
uptime
> memory
0% ram code bytes used
0% flash code bytes used
0% ram variable bytes used
0% flash parameter bytes used
0% variables used
> 10 dim a[100]
> 20 rem this is a loooooooooooooooooooooooooooooooooooooong line
> run
> memory
4% ram code bytes used (unsaved changes!)
0% flash code bytes used
19% ram variable bytes used
0% flash parameter bytes used
1% variables used
> save
> memory
0% ram code bytes used
1% flash code bytes used
19% ram variable bytes used
0% flash parameter bytes used
1% variables used
> clear
> memory
0% ram code bytes used
1% flash code bytes used
0% ram variable bytes used
0% flash parameter bytes used
0% variables used
> list
10 dim a[100]
20 rem this is a loooooooooooooooooooooooooooooooooooooong line
end
> uptime
1d 15h 38m
> reset
_
BASIC Program statements are typically entered into the StickOS BASIC program with an associated line number, and then are executed when the program runs.
Most BASIC program statements can also be executed in immediate mode at the command prompt, without a line number, just as if the program had encountered the statement at the current point of execution.
All variables must be dimensioned prior to use. Accessing undimensioned variables results in an error and a value of 0.
Simple RAM variables can be dimensioned as either integer (32 bits, signed), short (16 bits, unsigned), or byte (8 bits, unsigned) with the following statements:
dim var
dim var as (integer|short|byte)
Array RAM variables can be dimensioned with the following statements:
dim var[n]
dim var[n] as (integer|short|byte)
Where n is the length of the array. Array indices start at 0 and end at the length of the array minus one.
Note that simple variables are really just array variables with only a single array element in them, so the array element var[0] is the same as var, and the dimension dim var[1] is the same as dim var.
Note that if no variable size (integer, short, or byte) is specified in a dimension statement, integer is assumed; if no as ... is specified, a RAM variable is assumed.
Multiple variables can be dimensioned in the same statement, by separating them with commas:
dim var, var, ...
Variables can also be dimensioned as MCU register variables at absolute addresses with the following statements:
dim varabs at address addr
dim varabs as (byte|short) at address addr
dim varabs[n] at address addr
dim varabs[n] as (byte|short) at address addr
Note that you can trivially crash your MCU by accessing registers incorrectly.
Variables can also be dimensioned as persistent integer (32 bits) flash variables with the following statements:
dim varflash as flash
dim varflash[n] as flash
Persistent flash variables retain their values from one run of a program to another (even if power is lost between runs), unlike RAM variables which are cleared to 0 at the start of every run.
Note that since flash memory has a finite life (100,000 writes, typically), rewriting a flash variable should be a rare operation reserved for program configuration changes, etc. To attempt to enforce this, StickOS delays all flash variable modifications by 0.5 seconds (the same as all other flash memory updates).
Finally, variables can be dimensioned as pin variables, used to manipulate or examine the state of MCU I/O pins with the following statements:
dim varpin as pin pinname for (digital|analog|frequency|uart)\
(input|output) [debounced] [inverted][open_drain]
dim varpin[n] as pin pinname for (digital|analog|frequency|uart)\
(input|output) [debounced] [inverted][open_drain]
These are discussed in detail below, in the sections on Digital I/O, Analog I/O, Servo I/O, Frequency I/O, and UART I/O.
See also: ZigFlea Remote Variables
> new
> 10 dim array[4], b, volatile
> 20 dim led as pin dtin0 for digital output
> 30 dim potentiometer as pin an0 for analog input
> 40 dim persistent as flash
> 50 for b = 0 to 3
> 60 let array[b] = b*b
> 70 next
> 80 for b = 0 to 3
> 90 print array[b]
> 100 let led = !led
> 110 next
> 120 print "potentiometer is", potentiometer
> 130 print "volatile is", volatile
> 140 print "persistent is", persistent
> 150 let persistent = persistent+1
> run
0
1
4
9
potentiometer is 1745
volatile is 0
persistent is 0
> run
0
1
4
9
potentiometer is 1745
volatile is 0
persistent is 1
> dim pcntr0 as short at address 0x40150004
> print pcntr0
5338
> print pcntr0
2983
> _
The following system variables may be used in expressions or simply with “print” statements to examine internal system state. These variables are all read-only.
|
nodeid |
zigflea nodeid |
|
msecs |
number of milliseconds since boot |
|
seconds |
number of seconds since boot |
|
ticks |
number of ticks since boot |
|
ticks_per_msec |
number of ticks per millisecond |
> print seconds, ticks, ticks/1000
2640 10562152 10562
>
Simple variables are assigned with the following statement:
let variable = expression
If the variable represents an output "pin variable", the corresponding MCU output pin is immediately updated.
Similarly, array variable elements are assigned with the following statement:
let variable[expression] = expression
Where the first expression evaluates to an array index between 0 and the length of the array minus one, and the second expression is assigned to the specified array element.
Multiple variables may be assigned in a single statement by separating them with commas:
let var1 = expr1, var2 = expr2, ...
> 10 dim simple, array[4]
> 20 while simple<4 do
> 30 let array[simple] = simple*simple
> 40 let simple = simple+1
> 50 endwhile
> 60 for simple = 0 to 3
> 70 print array[simple]
> 80 next
> run
0
1
4
9
> _
StickOS BASIC expressions are very similar to C expressions, and follow similar precedence and evaluation order rules.
The following operators are supported, in order of decreasing precedence:
|
n |
decimal constant |
|
0xn |
hexadecimal constant |
|
variable |
simple variable |
|
variable[expression] |
array variable element |
|
( ) |
grouping |
|
! ~ |
logical not, bitwise not |
|
* / % |
times, divide, mod |
|
+ - |
plus, minus |
|
>> << |
shift right, left |
|
<= < >= > |
inequalities |
|
== != |
equal, not equal |
|
| ^ & |
bitwise or, xor, and |
|
|| ^^ && |
logical or, xor, and |
The plus and minus operators can be either binary (taking two arguments, one on the left and one on the right) or unary (taking one argument on the right); the logical and bitwise not operators are unary. All binary operators evaluate from left to right; all unary operators evaluate from right to left.
Logical and equality/inequality operators, above, evaluate to 1 if true, and 0 if false. For conditional expressions, any non-0 value is considered to be true, and 0 is considered to be false.
If the expression references an input "pin variable", the corresponding MCU input pin is sampled to evaluate the expression.
Note that when StickOS parses an expression and later displays it (such as when you enter a program line and then list it), what you are seeing is a de-compiled representation of the compiled code, since only the compiled code is stored, to conserve RAM and flash memory. So superfluous parenthesis (not to mention spaces) will be removed from the expression, based on the precedence rules above.
> 10 print 2*(3+4)
> 20 print 2+(3*4)
> list
10 print 2*(3+4)
20 print 2+3*4
end
> run
14
14
> print 3+4
7
> print -3+2
-1
> print !0
1
> print 5&6
4
> print 5&&6
1
> print 3<5
1
> print 5<3
0
> print 3<<1
6
> _
While the MCU is connected to the host computer, print statements can be observed on the Hyper Terminal console window.
Print statements can be used to print integer expressions, using either a decimal or hexadecimal output radix:
print [dec|hex] expression
Or strings:
print "string"
Or various combinations of both:
print "string", [dec|hex] expression, ...
If the expression references an input "pin variable", the corresponding MCU input pin is sampled to evaluate the expression.
Note that when the MCU is disconnected from the host computer, print statement output is simply discarded.
> print "hello world"
hello world
> print 57*84
4788
> print hex 57*84
0x12b4
> print 9, "squared is", hex 9*9
9 squared is 0x51
> _
A program can declare read-only data in its code statements, and then consume the data at run-time.
To declare the read-only data, use the data statement as many times as needed:
data n
data n, n, ...
To consume data values and assign them to variables at runtime, use the read statement:
read variable
read variable, variable, ...
If a read is attempted when no more data exists, the program stops with an "out of data" error.
A line may be labeled and the current data consumer pointer may be moved to a specific (labeled) line with the statements:
label label
restore label
> 10 dim a, b
> 20 data 1, 2, 3
> 30 data 4
> 40 data 5, 6
> 50 data 7
> 60 while 1 do
> 70 read a, b
> 80 print a, b
> 90 endwhile
> 100 data 8
> run
1 2
3 4
5 6
7 8
out of data
STOP at line 70!
> _
Non-looping conditional statements are of the form:
if expression then
statements
elseif expression then
statements
else
statements
endif
Where statements is one or more program statements and the elseif and else clauses (and their corresponding statements) are optional.
> 10 dim a
> 20 for a = -5 to 5
> 30 if !a then
> 40 print a, "is zero"
> 50 elseif a%2 then
> 60 print a, "is odd"
> 70 else
> 80 print a, "is even"
> 90 endif
> 100 next
> run
-5 is odd
-4 is even
-3 is odd
-2 is even
-1 is odd
0 is zero
1 is odd
2 is even
3 is odd
4 is even
5 is odd
> _
Looping conditional statements include the traditional BASIC for-next loop and the more structured while-endwhile and do-until loops.
The for-next loop statements are of the form:
for variable = expression to expression step expression
statements
next
Where statements is one or more program statements and the step expression clause is optional and defaults to 1.
The for-next loop expressions are evaluated only once, on initial entry to the loop. The loop variable is initially set to the value of the first expression. Each time the loop variable is within the range (inclusive) of the first and second expression, the statements within the loop execute. At the end of the loop, if the incremented loop variable would still be within the range (inclusive) of the first and second expression, the loop variable is incremented by the step value, and the loop repeats again. On exit from the loop, the loop variable is equal to the value it had during the last iteration of the loop.
The while-endwhile loop statements are of the form:
while expression do
statements
endwhile
Where statements is one or more program statements .
The while-endwhile loop conditional expression is evaluated on each entry to the loop. If it is true (non-0), the statements within the loop execute, and the loop repeats again. On exit from the loop, the conditional expression is false.
The do-until loop statements are of the form:
do
statements
until expression
Where statements is one or more program statements .
The do-until loop conditional expression is evaluated on each exit from the loop. If it is false (0), the loop repeats again. On exit from the loop, the conditional expression is true.
In all three kinds of loops, the loop can be exited prematurely using the statement:
break
This causes program execution to immediately jump to the statements following the terminal statement (i.e., the next, endwhile, or until) of the innermost loop.
Additionally, multiple nested loops can be exited prematurely together using the statement:
break n
Which causes program execution to immediately jump to the statements following the terminal statement (i.e., the next, endwhile, or until) of the innermost n loops.
Similarly, a loop can be continued, causing execution to resume immediately with the conditional expression evaluation, using the statement:
continue
This causes program execution to immediately jump to the conditional expression evaluation, at which point the loop may conditionally execute again.
Multiple nested loops can be continued together using the statement:
continue n
Which causes program execution to immediately jump to the conditional expression evaluation of the innermost n loops.
> 10 dim a, b, sum
> 20 while 1 do
> 30 if a==10 then
> 40 break
> 50 endif
> 60 let sum = 0
> 70 for b = 0 to a
> 80 let sum = sum+b
> 90 next
> 100 print "sum of integers 0 thru", a, "is", sum
> 110 let a = a+1
> 120 endwhile
> run
sum of integers 0 thru 0 is 0
sum of integers 0 thru 1 is 1
sum of integers 0 thru 2 is 3
sum of integers 0 thru 3 is 6
sum of integers 0 thru 4 is 10
sum of integers 0 thru 5 is 15
sum of integers 0 thru 6 is 21
sum of integers 0 thru 7 is 28
sum of integers 0 thru 8 is 36
sum of integers 0 thru 9 is 45
> _
A subroutine is called with the following statement:
gosub subname [expression, ...]
A subroutine is declared with the following statements:
sub subname [param, ...]
statements
endsub
The sub can be exited prematurely using the statement:
return
This causes program execution to immediately return to the statements following the gosub statement that called the subroutine.
In general, subroutines should be declared out of the normal execution path of the code, and typically are defined at the end of the program.
Subroutine parameters are essentially variables local to the subroutine which are initialized to the values of the caller’s gosub expressions. Simple variable caller’s gosub expressions, however, are passed to sub params by reference, allowing the sub to modify the caller’s variables.
Any variables dimensioned in a subroutine are local to that subroutine. Local variables hide variables of the same name dimensioned in outer-more scopes. Local variables are automatically un-dimensioned when the subroutine returns.
> 10 dim a
> 20 for a = 0 to 9
> 30 gosub sumit a
> 40 next
> 50 end
> 60 sub sumit numbers
> 70 dim a, sum
> 80 for a = 1 to numbers
> 90 let sum = sum+a
> 100 next
> 110 print "sum of integers 0 thru", numbers, "is", sum
> 120 endsub
> run
sum of integers 0 thru 0 is 0
sum of integers 0 thru 1 is 1
sum of integers 0 thru 2 is 3
sum of integers 0 thru 3 is 6
sum of integers 0 thru 4 is 10
sum of integers 0 thru 5 is 15
sum of integers 0 thru 6 is 21
sum of integers 0 thru 7 is 28
sum of integers 0 thru 8 is 36
sum of integers 0 thru 9 is 45
> new
> 10 dim a
> 20 print a
> 30 gosub increment a
> 40 gosub increment a
> 50 print a
> 60 end
> 70 sub increment value
> 80 let value = value+1
> 90 endsub
> run
0
2
> _
StickOS supports up to four internal interval timers (0 thru 3) for use by the program. Timer interrupts are delivered when the specified time interval has elapsed since the previous interrupt was delivered.
Timer interrupt intervals are configured with the statement:
configure timer n for m (s|ms|us)
This configures timer n to interrupt every m seconds, milliseconds, or microseconds.
Note that the minimum timer resolution is the clock tick, which is 0.25 milliseconds.
The timer interrupt can then be enabled, and the statement(s) to execute when it is delivered specified, with the statement:
on timer n statement
If statement is a "gosub subname ...", then all of the statements in the corresponding sub are executed when the timer interrupt is delivered; otherwise, just the single statement is executed.
The timer interrupt can later be completely ignored (i.e., discarded) with the statement:
off timer n
The timer interrupt can be temporarily masked (i.e., held off but not discarded) with the statement:
mask timer n
And can later be unmasked (i.e., any pending interrupts delivered) with the statement:
unmask timer n
> 10 dim ticks
> 20 configure timer 0 for 1000 ms
> 30 on timer 0 do print "slow"
> 40 configure timer 1 for 200 ms
> 50 on timer 1 do gosub fast
> 60 sleep 3 s
> 70 print "ticks is", ticks
> 80 end
> 90 sub fast
> 100 let ticks = ticks+1
> 110 endsub
> run
slow
slow
slow
ticks is 14
> _
StickOS supports digital I/O on all pins.
A pin is configured for digital I/O, and a variable bound to that pin, with the following statement:
dim varpin as pin pinname for digital (input|output) \
[debounced] [inverted] [open_drain]
If a pin is configured for digital input, then subsequently reading the variable varpin will return the value 0 if the digital input pin is currently at a low level, or 1 if the digital input pin is currently at a high level. It is illegal to attempt write the variable varpin (i.e., it is read-only).
If a pin is configured for digital output, then writing varpin with a 0 value will set the digital output pin to a low level, and writing it with a non-0 value will set the digital output pin to a high level. Reading the variable varpin will return the value 0 if the digital output pin is currently at a low level, or 1 if the digital output pin is currently at a high level.
If the debounced pin qualifier is used, input values are passed thru a 12ms glitch-elimination filter.
If the inverted pin qualifier is used, all input and output values are logically inverted (i.e., 0->1, 1->0) at the pin.
If the open_drain pin qualifier is used, an output pin is tri-stated for a logic 1 output.
Use the help pins command to see the list of MCU pin names and their analog capabilities.
A pin is configured for analog I/O, and a variable bound to that pin, with the following statement:
dim varpin as pin pinname for analog (input|output) \
[debounced] [inverted]
If a pin is configured for analog input, then subsequently reading the variable varpin will return the analog voltage level, in millivolts (mV). It is illegal to attempt write the variable varpin (i.e., it is read-only).
If a pin is configured for analog output, then writing varpin with a millivolt value will set the analog output (PWM actually) pin to a corresponding analog voltage level. Reading the variable varpin will return the analog voltage level, in millivolts (mV).
If the debounced pin qualifier is used, input values are passed thru a 12ms glitch-elimination filter.
If the inverted pin qualifier is used, all input and output values are logically inverted at the pin (i.e., the pin mV is replaced with the maximum analog supply voltage millivolts minus the pin mV).
The maximum analog supply voltage millivolts may be displayed with the command:
analog
Configure the maximum analog supply voltage millivolts with the following command:
analog millivolts
This value defaults to 3300 mV and is stored in flash and affects all analog I/O pins.
Use the help pins command to see the list of MCU pin names and their servo capabilities.
A pin is configured for servo I/O, and a variable bound to that pin, with the following statement:
dim varpin as pin pinname for servo output
If a pin is configured for servo output, then writing varpin with a centi-millisecond (cms) value will set the servo output pin to the specified pulse duration. Reading the variable varpin will return the output pulse duration, in centi-milliseconds (cms).
The servo frequency may be displayed with the command:
servo
Configure the servo frequency (in Hz) with the following command:
servo Hz
This value defaults to 45 Hz and is stored in flash and affects all servo I/O pins.
Use the help pins command to see the list of MCU pin names and their frequency capabilities.
A pin is configured for frequency I/O, and a variable bound to that pin, with the following statement:
dim varpin as pin pinname for frequency output
If a pin is configured for frequency output, then writing varpin with a hertz (Hz) value will set the frequency output pin to the specified frequency. Reading the variable varpin will return the output frequency, in hertz (Hz).
Use the help pins command to see the list of MCU pin names and their UART capabilities.
UARTs can be configured for a specific serial communication protocol and then used to transmit or receive serial data. UARTs can also be configured to generate interrupts when they receive or transmit a character (or more specifically, when the uart receive buffers are not empty, or when the uart transmit buffers are empty).
UART serial communication protocols are configured with the statement:
configure uart n for b baud d data (even|odd|no) parity
configure uart n for b baud d data (even|odd|no) parity loopback
This configures uart n for b baud operation, with d data bits and the specified parity; 2 stop bits are always transmitted and 1 stop bit is received. If the optional "loopback" parameter is specified, the UART is configured to loop all transmit data back into its own receiver, for testing purposes.
Once the UART is configured, pin variables should be bound to the specified UART's transmit and receive pins with one or more of the following statements:
dim varrx as pin pinname for uart input
dim vartx as pin pinname for uart output
This binds the varrx variable to the specified UART's receive data pin, and the vartx variable to the specified UART's transmit data pin. From then on, receive data can be examined by reading the varrx variable, and transmit data can be generated by writing the vartx variable.
At this point, if desired, interrupt handlers can be set up to handle UART receive and/or transmit interrupts. UART receive interrupts are delivered when the uart receive buffers are not empty; UART transmit interrupts are delivered when the uart transmit buffers are empty.
The UART receive or transmit interrupt can be enabled, and the statement(s) to execute when it is delivered specified, with the statement:
on uart n (input|output) statement
If statement is a "gosub subname ...", then all of the statements in the corresponding sub are executed when the timer interrupt is delivered; otherwise, just the single statement is executed.
Note that an initial UART transmit interrupt is generated when the transmit interrupt is enabled, since the uart transmit buffers are empty!
The UART receive or transmit interrupt can later be completely ignored (i.e., discarded) with the statement:
off uart n (input|output)
The UART receive or transmit interrupt can be temporarily masked (i.e., held off but not discarded) with the statement:
mask uart n (input|output)
And can later be unmasked (i.e., any pending interrupts delivered) with the statement:
unmask uart n (input|output)
See UART I/O Example
Use the help pins command to see the list of MCU pin names and their QSPI capabilities.
Unlike UART I/O, pin variables are not used for QSPI I/O. Instead, there is a new qspi statement which specifies the data variables (and implicitly, the data sizes) to be transferred via QSPI. QSPI transfers are always bidirectional -- the current values of the variables are shifted out and new values are shifted in.
Data is transferred (again, bidirectionally) with a single statement:
qspi variable, ...
The BASIC program is responsible for defining a chip select pin as a digital output and asserting it prior to the qspi statement, and deasserting it afterwards. During the qspi statement, the current value of all variables, for their corresponding sizes (8 bits, 16 bits, or 32 bits) will be shifted out, and the new values for the same variables will be shifted in. If an array variable is specified, its entire array contents are used.
StickOS can also support pin interrupts on any input (or output, for that matter) pin thru the use of pin variables in the watchpoint expression:
line on expression do statement
The watchpoint expression is re-evaluated before every line of the program is executed; if the expression transitions from false to true, the watchpoint statement handler runs.
Since watchpoints have to transition from false to true, you can think of them as an edge-sensitive interrupt on a digital input pin. On an analog input pin, you can think of then as detecting an edge at a specific voltage level.
To set the smart watchpoint mode, which dramatically reduces watchpoint overhead at a slight delay of input pin sensitivity, use the command:
watchsmart (on|off)
To display the smart watchpoint mode, use the command:
watchsmart
For the example below, the MCU sw1 should be pressed one or more times after running the program.
> 10 dim switch as pin irq1* for digital input
> 20 on ! switch do print "switch pressed!"
> 30 sleep 1000 s
> run
switch pressed!
switch pressed!
switch pressed!
> _
You can delay program execution for a number of seconds, milliseconds, or microseconds using the statement:
sleep expression (s|ms|us)
Note that the minimum sleep resolution is the clock tick, which is 0.25 milliseconds. Note also that in general it would be a bad idea to use a sleep statement in the on handler for a timer or uart interrupt.
You can add remarks to the program, which have no impact on program execution, with the statement:
rem remark
> 10 rem this program takes 5 seconds to run
> 20 sleep 5 s
> run
> _
StickOS typically runs about 1000 BASIC statements per second per processor MHz (i.e., 50,000 lines/second on a 50MHz processor). Many issues affect performance, most notably the specific statement mix.
StickOS runs fastest when not merging program lines from RAM and flash, so you should always save your program (causing RAM and flash program lines to be re-merged back to flash) before running it. It is also a good idea to renumber your program to ensure profile buckets are evenly distributed to the lines of your program.
Once you have run a saved/renumbered program, you can use the following command to list the time spent in each line of the program:
profile
profile line
profile -line
profile line-
profile line-line
Alternately, you can list the time spent in an entire subroutine by name with the command:
profile subname
> new
> 10 dim a, sum
> 20 for a = 1 to 10000
> 30 let sum = sum+a
> 40 next
> 50 print sum
> save
> run
50005000
> profile
0ms 10 dim a, sum
22ms 20 for a = 1 to 10000
315ms 30 let sum = sum+a
141ms 40 next
2ms 50 print sum
end
>
Prior to any wireless operation, each MCU/IOStick pair needs to have a unique zigflea nodeid set. ZigFlea nodeid’s are integers from 0 to 65534. The zigflea nodeid is set with the following command:
nodeid nodeid
To connect to another MCU from the current one, use the command:
> connect new-nodeid
press Ctrl-D to disconnect
At that point you should press <Enter> to get a prompt from the remote MCU (or press <Ctrl-C> to stop it if it is running a program), and verify its nodeid:
<Enter>
> nodeid
new-nodeid
> _
When you are done using the other MCU, press <Ctrl-D> and the original MCU will print the following message, followed by a prompt:
...disconnected
It is always a good idea to re-verify its nodeid:
> nodeid
old-nodeid
> _
A MCU can modify variables on a remote MCU using zigflea remote variables. A remote variable is declared with the statement:
dim varremote as remote on nodeid nodeid
This tells StickOS that the variable varremote is actually dimensioned on another node, nodeid, and any updates of that variable on this node should be forwarded to that other node for processing.
When varremote is modified, the request will be forwarded to the other nodeid; if the other nodeid does not accept the request, varremote will be reset to -1 instead.
The following example shows a remote LED dimmer, where the potentiometer on nodeid 1 is used to control the LED on nodeid 2.
> nodeid
1
> 10 dim potentiometer as pin an0 for analog input
> 20 dim led as remote on nodeid 2
> 30 while 1 do
> 40 let led = potentiometer
> 50 sleep 100 ms
> 60 endwhile
> save
> autorun on
> connect 2
press Ctrl-D to disconnect
<Enter>
> nodeid
2
> 10 dim led as pin dtin0 for analog output
> 20 while 1 do
> 30 endwhile
> save
> autorun on
> run
<Ctrl-D> ...disconnected
> nodeid
1
> run
now adjust the potentiometer on nodeid 1
and watch the LED change on nodeid 2!!!
Once the MCU is disconnected from the host computer, it may be run standalone.
Based on the “autorun” mode, when the MCU is powered up, it will typically start running the (saved) BASIC program automatically.
Again, when the StickOS is running the “heartbeat” LED will blink slowly; when the BASIC program in the MCU is running, the “heartbeat” LED will blink quickly.
Note that any unsaved changes to the BASIC program will be lost if the MCU is reset or loses power.
Though this probably goes without saying, the MCU can also be permanently connected to the host computer and used as a slave data acquisition/control device, all under host computer software control!
To do this, the host computer software program would simply open the MCU virtual COM port or TCP/IP port and then write StickOS commands and/or statements to it, and then read the results back from it.
Often it is useful to disable terminal echo and prompts when running in slave mode.
To set the terminal echo and prompt modes, use the commands:
echo (on|off)
prompt (on|off)
To display the terminal echo and prompt modes, use the commands:
echo
prompt
A master MCU can clone its flash to a slave MCU, including any BASIC programs and flash parameter values, by simply connecting the master MCU to the slave MCU with the following cable:
|
master |
slave |
|
qspi_clk |
qspi_clk (ezpck) |
|
qspi_din |
qspi_dout (ezpq) |
|
qspi_dout |
qspi_din (ezpd) |
|
pins qspi_cs* |
rcon* (ezpcs*) |
|
pins qspi_rst* |
rsti* |
|
vss |
vss |
|
vdd |
vdd |
And then using the following command on the master MCU:
> clone
Welcome to StickOS for Freescale MCF52221 v1.2!
Copyright (c) 2008; all rights reserved.
cloning...
done!
> _
Or if you want the slave MCU to start running immediately following the clone procedure, use the following command instead:
> clone run
Welcome to StickOS for Freescale MCF52221 v1.2!
Copyright (c) 2008; all rights reserved.
cloning...
done!
> _
Note that the upgrade procedure wipes out all BASIC programs and parameters from flash memory.
A MCU’s StickOS firmware (i.e., the BASIC development environment itself) can be upgraded with the following command:
> upgrade
paste S19 upgrade file now...
At that point you should paste the entire S19 upgrade file into your terminal emulator.
When upgrade is nearly complete (about two minutes), you will see:
paste done!
programming flash...
wait for MCU heartbeat LED to blink!
Then wait for the MCU “heartbeat” LED to blink, indicating flash programming is complete; press the “Disconnect” button followed by the “Call” button, to reconnect Hyper Terminal.
After the upgrade, you must then update the StickOS pin assignments; see First Boot & Pin Assignments in both this User’s Guide and the CPUStick User’s Guide, as appropriate. Until you update the StickOS pin assignments, the heartbeat LED will not blink and zigflea will be unusable, but StickOS will still be running, so you can connect the terminal emulator and make the necessary pin assignment updates.
Note that once flash programming begins, a failed (or interrupted) upgrade procedure may only be able to be recovered via a re-clone from a working MCU.
auto <line> -- automatically number program lines
clear [flash] -- clear ram [and flash] variables
clone [run] -- clone flash to slave MCU [and run]
cls -- clear terminal screen
cont [<line>] -- continue program from stop
delete [<line>][-][<line>] -- delete program lines or <subname>
download <slave Hz> -- download for relay to QSPI to EzPort
dir -- list saved programs
edit <line> -- edit program line
help [<topic>] -- online help
list [<line>][-][<line>] -- list program lines or <subname>
load <name> -- load saved program
memory -- print memory usage
new -- erase code ram and flash memories
profile [<line>][-][<line>] -- like list, but display profile info
purge <name> -- purge saved program
renumber [<line>] -- renumber program lines (and save)
reset -- reset the MCU!
run [<line>] -- run program
save [<name>] -- save code ram to flash memory
undo -- undo code changes since last save
upgrade -- upgrade StickOS firmware!
uptime -- print time since last reset
analog [<millivolts>] -- set/display analog voltage scale
autorun [on|off] -- autorun (on reset) mode
echo [on|off] -- terminal echo mode
indent [on|off] -- listing indent mode
nodeid [<nodeid>|none] -- set/display zigflea nodeid
numbers [on|off] -- listing line numbers mode
pins [<assign> [<pinname>|none]] -- set/display StickOS pin assignments
prompt [on|off] -- terminal prompt mode
servo [<Hz>] -- set/display servo Hz (on reset)
sleep [on|off] -- debugger sleep mode
step [on|off] -- debugger single-step mode
trace [on|off] -- debugger trace mode
watchsmart [on|off] -- low-overhead watchpoint mode
pin assignments:
heartbeat safemode*
qspi_cs* clone_rst* zigflea_rst* zigflea_attn* zigflea_rxtxen
<line> <statement> -- enter program line into code ram
assert <expression> -- break if expression is false
data <n> [, ...] -- read-only data
dim <variable>[[n]] [as ...] [, ...] -- dimension variables
end -- end program
label <label> -- read/data label
let <variable> = <expression> [, ...] -- assign variable
print ("string"|<expression>) [, ...] -- print strings/expressions
qspi <variable> [, ...] -- perform qspi I/O by reference
read <variable> [, ...] -- read read-only data into variables
rem <remark> -- remark
restore [<label>] -- restore read-only data pointer
sleep <expression> (s|ms|us) -- delay program execution
stop -- insert breakpoint in code
if <expression> then
[elseif <expression> then]
[else]
endif
for <variable> = <expression> to <expression> [step <expression>]
[(break|continue) [n]]
next
while <expression> do
[(break|continue) [n]]
endwhile
do
[(break|continue) [n]]
until <expression>
gosub <subname> [<expression>, ...]
sub <subname> [<param>, ...]
[return]
endsub
timers:
configure timer <n> for <n> (s|ms|us)
on timer <n> do <statement> -- on timer execute statement
off timer <n> -- disable timer interrupt
mask timer <n> -- mask/hold timer interrupt
unmask timer <n> -- unmask timer interrupt
uarts:
configure uart <n> for <n> baud <n> data (even|odd|no) parity [loopback]
on uart <n> (input|output) do <statement> -- on uart execute statement
off uart <n> (input|output) -- disable uart interrupt
mask uart <n> (input|output) -- mask/hold uart interrupt
unmask uart <n> (input|output) -- unmask uart interrupt
watchpoints:
on <expression> do <statement> -- on expr execute statement
off <expression> -- disable expr watchpoint
mask <expression> -- mask/hold expr watchpoint
unmask <expression> -- unmask expr watchpoint
the following operators are supported as in C,
in order of decreasing precedence:
<n> -- decimal constant
0x<n> -- hexadecimal constant
<variable> -- simple variable
<variable>[<expression>] -- array variable element
( ) -- grouping
! ~ -- logical not, bitwise not
* / % -- times, divide, mod
+ - -- plus, minus
>> << -- shift right, left
<= < >= > -- inequalities
== != -- equal, not equal
| ^ & -- bitwise or, xor, and
|| ^^ && -- logical or, xor, and
all variables must be dimensioned!
variables dimensioned in a sub are local to that sub
simple variables are passed to sub params by reference
array variable indices start at 0; v[0] is the same as v
ram variables:
dim <var>[[n]]
dim <var>[[n]] as (byte|short)
absolute variables:
dim <var>[[n]] [as (byte|short)] at address <addr>
flash parameter variables:
dim <varflash>[[n]] as flash
pin alias variables:
dim <varpin> as pin <pinname> for (digital|analog|servo|frequency|uart) \
(input|output) \
[debounced] [inverted] [open_drain]
system variables:
nodeid msecs seconds ticks ticks_per_msec (read-only)
pin names:
0 1 2 3 4 5 6 7
-------- --------- --------- -------- ----- -------- -------- ------+
an0 an1 an2 an3 an4 an5 an6 an7 | AN
scl sda | AS
irq1* irq4* irq7* | NQ
qspi_dout qspi_din qspi_clk qspi_cs0 qspi_cs2 qspi_cs3 | QS
dtin0 dtin1 dtin2 dtin3 | TC
utxd0 urxd0 urts0* ucts0* | UA
utxd1 urxd1 urts1* ucts1* | UB
all pins support general purpose digital input/output
an? = potential analog input pins (mV)
dtin? = potential analog output (PWM) pins (mV)
dtin? = potential servo output (PWM) pins (cms)
dtin? = potential frequency output pins (Hz)
urxd? = potential uart input pins (received byte)
utxd? = potential uart output pins (transmit byte)
connect <nodeid> -- connect to MCU <nodeid> via zigflea
remote node variables:
dim <varremote>[[n]] as remote on nodeid <nodeid>
zigflea cable:
MCU MC1320X
------------- -----------
qspi_clk spiclk
qspi_din miso
qspi_dout mosi
irq4* irq*
pins qspi_cs* ce*
pins zigflea_rst* rst*
pins zigflea_rxtxen rxtxen
vss vss
vdd vdd