• gpio.c

  • §

    Jonas Altrock ew20b126@technikum-wien.at

    To overview

    main.c

    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
  • §

    Types to name internal constants.

    enum GPIO {
        GPIO87 = 0,
        GPIO89 = 1
    };
    
    enum Value {
        OFF,
        ON
    };
    
    enum Direction {
        OUT,
        IN
    };
  • §

    Internal structure to keep the information for one GPIO together.

    typedef struct gpio_file {
        char *direction_path;
        FILE *direction;
        char *value_path;
        FILE *value;
    } gpio_t;
  • §

    The classic sysfs has been deprecated in Linux kernel, so we use the new character device paths in /sys/bus/gpio.

    That GPIO 87 is on gpiochip2 we could find out by either looking for a directory named gpio87 in the gpio folders or by checking the data sheet am3358.pdf and figuring out that each of the GPIO chips provides 32 pins, meaning 87 = 64 + 23 is in the third chip (0-31, 32-63, 64-95), so index 2.

    gpio_t gpios[2] = {
        {
            .direction_path = "/sys/bus/gpio/devices/gpiochip2/gpio/gpio87/direction",
            .value_path = "/sys/bus/gpio/devices/gpiochip2/gpio/gpio87/value",
        },
        {
            .direction_path = "/sys/bus/gpio/devices/gpiochip2/gpio/gpio89/direction",
            .value_path = "/sys/bus/gpio/devices/gpiochip2/gpio/gpio89/value",
        }
    };
  • §

    Originally I only opened the files and let Linux close the FDs at the end of the program, but the lecturers did not like it, so now there is an atexit cleanup.

    void _gpio_deinit()
    {
        for (int i = 0; i < 2; i++) {
            fclose(gpios[i].direction);
            fclose(gpios[i].value);
        }
    }
  • §

    GPIO init

  • §

    We open the direction and value files for the two GPIOs we need to use and store them in an internal array.

    int gpio_init()
    {
        for (int i = 0; i < 2; i++) {
            if (NULL == (gpios[i].direction = fopen(gpios[i].direction_path, "w+"))) {
                perror("Could not open GPIO direction file");
                return -1;
            }
            if (NULL == (gpios[i].value = fopen(gpios[i].value_path, "w+"))) {
                perror("Could not open GPIO value file");
                return -1;
            }
        }
    
        atexit(_gpio_deinit);
    
        return 0;
    }
  • §

    _gpio_write

  • §

    Write to a GPIO file (direction or value). We flush the output buffer to make sure the kernel sees our new value. We use fprintf so that we do not need to know how long the string we write is. (write would need that.)

    void _gpio_write(FILE *file, const char *value)
    {
        fprintf(file, "%s\n", value);
        fflush(file);
    }
  • §

    _gpio_read

  • §

    Read from the GPIO as a string - we know we only need to read 1 or 0 for value and out or in for direction, so read max 3 characters.

    void _gpio_read(FILE *file, char *value)
    {
        rewind(file);
        fscanf(file, "%3s", value);
    }
  • §

    gpio_get_dir

  • §

    Direction can only be out or in.

    enum Direction gpio_get_dir(enum GPIO gpio)
    {
        char str[4];
        _gpio_read(gpios[gpio].direction, str);
    
        if (str[0] == 'o') {
            return OUT;
        }
    
        return IN;
    }
  • §

    gpio_set_dir

  • §
    void gpio_set_dir(enum GPIO gpio, enum Direction dir)
    {
  • §

    If you don’t remember, condition ? if_true : if_false is the ternary expression syntax that is like an if/else but evaluates to a value, so can be on the right hand side of an assignment or in the position of a function parameter.

        const char *d = dir == OUT ? "out" : "in";
        _gpio_write(gpios[gpio].direction, d);
    }
  • §

    gpio_get_value

  • §
    enum Value gpio_get_value(enum GPIO gpio)
    {
        char str[4] = "";
        _gpio_read(gpios[gpio].value, str);
    
        if (str[0] == '0') {
            return OFF;
        }
    
        return ON;
    }
  • §

    gpio_set_value

  • §
    void gpio_set_value(enum GPIO gpio, enum Value value)
    {
        const char *s = value == ON ? "1" : "0";
        _gpio_write(gpios[gpio].value, s);
    }