Class for using AD7730 chip

AD7730.cpp

Committer:
cfscontrols2
Date:
2011-12-16
Revision:
0:c584a588c24f

File content as of revision 0:c584a588c24f:

#include "AD7730.h"

/************************************************************************************************************************/
//default constructor requires pin names for spi and ready pin
/************************************************************************************************************************/
AD7730::AD7730(PinName mosi, PinName miso, PinName sclk, PinName ready, PinName cs) : _spi(mosi, miso, sclk), _cs(cs), _readyDig(ready), _LED3(LED3){
    
    _readyPin = ready;
      
    //setup default parameters for spi
    _spi.format(8, 1);  //8bits with polarity 0 (clock idle low), phase 1 (write on falling edge, read on rising edge)
    _spi.frequency(1000000); //2Mhz
    
    //set default setup
    _minWeight = 1;
    _maxWeight = 5;
    _fullScaleWeight = 11.023; //5kgs
    
    //reset the device
    reset(true);
    
    //get default registry values
    _mode = readRegistry(MODE_REG);
    _filter = readRegistry(FILTER_REG);
    _dac = readRegistry(DAC_REG);
    _offset = readRegistry(OFFSET_REG);
    _gain = readRegistry(GAIN_REG);
    
    //set chip select high
    _cs = 1; 
    
    //run internal calibration
    internalFullCal();
    internalZeroCal(); 
    
    //turn off LED3
    _LED3 = false; 
    

    _continous = false;
    _bufferCount = 0;
    memset(_readBuffer, 0, 100);
      
}

/************************************************************************************************************************/
// default destructor
/************************************************************************************************************************/
AD7730::~AD7730(void){}




/************************************************************************************************************************/
//function to read the specified registry value
/************************************************************************************************************************/
int AD7730::readRegistry(int registry){

    int readReg = 0;
    int bytes = 0;
    
    switch(registry){
    
        case STATUS_REG: //status registry
        {   
            readReg = 0x10;
            bytes = 1;
            break;
        }
        case DATA_REG: //data registry
        {
            _LED3 = _readyDig ? true : false;
            readReg = 0x11;
            bytes = 3;
            break;
        }
        case MODE_REG: //mode registry
        {
            readReg = 0x12;
            bytes = 2;
            break;
        }
        case FILTER_REG: //filter registry
        {
            readReg = 0x13;
            bytes = 3;
            break;
        }
        case DAC_REG: //dac registry
        {
            readReg = 0x14;
            bytes = 1;
            break;
        }
        case OFFSET_REG: //offset registry
        {
            readReg = 0x15;
            bytes = 3;
            break;
        }
        case GAIN_REG: //gain registry
        {
            readReg = 0x16;
            bytes = 3;
            break;
        }
        case TEST_REG: //test registry
        {
            readReg = 0x17;
            bytes = 3;
            break;
        }
        default: //default to status registry
        {
            readReg = 0x10;
            bytes = 1;
            break;
        }
    } // end of switch statement
    
    //send command to read the registry
    _cs = 0; 
    wait_us(5);
    _spi.write(readReg);
    
    int value = 0;
    //loop through bytes of returned data
    for(int i=0; i<bytes; i++){
        value = (value << 8) | _spi.write(EMPTY_SPI); //shift previous return value up by 8 bits to make room for new data
    }
    wait_us(5);
    _cs = 1;
    
    return value;
}

/************************************************************************************************************************/
//function to write to the specified registry
/************************************************************************************************************************/
int AD7730::writeRegistry(int registry, int value){

    int writeReg = 0;
    int bytes = 0;
    
    switch(registry){
    
        case MODE_REG: //mode registry
        {
            writeReg = 0x02;
            bytes = 2;
            _mode = value;
            //check if continous converstion is being stopped or started
            _continous = ((value & 0x2000) > 0)? true : false;
            break;
        }
        case FILTER_REG: //filter registry
        {
            writeReg = 0x03;
            bytes = 3;
            _filter = value;
            break;
        }
        case DAC_REG: //dac registry
        {
            writeReg = 0x04;
            bytes = 1;
            _dac = value;
            break;
        }
        case OFFSET_REG: //offset registry
        {
            writeReg = 0x05;
            bytes = 3;
            _offset = value;
            break;
        }
        case GAIN_REG: //gain registry
        {
            writeReg = 0x06;
            bytes = 3;
            _gain = value;
            break;
        }
        default: //default to communications register
        {
            writeReg = 0x00;
            bytes = 0;
            break;
        }
    } // end of switch statement
    
    //send command to write to a the registry
    _cs = 0;
    wait_us(5);
    _spi.write(writeReg);
    
    //send data
    switch(bytes)
    {
        case 3:
            _spi.write(((value >> 16) & 255));
        case 2:
            _spi.write(((value >> 8) & 255));
        case 1:
            _spi.write((value & 255));
            break;
        default:
            break;
    }
    wait_us(5);
    _cs = 1;
    
    return value;
}


