Applications - Pin Control and GPIO

Last modified by Microchip on 2023/11/10 11:03

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.

Expansion features of the ATSAMA5D27-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).

Schematic of mikrobus sockets on the ATSAMA5D27-SOM1-EK1

Back to Top

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 Target packages ---->

Select Libraries ---->

Select Hardware handling ---->

Select [*] libgpiod

Observe a new submenu appears below [*] libgpiod


Select [*] install tools

Return to the top menu, save changes, and exit.

Build the image by typing the command:

$ make

Back to Top

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:

667        pioA_clk: pioA_clk {
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.

1493    pioA: pinctrl@fc038000 {
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.

Back to Top

Kernel

Objective: Observe how the peripheral I/O pins were configured as GPIO in the Linux kernel. No changes are required.

From the buildroot directory, run the Linux kernel menuconfig:

$ make linux-menuconfig

The top-level menu will be displayed:

Linux menuconfig top menu

Device Drivers

Select Device Drivers ---->

Linux menuconfig device drivers

Pin Controllers

Next, select Pin controllers ---->

Linux menuconfig 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

Type ESC-ESC to go back to the Device Driver menu page.

Select GPIO Support ---->

Linux menuconfig 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/

Back to Top

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 <stdio.h>
#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:

Call the gcc compiler that is installed on your machine.

$ /buildroot/output/host/bin/arm-buildroot-linux-gnueabihf-gcc gpio.c -o gpio_test

Make gpio_test executable:

$ sudo chmod +x gpio_test

Run gpio_test:

$ ./gpio_test

​A 100 Hz square wave will output from GPIO PB2 (J24 pin 2 of the mikroBUS 1).

Back to Top

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:

​$ cd output/build/libgpiod-0.3.2/src/tools/
$ 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.

​In the past, GPIO was accessed by the Shell from the sysfs interface. As of Linux version 4.8, this use has been deprecated. The libgpiod was created as a better way to access the GPIO driver.

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:

# gpiodetect
gpiochip0 [fc038000.pinctrl] (128 lines)

Print all lines information:

# gpioinfo
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

# gpiofind PB2
gpiochip0 34

Set PB2 output high:

# gpioset gpiochip0 34=1

Set PB2 output low:

# gpioset gpiochip0 34=0

Toggle PB2 high for 1 second:

# gpioset —mode=time —sec=1 gpiochip0 34=0

Monitor PB2 pin status:

# gpiomon gpiochip0 34
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.

Back to Top