//******************************************************************************
//  Include(s) for External Component API(s)
//******************************************************************************
#include "spi.h"
#include "gpio.h"
#include "sensors_universal.h"

//******************************************************************************
// Include(s) for Common Component API(s)
//******************************************************************************

//******************************************************************************
// Include(s) for Internal Component API(s)
//******************************************************************************

#include "ad77x8.h"

//******************************************************************************
// Defines
//******************************************************************************

#define ADREG_COMM					   0x00		/**<       Communications Register   (WO, 8-bit)   */
#define ADREG_STATUS				   0x00		/**<       Status Register           (RO, 8-bit)   */
#define ADREG_MODE                     0x01		/**<       Mode Register             (RW, 8-bit)  */
#define ADREG_ADC_CONTROL              0x02		/**<       Adc Control Register      (RW, 8-bit)  */
#define ADREG_FILTER                   0x03
#define ADREG_ADC_DATA				   0x04
#define ADREG_ADC_OFFSET               0x05
#define ADREG_ADC_GAIN			       0x06
#define ADREF_IO_CONTROL               0x07
#define ADREG_ID                       0x0F


#define AD77X8_SPI_FREQ			    3000000
#define AD77X8_TIME_DELAY			0x01

//******************************************************************************
// Typedefs
//******************************************************************************

typedef struct Ad77x8ModeReg
{
  unsigned int md               : 3;
  unsigned int oscpd            : 1;
  unsigned int chcon            : 1;  
  unsigned int refsel           : 1;
  unsigned int negbuf           : 1;
  unsigned int chop			    : 1;
}Ad77x8ModeReg_t;

typedef struct Ad77x8AdcconReg
{
	unsigned int inputrange     : 3;
	unsigned int unibipolar     : 1;
	unsigned int adcchansel     : 4;
}Ad77x8AdcconReg_t;

typedef struct Ad77x8FilterReg
{
	unsigned int sfword         : 8;
}Ad77x8FilterReg_t;

typedef struct Ad77x8IOconReg
{
	unsigned int p1dat          : 1;
	unsigned int p2dat          : 1;
	unsigned int                : 2; // reserved	
	unsigned int p1dir          : 1;
	unsigned int p2dir          : 1;
	unsigned int                : 2; // reserved
}Ad77x8IOconReg_t;

typedef enum
{
	ADC_3V3,
	ADC_5V,
	ADC_5V_SW,
	ADC_3V3_B,
	ADC_3V3_A,
	ADC_3V3_C,
	ADC_EXT_PWR,
	ADC_EXT_PWR_2
} Ad77x8_channel_e;

typedef enum
{
	AD_CONVERTING,
	AD_CONVERTED
} Ad77x8_state_e;

//******************************************************************************
// Support Function Forward Declaration
//******************************************************************************
static inline unsigned int ad77x8Valid(sensor_select_e Decoder);
static inline uint8_t  ad77x8RawRead(uint8_t Address, sensor_select_e Decoder);
static inline uint32_t ad77x8RawReadData(sensor_select_e Decoder);
static inline uint8_t ad77x8SetChannel(Ad77x8_channel_e Channel, sensor_select_e Decoder);
static inline uint8_t ad77x8RDY(sensor_select_e Decoder);

//******************************************************************************
// Global Data
//******************************************************************************

//******************************************************************************
// Local Data
//******************************************************************************

//******************************************************************************
//! @fn unsigned int ad77x8Init(sensor_select_e Decoder, sensor_axis_e Axis, sensor_board_e Board)
//!
//! @description
//! A routine for initialization of ADC7718 in ATSAM4SD32C, which
//! returns value 1 in case that all values are written
//! to the sensors registers correctly, otherwise it returns 0
//!
//! @return   validity of written bytes to the ADC
//!
//! @calls    SPI_master_init()
//!           sensor_write_8bit()
//!			  ad77x8Valid()
//!
//! @req
//!
//! @example
//! initialize the ADC and returns validity of initialization
//! @code
//! unsigned int validity;
//! validity = ad77x8Init();
//! @endcode
//******************************************************************************
unsigned int ad77x8Init(sensor_select_e Decoder)
{
	Ad77x8ModeReg_t modereg = {0};
	Ad77x8AdcconReg_t adcconreg = {0};
	Ad77x8FilterReg_t filter = {0};
 
	unsigned int address, data;
	unsigned int validSignal = 1;

	SPI_master_init(AD77X8_SPI_FREQ, SPI_POL_0, SPI_PHA_0, SPI_8_BIT, 0);

	filter.sfword = 69; // Select fadc to 19.79Hz
	address = ADREG_FILTER; // Select filter reg
	data = *(unsigned int*)&filter;
  
	sensor_write_8bit(Decoder, address, data); // Set up mode register
	delay_ms(AD77X8_TIME_DELAY);

	adcconreg.adcchansel = 0; // Select channel 0
	adcconreg.unibipolar = 1; // Set unipolar mode
	adcconreg.inputrange = 7; // Select ADC input range = +-2,56V
	address = ADREG_ADC_CONTROL; // Select ADCCON reg
	data = *(unsigned int*)&adcconreg;
  
	sensor_write_8bit(Decoder, address, data); // Set up mode register
	delay_ms(AD77X8_TIME_DELAY);
  

	modereg.md = 3; // set up Continuous Conversion mode
	address = ADREG_MODE; // Select Mode register
	data = *(unsigned int*)&modereg;
  
	sensor_write_8bit(Decoder, address, data); // Set up mode register
	delay_ms(AD77X8_TIME_DELAY);


	//returns the validity of the stored data in the corresponding registers
	validSignal = ad77x8Valid(Decoder);

	return validSignal;
}

