Problem with SD-card and SPI bus: Can't use the SPI bus for anything b- [SOLVED]

Dear all.

I am having some trouble with my SD-card and the SPI bus. I am using Arduino Uno R3 with the MP3 Player Shield from SparkFun. SparkFun MP3 Player Shield - DEV-12660 - SparkFun Electronics I am trying to use it in conjunction with a Real-Time Clock, but for now, the question is regarding the SPI bus and SD-card.

I am using the SD library included with the Arduino, and I am able to access the SD card fine, as long as nothing else access the SPI bus before it. See the following code, which is a slightly modified version of the "Datalogger" example sketch.

#include <SPI.h>
#include <RTC.h>



/*
  SD card datalogger
 
 This example shows how to log data from three analog sensors 
 to an SD card using the SD library.
 	
 The circuit:
 * analog sensors on analog ins 0, 1, and 2
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 	 
 */

#include <SD.h>

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 9;

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  pinMode(15, OUTPUT);
  digitalWrite(15, HIGH);
  pinMode(16, OUTPUT);
  digitalWrite(16, HIGH);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  pinMode(6, OUTPUT);
  digitalWrite(6, HIGH);
  
  SPI.begin();
  RTC.setup();

  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:

  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
 RTC.setTime(00, 44, 10, 17, 7, 13);
  
}

void loop()
{
  /*
  Serial.print(RTC.getWeekday());
  Serial.print(" ");
  Serial.print(RTC.getDay());
  Serial.print("/");
  Serial.print(RTC.getMonth());
  Serial.print("/");
  Serial.print(RTC.getYear());
  Serial.print("    ");
  Serial.print(RTC.getHour());
  Serial.print(":");
  Serial.print(RTC.getMinute());
  Serial.print(":");
  Serial.println(RTC.getSecond());
  */
  
  // make a string for assembling the data to log:
  String dataString = "";

  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ","; 
    }
  }
  

  
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  }
  
  /*
  Serial.print("PIN 6: ");
  Serial.println(digitalRead(6));
  Serial.print("PIN 9: ");
  Serial.println(digitalRead(9));
  Serial.print("PIN 10: ");
  Serial.println(digitalRead(10));
  
  SPI.setClockDivider(SPI_CLOCK_DIV4);
  SPI.setDataMode(SPI_MODE1);
  */
  
  //digitalWrite(10, LOW);
  //SPI.transfer(0x05);
  //int result = SPI.transfer(0x00);
  //digitalWrite(10, HIGH);
  
  /*SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.setDataMode(SPI_MODE0);
  */
  
  delay(1000);
}

This code works perfectly and outputs the following result:

RTC setup complete
Initializing SD card...card initialized.
42,1018,1018
42,1018,1018
42,1018,1018
42,1018,1018
42,1018,1018
42,1018,1017
42,1018,1017
41,1018,1018
42,1018,1018
42,1018,1018

I then alter the code slightly, adding a term SPI.transfer(0x00); at the end of the file. The long term goal is to communicate with the RTC as mentioned, but for now I am troubleshooting. The foll code is then:

#include <SPI.h>
#include <RTC.h>



/*
  SD card datalogger
 
 This example shows how to log data from three analog sensors 
 to an SD card using the SD library.
 	
 The circuit:
 * analog sensors on analog ins 0, 1, and 2
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 	 
 */

#include <SD.h>

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 9;

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  pinMode(15, OUTPUT);
  digitalWrite(15, HIGH);
  pinMode(16, OUTPUT);
  digitalWrite(16, HIGH);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  pinMode(6, OUTPUT);
  digitalWrite(6, HIGH);
  
  SPI.begin();
  RTC.setup();

  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:

  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
 RTC.setTime(00, 44, 10, 17, 7, 13);
  
}

void loop()
{
  /*
  Serial.print(RTC.getWeekday());
  Serial.print(" ");
  Serial.print(RTC.getDay());
  Serial.print("/");
  Serial.print(RTC.getMonth());
  Serial.print("/");
  Serial.print(RTC.getYear());
  Serial.print("    ");
  Serial.print(RTC.getHour());
  Serial.print(":");
  Serial.print(RTC.getMinute());
  Serial.print(":");
  Serial.println(RTC.getSecond());
  */
  
  // make a string for assembling the data to log:
  String dataString = "";

  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ","; 
    }
  }
  

  
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  }
  
  /*
  Serial.print("PIN 6: ");
  Serial.println(digitalRead(6));
  Serial.print("PIN 9: ");
  Serial.println(digitalRead(9));
  Serial.print("PIN 10: ");
  Serial.println(digitalRead(10));
  
  SPI.setClockDivider(SPI_CLOCK_DIV4);
  SPI.setDataMode(SPI_MODE1);
  */
  
  //digitalWrite(10, LOW);
  //SPI.transfer(0x05);
  int result = SPI.transfer(0x00);
  //digitalWrite(10, HIGH);
  
  /*SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.setDataMode(SPI_MODE0);
  */
  
  delay(1000);
}

The only change is the addition of a SPI.transfer(), and the output is this:

