Your First Program
Using the Mosaic integrated development environment (IDE)
to program the Handheld instrument's embedded computer in C
This Chapter will get you started using the Control-C language to program your Handheld. 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 Handheld's extended memory space, and prints the results to the terminal;
- Selectively execute any program function using the Handheld’s on-board debugging environment (called QED-Forth);
- Use the Handheld’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.
Installing the Mosaic IDE and Control-C Compiler
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 then turn it back on; lesser restarts don’t always restart fully.
You are now ready to talk with your Handheld!
Turning on Your Handheld
Familiarize yourself with the locations of the power and serial connectors as shown in Chapters 1 and 2. After finding them follow these steps to turn on your system and establish communications with it:
- Connect your PC serial cable to the Handheld. You can use the 10-pin-socket-to-DB9 cable to connect to the Handheld’s internal serial header (on the Processor Board with the instrumetn open and the two halves connected with the high-density interconect cable) or you can use the DB25-to-DB9 cable to connect to the DB25 connector on the bottom of the Handheld. Connect the female end of the 9-Pin serial communications cable to your computer terminal’s RS-232 serial communications port. You can use any of your PC’s COM ports. COM2 is usually available, but some PCs only have COM1 available. If your computer does not have an RS-232 serial port, low cost USB-to-RS-232 serial cables are available; contact Mosaic Industries for details.
- Power up your PC computer or terminal.
- You should check the configuration of your Windows communications drivers:
- 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.
- In the list of devices open up the list of "Ports". If you are using a built-in serial port on your computer, double click on "Communications Port (COMx)". If you are using a USB serial adapter, double-click on "USB Serial Port (COMx)". Take note of which COM port number (i.e. COM1, COM3) you are using. If there are multiple ports listed under the "Ports" section, it may take some trial and error to determine which one corresponds with the physical port you are using to connect to the Mosaic Handheld.
In the new dialog box that appears, click the general tab and make sure you have these settings:
Baud Rate - 19200
Data Bits - 8
Parity - None
Stop Bits - 1
Flow Control - Xon/Xoff
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 Mosaic IDE executable (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 using COM2 by default at a speed of 19200 baud, 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 (
<html>ˆ</html>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 use 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 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 3. above.
- Verify the baud rate of the Mosaic Terminal by going to "Settings→Comm→Baud Rate" and making sure that 19200 is selected. This is the baud rate used to communicate with the Mosaic Handheld. If the baud rate is incorrect, garbled characters may appear in the terminal when you try to communicate with the controller.
- Plug the Handheld’s power adapter 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 side of the Handheld and turn it ON by pressing the ON/OFF keypad button in the lower right corner of the keypad.
- If no application program is running you should now see the following response:
Coldstart QED-Forth V4.4x
- If there is a demonstration program running and if you hit the enter key while the cursor is in your terminal window you should see the Handheld respond with,
ok
The serial communications responses indicate that your Handheld is now working!
If Something Doesn’t Work
If no message appears on your terminal there’s something wrong, so:
- Verify that power is being properly applied to the controller.
- Verify that the serial cable is properly connected.
- Check the terminal configurations of the Mosaic Terminal (using the menu item "Settings → Comm"), and recheck the communications properties of the Windows communications port.
- Perform a "special cleanup".
If you ever need to return your Handheld to its factory-new condition, just do a Special Cleanup: With the power on, install jumper J11 on the processor board, install and remove the reset jumper, J12, then remove J11. This procedure will remove any application programs and reinitialize all operating system parameters to their factory-new condition. Note that this procedure removes any demo programs too.
If you still are having trouble, email or give us a call.
Using the Mosaic IDE
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 serial 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.
- 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 into the memory of the Handheld. 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 Handheld’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 Handheld, 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 Handheld. 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 some 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 icon calls the C compiler and assembler only, the Single-Page Compile icon performs a standard build of your program, and the Multi-Page Compile 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 Terminal icon useful; it launches the Mosaic Terminal program. Each of these tools is described in more detail below.
The ‘Debug Tool’ Finds Syntax Errors in C Programs
The Debug Tool, designated by the "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 Compile Tool’ Compiles and Makes a Downloadable Single-Page C Program
The Single-Page Compile Tool, designated by the "Single-Page" 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 (using the Multi-Page Make icon):
WARNING: Input section ".doubleword" from ‘progname.o11’ is not used !
The ‘Multi-Page Compile Tool’ Compiles and Makes a Downloadable Long C Program
The Multi-Page Compile Tool, designated by the "Multi-Page" 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 Handheld. 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 4.
You may wonder why there are both "Single-Page" and "Multi-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.
‘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.
Your First C Program
Now that we’ve learned about the Handheld'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 Handheld’s on-board operating system and use it to interactively debug a program.
Compiling 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\Demos_and_Drivers\Misc\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 Compile 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.
Downloading and Running the 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. 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 "\Mosaic\Demos_and_Drivers\Misc\C Examples" directory, or wherever you compiled the program. Set the file type to "Download 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 ‘Enter’ key is represented by the symbol in the line above.
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 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 "\Mosaic\Demos_and_Drivers\Misc\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.
Interactively Debugging Your Program
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 Handheld’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 compiler 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 <html>%</html>
characters are formatting symbols that tell the compiler to substitute the listed arguments (in this case, the radius and area) for the <html>%</html>
sequences at runtime. The <html>%</html>6u
sequence tells the compiler to display the radius as an unsigned decimal number with a minimum field width of 6. The <html>%</html>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.
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
".
An Introduction to Extended Memory
The Handheld’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.
1 Megabyte Addressable Memory Space
The standard 68HC11 processor can address 64 kilobytes of memory using 16-bit addressing. The Handheld expands the address space to 1 Megabyte, addressing the lower 32 Kbytes of the processor’s memory space by means of a 5-bit "Page Latch" that selects one of 32 pages. The 32 pages times 32 Kbytes per page yields 1 Megabyte 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 Handheld’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 Handheld 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 later chapter, they are very useful for storing data from the Handheld’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 <html>%</html>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[pkc3]; 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.
An Introduction to Multitasking
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 Handheld’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 Handheld, 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 1 Kbyte on page 0. 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 Handheld 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).
Autostarting Your Application
You can configure QED-Forth to automatically execute a specified application program after every reset, restart, and ABORT. This makes it easy to design a production instrument based on the Handheld; the instrument will automatically perform its required function when it is turned on or reset.
QED-Forth provides two functions named AUTOSTART and PRIORITY.AUTOSTART that allow you to specify a startup routine. Both write a pattern in memory that instructs QED-Forth to execute a user-specified program. AUTOSTART stores the pattern in EEPROM which is inside the 68HC11 processor chip, and PRIORITY.AUTOSTART stores the pattern near the top of page 4 which is typically in PROM in a final turnkeyed system. The EEPROM-based AUTOSTART function is convenient during program development and debugging, or in the development of one-of-a-kind systems. But because the startup pattern is stored in EEPROM inside the 68HC11, it is cannot be automatically transferred with the application program to a different board.
The PRIORITY.AUTOSTART routine should be used for PROM-based production systems. It installs the startup pattern in PROM, so simply reproducing the PROM and plugging it into any Handheld turns that board into a turnkeyed instrument controller. In other words, the startup instructions are stored in the same PROM as the application program itself.
Let’s assume that you want to want to run the main routine every time you turn on, reset, or restart the Handheld. The following command:
CFA.FOR main AUTOSTART
leaves the extended code field address (cfa) of main on the stack. AUTOSTART then writes a pattern into EEPROM comprising a 16-bit flag (equal to 1357H) followed by the 32-bit extended cfa of the specified startup program. All subsequent resets and restarts will call the specified application program after QED-Forth initializes the system.
To specify the startup vector so that it can eventually reside in PROM, we would execute a different command:
CFA.FOR main PRIORITY.AUTOSTART
PRIORITY.AUTOSTART writes a pattern starting at 7FFAH on page 4 comprising a 16-bit flag (equal to 1357H) followed by the 32-bit extended cfa of the specified startup program. All subsequent resets and restarts will call the specified application program after QED-Forth initializes the system.
The priority autostart and autostart locations are checked each time QED-Forth executes ABORT, which is called after every reset, COLD or WARM restart, or error. ABORT first checks the priority autostart location at 7FFAH\4, and if 1357 is stored there it executes the program whose xcfa is stored in the four bytes starting at 7FFCH\4. If the priority autostart pattern is not present, or if the specified priority startup program finishes executing and "returns", ABORT then checks the autostart pattern at AE00H in common memory. If 1357 is stored there it executes the program whose 32-bit xcfa is stored in the four bytes starting at AE02H.
To remove the autostart pattern or patterns, execute:
NO.AUTOSTART
This command clears the priority startup pattern at 7FFAH\4 and the startup pattern at AE00H.
Summary
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_ARRAY
s in paged memory, define a multitasking application with an interactive terminal interface, and autostart an application. That’s pretty good considering that this is your first C program on the Handheld!