/*
 * David Muller; Germαn Alfaro
 * davehmuller@gmail.com; alfaro.germanevera@gmail.com
 *
 * Modified for MBARI MFET  Thom Maughan 2018
 * tm@mbari.org
 */


/*
 * ADS1248_iso.c
 * Driver library for isolated ADS1248.
 */



#include "system.h"
#include "ads1248_iso.h"
#include "uartstdio.h"	// User local version with larger RX buffer
#include "user_io.h"

extern struct systemSamplingData sys_samp;  // TODO: figure out the includes to make this line go away

#define PGA        	1
#define SDATAC 		0x16	// Stop reading data continuously command for ADS1248
#define RDATA  		0x12    // Read data command
#define RDATAC 		0x14	// Start reading data continuously
#define NOP 		0xFF    // No operation command
#define SELFOCAL 	0x62   	// Self offset calibration command

// ADS1248 Register macros
#define REG_MUX0    0x0
#define REG_VBIAS   0x1
#define REG_MUX1    0x2
#define REG_SYS0    0x3
#define REG_OFC0    0x4
#define REG_OFC1    0x5
#define REG_OFC2    0x6
#define REG_FSC0    0x7
#define REG_FSC1    0x8
#define REG_FSC2    0x9
#define REG_IDAC0   0xa
#define REG_IDAC1   0xb
#define REG_GPIOCFG 0xc
#define REG_GPIODIR 0xD
#define REG_GPIODAT 0xE

#define RREG        0x20        // 0010 rrrr where rrrr is register
#define WREG        0x40        // 0100 rrrr where rrrr is register

//9.5.3.9 WREG (0100 rrrr, 0000 nnnn)
//The WREG command writes to the registers, starting with the register specified as part of the instruction. The
//number of registers that are written is one plus the value of the second byte. The command structure for WREG
//is listed below.
// First Command Byte: 0100 rrrr, where rrrr is the address of the first register to be written.
// Second Command Byte: 0000 nnnn, where nnnn is the number of bytes to be written  1.
// Byte(s): data to be written to the registers

//9.5.3.8 RREG (0010 rrrr, 0000 nnnn)
//The RREG command outputs the data from up to 15 registers, starting with the register address specified as part
//of the instruction. The number of registers read is one plus the value of the second byte. If the count exceeds the
//remaining registers, the addresses wrap back to the beginning. The two byte command structure for RREG is
//listed below.
// First Command Byte: 0010 rrrr, where rrrr is the address of the first register to read.
// Second Command Byte: 0000 nnnn, where nnnn is the number of bytes to read 1.
// Byte(s): data read from the registers are clocked out with NOPs

#if BOARD_MFET >= 1 || BOARD_MPHOX == 1
// Function macros for Mini-Board (removed the invert on START and RESET)
#define iso_DRDY()          ROM_GPIOPinRead(GPIO_PORTN_BASE, GPIO_PIN_3)
#define iso_assert_CS()     ROM_GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_1, 0x00);    // Assert chip select (set low)
#define iso_deassert_CS()   ROM_GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_1, 0xff);    // Deassert chip select (set high)

#define iso_START_high()    ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_6, 0xff);    // Not inverted on mini-board
#define iso_START_low()     ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_6, 0x00);    // Not inverted on mini-board
#define iso_RESET_high()    ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, 0xff);    // Not inverted on mini-board
#define iso_RESET_low()     ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, 0x00);    // Not inverted on mini-board
#endif

#if BOARD_NANOFET >= 1
// Function macros for NanoFET
#define iso_DRDY()          ROM_GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_6)           // PD6
#define iso_assert_CS()     ROM_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_1, 0x00);    // PD1 Assert chip select (set low)
#define iso_deassert_CS()   ROM_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_1, 0xff);    // PD1 Deassert chip select (set high)

#define iso_START_high()    ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_6, 0xff);
#define iso_START_low()     ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_6, 0x00);
#define iso_RESET_high()    ROM_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_4, 0xff);
#define iso_RESET_low()     ROM_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_4, 0x00);
#endif

