0

I am working on a project with a bunch of Raspberry devices and have hit a bit of a road block.

I have a central raspberry Pi 4 that has 4 peripheral Raspberry Pi Picos all connected through an SPI bus.

For testing purposes the Picos are running a dummy program – they just write a string of numbers into the bus. The problem is, I am not able to read out the sent data on the Raspberry Pi 4. If only one Pico is connected at a time my program works fine, but as soon as another is connected to the bus, I only read zeros.

I think the problem might be, that the picos are fighting for contention in the data line, which is causing the zeros.

What is causing the problem?

Here is my code for the RPi4 (SPI master, in Python using spidev):

import spidev
import RPi.GPIO as GPIO
import time
import threading
import concurrent.futures

# Setup GPIO for manual CS
GPIO.setmode(GPIO.BCM)
DEVICE2_CS = 17
GPIO.setup(DEVICE2_CS, GPIO.OUT, initial=GPIO.HIGH)

# SPI for CE1
spi0 = spidev.SpiDev()
spi0.open(0, 1)  # Bus 0, CE0
spi0.max_speed_hz = 50000

# SPI for manual CE0
spi1 = spidev.SpiDev()
spi1.open(0, 0)
spi1.max_speed_hz = 50000

def read_device_0():
    adc = spi0.xfer2([1,2,3,4])
    time.sleep(0.1)
    value = adc
    return value

def read_device_1():
    adc = spi1.xfer2([1,2,3,4])
    time.sleep(0.1)
    value = adc
    return value

try:
    while True:
        val0 = read_device_0()
        val1 = read_device_1()
        print("Device 0: {val0} , Device 1: {val1} ")
        time.sleep(1)
except KeyboardInterrupt:
    pass
finally:
    spi0.close()
    spi1.close()
    GPIO.cleanup()

And here the code for the Picos (SPI slaved, in C using the Raspberry Pi Pico extension in VS Code):

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/spi.h"

#ifdef CYW43_WL_GPIO_LED_PIN
#include "pico/cyw43_arch.h"
#endif

#ifndef LED_DELAY_MS
#define LED_DELAY_MS 250
#endif

// Perform initialisation
int pico_led_init(void) {
#if defined(PICO_DEFAULT_LED_PIN)
    // A device like Pico that uses a GPIO for the LED will define PICO_DEFAULT_LED_PIN
    // so we can use normal GPIO functionality to turn the led on and off
    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    return PICO_OK;
#elif defined(CYW43_WL_GPIO_LED_PIN)
    // For Pico W devices we need to initialise the driver etc
    return cyw43_arch_init();
#endif
}

// Turn the led on or off
void pico_set_led(bool led_on) {
#if defined(PICO_DEFAULT_LED_PIN)
    // Just set the GPIO on or off
    gpio_put(PICO_DEFAULT_LED_PIN, led_on);
#elif defined(CYW43_WL_GPIO_LED_PIN)
    // Ask the wifi "driver" to set the GPIO on or off
    cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on);
#endif
}

#define BUF_LEN         0x100

void printbuf(uint8_t buf[], size_t len) {
    size_t i;
    for (i = 0; i < len; ++i) {
        if (i % 16 == 15)
            printf("%02x\n", buf[i]);
        else
            printf("%02x ", buf[i]);
    }

    // append trailing newline if there isn't one
    if (i % 16) {
        putchar('\n');
    }
}

