6

I'm trying to have a Raspberry PI act as a SPI master and send data to an Arduino Nano (one of the cheap clones). I partly succeeded, but I don't get a continuous communication, and some bytes get lost.

I only use simple wires on a breadboard to connect the two devices, no other components involved. These are the connections:

  • Pi MOSI - Arduino D11
  • Pi MISO - Arduino D12
  • Pi SCLK - Arduino D13
  • Pi GND - Arduino GND

Pi 5V is connected to a bench power supply. The Arduino is powered via USB cable connected to my Mac. Ground is common.

Here's a picture:

enter image description here

Following the instructions I found here, on the Arduino sketch I initialize SPI slave mode (I know it can't be master, or it will send 5V down the line and fry the Pi) this way:

pinMode(SCK, INPUT);
pinMode(MOSI, INPUT);
pinMode(MISO, INPUT);
SPCR |= _BV(SPE);

then I use SPI.attachInterrupt() and define a function ISR (SPI_STC_vect) { byte c = SPDR; ... } to read the bytes into a memory buffer in the quickest possible way (since it's an interrupt).

On the RaspberryPI side I tried several libraries:

  • node.js pi-spi
  • python wiringpi
  • python spidev

With all the above libraries I tried to:

  • change frequency from 1 KHz to 5 MHz
  • change all the 4 possible SPI modes (2 x 2 combinations of clock polarity and phase)
  • send a burst of 10-20 bytes, or send one byte at a time, with a program waiting for my user input on the keyboard

In all cases, as I said, I experienced a significant data loss (from, say, 10% to 80%).

I even tried to debug the signal with an oscilloscope. It looked pretty good to me, but I'm no expert.

I saw posts from people having (supposedly) done this with success. Why am I failing?

Hypothesis:

  1. My wires are crappy
  2. My Arduino is crappy
  3. My Raspberry PI is crappy (actually I tried 2 different ones at some point)
  4. My interrupt handler is too slow

Is there a better way to communicate?

A. I2C B. serial over USB

Please advice, oh knowledgeable crowd, and I shall be happy.

  • 1
    There is little to go wrong with SPI. What bit rate are you using? An Arduino is not very powerful, perhaps you are simply transmitting too fast. A photo or photos clearly showing your connections would be useful. – joan Mar 17 '16 at 22:28
  • 1
    Bit rate: as I said, I tried several frequencies, from 1 KHz (which I think is quite slow, even for an arduino) to 5 MHz (which is probably too fast). I added a picture of the wiring. – Stefano Masini Mar 18 '16 at 9:10
  • 1
    I felt the 1 KHz to 5 MHz was ambiguous. If you had put 1 Kbps or 5 Mbps (bits per second) I wouldn't have asked. Are you sure you have power and ground connections between the Pi and the Arduino. You have a breadboard with the power/ground rails with a break in the middle. – joan Mar 18 '16 at 9:14
  • 1
    I specified the frequency of the CLK signal, that's why I used Hertz. The breadboard rail has a break in the middle that's right, but I'm plugging my jumper wires before that. Ground, as I said, is common, as you can see from the black jumper wire going from the RaspberryPI GND rail to one of the Arduino GND pins. Power, as I said, is separate: Raspberry PI being powered from the bench power supply, and Arduino from the USB. I might try powering everything from USB, but I don't think it matters. – Stefano Masini Mar 18 '16 at 12:20
2

I don't know what is wrong with your set-up but I can confirm that it can work.

I've just tried between a Pi and a Pro Mini.


The only odd thing is that the transmitting serial baud rate was twice the requested baud rate. I.e. the Pro Mini code asked for 19.2 and actual was 38.4.

EDITED TO ADD

The baud rate discrepancy was because I had set the board to Pro Mini 8 MHz rather that Pro Mini 16 MHz in the Arduino IDE. Once I corrected the board type in the IDE the baud rate was set properly.


Here is a webm video showing the Pi end display.

#!/usr/bin/env python

# mini-spi.py
# 2016-03-18
# Public Domain

import time

import pigpio # http://abyz.me.uk/rpi/pigpio/python.html

pi = pigpio.pi()

if not pi.connected:
   exit(0)

h = pi.spi_open(0, 40000)

stop = time.time() + 120.0

n = 0

while time.time() < stop:

   n += 1
   pi.spi_xfer(h, "This is message number {}\n".format(n))
   time.sleep(1)

pi.spi_close(h)

pi.stop()

Pro Mini

// Written by Nick Gammon
// February 2011


#include <SPI.h>

char buf [100];
volatile byte pos;
volatile boolean process_it;

void setup (void)
{
  Serial.begin (19200);   // debugging

  // turn on SPI in slave mode
  SPCR |= bit (SPE);

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);

  // get ready for an interrupt 
  pos = 0;   // buffer empty
  process_it = false;

  // now turn on interrupts
  SPI.attachInterrupt();

}  // end of setup


// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register

  // add to buffer if room
  if (pos < sizeof buf)
    {
    buf [pos++] = c;

    // example: newline means time to process buffer
    if (c == '\n')
      process_it = true;

    }  // end of room available
}  // end of interrupt routine SPI_STC_vect

