Go Down

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

torgil


#1 That's the problem. The CS pin from the ethernet chip is hardwired to pin 10. I have to find out if it is necessary to have pin 10 low when the Arduino is running as SPI slave. Also have to check that the ethernet chip doesn't freak out on getting the SD card data.

#3 The goal with the project is to monitor a failing heater controller. If the controller fails the monitor can't sit forever and wait for input in a polling loop. Therefore I need some kind of watchdog that breaks out of the loop when no input is recieved. This can be done using timer interrupts, but since interrupts need to be disabled for the Arduino to recieve data @4Mhz that option is out. So I need to figure out something simple, like an incrementing counter that doesn't overflow.


Graynomad

#76
Sep 06, 2011, 01:42 am Last Edit: Sep 06, 2011, 01:45 am by Graynomad Reason: 1
I gather you're worried about this never exiting
Code: [Select]
   while (rpos < 1060)
      {
      // wait for a byte
      while(!(SPSR & (1<<SPIF)))
        { }


You could add a counter

Code: [Select]

    counter = 0;
     while(!(SPSR & (1<<SPIF)) && counter++ < 1000)
        { }
     if (counter == 1000) oops();


This will however add to the time it takes to test SPSR so may cause an issue. Only one way to find out.

Another thing to do is not to disable all interrupts, just the timer used for millis(). Then you can set up another timer or use the watchdog timer.

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

torgil

#77
Sep 06, 2011, 03:21 pm Last Edit: Sep 06, 2011, 09:45 pm by torgil Reason: 1
I have tried the setup on an Arduino Ethernet board. The AVR only recieves data in slave mode when SS pin is low. This pin is shared (hard wired) with the ethernet controller. This means that the ethernet controller will also recieve data from the SD card master. Probably this hangs the ethernet controller. After recieving SPI data the ethernet chip no longer responds. Finding and cutting the wire from the ethernet controller to SS and wire it to another pin might be possible, but it is tight.

I think I'll give the other aproach, the one where two masters share the same SD card, a try.

torgil

Had to try bitbanging SPI slave mode using inline assembler, the result is better than expected but not good enough. Clock is getting out of sync and  some bits are missing. The idea is to wait for the first high clock pulse and then sample the data line every 4th instruction (16Mhz/4 = 4 Mhz):

Code: [Select]

void setup() {               
  DDRD = 0;
  PORTD = 0;
  noInterrupts();
  Serial.begin(9600);
}

byte recieve()
{
   uint8_t b = 0;

    // store b in reg
   asm volatile (
   // clear r21
   "ldi %[retval],0"    "\n\t"
   "ldi r21,0"    "\n\t"
   
   // wait for clock to go high version1
   // "in r20,0x09"               "\n\t"
   // "sbrs r20,7"           "\n\t"
   // "rjmp .-6"                     "\n\t"

   // wait for clock to go high version2
   "ldi r20, 0"            "\n\t"
   "sbis 0x09,7"           "\n\t"
   "rjmp .-4"                     "\n\t"

   // bit first bit is in r20
   "sbis 0x09, 6"                 "\n\t"
   "ldi r21, 64"          "\n\t"
   "add %[retval], r21"             "\n\t"
   "ldi r21,0"    "\n\t"

   "sbis 0x09, 6"                 "\n\t"
   "ldi r21, 32"          "\n\t"
   "add %[retval], r21"             "\n\t"
   "ldi r21,0"    "\n\t"

   "sbis 0x09, 6"                 "\n\t"
   "ldi r21, 16"          "\n\t"
   "add %[retval], r21"             "\n\t"
   "ldi r21,0"    "\n\t"

   "sbis 0x09, 6"                 "\n\t"
   "ldi r21, 8"          "\n\t"
   "add %[retval], r21"             "\n\t"
   "ldi r21,0"    "\n\t"

   "sbis 0x09, 6"                 "\n\t"
   "ldi r21, 4"          "\n\t"
   "add %[retval], r21"             "\n\t"
   "ldi r21,0"    "\n\t"

   "sbis 0x09, 6"                 "\n\t"
   "ldi r21, 2"          "\n\t"
   "add %[retval], r21"             "\n\t"
   "ldi r21,0"    "\n\t"

   "sbis 0x09, 6"                 "\n\t"
   "ldi r21, 1"          "\n\t"
   "add %[retval], r21"             "\n\t"
   "ldi r21,0"    "\n\t"

    // 7 bit
   "sbrs r20, 6"                 "\n\t"
   "ldi r21, 128"                    "\n\t"
   "add %[retval], r21"             "\n\t"

   : [retval] "=r" (b) :);

   return b;
}

void loop() {
  byte rbuf[255];
  int pos = 0;
  Serial.println("Waiting for SS");
  while(digitalRead(5) == HIGH)
  {}
  while(pos < 255)
    rbuf[pos++] = recieve();
 
  for(int a = 0; a < 255; a++)
  {
    Serial.print(rbuf[a],DEC);
    Serial.print(",");
  }
  Serial.println("");
}


As you can see I'm skipping the MSB in a try to get better sync with the clock. My assumption is that the problem lies in syncing with the clock since:

sbis 0x09,7
rjmp .-4

Is three clock cycles.

And the output:

Waiting for SS
240,252,253,252,246,250,249,240,247,246,234,244,243,228,241,240,222,238,237,216,235,234,210,232,231,204,229,228,198,226,225,224,190,222,221,184,219,218,178,216,215,172,213,212,166,210,209,160,207,206,154,204,203,148,201,200,142,198,197,136,195,194,193,128,191,190,250,188,187,244,185,184,238,182,181,232,179,178,226,176,175,220,173,172,214,170,169,208,167,166,202,164,163,162,194,160,159,188,157,156,182,154,153,176,151,150,170,148,147,164,145,144,158,142,141,152,139,138,146,136,135,140,133,132,131,132,129,128,254,254,253,248,251,250,242,248,247,236,245,244,230,242,241,224,239,238,218,236,235,212,233,232,206,230,229,228,161,226,225,192,223,222,186,220,219,180,217,216,174,214,213,168,211,210,162,208,207,156,205,204,150,202,201,144,199,198,138,128,195,194,130,192,191,252,189,188,246,186,185,240,183,182,234,180,179,228,177,176,222,174,173,216,171,170,210,168,167,204,128,164,163,196,161,160,190,158,157,184,155,154,178,152,151,172,149,148,166,146,145,160,143,142,154,140,139,148,137,136,142,130,133,132,134,130,129,
Waiting for SS
240,254,253,248,251,250,242,248,247,236,245,244,230,242,241,224,239,238,218,236,235,212,233,232,206,226,229,228,198,226,225,192,223,222,186,220,219,180,217,216,174,214,213,168,211,210,162,208,207,156,205,204,150,202,201,144,195,198,197,136,195,194,130,192,191,252,189,188,246,186,185,240,183,182,234,180,179,228,177,176,222,174,173,216,171,170,210,168,167,166,202,164,163,196,161,160,190,158,157,184,155,154,178,152,151,172,149,148,166,146,145,160,143,142,154,140,139,148,137,136,135,140,133,132,134,130,129,128,255,254,250,252,251,244,249,248,238,246,245,232,243,242,226,240,239,238,237,216,235,234,233,208,231,230,202,228,227,196,225,224,190,222,221,184,219,218,178,216,215,172,213,212,166,210,209,160,207,206,154,204,203,148,128,200,199,140,197,196,134,194,193,128,191,190,250,188,187,244,185,184,238,182,181,232,179,178,226,176,175,220,173,172,214,128,169,168,206,166,165,200,163,162,194,160,159,188,157,156,182,154,153,176,151,150,170,148,147,164,145,144,158,142,141,152,129,138,137,144,135,134,138,132,131,132,129,
Waiting for SS
254,250,252,251,250,242,248,247,236,245,244,230,242,241,224,239,238,218,236,235,212,233,232,206,230,229,200,227,226,194,224,223,188,221,220,182,180,217,216,174,214,213,168,211,210,162,208,207,156,205,204,150,202,201,144,199,198,138,196,195,132,193,192,254,190,189,188,153,186,185,240,183,182,234,180,179,228,177,176,222,174,173,216,171,170,210,168,167,204,165,164,198,162,161,192,159,158,186,140,155,154,178,152,151,172,149,148,166,146,145,160,143,142,154,140,139,148,137,136,142,134,133,136,131,130,130,128,255,252,188,252,251,244,249,248,238,246,245,232,243,242,226,240,239,220,237,216,235,234,210,232,231,204,229,228,198,226,225,192,143,222,221,184,219,218,178,216,215,172,213,212,166,210,209,160,207,206,154,204,203,148,201,200,142,198,197,136,195,194,130,192,191,190,250,188,187,244,185,184,238,182,181,232,179,178,226,176,175,220,173,172,214,170,169,208,167,166,202,164,163,196,161,160,159,188,157,156,182,154,153,176,151,150,170,148,147,164,145,144,158,142,141,152,139,138,146,136,135,140,133,132,134,130,129,254,

Could there be a smarter way to do this and to get the timing better? From the AVR instruction set I can't find any instruction that is waiting for a port condition.


Graynomad

Man I hate that inline ASM format.

I don't see any loop structure there, won't the whole thing blat through in one go after the first clock pulse?

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

Graynomad

#80
Sep 07, 2011, 11:10 am Last Edit: Sep 07, 2011, 11:25 am by Graynomad Reason: 1
Here's a quick version using a loop (of the top of my head and I'm rusty with my ASM so buyer beware)

