An Introduction to chardev GPIO and Libgpiod on the Raspberry PI

Linux 4.8 introduced a new GPIO user space subsystem for accessing GPIO. This tutorial provides an introduction to the new Character Device GPIO and explores how to control GPIO from the command line.

sysfs GPIO

User-mode GPIO (General Purpose Input/Output) has historically been performed via the legacy “integer-based”sysfs pseudo file system.  For example, to set GPIO25, one would:

# echo out > /sys/class/gpio/gpio25/direction
# echo 1 > /sys/class/gpio/gpio25/value

GPIO access via this legacy sysfs interface has been deprecated since version 4.8 of the Linux kernel.

chardev GPIO

The new way of doing GPIO is via the “descriptor-based” character device ABI (Application Binary Interface). The interface is exposed at /dev/gpiochipN or /sys/bus/gpiochipN where N is the chip number.

Outside of ensuring all allocated resources are cleaned up on closing, the main feature of this new interface is a discovery mechanism, mitigating the need to use magic numbers to address IO. Other new features include open-drain I/O support and the ability to read and set multiple I/O lines at once. Going forward, new features will only be supported in the chardev GPIO.

Unfortunately the new interface prevents manipulating GPIO with standard command line tools (i.e. echo).  Dedicated user-mode tools are now required.

The Linux kernel is distributed with three basic user-mode tools written primarily for testing the interface. The source can be found in linux/tools/gpio/

The three tools are:

  • lsgpio – example on how to list the GPIO lines on a system.
  • gpio-event-mon – monitor GPIO line events from userspace.
  • gpio-hammer – example swiss army knife to shake GPIO lines on a system.

To cross-compile, simply use:

# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-

lsgpio is the most useful of these debug tools, however none of these tools will allow the user to configure, set and clear GPIO lines.

Libgpiod

Libgpiod (Library General Purpose Input/Output device)  provides both API calls for use in your own programs and the following six user-mode applications to manipulate GPIO lines:

  • gpiodetect – list all gpiochips present on the system, their names, labels and number of GPIO lines
  • gpioinfo – list all lines of specified gpiochips, their names, consumers, direction, active state and additional flags
  • gpioget – read values of specified GPIO lines
  • gpioset – set values of specified GPIO lines, potentially keep the lines exported and wait until timeout, user input or signal
  • gpiofind – find the gpiochip name and line offset given the line name
  • gpiomon – wait for events on GPIO lines, specify which events to watch, how many events to process before exiting or if the events should be reported to the console

To cross-compile Libgpiod for the Raspberry PI on your Ubuntu 18.04 host, first install the following prerequisites:

sudo apt-get install autoconf autoconf-archive libtool libkmod-dev pkg-config

Then download, cross-compile and install.

wget https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/snapshot/libgpiod-1.1.1.tar.gz 
tar -xzf libgpiod-1.1.1.tar.gz 
cd libgpiod-1.1.1/ 
./autogen.sh --enable-tools=yes --host=arm-linux-gnueabi --prefix=/home/<home dir>/export/rootfs ac_cv_func_malloc_0_nonnull=yes 
make 
make install

