Black Box with a View
Pages: 1, 2, 3
Hello (Embedded) World
When you start programming using an unfamiliar language or operating system, it is very helpful to write a Hello World program. Such simple programs are the first step in understanding the new environment.
For embedded systems, however, a Hello World program has a critical additional role. This role is to verify that the entire development tool chain--especially the target hardware--works. The process of running a program on an embedded platform is quite complex. You write the code on a host machine (typically a PC), compile it with a cross-compiler, and then upload the result onto the target through an interface (such as the JTAG adapter). Numerous problems can arise at each step.
For example, microcontrollers, even in the same family, often differ dramatically from each other. If you give the wrong options to the cross-compiler, or assume the existence of certain hardware features when writing your program, it is quite possible that the target device will be unable to execute the resulting object code.
The target hardware itself is often an early prototype; in the embedded world, it is rare for the electronics design to be completed before software development starts. Hardware errors and component failures--very common in such experimental devices--have a drastic impact on the operation of embedded software.
Finally, uploading the compiled program to the target is not fully reliable; it is not unusual for the transfer to introduce errors into the object code.
As mentioned previously, typical microcontroller applications do not use an operating system. There is no console, no log file, nor any other standard error-reporting mechanism. In consequence, embedded software often fails silently, with no feedback regarding the cause of the fault. In this situation, a simple Hello World program is the key to creating a minimally functional development environment. Example 1 is such a program.
/* This program flashes the LED connected to pin 1 of port 2 on
the MSP430F149. The easyWeb2 development board has an LED
connected in this way. */
#include <io.h>
int main(void) {
P2DIR=0xFF; /* Configure all pins on port 2 as outputs. */
P2OUT=0x02; /* Set pin 1 to the high state; all others to
the low state. The mask "0x01" would set pin 0
to the high state, "0x04" would set pin 2
to the high state, etc. */
for(;;) {
/* The following two lines implement a very crude delay loop.
The actual length of the delay can vary significantly.
This approach may not work with all compilers. */
volatile int i; /* Declare "i" as volatile ... */
for(i=0;i<20000;i++); /* ... so that this loop will not be optimized. */
P2OUT=~P2OUT; /* Toggle the state of all pins on port 2. Every
pin that is high will be set low, and every
pin that is low will be set high. */
}
}
Example 1. An embedded Hello World
Note the volatile keyword--a very common occurrence in
embedded C development. This keyword most often designates a variable that may
be read or written to in the background (usually by an interrupt service routine, or ISR).
The compiler disables many optimizations for a volatile
variable (such as caching it in a register). In the example, the delay loop
would run much faster if i were not volatile; the
compiler could even remove the loop altogether. Some compilers may still
render the delay loop ineffective despite the use of volatile, but
mspgcc does not do so.
Save the code from Example 1 in a file (such as hello-world.c) and compile it with:
$ msp430-gcc -std=gnu89 -pedantic -W -Wall -Os -mmcu=msp430x149 \
-o hello-world.elf hello-world.c
This produces the binary file hello-world.elf. Now, load the file onto the target using the pyJTAG tool.
$ msp430-jtag -epv hello-world.elf
You should see a flashing LED on your development board. You may have to
type msp430-jtag.py (note the .py extension) instead
of just msp430-jtag, depending on how you installed pyJTAG.
If you are not using an easyWeb2 development board, your LED may be
connected to a different port and pin of the microcontroller; in that case, you
will need to make a minor change to the code. For example, for the TI MSP430
Development Tool model MSP-FET430X140, use port 1 (P1DIR and
P1OUT) pin 0 (P1OUT=0x01 instead of
P2OUT=0x02).
Choosing the right compiler options is especially important for embedded
systems. In particular, note the -mmcu=msp430x149 option, which
selects the correct MSP430 family member for the easyWeb2 development board.
You will need to change this (see the "Compiler options" section of the mspgcc
manual) if you are using a different variant of the MSP430. The
-std=gnu89 option selects the ISO C 1989 standard, with GCC
extensions. Some mspgcc header files require the extensions in order to
compile. gnu89 is currently the default if you do not specify any
-std option.
The -Os option tells the compiler to optimize the code for
space rather than speed. While space optimization is not necessary in this
small example, it is a common requirement when compiling more complex embedded
software. In order to improve standards compliance and catch some unsafe
constructs in the code, use the -pedantic, -W
(capital W), and -Wall options. The GCC manual describes
these options in detail. Finally, the -o option specifies the
output filename.
It is also important to understand the options to the target programming
tool (pyJTAG in this case). The example given uses three single-letter options
(-epv). The first option erases all the flash memory of the
target device, the second programs the device, and the third verifies the
programming operation. The file used to program and verify the device is the
last item on the command line in the example. For more information on pyJTAG,
use the -h option (msp430-jtag -h or
msp430-jtag.py -h), or see the pyJTAG documentation in the mspgcc
manual.
The highly primitive nature of the embedded Hello World is deliberate and very important. You want the lowest possible chance of something going wrong. This kind of program is something that you will write many times if you work with embedded systems. If the program runs, you immediately have the answers to several key questions about your development environment:
- You have figured out how to write a functioning program for your device.
- The cross-compiler generates the right code for your microcontroller.
- The process of loading the executable onto the target system works.
- The hardware is at least minimally functional; there may still be many mistakes, but the microcontroller can run a program, which is the critical first step.
The next article will cover several important embedded development techniques. These design patterns form the foundation for the subsequent discussion of connected embedded systems.