Code: [Select]
  clr r_byte
   ldi r_count,8
   
wait_for_clock:
   sbis   PIND, 7
   rjmp  wait_for_clock
   
loop:
  sbis PIND, 6
  ori r_byte,1
  lsl r_byte
  dec r_count
  brne loop

done:


I didn't count the cycles, If that's not fast enough you could unroll the loop as you did.

I don't know if it's any better, just different and often a rewrite is a good way to go.

How long have you got from SS to the first CLK?

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

Nick Gammon

I doubt that, using assembler or any code, you will do better than the dedicated hardware.

The advertised performance of the hardware is that it will detect clock changes in "longer than 2 clock cycles", but we have found that it works on "equal to 2 clock cycles".

Considering that each instruction, assembler or generated by the C compiler, must take at least 1 cycle, and in some cases 2 or 3, I don't see how any loop (test, branch) can improve on 2 clock cycles.

Graynomad

Yep, good point. For some reason I forgot we'd already been down the hardware path.

Chalk it up to a "senior moment" :)
______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

torgil


Yes, I realize that the hardware way is the best way. But the initial problem is still not solved. Since the CS pin is occupied by the ethernet chip I can't use the Arduino in SPI slave mode without messing with the ethernet chip. Before testing the two masters sharing the same SD card aproach, which requires more logics and a lot more wiring, I had to try this. And I'm a bit surprised that it actually caught some of the bits correctly. Would probably work ok on slow SPI lines where there is enough time to check the clock line.

