2

I'm trying to connect my Raspberry Pi 5 to Arduino via SPI inteface. As a reference, I've used connection scheme and code from this manual on penguintutor and it worked like a charm.

But in my project SPI0 bus will be connected to display, so I decided to use SPI5 bus and faced a problem connecting to Arduino. As I unsterstood, It's because of wrong bus mode. SPI0 is using single mode (MISO and MOSI pins), but SPI5 is using multi mode (SIO1 and SIO2 pins).

RP1 documentation says that dual mode for SPI5 is maximum I/O width. So is it possible to set single mode on SPI5, or I'm wrong?

My overlays in /boot/firmware/config.txt:

...
dtoverlay=spi0-1cs
dtoverlay=spi5-1cs
...

pinctrl get 0-39 output:

 0: ip    pu | hi // ID_SDA/GPIO0 = input
 1: ip    pu | hi // ID_SCL/GPIO1 = input
 2: a3    pu | hi // GPIO2 = SDA1
 3: a3    pu | hi // GPIO3 = SCL1
 4: no    pu | -- // GPIO4 = none
 5: no    pu | -- // GPIO5 = none
 6: no    pu | -- // GPIO6 = none
 7: no    pu | -- // GPIO7 = none
 8: op dh pu | hi // GPIO8 = output
 9: a0    pn | lo // GPIO9 = SPI0_MISO
10: a0    pn | lo // GPIO10 = SPI0_MOSI
11: a0    pn | lo // GPIO11 = SPI0_SCLK
12: op dh pd | hi // GPIO12 = output
13: a8    pn | lo // GPIO13 = SPI5_SIO1
14: a8    pn | lo // GPIO14 = SPI5_SIO0
15: a8    pn | lo // GPIO15 = SPI5_SCLK
16: no    pd | -- // GPIO16 = none
17: no    pd | -- // GPIO17 = none
18: no    pd | -- // GPIO18 = none
19: no    pd | -- // GPIO19 = none
20: no    pd | -- // GPIO20 = none
21: no    pd | -- // GPIO21 = none
22: no    pd | -- // GPIO22 = none
23: no    pd | -- // GPIO23 = none
24: no    pd | -- // GPIO24 = none
25: no    pd | -- // GPIO25 = none
26: no    pd | -- // GPIO26 = none
27: no    pd | -- // GPIO27 = none
28: ip    pd | lo // PCIE_RP1_WAKE/GPIO28 = input
29: no    pu | hi // FAN_TACH/GPIO29 = none
30: no    pu | -- // HOST_SDA/GPIO30 = none
31: no    pu | -- // HOST_SCL/GPIO31 = none
32: op dh pd | hi // ETH_RST_N/GPIO32 = output
33: no    pd | lo // GPIO33 = none
34: op dl pd | lo // CD0_IO0_MICCLK/GPIO34 = output
35: no    pd | lo // CD0_IO0_MICDAT0/GPIO35 = none
36: no    pd | lo // RP1_PCIE_CLKREQ_N/GPIO36 = none
37: no    pd | lo // GPIO37 = none
38: ip    pd | hi // CD0_SDA/GPIO38 = input
39: ip    pd | hi // CD0_SCL/GPIO39 = input

ls /dev/spidev* output:

/dev/spidev0.0  /dev/spidev10.0  /dev/spidev5.0

Arduino code:

#include <SPI.h>

// track if digital pins are set as input or output (saves reading registers) // Pins 0 to 9 can be used, pin 10 can be used for SS (but can be used if that's not required) bool pin_output[11];

void setup() { // by default all pins are inputs for (int i=0; i<11; i++) { pin_output[i] = false;
}

// Set the Main in Secondary Out as an output pinMode(MISO, OUTPUT); // turn on SPI as a secondary // Set appropriate bit in SPI Control Register SPCR |= _BV(SPE); Serial.begin(9600); }

void loop () { char print_text [50]; byte in_byte; // SPIF indicates transmission complete (byte received) if ((SPSR & (1 << SPIF)) != 0) { in_byte = SPDR; // if no action bit sent then return same if (in_byte & 0x80) { SPDR = in_byte; Serial.println("0x80 received"); } // if write set else if (in_byte & 0x20) { // set digital pin output - use mask to extract pin and output if (set_digital_pin (in_byte & 0x0F, in_byte & 0x40) != 0xFF) SPDR = in_byte; }
// If value is only pin numbers then digital read else if (in_byte < 0x10) { byte return_val = read_digital_pin (in_byte); SPDR = return_val; } else if ((in_byte & 0x10) && !(in_byte & 0x20)) { byte return_val = read_analog_pin (in_byte & 0x0F); SPDR = return_val; } else // Otherwise an error - return 0xff { SPDR = 0xFF; }

} }