/*
 * ADS1248 Delays
 */
//205ms delay. At default 5SPS, digital filter reset takes about 200ms
#define SPS5DELAY (MILLISECOND * 205)

//55ms delay. At 20SPS, digital filter reset takes about 50ms
#define SPS20DELAY (MILLISECOND * 55)

//805ms delay. At 20SPS, calibration takes about 801ms
#define SPS20CALIBRATIONDELAY (MILLISECOND * 805)

#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1 || BOARD_NANOFET >= 1
#define ADS1248_POWERON_RESET_DELAY (MILLISECOND * 20)      // Datasheet says 16ms, we'll give it 20ms
#endif

#if BOARD_SEAPHOX == 1
#define ADS1248_POWERON_RESET_DELAY (MILLISECOND * 200)      // TPS3836 reset is strapped for 200msec
#endif


extern unsigned int iso_flag;

// Local function prototypes
unsigned char ADS1248_gain_sps(int gain, int sps);

/*
 * openADS1248_iso()
 * Performs initial set up of the isolated ADS1248.
 * Note that SSI3FSS is configured to toggle chip select manually.
 * This is needed as ADS1248 requires CS to stay low during complete
 * SSI register transactions.
 * Pseudocode from the ADS1248 datasheet:
 *
 * Power up;
 * Delay for a minimum of 16 ms to allow power supplies to settle and power-on reset to complete;
 * Enable the device by setting the START pin high;
 * Configure the serial interface of the microcontroller to SPI mode 1 (CPOL = 0, CPHA =1);
 * If the CS pin is not tied low permanently, configure the microcontroller GPIO connected to CS as an output;
 * Configure the microcontroller GPIO connected to the DRDY pin as a falling edge triggered interrupt input;
 * Set CS to the device low;
 * Delay for a minimum of tCSSC;
 * Send the RESET command (06h) to make sure the device is properly reset after power up;
 * Delay for a minimum of 0.6 ms;
 * Send SDATAC command (16h) to prevent the new data from interrupting data or register transactions;
 * Write the respective register configuration with the WREG command (40h, 03h, 01h, 00h, 03h and 42h);
 * As an optional sanity check, read back all configuration registers with the RREG command (four bytes from 20h, 03h);
 * Send the SYNC command (04h) to start the ADC conversion;
 * Delay for a minimum of tSCCS;
 * Clear CS to high (resets the serial interface);
 *
 * Loop
 * {
 *   Wait for DRDY to transition low;
 *   Take CS low;
 *   Delay for a minimum of tCSSC;
 *   Send the RDATA command (12h);
 *   Send 24 SCLKs to read out conversion data on DOUT/DRDY;
 *   Delay for a minimum of tSCCS;
 *   Clear CS to high;
 * }
 * Take CS low;
 * Delay for a minimum of tCSSC;  (10 nano-sec)
 * Send the SLEEP command (02h) to stop conversions and put the device in power-down mode;
 *
 *
 */
// Removed BOARD_SEAPHOX openADS1248



