Go Down

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

torgil


Yes I'm here. Looks promising. Thanks for your effort. I did tight loop polling but without success, but without disabling interrupt. I'll try this and report back to you!



CrossRoads

Nick,
Can you try a test where the slave sends out at the same time as the master sends out, see if the both receive the data okay?
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Nick Gammon

I'm not clear about what you mean. Are we talking about 3 devices?

If not, the slave does a
Code: [Select]
y = SPI.transfer (x); where it sends x and receives y at the same time.

After all this is what SPI.transfer does:

Code: [Select]

byte SPIClass::transfer(byte _data) {
 SPDR = _data;
 while (!(SPSR & _BV(SPIF)))
   ;
 return SPDR;
}


So one more line of code (to assign to SPDR) and you are sending as well as receiving. I think that would be done at the hardware level as the clock pulses come in, so I don't see why that wouldn't work.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

CrossRoads

Yes, I am talking Master sending and receiving from slave at same time, while slave receives & sends at same time.
Then can do something like send a burst of data over for the slave to process, when send the 2nd burst it returns the results from the first burst.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Nick Gammon

Yes, you would typically use one-byte lookahead here. Since you can't "reply" to the first byte (as it each bit is being sent as each bit is being received) you would get byte 1, and "reply" to it by sending something on byte 2, which is the response to byte 1.

Running at high speed, I am not sure if the extra overhead of fetching the responses (or indeed computing them) would slow down the slave too much.

I suppose if you had a buffer already set up, yes you could do what you suggest and just copy a 256 byte buffer back to the master at the same time you receive another 256 byte buffer. Is that what you want me to test?
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

CrossRoads

Yes please. My coding at that level is not good, no experience at the very low level since 1 assembler class back in college. That's one of the things I like about the IDE, it seems to me to be not much harder than writing in BASIC.
But once down in the register level stuff, I just kind of glaze over looking at stuff like while (!(SPSR & _BV(SPIF))) after working all day.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Nick Gammon

Hmm, well I got it to work but it was tight. That is, this works ...

Master:

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

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

byte rbuf [256];

void setup (void)
{
 SPI.begin ();
 Serial.begin (115200);  
}  // 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++)
   rbuf [i] = SPI.transfer (i);   // send and receive one byte

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

 // check valid
 Serial.println ("Checking ...");
 for (byte i = 0; i < 255; i++)
   if (rbuf [i] != i)
     {
     Serial.print ("Error at position ");
     Serial.print (i, DEC);
     Serial.print (" got ");
     Serial.println (rbuf [i], DEC);
     return;  
     }
     
 Serial.println ("Passed.");    
   
}  // end of loop


Slave:

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

#include "pins_arduino.h"

byte rbuf [256];

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)
{
byte rpos;

   Serial.println ("Starting ...");
   rpos = 0;
   
   noInterrupts ();    
   while (rpos < 255)
     {
     // send a byte
     SPDR = rpos;
     
     // receive a byte
     while(!(SPSR & (1<<SPIF)))
       {}
     rbuf [rpos++] = SPDR;
     }
   
   interrupts ();
       
   // check valid
   Serial.println ("Checking ...");
   for (byte i = 0; i < 255; i++)
     if (rbuf [i] != i)
       {
       Serial.print ("Error at position ");
       Serial.print (i, DEC);
       Serial.print (" got ");
       Serial.println (rbuf [i], DEC);
       return;  
       }
       
   Serial.println ("Passed.");        
}  // end of loop


But I was getting errors until I moved:

Code: [Select]
byte rpos;

from being a global variable to a local one (maybe that got put into a register). And I couldn't send a buffer. So CPU cycles must be really tight. I doubt if you could do anything useful at that speed.

Quote
I just kind of glaze over looking at stuff like while (!(SPSR & _BV(SPIF))) after working all day.


Yeah that stuff can be mind-boggling, but really it is just reading a byte from the processor (the SPI status byte) and seeing if a particular bit is set. In this case the SPIF bit is the high-order bit, so you could write this, but perhaps not be as portable:

Code: [Select]
while (SPSR & 0x80 == 0)
   ;


Or even:

Code: [Select]
while (SPSR & 0b10000000 == 0)
   ;
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

