Arduino as SPI slave

I think you're good to go. The code assumes two-way comms which you don't need so you can drop this line

// have to send on master in, slave out
pinMode(MISO, OUTPUT);

Also on the slave you have to pull the SS pin low (use a flying wire for starters) or the SPI won't receive.


Rob

Hi Rob

sorry to be thick

but what do you mean fly wire, would i not just set that PIN(HIGH) in the code?

ie
digitalWrite(SSPin,LOW);

I'll give it a go today and let you know the results :slight_smile: once I am clear on that one

thank you for the help

You can't do that because the SS signal is an input on your slave Arduino, it needs to be held low for the SPI hardware to work. The quickest way to do this (just to get things working) is to plug a wire (aka flying wire/lead) in with one end in a GND hole (one the Arduino headers) and the other end into the SS input, D10 on the Duemilanove and D53 on Mega. (not sure what you have).


Rob

Hi Guys

I have tried the Arduino connected to the Glider and I am not getting anything, not even spirilious characters!

This could be that the SPI does not activate until the glider detects a flight as I have set the aux port to SPI as described in the manual.

I have connected in the following fashion -

Bird Arduini Duemilanove
Gnd Gnd
Aux1 Pin 13 sck
Aux2 Pin 11 MOSI
Pin 12 MISO not connected
Pin 10 SS connected to Gnd

I hope that’s correct, but I don't understand how the slave detects the speed and mode ETC and I notice we don't seem to have included any SPI library. Could you please explain? Is the code
SPCR |= _BV(SPE); some sort of built ASM cmd I don’t see a reference to it in the SPI reference page?
Should there not be some SPI library included and some settings made ie setClockDivider()???

failing my setup it has to be that a flight detection is required, in which case I need Synco and will have to wait until he gets back to NZ to chek on that

:frowning:

Graynomad:
You can't do that because the SS signal is an input on your slave Arduino, it needs to be held low for the SPI hardware to work.

From the Atmega manual, p171:

When the SPI is configured as a Slave, the Slave Select (SS) pin is always input. When SS is held low, the SPI is activated, and MISO becomes an output if configured so by the user. All other pins are inputs. When SS is driven high, all pins are inputs, and the SPI is passive, which
means that it will not receive incoming data. Note that the SPI logic will be reset once the SS pin is driven high.

So if your Arduino is an SPI slave, then pin 10 (for the Uno and variants) needs to be low or nothing will happen. As Graynomad said, grounding it should work.

My slave example didn't need the SPI library because the SPI library was designed for the master end. There is nothing particularly useful for slaves. Can you post your code? You should have the stuff in setup like this:

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

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

And you need the interrupt service routine:

