/*
 * File: INA260.c
 *
 * Created: Created: Sunday August 2025 21:52:23
 *   Author: Chris
 */
#include "INA260.h"


// ============================================================================================
// Includes
#include "I2C_Master.h"
#include "EEPROM_M24C64.h"


// ============================================================================================
// Defines
#define INA260_I2CADDR_DEFAULT      0x40    ///< INA260 default i2c address

#define INA260_CONGIG_RST_BIT       15
#define INA260_CONGIG_AVG0_BIT      9
#define INA260_CONGIG_VBUSCT0_BIT   6
#define INA260_CONGIG_ISHCT0_BIT    3
#define INA260_CONGIG_MODE1_BIT     0

#define INA260_ENABLE_OCL_BIT       15
#define INA260_ENABLE_APOL_BIT      1
#define INA260_ENABLE_LEN_BIT       0

#define UINT8_ARR_TO_UINT16(_U8_)   ((uint16_t)(_U8_[0]) << 8) | (uint16_t)(_U8_[1])
#define UINT16_SWAP_BYTES(_U16_)    ((_U16_ << 8) | (_U16_ >> 8 ))


// ============================================================================================
// Typedefs
typedef enum _INA260_Mode {
  INA260_MODE_SHUTDOWN      = 0x00,     ///< SHUTDOWN: Minimize quiescient current and turn off current into the device inputs. Set another mode to exit shutown mode
  INA260_MODE_TRIGGERED     = 0x03,     ///< TRIGGERED: Trigger a one-shot measurement of current and bus voltage. Set the TRIGGERED mode again to take a new measurement
  INA260_MODE_CONTINUOUS    = 0x07,     ///< CONTINUOUS: (Default) Continuously update the current, bus voltage and power registers with new measurements
} INA260_MeasurementMode;

typedef enum _INA260_ConversionTime {
    INA260_TIME_140_us,     ///< Measurement time: 140us
    INA260_TIME_204_us,     ///< Measurement time: 204us
    INA260_TIME_332_us,     ///< Measurement time: 332us
    INA260_TIME_588_us,     ///< Measurement time: 588us
    INA260_TIME_1_1_ms,     ///< Measurement time: 1.1ms (Default)
    INA260_TIME_2_116_ms,   ///< Measurement time: 2.116ms
    INA260_TIME_4_156_ms,   ///< Measurement time: 4.156ms
    INA260_TIME_8_244_ms,   ///< Measurement time: 8.224ms
} INA260_ConversionTime;

typedef enum _count {
    INA260_COUNT_1,         ///< Window size: 1 sample (Default)
    INA260_COUNT_4,         ///< Window size: 4 samples
    INA260_COUNT_16,        ///< Window size: 16 samples
    INA260_COUNT_64,        ///< Window size: 64 samples
    INA260_COUNT_128,       ///< Window size: 128 samples
    INA260_COUNT_256,       ///< Window size: 256 samples
    INA260_COUNT_512,       ///< Window size: 512 samples
    INA260_COUNT_1024,      ///< Window size: 1024 samples
} INA260_AveragingCount;

typedef enum _INA260_AlertType {
    INA260_ALERT_CONVERSION_READY   = 0x01,     ///< Trigger on conversion ready
    INA260_ALERT_OVERPOWER          = 0x02,     ///< Trigger on power over limit
    INA260_ALERT_UNDERVOLTAGE       = 0x04,     ///< Trigger on bus voltage under limit
    INA260_ALERT_OVERVOLTAGE        = 0x08 ,    ///< Trigger on bus voltage over limit
    INA260_ALERT_UNDERCURRENT       = 0x10,     ///< Trigger on current under limit
    INA260_ALERT_OVERCURRENT        = 0x20,     ///< Trigger on current over limit
    INA260_ALERT_NONE               = 0x00      ///< Do not trigger alert pin (Default)
} INA260_AlertType;

typedef enum _INA260_AlertPolarity {
    INA260_ALERT_POLARITY_NORMAL      = 0x0,  ///< Active high open-collector (Default)
    INA260_ALERT_POLARITY_INVERTED    = 0x1   ///< Active low open-collector
} INA260_AlertPolarity;

typedef enum _INA260_AlertLatch {
    INA260_ALERT_LATCH_ENABLED        = 0x1,  ///< Alert will latch until Mask/Enable register is read
    INA260_ALERT_LATCH_TRANSPARENT    = 0x0   ///< Alert will reset when fault is cleared
} INA260_AlertLatch;


// ============================================================================================
// Variables
static uint16_t _INA260_BusVoltage_mV;
static uint16_t _INA260_Current_mA;
static uint16_t _INA260_Threshold;

static int32_t _Written_Current_Threshold;


// ============================================================================================
// Function Declarations
void INA260_Write_Register(uint8_t reg_address, uint16_t reg_data);
uint16_t INA260_Read_Register(uint8_t reg_address);


/*******************************************************************
    Functions
*******************************************************************/
void INA260_Init()
{
    _INA260_BusVoltage_mV = 0;
    _INA260_Current_mA = 0;
    _Written_Current_Threshold = -1;
    
    uint16_t Configuration_Register_Value_Reset = (1 << INA260_CONGIG_RST_BIT);
    INA260_Write_Register(INA260_REG_CONFIG, Configuration_Register_Value_Reset);

    

    uint16_t Enable_Register_Value = 
        (1 << INA260_ENABLE_OCL_BIT)    |   // Over Current Limit
        (0 << INA260_ENABLE_APOL_BIT)   |   // Alert Polarity Bit
        (1 << INA260_ENABLE_LEN_BIT);       // Alert Latch Enable

    uint16_t Configuration_Register_Value = 
        (0                      << INA260_CONGIG_RST_BIT)       |   // No Reset
        (INA260_COUNT_4         << INA260_CONGIG_AVG0_BIT)      |   // Averaging Count
        (INA260_TIME_8_244_ms   << INA260_CONGIG_VBUSCT0_BIT)   |   // Bus Voltage Conversion Time
        (INA260_TIME_8_244_ms   << INA260_CONGIG_ISHCT0_BIT)    |   // Shunt Current Conversion Time
        (INA260_MODE_CONTINUOUS << INA260_CONGIG_MODE1_BIT);        // Operating Mode

    sleep_ms(20);
    INA260_Update_Current_Threshold();
    sleep_ms(20);
    INA260_Write_Register(INA260_REG_CONFIG, Configuration_Register_Value);
    sleep_ms(20);
    INA260_Write_Register(INA260_REG_MASK_ENABLE, Enable_Register_Value);
}

void INA260_Update_Current_Threshold()
{
    if(_Written_Current_Threshold == _EEPROM_Content.Device_Configuration.Current_Threshold) {
        return;
    }

    INA260_Write_Register(INA260_REG_ALERT_LIMIT, (uint16_t)(_EEPROM_Content.Device_Configuration.Current_Threshold * 4));

    _Written_Current_Threshold = _EEPROM_Content.Device_Configuration.Current_Threshold;
}

void INA260_Read_BusVoltage()
{
    uint32_t INA260_BusVoltage = (uint32_t)INA260_Read_Register(INA260_REG_BUSVOLTAGE);

    _INA260_BusVoltage_mV = (int16_t)((INA260_BusVoltage * 1250) / 1000);
}

void INA260_Read_Current()
{
    uint32_t INA260_Current = (uint32_t)INA260_Read_Register(INA260_REG_CURRENT);

    _INA260_Current_mA = (int16_t)((INA260_Current * 1250) / 1000);
}

void INA260_Read_Threshold()
{
    uint16_t INA260_Threshold = (uint16_t)INA260_Read_Register(INA260_REG_ALERT_LIMIT);

    _INA260_Threshold = (uint16_t)(INA260_Threshold);
}

uint16_t INA260_Get_BusVoltage_mV()
{
    return _INA260_BusVoltage_mV;
}

uint16_t INA260_Get_Current_mA()
{
    return _INA260_Current_mA;
}

uint16_t INA260_Get_Threshold()
{
    return _INA260_Threshold;
}


/*******************************************************************
    Internal Functions
*******************************************************************/
void INA260_Write_Register(uint8_t reg_address, uint16_t reg_data)
{
    uint16_t Swapped_Data = UINT16_SWAP_BYTES(reg_data);
    I2CM_Packet_Transmit(INA260_I2CADDR_DEFAULT, reg_address, 1, (uint8_t*)(&Swapped_Data), 2);
}

uint16_t INA260_Read_Register(uint8_t reg_address)
{
    uint8_t Receive_Data[2];
    
    I2CM_Packet_Receive(INA260_I2CADDR_DEFAULT, reg_address, 1, Receive_Data, 2);

    return UINT8_ARR_TO_UINT16(Receive_Data);
}