Run multiple SD cards on a single SPI bus.

I'm probably doing something wrong but I dunno what at this point.

I have 2 SD modules sharing power, ground, MOSI, MISO, and SCK, each with separate CS.

I want to be able to keep both open even if only 1 runs at a time.

With CD library you get an SDClass object instantiated for you, SD, that can handle 1 SD card.
So I made another that I called SD2. And I made 2 File objects so they'd each have their file info but I get the feeling either there's more or I went about this all wrong.

It runs. But it only sees the card on CS 10 even when I went and coded the CS pins to change.
There's primitive user i/o; d0 or d1 is supposed to select device, fwhoot makes the current file name whoot.txt, s## changes the state to ## where currently 2 checks for file exist and 3 opens file both using the current name and device except it always gets the same chip!

#include <SPI.h>

#include <SD.h>

#define CARD_0_CS 10
#define CARD_1_CS 9

SDClass SD2;
SDClass *sdcard = &SD;

#define BUFF_SIZE 512
byte buff[ BUFF_SIZE ];
byte *BP = buff;
int  count = 0;
int  numret = 0;

byte state = 0; // controls what operation is run
byte parstate = 0;
byte flag = 0;  // general use True/False
byte B;         // single byte buffer
char fname[ 14 ] = "text.txt";
File sdfile[ 2 ];    // File objects
byte CSpin = CARD_0_CS;
byte  i = 0;

int readSerialNum( byte SB )
{
  static  int total;

  if (( SB == ' ' ) || ( SB == 13 ) || ( SB == 10 ))
  {
    return total;
  }

  if (( SB < '0' ) || ( SB > '9' ))
  {
    total = 0; // initialize by SB == 0
    return  -1;
  }

  if ( total >= 3176 )
  {
    total = 0;
    return  -2;
  }

  total *= 10;
  total += ( SB - '0' );

  return  0;
}



void setup(void)
{
  Serial.begin( 9600 );
  pinMode( 10, OUTPUT );
  digitalWrite( 10, LOW );
  pinMode( 9, OUTPUT );
  digitalWrite( 9, HIGH );

  flag = SD.begin( CARD_0_CS ); // test if card is recognized
  Serial.print( "Card 0 status " );
  Serial.println( flag, DEC ); // show success as 1, fail as 0

  flag = SD2.begin( CARD_1_CS ); // test if card is recognized
  Serial.print( "Card 1 status " );
  Serial.println( flag, DEC ); // show success as 1, fail as 0

  Serial.println( "\nFinished\n"  ); 
}

void loop(void)
{
  switch ( state )  
  {
  case 0 :
    {
      parstate = 0;
    }
    break;

  case 2 : // exists()
    {
      flag = sdcard->exists( fname );
      if ( CSpin == 10 )
      { 
        Serial.print( "Card 0 " );
      }
      else
      {
        Serial.print( "Card 1 " );
      }
      Serial.print( fname );
      Serial.print( " exists? T/F " );
      Serial.println( flag, DEC ); // show success as 1, fail as 0
      state = 0;
    }
    break;

  case 3 : // open()
    {
      i = ( CSpin == 10 ) ? 0 : 1;
      sdfile[ i ] = sdcard->open( fname, FILE_READ );
      if ( CSpin == 10 )
      { 
        Serial.print( "Card 0 " );
      }
      else
      {
        Serial.print( "Card 1 " );
      }
      Serial.print( fname );
      Serial.print( " opens? T/F " );
      Serial.println( flag, DEC ); // show success as 1, fail as 0
      state = 0;
    }
    break;

  case 4 : // close()
    break;

  }

  if ( Serial.available())
  {
    state = 255;

    B = Serial.read();

    switch ( parstate )
    {
    case 0 :
      {
        switch ( B )
        {

        case 'd' : // set device 
          {
            parstate = 3; // d for set device CS pin 4 or 5 
          }
          break;

        case 'f' : // set filename
          {
            *fname = 0;
            count = 0;
            parstate = 2; // f for set file name, next get name
          }
          break;

        case 's' :  // set state
          {
            readSerialNum( 0 );
            parstate = 1; // s for set state, next get value
          }
          break;

        }
      }
      break;

    case 1 :
      {
        numret = readSerialNum( B );

        if ( numret > 0 )
        {
          state = numret;
          Serial.print( "\nState changed to " );
          Serial.println( state, DEC );
          parstate = 0;
        }
        else if ( numret == -1 )
        {
          Serial.println( "\nNon-numeric in number." );
          state = 0;
        }
        else if ( numret == -2 )
        {
          Serial.println( "\nNumber too big." );
          state = 0;
        }
      }
      break;

    case 2 :
      {
        if (( B == ' ' ) || ( B == 13 ) || ( B == 10 ))
        {
          strcat( fname, ".txt" );
          Serial.print( "\nFile name set to " );
          Serial.println( fname );
          state = 0;
        }
        else if ( count > 7 )
        {
          Serial.println( "\nFile name too long." );
          state = 0;
        }
        else
        {
          fname[ count++ ] = B;
          fname[ count ] = 0;
        }

      }
      break;

    case 3 : // set device 
      {
        if ( B == '0' )
        {
          sdcard = &SD;
          CSpin = 10;
          digitalWrite( 9, HIGH );
          digitalWrite( 10, LOW );
          Serial.println( "\ndevice set to 0, CS now 10" );
        }
        else if ( B == '1' )
        {
          sdcard = &SD2;
          CSpin = 9;
          digitalWrite( 10, HIGH );
          digitalWrite( 9, LOW );
          Serial.println( "\ndevice set to 1, CS now 9" );
        }
        state = 0;
      }
      break;
    }
  }

}