Nick Gammon

Aren't you sniffing SPI with a separate processor?

torgil

I have been thinking of using two processors, one for sniffing and one with ethernet board for communication. How would I then solve communication between the two processors? The SPI bus is occupied. Are there any other solutions? I've been looking around for a way to make an AVR act as onewire slave but haven't found any examples.

CrossRoads

Well, there's serial comm's between the two, there's I2C ... How much data do you need to pass back & forth?
Wireless! RF modules with virtualwire ....
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

Why do you need to communicate between them? I thought you were just trying to find what was being sent to the SD card.

1-Wire is quite slow, around 1500 bytes per second. Even serial is faster than that.

Graynomad

#88
Sep 08, 2011, 07:28 am Last Edit: Sep 08, 2011, 07:32 am by Graynomad Reason: 1
i2c should work, or do the SPI bit banging, or new soft serial.

I'm having trouble keeping up here, I can't decide it we're just sniffing data or this is part of the application.

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

torgil

Sorry for me not being clear:

The goal with the project is to continously monitor a heater controller. The heater controller itself is AVR based and does logging to a SD card. Logging is done by adding one line of text to a text file on the SD Card. The card is FAT16 and files are named BCmmddhhnn.LOG.

The plan is to catch this line of text by sniffing the SPI line and then forward the text to a webservice together with some 1-wire sensor reading and other sensors. Sniffing SPI, reading sensors and sending data is working but since the ethernet board occupies the SPI line this can not be done from within the same Arduino. I have found out the following alternatives:

1. Use two AVRs, one for sniffing and one for ethernet (thankyou Nick for the idea). Have to solve communication between the two AVRs.

2. Use logics to have two masters accessing the same SD Card, one for writing (the heater controller) and one for reading (the arduino). Possible, but will require additional logics.

Go Up