Library to communicate with LDC1614

Dependencies:   SHTx

Dependents:   Inductive_Sensor_3

Fork of LDC1101 by Bob Giesberts

LDC1614.cpp

Committer:
bobgiesberts
Date:
2016-09-10
Revision:
32:9712c9bdaf44
Parent:
31:ab4354a71996
Child:
33:2f4c791f37b2

File content as of revision 32:9712c9bdaf44:

/** LDC1614 library
* @file LDC1614.cpp
* @brief this C++ file contains all required
* functions to interface with Texas
* Instruments' LDC1614.
*
* @author Bob Giesberts
*
* @date 2016-08-09
*
* @code
* Serial pc(USBTX, USBRX);
* LDC1614 ldc(PTC5, PTC4, PTC6, 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) );
*    }
* }
* @endcode
*/

#include "LDC1614.h"
#include "mbed_debug.h"

#include "i2c.hpp"


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;            // highest 16-bit of 32-bit number (so e.g. 100E6 / 65536 = 1525) 
    _Rcount = 0xffff;       // maximum for greatest precision
    _SettleCount = 500;     // CHx_SETTLECOUNT = t_settle * f_REFx/16 = 50 (p.12)
    _DriveCurrent = 20;     // max = 31, automatically settles at 20
       
    // start communication
    _i2c.setFrequency( 400000 ); // 400 kHz (p.6)
    
    // Initilialize the LDC1614
    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)    
    ************************************/
    
    // Configuring setup, set LDC in configuration modus
    sleep();
    
    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
    set( ERROR_CONFIG, UR_ERR2OUT,    1 );
    set( ERROR_CONFIG, OR_ERR2OUT,    1 );
    set( ERROR_CONFIG, WD_ERR2OUT,    1 );
    // set( ERROR_CONFIG, AH_ERR2OUT,    1 );
    // set( ERROR_CONFIG, AL_ERR2OUT,    1 );    
    
    // mux_config    
    set( MUX_CONFIG, AUTOSCAN_EN,     _channels > 1 );
    set( MUX_CONFIG, RR_SEQUENCE,     ( ( _channels - 2 > 0 ) ? _channels - 2 : 0 ) );
    set( MUX_CONFIG, DEGLITCH,        DEGLITCH_10M );
    
    // override Rp and use own Drive Current to reduce power consumption
    set( CONFIG, ACTIVE_CHAN,         0 ); // CH0. Will be overruled when _channels > 1
    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:

            // turn on LDC
            _shutdown_pin.write( 0 );
            wait_us(10);
            set( CONFIG, SLEEP_MODE_EN, mode );
            wait_us(377); // Wait 16384 f_INT clock cycles (0.377 ms) (p.16)
            wait_ms(1);
            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::shutdown( void ) { func_mode( LDC_MODE_SHUTDOWN ); }

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

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

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  = (( divIN <  15) ? (( divIN > 1) ? divIN  : 1) :  15 ); // 4 bit
    _dividerIN  = ((divREF < 255) ? ((divREF > 1) ? 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 = ((idrive < 31) ? 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 );
}

uint8_t LDC1614::get( ADDR addr, SETTING setting, uint8_t mask )
{
    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
        }    
    }
    
    uint16_t data[1]; 
    readI2C( data, addr ); 
    return ( data[0]>>setting ) & mask;    
}

uint16_t LDC1614::get_config()
{
    uint16_t data[1];
    readI2C( data, CONFIG );
    return data[0];
}

uint16_t LDC1614::get_error_config()
{
    uint16_t data[1];
    readI2C( data, ERROR_CONFIG );
    return data[0];
}


/* GETTING DATA FROM SENSOR */