//******************************************************************************
//! @fn static inline unsigned int mpu6000Valid(sensor_select_e Decoder, sensor_axis_e Axis, sensor_board_e Board)
//!
//! @description
//! A routine for verification of correctness initialization to the sensors by
//! reading three selected registers from each sensor (sample rate, gyro scale, accel scale,
//! DLP filter)
//!
//! @return   validity (0 - error, 1 - OK)
//!
//! @calls    ad77x8RawRead()
//!           delay_ms()
//!
//! @req
//!
//! @example
//! establishment of validity
//! @code
//! unsigned int valid;
//! valid = ad77x8Valid();
//! @endcode
//******************************************************************************

static inline unsigned int ad77x8Valid(sensor_select_e Decoder)
{
	unsigned int buffer;
	const unsigned int modereg = 3;
	const unsigned int adcconreg = 15;
	const unsigned int filter = 69;

	unsigned int validation = 1;
 
	buffer = ad77x8RawRead (Decoder, (ADREG_FILTER | 0x40));
    validation = (((buffer & 0xFF) == filter) && (validation == 1)) ? 1 : 0;
	delay_ms(AD77X8_TIME_DELAY);
	
	buffer = ad77x8RawRead (Decoder, (ADREG_ADC_CONTROL | 0x40));
    validation = (((buffer & 0xFF) == adcconreg) && (validation == 1)) ? 1 : 0;
	delay_ms(AD77X8_TIME_DELAY);
	
	buffer = ad77x8RawRead (Decoder, (ADREG_MODE | 0x40));
    validation = (((buffer & 0xFF) == modereg) && (validation == 1)) ? 1 : 0;
	delay_ms(AD77X8_TIME_DELAY);
	
  return validation;
}

//******************************************************************************
//! @fn static inline uint16_t mpuRawRead(uint8_t Address, sensor_select_e Decoder, sensor_axis_e Axis, sensor_board_e Board)
//!
//! @description
//!
//!
//! @return   
//!
//! @calls    SPI_setDec()
//!			  SPI_setCs()
//!           SPI_master_write()
//!           SPI_master_read()
//!
//! @req
//!
//! @example
//!
//! @code
//!
//!
//! @endcode
//******************************************************************************
static inline uint8_t ad77x8RawRead(sensor_select_e Decoder, uint8_t Address)
{	
	SPI_setDec(Decoder);
    SPI_setCs();
	SPI_master_write(Address);

	//send dummy data to generate clock
	SPI_master_write(0);

	SPI_resetAllCs();
		
	return (uint8_t)SPI_master_read();
}

//******************************************************************************
//! @fn static inline uint16_t mpuRawRead(uint8_t Address, sensor_select_e Decoder, sensor_axis_e Axis, sensor_board_e Board)
//!
//! @description
//!
//!
//! @return   
//!
//! @calls    SPI_setDec()
//!			  SPI_setCs()
//!           SPI_master_write()
//!           SPI_master_read()
//!
//! @req
//!
//! @example
//!
//! @code
//!
//!
//! @endcode
//******************************************************************************
static inline uint32_t ad77x8RawReadData(sensor_select_e Decoder)
{	
	uint32_t temp = 0;

	SPI_setDec(Decoder);
    SPI_setCs();
	SPI_master_write(ADREG_ADC_DATA | 0x40);

	SPI_master_write(0);
	temp = (SPI_master_read() << 16);
	SPI_master_write(0);
	temp |= (SPI_master_read() << 8);
	SPI_master_write(0);
	temp |= SPI_master_read();

	SPI_resetAllCs();
		
	return (uint32_t)temp;
}

