• gpio.c

  • §

    Jonas Altrock ew20b126@technikum-wien.at

    To overview

    main.c

    This is basically the same as in assigment 2 and 3, but includes code to read the value of the first analog input AIN0. Also I got a BeagleBone Black which has different wiring than the PocketBeagle, so I added the possibility to override which GPIOs to use for the LEDs.

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

    gpio.h

    #include "gpio.h"
  • §

    If this should run on a different board, we can override the GPIO pins connected to the LEDs with compiler arguments lik -DLED1_GPIO=53.

    To stringify the preprocessor constants, we have to do a little dance with two macro expansions. And because the chosen GPIOs could be on another gpiochip, we have to do a little arithmetic as well.

    #ifndef LED1_GPIO
    #define LED1_GPIO 87
    #endif
    
    #ifndef LED2_GPIO
    #define LED2_GPIO 89
    #endif
    
    #ifndef POTI_AIN
    #define POTI_AIN 0
    #endif
    
    #define _STR(x) #x
    #define STR(x) _STR(x)
    
    #if (LED1_GPIO / 32 == 0)
    #define LED1_GPIO_CHIP 0
    #elif (LED1_GPIO / 32 == 1)
    #define LED1_GPIO_CHIP 1
    #elif (LED1_GPIO / 32 == 2)
    #define LED1_GPIO_CHIP 2
    #elif (LED1_GPIO / 32 == 3)
    #define LED1_GPIO_CHIP 3
    #endif
    
    #if (LED2_GPIO / 32 == 0)
    #define LED2_GPIO_CHIP 0
    #elif (LED2_GPIO / 32 == 1)
    #define LED2_GPIO_CHIP 1
    #elif (LED2_GPIO / 32 == 2)
    #define LED2_GPIO_CHIP 2
    #elif (LED2_GPIO / 32 == 3)
    #define LED2_GPIO_CHIP 3
    #endif
  • §

    I changed the structure here to accomodate GPIO and AIN.

    typedef struct gpio_file {
        char *path;
        FILE *file;
    } gpio_file_t;
  • §

    A GPIO pin is two files,

    typedef struct gpio_pin {
        gpio_file_t direction;
        gpio_file_t value;
    } gpio_t;
    
    gpio_t gpios[2] = {
        {
            .direction.path = "/sys/bus/gpio/devices/gpiochip" STR(LED1_GPIO_CHIP) "/gpio/gpio" STR(LED1_GPIO) "/direction",
            .value.path = "/sys/bus/gpio/devices/gpiochip" STR(LED1_GPIO_CHIP) "/gpio/gpio" STR(LED1_GPIO) "/value",
        },
        {
            .direction.path = "/sys/bus/gpio/devices/gpiochip" STR(LED2_GPIO_CHIP) "/gpio/gpio" STR(LED2_GPIO) "/direction",
            .value.path = "/sys/bus/gpio/devices/gpiochip" STR(LED2_GPIO_CHIP) "/gpio/gpio" STR(LED2_GPIO) "/value",
        }
    };
  • §

    and an AIN input is only one file.

    gpio_file_t AIN = {
        .path = "/sys/bus/iio/devices/iio:device0/in_voltage" STR(POTI_AIN) "_raw"
    };
    
    void _gpio_deinit()
    {
        for (int i = 0; i < 2; i++) {
            fclose(gpios[i].direction.file);
            fclose(gpios[i].value.file);
        }
    
        fclose(AIN.file);
    }
    
    int gpio_init()
    {
        for (int i = 0; i < 2; i++) {
            if (NULL == (gpios[i].direction.file = fopen(gpios[i].direction.path, "w+"))) {
                perror("Could not open GPIO direction file");
                return -1;
            }
            if (NULL == (gpios[i].value.file = fopen(gpios[i].value.path, "w+"))) {
                perror("Could not open GPIO value file");
                return -1;
            }
        }
    
        if (NULL == (AIN.file = fopen(AIN.path, "r"))) {
            perror("Could not open ADC AIN file");
            return -1;
        }
    
        atexit(_gpio_deinit);
    
        return 0;
    }
    
    void _gpio_write(FILE *file, const char *value)
    {
        fprintf(file, "%s\n", value);
        fflush(file);
    }
  • §

    Reading a GPIO/AIN file must accommodate at most 4095 + \n + \0 bytes.

    #define _GPIO_READ_VALUE_LEN 6
    
    void _gpio_read(FILE *file, char *value)
    {
        rewind(file);
        fscanf(file, "%5s", value);
        fflush(file);
    }
    
    enum Direction gpio_get_dir(enum GPIO gpio)
    {
        char str[_GPIO_READ_VALUE_LEN];
        _gpio_read(gpios[gpio].direction.file, str);
    
        if (str[0] == 'o') {
            return OUT;
        }
    
        return IN;
    }
    
    void gpio_set_dir(enum GPIO gpio, enum Direction dir)
    {
        _gpio_write(gpios[gpio].direction.file, dir == OUT ? "out" : "in");
    }
    
    enum Value gpio_get_value(enum GPIO gpio)
    {
        char str[_GPIO_READ_VALUE_LEN] = "";
        _gpio_read(gpios[gpio].value.file, str);
    
        if (str[0] == '0') {
            return OFF;
        }
    
        return ON;
    }
    
    void gpio_set_value(enum GPIO gpio, enum Value value)
    {
        _gpio_write(gpios[gpio].value.file, value == ON ? "1" : "0");
    }
  • §

    ain_get_value()

  • §
    int ain_get_value()
    {
        char value[_GPIO_READ_VALUE_LEN] = "";
        _gpio_read(AIN.file, value);
  • §

    We know the value in the file will always be numeric, as it is continuously updated from the hardware, so we can use atoi and don’t need strtol with rest check.

        return atoi(value);
    }