Go Down

Topic: SD Library conflicts with other SPI devices? (Read 2349 times) previous topic - next topic

Jeremyvnc

So I'm trying to make an i2C controlled MP3 player by using the VS1053, SD card and an arduino.  I have successfully tied the three devices into one another on the SPI bus and can get the sample "Hello" from VLSI to play just fine (woohoo!) and if I load another sketch that uses SD (or even SDFat) then I can pull data from SD cards (alright!).  However, if I try to put the two together, neither works and neither does serial communication at all.  I'm thinking there is some sort of interfering code between the standard SPI library and the SDFat library (SD is a wrapper of SDFat).  From trial and error of commenting/uncommenting & downloading I have come to the conclusion that the SDCard class isn't doing it, but when I make an instance of the SDFile class, my "Hello" becomes a blip, I can't read from the sd card, and my serial (9600) drops out.  My latest idea was to use the software SPI and isolate the two buses, but that hasn't fixed anything either.

FYI, I'm using an arduino pro mini 3.3V @ 8MHz.

Here is my code:
Code: [Select]

//RA: Hello, World code for VS1053 and ATMega328 microcontroller on an arduino board
//Uses a sparkfun mp3 shield for arduino available at www.sparkfun.com

#include <SPI.h>
#include <SdFat.h>
#include <HelloMP3.h>
//#include <SdFatUtil.h>

Sd2Card card;
SdVolume volume;
//SdFile root;
SdFile file;  //comment this out for MP3 and serial to work again
//// store error strings in flash to save RAM
//#define error(s) error_P(PSTR(s))

//#include <SD.h>

//File myFile;

/** Control Chip Select Pin (for accessing SPI Control/Status registers) */
#define MP3_XCS 8

/** Data Chip Select / BSYNC Pin */
#define MP3_XDCS 9

//** Data Request Pin: Player asks for more data */
#define MP3_DREQ 14

/** VS10xx SCI Registers */
#define SPI_MODE 0x0   /**< VS10xx register */
#define SPI_STATUS 0x1   /**< VS10xx register */
#define SPI_BASS 0x2   /**< VS10xx register */
#define SPI_CLOCKF 0x3   /**< VS10xx register */
#define SPI_DECODE_TIME 0x4   /**< VS10xx register */
#define SPI_AUDATA 0x5   /**< VS10xx register */
#define SPI_WRAM 0x6   /**< VS10xx register */
#define SPI_WRAMADDR 0x7   /**< VS10xx register */
#define SPI_HDAT0 0x8   /**< VS10xx register */
#define SPI_HDAT1 0x9   /**< VS10xx register */
#define SPI_AIADDR 0xa   /**< VS10xx register */
#define SPI_VOL 0xb   /**< VS10xx register */
#define SPI_AICTRL0 0xc   /**< VS10xx register */
#define SPI_AICTRL1 0xd   /**< VS10xx register */
#define SPI_AICTRL2 0xe   /**< VS10xx register */
#define SPI_AICTRL3 0xf   /**< VS10xx register */

unsigned char SPIGetChar(){
 unsigned char returned = SPI.transfer(0xFF);
 return returned; /* Return the received byte */
}

/** Pull the VS10xx Control Chip Select line Low */
void Mp3SelectControl(){
 digitalWrite(MP3_XCS, LOW);
}

/** Pull the VS10xx Control Chip Select line High */
void Mp3DeselectControl(){
 digitalWrite(MP3_XCS, HIGH);
}

/** Pull the VS10xx Data Chip Select line Low */
void Mp3SelectData(){
 digitalWrite(MP3_XDCS, LOW);
}

/** Pull the VS10xx Data Chip Select line High */
void Mp3DeselectData(){
 digitalWrite(MP3_XDCS, HIGH);
}

void SPIWait(){
 while (!digitalRead(MP3_DREQ)){};  
}

/** Write VS10xx register */
void Mp3WriteRegister(unsigned char addressbyte, unsigned char highbyte, unsigned char lowbyte){
 Mp3DeselectData();
 Mp3SelectControl();
 delay(1);
 SPI.transfer(0x02);  //write command
 SPI.transfer(addressbyte);
 SPI.transfer(highbyte);
 SPI.transfer(lowbyte);
 SPIWait();
 Mp3DeselectControl();
}

