Hallo,
ich versuche gerade, zwei UNOs via SPI miteinander kommunizieren zu lassen.
Es soll darauf hinauslaufen, daß Arduino 1 (Master) Daten zu Arduino 2 (Slave) schickt, der Salve etwas damit berechnet (hier im 2. Beispiel die Quadratzahl), und das Ergebnis wieder zum Master zurückschickt.
Als Daten sollen prinzipiell Typen wie byte, int, float, am liebsten auch structs genutzt werden können.
Von Nick Gammon gibt es ein sehr kompaktes und elegantes Beispiel, mit dem beliebige Datentypen, auch Structs, von einem Arduino auf einen zweiten übertragen werden können.(Gammon Forum : Electronics : Microprocessors : SPI - Serial Peripheral Interface - for Arduino)
Nach meinem Verständnis ist damit aber nur ein Transfer in eine Richtung möglich, Ich fand keinen Weg, Daten auch wieder zurückzugeben.
Nick Gammons Code des Masters
#include <SPI.h>
#include "SPI_anything.h"
typedef struct myStruct // create a structure to store the different data values:
{
byte a;
int b;
long c;
};
#define SS 10
myStruct foo; // Beispiel für den Datenexport einer Struktur
void setup ()
{
SPI.begin ();
SPI.setClockDivider(SPI_CLOCK_DIV8); // Slow down the master a bit
foo.a = 42;
foo.b = 32000;
foo.c = 10;
} // end of setup
void loop ()
{
digitalWrite(SS, LOW); // SS is pin 10
SPI_writeAnything (foo);
digitalWrite(SS, HIGH);
delay (10); // for testing
foo.c++;
} // end of loop
#include <Arduino.h>
template <typename T> unsigned int SPI_writeAnything (const T& value)
{
const byte * p = (const byte*) &value;
unsigned int i;
for (i = 0; i < sizeof value; i++)
SPI.transfer(*p++);
return i;
} // end of SPI_writeAnything
Nick Gammons Code für den Slave:
#include <SPI.h>
typedef struct myStruct // create a structure to store the different data values:
{
byte a;
int b;
long c;
};
volatile myStruct foo;
volatile bool haveData = false;
void setup ()
{
Serial.begin (115200); // debugging
pinMode(MISO, OUTPUT); // have to send on master in, *slave out*
SPCR |= _BV(SPE); // turn on SPI in slave mode
SPI.attachInterrupt(); // now turn on interrupts
} // end of setup
void loop ()
{
if (haveData)
{
Serial.println ((int) foo.a);
Serial.println (foo.b);
Serial.println (foo.c);
Serial.println ();
haveData = false;
}
} // end of loop
// SPI interrupt routine
ISR (SPI_STC_vect)
{
SPI_readAnything_ISR (foo);
haveData = true;
} // end of interrupt routine SPI_STC_vect
template <typename T> unsigned int SPI_readAnything_ISR(T& value)
{
byte * p = (byte*) &value;
unsigned int i;
*p++ = SPDR; // get first byte
for (i = 1; i < sizeof value; i++)
*p++ = SPI.transfer (0);
return i;
} // end of SPI_readAnything_ISR
Im Netz fand ich unter von davyzhu aus: Tutorial: Two Arduino SPI communication - Networking, Protocols, and Devices - Arduino Forum eine Zweiwege-Kommunikation. Das ist aber nur Einzelbyte-transfer, ich habe das zumindest auf 2 Bytes (für Ints) erweitern können, schaffe das aber nicht für andere Datenstrukturen wie Structs oder floats zu erweitern. Den "...anything..." Code von Nick hier zu nutzen geht deutlich über meine Fähigkeiten hinaus.
davyzhu Code für den Master
#include <SPI.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include "pins_arduino.h"
#define SDRDY 9 // slave data ready
#define mackPin PINB3
byte t = 0;
byte r = 0;
int Ergebnis = 0;
void setup() // *******************************************
{
Serial.begin (115200); // debugging
Serial.println("Ich bin der Master, der die Zahl an den Slave gibt, und das dort berechnete Ergebnis anzeigt");
pinMode(SDRDY, INPUT);
SPI_MasterInit();
}
void loop() // *******************************************
{
digitalWrite(SS, LOW);
t++;
if (t >= 15) t = 0;
r = SPI_MasterTransmit(t);
//block when slave data is not ready (not write to SPDR yet)
while (digitalRead(SDRDY) == LOW)
;
Ergebnis = SPI_MasterReceive();
digitalWrite(SS, HIGH);
Serial.print (t);
Serial.print("\t");
Serial.print (r);
Serial.print("\t");
Serial.println (Ergebnis);
// delay (1);
}
// copy from http://arduino.googlecode.com/svn
// SPI.cpp, SPI.h
void SPI_MasterInit(void) // *******************************************
{
pinMode(SCK, OUTPUT);
pinMode(MOSI, OUTPUT);
pinMode(SS, OUTPUT);
digitalWrite(SCK, LOW);
digitalWrite(MOSI, LOW);
digitalWrite(SS, HIGH);
// Warning: if the SS pin ever becomes a LOW INPUT then SPI
// automatically switches to Slave, so the data direction of
// the SS pin MUST be kept as OUTPUT.
SPCR |= _BV(MSTR);
SPCR |= _BV(SPE);
//default SPI speed is osc/2 (in arduino uno, 4Mhz, too fast)
//change it to osc/16
setClockDivider(SPI_CLOCK_DIV32);
}
byte SPI_MasterTransmit(byte cData) // *******************************************
{
/* Start transmission */
SPDR = cData;
/* Wait for transmission complete */
while (!(SPSR & (1 << SPIF)))
;
return SPDR;
}
byte SPI_MasterReceive(void) // *******************************************
{
SPDR = 0x00;
/* Wait for reception complete */
while (!(SPSR & (1 << SPIF)))
;
/* Return Data Register */
return SPDR;
}
void setClockDivider(byte rate) // *******************************************
{
SPCR = (SPCR & ~SPI_CLOCK_MASK) | (rate & SPI_CLOCK_MASK);
SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | (rate & SPI_2XCLOCK_MASK);
}
davyzhu Code für den Slave
// arduino spi library only use arduino as master
//#include <SPI.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include "pins_arduino.h"
#define SDRDY 9 // slave data ready
byte t = 0;
byte r = 0;
// create a structure to store the different data values:
typedef struct myStruct
{
byte a;
int b;
long c;
};
void setup() // *******************************************
{
SPI_SlaveInit();
pinMode(SDRDY, OUTPUT);
digitalWrite(SDRDY, LOW);
//Serial.begin(9600);
}
void loop() // *******************************************
{
r = SPI_SlaveReceive();
t = SPI_SlaveTransmit(r * r);
}
void SPI_SlaveInit(void) // *******************************************
{
pinMode(MISO, OUTPUT);
digitalWrite(MISO, LOW);
SPCR |= _BV(SPE);
}
char SPI_SlaveReceive(void) // *******************************************
{
/* Wait for reception complete */
while (!(SPSR & (1 << SPIF)))
;
/* Return Data Register */
return SPDR;
}
char SPI_SlaveTransmit(byte cData) // *******************************************
{
/* Start transmission */
SPDR = cData;
// assert drdy signal(to master)
digitalWrite(SDRDY, HIGH);
/* Wait for transmission complete */
while (!(SPSR & (1 << SPIF)))
;
// deassert drdy signal
digitalWrite(SDRDY, LOW);
return SPDR;
}
Interessanterweise wird neben den 4 Standard SPI-Leitungen incl. SS noch eine 5. (Slave Data ready) gefordert, damit der Slave anzeigen kann, daß der Master nicht senden darf.
Diese Zusatzleitung finde ich auch im Drucksensor-Beispiel der SPI-Bibliothek.
Und mehrfach hab ich im Netz die Aussage gelesen, daß die standard SPI-lib nicht als Slave fungieren kann. Nick Gammon nutzt sie trotzdem dafür.
Frage:
Wie kann entweder Nick Gammons elegante Lösung auf bidirektionalen Betrieb erweitert werden,
oder davyzhu's Lösung auf allgemeine Datentypen ?