Two arduino modules communicate via SPI

I want to test the SPI communication bewteen the Duemilanove(Atmega328p) and ATMega1280. I make the Duemilanove as the Master and ATMega1280 as Slave. The wiring and the code are listed below (Define the macro SPI_MASTER_MODEL and SPI_SLAVE_MODEL for the Master and Slave side code respectively). Through the PC serial monitor, I found the received data from SPI is wrong. Does anyone can point out the error in my code? Thanks in andvance.

Wiring
ATMega328p ATMega1280
SS(10) -------------- SS(53)
MOSI(11)-------------- MOSI(51)
MISO(12)-------------- MISO(50)
SCK(13) -------------- SCK(52)

Code

/**************************************************************************************
Functionality:
Two Arduinos communicate via SPI.One is master, the other is slave.
atmega 328p works as master
atmega 1280 works as slave.

The circuit:
Use atmega 328p and atmega 1280 as test boards.
ATMega328p ATMega1280
SS(10) -------------- SS(53)
MOSI(11)-------------- MOSI(51)
MISO(12)-------------- MISO(50)
SCK(13) -------------- SCK(52)

Connect the atmega 328p/1280 board to PC through USB. Monitor the data from it.

***************************************************************************************/

#include “pins_arduino.h”

void SPI_Init(void);
unsigned char SPI_ReadWrite(unsigned char data);

#define SPI_MASTER_MODEL
//#define SPI_SLAVE_MODEL

#if defined(SPI_MASTER_MODEL)
const char* pSPIStr = “Master”;
#else
const char* pSPIStr = “Slavee”;// The same length
#endif

// init as SPI-Master
void SPI_MASTER_Init(void) {
pinMode(SCK,OUTPUT);
pinMode(MOSI,OUTPUT);
pinMode(SS,OUTPUT);
pinMode(MISO,INPUT);

// INIT interface, Master, set clock rate fck/4
// SPCR – SPI Control Register
//
// SPE: SPI Enable. When the SPE bit is written to one, the SPI is enabled. This bit must be set to enable any SPI operations.
// MSTR: Master/Slave Select. This bit selects Master SPI mode when written to one, and Slave SPI mode when written logic zero.
// If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set.
// The user will then have to set MSTR to re-enable SPI Master mode.
// SPR1, SPR0: SPI Clock Rate Select 1 and 0. These two bits control the SCK rate of the device configured as a Master.
// SPR1 and SPR0 have no effect on the Slave.

SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR0)|(0<<SPR1);
// enable double rate
// SPSR – SPI Status Register
//
// SPI2X: Double SPI Speed Bit.When this bit is written logic one the SPI speed (SCK Frequency) will be doubled when the SPI
// is in Master model.
SPSR = (1<<SPI2X); // we will now gain fck/2 instead of fck/4
}

void SPI_SLAVE_Init(void) {
pinMode(SCK,INPUT);
pinMode(MOSI,INPUT);
pinMode(SS,INPUT);
pinMode(MISO,OUTPUT);

// INIT interface, Slave, set clock rate fck/4
SPCR = (1<<SPE)|(0<<MSTR)|(0<<SPR0)|(0<<SPR1);
// enable double rate
SPSR = (1<<SPI2X); // we will now gain fck/2 instead of fck/4
}

unsigned char SPI_ReadWrite(unsigned char data) {
// set data to send into SPI data register
// SPDR – SPI Data Register. The SPI Data Register is a read/write register used for data transfer between the Register File
// and the SPI Shift Register. Writing to the register initiates data transmission. Reading the register causes
// the Shift Register Receive buffer to be read.
SPDR = data;
// Wait for transmission complete
// SPIF: SPI Interrupt Flag.
// When a serial transfer is complete, the SPIF Flag is set. An interrupt is generated if SPIE in
// SPCR is set and global interrupts are enabled. If SS is an input and is driven low when the SPI is
// in Master mode, this will also set the SPIF Flag. SPIF is cleared by hardware when executing the
// corresponding interrupt handling vector. Alternatively, the SPIF bit is cleared by first reading the
// SPI Status Register with SPIF set, then accessing the SPI Data Register (SPDR).
while(!(SPSR & (1<<SPIF)));
// return data read from SPI (if any)
return SPDR;
}

void setup() {

#if defined(SPI_MASTER_MODEL)
SPI_MASTER_Init();
#else
SPI_SLAVE_Init();
#endif

Serial.begin(9600);

}