/************************************************************************************************************************/
//function to initiate an internal full-scale calibration
/************************************************************************************************************************/
int AD7730::internalFullCal(void){
    
    //reset execution timer
    _exeTmr.reset();

    //1011000100110000 (0xB130) Internal Full-Scale Calibration, unipolar, long data, low reference, 0-80mv, channel 0
    writeRegistry(MODE_REG, 0xB130);
    wait_us(1); //give time for ready line to go high
    
    //wait for ready pin to go low indicating calibration complete with timeout of 2000ms
    int time = 0;
    while(_readyDig && time < 2000){
        wait_ms(2);
        time += 2;
    }
    
    //update execution time
    _exeTime = _exeTmr.read_us();
    
    //check if timeout occurred
    if(time >= 2000){
        _exeError = 56;
        return 1;
    }
    else {
        _exeError = 0;
        return 0;
    } 
}


/************************************************************************************************************************/
//function to initiate an internal zero-scale calibration
/************************************************************************************************************************/
int AD7730::internalZeroCal(void){
    
    //reset execution timer
    _exeTmr.reset();

    //1001000100110000 (0x9100) Internal Zero-Scale Calibration, unipolar, long data, low reference, 0-10mv, channel 0
    writeRegistry(MODE_REG, 0x9100);
    wait_us(1); //give time for ready line to go high
    
    //wait for ready pin to go low indicating calibration complete with timeout of 2000ms
    int time = 0;
    while(_readyDig && time < 2000){
        wait_ms(2);
        time += 2;
    }
    
    //update execution time
    _exeTime = _exeTmr.read_us();
    
    //check if timeout occurred
    if(time >= 2000){
        _exeError = 56;
        return 1;
    }
    else {
        _exeError = 0;
        return 0;
    } 
}



/************************************************************************************************************************/
//function to initialize the chip settings to default values after power up and to run internal calibration
/************************************************************************************************************************/
int AD7730::initialize(void){

    //reset device
    reset(true); //initate a full reset
    
    systemLowCal(_minWeight);
    
    //set the Offset
    writeRegistry(OFFSET_REG, _offset);
    
    //set the Gain
    writeRegistry(GAIN_REG, _gain);
    
    //set the DAC
    writeRegistry(DAC_REG, _dac); 
    
    return 0;   
     
}


/************************************************************************************************************************/
//function to initiate a system zero-scale calibration
/************************************************************************************************************************/
int AD7730::systemLowCal(double wgt){
    
    //set minimum weight for low calibration
    _minWeight = wgt;
    
    
    //1101000100000000 (0xD100) System Zero-Scale Calibration, unipolar, long data, low reference, 0-10mv, channel 0
    int mode = 0xD100;
    
    writeRegistry(MODE_REG, mode);
    
    wait_us(1);  //give time for ready pin to go high
    int time = 0;
    while(_readyDig && time < 2000){
        time += 2;
        wait_ms(2);
    }//wait for ready pin to go low     
    
    if(_readyDig){
        //printf("External Zero Failed\r\n");
    }
    
    return (time >= 2000) ? 1 : 0;
    
}


