Link here

Writing and Compiling Programs

Techniques for programming using the C language for the HCS12/9S12 MCU on the PDQ Board

In this Chapter we’ll explore the Mosaic IDE Plus™ tool suite for writing and editing your application program. You’ll learn:

  • How to efficiently use the editor and compiler to write and compile programs;
  • How to structure your project correctly from the beginning;
  • Coding and file-naming conventions;
  • How to access the PDQ Board’s onboard functions.

Using these techniques will speed the development of your instrument control applications using the PDQ Single Board Computer (SBC).

 

Writing your application program

Using the CODEBLOCKS editor and GNU C compiler

The Mosaic IDE Plus™ Users Guide details the IDE which you will use to edit and compile your program. Forth programmers will use only the text editor and the terminal, while C programmers will find the entire toolset useful. If you have not read this document, you may want to take a look at it now.

 

File management for large programs

It is often useful to break up an application program into multiple source code files. It is important that the compiled size of a single .C file not be larger than a single memory page. There isn't a direct conversion from lines of C code into compiled bytes, however a general limit of 150 lines per file will keep code size within reason. If your code overflows the boundary of a memory page, you will see one of the following error messages:

  1. Error: Ran out of room allocating section .text.3 object
  2. Error: region data is full …
  3. banked address [0:ae0f] (ae0f) is not in the same bank as current banked address
  4. m6811-elf-objalloc.exe has encountered a problem and needs to close.
 

All about header (.H) files

When you click Build→ Build, each .C file in your project is compiled separately. After this step, the resulting binary files are linked together to produce the final product, a .DLF file.

The purpose of a .H file is to notify the compiler of all the functions provided by a .C file, without explicitly knowing the body of these functions. This is also called a forward declaration. In the following example, the 'process.c' file has a line near the top to #include the 'data.h' file. When the compiler compiles process.c, it trusts that a function matching 'int AcquireData( int )' exists somewhere in the project because it is specified in the .H file. Without this .H file, the compiler will throw an error. At link time, the compiler assembles all the compiled code and verifies that the 'AcquireData()' function actually exists. If the function exists only in the .H file, but not in a .C file, you will receive a link error.

.H files should not declare anything that takes code space. They exist purely to satisfy the compiler. .H files may be included by many .C files, and therefore they are compiled many times. If a .H file declares a variable or function, it will be re-created one time for every .C file which includes it. At link time, this will cause redefinition errors.
 

Writing multiple source code files

This section describes the steps a programmer can take to add additional source code files to a project.

  1. Open the project file in the Mosaic IDE Plus. This section assumes that a single file "main.c" already exists in the project
  2. Decide the purpose of your new file, and a good name for it. Generally, .C files group functions and variables together which work to accomplish a specific goal. In this example we will be adding a 'data.c' file
  3. Click File→ New File from the toolbar menu. Type in 'data.c' for the file name, and press 'save'.
  4. A prompt will appear asking: 'Do you want to add this new file in the active project'. Click 'Yes'.
  5. Click File→ New File again, and add 'data.h', Click 'Yes' to the prompt.
  6. Edit your data.c file. Add functions that accomplish your goal.
  7. Edit your data.h file. Before adding any forward delcarations, you must add a special structure to this file. This structure consists of 2 lines at the top of the file, and one at the end, something like this:
    #ifndef _DATA_H_
    #define _DATA_H_
     
    // ...
     
    #endif

    The keyword _DATA_H_ does not need to be similar to the filename, 'data.h', it can be any word. The first two lines must have the same keyword. It must also be unique throughout your entire project. The uniqueness of this keyword allows the compiler to only interpret this file once; preventing redefinition errors. This example keyword is in all capital letters because the C standard suggests that #define keywords be in capitals.

  8. Add forward declarations to your .H file. These declarations consist of the first line of a C function, with no function body:

    int AcquireData( int channel );

  9. Add extern declarations for your global variables. These are similar to a variable declaration, with the added keyword 'extern':

    extern int data_point;

  10. Add #define constants. These are usually used for numbers that are read-only at runtime, but may need to change as you continue to write your application:

    #define UTILIZED_INPUT_PINS (5)

  11. Add #include "data.h" to any file that needs to use variables or functions provided by 'data.c':

    #include "data.h"

With these steps accomplished, you have added a new file, data.c, to your project. When you click Build →  Build, this new file will be compiled and linked together with your main.c file.

Note that only one of the .C program files may include a function named main().

 

Multiple source code example

The following files are a mock example of a data acquisition program using good coding practices. The program is split into 5 files. The files data.c and process.c have matching .h files, while main.c does not:

data.h

#ifndef _DATA_H_
#define _DATA_H_
 
// Forward declaration of all functions in the accompanying .C file
int AcquireData( int channel );
 
#define UTILIZED_INPUT_PINS (5)
 
#endif

data.c

#include <mosaic\allqed.h>
#include "data.h"
 
// Acquire data from input channel
// This is a mock function
int AcquireData( int channel )
{
    int return_value;
    return_value = 1;
 
    if( channel > UTILIZED_INPUT_PINS )
    {
        return_value = UTILIZED_INPUT_PINS;
    }
    return return_value;
}

process.h

#ifndef _PROCESS_H_
#define _PROCESS_H_
 
// Forward declaration of all functions in the accompanying .C file
int ProcessData(int channel);
int ConversionFormula( int input );
 
// This global variable is visible to all .C files in the project which
// have #include "process.h" at the top
extern int data_point;
 
#endif

process.c

#include <mosaic\allqed.h>
#include "process.h"
#include "data.h"
 
// This global variable is visible to all functions in this .C file
// If other .C files need to use this variable, they must have #include "process.h"
int data_point;
 
int ProcessData(int channel)
{
    // Mock function for acquiring and processing data
    int data;
    data = AcquireData( channel );
    data = ConversionFormula( data );
    return data;
}
 
int ConversionFormula( int input )
{
    // Mock conversion formula
    input = input + 1;
    return input;
}

main.c

// Mosaic Industries sample application
 
#include <mosaic\allqed.h>
#include "process.h"
 
// Here, main.c only has #include "process.h" and not "data.h"
// This is possible because main.c does not directly call any of
// the functions in data.c.
 
void Welcome()
{
    printf("\nWelcome to example program\n");
}
 
void Results( int data )
{
    printf("Final data output: %d\n", data );
}
 
int main()
{
    int final_data;
    Welcome();
    final_data = ProcessData(1);
    Results( final_data );
    return 0;
}
 

More about this example

  1. All of these files must be added to your project.
  2. Do not #include a .C file. This causes the C compiler to treat all of your source code as one large block that cannot be allocated across memory pages, causing the memory errors described above.
  3. The #ifndef, #define structure found at the beginning of the .H files prevents functions and variables from being defined twice. The name used in this structure must be unique throughout your project.
 

Stylistic conventions

Commenting your code

At the top of the GETSTART.c source code file are some comments that tell what the program does. Single- or multi-line comments can be enclosed in the standard

/*    */

delimiters. The double-slash

//

token means that the remainder of the line is a comment; this comment marker works for either C or Forth source code, and can even be used to type comments at the terminal prompt. Note that the editor colors all comments differently to make it easy to distinguish comments from source code. C keywords are also colored differently than user-defined routines. You can change the default colors if you like. To change the colors click Settings →  Editor, then choose the "Colors" tab.

 

Coding style conventions

The example programs included with Mosaic IDE Plus follow several stylistic conventions. Here is a brief summary:

  • Macros and constants are spelled with CAPITAL_LETTERS.
  • Variable names are spelled with small_letters.
  • Function names use both capital and small letters, with underscores or capital letters indicating the start of a new subword within the function name. For example:
void SaveCircleParameters(void)

To minimize the need to skip from one file to another, we have decided not to group all #define statements in a header file that is separate from the program being compiled. Rather, the #define statements are defined close to where they are used in the program file that is being compiled.

 

C language file naming conventions

C source code files have the .c extension, and header files have the .h extension. When you use the Mosaic IDE Plus to compile a source code file with the filename,

NAME.c

several files with the extensions shown in Table 4-1 are created:

