The C Programmer's Guide to the QVGA ControllerTable of ContentsPART 1 GETTING STARTED Introduction. How to Use This Manual Chapter 1: Getting to Know Your QVGA PART 2 PROGRAMMING THE QVGA CONTROLLER Installing the Mosaic IDE and Control-C Compiler Turning on Your QVGA Controller Removing and ReInstalling the Demo Program Downloading and Running the Program Interactively Debugging Your Program An Introduction to Extended Memory An Introduction to Multitasking Chapter 3: The IDE: Writing, Compiling, Downloading and Debugging Programs Chapter 4: Making Effective Use of Memory Chapter 5: Programming the Graphical User Interface 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 the QVGA Controller Chapter 10: Outputting Voltages with Digital to Analog Conversion Chapter 11: Serial Communications Chapter 12: The Battery-Backed Real Time Clock PART 4: PUTTING IT ALL TOGETHER Chapter 13: A Turnkeyed Application PART 5: REFERENCE DATA Appendix A: QVGA Electrical Specifications |
Chapter 2 Chapter 2: Your First ProgramThis Chapter will get you started using the Control-C language to program your QVGA Controller. It will guide you through the installation of the Mosaic IDE, an integrated editor, compiler, and terminal, and you'll start up and talk with your controller. You'll also: Compile and download your first program using an example multitasking program that performs calculations using floating point math, stores data in arrays in the QVGA Controller's extended memory space, and prints the results to the terminal; Selectively execute any program function using the QVGA Controller's on-board debugging environment (QED-Forth); Use the QVGA Controller's built-in operating system to access extended memory; Use the terminal to interact with a running multitasking application; and, See how easy it is to set up a multitasking application. Installing the Mosaic IDE and Control-C CompilerThe Mosaic IDE, which includes the Control-C Compiler, a full-featured editor and communications terminal, is provided on an installation CD-ROM. To install it onto your PC, first insert the Installation CD-ROM into your CD Drive. If the installer does not launch automatically, browse to your computer's CD drive using the 'My Computer' icon and double click on 'Setup.exe' to manually launch the installer. We recommend that you use the default installation directory ("C:\Mosaic\") and choose 'Typical Setup' when asked. If you wish to install into a different directory, you may type in any pathname provided that it does not contain any spaces. The 'Custom' setup option can be used if another version of either TextPad (the editor used within the Mosaic IDE), the Mosaic Terminal (previously called QED-Term), or an earlier version of the Mosaic IDE has already been installed. However, the Mosaic IDE requires all of its components to work properly. Please contact us if you have any questions. When the installation is complete, you will need to restart your computer unless you are installing onto a Windows 2000 machine. Be sure to choose 'Yes' when asked to restart — if you don't, the installation may not complete properly. If you choose 'No' and restart later we recommend that to assure a full restart you fully shutdown your computer and restart it; lesser restarts don't always restart fully. You are now ready to talk with your QVGA Controller! Turning on Your QVGA ControllerFamiliarize yourself with the locations of the power and serial connectors as shown in Chapter 1. After finding them follow these steps to turn on your system and establish communications with it: 1. Examine the settings of the 7 onboard switches. Dip switches 1, 5, 6, and 7 should be turned OFF by depressing the side of the switch towards the edge of the board. Dip switches 2, 3, and 4 should be turned ON. The function of each Dip switch is described in Chapter 1. 2. Connect the female end of the 9-Pin serial communications cable to your computer terminal's serial communications port and the male end to the primary serial port on the QVGA Controller. You can use any of your PC's COM ports. COM2 is usually available, but some PCs only have COM1 available. 3. Power up your PC computer or terminal. 4. You should check the configuration of your Windows communications drivers: 1. On your PC go to the device manager dialog box by double clicking "System" in the "Control Panel", clicking the "Hardware" tab, and clicking the "Device Manager" button. 2. In the list of devices open up the list of "Ports" and double click on "Communications Port (COM2)". (If COM2 is tied up with another service, such as a fax/modem, you may want to use COM1 instead.) 3. You'll now have a dialog box called "Communications Port (COM2) Properties". Click the general tab and make sure you have these settings:
5. We recommend that you use Mosaic Terminal, the terminal program that comes with the Mosaic IDE. You can start the terminal by double clicking the TextPad editor (the primary application of the Mosaic IDE) and choosing the terminal toolbar button which looks like this:
(The appearance of this and other toolbar icons may change in subsequent versions of the Mosaic IDE.) You can also start the terminal by double clicking the application "MosaicTerminal.exe". The terminal starts with using COM2 by default at a speed of 19200 baud (you will change this to 9600 baud in the next step), no parity, and 1 stop bit. Xon/Xoff flow control is enabled, and the file transfer options are set so that the terminal waits for a linefeed (^J) character before sending each line (this is very important). You can use any other terminal program, but it must be configured with these same settings. If you use another terminal program, you must specify that it uses the linefeed character as its prompt character. It might be denoted as LF, ^J, ascii decimal 10, or ascii hex A. If your PC is set up to use a COM port other than COM2 the Mosaic Terminal will respond with a warning box saying "Invalid port number". If so, just go to the Mosaic Terminal menu item "Settings→Comm→Port" and choose the COM port you chose when configuring Windows in step 4. above. 6. Change the baud rate of the Mosaic Terminal by going to "Settings→Comm→Baud Rate" and selecting 9600 baud. This is the baud rate used to communicate with the QScreen Controller. If the baud rate is incorrect, garbled characters may appear in the terminal when you try to communicate with the QScreen. 7. Plug the QVGA Controller's power supply into a 110 VAC outlet. European users will need a power transformer for changing European 220 VAC to 110 VAC. Insert the power supply output plug into the power jack on the QVGA Controller and turn the power switch ON by depressing the side of the switch towards the outer edge of the QVGA Board. After a short pause two things should happen: 1. A demonstration program should appear on the screen; and, 2. If you hit the enter key while the cursor is in your terminal window you should see the QVGA Controller respond with, ok The demo program, and the serial communications response indicate that your QVGA Controller is now working! The working software demo that is included in each QVGA Controller automatically starts when power is first applied and the power switch is turned ON. The demo shows three screens, accessible by touching file tabs at the top of the screen. The first screen shows a typical numeric keypad entry screen, the second provides examples of font sizes, and the third shows random numbers being plotted into a plot window. Touching the file tabs transfers focus from one screen to another. While the demo is running as one task, another task on the controller responds to commands from the terminal, and responds with the 'ok' prompt to an enter key. You can play with the demo to see how it works. Observing this demo program run is a good way to verify that all of the hardware is functioning properly and also demonstrates some basic capabilities of the menu control software. If Something Doesn't WorkIf your demo is not running, check that power is being properly applied to the controller. The demo should always run on a new QVGA Controller when power is initially applied. If you see only a blank screen and no message appears on your terminal there's something wrong, so: 1. Verify that power is being properly applied to the controller. 2. Verify that the serial cable is properly connected. 3. Check the terminal configurations of the Mosaic Terminal (using the menu item "Settings → Comm"), and recheck the communications properties of the Windows communications port. 4. Email or call us if you are still having trouble. Removing and ReInstalling the Demo ProgramDisabling the Demo ProgramWhen you tire of the demo you can disable it, but don't do that too quickly because once the demo is disabled you can't restart it without sending special commands to the controller. So, feel free to play with the demo for awhile, to see how it uses buttons and menus. Eventually, you will want to disable the demo. You can disable it by doing a special cleanup that places the QVGA Controller in a pristine state with no application program running. Doing a "Special Cleanup" If you ever need to return your QVGA Controller to its factory-new condition, just do a Special Cleanup: Turn ON dip switch 6, toggle dip switch 7 ON and then back OFF again, and finally turn OFF dip switch 6. This procedure will remove any application programs and reinitialize all operating system parameters to their factory-new condition. After you do disable the demo your display should be blank. You can verify that serial communications are working properly by hitting the enter key while your insertion point is in the terminal window. Whenever you hit the 'Enter' key, as represented by the symbol in the line above, you should see an 'ok' prompt. Now, whenever the QVGA Controller is powered up with your terminal communicating with it, this message, QED-Forth V4.xx ok where "V4.xx" represents the current software version number of your QVGA Controller, should appear, and the 'ok' prompt should repeat each time you press the carriage return. This message indicates that the onboard operating system is ready to receive commands. Although the demo now no longer automatically starts when the QVGA Controller is turned on, it still resides in Flash memory. The special cleanup has merely erased an autostart vector — a special location that tells the controller the starting address of the program to run when it starts up. You can still start the demo program by typing the following into your terminal window, RESTORE main in which the symbol represents hitting the 'Enter' key on your keyboard. The demo program should now be running. The first command you typed, RESTORE, restored pointers to the operating system's list of names of programs. The pointers had been saved in EEPROM by a SAVE command. The second line is the name of the program. When you type the name of a program the operating system responds by executing that program. The GUI demo (main) program will continue operating until the board is reset or the power turned off. If you'd like to restore the demo so that it automatically starts whenever the controller is turned on, simply type the following two lines: RESTORE CFA.FOR main PRIORITY.AUTOSTART Note that you must type the lines with the spaces between the words just as in the lines above. The case of the characters isn't important. These commands store the starting address of the main routine in the autostart vector. The two words, CFA.FOR main, find the code field address of the routine main and send it to the routine PRIORITY.AUTOSTART which stores it in the autostart vector. Now, the demo should automatically start up whenever the controller is turned on. If you download other programs to the QVGA Controller you will overwrite the demo program, which is stored in Flash memory. Also, any execution of the SAVE command will overwrite the saved pointers. In either case, to revive the demo you will need to reload it. That is done by using the Mosaic Terminal to reload the compiled demo file to the controller's Flash memory. For instructions for doing that please see the comments in the folder, "C:\Mosaic\GUI Toolkit\Demo". Using the Mosaic IDEUsing the Editor and CompilerThe Mosaic IDE has two main components, the TextPad editor, which includes the Control-C compiler, and the Mosaic Terminal program, both of which you'll find in the default directory "C:\Mosaic\": TextPad is a fully featured and highly configurable text and program editor. You'll use it to write and compile your code. All of the functions of the C compiler tools are available through the controls in TextPad. You can launch TextPad from the 'Mosaic IDE' group in the 'Programs' section of your Windows 'Start' menu. For convenience, you may want to place a shortcut to it on your desktop or on your Windows Taskbar. The Mosaic Terminal is a serial communications terminal that allows you to interactively control your controller over its RS-232 interface. You'll also use it to download your compiled C programs into the memory of the QVGA Controller. Mosaic Terminal may be launched from the 'Mosaic IDE' group within 'Start→Programs', but it is also available from within TextPad, either from the 'Tools' dropdown menu or by clicking the terminal icon on TextPad's toolbar. You can type characters directly into the terminal window, and they will be accepted by the QVGA Controller's line editor and interpreter. This mode of interaction is convenient when debugging or typing short code fragments. If you are sending source code to the QVGA Controller, it is best to create a file first. The file can be saved to disk to provide a record of your work, and the terminal program can be used to download the file to the QVGA Controller. You can also use the terminal to record your debugging sessions and save them as a file on disk The TextPad Tool BarAlong with the standard tools you expect in a text editor you'll find four custom tools available in the toolbar that you'll use to compile and download your programs. Each of these tools is also accessible in the 'Tools' dropdown menu of TextPad. For C programmers the Debug (magnifying glass) icon calls the C compiler and assembler only, the Single-Page C icon performs a standard build of your program, and the Multi-Page C icon performs a multi-page memory model build of your program. Forth programmers won't need to use these tools. Both C and Forth programmers will find the Image Converter and Terminal icons useful. The Image Converter facilitates converting bitmap images to a form the QVGA Controller can use, and the terminal icon launches the Mosaic Terminal program. Each of these tools is described in more detail below. The 'Compile Tool' Finds Syntax Errors in C ProgramsThe Compile Tool, designated by the "Debug" or "Magnifying Glass" icon, invokes the compiler and assembler only — it does not produce downloadable code. Use it to quickly check the syntax of a program and find compilation errors without performing the full build which would be needed to download the program into the microcontroller. The 'Single-Page Make Tool' Compiles and Makes a Downloadable Single-Page C ProgramThe Single-Page Make Tool, designated by the "Make" or "Single-Page C" icon, performs a standard, single-page memory model build of your program. If you are compiling a program whose compiled size does not exceed 32 Kbytes of memory you should use this mode for fastest execution. Although this program size is sufficient for many applications, you may need to use the multi-page build if your application grows beyond 32 K. If you see the following warning printed in the compiler output, then you must switch to the multi-page memory model build (Multi-page C icon): WARNING: Input section ".doubleword" from 'progname.o11' is not used ! The 'Multi-Page Make Tool' Compiles and Makes a Downloadable Long C ProgramThe Multi-Page Make Tool, designated by the "Multi-Page C" icon, invokes the C compiler's multi-page build mode. Programs compiled in this mode may be many pages in length limited only by the amount of FLASH installed in the QVGA Controller. It is always a good programming practice to break large projects into multiple smaller source code files for organization, and the multi-page build also uses this organization for distributing the compiled program across multiple 32 Kbyte pages. Thus, no source code file may contain more than 32 Kbytes worth of compiled source code or the above warning will be issued and the program will not run. A more detailed description of this behavior is available in Chapter 3. You may wonder why there are both "Multi-Page" and "Single Page" compile modes. The reason is that C function calls between pages take just a little longer to execute (calls to functions on a different page take 49 microseconds while those on the same page or to common memory take only 11.5 or 13.75 microseconds, respectively). Because most function calls are to functions on the same page or to common memory page changes are rare; the average execution speed of multi-page C applications is still quite fast. The 'Mosaic Terminal' Communicates with Your Product The Terminal icon launches the communications program, Mosaic Terminal. When you launch Mosaic Terminal for the first time, check the communications settings (Settings→Comm) to verify that the serial port is set correctly for your computer. Even if your QVGA Controller is running a demo program it will still respond to the terminal if the communications settings are correct. Your First C ProgramNow that we've learned about the QVGA Controller's hardware, established serial communications, and installed the Mosaic IDE on the PC, it's time to compile, download and execute a C program. We'll also explore the QVGA Controller's on-board operating system and use it to interactively debug a program. Compiling a ProgramIn this section we'll be running a simple program that does some computation and communicates its results on the serial port. The program is one of several examples for use with the Control-C IDE in the "\Mosaic\C_Examples" directory. Let's compile, download, and run it. Start TextPad, and open the source-code file "getstart.c" from the C_Examples directory. You should see the source code in an open window — browse through it to get a feel for it. You'll see that the final routine in the file is called main(); it's the top-level, executable program. The first routine within main() that is called when the program starts is InitVars(). Note that in the run-from-place applications of embedded systems it's important to initialize all variables with a run-time procedure when the program starts. Variables that are initialized when the program is compiled are not automatically initialized when the program runs; you should have a runtime routine in your code that does that. Clicking on the Single-Page C icon will compile and produce a downloadable form of the program, named "getstart.dlf". A new window named 'Command Results' will appear so that you can watch the compilation process. When compilation has finished, you can scroll the Command Results window up and look for warnings, which don't prevent creating a valid download file, and errors, which do. You should see two warnings near the beginning: GETSTART.C(126): Warning: Expression is always TRUE ! GETSTART.C(197): Warning: Symbol 'unused_variable' is never used in function 'main' ! We deliberately inserted into 'main' a variable named unused_variable that is never used in the function. If you double click on an error or warning line in the command results, TextPad will jump to the corresponding line in the affected source file. Despite the warnings, the program should have compiled successfully; the command results will end with: Qcc-> Creating QED Download File: getstart.dlf Tool Completed Successfully You can quickly switch between the Command Results window and your source code file either by hitting Ctrl-Tab, or by clicking on the file tabs at the bottom of the TextPad window. The file named "getstart.dlf" is ready to be downloaded to the microcontroller using the Mosaic Terminal program. Downloading and Running the ProgramIf it is not already open, launch Mosaic Terminal either from the 'Start' menu or using the TextPad toolbar or dropdown menu. It's most convenient to use the Terminal icon on the TextPad toolbar. If the factory demo is still running on your QVGA Controller, use the special cleanup procedure as outlined above. A special cleanup is performed by simply turning dip switch number 6 on, power cycling the QVGA or resetting it by toggling switch 7, and turning dip switch number 6 back off. This procedure causes the microcontroller to disable the automatic start up, but does not delete the GUI Toolkit demo program from FLASH. Now, you should be able to hit enter at the Mosaic Terminal prompt and see the 'ok' response with the microcontroller plugged in and turned on. If this is not the case, check your communications settings and cabling. Now, select 'File → Send File' from the Mosaic Terminal menu and enter the "\c_examples" directory, or wherever you compiled the program. Set the file type to "Text Files (*.dlf)" and select "getstart.dlf". You will see various commands and hex data scrolling on the screen as the file is downloaded to the microcontroller. When the download is complete, the text will turn from gray to green to indicate that it is finished. Now, it's time to run your program. To execute the top level function of your code, simply type 'main' and press enter, main The getstart program will respond with: Starting condition: The radius is 0; the circular area is 0. ok While on its face that doesn't seem a very impressive response, you're running your first program! This particular example program uses multitasking. The program runs a background task called CalculationTask continuously, incrementing a radius variable and using it to compute a new area. The program is running in its own task, leaving the communications task free so you can continue to interact with the controller. You will notice that you can hit enter, and use the interactive debugging capabilities discussed in Getting Started with the Control-C Programming Language even though the program is still running. For example, try executing the following function interactively from the terminal: Announce( ) Note that you must type the space after the ( character. Each time you execute this function you'll notice that the output is different, as the radius is being continuously incremented by the background task. Now try executing, Nap( ) which puts the background CalculationTask ASLEEP. If you again execute Announce( ) several times, you will notice that the radius and area are no longer being updated by the CalculationTask. To wake up the CalculationTask again, type Wakeup( ) and notice that the calculation is again being performed by the task. You may want to stop the program; in particular you'll need to stop it before attempting any new downloads. This can be done most easily by simply entering 'warm' at the microcontroller's prompt. The warm restart causes a soft reset to occur, terminating any background tasks that may be running. After having run this program, you may want to play with the other example programs in the "\C_Examples" directory. We strongly recommend that you compile these programs and work through the examples as suggested in the text of this manual. This will provide you with a thorough "hands-on" introduction to the Control-C programming environment. Interactively Debugging Your ProgramWe have seen how to interactively call the main() function from the terminal to execute our entire program; most C development environments let you do this. But the QVGA Controller's operating system makes it easy to interactively execute any designated function in your program. By simply preceding a function definition or prototype with the _Q keyword (we chose "_Q" as a unique keyword that suggests QED), you can ensure that the function will be interactively callable from your terminal. An example: Announce( ) Displays an Area and RadiusFor example, to display a summary of the current values of the radius and calculated circular area variables, we would like to call the function Announce( ). Using the editor, look near the top of the GETSTART.C file and you'll see that its definition is:
_Q void Announce(void) { printf("\nThe radius is %6u; the circular area is %5.4g.\n",radius,area); } The void keywords indicate that the Announce( ) function does not return a value, and does not expect any input parameters to be passed to it. The _Q declarator instructs the Make Tool that we want to be able to interactively call this function using the on-board QED-Forth interpreter. The names and execution addresses of all functions defined with the _Q designator are placed in the .DLF download file so that QED-Forth will recognize them and will be able to interactively execute them. The printf() function invoked in Announce( ) prints the specified string to the serial1 port. The parameters of the printf() function are well defined by the ANSI standard, and are described in many excellent texts. Briefly, the \n is an escape sequence that instructs printf to insert a newline at the designated places in the string. The % characters are formatting symbols that tell the compiler to substitute the listed arguments (in this case, the radius and area) for the % sequences at runtime. The %6u sequence tells the compiler to display the radius as an unsigned decimal number with a minimum field width of 6. The %5.4g sequence tells the compiler to display the area using either decimal or exponential notation with a precision of 4 decimal places to the right of the decimal point, and a minimum field width of 5. The printf() function in Control-C differs from the ANSI standard in one respect: the maximum length of a printed string is limited to 80 characters instead of the standard 255 characters. This limitation also applies to the related functions named sprintf() (which writes a string to a buffer) and scanf() (which inputs a string). Of course, you can handle strings longer than 80 characters by using multiple calls to these functions. Interactively Calling Announce( )To interactively call this function, simply type at your terminal Announce( ) followed by a carriage return (indicated by the arrow above). Spaces are important to the QED-Forth interpreter which processes this command; make sure that there is no space between the function name Announce and the opening parenthesis (, and there must be at least one space after the opening parenthesis. If QED-Forth does not recognize your command, it will repeat what you typed followed by a "?" character and wait for another command, so you can try again. The case of the letters does not matter: you can use all uppercase, all lowercase, or any combination when typing commands for the QED-Forth interpreter. After calling Announce( ), you should now see the message The radius is 0; the circular area is 0. on your screen, except that the printed values of the radius and area will correspond to the values they had when you executed the "WARM" command to stop the calculations. Then you will see an additional line of text starting with "Rtn:" that summarizes the return value of the function in several formats, followed by the "ok" prompt. Because the Announce() function has no return value, the return value summary is not relevant. The "ok" prompt indicates that QED-Forth has successfully called the function and is now ready to execute another command. If you make a mistake while typing a command, just type "backspace" or "delete" to erase as many characters as necessary on the current line. Once you've typed a carriage return, though, QED-Forth executes your command. You can't edit a command that was entered on a previous line. If you type an incorrect command and then type a carriage return, you may receive the "?" error message which means that QED-Forth does not understand the command you typed. If this happens, you can usually just re-type the command line and continue. Area CalculationThe 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 _QNearly every function in the many sample programs in the \MOSAIC\DEMOS_AND_DRIVERS\MISC\C EXAMPLES directory is declared with the _Q keyword to facilitate easy debugging. There are, however, two restrictions associated with the use of the _Q declarator. First, a function defined using the _Q keyword cannot use
... (ellipsis) in its parameter list; rather, the number of input parameters must be specified when the function is defined. (If you try to define the _Q function with an ellipsis as an input parameter, the compiler will issue a warning and remove the _Q specifier, so you will not be able to interactively call the function during debugging.) The second restriction is that the _Q function cannot be called via a function pointer if the function accepts input parameters. In other words, do not use the _Q declarator if: a. You need to call the function using a function pointer; and, b. The function accepts input parameters. To see an example in which the _Q keyword cannot be used, open the sample program named GRPHEXT.C in the \MOSAIC\DEMOS_AND_DRIVERS\MISC\C EXAMPLES directory and search for the function named SetTheBits. This restriction does not affect many functions. Any function declared using _Q can always be called in the standard way (that is, by invoking the function name followed by parentheses that contain any input parameters). Moreover, any _Q function can be called indirectly via a function pointer (by passing its name without any parentheses) if the function's input parameter list is "void". An Introduction to Extended MemoryThe QVGA Controller's onboard operating system, called QED-Forth, provides numerous run-time services, including providing a heap memory manager. Using this memory manager we can access the controller's extended memory. 8 Megabyte Addressable Memory SpaceThe standard 68HC11 processor can address 64 kilobytes of memory using 16-bit addressing. The QVGA Controller expands the address space to 8 Megabytes, addressing the lower 32 Kbytes of the processor's memory space by means of an 8-bit "Page Latch" that selects one of 256 pages. The 256 pages times 32 Kbytes per page yields 8 Megabytes of addressable memory. The upper 32 Kbytes of the 68HC11's address space is called the "common memory". This address space is always accessible, regardless of the contents of the Page Latch. Available Common RAMThe ANSI C compiler supports the standard 16-bit addressing scheme via its small memory model. It also supports a medium memory model that allows functions to be called on any specified page using a 24-bit address (16-bit standard address plus an 8-bit page). All C variables and C arrays, however, must be accessible using a simple 16-bit address. For practical purposes, this means that variables and C arrays must reside in the QVGA Controller's available 8 kilobytes of available common RAM located at addresses 0x8E00 to 0xADFF. In multitasking applications, this RAM is also used for task areas; each task requires 1 Kbyte of common RAM area. You are of course free to use ANSI-standard C arrays located in the variable area in common RAM. These arrays allow you to use standard C pointer arithmetic, and their use is explained in all of the C textbooks. However, if you need to store a lot of data, the available 8K of common RAM may not be sufficient. But don't worry — you can still use all the memory. Built-in Array Library Routines Manage Access to Paged MemoryThe FORTH_ARRAY routines that reside in ROM on the QVGA Controller provide an efficient means of accessing the large paged address space for storage of data. The pre-defined DIM() macro makes it easy to dimension a 2-dimensional array to hold signed or unsigned characters, integers, longs, or floating point values. Other pre-defined library functions handle storing, fetching, and copying data to and from the arrays. These QED-Forth functions are callable from C, and provide access to a large contiguous memory space that is very useful for real-time data storage and analysis. Each array is referred to using a named 16-bit pointer to a "parameter field" structure in common RAM. Once the array has been "dimensioned", this structure holds the number of rows and columns, data size, and a pointer to the QED-Forth heap where the array is allocated. The ROM-resident heap manager allocates and deletes the arrays in real time under the control of the C program, thereby maximizing the effective use of available paged RAM. This section introduces the use of the arrays, and as we'll see in a late Chapter they are very useful for storing data from the QVGA Controller's A/D convertors. The header file named ARRAY.H in the \MOSAIC\FABIUS\INCLUDE\MOSAIC directory contains all of the function and macro definitions that are used to access Forth arrays, including the DIM(), FARRAYFETCH() and FARRAYSTORE() macros that are mentioned in this section. Declaring and Dimensioning a FORTH ARRAYLet'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 ArrayIt is easy to interactively call the functions that we've examined. The syntax that we'll type at the terminal looks similar to an ANSI C function prototype, with one of the following type declarators being used before input parameters that are passed by value:
char int long float char* int* long* float* When passing the address of a variable or a structure, use only the name of the variable or structure, without any additional declarators or & operators. All of this is explained in detail in a later Chapter; for now, the goal is see how easy it is to use the interactive function calling tools. For example, to interactively dimension and initialize the circle_parameters array to have 10 rows, 2 columns, with each element initialized to a value of 34.56, type the following line at your terminal: DimAndInitFPArray( float 34.56,int 10,int 2,circle_parameters) Remember to type at least one space after the ( character, and after the float and int keywords. QED-Forth will respond to your command with a line of text that summarizes the return value of the function, followed by the "ok" prompt. We can ignore the return value summary, because this function does not return a value. Now to verify that the initialization was performed correctly, we can type at the terminal: PrintFPArray( circle_parameters) and, as always, we make sure that there is a space after the ( character. Note that we do not use the & (address-of) operator before the circle_parameters argument; it turns out that circle_parameters has already been defined in QED-Forth as the base address of the FORTH_ARRAY structure. QED-Forth calls the function which prints the contents of the circle_parameters array, and then summarizes the return information (which we can ignore in this case). You can verify that the value of each array element is the same one that you specified when you called the DimAndInitFPArray() function. (Slight differences in the values are due to rounding errors in the floating point conversion and printing routines.) Using this interactive method, you can test each function with a variety of dimensioning and initialization information. An Introduction to MultitaskingMany instrumentation and automation applications can be logically conceived of in terms of a set of distinct "tasks" that cooperate to solve the problem at hand. For example, a program that manages a hand-held sensing instrument might have one task that acquires sensory data, another that performs calculations to process the data, and a third task that displays the results on a liquid crystal display. Using the QVGA Controller's built-in multitasking executive confers significant advantages when designing real-time systems. Breaking up a complex program into easily understood modular tasks speeds debugging, improves maintainability, and prevents source code modifications of one task from adversely affecting the required real-time performance of another task. The Task Activation RoutineIn a multitasking environment, a "task" is an environment capable of running a program. After declaring (naming) a new task (which also allocates a 1 Kbyte task area), its environment is "built" by initializing its required stacks, buffers and pointers in the 1 Kbyte task area. Then the task is "activated" by associating it with an "activation routine" that performs a specified set of actions. A typical task activation routine is the CalcForever() function in the GETSTART.C file. Its definition is straightforward:
_Q void CalcForever(void) // this infinite loop function can be used as a task activation routine { DimAndInitFPArray(0.0,CIRCLE_ROWS,CIRCLE_COLUMNS,&circle_parameters); while(1) // infinite loop { IncrementRadius(); // updates radius variable area = CalcArea(radius); // updates area variable if(radius%10 == 0) // on even multiples of 10... SaveCircleParameters(); // save data in FORTH_ARRAY Pause(); // give other tasks a chance to run } } The first thing that this function does is to dimension and initialize the circle_parameters array. Then it enters an infinite loop that increments the radius variable, calculates the corresponding circular area and stores it in the area variable, and saves the radius and area in the circle_parameters array if the radius is an even multiple of 10. The function calls Pause() on every pass through the loop. Pause() is a multitasking function that instructs the multitasking executive to change to the next task (if any) in the round-robin task list. This enables "cooperative multitasking", in which a task willingly lets other tasks run by executing Pause(). The other type of multitasking, also supported by the QVGA Controller, is "pre-emptive multitasking", in which an interrupt-driven timeslice clock forces a task switch on a periodic basis. In summary, the CalcForever() function is an infinite loop that manages the calculation and storage of the radius and circular area. This function can be the "activation routine" for a stand-alone task running in a multitasking environment. Declare, Build and Activate a TaskThe short section titled "Multitasking" in the GETSTART.C file demonstrates how easy it is to set up a task using the pre-defined macros. First we declare the new task as:
TASK CalculationTask; The TASK typedef allocates a 1 Kbyte task structure named CalculationTask in the common RAM. The function SetupTask() builds and activates the new task; its definition is:
void SetupTask() { NEXT_TASK = TASKBASE; // empty task loop before building BUILD_C_TASK(HEAP_START,HEAP_END,&CalculationTask); // private heap ACTIVATE(CalcForever, &CalculationTask); // define task's activity } The first statement empties the round-robin task loop by setting the NEXT_TASK pointer in the task's user area to point to the task's own TASKBASE. The next statement invokes the BUILD_C_TASK() macro which expects starting and ending addresses for the task's heap, and the address at which the task is located. We have defined the constants HEAP_START and HEAP_END to specify a task-private heap occupying a full 32 Kbytes on page 7. The task base address is simply &CalculationTask. BUILD_C_TASK() sets up all of the stacks, buffers and pointers required by the task. The final statement in SetupTask() invokes the ACTIVATE() macro which expects a pointer to the activation function (which is CalcForever) and the TASKBASE address (which is &CalculationTask). Multiple tasks can be declared, built and activated in the same way. Putting a Task AsleepA "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 GoingThe main() function is the highest level routine in the program. Its definition is:
void main(void) // Print starting area and radius, build and activate CalculationTask. { int unused_variable; // an example of how warnings are handled! InitVars(); printf("\nStarting condition:"); Announce(); // print starting values of radius and area SetupTask(); // build and activate the CalculationTask } As you recall, the declaration of the unused_variable was inserted to demonstrate how the Control-C IDE highlights the source code line associated with compiler errors and warnings. InitVars() performs a runtime initialization of the variables used by the program; this is very important, because compile-time initializations won't ensure that variables are properly initialized after the program has run once, or after the processor is restarted. After initializing the variables, main() announces the starting values of radius and area and then calls SetupTask() to build and activate the CalculationTask. To execute the program, simply type at your terminal: main You'll see the following message: Starting condition: The radius is 0; the circular area is 0. ok The "ok" prompt lets you know that QED-Forth is ready to accept more commands. We have set up a two-task application: the default startup task (named the FORTH_TASK) is still running the QED-Forth interpreter, and the CalculationTask that we built is running the CalcForever() activation routine. At any time we can monitor the current values of radius and area by interactively calling the function: Announce( ) Remember to type a space after the ( character, and you may have to type slowly so that the multitasking program does not miss any of the incoming characters from the terminal. To view the contents of the circular buffer array named circle_parameters, type the command: PrintFPArray( circle_parameters) To suspend the operation of the CalculationTask, type: Nap( ) Now notice that successive invocations of: Announce( ) all show the same values of the radius and area; this is because the CalculationTask is no longer updating them. To re-awaken the CalculationTask, simply type: Wakeup( ) To abort the multitasking program altogether and return to a single task running the QED-Forth monitor, you can perform a "warm" restart by typing: WARM The QED-Forth startup message will be displayed. Of course, if you want to run the program again, you can type main or any of the interactively callable function names at any time. Remember to type WARM or COLD before trying to download another program file; the QVGA Controller can't run multiple tasks and accept a download file at the same time. (Both WARM and COLD re-initialize the system, but COLD performs a more thorough initialization and causes QED-Forth to immediately "forget" the definitions of the C functions that were sent over in the .DLF download file). SummaryNow you've worked through the GETSTART.C program in detail. You know how to compile, download and execute programs, perform simple floating point calculations, print formatted strings and numbers to the terminal, dimension and access FORTH_ARRAYs in paged memory, and define a multitasking application with an interactive terminal interface. That's pretty good considering that this is your first C program on the QVGA Controller!
routine. For example, if all of your code and names are located on page 4, simply execute:
, and even are treated as the same command.
Consult another chapter
inverts it, and then
routine.
|
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