7

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.

Stefano Masini
  • 171
  • 1
  • 1
  • 3

3 Answers3

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
joan
  • 71,852
  • 5
  • 76
  • 108
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

Alexey Vesnin
  • 926
  • 10
  • 17
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.

Dave Turner
  • 101
  • 2