Go Down

Topic: Arduino as SPI slave? (Read 10 times) previous topic - next topic

Graynomad

#40
Sep 01, 2011, 03:09 pm Last Edit: Sep 01, 2011, 03:13 pm by Graynomad Reason: 1
Everything you describe is already present in the CPU, it's called an SPI interface. I can't see how moving the logic outside will help.

I think you can record bytes every 6.5uS but I haven't tried I admit. This is the fastest code I can think of

Code: [Select]
byte buffer [100];
byte *buffer_end = buffer + sizeof(buffer) + 1;
byte *pos = buffer;
byte mask = (1<<SPIF);  // assume this will wind up in a reg and so will be faster than a literal
byte temp;

do {
  while(!(SPSR & mask));   
  temp = SPSR; // dummy read to setup a clear of SPIF
  *pos++ = SPDR;  // get the data and clear SPIF
} while (pos < buff_end)


That will record 100 bytes then exit. After that you print the array to the serial port.

If that (or something similar) doesn't do the trick then it doesn't doesn't matter how many Arduinos you have you need either a faster processor, write the code in assembler, or use some hardware like what Crossroads has designed. 

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

torgil



Everything you describe is already present in the CPU, it's called an SPI interface. I can't see how moving the logic outside will help.


The AVR running as a slave samples the clock signal in software. That is the reason to why the 4 MHz clock signal is missed. The clock is not high long enough for the AVR to recognize it as a clock pulse. This could be solved using an external shift register?


Graynomad

#42
Sep 01, 2011, 04:01 pm Last Edit: Sep 01, 2011, 04:21 pm by Graynomad Reason: 1
Quote
as a slave samples the clock signal in software.

Not sure where you get that from, AFAIK its all done with hardware.

However there is a speed limitation and unfortunately the data sheet is a bit ambiguous here

Quote
To ensure
correct sampling of the clock signal, the minimum low and high periods should be:
Low periods: Longer than 2 CPU clock cycles.
High periods: Longer than 2 CPU clock cycles.


So does that mean 3 clock cycles?

Bottom line is that without some really good code you will never know if it's the hardware or the software that's not working.

Quote
This could be solved using an external shift register?

That's true if the SPI can't use a 4MHz clock.

Quote
Only problem is that this occupies 9 input pins on the Arduino. My goal is to monitor the heater controller and I'll need some of those pins

So this is needed permanently on the final application is it?

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

CrossRoads

@rob,
Yes, better 'scope or logic analzer would certainly help. I bet wirewrapping this up would help also, signals are pretty crappy looking with jumper wires all over a breaboard.

@torgil,
What you describe in #39 is what I have working in #36 (just control signals so far). You can do it without the FIFO - capture serially in one shift register, move in parallel to a 2nd, and read it in serially with SPI command, that way its hardware all the way thru and not software sampling.
Have to check the control signals, make sure the parallell load signal is seperate from the clock out signal.
Still have to know when to bring the data in - so use the same parallel load signal to the 2nd shift register to create an interrupt to read the data?
I think interrupts have the same issue of having to be valid in time to be clocked in.
Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Nick Gammon

#44
Sep 02, 2011, 02:00 am Last Edit: Sep 02, 2011, 02:01 am by Nick Gammon Reason: 1
Here's some test code ...

Master:

Code: [Select]
// Written by Nick Gammon
// September 2011

#include <SPI.h>
#include "pins_arduino.h"

void setup (void)
{
 SPI.begin ();

 // Slow down the master a bit
 SPI.setClockDivider(SPI_CLOCK_DIV8);
 
}  // end of setup

void loop (void)
{
 delay (2000);

 // enable Slave Select
 digitalWrite(SS, LOW);    // SS is pin 10

 for (byte i = 0; i < 255; i++)
   SPI.transfer (i);

 // disable Slave Select
 digitalWrite(SS, HIGH);

}  // end of loop


This sends out 255 bytes (where each byte is itself, ie. 0, 1, 2, 3, 4, 5 etc.).

Slave:

Code: [Select]
// Written by Nick Gammon
// September 2011

#include "pins_arduino.h"

byte buf [256];
byte pos;

void setup (void)
{
 Serial.begin (115200);   // debugging

 // have to send on master in, *slave out*
 pinMode(MISO, OUTPUT);
 
 // turn on SPI in slave mode
 SPCR |= _BV(SPE);

}  // end of setup

// main loop - wait for flag set in interrupt routine
void loop (void)
{

   Serial.println ("Starting ...");
   pos = 0;
   
   while (pos < 255)
   {
   /* Wait for reception complete */
   while(!(SPSR & (1<<SPIF))) {}
   buf [pos++] = SPDR;
   }
   
   // check valid
   Serial.println ("Checking ...");
   for (byte i = 0; i < 255; i++)
     if (buf [i] != i)
       {
       Serial.print ("Error at position ");
       Serial.print (i, DEC);
       Serial.print (" got ");
       Serial.println (buf [i], DEC);
       return;  
       }
       
   Serial.println ("Passed.");        
}  // end of loop


Now to save CPU cycles the slave does not use interrupts. It uses a tight loop waiting for the byte to appear in the hardware register.

Once 255 bytes have arrived it checks that each byte is itself (eg. 0, 1, 2, 3, 4, 5 ...).

My results with the above code are:

Code: [Select]
Starting ...
Checking ...
Passed.
Starting ...
Checking ...
Passed.
Starting ...
Checking ...
Passed.
Starting ...
Checking ...
Error at position 198 got 199
Starting ...
Checking ...
Error at position 0 got 254
Starting ...
Checking ...
Passed.
Starting ...
Checking ...
Error at position 153 got 154
Starting ...
Checking ...
Error at position 0 got 254
Starting ...
Checking ...
Error at position 0 got 254
Starting ...
Checking ...
Passed.
Starting ...
Checking ...
Passed.
Starting ...
Checking ...
Passed.
Starting ...
Checking ...
Passed.


So it seems to sometimes send all 255 bytes without error. But sometimes not. And this is with clock/8.

With this instead:

Code: [Select]
 SPI.setClockDivider(SPI_CLOCK_DIV16);

... it ran without errors.

Go Up