int main() {
    int rc = pico_led_init();
    hard_assert(rc == PICO_OK);
    pico_set_led(true);
    // Enable UART so we can print
    stdio_init_all();
#if !defined(spi_default) || !defined(PICO_DEFAULT_SPI_SCK_PIN) || !defined(PICO_DEFAULT_SPI_TX_PIN) || !defined(PICO_DEFAULT_SPI_RX_PIN) || !defined(PICO_DEFAULT_SPI_CSN_PIN)
#warning spi/spi_slave example requires a board with SPI pins
    puts("Default SPI pins were not defined");
#else

    printf("SPI slave example\n");

    // Enable SPI 0 at 1 MHz and connect to GPIOs
    spi_init(spi_default, 1000 * 1000);
    spi_set_slave(spi_default, true);
    gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
    gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
    gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
    gpio_set_function(PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI);
    // Make the SPI pins available to picotool
    bi_decl(bi_4pins_with_func(PICO_DEFAULT_SPI_RX_PIN, PICO_DEFAULT_SPI_TX_PIN, PICO_DEFAULT_SPI_SCK_PIN, PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI));

    uint8_t out_buf[BUF_LEN], in_buf[BUF_LEN];

    // Initialize output buffer
    for (size_t i = 0; i < BUF_LEN; ++i) {
        out_buf[i] = i;
    }

    //printf("SPI slave says: When reading from MOSI, the following buffer will be written to MISO:\n");
    //printbuf(out_buf, BUF_LEN);


    for (size_t i = 0; ; ++i) {
        if (gpio_get(PICO_DEFAULT_SPI_CSN_PIN)==0){
        pico_set_led(true);
        // Write the output buffer to MISO, and at the same time read from MOSI.
        spi_write_read_blocking(spi_default, out_buf, in_buf, BUF_LEN);
        pico_set_led(false);
        sleep_ms(10);
        // Write to stdio whatever came in on the MOSI line.
        //printf("SPI slave says: read page %d from the MOSI line:\n", i);
        //printbuf(in_buf, BUF_LEN);
        }
    }
#endif
}

EDIT: Answering Milliways on 14.10:I'm sorry for not knowing the correct way to answer questions in forums like this, this my first time. So in advance apologies for anything I write in the wrong place or the wrong way. (So even if this is the wrong way of addressing comments, I'm sorry and please tell me the right way to do it.) Regarding the "default way", I explained in my comment that I set it up through the SPI settings (either through the on board settings application if you have a screen connected or through raspi-config and then enabling SPI). And it was suggested that I work on getting two devices running first. That is exactly what I am trying to do.As I explained in the original post, it is working just fine for one Pico. But as soon as two (or more) are connected at the same time, my program stops working.

Edit: The imports threading and concurrent futures are from tests I ran, to see if it would work with that (It didn't). They serve no purpose for this question, I'm sorry for any confusion it may have caused. Further it was asked how everything is connected. As I explained in the original post, the Raspberry Pi Picos are connected through a shared bus. This means they share a Master Input Slave Output (MISO), Maser Out Salve In (MOSI) and a clock (SCLK) line. Each pico has a seperate power and chip select connection. As for the manual pins mixing with the pre-existing CS, I found an article in which they suggest it is very much possible to use both at the same time. In my code I have deleted out most of the manual CS selection code I used from their example, you may ignore the lines"#Setup GPIO for manual CS" until "GPIO.setup(...)" as they are not really relevant for this specific question, unless anyone suggests using only manual chip select. I apologize once more if it caused any confusion. @Milliways: I don't think I quite understand the last sentence in your comment...("Indeed I don't understand why as the Pi has 2 SPI devices one with 2 CS the other 3") What do you mean with 2 SPI devices? Why does one have 3 Chip selects? Are you referring to the "hidden" SPI buses that you can enable on certain other pins?

7
  • Unless you explain how the devices are connected and how SPI is enabled on the Pi this is unanswerable. Commented Oct 12 at 21:10
  • NOTE spidev uses hardware SPI which only supports 2 CS on spi0 but this question is about using 4 devices. Commented Oct 13 at 10:34
  • To answer your questions, the devices are connected through a shared SCLK, MISO and MOSI line and each device has its own CS connection. SPI is enabled the default way, through the SPI setting. In addition to that I did some enabling with a "dtoverlay" command according to an instruction I followed. Regarding spidev, I have found multiple guides explaining a workaround, where you can use almost any other Pin as a chip select. If RPi.GPIO isn't a good library, what library would you suggest? Thanks for the answer! Commented Oct 14 at 9:52
  • DO NOT put details in comments; EDIT your question. Rather than claiming to do things "the default way" explicitly say what YOU did. The "normal" method only supports 2 CS. Commented Oct 14 at 9:58
  • I suggest you try 2 devices first then after you get that working ask. Commented Oct 14 at 10:03

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.