I'm writing a real-time operating system for microcontrollers in C++11 - distortos. Currently I'm thinking about a C++ framework for various peripherals. The most basic peripheral which I would like to have there are input/output pins. Because such input/output pins can both be built into the chip or be external (I2C expanders, shift registers, ...) I plan to implement interfaces for them with pure abstract classes.
These are some of mine requirements:
- some pins can only be inputs and it shouldn't be possible to (easily) change their direction - for example if you change the direction of pin connected to push-button to "output" you can irreversibly damage the chip, the board or the device that is connected to the other side of the wire;
- some pins can only be outputs and it shouldn't be possible to (easily) change their direction - if you are using a HC595 shift register then this can only be "output" and nothing more;
- some pins can change their direction freely - this is required if you want to implement software I2C or software 1-wire;
Initially I have thought about doing it like this:
- The most basic interface class would be
InputPin
- it would only support "reading" the state of the pin. - Second interface class would be
OutputPin
- it would inherit from InputPin (as you can read the value of pin configured as output) and also add the ability to "write" the value. - Third interface class -
InputOutputPin
would inherit fromOutputPin
and also provide a function to change direction.
Such approach has a very nice feature that I can use use any derived class in function that requires one of the bases. But actually there is one substitution that is not always correct - using InputOutputPin
in a function that requires OutputPin
can be a problem when the instance of InputOutputPin
is currently configured to be an input...
I've tried googling around for some inspiration and it seems that there is not much to use. ARM's mbed framework has 3 completely unrelated classes (there is no inheritance there) - one for "in", one for "out" and one for "in/out". Most of the code that I found has only one class that is for "in/out". I've actually found an article about exactly the same issue that I'm facing - http://www.embeddedrelated.com/showarticle/108.php . The solution presented there doesn't suit my needs - in a multi-threaded environment the operation of changing the direction of the pin is not so simple and usually requires exclusive access to configuration registers (the change is a read-modify-write operation).
I wouldn't like to implement any solution where the functions could fail (for example return error codes when the operation is not possible) - this would really complicate the usage.
Implementing a type-conversion operator - so that conversion from InputOutputPin
to OutputPin
would be done in a function which also makes sure the direction is correct - is error prone, because the reference to OutputPin
can be stored for later use and at that time the direction may have changed again.
Maybe I should just simplify the hierarchy - OutputPin
and InputOutputPin
would both inherit from InputPin
(this would always be correct), so OutputPin
wouldn't be a base for InputOutputPin
. This looses some of the flexibility, but I'm not sure that such substitution would be required anyway.
InputOutputPin
react if the pin is configured for input and you try to write to it?InputOutputPin
as output you must make sure that the direction is correct. But this wouldn't be so evident when convertingInputOutputPin
toOutputPin
and using it through reference to base class - in that case it can be assumed that the direction is always "output", which is not necessarily true in that case and cannot be ensured...