Navigation C API Pages Python bindings Applications

Linux SPI and GPIO

This is a low level interface to a GPIO and SPI under Linux. This functionality is used by the display backends to communicate to the hardware.

Linux userspace GPIO
enum gp_gpio_dir {
        GP_GPIO_IN = 0,
        GP_GPIO_OUT = 1,
};

struct gp_gpio {
        const uint16_t nr;
        /* enum gp_gpio_dir */
        const uint16_t dir:1;
        int fd;
};

/**
 * @brief Closes and unexports previously exported GPIOs
 *
 * @gpio An array of GPIO descriptions
 * @gpio_cnt A number of GPIOs in the gpio array
 */
void gp_gpio_unexport(struct gp_gpio *gpio, unsigned int gpio_cnt);

/**
 * @brief Exports and opens GPIOs
 *
 * Export and opens all GPIOs in the description atomically.
 *
 * @gpio An array of GPIO descriptions
 * @gpio_cnt A number of GPIOs in the gpio array
 * @return Non-zero on success (all GPIOs were exported succesfully), non-zero
 *         otherwise.
 */
int gp_gpio_export(struct gp_gpio *gpio, unsigned int gpio_cnt);

/**
 * @brief Writes a GPIO value
 *
 * @self An output GPIO
 * @val A value, zero turns the GPIO off, non-zero on.
 */
int gp_gpio_write(struct gp_gpio *self, int val);

/**
 * @brief Reads a GPIO value.
 *
 * @self An input GPIO
 * @return A GPIO state.
 */
int gp_gpio_read(struct gp_gpio *self);

enum gp_gpio_edge {
        GP_GPIO_EDGE_NONE,
        GP_GPIO_EDGE_FALL,
        GP_GPIO_EDGE_RISE,
        GP_GPIO_EDGE_BOTH,
};

/**
 * @brief Sets or resets a GPIO edge trigger.
 *
 * If set as edge interrupt source the GPIO file descriptor can be passed to
 * poll(2) with POLLPRI for asynchronous edge notification.
 *
 * @self An input GPIO.
 * @edge Edge to watch for.
 * @return A zero on success, non-zero otherwise.
 */
int gp_gpio_edge_set(struct gp_gpio *self, enum gp_gpio_edge edge);

Linux GPIO implements an API to atomically export an array of GPIOs.

Example usage
static struct gp_gpio gpio_map[] = {
        {.nr = 10, dir = GP_GPIO_OUT},
        {.nr = 12, dir = GP_GPIO_OUT},
};

...
        /* Export GPIO 10 and 12 to be usable from userspace */
        if (gp_gpio_export(gpio_map, 2)) {
                printf("Failed to export GPIOs\n");
                return 1;
        }

        /* Turn ON GPIO 10 */
        gp_gpio_write(&gpio_map[0], 1);

        ...


        /* Unexport the GPIOs */
        gp_gpio_unexport(gpio_map);
...
Linux userspace SPI (spidev)
#include <linux/spi/spidev.h>

/**
 * @brief Opens a SPI bus.
 *
 * @spi_dev A SPI device dev path, e.g. /dev/spidev0.0
 * @mode A SPI mode, SPI_* constants in the linux/spi/spidev.h
 * @speed A SPI speed in Hz.
 *
 * @return A file descriptor or -1 in a case of a failure.
 */
int gp_spi_open(const char *spi_dev, uint8_t mode, uint32_t speed);

/**
 * @brief Writes a single byte to the SPI bus.
 *
 * @spi_fd An SPI bus file descriptor.
 * @byte A byte to transfer.
 *
 * @return Zero on success, non-zero otherwise.
 */
int gp_spi_write(int spi_fd, uint8_t byte);

/**
 * @brief Runs an SPI transfer.
 *
 * @spi_fd An SPI bus file descriptor.
 * @rx_buf A buffer for received data, may be NULL.
 * @tx_buf A buffer for transfered data, may be NULL.
 * @size The size of the rx_buf and tx_buf buffers.
 *
 * @return Zero on success, non-zero otherwise.
 */
int gp_spi_transfer(int spi_fd, uint8_t *rx_buf, uint8_t *tx_buf, size_t size);

/**
 * @brief Closes SPI bus.
 */
void gp_spi_close(int spi_fd);

This is a very thin wrapper for the Linux userspace SPI driver (spidev). The kernel takes care of the SPI signaling (RX, TX, CLK) as well as the CS (chip select).

SPI display
struct gp_gpio_map {
        union {
                struct gp_gpio gpio[4];
                struct {
                        struct gp_gpio reset;
                        struct gp_gpio dc;
                        struct gp_gpio pwr;
                        struct gp_gpio busy;
                };
        };
};

struct gp_display_spi {
        int spi_fd;
        struct gp_gpio_map *gpio_map;
        ...
};

int gp_display_spi_init(struct gp_display_spi *self,
                        const char *spi_dev, uint8_t mode, uint32_t speed,
                        struct gp_gpio_map *map);

void gp_display_spi_exit(struct gp_display_spi *self);

/**
 * @brief Sends a single command byte to the display.
 *
 * Sets the dc pin low and writes a command byte to SPI bus.
 *
 * @self A SPI display.
 * @cmd A byte to be send.
 */
void gp_display_spi_cmd(struct gp_display_spi *self, uint8_t cmd);

/**
 * @brief Sends a single data byte to the display.
 *
 * Sets the dc pin high and writes a data byte to SPI bus.
 *
 * @self A SPI display.
 * @data A byte to be send.
 */
void gp_display_spi_data(struct gp_display_spi *self, uint8_t data);

/**
 * @brief Runs a SPI data transfer.
 *
 * Sets the dc pin high and does SPI transfer.
 *
 * @self A SPI display.
 * @tx_buf A transmit buffer, can be NULL.
 * @rx_buf A receive buffer, can be NULL.
 * @len The size of tx and rx buffers.
 */
void gp_display_spi_data_transfer(struct gp_display_spi *self,
                                  uint8_t *tx_buf, uint8_t *rx_buf, size_t len);

/**
 * @brief Sets up an GPIO as an interrupt source.
 *
 * The GPIO busy file descriptor can be passed to poll() with POLLPRI in order
 * to get asynchronous edge change notification. Note that you need to set up
 * the poll() handler elsewhere, this only enables the kernel functionality.
 *
 * @self A SPI display.
 * @edge An GPIO edge pass GP_GPIO_EDGE_NONE to disable edge notifications.
 *
 * @return Zero on success, non-zero otherwise.
 */
int gp_display_spi_busy_edge_set(struct gp_display_spi *self, enum gp_gpio_edge edge);

/**
 * @brief Spin wait for busy signal.
 *
 * Spins in place until busy signal == ready.
 *
 * @self A SPI display.
 * @ready Value when display is ready, 0 == GND, 1 == VDD.
 */
void gp_display_spi_wait_ready(struct gp_display_spi *self, int ready);

SPI display abstraction combines Linux SPI and GPIOs into a display driver into a single entity.

The dc (data/command) GPIO is set automatically based on which function is called. The busy GPIO can be either waited for with gp_display_spi_wait_ready() or used asynchronously with poll(2) if set as edge interrupt source.

The reset and pwr pins are supposed to be used by the display driver and the display library does not set set them.