Library to communicate with LDC1614

Dependencies:   SHTx

Dependents:   Inductive_Sensor_3

Fork of LDC1101 by Bob Giesberts

LDC1614.cpp

Committer:
bobgiesberts
Date:
2016-08-23
Revision:
28:76a2fc42f888
Parent:
LDC1101.cpp@ 27:05dd145c7997
Child:
29:41815fd13822

File content as of revision 28:76a2fc42f888:

/**
* @file LDC1614.cpp
* @brief this C++ file contains all required
* functions to interface with Texas
* Instruments' LDC1614.
*
* @author Bob Giesberts
*
* @date 2016-08-09
*
* @example
* Serial pc(USBTX, USBRX);
* LDC1614 ldc(PTC6, PTC7, PTC5, 16E6, 2, 120E-12);
* int main(){
*    while(1) {
*       while( !ldc.is_ready() ) {}
*    
*       pc.printf("sensor 1: %d | sensor 2: %d\r\n", ldc.get_Data(0), ldc.get_Data(1) );
*    }
* }
*/

#include "LDC1614.h"


LDC1614::LDC1614(PinName sda, PinName scl, PinName sd, float f_CLKIN, int channels, float capacitor) : _i2c(sda, scl), _shutdown_pin(sd)
{
    // settings
    _channels = channels;   // number of sensors
    _cap      = capacitor;
    _fCLKIN  = f_CLKIN;

    _Offset = 0;            // no offset needed
    _Rcount = 0xffff;       // maximum for greatest precision
    _SettleCount = 50;      // CHx_SETTLECOUNT = t_settle * f_REFx/16 = 50 (p.12)
    _DriveCurrent = 31;     // max???
       
    _i2c.frequency(400000); // 400 kHz (p.6)
    
    // Turn the LDC1614 on (exit shutdown)
    _shutdown_pin.write(0);
    wait_us(100);
    
    init();
}

LDC1614::~LDC1614()
{

}

void LDC1614::init()
{
    /********* SETTINGS *****************
    ** C_sensor     =   120 pF
    ** L_sensor     =     5 uH
    ** Rp_min       =  1500 Ohm
    **
    ** RCount       = 65535     (max)
    ** Settlecount  =    50
    ** Samplerate   =    15.3 Hz
    ** t_conv       =    65.5 ms
    **
    ** f_sensor_min =     6.4 MHz (d = inf)
    ** f_sensor_max =    10   MHz (d = 0)
    ** 
    ** CHx_FIN_DIVIDER  =     2       (6.4/2 = 3.2 < 16.0/4 = 4)
    ** CHx_FREF_DIVIDER =     1       (16.0 MHz)    
    ************************************/
    
    for(int i = 0; i < _channels; i++)
    {
        // set Reference Count to highest resolution
        setReferenceCount( i, _Rcount );

        // set the settling time
        // t_settle = (settlecount * 16) /f_REF
        setSettlecount( i, _SettleCount );
        
        // set Divider to 1 (for large range / ENOB / resolution)
        setDivider( i, 1, 2 ); // IN = 2 | REF = 1
        
        // set the drive current during sampling 
        setDriveCurrent( i, _DriveCurrent );   // (p. 15 | Figure 14)
        
        // shift the signal down a bit
        setOffset( i, _Offset );
    }

    // error_config (all  is standard)
    
    // mux_config    
    set( MUX_CONFIG, AUTOSCAN_EN,    _channels > 1 );
    set( MUX_CONFIG, RR_SEQUENCE,    max(0, _channels - 2) );
    set( MUX_CONFIG, DEGLITCH,       DEGLITCH_10M );
    
    // override Rp and use own Drive Current to reduce power consumption
    set( CONFIG, RP_OVERRIDE_EN,      1 );
    set( CONFIG, SENSOR_ACTIVATE_SEL, 1 );
    set( CONFIG, AUTO_AMP_DIS,        1 );
    set( CONFIG, REF_CLK_SRC,         1 ); // external f_CLKIN
    set( CONFIG, INTB_DIS,            1 );
    set( CONFIG, HIGH_CURRENT_DRV,    0 );
    
    // Done configuring settings, set LDC1614 in measuring modus
    wakeup();
}


void LDC1614::func_mode(LDC_MODE mode)
{ 
    switch (mode)
    {
        case LDC_MODE_ACTIVE:
        case LDC_MODE_SLEEP:
            _shutdown_pin.write( 0 );
            set( CONFIG, SLEEP_MODE_EN, LDC_MODE_ACTIVE );
            break;
        
        case LDC_MODE_SHUTDOWN:
            _shutdown_pin.write( 1 );
            break;
    }
}
void LDC1614::sleep( void )  { func_mode( LDC_MODE_SLEEP );  }
void LDC1614::wakeup( void ) { func_mode( LDC_MODE_ACTIVE ); }

void LDC1614::setReferenceCount( uint8_t channel, uint16_t rcount )
{
    writeI2Cregister( RCOUNT_CH0 + channel, rcount );
}

