Go Down

Topic: ADC + SD Card (Read 915 times) previous topic - next topic

SOnuts

Hello all,

I am trying to use an Analog to digital converter to read a voltage, then save that voltage to an SD card. Should be pretty simple right? Both components are connected to my Arduino Fio on the same SPI bus. I know my wiring is okay and my code will work for a while. But eventually, something goes wrong and I begin receiving only all 0's or all 1's from my ADC when I should be getting a different voltage reading. If I change the voltage being sent to my ADC, I still receive the same incorrect values of all 0's or all 1's.

My Arduino Sketch:

Code: [Select]

#include <SD.h>
#include <ADC.h>
#include <SPI.h>

//ADC on pin 5
Adc adc(5);

void setup()
{
 Serial.begin(57600);
 
 delay(1000);
 
 pinMode(5,OUTPUT); //ADC Chip select
 pinMode(7,OUTPUT); //SD Chip select
 pinMode(10,OUTPUT);  

 //initialize the ADC to start SPI and set pins
 adc.initialize();
       
 //see if the card is present and can be initialized:
 if (!SD.begin(7)) {
   Serial.println("Card failed, or not present");
 }
 digitalWrite(7,HIGH); //delelect SD card
 
 delay(1000);
}

void loop()
{
   delay(1000);            
   adc.getVoltage();
}


SOnuts

My Library header file:
Code: [Select]
 #include "ADC.h"

int ADC_pin;

Adc::Adc(int pin)
{
   _pin = pin;

}

void Adc::initialize()
{
   //setup SPI
   SPI.begin();
     
   SPI.setBitOrder(MSBFIRST);
   
   SPI.setDataMode(SPI_MODE2);//KEEP!!
   
   //setup slave select pin
   pinMode(_pin, OUTPUT);

   SPI.setClockDivider(SPI_CLOCK_DIV128);
   delay(5);
   
   configFilterRegisters();
   configModeRegister(); //starts calibration
   
   Serial.println("Initialized");
   
   
}

double Adc::getVoltage()
{
   
   while(digitalRead(RDY)); //once RDY pin is pulled low
       
   digitalWrite(_pin, LOW);//pull CS low
   
   selectADC();

   delay(100); // a delay here could help
   
   SPI.transfer(0x5C); // Channel 1 Data Register
   
   byte inputBytes[4];
   inputBytes[3] = 0x00; //MSB all 0's
   inputBytes[2] = SPI.transfer(0x00);
   inputBytes[1] = SPI.transfer(0x00);
   inputBytes[0] = SPI.transfer(0x00);
   
   digitalWrite(_pin, HIGH);
           
   String dataString = "";
   
   for (int i = 3; i >= 0; i--) {
       int specificByte = inputBytes[i];
       dataString += String(specificByte);
       dataString += ",";
   }
   dataString += "end";
       
   Serial.println(dataString);
   

   writeToSD(dataString); //change in code
   
   return 0;
}

void Adc::configFilterRegisters() { //used
   //select chip
   digitalWrite(_pin, LOW);
   
   byte to_Comm_Register_1 = mergeBytes(WRITE, FILTER_HI_REGISTER, _channel);
   byte to_FILTER_HI_REG   = mergeBytes(_polarity, _word_Length, _hi_Filter_Selection);
   byte to_Comm_Register_2 = mergeBytes(WRITE, FILTER_LOW_REGISTER, _channel);
   byte to_FILTER_LOW_REG  = mergeBytes(_low_Filter_Selection, 0x00, 0x00);

   //Setup a write to Filter High Register
   SPI.transfer(to_Comm_Register_1);
       
   //Write to Filter High Register
   //select required values
   //select bipolar, 24bit (For Filter HI Register), does something with filter cut-off frequency
   SPI.transfer(to_FILTER_HI_REG); // 0100 1111
   
   //deselect chip
   digitalWrite(_pin, HIGH);
   
   //select chip
   digitalWrite(_pin, LOW);
           
   //Setup a write to Filter Low Register
   //SPI.transfer(0x37); //and select 'test mode' channel
   SPI.transfer(to_Comm_Register_2);//Or select channel  
           
   //Write to filter low register
   //Select some other magical cut-off frequency stuff
   SPI.transfer(to_FILTER_LOW_REG);
   
   //deselect chip
   digitalWrite(_pin, HIGH);
}

void Adc::configModeRegister() {   //used
   //select chip
   digitalWrite(_pin, LOW);
   
   byte to_Comm_Register = mergeBytes(WRITE, MODE_REGISTER, _channel);
   byte to_Mode_Register = mergeBytes(_calibration, _gain, 0x00);
   
   //Setup a write to Mode Register
   SPI.transfer(to_Comm_Register);//or select channel 1
       
   //Write to Mode Register
   SPI.transfer(to_Mode_Register); //
       
   //deselect chip
   digitalWrite(_pin, HIGH);
}

