Go Down

Topic: Sending SPI data with Arduino as slave (Read 2387 times) previous topic - next topic

I am atempting to transfer data between two Arduino boards using SPI. I can find lots of examples of moving data from a master to a slave which work great but I can find no examples of moving data from the slave to the master. No mater what I do at the slave, the only data the arrives at the master on the MISO is the data that goes out from the master on the MOSI.

I have tried everything on the slave I can think of to write data but it ignores it completely and simply wraps the data it sees coming in from the master. Does anyone know how to send data from the Arduino acting as the slave to the Arduino acting as the master?

retrolefty

It's certainly possible to have two arduinos talk to each other using SPI, I have seen examples posted here before somewhere. Perhaps if you posted the code you tried for both the master and slave, someone can help.

Lefty


Graynomad

Receiving is not that hard but there are some tricks, for example the master has to insert some delays to give the slave time to respond.

Have a look at Nick Gammon's site (http://www.gammon.com.au/forum/?id=10892) for a good tutorial.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Thanks for the response. I have been through his site and it is very helpful if you are a master. That part I have working fine. My problem is that there are no examples on the web that I can find showing the Arduino sending data as a slave. Nick has an example posted of the Arduino receiving data as a slave but that is all. I assume you still use the SPI.transfer command to send the data from the slave to the master but this has no effect on the data stream. As a slave you cannot provide the clock, the master must do that by sending marker characters such as nulls. The Slave should then put the characters it wishes to send into its transmit buffer to be returned to the Master. This is where the Arduino falls down. No matter when or how I try to insert my return characters (0xAA for my test) at the slave end, the only data being returned on the MISO line is the data being sent in on the MOSI line from the master, namely 0x00.

As far as I can tell, the Arduino is a receive only device in slave mode. I am sure this is not the case and that I am missing something but I have no idea what that something is. If anyone out there knows of a code example of using the Arduino for bidirection communications as an SPI slave or even just sending data as a slave, please let me know.

Graynomad

AFAIK you cannot use the SPI library function as a slave.

Quote
As a slave you cannot provide the clock, the master must do that by sending marker characters such as nulls.

No, the clock is independent of the data with SPI, it's a separate signal.

The way most people do it I think is to "sacrifice" the first byte. ie the first master SPI.transfer returns whatever crap was in the slave's output register (SPDR). You then have to do another one to get real data.

One problem with the AVR implementation of SPI is that there is no buffering, so even if you tie SS to a slave interrupt unless the master adds some delays the slave is not fast enough to put data in the SPDR before the master starts to shift it out.

I do have an example of a slave ISR that does this but I'll have to remove some junk that's specific to my application. If you want I can do this and post it.

______
Rob

Rob Gray aka the GRAYnomad www.robgray.com

Rob, on my scope the only time the clock is active (which is generated by the master) is when the master is sending characters.  I am very interested in seeing how you are telling the master to generate the clock without having it sending characters. The first byte returned is what ever was in the buffer of the slave (in my case it is the last byte sent by the master) and after that all I can get is the last byte sent by the master echoed back. My problen is that I have not found a way for the slave to put data into the SPDR.

So YES! I would love it if you could provide code that will send anything back from an Arduino slave to a master that is not just echoing what the master sent it. Thank you very much for the assist.

The lack of buffering may be a problem because I must send out specific 8 byte messages and a leading junk character is not allowed but I may be able to find a way to make this work. And I do get a small delay between the time I get the select and when the clock starts but in my case the select does not initiate until my slave sets an attention line so I can preload before setting it. The only way to find out for sure is to try it. Thanks again.

Graynomad

Quote
on my scope the only time the clock is active (which is generated by the master) is when the master is sending characters.

That's correct, I was referring to this

Quote
the master must do that by sending marker characters such as nulls.


and meant that there is no need for special marker or sync characters. Maybe I should have said that the clock pulses are independant of the "contents" of the data, not the fact that there is data.

Here is a code snippet that should send 8 bytes back to the master

Code: [Select]
volatile byte spi_frame[] = {1,2,3,4,5,6,7,8};
volatile byte *spi_frame_ptr = NULL;


setup ()  {
SPDR = spi_buffer[0];
SPCR = (1 << SPE) | (1 << SPIE);
}


ISR (SPI_STC_vect) {
/////////////////////////////////////////////////////
// this interrupt is caused by the fact that the first byte
// has already gone.
//
// Now we're here though we don't use interrupts but poll the
// SPIF flag. This gives much faster response to the server and
// reduces the delays the server has to apply between bytes.

byte x;
unsigned int count = 0;
spi_frame_ptr = &spi_frame[1];

////////////////////////////////////////////////////
// set up a mask so we're not shifting bits during reception
// Why, because I'm not convinced the optimizer will turn
// (1 << SPIF) into a constant (should check this)
byte and_mask = (1 << SPIF);

////////////////////////////////////////////////////
// the first byte of the frame has gone, send the next 7
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {};  // potential hangs here !!!!!!!!!!!!!
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {};  // byte 3
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {};  // byte 4
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {};  // byte 5
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {};  // byte 6
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {};  // byte 7
SPDR = (*spi_frame_ptr)++;
while ((SPSR & and_mask) == 0) {};  // byte 8

////////////////////////////////////////////////////
// clear the interrupt flag that would have been set
// by the above serial transfers
x = SPSR;
x = SPDR;

////////////////////////////////////////////////////////
// seed the SPI data register with the first character
// of the next frame ready for the next clock pulse
// stream from the master
SPDR = spi_frame[0];  // get a value from wherever so it's ready for the next transfer
}


NOTES:

  • I have cut this down from something that had some error checking and double buffering of the data so there's a big chance that I stuffed something up in the process.

  • For an ISR this is a very long function, however if the slave has nothing else to do this doesn't matter and anyway if you need speed I see no other way,

  • If for any reason the master stops mid frame the slave will hang

  • The master still has to insert delays, you need a longish delay after the first transfer to allow the ISR to be serviced, then short delays after each byte

  • If the master and slave get out of sync you are in big trouble, most of the code I removed was to handle this situation but I didn't have an SS line to sync on and you do



This is the fastest way I know to get SPI data from an AVR processor without using assembler (damn AVR for not buffering the SPI). You could use the SPI_STC_vect interrupt for every byte but that would be a lot slower. To do this basically just remove all the while loops and load the next byte into SPDR, however the master will have to insert a longish delay for every byte in that case.

Quote
so I can preload before setting it


AFIAK that's the only way to do this.

______
Rob


Rob Gray aka the GRAYnomad www.robgray.com

Thanks Rob. I will give this a go later today and post the results. Now there is and example on the web of a SPI slave sending data for the next person searching for an answer! It is greatly appreciated.

I will have no control over the master so the buffer preload may be an issue but I won't know until testing next week with the real device. Thanks again for the help.

Graynomad

Quote
I will have no control over the master

Then that may be a problem, if the master just blats 8 bytes at high speed I don't think you can keep up with that code.

Another way to do it is to interrupt on the SS going low then do 8 while loops like I did.

Anyway see how it goes.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Again, I have written the perfect code. After getting it written with no errors in the compile, it was decided that the function was no longer needed and dropped. I learned something, the code is performing perfectly (by doing nothing) and the pay is the same, hence perfect code. Thanks for all the feedback. Sorry I can't report on the results.

Go Up