// Scanlib.c

//Includes
#include "scan.h"

// COM Port Definitions
#define RECBUFFSIZE (2048)		//Make this bigger if you think you need it
#define XMITBUFFSIZE (2048)
ushort ReadData = 0x0000;
ushort ReadConfig = 0x4000;
ushort COM2_Config = 0xC429;				// 38400/O/8/1  (Odd parity generated in putchar())
ushort COM3_Config = 0xC40B;				// 9600/N/8/1

unsigned char *TopOfRecBuffer0, *HeadRecBuffer0, *TailRecBuffer0;
unsigned char *TopOfRecBuffer1, *HeadRecBuffer1, *TailRecBuffer1;


// QPB Definitions
QPBDev MAX146DevTemplate = 
	{
	MAX146_SLOT,		// qslot			our qspi slot
	"MAX146",			// devName			C string with device name (15 max)
	2000000,			// maxBaud			maximum baud rate in Hz for device
	16,					// bits				Bits Per Transfer
	iaLowSCK,			// clockPolar		SPI Clock Polarity
	captLead,			// clockPhase		SPI Clock Phase
	true,				// contCSMulti		Continue CS assert between mult xfrs
	true,				// autoTiming		Auto adjust timing to clock flag
	0,					// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,					// psDelayTXFR		Min. Delay After Transfer
	0,					// *rcvData			pointer to received data buffer
	0					// xfrCount			words transferred
	};

QPBDev U7DevTemplate = 
	{
	U7_SLOT,			// qslot			our qspi slot
	"U7",				// devName			C string with device name (15 max)
	2000000,			// maxBaud			maximum baud rate in Hz for device
	8,					// bits				Bits Per Transfer
	iaLowSCK,			// clockPolar		SPI Clock Polarity
	captLead,			// clockPhase		SPI Clock Phase
	false,				// contCSMulti		Continue CS assert between mult xfrs
	true,				// autoTiming		Auto adjust timing to clock flag
	0,					// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,					// psDelayTXFR		Min. Delay After Transfer
	0,					// *rcvData			pointer to received data buffer
	0					// xfrCount			words transferred
	};

QPBDev COM2DevStruct = 
	{
	COM2_SLOT,				// qslot				our qspi slot (non-multiplexed connection to PCS0 -- tps)
	"Max3100_0\0",			// devName			C string with device name (15 max)
	42500000,				// maxBaud			maximum baud rate in Hz for device
	16,						// bits				Bits Per Transfer
	iaLowSCK,				// clockPolar		SPI Clock Polarity (iaHighSCK,iaLowSCK)
	captLead,				// clockPhase		SPI Clock Phase (captLead/captFall)
	true,						// contCSMulti		Continue CS assert between mult xfrs
	true,						// autoTiming		Auto adjust timing to clock flag
	0,							// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,							// psDelayTXFR		Min. Delay After Transfer
	0,							// *rcvData			pointer to received data buffer
	0							// xfrCount			words transferred
	};

QPBDev COM3DevStruct = 
	{
	COM3_SLOT,				// qslot				our qspi slot (non-multiplexed connection to PCS0 -- tps)
	"Max3100_1\0",			// devName			C string with device name (15 max)
	42500000,				// maxBaud			maximum baud rate in Hz for device
	16,						// bits				Bits Per Transfer
	iaLowSCK,				// clockPolar		SPI Clock Polarity (iaHighSCK,iaLowSCK)
	captLead,				// clockPhase		SPI Clock Phase (captLead/captFall)
	true,						// contCSMulti		Continue CS assert between mult xfrs
	true,						// autoTiming		Auto adjust timing to clock flag
	0,							// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,							// psDelayTXFR		Min. Delay After Transfer
	0,							// *rcvData			pointer to received data buffer
	0							// xfrCount			words transferred
	};

QPBDev U1DevTemplate = 
	{
	U1_SLOT,			// qslot			our qspi slot
	"U1",				// devName			C string with device name (15 max)
	2000000,			// maxBaud			maximum baud rate in Hz for device
	8,					// bits				Bits Per Transfer
	iaLowSCK,			// clockPolar		SPI Clock Polarity
	captLead,			// clockPhase		SPI Clock Phase
	false,				// contCSMulti		Continue CS assert between mult xfrs
	true,				// autoTiming		Auto adjust timing to clock flag
	0,					// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,					// psDelayTXFR		Min. Delay After Transfer
	0,					// *rcvData			pointer to received data buffer
	0					// xfrCount			words transferred
	};

QPBDev U14DevTemplate = 
	{
	U14_SLOT,			// qslot			our qspi slot
	"U14",				// devName			C string with device name (15 max)
	2000000,			// maxBaud			maximum baud rate in Hz for device
	8,					// bits				Bits Per Transfer
	iaLowSCK,			// clockPolar		SPI Clock Polarity
	captLead,			// clockPhase		SPI Clock Phase
	false,				// contCSMulti		Continue CS assert between mult xfrs
	true,				// autoTiming		Auto adjust timing to clock flag
	0,					// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,					// psDelayTXFR		Min. Delay After Transfer
	0,					// *rcvData			pointer to received data buffer
	0					// xfrCount			words transferred
	};

QPBDev U5DevTemplate = 
	{
	U5_SLOT,			// qslot			our qspi slot
	"U5",				// devName			C string with device name (15 max)
	2000000,			// maxBaud			maximum baud rate in Hz for device
	8,					// bits				Bits Per Transfer
	iaLowSCK,			// clockPolar		SPI Clock Polarity
	captLead,			// clockPhase		SPI Clock Phase
	false,				// contCSMulti		Continue CS assert between mult xfrs
	true,				// autoTiming		Auto adjust timing to clock flag
	0,					// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,					// psDelayTXFR		Min. Delay After Transfer
	0,					// *rcvData			pointer to received data buffer
	0					// xfrCount			words transferred
	};