byte Adc::mergeBytes(byte first, byte second, byte third) {
   byte result = BUFFER | first;
   result = result | second;
   result = result | third;
   return result;
}

void Adc::writeToSD(String dataString)
{
   selectSD();
   
       File dataFile = SD.open("datalog.txt", FILE_WRITE);
       
       if (dataFile) {
           dataFile.println(dataString);
           dataFile.close();
           Serial.println(dataString);      //debug
       }
       else {
           Serial.println("Error opening datalog.txt");
       }
  }


void Adc::selectADC()  //used
{
   SPI.begin();
   SPI.setBitOrder(MSBFIRST);
   SPI.setDataMode(SPI_MODE2);
   SPI.setClockDivider(SPI_CLOCK_DIV128);
}

void Adc::selectSD() {
   if(!SD.begin(7)) { //begin SD SPI
   }
   else {
       Serial.println("SD does not work");
   }
   pinMode(10, OUTPUT);//required by library
}


My library cpp file
Code: [Select]
#ifndef ADC_h
#define ADC_h

#include "Arduino.h"
#include "SPI.h"
#include "SD.h"

//digital IO pins
#define RDY 9  

//Comm register
#define BUFFER 0x00
#define COMM_REGISTER 0x00 //0000 0000
#define MODE_REGISTER 0x10 //0001 0000
#define FILTER_HI_REGISTER 0x20 //0010 0000
#define FILTER_LOW_REGISTER 0x30 //
#define TEST_REGISTER 0x40
#define DATA_REGISTER 0x50
#define ZERO_SCALE_CALIB_REGISTER 0x60
#define FULL_SCALE_CALIB_REGISTER 0x70
#define READ 0x08 // 0000 1000
#define WRITE 0x00 // 0000 0000
//_channel specs
#define CHANNEL_1_6 0x00
#define CHANNEL_2_6 0x01
#define CHANNEL_3_6 0x02
#define CHANNEL_4_6 0x03
#define CHANNEL_1_2 0x04
#define CHANNEL_3_4 0x05
#define CHANNEL_5_6 0x06
#define CHANNEL_6_6 0x07 //test mode

//Mode Register
#define NORMAL_MODE 0x00
#define SELF_CALIB 0x20            //0010
#define ZERO_SCALE_SYS_CALIB 0x40  //0100
#define FULL_SCALE_SYS_CALIB 0x60  //0110
#define SYSTEM_OFFSET_CALIB 0x80   //1000
#define BACKGROUND_CALIB 0xA0      //1010
#define ZERO_SCALE_SELF_CALIB 0xC0 //1100
#define FULL_SCALE_SELF_CALIB 0xE0 //1110
#define BURNOUT_CURRENT_OFF 0x00
#define BURNOUT_CURRENT_ON 0x02 //default is off
#define FILTER_SYNC_OFF 0x00
#define FILTER_SYNC_ON 0x01 //default is off
//_gain
#define GAIN_1 0x00
#define GAIN_2 0x0    //0000 0100
#define GAIN_4 0x08   //0000 1000
#define GAIN_8 0x0C   //0000 1100
#define GAIN_16 0x10  //0001 0000
#define GAIN_32 0x14  //0001 0100
#define GAIN_64 0x18  //0001 1000
#define GAIN_128 0x1C //0001 1100

//Filter high Register
//_polarity
#define BIPOLAR 0x00
#define UNIPOLAR 0x80
//_word_Length
#define WORD_LENGTH_16 0x00
#define WORD_LENGTH_24 0x40
#define CURRENT_BOOST_OFF 0x00 //for gains 1 - 4
#define CURRENT_BOOST_ON 0x20  //for gains 8 - 128
#define MASTER_CLK_DISABLE 0x10 //which version do I have??
//_hi_Filter_Selection
#define DEFAULT_HI_FILTER 0x0F

//Filter low register
//_low_Filter_Selection
#define DEFAULT_LOW_FILTER 0xA0

//SRAM parameters
#define SRAM_BUFFER_SIZE 32768

class Adc
{
 public:
   Adc(int pin);
   
   //start SPI, enable pins, etc.
   void initialize();
   
boolean reset();
   void calibrate();
   void calibrate(byte);
   
byte getID();
byte getStatus();
word getMode();
word getConfig();
   
   double getGain();
   double getAVdd();
   
   //use in application
   void selectADC();
   void selectSD();
   double getVoltage(); //conversion takes ~50ms
   double getVoltageFast(); //conversion takes ~1.2 ms
   unsigned long getVoltageFastLong();
   