RTC setup complete
Initializing SD card...card initialized.
42,1018,1018
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt

The code runs fine the first time in the loop, but after having the SPI.transfer called, it refuses to operate ever again. I have checked all the SC pins, and they are all high when this is called, so I did not think it would affect anything. I am utterly baffled as to why this happens, and would greatly appreciate some input.

Thank you all very much!

What digital pin do you expect this to use?

  if (!SD.begin(chipSelect)) {

What digital pin is the SD slave select connected to?

edit: What pin is the RTC slave select connected to?

This is defined just before void loop(), and works nicely when I do not add the SPI.transfer(0x00).

const int chipSelect = 9;

The RTC is not actually connected in this example of the code, but I have done the same test with the same result with the RTC connected to pin 17 (or A3 if you will), pin 10 and pin 5.

The test above is with nothing connected apart from the MP3 shield.

Thanks for the reply! :slight_smile:

Is this your shield?

I had trouble using the String data type, so I don't use it any longer. Try a test inserting just a "test" into the file. Does that work?

  if (dataFile) {
    dataFile.println("test");
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }

Ok, so I did some more testing following your advice. :slight_smile:

Removing the String data type and just writing straight to the file did not make any difference.

When the SPI.transfer(0x00) part is commented out, everything works like a charm.

When it is included the program runs like expected through the first round in the loop, but on the second round (i.e. after the first time SPI.transfer was called), the same error as before is presented.

However, I added a part in order to read the file on the SD card and display it in the serial monitor, and this continued to work even after the write part had stopped to function. To sum up, this is the current progression:

Without SPI.transfer:
Setup ok, first write ok, first read ok, all read/write following ok.

With SPI.transfer:
Setup ok, first write ok, first read ok, first SPI.transfer called, second read fail, second read succeed, and so on.

There seems to be something with the SPI.transfer(0x00) call that makes the SD-card unavailable for reading, but I have no idea what...

Any other ideas? :slight_smile: I really appreciate the help. :slight_smile:

The full code used is here:

#include <SPI.h>
#include <RTC.h>



/*
  SD card datalogger
 
 This example shows how to log data from three analog sensors 
 to an SD card using the SD library.
 	
 The circuit:
 * analog sensors on analog ins 0, 1, and 2
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 	 
 */

#include <SD.h>

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 9;

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  pinMode(15, OUTPUT);
  digitalWrite(15, HIGH);
  pinMode(16, OUTPUT);
  digitalWrite(16, HIGH);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  pinMode(6, OUTPUT);
  digitalWrite(6, HIGH);
  
  SPI.begin();
  RTC.setup();

  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:

  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
 RTC.setTime(00, 44, 10, 17, 7, 13);
  
}

void loop()
{
  /*
  Serial.print(RTC.getWeekday());
  Serial.print(" ");
  Serial.print(RTC.getDay());
  Serial.print("/");
  Serial.print(RTC.getMonth());
  Serial.print("/");
  Serial.print(RTC.getYear());
  Serial.print("    ");
  Serial.print(RTC.getHour());
  Serial.print(":");
  Serial.print(RTC.getMinute());
  Serial.print(":");
  Serial.println(RTC.getSecond());
  */
  
  // make a string for assembling the data to log:
  /*
  String dataString = "";

  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ","; 
    }
  }
  
*/
  
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("test.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println("Test 1, 2, 3!");
    dataFile.close();
    // print to the serial port too:
    //Serial.println(dataString);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening test.txt for writing");
  }
  
  
dataFile = SD.open("test.txt");
  if (dataFile) {
    Serial.println("test.txt:");
    
    // read from the file until there's nothing else in it:
    while (dataFile.available()) {
    	Serial.write(dataFile.read());
    }
    // close the file:
    dataFile.close();
  } else {
  	// if the file didn't open, print an error:
    Serial.println("error opening test.txt for reading");
  }
    
  
  /*
  Serial.print("PIN 6: ");
  Serial.println(digitalRead(6));
  Serial.print("PIN 9: ");
  Serial.println(digitalRead(9));
  Serial.print("PIN 10: ");
  Serial.println(digitalRead(10));
  
  SPI.setClockDivider(SPI_CLOCK_DIV4);
  SPI.setDataMode(SPI_MODE1);
  */
  
  //digitalWrite(10, LOW);
  //SPI.transfer(0x05);
  int result = SPI.transfer(0x00);
  //digitalWrite(10, HIGH);
  
  /*SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.setDataMode(SPI_MODE0);
  */
  
  delay(1000);
}

I'm not a MP3 kinda person, but there appears to be two chip select lines on that mp3 player. D6 is the control slave select and D7 is the data slave select. Try disabling D7 in setup also just after D6.

pinMode(7,OUTPUT);
digitalWrite(7,HIGH);

I don't see you enabling chip select before doing the SPI.transfer. Normally each device needs chip select (a different one) brought low before talking to it and high afterwards. I'm not sure, but in the case of the SD card you may need to take its chip select high first (to deselect it).