static QPB		*QPB_MAX146_Slot = NULL;
static QPB		*QPB_U7_Slot = NULL;
static QPB		*QPB_COM2_Slot = NULL;
static QPB		*QPB_COM3_Slot = NULL;
static QPB		*QPB_U1_Slot = NULL;
static QPB		*QPB_U14_Slot = NULL;
static QPB		*QPB_U5_Slot = NULL;


// MAX146 Definitions
enum { 	Max146CMD		= 0x80	};
enum { 	Max146SGL		= 0x04, 		Max146DIF		= 0x00	};
enum { 	Max146UNI		= 0x08, 		Max146BIP		= 0x00	};
enum { 	Max146PDFull	= 0x00,			Max146PDFast	= 0x01,
		Max146INT		= 0x02, 		Max146EXT		= 0x03	};


const uchar Max146SglChSel[8] = 	// convert chan to single-ended selector
		{ 0x00, 0x40, 0x10, 0x50, 0x20, 0x60, 0x30, 0x70 };
const uchar Max146DifChSel[8] = 	// convert chan to differential selector
		{ 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70 };


// Video Camera Definitions
unsigned char video_record[] = 	{3,0x20,0x02,0x22}; 
unsigned char video_stop[] = 	{3,0x20,0x00,0x20}; 
		
// Function pointers
vfptr	scan_mot_int_ptr;

// Global Variables
extern struct def defaults;

int		g_grab_turns;
int		g_scan_mot_steps;
ushort 	g_U1_byte = 0;
ushort	g_U5_byte = 0x3F;
ushort	g_U7_byte = 0;
ushort	g_U14_byte = 0;

/*************************************************/

void init(void)
{

	PinWrite(33, 0);			// 5V BUS
	PinWrite(34, 0);			// LED1-ON
	PinWrite(35, 0);			// LED2-ON
	PinWrite(37, 0);			// STEP
	PinWrite(22, 0);			// CLEAR-FIFO
	PinWrite(23, 0);			// READ-FIFO
	PinWrite(25, 0);			// PING
	PinRead(24);				// PM_COUNTS
	PinRead(26);				// FILTER-INDEX
	PinRead(27);				// FOOTPAD
	PinRead(29);				// GRAB_LIMIT
	PinRead(32);				// GRAB_TURNS
	PinRead(31);				// Z_LIMIT
	PinRead(28);				// Y_LIMIT
	PinRead(30);				// X_LIMIT
	
	
	QPBInit(true);	
	QPB_U1_Slot = QPBInitSlot(&U1DevTemplate);		// Init U1 slot
	QPBTransact(QPB_U1_Slot, 0, 1, &g_U1_byte);		// All off

	QPB_U5_Slot = QPBInitSlot(&U5DevTemplate);		// Init U5 slot
	QPBTransact(QPB_U5_Slot, 0, 1, &g_U5_byte);		// Burnwires off, Stir power off

	QPB_U7_Slot = QPBInitSlot(&U7DevTemplate);		// Init U7 slot
	QPBTransact(QPB_U7_Slot, 0, 1, &g_U7_byte);		// All off

	QPB_U14_Slot = QPBInitSlot(&U14DevTemplate);	// Init U14 slot
	QPBTransact(QPB_U14_Slot, 0, 1, &g_U14_byte);		// All off

	QPB_MAX146_Slot = QPBInitSlot(&MAX146DevTemplate);	// Init MAX146 Slot
	
	
	// COM Port Initialization
	QPB_COM2_Slot = QPBInitSlot(&COM2DevStruct);	
	QPB_COM3_Slot = QPBInitSlot(&COM3DevStruct);	
	COM2Init();
	COM3Init();
	
	// Transceiver data port setup
	CS8Setup(0x00100000, 0x00000001, true);	// BaseAddr/size/16-bit
	CS8Options(true, false, false, 0);		// Read/Write/dsSync/wait

	// Grab turns interrupt setup
	CTSAModeSelect (SASM18B, SASMInCap);
	CTSAEdgeSelect (SASM18B, false);		// Grab turns, negative edge
	CTSAInterruptFunction (SASM18B, &grab_turns_interrupt);
	CTSAInterruptLevel (SASM18B, 1,1);

	PinWrite(33, 1);	// Turn on 5V bus
}

/****************************************************/

float battery(void)
{
	short sample;
	
	sample = Max146Sample(AD0, true, true, false);	// Chan, Uni, Sgl, Pd
	return ((float)sample) / BATTERY_CAL;
}

/***************************************************/

float backup_bat(void)
{
	short sample;
	
	sample = Max146Sample(AD1, true, true, false);	// Chan, Uni, Sgl, Pd
	return ((float)sample) / BACKUP_BAT_CAL;
}

/***************************************************
	COM Port Routines (MAX3111)
****************************************************/

// COM2 Interrupt Service Routine

IEV_C_FUNCT(COM2Interrupt)
{
	#pragma unused(ievstack)

	ushort RecDataPort0;
	
	//	Read data from Port0
	QPBTransact(QPB_COM2_Slot, 0, 1, &ReadData);	// Read data command
	RecDataPort0 = QPB_COM2_Slot->dev->rcvData[0];

	if(RecDataPort0 & 0x8000)		//	If R (bit 15) == 1
	{
		*HeadRecBuffer0 = (uchar)(RecDataPort0 & 0x00FF);	// Move lower 8 bits to the receive buffer
		HeadRecBuffer0++;
		if(HeadRecBuffer0 >= (char *)(TopOfRecBuffer0+RECBUFFSIZE))	// Loop around if end of ring buffer
		{
			HeadRecBuffer0 = TopOfRecBuffer0;
		}
	}

	
}


/*****************************************************/

