Go Down

Topic: Capturing SD card writes using SPI - 3.3v to 5v problem? (Read 2732 times) previous topic - next topic



My plan is to monitor a AVR based heater control system using Arduino. The controller has a SD card slot and live data is written to the SD card every 5 seconds. I've soldered wires on the data, clock and chip select pins and now I'm trying to capture the data written using an Arduino in slave mode. The signal looks like this:

Clock signal from SD card

Data signal from SD card

(from pratical reasons I've only used one channel on the oscilloscope)

As you can see the signals are just below 3V. I've tried to feed them directly to the 5V Arduino and some data is recieved but the data loss is significant and the data is more or less garbage - about 1% of the data is ok.

I've tried to adjust the 3V signals to 5V using a 7400 NAND connected as a buffer, but when I connect the data and clock lines to one of the NAND inputs the signal changes:

Clock signal when connected to NAND input, 7400 powered with 5V:

Data signal when connected to NAND input, 7400 powered with 5V:

It seems like if the SPI clock and data lines are not strong enough to drive a NAND input? Or is there another reason to why the signal does not drop to ground level? As far as I can understand the circuit is properly grounded and signal wires are very short, max 10 cm.  Any suggestions on how to build a simple level shifter from 3.3V to 5V? Maybe a pull-up / pull-down resitor and/or some capacitors are enough to adjust the signal and I can still use the 7400 as level shifter?

I would be thankful for any help on solving this issue!



Which logic family is the 7400?   Yes, I'd expect a low drive level, you need the oscilloscope probe to be x10 so it doesn't overload the signal.  It ought to be able to drive one CMOS (74HC series) load fine though, with short lead lengths.  In theory it ought to drive an Arduino OK in slave mode, but those lead lengths must be short to avoid too much capacitance.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]


Of course you need the SDcard to be connected too (to implement the transflash SPI protocol), which makes it all too easy to overload the driver...
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]


Yes, the SD card must be connected . otherwise there is no point eavesdropping the signa. The 7400 is a DM7400 (TTL i guess?). Is there another way to do this without messing up the signal?


TTL?   Could be the problem (too large a load, wrong logic threshold?).  Try a 74HC00 CMOS device, try powering it from 3V3 or somewhat higher (4V?).  Try just the Arduino but with short leads (10cm or less) just in case that's working.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]


Hi again!

Thankyou for telling me to use CMOS instead of TTL. Now the signal looks nice. Had to change to a 74HC08, the only CMOS logic that I found in the desktop drawer.

When feeding the 74HC08 with 3v3 the clock (red) and data (blue) looks like this:

And on 5v like this:

But the problem is still there! I get more or less only garbage on the Arduino. When this line is written to the SD Card:


The Arduino outputs this:



I've tried to change clock phase, clock polarity and bit order without any progress. Arduino source code:


// Sample SPI slave that echos to Serial on EOL
#include "pins_arduino.h"

char buf [150];
volatile byte pos;
volatile boolean process_it;

void setup (void)
  Serial.begin (9600);   // debugging

  // setup pins
  pinMode(MISO, OUTPUT);
  pinMode(MOSI, INPUT);
  pinMode(SS, INPUT);
  pinMode(SCK, INPUT);
  // 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 == 0x0a || pos >= 149)
      process_it = true;
    } else {
      // reset buffer
      pos = 0;
    } // end of room available
}  // end of interrupt routine SPI_STC_vect

// main loop - wait for flag set in interrupt routine
unsigned long oldmillis = millis();
void loop (void)
  if (process_it)
      // terminate string
      buf [pos] = 0;  
      Serial.println (buf);
      pos = 0;
      process_it = false;
    }  // end of flag set
   // print status every 5 secs
   if(millis() > (oldmillis + 5000))
     Serial.print("SPCR=" );
     Serial.println(SPCR, BIN);
     Serial.print("SPDR=" );
     Serial.print(SPDR, BIN);
     Serial.print(SPDR, HEX);
     Serial.print("SPSR=" );
     Serial.println(SPSR, BIN);
     oldmillis = millis();
}  // end of loop

Any ideas?



Perhaps the bytes are coming in faster than the interrupt routine can run - have you tried polling the SPI hardware in a tight loop instead?  I'd reduce the code to the bare minimum since we're clocking SPI at 4MHz, something like:
Code: [Select]

  while (pos < sizeof buf)
    while (!(SPSR & (1<<SPIF)))  {}
    buf [pos++] = SPDR ;

(I'm assuming the same polling method that works in master mode also works in slave mode, BTW, haven't checked this)
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]


Found out that the clock speed is the problem. According to the specifications the AVR could be clocked at 4 MHz when running as slave but in reality the clock line is not high long enough to be sampled. The $FF's come from the SD card initialization sequence. Direct polling is a lot faster than ISR, but since the problem is the sampling of the clock that doesn't help. I'm now testing a new aproach with two masters connected to the same SD card, one writing and one reading. Another solution might be to use shift register and FIFO.

Go Up