Link here

3: Coding Your Application

How to properly write C code for the Mosiac IDE Plus™: #include files, special Mosaic extensions, and useful C constructs

Software development in C uses the Codeblocks-based Mosaic IDE Plus Integrated Development Environment. This chapter explains how to properly write C code for the Mosaic IDE Plus™. This section describes include files, accessing Wildcard IO modules, using pointers and 32-bit absolute addresses in the 9S12 (HCS12) processor's memory space, calling Forth routines, using lookup tables, and locating objects in non-volatile EEPROM memory.

 

The basics

Every project compiled in the Mosaic IDE Plus should have the following lines at the top of every .C file:

#include <mosaic\allqed.h>
// this include statement should appear at the top of each source code file.

This file provides access to the operating system functions on the controller board, including device drivers for all of the "native I/O" on the PDQ line of controllers. These drivers control the Timer/Counter, Pulse-Width Modulation (PWM), Analog to Digital (ATD), serial channels, and multitasker available on the HCS12 processor. These functions are documented in the C Function Glossary (A-H), and prototypes are located in the C:\MosaicPlus\c\libraries\include\mosaic directory.

 

Initializing memory

There are two ways to initialize variables in the C language; You can set the initial value of a variable when you declare it, or you can have line of code which sets the value at runtime. When programming for the PDQ Board you should not use the first method. This is an example of the wrong way to initialize memory:

int wrong = 1;

The correct way to do this is is:

  int correct;
 
  correct = 1;

The difference here is that the second example compiles instructions which which will execute on the CPU of the PDQ board at run time. You can also group all of your initializations into a single routine, and call it from main() like this:

int correct;
int temperature;
void InitializeVariables( void )
{
  correct = 1;
  temperature = 98;
}
 
int main( void )
{
  InitializeVariables();
 
  ...
 
  return 0;
}
 

Wildcards

Most applications include hardware beyond the "native I/O" present on the HCS12 processor. You may have one or more Wildcards installed on your controller, or you may be using a touchscreen equipped product which is powered by the GUI Toolkit. Pre-coded add-on drivers are provided for all of the Wildcards and GUI products from Mosaic.

Mosaic controllers use a unique mechanism to provide add-on drivers. Traditional C uses classic C libraries. The Mosaic system generates a standard header file plus a set of directives and S-records that place the executable code into flash memory on the controller when you download your program with a new library added.

 

Using a C library

Using a library in C is very simple. All you have to do is #include the header file for the driver in your source file, and all the rest will be handled for you! All you need to do is put this at the top of your C code:

#include "library.h"

Then you can call any of the functions the library provides with no hassle. Mosaic's Codeblocks-based IDE Plus Integrated Development Environment takes care of linking the object code for you. When you compile your code using the IDE's GNC C Compiler (GCC), the driver file will automatically be included in the generated .DLF file. You can see a list of available c libraries by looking in the C:\MosaicPlus\c\libraries\include folder.

 

Using a Forth library

To use a Forth library, simply including the following line in your source code file after initializing the memory map using DEFAULT.MAP:

#include "C:\MosaicPlus\forth\libraries\firmware\library.fin"

Remember that instead of "library.fin" you will want to include the real name of the driver listed below, such as "waim.fin" for the Analog I/O Wildcard.

 

Wildcard drivers

The following is a list of available libraries that act as a driver for specific Wildcards. Only the base name is listed here. To include a driver from a C source code file, simply #include the name below with ".h" appended to the end, and the IDE Plus will compile and link the required driver files.

 

Forth-callable routines ( _Q )

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 QED-Forth operating system also makes it easy to interactively execute any designated function in your program. By simply preceding a function definition or prototype with the _Q keyword, you can ensure that the function will be interactively callable from your terminal.

For example, the following function calculates the Pythagorean theorem:

