/*
 Linear Technology DC2343A Demonstration Board.
 Driver File for LTC3335 Nanopower Buck-Boost DC/DC with Integrated Coulomb Counter.
 All datasheet references in this file refer to Linear Technology document 3335p.pdf

 @verbatim
    Demonstration Circuit DC2343A is a complete system level solution for a Nanopower Buck-Boost
    DC/DC with Integrated Coulomb Counter.  The DC2343A contains a PIC16F1459 embedded processor
    for communication to the PC over USB and the LTC3335 via its I2C port.  The GUI is capable of
    reading and writing all the control registers on the LTC3335 as well as displaying and
    resetting all its alarm registers.  The firmware and software for the embedded system and the GUI
    are available at the LTC3335 solutions page located at http://www.linear.com/product/LTC3335#demoboards.
    The DC2343A uses two Analog-to-Digital Convert-er channels to sample the Battery and Output voltages.
    The voltage samples improve the functionality of the GUI, and allow optimal software correction to the
    measured coulombs.  By adding the software correction the first order known errors are compensated
    for over the operating range and the resultant coulomb count is accurate to within 3%.

    The DC2343A contains an LTC3335 which is a high efficiency, low quiescent current (680nA) buck-boost
    DC/DC converter with an integrated precision cou-lomb counter which monitors accumulated battery discharge
    in long life battery powered applications. The buck-boost can operate down to 1.8V on its input and
    provides eight pin selectable output voltages with up to 50mA of output current. The coulomb counter
    stores the accumulated battery discharge in an internal register accessible via an I2C interface. The
    LTC3335 features a programmable discharge alarm threshold. When the threshold is reached, an interrupt
    is generated at the IRQ pin. To accommodate a wide range of battery types and sizes, the peak input
    current can be selected from as low as 5mA to as high as 250mA and the full-scale coulomb counter has a
    range from 1.1mAh (with 5mA IPEAK) to 1793Ah (with 250mA IPEAK).
 @endverbatim

 http://www.linear.com/product/LTC3335

 REVISION HISTORY
 $Revision: 816 $
 $Date: 2014-10-13 13:07:16 -0400 (Mon, 13 Oct 2014) $

 Copyright (c) 2015, Linear Technology Corp.(LTC)
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 1. Redistributions of source code must retain the above copyright notice, this
    list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 The views and conclusions contained in the software and documentation are those
 of the authors and should not be interpreted as representing official policies,
 either expressed or implied, of Linear Technology Corp.

*/

/*! @file
    Firmware Driver Code File for LTC3335 Nanopower Buck-Boost DC/DC with Integrated Coulomb Counter.

    This driver was designed to provide the following services to application code:
       1. Perform the basic register commands necessary to access the features of the LTC3335.
       2. Minimize I2C communication in an effort to minimize power consumption.
       3. Use the Counter Test feature of the LTC3335 to provide a real time battery current measurement.

    Software correction was not included in this driver due to Program Memory limitations in the PIC16F1459.
    See the LTC3335.vb file in with the DC2343A GUI source code for an example of software correction of the
    LTC3335 coulomb count.
*/

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Includes
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#include "adi_ltc3335.h"
#include <drivers/i2c/adi_i2c.h>

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Definitions
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

//! @name LTC3335 Register Definitions
//! @{
//! Register Map from datasheet table 3
#define ADI_LTC3335_REGISTER_A      01 //!< VOUT selection and prescaler selection register.
#define ADI_LTC3335_REGISTER_B      02 //!< Alarm threshold register.
#define ADI_LTC3335_REGISTER_C      03 //!< Accumulator register.
#define ADI_LTC3335_REGISTER_D      04 //!< Alarms register.
#define ADI_LTC3335_REGISTER_E      05 //!< Status register.
#define ADI_LTC3335_INVALID         0xFF //!< code to indicate that cached data has not be set yet.
//! @}
// \endcond

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Global Data
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Local Data
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

//! @name Cached LTC3335 Data
//! @{
//! Cached copies of values in registers, so that they do not need to be read/modified/written each time a function is called to only set a portion of that register.