//******************************************************************************
//! @fn static inline uint8_t ad77x8RDY(sensor_select_e Decoder, sensor_axis_e Axis, sensor_board_e Board)
//!
//! @description
//!
//!
//! @return   
//!
//! @calls    SPI_setDec()
//!			  SPI_setCs()
//!           SPI_master_write()
//!           SPI_master_read()
//!
//! @req
//!
//! @example
//!
//! @code
//!
//!
//! @endcode
//******************************************************************************
static inline uint8_t ad77x8RDY(sensor_select_e Decoder)
{	
	return ((ad77x8RawRead(Decoder, (ADREG_STATUS | 0x40)) & 0xFF) == 0x81) ? AD_CONVERTED : AD_CONVERTING;
}

//******************************************************************************
//! @fn static inline uint8_t ad77x8SetChannel(Ad77x8_channel_e Channel, sensor_select_e Decoder, sensor_axis_e Axis, sensor_board_e Board)
//!
//! @description
//!
//!
//! @return   
//!
//! @calls    SPI_setDec()
//!			  SPI_setCs()
//!           SPI_master_write()
//!           SPI_master_read()
//!
//! @req
//!
//! @example
//!
//! @code
//!
//!
//! @endcode
//******************************************************************************
static inline uint8_t ad77x8SetChannel(Ad77x8_channel_e Channel, sensor_select_e Decoder)
{	

	Ad77x8AdcconReg_t adcconreg = {0};
	unsigned int address, data;
	
	adcconreg.adcchansel = Channel; //
	adcconreg.unibipolar = 1; // Set unipolar mode
	adcconreg.inputrange = 7; // Select ADC input range = +-2,56V
	address = ADREG_ADC_CONTROL; // Select ADCCON reg
	data = *(unsigned int*)&adcconreg;

	sensor_write_8bit(Decoder, address, data); // Set up mode register

	return 1;
}


//******************************************************************************
//! @fn unsigned int ad77x8Read(Ad77x8Data_t *p_adc_data, sensor_select_e Decoder, sensor_axis_e Axis, sensor_board_e Board)
//!
//! @description
//!
//!
//! @return   current_state (0 - AD_CONVERTING, 1 - AD_CONVERTED)
//!
//! @calls    SPI_master_init()
//!           ad77x8RDY()
//!           ad77x8RawReadData()
//!           ad77x8SetChannel()
//!
//! @req
//!
//! @example
//! 
//! @code
//! 
//! 
//! @endcode
//******************************************************************************
unsigned int ad77x8Read(Ad77x8Data_t *p_adc_data, sensor_select_e Decoder)
{
    static uint8_t current_channel = ADC_3V3;
	static uint8_t current_state = AD_CONVERTING;
	
	uint32_t buffer = 0.0;
	SPI_master_init(AD77X8_SPI_FREQ, SPI_POL_0, SPI_PHA_0, SPI_8_BIT, 0);
	
	current_state = ad77x8RDY(Decoder);
	
    if(current_state == AD_CONVERTED)
	{
		buffer = ad77x8RawReadData(Decoder);
		
		switch(current_channel){
			case ADC_3V3:       p_adc_data->adc_3v3      = (((float)buffer * 2.56)/(16777216))/(0.625000); current_channel++;   break;
			case ADC_3V3_A:     p_adc_data->adc_3v3a     = (((float)buffer * 2.56)/(16777216))/(0.625000); current_channel++;   break;
			case ADC_3V3_B:     p_adc_data->adc_3v3b     = (((float)buffer * 2.56)/(16777216))/(0.625000); current_channel++;   break;
			case ADC_3V3_C:     p_adc_data->adc_3v3c     = (((float)buffer * 2.56)/(16777216))/(0.625000); current_channel++;   break;
			case ADC_5V:        p_adc_data->adc_5v       = (((float)buffer * 2.56)/(16777216))/(0.416666); current_channel++;   break;
			case ADC_5V_SW:     p_adc_data->adc_5v_sw    = (((float)buffer * 2.56)/(16777216))/(0.416666); current_channel++;   break;
			case ADC_EXT_PWR:   p_adc_data->adc_ext_pwr  = (((float)buffer * 2.56)/(16777216))/(0.076923); current_channel++;   break;
			case ADC_EXT_PWR_2: p_adc_data->adc_ext_pwr2 = (((float)buffer * 2.56)/(16777216))/(0.076923); current_channel = 0; break;
			default: break;
	    }
		
		ad77x8SetChannel(current_channel, Decoder);
	}

	return current_state;

}




