Applications - Pin Control and GPIO
Contents
Introduction
Writing to General Purpose Input/Output (GPIO) is useful to control external circuits such as Light Emitting Diodes (LEDs) or relays. You can also read GPIO pins to sense button presses and contact switches. You can control single or multiple GPIO pins depending on the circuit(s) you are controlling.
In this topic, using Buildroot, you will
- Build a bootable image to include the GPIO Devices library (libgpiod) in the Linux® kernel that will provide a straightforward Application Programming Interface (API) to control GPIO.
- Observe how the device tree and kernel are configured and controls GPIO pins.
- Write a C Language application and use shell commands to read and write to GPIO pins of a SAMA5D2 Series ARM® Cortex®-A5 Microprocessor Unit (MPU).
Prerequisites
This application is developed for the ATSAMA5D27-SOM1-EK1 development platform:
This application is developed using the Buildroot build system.
Hardware
For this application, you will be controlling a single GPIO pin of the mikroBUS 1 expansion socket of the ATSAMA5D27-SOM1-EK1. The accompanying figure shows the expansion capability of the SOM1-EK1.
You will control pin PB2 from the ATSAMA5D27 SOM1 which connects to J24 pin 2 of the mikroBUS 1 connector (labeled RST_mBUS1 on the schematic).
Buildroot Configuration
Objective: Using Buildroot, build a bootable image and Flash it onto an SD Memory Card for the ATSAMA5D27-SOM1-EK1 development board.
Follow the steps for building the image in the topic Buildroot - Create Project with Default Configuration. In the topic, you will use the default configuration file: atmel_sama5d27_som1_ek_mmc_dev_defconfig
There is one change to the _defconfig you will need to make in order to compile in the libgpiod (Library GPIO Device) code.
Select [*] libgpiod
Observe a new submenu appears below [*] libgpiod
Build the image by typing the command:
Device Tree
Objective: Observe how the peripheral I/O pins were configured as GPIO in the device tree. No changes are required.
Once Buildroot has completed its build, the pin assignments for the ATSAMA5D27-SOM1-EK1 were configured by a device tree. The device tree source includes (*.dtsi) file is located in the Buildroot output directory: /output/build/linux-linux4sam_6.0/arch/arm/boot/dts/sama5d2.dtsi
Examine the sama5d2.dtsi file and observe the Parallel Input/Output (PIO) assignments:
668 #clock-cells = <0>;
669 reg = <18>;
670 atmel,clk-output-range = <0 83000000>;
671 };
Line 669 sets the Peripheral Identification (PID) of pioA to 18. This will be used to enable the pioA peripheral clock in the Power Management Controller (PMC).
Line 670 sets the pioA input clock to a maximum frequency of 83 MHz.
1494 compatible = "atmel,sama5d2-pinctrl";
1495 reg = <0xfc038000 0x600>;
1496 interrupts = <18 IRQ_TYPE_LEVEL_HIGH 7>,
1497 <68 IRQ_TYPE_LEVEL_HIGH 7>,
1498 <69 IRQ_TYPE_LEVEL_HIGH 7>,
1499 <70 IRQ_TYPE_LEVEL_HIGH 7>;
1500 interrupt-controller;
1501 #interrupt-cells = <2>;
1502 gpio-controller;
1503 #gpio-cells = <2>;
1504 clocks = <&pioA_clk>;
1505 };
Line 1494 specifies which driver will be used for the pioA device.
Line 1495 sets the pioA base address to 0xfc038000 and size 0x600
Line 1496-1499 sets each PIO bank (there are four total) with its own interrupt line. The file output/build/linux-linux4sam_6.0/include/dt-bindings/interrupt-controller/irq.h provides the definitions of TRQ_TYPE.
Line 1504 provides the definition for the pioA clock source.
Kernel
Objective: Observe how the peripheral I/O pins were configured as GPIO in the Linux kernel. No changes are required.
The top-level menu will be displayed:
Device Drivers
Select Device Drivers ---->
Pin Controllers
Next, select Pin controllers ---->
Observe that AT91 pinctrl driver and AT91 PIO4 pinctrl driver are selected as shown by the “-*-“. The drivers are accessed from the root filesystem in the device directory: /dev/gpiochip0
GPIO Support
Select GPIO Support ---->
Observe that the pseudo filesystem sysfs for GPIO from is selected.
The GPIO driver can be accessed from the root filesystem in the system directory: /sys/class/gpio/
Application Programming in C Language
Objective: Explain how GPIO can be accessed using a C Language program.
The GPIO drivers can be accessed by input/output control calls to ioctl(). The following C program will generate a 100 Hz square wave on GPIO PB2 (J24 pin 2 of the mikroBUS 1).
Copy and paste the below C program using your favorite editor.
Name the file gpio.c
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <linux/gpio.h>
#include <sys/ioctl.h>
#define DEV_GPIO "/dev/gpiochip0"
int main(int argc, char *argv[])
{
int fd;
int ret;
struct gpiochip_info cinfo;
struct gpioline_info linfo;
struct gpiohandle_request req;
struct gpiohandle_data data;
/* open gpio */
fd = open(DEV_GPIO, 0);
if (fd < 0) {
printf("ERROR: open %s ret=%d\n", DEV_GPIO, fd);
return -1;
}
/* get gpio chip info */
ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo);
if (ret < 0) {
printf("ERROR get chip info ret=%d\n", ret);
return -1;
}
printf("GPIO chip: %s, \"%s\", %u GPIO lines\n",
cinfo.name, cinfo.label, cinfo.lines);
ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &linfo);
if (ret < 0) {
printf("ERROR get line info ret=%d\n", ret);
return -1;
}
printf("line %2d: %s\n", linfo.line_offset,
linfo.name);
/* set gpio_pb2 output */
// 128 gpio in gpiochip0
// 0 ~ 31 PA0 -> PA31
// 32 ~ 63 PB0 -> PB31
// 33 ~ 95 PC0 -> PC31
// 96 ~ 127 PD0 -> PD31
req.lineoffsets[0] = 34;
req.lines = 1;
req.flags = GPIOHANDLE_REQUEST_OUTPUT;
strcpy(req.consumer_label, "RST_mBUS1");
int lhfd = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (lhfd < 0) {
printf("ERROR get line handle lhdf=%d\n", lhfd);
return -1;
}
data.values[0] = 1;
ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret < 0) {
printf("ERROR set line value ret=%d\n", ret);
return -1;
}
while (1) {
// set gpio_pb2 low
data.values[0] = 0;
ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
usleep(5*1000);
// set gpio_pb2 high
data.values[0] = 1;
ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
usleep(5*1000);
}
/* close gpio */
close(fd);
return 0;
}
To compile the program, type the command:
Make gpio_test executable:
Run gpio_test:
libgpiod – C library and tools
Objective: Explain how to interact with GPIO from the Shell using the libgpiod library and tools,
libgpiod (GPIO device library) is a C library and tools for interacting with the Linux GPIO character device. This library encapsulates the ioctl() calls and data structures using a straightforward API. For more information see: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about/
Prerequisite: Using Buildroot, select and build an image with the libgpiod library. See the "Buildroot Configuration" section above.
APIs of libgpiod
libgpiod provides a simple API to access the GPIO driver. When Buildroot completes its build, the C header file for these APIs are located at: /output/build/libgpiod-0.3.2/include/gpiod.h
The libgpiod tools are located in the following folder. These provide demonstration source code that shows you how to work with the libgpiod API.
From the buildroot directory:
$ ls *.c
gpiodetect.c gpiofind.c gpioget.c gpioinfo.c gpiomon.c gpioset.c tools-common.c
Tools of libgpiod
There are some simple tools provided by libgpiod for accessing the GPIO driver from the Shell.
There are six commands in libgpiod tools:
- 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
Using libgpiod tools on ATSAMA5D27-SOM1-EK1
Detect GPIO on the target processor:
gpiochip0 [fc038000.pinctrl] (128 lines)
Print all lines information:
gpiochip0 - 128 lines:
line 0: "PA0" unused input active-high
line 1: "PA1" unused input active-high
line 2: "PA2" unused input active-high
line 3: "PA3" unused input active-high
line 4: "PA4" unused input active-high
line 5: "PA5" unused input active-high
line 6: "PA6" unused input active-high
line 7: "PA7" unused input active-high
line 8: "PA8" unused input active-high
line 9: "PA9" unused input active-high
[...]
Find the GPIO chip name and offset of GPIO PB2
gpiochip0 34
Set PB2 output high:
Set PB2 output low:
Toggle PB2 high for 1 second:
Monitor PB2 pin status:
event: FALLING EDGE offset: 34 timestamp: [1325983345.255958082]
event: RISING EDGE offset: 34 timestamp: [1325983345.256686960]
event: FALLING EDGE offset: 34 timestamp: [1325983348.205010375]
event: RISING EDGE offset: 34 timestamp: [1325983348.577229302]
event: FALLING EDGE offset: 34 timestamp: [1325983348.657488131]
event: RISING EDGE offset: 34 timestamp: [1325983348.695700717]
event: FALLING EDGE offset: 34 timestamp: [1325983348.830615058]
event: RISING EDGE offset: 34 timestamp: [1325983349.395371156]
event: FALLING EDGE offset: 34 timestamp: [1325983349.938427156]
event: RISING EDGE offset: 34 timestamp: [1325983349.938456229]
Summary
In this topic, you used Buildroot to build an image with the libgpiod library and tools. You also wrote an application in C language and interacted via the Shell to read and write to GPIO pins of a SAMA5D2 Series ARM Cortex-A5 MPU. You walked through the device tree and kernel to observe how the embedded Linux system configures and controls GPIO pins.