giri giri - 3 months ago 19
C Question

Unreliable SPI byte array transfer from Arduino to Raspberry Pi

I'm working on a project that collects data from an Arduino Pro Mini and sends it using SPI to a raspberry Pi for storage.

The Pro Mini will be reading analog input and calculating voltage (once I finish), and passing values to the Pi when prompted using an ISR.

I'm using C/C++ for both platforms to keep it uniform. The slave code is snipped together using Arduino IDE and the master code is based off a BCM2835 SPI library example for the Pi.

Arduino code is meant to calculate a float value and pre-process the float value into an array of 4 bytes/chars (I'm shooting for binary because I think it is the best way to go).
Once prompted by the Pi, each byte is sent and will be recompiled into a float.

Here is what I have now:

Slave

/*************************************************************
ARDUINO BREAKER READ/SPI PRE-PROC/TRANSMIT CASES
****************************************************************/

/***************************************************************
Global Variables
***************************************************************/

byte command = 0; //command from PI
byte bytes[4]; //

int sensorVoltage, sensorCurrent; //eventual live reading vars
float Voltage, Current, RealCurrent, RealVoltage, Power;

/***************************************************************
Set Up
-designate arudino as slave
-turn on interrupts
***************************************************************/

void setup (void)
{
//debugging with serial monitor
Serial.begin(9600);

// Set up arduino as slave
pinMode(MOSI, INPUT);
pinMode(SCK, INPUT);
pinMode(SS, INPUT);
pinMode(MISO, OUTPUT);

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

// turn on interrupts
SPCR |= _BV(SPIE);

} // end of setup

/*************************************************************
Interrupt Service Routine
************************************************************/

// SPI interrupt routine
ISR (SPI_STC_vect)
{
delay(500); //for errors

// Create union of shared memory space
union
{
float f_var;
unsigned char bytes[4];
} u;

// Overwrite bytes of union with float variable
u.f_var = RealVoltage;

// Assign bytes to input array
memcpy(bytes, u.bytes, 4);

byte c = SPDR;
command = c;

switch (command)
{
// null command zeroes register
case 0:

SPDR = 0;
break;

// case a - d reserved for voltage
case 'a':
SPDR = bytes[3];
break;

// incoming byte, return byte result
case 'b':

SPDR = bytes[2];
break;

// incoming byte, return byte result
case 'c':

SPDR = bytes[1];
break;


// incoming byte, return byte result
case 'd':

SPDR = bytes[0];
break;

/** // case e -h reserved for current
case 'e':

SPDR = amps.b[0];
break;

// incoming byte, return byte result
case 'f':

SPDR = amps.b[1];
break;

// incoming byte, return byte result
case 'g':

SPDR = amps.b[2];
break;

// incoming byte, return byte result
case 'h':

SPDR = amps.b[3];
break;

// case i - l reserved for wattage
case 'i':

SPDR = watts.b[0];
break;

// incoming byte, return byte result
case 'j':

SPDR = watts.b[1];
break;

// incoming byte, return byte result
case 'k':

SPDR = watts.b[2];
break;

// incoming byte, return byte result
case 'l':

SPDR = watts.b[3];
break;**/

} // end of switch

} // end of interrupt service routine (ISR) SPI_STC_vect

/***************************************************************
Loop until slave is enabled by Pi.
****************************************************************/
void loop (void)
{
/*************************************************************
Read and Calculate
****************************************************************/

/**
sensorVoltage = analogRead(A2);
sensorCurrent = analogRead(A3);
Voltage = sensorVoltage*(5.0/1023.0);
Current = sensorCurrent*(5.0/1023.0);
RealCurrent = Current/0.204545;
RealVoltage = (Voltage/0.022005);
Power = RealVoltage*RealCurrent;
**/
RealVoltage = 1.234;
/*************************************************************
Loop Check for SS activation
****************************************************************/

// if SPI not active, clear current command, else preproc floats and pass to SPI
if (digitalRead (SS) == HIGH){
command = 0;
}
/*************************************************************
Debug with serial monitor
****************************************************************/
/*
Serial.print("Byte 3: ");
Serial.println(bytes[3],BIN);
delay(500);
Serial.print("Byte 2: ");
Serial.println(bytes[2],BIN);
delay(500);
Serial.print("Byte 1: ");
Serial.println(bytes[1],BIN);
delay(500);
Serial.print("Byte 0: ");
Serial.println(bytes[0],BIN);
delay(1000);
Serial.println();*/
}


Master

#include <bcm2835.h>
#include <stdio.h>


void setup()
{
bcm2835_spi_begin();
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST); // The default
bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // The default
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_65536); // The default
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // the default
}

char getByte(const char command){

char read_data = bcm2835_spi_transfer(command);
delay(100);
return read_data;
}

int main(int argc, char **argv)
{
//If you call this, it will not actually access the GPIO
//Use for testing
//bcm2835_set_debug(1);

if (!bcm2835_init())
return 1;
setup();

//Start communication
bcm2835_spi_chipSelect(BCM2835_SPI_CS0);// Enable 0

//voltage 1-4
char read_data = getByte('a');
printf("byte is %02d\n", read_data);

read_data = getByte('b');
printf("byte is %02d\n", read_data);

read_data = getByte('c');
printf("byte is %02d\n", read_data);

read_data = getByte('d');
printf("byte is %02d\n", read_data);

/** voltage = volts.f;
printf("%.6f", voltage);
printf("\n");
**/
delay(1000);

bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, HIGH);
bcm2835_spi_chipSelect(BCM2835_SPI_CS0);// Disable 0
bcm2835_spi_end();
bcm2835_close();

return 0;
}


I'm using a fixed value to debug the code. Sometimes the output from SPI on the Pi is accurate, but otherwise it changes and outputs partially accurate and/or random bytes.

The issue I have been unable to work out is stability on the side of the Pi, so I am looking for help evaluating whether my code is causing inaccuracies or if it's my hardware.

Answer

At a guess I would say that this is a timing issue between the sender and receiver. Try looking at the bits of the data that you are receiving and see if they are shifted forward or backward. This will possibly indicate that the pi is waiting too long to begin receiving, or not waiting long enough for all of the data. I think the issues are probably around the delays:

delay(500); //for errors

on the Arduino, and

delay(1000); 

on the receiver. Why are you using these? 500ms is a long time to keep the pi waiting for a response to the SPI data.

Just a note, there is also now an SPI kernel driver (spidev) for the pi - this is a much more industry standard approach, and is potentially a more robust method.

There's a good example of this on the raspberry pi github site: https://github.com/raspberrypi/linux/blob/rpi-4.4.y/Documentation/spi/spidev_test.c

Comments