Object-oriented C: HAL meets RTOS
Object-oriented C shines when used in programming a simple real-time operating system. I say "simple" because in many low-level firmware applications, simple is all you need. Obviously, there's no reason to take on a complex real-time operating system (RTOS) if you all you need are redundant processes and a way to send messages to them.
This month, I'll demonstrate how I incorporate object-oriented concepts into a simple signal-based RTOS. For convenience, I'll call this one SimpleRTOS. Our simple RTOS does a couple of, you guessed it, simple things:
- SimpleRTOS creates processes. Each process can run and stop and run again as I enter and exit the operating system. If I "enter" the operating system, my process is exited and the queue of messages sent to other processes is reviewed by SimpleRTOS to determine who has priority.
- SimpleRTOS sends and receives messages. Using the operating system, I can package up a message, give it a handle, and send it on its way to a designated process. Once received by the other process, it will enter that processes queue patiently awaiting priority. Once priority is given, the receiving process reads the message and executes until yet again I enter the RTOS.
Simple is good. I like simple. Simple has simple bugs. Complicated can have "really complicated" bugs. Not good! So again keep, it simple whenever possible.
To use what I explained about object-oriented C (OOC) and apply it to an RTOS, see the topology example below. The Execute Channel n boxes represent processes. Each box including the processes are OOC modules in the vein of my early explanation. So let's begin at the interface.
Click on image to enlarge.
Interface 1 and 2 could be any old interface. Once could be a serial port and the other a PCI bus interface. It doesn't matter. Notice that these "interfaces" are placed into the realm of objects.
The next layer down is what I'm calling an Interface Router. In the OOC module, I route commands and queries to and from the interface layer. Say that I send a query request for data into Interface 1. Some module needs to remember where the data request came from so I can dutifully send the requested data back to the same interface. Router takes care of this for us. It remembers who requested it and routes the data to the right place.
The next layer down is what I call the Command Router. It could be called the Command and Query Router for that matter but a query is really just a type of command. Embedded in the command/query is the channel that the request is intended for. Say it's channel 2. Now at this point I want to get the RTOS in the mix. I now need to build a message that includes the process (Execute Channel 2), the command (in this case a data query), and any other critical data that the process may need to collect the requested data. This packet of variables is my "message." It is Command Router's job to package up the message and launch it into the RTOS (never to be seen again . . . kidding!).
The message will arrive safe and sound in the message queue of Execute Channel 2. Eventually this queue will be read and the message pulled from the queue. At this point I process the request, grab the data, and call Interface Router to return the data to the caller.
The layers below Execute our various firmware abstractions of the electronics. I always want to use abstraction before touching the hardware, and thus the hardware abstraction layer (HAL) plays that role. These devices could be the representation and variables associated with any aspect of the hardware. So this could be a power supply, a measurement circuit, a temperature sensor, an analog source, and so on. It doesn't matter. What does matter is that each one of these hardware representations is objectively implemented.
You should see that with this type of implementation everything is neatly segregated with public interfaces allowing access to each.
Note that simplification is the byproduct of creating basic C objects, using some simple RTOS features, and abstracting the hardware. They can all work together to develop a simple to implement and troubleshoot solution that is highly leverage-able, flexible, and expandable. Back to the trenches and see you soon.
Robert Scaccia is president of USA Firmware, LLC, which deploys teams to provide embedded consulting, firmware, software, hardware, product development, resourcing, and training needs. To learn more about Bob or his company, email him at firstname.lastname@example.org or check out his website at www.usafirmware.com.