static uint8_t                     ltc3335_prescaler_last;                  //!<  last prescaler selected.

static ADI_I2C_HANDLE              LTC3335_I2C;
uint8_t                            LTC3335_I2C_DevMem[ADI_I2C_MEMORY_SIZE];
//! @}

/*
//! @name Counter Test Variables
//! Variables used to count edges per second when the Counter Test feature is turned on, providing an instantaneous measurement of the battery current.
static uint16_t ltc3335_hw_timer_last;                                 //!<  last value of the hardware timer accessed by LTC3335 driver.
static uint16_t ltc3335_hw_counter_last;                               //!<  last value of the hardware counter accessed by LTC3335 driver.
static bool     ltc3335_counter_test_last;                             //!<  value of the counter test bit last written to the LTC3335.
static uint32_t ltc3335_counter_test_edge_count;                       //!<  the number of rising edges on the /IRQ pin since the Counter Test results were last cleared.
static uint32_t ltc3335_counter_test_time;                             //!<  the amount of timer ticks since the Counter Test results were last cleared.
//! @}
*/

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Local Prototypes
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

static bool     ltc3335_set_register(uint8_t subaddress, uint8_t* ltc3335_data);
static bool     ltc3335_get_register(uint8_t subaddress, uint8_t* ltc3335_data);
static uint8_t  ltc3335_encode_register_a(bool enabled, adi_ltc3335_outputVoltage_t voltage, uint8_t prescaler);
static void     ltc3335_report_error(uint8_t subaddress, bool set);

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Global Functions
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

// Initializes the LTC3335 driver.
void adi_ltc3335_Init(void)
{
    // Init the local variables to INVALID, so that they will be read/modified/written upon first attempt
    // to write to their registers.  After this cached values will be used.s
    ltc3335_prescaler_last                 = ADI_LTC3335_INVALID;

    // Reset Counter Test and read LTC3335 to determine if Counter Test was on when PIC woke up.
    // adi_ltc3335_Reset_Counter_Test();
    // adi_ltc3335_Get_Counter_Test(&ltc3335_counter_test_last);
	
	  // Init I2C peripheral
	  adi_i2c_Open(0, LTC3335_I2C_DevMem, ADI_I2C_MEMORY_SIZE, &LTC3335_I2C);
	  adi_i2c_SetBitRate(LTC3335_I2C, ADI_LTC3335_BAUD_RATE);
	  adi_i2c_SetSlaveAddress(LTC3335_I2C, ADI_LTC3335_BASE_ADDRESS);
	//adi_i2c_SetSlaveAddress(LTC3335_I2C, 0x02);

    return;
}

// Enables/Disables software control of the LTC3335 output voltage.
// If software control is enabled, the voltage is set to the specified setting.
bool adi_ltc3335_Set_Voltage(bool enabled, adi_ltc3335_outputVoltage_t voltage)
{
    bool    success;
    uint8_t ltc3335_data;

    // Can not set out of range.
    if(ADI_LTC3335_NUM_OUTPUT_VOLTAGES <= voltage)
    {
        return false;
    }

    // Send command to set the voltage.
    ltc3335_data = ltc3335_encode_register_a(enabled, voltage, ltc3335_prescaler_last);
    success      = ltc3335_set_register(ADI_LTC3335_REGISTER_A, &ltc3335_data);

    return success;
}

// Sets the specified prescaler setting for the LTC3335.
bool adi_ltc3335_Set_Prescaler(uint8_t prescaler)
{
    bool    success;
    uint8_t ltc3335_data;

    // Can not set out of range.
    if(ADI_LTC3335_PRESCALER_MAX < prescaler)
    {
        return false;
    }

    // Send command to set the voltage.
    ltc3335_data = ltc3335_encode_register_a(false, ADI_LTC3335_OUTPUT_VOLTAGE_2_8V, prescaler);
    success      = ltc3335_set_register(ADI_LTC3335_REGISTER_A, &ltc3335_data);

    return success;
}

