Other Possibilities
The ideas presented here may be developed further to add additional
capabilities or to address different, but similar, problems.
Possibilities include:
* Add an exclusive-OR operator: ^=
* Provide a means of retrieving the shadow data (perhaps by
overloading the int casting operator)
* Treat a port as an array of bits (overloading the [] operator)
* Accommodate arrays of ports (at consecutive addresses)
* Apply the same ideas to handle indexed ports, where the "address"
of a device's internal register needs to be written first, followed by
the actual data reading or writing
C++ and an RTOS
When an embedded software application is being developed using a
real-time operating system, there are some distinct benefits to be
gained from the careful use of C++.
The two clear advantages are: application programmers do not need to
have detailed knowledge of the RTOS programming interface and the
application code is likely to be portable to an alternate RTOS, if a
change is necessitated.This topic is quite large and only a few aspects
may be considered here:
Task/Object
Creation. Most RTOSes have a conventional C function library
which serves as an application program interface (API). This may be
proprietary or may conform to a standard (like POSIX, for example). In
either case, many API calls may be quite complex, with numerous
parameters. This is an ideal opportunity for encapsulation of
expertise.
Consider the
creation of tasks. A base class could be designed which
characterizes a generic task. The key components of the class would be
a constructor (which creates the task), a destructor (which deletes the
task) and an empty virtual (i.e. replaceable) main() function.
An application task could then be defined by deriving a new class,
which replaces the main() function with the actual task code and adds
in other requirements (like the task stack perhaps).
Instances of the task could then be created by simply instantiating
objects from the class. Note the separation of task definition
(creating the class) and the actual creation of tasks (object
instantiation).
The same kind of approach may be taken to the handling of other RTOS
objects like mailboxes, event flags, semaphores, timers, queues, pipes
etc.
Thread Local
Objects. C++, like C, has no in-built concept of a thread or a
task " the language essentially assumes a single thread of execution.
By creating a class that represents a task, this shortcoming is
effectively overcome. This brings with it some useful benefits. A key
one is the ability to have thread local objects (and variables).
When using an RTOS conventionally, it may be quite common to create
a number of identical tasks, each of which operates on different data,
while sharing code. This is a challenge as there is no straightforward
way to have conventional variables that are specific to a particular
task (i.e. thread local variables).
By adding member variables to the task definition class, each
resulting object has its own copy of each variable (unless they need to
be shared, in which case they need to be declared static).
An extension of this idea is thread local variants of other RTOS
objects. If the task definition class includes a declaration of a
semaphore (for example), that object will be created specifically for
each task instance and be inaccessible elsewhere.
Paired
Operations. It is a common requirement in real time applications
(with or without an RTOS) to define paired operations: two actions that
complement one another and are executed at the beginning and end of a
code sequence.
For example, if a set of instructions must be executed without any
possibility of interruption, it may be preceded by a disable interrupts
and succeeded by an enable interrupts. It may be problematic to ensure
that both operations are performed, particularly if the code logic is
complex.
An object oriented "trick" can provide a simple way out. All that is
required is a simple class which only contains a constructor and a
destructor. The constructor disables interrupts and destructor enables
them again.
<>Then it is simply a matter of instantiating an instance of this class
(an object) at the head of a block. Interrupts will be turned off on
entry to the block and turned on again when the object goes out of
scope " i.e. when execution leaves the block by whatever means. The
structure of the code looks like this:
{
object declaration
(constructor disables interrupts)
(destructor re-enables interrupts)
}
>
This approach has numerous applications. Others include: locking
resources, turning on/off an RTOS scheduler and providing pre-amble and
post-amble for an ISR.
Here is some possible code for a class to provide device locking
using an RTOS semaphore:
class Device_Locked
{
private:
static Semaphore device; // NB
static
public:
Device_Locked()
{ device.acquire(); };
~ Device_Locked()
{ device.release(); };
};
To read Part 1, go to: Why is C++
not more widely used?
Colin Walls has over twenty-five years experience in the
electronics industry, largely involved with embedded software. A
frequent presenter at conferences and seminars including the Embedded
Systems Conference he is a member of the marketing team of the Embedded
Systems Division of Mentor Graphics.
Click here to see Part 1.