SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By knightschade
#170267
This is my first project using the SPI pins to communicate and I'm having trouble.
I'm using the SD card Shield from Sparkfun as well as the ADXL345 breakout.
The ADXL345 uses CS pin 10 and the SD card Shield uses CS pin 8 by default so they are already set on different chip select pins.
My problem is I try to combine the two sample codes to get something logging but I can't get it to work.
Separately, I can get the Accelerometer and the SD card to do what they are supposed to. But when I try combining the code, I get nothing.
Does someone have a working sample of the code so I can at least get them working together? Once I understand better what is going on, I can try tweaking it from there.
Thanks.
By knightschade
#170275
Here is my non working code. I get compile errors that I'm not sure how to resolve.
Code: Select all
// SD and SPI libraries
#include <SD.h>
#include <SPI.h>

// Used to Create File
File myFile;

//Assign the Chip Select signal to pin 10.
int CS=10;
int CSb=8;
//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL = 0x2D;	//Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32;	//X-Axis Data 0
char DATAX1 = 0x33;	//X-Axis Data 1
char DATAY0 = 0x34;	//Y-Axis Data 0
char DATAY1 = 0x35;	//Y-Axis Data 1
char DATAZ0 = 0x36;	//Z-Axis Data 0
char DATAZ1 = 0x37;	//Z-Axis Data 1

//This buffer will hold values read from the ADXL345 registers.
char values[10];
//These variables will be used to hold the x,y and z axis accelerometer values.
int x,y,z;

  
void setup()
{
  //Initiate an SPI communication instance.
  SPI.begin();
  //Configure the SPI connection for the ADXL345.
  SPI.setDataMode(SPI_MODE0);
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  
  Serial.print("Initializing SD card...");
  
  pinMode(10, OUTPUT);

  if (!SD.begin(8)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
 
digitalWrite(8, HIGH);

 //Configure the SPI connection for the ADXL345.
  SPI.setDataMode(SPI_MODE3);
  
  //Set up the Chip Select pin to be an output from the Arduino.
  pinMode(CSb, OUTPUT);
  //Before communication starts, the Chip Select pin needs to be set high.
  digitalWrite(CS, HIGH);
  
  //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
  writeRegister(DATA_FORMAT, 0x01);
  //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
  writeRegister(POWER_CTL, 0x08);  //Measurement mode  
}


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

  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to Accel.txt...");
    myFile.println( //Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
  //The results of the read operation will get stored to the values[] buffer.
  readRegister(DATAX0, 6, values);

  //The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
  //The X value is stored in values[0] and values[1].
  x = ((int)values[1]<<8)|(int)values[0];
  //The Y value is stored in values[2] and values[3].
  y = ((int)values[3]<<8)|(int)values[2];
  //The Z value is stored in values[4] and values[5].
  z = ((int)values[5]<<8)|(int)values[4];

  //Print the results to the SD Card.
  file.print(x, DEC);
  file.print(',');
  file.print(y, DEC);
  file.print(',');
  file.println(z, DEC);      
  delay(10); 
}

//This function will write a value to a register on the ADXL345.
//Parameters:
//  char registerAddress - The register to write a value to
//  char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
  //Set Chip Select pin low to signal the beginning of an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the register address over SPI.
  SPI.transfer(registerAddress);
  //Transfer the desired register value over SPI.
  SPI.transfer(value);
  //Set the Chip Select pin high to signal the end of an SPI packet.
  digitalWrite(CS, HIGH);
}

//This function will write a value to a register on the ADXL345.
//Parameters:
//  char registerAddress - The register to write a value to
//  char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
  //Set Chip Select pin low to signal the beginning of an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the register address over SPI.
  SPI.transfer(registerAddress);
  //Transfer the desired register value over SPI.
  SPI.transfer(value);
  //Set the Chip Select pin high to signal the end of an SPI packet.
  digitalWrite(CS, HIGH);
}

