/************************************************************************/
/* Copyright 2003-2012 MBARI											*/
/************************************************************************/
/* Summary	: A/D (Analog) Module for BEDS								*/
/* Filename : adc.c														*/
/* Author	: Robert Herlien (rah)										*/
/* Project	: BEDS (Benthic Event Detection System)						*/
/* Revision : 1.0														*/
/* Created	: 10/11/2012												*/
/************************************************************************/
/* Modification History:												*/
/* 11oct2012 rah - created												*/
/* $Log$
 */
/************************************************************************/
#include <beds.h>				/* BEDS general definitions				*/
#include <spi.h>				/* BEDS SPI definitions					*/
#include <adc.h>				/* BEDS Analog Module					*/
#include <spi.h>				/* BEDS SPI Module						*/
#include <clock.h>				/* BEDS Clock module					*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/********************************/
/*		External Data			*/
/********************************/

Extern Flt32 pressFullScale;


/********************************/
/*		Module Local Data		*/
/********************************/

/* convert chan to single-ended selector	*/
const Byte ADS8344SglChSel[8] =
    { 0x00, 0x40, 0x10, 0x50, 0x20, 0x60, 0x30, 0x70 };

typedef struct
{
	Flt32		a;
	Flt32		b;
	char		*units;
} ADConversion;

MLocal ADConversion adConv[] =
{ {12.11, 0.0, "Batt Volts"},  {12.11, 0.0, "Batt Volts"}, 
  {50.0, 12.5, "bar"}, {50.0, 12.5, "psia"},
  {100.0, 50.0, "degC"}, {47.175, 23.82, "%"},
  {1.0, 0.0, "Volts"}
};


/************************************************************************/
/* Function	   : adcInit												*/
/* Purpose	   : Initialize A/D hardware for BEDS						*/
/* Inputs	   : None													*/
/* Outputs	   : TRUE if OK, FALSE if not								*/
/************************************************************************/
MBool adcInit(void)
{
	adcSample(0);				/* Sample one chan to put into powerdown*/
	return(TRUE);

} /* adcInit() */


Nat32 adcSpiReadWrite(Nat32 dat)
{
    while ( SPI2STATbits.SPITBF )	/* Wait for Tx buf available	*/
        ;

    SPI2BUF = dat;				/* put data in SPI TX buffer	*/

    while ( !SPI2STATbits.SPIRBF )	/* wait for received byte	*/
        ;

    return(SPI2BUF);			/* Return received byte		*/
}

/************************************************************************/
/* Function	   : adcSample												*/
/* Purpose	   : Sample an analog channel								*/
/* Inputs	   : A/D channel number										*/
/* Outputs	   : Raw A/D Count											*/
/************************************************************************/
Nat32 adcSample(Nat32 chan)
{
	Byte	wrReg[4];
	Byte	rdReg[4];

	memset(wrReg, 0, sizeof(wrReg));
	spiSelectADC();							/* Assert /CS on ADC		*/
	wrReg[0] = (ADS8344SglChSel[chan] | 0x84);

	spi2WriteRead(wrReg, rdReg, 4);			/* Do one conversion		*/
//	clkDelayUs(1000);
//	spi2WriteRead(wrReg, rdReg, 4);			/* Do one conversion		*/

	spi2Unselect();							/* De-assert /CS on ADC		*/

	return(((rdReg[1] << 9) & 0xfe00) | ((rdReg[2] << 1) & 0x1fe) |
		   ((rdReg[2] >> 7) & 1));
	
} /* adcSample() */


/************************************************************************/
/* Function	   : adcSampleAvg											*/
/* Purpose	   : Sample and average an analog channel					*/
/* Inputs	   : A/D channel number, number of samples to average		*/
/* Outputs	   : Raw A/D Count											*/
/* Comments	   : Samples A/D channel nsamples times at 1 KHz			*/
/************************************************************************/
Nat32 adcSampleAvg(Nat32 chan, Nat32 nsamples)
{
	Nat32	i, val;
	Nat32	accumBuf = 0L;
	Byte	wrReg[4];
	Byte	rdReg[4];

	memset(wrReg, 0, sizeof(wrReg));
	spiSelectADC();							/* Assert /CS on ADC		*/
	wrReg[0] = (ADS8344SglChSel[chan] | 0x84);
	spi2WriteRead(wrReg, rdReg, 3);			/* Do first conversion		*/

	for (i = 0; i < nsamples; i++)
	{
		clkDelayUs(1000);
		val = (((rdReg[1] << 9) & 0xfe00) | ((rdReg[2] << 1) & 0x1fe));
		spi2WriteRead(wrReg, rdReg, 3);			/* Do first conversion		*/
		val |= ((rdReg[0] >> 7) & 1);
		accumBuf += val;
	}

	spi2Unselect();							/* De-assert /CS on ADC		*/
	return((accumBuf/nsamples) & 0xffff);

} /* adcSampleAvg() */


/************************************************************************/
/* Function	   : adcGetRawVolts											*/
/* Purpose	   : Return Raw A/D Voltage on a given channel				*/
/* Inputs	   : A/D Channel											*/
/* Outputs	   : Raw A/D voltage as a float								*/
/************************************************************************/
Flt32 adcGetRawVolts(Nat32 chan)
{
	return(RAW_TO_VOLTS(adcSampleAvg(chan, 100)));

} /* adcGetRawVolts() */


/************************************************************************/
/* Function	   : adcEngValue											*/
/* Purpose	   : Convert ADC counts into final Engineering value with units*/
/* Inputs	   : Channel number, ADC Count								*/
/* Outputs	   : Voltage/Pressure/Temp/RH as Flt32, units as string		*/
/************************************************************************/
Flt32 adcEngValue(Nat32 chan, Nat32 counts, char **unitp)
{
	if (chan >= NumberOf(adConv))
		chan = HUMIDITY_CHAN + 1;

   if (unitp != NULL)
	   *unitp = adConv[chan].units;

   if ((chan == EXT_PRESS_CHAN) && (pressFullScale > 1.0))
	   return((pressFullScale/2.0) * RAW_TO_VOLTS(counts) - pressFullScale/8.0);

   return((adConv[chan].a * RAW_TO_VOLTS(counts)) - adConv[chan].b);

} /* adcEngValue() */