void LDC1614::setOffset( uint8_t channel, uint32_t offset )
{
    _Offset = offset;
    writeI2Cregister( OFFSET_CH0 + channel, uint16_t (offset >> 16) );
}

void LDC1614::setSettlecount( uint8_t channel, uint16_t settlecount )
{
    // _t_settle = (settlecount * 16) / (_f_CLKIN / dividerREF[channel])
    writeI2Cregister( SETTLECOUNT_CH0 + channel, settlecount );    
}

void LDC1614::setDivider( uint8_t channel, uint8_t divIN, uint8_t divREF )
{
    // make sure the values fit
    _dividerIN  = min(max(divIN,  1), 15);  // 4 bit
    _dividerREF = min(max(divREF, 1), 255); // 8 bit    
    writeI2Cregister( CLOCK_DIVIDERS_CH0 + channel, uint16_t ((_dividerIN << 12) + _dividerREF) );
} 

void LDC1614::setDriveCurrent( uint8_t channel, uint8_t idrive )
{   
    _DriveCurrent = min( idrive, 31 ); // 5-bit (b1 1111)
    // todo: read initial idrive [10:6]

    writeI2Cregister(DRIVE_CURRENT_CH0 + channel, uint16_t (_DriveCurrent<<10) );
}

void LDC1614::set( ADDR addr, SETTING setting, uint8_t value )
{
    uint8_t mask = 1;
    if ( addr == MUX_CONFIG )
    {
        switch (setting){
            case AUTOSCAN_EN: mask = 1; break;  // 1
            case RR_SEQUENCE: mask = 3; break;  // 11
            case DEGLITCH:    mask = 7; break;  // 111
        }    
    }
    regchange( addr, setting, value, mask );
}






/* GETTING DATA FROM SENSOR */

uint16_t LDC1614::get_status( void )
{
    uint16_t status[1]; 
    readI2C( status, STATUS );  
    return status[0];
}
bool LDC1614::is_ready( void )            { return ( get_status() & 0x0080 ); }


uint16_t LDC1614::get_Rcount( uint8_t channel )
{
    uint16_t rcount[1]; 
    readI2C( rcount, RCOUNT_CH0 + channel ); 
    return rcount[0];
}

uint32_t LDC1614::get_Data( uint8_t channel )
{
    uint16_t data[2];
    readI2C(data, DATA_MSB_CH0 + channel, 2);
    return ( (data[0] & 0x0fff)<<16) | data[1]; // MSB + LSB
}





void LDC1614::readI2C( uint16_t *data, uint8_t address, uint8_t length )
{
    // I2C reads per 8-bits, char is 8-bit, combine 8-bit in 16-bit sets
    char temp[length*2];
    _i2c.read( address, temp, length*2 );
    
    for ( int i = 0; i < length; i++ ) 
        data[i] = (uint16_t) (temp[2*i+1]<<8) | temp[2*i];
}

void LDC1614::writeI2C( uint16_t *data, uint8_t address, uint8_t length )
{
    // I2C reads per 8-bits, char is 8-bit, split 16-bit data up in sets of 8-bit
    char temp[length*2];
    for ( int i = 0; i < length; i++ )
    {
        temp[2*i]   = (data[i] & 0x00ff) >> 0; // 0, 2, 4 ...
        temp[2*i+1] = (data[i] & 0xff00) >> 8; // 1, 3, 5 ...
    }
    _i2c.write( address, temp, length*2 );
}

void LDC1614::writeI2Cregister(uint8_t reg, uint16_t value)
{
    writeI2C( &value, reg ); 
}

void LDC1614::regchange( uint8_t addr, uint8_t setting, uint8_t value, uint8_t mask )
{
    uint16_t config[1];
    readI2C( config, addr );
    writeI2Cregister( addr, uint16_t ( (config[0] & !(mask<<setting)) | (value<<setting)) ); // replace bits with number SETTING
}



/* CALCULATE STUFF WITH SENSOR DATA */

float LDC1614::get_fsensor( uint32_t LData )
{    
    _fsensor = _dividerIN * (_fCLKIN/_dividerREF) * ((LData / 268435456) + (_Offset / 65536)); // (p.14)
       
    return _fsensor;
}   

float LDC1614::get_Inductance( uint32_t Ldata )
{  
    float fsensor = get_fsensor( Ldata );
    _inductance = 1.0 / (_cap * 4 * PI*PI * fsensor*fsensor ); // ???
    return _inductance;
}



// EXTRA test: Get&print values of all variables to verify (to calculate the induction)
// The data will be printed on the screen using RealTerm: baud 9600.
// Begin ***********************************************************
    float LDC1614::get_fCLKIN()             {return _fCLKIN;}    
    uint8_t LDC1614::get_dividerIN()        {return _dividerIN;}
    uint8_t LDC1614::get_dividerREF()       {return _dividerREF;}
    uint32_t LDC1614::get_Offset()          {return _Offset;}
    float LDC1614::get_cap()                {return _cap;}
// END ***********************************************************