   //transfer values to the test register to test connection to the ADC
void writeTestRegister(); // switched from private
   
//transfer values from the test register to test connection to the ADC
void readTestRegister(); // switched from private
   
//    bool ADC_to_SRAM();  //writes a single ADC value to SRAM in sequential mode
                       // returns TRUE when SRAM full
   
//    void SRAM_to_Serial(); //prints all SRAM values to Serial port (radio)
   
   //used for debugging
   unsigned long getVoltage(byte &MSB, byte &SB, byte &LSB);
   
   //use to continuosly sample
   void readVoltages(double *volts, const unsigned int SIZE);

   void setFreq(byte freq);
void setChannel(byte channel);
   void setConversion(byte conversion);
   void setGain(byte gain);
   void setPolarity(byte polarity);
   
   void configFilterRegisters();
   void configModeRegister();
   void readFilterRegister();
   byte mergeBytes(byte first, byte second, byte third);
   void convertToVoltage(byte inputBytes[4]);
   //String convertDoubleToString(double sample);
   void writeToSD(String dataString);



 private:
   int _pin;
byte _channel;
byte _gain;
byte _polarity;
byte _freq;
   byte _conversion;
   byte _calibration;
   byte _word_Length;
   byte _spi_mode;
   byte _spi_clock;
   byte _hi_Filter_Selection;
   byte _low_Filter_Selection;
   
   
   //call function to change mode register:
   // used to change the operating mode (single vs. continuous) and sampling rate
   void writeModeRegister();
   
   // change configuration register:
   // used to change unipolar vs. bipoloar, select gain, and change channel
   void writeConfigRegister();
   
   //used to convert ADC code to voltage (given channel's gain).
   double convertCodeToVoltage(unsigned long);
   
   unsigned int SRAM_ptr;
};
#endif


SOnuts

May the force be with you.

tuxduino

Pretty hard to understand, but I think the culprit might hide here:

Code: [Select]
String dataString = "";
   
    for (int i = 3; i >= 0; i--) {
        int specificByte = inputBytes[i];
        dataString += String(specificByte);
        dataString += ",";
    }
    dataString += "end";
       
    Serial.println(dataString);
   

    writeToSD(dataString); //change in code


A bug in the String class is often mentioned as the cause of out-of-ram errors in long running sketches.
First thing I'd try is substitute String with a char array.

SOnuts

thanks for the reply, I am not sure how to replace the string with a char array. Unless I go from my byte to the string then to a char array.

If I understand what you're saying, problems arrise when I send a string over the SPI interface to my SD card?

I tried changing my code to this (see below) to see if that was the problem, but I am still getting the same problem :(
Code: [Select]

    String dataString = "";
    const char dummy = 'D';
   
    for (int i = 3; i >= 0; i--) {
        int specificByte = inputBytes[i];
        dataString += String(specificByte);
        dataString += ",";
    }
   
    Serial.println(dataString);
   
    writeToSD(dummy);


And in my writeToSD function:
Code: [Select]

void Adc::writeToSD(char dummy) ///SD
{   
    delay(100);

    selectSD();
   
    delay(100);
   
    File dataFile = SD.open("datalog.txt", FILE_WRITE);
    if (dataFile) {
        dataFile.println(dummy);
        dataFile.close();
        delay(100);
    }
    else {
        Serial.println("Error opening datalog.txt");
    }
    //state = SLEEP;
   
    delay(100);
   
    digitalWrite(7, HIGH);

}



Does that address the potential string problem?

SOnuts

Just to make sure, I got rid of any uses of string in my code. and I am still getting the same problem.

tuxduino

I don't know. Can you post your String-less code ?

SOnuts

Heres my new get voltage function

Code: [Select]

double Adc::getVoltage()
{
   
    while(digitalRead(RDY)); //once RDY pin is pulled low
       
    digitalWrite(_pin, LOW);//pull CS low
   
    selectADC();

    delay(100); // a dealy here could help
   
    SPI.transfer(0x5C); // Channel 1 Data Register
    //(0101 1100)
   
    byte inputBytes[4];
    inputBytes[3] = 0x00; //MSB all 0's
    inputBytes[2] = SPI.transfer(0x00);
    inputBytes[1] = SPI.transfer(0x00);
    inputBytes[0] = SPI.transfer(0x00);
   
    digitalWrite(_pin, HIGH);
   
    convertToVoltage(inputBytes);
   
    const char dummy = 'D';
   
    writeToSD(dummy);
   
    return 0;
}


and the convert to voltage function i'm using:
Code: [Select]

void Adc::convertToVoltage(byte inputBytes[4]) {
    double MSB = inputBytes[2];
    double SB = inputBytes[1];
    double LSB = inputBytes[0];
   
    double result = (MSB * 256 * 256);
    result = result + (SB * 256);
    result = result + LSB ;
    result = (result * 6.6 / 256 / 256 / 256) - 3.3;
   
    Serial.println("result is");
    Serial.print(result);
    Serial.print(" volts");
   
    }

Go Up