Libgpiod - Toggling GPIOs The Right Way In Embedded Linux
We all know that GPIO is one of the core elements of any embedded system. We use GPIOs to control LEDs and use them to monitor switches and button presses. In modern embedded systems, GPIOs can also be used as pins for other peripheral busses, such as SPI and I2C. Similar to the previous article on interacting with peripherals on an SPI bus in userspace via SPIdev (https://www.embeddedrelated.com/showarticle/1485.php), we can also control GPIOs from userspace on an embedded Linux system.
The Wrong Way
Many years ago, the only way to access GPIOs from userspace on an embedded Linux system was through a filesystem exposed by the Linux kernel called “sysfs.” This could be done by executing the following commands in a terminal (if we wanted to access GPIO 42, for example):
$> cd /sys/class/gpio $> echo 42 > export
Then, if we want to configure GPIO42 to be an output and set it to a HIGH, we can execute the following commands:
$> cd /sys/class/gpio/gpio42 $> echo out > direction $> echo 1 > value
Similarly, if we wanted to configure GPIO42 to be an input and query the state of the GPIO, we can execute the following commands:
$> cd /sys/class/gpio/gpio42 $> echo in > direction $> cat value
This mechanism has a significant drawback. As an example, if two different processes are running and they both wish to exercise GPIO42, they would have to perform the following actions:
- Check that GPIO42 has been exported (by checking the presence of /sys/class/gpio/gpio42) and export it if it has not been.
- Set the “direction” file to either “input” or “output.”
- Set the “value” file to either “1” or “0” for an output, or query the contents of “value” if the GPIO is configured to be an input.
Specifically, process A wishes to configure GPIO42 as an output and set it to a HIGH. However, process B also needs to use GPIO42 but requires that it be configured as an input. From the list above, process A will have to set the “direction” file to “output” and then set the “value” file to “1”. However, let’s say right before GPIO42 sets the “value” file to “1”, process B has an opportunity to execute and sets the “direction” file to “input.” When process A resumes execution, it will no longer be able to write a “1” to the “value” file since the direction of GPIO42 has been altered by another process.
Libgpiod To The Rescue
A more robust mechanism was introduced in v4.8 of the Linux kernel to allow userspace applications to interact with GPIOs. This mechanism is referred to as “libgpiod.” The steps to use libgpiod are straightforward:
- Access the desired GPIO line and set its direction
- Set the GPIO output value (if it’s an output) or retrieve the state of the GPIO (if it’s an input)
We will now illustrate how to perform the above with code samples in Python from a recent MAB Labs project using a Toradex Verdin iMX8M Plus SoM. For this project, we had to monitor certain buttons' states and act when pressed. We also had to display text on an LCD controlled by GPIO lines. First, we can create two variables in Python that we could reuse to configure a GPIO as either an input or an output:
1 import gpiod 2 3 def configure_gpio(): 4 config_out = gpiod.line_request() 5 config_out.consumer = “MAB project” 6 config_out.request_type = gpiod.line_request.DIRECTION_OUTPUT 7 8 config_in = gpiod.line_request() 9 config_in.consumer = “MAB project” 10 config_in.request_type = gpiod.line_request.DIRECTION_INPUT 11 config_in.flags = gpiod.line_request.FLAG_ACTIVE_LOW
There are a few lines of interest in the above snippet. First, on lines 6 and 9, we set the “consumer” variable of each instance of the line_request class in gpiod to a meaningful string (“MAB project” in this case). This allows us to identify processes that are using a particular GPIO. Second, on line 11, we set the active state of the input to be low. Later on, when we use the input to determine state, libgpiod will associate the “true” state of the input with when it is grounded (which, in this case, is when the button is pressed).
Then, we can set the GPIOs associated with the LCD as an output and set them to an initial value of 0, which is shown below:
1 gpio_d4 = gpiod.find_line(“SODIMM_206”) 2 gpio_d4.request(config_out) 3 gpio_d4.set_value(0) 4 5 gpio_but1 = giod.find_line(“SODIMM_222”) 6 gpio_but1.request(config_in)
If we wish to update the value output by the GPIO, we can use the “set_value” method with the appropriate value. If we wish to query the state of the input GPIO, we can use the get_value() method. The following code sample shows the button state can be retrieved using this method:
but1_state = gpio_but1.get_value()
This blog post showed how GPIOs could be accessed from userspace on an embedded Linux platform. We understood how the original mechanism, by accessing files from Linux’s sysfs interface, is not a scalable option. Instead, libgpiod is the accepted mechanism to access and control GPIOs from userspace. We saw how we could use libgpiod to control an output GPIO and query an input GPIO using Python.
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: