system
January 1, 2013, 7:14pm
1
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:
#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();
}
system
January 1, 2013, 7:15pm
2
My Library header file:
#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
#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
system
January 1, 2013, 7:16pm
3
May the force be with you.
Pretty hard to understand, but I think the culprit might hide here:
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.
system
January 1, 2013, 8:52pm
5
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
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:
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?
system
January 1, 2013, 9:05pm
6
Just to make sure, I got rid of any uses of string in my code. and I am still getting the same problem.
I don't know. Can you post your String-less code ?
system
January 1, 2013, 9:58pm
8
Heres my new get voltage function
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:
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");
}