Raspberry Pi Linux User Mode GPIO in C++ - Part 1

Thursday, 26 Mar 2015 - 15:22 +0100| Tags: Raspberry Pi, Linux, code, C++, GPIO, mmap

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.

UML static structure style diagram of the rpi::peripherals C++ library GPIO support
Figure 1. Rough first cut structure

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.