Search by Tags

How to Use GPIO on TorizonCore

 

Article updated at 29 Jul 2020
Compare with Revision




Subscribe for this article updates

Introduction

The Kernel Linux GPIO user space SysFS is deprecated and has been discontinued. It can still be enabled by CONFIG_GPIO_SYSFS but its use is discouraged and will be removed from mainline Linux after 2020. For GPIO access from user space the new char device API must be used.

The new user space API use the /dev/gpiochip devices through ioctl calls to manage GPIOs:

# ls /dev/gpiochip*
/dev/gpiochip0  /dev/gpiochip2  /dev/gpiochip4  /dev/gpiochip6
/dev/gpiochip1  /dev/gpiochip3  /dev/gpiochip5  /dev/gpiochip7

Each entry on the /dev/gpiochip corresponds to a GPIO bank that the operating system has access to.

This article complies to the Typographic Conventions for Torizon Documentation

Prerequisites

Operating System in the Computer on Module:

Optional

To get the most out of this article and test things in practice, we recommend that you clone the torizon-samples repository to your computer:

$ cd ~
$ git clone https://github.com/toradex/torizon-samples.git
$ cd torizon-samples/gpio

Also, to build Container images for ARM architectures make sure to prepare your build environment according to the following article:

Prepare the Hardware

If you want to see things happening, hook up a LED (or a multimeter) and switch (and of course other components to interface with the TTL level from your SoM) to the GPIO pins:

  • Apalis MXM3 pin 3 (Apalis GPIO2): Switch
  • Apalis MXM3 pin 5 (Apalis GPIO3): LED (or a multimeter)

For Colibri and Verdin, we leave it up to you to choose the pins.

Attention: The TTL level for Colibri and Apalis I/O is 3.3V and for Verdin is 1.8V. Do not apply 3.3V signals to Verdin I/O.

GPIO Bank/Lines

To verify the GPIO banks and lines check the datasheet of the module on the section I/O Pins -> Functions List. For example, let's assume we need to access MXM3 X1 pin 5 (Apalis GPIO3) from an Apalis iMX8:

Apalis iMX8 I/O Pins -> Function List

From the datasheet we can see that Apalis MXM3 X1 Pin 5 has the GPIO function on ALT3 accessible at LSIO.GPIO0_IO12. This means we need to access GPIO bank 0 line 12.

Note: This bank and line nomenclature differ slightly depending on the processor family. Apalis iMX6 e.g. starts enumerating GPIO banks with 1, while GPIO banks in Linux always start at 0. That means that GPIO2_IO6 on Apalis iMX6 is GPIO bank 1 line 6 on Linux.

Warning: The highlighted function in the datasheet indicates the default function of the given pin. If the GPIO function is not indicated as default, there is a high chance that you will need to define the pins you will be using as GPIO in a custom Device Tree Overlay (DTO). Find further documentation and examples on how to do so here: Device Tree Overlays

libgpiod

libgpiod library encapsulates the ioctl calls and data structures behind a straightforward API.

Additionally, libgpiod project contains a set of command-line tools allowing access from shell scripts. These command-line tools can also be used to replace scripts which used the deprecated sysfs API directly.

Command Line Tools

For getting started with the libgpiod command-line tools let's exemplify some commands. We will use TorizonCore for testing with a pre-built container. We also provide optional instructions for you to re-build the container if you want.

Bring up the Container

Choose your SoM from the tabs below:

The Dockerfile below is built into the image torizonextras/arm64v8-gpiod:

Run the following command on the board terminal to download the image and mount the container for testing:

# docker run --rm -it --device /dev/gpiochip0 torizonextras/arm64v8-gpiod

The Dockerfile below is built into the image torizonextras/arm32v7-gpiod:

Run the following command on the board terminal to download the image and mount the container for testing:

# docker run --rm -it --device /dev/gpiochip0 torizonextras/arm32v7-gpiod

Note that in the docker run command, the argument --device pass the char device from the GPIO bank that will be shared and accessed inside the container. You may specify that argument multiple times if access to different GPIO banks is required. Normally root user is required to access /dev/gpiochip devices. TorizonCore comes with udev rules which allow access for users part of the gpio group. Adding the torizon user to the group allows to also access GPIOs as a user from within the container. Use USER torizon in your Dockerfile if you prefer running the container as non-root user, as exemplified in our sample Dockerfile.