// main loop - wait for flag set in interrupt routine
void loop (void)
{
  int i, c;
  delay(5);
  if (process_it)
  { buf[pos]=0;
    Serial.println(buf);
    pos = 0;
    process_it = false;
  }  // end of flag set
}  // end of loop
  • As far as I understand, a SPI slave cannot "ask" for a specific frequency, but is entirely driven by the frequency of the CLK line. Are you maybe referring to the Serial.begin (19200) line? Doesn't that refer to the USB serial interface? As far as I understand, that's totally separate from SPI. Also, how did you measure 38.4? – Stefano Masini Mar 18 '16 at 20:06
  • The SPI bps is separate from the serial link bps. I set the SPI bps to 40k so that I could monitor the traffic with my piscope. I set the serial bps to 19200 with Serial.begin(19200). I used piscope to measure the serial bit length and it was 38400. I used both the serial monitor connected via USB and the Pro Mini's other TXD line via the Pi's UART. Both were receiving serial messages at 38400 bps. I don't know why. Perhaps a quirk of the Pro Mini or a quirk of using SPI. – joan Mar 18 '16 at 20:13
  • I had slave select connected. You didn't mention whether you had connected slave select or not. – joan Mar 18 '16 at 20:15
  • Can you be more specific about slave select? Which pin of the rasberry pi did you connect to which on the arduino? – Stefano Masini Mar 18 '16 at 20:16
  • I used Pi CE0 (GPIO 8) connected to Pro Mini D10. I think D10 is the standard pin used for slave select on the Arduino. – joan Mar 18 '16 at 20:18
0

If you need a speed like 19200 to 115200 bauds - do not even bother yourself with SPI! Use I2C interface - it's designed exactly for it. For SPI(and for I2C) there are two rules must-be in checklist :

  • Configure manually/by hand both Master and Slave to the same duplex mode, i.e. data rate will be jittering if slawe works half or twice fast than a master
  • Remember to hard-test in real life your slave's speeds. It is driven by CLK(SPI)/SCL(I2C) from the Master side, but it can not negotiate back, i.e. it can't "say" to master "we have a problem with this speed" - it just will glitch or will not work at all.

Check your wires twice - it can be a speed/perfomance issue in SPI, I2C is far more tolerant to the wiring, so that's another argument why I do recommend you to use I2C

0

I am suspicious about this line:

pinMode(MISO, INPUT);

I would expect the MISO pin to be an output on the slave.

If it's an input then writing data to it will be switching the pull-up resistor on and off on that pin, which might actually work some of the time if the other end of the line is pulling it down gently enough. This could explain why you're​ seeing intermittent problems rather than outright failure.

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.