A compilation of some hardware sensors and their shared programming interfaces.

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers INA219.cpp Source File

INA219.cpp

00001 /* INA219.cpp
00002  * Tested with mbed board: FRDM-KL46Z
00003  * Author: Mark Gottscho
00004  * mgottscho@ucla.edu
00005  */
00006  
00007 #include "mbed.h"
00008 #include "I2CSensor.h"
00009 #include "INA219.h"
00010  
00011 INA219::INA219 (PinName sda, PinName scl, int i2c_addr) :
00012                                     I2CSensor(sda, scl, i2c_addr),
00013                                     PeriodicSensor(0.1), //default max sampling rate of 10Hz
00014                                     __bus_voltage_range_32V(true),
00015                                     __shunt_amp_gain(8),
00016                                     __shunt_resolution(12),
00017                                     __shunt_num_samples_in_average(0),
00018                                     __bus_resolution(12),
00019                                     __bus_num_samples_in_average(0),
00020                                     __active(true),
00021                                     __measure_shunt_voltage(true),
00022                                     __measure_bus_voltage(true),
00023                                     __continuous_measurements(true),
00024                                     __shunt_voltage(0),
00025                                     __bus_voltage(0),
00026                                     __power(0),
00027                                     __shunt_current(0),
00028                                     __calibration(0),
00029                                     __current_div(1),
00030                                     __power_div(1)
00031                     {
00032      
00033 }
00034 
00035 INA219::~INA219() { }
00036 
00037 void INA219::selfInit() {
00038     __i2c.frequency(400000);
00039     reset();
00040     __calibrate();
00041 }
00042 
00043 void INA219::reset() {
00044     uint16_t data = CONFIG_RST_MASK;
00045     setRegister16b(CONFIG, data);
00046     wait(0.1);
00047     
00048     //reset private data to match device default state
00049     __bus_voltage_range_32V = true;
00050     __shunt_amp_gain = 8;
00051     __shunt_resolution = 12;
00052     __shunt_num_samples_in_average = 0;
00053     __bus_resolution = 12;
00054     __bus_num_samples_in_average = 0;
00055     __active = true;
00056     __measure_shunt_voltage = true;
00057     __measure_bus_voltage = true;
00058     __continuous_measurements = true;
00059     __shunt_voltage = 0;
00060     __bus_voltage = 0;
00061     __power = 0;
00062     __shunt_current = 0;
00063     __calibration = 0;
00064     __current_div = 1;
00065     __power_div = 1;
00066 }
00067 
00068 void INA219::setBusVoltageRange32V(bool enable) {
00069     uint16_t data = getRegister16b(CONFIG);
00070     if (enable) {
00071         data |= CONFIG_BRNG_MASK; //set bit
00072         __bus_voltage_range_32V = true;
00073     }
00074     else {
00075         data &= ~CONFIG_BRNG_MASK; //clear bit
00076         __bus_voltage_range_32V = false;
00077     }
00078     setRegister16b(CONFIG, data);
00079 }
00080 
00081 bool INA219::isBusVoltageRange32V () {
00082     return __bus_voltage_range_32V;
00083 }
00084 
00085 void INA219::setShuntAmpGain(unsigned int gain) {
00086     uint16_t tmp = 0;
00087     switch (gain) {
00088         case 1:
00089             __shunt_amp_gain = 1;
00090             break; //PG = b00
00091         case 2:
00092             tmp |= (CONFIG_PG_MASK & (1 << 11)); //PG = b01
00093             __shunt_amp_gain = 2;
00094             break;
00095         case 4:
00096             tmp |= (CONFIG_PG_MASK & (2 << 11)); //PG = b10
00097             __shunt_amp_gain = 4;
00098             break;
00099         case 8:
00100             tmp |= (CONFIG_PG_MASK & (3 << 11)); //PG = b11
00101             __shunt_amp_gain = 8;
00102             break;
00103         default:
00104             return; //bad input, do nothing
00105     }
00106     uint16_t data = getRegister16b(CONFIG);
00107     data = (data & (~CONFIG_PG_MASK)) | tmp; //Set the gain bits
00108     setRegister16b(CONFIG, data);
00109 }
00110 
00111 unsigned int INA219::getShuntAmpGain() {
00112     return __shunt_amp_gain;
00113 }
00114 
00115 void INA219::setADCResolutionAndAveraging(bool shunt, bool resolution, unsigned int value) {
00116     uint16_t code = 0;
00117     uint16_t resolution_tmp;
00118     uint16_t num_samples_in_average_tmp;
00119     
00120     if (resolution) {
00121         switch (value) {
00122             case 9:
00123                 resolution_tmp = 9;
00124                 code = 0x00; //b0000 0X00
00125                 break; 
00126             case 10:
00127                 resolution_tmp = 10;
00128                 code = 0x01; //b0000 0X01
00129                 break;
00130             case 11:
00131                 resolution_tmp = 11;
00132                 code = 0x02; //b0000 0X10
00133                 break;
00134             case 12:
00135                 resolution_tmp = 12;
00136                 code = 0x03; //b0000 0X11, default
00137                 break;
00138             default:
00139                 return; //bad input, do nothing
00140         } 
00141         num_samples_in_average_tmp = 0; 
00142     } else { //sample averaging
00143         switch (value) {
00144             case 2:
00145                 num_samples_in_average_tmp = 2;
00146                 code = 0x09; //b0000 1001
00147                 break; 
00148             case 4:
00149                 num_samples_in_average_tmp = 4;
00150                 code = 0x0A; //b0000 1010
00151                 break; 
00152             case 8:
00153                 num_samples_in_average_tmp = 8;
00154                 code = 0x0B; //b0000 1011
00155                 break;
00156             case 16:
00157                 num_samples_in_average_tmp = 16;
00158                 code = 0x0C; //b0000 1100
00159                 break;
00160             case 32:
00161                 num_samples_in_average_tmp = 32;
00162                 code = 0x0D; //b0000 1101
00163                 break;
00164             case 64:
00165                 num_samples_in_average_tmp = 64;
00166                 code = 0x0E; //b0000 1110
00167                 break;
00168             case 128:
00169                 num_samples_in_average_tmp = 128;
00170                 code = 0x0F; //b0000 1111
00171                 break;
00172             default:
00173                 return; //bad input, do nothing
00174         } 
00175         resolution_tmp = 0; 
00176     }
00177     
00178     //Now set the actual bits based on whether it is shunt or bus setting
00179     uint16_t data = getRegister16b(CONFIG);
00180     if (shunt) { //shunt ADC
00181         data &= ~CONFIG_SADC_MASK; //clear SADC bits
00182         data |= (code << 3); //set SADC bits
00183         __shunt_resolution = resolution_tmp;
00184         __shunt_num_samples_in_average = num_samples_in_average_tmp;
00185     } else { //bus ADC
00186         data &= ~CONFIG_BADC_MASK; //clear BADC bits
00187         data |= (code << 7); //set BADC bits
00188         __bus_resolution = resolution_tmp;
00189         __bus_num_samples_in_average = num_samples_in_average_tmp;
00190     }
00191     
00192     setRegister16b(CONFIG, data); //update the register
00193 }
00194 
00195 unsigned int INA219::getADCResolutionAndAveraging(bool shunt, bool &resolution) {
00196     if (shunt) { //get shunt ADC setting
00197         if (__shunt_resolution == 0) { //sample averaging
00198             resolution = false;
00199             return __shunt_num_samples_in_average;
00200         } else if (__shunt_num_samples_in_average == 0) { //resolution
00201             resolution = true;
00202             return __shunt_resolution;
00203         } else { //this should never happen
00204             return 0; //to keep compiler happy, this should never get reached
00205         }
00206     }
00207     else {//get bus ADC setting
00208         if (__bus_resolution == 0) { //sample averaging
00209             resolution = false;
00210             return __bus_num_samples_in_average;
00211         } else if (__bus_num_samples_in_average == 0) { //resolution
00212             resolution = true;
00213             return __bus_resolution;
00214         } else { //this should never happen
00215             return 0; //to keep compiler happy, this should never get reached
00216         }
00217     }
00218 }
00219 
00220 void INA219::setMode(bool shuntVoltage, bool busVoltage, bool continuous) {
00221     uint16_t code = 0;
00222     
00223     //Handle all 8 cases
00224     if (continuous) {
00225         code += 4;
00226         __continuous_measurements = true;
00227     } else
00228         __continuous_measurements = false;
00229     if (busVoltage) {
00230         code += 2;
00231         __measure_bus_voltage = true;
00232     } else
00233         __measure_bus_voltage = false;
00234     if (shuntVoltage) {
00235         code += 1;
00236         __measure_shunt_voltage = true;
00237     } else
00238         __measure_shunt_voltage = false;
00239         
00240     uint16_t data = getRegister16b(CONFIG);
00241     data &= ~CONFIG_MODE_MASK; //Clear mode bits
00242     data |= (CONFIG_MODE_MASK & code); //Set mode bits
00243     setRegister16b(CONFIG, data);
00244 }
00245 
00246 void INA219::getMode(bool &shuntVoltage, bool &busVoltage, bool &continuous) {
00247     shuntVoltage = __measure_shunt_voltage;
00248     busVoltage = __measure_bus_voltage;
00249     continuous = __continuous_measurements;
00250 }
00251 
00252 
00253 int16_t INA219::getShuntVoltage(bool sampleNow) {
00254      __disable_irq();
00255     if (sampleNow)
00256         __shunt_voltage = (int16_t) getRegister16b(SHUNT_VOLTAGE);
00257     __dataReady = false;
00258     __enable_irq();
00259     
00260     return __shunt_voltage;
00261 }
00262 
00263 float INA219::getShuntVoltageFloat(bool sampleNow) {
00264     return getShuntVoltage(sampleNow) * SHUNT_VOLTAGE_DIV;
00265 }
00266 
00267 int16_t INA219::getBusVoltage(bool sampleNow) {
00268     __disable_irq();
00269     if (sampleNow) {
00270         uint16_t tmp = getRegister16b(BUS_VOLTAGE);
00271         tmp &= BUS_VOLTAGE_BD_MASK; //Clear the irrelevant bits
00272         int16_t data = (((int16_t) tmp) >> 3); //extract bits and keep sign from arithmetic right shift
00273         __bus_voltage = data;
00274     }
00275     __dataReady = false;
00276     __enable_irq();
00277     
00278     return __bus_voltage;
00279 }
00280 
00281 float INA219::getBusVoltageFloat(bool sampleNow) {
00282     return getBusVoltage(sampleNow) * BUS_VOLTAGE_DIV;
00283 }
00284 
00285 int16_t INA219::getPower(bool sampleNow) {
00286     __disable_irq();
00287     if (sampleNow) {
00288         uint16_t unsignedPower = getRegister16b(POWER); //The INA219 does not internally track sign of power, only current and voltage, so we need to do it
00289         __shunt_current = (int16_t) getRegister16b(CURRENT);
00290         if (__shunt_current < 0) //check sign
00291             __power = -((int16_t) unsignedPower);
00292         else
00293             __power = (int16_t) unsignedPower;
00294     }
00295     __dataReady = false;
00296     __enable_irq();
00297     
00298     return __power;
00299 }
00300 
00301 float INA219::getPowerFloat(bool sampleNow) {
00302     return getPower(sampleNow) * __power_div;
00303 }
00304 
00305 int16_t INA219::getCurrent(bool sampleNow) {
00306     __disable_irq();
00307     if (sampleNow)
00308         __shunt_current = (int16_t) getRegister16b(CURRENT);   
00309     __dataReady = false;
00310     __enable_irq();
00311     
00312     return __shunt_current;
00313 }
00314 
00315 float INA219::getCurrentFloat(bool sampleNow) {
00316     return getCurrent(sampleNow) * __current_div;
00317 }
00318 
00319 uint16_t INA219::getCalibration() {
00320     __disable_irq();
00321     __calibration = getRegister16b(CURRENT);   
00322     __enable_irq();
00323     
00324     return __calibration;
00325 }
00326 
00327 void INA219::__calibrate() {
00328     /*float vbus_max = 6; //5V supply, so guardband to 6V just in case
00329     float rshunt = 0.1; //0.1 Ohm CSR
00330     float curr_max = 0.6; //600 mA worst case
00331     float vshunt_max = rshunt * curr_max;
00332     float curr_max_expected = 0.5; //500 mA expected worst case
00333     float min_lsb = 0.00001525925474; //curr_max_expected / 32767
00334     float max_lsb = 0.00012207031250; //curr_max_expected / 4096*/
00335     __current_div = 0.000016; //round up min_lsb to a nice number, in this case 16 uA
00336     __power_div = __current_div * 20;
00337     uint16_t calibration = 25600 & 0xFFFE; //trunc(0.04096 / (sel_lsb * Rshunt))
00338     setRegister16b(CALIBRATION, calibration);
00339     __calibration = calibration;
00340 }
00341 
00342 void INA219::__sample_data_ISR() {
00343     getBusVoltage(true);
00344     getShuntVoltage(true);
00345     getCurrent(true);
00346     getPower(true);
00347     __dataReady = true;
00348 }