/************************************************************************/
/* 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 <cfxbios.h>		/* Persistor BIOS definitions		*/
#include <cfxpico.h>		/* Persistor PicoDOS definitions	*/
#include <cfxad.h>		/* Generic SPI A-D QPB Driver for CFx	*/
#include <beds.h>		/* BEDS general definitions		*/
#include <spi.h>		/* BEDS SPI definitions		        */
#include <adc.h>		/* BEDS Analog Module			*/
#include <ADS8344.h>		/* Support for ADS8344 A/D chip		*/
#include <io.h>			/* BEDS I/O definitions			*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/********************************/
/*	External Data	        */
/********************************/

Extern Flt32 pressFullScale;


/********************************/
/*	Module Local Data	*/
/********************************/

MLocal CFxAD	adbuf, *ad = NULL;
MLocal MBool	adIsPowered = FALSE;

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)
{
    if ((ad = CFxADInit(&adbuf, ADSLOT, ADS8344Init)) == NULL)
    {
	cprintf("Warning!! - Failed to initialize A/D converter!!!\n");
	cprintf("Attempt to record will probably fail.\n\n");
	return(FALSE);
    }

//    PIOClear(AD_REF_SHDN_PIN);	/* Disable /ADShDn		*/
    PIOSet(AD_REF_SHDN_PIN);		/* Always leave REF pwrd on	*/
    CFxADSample(ad, 0, TRUE, TRUE, FALSE); /* Choose external clock	*/
    return(TRUE);

} /* adcInit() */


/************************************************************************/
/* Function    : adcSample						*/
/* Purpose     : Sample an analog channel				*/
/* Inputs      : A/D channel number					*/
/* Outputs     : Raw A/D Count						*/
/* Comments    : Samples A/D channel nsamples times at 100 Hz		*/
/************************************************************************/
Nat16 adcSample(Nat16 chan)
{
    if (!adIsPowered)
	adcPowerUp();

    return(CFxADSample(ad, chan, TRUE, TRUE, TRUE));

} /* 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		*/
/************************************************************************/
Nat16 adcSampleAvg(Nat16 chan, Nat16 nsamples)
{
    Nat16	i;
    Nat32	accumBuf = 0L;

    if (!adIsPowered)
	adcPowerUp();

    for (i = 0; i < nsamples; i++)
    {
	accumBuf += CFxADSample(ad, chan, TRUE, TRUE, FALSE);
	Delay1ms();
    }

    CFxADSample(ad, chan, TRUE, TRUE, TRUE);
    return((Nat16)(accumBuf/nsamples));

} /* adcSampleAvg() */


/************************************************************************/
/* Function    : adcSampleMult						*/
/* Purpose     : Sample and average multiple analog channels		*/
/* Inputs      : Start channel, number of channels, num samples to avg,	*/
/*		 ptr to data						*/
/* Outputs     : None.  Results placed in *dp				*/
/* Comments    : Samples A/D channel nsamples times at 1 KHz		*/
/************************************************************************/
Void adcSampleMult(Nat16 chan, Nat16 nchans, Nat16 nsamples, Nat16 *dp)
{
    Nat16	i, ch;
    Nat32	accumBuf[NUM_ADC_CHANS];

    if (dp == NULL)
	return;

    if (!adIsPowered)
	adcPowerUp();

    memset(accumBuf, 0, sizeof(accumBuf));

    for (i = 0; i < nsamples; i++)
    {
	for (ch = chan; ch < nchans; ch++)
	{
	    accumBuf[ch] += CFxADSample(ad, ch, TRUE, TRUE, FALSE);
	    Delay100us();
	}

	Delay1ms();
    }

    CFxADSample(ad, chan, TRUE, TRUE, TRUE);

    for (ch = chan; ch < nchans; ch++)
	dp[ch] = accumBuf[ch]/nsamples;

} /* adcSampleMult() */


/************************************************************************/
/* Function    : adcPowerUp						*/
/* Purpose     : Power up the ADC					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void adcPowerUp(Void)
{
    PIOSet(AD_REF_SHDN_PIN);		/* Enable Reference		*/
    CFxADSample(ad, 0, TRUE, TRUE, TRUE); /* Do one A/D conv. to clear pwrdown*/
    adIsPowered = TRUE;

} /* adcPowerUp() */


/************************************************************************/
/* Function    : adcPowerDown						*/
/* Purpose     : Power down the ADC					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void adcPowerDown(Void)
{
    CFxADPowerDown(ad, FALSE);		/* Put ADC in full powerdown mode*/
    PIOClear(AD_REF_SHDN_PIN);		/* Disable /ADShDn		*/
    adIsPowered = FALSE;

} /* adcPowerDown() */


/************************************************************************/
/* 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(Nat16 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(Nat16 chan, Nat16 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() */
