/*  TT8 specific includes  */
#include        <TT8.h>                 /* Tattletale Model 8 Definitions */
#include        <tat332.h>              /* 68332 Tattletale (7,8) Hardware Definitions */
#include        <sim332.h>              /* 68332 System Integration Module Definitions */
#include        <qsm332.h>              /* 68332 Queued Serial Module Definitions */
#include        <dio332.h>              /* 68332 Digital I/O Port Pin Definitions */
#include        <tt8lib.h>              /* definitions and prototypes for Model 8 library */

/*  general C includes  */
#include        <stdio.h>
#include        <stdlib.h>
#include        <userio.h>
#include		<math.h>

/*  includes specific to the ad7716 routines  */
#include		"ASSERT.h"
#include		"memcheck.h"
#include		"ad7716.h"
#include		"logtime.h"
#include		"misc.h"
#include		"text_log.h"

//  Frequency of the crystal which the AD7716 is using
#define			XTAL_FREQ		7.3728e6F

//  Local routines used only within AD7716.c
int AD7716_SPI_buffer_open (long n_points);
void AD7716_SPI_buffer_delete (void);
void AD7716_SPI_buffer_empty (void);
void AD7716_SPI_interface_init (void);
void AD7716_SPI_master_out (void);
void AD7716_SPI_master_in (void);
void AD7716_SPI_rupt_handler (void);
void AD7716_SPI_read_data (void);
void AD7716_reset (void);
void AD7716_command (short LSByte, short MSByte);
void AD7716_rupt_handler (void);
long AD7716_data_to_long (unsigned short low, unsigned short high);
void AD7716_push_data_to_buff (long value);
int AD7716_interrupt_serviced (void);
int AD7716_data_saved (void);
void AD7716_program_hardware (void);
void AD7716_software_SPI_write (int byte);

void AD7716_filter_chan_zero (unsigned short high, unsigned short low);
void AD7716_filter_chan_one (unsigned short high, unsigned short low);
void AD7716_filter_chan_two (unsigned short high, unsigned short low);
void AD7716_filter_chan_three (unsigned short high, unsigned short low);


//  Local structure used to track information describing the 7716 converter
//  setup
struct AD7716  {
	short AD7716_open_flag;			//  non-zero if we the 7716 is active
	short convert_rate;				//	conversion rate
	short save_channels;			//  bit pattern specifies which channels to save
	short n_channels;				//  number of channels to save

	volatile unsigned long icounter;

	volatile unsigned long data_pushed;
									//  reset to zero when the start time is saved; incremented
									//  each time a piece of data is pushed onto the SPI_buffer
	time_tt start_time;				//  time at which we last saved data used in calculating
									//  the actual conversion rate
	double measured_rate;			//  actual conversion rate, as measured using the precision
									//  TT8 clock and a count of the number of conversions recieved
	long rate_calc_threshold;		//  number of data points to push on buffer before recalculating
									//  the conversion rate									
	short n_to_average;				//  Number of points which are averaged together
									//  Also the decimation rate.									
	long c0_total;
	long c1_total;
	long c2_total;
	long c3_total;
	short c0_n_averaged;									
	short c1_n_averaged;									
	short c2_n_averaged;									
	short c3_n_averaged;									
	short SPI_finished;				//  flag set when the SPI completes
	#ifdef SIMULATE_DATA
		short sim_chan[4];			//  remembers the bit pattern used for channel
									//  numbers when simulating data
		unsigned long n_simulated;									
	#endif
} AD7716;

//  Recalculate the rate every 300 seconds.  This will have given 12 million
//  ticks of the clock, which I presume to give an accuracy of approximately
//  one part in 12 million.  This should be accurate enough.  An alternative
//  way of looking at this is that we can project time forward or backward by
//  counting samples and using the rate, and err by only 1 tick over every 300
//  seconds we project.
#define		DTIME_RATE_CALCULATION	(300L)


#ifdef SIMULATE_DATA
	unsigned long AD7716_get_sim_count (void)  {
		return AD7716.n_simulated;
	}	
	void AD7716_reset_sim_count (void)  {
		AD7716.n_simulated = 0;
	}	
#endif



/*  This struct is used by the SPI interrupt handler routines to buffer data before
 *  passing it on to the main routines.  This structure is not designed for use
 *  by the end user.
*/
struct SPI_buffer  {
	long* data_start;			//  pointer to the start of the data buffer
	long n_points;				//  number of longs which the buffer can hold
	long* data_end;				//  pointer to the position one past the end of the buffer.
								//  this is just here for convenience so we don't have to
								//  keep calculating it each time we need it.
	long* next_in;				//  pointer to position in the buffer to which we will
								//  writing the next data point
	long* next_out;				//  pointer to position in the buffer from which we will
								//  remove the next data point when taking data from the
								//  buffer; NULL if the routines have not been inited.
								//  Equals next_in if there is no data available.
	short overflow;				//  flag is set non-zero if we accidentally overflow the
								//  the buffer and overwrite data.								
} SPI_buffer;
 

#define         AD7716_BAUD             4
#define         AD7716_DSCKL            1
#define         AD7716_DTL              1

#define			AD7716_CONVERT_129HZ	1
#define			AD7716_CONVERT_257HZ	2


