SD card and SPI sensor??

Hello,
I am trying to use the SPI library to talk to a sensor, then use the SD library to write data to an SD card. However, I am having mucho problems. If anyone can shed any light on the topic, I'd appreciate it.

The sensor uses SPI mode 3, and the SD card uses SPI mode 0. I know we can use something like

SPI.setDataMode(SPI_MODE3);

to change the mode, but can we change back and forth throughout the code? I am running into problems with that (SD card errors and erroneous readings from the sensor).

Also, I tried to just change the chip selects to deselect the SD card

  digitalWrite(SDcs,HIGH);
  digitalWrite(ADIScs,LOW);

then send some data to the sensor

  SPI.transfer(0x56);
  SPI.transfer(0x00);

and switch back

  digitalWrite(ADIScs,HIGH);
  digitalWrite(SDcs,LOW);

However, after this when I try to write to the SD card, I get an error! Why would sending data on SPI cause problems when the chip select is not selected?

Even stranger, it does not cause an error on the SD card if I transfer all ones... 0xFF. Does this make any sense to anyone?

Thanks

Hi,

You can check my answer in the other topic :

http://arduino.cc/forum/index.php/topic,49987.0.html

Hi,

Later, Me too I will have to control two devices with SPI protocol. It's because, I'm interesting to understand your problem and I hope help you to resolve it.

So, to be sure, if I understand, your sensor and SD card share the same MISO, MOSI, and CLK lines on your arduino board ?
And you tried to select/deselect SD card and sensor with "digitalWrite" in high/low mode ?

Also, I tried to just change the chip selects to deselect the SD card

  digitalWrite(SDcs,HIGH);

digitalWrite(ADIScs,LOW);

Be careful, because if you SDcs is in high value, it means SDcs ignores the master. I think CS (Chip select is complementary). It's /CS, not CS. But I'm not sure.
When a device's Slave Select pin is low, it communicates with the master. When it's high, it ignores the master.
Try to inverse, :slight_smile:

EDIT : I'm sorry I made a mistake, here in your code, it's correct. Sorry :*

Can you give reference of your sensor and SD card please ? :slight_smile:

The problem is most likely caused by setting the SPI bus to mode 3. The SD library is a wrapper for a version of my SdFat library that does not reinitialize the SPI mode and speed after the SD.begin() call. You could try setting SPI to mode 0 before calling SD functions.

I have a new version in test that should work better but it is not designed to be used with the current SD wrapper. Here is the URL of the test version Google Code Archive - Long-term storage for Google Code Project Hosting..

I appreciate the input! But my post was a bit misleading. I am not using the SD library. I am using the SdFat library that I found through Ladyada's light/temp logger project, and it looks like this was your original library.

After changing to SPI mode 3, I do change back to mode 0, but it gives me an error when I try to write.

error: writeData failed
SD error: 11,FF

It is SD error 11,FF or 11,00 depending on if I send data in between and what data is sent.

Here is my entire code if you are at all interested. Some code in the ISR is commented out because I am playing with what may make that work.

#include <SdFat.h>
#include <SdFatUtil.h>
#include "TimerOne.h"
#include <SPI.h>

volatile int sample_num = 0;

// pins used for the connections
const int dataReadyPin = 2;
const int ADIScs = 7;
const int SDcs = 8;

// data strings
volatile uint8_t dataString[512];
//volatile char writeString[512];

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
SdFile configfile;

uint32_t bgnBlock, endBlock;

unsigned int fs = 2;
unsigned int time = 5;
const unsigned int sample_size = 100;
unsigned int samples_per_block = 512/sample_size;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))