float _Q pythag( int a, int b )
{
    int sum = a*a + b*b;
    printf("\nBefore the Square root the Sum is %d, the Answer is %g\n", sum, sqrt( sum ) );
    return sqrt( sum );
}

Simply add this _Q function inside a working program (Hello World works as a good template.) Compile your code and load it onto your controller. Then type:

DECIMAL

to put the board in decimal base. If the board is in the default HEX mode, the results will not make sense. Now simply type the name of the function and parameters like this:

pythag( 2, 4 )

The board will respond by running the function, and displaying the return value

 
Before the Square root the Sum is 20, the Answer is 4.47213
 
Rtn: 0x408F 0x1BB8 = 0x408F1BB8 =fp:  4.4721  OK

Note that the last line printed displays both the hexadecimal and floating point values returned from the function. After the =fp: characters we see 4.4721, our correct answer.

Spaces are important to the QED-Forth interpreter which processes this command; make sure that there is no space between the function name pythag 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.

See Also → Your First C Program Using Mosaic IDE Plus
Debugging Programs

 

Using lookup tables

There is much more paged memory than common RAM available on Mosaic controllers. In large applications, it may be convenient to locate a lookup table, which does not change at runtime, in paged memory along with the code. This conserves common RAM and makes efficient use of the available memory on the board.

Open the sample project to follow along with this section:

Open a new "Sine Lookup Table" project.

Look for this icon under Project→ New Project:
Gui Development Example
Sine Lookup Table

With the code open, scan through until you find the lookup table:

const _rom float sinLUT[TABLE_LENGTH]

The const keyword tells C that nothing is allowed to modify this array. The _rom keyword is the most important; it puts this array in non-volatile flash memory. Build the code and send it to your Mosaic controller using the Terminal, and then type in the Terminal window:

main

to run the program. The entire sine table will print out, as well as two memory addresses. The address known to C is displayed, as well as the full address of the lookup table. Notice that the full address displays the page, while C does not understand this extra digit. If you wish, you can remove the _rom keyword, rebuild, and again send and run the program. You will notice that the address changes from 0x8*** to 0x2***. Compare these values against the Memory Map and you will see that address 0x8000 and above is in paged memory.

Accessing a _rom array is only guaranteed to work within the same C file. To get around this problem, simply use the safeGetLUT() function that is provided in the Sine Lookup Table source code file.

You can also type:

printtable( )

from the console to generate the C source code for this lookup table. Notice that it takes longer to print out this data. This is because each entry is calling sin() directly. Calling main is much quicker, demonstrating the utility of using this lookup table to boost runtime execution speed.

 

Memory allocation for C strings

When a string literal is declared it is generally stored in common memory – the area of RAM that is accessible regardless of the current memory page selected. Even if a string literal is not assigned to a declared character array, it still is stored as a constant in common memory. Consequently, if an application needs very large string literals or a large number of string literals, for example if it involves a lot of user interaction and many separate calls to printf, then the necessary string literals may end up occupying a significant portion of the common memory space.

A solution to this problem is to store strings in flash ROM, and only copy a string to RAM when it is needed. The Forth operating system provides a scratchpad area of common memory called pad for each application task that is intended for temporary data manipulation such as this. A set of macros described in this section makes it simple to declare a string in flash ROM, copy it to pad, and reference it, thereby avoiding additional consumption of common memory space for each string declared.

SAVE_STRING LOAD_STRING LOADED_STRING LOAD_STRING_TO
 

Declaring and storing a string literal

The first step is to declare the string literal, generally at the top of your main application source code outside of any functions, using SAVE_STRING to store it in flash ROM:

SAVE_STRING Example

1: #include <mosaic\allqed.h>
2:
3: SAVE_STRING( greeting, "Greetings, current date is 20%02d-%02d-%02d.\n\n" );
 

Loading a string literal into memory

