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

Revision:
0:8d34cc2ff388
Child:
1:15396cab58d1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/INA219.cpp	Sun Mar 16 01:48:59 2014 +0000
@@ -0,0 +1,354 @@
+/* INA219.cpp
+ * Tested with mbed board: FRDM-KL46Z
+ * Author: Mark Gottscho
+ * mgottscho@ucla.edu
+ */
+ 
+#include "mbed.h"
+#include "I2CSensor.h"
+#include "INA219.h"
+#include "Utility.h"
+
+extern Utility G_util;
+ 
+INA219::INA219(PinName sda, PinName scl, int i2c_addr) :
+                                    I2CSensor(sda, scl, i2c_addr),
+                                    PeriodicSensor(0.1), //default max sampling rate of 10Hz
+                                    __bus_voltage_range_32V(true),
+                                    __shunt_amp_gain(8),
+                                    __shunt_resolution(12),
+                                    __shunt_num_samples_in_average(0),
+                                    __bus_resolution(12),
+                                    __bus_num_samples_in_average(0),
+                                    __active(true),
+                                    __measure_shunt_voltage(true),
+                                    __measure_bus_voltage(true),
+                                    __continuous_measurements(true),
+                                    __shunt_voltage(0),
+                                    __bus_voltage(0),
+                                    __power(0),
+                                    __shunt_current(0),
+                                    __calibration(0),
+                                    __current_div(1),
+                                    __power_div(1)
+                    {
+     
+}
+
+INA219::~INA219() { }
+
+void INA219::selfInit() {
+    __i2c.frequency(400000);
+    reset();
+    __calibrate();
+}
+
+void INA219::reset() {
+    uint16_t data = CONFIG_RST_MASK;
+    setRegister16b(CONFIG, data);
+    wait(0.1);
+    
+    //reset private data to match device default state
+    __bus_voltage_range_32V = true;
+    __shunt_amp_gain = 8;
+    __shunt_resolution = 12;
+    __shunt_num_samples_in_average = 0;
+    __bus_resolution = 12;
+    __bus_num_samples_in_average = 0;
+    __active = true;
+    __measure_shunt_voltage = true;
+    __measure_bus_voltage = true;
+    __continuous_measurements = true;
+    __shunt_voltage = 0;
+    __bus_voltage = 0;
+    __power = 0;
+    __shunt_current = 0;
+    __calibration = 0;
+    __current_div = 1;
+    __power_div = 1;
+}
+
+void INA219::setBusVoltageRange32V(bool enable) {
+    uint16_t data = getRegister16b(CONFIG);
+    if (enable) {
+        data |= CONFIG_BRNG_MASK; //set bit
+        __bus_voltage_range_32V = true;
+    }
+    else {
+        data &= ~CONFIG_BRNG_MASK; //clear bit
+        __bus_voltage_range_32V = false;
+    }
+    setRegister16b(CONFIG, data);
+}
+
+bool INA219::isBusVoltageRange32V() {
+    return __bus_voltage_range_32V;
+}
+
+void INA219::setShuntAmpGain(unsigned int gain) {
+    uint16_t tmp = 0;
+    switch (gain) {
+        case 1:
+            __shunt_amp_gain = 1;
+            break; //PG = b00
+        case 2:
+            tmp |= (CONFIG_PG_MASK & (1 << 11)); //PG = b01
+            __shunt_amp_gain = 2;
+            break;
+        case 4:
+            tmp |= (CONFIG_PG_MASK & (2 << 11)); //PG = b10
+            __shunt_amp_gain = 4;
+            break;
+        case 8:
+            tmp |= (CONFIG_PG_MASK & (3 << 11)); //PG = b11
+            __shunt_amp_gain = 8;
+            break;
+        default:
+            return; //bad input, do nothing
+    }
+    uint16_t data = getRegister16b(CONFIG);
+    data = (data & (~CONFIG_PG_MASK)) | tmp; //Set the gain bits
+    setRegister16b(CONFIG, data);
+}
+
+unsigned int INA219::getShuntAmpGain() {
+    return __shunt_amp_gain;
+}
+
+void INA219::setADCResolutionAndAveraging(bool shunt, bool resolution, unsigned int value) {
+    uint16_t code = 0;
+    uint16_t resolution_tmp;
+    uint16_t num_samples_in_average_tmp;
+    
+    if (resolution) {
+        switch (value) {
+            case 9:
+                resolution_tmp = 9;
+                code = 0x00; //b0000 0X00
+                break; 
+            case 10:
+                resolution_tmp = 10;
+                code = 0x01; //b0000 0X01
+                break;
+            case 11:
+                resolution_tmp = 11;
+                code = 0x02; //b0000 0X10
+                break;
+            case 12:
+                resolution_tmp = 12;
+                code = 0x03; //b0000 0X11, default
+                break;
+            default:
+                return; //bad input, do nothing
+        } 
+        num_samples_in_average_tmp = 0; 
+    } else { //sample averaging
+        switch (value) {
+            case 2:
+                num_samples_in_average_tmp = 2;
+                code = 0x09; //b0000 1001
+                break; 
+            case 4:
+                num_samples_in_average_tmp = 4;
+                code = 0x0A; //b0000 1010
+                break; 
+            case 8:
+                num_samples_in_average_tmp = 8;
+                code = 0x0B; //b0000 1011
+                break;
+            case 16:
+                num_samples_in_average_tmp = 16;
+                code = 0x0C; //b0000 1100
+                break;
+            case 32:
+                num_samples_in_average_tmp = 32;
+                code = 0x0D; //b0000 1101
+                break;
+            case 64:
+                num_samples_in_average_tmp = 64;
+                code = 0x0E; //b0000 1110
+                break;
+            case 128:
+                num_samples_in_average_tmp = 128;
+                code = 0x0F; //b0000 1111
+                break;
+            default:
+                return; //bad input, do nothing
+        } 
+        resolution_tmp = 0; 
+    }
+    
+    //Now set the actual bits based on whether it is shunt or bus setting
+    uint16_t data = getRegister16b(CONFIG);
+    if (shunt) { //shunt ADC
+        data &= ~CONFIG_SADC_MASK; //clear SADC bits
+        data |= (code << 3); //set SADC bits
+        __shunt_resolution = resolution_tmp;
+        __shunt_num_samples_in_average = num_samples_in_average_tmp;
+    } else { //bus ADC
+        data &= ~CONFIG_BADC_MASK; //clear BADC bits
+        data |= (code << 7); //set BADC bits
+        __bus_resolution = resolution_tmp;
+        __bus_num_samples_in_average = num_samples_in_average_tmp;
+    }
+    
+    setRegister16b(CONFIG, data); //update the register
+}
+
+unsigned int INA219::getADCResolutionAndAveraging(bool shunt, bool &resolution) {
+    if (shunt) { //get shunt ADC setting
+        if (__shunt_resolution == 0) { //sample averaging
+            resolution = false;
+            return __shunt_num_samples_in_average;
+        } else if (__shunt_num_samples_in_average == 0) { //resolution
+            resolution = true;
+            return __shunt_resolution;
+        } else { //this should never happen
+            G_util.panic("INA219 had an illegal shunt ADC setting", -1);
+            return 0; //to keep compiler happy, this should never get reached
+        }
+    }
+    else {//get bus ADC setting
+        if (__bus_resolution == 0) { //sample averaging
+            resolution = false;
+            return __bus_num_samples_in_average;
+        } else if (__bus_num_samples_in_average == 0) { //resolution
+            resolution = true;
+            return __bus_resolution;
+        } else { //this should never happen
+            G_util.panic("INA219 had an illegal bus ADC setting", -1);
+            return 0; //to keep compiler happy, this should never get reached
+        }
+    }
+}
+
+
+void INA219::setMode(bool shuntVoltage, bool busVoltage, bool continuous) {
+    uint16_t code = 0;
+    
+    //Handle all 8 cases
+    if (continuous) {
+        code += 4;
+        __continuous_measurements = true;
+    } else
+        __continuous_measurements = false;
+    if (busVoltage) {
+        code += 2;
+        __measure_bus_voltage = true;
+    } else
+        __measure_bus_voltage = false;
+    if (shuntVoltage) {
+        code += 1;
+        __measure_shunt_voltage = true;
+    } else
+        __measure_shunt_voltage = false;
+        
+    uint16_t data = getRegister16b(CONFIG);
+    data &= ~CONFIG_MODE_MASK; //Clear mode bits
+    data |= (CONFIG_MODE_MASK & code); //Set mode bits
+    setRegister16b(CONFIG, data);
+}
+
+void INA219::getMode(bool &shuntVoltage, bool &busVoltage, bool &continuous) {
+    shuntVoltage = __measure_shunt_voltage;
+    busVoltage = __measure_bus_voltage;
+    continuous = __continuous_measurements;
+}
+
+
+int16_t INA219::getShuntVoltage(bool sampleNow) {
+     __disable_irq();
+    if (sampleNow)
+        __shunt_voltage = (int16_t) getRegister16b(SHUNT_VOLTAGE);
+    __dataReady = false;
+    __enable_irq();
+    
+    return __shunt_voltage;
+}
+
+float INA219::getShuntVoltageFloat(bool sampleNow) {
+    return getShuntVoltage(sampleNow) * SHUNT_VOLTAGE_DIV;
+}
+
+int16_t INA219::getBusVoltage(bool sampleNow) {
+    __disable_irq();
+    if (sampleNow) {
+        uint16_t tmp = getRegister16b(BUS_VOLTAGE);
+        tmp &= BUS_VOLTAGE_BD_MASK; //Clear the irrelevant bits
+        int16_t data = (((int16_t) tmp) >> 3); //extract bits and keep sign from arithmetic right shift
+        __bus_voltage = data;
+    }
+    __dataReady = false;
+    __enable_irq();
+    
+    return __bus_voltage;
+}
+
+float INA219::getBusVoltageFloat(bool sampleNow) {
+    return getBusVoltage(sampleNow) * BUS_VOLTAGE_DIV;
+}
+
+int16_t INA219::getPower(bool sampleNow) {
+    __disable_irq();
+    if (sampleNow) {
+        uint16_t unsignedPower = getRegister16b(POWER); //The INA219 does not internally track sign of power, only current and voltage, so we need to do it
+        __shunt_current = (int16_t) getRegister16b(CURRENT);
+        if (__shunt_current < 0) //check sign
+            __power = -((int16_t) unsignedPower);
+        else
+            __power = (int16_t) unsignedPower;
+    }
+    __dataReady = false;
+    __enable_irq();
+    
+    return __power;
+}
+
+float INA219::getPowerFloat(bool sampleNow) {
+    return getPower(sampleNow) * __power_div;
+}
+
+int16_t INA219::getCurrent(bool sampleNow) {
+    __disable_irq();
+    if (sampleNow)
+        __shunt_current = (int16_t) getRegister16b(CURRENT);   
+    __dataReady = false;
+    __enable_irq();
+    
+    return __shunt_current;
+}
+
+float INA219::getCurrentFloat(bool sampleNow) {
+    return getCurrent(sampleNow) * __current_div;
+}
+
+uint16_t INA219::getCalibration() {
+    __disable_irq();
+    __calibration = getRegister16b(CURRENT);   
+    __enable_irq();
+    
+    return __calibration;
+}
+
+void INA219::__calibrate() {
+    /*float vbus_max = 6; //5V supply, so guardband to 6V just in case
+    float rshunt = 0.1; //0.1 Ohm CSR
+    float curr_max = 0.6; //600 mA worst case
+    float vshunt_max = rshunt * curr_max;
+    float curr_max_expected = 0.5; //500 mA expected worst case
+    float min_lsb = 0.00001525925474; //curr_max_expected / 32767
+    float max_lsb = 0.00012207031250; //curr_max_expected / 4096*/
+    __current_div = 0.000016; //round up min_lsb to a nice number, in this case 16 uA
+    __power_div = __current_div * 20;
+    uint16_t calibration = 25600 & 0xFFFE; //trunc(0.04096 / (sel_lsb * Rshunt))
+    setRegister16b(CALIBRATION, calibration);
+    __calibration = calibration;
+}
+
+void INA219::__sample_data_ISR() {
+    getBusVoltage(true);
+    getShuntVoltage(true);
+    getCurrent(true);
+    getPower(true);
+    __dataReady = true;
+}
\ No newline at end of file