Controlling the Raspberry Pi’s GPIOs using Direct Register Access in C++

In this blog entry I will present the mmapGpio class that provides basic access to all the GPIO’s on the RPI’s 26-pin header using direct register access. The advantage of this approach to GPIO control is that one can bypass Linux and talk directly with the GPIO registers which enables much faster GPIO toggling speeds. Using this approach I was able to get GPIO toggling speeds that exceed 25MHz!!!! That’s much faster than the toggling speeds that can be achieved using the safer but slower SYSFS approach to GPIO control.

The mmapGpio class is intentionally designed to be simple but useful. A great feature of this class is that a single instance can be used to control the all the RPI’s GPIOs. The mmapGpio class with two examples and makefiles can be downloaded from here.

We will now look at the two examples that demonstrate the use of the mmapGpio class.

mmapGpioToggle Example

The purpose of this example is to benchmark the speed of GPIO toggling. The code is provided below:

#include "mmapGpio.h"
#include "stdio.h"

int main(void){
	mmapGpio rpiGpio; // instantiate an instance of the mmapGpio class
	rpiGpio.setPinDir(17,mmapGpio::OUTPUT); // set GPIO17 to output
	while(1) {// toggle pin as fast as possible
		   rpiGpio.writePinHigh(17);
		   rpiGpio.writePinLow(17); 
	}
		
return 0;
}

As you can see the code is pretty straightforward. First instantiate the mmapGpio and then call the ‘setPinDir()‘ function to set the direction of the pin. The pin number/label follows the standard RPI & BCM2835 GPIO pin definitions. We also need to specify the direction of the pin with either ‘mmapGpio::OUTPUT‘ for output or ‘mmapGpio::INPUT‘ for input. We can then use the ‘writePinHigh()‘ & ‘writePinLow()‘ inline methods to toggle the GPIO as fast as possible. When building this code with no optimization (optimization flag o0)  we were able to achieve a squarish waveform with a frequency of 9.23MHz.

GPIO17 output when running the toggle example built with o0 flag

GPIO17 output when running the toggle example built with o0 flag

When building this code with speed optimization (optimization flag o3)  we were able to achieve a not so squarish waveform with a frequency of  25MHz. This is approximately in line with the toggling speed results achieved here.

GPIO17 output when running the toggle example built with o3 flag

GPIO17 output when running the toggle example built with o3 flag

The reasons for why we are not getting a proper squar ewave at these speeds are many; long wiring, grounding issues, breadboard noise….scope approaching its analog bandwidth limitations…I could go on.

Finally there’s also another method that can be used to set/clear output pins and that is ‘writePinState()‘. This method is not as optimized as ‘writePinHigh()‘ & ‘writePinLow()‘  but it can set an output pin to either high or clear it to low as follows:

  • rpiGpio.writePinState(17, mmapGpio::HIGH) – Sets GPIO17 to  high
  • rpiGpio.writePinState(17, mmapGpio::LOW) - Clears GPIO17 to low

The writePinState()‘, ‘writePinHigh()‘ & ‘writePinLow()‘ methods should only be used on GPIO pins already configured as outputs. They have no effect on GPIO pins configured as inputs (untested!).

mmapGpioTest Example

In this example we test the class’s ability to set a GPIO pin as input and read the value of the input pin. First setup the following circuit on the breadboard. Make sure that GPIO4 is connected to an LED via a limiting resistor and GPIO17 is connected to a push button and a 10Kohm pull-up resistor:

Figure 1 Raspberry Pi Connection Diagram

Raspberry Pi Connection Diagram

The example code is provided below:

#include "mmapGpio.h"
#include "stdio.h"

int main(void){
	unsigned int val = 10;
	mmapGpio rpiGpio; // instantiate an instance of the mmapGpio class
	rpiGpio.setPinDir(4,mmapGpio::OUTPUT); // set GPIO4 to output
	rpiGpio.setPinDir(17,mmapGpio::INPUT); // set GPIO17 to input
	while(val > 0) {
		usleep(500000); //delay for 0.5 seconds
		if(rpiGpio.readPin(17) == mmapGpio::HIGH) // read pin state (no debounce to make code more readable)
			printf("Button not pressed\n"); // if GPIO17 is HIGH button not pressed (due to pull-up resistor)
		else{
			printf("Button is pressed....toggling LED\n"); //else if GPIO17 is low button is pressed
		    while(rpiGpio.readPin(17) == mmapGpio::LOW ){ // repeat toggling of GPIO4 until GPIO17 goes back to HIGH i.e. button de-pressed
				rpiGpio.writePinHigh(4); // write GPIO4 pin high
				usleep(500000);
                rpiGpio.writePinLow(4); // write GPIO4 pin low
                usleep(500000);
			}
			printf("Button not pressed anymore....toggling LED stopped\n");
		}
        val--;
	}
		

return 0;
}

First instantiate the mmapGpio and then call the ‘setPinDir()‘ function to set the direction of GPIO pins 4 & 17. Use the ‘readPin()’ method to read the value of the input pin GPIO17. The return value of the ‘readPin()’ method is either mmapGpio::HIGH if the pin is high (i.e push button not pressed) or mmapGpio::LOW if GPIO17 is low (i.e. push button is pressed).

If the pushbutton is pressed we enter a while loop that continues to toggle GPIO4 so long as GPIO17 continues to be pressed. Once GPIO17 is de-pressed, then the GPIO4 output pin is set to LOW again.

The complete code for both examples, makefiles and the mmapGpio class are provided here for download.

This entry was posted in GPIO, Raspberry Pi, Raspberry Pi Peripherals. Bookmark the permalink.

4 Responses to Controlling the Raspberry Pi’s GPIOs using Direct Register Access in C++

  1. Pingback: Add Analog to Digital Conversion Capability to The Raspberry Pi Without an ADC Chip | Hertaville

  2. Steve Ferry says:

    Using this method, I have been able to control a SPI ADC connected to any GPIO pins of my choosing. My code is available at http://www.baart.co.uk/bangin.zip Please note that the work is only 5% mine and 95% Hussam’s code above.

    Also, by using this method of controlling SPI devices, you can not only add extra devices by using more than the two chip enable lines provided on the Pi, but by making the chip enable line common to multiple ADC’s and giving each one its own miso line, you can do simultaneous sampling by sending one bit to all ADC’s and read their replies from the individual miso’s.

    I have also read 1 million samples from a 12 bit MCP3208 in just over 12 seconds which works out at a sampling rate of about 82k s/sec which does seem to be pretty impressive. Good work Hussam.

    • halherta says:

      Steve, Thanks! but I just wrote the GPIO access section while you wrote all the SPI stuff (the tricky parts). Thank you for sharing your ‘bitbanging SPI over GPIO code’ on my blog! I’m sure that others will find it useful. Especially if they’re using the SPI peripheral for other things like LCD e.t.c.

  3. Pingback: Control the Raspberry Pi’s GPIO from a Qt4-based graphical application | Hertaville

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>