//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
//  char registerAddress - The register addresse to start the read sequence from.
//  int numBytes - The number of registers that should be read.
//  char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister(char registerAddress, int numBytes, char * values){
  //Since we're performing a read operation, the most significant bit of the register address should be set.
  char address = 0x80 | registerAddress;
  //If we're doing a multi-byte read, bit 6 needs to be set as well.
  if(numBytes > 1)address = address | 0x40;

  //Set the Chip select pin low to start an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the starting register address that needs to be read.
  SPI.transfer(address);
  //Continue to read registers until we've read the number specified, storing the results to the input buffer.
  for(int i=0; i<numBytes; i++){
    values[i] = SPI.transfer(0x00);
  }
  //Set the Chips Select pin high to end the SPI packet.
  digitalWrite(CS, HIGH);
}
    // close the file:
    myFile.close();
    Serial.println("done.");
  } 
  else {
    // if the file didn't open, print an error:
    Serial.println("error opening Accel.txt");
  }
}


By knightschade
#170304
OK, I got it working. It samples and writes to the SD card in 8ms intervals, or about 120 writes per second. It saves the time in Milliseconds, x axis, y axis, z axis, and also converts to G's for the x,y, and z.
The files it saves have a weird naming convention at the end for some unknown reason. All you have to do is change the .0*T to .txt or .csv and you can open it in a comma delineated spread sheet.
Here's the working code. Do with it as you please.
Code: Select all
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Sonny Schade   04/15/2014
//  Accelerometer Project using SparkFun ADXL345 and SD Data Logger Breakout boards
//  Both Sheilds communicating over SPI
//  SD Card uses Pin 8 by default for SS/CS Pin (Spark Fun Board)
//  ADXL345 using Pin 10 for CS pin but you can use another digital pin if you choose
//  
//  This code was created by mutilating and sewing together code from sample code and other people's projects 
//  so feel free to do the same to this one, I hope this helps someone starting out.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Include the relevant libraries provided with the Arduino
#include <SPI.h>
#include <SD.h>

// place holder for a large number
unsigned long time; // called it "time"

//Factors to change the data output by the accelerometer from raw data to G forces
double g=0.0039; //+/- 2g Make sure this matches the writeRegister Data Format on line 91!
//double g=0.0078; //+/- 4g
//double g=0.016;  //+/- 8g
//double g=0.031; //+/- 16g

#define SYNC_INTERVAL 10000 // mills between calls to flush() - to write data to the card
uint32_t syncTime = 0; // time of last sync()

// Assign the ADXL345 CS pin to Pin 10 ** make this whatever digital pin you chose to use**
int CS=10;

//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL = 0x2D;    //Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32;    //X-Axis Data 0
char DATAX1 = 0x33;    //X-Axis Data 1
char DATAY0 = 0x34;    //Y-Axis Data 0
char DATAY1 = 0x35;    //Y-Axis Data 1
char DATAZ0 = 0x36;    //Z-Axis Data 0
char DATAZ1 = 0x37;    //Z-Axis Data 1
char BW_RATE = 0x2C;    //Sampling Rate

//This buffer will hold values read from the ADXL345 registers.
char values[10];
//Initalize variables
int x,y,z;
int i;
double xg,yg,zg;
double totals[3];
File myFile;

