Go Down

Topic: ADC + SD Card (Read 972 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
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy