/*
INA219.cpp - Class file for the INA219 Zero-Drift, Bi-directional Current/Power Monitor Arduino Library.

Version: 1.0.0
(c) 2014 Korneliusz Jarzebski
www.jarzebski.pl

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "INA219.h"
#include "i2c.h"

static uint16_t vShuntMax; // millivolt
static uint16_t vBusMax; // millivolt
static uint16_t rShunt; // milliohms

static uint8_t flagCNVR; // Conversion Ready
static uint8_t flagOVF; // Math Overflow Flag

static t_i2c_status status = I2C_SUCCESS;

/**
  * Configure I2C, INA219
  */
void INA219_Config(void) {
  uint16_t config;

  vBusMax = 32000; // !!! Maximum input voltage only 26V !!!
  vShuntMax = 80; // !! for shunt 10 mOhm and maximum 8A input current
  rShunt = CURRENT_SHUNT_RESISTANCE; // in mOhm

  /** INA219 configure */
  config  = INA219_RESET_OFF; // INA219_RST
  config |= INA219_RANGE_32V; // INA219_BVR
  config |= INA219_GAIN_80MV; // INA219_PG
  config |= INA219_BUS_RES_12BIT_128S; // INA219_BADC
  config |= INA219_SHUNT_RES_12BIT_128S; //INA219_SADC
  config |= INA219_MODE_SHUNT_BUS_CONT; //INA219_MODE

  i2c_wr_reg(INA219_ADDRESS, INA219_REG_CONFIG, config);

  i2c_wr_reg(INA219_ADDRESS, INA219_REG_CALIBRATION, CALIBRATION_VALUE);
}

/** */
uint16_t getMaxPossibleCurrent(void) {
  return (((1000 * vShuntMax) + (rShunt>>1)) / rShunt);
}

/** */
uint16_t getMaxCurrent(void) {
  uint16_t maxCurrent = ((CURRENT_LSB * 32767) + 500) / 1000;
  uint16_t maxPossible = getMaxPossibleCurrent();

  if (maxCurrent > maxPossible) {
    return maxPossible;
  } else {
    return maxCurrent;
  }
}

/** */
uint16_t getMaxShuntVoltage(void) {
  uint16_t maxVoltage = ((getMaxCurrent() * rShunt) + 500) / 1000;

  if (maxVoltage >= vShuntMax) {
    return vShuntMax;
  } else {
    return maxVoltage;
  }
}

/** */
uint32_t getMaxPower(void) {
  return (((getMaxCurrent() * vBusMax) + 500000) / 1000000);
}

/** */
uint16_t readBusCurrent(void) {
  uint32_t current;;
  int16_t tmp;

  status = i2c_rd_reg(INA219_ADDRESS, INA219_REG_CURRENT, (uint16_t *)&tmp);
  if (status != I2C_SUCCESS) {
    return 9999;
  }

  if (tmp < 0) {
    tmp = - tmp;
  }
  current = tmp;
  current *= CURRENT_LSB;
  current += 500;
  current /= 1000;

  return (uint16_t)current;
}

/** */
uint32_t readBusPower(void) {
  uint32_t power;
  uint16_t tmp;

  i2c_rd_reg(INA219_ADDRESS, INA219_REG_POWER, &tmp);
  power = tmp;
  power *= POWER_LSB;
  power += 500;
  power /= 1000;
  return power;
}

/**
  * Currently return raw value of shunt voltage in 10 uV
  */
int16_t readShuntVoltage(void) {
  uint16_t shvolt;
  status = i2c_rd_reg(INA219_ADDRESS, INA219_REG_SHUNTVOLTAGE, &shvolt);
  if (status != I2C_SUCCESS) {
    return 999;
  }
  return shvolt;
}

/**
  * Return bus voltage in mV
  */
uint16_t readBusVoltage(void) {
  uint16_t volt;

  flagCNVR = 0;
  flagOVF = 0;

  status = i2c_rd_reg(INA219_ADDRESS, INA219_REG_BUSVOLTAGE, &volt);
  if (status != I2C_SUCCESS) {
    return 65535;
  }

  if ((volt & 0x0001) != 0) {
    flagOVF = 1;
  }
  if ((volt & 0x0002) != 0) {
    flagCNVR = 1;
  }

  return ((volt >> 3) * 4);
}

ina219_bvr_t getRange(void) {
  uint16_t value;

  i2c_rd_reg(INA219_ADDRESS, INA219_REG_CONFIG, &value);
  value &= 0x2000;
  value >>= 13;

  return (ina219_bvr_t)value;
}

ina219_pg_t getGain(void) {
  uint16_t value;

  i2c_rd_reg(INA219_ADDRESS, INA219_REG_CONFIG, &value);
  value &= 0x1800;
  value >>= 11;

  return (ina219_pg_t)value;
}

ina219_badc_t getBusRes(void) {
  uint16_t value;

  i2c_rd_reg(INA219_ADDRESS, INA219_REG_CONFIG, &value);
  value &= 0x0780;
  value >>= 7;

  return (ina219_badc_t)value;
}

ina219_sadc_t getShuntRes(void) {
  uint16_t value;

  i2c_rd_reg(INA219_ADDRESS, INA219_REG_CONFIG, &value);
  value &= 0x0078;
  value >>= 3;

  return (ina219_sadc_t)value;
}

ina219_mode_t getMode(void) {
  uint16_t value;

  i2c_rd_reg(INA219_ADDRESS, INA219_REG_CONFIG, &value);
  value &= 0x0007;

  return (ina219_mode_t)value;
}