CrossRoads

"I doubt if you could do anything useful at that speed."  Rats.
Okay, I'm gonna keep working on a FIFO interface then.
Guess I should make it FIFO both direction for ultimate flexibility.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Graynomad

But you can save and display at 4MHz, that does make a somewhat useful sniffer but only for one channel, ie MISO or MOSI but not both and to be really useful though I think you need to be able to sample both, so your FIFO setup would be the go for that I reckon but you'll need two FIFOs.

Another way to do this is with high-speed SRAMs, you can get 1 or 4 bit versions with fast access times that would make good sampling chips, although they would need more support hardware.

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

CrossRoads

The FIFOs are really fast, just need the serial/parallel shift registers, and a counter to keep in sync with the bytes.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Graynomad

Crossroads, did you do any more work on that FIFO design?

I just had a brilliant (I think) idea to do a cheap analyser, I might start another thread to write it up.

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

CrossRoads

Ordered some 50 MHz FIFO parts and the Saleae analyzer, busy with fencing this weekend.  Competed this morning, marked out strips at our club this afternoon, back at it tomorrow, classes on Tuesdays. Entering stuff for some upcoming tournaments now.
What'd you have in mind?
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Graynomad

I started a thread here

http://arduino.cc/forum/index.php/topic,71396.new.html#new

Just a thought. Mind you the FIFOs would do a similar thing.

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

torgil


Done some more test on the heater controller today and finally I got it working. My conclusions:

- Yes, the AVR can be a SPI slave running at 4 MHz. Since there is also a few us delay between incoming bytes on the controller that I'll monitor you can also do some processing on the incoming bytes before storing them in an array for printing.

- A logic level converter is necessary - otherwise clock line is lost, I used a 78HC08 running av 5V. I also inverted the CS line from the controller and used that signal as the other AND input. In that way the clock line is low

- The clock is, of course, running also when data is transfered from slave to master. When a slave is eavesdropping only one line (MOSI) on the SPI traffic this results in lots of $FF. A large buffer wouldn't even get me half the way to the data I'm after.

- After analyzing output from the SPI line, dropping the $FF's and being careful on what to print out on serial I found that the data I was after was found on position 549 and onwards. This does not include the $FF's. The controller was reading two blocks and writing one block of data before writing one block to the textfile.

Here is the code for printing out the useful data:

Code: [Select]

#include "pins_arduino.h"

byte rbuf [512];

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

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

  // turn on SPI in slave mode
  SPCR |= _BV(SPE);
}  // end of setup

// main loop - wait for flag set in interrupt routine
void loop (void)
{
    int rpos;
    int wd;
   
    Serial.println ("Starting ...");
   
    rpos = 0;
    // wait for SS pin to go low
    while(digitalRead(SS) == HIGH)
    {}
   
    // data is found at position 549 -1060
    noInterrupts ();   
    while (rpos < 1060)
      {
      // wait for a byte
      while(!(SPSR & (1<<SPIF)))
        { }
     
      // read SPDR
      byte c = SPDR;
     
      // filter out 0xFF and store pos 549 - 1060
      if(c != 0xFF )
      {
         if(rpos >=549)
            rbuf [rpos-549] = c;
        // increment counter
        rpos++;
      } 
      }
    interrupts ();
   
    Serial.println ("Printing ...");
    for (int i = 0; i < 512; i++)
    {
      Serial.print(i);
      Serial.print(";");
      Serial.print(rbuf[i],DEC);
      Serial.print(";");
      Serial.println(rbuf[i]);
      rbuf[i] = 0;
    }
    Serial.println ("Done ...");       
} // end of loop


Thanks for the help!

Remaining issues:

1. Find out if an Arduino Ethernet can be a SPI slave or if the ethernet IC messes up the spi lines. Check if another pin can be CS line

2. Find the last line of text from the rbuf, probably will have to check for $FF since this is 0xFFFF is FAT eof marker.

3. Find out how to implement some kind of watchdog working when interrupts are disabled. Could a ++ counter be enough?

Regards,
Torgil



CrossRoads

For #1, each SPI slave needs its own CS line from the master. Any pin can be used as the SS line.

#2, sounds like a function of your code.

#3, not sure what you're after.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Go Up