At the point in your code where a string literal is to be used, it must first be loaded into the Forth task's pad using LOAD_STRING. Note that the size of the Forth task's pad places a limit on the size of string literals that may be used, and a string larger than pad will be truncated when loaded into pad. At this time, only medium and large tasks have a pad available, so this method may not be used within small tasks. Currently the size of pad in medium and large tasks is 128 bytes/characters, including the Null character at the end of the string.

After a string is loaded into pad, it is referenced using LOADED_STRING. No other operations should be performed between loading and referencing the string, particularly no Forth functions should be called, as any function may make use of pad and thus overwrite the previously-loaded string.

LOAD_STRING Example

 5: int main()
 6: {
 7:   int year, month, day;
 8:
 9:   ReadWatch();
10:   year  = WATCH_YEAR;
11:   month = WATCH_MONTH;
12:   day   = WATCH_DATE;
13:
14:   LOAD_STRING( greeting );
15:   printf( LOADED_STRING, year, month, day );
16:
17:   return 0;
18: }
 

Loading a string literal to a location other than pad

This method should only be used if there is a reason that the Forth task pad is unavailable at the time a string literal needs to be loaded. Note that allocating a character array of a certain size as a local variable will cause a stack overflow and memory corruption.

LOAD_STRING_TO allows you to specify where in common memory to place the string, along with the size of the destination memory area. The address given must be in common memory, and the size must not define a range that goes beyond common memory.

LOAD_STRING_TO Example

 5: int main()
 6: {
 7:   int year, month, day;
 8:   char local_buffer[ sizeof(greeting) ];
 9:
10:   ReadWatch();
11:   year  = WATCH_YEAR;
12:   month = WATCH_MONTH;
13:   day   = WATCH_DATE;
14:
15:   LOAD_STRING_TO( greeting, local_buffer, sizeof(greeting) );
16:   printf( local_buffer, year, month, day );
17:
18:   return 0;
19: }
 

Using 16-bit and 32-bit pointers

The GNU C (GCC) compiler targeted to the 9S12 (HCS12) microcontroller typically treats pointers as 16 bit values. Extra steps have to be taken in order to get a 32-bit extended address of a symbol, instead of just getting its regular C 16-bit pointer. Mosaic drivers and kernel (operating system) functions understand 32-bit pointers, and supplying a regular C pointer using the address of (&) operator would be insufficient if the data or function being pointed to were not in common memory (i.e., not a variable in RAM or EEPROM). You can obtain the 32-bit extended address of any symbol, regardless of type or whether it is in common or paged memory, by using the MAKE_XADDR() macro. Here is a code snippet from the sine lookup table example above:

const _rom float sinLUT[TABLE_LENGTH] = { ... };
 
xaddr sinLUT_xaddr;
 
float _Q safeGetLUT( int degrees )
{
    float value;
 
    // MAKE_XADDR() saves the 32bit address of sinLUT into sinLUT_xaddr
    // which we can use to access our _rom lookup table regardless of
    // which page we are on.
    MAKE_XADDR( sinLUT );
 
    // We pass the full 32bit address of sinLUT[] to FetchFloat
    // We use simple arithmetic to offset from the base of the array.
    value = FetchFloat( sinLUT_xaddr + degrees * sizeof(float) );
    // Print our results
    printf("\nsin(%3d) = %f\n", degrees, value);
    // Also return our results
    return value;
}

When MAKE_XADDR( sinLUT ) is called, the full address of sinLUT is saved into sinLUT_xaddr. In other words, the 32-bit address is stored into a symbol whose name has the suffix _xaddr added to the name passed to the MAKE_XADDR() macro. Before calling MAKE_XADDR() the symbol sinLUT_xaddr must be declared in the source code. After invoking MAKE_XADDR() the full address is known; this address is passed to the operating system function FetchFloat(). Examine this output from main(), generated by the "Sine Lookup Table", for example,

The C address is     0x  80e5
The full address is  0x 580e5

