GPIO Programming Part 8

Examining the Cross-Platform libsoc Library

By Jeff Tranter

In this post, part 8 in our series on GPIO programming, we'll look at libsoc, a cross-platform library intended for low-level embedded programming, including GPIO. (If you missed part 7, read it here.)

Libsoc [1] is a C library to interface with common peripherals found in System on a Chip (SoC) devices under Linux. It is generic and should be compatible with any SoC that has Linux drivers exposing the interfaces for the peripherals. It is written in C and has a native C API with a subset of the API supported with Python bindings.

The basic features are the following:

  • GPIO manipulation through sysfs (value, edge, direction, and exporting)
  • Blocking GPIO interrupts with timeout
  • Non-blocking GPIO interrupts with callback mechanism (pthread-based)
  • SPI transfers using spidev
  • I2C transfers using ioctls
  • PWM support through sysfs
  • Automatic board probing via installed configuration files
  • Python Bindings

It is licensed under the LGPLv2.1.

Toradex ships it with their SDK or you can cross-compile it from source. It is available for the Raspberry Pi under Raspbian (the packages are libsoc2 and libsoc-dev).

An Example

The API [2] is quite intuitive and the code comes with some examples. I wrote a small example program [3] that toggles the LEDs on the GPIO learning board until the switch is pressed.

It is just over 100 lines of code, small enough to show the full listing here:

/*

Example of using libsoc for GPIO programming.

Toggles three LEDs in a "Cylon" pattern until the pushbutton is
pressed, and then exits. Will run on Raspberry Pi or Toradex Colibri
platforms.

*/

#include <stdio.h>
#include <unistd.h>
#include "libsoc_gpio.h"
#include "libsoc_debug.h"

// Uncomment the appropriate lines below for the hardware it is
// running on.

// Raspberry Pi
//const int led1   = 24; // Red LED
//const int led2   = 25; // Green LED
//const int led3   =  5; // Yellow LED
//const int button =  6; // Pushbutton

// Toradex Colibri
const int led1   = 52; // Red LED
const int led2   = 53; // Green LED
const int led3   = 63; // Yellow LED
const int button = 93; // Pushbutton

// Delay (in microseconds)
const int delay = 100000;
    
int main(void)
{
    // Uncomment the next line to enable debug output if desired.
    //libsoc_set_debug(1);

    // Request gpios. May need to use LS_SHARED with older versions
    // of libsoc.
    gpio *gpio_led1 = libsoc_gpio_request(led1, LS_GPIO_SHARED);
    gpio *gpio_led2 = libsoc_gpio_request(led2, LS_GPIO_SHARED);
    gpio *gpio_led3 = libsoc_gpio_request(led3, LS_GPIO_SHARED);
    gpio *gpio_button = libsoc_gpio_request(button, LS_GPIO_SHARED);

    // Ensure gpios were successfully requested.
    if (gpio_led1 == NULL) {
        printf("Failed gpio_led1 request.\n");
        exit(EXIT_FAILURE);
    }
    if (gpio_led2 == NULL) {
        printf("Failed gpio_led2 request.\n");
        exit(EXIT_FAILURE);
    }
    if (gpio_led3 == NULL) {
        printf("Failed gpio_led3 request.\n");
        exit(EXIT_FAILURE);
    }
    if (gpio_button == NULL) {
        printf("Failed gpio_button request.\n");
        exit(EXIT_FAILURE);
    }
  
    // Set directions.
    libsoc_gpio_set_direction(gpio_led1, OUTPUT);
    libsoc_gpio_set_direction(gpio_led2, OUTPUT);
    libsoc_gpio_set_direction(gpio_led3, OUTPUT);
    libsoc_gpio_set_direction(gpio_button, INPUT);
  
    // Check directions.
    if (libsoc_gpio_get_direction(gpio_led1) != OUTPUT) {
        printf("Failed to set direction of gpio_led1\n");
        exit(EXIT_FAILURE);
    }
    if (libsoc_gpio_get_direction(gpio_led2) != OUTPUT) {
        printf("Failed to set direction of gpio_led2\n");
        exit(EXIT_FAILURE);
    }
    if (libsoc_gpio_get_direction(gpio_led3) != OUTPUT) {
        printf("Failed to set direction of gpio_led3\n");
        exit(EXIT_FAILURE);
    }
    if (libsoc_gpio_get_direction(gpio_button) != INPUT) {
        printf("Failed to set direction of gpio_button\n");
        exit(EXIT_FAILURE);
    }

    while (1) {

        // Turn LEDs on and off in desired sequence.
        libsoc_gpio_set_level(gpio_led1, HIGH);
        libsoc_gpio_set_level(gpio_led2, LOW);
        libsoc_gpio_set_level(gpio_led3, LOW);
        usleep(delay);
 
        libsoc_gpio_set_level(gpio_led1, LOW);
        libsoc_gpio_set_level(gpio_led2, HIGH);
        libsoc_gpio_set_level(gpio_led3, LOW);
        usleep(delay);

        libsoc_gpio_set_level(gpio_led1, LOW);
        libsoc_gpio_set_level(gpio_led2, LOW);
        libsoc_gpio_set_level(gpio_led3, HIGH);
        usleep(delay);

        libsoc_gpio_set_level(gpio_led1, LOW);
        libsoc_gpio_set_level(gpio_led2, HIGH);
        libsoc_gpio_set_level(gpio_led3, LOW);
        usleep(delay);

        // Check for switch being pressed.
        int status = libsoc_gpio_get_level(gpio_button);
        printf("Button status is %d\n", status);
        if (status == LOW) {
            break;
        }
    }

    // Turn all LEDs off.
    libsoc_gpio_set_level(gpio_led1, LOW);
    libsoc_gpio_set_level(gpio_led2, LOW);
    libsoc_gpio_set_level(gpio_led3, LOW);
  
    // Clean up.
    libsoc_gpio_free(gpio_led1);
    libsoc_gpio_free(gpio_led2);
    libsoc_gpio_free(gpio_led3);
    libsoc_gpio_free(gpio_button);
  
    return EXIT_SUCCESS;
}

This example works on a Raspberry Pi or Toradex Colibri. As mentioned in the source code comment above, at the time I wrote this the version of libsoc in Raspbian was an older one and used the name LS_SHARED rather than LS_GPIO_SHARED.

With just a few changes, I extended the example to read the switch status using interrupts and a callback function, rather than needing to poll it. You can download and try that example from the same download link listed in the reference section at the end of the blog post.

Summary

Libsoc is a cross-platform library for hardware-level programming. I found it easy to use for GPIO programming from C or C++. (I didn't look at other features, such as SPI and I2C.) The main limitation of the library is that, because it uses sysfs it can't achieve the performance possible with direct register access. Some other libraries, like WiringPi, do support direct register access, but at the price of being hardware dependent.

References

  1. http://jackmitch.github.io/libsoc/
  2. https://github.com/jackmitch/libsoc/
  3. Link to code examples: https://github.com/tranter/blogs/tree/master/gpio/part8