/** Read the 16-bit value of a VS10xx register */
unsigned int Mp3ReadRegister (unsigned char addressbyte){
 unsigned int resultvalue = 0;
 unsigned int aux = 0;
 Mp3DeselectData();
 Mp3SelectControl();
 SPI.transfer(0x03);  //read command
 SPI.transfer(addressbyte);
 aux = SPI.transfer(0xff);
 SPIWait();
 resultvalue = aux << 8;
 SPI.transfer(0xff);
 SPIWait();
 resultvalue |= aux;
 Mp3DeselectControl();
 return resultvalue;
}

/** Set VS10xx Volume Register */
void Mp3SetVolume(unsigned char leftchannel, unsigned char rightchannel){
 Mp3WriteRegister(SPI_VOL,leftchannel,rightchannel);
}

/** Soft Reset of VS10xx (Between songs) */
void Mp3SoftReset(){
 Mp3WriteRegister (SPI_MODE, 0x08, 0x04); /* Newmode, Reset, No L1-2 */
 delay(1); /* One millisecond delay */
 while (!digitalRead(MP3_DREQ)) /* wait for startup */
   ;
/* Set clock register, doubler etc. */
 Mp3WriteRegister(SPI_CLOCKF, 0xb3, 0xfe);
 delay(1); /* One millisecond delay */
/* Wait for DREQ */
 while (!digitalRead(MP3_DREQ))
   ;
   
   /***********************************ADDED AS TESTS*/
   
 Mp3DeselectControl();
 Mp3DeselectData();
 Mp3SetVolume(0xff,0xff); //Declick: Immediately switch analog off
  /* Declick: Slow sample rate for slow analog part startup */
 Mp3WriteRegister(SPI_AUDATA, 0, 10); /* 10 Hz */
 delay(100);
 /* Switch on the analog parts */
 Mp3SetVolume(0xfe,0xfe);
 Mp3WriteRegister (SPI_AUDATA, 31, 64); /* 8kHz */
 Mp3SetVolume(20,20); // Set initial volume (20 = -10dB)  
   
   /*******************************END ADDED AS TESTS*/  
}

void setup() {
 uint32_t iByteCount = 0;
 int16_t n;
 uint8_t buf[7];// nothing special about 7, just a lucky number.
 Serial.begin(9600);
 Serial.println();
 Serial.println("type any character to start");
 while (!Serial.available());
 Serial.println();
 
 pinMode(17,OUTPUT);
 digitalWrite(17, LOW);
 delay(1000);
 digitalWrite(17, HIGH);
 delay(1000);
 pinMode(MP3_DREQ, INPUT);
 pinMode(MP3_XCS, OUTPUT);
 pinMode(MP3_XDCS, OUTPUT);
 //Note that even if you're not using the SS pin, it must remain set as an output;
 //otherwise, the SPI interface can be put into slave mode, rendering the library inoperative.
 //pinMode(10, OUTPUT);//the SS of the SPI interface on the arduino board
 SPI.begin();
 SPI.setBitOrder(MSBFIRST);
 SPI.setDataMode(SPI_MODE0);
 //max SDI clock freq = CLKI/7 and (datasheet) CLKI = 36.864, hence max clock = 5MHz
 //SPI clock arduino = 16MHz. 16/ 4 = 4MHz -- ok!
 //Serial.begin(9600);
 //Serial.print("Initializing SD card...");
//  if (!SD.begin()) {
//    //Serial.println("initialization failed!");
//    return;
//  }
 //Serial.println("initialization done.");
 
 //initialize chip
 Mp3DeselectControl();
 Mp3DeselectData();
 
 if (!card.init(SPI_HALF_SPEED))
 {
    digitalWrite(13,HIGH);
    delay(500);
    digitalWrite(13,LOW);
    delay(500);
    digitalWrite(13,HIGH);
    Serial.println("card.init failed");
 }
 else
 {
   Serial.println("card.init successful");
    digitalWrite(13,HIGH);
    delay(500);
    digitalWrite(13,LOW);
    delay(500);
    digitalWrite(13,HIGH);
    delay(500);
    digitalWrite(13,LOW);
    delay(500);
    digitalWrite(13,HIGH);
    delay(500);
    digitalWrite(13,LOW);
 }
 Serial.flush();
 while (!Serial.available());
 
 Mp3SetVolume(0xff,0xff); //Declick: Immediately switch analog off
  /* Declick: Slow sample rate for slow analog part startup */
 Mp3WriteRegister(SPI_AUDATA, 0, 10); /* 10 Hz */
 delay(100);
 /* Switch on the analog parts */
 Mp3SetVolume(0xfe,0xfe);
 Mp3WriteRegister (SPI_AUDATA, 31, 64); /* 8kHz */
 Mp3SetVolume(20,20); // Set initial volume (20 = -10dB)
 Mp3SoftReset();
}

