I started experimenting with Raspberry Pi GPIO using Python[1][2]. Then the original Gertboard[3] kit was released so I ordered and built one which enabled me to play with BCM2835 peripheral IO beyond basic GPIO. At the time the Gertboard had test and example C code available[4] that, as with the Python case, I felt could be expressed more cleanly. I thought it would be interesting to see what advantages C++, hopefully C++11, features and idioms might provide.
A thought: germ of a library
The originally provided Gertboard test and example C code, in a fashion similar to the Wiring Pi library[5], manages the Raspberry Pi’s BCM2835 peripherals by directly accessing peripherals’ memory mapped control registers from user mode by using mmap
to map sections of the /dev/mem
physical memory device. A single function sets up mmap
-ed regions in a rather long winded and repetitive way for all supported peripherals - even if only one or two peripheral regions are required. Oddly, for each mapped register-block a block of free-store is first allocated which is larger than required to ensure a correctly aligned block can be passed to mmap
. When done of course all these resources have to be explicitly released.
It occurred to me that in C++ I would wrap mapped of parts of /dev/mem
in a resource managing type that used the RAII (Resource Acquisition Is Initialisation)[6] idiom. It was this thought that was to lead to a Raspberry Pi user mode C++ library by me putting some code were my brain was so to speak and implementing such a beast. The result was a class template called phymem_ptr
, with most of the implementation being taken care of by the non-template base class raw_phymem_ptr
which works with un-typed pointers (void*
) .
Having implemented, and tested, phymem_ptr
using Phil Nash’s Catch library[7], I re-implemented one of the simpler Gertboard example programs – ocol.c
– in C++ using phymem_ptr
. I felt the result was a definite improvement but more could be done and the obvious next step seemed to be to start with basic GPIO and, following my thinking with the Raspberry Pi GPIO Python library, model the abstractions after C++ library IO. Of course as this would be solving essentially the same problem as the Python GPIO library there were bound to be other concepts that would transfer to the C++ implementation – a notion of GPIO pin number as represented in the Python library as the PinId
type for example.
In the beginning…
Having decided to write more than phymem_ptr
, its tests and the ocol-in-C++ example it was time to bootstrap a project. Things such as project name, licensing terms, directory structure and what development process to utilise had to be decided.
The name rpi-peripherals
was chosen for the library (though embarrassingly due to a typo it was rpi-periphals for quite a while – doh!). The longer term ‘peripherals’ was chosen rather than ‘gpio’ as the intention was for the library to provide support for more than just basic GPIO. As there was no reason to do otherwise the same dual BSD/GPL licensing used for the Python Raspberry Pi GPIO library was selected.
A fairly standard project directory structure was devised having few files in the project root and source, build and outputs etc. in various subdirectories such as src
, bin
, lib
. At the time of writing all the source with the exception of header files required for the public interface of the library is in the src
subdirectory with no further division. On the other hand all test and example source are in subdirectories tests
and examples
of src
respectfully.
I did not want the library to require any development tools other than what could be expected to be installed on a typical Raspberry Pi Raspbian installation (plus git
– keep reading…). That meant using the Raspbian distribution’s default version of g+_` (4.6.3) and I fell back to using `_make_` – well GNU make – and wrote a `_Makefile_` for (and placed in) each directory in which code was to be compiled, as well as a top level `_Makefile_` as a driver and a common file, `makeinclude.mak`, that defined common values such as directory and file names and tool names and flags. Code was developed on a Raspberry Pi running Raspbian, edited from a Windows session in Notepad+ with builds, tests etc. run via `_ssh
accessed from a Ubuntu Desktop Linux virtual machine. The project was placed under git
source control on GitHub as dibase-rpi-peripherals[8]. As previously mentioned Phil Nash’s Catch library was used for tests, which was added to the repository as a git sub-module
.
Rather than use GitHub’s repository wiki for documentation, as was done for the Python Raspberry Pi GPIO library, this time Doxygen commenting was used in header files, and a Doxygen project configuration Doxyfile
was added to the project.
What did you want for starters?
The scope for the initial functionality was mostly a subset of that of the Python Raspberry Pi GPIO library:
-
Targeting only Raspberry Pi Linux distributions, especially Raspbian
-
Single GPIO pin input and output, with no edge event support
-
A pin id abstraction
-
Internal pin in use / pin free resource management
-
Library specific exception types
Since the development of the Python Raspberry Pi GPIO library a major Raspberry Pi board revision had taken place that changed some of the pin assignments on the Raspberry Pi P1 connector and added a new P5 connector with additional GPIO pins. This meant that converting a P1 connector pin number to a BCM2835 GPIO pin number was no longer a simple fixed mapping. It was now dynamically dependent on which board revision the code was running on. Additionally, of course, the newer boards had a P5 to BCM2835 pin mapping. Hence additional support for determining a board revision was needed.
Note
|
Following the initial development of the library there have been many further additions to the Raspberry Pi family in the form of the A+, B+, versions 2, 3 and 4, compute modules and trimmed down Raspberry Pi Zero models. I am yet to find the time to completely update the library to support all these board versions’ GPIO specifics. |
First cut
The library usage would be broadly similar to the Python Raspberry Pi GPIO library: an object representing an input or output pin would be associated with a GPIO pin by opening the required pin which would be specified as a pin id type instance. If valid and the pin is not in use the open request would be successful and the GPIO pin object could be used to either read or write Boolean values from/to the associated GPIO pin. On GPIO pin object destruction the pin would be marked as free for further use.
Here we have several types of object: input and output pin objects, pin id objects, some form of GPIO pin allocation management object and of course direct access via a phymem_ptr
to the GPIO peripheral’s registers.
Rather than mapping the BCM2835 GPIO peripheral’s registers as an array of structure-less 32-bit words (i.e. unsigned integers), as had been done for the original ocol-in-C++ example, it seemed more reasonable to provide a type reflecting the BCM2835 GPIO peripheral’s register layout that could be used to specialise phymem_ptr
.
This rough structure outline is shown in Figure 1.
The ipin
, opin
and pin_id
types are intended as the public interface to the library while the rest would be internal to the library – in fact during refactoring after the initial development phase such code was placed into a separate inner namespace called internal
and the header files for the public entities were moved from the project src
to the include
directory. The ‘o’ and ‘i’ prefixes to ipin
and opin
were taken from C++ IOStream library std::istream
and std::ostream
naming.
So how do I register?
In order to create a structure representing the GPIO peripheral’s register-block detailed knowledge of register layout, usage and location are obvious requirements. Such detailed technical information is to be found in the Broadcom BCM2835 ARM Peripherals document[9], although as it contains errors and omissions the errata page at eLinux[10] is also useful.
Note
|
When perusing the Raspberry Pi’s Linux kernel source code[11] you will find very little specific BCM2835 support[12]; most chip-specific support appears to be provided by the BCM2708 specific code[13]. It is to be assumed that this chip is very similar in its peripheral support to the Raspberry Pi’s BCM2835, so detailed BCM2835 specifics may well apply to the BCM2708 as well. |
Separating the mapping of peripheral register block addresses with phymem_ptr
and the register block layout representation as manifested by the gpio_registers
class type for the GPIO peripheral allows instances of register layout representation types to be arbitrarily created in memory - as function automatic objects for example. This allows easy unit testing of the representation type to ensure it is sane before having to rely on it when mapped over a peripheral’s register block and fiddling with real peripheral registers.
It seemed that adding operations (member functions) for manipulating the fields of the GPIO peripheral’s registers would be convenient for users of the gpio_registers
type and would allow such operations to be fully unit tested. However, a balance had to be struck between spending time developing an all-singing all-dancing set of operations and just getting things done. The balance point I chose was to implement operations to access single fields. Many peripheral fields are bit fields and so in certain scenarios reading or writing to multiple bits fields in one go could be more efficient. I left this as optimisation territory, facilitated only by having all register data members be public. In fact the gpio_registers
class type was implemented as a struct
. I felt this was justified as it was intended to be a low-level type internal to the library.
I decided against using C/C++ bit fields – instead using 32-bit unsigned integer types as register data members with access via individual member functions. I suppose I could have looked up the specifics the GCC bit field implementation, checked whether they were compatible (e.g. guaranteed 32-bit reads and writes for underlying 32-bit integer field types) and if they were then utilised unions to allow accessing whole registers or bit field parts thereof. Such a scheme would probably cover many, maybe even most, cases.
As the gpio_registers
struct
was a direct reflection of the GPIO peripheral’s registers and fields I decided that all registers would be represented by data members and operation member functions would be provided to access all fields even if they were not known to be needed. One quite major upside of this decision is that it made me really read and understand the associated documentation - making me more able to later decide on what would be needed!
What pin was that?
The need to identify and validate GPIO pin numbers and their encapsulation in the pin_id
type has been mentioned in connection with the public ipin
and opin
types. However, it turns out, unsurprisingly when you think about it, that the GPIO peripheral has many register-sets containing fields for each of the BCM2835’s 54 GPIO pins. Thus operations often need to specify which GPIO pin they were to be applied to. As the pin_id
type would obviously be useful as a GPIO pin id parameter type in these cases its implementation occurred earlier than I originally anticipated.
The basic idea behind the pin_id
type is that instances may be explicitly constructed from a suitable integer type, and may be converted back to an integer type. However during construction the range of the integer passed is validated to ensure it is in the required range. If it is not a std::invalid_argument
exception is thrown. Hence, barring deliberate malice, if you have a pin_id
object you know it represents a valid GPIO pin number. Only allowing explicit construction makes clear what the number represents.
To support using Raspberry Pi P1 or P5 connector pin values the base pin_id
type is sub-classed to provide the logic for mapping from one value to another by the rather clumsily named rpi_version_mapped_pin_id
which provides a single, rather cumbersome, constructor. This class is not meant to be used directly, rather its two sub-classes p1_pin
and p5_pin
are designed to be used directly and provide constructors that require only a connector pin number as parameter, which is passed along with a bunch of fixed values relevant for each connector to their base rpi_version_mapped_pin_id
constructor. Note that this is a case where a base class destructor does not need to be virtual as by design all pin_id
sub-types add no extra state that might require cleaning up. Once successfully constructed a pin_id
or sub-type instance always represents a valid BCM2835 GPIO pin number value.
The rpi_version_mapped_pin_id
type’s constructor takes a 2-dimensional array mapping connector pin numbers to BCM2835 GPIO pin numbers for each supported major Raspberry Pi board revision – termed versions, along with dimension size values: the number of pins and number of versions supported by the mapping array. A major revision here is one that changes available connectors and/or GPIO pin associations with connector pins.
The implementation logic makes use of the rpi_info
type that lives in the outer rpi
namespace and provides an interface to obtain information on the Raspberry Pi system the code is running on. It currently only provides Raspberry Pi raw revision and version information, derived from the Revision
entry in the Raspbian /proc/cpuinfo
pseudo file. There is no formula to map revisions to versions and relies on published information[14]. Of course this means rpi_info
code will require updating to take advantage of future major board revisions. However the mapping arrays used by p1_pin
and p5_pin
would also require updating, and of course support for any new connectors would need to be added.This is an area of the code base that is likely to change while adding support for all the various Raspberry Pi models now available.
To support referring to Raspberry Pi P1 and P5 connector pins by name I created a set of static global constant pin_id
objects. These are created by specialisations of special static pin id type templates static_pin_id
, static_p1_pin
and static_p5_pin
that contain no data providing just a single conversion operator member function that converts to a pin_id
. Each template takes a single integer template parameter representing a connector pin or BCM2835 GPIO pin number. Each conversion function defines a static object of pin_id
type or associated sub-type. For the P1 pins that changed between board versions and the P5 pins, if none are used then the rpi_info
machinery should not be required. Those P1 pins that did not change between board versions are defined directly as their associated BCM2835 GPIO pin value by static_pin_id
objects. The case for using static_pin_id
objects over just defining global constant pin_id
objects is not as great as for the use of static_p1_pin
and static_p5_pin
objects. The main benefit would be ensuring initialisation before use of the function-scope static pin_id
object.
A future direction for pin_id
et al would be to investigate whether use could be made of constant expressions (constexpr).
Excuse me, is that pin free?
GPIO pins are a resource and as such I had to decide on an allocation policy. The easy part was deciding that a pin should not be permitted to be used by more than one object at a time within the same process. However the inter-process policy is not so easy. For a start there is no magic system wide registry of in use GPIO pins, so any policy implemented would have to rely on other processes doing likewise. There are various possible policies, including:
-
Ignoring the issue and only track pin usage within a process
-
Require pins to be marked as allocated beforehand by some other agent, and presumably marked as free after executing by a similar external agent
-
Require that pins are marked as free for use before allocation and mark them as allocated / free as required.
How to determine whether a pin was in use or free was another choice. This boiled down to either devising some sort of custom registry which would not be too useful if only my library used it or use some pre-existing indicator that might also allow such in use / is free information to work between libraries.
The most obvious policy, and the one I decided on, was to follow that which I had used for the Python Raspberry Pi GPIO library: check to see if the pin was in use by checking to see if it was exported from the /sys/class/gpio/
driver. If not then export it to acquire the pin and un-export the pin to release it. This scheme is in no way water tight. For a start any process with sufficient access rights can un-export an ‘allocated’ pin at any moment. Then there is the matter of un-clean exit from a process leaving pins marked as unavailable because the un-exporting de-allocations failed to run – the main problem here is with signals, especially SIGKILL
, that cannot be caught and handled to force de-allocation before unceremonious exit.
This is all taken care of by the pin_allocator
type. Or to be precise the pin_allocator
type alias as it is a typedef
for a specialisation of the pin_cache_allocator
class template that provides the intra-process allocator logic by deferring decisions initially to an alternate allocator whose type is provided by the single template type parameter and caches the results. The pin_allocator
type alias specialises pin_cache_allocator
with the pin_export_allocator
that provides support, with the help of a sys
file system module, for allocating by exporting from /sys/class/gpio/
and de-allocating by un-exporting.
It’s a wrap!
The services of gpio_registers
and pin_allocator
types are wrapped up behind the public interface types ipin
and opin
. As mentioned previously the ipin
and opin
types provide an abstraction modelled after the C++ IOStream library. In particular those of the std::ifstream
and std::ofstream
types (or any other specialisation from the underlying basic-templates). Operations on single bits are quite limited compared to those on streams of characters so other than providing open and close semantics the only other operations from the IOStreams types that really applied were get
for ipin
to read the Boolean value of a GPIO pin and put
for opin
to write, or set, the Boolean value of a GPIO pin.
There is quite a lot of common functionality around the implementation of the open and close operations of ipin
and opin
. This was pulled out into a common base type pin_base
having a totally protected interface. Following the std::ifstream
and std::ofstream
examples I originally provided explicit open
and close
operations which were called as appropriate by ipin
and opin
constructors and destructors. Later, having observed that a purer form of RAII that only allowed resources to be acquired through construction was proving to be a Good Thing™, I refactored ipin
, opin
and pin_base
to remove the default constructors and the open
and close
member functions which considerably simplified the implementation.
Each ipin
and opin
instance needs to acquire a GPIO pin for use if available, access the GPIO register block and, of course, to release the pin when done. Unsurprisingly pin_id
objects are used to pass around GPIO pin values. As they appear in the public interface of the public library types ipin
and opin
, pin_id
and related types are also part of the library’s public API.
Each BCM2835 has only one set of GPIO pins so there need only be one instance of the pin_allocator
type. Likewise, they only have one GPIO peripheral register block hence there only needs to be a single instance of gpio_registers
mapped to the relevant physical memory address as a phymem_ptr<volatile gpio_registers>
(the type pointed to by the phymem_ptr
specialisation is qualified volatile
because the hardware at the mapped locations are not memory but device registers that have to be accessed in the prescribed way and so the compiler is not free to assume it knows what is going on).
So without getting overly sophisticated the obvious pattern to use here was singleton (I’ll wait until some of you have recovered your composure…).
For those wondering about systems using multiple BCM2835 chips that as they have their peripheral registers’ memory locations hard-baked into the silicon such systems would not be feasible, GPIO and peripherals wise, without doing something exotic.
So what was needed was a singleton class containing a pin_allocator
and a phymem_ptr<volatile gpio_registers>
with the single instance being accessed via an instance member function that returned a reference to the function local static instance. Singletons of this form are sometimes known as Meyers Singletons[15] after advice given in Scott Meyer’s book Effective C++[16]. In fact this type was implemented as a struct
called gpio_ctrl
that allows direct access to the singleton’s allocator and GPIO registers members. The implementation was initially placed within, and local to, the implementation file for the ipin
, opin
and pin_base
classes as there was at the time no need for anything else to access it. This changed later on and at that time gpio_ctrl
was moved out into its own library-internal header and implementation file.
Testing, testing
Unit testing has already been mentioned in relation to the gpio_registers
type. However, after a while the project settled into three classes of tests:
-
Unit tests that relied on nothing else and could in theory be built and executed on another platform.
-
Platform tests, a form of integration tests that required a Raspberry Pi and/or Linux/Raspbian operating system services such as
/dev/mem
or/sys/class/gpio/
. -
Interactive tests, a form of platform integration tests that additionally require some user action. Usually this meant ensuring the hardware was suitably connected for the tests and often that the tester perform a requested action with the test hardware such as closing or opening a switch or confirming that the expected result such as a lamp lighting or turning off occurred.
Each class of test has its own executable. Unit tests do not require any special access to execute but the two integration test varieties generally require root access via sudo or similar - as do applications – including examples - built with the library.
Unit and platform tests can be run quickly as regression tests and could be run automatically. Interactive tests take a bit more time and care and obviously need to be run manually. The facility of the Catch test runner allowing the tests to be executed to be specified using wildcards is especially useful for only running the group of interactive tests that are currently of interest, especially as the hardware to support other interactive tests may not be wired up at the time.
It has occurred to me that it would be possible to create a specialist piece of hardware – a custom circuit board or similar – designed specifically to support the sort of testing performed by the interactive tests. It might even be possible to arrange for many tests to be performed and verified automatically. Such a device combined with the test software would I suppose be similar to ATE – Automated Test Equipment. It would however be quite involved and would almost certainly be overkill.
References
-
In a tar-ball called gertboard_sw_10_07_12.tar.gz
originally available at: www.element14.com/raspberrypi
but now seems only available elsewhere such as: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=42&t=20382 . -
See for example the Wikipedia Resource acquisition is initialization article
-
Catch: C++ Automated Test Cases in Headers, Phil Nash GitHub
-
Raspberry Pi Linux source code, Raspberry Pi Foundation Github
-
Raspberry Pi Linux kernel source BCM2835 specific support, Raspberry Pi Foundation Github
(note: for the rpi-3.10.y branch, use branch selection dropdown to select another) -
Raspberry Pi Linux kernel source BCM2708 specific support, Raspberry Pi Foundation Github
(note: for the rpi-3.10.y branch, use branch selection dropdown to select another) -
Meyer’s Singleton description, Los Alamos National Laboratory