0

I'm using a Rapsberry Pi Zero 2 to control an FPGA, the Lattice iCE40, via SPI (which I have accordingly enabled via raspi-config). In order to upload the image on the FPGA, I need to follow the procedure shown at page 30 of the technical manual of the board. As shown in Figure 13.2, that I pasted below, this means that I need to bring the SPI_SS signal low, then high, then low again. This signal corresponds to the slave select pin, which on RPi is indicated as SPI_CE0_N (if one uses the /dev/spidev0.0 SPI device, as I am currently doing) and corresponds to GPIO8.

I tried to do this by treating the SPI_CE0_N as a GPIO. Therefore, I thus tried to control the pin with the approach indicated here by simply copying and pasting the blink.c file, selecting the GPIO8 pin and making some other minor modifications:

/* blink.c
 *
 * Raspberry Pi GPIO example using sysfs interface.
 * Guillermo A. Amaral B. <g@maral.me>
 *
 * This file blinks GPIO 4 (P1-07) while reading GPIO 24 (P1_18).
 */

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define IN  0
#define OUT 1

#define LOW  0
#define HIGH 1

#define PIN  24 /* P1-18 */
#define POUT 7  /* P1-24 */

static int
GPIOExport(int pin)
{
#define BUFFER_MAX 3
    char buffer[BUFFER_MAX];
    ssize_t bytes_written;
    int fd;

    fd = open("/sys/class/gpio/export", O_WRONLY);
    if (-1 == fd) {
        fprintf(stderr, "Failed to open export for writing!\n");
        return(-1);
    }

    bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
    write(fd, buffer, bytes_written);
    close(fd);
    return(0);
}

static int
GPIOUnexport(int pin)
{
    char buffer[BUFFER_MAX];
    ssize_t bytes_written;
    int fd;

    fd = open("/sys/class/gpio/unexport", O_WRONLY);
    if (-1 == fd) {
        fprintf(stderr, "Failed to open unexport for writing!\n");
        return(-1);
    }

    bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
    write(fd, buffer, bytes_written);
    close(fd);
    return(0);
}

static int
GPIODirection(int pin, int dir)
{
    static const char s_directions_str[]  = "in\0out";

#define DIRECTION_MAX 35
    char path[DIRECTION_MAX];
    int fd;

    snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin);
    fd = open(path, O_WRONLY);
    if (-1 == fd) {
        fprintf(stderr, "Failed to open gpio direction for writing!\n");
        return(-1);
    }

    if (-1 == write(fd, &s_directions_str[IN == dir ? 0 : 3], IN == dir ? 2 : 3)) {
        fprintf(stderr, "Failed to set direction!\n");
        return(-1);
    }

    close(fd);
    return(0);
}

static int
GPIORead(int pin)
{
#define VALUE_MAX 30
    char path[VALUE_MAX];
    char value_str[3];
    int fd;

    snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
    fd = open(path, O_RDONLY);
    if (-1 == fd) {
        fprintf(stderr, "Failed to open gpio value for reading!\n");
        return(-1);
    }

    if (-1 == read(fd, value_str, 3)) {
        fprintf(stderr, "Failed to read value!\n");
        return(-1);
    }

    close(fd);

    return(atoi(value_str));
}

static int
GPIOWrite(int pin, int value)
{
    static const char s_values_str[] = "01";

    char path[VALUE_MAX];
    int fd;

    snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
    fd = open(path, O_WRONLY);
    if (-1 == fd) {
        fprintf(stderr, "Failed to open gpio value for writing!\n");
        return(-1);
    }

    if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1)) {
        fprintf(stderr, "Failed to write value!\n");
        return(-1);
    }

    close(fd);
    return(0);
}

int
main(int argc, char *argv[])
{
    int repeat = 10;

    /*
     * Enable GPIO pins
     */
    if (-1 == GPIOExport(POUT) || -1 == GPIOExport(PIN))
    {
        return(1);
    }

    /*
     * Set GPIO directions
     */
    if (-1 == GPIODirection(POUT, OUT) || -1 == GPIODirection(PIN, IN))
    {
        return(2);
    }

    do {
        /*
         * Write GPIO value
         */
        if (-1 == GPIOWrite(POUT, HIGH))
        {
            return(3);
        }

        usleep(100);
        if (-1 == GPIOWrite(POUT, LOW))
        {
            return(3);
        }
        usleep(100);
    }
    while (repeat--);

    if (-1 == GPIOWrite(POUT, LOW))
    {
        return(3);
    }

    /*
     * Disable GPIO pins
     */
    if (-1 == GPIOUnexport(POUT) || -1 == GPIOUnexport(PIN))
    {
        return(4);
    }

    return(0);
}

However, when I try to compile and run this script with g++, I get the error "Failed to open gpio direction for writing!", which means that fd = open(path, O_WRONLY); didn't work. I tried to run the same script with other GPIOs, and this happens only with the two SPI select ones, i.e. SPI_CE0_N and SPI_CE1_N (and not, for example, with the SPI_MOSI (GPIO10) one).

Why does this happen? Is there a way to solve this? The only one that comes to my mind is to use another GPIO pin as SPI_SS, but I guess that that would make the process slower, as the signal wouldn't be synchronized with the clock anymore.

1 Answer 1

0

The sysfs interface has been deprecated. I would not be surprised if it is removed from the Raspberry Pi kernel at some stage.

Why not just use SPI? That would handle the setting of chip select automatically.

You should really be using the /dev/gpiochip interface going forward (rather than the deprecated sysfs interface).

My lg library uses the /dev/gpiochip interface.

2
  • Thank you very much for the suggestions, @joan. I really used the sysfs because I found the Linux RPi GPIO code samples and the sysfs seemed the most sound approach, but evidently I was wrong...
    – Eggman
    Dec 14, 2021 at 14:25
  • About using only the SPI, I followed the script that I found here (the second one, without the BCM2835 library), and, from what I could understood (I'm new to the topic), the SPI_CE0_N pin just goes from high to low when the SPI port is opened (i.e. when SpiOpenPort() is called). In my case, as shown in the figure, I need to bring the SPI_CE0_N (or SPI_SS) back to high for 8 dummy clocks, without closing the port, as the clocks need also to be sent. Is there a way to do this using only the SPI?
    – Eggman
    Dec 14, 2021 at 14:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.