/*
 * David Muller; Germán Alfaro
 * davehmuller@gmail.com; alfaro.germanevera@gmail.com
 *
 * Thom Maughan; tm@mbari.org
 */


/*
 * ADS1248_noniso.c is the "driver" for non-isolated ADS1248.  It assumes 20SPS is
 * desired.
 *
 * openADS1248(): gets the ADS1248 ready for conversions; POWERS THE ADC, ties
 * start and reset high, sets up the SSI lines, sets ADC to 20SPS,
 * default channel, and SDATAC mode. 24 bit counts can be asked for immediately
 * upon openADS1248()s return.
 *
 * pollADS1248(ain_types channel1, ain_types channel2, int trials): gets trials # conversions from the specified
 * channels.  The counts from all the trials are averaged and returned
 * as a float.
 *
 * closeADS1248(): POWERS OFF THE ADC, ties start and reset to low, and disables
 * SSI3.
 *
 *
 *
 *
 * ADS1248GetValue() returns just 1 count. ADS1248GetValue()
 * ADS1248ChangeChannel(...) enter the 2 channels you'd like to measure between.
 */


#include "system.h"
#include "ads1248_noniso.h"


#include "uartstdio.h"	// User local version with larger RX buffer
#include "user_io.h"


#define SDATAC 0x16     //stop reading data continuously command for ADS1248
#define RDATA  0x12      //read data command
#define RDATAC 0x14
#define NOP 0xFF        //no operation command
#define SELFOCAL 0x62   //self offset calibration command

#define MUX_CONTROL_REG0_POS_IN_OFFSET 3
#define MUX_CONTROL_REG0_NEG_IN_OFFSET 0

// 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

/*
 * 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)


//static unsigned long read_ADS1248_noniso_register(unsigned long reg_num);
static uint32_t read_ADS1248_noniso_register(uint32_t reg_num);
static void write_ADS1248_noniso_register(uint32_t reg_num, uint32_t data);
static long Read_ADS1248_noniso_DRDY(void);
static void ADS1248_noniso_print_registers(void);
//static void ADS1248_noniso_set_mode_RDATAC(void);
static void set_ADS1248_noniso_start_high(void);
static void set_ADS1248_noniso_start_low(void);

void ADS1248_noniso_set_mode_SDATAC(void);



static long Read_ADS1248_noniso_DRDY(void)
{
	return ROM_GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_5);
}


void ADS1248_noniso_print_registers(void)
{
	uint32_t data;
	uint32_t count = 0;


	for(count = 0; count <= 0xf; ++count)
	{
		data = read_ADS1248_noniso_register(count);
		uprintf("Reg[%d]: %02x\n\r",count, data);

	}
}


// use of static is to insure this function is called only from this file scope...  getting a warning that the statement is unreachable from compiler
//static void ADS1248_noniso_set_mode_SDATAC(void)
void ADS1248_noniso_set_mode_SDATAC(void)
{
	//Send the stop reading data continuously command (so we can read registers and get conversions by sending RDATA)
	ROM_SSIDataPut(SSI1_BASE, SDATAC);

	// Wait until SSI is done transferring all the data in the transmit FIFO.
	while(ROM_SSIBusy(SSI1_BASE))
	{
		;
	}

	//SDATAC takes effect after the next DRDY low, so wait for that to occur
	while(Read_ADS1248_noniso_DRDY())
	{
		;
	}
}

static void set_ADS1248_noniso_start_high(void)
{
	ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_7, 0xff); 	// START high
}

static void set_ADS1248_noniso_start_low(void)
{
	ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_7, 0x00);	// START low
}

static void power_ADS1248_noniso_on(void)
{
	ROM_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_4, 0xff);	// 2.5V on
}

static void power_ADS1248_noniso_off(void)
{
	ROM_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_4, 0x00);	// 2.5V off
}


static void set_ADS1248_noniso_RESET_high(void)
{
	ROM_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_6, 0xff);	// RESET high
}

static void set_ADS1248_noniso_RESET_low(void)
{
	ROM_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_6, 0x00);	// RESET low
}

void write_ADS1248_noniso_register(uint32_t reg_num, uint32_t data)
{
	ROM_SSIDataPutNonBlocking(SSI1_BASE, 0x40 + reg_num);  //move to the indicated register
	ROM_SSIDataPutNonBlocking(SSI1_BASE, 0x00);            //we'll write in 1 byte
	ROM_SSIDataPutNonBlocking(SSI1_BASE, data);            //this byte
	ROM_SSIDataPutNonBlocking(SSI1_BASE, NOP);             //NOP ensures new value is shifted in all the way
	while(ROM_SSIBusy(SSI1_BASE))                          //let SSI finish sending
	{
		;
	}
}

uint32_t read_ADS1248_noniso_register(uint32_t reg_num)
{
	uint32_t data;
	long count;
	while(ROM_SSIDataGetNonBlocking(SSI1_BASE, &data))
	{
		;
	}
	ROM_SSIDataPutNonBlocking(SSI1_BASE, 0x20  + reg_num);   //read from the indicated register
	ROM_SSIDataPutNonBlocking(SSI1_BASE, 0x00);              //we're just reading 1 byte
	ROM_SSIDataPutNonBlocking(SSI1_BASE, NOP);               //send a NOP to clock out that byte
	for(count=0;count<3;++count)
	{
		ROM_SSIDataGetNonBlocking(SSI1_BASE,&data);
	}
	return data;
}



/*
 * openADS1248() performs initial set up of the ADS1248.
 * POWERS THE ADC, ties start and reset high, sets up the SSI, sets
 * the sampling rate to 20SPS, sets up the internal
 * reference for use, and puts the ADS1248 in SDATAC mode. Conversions
 * can be asked for immediately after this function returns (no additional
 * delays are needed).
 *
 * For safety, it assumes the ADS1248 was just booted and gives the necessary
 * delays as the ADS1248 boots up.
 */