void setup(){
  //Initiate an SPI communication instance.
  SPI.begin();
  Serial.begin(38400);   
  
  //Set up the Chip Select pin for the SD card to be an output from the Arduino.
  pinMode(8, OUTPUT);
  //Set chip select for ADXL345 to be output from Arduino.
  pinMode(CS, OUTPUT);

  //Set the CS Pin for the accelerometer high so that the ADXL345 and SD card won't be trying to talk at same time.
  digitalWrite(CS, HIGH);

  //Initialize the SD card
  if (!SD.begin(8)) { // the (8) is the CS pin you chose to use, on my sheild that pin is hardwired to "8"
    return;
  }
  // create a new file 
  // starts with Crash00, and next one would be Crash01 
  char filename[] = "Crash.txt";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      myFile = SD.open(filename, FILE_WRITE);
      break;  // leave the loop!
    }
  }
   
  //Print header to file
  myFile.println("Time in MilliSeconds,X_Axis,Y_Axis,Z_Axis, X_G's,Y_G's,Z_G's");

  //Configure the SPI connection for the ADXL345.
  SPI.setDataMode(SPI_MODE3);
  //Before communication starts, the Chip Select pin needs to be set high.
  digitalWrite(CS, HIGH);

  //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
  writeRegister(DATA_FORMAT, 0x00);//0x00 +/- 2G, 0x01 +/- 4G. 0x10 +/- 8G,0x11 +/-16G
  //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
  writeRegister(POWER_CTL, 0x08);  //Measurement mode 
  //Try and change sampling rate
  writeRegister(BW_RATE, 0x0D);
}

void loop(){

  //Count Milliseconds from time Arduino powered on
  time = millis();
  
  //Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
  //The results of the read operation will get stored to the values[] buffer.
  readRegister(DATAX0, 6, values);

  //The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
  //The X value is stored in values[0] and values[1].
  x = ((int)values[1]<<8)|(int)values[0];
  //The Y value is stored in values[2] and values[3].
  y = ((int)values[3]<<8)|(int)values[2];
  //The Z value is stored in values[4] and values[5].
  z = ((int)values[5]<<8)|(int)values[4];

  //Convert raw data to g values
  xg=x*g;
  yg=y*g;
  zg=z*g;

  //Store g values in an array
  totals[0] = xg;
  totals[1] = yg;
  totals[2] = zg;

   // log time
    myFile.print(time);
    myFile.print(',');
   
  //Log accelerations
    myFile.print(x);
    myFile.print(',');
    myFile.print(y);
    myFile.print(',');
    myFile.print(z);
    myFile.print(',');
  for (i = 0; i < 3; i = i + 1) {
    myFile.print(totals[i]);
    myFile.print(',');
    // Open the serial monitor to see if it's working, select a baud rate of 38400, only displays milliseconds and G's
    Serial.print(time);
    Serial.print(',');
    Serial.print(totals[i]);
    Serial.print(',');
  }
  myFile.println();
  Serial.println();
 
  // Now we write data to disk! Don't sync too often - requires 2048 bytes of I/O to SD card
  // which uses a bunch of power and takes time
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();

  // flush the system
  myFile.flush();
 // delay to help clear some of the noise
 // delayMicroseconds(2);

}   //END OF LOOP!!!!!

//This function will write a value to a register on the ADXL345.
//Parameters:
//  char registerAddress - The register to write a value to
//  char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
  //Set Chip Select pin low to signal the beginning of an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the register address over SPI.
  SPI.transfer(registerAddress);
  //Transfer the desired register value over SPI.
  SPI.transfer(value);
  //Set the Chip Select pin high to signal the end of an SPI packet.
  digitalWrite(CS, HIGH);
}

//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
//  char registerAddress - The register addresse to start the read sequence from.
//  int numBytes - The number of registers that should be read.
//  char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister(char registerAddress, int numBytes, char * values){
  //Since we're performing a read operation, the most significant bit of the register address should be set.
  char address = 0x80 | registerAddress;
  //If we're doing a multi-byte read, bit 6 needs to be set as well.
  if(numBytes > 1)address = address | 0x40;

  //Set the Chip select pin low to start an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the starting register address that needs to be read.
  SPI.transfer(address);
  //Continue to read registers until we've read the number specified, storing the results to the input buffer.
  for(int i=0; i<numBytes; i++){
    values[i] = SPI.transfer(0x00);
  }
  //Set the Chips Select pin high to end the SPI packet.
  digitalWrite(CS, HIGH);
}
By TheIronMiner
#184566
You are literally my hero, I have been working on trying to get these things to play nice for about 10 hours (however using I2C and SPI). I have one additional question for you though. Have you got multiple accelerometers to work on the bus with the sd card shield say like 2?
By knightschade
#184579
I never tried using more then one, but you would just assign another pin to the CS pin. and create another variable called "CS2" or whatever. Just go through the code and anywhere it says something about a "CS" pin high low whatever, just copy that line and paste it below the orininal and chage "CS" to "CS2" or whatever you call it. For Example:

