StickOS™ BASIC User's Guide  v1.70


http://www.cpustick.com

 

1         Overview

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

 

1      Overview.. 1

2      Embedded Systems Made Easy. 5

3      Wireless Embedded Systems Made Just as Easy! 8

4      MCU Connections. 11

4.1        Interface. 11

4.2        External Pins. 11

4.3        Command-Line Transports. 12

4.3.1     USB Transport 13

4.3.2     USB Host Mode. 14

4.3.3     Ethernet Transport 15

4.3.4     UART Transport 16

5      StickOS. 18

5.1        First Boot & Pin Assignments. 19

5.2        Command-Line. 21

5.2.1     Digital I/O Example. 22

5.2.2     Analog I/O Example. 24

5.2.3     Servo I/O Example. 25

5.2.4     Frequency I/O Example. 26

5.2.5     UART I/O Example. 27

5.2.6     QSPI Master I/O Example. 29

5.3        StickOS Commands. 30

5.3.1     Getting Help. 30

5.3.2     Entering Programs. 32

5.3.3     Running Programs. 36

5.3.4     Loading and Storing Programs. 38

5.3.5     Debugging Programs. 39

5.3.6     Other Commands. 43

5.4        BASIC Program Statements. 45

5.4.1     Variable Declarations. 45

5.4.2     System Variables. 48

5.4.3     Variable Assignments. 49

5.4.4     Expressions. 50

5.4.5     Print Statements. 52

5.4.6     Read/Data Statements. 53

5.4.7     Conditional Statements. 54

5.4.8     Looping Conditional Statements. 55

5.4.9     Subroutines. 58

5.4.10       Timers. 60

5.4.11       Digital I/O.. 62

5.4.12       Analog I/O.. 63

5.4.13       Servo I/O.. 64

5.4.14       Frequency I/O.. 65

5.4.15       UART I/O.. 66

5.4.16       QSPI Master I/O.. 68

5.4.17       Pin Interrupts. 69

5.4.18       Other Statements. 70

5.5        Performance. 71

6      2.4GHz ZigFlea Wireless Operation. 72

6.1        ZigFlea Configuration. 72

6.2        ZigFlea Remote Control 72

6.3        ZigFlea Remote Variables. 73

7      Standalone Operation. 74

8      Slave Operation. 75

9      MCU Cloning. 76

10     MCU Upgrading. 77

11     Appendix. 78

11.1      StickOS Command Reference. 78

11.1.1       Commands. 78

11.1.2       Modes. 78

11.2      BASIC Program Statement Reference. 79

11.2.1       Statements. 79

11.2.2       Block Statements. 79

11.2.3       Device Statements. 80

11.2.4       Expressions. 80

11.2.5       Variables. 80

11.2.6       Pins (MCF52221 example) 81

11.2.7       ZigFlea (MCF52221 example) 81

 


2         Embedded Systems Made Easy

A simple embedded system, like a toaster oven temperature profile controller, can be brought online in record time!

It’s as easy as...

 

  1. wire the MCU I/O pins to the embedded circuit
    1. wire MCU pin an0 to thermocouple op-amp output
      (I use an LM358)
    2. wire MCU pin an1 to solid state relay control input
      (I use a Teledyne STH24D25)
  2. connect a host computer to the USB interface on the MCU
  3. let the host computer automatically install the FTDI Serial Port transport drivers
  4. open a Hyper Terminal console window and connect to the MCU; press <Enter> for a command prompt
  5. configure the MCU I/O pins as appropriate
    1. configure pin an0 as an analog input
    2. configure pin an1 as a digital output
  1. write and debug your BASIC control program, live on the MCU (see below)
  2. type “save”
  3. type “autorun on”

  4. turn the toaster oven full on (so that the relay can control it)
  5. type “reset”
  6. disconnect the host computer from the USB interface on the MCU

 

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

 


