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 ("Value of A0 is "+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 = <&spi5_cs_pins>;
frag0: __overlay__ {
brcm,pins = <12>;
brcm,function = <1>; /* output */
};
};
fragment@1 {
target = <&spi5>;
frag1: __overlay__ {
/* needed to avoid dtc warning */
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&spi5_pins &spi5_cs_pins>;
cs-gpios = <&gpio 12 1>;
status = "okay";
spidev5_0: spidev@0 {
compatible = "spidev";
reg = <0>; /* CE0 */
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <125000000>;
status = "okay";
};
};
};
__overrides__ {
cs0_pin = <&frag0>,"brcm,pins:0",
<&frag1>,"cs-gpios:4";
cs0_spidev = <&spidev5_0>,"status";
no_miso = <0>,"=3";
};
};
So what am I missing or doing wrong?