// Sets the specified alarm threshold for the LTC3335.
bool adi_ltc3335_Set_Alarm_Threshold(uint8_t threshold)
{
     return ltc3335_set_register(ADI_LTC3335_REGISTER_B, &threshold);
}

// Sets the specified accumulator value for the LTC3335.
bool adi_ltc3335_Set_Accumulator(uint8_t accumulator)
{
    return ltc3335_set_register(ADI_LTC3335_REGISTER_C, &accumulator);
}

// Gets the specified accumulator value from the LTC3335.
bool adi_ltc3335_Get_Accumulator(uint8_t* accumulator)
{
    bool success;
    uint8_t ltc3335_data;

    success = ltc3335_get_register(ADI_LTC3335_REGISTER_C, &ltc3335_data);

    if(success == true)
    {
        *accumulator = ltc3335_data;
    }

    return success;
}

// Gets the alarms active from the LTC3335.
bool adi_ltc3335_Get_Alarms(adi_ltc3335_alarm_t* alarms)
{
    bool    success;
    uint8_t ltc3335_data;

    success = ltc3335_get_register(ADI_LTC3335_REGISTER_D, &ltc3335_data);

    if(success == true)
    {			
        if(ltc3335_data & 0x01) // bit0
        {
            alarms->ac_on_time_overflow = 1;
        }
        else
        {
            alarms->ac_on_time_overflow = 0;
        }

        if(ltc3335_data & 0x02) // bit1
        {
            alarms->coulomb_counter_overflow = 1;
        }
        else
        {
            alarms->coulomb_counter_overflow = 0;
        }

        if(ltc3335_data & 0x04) //bit2
        {
            alarms->alarm_trip = 1;
        }
        else
        {
            alarms->alarm_trip = 0;
        }
    }

    return success;
}

// Sends the command to clear the INT condition.
// NOTE!  Additional registers will also need to be rewritten in order for the INT condition
//        to not be immediately reset.
bool adi_ltc3335_Clear_Int(void)
{
    bool    success = true;
    uint8_t ltc3335_data = 1;

	// TODO: ENABLE THIS, WHEN USING COUNTER TEST
    // If currently doing a Counter Test, do not turn it off when clearing the int.
	/*
    if(ltc3335_counter_test_last == true)
    {
        ltc3335_data |= 2;
    }
*/
    success &= ltc3335_set_register(ADI_LTC3335_REGISTER_E, &ltc3335_data);

    return success;
}