byte set_digital_pin (byte address, bool high_value) { if (address < 0 || address > 10) return 0xFF; // Is it currently set as output - if not then set to output if (!pin_output[address]) { pinMode(address, OUTPUT); pin_output[address] = true; } // set to low if 0, otherwise 1 if (high_value == 0) digitalWrite(address, LOW); else digitalWrite (address, HIGH); }

// Returns 1 for high and 0 for low // If invalid then returns 0xff byte read_digital_pin (byte address) { if (address < 0 || address > 10) return 0xFF; // if currently output then change to input if (pin_output[address]) { pinMode(address, INPUT); pin_output[address] = false; }

return digitalRead (address); }

// As full byte is used for value there is no invalid value // If invalid then returns 0x00 byte read_analog_pin (byte address) { if (address < 0 || address > 5) return 0x00; int analog_val = analogRead (address); // Analog value is between 0 and 1024 - div by 4 for byte return (analog_val / 4); }

Python code running on RPi 5:

import spidev
import time
import RPi.GPIO as GPIO
spi_bus = 5
spi_device = 0

spi = spidev.SpiDev() spi.open(spi_bus, spi_device) spi.max_speed_hz = 1000000

Send a null byte to check for value

send_byte = 0x80 rcv_byte = spi.xfer2([send_byte])

repeat to check for a response

rcv_byte = spi.xfer2([send_byte]) data_recv = rcv_byte[0] if (data_recv != 0x80): print ("Unable to communicate with Arduino "+str(data_recv)) quit()

Data is sent as single bytearray

lower 4 bits = pin number

returns a value that is used to set a digital pin

pin = Arduino pin number

state = True for high, False for low

returns byte or 0xff = invalid

def req_set_digital (pin, state): if (pin < 0 or pin > 10): return 0xff if (state == True): pin += 0x60 else : pin += 0x20 return pin

read analog pin

def req_read_analog (pin): if (pin < 0 or pin > 5): return 0xff return pin + 0x10

read digital pin

def req_read_digital (pin): if (pin < 0 or pin > 10): return 0xff return pin

while True: # Turn LEDs on for i in range (3, 10) : send_byte = req_set_digital (i, True) rcv_byte = spi.xfer2([send_byte]) time.sleep (0.5) # Turn back off again for i in range (3, 10) : send_byte = req_set_digital (i, False) rcv_byte = spi.xfer2([send_byte]) time.sleep (0.5) # Get status of pin 2 send_byte = req_read_digital(2) rcv_byte = spi.xfer2([send_byte]) # Delay to allow Arduino to get answer ready time.sleep (0.25) # Send null request send_byte = 0xf0 rcv_byte = spi.xfer2([send_byte]) print ("Value of digital pin 2 is "+str(rcv_byte[0]))

# Get status of analog 0
send_byte = req_read_analog(0)
rcv_byte = spi.xfer2([send_byte])
# Delay to allow Arduino to get answer ready
time.sleep (0.25)
# Send null request
send_byte = 0xf0
rcv_byte = spi.xfer2([send_byte])
print (&quot;Value of A0 is &quot;+str(rcv_byte[0]))

Also, I've tried to make custom overlay based on default spi5-cs1 overlay, sadly, but no effect. Here is code:

/dts-v1/;
/plugin/;

/ { compatible = "brcm,bcm2711";

fragment@0 {
    target = &lt;&amp;spi5_cs_pins&gt;;
    frag0: __overlay__ {
        brcm,pins = &lt;12&gt;;
        brcm,function = &lt;1&gt;; /* output */
    };
};

fragment@1 {
    target = &lt;&amp;spi5&gt;;
    frag1: __overlay__ {
        /* needed to avoid dtc warning */
        #address-cells = &lt;1&gt;;
        #size-cells = &lt;0&gt;;

        pinctrl-names = &quot;default&quot;;
            pinctrl-0 = &lt;&amp;spi5_pins &amp;spi5_cs_pins&gt;;
        cs-gpios = &lt;&amp;gpio 12 1&gt;;
        status = &quot;okay&quot;;

        spidev5_0: spidev@0 {
            compatible = &quot;spidev&quot;;
            reg = &lt;0&gt;;      /* CE0 */
            #address-cells = &lt;1&gt;;
            #size-cells = &lt;0&gt;;
            spi-max-frequency = &lt;125000000&gt;;
            status = &quot;okay&quot;;
        };
    };
};

__overrides__ {
    cs0_pin  = &lt;&amp;frag0&gt;,&quot;brcm,pins:0&quot;,
           &lt;&amp;frag1&gt;,&quot;cs-gpios:4&quot;;
    cs0_spidev = &lt;&amp;spidev5_0&gt;,&quot;status&quot;;
            no_miso = &lt;0&gt;,&quot;=3&quot;;
};

};

So what am I missing or doing wrong?

42isanswer
  • 21
  • 2

0 Answers0