void error_P(const char* str) {
  Serial.print("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    Serial.print("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}

void setup(void) {
  Serial.begin(115200);
}

void loop(void) {

  Serial.flush();
  Serial.println("Type any character to start");
//  while (!Serial.available());

  // initialize the SD card
  uint16_t start = millis();
  
  pinMode(SDcs, OUTPUT);
  digitalWrite(SDcs, LOW);
  // initialize the SD card at SPI_FULL_SPEED for best performance.
  // try SPI_HALF_SPEED if bus errors occur.
  if (!card.init(SPI_HALF_SPEED)) error("card.init failed");

  // start the SPI library:
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0); // set SPI mode 0

  // initalize the  data ready and chip select pins
  pinMode(dataReadyPin, INPUT);
  pinMode(ADIScs, OUTPUT);
  digitalWrite(ADIScs, HIGH);

  start = millis() - start;
  
  Serial.print("Card init time: ");
  Serial.print(start);
  Serial.println(" millis");
  
  // initialize a FAT volume
  if (!volume.init(&card)) error("volume.init failed");
  
  // open the root directory
  if (!root.openRoot(&volume)) error("openRoot failed");

  if (configfile.open(&root, "CONFIG.TXT", O_READ)) {
    Serial.println("Reading configuration file...");
    fs = (configfile.read()-'0')*100;
    fs += (configfile.read()-'0')*10;
    fs += configfile.read()-'0';
    time = (configfile.read()-'0')*1000;
    time += (configfile.read()-'0')*100;
    time += (configfile.read()-'0')*10;
    time += configfile.read()-'0';
    configfile.close();
  }
  else {
    Serial.println("No CONFIG.TXT found, using default values");
  }
  
  Serial.print("fs (sps) = ");Serial.println(fs);
  Serial.print("time (s) = ");Serial.println(time);
  Serial.print("sample size (chars) = ");Serial.println(sample_size);
  Serial.print("samp/block = ");Serial.println(samples_per_block);
  unsigned int block_count = (fs*time)/samples_per_block;
  Serial.print("blocks = ");Serial.println(block_count);

  char filename[] = "DATA00.TXT";
  byte filenum = 0;
  while (file.open(&root, filename, O_READ)) {
    file.close();
    filenum++;
    filename[4] = (filenum/10)+'0';
    filename[5] = (filenum%10)+'0';
  }
  
  // create a contiguous file
  if (!file.createContiguous(&root, filename, 512UL*block_count)) {
    error("createContiguous failed");
  }
  // get the location of the file's blocks
  if (!file.contiguousRange(&bgnBlock, &endBlock)) {
    error("contiguousRange failed");
  }
  //*********************NOTE**************************************
  // NO SdFile calls are allowed while cache is used for raw writes
  //***************************************************************
  
  // clear the cache and use it as a 512 byte buffer
  uint8_t* pCache = volume.cacheClear();
  
  // fill cache with samples_per_block lines
  memset(pCache, ' ', 512);
  for(uint16_t i = 0 ; i<512 ; i++) {
    dataString[i] = ' ';
  }
  // loop through each sample
  for (uint16_t i = sample_size ; i < 512-sample_size; i += sample_size) {
    // put newline/carriage return at end of line
    pCache[i-2] = '\r';
    pCache[i-1] = '\n';
    dataString[i-2] = '\r';
    dataString[i-1] = '\n';
  }
  dataString[509] = '\r';
  dataString[510] = '\n';
  dataString[511] = '\0';
  pCache[509] = '\r';
  pCache[510] = '\n';
  pCache[511] = '\0';
  
  Serial.print("Start raw write of ");
  Serial.print(file.fileSize());
  Serial.println(" bytes at");
  
  Serial.print((512UL*block_count)/time);
  Serial.println(" bytes per second");
  
  Serial.print("to ");
  Serial.println(filename);
  
  Serial.print("Please wait ");
  Serial.print(time);
  Serial.println(" seconds");
  
  // tell card to setup for multiple block write with pre-erase
  if (!card.erase(bgnBlock, endBlock)) error("card.erase failed");
  if (!card.writeStart(bgnBlock, block_count)) {
    error("writeStart failed");
  }

  // init stats
  start = millis();
  uint16_t maxWriteTime = 0;

  // start Timer1
  Timer1.initialize((1000000UL/fs)); // Start timer at fs Hz
  // attach timer overflow interrupt
  Timer1.attachInterrupt(isr);

  // write data
  while( (millis()-start) < (time*1000) ) {
    if (sample_num==samples_per_block) {
      sample_num = 0;
      strlcpy((char*)pCache,(char*)dataString,512);
      Serial.print((char*)pCache);
      // write a 512 byte block
      uint32_t tw = micros();
      if (!card.writeData(pCache)) {
        error("writeData failed");
        while(1);
      }
      tw = micros() - tw;
      // check for max write time
      if (tw > maxWriteTime) {
        maxWriteTime = tw;
      }
    }
  }
  
  // Stop the interrupt
  Timer1.detachInterrupt();

  // end multiple block write mode
  if (!card.writeStop()) error("writeStop failed");
  
  Serial.println("Done");
  
  Serial.print("Elapsed time: ");
  Serial.print(millis()-start);
  Serial.println(" millis");
  
  Serial.print("Max write time: ");
  Serial.print(maxWriteTime);
  Serial.println(" micros");
  
  // close files for next pass of loop
  root.close();
  file.close();
  Serial.println();
  while(1);
}

