Go Down

Topic: SD card and SPI sensor?? (Read 7490 times) previous topic - next topic

tetwin11

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
Code: [Select]

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
Code: [Select]

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

then send some data to the sensor
Code: [Select]

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

and switch back
Code: [Select]

  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
Sometimes things are as they appear


SamoLaw

#2
Jan 26, 2011, 11:07 pm Last Edit: Jan 26, 2011, 11:22 pm by SamoLaw Reason: 1
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 ?

Quote
Also, I tried to just change the chip selects to deselect the SD card
Code: [Select]
 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,  :)


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 ? :)

fat16lib

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 http://code.google.com/p/beta-lib/downloads/list

tetwin11

#4
Jan 27, 2011, 06:32 pm Last Edit: Jan 27, 2011, 06:57 pm by tetwin11 Reason: 1
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.
Code: [Select]
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.

Code: [Select]
#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);
}




Sometimes things are as they appear

fat16lib

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.

tetwin11

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
Sometimes things are as they appear

Go Up