void COM2Init(void)
	{

//	To handle our interupts from the MAX3100 we install this handler
	IEVInsertCFunct(&COM2Interrupt, (short) level2InterruptAutovector);


//	Buffers point nowhere to start
	TopOfRecBuffer0 = NULL;
	HeadRecBuffer0 = NULL;
	TailRecBuffer0 = NULL;

//	Allocate memory for the buffers
	TopOfRecBuffer0 = malloc(RECBUFFSIZE * sizeof(char));
	if(TopOfRecBuffer0 == NULL)
	{
		cprintf("Cannot allocate memory for receive buffer 0\n\n");
		BIOSResetToPicoDOS();
	}
	else
	{
		COM2Flush();	//Resets everthing
	}

//	Send the configuration command
	QPBTransact(QPB_COM2_Slot, 0, 1, &COM2_Config);	// Configure COM2

//	Enable -IRQ2 as a bus function (normally it is an I/O pin and by default it is an input)
	PIOBusFunct(41);

	}	// COM2Init()

/***************************************************/

int COM2ByteAvail(void)
{
	if(TailRecBuffer0 == HeadRecBuffer0) return 0;
	else return 1;
}


/********************************************************/

void COM2Flush(void)
{
	ushort SaveSR;
//	Disable interupts 
	SaveSR = IEVSaveSRThenDisable();

	HeadRecBuffer0 = TailRecBuffer0 = TopOfRecBuffer0;

//	Restore interupts to what they were
	IEVRestoreSavedSR(SaveSR);
}




/********************************************************/

int COM2GetChar(void)
{
	ushort SaveSR;
	unsigned char c;

//	Disable interupts 
	SaveSR = IEVSaveSRThenDisable();

	if(TailRecBuffer0 == HeadRecBuffer0)
	{
		c = -1;
	}
	else
	{
		c = *TailRecBuffer0;

		TailRecBuffer0++;

		if(TailRecBuffer0 >= (char *)(TopOfRecBuffer0+RECBUFFSIZE))
		{
			TailRecBuffer0 = TopOfRecBuffer0;
		}
	}

//	Restore interupts to what they were
	IEVRestoreSavedSR(SaveSR);

	return (int ) c;
}


/***************************************************/

bool COM2PutChar(ushort c)
{
	int odd = 0, bit;
	ushort txdata, parity_mask;
	
	txdata = c;
	for(bit=0;bit<8;bit++){				// Determine if output byte is odd or even
		if(txdata & 0x0001) odd = ~odd;
		txdata >>= 1;
	}
	if(odd) parity_mask = 0x8000;		// If byte is odd, clear parity bit for odd parity
	else parity_mask = 0x8100;
	
	// Loop here until tx buffer is empty
	do{
		QPBTransact(QPB_COM2_Slot, 0, 1, &ReadData);
		txdata = QPB_COM2_Slot->dev->rcvData[0]; 
	}while(!(txdata & 0x4000));	
		
	txdata = c & 0x00FF;
	txdata |= parity_mask;
	QPBTransact(QPB_COM2_Slot, 0, 1, &txdata);

	return true;	

}


/**********************************************************************/

// COM3 Interrupt Service Routine

IEV_C_PROTO(COM3Interrupt);
IEV_C_FUNCT(COM3Interrupt)
{
	#pragma unused(ievstack)

	ushort RecDataPort0;
	
	//	Read data from Port0
	QPBTransact(QPB_COM3_Slot, 0, 1, &ReadData);	// Read data command
	RecDataPort0 = QPB_COM3_Slot->dev->rcvData[0];

	if(RecDataPort0 & 0x8000)		//	If R (bit 15) == 1
	{
		*HeadRecBuffer1 = (uchar)(RecDataPort0 & 0x00FF);	// Move lower 8 bits to the receive buffer
		HeadRecBuffer1++;
		if(HeadRecBuffer1 >= (char *)(TopOfRecBuffer1+RECBUFFSIZE))	// Loop around if end of ring buffer
		{
			HeadRecBuffer1 = TopOfRecBuffer1;
		}
	}

	
}

/***************************************************/


void COM3Init(void)
	{

//	To handle our interupts from the MAX3111 we install this handler
	IEVInsertCFunct(&COM3Interrupt, (short) level5InterruptAutovector);


//	Buffers point nowhere to start
	TopOfRecBuffer1 = NULL;
	HeadRecBuffer1 = NULL;
	TailRecBuffer1 = NULL;

//	Allocate memory for the buffers
	TopOfRecBuffer1 = malloc(RECBUFFSIZE * sizeof(char));
	if(TopOfRecBuffer1 == NULL)
	{
		cprintf("Cannot allocate memory for receive buffer 1\n\n");
		BIOSResetToPicoDOS();
	}
	else
	{
		COM3Flush();	//Resets everthing
	}

	QPBTransact(QPB_COM3_Slot, 0, 1, &COM3_Config);	// Configure COM3

//	Enable -IRQ5 as a bus function
	PIOBusFunct(39);

	}	// COM3Init()

/*****************************************************/


int COM3ByteAvail(void)
{
	if(TailRecBuffer1 == HeadRecBuffer1) return 0;
	else return 1;
}


/********************************************************/

void COM3Flush(void)
{
	ushort SaveSR;
	SaveSR = IEVSaveSRThenDisable();

	HeadRecBuffer1 = TailRecBuffer1 = TopOfRecBuffer1;

	IEVRestoreSavedSR(SaveSR);
}


/********************************************************/

int COM3GetChar(void)
{
	ushort SaveSR;
	unsigned char c;

//	Disable interupts 
	SaveSR = IEVSaveSRThenDisable();

	if(TailRecBuffer1 == HeadRecBuffer1)
	{
		c = -1;
	}
	else
	{
		c = *TailRecBuffer1;

		TailRecBuffer1++;

		if(TailRecBuffer1 >= (char *)(TopOfRecBuffer1+RECBUFFSIZE))
		{
			TailRecBuffer1 = TopOfRecBuffer1;
		}
	}

//	Restore interupts to what they were
	IEVRestoreSavedSR(SaveSR);

	return (int ) c;
}


/***************************************************/

bool COM3PutChar(ushort c)
{
	ushort txdata;
	
	// Loop here until tx buffer is empty
	do{
		QPBTransact(QPB_COM3_Slot, 0, 1, &ReadData);
		txdata = QPB_COM3_Slot->dev->rcvData[0]; 
	}while(!(txdata & 0x4000));	
		
	txdata = (ushort)c & 0x00FF;
	txdata |= 0x8000;
	QPBTransact(QPB_COM3_Slot, 0, 1, &txdata);

	return true;	

}


/***************************************************/

void delay_secs(int secs)
{
	int x;
	
	for(x=0;x<secs;x++)	RTCDelayMicroSeconds(1000000);

}

/***************************************************/

float depth(void)
{

	short sample;
	press_pwr_on();
	RTCDelayMicroSeconds(100000);	
	sample = Max146Sample(AD2, true, true, false);	// Chan, Uni, Sgl, Pd
	press_pwr_off();
	return ((float)sample - defaults.depth_zero) / defaults.depth_cal;
}


/***************************************************/

int filter_motor(int function)
{
	int step;
	
	if(function == INDEX)
	{
	
		step = 0;
		while(PinRead(26))					// Move to index
		{
			PinWrite(37, 1);				// Set STEP pin
			RTCDelayMicroSeconds(10000);	// Set for maximum step rate
			PinWrite(37, 0);				// Clear STEP pin
			step++;
			if(step > 200)
			{
				cprintf("\nFilter motor timeout...\n");
				return 1;
			}
		}
	}
	
	else if(function == NEXT_FILTER)
	{	
		for(step=0;step<24;step++)			// Move to next filter position
		{
			PinWrite(37, 1);				// Set STEP pin
			RTCDelayMicroSeconds(5000);		// Set for maximum step rate
			PinWrite(37, 0);				// Clear STEP pin
			RTCDelayMicroSeconds(5000);		// Set for maximum step rate
		}
	}
	
	return 0;
}


/**********************************************************/

void fluor_cycle(int x, int y)
{
	int pe_count, cl_count;

	photomult_pwr_on();
	filter_motor(INDEX);
	led1_on();
	pe_count = pm_counts();
	led1_off();
	filter_motor(NEXT_FILTER);
	led1_on();
	cl_count = pm_counts();
	led1_off();
	fprintf(fluor_file_ptr, "\n%d\t%d\t%d\t%d", x, y, pe_count, cl_count);	
	photomult_pwr_off();
	
}


/**********************************************************/

int grab_down_cycle(void){
  time_t	time_out;
  
  sensor_pwr_on();
  delay_secs(1);

  kbflush();
  store_dep_data("Grab down cycle start");
  time_out = time(NULL) + GRAB_TIME_OUT;     /* Start time for timeout */
  CTSAInterruptEnable (SASM18B, ON);	/* Enable grab turns interrupt */
  g_grab_turns = 0;
  grab_motor(DOWN);         /* Move grab down */
  cprintf("\nGoing down!");
  
  while(1)
  {
	if(g_grab_turns >= defaults.grab_turns) break;	// Too many turns?
	if(footpad()) break;							// Footpad activated?      
  	if(time(NULL) > time_out)	// Too much time?
  	{
		grab_motor(STOP);
		store_dep_data("Grab down cycle time out");
		return 1;	
    }

  }         /* End of rack down main loop */

  grab_motor(STOP);              /* Benthic chambers planted--hopefully */
  CTSAInterruptEnable (SASM18B, OFF);	/* Disable grab turns interrupt */
  sensor_pwr_off();
  cprintf("\nGrab down");
  
  store_dep_data("Grab down cycle end"); 
  return 0;
}

/************************************************************/

int grab_up_cycle(void)
{
  time_t	time_out;

  sensor_pwr_on();
  delay_secs(1);

  kbflush();
  store_dep_data("Grab up cycle start");
  time_out = time(NULL) + GRAB_TIME_OUT;     /* Start time for timeout */
  grab_motor(UP);         /* Move grab up */
  cprintf("\nGoing up!");
  
  while(1)
  {
	      
  	if(grab_limit()) break;		// We're at the top!
  	
  	if(time(NULL) > time_out)	// Too much time?
  	{
		grab_motor(STOP);
		store_dep_data("Grab up cycle time out");
		return 1;	
    }

  }         /* End of rack down main loop */

  grab_motor(STOP);    
  sensor_pwr_off();
  cprintf("\nGrab at top");
  store_dep_data("Grab up cycle end"); 
  return 0;

}


/***************************************************/

void grab_motor(int dir)
{

	switch(dir)
	{
		case DOWN:
			U1_control(GRAB_UP, ON);		
			U1_control(GRAB_DOWN, OFF);		
			U1_control(GRAB_MOT_ON, ON);		
			break;
			
		case UP:
			U1_control(GRAB_UP, OFF);		
			U1_control(GRAB_DOWN, ON);		
			U1_control(GRAB_MOT_ON, ON);
			break;
			
		case STOP:
			U1_control(GRAB_MOT_ON, OFF);
			U1_control(GRAB_UP, OFF);		
			U1_control(GRAB_DOWN, OFF);		
			break;
			
	}
					

}

/***************************************************/


IEV_C_FUNCT(grab_turns_interrupt)
{
	#pragma unused(ievstack)

	CTSAClearEventFlag (SASM18B);
	g_grab_turns++;
}

/******************************************************************************\
**	Irq4RxISR			Interrupt handler for IRQ4 (tied to CMOS RxD)
**	
**	This single interrrupt service routine handles both the IRQ4 interrupt
**	and the very likely spurious interrupts that my be generated by the
**	repeated asynchronous and non-acknowledged pulses of RS-232 input.
**	
**	The handler simply reverts IRQ4 back to input (to prevent further level
**	sensative interrupts) and returns. It's assumed the routine that set this
**	up is just looking for the side effect of breaking the CPU out of STOP
**	or LPSTOP mode.
**	
**	Note that this very simple handler is defined as a normal C function
**	rather than an IEV_C_PROTO/IEV_C_FUNCT. We can do this because we know
**	(and can verify by checking the disassembly) that is generates only
**	direct addressing instructions and will not modify any registers.
\******************************************************************************/
void Irq4RxISR(void)
	{

	PinIO(IRQ4RXD);		// 31 // /IRQ4 (tied to Rx)
	RTE();

	}	//____ Irq4RxISR() ____//


/******************************************************************************/

short Max146Sample(ushort chan, bool uni, bool sgl, bool pd)
	{
	ushort		adcmd[3];
	short		count = 2;

//	Setup the command word we send to the Max146 which is a combination of
//	selector bits that pick the channel, mode, and clocking.
	adcmd[0] = adcmd[1] =
			 		(sgl	? (Max146SglChSel[chan] | Max146SGL)
							: (Max146DifChSel[chan] | Max146DIF))
				|	(uni	? Max146UNI : Max146BIP)
				|	Max146CMD;

	adcmd[0] |= Max146EXT;
	if (pd)
		{
		adcmd[1] |= Max146PDFast;
		adcmd[count++] = 0;
		}
	else
		adcmd[1] |= Max146EXT;
		
//	Perform a QSPI transaction with two transfers, the first sets up the next
//	conversion, and the second value clocks out the previous channel results.
//	We don't set the QPBASync bit in the command count so the function will
//	wait for the conversion to complete before returning.

	QPBTransact(QPB_MAX146_Slot, 0, count, adcmd);
//	The rcvData array contains the real result in its second word since word
//	one is the result of some previous conversion.

	return QPB_MAX146_Slot->dev->rcvData[1];

	}	//____ Max146Sample() ____//


/***************************************************/

int pm_counts(void)
{
	int counts=0, pin=0;
	long timer;
	
	timer = 160000;		// About 1 sec at 16MHz clock
	while(timer)
	{
		timer--;
		if(PinRead(24)) pin = 1;
		if(!PinRead(24) && pin == 1)	// Look for the transition from 1 to 0
		{
		 	counts++;
		 	pin = 0;
		}
	}
	
	
	return counts;
	
}


/***************************************************/

void ping_cycle(int ping_avg)
{
	int sample, ping, bin, bin_sample, samples_per_bin;
	ulong delay_time;
	long temp;
		
	delay_time = MAX_SAMPLE_TIME;	// Roughly 15 cm sample length 
	samples_per_bin = SAMPLES_PER_MM * BIN_LENGTH;

	for(sample=0; sample < MAX_SAMPLES; sample++) g_buffer[sample] = 0;	// Clear buffer	
	
	for(ping=0; ping<ping_avg; ping++)		// Ping many times and store sum of echos
	{
		clear_fifo_on();
		clear_fifo_off();
		ping_on();
		RTCDelayMicroSeconds(delay_time);
		ping_off();
	
		for(sample=0; sample < MAX_SAMPLES; sample++)
		{
			read_fifo_on();
			read_fifo_off();		
			g_buffer[sample] += *((vushort *)0x00100000);
		}
	}

	for(sample=0; sample < MAX_SAMPLES; sample++) g_buffer[sample]/= ping_avg;	// Divide buffer by PING_AVG	

											
	// Rectify and sum the samples
	sample = 0;
	for(bin=START_BIN; bin<=END_BIN; bin++)
	{
		temp = 0;

		for(bin_sample=0; bin_sample<samples_per_bin; bin_sample++)
		{
			temp += labs(g_buffer[sample] - g_ad_zero);
			sample++;
		}
		
		g_bin_buffer[bin] = (ushort)(temp/samples_per_bin);
	}
}


/***************************************************/

void scan_ad_zero(void)
{
	int sample;
	ulong delay_time;
	long temp;
		
		
	delay_time = MAX_SAMPLE_TIME;	// Roughly 15 cm sample length 

	clear_fifo_on();
	clear_fifo_off();
	ping_on();
	RTCDelayMicroSeconds(delay_time);
	ping_off();

	for(sample=0; sample < MAX_SAMPLES; sample++)
	{
		read_fifo_on();
		read_fifo_off();		
		g_buffer[sample] = *((vushort *)0x00100000);
	}

	// Find the A/D converter zero point
	temp = 0;
	for(sample=200; sample<500; sample++)
	{
		temp += g_buffer[sample];
	}
	g_ad_zero = temp/300;
	cprintf("\nA/D zero = %ld", g_ad_zero);
											
}

/***************************************************/

int scan_cycle(void)
{
	int steps, x_cell, y_cell, max_x_cells, max_y_cells, x_cell_steps, y_cell_steps;
	int x_fluor_cell, y_fluor_cell, max_x_fluor_cells, max_y_fluor_cells;
	int elev_error, check_z_elev, z_elev, dir, z_elev_buff[MAX_Y_FLUOR_CELLS];
	char fname[15];
	FILE	*fp;
	
	// Setup the interrupts for the Y motor 
	scan_mot_int_ptr = scan_mot_int;
	PITSet100usPeriod(50);			// 5 millisecond
	
	max_x_fluor_cells = defaults.x_scan_len;	// Scan length in cm
	max_y_fluor_cells = defaults.y_scan_len;

	max_x_cells = 10/CELL_SIZE;	// Cell size in mm
	max_y_cells = 10/CELL_SIZE;

	x_cell_steps = CELL_SIZE * X_STEPS_PER_MM;
	y_cell_steps = CELL_SIZE * Y_STEPS_PER_MM;
	
	kbflush();
  	
	
	if(scan_mot_return()) return 1;		// Return all scanner axis to limits
	
	scan_ad_zero();		// Find the a/d converter zero point while transducer is up
	z_elev = 0;

	// Scan an area
	for(x_fluor_cell=0; x_fluor_cell<max_x_fluor_cells; x_fluor_cell++)
	{	
		check_z_elev = 1;		// Run scan_z_cycle() to check elevation
		
		if(x_fluor_cell == 1)	// Shut off the camera after the first ten scans
		{
			flood_off();	  	// Floodlight off.
			video(VIDEO_STOP);
		}
	
		
		// Scan a 1 cm fluorometer swath
		for(x_cell=0; x_cell<max_x_cells; x_cell++)
		{

			// Return Y-axis to limit
			cprintf("\nY-axis to limit...\n");
			steps = 0;
			while(!y_limit())
			{
				scan_motor(Y, BKWD, 1);
				steps++;
				if(steps > MAX_Y_STEPS)
				{
					store_dep_data("Y-axis motor error during scan");
				 	return 1;
				}
			}


			// Open a data file for the forward scan slice
			sprintf(fname, "%d.bin", (x_fluor_cell*10)+x_cell);
			if ((fp = fopen(fname, "wb")) == 0)
			{
				cprintf("\nCouldn't open data file!\n");
				return 1;
			}
			else cprintf("\nOpened %s", fname);
			
			
			// Scan the slice forward checking the Z elevation on each new X fluor cell
			for(y_fluor_cell=0; y_fluor_cell<max_y_fluor_cells; y_fluor_cell++)
			{
				cprintf("\nFWD y_fluor_cell = %d", y_fluor_cell);
				
				if(check_z_elev)
				{
					// Move the transducer down to 2 cm above the sediment 
					if(scan_z_cycle(&z_elev)) return 1;			// Bail out if there's a problem
					z_elev_buff[y_fluor_cell] = z_elev; 		// Remember the z-elevation
					cprintf("\nScan_z_cycle = %d", z_elev);
					// Do the fluorometer thing
					if(x_fluor_cell > 0) fluor_cycle(x_fluor_cell, y_fluor_cell);	// Do the fluorometer thing
				}

				else
				{
					// Figure out how many steps to move the Z axis from stored data
					elev_error = z_elev_buff[y_fluor_cell] - z_elev;
					if(elev_error >= 0) dir = Z_DOWN;
					else if(elev_error < 0) dir = Z_UP;
					scan_motor(Z, dir, abs(elev_error * Z_STEPS_PER_MM));
					z_elev += elev_error;
					cprintf("\nz_elev = %d", z_elev);
				}

				// Store z elevation in mm with ping data
				g_bin_buffer[0] = z_elev_buff[y_fluor_cell];		

				// Setup the Y scan motor to move forward in the background
				PinWrite(37, 0);						// Clear STEP pin
				dir_on();								// Go forward
				y_motor_on();
				
				// Scan forward within a 1cm fluorometer cell
				for(y_cell=0; y_cell<max_y_cells; y_cell++)
				{
					if(kbhit()) return 0;
					cprintf("\ny_cell = %d", y_cell);
					
					// Ping a column
					ping_cycle(PING_AVG);
					g_scan_mot_steps = 0;
					PITAddChore(scan_mot_int_ptr, 6);		// Start the interrupts

					// Store the samples
					fwrite(g_bin_buffer, sizeof(ushort), END_BIN+1, fp);
			
					while(g_scan_mot_steps < Y_STEPS_PER_MM*2){}	// Wait for the stepper to finish
					PITRemoveChore(scan_mot_int_ptr);
				}										// End of forward y_cell

				y_motor_off();
				PinWrite(37, 0);						// Clear STEP pin
				dir_off();
			}											// End of forward y_fluor_cell
			fclose(fp);									// Done with the forward scan slice file
		
			// Move x-axis one cell forward
			scan_motor(X, FWD, x_cell_steps);
			x_cell++;
			check_z_elev = 0;	// Don't need to check elevation after first slice
			
			// Open a data file for the backward scan slice
			sprintf(fname, "%d.bin", (x_fluor_cell*10)+x_cell);
			if ((fp = fopen(fname, "wb")) == 0)
			{
				cprintf("\nCouldn't open data file!\n");
				return 1;
			}
			else cprintf("\nOpened %s", fname);
			
			y_fluor_cell--;		// Decrement once to line things up right
			
			// Scan the slice backward changing the Z elevation from stored data
			for(y_fluor_cell; y_fluor_cell>=0; y_fluor_cell--)
			{
				cprintf("\nBKWD y_fluor_cell = %d", y_fluor_cell);
	
				// Figure out how many steps to move the Z axis using stored data
				elev_error = z_elev_buff[y_fluor_cell] - z_elev;
				if(elev_error >= 0) dir = Z_DOWN;
				else if(elev_error < 0) dir = Z_UP;
				scan_motor(Z, dir, abs(elev_error * Z_STEPS_PER_MM));
				z_elev += elev_error;
				cprintf("\nz_elev = %d", z_elev);
					
				// Store z elevation with ping data
				g_bin_buffer[0] = z_elev_buff[y_fluor_cell];		

				PinWrite(37, 0);						// Clear STEP pin
				dir_off();								// Go backward
				y_motor_on();
				
				// Scan the slice backward
				for(y_cell=max_y_cells-1; y_cell>=0; y_cell--)
				{
					if(kbhit()) return 0;				// Give us a way out
					cprintf("\ny_cell = %d", y_cell);
		
					// Ping a column
					ping_cycle(PING_AVG);
					g_scan_mot_steps = 0;
					PITAddChore(scan_mot_int_ptr, 6);		

					// Store the samples
					fwrite(g_bin_buffer, sizeof(ushort), END_BIN+1, fp);
				
					while(g_scan_mot_steps < Y_STEPS_PER_MM*2){} 
					PITRemoveChore(scan_mot_int_ptr);
				}

				y_motor_off();
				PinWrite(37, 0);						// Clear STEP pin
				dir_off();
			}			// End of backward y_fluor_cell
			fclose(fp);	// Done with the backward scan slice file

			// Move x-axis one cell forward
			scan_motor(X, FWD, x_cell_steps);
		
		}	// End of x_cell
	}	// End of x_fluor_cell


	return 0;	
}


/***************************************************/

void scan_motor(int motor, int dir, int steps)
{
	int step;
	
	switch(motor){
		case X:
			x_motor_on();
			break;
		
		case Y:
			y_motor_on();
			break;
			
		case Z:
			z_motor_on();
			break;
				
		default:
			cprintf("\nUnrecognized motor command...\n");
			
	}  

	if(dir == FWD) dir_on();		// Set motor direction
	else dir_off();
	
	for(step=0;step<steps;step++)
	{
		PinWrite(37, 1);						// Set STEP pin
		RTCDelayMicroSeconds(SCAN_STEP_PER/2);	// Set for maximum step rate
		PinWrite(37, 0);						// Clear STEP pin
		RTCDelayMicroSeconds(SCAN_STEP_PER/2);	// Set for maximum step rate
	}

	switch(motor){
		case X:
			x_motor_off();
			break;
		
		case Y:
			y_motor_off();
			break;
			
		case Z:
			z_motor_off();
			break;
				
		default:
			cprintf("\nUnrecognized motor command...\n");
			
	}  

}

/***************************************************/

void scan_mot_int(void)
{
	// Advance stepper if permissible
	if(g_scan_mot_steps < Y_STEPS_PER_MM*2)	PinToggle(37);
	g_scan_mot_steps++;
}

/***************************************************/

int scan_mot_return(void)
{
	int steps;

	// Return all scanner axes to limits
	cprintf("\nZ-axis to limit...\n");
	steps = 0;
	while(!z_limit())
	{
		scan_motor(Z, Z_UP, 1);
		steps++;
		if(steps > MAX_Z_STEPS)
		{
			cprintf("\nZ-axis motor error\n");
			store_dep_data("Z-axis motor error during scan_mot_return()");
			return 1;
		}
	}
	
	cprintf("\nY-axis to limit...\n");
	steps = 0;
	while(!y_limit())
	{
		scan_motor(Y, BKWD, 1);
		steps++;
		if(steps > MAX_Y_STEPS)
		{
			cprintf("\nY-axis motor error\n");
			store_dep_data("Y-axis motor error during scan_mot_return()");
			return 1;
		}
	}
	
	cprintf("\nX-axis to limit...\n");
	steps = 0;
	while(!x_limit())
	{
		scan_motor(X, BKWD, 1);
		steps++;
		if(steps > MAX_X_STEPS)
		{
			cprintf("\nX-axis motor error\n");
			store_dep_data("Y-axis motor error");
			return 1;
		}
	}
	
	return 0;
}

/***************************************************/

int scan_z_cycle(int *z_elev) 
{
	int z, retry=0; 
	ulong ad_sum_1 = 0, ad_sum_2 = 0;
	int steps, bin;
	ulong delay_time = MAX_SAMPLE_TIME;	// Roughly 15 cm sample length

	kbflush();

	// Move Z-axis to limit
	cprintf("\nZ-axis to limit...\n");
	steps = 0;
	while(!z_limit())
	{
		scan_motor(Z, Z_UP, 1);
		steps++;
		if(steps > MAX_Z_STEPS)
		{
			cprintf("\nZ-axis motor error\n");
			store_dep_data("Z-axis motor error during scan_mot_return()");
			return 1;
		}
	}
	*z_elev = 0;
	

	while(1)
	{
		ping_cycle(32);
		
		for(bin=30; bin<60; bin++)
		{
			ad_sum_1 = g_bin_buffer[bin-2] + g_bin_buffer[bin-1];	// Water
			ad_sum_2 = g_bin_buffer[bin+1] + g_bin_buffer[bin+2];	// Sediment
			
			// Prevent a divide by zero error
			if(!ad_sum_1) ad_sum_1 = 1;			 
		
			// Look for an echo and break if found
			if(ad_sum_2/ad_sum_1 >= 2)
			{
				// Move the transducer down to 30 mm above sediment
				scan_motor(Z, Z_DOWN, Z_STEPS_PER_MM*BIN_LENGTH*(bin-30));
				*z_elev += BIN_LENGTH*(bin-30);
				cprintf("\nVariable step, z_elev = %d", *z_elev);
			 	return 0;
			}
		}
		
		// Move the transducer down to 20 mm
		scan_motor(Z, Z_DOWN, Z_STEPS_PER_MM*20);
		*z_elev += 20;
		cprintf("\n20mm step, z_elev = %d", *z_elev);
		
		// If the transducer gets too low, come back up to the limit and try again
		if(*z_elev >= 180)
		{
			store_dep_data("No sediment surface found");
			z = 0;
			cprintf("\nZ-axis to limit...\n");
			while(!z_limit())
			{
				scan_motor(Z, Z_UP, 1);
				z++;
				if(z > MAX_Z_STEPS)
				{
					cprintf("\nZ-axis motor error\n");
					store_dep_data("Z-axis motor error");
					return 1;		// Fatal error
				}
			}
			
			*z_elev = 0;
			cprintf("\nUsing default elevation");
			store_dep_data("Using default elevation");
			scan_motor(Z, Z_DOWN, defaults.def_elev*Z_STEPS_PER_MM);
			*z_elev = defaults.def_elev;
			cprintf("\nz_elev = %d", *z_elev);
			return 0;
		}
		
		RTCDelayMicroSeconds(10000);	// Let the motor settle
		if(kbhit()) break;				// Give us a way out
	}

	return 0;
}


/***************************************************/

void sleep_with_break(void)
{
	// Shut down all power

	QPBTransact(QPB_U1_Slot, 0, 1, &g_U1_byte);		// All off
	QPBTransact(QPB_U5_Slot, 0, 1, &g_U5_byte);		// Burnwires off, Stir power off
	QPBTransact(QPB_U7_Slot, 0, 1, &g_U7_byte);		// All off
	QPBTransact(QPB_U14_Slot, 0, 1, &g_U14_byte);	// All off
	PinWrite(33, 0);								// Turn off 5V bus

	// Install the interrupt handlers that will break us out from keystroke.
	IEVInsertAsmFunct(Irq4RxISR, level4InterruptAutovector);
	IEVInsertAsmFunct(Irq4RxISR, spuriousInterrupt);

    cprintf("\n\nPress any key to wake from sleep...");

	PITSet51msPeriod(PITOff);	// disable timer (drops power)
	CTMRun(false);				// turn off CTM6 module
	SCITxWaitCompletion();		// let any pending UART data finish
	EIAForceOff(true);			// turn off the RS232 driver
	QSMStop();					// shut down the QSM
	CFEnable(false);			// turn off the CompactFlash card
	PinBus(IRQ4RXD);			// make it an interrupt pin

	LPStopCSE(LPMODE);			// we're here until interrupted

	EIAForceOff(false);			// turn on the RS232 driver
	QSMRun();					// bring back the QSM
	CFEnable(true);				// turn on the CompactFlash card
	ciflush();					// discard any garbage characters
	CTMRun(true);
	PinWrite(33, 1);			// Turn on 5V bus
	cprintf(" Awake\n");			// tell 'em we're back

}

/***************************************************/

void stir_motor(ushort speed, int on_off)
{
#pragma unused(on_off)	

//	*CTMCModulusReg(MCSM30) = speed;
//	CTDASetupOPWM(DASM26, false, false, 14, 0, 8192);	// Pin,busB,posB,bits,le,te
	cprintf("\nStir motor running...press any key to stop\n");
	TMGSetSpeed(320);				// Slow down clock
//	CTMPrescalerRun(true);	// Start the prescaler
	stir_5V_on();
	kbflush();
	if(!speed) speed = 9;	// Prevent a divide by zero error
	while(!kbhit())			// Loop here till key hit
	{
		RTCDelayMicroSeconds ((300000L/speed));
		PIOToggle(37);	// Toggling the pin makes the stir motor step
	}
	
	stir_5V_off();
	TMGSetSpeed(16000);
	PinWrite(37, 0);			// Stop PWM on STEP pin
	
}

/***************************************************/

void store_dep_data(char *msg)
{
	char time_buf[80];
	time_t temp_time;
		
	temp_time = time(NULL);
	strftime(time_buf, 80, "%y/%m/%d %H:%M:%S", localtime(&temp_time));
	fprintf(dep_file_ptr, "\n%s\t%f\t%f\t%s", time_buf, depth(), battery(), msg);		// Time, Depth, Batt
}


/***************************************************/

void test_scan_motor(int motor)
{
	char in_key;
	ushort distance = 1, steps;
	
	do
	{
  		cprintf("\n\n\nF = Forward (Z up)");
  		cprintf("\nB = Backward (Z down)");
  		cprintf("\nE = Exit to Test Menu");
  		cprintf("\n\nEnter Selection: ");

  		kbflush();
		in_key = (char)tolower( getch() );       // Get command in lower case
		switch(in_key)
		{
			case 'f':
				QRushort("\n\nEnter distance (mm)",	"%u", true, &distance, 1, 200);
  				switch(motor)
  				{
  					case X:
  						steps = distance * X_STEPS_PER_MM;
  						break;
  					case Y:
  						steps = distance * Y_STEPS_PER_MM;
  						break;
  					case Z:
  						steps = distance * Z_STEPS_PER_MM;
  						break;
  				}
  				scan_motor(motor, FWD, steps);
  				break;

    		case 'b':
				QRushort("\n\nEnter distance (mm)",	"%u", true, &distance, 1, 200);
  				switch(motor)
  				{
  					case X:
  						steps = distance * X_STEPS_PER_MM;
  						break;
  					case Y:
  						steps = distance * Y_STEPS_PER_MM;
  						break;
  					case Z:
  						steps = distance * Z_STEPS_PER_MM;
  						break;
  				}
      			scan_motor(motor, BKWD, steps);
      			break;
    		
    		case 'e':
      			break;
		}

  		printf("\n\nBattery Voltage: %.2f volts\n", battery());
		if(	x_limit() ) cprintf("\nX-axis limit!");
		if(	y_limit() ) cprintf("\nY-axis limit!");
		if(	z_limit() ) cprintf("\nZ-axis limit!");
  
	} while(in_key != 'e');

}



/***************************************************/

void U1_control(ushort device, char on_off)
{

	if(on_off) g_U1_byte |= device;
	else g_U1_byte &= ~device;
	
	QPBTransact(QPB_U1_Slot, 0, 1, &g_U1_byte);
	
}
	

/***************************************************/

void U5_control(ushort device, char on_off)
{
	
	if(on_off) g_U5_byte |= device;
	else g_U5_byte &= ~device;
	
	QPBTransact(QPB_U5_Slot, 0, 1, &g_U5_byte);
	
}
	

/***************************************************/

void U7_control(ushort device, char on_off)
{
	
	if(on_off) g_U7_byte |= device;
	else g_U7_byte &= ~device;
	
	QPBTransact(QPB_U7_Slot, 0, 1, &g_U7_byte);
	
}
	

/***************************************************/

void U14_control(ushort device, char on_off)
{
	
	if(on_off) g_U14_byte |= device;
	else g_U14_byte &= ~device;
	
	QPBTransact(QPB_U14_Slot, 0, 1, &g_U14_byte);
	
}
	

/***********************************************************/

void video(short function)
{
	com2_on();	
	delay_secs(1);

  	switch(function){

    	case VIDEO_START:
    		video_pwr_on();
	   		delay_secs(10);
    		video_send_packet(video_record);
    		cprintf("\nvideo started\n");
    		break;

    	case VIDEO_STOP:
    		video_send_packet(video_stop);
	   		delay_secs(10);
    		video_pwr_off();
    		cprintf("\nvideo stopped\n");
    		break;

    	default:
      		cprintf("\nUnrecognized video command\n");
  	}
  
	com2_off();
}


/****************************************************/

void video_send_packet(unsigned char *packet)
{
  	int msglen; 		//This function sends a data packet to the video camera
 
  	msglen = *packet;
  	packet++;

  	while(msglen){
  		COM2PutChar(*packet);
   		RTCDelayMicroSeconds(500);
   		packet++;
  		msglen--;
  }
}



