Library to communicate with LDC1614
Dependents: Inductive_Sensor_3
Fork of LDC1101 by
LDC1614.cpp
- Committer:
- bobgiesberts
- Date:
- 2016-09-21
- Revision:
- 34:b03d7bb9010c
- Parent:
- 33:2f4c791f37b2
- Child:
- 35:00c9c01f0c0f
File content as of revision 34:b03d7bb9010c:
/** 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" LDC1614::LDC1614(PinName sda, PinName scl, PinName sd, uint8_t f_CLKIN, int channels, uint8_t 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 (0xffff) _SettleCount = 50; // CHx_SETTLECOUNT = t_settle * f_REFx/16 = 50 (p.12) _DriveCurrent = 21; // max = 31, automatically settles at 20 _dividerREF = 2; // 2 (f_REF = f_CLKIN/2 = 40/2 = 20 < 35) (p.10) _dividerIN = 2; // 2 (f_IN = f_SENSOR/2 = 6.5/2 = 3.4 < 5 = 20/4 = f_REF/4 for(int i = 0; i < channels; i++) error[i] = 0; // start communication _i2c.setFrequency( 400000 ); // max 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 ** ** f_sensor_min = 6.4 MHz (d = inf) ** f_sensor_max = 10 MHz (d = 0) ** ************************************/ // 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 // settlecount > Q * f_REF / (16 * f_SENSOR) setSettlecount( i, _SettleCount ); // set Divider to 1 (for large range / ENOB / resolution) setDivider( i, _dividerIN, _dividerREF ); // set the drive current during sampling setDriveCurrent( i, _DriveCurrent ); // (p. 15 | Figure 14) // shift the signal down a bit setOffset( i, _Offset ); } // error_config (does not work?) 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 ); // debug("[ReferenceCount channel %d: 0x%02x]\r\n", channel, get_ReferenceCount( channel ) ); } 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 ) { 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 _dividerREF = ((divREF < 255) ? ((divREF > 1) ? divREF : 1) : 255 ); // 8 bit writeI2Cregister( CLOCK_DIVIDERS_CH0 + channel, uint16_t ((_dividerIN << CHx_FIN_DIVIDER) | (_dividerREF << CHx_FREF_DIVIDER)) ); } void LDC1614::setDriveCurrent( uint8_t channel, uint8_t idrive ) { _DriveCurrent = ((idrive < 31) ? idrive : 31 ); // 5-bit (b1 1111) regchange( DRIVE_CURRENT_CH0 + channel, CHx_IDRIVE, _DriveCurrent, 31 ); // debug("[DriveCurrent channel %d: 0x%02x]\r\n", channel, get_DriveCurrent( channel ) ); } 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 */ 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]; } uint16_t LDC1614::get_status( void ) { uint16_t data[1]; readI2C( data, STATUS ); return data[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 ) { // DOES NOT WORK PROPERLY YET!! // STATUS is reset after reading DATA_MSB_CHx if( status == 17 ) { status = get_status(); } return ((( status>>ERR_ZC ) & 7) != 0); } uint8_t LDC1614::what_error( uint8_t channel ) { // DOES NOT WORK PROPERLY YET!! // STATUS is reset after reading DATA_MSB_CHx 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; } uint8_t LDC1614::get_error( uint8_t channel ) { if( ( (error[channel]>>(CHx_ERR_UR - CHx_ERR_AE)) & 1) == 1 ) { debug( "Sensor %d: Under-range Error\r\n", channel ); } if( ( (error[channel]>>(CHx_ERR_OR - CHx_ERR_AE)) & 1) == 1 ) { debug( "Sensor %d: Over-range Error\r\n", channel ); } if( ( (error[channel]>>(CHx_ERR_WD - CHx_ERR_AE)) & 1) == 1 ) { debug( "Sensor %d: Watchdog Timeout Error\r\n", channel ); } if( ( (error[channel]>>(CHx_ERR_AE - CHx_ERR_AE)) & 1) == 1 ) { debug( "Sensor %d: Amplitude Error\r\n", channel ); } return error[channel]; } uint16_t LDC1614::get_ReferenceCount( uint8_t channel ) { uint16_t rcount[1]; readI2C( rcount, RCOUNT_CH0 + channel ); return rcount[0]; } uint8_t LDC1614::get_DriveCurrent( uint8_t channel ) { ADDR addr; switch ( channel ){ case 1: addr = DRIVE_CURRENT_CH1; break; case 2: addr = DRIVE_CURRENT_CH2; break; case 3: addr = DRIVE_CURRENT_CH3; break; default: addr = DRIVE_CURRENT_CH0; break; } return get( addr, CHx_IDRIVE, 31 ); } uint32_t LDC1614::get_Data( uint8_t channel ) { uint16_t data[2]; readI2C( data, DATA_MSB_CH0 + 2*channel, 2 ); error[channel] = ((data[0]>>CHx_ERR_AE) & 0x0f); // debug("[Error channel %d (0x%02X): 0x%01X] 0x%04X %04X\r\n", channel, DATA_MSB_CH0 + 2*channel, error[channel], data[0], data[1] ); return ( (data[0] & 0x0fff)<<16 ) | data[1]; // MSB + LSB } uint16_t LDC1614::get_device_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]; } /* REGISTER FUNCTIONS (READ / WRITE) */ void LDC1614::readI2C( uint16_t *data, uint8_t address, uint8_t length ) { for( int i = 0; i < length; i++ ) { // start sequence (Device ID + Register Address + write) _i2c.start(); _i2c.write( ( 0x2A << 1 ) | 0 ); _i2c.write( address + i ); // start sequence (Register Address + read) _i2c.start(); _i2c.write( ( 0x2A << 1 ) | 1 ); // Build up 16 bit result data[i] = _i2c.read(1) << 8; // MSB data[i] |= _i2c.read(0); // LSB // debug("Read from 0x%02X : 0x%04X\r\n", address + i, data[i]); // Stop command _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*1E6)/_dividerREF) * (LData / 268435456.0); // (p.14) return _fsensor; } float LDC1614::get_Inductance( uint32_t Ldata ) { float fsensor = get_fsensor( Ldata ); _inductance = 1.0 / ((_cap*1E-12) * 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 ***********************************************************