void InitMicrocontroller(){
 SPI.setClockDivider(SPI_CLOCK_DIV64);//slow SPI bus speed
 SPI.transfer(0xFF);
}

void SPIInit(){
 SPI.setClockDivider(SPI_CLOCK_DIV4); //basically just set fast SPI bus speed
}

void loop(){
 
 unsigned char *p;
 unsigned int i;

 InitMicrocontroller();
 SPIInit();
 
 p = &HelloMP3[0]; // Point "p" to the beginning of array
   while (p <= &HelloMP3[sizeof(HelloMP3)-1]) {
     while (!digitalRead(MP3_DREQ)) {
      // MP3 buffer is full, time to do something else...
      SPIWait(); // Wait until SPI transfer is completed
      Mp3DeselectData(); // Release the SDI bus
      // You can do something else here, the bus is free...
      // Maybe set the volume or whatever...
     }
     Mp3SelectData(); // Pull XDCS low
     SPI.transfer(*p++); // Send SPI byte
     // You can actually send 32 bytes here before checking for DREQ again
   }
   
   // End of file - send 2048 zeros before next file
   Mp3SelectData();
   for (i=0; i<2048; i++) {
     while (!digitalRead(MP3_DREQ)); // wait here until DREQ is high again
     SPI.transfer(0);      
   }
   SPIWait(); // Wait until SPI transfer is completed
   Mp3DeselectData();
   //Mp3SoftReset();
     delay(10000);
}


Pleease help me and thanks in advance!
-Jeremy

johnwasser

This looks odd to me:
Code: [Select]

  aux = SPI.transfer(0xff);
  SPIWait();
  resultvalue = aux << 8;
  SPI.transfer(0xff);
  SPIWait();
  resultvalue |= aux;
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Jeremyvnc

That pulls the 16 byte register from the VS1053.  What looks weird about it?

johnwasser

  aux = SPI.transfer(0xff);  // Get a byte from the SPI interface and save it in aux
  resultvalue = aux << 8;  //  Put aux in the upper byte of resultvalue
  SPI.transfer(0xff);    // THROW AWAY a byte from the SPI interface
  resultvalue |= aux;  //Put the same value of aux in the lower byte of resultvalue
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Jeremyvnc

Ah very true.  Thanks for that find.  However, I'm never calling that function so that can't be the cause of my issues.

Any other thoughts?  Has anyone had issue with interfacing the SPI library and the SD or SDfat library together?

Thanks,
Jeremy

johnwasser

How do you tell the SPI and SD libraries to use separate Slave Select (SS) lines?  I can see you don't pass an argument to SPI.begin() so that uses the default of D10.  How do you tell the SD library which pin to use for SD?  I don't see that.

It looks like one of your SPI devices has specific protocol needs.  Perhaps those conflict with the SPI requirements of the SD card?

You might want to find a library that implements software SPI and use that for your SPI device so the SD card can use the hardware SPI unmolested.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Nick Gammon

Without reading all your code, let me tell you the theory ;) ...

You should be able to connect together MISO, MOSI, and SCK for all devices. Then you need a separate SS for each one. One could be D10 which SPI.begin () has already configured as an output (which it needs to stay as), and is set to a high state.

Then, when communicating with each device you should do:

Code: [Select]
digitalWrite(device, LOW);  // enable this device
byte received_byte = SPI.transfer (sent_byte);  // send and receive
digitalWrite(device, HIGH);  // disable this device


... where "device" is the correct pin you have chosen for each device (eg. 10, 9, 8,7). Setting the relevant slave-select low selects that device, and the others then ignore the SPI.transfer. Setting it back high afterwards makes sure that when you select another one, this one will ignore the transfer.
Please post technical questions on the forum, not by personal message. Thanks!

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

Go Up