Object oriented programming
in embedded systems is still not widely employed. Many programmers in
this
area either come from a hardware electronics background or are pure
computer scientists.
This article discusses one role that object
oriented programming can
play in embedded systems while maintaining requirements concerning
safety and reliability that are often imposed by standards like MISRA.
I will show how good
object-oriented design of embedded software can result in many
benefits - smaller code and improved ease of source-code maintenance -
but
with only a slight trade off in terms of performance.
Using an example as the starting point (a standard timer module
written in C that services timer units that are provided by nearly
every MCU) I will describe a six step process by which to
convert C code into object oriented C++ code classes.
Several steps in this process will illustrate a safe path for
migrating this source code into an object oriented class. An instance
of this class then is a HW- Timer-Peripheral with a much nicer
SW-Interface than registers and interrupts. Then it is a matter of
simply instantiating the timer class once per HW-Timer
available on the particular MCU.
This process can be applied to any situation where more than a
single instance of one type of HW-peripheral is to be used in an
embedded project. Measurements of performance and code size are
provided and topics like OO-in embedded systems and the improved
SW-Architecture will be discussed more generally.
The examples we will work through use two timers on an ARM7.
They
can be completely worked through without any real hardware, using
Keil's ARM7 simulator and
the Keil Realview Compiler only.
Because many
other MCUs have at least 2 on-chip-timers these examples can be ported
to other platforms, too.
Starting point: the standard
Timer-Module in C
A hardware-timer is a counter/comparator with reset- and
interrupt-logic. This timer "wakes up" an interrupt service routine at
regular programmed intervals. But many embedded applications need more
timers than HW-timers that are available on a given MCU.
We can duplicate the timer, by e.g. letting the HW-timer interrupt
at 1 ms intervals. The ISR can then call function A every 3 ms and call
another function B every 5 ms. This way we have used one HW-timer to
realize several SW-timers, each with it's own time base. This method is
fairly common practice.
For reasons out of the scope of this article the project's various
timer-functions are registered with the timer-module and will then be
called back when their time has come. The timer module (Figure 1 below) is based on a single
HW-timer and each registered timer-callback-function has its own time
interval. This is very similar to how a cyclic-task-scheduler works.
 |
| Figure
1. Module and process-flow overview. Registration of timer callbacks (1
& 2) and their cyclic execution (x, a and b) |
The time base of the timer module depends on the cycle-times
requested by the various application modules. If app. A wants 30 ms and
B wants 50 ms, then the HW-timer can realize this using an overall time
base of 10 ms. If A wants 9 and B wants 3 ms, then the main time base
needs to interrupt every 3 ms.
Raising the interrupt no more often than absolutely necessary saves
processor performance. This main-time-base is automatically adjusted
(by reprogramming the counter register) when timer-callbacks are
registered by means of a "greatest common divider" function.
 |
| Listing
1 |
Listing 1 above and Listing 2 below show excerpts of
the interface and implementation which realize this concept of a timer
module.
The result is a module that represents a single HW-timer and
provides many SW-timers.
Let us now return to the main topic and start converting it into a
class. Afterwards we will be able the instantiate a class for each
HW-timer, where each provides many SW-timers.
 |
| Listing
2. Implementation of timer module |