Files Created by the C Compiler
FILENAME.ext Description
NAME.c Source code text file created by you, the programmer
.deps\NAME.d List of header file dependencies for each source code file
.objs\NAME.o Compiled object code from first stage of compiling each source code file
asms\NAME.s Assembly output text file created by the C compiler for debugging
NAME.dlf Final download file to be loaded on a PDQ Board; includes S-records of compiled code and initialization data, and definitions for interactive debugging
NAME.elf The ELF binary file is the primary output of the compilation and linking process. It is in the GNU ELF32 format which is readable by various third party software packages and by the GNU tools. The build system extracts various sections from this file to form the download file.
NAME_symbols.txt A symbol table holding the final memory addresses of C variables and functions at the end of linking, which may be useful for debugging. See the --syms option for objdump for ELF files for details.
NAME_function_sizes.txt A list of the total size of code compiled from each source code file, as a portion of the PDQ Board memory page size. Keeping the total amount of code in each file smaller than the size of one page will prevent linker errors.
NAME.cbp Codeblocks Project File; includes information about every C file, and general project options, in XML format
NAME.layout Saves information about which project files were open at previous close
Intermediate files deleted during normal compilation
NAME_shifted.elf This file is only useful for symbol table generation. More
NAME_prelink1.elf This is the elf that is generated from prelinking all objects- internally the file is not yet linked, but rather contains ALL code that will end up in the final target. This is the first link stage.
NAME_prelink.elf This is generated from the _prelink1 file. more
NAME_code.txt Binary S-record file extracted from the final elf target. For the PDQ Board this file contains only the target executable code; for the PDQ Board Lite it contains both executable code and also the initialization data for initialized variables.
NAME_data.txt Binary S-record file extracted from the final elf target. For the full PDQ Board only, this file contains an image of the RAM (.data) section of initialized variables to be initialized into RAM at program startup.
NAME_forthcalls.txt A Forth file which gets bundled into the .dlf file which holds headers for _Q functions and _QV variables that may be used for interactive debugging
NAME_post.txt This file contains all the text that is appended to the download file. Text can be added to this section using the DLF_APPEND() macro. It becomes part of the download file (.DLF).
NAME_pre.txt This file contains all the text that is to be placed at the beginning of the download file. Text can be added to this section using the DLF_PREPEND() macro. It becomes part of the download file (.DLF).

While this list may seem overwhelming, you won’t have to worry about most of these files. You’ll create your NAME.c and *.h source code and header files in a directory of your choice, run the GNU C automated Make Tool by clicking Build →  Build, and send the resulting NAME.dlf download file to the PDQ Board using the Terminal program. In fact, unless you tell it otherwise, the editor’s File menu will typically show you only files with the .c and .h extensions (Source Files); you won’t have to wade through the files with the other extensions. Similarly, the Mosaic Terminal typically lists only files with the .dlf extension, so it will be easy to select the download file to send to the PDQ Board.

 

Using C function prototypes

This stylistic convention deserves its own section. We strongly urge that you define or prototype each function before it is called. If the compiler generates a warning that a function has been called without a prototype, we recommend that you check your source code and insert the required function prototype, or move the definition of the function so that it is defined before it is called.

A prototype is a declaration that specifies the function name and the types of its return value and input parameters. For example, the following is a valid function prototype:

float _Q CalcArea( unsigned int radius);

This declaration specifies that CalcArea() is a function that expects one unsigned integer input and returns a floating point value. As discussed below, the _Q tags the function as one that is inter-actively callable during debugging. The CalcArea() function can then be defined later in the source code file. If a function is prototyped in one file and defined in another, add the extern specifier before the prototype.

You can preface the function name in any function prototype with the _Q tag if you want to interactively call the function from the terminal. The PDQ Board’s onboard operating system maintains a list, called the dictionary, of the names of functions tagged with the _Q so that it can recognize them when you send a command line from your terminal.

Prototype and Declare the Parameter Types of Every Function

Defining or prototyping a function before it is called allows the compiler to help find parameter passing errors, and it also prevents unnecessary promotion of parameters that can render the code slower and defeat the PDQ Board’s interactive function-calling capability. To avoid unwanted promotion and runtime errors, each and every parameter in the function prototype or function definition must be preceded with a type specifier. For example, leaving out the unsigned int keywords in the prototype for CalcArea() above would lead to promotion of the input parameter, possibly resulting in a runtime error message from the compiler or linker.

Using function prototypes and definitions that explicitly specify the type of each and every input and output parameter results in more readable and reliable code.
 

Accessing the standard (kernel) C library functions

The command

#include < mosaic\allqed.h >

near the top of the GETSTART.c file is a preprocessor directive that includes all of the relevant header files for the PDQ Board, and all of the standard C header files. We strongly recommend that this statement be placed at the top of each C program file that you write. It gives you access to all of the pre-coded library routines that reside on the PDQ Board. These routines include all the I/O drivers for the A/D converters, digital I/O, serial ports, real-time clock, and many other useful functions. The ALLQED.h file also gives you access to the PDQ Board’s multitasking and paged memory capabilities, as well as the standard ANSI C library functions including printing and string conversion routines such as printf() and sprintf(). Including these files is very efficient; it generates almost no additional runtime code while giving you access to very powerful capabilities.

The names and contents of the header files included by allqed.h are as follows:

