A compilation of some hardware sensors and their shared programming interfaces.
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Wed Jul 13 2022 19:24:41 by 1.7.2