/************************************************************************************************************************/
//function to initiate a system high calibration
/************************************************************************************************************************/
int AD7730::systemHighCal(double max, double fullScale){
    
    //get the current offset value
    int offset = readRegistry(OFFSET_REG);
    int fullScaleAdjust = ((double)offset - 8388608) + 16777215;
    fullScaleAdjust /= 2;
    //double calFactor = fullScale / (fullScaleAdjust / 2); //dual load cells splits the weight in half
    
    //set maximum weight for high calibration
    _maxWeight = max;
    //calculate the expected value for the maximum weight based on the full scale of the load cell
    double expected = ((fullScaleAdjust * max) / fullScale);
    
    //take some samples
    double avg = 0;
    double value = 0;
    for(int i=0; i<20; i++){
        value = (double)read();
        avg += value;  
    }
    
    avg = avg / 20;
    
    //get current gain setting
    double gain = (double)readRegistry(GAIN_REG);
    
    //calculate new gain value
    double adjustedGain = gain * (expected / avg);
    
    printf("Expected: %.3f\r\nActual: %.3f\r\n", expected, avg);
    
    int err = 0;
    if(adjustedGain > 16777215){
        //printf("Exceeded Maximum Gain Value\r\n");
        err = 1;
    }
    
    //update gain registry
    writeRegistry(GAIN_REG, (int)adjustedGain);
    
    return err;
}



/************************************************************************************************************************/
//function to initiate the conversion of a sample
/************************************************************************************************************************/
int AD7730::startConversion(bool wait){
    
    //set the mode to do a single conversion
    //0101000100000000 (0x5000)  Single Conversion, unipolar, short data, low reference, 0-10mv, channel 0
    int mode = 0x5100;

    writeRegistry(MODE_REG, mode);
    
    if(wait){
        //wait for conversion to complete
         
        wait_us(1); //give time for ready to go high*/
    
        int time = 0;
        while(_readyDig && time < 2000000){
            time += 2;
            wait_us(2);
        }//wait for ready pin to go low.*/
        
        if(time >= 2000000){
            //printf("Convert Timeout\r\n");
            _exeError = 56;
            return 1;
        }
    }
    
    return 0;
}

/************************************************************************************************************************/
//function to do a single read with conversion
/************************************************************************************************************************/
int AD7730::read(){

    if(_continous){
        //chip is running in continous conversion mode
        return _lastValue;
    }
    else {
        //a new conversion must be started
        startConversion(true);
        return readRegistry(DATA_REG);
    }              
}


/************************************************************************************************************************/
//function to set the filter settings
/************************************************************************************************************************/
void AD7730::setFilter(int SF, bool chop, int filter2){
    
    SF = SF << 12; //slide SF settings up 12 bits
    
    switch(filter2){
        case 2:
            SF = SF | 512;  //turn on bit 10 for skip mode
            break;
        case 1:
            SF = SF | 256; //turn on bit 09 for fast mode
            break;
        default:
            break; //leave bits 9 & 10 off so secondary filter is fully enabled
    }
    
    if(chop){
        SF = SF | 16; //turn on bit 5 to enable chop mode
    }
    
    writeRegistry(FILTER_REG,SF);
    
}

/************************************************************************************************************************/
//function to reset the chip
/************************************************************************************************************************/
void AD7730::reset(bool fullReset){
    
    _cs = 0;
    wait_us(5);
    _spi.write(0xFF);
    _spi.write(0xFF);
    _spi.write(0xFF);
    _spi.write(0xFF);
    _spi.write(0xFF);
    wait_us(5);
    _cs = 1;
    
    //if not a full reset, then reload registry settings
    if(!fullReset){
        writeRegistry(MODE_REG, _mode);
        writeRegistry(FILTER_REG, _filter);
        writeRegistry(DAC_REG, _dac);
        writeRegistry(OFFSET_REG, _offset);
        writeRegistry(GAIN_REG, _gain);
    }
    else {
        //reinitiate internal calibration
        internalFullCal();
        wait_ms(100);
        internalZeroCal();
        wait_ms(100);
    }
}

