// ****************************************************************************
// INCLUDE FILES: Header files of modules referenced by this module
// ****************************************************************************
#include <HealthData.h>
#include <Ad77x8Driver.h>
#include <Protocol_iSTP.h>
#include <delay.h>
#include <spi.h>


// ****************************************************************************
// PRIVATE TYPES: Private types used by this module only
// ****************************************************************************
typedef enum
{
    HD_3V3_RAIL_MV,
    HD_5V_RAIL_MV,
    HD_5V_SW_RAIL_MV,
    HD_3V3_B_RAIL_MV,
    HD_3V3_A_RAIL_MV,
    HD_3V3_C_RAIL_MV,
    HD_EXT_PWR_RAIL_MV,
    HD_EXT_PWR2_RAIL_MV
}HealthData_t;


// ****************************************************************************
// PRIVATE MACROS: Private #define directives used by this module only
// ****************************************************************************
#define HD_NUM_OF_HEALTH_DATA   (8)

#define HD_SINGLE_DATA_TO_MEASURE ( HD_3V3_A_RAIL_MV )
#define HD_USED_ADC_RANGE_MV      ( 2560 )

// ****************************************************************************
// PRIVATE VARIABLES: Variables used by this module only (static)
// ****************************************************************************
// HealthData module uses common settings of AD converter for all
// measurements. Such settings are defined by following variable
static const Ad77x8Opt_t hdADCoptions = 
{
    .ChannelConfiguration = AD77X8_OPT_CHANNEL_CFG_8_INPUTS,
    .ChopingDisabled = AD77X8_OPT_CHOPING_DISABLED,
    .CommonInputBuffered = AD77X8_OPT_COM_IN_UNBUFFERED,
    .OscilatorPowerDownEnabled = 0,
    .ReferenceSelection = 0,
};

// HealthData module uses common settings for all channels of AD converter.
// Such settings are defined by following variable
static const Ad77x8ChnlOpt_t hdADCchnlOptions = 
{
    .polarity = AD77X8_POLARITY_UNIPOLAR,
    .range = AD77X8_RANGE_2560_MV,
};

// Multiplier of measured ADC ratio (AIN counts / Max counts)
// to obtain result in milivolts. Multiplier contains also 
// ratio of the resistor divider.
// The value of 1.024 comes from the description of the AD converter.
// The values of 1.6, 2.4 etc... represents ratio of the resistor divider
// of the specific channel.
static const float hdADCratioMulitplier[] =
{
    1.024 * HD_USED_ADC_RANGE_MV * 1.6,
    1.024 * HD_USED_ADC_RANGE_MV * 2.4,
    1.024 * HD_USED_ADC_RANGE_MV * 2.4,
    1.024 * HD_USED_ADC_RANGE_MV * 1.6,
    1.024 * HD_USED_ADC_RANGE_MV * 1.6,
    1.024 * HD_USED_ADC_RANGE_MV * 1.6,
    1.024 * HD_USED_ADC_RANGE_MV * 13,
    1.024 * HD_USED_ADC_RANGE_MV * 13
};

static const Ad77x8Channel_t hdHealthDataToAdcChLkup[] = 
{
    AD77X8_CHANNEL_AIN1_AINCOM, // HD_3V3_RAIL_MV,
    AD77X8_CHANNEL_AIN2_AINCOM, // HD_5V_RAIL_MV,
    AD77X8_CHANNEL_AIN3_AINCOM, // HD_5V_SW_RAIL_MV,
    AD77X8_CHANNEL_AIN4_AINCOM, // HD_3V3_B_RAIL_MV,
    AD77X8_CHANNEL_AIN5_AINCOM, // HD_3V3_A_RAIL_MV,
    AD77X8_CHANNEL_AIN6_AINCOM, // HD_3V3_C_RAIL_MV,
    AD77X8_CHANNEL_AIN7_AINCOM, // HD_EXT_PWR_RAIL_MV,
    AD77X8_CHANNEL_AIN8_AINCOM  // HD_EXT_PWR2_RAIL_MV
};