3         Wireless Embedded Systems Made Just as Easy!

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...

 

  1. set the 2.4GHz zigflea wireless nodeid on each MCU
  2. wire MCU #1 to its embedded circuit
    1. wire MCU #1 pin an0 to the potentiometer
  1. wire MCU #2 to its embedded circuit
    1. wire MCU #2 pin dtin0 to the LED
  2. connect a host computer to the USB interface on MCU #1
  3. write and debug your BASIC control program, live on MCU #1 (see below)
  4. use the 2.4GHz zigflea wireless transport to connect to MCU #2
  5. write and debug your BASIC control program, live on MCU #2 (see below)
  6. run the program on MCU #2
  7. disconnect from MCU #2
  8. run the program on MCU #1

 

 

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!!!

 


4         MCU Connections

4.1      Interface

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.

4.2      External Pins

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.

 

 


4.3      Command-Line Transports

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.


4.3.1      USB Transport

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!

4.3.2      USB Host Mode

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.


4.3.3      Ethernet Transport

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.

 


4.3.4      UART Transport

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!

5         StickOS

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.


5.1      First Boot & Pin Assignments

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

 

Examples

> 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

> _


5.2      Command-Line

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 -   ^

> _

 

 


5.2.1       Digital I/O Example

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.


5.2.2       Analog I/O Example

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.


5.2.3      Servo I/O Example

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

> _


5.2.4      Frequency I/O Example

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.


5.2.5       UART I/O Example

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

> _


5.2.6      QSPI Master I/O Example

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.


5.3       StickOS Commands

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.

5.3.1      Getting Help

The help command displays the top level list of help topics:

 

help

 

To get help on a subtopic, use the command:

 

help subtopic


Examples

> 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

> _


5.3.2      Entering Programs

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


Examples

> 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

> _


5.3.3      Running Programs

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.


Examples

> 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

> _


5.3.4      Loading and Storing Programs

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

Examples

> 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

> _


5.3.5      Debugging Programs

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.


Examples

> 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!

> _

 

5.3.6      Other Commands

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


Examples

> 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

_

 


5.4       BASIC Program Statements

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.

5.4.1      Variable Declarations

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

Examples

> 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

> _


5.4.2      System Variables

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

Examples

> print seconds, ticks, ticks/1000

2640 10562152 10562


5.4.3      Variable Assignments

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, ...

Examples

> 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

> _


5.4.4       Expressions

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.

Examples

> 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

> _

 


5.4.5      Print Statements

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.

Examples

> 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

> _


5.4.6      Read/Data Statements

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

Examples

> 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!

> _

 


5.4.7      Conditional Statements

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.

Examples

> 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

> _

 

 


5.4.8      Looping Conditional Statements

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.

 

Examples

> 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

> _


5.4.9      Subroutines

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.


Examples

> 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

> _


5.4.10  Timers

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


Examples

> 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

> _


5.4.11 Digital I/O

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.

Examples

See Digital I/O Example


5.4.12 Analog I/O

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.

Examples

See Analog I/O Example


5.4.13 Servo I/O

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.

Examples

See Servo I/O Example


5.4.14 Frequency I/O

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).   

Examples

See Frequency I/O Example

 


5.4.15 UART I/O

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)

Examples

See UART I/O Example

 


5.4.16 QSPI Master I/O

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.

Examples

See QSPI Master I/O Example


5.4.17 Pin Interrupts

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.

Examples

> 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!

> _

 


5.4.18 Other Statements

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

Examples

> 10 rem this program takes 5 seconds to run

> 20 sleep 5 s

> run

> _

 


5.5      Performance

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

Examples

> 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

 


6         2.4GHz ZigFlea Wireless Operation

6.1      ZigFlea Configuration

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

6.2      ZigFlea Remote Control

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

> _


6.3      ZigFlea Remote Variables

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.

Examples

> 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!!!


7         Standalone Operation

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.


8         Slave Operation

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

 


9         MCU Cloning

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!

> _

 

 

 


10   MCU Upgrading

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.

 


11   Appendix

11.1StickOS Command Reference

11.1.1 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

11.1.2 Modes

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


11.2BASIC Program Statement Reference

11.2.1 Statements

<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

11.2.2 Block Statements

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


11.2.3 Device Statements

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

11.2.4 Expressions

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

11.2.5 Variables

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)

11.2.6 Pins (MCF52221 example)

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)

11.2.7 ZigFlea (MCF52221 example)

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