
/* Copyright 2013 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Written by Bob Herlien for SensorNode for xFOCE, May 2013                */
/****************************************************************************/
#include "adc.h"
#include "dig_io.h"
#include "sys_timer.h"
#include "spi.h"
#include "config.h"
#include "p24Fxxxx.h"
#include "GenericTypeDefs.h"
#include "mb.h"
#include "port.h"
#include <string.h>
#include <stdio.h>
#include "serial.h"

//#define DEBUG 1
//#define DEBUG1 1

#ifdef DEBUG
#define DEBUG_PORT CFG_SERPORT
#endif

#define MAX_TOT_DELAY	250

static UINT16	adcNumAvg=1, adcAvgMs=1;

static UINT32	adcAccum[ADC_CHANS];

static ADCDesc adcChans[] =
{
  {ADC_01, 0},			// VICOR_01_TEMP
  {ADC_01, 1},			// VICOR_02_TEMP
  {ADC_01, 2},			// VICOR_03_TEMP
  {ADC_01, 3},			// HEATSINK_TEMP
  {ADC_48V_01, 0},		// 48V_01_CURR
  {ADC_48V_01, 1},		// 48V_01_VOLT
  {ADC_48V_02, 0},		// 48V_02_CURR
  {ADC_48V_02, 1},		// 48V_02_VOLT
  {ADC_02, 0},			// 24V_CURR
  {ADC_02, 1},			// 24V_VOLT
  {ADC_02, 2},			// 3.3V_CURR
  {ADC_02, 3},			// 3.3V_VOLT
  {ADC_02, 4},			// AMB_HUMID
  {ADC_02, 5},			// AMB_TEMP
  {ADC_02, 6}			// GF_CURR is "15th" channel
};


#ifdef DEBUG
static char     adcDbgBuff[256];
#endif

/* convert chan to single-ended selector	*/
const USHORT ADS8344ChSel[8] =
    { 0x0000, 0x4000, 0x1000, 0x5000, 0x2000, 0x6000, 0x3000, 0x7000 };

/* convert chan to single-ended selector	*/
const USHORT ADS8341ChSel[4] =
    { 0x1000, 0x5000, 0x2000, 0x6000 };

/* init registers */
void adcInit()
{
    ADC_01_CS = 1;
    ADC_02_CS = 1;
    ADC_48V_01_CS = 1;
    ADC_48V_02_CS = 1;
    TRISGbits.TRISG7 = 1;	//SPI_MOSI
    TRISGbits.TRISG8 = 1;	//SPI_MISO
    TRISGbits.TRISG9 = 1;	//SPI_SCK

}

/* Convert all channels corresponding to the Modbus request	*/
void adcConvert(USHORT address, USHORT nRegs)
{
    USHORT	curAddr, nCurRegs, offset, i, chan, n;

#ifdef DEBUG
    sprintf(adcDbgBuff, "adcConvert(0x%x, %u)\r\n", address, nRegs);
    serPutString(DEBUG_PORT, adcDbgBuff);
#endif

    if (((address + nRegs) < ADC_CONVERT_START) || (address > ADC_CONVERT_END))
	return;

    memset(adcAccum, 0, sizeof(adcAccum));

    curAddr = address;
    offset = 0;
    nCurRegs = nRegs;

    if (address < ADC_CONVERT_START)
    {
	curAddr = ADC_CONVERT_START;
	offset = ADC_CONVERT_START - address;
	nCurRegs = nRegs - offset;
    }

    if ((curAddr + nCurRegs) > ADC_CONVERT_END1)
	nCurRegs = ADC_CONVERT_END1 - curAddr;

    curAddr -= ADC_CONVERT_START;		//Convert Modbus addr to chan number

    for (i = 0; i < adcNumAvg; i++)
    {
	if (i)
	    tmrDelayMs(adcAvgMs);

	for (chan = curAddr, n = 0; n < nCurRegs; n++, chan++)
	    adcAccum[chan] += ads834xConvert(chan);
    }
}

USHORT adcReadReg(USHORT address)
{
    if (adcNumAvg == 0)
	return(0);
    
    if ((address >= ADC_CONVERT_START) && (address <= ADC_CONVERT_END))
	return((USHORT)(adcAccum[address - ADC_CONVERT_START] / adcNumAvg));
    else if (address == ADC_NUM_AVG)
	return(adcNumAvg);
    else if (address == ADC_AVG_MS)
	return(adcAvgMs);

    return(0);
}

eMBErrorCode adcWriteRegs(UCHAR *pRegBuffer, USHORT address, USHORT nRegs)
{
    USHORT      val, newNumAvg, newAvgMs;

    newNumAvg = adcNumAvg;
    newAvgMs  = adcAvgMs;

    while( nRegs > 0 )
    {
	if ((address >= ADC_PARM_START) && (address <= ADC_PARM_END))
	{
	    /* get a register from the reg buffer */
	    val = *(pRegBuffer++) & 0xff;
	    val <<= 8;
	    val += *(pRegBuffer++) & 0xff;

	    if (val < 1)	//adcNumAvg and adcAvgMs must both be >= 1
		val = 1;

	    if (address == ADC_NUM_AVG)
		newNumAvg = val;
	    else if (address == ADC_AVG_MS)
		newAvgMs = val;
	}

	/* increment register index and address */
	--nRegs;
	++address;
    }

    if (newNumAvg * newAvgMs <= MAX_TOT_DELAY)
    {
	adcNumAvg = newNumAvg;
	adcAvgMs  = newAvgMs;
	return(MB_ENOERR);
    }

    return(MB_EINVAL);
}

static void adcSel(USHORT converter, USHORT sel)
{
    switch(converter)
    {
      case ADC_01:
	  ADC_01_CS = sel ? 0 : 1;
	  break;

      case ADC_02:
	  ADC_02_CS = sel ? 0 : 1;
	  break;

      case ADC_48V_01:
	  ADC_48V_01_CS = sel ? 0 : 1;
	  break;

      case ADC_48V_02:
	  ADC_48V_02_CS = sel ? 0 : 1;
	  break;
    }
}

USHORT ads834xConvert(USHORT chan)
{
    USHORT  val, converter, adcChan, chanSel;

    if (chan > ADC_CHANS)
	return(0);
    
    converter = adcChans[chan].converter;
    adcChan = adcChans[chan].chan;

    chanSel = (converter == ADC_02) ? ADS8344ChSel[adcChan] : ADS8341ChSel[adcChan];

    adcSel(converter, 1);

    val = (spiReadWrite(chanSel | ADS8344START | ADS8344SGL | ADS8344EXT) & 0x7f) << 9;

    val |= ((spiReadWrite(0) >> 7) & 0x1ff);

    adcSel(converter, 0);

#ifdef DEBUG1
    sprintf(adcDbgBuff, "ads834xConvert(%u) = %u (Conv %u chan %u)\r\n", 
	    chan, val, converter, adcChan);
    serPutString(DEBUG_PORT, adcDbgBuff);
#endif
    return(val);
}


USHORT adsConvertGFchan(void)
{
    USHORT  val;

    ADC_02_CS = 0;

    val = (spiReadWrite(GF_CH_SEL | ADS8344START | ADS8344SGL | ADS8344EXT) & 0x7f) << 9;

    val |= ((spiReadWrite(0) >> 7) & 0x1ff);

    ADC_02_CS = 0;

    return(val);
}