uint16_t LDC1614::get_status( void )
{
    uint16_t status[1]; 
    readI2C( status, STATUS );  
    return status[0];
}
bool LDC1614::is_ready( uint8_t channel )
{
    uint8_t status = get_status();
    if( channel < 4 )
    {
        return ( status>>(3-channel)) & 1; // this specific channel is ready
    }else{
        return ( status>>DRDY ) & 1;       // all channels are ready
    }
}
bool LDC1614::is_error( uint8_t status )
{
    if( status == 17 ) { status = get_status(); }
    return ((( status>>ERR_ZC ) & 7) != 0);
}
uint8_t LDC1614::what_error( uint8_t channel )
{
    uint8_t status = get_status();
    if ( ( ( status>>ERR_CHAN ) & 2 ) == channel )
    {
        if ((( status>>ERR_AHE ) & 1) != 0) return 1; // Amplitude High Error
        if ((( status>>ERR_ALE ) & 1) != 0) return 2; // Amplitide Low Error
        if ((( status>>ERR_ZC  ) & 1) != 0) return 3; // Zero Count Error
    }
    return 0;                                     // no error?
}

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 );
    
    if( ((data[0]>>CHx_ERR_UR) & 1) == 1 ) { debug( "Sensor %d: Under-range Error\r\n", channel ); }
    if( ((data[0]>>CHx_ERR_OR) & 1) == 1 ) { debug( "Sensor %d: Over-range Error\r\n", channel ); }
    if( ((data[0]>>CHx_ERR_WD) & 1) == 1 ) { debug( "Sensor %d: Watchdog Timeout Error\r\n", channel ); }
    if( ((data[0]>>CHx_ERR_AE) & 1) == 1 ) { debug( "Sensor %d: Amplitude Error\r\n", channel ); }
    
    return ( (data[0] & 0x0fff)<<16 ) | data[1]; // MSB + LSB
}

uint16_t LDC1614::get_ID( void )
{
    uint16_t ID[1]; 
    readI2C( ID, DEVICE_ID, 1 );
    return ID[0];

}

uint16_t LDC1614::get_manufacturer_ID( void )
{
    // uint16_t ID[1]; 
    // readI2C( ID, MANUFACTURER_ID, 1 );
    // return ID[0];

    _i2c.start();
    
    // Write address + 0 (write)
    if( _i2c.write( 0x2A << 1 ) == true ){ // NACK = true
        _i2c.stop();
        return 1;
    }
    
    // Write register address
    if ( _i2c.write( 0x7E ) == true ) { // NACK = true
        _i2c.stop();
        return 2;
    }

    _i2c.start();

    // Write address + 1 (read)
    if ( _i2c.write( (0x2A << 1) | 0x01 ) == true ) { // NACK = true
        _i2c.stop();
        return 3;
    }
        
    uint16_t data;
    data = _i2c.read(1) << 8;   // ACK
    data |= _i2c.read(0);       // NACK
    _i2c.stop();
    return data;
    
    //return _i2c.read(1) << 8;    // MSB
    //return data[i] |= _i2c.read(0);        // LSB
    //_i2c.stop();


}


/* REGISTER FUNCTIONS (READ / WRITE)  */

void LDC1614::readI2C( uint16_t *data, uint8_t address, uint8_t length )
{
    for( int i = 0; i < length; i++ )
    {
        _i2c.start();
        _i2c.write( ( 0x2A << 1 ) | 0 ); // 7 bit 0x2A + 0 (write) = 0x55
        _i2c.write( address );

        _i2c.start();
        _i2c.write( ( 0x2A << 1 ) | 1 ); // 7 bit 0x2A + 1 (read) = 0x55

        data[i] = _i2c.read(1) << 8;    // MSB
        data[i] |= _i2c.read(0);        // LSB
        _i2c.stop();
    }        
}

void LDC1614::writeI2C( uint16_t *data, uint8_t address, uint8_t length )
{
   
    for ( int i = 0; i < length; i++ )
    {
        _i2c.start();
        _i2c.write( ( 0x2A << 1 ) | 0 );   // 7 bit 0x2A + 0 (write) = 0x54
        _i2c.write( address + i );
        
        _i2c.write( ( data[i] & 0xff00 ) >> 8 ); // MSB
        _i2c.write( ( data[i] & 0x00ff ) >> 0 ); // LSB
        _i2c.stop();
    }
    
    
}

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); // (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;}
    uint16_t LDC1614::get_Offset()          {return _Offset;}
    float LDC1614::get_cap()                {return _cap;}
// END ***********************************************************