// Assign the ADXL345 CS pin to Pin 10 ** make this whatever digital pin you chose to use**
int CS=10;
int CS2=11;

//Before communication starts, the Chip Select pin needs to be set high.
digitalWrite(CS, HIGH);
digitalWrite(CS2, HIGH);

etc. etc. the whole way down the code. Hope that helps. I'm not a programmer I'm a hobbyist/sloppy code hacker/do whatever it takes to make it work/maybe/sometimes...kinda guy. Good Luck, and I'm glad my code worked for you.
By TheIronMiner
#184592
At any point did you get noise on the serial monitor and simultaneously have the sd not record data? Right now I'm only seeing random flashing ascii characters across the terminal
By knightschade
#184593
Make sure your serial monitor is set to a bit rate of 38400. Should be in the settings. Otherwise, no I never got that.
By TheIronMiner
#184600
I have run the code exactly as you created, however I only get random characters on the serial monitor and it doesn't write any data to the SD card itself. All the sensors and the board are wired correctly
By TheIronMiner
#184601
Okay so I'm getting data from your code, but only for the first 14 samples as shown here:

Time in MilliSeconds,X_Axis,Y_Axis,Z_Axis, X_G's,Y_G's,Z_G's
205,0,0,0,0.00,0.00,0.00,
209,104,-17,-122,0.41,-0.07,-0.48,
214,107,-16,-126,0.42,-0.06,-0.49,
218,104,-19,-124,0.41,-0.07,-0.48,
222,106,-18,-124,0.41,-0.07,-0.48,
230,103,-20,-122,0.40,-0.08,-0.48,
237,105,-16,-121,0.41,-0.06,-0.47,
245,105,-16,-117,0.41,-0.06,-0.46,
253,104,-16,-118,0.41,-0.06,-0.46,
262,102,-16,-117,0.40,-0.06,-0.46,
270,105,-19,-116,0.41,-0.07,-0.45,
278,105,-18,-114,0.41,-0.07,-0.44,
286,107,-17,-115,0.42,-0.07,-0.45,
357,0,0,0,0.00,0.00,0.00,
360,0,0,0,0.00,0.00,0.00,
364,0,0,0,0.00,0.00,0.00,
367,0,0,0,0.00,0.00,0.00,
374,0,0,0,0.00,0.00,0.00,
381,0,0,0,0.00,0.00,0.00,
390,0,0,0,0.00,0.00,0.00,
397,0,0,0,0.00,0.00,0.00,
404,0,0,0,0.00,0.00,0.00,
By knightschade
#184603
If it is working , then stops working that tells me the software is fine but there is a hardware problem. Check your solder joints, connections power supplies, grounds and so forth. Good luck trouble shooting.
By TheIronMiner
#184605
A hardware issue wouldn't make any sense being that it is recording data then just suddenly stops after x amount of entries every time, my joints are fine. I will continue to explore debugging but thank you for the help.
By cml029
#193153
I am having the same issue as @TheIronMiner where only the first 14 samples are being outputted to the SD card. If I change the filename that the data is being written to, more samples output on the serial monitor but it doesn't write to the SD card. I was wondering if anyone ever figured out what was wrong? Any help would be great.
By Valen
#193698
Do new lines not appear in the file, or are more new lines with zero-ed values added into the file? Change the code to spit out the ADXL345 values to serial before writing them to file. And see if they match with what ends up in the file. If they are the same then the problem is with reading the ADXL345 or it's SPI interfacing.