void loop(){

Serial.println();
Serial.println("SPI communication begin.");
Serial.println();
const char
str = pSPIStr;
while (*str)
{
unsigned char dataBuffer = (unsigned char)(*str);
Serial.print(dataBuffer, BYTE);
dataBuffer = SPI_ReadWrite(dataBuffer);
str++;
Serial.print(dataBuffer, BYTE);
}
}

I found the title .. Arduino Bards .. very amusing:

Bard: One of an ancient Celtic order of minstrel poets who composed and recited verses celebrating the legendary exploits of chieftains and heroes

No SPI expert by far but I think there is something wrong with the wiring. Please try

Wiring ATMega328p ATMega1280 MOSI(11)-------------- MISO(50) MISO(12)-------------- MOSI(51) SCK(13) -------------- SCK(52)

SS is not needed

MOSI = Master Out Slave In MISO = Master In Slave out

hope this helps, Rob

You appear to still be connecting the MOSI pin on the Duemilanove to the MISO pin on the Mega. If the Duemilanove is the Master, its output is on the MOSI pin (Master out). If the Mega is the slave, its input is on the MOSI pin (Slave in). So,the MOSI pins need to be connected, as do the MISO pins.

I just tried the connection as Rob said (MOSI---MISO) and removed the SS connection. It doesn't work.

In page 197 of the datasheet of AVR ATMega1280(Chaper 21, Figure 21-2 ), it shows the connection should be MISO---MISO and MOSI--MOSI.

Thanks, Jeffrey

The SS signal is required to be wired between the master and slave, and the user code on the master must control the state of the SS output pin. From the 1280 datasheet:

When configured as a Master, the SPI interface has no automatic control of the SS line. This must be handled by user software before communication can start. When this is done, writing a byte to the SPI Data Register starts the SPI clock generator, and the hardware shifts the eight bits into the Slave. After shifting one byte, the SPI clock generator stops, setting the end of Transmission Flag (SPIF). If the SPI Interrupt Enable bit (SPIE) in the SPCR Register is set, an interrupt is requested. The Master may continue to shift the next byte by writing it into SPDR, or signal the end of packet by pulling high the Slave Select, SS line. The last incoming byte will be kept in the Buffer Register for later use.

I set the SS pin at the Master side. The code change is pasted below. But the received data is still wrong.

unsigned char SPI_ReadWrite(unsigned char data) {

#if defined(SPI_MASTER_MODEL)
digitalWrite(SS, LOW);
#endif

// set data to send into SPI data register
// SPDR – SPI Data Register. The SPI Data Register is a read/write register used for data transfer between the Register File
// and the SPI Shift Register. Writing to the register initiates data transmission. Reading the register causes
// the Shift Register Receive buffer to be read.
SPDR = data;
// Wait for transmission complete
// SPIF: SPI Interrupt Flag.
// When a serial transfer is complete, the SPIF Flag is set. An interrupt is generated if SPIE in
// SPCR is set and global interrupts are enabled. If SS is an input and is driven low when the SPI is
// in Master mode, this will also set the SPIF Flag. SPIF is cleared by hardware when executing the
// corresponding interrupt handling vector. Alternatively, the SPIF bit is cleared by first reading the
// SPI Status Register with SPIF set, then accessing the SPI Data Register (SPDR).

while(!(SPSR & (1<<SPIF)));

#if defined(SPI_MASTER_MODEL)
digitalWrite(SS, HIGH);
#endif

// return data read from SPI (if any)
return SPDR;
}

I also suppect it’s the problem of the frequency. And use the lowest frequency (fosc/128). Unfortunately, the issue still exists.

Thanks,
Jeffrey

But the received data is still wrong.

What are you sending and what are you receiving?

Can you reshow the actual wiring used between the two arduinos again. Is the following still true?

Wiring ATMega328p ATMega1280 SS(10) -------------- SS(53) MOSI(11)-------------- MOSI(51) MISO(12)-------------- MISO(50) SCK(13) -------------- SCK(52)

You don't show a ground wire between the two arduinos?

Lefty

You don't show a ground wire between the two arduinos?

very good point Lefty,

I'm no SPI master, but I'm pretty sure that since there is only one shared shift buffer, that it's the Master's responsibility to orchestrate the transfers. The Master shouldn't write data to SPDR while the slave is also writing, and the slave should wait until the Master has deemed it worthy of speaking.

You've got both sides talking at once. i think you're pretty much guaranteeing that whatever WAS sent is being overwritten by new data shifted in before it's being pulled out of the SPDR. What i would expect, is that what you're actually receiving is a random string of chars made up of letters in both "Master" and "Slavee" on both Master and Slave.