C is not "page smart", but thanks to MAKE_XADDR() the program knows exactly which page the data is on (page 5), enabling the call to FetchFloat() to access the data in paged memory.

 

Locating items in EEPROM

Non-volatile EEPROM (Electrically Eraseable Programmable Read Only Memory) provides a convenient place to store calibration constants and other values that rarely change. EEPROM is in common memory. Thus, variables placed in EEPROM are persistent and un-writable by usual means, and their addresses are directly available to code on all pages of memory, just like regular variables. When you initialize EEPROM variables in a declaration, they will be properly initialized at download time. These variables may be read like any other, but attempts to change them with a standard C assignment statement will fail. To write to EEPROM at runtime, use the functions StoreEEChar, StoreEEInt, StoreEELong, StoreEELong, and ToEEPROM as described in the C Function Glossary (A-H). The following declarations show how to create a single byte or integer "variable" located in EEPROM:

char _eeprom myeepromvar;
int  _eeprom myeepromvar;

The _eeprom tag may actually be placed anywhere on the line.

Once an EEPROM variable is declared, you must initialize it at runtime within a routine called by your main.

To initialize or set this value, simply use StoreEEChar or StoreEEInt like this:

StoreEEInt( 1000, &myeepromvar );

Reading from this EEPROM variable is the same as reading from a normal (RAM-resident) int variable.

 

Using function pointers

Sometimes, it is useful to refer to functions anonymously by loading a pointer to the function into a variable. When the calling code might not know what function it is calling by name, using interchangeable function pointers is a useful strategy. Mosaic controllers rely on function pointers in a variety of ways including invocation of function call-backs from GUI Toolkit button press events, and activating a function as a task’s infinite-loop action routine. It is generally safe to use the standard native C syntax for function pointers to be used by C. Here is a simple example of this technique:

float (*myfunc_ptr)(int, char);      // create a standard function pointer
… …
{
  … …
  myfunc_ptr=&SomeFunction;          // set the pointer to point to SomeFunction
  return myfunc_ptr( 0x23, ‘c’); // execute SomeFunction using its pointer
}

The reason that this is safe is that the pointer returned is that of the "trampoline" in common memory (as described here) and not that of the original function in paged memory. This layer of abstraction is the primary method that C uses to ensure that the function pointer works regardless of which page the function is on.

You can avoid the use of the intermediate trampoline function by using the method described in the prior section to create an extended address pointer that points to the real function. You can obtain the real 32-bit extended address by simply using MAKE_XADDR() and defining a symbol with the same name as the function with _xaddr appended as a suffix. At runtime the code will initialize this 32-bit symbol to point to the function in paged memory. Note that the 32-bit extended address cannot be used directly as a C function pointer because GCC supports only 16-bit pointers.

 

Specifying absolute addresses

Somtimes a program needs to create a variable or function that points to a specific place in memory, thus completely bypassing the linker’s memory management. This is quite rare in practice, so look carefully at other alternatives before creating absolute symbols. Always feel free to contact Mosaic Industries for support and advice on how to implement your memory needs. Many absolute symbols are already defined by the system libraries such as those that point to the processor registers. To reference a specific memory location, which must be specified as 16 bits, use the following syntax:

*(volatile unsigned char *)(0x1234)

This example refers to 1 memory containing 1 byte at address 0x1234.



See also → 4: Additional Information for Advanced Users

 
This page is about: How to Write C Code for Mosaic IDE Plus™, #include Files, Accessing Wildcard IO Modules, Using Pointers and 32-bit Absolute Addresses on 9S12 (HCS12), Calling Forth Routines, Using Lookup Tables, Locating Objects in Non-volatile EEPROM Memory – Describes include files, accessing Wildcard IO modules, using pointers and 32-bit absolute addresses in the 9S12 (HCS12) processor's memory space, calling Forth routines, using lookup tables, and locating objects in non-volatile EEPROM memory.
 
 
Navigation