void isr() {
  digitalWrite(SDcs,HIGH);
  digitalWrite(ADIScs,LOW);
  addhexchar8(dataString,millis(),sample_num*sample_size);
  delayMicroseconds(300);
  //  SPI.setDataMode(SPI_MODE3); // set SPI mode 3
  SPI.transfer(0x56);
  SPI.transfer(0xFF);
  SPI.transfer(0xFF);
  SPI.transfer(0xFF);
//  readID();
//  SPI.setDataMode(SPI_MODE0); // set SPI mode 0
  digitalWrite(ADIScs,HIGH);
  digitalWrite(SDcs,LOW);
  Serial.println(sample_num);
  sample_num++;
}



// creates char[] from int in hex with 8 chars
void addhexchar8(volatile uint8_t* c, int n, int start)
{
  static const char hex_digits[] = "0123456789ABCDEF";
  c[start+0] = hex_digits[(n >> 28) & 0xf];
  c[start+1] = hex_digits[(n >> 24) & 0xf];
  c[start+2] = hex_digits[(n >> 20) & 0xf];
  c[start+3] = hex_digits[(n >> 16) & 0xf];
  c[start+4] = hex_digits[(n >> 12) & 0xf];
  c[start+5] = hex_digits[(n >> 8) & 0xf];
  c[start+6] = hex_digits[(n >> 4) & 0xf];
  c[start+7] = hex_digits[n & 0xf];
}

// creates char[] from int in hex with 4 chars
void addhexchar4(volatile uint8_t* c, int n, int start)
{
  static const char hex_digits[] = "0123456789ABCDEF";
  c[start+0] = hex_digits[(n >> 12) & 0xf];
  c[start+1] = hex_digits[(n >> 8) & 0xf];
  c[start+2] = hex_digits[(n >> 4) & 0xf];
  c[start+3] = hex_digits[n & 0xf];
}


// creates char[] from int in hex with 3 chars
void addhexchar3(volatile uint8_t* c, int n, int start)
{
  static const char hex_digits[] = "0123456789ABCDEF";
  c[start+0] = hex_digits[(n >> 8) & 0xf];
  c[start+1] = hex_digits[(n >> 4) & 0xf];
  c[start+2] = hex_digits[n & 0xf];
}


//Sends a read command to the ADIS16367:
void readID() {
  unsigned int result = 0;   // result to return
  SPI.transfer(0x56);
  SPI.transfer(0x00);
  delayMicroseconds(4);
  result = SPI.transfer(0x00) << 8;
  result |= SPI.transfer(0x00);
  Serial.println(result, DEC);
}

It looks like you access the SPI bus from an ISR to read sensor data while doing a multiple block write. You can not use the SPI bus during a multiple block write.

You could use software SPI to read the sensor in an ISR using pins other than the hardware SPI pins. I have been able to do over 90 KB/sec using software SPI to a DAC from an ISR.

That is exactly what I was trying to do! So I see there is the option of using a software implementation of the SPI interface using different pins. I suppose that would solve the problem I am having. Do you have any suggestions on where I may find an example or information on how to do this? Maybe I should move this thread to a different area in the forum... Thank you!
Matt