// 22msec Thom 29 May 2019
void openADS1248_iso(void)
{
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1

    // Power up by enabling regulator
    ROM_GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_2, 0xff);    // Iso 3.3V power on for ADS1248 via ADM7160 regulator

    //Deassert GPIO control signals for ADS1248: ADC_Reset reset high (active lo), START high, CS high
    iso_RESET_high();    // ADC_Reset is PortN.5
    iso_START_high();    // START is PortN.6
    iso_deassert_CS();   // PortH.1

    // delay for ADM7160 3.3v stability
    ROM_SysCtlDelay(MILLISECOND*2);                        // ADM7160 is within 90% after 180 usec, 2msec should be way plenty

    // Pulse ADS1248 RESET
    iso_RESET_low();
    ROM_SysCtlDelay(MICROSECOND * 10);  // ADS1258 reset is 1 usec, 10 usec is conservative
    iso_RESET_high();

    // Setup SSI port 3
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3);
    ROM_GPIOPinConfigure(GPIO_PH2_SSI3RX);
    ROM_GPIOPinConfigure(GPIO_PH3_SSI3TX);
    ROM_GPIOPinConfigure(GPIO_PH0_SSI3CLK);
    ROM_GPIOPinTypeSSI(GPIO_PORTH_BASE, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);


    // Configure and enable SSI for SPI master mode.
    ROM_SSIConfigSetExpClk(SSI3_BASE, ROM_SysCtlClockGet(), SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER, 1000000, 8);   //MOTO_MODE_1
    ROM_SSIEnable(SSI3_BASE);

    // Wait for ADS1248 clock initialization and calibration to complete after reset (datasheet says 16msec), SPI communication can begin 0.6 msec after reset
    ROM_SysCtlDelay(MILLISECOND*20);

    // Note: the ADS1248evm and SEAPHOX boards have a reset chip TPS3836 strapped for 200msec (300msec max), the MFET has a GPIO iso reset.
#endif   //#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1

#if BOARD_NANOFET >= 1

    // nanoFET has ADS1284 always powered

    //Deassert GPIO control signals for ADS1248: ADC_Reset reset high (active lo), START high, CS high
    iso_RESET_high();    // ADC_Reset is PortN.5
    iso_START_high();    // START is PortN.6
    iso_deassert_CS();   // PortH.1


    // Pulse ADS1248 RESET
    iso_RESET_low();
    ROM_SysCtlDelay(MICROSECOND * 10);  // ADS1258 reset is 1 usec, 10 usec is conservative
    iso_RESET_high();

    // Setup SSI port 3 but on 64pin part it is mapped to PD0-3
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3);
    ROM_GPIOPinConfigure(GPIO_PD2_SSI3RX);   // MISO
    ROM_GPIOPinConfigure(GPIO_PD3_SSI3TX);   // MOSI
    ROM_GPIOPinConfigure(GPIO_PD0_SSI3CLK);
    ROM_GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);


    // Configure and enable SSI for SPI master mode.
    ROM_SSIConfigSetExpClk(SSI3_BASE, ROM_SysCtlClockGet(), SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER, 1000000, 8);   //MOTO_MODE_1
    ROM_SSIEnable(SSI3_BASE);

    // Wait for ADS1248 clock initialization and calibration to complete after reset (datasheet says 16msec), SPI communication can begin 0.6 msec after reset
    ROM_SysCtlDelay(MILLISECOND*20);

#endif



}




/*
 * closeADS1248()
 * Clears uP output pins for low power.
 * Shuts of power to optoisolators and ADS1248 analog supply.
 */
void closeADS1248_iso(void)
{

#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1
	iso_START_high();			// Pin cleared
	iso_RESET_high();			//  "
	ROM_SSIDisable(SSI3_BASE);

	// Change the SSI pins to GPIO to reduce power consumption
	ROM_SysCtlPeripheralDisable(SYSCTL_PERIPH_SSI3);

	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTH_BASE, GPIO_PIN_0);		// SSI3CLK
	ROM_GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0x00);

	ROM_GPIOPinTypeGPIOInput(GPIO_PORTH_BASE, GPIO_PIN_2);		// SSI3Rx

	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTH_BASE, GPIO_PIN_3);		// SSI3Tx
	ROM_GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_3, 0x00);

	iso_assert_CS();	// ADS1248 CS asserted low

	// Power down
	ROM_GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_2, 0x00);  	// Iso 3.3V power off


#endif

#if BOARD_NANOFET >= 1

    iso_START_high();           // Pin cleared
    iso_RESET_high();           //  "
    ROM_SSIDisable(SSI3_BASE);

    // Change the SSI pins to GPIO to reduce power consumption
    ROM_SysCtlPeripheralDisable(SYSCTL_PERIPH_SSI3);

    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_0);     // SSI3CLK PD0
    ROM_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_0, 0x00);

    ROM_GPIOPinTypeGPIOInput(GPIO_PORTD_BASE, GPIO_PIN_2);      // SSI3Rx PD2

    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_3);     // SSI3Tx PD3
    ROM_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_3, 0x00);

    iso_assert_CS();    // ADS1248 CS asserted low

#endif


}

/*
 * ADS1248_gpio_init()
 * Initialize the ADS1248 GPIO0,1 as outputs that are used for analog mux address control
 *
 */
void ADS1248_gpio_init(void)
{
    uint32_t gpiocfg, gpiodir, gpiodat, data[4];

//#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1 || BOARD_NANOFET >= 1
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1

    // Write configuration data to GPIOCFG, GPIODIR, GPIODAT in one transaction
    iso_assert_CS();                                    // Start SSI register write transaction

    gpiocfg = 0x0000000F;   //0x00000007;               // enable GPIO0 and GPIO1 and GPIO2 and GPIO3
    gpiodir = 0x00000000;                               // gpio as output
    gpiodat = 0x00000000;                               // gpio data write to 1 (was 0)

    while(ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[0]));     // Flush receive FIFO

    ROM_SSIDataPutNonBlocking(SSI3_BASE, WREG + REG_GPIOCFG);   // Start writing at REG_GPIOCFG
    ROM_SSIDataPutNonBlocking(SSI3_BASE, 2);                    // Write 3 bytes (n-1)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiocfg);
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiodir);
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiodat);

    while(ROM_SSIBusy(SSI3_BASE))
        ;                      // Let SSI finish sending bytes
    iso_deassert_CS();                                  // Finish SSI register write transaction
#endif

#if BOARD_NANOFET >= 1

    // Write configuration data to GPIOCFG, GPIODIR, GPIODAT in one transaction
    iso_assert_CS();                                    // Start SSI register write transaction

    gpiocfg = 0x00000001;                               // enable gpio0
    gpiodir = 0x000000FE;                               // write all bit.0-7 gpio as input (1) except gpio0
    gpiodat = 0x00000000;                               // not needed

    while(ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[0]));     // Flush receive FIFO

    ROM_SSIDataPutNonBlocking(SSI3_BASE, WREG + REG_GPIOCFG);   // Start writing at REG_GPIOCFG
    ROM_SSIDataPutNonBlocking(SSI3_BASE, 2);                    // Write 3 bytes (n-1)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiocfg);
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiodir);
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiodat);

    while(ROM_SSIBusy(SSI3_BASE))
        ;                      // Let SSI finish sending bytes
    iso_deassert_CS();                                  // Finish SSI register write transaction
#endif




}

/*
 * ADS1248_gpio_ADG1609_mux()
 * Write the ADS1248 GPIO used as ADG1609 mux control
  */
void ADS1248_gpio_ADG1609_mux(uint32_t chan)
{
    uint32_t gpiodat, data[4];
//#if BOARD_MFET >= 1  || BOARD_MPHOX == 1 || BOARD_NANOFET >= 1
#if BOARD_MFET >= 1  || BOARD_MPHOX == 1
    // if chan==0 or chan==3
    // then turn on ADS1248_gpio_num=2 (pin 17)
    // GPIO=0,1 are used for MUX address

    if(chan > 3)
    {
        uprintf("Mux channels are 0 to 3 in ADS1248_iso.c, error in arg for ADS1248_gpio_ADG1609_mux\r\n");
        chan = 3;
    }



    // Write configuration data to GPIOCFG, GPIODIR, GPIODAT in one transaction
    iso_assert_CS();                                    // Start SSI register write transaction

    //gpiodat = chan;                               // gpio data
    // invert A0,A1 to the mux (chan selector) to compensate for hardware voltage level translation
    gpiodat = (~chan) & 0x00000003;                 // gpiodat has the inverted mux address (to compensate for the hardware level translator inversion)
    //uprintf("ADG1609 chan %lu invert for level shift, %lu\r\n", chan, gpiodat);
    if((chan==0) || (chan==3))      // THOM support for new MFET/MCAP (not connected on first MFET)
    {
        gpiodat |= 0x00000004;         // Set ADS1248 GPIO2 if chan0 or chan3
    }
    else
    {
        gpiodat &= ~0x00000004;         // Clear ADS1248 GPIO2 if not chan0 or chan3
    }

    while(ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[0]));     // Flush receive FIFO

    ROM_SSIDataPutNonBlocking(SSI3_BASE, WREG + REG_GPIODAT);   // Start writing at REG_GPIODAT  (WREG=0x40, see ADS1248 datasheet)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, 0);                    // Write 1 bytes (n-1)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiodat);              // write 00, 01, 10, 11 for channel select

    while(ROM_SSIBusy(SSI3_BASE))
        ;                      // Let SSI finish sending bytes (TODO: fix possible hangspot)
    iso_deassert_CS();                                  // Finish SSI register write transaction
#endif




}





/*
 * pollADS1248_iso()
 * Returns the voltage between to two specified differential pins.
 * Sample averaging indicated by value of "trials".
 * Gain and data rate can be set for each channel.
 */
float pollADS1248_iso(unsigned char pos_chan, unsigned char neg_chan, int trials, int gain, int sps)  // was int trials 28 Apr 2021
{
	long sample = 0, result;
	int i, byte;
	uint32_t mux0, vbias, mux1, sys0, data[10];

#define MAX_TRIALS  120
	float voltages[MAX_TRIALS];
	float sum_deviation = 0.0;
	float mean;

	if(trials > MAX_TRIALS)     // THOM 3/19/2018, fixing a crash (app exit) that ends in loader_exit()
	{
	    uprintf("MAX TRIALS is %u in ADS1248_iso.c, more trials (avg) than %u was requested, edit pollADS1248_iso() and recompile\r\n", MAX_TRIALS, MAX_TRIALS);
	    trials = MAX_TRIALS;
	}

	// Write configuration data to MUX0, VBIAS, MUX1, and SYS0 in one transaction
	iso_assert_CS();							// Start SSI register write transaction

	mux0 = (pos_chan << 3) | neg_chan;			// MUX0 channel register
	vbias = 0;									// VBIAS register
	mux1 = 0x30;								// 2.048 V internal reference selected
	sys0 = ADS1248_gain_sps(gain, sps);			// SYS0 register. Sets gain and data rate

	while(ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[0]));		// Flush receive FIFO   THOM CRASH IS HAPPENING HERE...  2/2/2022 TODO

	ROM_SSIDataPutNonBlocking(SSI3_BASE, 0x40 + REG_MUX0);		// Start writing at MUX0
	ROM_SSIDataPutNonBlocking(SSI3_BASE, 3);					// Write 4 bytes (n-1)
	ROM_SSIDataPutNonBlocking(SSI3_BASE, mux0);
	ROM_SSIDataPutNonBlocking(SSI3_BASE, vbias);
	ROM_SSIDataPutNonBlocking(SSI3_BASE, mux1);
	ROM_SSIDataPutNonBlocking(SSI3_BASE, sys0);

	while(ROM_SSIBusy(SSI3_BASE))
	    ;		// Let SSI finish sending bytes
	iso_deassert_CS();					// Finish SSI register write transaction

	// A/D is sampling continuously, so get as many samples as we need
	for(i = 0; i < trials; i++)
	{
		while( iso_DRDY() )
		    ;										// Wait for conversion to complete

		while(ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[0] ))
		    ;		// Flush receive FIFO

		iso_assert_CS();									// Start SSI read transaction
		ROM_SSIDataPutNonBlocking(SSI3_BASE, NOP);			// Clock out result bytes
		ROM_SSIDataPutNonBlocking(SSI3_BASE, NOP);			// Clock out result bytes
		ROM_SSIDataPutNonBlocking(SSI3_BASE, NOP);			// Clock out result bytes
		while(ROM_SSIBusy(SSI3_BASE));						// Let SSI finish sending bytes

		for(byte=0; byte<3; byte++)	// Get the 24-bit result.
		{
			ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[byte]);		// Read the result bytes
			data[byte] &= 0x00FF;									// Save only LSB
		}

		iso_deassert_CS();				// Finish SSI read transaction

		// Concatenate the 3 bytes of the 24 bit count
		result =  (data[0] << 16) | (data[1] << 8 ) | data[2];

		// Mask off MSB of 32 bit word
		result = 0x00FFFFFF & result;


		// Check to see if MSB is set (if MSB == 1, then we have a negative #, must convert appropriately)
		if( (result & 0x00800000) == 0x00800000 )
		{
			// Do the 2's complement conversion (we have a negative number)
			result = ~result;
			result = 0x007FFFFF & result;
			++result;
			result *= -1;
		}

		//voltages[i] = ((float) sample/gain) * COUNTSTOVOLTS;
		voltages[i] = ((float) result/gain) * COUNTSTOVOLTS;
		sample += result;
	}

	mean = ((float) (sample/trials/gain)) * COUNTSTOVOLTS;

	if(trials > 1)
	{
	    sum_deviation = 0;
		for (i=0; i<trials; i++)
		{
			sum_deviation += (voltages[i]-mean)*(voltages[i]-mean);
			//sum_deviation += pow((voltages[i]-mean), 2);
		}
		//sys_data.AD24_std = sqrt(sum_deviation/(trials-1));   //Bessel correction is not good at low number of trials
		sys_samp.AD24_std = sqrt(sum_deviation/trials);       // THOM 7 Jun 2018 - should check for number of trials and if low num use standard


	}
	else
	{
		sys_samp.AD24_std = 0;
	}

	return ((float) (sample/trials/gain)) * COUNTSTOVOLTS;
}

/*
 * ADS1248_set_gain_sps()
 * Sets the PGA gain and data rate (samples per second).
 * Valid values for gain are: 1,2,4,8,16,32,64 and 128.
 * Valid values for data rate are: 5,10,20,40,80,160,320,640,1000 and 2000 sps.
 */
unsigned char ADS1248_gain_sps(int gain, int sps)
{
	unsigned char gain_sps;

	switch(gain)
	{
		case 1:
			gain = PGA_1;
			break;

		case 2:
			gain = PGA_2;
			break;

		case 4:
			gain = PGA_4;
			break;

		case 8:
			gain = PGA_8;
			break;

		case 16:
			gain = PGA_16;
			break;

		case 32:
			gain = PGA_32;
			break;

		case 64:
			gain = PGA_64;
			break;

		case 128:
			gain = PGA_128;
			break;

		default:
			uprintf("\r\n\r\nUnrecognized PGA gain. Setting to 1.");
			gain = PGA_1;
	}

	switch(sps)
	{
		case 5:
			sps = SPS_5;
			break;

		case 10:
			sps = SPS_10;
			break;

		case 20:
			sps = SPS_20;
			break;

		case 40:
			sps = SPS_40;
			break;

		case 80:
			sps = SPS_80;
			break;

		case 160:
			sps = SPS_160;
			break;

		case 320:
			sps = SPS_320;
			break;

		case 640:
			sps = SPS_640;
			break;

		case 1000:
			sps = SPS_1000;
			break;

		case 2000:
			sps = SPS_2000;
			break;

		default:
			uprintf("\r\n\r\nUnrecognized data rate. Setting to 5 sps.");
			sps = SPS_5;
	}

	gain_sps = gain | sps;	// Combine the two nibbles

	return gain_sps;
}

/*
 * ADS1248_gpio0()
 * Write ADS1248 GPIO0 used to measure bias bat on NanoFET
  */
