Black Box with a View, Part 2
by George Belotsky02/02/2006
This article discusses several key development techniques for microcontroller-based embedded systems. The information covered here builds on the first article, Black Box with a View. You should review that article at least briefly, because it describes the tools (hardware and software) needed to follow the examples.
If you lack experience in working with microcontrollers, you should also read Appendix A (which discusses arithmetic operations) and Appendix B (which describes bitwise operations). These appendixes will help you avoid many common mistakes in microcontroller programming.
The design patterns presented here will help you handle the complexity of the network-enabled embedded environment. Connected embedded systems must support a variety of different functions (such as the network hardware and protocol) that simpler devices without connectivity never require. Layered, object-oriented architectures are highly effective in such situations. Yet how can a microcontroller, with its severely limited resources, support these server-grade techniques?
Despite the fundamental differences between embedded systems and conventional PCs, the best development methods for both turn out to be variations on a common theme. The constraints of the embedded environment, however, reduce the design patterns to their most austere, minimalist form. In consequence, you must have an absolutely clear understanding of the patterns in order to apply them successfully in the embedded context. That is the purpose of this article.
A Layered (Hello) World
Layering is an important development technique for managing complexity in software. For example, operating systems use device drivers to create a consistent interface to low-level hardware functions.
Example 1 is a device driver for the LED from the original Hello World program in the first article. Electronic designs often specify several such LEDs to indicate the status of the system (power on, error, data transmission, etc.). These LEDs are simple devices, so one short header (or include) file is sufficient to implement a complete driver.
/* File "LED_driver.h".
This file is an LED device driver for MSP430 family of
microcontrollers. It uses some Object-Oriented Programming (OOP)
features.
The first two lines of code as well as the last line prevent this
file from being processed more than once, which would lead to
errors. This "header guard" is a standard technique in C
programming. */
#ifndef LED_DRIVER_H
#define LED_DRIVER_H
/* A structure that stores the state of the LED device driver.
You must create an instance of this structure for each LED. */
struct _LED {
volatile unsigned char* reg;
unsigned char mask;
};
/* Use the following "typedef" to create LED objects.
The single-element array works just like an ordinary pointer in
most cases, except that it cannot be made to point to another
location. This effectively creates a "safe pointer", which acts
more like a C++ reference. */
typedef struct _LED LED_Ref[1];
/* Macro to initialize an LED object (i.e. the constructor).
This macro is an example of a comma expression, because commas
separate the subexpressions, which the system evaluates in
left-to-right order. The result of the last subexpression becomes
the result of the whole comma expression. In this case, the result
is not used -- only the side effects of the comma expression are
important. */
#define LED_Init(ledp,r,m) ((ledp)->reg=(r), (ledp)->mask=(m))
/* Macro to toggle the state of an LED. */
#define LED_Toggle(ledp) (*((ledp)->reg) ^= (ledp)->mask)
#endif
Example 1. The LED device driver
You can use Example 1 as a class definition, to create LED objects. Example 2 illustrates this process, by modifying the Hello World program from the first article to use the device driver of Example 1.
/* File "hello-world-layered.c".
This program illustrates the use of a device driver in an embedded
system.
Just like the example in the first article, the code here flashes an
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>
#include "LED_driver.h" /* The LED device driver. */
int main(void) {
LED_Ref led; /* Create the LED object.
BEWARE THAT THE OBJECT IS NOT YET INITIALIZED.*/
P2DIR=0xFF; /* Configure port 2; all pins are outputs in this case. */
LED_Init(led,&P2OUT,2); /* Initialize the LED object. */
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. */
LED_Toggle(led); /* Toggle the state of the LED */
}
}
Example 2. The layered Hello World
To test the layered Hello World, place the files from Example 1 and Example 2 in the same directory, change to that directory, and compile the program with the following command. Note that in a larger project, you should use a build tool such as make instead of running the compiler directly.
$ msp430-gcc -std=gnu89 -pedantic -W -Wall -Os -mmcu=msp430x149 \
-o layered.elf hello-world-layered.c
Now, load the result onto the microcontroller, just like you did in the first article:
$ msp430-jtag -epv layered.elf
The new program is not much larger than the original version, but it has several important features:
- The LED device driver provides an object-oriented interface to the underlying hardware. One hardware initialization step, however (
P2DIR=0xFF), takes place outside the driver. That allows for future sharing of the hardware by multiple devices. - There is no dynamic memory allocation from the heap (for example, with
malloc). That is why it takes two steps to create the LED object. - The implementation of the LED driver uses macros instead of function calls.
These features, some of which may seem counterintuitive, reflect important characteristics of embedded software development. The next three sections of this article consider each of the three features in detail.
|
Related Reading Linux Device Drivers |
