Raspberry Pi GPIO lines.

There are two connectors, P1 and P5, on the Model A and Model B version 2 devices, where various signals are available. All the signals are digital and 3.3V logic level, even though there are 5V power lines avaiable as well. This refers to the Version 2 devices that I have, version 1 devices have slightly different pin-allocations. (pins 3, 5, and 13 are GPIOs 0, 1, and 21 instead of 2, 3, and 27, and there is no P5 connector at all)

Ever since the Model B+ came, all versions since then (A+, Zero, Zero-W, Pi2, Pi3, Pi4 ) have a 40-pin header where the first 26 pins are the same as the P1 connector on the models A and B. There is however no P5 connector or equivalent anymore.

P1: (RP.P1) (A and B)

1 3.3V 2 5.0V
3 GPIO2 SDA1 4 5.0V
5 GPIO3 SCL1 6 Gnd
7 GPIO4 1Wire* 8 GPIO14 UART0_TXD
9 Gnd 10 GPIO15 UART0_RXD
11 GPIO17 12 GPIO18 PWM*
13 GPIO27 PCM_DOUT 14 Gnd
15 GPIO22 16 GPIO23
17 3.3V 18 GPIO24
19 GPIO10 SPI0_MOSI 20 Gnd
21 GPIO9 SPI0_MISO 22 GPIO25
23 GPIO11 SPI0_SCLK 24 GPIO8 SPI0_CE0
25 Gnd 26 GPIO7 SPI0_CE1

The P1.3/GPIO2/SDA1 and P1.5/GPIO3/SCL1 have an internal pull-up resistor, so no additional pull-up is needed when this is used as an i2c bus. If configured as plain inputs, they will read High when open and can be shorted to GND for Low -- no additional components needed.

P5: (RP.P5) (A and B)

2 3.3V 1 5.0V
4 GPIO29 SCL0 3 GPIO28 SDA 0
6 GPIO31 5 GPIO30
8 Gnd 7 Gnd

The i2c functions, SDA0 on GPIO28 and SCL0 on GPIO 29, are enabled by setting the alternate 0 functions on these, and re-setting the functions on GPIO0 (SDA0) and GPIO1 (SCL0) to plain IO.

Orientation seeing the card with P5 to the left of P1, and P1 to the top right; SD-card and power towards the top, network and USB to the bottom, HDMI to the left. P5 has opposite numbering compared to P1.

J8: (RP.J8) (all versions since B+; B+, A+, Z, ZW, Pi2, Pi3, Pi4)

1 3.3V 2 5.0V
3 GPIO2 SDA1 4 5.0V
5 GPIO3 SCL1 6 Gnd
7 GPIO4 1Wire* 8 GPIO14 UARTx_TXD
9 Gnd 10 GPIO15 UARTx_RXD
11 GPIO17 SPI1_CE1 12 GPIO18 PWM* i2s-BCK SPI1_CE0
13 GPIO27 PCM_DOUT 14 Gnd
15 GPIO22 16 GPIO23
17 3.3V 18 GPIO24
19 GPIO10 SPI0_MOSI 20 Gnd
21 GPIO9 SPI0_MISO 22 GPIO25
23 GPIO11 SPI0_SCLK 24 GPIO8 SPI0_CE0
25 Gnd 26 GPIO7 SPI0_CE1
27 (GPIO0) IDSD I2C0_SDA 28 (GPIO1) IDSC I2C0_SCL
29 GPIO5 30 GND
31 GPIO6 32 GPIO12
33 GPIO13 34 GND
35 GPIO19 i2s-LRCK SPI1_MISO 36 GPIO16 SPI1_CE2
37 GPIO26 38 GPIO20 i2s-DIN SPI1_MOSI
39 Gnd 40 GPIO21 i2s-DOUT SPI1_SCLK

The first 26 pins are the same as the ones on the P1 on the older Models A and B. Also, like these, the J8.3/GPIO2/SDA1 and J8.5/GPIO3/SCL1 lines have an internal pull-up resistor, so no additional pull-up is needed when this is used as an i2c bus. If configured as plain inputs, they will read High when open and can be shorted to GND for Low -- no additional components needed.

We can also note that, apart from special use assignments, (i2c, serial, SPI), all GPIOs numbered from 0 thru 27 are available here.

IDSD and IDSC are supposed to be used for an identifier eeprom on an expansion card, and nothing else. As this is also used with the display and camera, re-allocating these when display and/or camera is used, will make those not work right if at all.

However, these are actually the /dev/i2c-0 lines, and GPIO0 and GPIO1, so they might be used in less than compatible ways for other things. Kinda like the i2c bus on the Picotuxes that uses one of the IOs marked as reserved. And like this one, some sort of emission on startup can be expected.

All signals are at 3.3 V -- and many available devices work just fine on 3.3V so connections can be easy. And when needing to run at 5V we can use level translators or similar tricks as has been done with the Picotux or WD Books. Interfacing to a PIC16F690 at 5 V for example will be very similar to what has been done with these.