Setting ac_cv_func_malloc_0_nonnull=yes will prevent an undefined reference to `rpl_malloc’ error at linking.

Usage

Below is example usage on a Raspberry PI Model B+ V1.2.

To detect/list GPIO character devices:

# gpiodetect
gpiochip0 [pinctrl-bcm2835] (54 lines)

To list the I/O lines available on this device:

# gpioinfo pinctrl-bcm2835
gpiochip0 - 54 lines:
        line   0:       "SDA0"       unused   input  active-high
        line   1:       "SCL0"       unused   input  active-high
        line   2:       "SDA1"       unused   input  active-high
        line   3:       "SCL1"       unused   input  active-high
        line   4:  "GPIO_GCLK"       unused   input  active-high
        line   5:  "CAM_GPIO1"       unused   input  active-high
        line   6:    "LAN_RUN"       unused   input  active-high
        line   7:  "SPI_CE1_N"       unused   input  active-high
        line   8:  "SPI_CE0_N"       unused   input  active-high
        line   9:   "SPI_MISO"       unused   input  active-high
        line  10:   "SPI_MOSI"       unused   input  active-high
        line  11:   "SPI_SCLK"       unused   input  active-high
        line  12:         "NC"       unused   input  active-high
        line  13:         "NC"       unused   input  active-high
        line  14:       "TXD0"       unused   input  active-high
        line  15:       "RXD0"       unused   input  active-high
        line  16: "STATUS_LED_N" "ACT" output active-low [used]
        line  17:     "GPIO17"       unused   input  active-high
        line  18:     "GPIO18"       unused   input  active-high
        line  19:         "NC"       unused   input  active-high
        line  20:         "NC"       unused   input  active-high
        line  21:     "GPIO21"       unused   input  active-high
        line  22:     "GPIO22"       unused   input  active-high
        line  23:     "GPIO23"       unused   input  active-high
        line  24:     "GPIO24"       unused   input  active-high
        line  25:     "GPIO25"       unused  output  active-high
        line  26:         "NC"       unused   input  active-high
        line  27:  "CAM_GPIO0"       unused   input  active-high
        line  28:    "CONFIG0"       unused   input  active-high
        line  29:    "CONFIG1"       unused   input  active-high
        line  30:    "CONFIG2"       unused   input  active-high
        line  31:    "CONFIG3"       unused   input  active-high
        line  32:         "NC"       unused   input  active-high
        line  33:         "NC"       unused   input  active-high
        line  34:         "NC"       unused   input  active-high
        line  35:         "NC"       unused   input  active-high
        line  36:         "NC"       unused   input  active-high
        line  37:         "NC"       unused   input  active-high
        line  38:         "NC"       unused   input  active-high
        line  39:         "NC"       unused   input  active-high
        line  40:   "PWM0_OUT"       unused   input  active-high
        line  41:         "NC"       unused   input  active-high
        line  42:         "NC"       unused   input  active-high
        line  43:         "NC"       unused   input  active-high
        line  44:         "NC"       unused   input  active-high
        line  45:   "PWM1_OUT"       unused   input  active-high
        line  46: "HDMI_HPD_P"       unused   input  active-high
        line  47: "SD_CARD_DET"      unused   input  active-high
        line  48:   "SD_CLK_R"       unused   input  active-high
        line  49:   "SD_CMD_R"       unused   input  active-high
        line  50: "SD_DATA0_R"       unused   input  active-high
        line  51: "SD_DATA1_R"       unused   input  active-high
        line  52: "SD_DATA2_R"       unused   input  active-high
        line  53: "SD_DATA3_R"       unused   input  active-high

Display help for the gpioset command:

# gpioset --help
Usage: gpioset [OPTIONS]  = = ...
Set GPIO line values of a GPIO chip
Options:
  -h, --help:           display this message and exit
  -v, --version:        display the version and exit
  -l, --active-low:     set the line active state to low
  -m, --mode=[exit|wait|time|signal] (defaults to 'exit'):
                tell the program what to do after setting values
  -s, --sec=SEC:        specify the number of seconds to wait (only valid for --mode=time)
  -u, --usec=USEC:      specify the number of microseconds to wait (only valid for --mode=time)
  -b, --background:     after setting values: detach from the controlling terminal

Modes:
  exit:         set values and exit immediately
  wait:         set values and wait for user to press ENTER
  time:         set values and sleep for a specified amount of time
  signal:       set values and wait for SIGINT or SIGTERM

To toggle GPIO25 high for 1 second:

# gpioset --mode=time --sec=1 pinctrl-bcm2835 25=1

5 Comments

  1. Hello
    I have a question I didn’t cross compile
    (I don’t have another computer set up for that ) I compiled
    on the raspberry pi 3 and everything else works
    the gpio pins are reached and turn on and off those pins correctly
    however the gpioinfo doesn’t get the labels
    #gpioinfo pinctrl-bcm2835
    or #gpioinfo gpiochip0
    command gives

    line 0: unnamed unused input active-high
    line 1: unnamed unused input active-high

    whereas you are gettting
    line 0: “SDA0” unused input active-high
    line 1: “SCL0” unused input active-high

    notice the label SDA0 and SCLO?

    For some reason the info is not seen
    maybe when you cross compiled or that you have the B+ version
    you are getting the correct results?
    anyway could you try and compile on the raspberry pi instead of cross
    compiling most people will compile on the raspberry pi
    anything needed for the raspberry pi
    I figure you did that to keep your raspberry pi clean with less clutter
    thanks if you can confirm this
    your blog is very clear and would even be better
    if you added a raspberry pi compile
    and confirm the output of gpioinfo again
    thanks
    Joe

  2. I found the libgpiod , but I’m wondering how we can switch mode in to out a particular line.
    To be concrete, how can I translate the following wiringPi commands
    * gpio mode 2 out
    * gpio write 2 1
    * gpio mode 0 down
    * gpio mode 0 up

    If I well understood , gpio write can be translated to gpioset 0 2=1, but what about the mode one ?

  3. The gpio pin names weren’t added for rpi3 until the Linux 4.20 kernel. So you’ll see “unnamed” for earlier kernels unless you recompile the device tree. FWIW the names are “GPIO0” … “GPIO47” for the first 48 pins, so not very useful.

  4. Hi,
    when you say “To cross-compile Libgpiod for the Raspberry PI on your Ubuntu 18.04 host, first install the following prerequisites:” that procedure does not cross-compile, but produces binaries and libs for the host. I have tried adding the ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- CC=arm-linux-gnueabihf-gcc but does not work (complains about not finding linux/gpio.h). From there I try to add CFLAGS and stuff like that but I end up in not finding headers related to size_t… Do you have the exact procedure to cross-compile in your case?

    Thanks

Leave a Reply to Daniel Cancel reply

Your email address will not be published.


*