/*
// Enables/Disables the LTC3335 Counter Test feature.
bool adi_ltc3335_Set_Counter_Test(bool enabled)
{
    bool    success;
    uint8_t ltc3335_data;

    // Send command to start the test.
    ltc3335_data = ((enabled == true) ? 0x02 : 0);
    success      = ltc3335_set_register(ADI_LTC3335_REGISTER_E, &ltc3335_data);

    // Store results of the test so that we don't need to poll the IC for that info.
    if(success == true)
    {
        ltc3335_counter_test_last = enabled;
    }

    // Initialize after the command is sent, as the first edge that occurs
    // when the test is started should not be counted.
    if(enabled == true)
    {
        // Take initial readings when test is started.
        adi_ltc3335_Counter_Test_Task();
    }
    else
    {
        // Reset test results when test is stopped.
        adi_ltc3335_Reset_Counter_Test();
    }

    return success;
}

// Gets whether the LTC3335 Counter Test feature is Enabled/Disabled.
// Note that this function get the value from the LTC3335.
bool adi_ltc3335_Get_Counter_Test(bool* enabled)
{
    bool    success;
    uint8_t ltc3335_data;

    success = ltc3335_get_register(ADI_LTC3335_REGISTER_E, &ltc3335_data);

    if(success == true)
    {
        ltc3335_counter_test_last = ((ltc3335_data & 0x02) == 0) ? false : true;
        *enabled                  = ltc3335_counter_test_last;
    }

    return success;
}

// Gets whether the LTC3335 Counter Test feature is Enabled/Disabled.
// Note that this function get the cached value of what was last read from the LTC3335.
// Gets the number of edges and the amount of time since the Counter Test
// was last reset, which allows edges/sec to be calculated.  The edges/sec is proportional to
// battery current.
bool adi_ltc3335_Get_Counter_Test_Results(bool* enabled, uint32_t* edge_count, uint32_t* time)
{
    bool    success = true;
    uint8_t ltc3335_data;

    *enabled    = ltc3335_counter_test_last;
    *edge_count = ltc3335_counter_test_edge_count;
    *time       = ltc3335_counter_test_time;

    return success;
}

// Resets the number of edges and the amount of time stored for the Counter Test feature.
bool adi_ltc3335_Reset_Counter_Test(void)
{
    ltc3335_counter_test_edge_count = 0;
    ltc3335_counter_test_time       = 0;
    ltc3335_hw_timer_last           = PIC16F_Timer_Get16();
    ltc3335_hw_counter_last         = PIC16F_Counter_Get();

    return true;
}

// Task that must be run periodically, for the edges and time to be stored for the
// LTC3335 Counter Test feature.
void adi_ltc3335_Counter_Test_Task(void)
{
    if (ltc3335_counter_test_last == true)
    {
        // Get new timer and counter values
        uint16_t hw_timer_new   = PIC16F_Timer_Get16();
        uint16_t hw_counter_new = PIC16F_Counter_Get();

        // Add difference between last timer and counter to summations.
        ltc3335_counter_test_edge_count += (hw_counter_new - ltc3335_hw_counter_last);
        ltc3335_counter_test_time       += (hw_timer_new - ltc3335_hw_timer_last);

        // Save new timer and counter values for next execution of task.
        ltc3335_hw_timer_last   = hw_timer_new;
        ltc3335_hw_counter_last = hw_counter_new;
    }

    return;
}
*/

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Local Functions
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

//! Writes to an LTC3335 register.
static bool ltc3335_set_register(uint8_t subaddress, uint8_t* ltc3335_data)
{
    bool                success     = true;
	  uint32_t            hwErr;
	  uint8_t             data[2] = {subaddress, *ltc3335_data};
		ADI_I2C_TRANSACTION transaction = {
			.pPrologue     = NULL,    
			.nPrologueSize = 0,
			.pData         = data,
			.nDataSize     = 2,
			.bReadNotWrite = false,
			.bRepeatStart  = false  // ignored in write operation
		};		
	
		if (adi_i2c_ReadWrite(LTC3335_I2C, &transaction, &hwErr) != ADI_I2C_SUCCESS)
		{
			  success = false;
        ltc3335_report_error(subaddress, true);
		}

    return success;
}

//! Reads from a LTC3335 register.
static bool ltc3335_get_register(uint8_t subaddress, uint8_t* ltc3335_data)
{
    bool                success     = true;
	  uint32_t            hwErr;
		ADI_I2C_TRANSACTION transaction = {
			.pPrologue     = &subaddress,    
			.nPrologueSize = 1,
			.pData         = ltc3335_data,
			.nDataSize     = 1,
			.bReadNotWrite = true,
			.bRepeatStart  = true
		};		
	
		if (adi_i2c_ReadWrite(LTC3335_I2C, &transaction, &hwErr) != ADI_I2C_SUCCESS)
		{
			  success = false;
        ltc3335_report_error(subaddress, true);
		}

    return success;
}

//! Encodes elements of register A.
static uint8_t ltc3335_encode_register_a(bool enabled, adi_ltc3335_outputVoltage_t voltage, uint8_t prescaler)
{
    return ((enabled == true) ? (1 << 7) : 0) | (voltage << 4) | prescaler;
}

//! Saves error bytes associated with a failed I2C transaction.
static void ltc3335_report_error(uint8_t subaddress, bool set)
{   
    // REPORT ERROR
	  ERR_MSG("LTC3335 I2C error, subaddr: %u, write: %b\n", subaddress, set);
		LTC3335_I2C_ERR();

    return;
}