How to Use the Command Line Tools

Here we present how to use the command-line tools.

gpiodetect

Search for the GPIO banks, /dev/gpiochiop0 ... /dev/gpiochipX, and how many GPIO lines they have:

## gpiodetect
gpiochip0 [5d080000.gpio] (32 lines)

gpioinfo

Read and displays the information contained in the GPIO bank lines:

## gpioinfo
gpiochip0 - 32 lines:
    line   0:      unnamed       unused   input  active-high 
    line   1:      unnamed       unused   input  active-high 
    line   2:      unnamed       unused   input  active-high 
    line   3:      unnamed       unused   input  active-high 
    line   4:      unnamed       unused   input  active-high 
    line   5:      unnamed       unused   input  active-high 
    line   6:      unnamed       unused   input  active-high 
    line   7:      unnamed       unused   input  active-high 
    line   8:      unnamed "ov5640_mipi_reset" output active-high [used]
    line   9:      unnamed "ov5640_mipi_pwdn" output active-high [used]
    line  10:      unnamed       unused   input  active-high 
    line  11:      unnamed       unused   input  active-high 
    line  12:      unnamed       unused   input  active-high 
    line  13:      unnamed       unused   input  active-high 
    line  14:      unnamed       unused   input  active-high 
    line  15:      unnamed       unused   input  active-high 
    line  16:      unnamed       unused   input  active-high 
    line  17:      unnamed       unused   input  active-high 
    line  18:      unnamed       unused   input  active-high 
    line  19:      unnamed       unused   input  active-high 
    line  20:      unnamed       unused   input  active-high 
    line  21:      unnamed       unused   input  active-high 
    line  22:      unnamed       unused   input  active-high 
    line  23:      unnamed       unused   input  active-high 
    line  24:      unnamed       unused   input  active-high 
    line  25:      unnamed       unused   input  active-high 
    line  26:      unnamed       unused   input  active-high 
    line  27:      unnamed       unused   input  active-high 
    line  28:      unnamed       unused   input  active-high 
    line  29:      unnamed       unused   input  active-high 
    line  30:      unnamed       unused   input  active-high 
    line  31:      unnamed "usb3503 connect" output active-high [used]

This command is useful to check which rows are being used, with their respective use descriptions, for a GPIO bank.

gpioset

Writes the output value of a certain line in a gpiochip passed by argument. The example set the GPIO bank 0 line 12 to output low:

## gpioset /dev/gpiochip0 12=0

You can also use only the GPIO bank index as a parameter:

## gpioset 0 12=0

Now the example to set the GPIO bank 0 line 12 to output high:

## gpioset 0 12=1

gpioget

Reads the value of input from a certain line in a gpiochip passed by argument:

## gpioget 0 13
1

The return of this command can be 1 if the input is high and 0 if the input is low.

## gpioget 0 13
0

gpiomon

Wait for events on GPIO rows passed by argument:

## gpiomon 0 13
event: FALLING EDGE offset: 13 timestamp: [1570281706.661390750]
event: FALLING EDGE offset: 13 timestamp: [1570281706.661435750]
event:  RISING EDGE offset: 13 timestamp: [1570281706.661604000]
event:  RISING EDGE offset: 13 timestamp: [1570281706.916220125]
event: FALLING EDGE offset: 13 timestamp: [1570281706.918247625]

This command is useful for polling the lines to expect incoming input events.

How to Replace sysfs Commands

The most important commands are gpioget and gpioset, though you could possibly use gpiomon as well. Check out our example that writes and reads from GPIO pins. The sysfs commands are commented in the script:

C Language Examples

Some examples are provided in this chapter, illustrating how to use the C API from libgpiod. Pre-built containers are not provided, therefore you are encouraged to build your own, in which case you will need the optional dependencies.

First, we will build and deploy the container. Then, we will explain and run the samples.

Build the Docker Image With the C Samples

In this section, you will compile all the C examples and build a Container image to be easily deployed to the board.

Choose your SoM from the tabs below:

Note: For the examples below we will use the multi-stage Docker build. It is a best practice and you are encouraged to understand how it is implemented.