Failing getting that working, do what I did with another project and use bit-banged SPI (for your sensors). Code near the end of this post:

Ok, so I have done a bit more testing, with few results. I followed SurferTim's advice and set pin 7 to HIGH and OUTPUT, but with the same result as before. I also checked each pin before doing the SPI.transfer(0x00), and they are all set to high.

In response to Nick Gammon, the reason I do not enable a chip select before the SPI.transfer(0x00) is that I don't want anything to happen here. I am just trying to call the SPI.transfer function and transfer two bytes to nothing. I do this because my SD-card refuses to be written to after I call this function. The strange part of all of this is that the SPI.transfer(0x00) function affects the SD-card at all. If I understand correctly, calling SPI.transfer(0x00) without any chip select enabled should just clock out 0's and not have any effect on anything, as they are not selected.

I am not changing changing the clock rate or the transfer mode, and because the setup works perfectly if I do not try to transfer anything on the SPI bus between each read/write section, I don't think it should be hardware problem, but you never know. I checked the schematic of the MP3 board, and the SD-card slot has CS, MOSI and CLK connected through a logic level converter and MISO connected straight to the Arduino. Is it possible that my dummy byte is stored in a buffer or register or something and clocked out at an inconvenient time? I know it does not seem very likely, but I am grasping at straws here...

Thank you very much for the help though. It is nice to have some fresh perspectives on the problem. :slight_smile:

OOHH!! Exciting news. (For me at least. ;))

I had a small breakthrough by changing SPI.transfer(0x00) to SPI.transfer(0xFF), operation returns to what I expect. Why this happens I have no idea, but I hope I can make the program work by throwing this dummy byte at the bus before accessing the SD card. More to follow once tested.

Also, if anyone has an idea as to why this works, I would be very interested in knowing.

As so, Ladies and Gentlemen, the problem is solved. :smiley:

By adding the SPI.transfer(0xFF) term before accessing the SD-card, all my problems went away. The final code can be seen here:

#include <SPI.h>
#include <RTC.h>



/*
  SD card datalogger
 
 This example shows how to log data from three analog sensors 
 to an SD card using the SD library.
 	
 The circuit:
 * analog sensors on analog ins 0, 1, and 2
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 	 
 */

#include <SD.h>

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 9;

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  pinMode(15, OUTPUT);
  digitalWrite(15, HIGH);
  pinMode(16, OUTPUT);
  digitalWrite(16, HIGH);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  pinMode(6, OUTPUT);
  digitalWrite(6, HIGH);
  
  SPI.begin();
  RTC.setup();

  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:

  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
 RTC.setTime(00, 44, 10, 17, 7, 13);
  
}

void loop()
{
  
  Serial.print(RTC.getWeekday());
  Serial.print(" ");
  Serial.print(RTC.getDay());
  Serial.print("/");
  Serial.print(RTC.getMonth());
  Serial.print("/");
  Serial.print(RTC.getYear());
  Serial.print("    ");
  Serial.print(RTC.getHour());
  Serial.print(":");
  Serial.print(RTC.getMinute());
  Serial.print(":");
  Serial.println(RTC.getSecond());
  
  
  // make a string for assembling the data to log:
  
  String dataString = "";
  
  dataString += String(RTC.getWeekday());
  dataString += String(" ");
    dataString += String(RTC.getDay());
  dataString += String("/");
    dataString += String(RTC.getMonth());
  dataString += String("/");
    dataString += String(RTC.getYear());
  dataString += String(" ");
    dataString += String(RTC.getHour());
  dataString += String(":");
    dataString += String(RTC.getMinute());
  dataString += String(":");
    dataString += String(RTC.getSecond());
  dataString += String(",");

  SPI.transfer(0xFF);
  
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("test4.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    //Serial.println(dataString);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening test4.txt for writing");
  }
  
  
dataFile = SD.open("test4.txt");
  if (dataFile) {
    Serial.println("test4.txt:");
    
    // read from the file until there's nothing else in it:
    while (dataFile.available()) {
    	Serial.write(dataFile.read());
    }
    // close the file:
    dataFile.close();
  } else {
  	// if the file didn't open, print an error:
    Serial.println("error opening test4.txt for reading");
  }
  
  delay(1000);
}

Thank you again for all your help, and I hope this can be of use to someone else at some point.

A good day to you all!

If that worked, you must have another SPI device active, like the mp3 player. I use the SD and the w5100 together all the time, as do others like zoomkat. He is the w5100/SD master, and he doesn't need that in any of his code.

edit: Take a look at the mp3 shield schematic.

There are four pins used by the mp3 player besides the SPI data lines.
D2 = MP3-DREQ
D6 = MP3-CS
D7 = MP3-DCS
D8 = MP3-RST

I suspect you are not setting one of those correctly.

This is correct, and the same applies to SPI.transfer(0xFF):

If I understand correctly, calling SPI.transfer(0x00) without any chip select enabled should just clock out 0's and not have any effect on anything, as they are not selected.

However, if there is another SPI device not disabled, that may trash up the SPI bus.