Max current available from the 3.3V regulator on all the various models (pins P1.1, J8.1, P1.17, J8.17, P5.2, as applicable, combined) is 50 mA. Max current available on the 5.0V lines (P1.2 J8.2, P1.4 J8.4, P5.1 as applicable, combined) is whatever is available from the USB power input, minus what the Raspberry Pi needs, which is 700 mA for the model B or 500 mA for the model A.

The serial port pins are connected to UART1 on the Pi3 and UART0 on all the older ones. The system maps this to /dev/ttyAMA0 in either case. On Pi3 the UART0 is used for the Bluetooth subsystem.

GPIO Programming

There are several ways to access the GPIO lines, for example:

gpiod/dev/gpiochip* The gpio-device handling. Uses ioctl() calls, and much of the hard work is collected in the libgpio. Use libgpiod-dev, #include <gpiod.h>, and -lgpio. Powerful and reasonably easy to work with. There are also some command-line tools available.
userland/sys/class/gpio/* Obsolete as of May 2024. Fairly slow, easy to test and develop, and allows non-privileged users. Good when the speed required is low.
user-space/dev/memFaster, reasonably easy to develop and use, requires root. Does not directly support interrupts. Very non-portable, and not really recommended, when we have gpiod.
kernel module /dev/custom Fast, lots of setup-work needed (kernel compilation) and certain errors bring down the entire system. Once in place, it does allow non-privileged users.

I have used all four at the same time in some system, it will work as long as any of the GPIO lines are only handled in one way.

There is a pseudo-file, /sys/kernel/debug/gpio, which shows which of the gpios are being allocated via sysfs or a kernel-module, and which states these are in. /dev/mem gpio access does not show up here.

For kernel modules, the description of the gpio line shown comes from the second argument to the call to the gpio_request() function.

gpiod and libgpiod

To install this:

apt install gpiod libgpiod-dev libgpiod-doc

Look for info in /usr/include/gpiod.h

#include <gpiod.h> in source

add -lgpiod to the linker invocations

Userland via files in /sys/class/gpio, sysfs

As these files start out as read-only for other than root for the most part, the set-up may have to be done as root, but once the files exist and can be chmod'd 666 then the non-root user may proceed.

To enable a gpio line, echo its number into /sys/class/gpio/export, for example:

echo "7" > /sys/class/gpio/export

We now have a /sys/class/gpio/gpio7 directory, with various files in it, and can read and write these files, for example:

echo "out" > /sys/class/gpio/gpio7/direction

The electrical level (Low or High) depends on the values of active_low and value files. Writing to value for an output gpio line sets its level to whatever was written, and reading value for any gpio line shows the level on it at present.

The control files are

active_low 0 | 1 (0 means value=0 is Low and value=1 is High, 1 is the opposite)
direction in | out
edge none | rising | falling | both
value 0 is low and 1 is high (or vice versa if active_low is 1)
uevent
power/

Reading and writing these from a program is done via ordinary stdio functions such as fopen(), then fgets() or fputs(), then fclose().

The interrupts are enabled by setting the gpioN/edge file to one of rising, falling or both, for it to react to rising or falling edges, or both edges. The gpioN/direction file should read in. Then in a program, the gpioN/value file is opened with O_RDONLY | O_NONBLOCK and the poll(2) function is used to look for POLLPRI. As poll(2) can do several files, catching keypresses by handling POLLIN for stdin can be useful.

I noticed on kernel 4.1.7 that the flag never seemed to be reset, so I had to close and open the gpio value file once the poll() had returned indicating there is a value for me. The value file would otherwise give me the same number no matter what the voltage on the pin is.

This is nice, but it is fairly slow -- I found this useful for push- buttons, and for the rain-gages that close or open a switch for every 1mm or 1/10 mm, but for the 1/100 L pulses from a flowmeter assembly it was not fast enough.

The userland GPIO also shows that we can't perform bit-banging the interface to the HX711 Load Cell Amplifier reliably -- as this has to have 25-27 clock pulses pretty accurately timed, for reading the data and preparing the next reading all within 100ms. Random delays caused by other activity in the operating system precludes this from working reliably right, and occasional wrong values are returned.

Loadable kernel modules.

Like user-space gpio, this also allows non-privileged users to access the devices. One will have to be root when loading or unloading the modules however, for testing. And certain bugs may result in a non-functional system that has to be re-started...

There is also the matter of getting the development system right. The kernels available on downloads for the Raspberry Pi changes all the time, and when a new kernel is in place, the module will have to be recompiled with it in order to work.

Though a bit more complicated, this isn't impossible, see the page on kernel programming for all the gory details.

With the right settings, however, the makefile (named Kbuild) typically looks like:

MODULE=flowco

obj-m:= ${MODULE}.o

all:
	make -C ${KERNEL_SRC} M=$(PWD) modules

clean:
	make -C ${KERNEL_SRC} M=$(PWD) clean

and the only other files required are what will here be referred to as flowco.c which in turn needs flowco_u.h. Running make -f Kbuild on the above will compile the code in flowco.c and link it with other kernel elements and produce the flowco.ko file, which can then be loaded with insmod, maybe automated by adding this load command into /etc/rc.local, or putting the file in with others under /lib/modules.

I prefer using /etc/rc.local, as it is easier to "lose" a file in /lib/modules/somewhere... and when I revisit some project after 6 months, there is the matter of finding it again.

Code in this kernel module has access to the hardware, even directly, or via functions. After having got dibs on the line with gpio_request() we can use gpio_get_value(number), gpio_set_value(number, level), gpio_direction_input(number), gpio_direction_output(number, level) to read and control, as well as an interrupt line with gpio_to_irq() and then request_irq() including the handle function.

There's also the trick of registering the device as a miscellaneous one, if there is no need for a specific major number or many minor numbers or suchlike, using misc_register() -- the device file in /dev/ will then show up right away on startup.

See also more notes on these interrupts below.

User-space via /dev/mem

Then there is code for handling bit-banging into /dev/mem which isn't all that elegant, as it requires root permissions not just for development (as anything we want to put into /etc/rc.local or the kernel does) but also for use.

I found some notes on various speeds possible on this blog here.

14 MHz or more using C and the /dev/kmem interface? interesting...!

So getting a couple MCP3201s to sample at 100 ksps might be possible then.

From the Broadcom datasheet there is information on these pins and how to access registers for them. This can be done in user-space by mapping memory (have to be root) and then writing to and reading various registers.

I have been able to flip some of the bits up and down on request, there are three registers, one for set pin to H, one for clear pin to L, and the third for reading the state of a pin, which is correct whether this is input or output.

The 3.3 V outputs will drive an LS-TTL input without any problem.

The GPIO base address to map with is offset 0x200000 from the peripheral base address, which depends on the model of the Raspberry Pi, as follows:

Models Baseaddress + offset
1B, 1A, 1A+, 1B+, Z, ZW 0x20000000 +0x00200000
2B, 3B, 3A+, 3B+ 0x3F000000 +0x00200000
4B 0xFE000000 +0x00200000

Initial States

Having found out how to access the various registers, I made a utility, gpiostates, which lists the available GPIO lines and what they are currently set to be used for; whether input, output, or some special function. On a Rpi Model B just having been powered on, where the i2c and spi modules are no longer blacklisted, this shows:

GPIO02 pin P1-03 F0=I2C1_SDA
GPIO03 pin P1-05 F0=I2C1_SCL
GPIO04 pin P1-07 input
GPIO07 pin P1-26 F0=SPI0_CE1_N
GPIO08 pin P1-24 F0=SPI0_CE0_N
GPIO09 pin P1-21 F0=SPI0_MISO
GPIO10 pin P1-19 F0=SPI0_MOSI
GPIO11 pin P1-23 F0=SPI0_SCLK
GPIO14 pin P1-08 F0=UART0_TXD
GPIO15 pin P1-10 F0=UART0_RXD
GPIO17 pin P1-11 input
GPIO18 pin P1-12 input
GPIO22 pin P1-15 input
GPIO23 pin P1-16 input
GPIO24 pin P1-18 input
GPIO25 pin P1-22 input
GPIO27 pin P1-13 output
GPIO28 pin P5-03 input
GPIO29 pin P5-04 input
GPIO30 pin P5-05 input
GPIO31 pin P5-06 input

Interrupts

I found out (from someone calling themselves More Than User -- Many thanks!) how to make the system get an interrupt from a gpio line. Turns out, there is support for this sort of thing in the kernel, at least the 3.6.11 one. And I have made it work with an "accumulator"-type program, that can receive interrupts for the pulses coming from an anemometer, rain gage, or other kind of flow meter, and count and calculate correctly scaled volume. I have had this sort of thing going on the PIC16F628 and PIC16F690 for a while, but since the Raspberry Pi can do this directly, I won't need to add another microcontroller just for this.

To test this, I used an extension board where the switch closures from the anemometer or rain gage (which all use reed switches) is debounced using a capacitor, two resistors and a 4584-type CMOS Schmitt-trigger. This is then connected to the GPIO22, pin 15.

The source is in the file accumulator.c. It uses the gpio_ series of kernel functions to request the gpio line and assign an interrupt service function to it.

This would be how to handle other gpio actions. For example, adding various values depending on the states of some other GPIO inputs, or toggling an output when a new unity value is reached, or when some threshold value is encountered.

The kernel gpio structures are discussed in details in /kernel/linux/Documentation/gpio.txt which is a file that comes along with the kernel source.

The author of More Than User has also shown how to make the kernel interrupt send a selected software signal to a user-space process.


Valid XHTML 1.0 Transitional