void ADS1248_gpio0(uint32_t state)
{
    uint32_t gpiodat, data[4];

#if BOARD_NANOFET >= 1

    // Write configuration data to GPIOCFG, GPIODIR, GPIODAT in one transaction
    iso_assert_CS();                                    // Start SSI register write transaction

    gpiodat = 0x00000000;
    if(state == 1)
    {
        gpiodat = 0x00000001;
    }

    while(ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[0]));     // Flush receive FIFO

    ROM_SSIDataPutNonBlocking(SSI3_BASE, WREG + REG_GPIODAT);   // Start writing at REG_GPIODAT  (WREG=0x40, see ADS1248 datasheet)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, 0);                    // Write 1 bytes (n-1)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiodat);              // write 00, 01, 10, 11 for channel select

    while(ROM_SSIBusy(SSI3_BASE));                      // Let SSI finish sending bytes
    iso_deassert_CS();                                  // Finish SSI register write transaction
#endif
}

/*
 * ADS1248_gpio2()
 * Write ADS1248 GPIO2 used to control 'offset voltage measurement'
  */
void ADS1248_gpio2(uint32_t state)
{
    uint32_t gpiodat, data[4];

#if BOARD_MFET >= 1  || BOARD_MPHOX >= 1    // THOM TODO - gpio2 check if used on nanofet

    // Write configuration data to GPIOCFG, GPIODIR, GPIODAT in one transaction
    iso_assert_CS();                                    // Start SSI register write transaction

    gpiodat = 0x00000000;
    if(state == 1)
    {
        gpiodat = 0x00000004;
    }

    while(ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[0]));     // Flush receive FIFO

    ROM_SSIDataPutNonBlocking(SSI3_BASE, WREG + REG_GPIODAT);   // Start writing at REG_GPIODAT  (WREG=0x40, see ADS1248 datasheet)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, 0);                    // Write 1 bytes (n-1)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiodat);              // write 00, 01, 10, 11 for channel select

    while(ROM_SSIBusy(SSI3_BASE));                      // Let SSI finish sending bytes
    iso_deassert_CS();                                  // Finish SSI register write transaction
#endif
}


void ADS1248_gpio_test(void)
{
#if BOARD_MFET >= 1  || BOARD_MPHOX >= 1

    openADS1248_iso();
    ROM_SysCtlDelay(MILLISECOND * 100);
    ADS1248_gpio_init();
    while(1)
    {
        ADS1248_gpio2(0);
        ROM_SysCtlDelay(MILLISECOND*1);
        ADS1248_gpio3(0);
        ROM_SysCtlDelay(MILLISECOND*1);

        ADS1248_gpio2(1);
        ROM_SysCtlDelay(MILLISECOND*1);
        ADS1248_gpio3(1);
        ROM_SysCtlDelay(MILLISECOND*1);
    }

    closeADS1248_iso();
#endif
}

/*
 * ADS1248_gpio3()
 * Write ADS1248 GPIO3 used to control iso battery bias voltage  (float measures gnd, 0 measures bias-, 1 measures bias+)
  */
void ADS1248_gpio3(uint32_t state)
{
    uint32_t gpiodat, data[4];

#if BOARD_MFET >= 1  || BOARD_MPHOX >= 1

    // Write configuration data to GPIOCFG, GPIODIR, GPIODAT in one transaction
    iso_assert_CS();                                    // Start SSI register write transaction

    gpiodat = 0x00000000;
    if(state == 1)
    {
        gpiodat = 0x00000008;
    }

    while(ROM_SSIDataGetNonBlocking(SSI3_BASE, &data[0]));     // Flush receive FIFO

    ROM_SSIDataPutNonBlocking(SSI3_BASE, WREG + REG_GPIODAT);   // Start writing at REG_GPIODAT  (WREG=0x40, see ADS1248 datasheet)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, 0);                    // Write 1 bytes (n-1)
    ROM_SSIDataPutNonBlocking(SSI3_BASE, gpiodat);              // write 00, 01, 10, 11 for channel select

    while(ROM_SSIBusy(SSI3_BASE));                      // Let SSI finish sending bytes
    iso_deassert_CS();                                  // Finish SSI register write transaction
#endif
}