the included SD library is based off of

http://code.google.com/p/sdfatlib/

which already supports 2 cards

Got it to work with SdFat and the needed config file change, with my jumper lashup.
2 cards and starting with 460 bytes free ram! Not to mention 2 512 byte buffers on hand when either is not being used.

The SD modules have 2x8 rows of pins, it took me 7 female to male jumpers to connect 1 module and I have only 10 and wanted to run dual SD's. So I looked at a floppy cable and the headers have the right holes for pins and I notice that the end with the twist has > 16 wires from one end that don't twist. In fact the cable is 16 + 2 + 16 wires wide. So I plugged 1 module to one side of one end of the cable and the other module into the opposite side at the other end. The header in the middle is perfect for jumpering to a breadboard where I have the UNO pins broken out. I have a big bag of breadboard-breadboard jumpers.

The whole thing could be a lot cleaner, I have 2 sets of 3 lines to share and the CS to not.
Pity it works.

fun fact, the plug for a 5.25 inch floppy disk fits a SD card perfectly

There is soldering wires to a micro-SD adapter too. Even with proper leveling the parts cost wouldn't come to much. Run around 3.3V and it's straight wire isn't it?

With 2 cards I can keep 2 files open, perform read and write without open-close-repeat and seek-setting, I dunno what else just nothing good.

Read or write, the operation proceeds by the clock. IIUC I should be able to write any size block 1 byte at a time as they come in, that there is no requirement to pre-buffer blocks, that the SD runs by the SCK and doesn't have time limits on data except for upper limits on speed.

I could serve data as a stream device even to block devices.. don't have to have lots of buffer.

Why do you believe this?

Read or write, the operation proceeds by the clock. IIUC I should be able to write any size block 1 byte at a time as they come in, that there is no requirement to pre-buffer blocks, that the SD runs by the SCK and doesn't have time limits on data except for upper limits on speed.

Because data triggers on SCK. Because write the block doesn't say when it has to be filled.

fat16lib:
Why do you believe this?

Read or write, the operation proceeds by the clock. IIUC I should be able to write any size block 1 byte at a time as they come in, that there is no requirement to pre-buffer blocks, that the SD runs by the SCK and doesn't have time limits on data except for upper limits on speed.

How about because Sandisk says so in part 1 of the March 2000 SD Memory Card Specs?
As that is what was sent to me as the standard when I asked for docs somewhere.

4.4 Clock Control

The SD Memory Card bus clock signal can be used by the host to turn the cards into energy saving mode or to control the data flow (to avoid under-run or over-run conditions) on the bus. The host is allowed to lower the clock frequency or shut it down. For example, in a case that a host with 512 bytes pf data buffer would like to transfer data to a card with 1KByte write blocks. So, in order to preserve a continuous data transfer, from the cards point of view, the clock to the card shall be stopped after the first 512Bytes. Then the host will it it internal buffer with another 512Bytes. After the second half of the write block is ready in the host, it iwll contain the data transfer to the card by re-starting the clock supply. In such a way the card does not recognize any interruptions in the data transfer.

Maybe that's all there is to it.

I see.

March 2000 SD Memory Card Specs?

Find me a modern card with

1KByte write blocks.

Anyway, SPI clock is always stopped unless data is being transferred.

Modern cards do not allow chip select to go high during transfer of a write block so you can't use multiple cards or devices on the SPI bus this way.

You're right then. No. I'd have to run a soft-SPI bus per device. I2C would probably work.

Sandisk has their own 9-pin bus and system. The cards do a power-up check to find out which system they are running on then run that way. SD bus has no CS and can do all kinds of tricks including the be-faster trick. So maybe that's the way to go.

The 9-pin SanDisk cards just implement the standard SDIO bus. SDIO has Vdd, Vss1, Vss2, CLK, CMD, and four data lines.

Modern SD cards have latency no mater what you do. They have internal ram buffers and their internal proprietary algorithms decide when to program flash. You can send 512 bytes then the card may go busy and block you from sending more data.

This is true no matter which bus you use. The spec allows SD/SDHC cards to go busy for up to 250 milliseconds. Amazingly, SDXC cards can go busy for 500 milliseconds.

Few cards have latencies this long but all cards have occasional long latencies for wear leveling and other house keeping.

The class 10 rating assumes an average rate for many MB of data so you must have a ton of buffer to avoid problems with stuff like video.

In short you can't beat this with any of your clever tricks.

No, but I can try beat some other inefficiencies over single-card operation and live with the pauses. I'm not trying to beat high speed records.

But I think that for what I want to do, the SD bus is the way to go. Have to learn level shifting anyway?

Bit-bang SDIO? Wow what a choice!

Have you seen the protocol?