manufacturer of I/O-rich SBCs, operator interfaces, handheld instruments, and development tools for embedded control low cost single board computers, embedded controllers, and operator interfaces for scientific instruments & industrial control development tools for embedded control order our low cost I/O-rich embedded control products embedded controller manufacturer profile single board computers & embedded controllers development tools & starter kits for your embedded design operator interfaces with touchscreens and graphical user interface plug-in expansion modules for digital & analog I/O C language & Forth language integrated development tools, IDE single board and embedded computer accessories embedded controller enclosures, bezels, environmental gaskets

QCard C User Guide

Table of Contents

PART 1 GETTING STARTED

Chapter 1: Getting to Know Your QCard Controller

Chapter 2: Using Your PowerDock

Chapter 3: Your First Program

Installing the Mosaic IDE and Control-C Compiler

Turning on Your QCard Controller

Using the Mosaic IDE

Your First C Program

Compiling a Program

Downloading and Running the Program

Interactively Debugging Your Program

An Introduction to Extended Memory

An Introduction to Multitasking

Summary

PART 2 PROGRAMMING THE QCARD CONTROLLER

Chapter 4: The IDE: Writing, Compiling, Downloading and Debugging

Chapter 5: Making Effective Use of Memory

Chapter 6: Real Time Programming

Chapter 7: Failure and Run-Time Error Recovery

PART 3 COMMUNICATIONS, MEASUREMENT, AND CONTROL

Chapter 8: Digital and Timer-Controlled I/O

Chapter 9: Data Acquisition Using Analog to Digital Conversion

Chapter 10: Serial Communications

Chapter 11: The Battery-Backed Real-Time Clock

PART 4 PUTTING IT ALL TOGETHER

Chapter 12: A Turnkeyed Application

PART 5 REFERENCE DATA

Appendix A: QCard Electrical Specifications

Appendix B: Connector Pinouts

Appendix C: Schematics (zip)

Chapter 3

<< Previous | Next>>

Your First C Program

Now that we’ve learned about the QCard 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 QCard Controller’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 QCard 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 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 % 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.

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

1 Megabyte Addressable Memory Space

The standard 68HC11 processor can address 64 kilobytes of memory using 16-bit addressing.  The QCard Controller 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 QCard 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 QCard 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 later chapter, they are very useful for storing data from the QCard 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 to 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 QCard 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 QCard 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 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 QCard 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).

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 QCard; 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 QCard 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 QCard Controller.  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

 

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

 

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_ARRAYs 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 QCard Controller!

An Introduction to Programming in QED-Forth

The QCard 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 QCard 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 QCard 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 QCard Controller.  Thus you can rapidly edit your source code into files which are downloaded to the QCard Controller.  The Mosaic IDE allows you to switch between the editor and terminal windows quickly and easily.

How To Send Your Programs to the QCard Controller

To send a program to the QCard Controller, 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 select ‘File ® Send File’ from the Mosaic Terminal menu to send the program file to the QCard Controller.  If you discover errors while debugging the program, simply edit the file and re-transmit all or part of it to the QCard Controller.  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 QCard when the new version is transmitted.

You have complete flexibility in programming the QCard Controller.  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 QCard 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

The ‘Enter’ key is represented by the ż symbol in the line above.

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 QCard.  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.  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.4x

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. 

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.

 

: 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 PAUSEPAUSE 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 later in the “Multitasking” chapter.

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; 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 QCard 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 QCard to a known functional state.

For this demonstration we will initialize the QED software and then add a word to the dictionary to represent your application software.  After putting the routine in flash 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

DOWNLOAD.MAP

4  USE.PAGE

ANEW CRASH.DEMO

 The COLD command initializes QED-Forth, DOWNLOAD.MAP sets pages 4-6 as RAM and pages 1-3 as flash, 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 QCard while developing your program.

Once the code has been loaded into the QCard, we execute the commands

  4 PAGE.TO.FLASH

STANDARD.MAP

SAVE

by typing it at your terminal.  This moves the test routine into flash, changes pages 4-6 to flash, and 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 stored into flash, 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 pressing the reset switch.  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)  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 QCard 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 installing Jumper J1 and then pressing the reset button with the power ON.

Resetting or re-powering your QCard with Jumper J1 installed 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 remove Jumper J1 to prevent performing a Special Cleanup every time you turn on power.  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 19200 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 QCard again, execute RESTORE to restore access to your dictionary which contains the TEST routine.

<< Previous | Next>>


Home|Site Map|Products|Manuals|Resources|Order|About Us
Copyright (c) 2006 Mosaic Industries, Inc.
Your source for single board computers, embedded controllers, and operator interfaces for instruments and automation