/************************************************************************************************************************/
//function to adjust the DAC
/************************************************************************************************************************/
int AD7730::adjustDAC(int direction){

    int DAC = readRegistry(DAC_REG);
    int sign = DAC & 32; //get the sign bit
    DAC &= 31; //remove sign bit 
 
    
    if(direction > 0){
        //increment DAC
        if((sign > 0) && (DAC == 1)){ //sign bit is set and DAC is already at 1 change to 0;
            DAC = 0;
            sign = 0;
        }
        else if((sign == 0) && (DAC >= 0)){  //sign bit is not set increment DAC
            DAC++;
        }
        else if ((sign > 0) && (DAC > 0)){  //sign bit is set decrement DAC because it is negative
            DAC--;
        }
        
    }
    
   else if(direction < 0){
        //decrement DAC
        if((sign == 0) && (DAC == 0)){ //sign bit is not set and DAC is already at 0
            DAC = 1;
            sign = 1;
        }
        else if((sign == 0) && (DAC > 0)){  //sign bit is not set increment DAC
            DAC--;
        }
        else if ((sign > 0) && (DAC >= 0)){  //sign bit is set decrement DAC because it is negative
            DAC++;
        }
        
    }    

    else{
        //no change in direction
        //do nothing
        return DAC;
    }
    
    if(DAC > 31){
        DAC = 31;  //limit DAC to maximum value of 31 (5 bits)
    }
    
    if(sign > 0){
        DAC |= 32; //set the sign bit of DAC
    }
    
    //update DAC
    writeRegistry(DAC_REG, DAC);
    
    return DAC;
}

/************************************************************************************************************************/
//function to set the filtering SF setting
/************************************************************************************************************************/
void AD7730::setFilterSF(int sf){

    //get current filter setting
    int filter = readRegistry(FILTER_REG);
    
    //clear sf bits 
    filter &= 0xFFF;
    
    //bitshift sf up by 12 bits
    sf = sf << 12;
    
    //or sf bits with filter bits
    filter = filter | sf;
    
    //apply new filter setting
    writeRegistry(FILTER_REG, filter);
}

/************************************************************************************************************************/
//function to set the filtering chop mode
/************************************************************************************************************************/
void AD7730::setFilterChop(int enabled){

    //get current filter setting
    int filter = readRegistry(FILTER_REG);
    
    //clear bit 5 
    filter &= ~0x10;
    
    //apply chop setting
    if(enabled)
        filter |= 0x10;
    
    //apply new filter setting
    writeRegistry(FILTER_REG, filter);
}

/************************************************************************************************************************/
//function to set the mode of the second filter
/************************************************************************************************************************/
void AD7730::setFilterMode(int mode){

    //get current filter setting
    int filter = readRegistry(FILTER_REG);
    
    //clear bits 9 & 10 
    filter &= ~0x300;
    
    //apply mode setting
    if(mode == 1){
        filter |= 0x100; //apply fast mode
    }
    else if(mode == 2){
        filter |= 0x200; //apply skip mode
    }
    else {
        //leave both bits unset
    }
    
    
    //apply new filter setting
    writeRegistry(FILTER_REG, filter);
}

/************************************************************************************************************************/
//function to set the chip to continous conversion
/************************************************************************************************************************/
void AD7730::start(void){
    
    writeRegistry(MODE_REG, 0x3100); //set to continous conversion mode
    _interruptReady = new InterruptIn(_readyPin);
    _tmr.start();
    _frequency = 0;
    _interruptReady->fall(this, &AD7730::interruptRead);  
}

/************************************************************************************************************************/
//function to stop the chip running in continous conversion mode
/************************************************************************************************************************/
void AD7730::stop(void){
    
    writeRegistry(MODE_REG, 0x1100); //set to idle mode
    
    _interruptReady = NULL;
  
}

/************************************************************************************************************************/
//function to check if the ready digital line is low
/************************************************************************************************************************/
bool AD7730::isReady(void){
     
    //if digital line is high, return not ready  
    return (_readyDig)? false : true;
    
}

/************************************************************************************************************************/
//function to read data on falling edge of ready pin when running in continous conversion mode
/************************************************************************************************************************/
void AD7730::interruptRead(void){

    _continous = true; //set flag indicating that chip is running in continous mode
    
    _lastValue = readRegistry(DATA_REG);
    
    //store data in circular buffer
    _readBuffer[_bufferCount] = _lastValue;
    if(_bufferCount < 99){
        _bufferCount++;
    }
    else {
        _bufferCount = 0;
    }
    
    //update conversion speed frequency
    _frequency = _tmr.read_us();
    _tmr.reset();
}

/************************************************************************************************************************/
//function to return the hertz of the data conversion 
/************************************************************************************************************************/
double AD7730::getHz(void){

    return (1000000 / (double)_frequency);
}