I think in order to do Arduino to Arduino transfers like this, the Slave has to wait for the Master to send it a "Ok, you can talk now" command and respond by writing the next byte to send into SPDR. The master then receives the sent data that it just asked for.

In the SPI UART that I'm using, the Slave has a FIFO buffer, and sends the next byte on demand from the master, never on it's own. I don't think you can use the same transmit / receive code like that on both master and slave.

I found the link below by by Nick Gammon the best tutorial on using SPI in the arduino enviroment for me. There is a lot of meat there.

http://www.gammon.com.au/forum/?id=10892

Lefty

Lefty, I didn't wire the ground. I think this is the reason why I always received the random char at both sides (Master and Slave). I'll have a try tonight.

TeslaFan points out another issue. But if the Master and Slave can't transfer the data at once, it will become half duplex transmission. In my code, the Master and Slave both have six characters to be transferred. I want to finish the transmission with six sessions. Is it possible, and how should I modify the SPI_ReadWrite function?

Thank you guys for your inputs.

Regards, Jeffrey

Hi Lefty,
You shared a great page. It’s very helpful. Thank you so much.

I have a question regarding a code sample in it. The code is pasted below from the section Sending and receiving data . If I want the Master receive the data “1” and “2” when transfering “4” and “3”, how should I write the code at the Slave side?

char a, b;
a = SPI.transfer (4);
// a is now 1
b = SPI.transfer (3);
// b is now 2

Regards,
Jeffrey

How is the slave handling the data?

If there's no warning from the master the first byte has to be "preloaded" into SPDR so it's ready to go when the clock pulses start.

Then the slave only gets an interrupt when that byte has gone, if the master doesn't add a small delay before asking for the second character the slave won't get into the ISR in time and the master will either get 0 of maybe a copy of the last byte (can't remember).


Rob

The issues are fixed.

I committed three mistakes in the original wiring and code.

  1. The GND pins should be wired.
  2. The SS should be set to LOW at the Master side before transferring data and reset to HIGH after it.
  3. The Master and Slave can not send data at once.

The modified code is pasted below(comments are removed).

/**************************************************************************************
Functionality:
Two Arduinos communicate via SPI.One is master, the other is slave.
They can not send data at once. The Master send data and the Slave read the data.

The circuit:
Use atmega 328p and atmega 1280 as test boards.
ATMega328p ATMega1280
SS(10) -------------- SS(53)
MOSI(11)-------------- MOSI(51)
MISO(12)-------------- MISO(50)
SCK(13) -------------- SCK(52)

GND -------------- GND
+5V -------------- +5V

Connect the atmega 328p/1280 board to PC through USB. Monitor the data from it.
***************************************************************************************/
#include “pins_arduino.h”

void SPI_Init(void);
unsigned char SPI_ReadWrite(unsigned char data);

#define SPI_MASTER_MODEL
//#define SPI_SLAVE_MODEL

#if defined(SPI_MASTER_MODEL)
const char* pSPIStr = “Master”;
#else
const char* pSPIStr = “Slavee”;
#endif

void SPI_MASTER_Init(void) {
pinMode(SCK,OUTPUT);
pinMode(MOSI,OUTPUT);
pinMode(SS,OUTPUT);
pinMode(MISO,INPUT);

SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR1)|(0<<SPR0);
SPSR = (1<<SPI2X);

digitalWrite(SCK, LOW);
digitalWrite(MOSI, LOW);
digitalWrite(SS, HIGH);
}

void SPI_SLAVE_Init(void) {
pinMode(SCK,INPUT);
pinMode(MOSI,INPUT);
pinMode(SS,INPUT);
pinMode(MISO,OUTPUT);

SPCR = (1<<SPE)|(0<<MSTR)|(0<<SPR1)|(0<<SPR0);
SPSR = (1<<SPI2X);
}

unsigned char SPI_ReadWrite(unsigned char data) {

#if defined(SPI_MASTER_MODEL)
digitalWrite(SS, LOW);

SPDR = data;
#endif
while(!(SPSR & (1<<SPIF)));

#if defined(SPI_MASTER_MODEL)
digitalWrite(SS, HIGH);
#endif

return SPDR;
}

void setup() {

#if defined(SPI_MASTER_MODEL)
SPI_MASTER_Init();
#else
SPI_SLAVE_Init();
#endif

Serial.begin(9600);
}

void loop(){

Serial.println();
Serial.println("SPI communication begin.");
Serial.println();
const char
str = pSPIStr;
while (*str)
{
unsigned char dataBuffer = (unsigned char)(*str);
//Serial.print(dataBuffer, BYTE);
dataBuffer = SPI_ReadWrite(dataBuffer);
str++;
Serial.print(dataBuffer, BYTE);
}
}

The Master and Slave can not send data at once.

Yes that can, you just haven't got it to work :)

I don't see anything in the main loop that treats master and slave differently. They are two different beasts and need different code.


Rob

The executable code for the Master and Slave in the function SPI_ReadWrite are different. I will change the macros SPI_MASTER_MODEL and SPI_SLAVE_MODEL when compile the code.

Yes I see, I was looking at the version in the first post.

If you take out all the comments and conditional code you are left with this on the slave

unsigned char SPI_ReadWrite(unsigned char data) {

   SPDR = data;
 
   while(!(SPSR & (1<<SPIF)));

   return SPDR;
}

Which will write into the data reg, wait for the byte to be exchanged, then read the result. That looks reasonable on face value.

What happens if this function is called half way through a transmission? If this happens you stomp on the byte being transmitted and/or get some bits from one byte and some from the next. That’s a potential gotcha. Given that both Arduinos are in a tight loop calling SPI_ReadWrite() I’d say that’s a very high probability.

Also the slave will hang if the master doesn’t transmit, that may or may not be an issue for your app.


Rob

Do you have any suggestion how to write the code at slave side to implement the full-duplex transmission? At least the SPDR reg shouldn't be changed in the half way through a transmission.

[quote author=Jeffrey Sun link=topic=57615.msg414758#msg414758 date=1302076679] I have a question regarding a code sample in it. The code is pasted below from the section Sending and receiving data . If I want the Master receive the data "1" and "2" when transfering "4" and "3", how should I write the code at the Slave side?

char a, b; a = SPI.transfer (4); // a is now 1 b = SPI.transfer (3); // b is now 2

...

The Master and Slave can not send data at once. [/quote]

The master and slave always exchange data at once. That is how SPI works. They also have to be coded differently. One demands a response, the other provides it (plus you can have multiple slaves by playing with different SS lines).

From my page:

http://www.gammon.com.au/forum/?id=10892

The master initiates a transfer with SPI.transfer. This causes an interrupt at the slave end. The slave then reads that byte. Now I must admit I haven't tried getting the slave to respond, that can be a project for tomorrow. (It's late). ;)

However my reading of the Atmega328 manual on page 167 shows:

The Slave may continue to place new data to be sent into SPDR before reading the incoming data. The last incoming byte will be kept in the Buffer Register for later use.

Now tentatively I take this to mean you can modify:

// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register
  
// do something with incoming byte "c"  

}  // end of interrupt routine SPI_STC_vect

to read:

// SPI interrupt routine
ISR (SPI_STC_vect)
{
SPDR = something; 

byte c = SPDR;  // grab byte from SPI Data Register
  
// do something with incoming byte "c"  

}  // end of interrupt routine SPI_STC_vect

That extra line is doing "place new data to be sent into SPDR before reading the incoming data".

Now the thing about SPI is you cannot communicate a response to this incoming byte because the bits are shifted out simultaneously in both directions. So by the time you know you have received 0x42 it is too late to reply with 0x55. But you can reply with 0x55 to the next incoming byte.

So I am guessing that actually the interrupt routine could look like this:

// SPI interrupt routine
ISR (SPI_STC_vect)
{

byte c = SPDR;  // grab byte from SPI Data Register
  
SPDR = c + 22;  // or whatever

}  // end of interrupt routine SPI_STC_vect

In other words, we put a response to the incoming byte into the SPDR register (the SPI data register) to be picked up next time around. Again, subject to testing.

An example of a master doing this is here:

// read 'length' bytes into 'data' to device from address 'address'
void sram_read (const byte device, unsigned int address, byte * data, unsigned int length)
{
  if (length == 0)  // don't bother if nothing to do
     return;

  digitalWrite (device, LOW);     // select device
  SPI.transfer (SRAM_READ);       // read mode
  SPI.transfer (address >> 8);    // high-order address byte
  SPI.transfer (address & 0xFF);  // low-order address byte
  for ( ; length ; --length)
    *data++ = SPI.transfer (0);     // data byte
  digitalWrite (device, HIGH);    // deselect device
}  // end of sram_write

The first three transfers are unidirectional (that is, we ignore the returned byte). However after we have sent the SRAM (in this case) three bytes: "read mode" and "address", we then just send down dummy 0 bytes, and get back a response.

I'll try to get a working example going in the morning.