|
Chapter 2
<< Previous | Next>>
Chapter 2: Your First Program
This Chapter will get you started using the Control-CForth
language to program your QVGA Controller. It will guide you through the
installation of the Mosaic IDE, an integrated editor,
compiler, and terminal, and you'll start up and talk with your
controller. You'll also:
Compile and download your first program using an example multitasking program that performs calculations using floating point
math, stores data in arrays in the QVGA Controller's extended memory space, and
prints the results to the terminal;
Selectively execute any program function using the QVGA Controller's on-board debugging environment (QED-Forth);
Use the QVGA Controller's built-in operating system to access extended memory;
Use the terminal to interact with a running multitasking application; and,
See how easy it is to set up a multitasking application.
The Mosaic IDE, which includes the Control-C Compiler, a full-featured editor and communications
terminal, is provided on an installation CD-ROM. To install it onto your PC,
first insert the Installation CD-ROM into your CD Drive. If the installer does
not launch automatically, browse to your computer's CD drive using the 'My
Computer' icon and double click on 'Setup.exe' to manually launch the
installer.
We recommend that you use the default installation
directory ("C:\Mosaic\") and choose 'Typical Setup' when asked. If you wish to
install into a different directory, you may type in any pathname provided that
it does not contain any spaces. The 'Custom' setup option can be used if
another version of either TextPad (the editor used within the Mosaic
IDE), the Mosaic Terminal (previously called QED-Term), or an
earlier version of the Mosaic IDE has already been installed. However, the
Mosaic IDE requires all of its components to work properly. Please
contact us if you have any questions.
When the installation is complete, you will need to
restart your computer unless you are installing onto a Windows 2000 machine.
Be sure to choose 'Yes' when asked to restart — if you don't, the installation
may not complete properly. If you choose 'No' and restart later we recommend
that to assure a full restart you fully shutdown your computer and restart it;
lesser restarts don't always restart fully.
You are now ready to talk with your QVGA Controller!
Familiarize yourself with the locations of the power and
serial connectors as shown in Chapter 1. After finding them follow these steps
to turn on your system and establish communications with it:
1. Examine the settings of the 7 onboard switches.
Dip switches 1, 5, 6, and 7 should be turned OFF by depressing the side of the
switch towards the edge of the board. Dip switches 2, 3, and 4 should be
turned ON. The function of each Dip switch is described in Chapter 1.
2. Connect the female end of the 9-Pin serial
communications cable to your computer terminal's serial communications port and
the male end to the primary serial port on the QVGA Controller. You can use
any of your PC's COM ports. COM2 is usually available, but some PCs only have
COM1 available.
3. Power up your PC computer or terminal.
4. You should check the
configuration of your Windows communications drivers:
1. On your PC go to the device manager dialog
box by double clicking "System" in the "Control Panel", clicking the "Hardware"
tab, and clicking the "Device Manager" button.
2. In the list of devices open up the list of
"Ports" and double click on "Communications Port (COM2)". (If COM2 is tied up
with another service, such as a fax/modem, you may want to use COM1 instead.)
3. You'll now have a dialog box called
"Communications Port (COM2) Properties". Click the general tab and make sure
you have these settings:
|
|
|
|
Baud Rate
|
9600
|
|
Data Bits
|
8
|
|
Parity
|
None
|
|
Stop Bits
|
1
|
|
Flow Control
|
Xon/Xoff
|
5. We recommend that you use Mosaic Terminal,
the terminal program that comes with the Mosaic IDE. You can start the terminal
by double clicking the TextPad editor (the primary application of the Mosaic
IDE) and choosing the terminal toolbar button which looks like this:

(The appearance of this and other toolbar icons may
change in subsequent versions of the Mosaic IDE.) You can also start the
terminal by double clicking the application "MosaicTerminal.exe".
The terminal starts with using COM2 by default at a
speed of 19200 baud (you will change this to 9600 baud in the next step), no
parity, and 1 stop bit. Xon/Xoff flow control is enabled, and the file
transfer options are set so that the terminal waits for a linefeed (^J)
character before sending each line (this is very important). You can use any
other terminal program, but it must be configured with these same settings. If
you use another terminal program, you must specify that it uses the linefeed
character as its prompt character. It might be denoted as LF, ^J, ascii
decimal 10, or ascii hex A.
If your PC is set up to use a COM port other than COM2
the Mosaic Terminal will respond with a warning box saying "Invalid port
number". If so, just go to the Mosaic Terminal menu item "Settings→Comm→Port"
and choose the COM port you chose when configuring Windows in step 4. above.
6. Change the baud rate of the Mosaic Terminal by
going to "Settings→Comm→Baud Rate" and selecting 9600 baud. This is
the baud rate used to communicate with the QScreen Controller. If the baud
rate is incorrect, garbled characters may appear in the terminal when you try
to communicate with the QScreen.
7. Plug the QVGA Controller's power supply into a 110
VAC outlet. European users will need a power transformer for changing European
220 VAC to 110 VAC. Insert the power supply output plug into the power jack on
the QVGA Controller and turn the power switch ON by depressing the side of the
switch towards the outer edge of the QVGA Board. After a short pause two
things should happen:
1. A demonstration
program should appear on the screen; and,
2. If you hit the enter key while the cursor
is in your terminal window you should see the QVGA Controller respond with,
ok
The demo program, and the serial communications response
indicate that your QVGA Controller is now working!
The working software demo that is included in each QVGA
Controller automatically starts when power is first applied and the power
switch is turned ON. The demo shows three screens, accessible by touching file
tabs at the top of the screen. The first screen shows a typical numeric
keypad entry screen, the second provides examples of font sizes, and the third
shows random numbers being plotted into a plot window. Touching the file tabs
transfers focus from one screen to another.
While the demo is running as one task, another task on the
controller responds to commands from the terminal, and responds with the 'ok'
prompt to an enter key.
You can play with the demo to see how it works. Observing
this demo program run is a good way to verify that all of the hardware is
functioning properly and also demonstrates some basic capabilities of the menu
control software.
If Something Doesn't Work
If your demo is not running, check that power is being
properly applied to the controller. The demo should always run on a new QVGA
Controller when power is initially applied. If you see only a blank screen and
no message appears on your terminal there's something wrong, so:
1. Verify that power is being properly applied to the
controller.
2. Verify that the serial cable is properly connected.
3. Check the terminal configurations of the Mosaic
Terminal (using the menu item "Settings → Comm"), and recheck the
communications properties of the Windows communications port.
4. Email or call us if you are still having trouble.
Disabling the Demo Program
When you tire of the demo you can disable it, but don't do
that too quickly because once the demo is disabled you can't restart it without
sending special commands to the controller. So, feel free to play with the
demo for awhile, to see how it uses buttons and menus.
Eventually, you will want to disable the demo. You can
disable it by doing a special cleanup that places the QVGA Controller in
a pristine state with no application program running.
Doing a
"Special Cleanup"
If you ever
need to return your QVGA Controller to its factory-new condition, just do a Special
Cleanup: Turn ON dip switch 6, toggle dip switch 7 ON and then back OFF
again, and finally turn OFF dip switch 6. This procedure will remove any
application programs and reinitialize all operating system parameters to their
factory-new condition.
After you do disable the demo your display should be
blank. You can verify that serial communications are working properly by hitting
the enter key while your insertion point is in the terminal window.
Whenever you hit the 'Enter' key, as represented by the symbol in the line above, you should see an 'ok' prompt.
Now, whenever the QVGA Controller is powered up with your
terminal communicating with it, this message,
QED-Forth V4.xx
ok
where "V4.xx" represents the current software version
number of your QVGA Controller, should appear, and the 'ok' prompt should
repeat each time you press the carriage return. This message indicates that
the onboard operating system is ready to receive commands.
Although the demo now no longer automatically starts when
the QVGA Controller is turned on, it still resides in Flash memory. The
special cleanup has merely erased an autostart vector — a special
location that tells the controller the starting address of the program to run
when it starts up. You can still start the demo program by typing the
following into your terminal window,
RESTORE
main
in which the symbol represents hitting the 'Enter' key on your keyboard. The demo program should now be running. The
first command you typed, RESTORE, restored pointers to the operating system's
list of names of programs. The pointers had been saved in EEPROM by a SAVE
command. The second line is the name of the program. When you type the name
of a program the operating system responds by executing that program.
The GUI demo (main) program will continue
operating until the board is reset or the power turned off. If you'd like to
restore the demo so that it automatically starts whenever the controller is
turned on, simply type the following two lines:
RESTORE
CFA.FOR main PRIORITY.AUTOSTART
Note that you must type the lines with the spaces between
the words just as in the lines above. The case of the characters isn't
important.
These commands store the starting address of the main
routine in the autostart vector. The two words, CFA.FOR main, find the code
field address of the routine main and send it to the
routine PRIORITY.AUTOSTART
which stores it in the autostart vector. Now, the demo should automatically
start up whenever the controller is turned on.
If you download other programs to the QVGA Controller you
will overwrite the demo program, which is stored in Flash memory. Also, any
execution of the SAVE command will overwrite the saved pointers. In
either case, to revive the demo you will need to reload it. That is done by
using the Mosaic Terminal to reload the compiled demo file to the controller's
Flash memory. For instructions for doing that please see the comments in the
folder, "C:\Mosaic\GUI Toolkit\Demo".
Using the Editor and
Compiler
The Mosaic IDE has two main components, the TextPad editor, which includes the Control-C compiler, and the
Mosaic Terminal program, both of which you'll find in the default directory
"C:\Mosaic\":
TextPad is a fully featured and highly
configurable text and program editor. You'll use it to write and compile your code. All
of the functions of the C compiler tools are available through the controls in
TextPad. You can launch TextPad from the 'Mosaic IDE' group in the 'Programs'
section of your Windows 'Start' menu. For convenience, you may want to place a
shortcut to it on your desktop or on your Windows Taskbar.
The Mosaic Terminal is a serial communications
terminal that allows you to interactively control your controller over its
RS-232 interface. You'll also use it to download your
compiled C programs Forth source
code for compilation into the memory of the QVGA Controller. Mosaic
Terminal may be launched from the 'Mosaic IDE' group within
'Start→Programs', but it is also available from
within TextPad, either from the 'Tools' dropdown menu or by clicking the
terminal icon on TextPad's toolbar.
You can type characters directly into the terminal
window, and they will be accepted by the QVGA Controller's line editor and
interpreter. This mode of interaction is convenient when debugging or typing
short code fragments. If you are sending source code to the QVGA Controller,
it is best to create a file first. The file can be saved to disk to provide a
record of your work, and the terminal program can be used to download the file
to the QVGA Controller. You can also use the terminal to record your debugging
sessions and save them as a file on disk
The TextPad Tool Bar 
Along with the standard tools you expect in a text editor
you'll find four custom tools available in the toolbar that you'll use to
compile and download your programs. Each of these tools is also accessible in
the 'Tools' dropdown menu of TextPad. For C programmers the Debug (magnifying
glass) icon calls the C compiler and assembler only, the Single-Page C icon
performs a standard build of your program, and the Multi-Page C icon performs a
multi-page memory model build of your program. Forth programmers won't need to
use these tools. Both C and Forth programmers will find the Image Converter
and Terminal icons useful. The Image Converter facilitates converting bitmap
images to a form the QVGA Controller can use, and the terminal icon launches
the Mosaic Terminal program. Each of these tools is described in more
detail below.
The 'Compile Tool' Finds Syntax Errors in C
Programs
The Compile
Tool, designated by the "Debug" or "Magnifying Glass" icon, invokes the
Control-C compiler — Forth programmers do not need to use it because Forth
programs are compiled as they are downloaded onto the QVGA Controller. The Compile Tool, designated by the "Debug" or "Magnifying
Glass" icon, invokes the compiler and assembler only — it does not produce
downloadable code. Use it to quickly check the syntax of a program and find
compilation errors without performing the full build which would be needed to
download the program into the microcontroller.
The 'Single-Page Make Tool' Compiles and Makes a
Downloadable Single-Page C Program
The Single-Page
Make Tool, designated by the "Make" or "Single-Page C" icon is also for C
programmers. It controls how C programs are linked and stored in memory.
Forth programmers do not need to use it. The
Single-Page Make Tool, designated by the "Make" or "Single-Page C" icon,
performs a standard, single-page memory model build of your program. If you
are compiling a program whose compiled size does not exceed 32 Kbytes of memory
you should use this mode for fastest execution. Although this program size is
sufficient for many applications, you may need to use the multi-page build if
your application grows beyond 32 K. If you see the following warning printed
in the compiler output, then you must switch to the multi-page memory model
build (Multi-page C icon):
WARNING: Input section
".doubleword" from 'progname.o11' is not used !
The 'Multi-Page Make Tool' Compiles and Makes a
Downloadable Long C Program
Forth
programmers will also not need the Multi-Page Make Tool, designated by the
"Multi-Page C" icon, which assists in compiling long C programs. The Multi-Page Make Tool, designated by the "Multi-Page C"
icon, invokes the C compiler's multi-page build mode. Programs compiled in
this mode may be many pages in length limited only by the amount of FLASH
installed in the QVGA Controller. It is always a good programming practice to
break large projects into multiple smaller source code files for organization,
and the multi-page build also uses this organization for distributing the
compiled program across multiple 32 Kbyte pages. Thus, no source code file may
contain more than 32 Kbytes worth of compiled source code or the above warning will
be issued and the program will not run. A more detailed description of this
behavior is available in Chapter 3.
You may wonder why there are
both "Multi-Page" and "Single Page" compile modes. The reason is that C
function calls between pages take just a little longer to execute (calls to
functions on a different page take 49 microseconds while those on the same page
or to common memory take only 11.5 or 13.75 microseconds, respectively).
Because most function calls are to functions on the same page or to common
memory page changes are rare; the average execution speed of multi-page C applications
is still quite fast.
The 'Mosaic Terminal' Communicates with Your Product
The Terminal icon
launches the communications program, Mosaic Terminal. When you launch
Mosaic Terminal for the first time, check the communications settings
(Settings→Comm) to verify that the serial port is set correctly for your
computer. Even if your QVGA Controller is running a demo program it will still
respond to the terminal if the communications settings are correct.
Now that we've learned about the
QVGA Controller's hardware, established serial communications, and installed
the Mosaic IDE on the PC, it's time to compile, download and execute a C
program. We'll also explore the QVGA Controller's on-board operating system
and use it to interactively debug a program.
In this section we'll be running
a simple program that does some computation and communicates its results on the
serial port. The program is one of several examples for use with the Control-C
IDE in the "\Mosaic\C_Examples" directory. Let's compile, download, and run
it.
Start TextPad, and open the
source-code file "getstart.c" from the C_Examples directory. You should see
the source code in an open window — browse through it to get a feel for it.
You'll see that the final routine in the file is called main(); it's the top-level, executable program. The first
routine within main() that is called when
the program starts is InitVars(). Note
that in the run-from-place applications of embedded systems it's important to
initialize all variables with a run-time procedure when the program starts.
Variables that are initialized when the program is compiled are not automatically
initialized when the program runs; you should have a runtime routine in your
code that does that.
Clicking on the Single-Page C
icon will compile and produce a downloadable form of the program, named
"getstart.dlf". A new window named 'Command Results' will appear so that you
can watch the compilation process. When compilation has finished, you can
scroll the Command Results window up and look for warnings, which don't prevent
creating a valid download file, and errors, which do. You should see two warnings
near the beginning:
GETSTART.C(126): Warning:
Expression is always TRUE !
GETSTART.C(197): Warning:
Symbol 'unused_variable' is never used in function 'main' !
We deliberately inserted into
'main' a variable named unused_variable that is never used in the function. If you double click
on an error or warning line in the command results, TextPad will jump to the
corresponding line in the affected source file. Despite the warnings, the
program should have compiled successfully; the command results will end with:
Qcc-> Creating QED
Download File: getstart.dlf
Tool Completed
Successfully
You can quickly switch between
the Command Results window and your source code file either by hitting
Ctrl-Tab, or by clicking on the file tabs at the bottom of the TextPad window.
The file named "getstart.dlf" is
ready to be downloaded to the microcontroller using the Mosaic Terminal
program.
If it is not already open,
launch Mosaic Terminal either from the 'Start' menu or using the TextPad
toolbar or dropdown menu. It's most convenient to use the Terminal icon on the
TextPad toolbar. If the factory demo is still running on your QVGA Controller,
use the special cleanup procedure as outlined above. A special cleanup
is performed by simply turning dip switch number 6 on, power cycling the QVGA
or resetting it by toggling switch 7, and turning dip switch number 6 back
off. This procedure causes the microcontroller to disable the automatic start
up, but does not delete the GUI Toolkit demo program from FLASH. Now, you
should be able to hit enter at the Mosaic Terminal prompt and see the 'ok'
response with the microcontroller plugged in and turned on. If this is not the
case, check your communications settings and cabling.
Now, select 'File → Send File'
from the Mosaic Terminal menu and enter the
"\c_examples" directory, or wherever you compiled the program. Set the file
type to "Text Files (*.dlf)" and select "getstart.dlf". You will see various commands and hex data scrolling on
the screen as the file is downloaded to the microcontroller. When the download
is complete, the text will turn from gray to green to indicate that it is
finished. Now, it's time to run your program.
To execute the top level
function of your code, simply type 'main' and press enter,
main
The getstart program will
respond with:
Starting condition:
The radius is 0; the
circular area is 0.
ok
While on its face that doesn't
seem a very impressive response, you're running your first program! This
particular example program uses multitasking. The program runs a background
task called CalculationTask
continuously, incrementing a radius variable and using it to compute a new
area. The program is running in its own task, leaving the communications task
free so you can continue to interact with the controller.
You will notice that you can hit
enter, and use the interactive debugging capabilities discussed in Getting
Started with the Control-C Programming Language even though the program is
still running. For example, try executing the following function interactively
from the terminal:
Announce( )
Note that you must type the
space after the ( character. Each time
you execute this function you'll notice that the output is different, as the
radius is being continuously incremented by the background task. Now try
executing,
Nap( )
which puts the background
CalculationTask ASLEEP. If you again execute
Announce( )
several times, you will notice
that the radius and area are no longer being updated by the CalculationTask. To wake up the CalculationTask again, type
Wakeup( )
and notice that the calculation
is again being performed by the task.
You may want to stop the
program; in particular you'll need to stop it before attempting any new
downloads. This can be done most easily by simply entering 'warm' at the microcontroller's prompt. The warm restart causes
a soft reset to occur, terminating any background tasks that may be running.
After having run this program,
you may want to play with the other example programs in the "\C_Examples"
directory. We strongly recommend that you compile these programs and work
through the examples as suggested in the text of this manual. This will
provide you with a thorough "hands-on" introduction to the Control-C
programming environment.
We have seen how to
interactively call the main() function from
the terminal to execute our entire program; most C development environments let
you do this. But the QVGA Controller's operating system makes it easy to
interactively execute any designated function in your program. By simply
preceding a function definition or prototype with the _Q keyword (we chose "_Q"
as a unique keyword that suggests QED), you can ensure that the function
will be interactively callable from your terminal.
An example: Announce( )
Displays an Area and Radius
For example, to display a
summary of the current values of the radius and calculated circular area
variables, we would like to call the function Announce( ).
Using the editor, look near the
top of the GETSTART.C file and you'll
see that its definition is:
_Q void Announce(void)
{ printf("\nThe radius is %6u; the circular area is
%5.4g.\n",radius,area);
}
The void keywords indicate that the Announce( ) function does not return a value, and does not expect any
input parameters to be passed to it.
The _Q declarator instructs the Make Tool that we want to be able
to interactively call this function using the on-board QED-Forth interpreter.
The names and execution addresses of all functions defined with the _Q designator are placed in the .DLF
download file so that QED-Forth will recognize them and will be able to
interactively execute them.
The printf() function invoked in Announce( ) prints the specified string to the serial1 port. The
parameters of the printf() function are well
defined by the ANSI standard, and are described in many excellent texts.
Briefly, the \n is an escape sequence
that instructs printf to insert a newline
at the designated places in the string. The %
characters are formatting symbols that tell the compiler to substitute the
listed arguments (in this case, the radius and area) for the % sequences at runtime. The %6u
sequence tells the compiler to display the radius as an unsigned decimal number
with a minimum field width of 6. The %5.4g
sequence tells the compiler to display the area using either decimal or
exponential notation with a precision of 4 decimal places to the right of the
decimal point, and a minimum field width of 5.
The printf() function in Control-C differs from the ANSI standard in
one respect: the maximum length of a printed string is limited to 80 characters
instead of the standard 255 characters. This limitation also applies to the
related functions named sprintf() (which
writes a string to a buffer) and scanf() (which inputs a string). Of course, you can handle strings
longer than 80 characters by using multiple calls to these functions.
Interactively Calling
Announce( )
To interactively call this
function, simply type at your terminal
Announce( )
followed by a carriage return
(indicated by the arrow above). Spaces are important to the QED-Forth
interpreter which processes this command; make sure that there is no space
between the function name Announce and the opening parenthesis (, and there
must be at least one space after the opening parenthesis. If QED-Forth
does not recognize your command, it will repeat what you typed followed by a "?" character and wait for another command, so you can try
again. The case of the letters does not matter: you can use all uppercase, all
lowercase, or any combination when typing commands for the QED-Forth
interpreter.
After calling Announce( ), you should now see the message
The radius is 0; the
circular area is 0.
on your screen, except that the
printed values of the radius and area will correspond to the values they had
when you executed the "WARM" command to stop
the calculations. Then you will see an additional line of text starting with "Rtn:" that summarizes the return value of the function in several
formats, followed by the "ok"
prompt. Because the Announce() function has no
return value, the return value summary is not relevant. The "ok" prompt indicates that QED-Forth has successfully called
the function and is now ready to execute another command.
If you make a mistake while
typing a command, just type "backspace" or "delete" to erase as many characters
as necessary on the current line. Once you've typed a carriage return, though,
QED-Forth executes your command. You can't edit a command that was entered on
a previous line. If you type an incorrect command and then type a carriage
return, you may receive the "?"
error message which means that QED-Forth does not understand the command you
typed. If this happens, you can usually just re-type the command line and
continue.
Area Calculation
The next function defined in GETSTART.C is called IncrementRadius(). This simple function increments the radius variable, and
resets it to 0 when it exceeds the MAX_RADIUS constant. As described below, IncrementRadius() is called from the infinite loop in CalcForever(); this results in the radius taking on all integer values between
0 and 1000.
The next function defined in the
GETSTART.C file calculates the area of a circle; its definition is:
_Q float CalcArea(uint radius)
{ return PI * radius * radius;
}
As described above, the _Q designator flags this function as one that can be called
interactively. The "float" keyword declares that the function returns a
floating point value, and the parameter list tells us that the function expects
a single unsigned integer (uint) as its input.
(Note: uint and other useful type abbreviations and declarations are
defined in the TYPES.H header file in the \MOSAIC\FABIUS\INCLUDE\MOSAIC directory.)
To interactively test this
function with an input radius of 5, type at your terminal
CalcArea( int 5)
followed by a carriage return.
QED-Forth uses spaces as delimiters; consequently, you must type at least one
space after the ( character and after the "int"
keyword. You should see something like the following response at your
terminal:
Rtn: 17053 5242
=0x429D147A=fp: 78.54
This line summarizes the
returned value in several formats, including decimal or hexadecimal 16-bit
values, 32-bit hexadecimal, and floating point. Because the CalcArea() function returns a floating point (fp) value, the final number on the line, labeled
=fp: 78.54
is the relevant return value.
Indeed, 78.54 is the area of a circle that has the specified radius of 5. You
can execute the function with any integer input as the radius, and verify that
it returns the correct circular area. This capability enables interactive
testing of the function over its allowed range of input values. Such thorough
function-by-function testing of a program facilitates rapid development of
reliable programs.
In the next chapter the
interactive debugging process will be explored in more detail. You will learn
how to examine the values of static variables and Forth arrays, pass parameters
by value or by reference, generate hexadecimal and ascii dumps of memory
contents, and modify the contents stored in variables and Forth arrays.
Restrictions on the Use
of _Q
Nearly every function in the
many sample programs in the \MOSAIC\DEMOS_AND_DRIVERS\MISC\C EXAMPLES directory is declared with the _Q keyword to facilitate easy debugging. There are, however,
two restrictions associated with the use of the _Q
declarator.
First, a function defined using
the _Q keyword cannot use
...
(ellipsis) in its parameter
list; rather, the number of input parameters must be specified when the
function is defined. (If you try to define the _Q
function with an ellipsis as an input parameter, the compiler will issue a
warning and remove the _Q specifier,
so you will not be able to interactively call the function during debugging.)
The second restriction is that
the _Q function cannot be called via a function pointer if the
function accepts input parameters. In other words, do not use the _Q declarator if:
a. You need to call the
function using a function pointer; and,
b. The function accepts
input parameters.
To see an example in which the _Q keyword cannot be used, open the sample program named GRPHEXT.C in the \MOSAIC\DEMOS_AND_DRIVERS\MISC\C EXAMPLES directory and search for the function named SetTheBits.
This restriction does not affect
many functions. Any function declared using _Q
can always be called in the standard way (that is, by invoking the function
name followed by parentheses that contain any input parameters). Moreover, any
_Q function can be called indirectly via a function pointer
(by passing its name without any parentheses) if the function's input parameter
list is "void".
The QVGA Controller's onboard
operating system, called QED-Forth, provides numerous run-time services,
including providing a heap memory manager. Using this memory manager we can
access the controller's extended memory.
8 Megabyte Addressable
Memory Space
The standard 68HC11 processor
can address 64 kilobytes of memory using 16-bit addressing. The QVGA
Controller expands the address space to 8 Megabytes, addressing the lower 32
Kbytes of the processor's memory space by means of an 8-bit "Page Latch" that
selects one of 256 pages. The 256 pages times 32 Kbytes per page yields 8
Megabytes of addressable memory. The upper 32 Kbytes of the 68HC11's address
space is called the "common memory". This address space is always accessible,
regardless of the contents of the Page Latch.
Available Common RAM
The ANSI C compiler supports the
standard 16-bit addressing scheme via its small memory model. It also
supports a medium memory model that allows functions to be called on any
specified page using a 24-bit address (16-bit standard address plus an 8-bit
page). All C variables and C arrays, however, must be accessible using a
simple 16-bit address. For practical purposes, this means that variables and C
arrays must reside in the QVGA Controller's available 8 kilobytes of available
common RAM located at addresses 0x8E00 to 0xADFF. In multitasking
applications, this RAM is also used for task areas; each task requires 1 Kbyte
of common RAM area.
You are of course free to use
ANSI-standard C arrays located in the variable area in common RAM. These
arrays allow you to use standard C pointer arithmetic, and their use is
explained in all of the C textbooks. However, if you need to store a lot
of data, the available 8K of common RAM may not be sufficient. But don't worry
— you can still use all the memory.
Built-in Array Library
Routines Manage Access to Paged Memory
The FORTH_ARRAY routines that reside in ROM on the QVGA Controller provide
an efficient means of accessing the large paged address space for storage of
data. The pre-defined DIM() macro makes it
easy to dimension a 2-dimensional array to hold signed or unsigned characters,
integers, longs, or floating point values. Other pre-defined library functions
handle storing, fetching, and copying data to and from the arrays. These
QED-Forth functions are callable from C, and provide access to a large contiguous
memory space that is very useful for real-time data storage and analysis.
Each array is referred to using
a named 16-bit pointer to a "parameter field" structure in common RAM. Once
the array has been "dimensioned", this structure holds the number of rows and
columns, data size, and a pointer to the QED-Forth heap where the array is
allocated. The ROM-resident heap manager allocates and deletes the arrays in
real time under the control of the C program, thereby maximizing the effective
use of available paged RAM.
This section introduces the use
of the arrays, and as we'll see in a late Chapter they are very useful for storing
data from the QVGA Controller's A/D convertors. The header file named ARRAY.H in the \MOSAIC\FABIUS\INCLUDE\MOSAIC directory contains all of the function and macro definitions
that are used to access Forth arrays, including the DIM(), FARRAYFETCH() and FARRAYSTORE() macros that are mentioned in this section.
Declaring and
Dimensioning a FORTH ARRAY
Let's look at the example code
in the GETSTART.C file. Approximately 1/3 of the way into the file, you'll
find a section called "Array Dimensioning, Storing and Fetching". The first
command in this section is:
FORTH_ARRAY circle_parameters;
which declares a new FORTH_ARRAY named circle_parameters and allocates storage for the
structure in the variable area in common RAM. FORTH_ARRAY
is a struct
typedef (see the ARRAY.H file) that specifies how the dimensioning information for
the array is to be stored. Whenever we want to call a function to operate on
this array, we will pass the pointer
&circle_parameters
as an argument to the function.
After using #define directives to define some dimensioning constants, we
encounter the following function definition:
_Q void DimAndInitFPArray(float value,int rows,int
cols,FORTH_ARRAY* array_ptr)
{ int r,c;
DIM(float, rows, cols, array_ptr); //
dimension; allocate in heap
for(c = 0; c < cols; c++) // for each
column
for(r=0; r< rows; r++) // for each row
FARRAYSTORE(value,r,c,array_ptr); // store
in array
}
The function dimensions a FORTH_ARRAY and initializes all elements of the array to have a
specified floating point value. The inputs are the floating point value, the
number of rows and columns, and a pointer to the FORTH_ARRAY
structure in common memory. After declaring the automatic variables r and c, the DIM() macro is invoked to emplace the dimensioning information
in the FORTH_ARRAY structure, and allocate memory for the array in the heap.
The first parameter expected by DIM() is a type specifier; type definitions and abbreviations
are defined in the TYPES.H file in the \MOSAIC\FABIUS\INCLUDE\MOSAIC directory. Valid type arguments for DIM() include the following:
char unsigned char uchar
int unsigned int uint
long unsigned long ulong
float xaddr
The next two input parameters
expected by DIM() are the number of
rows and columns, and the final input parameter is a pointer to the FORTH_ARRAY structure. The nested for()
statements cycle through each row and column element in the array, calling the
macro FARRAYSTORE() to store the specified value into the array element. FARRAYSTORE() expects a floating point value, row and column indices,
and a pointer to the FORTH_ARRAY
as its inputs.
The starting "F" in the name FARRAYSTORE() means "floating point"; a parallel macro named ARRAYSTORE() is used for arrays that contain signed or unsigned char, int, or long data.
The SaveCircleParameters() function in the GETSTART.C file calls the macro FARRAYSTORE() to store the radius and area as floating point values in
their respective columns of the circle_parameters array. Then it increments the row_index variable, handling overflow by resetting the row_index to zero to implement a circular storage buffer.
The next function in GETSTART.C is called PrintFPArray() which prints an array of floating point values to the
terminal. Its definition is as follows:
_Q void PrintFPArray(FORTH_ARRAY* array_ptr)
{ int r, c;
putchar('\n');
for (r = 0; r < NUMROWS(array_ptr); r++)
// for each row
{ for (c = 0;c < NUMCOLUMNS(array_ptr);c++)
// for each col
printf("%9.4g ",FARRAYFETCH(float,r,c,array_ptr));
// min field width=9;precision=4;g=exp or decimal
notation
putchar('\n'); // newline after each row is printed
PauseOnKey(); // implement xon/xoff output flow control
}
}
As usual, the _Q declarator allows this function to be called interactively from the
terminal. PrintFPArray() expects a
pointer to a FORTH_ARRAY
as its input parameter, and uses 2 nested for()
statements to print the contents of the array one row at a time.
The printf() statement invokes the Forth library macro FARRAYFETCH() to fetch the contents of the array at the specified row
and column. FARRAYFETCH() returns the
value stored in the array; it expects a type specifier (used to cast the return
value to the required type), row and column indices, and a pointer to the FORTH_ARRAY as its inputs.
The %9.4g argument to printf() specifies that the number should be printed using either
decimal or exponential formatting (whichever displays better precision), with 4
digits to the right of the decimal point and a minimum field width of 9
characters. The putchar('\n') statement
inserts a newline character after each row is printed. The PauseOnKey() function is a handy library routine that serves 2
purposes:
It implements XON/XOFF output flow control to avoid
"inundating" the terminal with characters faster than the terminal can process
them, and
It allows the user to abort the printout by typing a carriage
return from the terminal.
For further details, please
consult the definition of PauseOnKey() in the
Control-C Glossary.
To see how the DimAndInitFPArray() function is called, scroll down to the function named CalcForever() in the GETSTART.C file. The first statement in the function is:
DimAndInitFPArray(0.0,CIRCLE_ROWS,CIRCLE_COLUMNS,&circle_parameters);
where 0.0 is the floating point
value to be stored in each element, the constants CIRCLE_ROWS
and CIRCLE_COLUMNS specify the number of rows and columns in the array, and &circle_parameters is a pointer to the FORTH_ARRAY.
Interactively Dimension,
Initialize and Print the Array
It is easy to interactively call
the functions that we've examined. The syntax that we'll type at the terminal
looks similar to an ANSI C function prototype, with one of the following type
declarators being used before input parameters that are passed by value:
char int long float
char* int* long* float*
When passing the address of a
variable or a structure, use only the name of the variable or structure,
without any additional declarators or &
operators. All of this is explained in detail in a later Chapter;
for now, the goal is see how easy it is to use the interactive function calling
tools.
For example, to interactively
dimension and initialize the circle_parameters array to have 10 rows, 2 columns, with each element
initialized to a value of 34.56, type the following line at your terminal:
DimAndInitFPArray( float
34.56,int 10,int 2,circle_parameters)
Remember to type at least one
space after the ( character, and after the float and int keywords.
QED-Forth will respond to your command with a line of text that summarizes the
return value of the function, followed by the "ok"
prompt. We can ignore the return value summary, because this function does not
return a value.
Now to verify that the
initialization was performed correctly, we can type at the terminal:
PrintFPArray( circle_parameters)
and, as always, we make sure
that there is a space after the ( character. Note that we do not use the & (address-of) operator before the circle_parameters argument; it turns out that circle_parameters has already been defined in QED-Forth as the base address
of the FORTH_ARRAY structure.
QED-Forth calls the function
which prints the contents of the circle_parameters array, and then summarizes the return information (which
we can ignore in this case). You can verify that the value of each array
element is the same one that you specified when you called the DimAndInitFPArray() function. (Slight differences in the values are due to
rounding errors in the floating point conversion and printing routines.) Using
this interactive method, you can test each function with a variety of
dimensioning and initialization information.
Many instrumentation and
automation applications can be logically conceived of in terms of a set of
distinct "tasks" that cooperate to solve the problem at hand. For example, a
program that manages a hand-held sensing instrument might have one task that
acquires sensory data, another that performs calculations to process the data,
and a third task that displays the results on a liquid crystal display.
Using the QVGA Controller's
built-in multitasking executive confers significant advantages when designing
real-time systems. Breaking up a complex program into easily understood
modular tasks speeds debugging, improves maintainability, and prevents source
code modifications of one task from adversely affecting the required real-time
performance of another task.
The Task Activation
Routine
In a multitasking environment, a
"task" is an environment capable of running a program. After declaring
(naming) a new task (which also allocates a 1 Kbyte task area), its environment
is "built" by initializing its required stacks, buffers and pointers in the 1
Kbyte task area. Then the task is "activated" by associating it with an
"activation routine" that performs a specified set of actions.
A typical task activation
routine is the CalcForever() function in
the GETSTART.C file. Its definition is straightforward:
_Q void CalcForever(void)
// this infinite loop function can be used as a task activation
routine
{ DimAndInitFPArray(0.0,CIRCLE_ROWS,CIRCLE_COLUMNS,&circle_parameters);
while(1) // infinite loop
{ IncrementRadius(); // updates radius variable
area = CalcArea(radius); // updates area variable
if(radius%10 == 0) // on even multiples of 10...
SaveCircleParameters(); // save data in FORTH_ARRAY
Pause(); // give other tasks a chance to
run
}
}
The first thing that this
function does is to dimension and initialize the circle_parameters array. Then it enters an infinite loop that increments
the radius variable, calculates the corresponding circular area and stores it
in the area variable, and saves the radius and area in the circle_parameters array if the radius is an even multiple of 10. The
function calls Pause() on every pass
through the loop. Pause() is a multitasking
function that instructs the multitasking executive to change to the next task
(if any) in the round-robin task list. This enables "cooperative
multitasking", in which a task willingly lets other tasks run by executing Pause(). The other type of multitasking, also supported by the
QVGA Controller, is "pre-emptive multitasking", in which an interrupt-driven
timeslice clock forces a task switch on a periodic basis.
In summary, the CalcForever() function is an infinite loop that manages the calculation
and storage of the radius and circular area. This function can be the
"activation routine" for a stand-alone task running in a multitasking
environment.
Declare, Build and
Activate a Task
The short section titled
"Multitasking" in the GETSTART.C file
demonstrates how easy it is to set up a task using the pre-defined macros.
First we declare the new task as:
TASK CalculationTask;
The TASK typedef allocates a 1 Kbyte task structure named CalculationTask in the common RAM.
The function SetupTask() builds and activates the new task; its definition is:
void SetupTask()
{ NEXT_TASK = TASKBASE; //
empty task loop before building
BUILD_C_TASK(HEAP_START,HEAP_END,&CalculationTask);
// private heap
ACTIVATE(CalcForever, &CalculationTask); // define
task's activity
}
The first statement empties the
round-robin task loop by setting the NEXT_TASK
pointer in the task's user area to point to the task's own TASKBASE. The next statement invokes the BUILD_C_TASK() macro which expects starting and ending addresses for the
task's heap, and the address at which the task is located. We have defined the
constants HEAP_START
and HEAP_END to specify a task-private heap occupying a full 32 Kbytes
on page 7. The task base address is simply &CalculationTask. BUILD_C_TASK() sets up all of the stacks, buffers and pointers required by the task.
The final statement in SetupTask() invokes the ACTIVATE() macro which expects a pointer to the activation function
(which is CalcForever) and the TASKBASE address (which is &CalculationTask).
Multiple tasks can be declared,
built and activated in the same way.
Putting a Task Asleep
A "sleeping" task remains in the
round-robin task loop, but is not entered by the multitasking executive. The
status of a task can be changed from AWAKE
to ASLEEP and back again by simply storing the appropriate constant
in the user_status variable in the task's USER_AREA.
The USER_AREA is a task-private structure initialized by BUILD_C_TASK() that contains the pointers that a task needs to operate;
it is defined in the USER.H file in the \MOSAIC\FABIUS\INCLUDE\MOSAIC directory. The USER_AREA
structure is the first element in the TASK
structure.
The Nap() function in GETSTART.C is a simple function that puts the CalculationTask asleep:
_Q void Nap(void) // put calculation task asleep
{ CalculationTask.USER_AREA.user_status =
ASLEEP;
}
This function simply stores the ASLEEP constant into the user_status variable in the CalculationTask's USER_AREA structure. A similar function named Wakeup() stores the AWAKE
constant into user_status to wake up the task. We'll see how to use these functions in the next
section.
The main Function Gets Us
Going
The main() function is the highest level routine in the program. Its
definition is:
void main(void)
// Print starting area and radius, build and activate
CalculationTask.
{ int unused_variable; // an example of how warnings
are handled!
InitVars();
printf("\nStarting condition:");
Announce(); // print starting values of radius and
area
SetupTask(); // build and activate the CalculationTask
}
As you recall, the declaration
of the unused_variable was inserted to demonstrate how the Control-C IDE
highlights the source code line associated with compiler errors and warnings. InitVars() performs a runtime initialization of the variables used by
the program; this is very important, because compile-time initializations won't
ensure that variables are properly initialized after the program has run once,
or after the processor is restarted.
After initializing the
variables, main() announces the
starting values of radius and area and then calls SetupTask() to build and activate the CalculationTask. To execute the program, simply type at your terminal:
main
You'll see the following
message:
Starting condition:
The radius is 0; the
circular area is 0.
ok
The "ok" prompt lets you know that QED-Forth is ready to accept
more commands. We have set up a two-task application: the default startup task
(named the FORTH_TASK)
is still running the QED-Forth interpreter, and the CalculationTask that we built is running the CalcForever() activation routine. At any time we can monitor the
current values of radius and area by interactively calling the function:
Announce( )
Remember to type a space after
the ( character, and you may have to type slowly so that the multitasking
program does not miss any of the incoming characters from the terminal. To
view the contents of the circular buffer array named circle_parameters, type the command:
PrintFPArray( circle_parameters)
To suspend the operation of the CalculationTask, type:
Nap( )
Now notice that successive
invocations of:
Announce( )
all show the same values of the
radius and area; this is because the CalculationTask is no longer updating them. To re-awaken the CalculationTask, simply type:
Wakeup( )
To abort the multitasking
program altogether and return to a single task running the QED-Forth monitor,
you can perform a "warm" restart by typing:
WARM
The QED-Forth startup message
will be displayed.
Of course, if you want to run
the program again, you can type main or any of the interactively callable
function names at any time. Remember to type
WARM
or
COLD
before trying to download
another program file; the QVGA Controller can't run multiple tasks and accept a
download file at the same time. (Both WARM
and COLD re-initialize the system, but COLD performs a more thorough initialization and causes
QED-Forth to immediately "forget" the definitions of the C functions that were
sent over in the .DLF download file).
Now you've worked through the GETSTART.C program in detail. You know how to compile, download and
execute programs, perform simple floating point calculations, print formatted
strings and numbers to the terminal, dimension and access FORTH_ARRAYs in paged memory, and define a multitasking application
with an interactive terminal interface. That's pretty good considering that
this is your first C program on the QVGA Controller!
<< Previous | Next>>
An
Introduction to Programming in QED-Forth
The QVGA
Controller accepts serial commands from a terminal which is typically a
personal computer (PC). QED-Forth's onboard interpreter, compiler, and
assembler compiles your program into executable code in the QVGA Controller's
memory, and the code can be immediately executed by simply stating the name of
a routine. This encourages a productive iterative programming style.
If you wanted, you
could program a QVGA Controller application by typing in your application
program one line at a time from a terminal. For all but the shortest programs,
this would be a tedious process. Fortunately, there is a better way. All you
need is Mosaic IDE. The IDE's text editor allows you to create and modify text
files and save them on the computer's disk drive. Its terminal program allows
you to send and receive characters via the PC's RS232 serial port to
communicate with the QVGA Controller. Thus you can rapidly edit your source
code into files which are downloaded to the QVGA Controller. The Mosaic IDE
allows you to switch between the editor and terminal windows quickly and
easily.
How To Send
Your Programs to the QED Board
To
send a program to the QED Board, edit the "source code" (that is, the
QED-Forth commands and any relevant comment statements) into a text file and
save the file on the PC's disk. Then use the "file transfer" or
"text transfer" feature of the terminal program to send the program
file to the QED Board. If you discover errors while debugging the program,
simply edit the file and re-transmit all or part of it to the QED Board. If
your file contains the ANEW command (as described in a later Chapter of this manual), the old erroneous version of your program will be automatically forgotten by the QED board when
the new version is transmitted to the board.
You
have complete flexibility in programming the QED Board. You can send programs
to the board using the file transfer method, and you can also use the terminal
interactively to compile and execute short commands or manage debugging operations.
Mosaic Terminal also allows you to record your debugging session as a text
file. This can be a useful technique; you can later edit the recorded file to
save the most worthwhile aspects of the debugging session.
You download and
develop your programs in RAM. Once they are sufficiently debugged you can then
transfer them to Flash memory for permanent storage. QED-Forth programs are
generally not relocatable — they must execute from the same addresses they were
compiled to. But if they are transferred from RAM to Flash how is this
possible? The solution is to swap the RAM and Flash addresses. Programs are
downloaded and compiled into RAM using a download memory map,
transferred to Flash, then the memory map is changed to the run-time (or
standard) memory map placing the Flash memory at the same addresses that
the RAM had been located. The program then executes from the Flash just as it
had from the RAM.
Let's go through
an example of the steps required to compile and execute a QED-Forth program on the
QVGA Controller. We'll use the RAM on memory pages 1-3 and the Flash on pages
4-6 of the standard memory map.
2. First
establish the download memory map by executing:
DOWNLOAD.MAP
This command
can be typed interactively from the terminal program, or can be the first statement
in your download file. This swaps the RAM and Flash, placing the RAM on pages
4-6 and the Flash on pages 1-3.
2. Establish memory
locations for code and names on pages 4, 5, and 6. One valid way to do this is
by executing,
4 USE.PAGE
which is
equivalent to the following set of commands:
HEX
0000 04 DP X! \ code
starts at address 0 on page 4; 20 Kbytes available
5000 04 NP X! \ names
start at address 5000 on page 4; 12 Kbytes available
8E00 00 VP X! \
variables start at address 8E00 in common RAM
4A00 0F 7FFF 0F
IS.HEAP \ 13.5 Kbyte heap at addresses 4A00-7FFF on page F
Typically, the
USE.PAGE command or other equivalent commands are the first commands in the
source code file. To customize the locations of these memory areas, you can
include edited versions of these commands in your source code file. Make sure
these commands come before the first ANEW statement in your code. For example, to
locate up to 32 Kbytes of code on page 4, and up to 32 Kbytes of names on page
5, you can execute:
HEX
04 USE.PAGE \ set
default use.page locations
0000 05 NP X! \
move names section to page 5, leaving all of page 4 for code.
or,
equivalently,
HEX
0000 04 DP X!
\ code starts at address 0 on page 4; 32 Kbytes available
0000 05 NP X!
\ names start at address 0000 on page 5; 32 Kbytes available
8E00 00 VP X!
\ variables start at address 8E00 in common RAM
4A00 0F 7FFF 0F IS.HEAP
\ 13.5 Kbyte heap at addresses 4A00-7FFF on page F
2. Send your
code to the QED Board. Typically, the first statement of the code is an ANEW command which serves as a "forget marker" to
simplify re-downloading of code. For example:
ANEW MY.CODE
\ choose any name you want here
: HELLO.WORLD ( -- )
\ define a new routine called HELLO.WORLD
CR ." Hi Everyone!"
\ print a message to the terminal
; \ end the
definition
\ < all of your
source code goes here >
2. After
successfully compiling your code, transfer the code and names to flash using
the PAGE.TO.FLASH routine. For example, if all of your code and names are located on page
4, simply execute:
04 PAGE.TO.FLASH \
transfers page 4 RAM to page 1 in flash
If your code is
on page 4 and your names are on page 5, you would execute
04 PAGE.TO.FLASH \
transfers page 4 RAM to page 1 in flash
05 PAGE.TO.FLASH \
transfers page 5 RAM to page 2 in flash
The routine PAGE.TO.FLASH will print an error message if you pass it
an illegal page, or if the flash cannot be programmed (for example, if the
flash is write protected because DIP switch 2 is OFF). The page parameter
passed to the routine must be the source page in RAM where the code has been
compiled. PAGE.TO.FLASH transfers all 32 Kbytes of the source page to the parallel
page in flash memory. At this point two copies of your code and names exist in
memory: one copy is in RAM, and the other resides in flash. But only the RAM
version can be executed properly, because it resides at the same pages at which
it was compiled. The next step makes the flash-resident copy of the code
executable.
2. Re-establish
the standard memory map by executing
STANDARD.MAP
from your
terminal. This remaps pages 4, 5, and 6 so that they are addressed in the
Flash device, and maps pages 1, 2, and 3 to be addressable in the RAM. At this
point, the Flash-resident code (typically on page 4, which is now in Flash) can
be executed. The RAM in pages 1, 2, and 3 are available; they can be written
over or used by the application program.
2. Run the
program by executing any of the names that have been defined. You can also
execute an autostart command to cause a specified function to be automatically
called upon each restart. To place the autostart vector in EEPROM inside the
processor, execute the command:
CFA.FOR <name>
AUTOSTART
where
<name> is the name of the designated function. To place the autostart
vector in Flash memory, execute the command:
CFA.FOR <name>
PRIORITY.AUTOSTART
For most
applications, the PRIORITY.AUTOSTART option is preferable because it locates
the autostart vector with the code in the flash memory device.
2. (OPTIONAL)
To download an additional source code file after the prior 6 steps have been
executed, you need to transfer the code back to RAM, establish the download
map, download the next source code file, re-program the flash, and re-establish
the standard map. Assuming you left off after step 6, you would execute the
following:
NO.AUTOSTART \ if you
installed an autostart, undo it
4 PAGE.TO.RAM \ move
code from page 4 in flash to page 1 in RAM
5 PAGE.TO.RAM \ (only
needed if page 5 used); move page 5 flash to page 2 RAM
DOWNLOAD.MAP \ get
ready for download
<send your source
code file here>
4 PAGE.TO.FLASH \
copy updated page 4 code to flash
5 PAGE.TO.FLASH \
copy updated page 5 code to flash
STANDARD.MAP \
code is now executable at pages 4 and 5 in Flash
CFA.FOR <name>
PRIORITY.AUTOSTART \ optional
FORTH
EXAMPLE SESSION
Here's a
transcript of an entire session to show how easy it is to compile a Forth
program into flash. The program, HELLO.WORLD, prints a simple message to the terminal:
DOWNLOAD.MAP
4 USE.PAGE
ANEW MY.CODE
: HELLO.WORLD ( -- )
CR ." Hi Everyone!" ;
4 PAGE.TO.FLASH
STANDARD.MAP
HELLO.WORLD\ executes
the program!
You've compiled
your program, downloaded it into RAM, transferred it to flash, and executed
it. If you want to set up an autostart routine, follow the procedure as
explained above, or consult the appropriate chapter of this manual.
Initializing
the Memory Map
Before downloading
a new program you'll want to make sure that you're starting with a clean slate
from a known initialized condition. To do so, type the command,
COLD
followed by
a carriage return. QED-Forth executes the command as soon as it receives the
carriage return. The COLD command tells QED-Forth to perform a cold restart, initializing
the system to a known state and causing it to FORGET all user-added words. QED-Forth responds
with its cold startup message:
Coldstart
QED-Forth
V4.xx
which tells
you the version number of the software on your board.
QED-Forth doesn't
care whether you use capital letters, small letters, or a combination of both
when you type a command or function name. It automatically performs case
conversion so that COLD, Cold, and even CoLd are treated as the same command.
To set up a memory
map that gives plenty of room for programming type the command
4 USE.PAGE
ANEW MY.ROUTINES
Be sure to type at
least one space between the words as shown above (for example, between the 4
and USE.PAGE).
The USE.PAGE
command configures the memory map so that the definitions of new words reside
on page 4 of the memory. The ANEW command followed by a name of your choice
creates a marker which facilitates re-loading your code during program
development. It is good policy to execute an ANEW command after setting the memory map
pointers.
If a bug or error
causes the COLDSTART message to be printed to your terminal while you are programming,
you should re-initialize the memory map with the USE.PAGE command. Consult another chapter
Output
Commands
First, let's have
QED-Forth print a statement. At your terminal, type
." Hi There!"
and QED-Forth
responds with
Hi There!
ok
We have underlined
QED-Forth's response for clarity. The command ." (pronounced dot-quote) tells QED-Forth to
print the characters until the next " (quote) character is encountered. The
space after the ."
is important because FORTH uses spaces to separate commands. To instruct
QED-Forth to print the message on a new line, execute the FORTH command CR (carriage return) before the print command,
as
CR ." Hi There!"
and notice how
QED-Forth inserts an extra carriage return/linefeed before printing the message
at your terminal.
If you make a
mistake while typing a command, just type "backspace" or "delete" to erase as
many characters as necessary on the current line. Once you've typed a carriage
return, though, QED-Forth executes your command. You can't edit a command that
was entered on a previous line. If you type an incorrect command and then type
a carriage return, you may receive the "?" error message which means that
QED-Forth does not understand the command you typed. If this happens, you can
usually just re-type the command line and continue programming. Error messages
are discussed in more detail in the QED Software Manual.
The Stack
In FORTH,
parameters are passed among routines on a push-down data stack (referred to as
"the data stack" or "the parameter stack" or simply "the stack"), and nesting
of subroutine calls is handled by a separate "return stack". Most
microprocessors are designed to handle stacks, so this scheme results in high
efficiency. In addition, stack-based parameter passing is a single organizing
principle that unifies and simplifies FORTH syntax.
All arithmetic,
logical, I/O, and decision operations remove any required arguments from the
stack and leave the results on the stack. This leads to "postfix" notation:
the operation is stated after the data or operands are placed on the stack.
To see how this
works, put some numbers on the data stack by typing
5 7
and QED-Forth
responds
ok ( 2 ) \
5 \ 7
QED-Forth is
showing a picture of its data stack. The ( 2 ) means that there are two items on the
stack. Each of the items is listed, and items are separated by a \ character, which can be read as "under". So
we could describe the stack right now as 5 under 7; the 7 is on top of the
stack, and the 5 is under it. If there are more than 5 items on the stack, the
stack print displays the number of stack items and the values of the top 5
items.
The stack print
that shows what's on the stack is a feature of the debugging environment. To
disable the stack print, you could execute DEBUG OFF. It is not recommended that you do this,
though; it's very helpful to keep track of the items on the data stack while
developing your program.
To multiply the
numbers that are now on the stack, type the multiply operator which is a * character:
*
and QED-Forth
responds
ok ( 1 ) \
35
The operator
removes the two operands 5 and 7 from the stack, multiplies them, and puts the
result of the multiplication on the stack. To subtract 5 from the number on
the stack, type
5 -
which produces the
response
ok ( 1 ) \
30
The - (minus)
operator takes the 35 and the 5 from the stack, subtracts, and puts the result
on the data stack.
To print the
result to the terminal, we could simply type the printing word . (dot). A slightly fancier way to do it is:
CR ." The result is " .
which prints the
response
The result is
30 ok
The command CR causes the output to be printed on a new
line, then the specified message is typed, and then the printing word . removes
the 30 from the stack and prints it. The stack is now empty, so QED-Forth does
not print a stack picture after the ok.
Notice that
throughout this exercise QED-Forth has been interpreting and executing commands
immediately. This is because FORTH is "interactive". The results of executing
commands can be immediately determined. If they are incorrect, the command can
be changed to correct the problem. This leads to a rapid iterative debugging
process that speeds program development.
Defining
New Words in QED-Forth
Let's define a
routine (called a "word" in FORTH) that increments the contents of a variable.
To create a variable named COUNTER execute
VARIABLE COUNTER
To initialize COUNTER to zero, type
0 COUNTER !
This command first
puts a 0 on the stack, then stating COUNTER leaves the address of COUNTER on the stack, and ! (pronounced "store")
stores the 0 into the COUNTER. To verify the contents of COUNTER, type
COUNTER @ .
and QED-Forth
responds with
0 ok
Here, COUNTER leaves its address on the stack, and the @ (pronounced "fetch") operator removes the
address from the stack, fetches the contents of the address, and leaves the
contents on the data stack. The printing word . (dot) then prints the contents.
To define a word
to add 1 to the contents of COUNTER, type
: INC.COUNTER (
-- )
1 COUNTER
+!
;
You have just
compiled a definition of a new QED-Forth word. The : (colon) tells QED-Forth to append the next
name it encounters (in this case, INC.COUNTER) to the name area of the dictionary. The
comment between parentheses is a "stack picture"; in this case the empty stack
picture reminds us that INC.COUNTER does not expect any inputs and does not
return any outputs on the stack. The remaining commands until the terminating ; (semicolon) form the body of the
definition. The definition puts an increment (1) on the stack, puts the
address of COUNTER on the stack, and calls +! (plus-store) which removes the increment and
address, and adds the increment to the current contents of the address, thus
increasing the value of COUNTER by 1. After receiving the terminating ; QED-Forth responds with the "ok" prompt
indicating that it has compiled (i.e., entered into the dictionary) the
definition; it has not executed the definition yet. To verify this, we could
type COUNTER @ .
just as we did above. But let's type the equivalent expression
COUNTER ?
and note that
QED-Forth responds with
0 ok
which tells us
that the counter is still initialized to 0. Note that the command ? has been pre-defined in the QED-Forth kernel
as:
: ?
@ .
;
So you see that
useful words are built up as combinations of lower level words in FORTH.
Useful sequences of commands (such as @ . ) are defined as higher level words with
descriptive names, thus building a lexicon of convenient and powerful words.
QED-Forth already has many useful functions; you'll define more to address the
unique requirements of your application.
Now we can execute
INC.COUNTER
and again check the contents of COUNTER to see how it is affected. Type
INC.COUNTER
COUNTER ?
and QED-Forth
responds
1 ok
INC.COUNTER increments the value of the variable by 1. This command can be
repeated to show that it always increments the variable. We'll use INC.COUNTER in the next section to demonstrate the
multitasking capability of QED-Forth.
A
Definition Using Floating Point Math and Local Variables
Let's define a
more interesting word that uses floating point math to sum the inverses of the
integers from 1 to 100. The text after the \ character on each line of the definition is
commentary that is ignored by QED-Forth.
: SUM.OF.INVERSES ( --
)
\ prints the sum of
inverses of integers from 1 to 100
ZERO LOCALS{
f&accumulator } \ define and initialize local variable
101 1 \
put loop limit and index on stack
DO \ start loop
I FLOT
1/F \ calculate next inverse
f&accumulator F+ \ add it to accumulator
TO
f&accumulator \ update value in accumulator
LOOP
CR ." The sum
of the inverses is " \ announce answer
f&accumulator F. \ print answer
;
After entering
this definition, we can execute
SUM.OF.INVERSES
and QED-Forth
responds
The sum of
the inverses is 5.185 ok
The first line of
the word defines a "local variable" named f&accumulator and initializes it to 0.0. Because the name
of the local begins with "f&", QED-Forth recognizes that it is a floating
point (as opposed to an integer) quantity. The next line sets up a loop that
starts at 1 and stops after 100 is reached. The command I puts the current
loop index on the stack, FLOT (pronounced float) converts it to a floating
point number, 1/F inverts it, and then F+ adds it to the accumulator (floating point
math operators typically start with the letter F). The TO instruction updates the contents of the accumulator.
After the loop is finished, the print command announces the result, and F. (f-dot) prints the final value of the
accumulator. Notice how easy it is to program with floating point mathematics,
and how the use of the named local variable eliminates stack juggling and makes
the definition easy to read.
Multitasking
Let's set up a task
whose job it is to continually increment the variable COUNTER while we simultaneously
run the QED-Forth interpreter. Type in (or download a text file containing)
the following set of commands; their meaning and syntax will be explained in
detail in the "Multitasking" chapter in the QED Software Manual.
: COUNT.FOREVER ( --
)
BEGIN
INC.COUNTER
PAUSE
AGAIN
;
HEX
9600 0 TASK: MY.TASK
0\0 0\0 0\0 MY.TASK
BUILD.STANDARD.TASK
CFA.FOR COUNT.FOREVER
MY.TASK ACTIVATE
DECIMAL
You are now
running two concurrent tasks: one task is the QED-Forth interpreter that you've
been using all along to execute your commands, and the second is a task that
increments the 16-bit variable named COUNTER. To verify this, type COUNTER
? and you'll see some
random number that changes each time you type the command as the background
task continually updates the value.
Let's look at what
we've done. First we defined an infinite loop in the word
COUNT.FOREVER that
increments the counter and executes PAUSE. PAUSE is the task-switching word. The remaining
command lines define and build a new task called MY.TASK and activate it so that it performs the
function COUNT.FOREVER. The QED-Forth interpreter is still running, and it executes
the PAUSE
task-switch word while it has spare time; namely, when it is waiting for serial
input from the terminal. Each time QED-Forth executes PAUSE, MY.TASK increments the value of COUNTER and itself executes PAUSE to return control to the QED-Forth
interpreter. Control passes back and forth between the tasks so quickly that
it seems as if the two tasks are executing simultaneously. We could add more
tasks to the round robin task list, and we could set up a timeslice clock to
force more frequent task switching. Those topics are described in the
"Multitasking" chapter in the QED Software Manual.
Working
with Matrices
QED-Forth makes it
easy to work with matrices, which are defined as 2-dimensional arrays of
floating point numbers. To create two matrices named MY.MATRIX and DESTINATION, type
MATRIX:
MY.MATRIX
MATRIX:
DESTINATION
To refer to a
matrix as a whole, we use the ' (tick) operator in FORTH; it is the
apostrophe character on your keyboard. For example, to dimension the matrix to
have 2 rows and 3 columns, type
2 3 '
MY.MATRIX DIMMED
making sure to
separate all of the words by spaces. To load some data into the matrix, type
MATRIX
MY.MATRIX = 1. 2. 3. 4. 5. 6.
(The decimal
points in the numbers flag the numbers as floating point quantities.) Now we
can display the contents of the matrix using the matrix display word M.. by
typing
' MY.MATRIX M..
and QED-Forth
responds by printing the matrix as
MY.MATRIX =
1. 2. 3.
4. 5. 6.
ok
Let's transpose MY.MATRIX so that each row becomes a column and each
column becomes a row. To place the transpose of MY.MATRIX into the matrix DESTINATION and then display the result, execute
' MY.MATRIX '
DESTINATION TRANSPOSED
' DESTINATION
M..
and QED-Forth
responds by printing the transposed matrix as
DESTINATION =
1. 4.
2. 5.
3. 6. ok
There are a host
of other pre-programmed matrix transformations; many of them are discussed in
the QED Software Manual. For now, let's use matrices to solve a common and
important problem.
Solving
Simultaneous Equations
Many applications
require the solution of simultaneous equations with several unknown
quantities. This is easy to accomplish with QED-Forth. Suppose that we need
to solve the following equations:
1.07 x1 + 0.19 x2 + 0.23 x3 = 2.98
0.38 x1 + 1.00 x2 + 0.74 x3 = 3.48
0.12 x1 + 0.53 x2 + 1.20 x3 = 3.70
We can express
them as the matrix equation:
MX = R
where M is the
coefficient matrix, X is the matrix of unknowns, and R is the matrix of
right-hand-side constants (called the residue matrix in linear algebra). That
is,
1.07 0.19 0.23 x1 2.98
M = 0.38
1.00 0.74 X = x2 R = 3.48
0.12 0.53 1.20 x3 3.70
To solve this
matrix equation (that is, to compute the values of X) using QED-Forth, type the
following commands. The text after the \ character on each line is commentary that
explains what is happening; you need not type the comments.
MATRIX:
M \ create coefficient matrix
3 3 ' M
DIMMED \ dimension coefficient matrix
MATRIX M = 1.07
0.19 0.23 0.38 1.00 0.74 0.12 0.53 1.20 \ initialize coefficients
MATRIX:
R \ create residue matrix
3 1 ' R
DIMMED \ dimension residue matrix
MATRIX R = 2.98
3.48 3.70 \ initialize residue
MATRIX:
X \ create matrix of unknowns
' M ' R ' X
SOLVE.EQUATIONS \ solve the equations
' X
M.. \ print the results
QED-Forth
responds,
Matrix X =
2.099
0.8259
2.509
ok
With these few
lines of code, you've solved the set of linear equations with floating point
coefficients. Larger sets of equations with many more unknowns are handled
with very similar code.
In Case
Your Software Crashes
This section
demonstrates why you need not worry about "crashes" caused by programming
errors. QED-Forth gives you complete control over the board's hardware and
software. With all this power, programming "bugs" can sometimes cause the
processor to get lost so that it fails to respond to commands or forgets some
of the functions that you have defined. These situations are called
"crashes". The QED Board is designed so that you can always recover from
software-induced crashes. In other words, as long as you have not damaged the
electronic hardware on the board, you can restore the QED-Board to a known
functional state.
Some software
crashes can cause the processor to execute a series of write instructions to
various locations in memory. If the code definitions that you are developing
reside in unprotected RAM, they may be corrupted by a crash. You can protect
your code from accidental corruption using the memory write-protection features
described above and some QED-Forth utilities. These development techniques can
save a substantial amount of development time by eliminating the need to reload
your code after crashes.
For this
demonstration we will initialize the QED software and then add a word to the
dictionary to represent your application software. After write-protecting and
saving the state of the dictionary, we will purposefully crash the board, and
then restore the dictionary to its prior state. This demonstrates that you can
recover from a crash without having to reload all of your application program.
We begin by
initializing the board and setting up an appropriate memory map by typing:
COLD
4 USE.PAGE
ANEW CRASH.DEMO
The COLD command initializes QED-Forth, and 4
USE.PAGE places the
dictionary areas (where new definitions will be compiled) on page 4. The ANEW command followed by a name of your choice
marks the point in the dictionary where we begin adding new code. It is good
policy to execute an ANEW command after setting the memory map pointers.
We now add a new
function to the dictionary by typing this definition at the terminal:
: TEST CR ."
Hello world..." ;
When executed, TEST prints a greeting to the terminal. We'll
use this definition to represent any custom code you might send to your QED Board
while developing your program.
Once the code has
been loaded into the QED Board, we execute the command
SAVE
by typing it at
your terminal. This saves a set of relevant information describing the present
state of your dictionary. The information is stored in EEPROM (electrically
erasable PROM) inside the 68HC11 chip.
Now that our code
has been protected, we can verify that our TEST word works by typing
TEST
at the terminal.
It should work. Now let's make a mistake by executing the following expression:
FORGET TEST
Oops! We just
lost the code we worked so hard on (Try to execute TEST and see what happens). Fortunately, we SAVEd the state of our machine after defining TEST. Executing
RESTORE
restores the prior
state of the dictionary, and TEST is available again (type TEST to see for yourself).
That was easy.
But what if a more serious bug occurs, for example one that erases the user
area where all of QED-Forth's key pointers are kept? To see what happens,
execute the following command:
HEX 8400 0 100
ERASE
To recover from
this crash you can simply reset the processor by turning the reset switch (DIP
switch 7) ON and then OFF again. In this case the processor realizes that the
problem that caused the crash was serious, and executes a COLD restart, so access to added definitions such
as TEST will be
lost. (Most resets cause a WARM restart that automatically preserves the
dictionary, as explained in the "Program Development Techniques" chapter of the
QED Software Manual.) Once again, you can recover the dictionary including the
TEST
definition by simply executing RESTORE.
The Special
Cleanup Mode
During software
debugging it is possible (though difficult) to crash your QED Board so that
even a reset will not re-establish communications. The following example
demonstrates this by diabolically revectoring the terminal input and output
routines so that no serial communications can occur. Type this command all
on one line at your terminal:
HEX 0\0 UKEY
X! 0\0 UEMIT X!
After executing
this expression, you will not be able to communicate with your system, even by
resetting it. The solution is to enter "Special Cleanup Mode" which resets all
critical operating system variables to their original factory-defined
settings. This is accomplished by turning DIP switch 6 ON, and then resetting
the QED Board by toggling switch 7 or by turning the computer OFF and then ON
again.
Resetting or
re-powering your QED Board with switch 6 ON causes a COLD reset, initializes
all system variables and EEPROM locations to their factory settings, and enters
the QED-Forth interpreter. Once a special cleanup has been performed, be sure
to turn switch 6 OFF again and execute another reset to return to the normal
operating mode. If you are using a graphics display you'll need to re-establish
the stored display configuration using the IS.DISPLAY routine. The Special Cleanup Mode also sets
the default baud rate of the primary serial port to 9600 baud; if you have
established a different baud rate as described in the next chapter, you'll need
to execute BAUD1.AT.STARTUP to re-establish your chosen baud rate.
Now that you can
communicate with your QED Board again, execute RESTORE to restore access to your dictionary which
contains the TEST routine.
Saving Your
Program in Flash
Any time you
execute the RESTORE command, the memory map is restored to the same state it was in
when SAVE was executed.
Once you have
write-protected and saved your memory map, you may wish to
define more words in a fresh area of RAM. To do this, simply initialize the
pointers that specify where the "dictionary" and "names" entries of new
definitions are placed in memory. The following code accomplishs this by
moving the dictionary pointer and the names pointer to un-write-protected RAM
on page 6:
HEX
0000 6 DP X!
\ set the dictionary pointer
5000 6 NP X!
\ set the names pointer
ANEW MORE.ROUTINES
You can now add
more custom definitions, and they will be compiled in the specified regions on
page 6. The "Program Development Techniques" Chapter discusses these
issues in detail.
|