The sample Dockerfile used is:

Assuming that you have cloned the torizon-samples as described in the optional dependencies, you can modify and re-build the image by yourself in your development PC.

First, we will build the container image.

$ docker build -f Dockerfile.arm64 -t <yourDockerHubUsername>/arm64v8-c-gpiod .

Upload the image generated in the command above to your Docker Hub:

$ docker login
$ docker push <yourDockerHubUsername>/arm64v8-c-gpiod

After that, on the board terminal, run the following command to pull the image from Docker Hub:

# docker pull <yourDockerHubUsername>/arm64v8-c-gpiod

The sample Dockerfile used is:

Assuming that you have cloned the torizon-samples as described in the optional dependencies, you can modify and re-build the image by yourself in your development PC.

First, we build the container image.

$ docker build -f Dockerfile.armhf -t <yourDockerHubUsername>/arm32v7-c-gpiod .

Upload the image generated in the command above to your Docker Hub:

$ docker login
$ docker push <yourDockerHubUsername>/arm32v7-c-gpiod

After that, on the board terminal, run the following command to pull the image from Docker Hub:

# docker pull <yourDockerHubUsername>/arm32v7-c-gpiod

Now you are ready to understand and run the samples.

How to Toggle a GPIO

The example below uses the libgpiod API to access a GPIO bank and line that are the arguments to the program:

Here are some relevant highlights.

Include libgpiod library:

#include <gpiod.h>

Define GPIO chip and line structs:

struct gpiod_chip *output_chip;
	struct gpiod_line *output_line;

Configure a GPIO as output:

/* open the GPIO bank */
	output_chip = gpiod_chip_open_by_number(bank);
 
	/* open the GPIO line */
	output_line = gpiod_chip_get_line(output_chip, line);
 
	/* config as output and set a description */
	gpiod_line_request_output(output_line, "gpio-test",
		GPIOD_LINE_ACTIVE_STATE_HIGH);

Toggle a GPIO:

/* Clear */
    int line_value = 0;
    gpiod_line_set_value(output_line, line_value);
 
    /* Set */
    line_value = 1;
    gpiod_line_set_value(output_line, line_value);

Run the example using the previously built and deployed container. Choose your SoM from the tabs below:

This example uses the Apalis iMX8 GPIO3 - LSIO.GPIO0.IO12:

# docker run --rm -it --init --device /dev/gpiochip0 yourdockerhubuser/arm64v8-c-gpiod
## gpio-toggle 0 12

This example uses the Apalis iMX6 GPIO3 - GPIO2_IO6:

# docker run --rm -it --init --device /dev/gpiochip1 yourdockerhubuser/arm32v7-c-gpiod
## gpio-toggle 1 6

Read GPIO Using Events (Interrupt Driven)

This example uses the libgpiod API to register for an event (interrupt driven) on a rising edge. For this example make sure to add 4 arguments, the first two specifying the bank and line number of the input GPIO and the next two for the bank and line number of the output GPIO:

Here are some relevant highlights. What has been discussed in the previous section is omitted.

Request rising events notification:

ret = gpiod_line_request_rising_edge_events(input_line, "gpio-test");

Wait for an event to happen, read which event it was and validate that it's a rising event:

while (1) {
		gpiod_line_event_wait(input_line, NULL);
 
		if (gpiod_line_event_read(input_line, &event) != 0)
			continue;
 
		/* this should always be a rising event in our example */
		if (event.event_type != GPIOD_LINE_EVENT_RISING_EDGE)
			continue;
}

Run the example using the previously built and deployed container. Choose your SoM from the tabs below:

This example uses the Apalis iMX8 GPIO3 - LSIO.GPIO0.IO12 as output and GPIO2 - LSIO.GPIO0.IO9 as input.

# docker run --rm -it --init --device /dev/gpiochip0 yourdockerhubuser/arm64v8-c-gpiod
## gpio-event 0 9 0 12

This example uses the Apalis iMX6 GPIO3 - GPIO2_IO6 as output and GPIO2 - GPIO2_IO5 as input.

# docker run --rm -it --init --device /dev/gpiochip1 yourdockerhubuser/arm32v7-c-gpiod
## gpio-event 1 5 1 6