#ifndef MAINBOARD54
static uint32_t lastRawResult = 0;
#endif

// ****************************************************************************
// PRIVATE FUNCTIONS PROTOTYPES: Functions used by this module only (static)
// ****************************************************************************
static void HDsendData(float * healthData);


// ****************************************************************************
// PUBLIC VARIABLES: Public variables shared with other modules
// ****************************************************************************


// ****************************************************************************
// PRIVATE FUNCTIONS: Private functions used by this module only
// ****************************************************************************
static void HDsendData(float * healthData)
{
    SendHealthDataMsg( (uint32_t*)healthData, HD_NUM_OF_HEALTH_DATA );
}

// ****************************************************************************
// PUBLIC FUNCTIONS: Public functions shared with other modules
// ****************************************************************************
uint8_t HDinit( void )
{
    uint8_t success = 1;

#ifndef MAINBOARD40 
    
    SPI_setDec( chip_adc );
    
    do 
    {
        success = Ad77x8DriverInit( &hdADCoptions );

        if( !success )
        {
            break;
        }

        success = Ad77x8DriverSetUpdateRate( 1024 );

        if( !success )
        {
            break;
        }

        Ad77x8DriverStartContinousConversion( hdHealthDataToAdcChLkup[HD_SINGLE_DATA_TO_MEASURE], &hdADCchnlOptions );

        // Wait for first conversion
        delay_ms(10);

    }while (0);

#endif // NOT MAINBOARD40
    
    return success;
}

bool HDcollectData( float * result, uint32_t * length )
{
    bool validReading;
    uint32_t resultsIterator = 0;
	
    SPI_setDec( chip_adc );
    

#ifdef MAINBOARD54
	validReading = true;
#else
	uint32_t rawResult;
	uint8_t status;
	Ad77x8DriverInitSPIOnly();
    // Read the latest measurement
    validReading = Ad77x8ReadRawConversionResult( &rawResult, &status );
    if( validReading )
    {
        // Trigger next measurement
        lastRawResult = rawResult;
    }
    else
    {
        rawResult = lastRawResult;
    }

    // Convert raw result to mV
    result[resultsIterator++] = ( ((float)rawResult) / (1 << 24) ) * hdADCratioMulitplier[HD_SINGLE_DATA_TO_MEASURE];
    result[resultsIterator++] = status;
#endif
	*length = resultsIterator;
	
    return validReading;
}

bool HDcollectDataFakeFifo( float * result, uint32_t * length, uint32_t nominalFifoRecords )
{
    uint32_t temporaryLength;
    uint8_t iteratorFifoRecs;
    uint8_t iteratorRecordLength;
    uint8_t iteratorResult;
    bool validReading;
    
    // Read health data (Adt7320 measurements) just once
    validReading = HDcollectData( result, &temporaryLength );
    // Remember length of one fifo record
    iteratorResult = temporaryLength;
    // One FIFO record is already in the buffer, so start iteration from 1
    for( iteratorFifoRecs = 1; iteratorFifoRecs < nominalFifoRecords; iteratorFifoRecs++ )
    {
        // Copy one record multiple times
        for( iteratorRecordLength = 0; iteratorRecordLength < temporaryLength; iteratorRecordLength++ )
        {
            // Resulting buffers still goes forward
            result[ iteratorResult++ ] = result[iteratorRecordLength];
        }
    }

    (*length) = (nominalFifoRecords * temporaryLength);

    return validReading;
}

void HDmain( void )
{
    static float result[HD_NUM_OF_HEALTH_DATA] = {0,0,0,0,0,0,0,0};
#ifndef MAINBOARD40 
    uint32_t length;
    
    // Configure the correct chip select line to be used for
    // AD7718 converter on main board
    SPI_setDec( chip_adc );
    
    // Collect the health data values
    HDcollectData(result, &length);

#endif // NOT MAINBOARD40
    
    // Send the health data
    HDsendData(result);
}