void openADS1248_noniso(void)
{
	power_ADS1248_noniso_on();

	//delay a few ms for power supply to stabilize
	ROM_SysCtlDelay(MILLISECOND*25);

	//tie start high
	set_ADS1248_noniso_start_high();

	//tie reset high
	set_ADS1248_noniso_RESET_high();

	//wait after supply is valid and start and reset are high
	ROM_SysCtlDelay(MILLISECOND*25);

	// Pulse TPS3836 Master Reset (/MR), to make sure everything is in proper state
	set_ADS1248_noniso_RESET_low();
	ROM_SysCtlDelay(MICROSECOND*2);  //reset needs to dwell low for 2 microseconds at least
	set_ADS1248_noniso_RESET_high();

	// SPI communication can begin >300 ms after TPS3836 RESET pulse...
	ROM_SysCtlDelay(MILLISECOND * 400);
	//ROM_SysCtlDelay(MILLISECOND * 300);     // THOM - data sheet says 300msec is absolute max - 200 msec is nominal, part can have short reset by changing hardware CT=GND
	//ROM_SysCtlDelay(MILLISECOND * 100);         // This delay does not affect timing for this routine!!!


	ADS1248_noniso_set_mode_SDATAC();           // 300 msec Note: a SPI bus condition is polled in this routine,

	write_ADS1248_noniso_register(REG_SYS0, 0x00);  // 5 sps = 0x00,  40 sps = 0x03
	// THOM - NOTE: changing the register to 0x03 causes readings at the rails
	//write_ADS1248_noniso_register(REG_SYS0, 0x01);  // 5 sps = 0x00, 10 sps = 0x01  40 sps = 0x03
	write_ADS1248_noniso_register(REG_MUX1, 0x30);  // set up 2.048V internal reference

}


/*
 * pollADS1248() returns the average number of counts from a user
 * specified number of conversions (trials).  User can specify
 * the channels they'd like.
 */
float pollADS1248_noniso(unsigned char channel1, unsigned char channel2, int trials)
{
	//change to the specified channel
	ADS1248ChangeChannel_noniso(channel1, channel2);

	long sample = 0;
	int i;


	//make trials # of conversions
	for (i = 0; i < trials; i++)
	{
		sample += ADS1248GetValue_noniso();
	}

	return ((float) (sample/trials)) * COUNTSTOVOLTS;
}





/*
 * closeADS1248 turns off start, reset, power and disables
 * SSI3.
 */
void closeADS1248_noniso(void)
{
	set_ADS1248_noniso_RESET_high();	// Deassert RESET
	set_ADS1248_noniso_start_low();		// Place ADS1248 in sleep mode
	power_ADS1248_noniso_off();			// 2.5V Analog power off

}



/*
 * Select between ADS1248 channels.  First argument is positive channel,
 * second argument is negative channel.
 * The only acceptable channels are 0-7.
 */
void ADS1248ChangeChannel_noniso(unsigned char channel1, unsigned char channel2)
{
	char newChannel = 0;
	uint32_t garbage[1];

	newChannel = (char) (channel1 << MUX_CONTROL_REG0_POS_IN_OFFSET) | (channel2 << MUX_CONTROL_REG0_NEG_IN_OFFSET);

	// Flush receive FIFO
	while(ROM_SSIDataGetNonBlocking(SSI1_BASE, &garbage[0] ))
	{
		;
	}

	write_ADS1248_noniso_register(REG_MUX0, newChannel);
}



/*
 * ADS1248GetValue() retrieves a conversion from the ADC.  It assumes
 * the ADC is operating in SDATAC mode.
 */
signed long ADS1248GetValue_noniso(void)
{
	//the last 3 cells each store a byte of the 24 bit count
	uint32_t counts[4];

	uint32_t garbage[1];

	//our 24 bit count
	signed long result;

	int i;

	// Flush receive FIFO
	while(ROM_SSIDataGetNonBlocking(SSI1_BASE, &garbage[0] ))
	{
		;
	}


	//send RDATA command (after DRDY goes low)
	while(Read_ADS1248_noniso_DRDY())
	{
		;
	}
	ROM_SSIDataPutNonBlocking(SSI1_BASE, RDATA);
	ROM_SSIDataPutNonBlocking(SSI1_BASE, NOP);  //1st NOP issues 8 SCLK's to get 1st byte of the count
	ROM_SSIDataPutNonBlocking(SSI1_BASE, NOP);  //2nd NOP for 2nd...
	ROM_SSIDataPutNonBlocking(SSI1_BASE, NOP);


	// Wait until SSI is done transferring all the data in the transmit FIFO.
	while(ROM_SSIBusy(SSI1_BASE))
	{
		;
	}


	//get the 4 bytes that come back (0x00 is received after we send RDATA--it is stored in counts[0])
	for(i = 0; i < 4; ++i)
	{
		//This fcn waits until there is data in the receive FIFO before returning.
		ROM_SSIDataGetNonBlocking(SSI1_BASE, &counts[i]);
		//Since we are using 8-bit data, mask off the MSB.
		counts[i] &= 0x00FF;
	}

	//counts[1] is  24:16
	//counts[2] is 15:8
	//counts[3] is 7:0

	//concatenate the 3 bytes of the 24 bit count (the 6 nibbles from the ADC)
	result =  (counts[1] << 16) | (counts[2] << 8 ) | counts[3];

	//make sure upper bits remain 0
	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;
	}

	return(result);


}