// SPI interrupt routine
ISR (SPI_STC_vect)
{
  byte c = SPDR;
 
 // process incoming byte here (eg. store it somewhere)

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

The slave should detect the clock rate of the master. That is, the slave clocks data in as SCK changes, the rate at which it changes is controlled by the master.

Hi Nick

I cut and pasted your slave code section from the start of this topic, so it should be spot on, I only removed the line as recommemed by Rob pertaining to MISO

// have to send on master in, slave out
pinMode(MISO, OUTPUT);

I have also connected pin10 to gnd to pull it down

The idea was any data sent would be viewable on the serial monitor as your code outputs at 9600baud on the arduino com port.

I am seeing nothing, if my connections are correct I can ony asume that the data port only activates during flight, that is when the software detects a launch.

If you agree then I'll have to maybe fake a launch on tethered balloon and see if data starts to churn out.

thank you for the help so far, I'll post the explanation when I have it :slight_smile:

It's possible it's inactive I suppose. Without reading the datasheet it is hard to be sure. Your "fake" launch may prove things. The other thing would be, if possible, to hook up a scope or logic analyzer and try to see if the SCK is being pulsed at all. You would need to check you have the device configured correctly.

I am seeing nothing

The print line

Serial.println (buf);

Will not print anything (or at least nothing viewable) if for example you have low binary values in the buf array.

Just to see I'd change it to

Serial.println (buf[0], HEX);

and then maybe a loop to dump the whole thing.

for (int i = 0; i < 100; i++)
    Serial.println (buf[i], HEX);

Although if the ISR is not firing of course there will be nothing useful to look at.


Rob

Thanks Rob, no joy yet, its looking like its not tramsmitting, I'll do some testing and post the results :disappointed_relieved:

Yep you need to verify things are working at the source then follow the data until you find where it stops.


Rob

Hi Gents, Can I just check something with you, testing tonight I noticed that when the SPI connection is plugged into the Arduino the LED marked L is flashing very faintly. Looking at the schematic this looks to be the SCK working would you agree? if so is this normal? doe sthe SCK continually send a pulse? or does thsi idicate the SPI is pumping data out were just not seeing it?

nathanBomberHarris:
... does the SCK continually send a pulse?

No it doesn't. See my logical analyzer data here:

However, it is important to note that SPI does not wait for any acknowledgement. So if you are the slave, and you are not processing the data, that's your bad luck. The master will keep sending it.

nathanBomberHarris:
... or does this indicate the SPI is pumping data out were just not seeing it?

Sounds like it is doing just that.

Sounds like either you have not configured your end as a slave, you haven't pulled SS low, or your interrupt service routine is not firing for some reason (like interrupts disabled).

Hello Nick/Rob

This is odd after studying the ARDUINO 2009 sckematic I copied the 1K resistor and LED L off the SCK PIN and did the same on my breadboard for PIN 11 boths pins flash away nicely!

I take it that this means SPI data must be traversing the connections but still no output on the seriasl monitor window????

Help! I feel its nearly there what I am I missing (PIN 10 to gnd)

:frowning:

Can we see the sketch for the receiving end?

Sure //note code added as suggested by Rob I tried with and without :slight_smile: thank you for helping

// Written by Nick Gammon
// February 2011
#include "pins_arduino.h"
char buf [100];
volatile byte pos;
volatile boolean process_it;
void setup (void)
{
Serial.begin (9600); // debugging
// have to send on master in, slave out
//pinMode(MISO, OUTPUT);

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

// get ready for an interrupt
pos = 0; // buffer empty
process_it = false;
// now turn on interrupts
SPCR |= _BV(SPIE);
} // 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)
{
if (process_it)
{
buf [pos] = 0;
for (int i = 0; i < 100; i++)// Code added on suggestion Rob
{
Serial.println (buf[0],HEX); // Code added on suggestion Rob
}
pos = 0;
process_it = false;
} // end of flag set

} // end of loop

It helps to use the [ code ] tags (hit the # button above the post).

Anyway, you won't see anything if process_it is not true, and that hinges on getting a newline. Does the device send one?

For debugging you could change it to something like this to see if the buffer is filling up:

void loop (void)
{

  // debugging  
  if (pos > 20)
  {
   for (int i = 0; i < pos; i++)
      {
      Serial.println (buf[i], HEX); 
      }
      pos = 0;
  }
  
   
}  // end of loop

Nick this is GREAT as I've just discovered my none-interrupt version can't keep up with 2 bytes of transfer - it needs a gap between bytes..

So - this is GREAT but forgive my slowness- how can I SEND data from the slave - so let's say that first byte comes in under interrupts and while receiving it - I want to return some info with the second incoming byte (as SPI is bidirectional) ????

Pete

Any ideas?

hmm, another question - you're assuming constant data coming in ending in say CR.. how would one END it when the SS line goes back up...

My slave needs to accept 2 bytes - passing a byte of info back on the second incoming one - then it is done - SS line goes back up and that's when i need to know about it..

??

Have a function called something like spiBusy() that returns the state of the SS line , also the ISR can look at a parameter that states how many bytes to accept before setting the string ready flag (Instead of the /r/n) or whatever.

Or

Have the ISR manage a count that states how many bytes have been received and all your main loop does is wait for the right number to be received.

None of the above however deals with the zero time between bytes (IE, no empty clock cycles)

I am looking into that now

So there is no hardware flow control available to the slave. The master can use clock delay tactics to help but again, this is not the slave saying "Wait... i'm busy getting your info" so in general it would be a bad idea to tie any significant processing within the messaging loop. I guess SPI is not really geared for this type of communications, I2C has the mechanics to delay responses but not SPI.

So making the slave simply send back a status of the inverse of the sent message or something may be the best you can hope for beyond a certain speed unless the messaging it broken up into separate packets.

  1. Send Command
  2. Check Status for completion

if reading several bytes is needed then a send routine that focuses on the already prepared byte stream would be the way to go.

Many SPI devices have all this implemented in hardware and are tuned to respond within the clock cycles (SAR ADCs for instance)

The example code above where a message is sent using a /r/n as a terminator is probably not the best example of how to do an interactive SPI communications, this is probably why it is Master to Slave only. the received string is sent out the serial port once the whole message is in the buffer.