123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- /*
- Arduino library for INA3221 current and voltage sensor.
- MIT License
- Copyright (c) 2020 Beast Devices, Andrejs Bondarevs
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- */
- #include "INA3221.h"
- // Configuration register
- typedef struct {
- uint16_t mode_shunt_en:1;
- uint16_t mode_bus_en:1;
- uint16_t mode_continious_en:1;
- uint16_t shunt_conv_time:3;
- uint16_t bus_conv_time:3;
- uint16_t avg_mode:3;
- uint16_t ch3_en:1;
- uint16_t ch2_en:1;
- uint16_t ch1_en:1;
- uint16_t reset:1;
- } conf_reg_t;
- // Mask/Enable register
- typedef struct {
- uint16_t conv_ready:1;
- uint16_t timing_ctrl_alert:1;
- uint16_t pwr_valid_alert:1;
- uint16_t warn_alert_ch3:1;
- uint16_t warn_alert_ch2:1;
- uint16_t warn_alert_ch1:1;
- uint16_t shunt_sum_alert:1;
- uint16_t crit_alert_ch3:1;
- uint16_t crit_alert_ch2:1;
- uint16_t crit_alert_ch1:1;
- uint16_t crit_alert_latch_en:1;
- uint16_t warn_alert_latch_en:1;
- uint16_t shunt_sum_en_ch3:1;
- uint16_t shunt_sum_en_ch2:1;
- uint16_t shunt_sum_en_ch1:1;
- uint16_t reserved:1;
- } masken_reg_t;
- // I2C buffers
- static uint8_t rx_data[4];
- static uint8_t tx_data[4];
- // Value of Mask/Enable register.
- static masken_reg_t _masken_reg;
- // Reads 16 bytes from a register.
- static void _read(ina3221_t * ina, const ina3221_reg_t reg, uint16_t *val);
- // Writes 16 bytes to a register.
- static void _write(ina3221_t * ina, const ina3221_reg_t reg, uint16_t *val);
- static void _read(ina3221_t * ina, ina3221_reg_t reg, uint16_t *val) {
- msg_t status = MSG_OK;
- sysinterval_t tmo = TIME_MS2I(4);
- /* read in burst mode */
- tx_data[0] = (uint8_t)reg;
- i2cAcquireBus(ina->i2cd);
- status = i2cMasterTransmitTimeout(ina->i2cd, ina->i2c_addr, tx_data, 1, rx_data, 2, tmo);
- i2cReleaseBus(ina->i2cd);
-
- if (status != MSG_OK) {
- *val = 0xFFFF;
- }
- // data in rx_data[]
- *val = rx_data[0] << 8;
- *val |= rx_data[1];
- }
- static void _write(ina3221_t * ina, ina3221_reg_t reg, uint16_t *val) {
- msg_t status = MSG_OK;
- sysinterval_t tmo = TIME_MS2I(4);
- tx_data[0] = (uint8_t)reg;
- tx_data[1] = (uint8_t)(*val >> 8);
- tx_data[2] = (uint8_t)(*val & 0xff);
- /* sending */
- i2cAcquireBus(ina->i2cd);
- status = i2cMasterTransmitTimeout(ina->i2cd, ina->i2c_addr, tx_data, 3, NULL, 0, tmo);
- i2cReleaseBus(ina->i2cd);
- if (status != MSG_OK) {
- *val = 0xFFFE;
- }
- }
- uint16_t INA3221_getReg(ina3221_t * ina, ina3221_reg_t reg) {
- uint16_t val = 0;
- _read(ina, reg, &val);
- return val;
- }
- void INA3221_reset(ina3221_t * ina) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.reset = 1;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setModePowerDown(ina3221_t * ina) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.mode_bus_en = 0;
- conf_reg.mode_continious_en =0 ;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setModeContinious(ina3221_t * ina) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.mode_continious_en =1;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setModeTriggered(ina3221_t * ina) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.mode_continious_en = 0;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setShuntMeasEnable(ina3221_t * ina) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.mode_shunt_en = 1;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setShuntMeasDisable(ina3221_t * ina) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.mode_shunt_en = 0;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setBusMeasEnable(ina3221_t * ina) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.mode_bus_en = 1;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setBusMeasDisable(ina3221_t * ina) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.mode_bus_en = 0;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setAveragingMode(ina3221_t * ina, ina3221_avg_mode_t mode) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.avg_mode = mode;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setBusConversionTime(ina3221_t * ina, ina3221_conv_time_t convTime) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.bus_conv_time = convTime;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setShuntConversionTime(ina3221_t * ina, ina3221_conv_time_t convTime) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- conf_reg.shunt_conv_time = convTime;
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setPwrValidUpLimit(ina3221_t * ina, int16_t voltagemV) {
- _write(ina, INA3221_REG_PWR_VALID_HI_LIM, (uint16_t*)&voltagemV);
- }
- void INA3221_setPwrValidLowLimit(ina3221_t * ina, int16_t voltagemV) {
- _write(ina, INA3221_REG_PWR_VALID_LO_LIM, (uint16_t*)&voltagemV);
- }
- void INA3221_setShuntSumAlertLimit(ina3221_t * ina, int32_t voltageuV) {
- int16_t val = voltageuV / 20;
- _write(ina, INA3221_REG_SHUNTV_SUM_LIM, (uint16_t*)&val);
- }
- void INA3221_setCurrentSumAlertLimit(ina3221_t * ina, int32_t currentmA) {
- int16_t shuntuV = currentmA * (int32_t)ina->shuntRes[INA3221_CH1];
- INA3221_setShuntSumAlertLimit(ina, shuntuV);
- }
- void INA3221_setWarnAlertLatchEnable(ina3221_t * ina) {
- masken_reg_t masken_reg;
- _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- masken_reg.warn_alert_latch_en = 1;
- _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- _masken_reg = masken_reg;
- }
- void INA3221_setWarnAlertLatchDisable(ina3221_t * ina) {
- masken_reg_t masken_reg;
- _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- masken_reg.warn_alert_latch_en = 1;
- _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- _masken_reg = masken_reg;
- }
- void INA3221_setCritAlertLatchEnable(ina3221_t * ina) {
- masken_reg_t masken_reg;
- _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- masken_reg.crit_alert_latch_en = 1;
- _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- _masken_reg = masken_reg;
- }
- void INA3221_setCritAlertLatchDisable(ina3221_t * ina) {
- masken_reg_t masken_reg;
- _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- masken_reg.crit_alert_latch_en = 1;
- _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- _masken_reg = masken_reg;
- }
- void INA3221_readFlags(ina3221_t * ina) {
- _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&_masken_reg);
- }
- bool INA3221_getTimingCtrlAlertFlag(void) {
- return _masken_reg.timing_ctrl_alert;
- }
- bool INA3221_getPwrValidAlertFlag(void) {
- return _masken_reg.pwr_valid_alert;
- }
- bool INA3221_getCurrentSumAlertFlag(void) {
- return _masken_reg.shunt_sum_alert;
- }
- bool INA3221_getConversionReadyFlag(void) {
- return _masken_reg.conv_ready;
- }
- uint16_t INA3221_getManufID(ina3221_t * ina) {
- uint16_t id = 0;
- _read(ina, INA3221_REG_MANUF_ID, &id);
- return id;
- }
- uint16_t INA3221_getDieID(ina3221_t * ina) {
- uint16_t id = 0;
- _read(ina, INA3221_REG_DIE_ID, &id);
- return id;
- }
- void INA3221_setChannelEnable(ina3221_t * ina, ina3221_ch_t channel) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- switch(channel){
- case INA3221_CH1:
- conf_reg.ch1_en = 1;
- break;
- case INA3221_CH2:
- conf_reg.ch2_en = 1;
- break;
- case INA3221_CH3:
- conf_reg.ch3_en = 1;
- break;
- default:
- break;
- }
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setChannelDisable(ina3221_t * ina, ina3221_ch_t channel) {
- conf_reg_t conf_reg;
- _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- switch(channel){
- case INA3221_CH1:
- conf_reg.ch1_en = 0;
- break;
- case INA3221_CH2:
- conf_reg.ch2_en = 0;
- break;
- case INA3221_CH3:
- conf_reg.ch3_en = 0;
- break;
- default:
- break;
- }
- _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
- }
- void INA3221_setWarnAlertShuntLimit(ina3221_t * ina, ina3221_ch_t channel, int32_t voltageuV) {
- ina3221_reg_t reg;
- int16_t val = 0;
- switch(channel){
- case INA3221_CH1:
- reg = INA3221_REG_CH1_WARNING_ALERT_LIM;
- break;
- case INA3221_CH2:
- reg = INA3221_REG_CH2_WARNING_ALERT_LIM;
- break;
- case INA3221_CH3:
- reg = INA3221_REG_CH3_WARNING_ALERT_LIM;
- break;
- default:
- break;
- }
- val = voltageuV / 5;
- _write(ina, reg, (uint16_t*)&val);
- }
- void INA3221_setCritAlertShuntLimit(ina3221_t * ina, ina3221_ch_t channel, int32_t voltageuV) {
- ina3221_reg_t reg;
- int16_t val = 0;
- switch(channel){
- case INA3221_CH1:
- reg = INA3221_REG_CH1_CRIT_ALERT_LIM;
- break;
- case INA3221_CH2:
- reg = INA3221_REG_CH2_CRIT_ALERT_LIM;
- break;
- case INA3221_CH3:
- reg = INA3221_REG_CH3_CRIT_ALERT_LIM;
- break;
- default:
- break;
- }
- val = voltageuV / 5;
- _write(ina, reg, (uint16_t*)&val);
- }
- void INA3221_setWarnAlertCurrentLimit(ina3221_t * ina, ina3221_ch_t channel, int32_t currentmA) {
- int32_t shuntuV = 0;
- shuntuV = currentmA * (int32_t)ina->shuntRes[channel];
- INA3221_setWarnAlertShuntLimit(ina, channel, shuntuV);
- }
- void INA3221_setCritAlertCurrentLimit(ina3221_t * ina, ina3221_ch_t channel, int32_t currentmA) {
- int32_t shuntuV = 0;
- shuntuV = currentmA * (int32_t)ina->shuntRes[channel];
- INA3221_setCritAlertShuntLimit(ina, channel, shuntuV);
- }
- void INA3221_setCurrentSumEnable(ina3221_t * ina, ina3221_ch_t channel) {
- masken_reg_t masken_reg;
- _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- switch(channel){
- case INA3221_CH1:
- masken_reg.shunt_sum_en_ch1 = 1;
- break;
- case INA3221_CH2:
- masken_reg.shunt_sum_en_ch2 = 1;
- break;
- case INA3221_CH3:
- masken_reg.shunt_sum_en_ch3 = 1;
- break;
- default:
- break;
- }
- _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- _masken_reg = masken_reg;
- }
- void INA3221_setCurrentSumDisable(ina3221_t * ina, ina3221_ch_t channel) {
- masken_reg_t masken_reg;
- _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- switch(channel){
- case INA3221_CH1:
- masken_reg.shunt_sum_en_ch1 = 0;
- break;
- case INA3221_CH2:
- masken_reg.shunt_sum_en_ch2 = 0;
- break;
- case INA3221_CH3:
- masken_reg.shunt_sum_en_ch3 = 0;
- break;
- default:
- break;
- }
- _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
- _masken_reg = masken_reg;
- }
- int32_t INA3221_getShuntVoltage(ina3221_t * ina, ina3221_ch_t channel) {
- int32_t res;
- ina3221_reg_t reg;
- uint16_t val_raw = 0;
- switch(channel){
- case INA3221_CH1:
- reg = INA3221_REG_CH1_SHUNTV;
- break;
- case INA3221_CH2:
- reg = INA3221_REG_CH2_SHUNTV;
- break;
- case INA3221_CH3:
- reg = INA3221_REG_CH3_SHUNTV;
- break;
- default:
- break;
- }
- _read(ina, reg, &val_raw);
- res = (int16_t)val_raw;
- res *= 5; // 1 LSB = 5uV
- return res;
- }
- bool INA3221_getWarnAlertFlag(ina3221_t * ina, ina3221_ch_t channel) {
- (void)ina;
- bool ret;
- switch(channel) {
- case INA3221_CH1:
- return _masken_reg.warn_alert_ch1;
- break;
- case INA3221_CH2:
- return _masken_reg.warn_alert_ch2;
- break;
- case INA3221_CH3:
- return _masken_reg.warn_alert_ch3;
- break;
- default:
- break;
- }
- return ret;
- }
- bool INA3221_getCritAlertFlag(ina3221_t * ina, ina3221_ch_t channel) {
- (void)ina;
- bool ret;
- switch(channel) {
- case INA3221_CH1:
- ret = _masken_reg.crit_alert_ch1;
- break;
- case INA3221_CH2:
- ret = _masken_reg.crit_alert_ch2;
- break;
- case INA3221_CH3:
- ret = _masken_reg.crit_alert_ch3;
- break;
- default:
- break;
- }
- return ret;
- }
- int32_t INA3221_estimateOffsetVoltage(ina3221_t * ina, ina3221_ch_t channel, uint32_t busV) {
- uint32_t bias_in = 10; // Input bias current at IN– in uA
- uint32_t r_in = 670; // Input resistance at IN– in KOhm
- int32_t adc_step = 40; // smallest shunt ADC step in uV
- uint32_t shunt_res = ina->shuntRes[channel];
- uint32_t filter_res = ina->filterRes[channel];
- int32_t offset = 0;
- int32_t reminder;
- offset = (shunt_res + (filter_res * 1000) + 500) / 1000; // resistance in Ohm
- offset *= (((busV * 1000) + (r_in/2)) / r_in) + bias_in; // cuurent in uA
- offset -= bias_in * filter_res; // offset in uV
- // Round the offset to the closest shunt ADC value
- reminder = offset % adc_step;
- if (reminder < adc_step/2) {
- offset -= reminder;
- } else {
- offset += adc_step - reminder;
- }
- return offset;
- }
- int32_t INA3221_getCurrent(ina3221_t * ina, ina3221_ch_t channel) {
- int32_t shunt_uV = 0;
- int32_t current_A = 0;
- shunt_uV = INA3221_getShuntVoltage(ina, channel);
- current_A = shunt_uV / (int32_t)ina->shuntRes[channel];
- return current_A;
- }
- int32_t INA3221_getCurrentCompensated(ina3221_t * ina, ina3221_ch_t channel) {
- int32_t shunt_uV = 0;
- int32_t bus_V = 0;
- uint32_t current_A = 0;
- int32_t offset_uV = 0;
- shunt_uV = INA3221_getShuntVoltage(ina, channel);
- bus_V = INA3221_getVoltage(ina, channel);
- offset_uV = INA3221_estimateOffsetVoltage(ina, channel, bus_V);
- current_A = (shunt_uV - offset_uV) / (int32_t)ina->shuntRes[channel];
- return current_A;
- }
- uint32_t INA3221_getVoltage(ina3221_t * ina, ina3221_ch_t channel) {
- uint32_t voltage_V = 0;
- ina3221_reg_t reg;
- uint16_t val_raw = 0;
- switch(channel) {
- case INA3221_CH1:
- reg = INA3221_REG_CH1_BUSV;
- break;
- case INA3221_CH2:
- reg = INA3221_REG_CH2_BUSV;
- break;
- case INA3221_CH3:
- reg = INA3221_REG_CH3_BUSV;
- break;
- default:
- break;
- }
- _read(ina, reg, &val_raw);
- voltage_V = val_raw;
- return voltage_V;
- }
|