//  The next few subroutines are error checking routines used during debug only.
//  If the variable DEBUG is defined, they execute, otherwise they are defined
//  as NOOP.  These must be the first routines in this file to insure that the
//  'defines' work correctly.  These routines are not designed for error checking
//  variables that the user enters.  That code is spread throughout the file.
//  Rather, they are designed to insure that the internal variables
//  describing what the code should do are all self-consistent, and that they
//  won't crash the system when the code executes.  If an error is discovered,
//  the code will abort and print an error message.  The routine
//  sci_logger_assert_valid() should be callable at the start or finish of any
//  of these routines without printing an error message.
//
#ifdef DEBUG
//  This is essentially a special version of ASSERT that allows us to print out
//  two line numbers.  It is used only here for the AD7716_assert_valid routine.
//  It is useful because it allows us to print both the line number of the assert
//  which failed and the line number from which we called the assert_valid routine.
//  We don't bother to print the file name, because it is used only within this file
//
void AD7716_VALIDITY_FAILURE (void* exp, unsigned line1, unsigned line2)  {
	fflush (stdout);
	fflush (stderr);
	printf ("\n\n***************************************\n");
	printf ("AD7716 validity failure: %s @\n", (char*)exp);
	printf ("  line %d, called from line %d\n", line1, line2);
	printf ("***************************************\n\n");
	fflush (stdout);
    fflush (stderr);
	exit (-1);
}
#define TEST_VALID(exp) \
	    ( (exp) ? (void) 0 : AD7716_VALIDITY_FAILURE (#exp, __LINE__, calling_line) )

//  AD7716_assert_valid (void)
//  Internal use only
//  Debugging use only
//
//  This routine checks the internal consistency of the science logger
//  data structure in every way that I can think of.  It should be updated
//  any time the logger code is changed.
//  If the variable DEBUG is not defined, then this subroutine is defined
//	so as to be a NOOP.  See code immediately following the #else.
//
void AD7716_assert_valid (unsigned calling_line)  {

	TEST_VALID ((AD7716.AD7716_open_flag == 0) || (AD7716.AD7716_open_flag == 1));
	TEST_VALID ((AD7716.convert_rate == AD7716_CONVERT_129HZ) || (AD7716.convert_rate == AD7716_CONVERT_257HZ));
	TEST_VALID ((AD7716.save_channels&0xFFF0) == 0);
	TEST_VALID ((AD7716.n_channels >= 0) && (AD7716.n_channels <= 4));

	if (AD7716.AD7716_open_flag == 0)  {
		//  if no points are allocated, then we should be closed, and the pointers are
		//  all NULL
		TEST_VALID (SPI_buffer.n_points == 0);
		TEST_VALID (SPI_buffer.data_start == NULL);
		TEST_VALID (SPI_buffer.data_end == NULL);
		TEST_VALID (SPI_buffer.next_in == NULL);
		TEST_VALID (SPI_buffer.next_out == NULL);
		TEST_VALID (SPI_buffer.overflow == -1);
	}  else  {
		TEST_VALID ((SPI_buffer.overflow == 0) || (SPI_buffer.overflow == 1));
		TEST_VALID (SPI_buffer.n_points > 0);
		TEST_VALID (SPI_buffer.data_start != NULL);
		TEST_VALID (SPI_buffer.data_end != NULL);
		TEST_VALID (SPI_buffer.next_in != NULL);
		TEST_VALID (SPI_buffer.next_out != NULL);
		
		TEST_VALID (SPI_buffer.data_end == &SPI_buffer.data_start[SPI_buffer.n_points]);
		TEST_VALID (SPI_buffer.next_in >= SPI_buffer.data_start);
		TEST_VALID (SPI_buffer.next_in < SPI_buffer.data_end);
		TEST_VALID (SPI_buffer.next_out >= SPI_buffer.data_start);
		TEST_VALID (SPI_buffer.next_out < SPI_buffer.data_end);
		TEST_VALID (AD7716.n_to_average >= 1);
		TEST_VALID (AD7716.n_to_average <= 8);
	}
}
#endif




//  AD7716_power_on_init (void)
//  User callable
//
//	Called when the power is first turned on to make sure that the
//  data structures are properly initialized.
//
void AD7716_power_on_init (void)  {
	//  first set all the hardware to power the chips off
	AD7716_turn_power_off ();

	//  then init the software variables
	AD7716.AD7716_open_flag = 0;
	AD7716.convert_rate = AD7716_CONVERT_129HZ;
	SPI_buffer.data_start = NULL;
	SPI_buffer.n_points = 0;
	SPI_buffer.data_end = NULL;
	SPI_buffer.next_in = NULL;
	SPI_buffer.next_out = NULL;
	SPI_buffer.overflow = -1;

	AD7716_assert_valid (__LINE__);
}




void AD7716_turn_power_off (void)  {

	//  Start by setting the shutdown pins for the power supply
	TPUSetPin(4, SET);		//  CLR to turn on -Analog
	TPUSetPin(5, SET);		//  CLR to turn on +Analog
	
	//  The adjust all the I/O pins.  Make sure they are driven low
	//  so that we don't power the chips through their logic inputs
	PConfOutp (E,0);	//  AD7716 TFS on the ADC module
	PClear (E,0);
	PConfOutp (E,1);	//  ADC Reset line on the ADC module
	PClear (E,1);

	PConfOutp (E, 3);   //	E3 -> Output to ADC/ASI board controls science
	PClear (E,3);		//  channel 0 gain
	PConfOutp (E, 5);	//	E5 -> Output to ADC/ASI board controls science
	PClear (E,5);		//  channel 1 gain
	PConfOutp (E, 6);   //	E6 -> Output to ADC/ASI board controls science
	PClear (E, 6);		//  channel 2 gain
	PConfOutp (E, 7);   //	E7 -> Output to ADC/ASI board controls science
	PClear (E, 7);      //  channel 3 gain
	
	PConfOutp (F, 4);   //  Data Ready output from the 7716 chip
	PClear (F, 4);      //  Hold low with analog power off
	PConfOutp(F,2);     //  DRDY output line from second ADC.  With the analog
	PClear (F, 2);		//  power supply off, this can be held low
	
	//  Set the QSPI lines.  Configure the chip selects as low outputs, which
	//  would normally select the chip, but which is irrelavent with the power
	//  off.
	PConfOutp (D,6);	//  pcs3 MAX186 A/D chip select active low
	PSet (D,6);			//  disable the MAX186 (it is powered on still)
	PConfOutp (D,5);	//  pcs2 1211 A/D chip select; active low, but
	PClear (D,5);		//  hold low while power is off
	PConfOutp (D,4);	//  pcs1 2nd 1211 A/D chip select as first
	PClear (D,4);
	PConfOutp (D,3);	//  pcs0 AD7716 RFS line.  hold low while power is off
	PClear (D,3);
	PConfOutp(D,2);	PClear(D,2);	//  SCLK
	PConfOutp(D,1);	PClear(D,2);	//  MOSI
	PInpToOut(D,0);					//  MISO - for minimum power draw we hope that
									//  the MAX186 is holding this low
}
	



void AD7716_turn_power_on (void)  {

	//  First make sure the power is off and the pins properly configured
	//  If we don't do this it is possible to have partially powered the
	//  chips through the logic lines, which can leave the ADC SPI interface
	//  in a screwy configuration that might hang things.  Nothing in
	//  the current code would do this, but the power off call is cheap
	//  insurance against any changes that might be made.
	AD7716_turn_power_off ();

	//  Then reconfigure the MISO and DRDY lines so that the ADCs can
	//  drive them; do this before powering the ADCs
	PConfInp (D, 0);    //  MISO
	PConfInp (F, 4);	//  Data ready output line from AD7716; convert this
						//  to a bus (interrupt) line when we are ready to
						//  capture data
	PConfInp (F, 2);	//  Data ready output line from BB1211
	
	//  Then turn on the analog power supply
	TPUSetPin(5, CLR);		//  CLR to turn on +Analog
	TPUSetPin(4, CLR);		//  CLR to turn on -Analog
	
	//  Then re-adjust all the I/O pins.
	PConfOutp (E,1);	//  ADC Reset line on the ADC module
	PSet (E,1);			//  active high

	PConfOutp (E, 3);   //	E3 -> Output to ADC/ASI board controls science
	PSet (E,3);			//  channel 0 gain; default to low gain
	PConfOutp (E, 5);	//	E5 -> Output to ADC/ASI board controls science
	PSet (E,5);			//  channel 1 gain
	PConfOutp (E, 6);   //	E6 -> Output to ADC/ASI board controls science
	PSet (E, 6);		//  channel 2 gain
	PConfOutp (E, 7);   //	E7 -> Output to ADC/ASI board controls science
	PSet (E, 7);		//  channel 3 gain

	//  Set the QSPI lines.  Configure the chip selects so the chips are not
	//  selected
	PConfOutp (D,6);	//  pcs3 MAX186 A/D chip select active low
	PSet (D,6);			//  disable the MAX186
	PConfOutp (D,5);	//  pcs2 BB1211 A/D chip select; active low
	PSet (D,5);
	PConfOutp (D,4);	//  pcs1 2nd BB1211 A/D chip select as first
	PSet (D,4);
	PConfOutp (D,3);	//  pcs0 AD7716 RFS line.
	PSet (D,3);
	PConfOutp (E,0);	//  AD7716 TFS on the ADC module
	PSet (E,0);
	
	//  de-assert the ADC reset line
	PClear (E,1);
}
	

	


//  AD7716_open (long n_buffer_points)
//  User callable
//
//	Sets up the data structures used to get conversions from the
//  AD7716 for conversions.reading.  Inits the buffers; does not
//  mark any channels for saving; does not begin conversions, but
//  sets up the hardware interface.
//	DON'T call this routine unless the power has been turned on.
//
//  Arguments:
//		n_buffer_points = length of the buffer into which the data
//			is pushed, measured in long words.
//	Returns: 0 if OK, negative on failure
//	Failures: Fails if there is insufficient memory for the buffer
//
int AD7716_open (long n_buffer_points)  {
	//  shouldn't call open when we're already open
	ASSERT (AD7716.AD7716_open_flag	== 0);

	//  First mark us as open so the other routine will respond correctly
	AD7716.AD7716_open_flag	= 1;

	//  no channels yet selected
	AD7716.n_channels = 0;
	AD7716.save_channels = 0;
	
	//  no averaging used to start
	AD7716.n_to_average = 1;
	
	//  make sure the system knows that the measured rate is not yet valid
	AD7716.measured_rate = -1.0;

	//  And allocate space for SPI data buffer
	if (AD7716_SPI_buffer_open (n_buffer_points) < 0)  {
		//  on failure make sure we unmark us as open
		AD7716.AD7716_open_flag	= 0;
		return -1;
	}
	//  make sure interrupt servicing for the 7716 DRDY line is off
	PConfInp (F,4);

	//  Init the SPI interface to the 7716
	AD7716_SPI_interface_init ();

	//  Reset the 7716 to a known state
	AD7716_reset ();
	
	//  Program the conversion rate, but don't start listening to the
	//  interrupts yet
	AD7716_program_hardware ();
	AD7716_assert_valid (__LINE__);
	return 0;
}




//  AD7716_close ()
//  User callable
//
//  Stops all conversions and deallocates all memory used.  The only
//  A/D routine which the user may call after this is AD7716_open ()
//
void AD7716_close (void)  {
	if (AD7716.AD7716_open_flag	== 0)
		return;

	//  stop the data flow (i.e., the interrupt servicing) and delete
	//  the buffers
	AD7716_stop_data ();
	AD7716_SPI_buffer_delete ();
	
	//  REVIEW
	//  we should probably configure the SPI port pins as outputs and drive them
	//  low so that we can be sure we don't power the A/D through its digital input
	//  pins

	//  And finally mark the 7716 as closed
	AD7716.AD7716_open_flag	= 0;
	AD7716_assert_valid (__LINE__);
	return;
}




//  AD7716_set_channels (short channels)
//  User callable
//
//  Sets which channels from the A/D are pushed into the data buffer for
//  later retrieval using the AD7716_get_data_point() routine
//
//  Arguments:
//		channel = bit pattern specifies which channels are to be
//			saved in the buffer.  A bit is on if a channel is to be saved.
//			Bit 0 represents what the AD7716 documentation calls channel 1.
//			The AD7716 implements 4 channels.  (The rest of our code numbers
//			channels as 0-3, not 1-4)
//  Returns: none 
//  Failures: during debugging, in an unused bit is set
//
void AD7716_set_channels (short channels)  {
	unsigned short chan_bit, n;
	#ifdef SIMULATE_DATA
		int count;
	#endif		
	
	//  Note: it isn't worth checking if we are actually opened
	//  before we execute this code as it is in effect a NOOP.
	
	//  Doesn't make sense to change channels while collecting data.
	//  Stop data (which also empties the buffers)
	AD7716_stop_data ();
	
	//  Calculate and record the channel information
	AD7716.n_channels = 0;
	for (n = 0;  n < 4;  n++)  {
		chan_bit = 0x01 << n;
		if (chan_bit&channels)
			AD7716.n_channels++;
	}
	AD7716.save_channels = channels;
	
	#ifdef DEBUG
		//  make sure no bits are set which are being ignored
		while (n < (8*sizeof(channels)))  {
			chan_bit = 0x01 << n++;
			ASSERT ((chan_bit&channels) == 0);
		}
	#endif
	#ifdef SIMULATE_DATA
		count = 0;
		for (n = 0;  n < 4;  n++)
			AD7716.sim_chan[n] = 0;		
		if (channels&0x01)
			AD7716.sim_chan[count++] = 0;
		if (channels&0x02)
			AD7716.sim_chan[count++] = 0x02;
		if (channels&0x04)
			AD7716.sim_chan[count++] = 0x01;
		if (channels&0x08)
			AD7716.sim_chan[count++] = 0x03;
		ASSERT (AD7716.n_channels == count);
	#endif		
	AD7716_assert_valid (__LINE__);
}	





//  AD7716_get_channels (void)
//  User callable
//
//  Returns the bit pattern representing which channels are being saved
//
short AD7716_get_channels (void)  {
	//  don't mislead user into thinking things are operational if the logger
	//  isn't open
	if (AD7716.AD7716_open_flag)
		return AD7716.save_channels;
	else
		return 0;	
}



//  AD7716_get_n_channels (void)
//  User callable
//
//  Returns the number of channels activated
//
short AD7716_get_n_channels (void)  {
	//  don't mislead user into thinking things are operational if the logger
	//  isn't open
	if (AD7716.AD7716_open_flag)
		return AD7716.n_channels;
	else
		return -1;	
}





//  AD7716_start_data (void)
//  User callable
//
//  Sets up the QSPI interface; resets the data buffer; starts the
//  data from the AD7716 into the local data buffer
//
void AD7716_start_data (void)  {
	//  make sure the logger has been opened before adjusting the hardware
	if (AD7716.AD7716_open_flag != 1)
		return;
	//  verify the setup is OK
	AD7716_assert_valid (__LINE__);

#ifndef SIMULATE_DATA
	//  make sure interrupt servicing is still off while we reset the interface
	PConfInp (F,4);

	//  Reset so we know we are at the beginning of a conversion cycle, which
	//  therefore allows plenty of time for the data xfer when conversion is complete
	//  This does not reset the programing of the conversion rate.
	//
	//  REVIEW - should we setup the interface first?
	AD7716.icounter = 0;
	AD7716.c0_total = 0;
	AD7716.c0_n_averaged = 0;
	AD7716.c1_total = 0;
	AD7716.c1_n_averaged = 0;
	AD7716.c2_total = 0;
	AD7716.c2_n_averaged = 0;
	AD7716.c3_total = 0;
	AD7716.c3_n_averaged = 0;
	
	if (AD7716.save_channels != 0)  {
		AD7716_SPI_master_in ();
		AD7716_reset ();
	
		//  turn interrupt servicing for the 7716 DRDY line back on
		#ifdef DEBUG
		printf ("AD7716_start_data: about to configure input as bus.\n");
		#endif
		PConfBus (F,4);
	}
	#ifdef DEBUG
	printf ("Did it.\n");	
	#endif
#endif	
	//  mark current measured conversion rate as bad	
	AD7716.measured_rate = -1;

	//  Init the data necessary for the calculation of the conversion rate
	//  Wait for data to be saved, then note the time
	AD7716_data_saved ();
	AD7716.start_time = logger_time_get_time ();
	AD7716.data_pushed = 0;
	AD7716.rate_calc_threshold = DTIME_RATE_CALCULATION * AD7716.n_channels * AD7716_get_conversion_rate ();	
}	
	


//  AD7716_pause_data (void)
//  User callable
//
//  Used to temporarily suspend processing of the interrupts which collect
//  data from the AD7716; this does not actually stop the 7716 from
//  converting, but merely pauses our collection of data from it.  If
//  the AD7716_resume () routine is called quickly enough, then no data
//  will be lost
//
void AD7716_pause_data (void)  {
	if (AD7716.AD7716_open_flag != 1)
		return;
		
	//  REVIEW
	//  suppose the logger has been opened, but the data flow hasn't been
	//  started.  Does this respond correctly?

	//  wait for an interrupt to complete processing
	AD7716_interrupt_serviced ();
	
	//  by configuring line 4 (AD7716's DRDY line) as an input instead of
	//  a bus line, we prevent the 7716's interrupts from being serviced
	//  and essentially pause the AD.  We call this a pause instead of a
	//  stop because we have not reset the data buffer structures, so when
	//  we restart the A/D, the new data is just added to the old
	PConfInp (F, 4);
}




//  AD7716_resume_data (void)
//  User callable
//
//  Restores the interrupts which process data from the 7716; resets the
//  control pins that interact with it; resets the QSPI interface; begins
//  data collection again at the point where it was paused.  Does NOT reset
//  any of the data queues; does NOT check to verify that no data was lost
//
void AD7716_resume_data (void)  {
	if (AD7716.AD7716_open_flag != 1)
		return;
	#ifdef SIMULATE_DATA
		//  never access hardware if simulating data
		return;
	#endif	    
    
	//  need to reinit the microcontroller's SPI interface and re-install
	//  the interrupt vectors before we can re-enable the interrupts.
	AD7716_SPI_interface_init ();
	PConfBus (F, 4);
}



//  AD7716_stop_data ()
//  User callable
//
//	Stops the data collection from the A/D converter and empties
//  all data from the buffers
//
void AD7716_stop_data ()  {
	//  don't do anything if we aren't open
	if (AD7716.AD7716_open_flag != 1)
		return;

	//  Disable interrupt processing by resetting the interrupt line
	//  from its bus function to a normal input function
	PConfInp (F, 4);
	
	//  stop_data is used when we don't intend to re-start sequential data,
	//  so we should throw out whatever we have
	AD7716_SPI_buffer_empty ();
}


		
//  AD7716_set_conversion_rate (float f)
//  User callable
//
//	Sets the conversion rate.  Currently allowed rates are only
//	129 or 257 Hz; the others could easily be activated by adding
//  a little code here.  If data is already flowing, then it is
//  stopped and the buffers emptied.
//
//  Note that it is OK to call this routine when the AD7716 is not
//  open.  This is a convenient thing to allow so that we can use
//  the frequency checking in this routine to verify that a frequency
//  which a user has requested is valid.
//
//  Arguments: the desired conversion rate, which must be within 1% of
//		the exact conversion rate for this routine to work.  Illegal
//		values cause the routine to do nothing and return negative to
//		indicate error
//	Returns: the exact conversion rate choosen assuming the XTAL_FREQ
//		is correct; negative on failure
//	Failure: if the requested rate is not within 1% of a known rate
//
double AD7716_set_conversion_rate (float rate)  {
	float base_rate;
	float rate_num;
	
	//  we can't reset the conversion rate while taking data.  Stop
	//  and empty the buffers.  This will do nothing if the 7716 is
	//  closed.  Note that the external user must restart the data
	//  if appropriate!
	AD7716_stop_data ();
	
	//  roughly 128.6 Hz for a 7.3728 Hz Xtal
	base_rate = ((XTAL_FREQ/14.0)/(16*256.0));
	
	rate_num = rate/base_rate;
	if ((rate_num > 0.99) &&
	    (rate_num < 1.01)   )  {
	    //  nominal rate is 128.5 Hz
	    AD7716.convert_rate = AD7716_CONVERT_129HZ;
	    AD7716_program_hardware ();
		return base_rate;
	}
	
	if ((rate_num > 1.98) &&
		(rate_num < 2.02)   )  {
		//  nominal rate is 257 Hz
	    AD7716.convert_rate = AD7716_CONVERT_257HZ;
	    AD7716_program_hardware ();
		return 2.0F*base_rate;
	}

	AD7716_assert_valid (__LINE__);
	return -1;
}





//  AD7716_set_averaging (short n_to_average)
//  User callable
//
//	Sets the number of samples which are averaged together to produce
//  a single sample for the end user.  Currently allowed numbers are
//  1 (no averaging) to 4; others could easily be added.  If data is
//  already flowing, then it is stopped and the buffers emptied.
//
//  Note that it is OK to call this routine when the AD7716 is not
//  open.  This is a convenient thing to allow so that we can use
//  the error checking in this routine to verify that a number
//  which a user has requested is valid.
//
//  Arguments: the desired number of samples to average together.
//	Returns: 0 if OK; negative on failure
//	Failure: if the requested number of samples is not between 1
//			 and 4
//
short AD7716_set_averaging (short n_to_average)  {
	if ((n_to_average < 1) ||
		(n_to_average > 4)   )
		return -1;
		
	//  we shouldn't reset the averaging while taking data.  Stop
	//  and empty the buffers.  This will do nothing if the 7716 is
	//  closed.  Note that the external user must restart the data
	//  if appropriate!
	AD7716_stop_data ();
	AD7716.n_to_average = n_to_average;
	
	//  Be sure to reset the measured rate since we changed the
	//  averaging.  The code stop using any previously calculated
	//  rate and will recalculate a new rate next time the data
	//  is started.
	AD7716.measured_rate = -1;
	return 0;
}		





//  AD7716_get_conversion_rate (void)
//  User callable
//
//	Returns the current conversion rate in conversions/second.
//	NOTE: This assumes a 7.3728 MHz xtal.  There is no convenient
//  way for the software to verify this.  We could fire up the
//  converter and time the incoming interrupts, but this is probably
//  an unnecessary pain in the ass.
//
double AD7716_get_conversion_rate (void)  {
	double base_rate;
	double rate;
	
	if (AD7716.AD7716_open_flag	== 0)
		return -1;
	
	//  if we have accumulated enough interrupts to have accurately
	//  measured the conversion rate, then return the measured rate.
	if (AD7716.measured_rate > 0)  {
		return AD7716.measured_rate;
//		printf ("measured rate is %.9lf\n", AD7716.measured_rate);
	}	
//	printf ("Using calculated rate.\n");
	//  If we don't yet have enough interrupts, then calculate an approximate
	//  conversion rate based on the target value of the crystal which controls
	//  the rate.
	//  The base rate is roughly 128.6 Hz for a 7.3728 Hz Xtal
	base_rate = ((XTAL_FREQ/14.0)/(16*256.0));
	switch (AD7716.convert_rate)  {
		default:
			//  shouldn't ever get here; abort during debug mode, default
			//  to 129 Hz in actual use
			ASSERT (0);
			AD7716.convert_rate = AD7716_CONVERT_129HZ; 
			
		case AD7716_CONVERT_129HZ:
			rate = base_rate;
			break;
			
		case AD7716_CONVERT_257HZ:
			rate =  2.0*base_rate;
			break;
	}
	
	//  If we are averaging, then divide the 7716 data rate by the number of
	//  samples averaged to get the actual rate.
	rate /= AD7716.n_to_average;
//	printf ("calculated rate is %.9lf\n", rate);	
	return rate;
}





//	AD7716_program_hardware (void)
//  Internal use only
//  Does not do anything if the 7716 is closed
//
//	Reads the internal variable to determine the conversion rate and programs
//  the 7716 appropriately
//
void AD7716_program_hardware (void)  {
	if (AD7716.AD7716_open_flag == 0)
		return;

//	printf ("programming hardware\n");
	switch (AD7716.convert_rate)  {
		default:
			//  shouldn't ever get here; abort during debug mode, default
			//  to 129 Hz in actual use
			ASSERT (0);
			AD7716.convert_rate = AD7716_CONVERT_129HZ; 
			
		case AD7716_CONVERT_129HZ:
			//  see the 7716 data sheet for meaning of numbers
			AD7716_command (0x00, 0xB1);
			return;
			
		case AD7716_CONVERT_257HZ:
			AD7716_command (0x01, 0xD1);
			return;
	}
	ASSERT (0);
}	





//  AD7716_SPI_interface_init (void)
//  Not for external use.
//
//  Turns off the MAX186, which shares the SPI interface with the AD7716.
//  Inits the SPI ports which we use to talk to the AD7716; installs the
//  interrupt handlers for the SPI interrupt (which occurs when an xfer
//  completes) and the AD7716 interrupt (which occurs when the DataReady
//  line of the 7716 is active).
//
//  Arguments, failures: none
//
void AD7716_SPI_interface_init (void)
	{
	//  frames for the SPI interrupt handler and the IRQ4 interrupt handler
	static ExcCFrame efspi;
	static ExcCFrame ef4;
	
	//  This is an internal routine, and it is not called unless we have
	//  been opened already.
//	ASSERT (AD7716.AD7716_open_flag == 1);
	
	//  Shut off MAX186 ADC; Also need to keep PCS3 line (to MAX186) HIGH
	//  to Tri-state its DOUT and DIN lines, which are connected to the QSPI
//	Max186PowerDown();
	
	// install QSPI interrupt handler
	InstallHandler(AD7716_SPI_rupt_handler, QSPI_INT_VECTOR, &efspi);

	// install AD7716 Data Ready interrupt handler
	InstallHandler(AD7716_rupt_handler, Level_4_Interrupt, &ef4);

	//  make sure we're set up to receive data
	AD7716_SPI_master_in ();

	// SETUP QSPI CONTROL REGISTERS 0,1, AND 3
	*SPCR0 =        AD7716_BAUD             //  4 = 2 MHz;  baud rate (2 is max)
			|       (8 << 10)               //  bits per transfer (0 = 16)
			|       M_MSTR  &       SET     //  1 = Master, 0 = Slave
			|       M_WOMQ  &       CLR     //  1 = open drain, 0 = cmos
			|       M_CPOL  &       CLR     //  1 = inactive SCK high
			|       M_CPHA  &       SET     //  1 = change leading edge, capture falling
	;

	*SPCR3 =        M_LOOPQ &       CLR     //  QSPI loop mode, 1 = on
			|       M_HMIE  &       CLR     //  HALTA & MODF interrupt enable
			|       M_HALT  &       CLR     //  Halt
	;

	*SPCR1 =        AD7716_DTL              //  DTL delay after transfer
			|       (AD7716_DSCKL << 8)     //  DSCKL delay before SCK
			|       M_SPE   &       CLR     //  QSPI enable = 1, 0 = I/O
	;



	//  Why are we running a command through here?
	//
	//	SET UP SPCR2 FOR ONE COMMAND
	*SPCR2 =        0                       //  NEWQP
			|       1 << 8                  //  ENDQP
			|       M_SPIFIE&       SET     //  SPI finished rupt enable = 1
			|       M_WREN  &       CLR     //  wraparound enable = 1
			|       M_WRTO  &       CLR     //  wrap to
	;
	

	SPIXMT[0]  = 0x00;      // DUMMY TRANSMISSION SETUP PCS'S
	
	//  COMMANDS ARE 0xFN, WHERE N IS PCS VALUES
	SPICMD[0] =     11                      //  SET PCS3 LOW, TFS, RFS HIGH, RESET LOW
			|       M_CONT  &       SET     //  1=keep CS active
			|       M_BITSE &       SET     //  0=8 bits, 1=use SPCR0
			|       M_DT    &       SET     //  0=no delay after, 1=use SPCR1
			|       M_DSCK  &       SET     //  0=1/2SCK, 1=use SPCR1
			;
	
	 SPICMD[1] = 0xFB;
	_SPSR->SPIF = 0;                        //  clear any old results (intrpt flag)
	AD7716.SPI_finished = 0;				//  reset the SPI complete flag
	_SPCR1->SPE = 1;                        //  start QSPI

	//  This Stop call halts the clock until the SPI xmission completes
//	Stop (0x2000);
	while (AD7716.SPI_finished == 0)
		{;}
	return;
}





//  AD7716_reset (void)
//	User callable
//
//  Toggles the AD7716 reset line to synchronize channels if we have more than
//  one converter.  Also insures that we will have a known amount of time to
//  grab the data when the DataReady interrupt occurs.
//
void AD7716_reset (void)
	{
	int n;
	
	//  don't do anything unless we are open
	if (AD7716.AD7716_open_flag != 1)
		return;
		
	//  make sure the reset line is configured as an output
	PConfOutp (E, 1);		//  this should already be true
	
	//  The 7716 data sheet specs that RESET must be held high for
	//  at least four CLKIN cycles, where CLKIN comes from the XTAL
	//  on the 7716, which is a 7.3728 MHz XTAL.  Therefore it must
	//  be high for at least 0.54 uSec.  For a loop count of 8 and
	//  a TT8 clock frequency of 16 MHz, the measured pulse width
	//  was approximately 12 usec.  A slower clock will only increase
	//  this further.
	PSet (E, 1);
	for (n = 0;  n < 8;  n++)
		{;}

	//  Now make sure the buffer is empty
	AD7716_SPI_buffer_empty ();

	//  And de-assert reset
	PClear (E, 1);

	AD7716_assert_valid (__LINE__);
	return;
}



//		QPDR-5 = PCS2 (connected to AD7716 RESET)
//		QPDR-4 = PCS1 (connected to AD7716 TFS)


//  AD7716_command
//  Internal use only
//
//  Transmits a two byte command to the 7716.  See the data sheet for
//  the AD7716 for the details on the meaning of these bytes.
//
//  The 7716 uses one line for both xmit and rcv, and distinguishes the
//  two cases by whether TFS or RFS is pulled low before the transfer.
//  This doesn't work well with the SPI, because the xmit and rcv pins
//  are seperate, and must remain seperate in order to work with the
//  TT8's on board A/D.  So we use the SPI to rcv bytes from the 7716,
//  and for the few times we need to xmit to it, we disable the SPI, set
//  the SPI lines as normal outputs, and simulate an SPI in software.
//  This is slow, but we transmit so seldom that it just doesn't matter.
//
//  Arguments: The least and most significant bytes of the command to
//		be transmitted.
//
//  SPI assignments:
//		QPDR-7 = TxD (used by SCI)
//		QPDR-6 = PCS3 (used by TT8's on-board MAXIM A/D converter)
//		QPDR-5 = PCS2 (connected to a BB1211 CS)
//		QPDR-4 = PCS1 (connected to a BB1211 CS)
//		QPDR-3 = PCS0 (connected to AD7716 RFS)
//		QPDR-2 = SCK (SPI clock)
//		QPDR-1 = MOSI (master out, slave in)
//		QPDR-0 = MISO (master in, slave out)
//
//		Port E0 = AD7716 TFS
//  	Port E1 = ADC Reset
//
//  The masks are defined in the TT8 include file QSM332.h
//
void AD7716_command (short LSByte, short MSByte)
	{
	ASSERT (AD7716.AD7716_open_flag == 1);
	//  This routine should not be called unless the interrupt servicing routine
	//  is turned off

//	if (_SPCR1->SPE)
//		printf ("wait for stop\n");
	
	//  set up for transmission of data over the SPI to the 7716
	AD7716_SPI_master_out ();
	
	//  we're all set up.  Now loop through setting the data pins and toggling
	//  the clock line to xfer the data
	AD7716_software_SPI_write (LSByte);
	AD7716_software_SPI_write (MSByte);
	
	//  be sure to reset the SPI interface to its default master_in state
	AD7716_SPI_master_in ();
	return;
}



//  Series of defines to allow convenient setting of SPI bits
#define AD7716_set_TFS_low      (PClear (E, 0))
#define AD7716_set_TFS_high		(PSet (E, 0))
#define AD7716_set_SCK_low		(_QPDR->SCK=0)
#define AD7716_set_SCK_high		(_QPDR->SCK=1)
#define AD7716_set_MISO_low		(_QPDR->MISO=0)
#define AD7716_set_MISO_high	(_QPDR->MISO=1)

//  AD7716_software_SPI_write (int byte)
//
//	Uses software control of the SPI interface to xmit a byte
//  to the 7716 using the lines normally assigned to recieve
//  data.  This is necessary so we can conveniently communicate
//  with the 7716, which uses a single line for both transmit
//  and receive.
//
//  IMPORTANT NOTE: this routine changes the DDR and PAR for the
//  SPI.  The user must disable the 7716 interrupt servicing before
//  call this routine, and should call SPI_master_in() before attempting
//  to read data from the 7716 again.
//
void AD7716_software_SPI_write (int byte)  {
	int n;
	
	//  start with the clock low
	AD7716_set_SCK_low;
	
	//  then pull TFS low
	AD7716_set_TFS_low;
	
	//  Now loop through setting the data bit and toggling the clock
	//  Note that we xmit the high order bits first
	for (n = 7;  n >= 0;  n--)  {
		//  to send a bit, first set the clock high
		AD7716_set_SCK_high;

		//  then set the data bit appropriately
		if (byte & (0x01 << n))
			AD7716_set_MISO_high;
		else	
			AD7716_set_MISO_low;
	
		//  finally, drop the clock low so the AD7716 will latch the data bit
		AD7716_set_SCK_low;
	}
	//  after all bits are sent, return TFS to its high state
	AD7716_set_TFS_high;

	//  and leave the MISO bit high too, though I don't think it matters
	AD7716_set_MISO_high;
	//  return clock to its normal high state
	AD7716_set_SCK_high;

	return;
}
		
		


//  AD7716_SPI_read_data
//  Internal use only
//
//  Reads 16 bytes from the AD7716 over the SPI interface
//      This is accomplished by establishing a cue of 8 x 16 bit
//      commands. The data sent by the AD7716 goes into receive RAM.
//      SPIRCV[0,1] have data for channel 1...SPIRCV[6,7] have data
//      for channel 4. Data is the most significant 22 bits, in
//      two's-complement form.
//      
void AD7716_SPI_read_data (void)  {
	ASSERT (AD7716.AD7716_open_flag == 1);

	/*	SETUP 16 BIT TRANSFERS to READ AD7716. 8 TRANSFERS ARE NEEDED TO FETCH DATA.        
		A FINAL TRANSFER IS NEEDED TO RESET DRDY
	*/ 
	*SPCR0 =        AD7716_BAUD             // 4 = 2 MHz; baud rate (2 is max)
			|       (0 << 10)               // bits per transfer (0 = 16)
			|       M_MSTR  &       SET     // 1 = Master, 0 = Slave
			|       M_WOMQ  &       CLR     // 1 = open drain, 0 = cmos
			|       M_CPOL  &       CLR     // 1 = inactive SCK high
			|       M_CPHA  &       SET     // 1 = chg lding edge, capt folng
	 ;


	/*	SET UP SPCR2 FOR NINE COMMANDS  */        
	*SPCR2 =        0                       // NEWQP
			|       8 << 8                  // ENDQP
			|       M_SPIFIE&       SET     // SPI finished rupt enable = 1
			|       M_WREN  &       CLR     // wraparound enable = 1
			|       M_WRTO  &       CLR     // wrap to
	;
	SPIXMT[0]=0x00;        
	SPIXMT[1]=0x01;
	SPIXMT[2]=0x02;
	SPIXMT[3]=0x03;
	SPIXMT[4]=0x04;        
	SPIXMT[5]=0x05;
	SPIXMT[6]=0x06;
	SPIXMT[7]=0x07;
	SPIXMT[8]=0x08;        
	SPIXMT[9]=0x09;
	SPIXMT[10]=0x0A;
	SPIXMT[11]=0x0B;
	SPIXMT[12]=0x0C;        
	SPIXMT[13]=0x0D;
	SPIXMT[14]=0x0E;
	SPIXMT[15]=0x0F;

	
    /*	COMMANDS ARE 0xFN, WHERE N IS PCS VALUES  */
	SPICMD[0] =     0xE                		// PCS0 = RFS low, others high
			|       M_CONT  &       SET     // 1=keep CS active
			|       M_BITSE &       SET     // 0=8 bits, 1=use SPCR0
			|       M_DT    &       SET     // 0=no delay after, 1=use SPCR1
			|       M_DSCK  &       SET     // 0=1/2SCK, 1=use SPCR1
			;
	
	SPICMD[1] =0xFE;
	SPICMD[2] =0xFE;
	SPICMD[3] =0xFE;
	SPICMD[4] =0xFE;
	SPICMD[5] =0xFE;
	SPICMD[6] =0xFE;
	SPICMD[7] =0xFE;
	SPICMD[8] =0xFE;
	
	
	_SPSR->SPIF = 0;                        // clear any old results (intrpt flag)
	AD7716.SPI_finished = 0;				//  reset the SPI complete flag
	_SPCR1->SPE = 1;                        // START QSPI
}




//  AD7716_SPI_master_out
//  Internal use only
//
//  Sets up the QSPI pins for output as an SPI master using
//  the software routines, which drive the data out over the
//  MISO line.
//
void AD7716_SPI_master_out (void)  {
	ASSERT (AD7716.AD7716_open_flag == 1);
	
	//  Previously set values:
	//		Port E0 = AD7716 TFS, already set high
	//		Port E1 = ADC Reset, already set low

	//  Set the data register, which controls the output values when
	//  the SPI is not active
	//		QPDR-7 = TxD (used by SCI)
	//		QPDR-6 = PCS3 (used by TT8's on-board MAXIM A/D converter, active low, idle high)
	//		QPDR-5 = PCS2 (connected to a BB1211 CS line, active low, idle high)
	//		QPDR-4 = PCS1 (connected to a BB1211 CS line, active low, idle high)
	//		QPDR-3 = PCS0 (connected to AD7716 RFS, active low)
	//		QPDR-2 = SCK (SPI clock) (set to idle low)
	//		QPDR-1 = MOSI (master out, slave in) (idle low)
	//		QPDR-0 = MISO (master in, slave out) (idle low)
	*QPDR = 0x78;		//  0111 1000

	//  No need to set up the PAR, which controls which pins the SPI takes over, cause
	//  the output of data is done in software, not hardware
	//  Now set up the date direction register
	//	  All PCS lines outputs
	//	  SCK and MISO outputs
	//    MOSI input
	//
	*QDDR = 0x7D;       //  0111 1101
}




//  AD7716_SPI_master_in (void);
//  Internal use only
//  
//  Sets up the QSPI for input as an SPI master
//
void AD7716_SPI_master_in (void)  {
//	ASSERT (AD7716.AD7716_open_flag == 1);

	//  Previously set values:
	//		Port E0 = AD7716 TFS, already set high
	//		Port E1 = ADC Reset, already set low

	//  If the SPI is not enabled, which is most of the time, then the state
	//  of the SPI pins (input/output and value) is defined by the values in
	//  the registers QDDR (the data direction register) and QPDR (the data
	//	register).  Which pins are assigned to the SPI when it is active is
	//	defined by the value in the QPAR (pin assignment register).
	//
	//  Set the QDDR first so that the pin values are corrected when we start
	//  driving the pins.
	//
	//  Setup the port QS data register to define PCS states between QSPI reads
	//		QPDR-7 = TxD (used by SCI)
	//		QPDR-6 = PCS3 (used by TT8's on-board MAXIM A/D converter, active low)
	//		QPDR-5 = PCS2 (connected to BB1211 CS, active low, idle high)
	//		QPDR-4 = PCS1 (connected to BB1211 CS, active low, idle high)
	//		QPDR-3 = PCS0 (connected to AD7716 RFS, active low)
	//		QPDR-2 = SCK (SPI clock) (set to idle low)
	//		QPDR-1 = MOSI (master out, slave in) (idle low)
	//		QPDR-0 = MISO (master in, slave out) (idle low)
	*QPDR = 0x78;		//  0111 1000

	//  Setup the pin assignment register, which determinew which pins the
	//  SPI takes over when it is enabled.
	//		Assign all PCS lines, MISO and SCK; note that the hardware is
	//			not capable of unassigning SCK when the QSPI is active
	//			(SPE bit set)
	//		MOSI is not assigned because we don't want to write data out to the
	//		AD7716 when reading data in the master in mode
	*QPAR = 0x7B;		//	0111 1101
	
	//  Now set up the date direction register
	//	  All PCS lines outputs
	//	  SCK output
	//    MISO and MOSI are both inputs
	//  We don't drive the MOSI line cause no one is listening anyway
	*QDDR = 0x7C;		//  0111 1100
}




//  AD7716_SPI_rupt_handler (void)  {
//  Internal use only
//
//  Interrupt handler for SPI
//
void AD7716_SPI_rupt_handler (void)  {
    //  clear any old results (intrpt flag)
	_SPSR->SPIF = 0;
	AD7716.SPI_finished = 1;
}




//  AD7716_rupt_handler (void)
//  Internal use only
//
//  Interrupt handler is invoked when the AD7716 Data Ready line is active
//
//    On Data Ready (DRDY or IRQ4 LOW), this handler grabs data from receive
//      RAM, checks the setup structure to decide which channels are to be
//		saved, and pushes those channels onto a common buffer.  Later, the user
//		program will call Get7716Data () to pull data out of the buffer.
//
void AD7716_rupt_handler (void)  {
	//  disable additional interrupts while we service this one
	PConfInp(F,4);
	
	AD7716_SPI_read_data();
	//  will exit the stop instruction when the SPI interrupts to acknowledge
	//  completion of the QSPI xfer
//	Stop (0x2000);
	while (AD7716.SPI_finished == 0)
		{;}

	//  count interrupts; calculation of the conversion rates is performed by
	//  the get_data_point routine as necessary.  The calculation can be time
	//  consuming, and we would rather not do it as part of an interrupt
	//  servicing routine.
	AD7716.icounter++;

	/*  check to see if we should save channel 1	*/
	if (AD7716.save_channels&0x01)  {
//		printf ("data 0x01 to buffer\n");
		AD7716_filter_chan_zero (SPIRCV[0], SPIRCV[1]);
	}	
		
	/*  now check channel 2 						*/	
	if (AD7716.save_channels&0x02)	
		AD7716_filter_chan_one (SPIRCV[2], SPIRCV[3]);
	/*  etc  */	
	if (AD7716.save_channels&0x04)	
		AD7716_filter_chan_two (SPIRCV[4], SPIRCV[5]);
	if (AD7716.save_channels&0x08)	
		AD7716_filter_chan_three (SPIRCV[6], SPIRCV[7]);

	//  re-enable interrupts from the Data Ready line
	PConfBus (F,4);
}




//  AD7716_interrupt_serviced ()
//  User callable.
//
//  Waits until we have just completed servicing an interrupt and then
//  returns to the caller.  Also returns if no interrupts occur within
//	a timeout period defined by the loop counter below.  This routine is
//  useful when another routine wishes to temporarily grab the
//	SPI interface for its own use.  Also used by the rate calculation
//  routines to time exactly when an interrupt occured.  It is therefore
//  important that the delay between the occurence of an interrupt and
//  the return of this subroutine is as uniform as possible.  Any
//	non-uniformity translates into inaccuracy in the timing information
//  and therefore inaccuracy in our rate calculations.  See also the
//  get_data () routine, which calculates conversion rates
//
//  Returns: 0 if interrupt was serviced
//			 -1 if we timed out
//  Failures: none - user probably doesn't care why we returned.
//
int AD7716_interrupt_serviced (void)  {
	long loop_count, old_icounter;

	#ifdef SIMULATE_DATA
		return 0;
	#endif	
    
    //  This routine must respond correctly even if the 7716 is not open or
    //  is not active.
	//
	//  We wait for the AD7716 to signal that it has just completed an
	//  interrupt servicing sequence by changing the icounter variable
	//  However, we don't want to get caught in an infinite loop if the
	//  AD7716 is broken, so we also keep a timeout counter going
	old_icounter = AD7716.icounter;

	//  A loop count of 1000 requires approximately 8 mS at 16 MHz.
	//  The slowest interrupts from the 7716 should occur every 8 mS.	
	//  this is slow, but is never executed unless we aren't receiving
	//  interrupts, so it doesn't affect the accuracy with which we
	//  time the completion of the interrupt servicing.
	for (loop_count = 0;  loop_count < 10000;  loop_count++)  {
		if (old_icounter != AD7716.icounter)
			return 0;
	}
	return -1;
}





//  AD7716_data_saved (void)
//  User callable.
//
//  Similar to interrupt_serviced (), except that it waits for data to
//  be pushed into the SPI buffer before returning.  These may not
//  be the same if we are averaging.
//
//  Returns: 0 if data was pushed
//			 -1 if we timed out
//  Failures: none - user probably doesn't care why we returned.
//
int AD7716_data_saved (void)  {
	long loop_count;
	long old_value;
	
	old_value = AD7716.data_pushed;
	for (loop_count = 0;  loop_count < 60000L;  loop_count++)  {
		if (old_value != AD7716.data_pushed)
			return 0;
	}
	return -1;
}




//  The filter routines are expected to preserve the channel number code,
//  which is embedded in the lower 2 bits of the data.  Unfortunately,
//  simple averaging will not preserve this code.  For example, channel
//  one has the code 0x0.  If the first data is 0, and the second data is
//  0x4, then the average is 0x02, and we have changed the channel code
//  Any averaging routine must preserve the code.  These routines do so.
//  They do not attempt to be portable or pretty because they are called
//  by the interrupt handler and must be fast.
//  
void AD7716_filter_chan_zero (unsigned short high, unsigned short low)  {
	long newest;

	newest = AD7716_data_to_long (high, low);
	AD7716.c0_total += newest;
	AD7716.c0_n_averaged++;
	if (AD7716.c0_n_averaged >= AD7716.n_to_average)  {
		AD7716.c0_total /= AD7716.n_to_average;
		AD7716_push_data_to_buff (AD7716.c0_total&0xFFFFFFFC);
		AD7716.c0_total = 0;
		AD7716.c0_n_averaged = 0;
	}
	return;
}
	
	
		


void AD7716_filter_chan_one (unsigned short high, unsigned short low)  {
	long newest;
	
	newest = AD7716_data_to_long (high, low);
	AD7716.c1_total += newest;
	AD7716.c1_n_averaged++;
	if (AD7716.c1_n_averaged >= AD7716.n_to_average)  {
		AD7716.c1_total /= AD7716.n_to_average;
		AD7716_push_data_to_buff (0x02 | (AD7716.c1_total&0xFFFFFFFC));
		AD7716.c1_total = 0;
		AD7716.c1_n_averaged = 0;
	}
	return;
}
	
	
		


void AD7716_filter_chan_two (unsigned short high, unsigned short low)  {
	long newest;
	
	newest = AD7716_data_to_long (high, low);
	AD7716.c2_total += newest;
	AD7716.c2_n_averaged++;
	if (AD7716.c2_n_averaged >= AD7716.n_to_average)  {
		AD7716.c2_total /= AD7716.n_to_average;
		AD7716_push_data_to_buff (0x01 | (AD7716.c2_total&0xFFFFFFFC));
		AD7716.c2_total = 0;
		AD7716.c2_n_averaged = 0;
	}
	return;
}
	
	
		


void AD7716_filter_chan_three (unsigned short high, unsigned short low)  {
	long newest;
	
	newest = AD7716_data_to_long (high, low);
	AD7716.c3_total += newest;
	AD7716.c3_n_averaged++;
	if (AD7716.c3_n_averaged >= AD7716.n_to_average)  {
		AD7716.c3_total /= AD7716.n_to_average;
		AD7716_push_data_to_buff (0x03 | AD7716.c3_total);
		AD7716.c3_total = 0;
		AD7716.c3_n_averaged = 0;
	}
	return;
}
	
	
		




/*	AD7716_SPI_buffer_open (long n_points)
 *  Internal use only
 *
 *  Allocates and initializes the buffer used by the SPI interrupt
 *  handler routine to store incoming points.  n_longs is the number
 *  of long integers (one per channel saved) which the buffer can hold,
 *  while n_points is the number of short integers which the buffer
 *  can hold.
 *
 *  Arguments: size of buffer (in points, one long per point)
 *  Returns: 0 on success; negative on failure
 *  Failures: only on memory allocation failure
 *
*/ 
int AD7716_SPI_buffer_open (long n_longs)  {
	//  REVIEW - is this true on a warm reboot?
	//  don't allow memory leaks
	ASSERT (SPI_buffer.data_start == NULL);
	ASSERT (AD7716.AD7716_open_flag == 1);

	//  n_points refers to number of 4 byte longs
	SPI_buffer.n_points = n_longs;
	SPI_buffer.data_start = (long*) malloc (sizeof (long) * SPI_buffer.n_points);
	if (SPI_buffer.data_start == NULL)  {
		SPI_buffer.next_in = NULL;
		SPI_buffer.next_out = NULL;
		SPI_buffer.data_end = NULL;
		SPI_buffer.n_points = 0;
		#ifdef DEBUG
			printf ("AD7716_SPI_buffer_open: Memory malloc (%ld) failure\n", (sizeof (long)*n_longs));
			mem_find_max_malloc ();	
		#endif
		return -1;
	}
	
	//  the buffer empty routine sets values for data_end, next_in, and next_out
	AD7716_SPI_buffer_empty ();
	return 0;
}




//  AD7716_SPI_buffer_empty
//  Internal use only
//
//  Marks the SPI buffer as empty;  the buffer must be allocated before
//  the routine call be called.
//
void AD7716_SPI_buffer_empty (void)  {
	#ifdef DEBUG
		//  if debugging, init buffer to known values
		long n;
		ASSERT (AD7716.AD7716_open_flag == 1);
		ASSERT (SPI_buffer.data_start != NULL);
		
		for (n = 0;  n < SPI_buffer.n_points;  n++)
			SPI_buffer.data_start[n] = 0xCF;
	#endif		
	SPI_buffer.next_in = SPI_buffer.data_start;
	SPI_buffer.next_out = SPI_buffer.data_start;
	//  Note that data_end points one past the end of the actual buffer
	//  I suppose this might be a problem with some compilers or memory
	//  ranges, but it seems to work fine here.
	SPI_buffer.data_end = &SPI_buffer.data_start[SPI_buffer.n_points];
	SPI_buffer.overflow = 0;
}




//  AD7716_SPI_buffer_delete (void)
//  Internal use only
//
//  Deletes an SPI buffer which was allocated above
//
void AD7716_SPI_buffer_delete (void)  {
	ASSERT (AD7716.AD7716_open_flag == 1);
	if (SPI_buffer.data_start == NULL)
		return;

	SPI_buffer.overflow = -1;
	#ifdef DEBUG
		//  if debugging, reset pointers to illegal values so as to
		//  catch errors later.
		SPI_buffer.next_in = NULL;
		SPI_buffer.next_out = NULL;
		SPI_buffer.data_end = NULL;
		SPI_buffer.n_points = 0;
	#endif
	ASSERT (SPI_buffer.data_start != NULL);
	free (SPI_buffer.data_start);
	SPI_buffer.data_start = NULL;
	return;
}






//	AD7716_push_data_to_buff
//	Internal use only
//
//  Puts the given data into the circular SPI buffer.  Continously
//	wraps around end of buffer.
//
//	This routine only checks the buffer pointer to see if it wrapped
//	after the data is added to the buffer.
//
//	Arguments: a long which is to be added to the buffer
//	Returns, failures: none
//
void AD7716_push_data_to_buff (long value)        
	{
	*SPI_buffer.next_in++ = value;
	AD7716.data_pushed++;

	if(SPI_buffer.next_in == SPI_buffer.data_end)  {
		//  We've reached the end of the buffer, so wrap to start
		SPI_buffer.next_in = &SPI_buffer.data_start[0];
	}
		
	//  If this fails, we are about to overwrite data; this would occur
	//  if we failed to remove data from the buffer fast enough.
	//  We need to make the buffer length long enough and the code fast
	//  enough to avoid this, but if it happens, it is important to catch
	//  it and handle it properly
	if (SPI_buffer.next_in == SPI_buffer.next_out)
		SPI_buffer.overflow = 1;
}




short AD7716_check_for_overflow (void)  {

	//  overflow equals -1 if ad7716 is closed
	if (SPI_buffer.overflow == 1)
		return 1;
	else
		return 0;
}


void AD7716_clear_overflow (void)  {
	//  don't clear unless we are open
	if (AD7716.AD7716_open_flag != 0)
		SPI_buffer.overflow = 0;
}




//  AD7716_get_data_point (long* value)
//  User callable
//
//  Collects the oldest data point from the SPI buffer and returns it to
//  to the user through the pointer passed.  The result returned
//  is a 24 bit result.  The lower two bits are the channel information
//  as defined in the AD7716 data sheet, and are a constant for a given
//  channel.  This results in an insignificant (and constant) offset
//  added to each channel.
//
//  Arguments: pointer to a long where the data will be put (if available)
//  Returns: 0 if data was returned;
//			 -1 if there is no data waiting
//  Failures: no failures, though data may not be returned
//
int AD7716_get_data_point (long* value)        
	{
	time_tt now;
	long n_samples;
	double old_rate, fraction;
	#ifdef SIMULATE_DATA
		static long sim_data = 0;
		static short chan_count;
	#endif	
	
	if (AD7716.AD7716_open_flag == 0)
		return -1;

	#ifdef SIMULATE_DATA
		#ifdef SHORT_SCIENCE_DATA
			*value = sim_data++ << 8;
			if (sim_data > 32766)
				sim_data = -32766;
			*value |= AD7716.sim_chan[chan_count++];
			if (chan_count >= AD7716.n_channels)
				chan_count = 0;
			AD7716.data_pushed++;	
			AD7716.n_simulated++;
			return 0;
		#else
			ASSERT(0);
			*value = 0x0FCFCFCF;
			return 0;
		#endif
	#endif
		
	/*  check if there are any numbers in the buffer; if not, return
	 *  negative number; if debugging, assign an impossible value to
	 *  the users pointer
	*/ 
	if (SPI_buffer.next_out == SPI_buffer.next_in)  {
		#ifdef DEBUG
			*value = 0x0FCFCFCF;
		#endif	
		return -1;
	}
	
	*value = *SPI_buffer.next_out++;
	if (SPI_buffer.next_out == SPI_buffer.data_end)
		SPI_buffer.next_out = SPI_buffer.data_start;

	//  After returning a certain number of data points we recalculate
	//  the actual measured conversion rate.
	//
	//  Rate calculation works as follows:
	//  The rate is calculated by recording a starting time, and a starting number
	//  of interrupts.  When the threshold is passed, the code calculates how much
	//  time has passed, how many interrupt occured, and the interrupts/second.
	//  The code recalculates the conversion rate after every threshold data points
	//  are returned to the user.  If we have reason to believe that the current
	//  data might be bad (for example, the interrupt processing was paused, and we
	//  have no way of knowing whether it was restarted in time), then we can
	//  temporarily suppress a calculation by setting the start time seconds to
	//  0 which is obviously an illegal value.  In this case, the software will save
	//  a new start time and interrupt count, use the old conversion rate, and
	//  recalculate the exact conversion rate next time around.  If we want to
	//  suppress the use of the calculated conversion rate till a new value is
	//  calculated, (as we would if the conversion rate is changed) then we set
	//  the measured_rate to be less than or equal to 0.
	if (AD7716.data_pushed > AD7716.rate_calc_threshold)  {
		//  wait for the interupt to be serviced and data to be saved
		AD7716_data_saved ();

		//  collect the data which defines this sample: the current time,
		//  and the counter which tell us how much data has been saved.
		now = logger_time_get_time ();
		
		//  The old rate is used for error checking because we are temporarily concerned
		//  about the real time clock.  The new rate should not differ from the old rate
		//  by very much.
		old_rate = AD7716.measured_rate;

//printf ("  Calculating new rate.\n");		
		//  calculate how many samples were taken between the two times		
		ASSERT ((AD7716.data_pushed%AD7716.n_channels) == 0);
		ASSERT (AD7716_get_n_channels() > 0);
		n_samples = AD7716.data_pushed / AD7716_get_n_channels ();
//printf ("  n samples is %ld\n", n_samples);
//printf ("  Start time is %s", logger_time_get_string (&AD7716.start_time));
//printf ("  Now is %s", logger_time_get_string (&now));
//printf ("  Difference is %ld ticks\n", (long)ttmcmp (now, AD7716.start_time));
		AD7716.measured_rate = ((double)n_samples * (double)GetTickRate ());
		AD7716.measured_rate /= (double)(ttmcmp (now, AD7716.start_time));
		//  This print used to be included so that we could track any unexpected gains
		//  or losses of 1 second, which we suspected at one point.  However, months
		//  of error free operation has led to the conclusion that our suspicions were
		//  wrong, and we no longer compile this printf
//		log_printf ("Rate %.9lf at %s", AD7716.measured_rate, logger_time_get_string (&now));

		//  Check for and log any discontinuity in the calculated rate.  A discontinuity
		//  could indicate a spurious count of the seconds.  This code will trigger the
		//  first time through when the old rate is -1.  I leave this in just to test
		//  its execution.
		fraction = old_rate / AD7716.measured_rate;
		fraction -= 1.0;
		fraction = fabs (fraction);
		//  Threshold currently set at 0.1 %, which will easily catch a one second
		//  error over 300 seconds.  Normal uncertainties in the rate should be of
		//  the order of ppm.
		if (fraction > 0.001)  {
			log_printf ("A/D conversion rate is %.8f\n", AD7716.measured_rate);
			if (old_rate != -1)
				log_printf ("That was an unexpected rate jump! (old rate %.9lf)\n", old_rate);
		}		
		
		//  and set up for next time
		AD7716_data_saved ();
		AD7716.start_time = logger_time_get_time ();
		AD7716.data_pushed = 0;
	}

	return 0;
}




//	AD7716_data_to_long
//  Internal use only
//
//  Converts two's-complement data from AD7716 (22 bits+10 status bits) 
//  to long integer format. Looks at overflow bit and assigns 
//  overflowed data to full scale value (with polarity as defined
//  by MSB of data).
//
//	Changed from Tom's original code in that the result returned is
//	now 24 bits, with the lower two bits being the channel number as
//	defined in the 7716 data sheet.  This changes the scaling and
//	produces an offset on each channel.  The offset is constant
//	and small enough to ignore.  This allows us to always know what
//	channel a piece of data came from.
//
//	Arguments: high = the high 16 bits of the two word input
//			   low = the low 16 bits
//  Returns: the long data, as described above
//  Failures: none
//
long AD7716_data_to_long (unsigned short high, unsigned short low)
	{
	long result;
	short chan;
	//  verify that the Device Address and the Pace Detect are both 0
//	ASSERT ((low&0x00F0) == 0);

	/*  start by checking for overflow; no need to look at data if
	 *  overflowed; just set to min or max according to sign
	 */
	if(low & 0x0008)  {
		/*  when making up the overflow number, make sure we keep
		 *  the channel information correct.
		*/ 
		chan = (low >> 8) & 0x03;

		/*  if overflowed, then next question is sign of data  */
		if(high & 0x8000)
			/*  NEGATIVE FULL SCALE  */
			result = ((-1*0x200000) << 2) | chan;
		else
			/*  POSITIVE FULL SCALE  */
			result = (0x1FFFFF << 2) | chan;
	}  else  {
		/*  not overflowed, so get the real data  */
//		result = ((0x0FFFFL & (long)high) << 8) | (0x00FFL & (((long)low) >> 8));	
		result = ((0x0FFFFL & (long)high) << 8) | (0x00FFL & (long)(low >> 8));	
				
		/*  now adjust the sign of the data if necessary */
		if(high & 0x8000)
/*		
			this was not adjusted when I went to a 24 bit result
			result = -1*(~(Channel1Data-1)-0xFFC00000);
*/	
			result |= 0xFF000000;
	}
	return result;
}






//	AD7716_get_data_point_time (void)
//  User callable
//
//	Returns the time at which the AD converter converted the data point
//  most recently returned by the AD7716_get_data_point() routine.  This
//  is necessary in order for the user to accurately determine the time
//  a particular data point was acquired.
//
//
time_tt AD7716_get_data_point_time (void)  {
	time_tt last_time;
	long n_waiting, secs, ticks;
	double delta_t;
	ASSERT (AD7716.AD7716_open_flag == 1);
	
	//  Improve the accuracy of this routine by waiting for data to
	//  be saved before doing the calculations.  This eliminates 
	//  random noise which would range from 0 to a conversion time.
	//  Probably irrelavent, but we may as well do it.
	//  REVIEW - we probably also should make sure we are still taking
	//  data - otherwise this routine is meaningless.
	AD7716_data_saved ();
	last_time = logger_time_get_time (); 
	n_waiting = AD7716_get_n_waiting ();

	//  The accuracy of the conversion from n_waiting to a delta_t depends
	//  on the accuracy of the conversion rate, which is only as accurate as
	//  the ADC crystal accuracy at first, and then quite good once we have
	//  converted enough data to have computed a measured conversion rate.
	delta_t = n_waiting / AD7716_get_conversion_rate ();	
	secs = (long) delta_t;
	ticks = (long)(0.5 + GetTickRate()*(delta_t - secs));
	
	last_time.secs -= secs;
	last_time.ticks -= ticks;
	if (last_time.ticks < 0)  {
		last_time.ticks += GetTickRate ();
		last_time.secs -= 1;
	}
	ASSERT ((last_time.ticks >= 0) && (last_time.ticks < GetTickRate ()));
	return last_time;
}
		



//	AD7716_get_channel_n (long value)
//  User callable
//
//	This routine returns the channel number (1 through 4) from
//	which this data was taken.  This is more complicated then
//  it should be because of the goofy way the Analog Devices
//  people encode the channel number as a bit pattern.  Why they
//  couldn't use (n-1) I don't know.  This routine may be called
//  even when the logger is not opened
//
//  Arguments: a A/D value as returned by AD7716_get_data_point()
//  Returns: The channel number from which the data was taken; the
//				channels are numbered 0-3, unlike the AD7716 docs,
//				which call them 1-4
//  Failures: none possible.  A return of -1 should be impossible
//		unless this code is broken.
//
int AD7716_get_channel_num (long value)  {
	value &= 0x03;
	
    switch (value)  {
    	case 0:
    		return 0;
    		
    	case 0x02:
    		return 1;
    		
    	case 0x01:
    		return 2;
    		
    	case 0x03:
    		return 3;
    		
    	default:
    		ASSERT (0);
    		break;
    }						
    return -1;
}	




unsigned long AD7716_get_icount (void)  {
	unsigned long icount;
	
	return AD7716.icounter;
}




//  REVIEW
//
//	The original code caught a compiler bug.  The compiler seems to use
//  logical shift right to divide by 2 (or sizeof short).  This works fine
//  as long as the number being divided is positive.  Fails miserably if
//  number is negative.  A logical shift replaces the leading sign bit with
//  a zero and converts from a negative number to a very large positive number
//
//  From the assembler code printout we see that the compiler does the
//  following:
//  	;	n_waiting /= sizeof(short); 
//		^	lsr.l	#1,d4
//  A collection of printf statements produced the following:
//		n_waiting before division = -30676
//		n_waiting after division = 2147468310
//
//  Write this routine so we never divide a negative number by 2
//
//
//  REVIEW
//  In theory this calculation could be screwed if an AD7716 interrupt occured in
//  the middle of the subtraction below, but this seems essentially impossible at
//  any usable clock speed (as long as there are NO other routines interrupting), so we don't
//  bother disabling interrupts here.  If other routines will interrupt (such as the
//  disk) then we should disable interrupts before we get the time.
//
//  Should really fix this so that overflows are properly indicated.
long AD7716_get_n_waiting (void)  {
	long result;

	if (AD7716.AD7716_open_flag != 1)
		return 0;

	#ifdef SIMULATE_DATA
		//  Fake data routines return data without actually collecting anything
		//  from the 7716.  Since the data is faked, there is always plenty waiting.
		return 100;
	#endif	
/*
printf ("n_points = %ld\n", SPI_buffer.n_points);		
printf ("Next in = %ld\n", (long)SPI_buffer.next_in);
printf ("Next out = %ld\n", (long)SPI_buffer.next_out);		
*/

	result = ((long)(SPI_buffer.next_in) - (long)(SPI_buffer.next_out));
	ASSERT ((result%4) == 0);
	if (result < 0)
		result += (sizeof (long) * SPI_buffer.n_points);

	//  convert from number of bytes to number of long words
	ASSERT (result >= 0);
	result /= sizeof (long);
	ASSERT (result <= SPI_buffer.n_points);	

	//  finally, one more division to account for the number of channels
	//  we are saving
	result /= AD7716.n_channels;
	return result;
}	





#ifdef DEBUG
void AD7716_critical_data_dump (void)  {
	long n, m, set, sets_pulled;
	long pushed;
	long pulled;
	long start, stop;
	long data_index;
	if (AD7716.AD7716_open_flag != 1)  {
		printf ("AD7716 closed; no data to dump.\n");
		return;
	}	
	
	PConfInp(F,4);
	printf ("AD7716 Critical Data Dump:\n");
	printf (" AD7716: data_start = 0x%08lX = %ld\n", (long)SPI_buffer.data_start,
													  (long)SPI_buffer.data_start);
	printf (" AD7716: n_points   = %ld [number of longs]\n", SPI_buffer.n_points);
	printf (" AD7716: data_end   = 0x%08lX = %ld\n", (long)SPI_buffer.data_end,
												 	(long)SPI_buffer.data_end);
	printf (" AD7716: next_in    = 0x%08lX = %ld\n", (long)SPI_buffer.next_in, 
													(long)SPI_buffer.next_in);
	printf (" AD7716: next_out   = 0x%08lX = %ld\n", (long)SPI_buffer.next_out,
														(long)SPI_buffer.next_out);
	printf (" AD7716: interrupt count = %ld\n", AD7716_get_icount ());
	
	printf ("AD7716 Inferred data:\n");
	pushed = ((long)(SPI_buffer.next_in)-(long)(SPI_buffer.data_start))/sizeof(long); 
	printf (" AD7716: number of words pushed = %ld\n", pushed);
	pulled = ((long)(SPI_buffer.next_out)-(long)(SPI_buffer.data_start))/sizeof(long); 
	printf (" AD7716: number of words pulled = %ld\n\n", pulled);
	printf ("Actual data:\n");
	sets_pulled = pulled/2;
	if (sets_pulled >= 3)  {
		start = sets_pulled - 3;
		stop = sets_pulled + 1;
	}  else  {
		start = 0;
		stop = 4;
	}

    /*
	start = 0;
	stop = 4;
	data_index = start*2*AD7716.n_channels;		
	for (set = start;  set < stop;  set += 1)  {
		for (m = 0;  m < 8;  m++)  {
			if (m == 0)
				printf (" AD7716: ");
			else
				printf ("         ");	 
			printf ("SPI[%ld] = 0x%04X;", m+set*8L, SPI_copy[m+set*8L]);
			switch (m)  {
				case 0:
				case 1:
					if (AD7716.save_channels&0x01)  {
						printf ("  data[%ld] = 0x%04X",
							 data_index, SPI_buffer.data_start[data_index]);
						if ((m%2) != 0)  {
							printf (";  value = 0x%08lX\n", (long)
								AD7716_data_to_long (SPI_buffer.data_start[data_index-1],
														SPI_buffer.data_start[data_index]));
						}  else
							printf ("\n");
						data_index++;
					}  else
						printf ("\n");
					break;
					
				case 2:
				case 3:
					if (AD7716.save_channels&0x02)  {
						printf ("  data[%ld] = 0x%04X",
							 data_index, SPI_buffer.data_start[data_index]);
						if (m%2 != 0)  {
							printf (";  value = 0x%08lX\n", (long)
								AD7716_data_to_long (SPI_buffer.data_start[data_index-1],
														SPI_buffer.data_start[data_index]));
						}  else
							printf ("\n");
						data_index++;
					}  else
						printf ("\n");
					break;
					
				case 4:
				case 5:
					if (AD7716.save_channels&0x04)  {
						printf ("  data[%ld] = 0x%04X",
							 data_index, SPI_buffer.data_start[data_index]);
						if (m%2 != 0)  {
							printf (";  value = 0x%08lX\n", (long)
								AD7716_data_to_long (SPI_buffer.data_start[data_index-1],
														SPI_buffer.data_start[data_index]));
						}  else
							printf ("\n");
						data_index++;
					}  else
						printf ("\n");
					break;
					
				case 6:
				case 7:
					if (AD7716.save_channels&0x08)  {
						printf ("  data[%ld] = 0x%04X",
							 data_index, SPI_buffer.data_start[data_index]);
						if (m%2 != 0)  {
							printf (";  value = 0x%08lX\n", (long)
								AD7716_data_to_long (SPI_buffer.data_start[data_index-1],
														SPI_buffer.data_start[data_index]));
						}  else
							printf ("\n");
						data_index++;
					}  else
						printf ("\n");
					break;
					
				default:
					printf ("\n");
			}			
		}		
	}															
	*/
}
#endif	


void AD7716_icount_print (void)  {
	printf ("interrupt count is %ld\n", AD7716.icounter);
	printf ("data push counter is %ld\n", AD7716.data_pushed);
}
	