Standard C
Header Files
Contents
ctype.h Functions useful for testing and mapping characters
math.h mathematical functions for double precision numbers
stdio.h defines variable types, macros, and functions for file and character I/O
string.h defines a variable, macro, and several functions for manipulating strings
QED Kernel
Header Files
Contents
ANALOG.h Onboard ATD (Analog To Digital) driver routines
ARRAY.h Routines that dimension, access and manipulate Forth Arrays in paged memory
COMPILER_UTIL_MACROS.h Macro definitions that interface the PDQ Kernel, and driver libraries
HCS12REGS.h Macro definitions for the Freescale HCS12 (9S12) registers
HEAP.h Heap memory manager functions
INTERRUPT.h Interrupt identifiers and functions to facilitate posting interrupt handlers
IOACCESS.h Memory access primitives for the Wildcard and Smart I/O modules
MEMORY.h Memory access functions for paged memory, flash and EEPROM
MTASKER.h Multitasking executive and elapsed-time clock routines
NUMBERS.h Formatted number-to-string and print routines
NUMUTIL.h Pure C library of numerical utilities
OPSYSTEM.h Operating system functions for initialization, autostarting, and error handling
PWM.h Pulse Width Modulation I/O (PortP) driver routines
SEGMENTS.h Facilitates the integration of Forth segments, typically device driver libraries
SERIAL.h RS232/RS485, SPI, and IIC serial I/O driver routines
TIMERIO.h ECT (Enhanced Capture Timer, PortT) driver routines for timer-controlled I/O
TYPES.h Useful type definitions
USER.h Declarations of USER_AREA and TASK structures, and user variable definitions
UTILITY.h Defines macros such as MIN(), MAX(), ABS(), TRUE and FALSE
WATCH.h Routines that set and read the battery-backed real-time smart watch
 

Do not nest functions!

You can call any of these functions from within your C code. There is one limitation however:

Do not nest functions!
Do not nest calls to any of the functions declared in standard C or these operating system header files. In other words, do not include function as a parameter in another function. Instead, call the functions sequentially, using a temporary variable to save the result of the first function and then use the variable as the input parameter of the next function.

There are several reasons for this limitation:

  1. The Forth operating system will not allow a function call to be included as a parameter in the function invocation of another Forth function call. Further, many functions that are callable from C are actually defined in the QED-Forth Kernel or a QED-Forth Kernel-extension library. This includes functions that are in the kernel on the PDQ Board, or are part of software distributions such as the Graphical User Interface (GUI) Toolkit. A call to one of these functions may not be made from within the parameter list of a call to another such function.
  2. The C compiler does allow functions to be used as parameters in another function call, but doing so greatly increases the stack space needed for the function, making run time errors owing to stack overflow much more likely. Many function calls in C, particularly calls to math functions, use the runtime stack very greedily, often setting up temporary buffers on the stack. Nesting such functions piles up those buffers on the stack, while calling them sequentially does not.

There is always a straightforward way to avoid nesting function calls: simply use a variable to hold the required intermediate return value/parameter. For example, if you need to use the Kernel function StoreChar() to store at xaddress 0x0F8000 the contents fetched by FetchChar() from xaddress 0x0F8100 in paged memory, you could execute the following statements:

static char contents = FetchChar( (xaddr) 0x0F8100 );
StoreChar( contents, (xaddr) 0x0F8000 );

This code is correct, while nesting the call to FetchChar() inside the parameter list of StoreChar() would be incorrect.

 

Initializing variables

Unlike the older textpad based Mosaic IDE, the GCC-based Mosaic IDE Plus for the PDQ Board supports compile-time initialization of variables, and properly carries out the initialization each time that main (the top level program) is executed. For example, if your source code contains the statements:

static int myvar = 0x1234;
static int otherVar;

then each time that main() is executed, myvar will be initialized to 0x1234, and otherVar will be zeroed. Careful attention to this issue is still warranted, however, as improper variable initializations are a major source of bugs in embedded systems programs.

 

Compiling programs

The Mosaic IDE Plus is the main software you will use to compile programs. The IDE Plus uses "projects" to manage your source code files. All files added to a project will be compiled at build time.

To compile a project that is already open, simply click Build from the toolbar menu, and select Build. This command will execute a build, and display all output in the "Build log" tab at the bottom of the IDE Plus window. For more information about compiling programs with the Mosaic IDE Plus, see Using the IDE Plus.



See also → Mosaic IDE Plus

 
This page is about: Using PDQ Board's GNU C IDE for HCS12/9S12 – Using the Mosaic IDE Plus™ tool suite for designing, writing, editing, and compiling your C language application programs. Mosaic's GNU C IDE helps you structure your project, manage your C files, code your application, and easily access the PDQ Board’s pre-coded onboard C library functions. header files, C code files, forward references, extern, run-time variable initialization, function prototypes
 
 
Navigation