An object oriented language such as C++ enables data and code to be
encapsulated into objects, which can then be utilized without any
knowledge of their internal workings.
This approach is particularly attractive when a large software team
is used for a project. Different members of the team will have
different knowledge and capabilities and it is the encapsulation of
that expertise which is so useful.
This perspective is strongly emphasized in embedded software
development, where there may be a wide spread of expertise from the
"embedded expert", who is used to working close to the hardware with
drivers etc., to the applications specialist, who knows little about
the ways that embedded systems differ from desktop computers.
If the expertise of the embedded expert can be encapsulated into C++
objects, the applications specialists can proceed efficiently and
safely. In the case study that follows, a classic embedded system
problem will be addressed: the handling of a write-only port.
Case Study: A Write-only Port
It is not uncommon for an embedded system to have a device (a port) to
which a data value may be written, but from which no data may be read "
a write-only port.
Although this sounds like a joke played upon software engineers by
the hardware designers, it is really just a design which makes economic
use of hardware design elements. Handling a write-only port in software
is reasonably straightforward " it is just a matter of keeping a
"shadow" copy of the last data written.
The matter becomes more complex when, as is often the case, the bits
in the port have a selection of functionally unconnected purposes. This
means that a number of parts of the software need to use the port
correctly, which presents some challenges.
A Solution in C
It is entirely possible to implement a solution for write-only ports in
C. Here is some possible code:
extern int ports[10];
int shadow[10];
void wop_set(int port, int bit)
{
int val;
val = 1 << bit;
shadow[port] |= val;
ports[port] = shadow[port];
}
void wop_clear(int port, int bit)
{
int val;
val = ~(1 << bit);
shadow[port] &= val;
ports[port] = shadow[port];
}
Although this code will do the job, it suffers a number of
shortcomings:
1) There is no provision for
initializing the data. Something (the linker probably) would need to
map ports[] onto a sequence of write-only ports. At some point, an
initial values need to be written to the ports and to shadow[].
2) There is no mechanism
compelling programmers to use these functions " accessing ports[] and
shadow[] directly is a possibility, even if it is ill-advised.
3) The code is not
reentrant, which would be a problem if a real-time operating system
(RTOS) is in use or even if interrupt service routines might need to
access the ports. Reentrancy could be implemented, but would be very
application specific.
4) This bundle of code and
data is not encapsulated, so distribution is not readily controllable.