#include <stdio.h>
#include <asm/io.h>
#include <unistd.h>
#include <sys/time.h>


//	Copyright 2004-2005 by JDF of GhostGadgets.com
//	Written by JDF - info@GhostGadgets.com
//	Rev. B, Released 06-30-2005

//	06-30-2005: Fixed timing issues with need delays for TLC1542


//	Driver for the 10 bit TLC1542 11 channel A/D Converter
//	Compiling the c driver code in Linux (output file will be adc_driver):  cc 10BitDriver.c -o adc_driver
//	The function that takes samples is called "getSample(int Channel)", in which you would pass the integer
//	of the channel you want to read (1 - 11), and then the voltage reading on that channel is then returned
//	as a double after sampling.  Included is a main() function that prints a test reading of each channel.



// Define addresses
int writePort = 0x378;
int readPort = 0x379;

//	Define port write hex codes
int ioclk = 0x2;
int cs = 0x4;	
int address = 0x8;
	
//	Define port read hex codes
int EOC = 0x20;
int data = 0x40;

static int Value;
static int i;



double getSample(int Channel); // Declare this function
float getmS(void);
void mS_Delay(float Time);


struct timeval tv;  // Used for timestamps
unsigned long offsetTime;	// Use this to subtract from seconds





// Pause for x mS
void mS_Delay (float Time) {
	float DelayTime = getmS();
	while ((getmS() - DelayTime) < Time) { }
}




// Return the time elapsed in mS (with resolution to microseconds!)
float getmS () {
	gettimeofday(&tv,NULL);	
	return ( ((tv.tv_sec - offsetTime) *1000) + ((float)tv.tv_usec / 1000) );
}







//	Send in channel address
//	Then read in results after conversion is done
//	These two passes could be shorted to run faster if needed
double getSample(int Channel) {
	
	//	Decrease Channel by 1 (convert to computer counting mode)
	--Channel;
	
	//	CS High & 60 uS settle time
	mS_Delay(.06);
	outb(cs,writePort);

	//	Make sure EOC is high and safe to sample, if low wait for it
	while (((inb(readPort) & EOC) >> 5) == 0) {}
				
	//	10 uS CS settle, CS Low & 2 uS delay settle time
	mS_Delay(.01);
	outb(0,writePort);
	mS_Delay(.002);
	
	//	Read in channel address to chip
	for (i=0;i<12;i++) {

		//	output the address MSB, also makes I/O Clock low
		//	address is 1000 
		outb((Channel & address),writePort);

		//	Tap I/O Clock high so it is ready for the next bits
		outb(ioclk,writePort);
		
		// Move to next bit of Channel address, if past the end zeros outputed.
		Channel <<= 1;
		
	}

	//	CS High & 60 uS settle time
	mS_Delay(.06);
	outb(cs,writePort);
	
	//	Initialize value
	Value = 0;
	
	//	Make sure EOC is high and safe to sample, if low wait for it
	while (((inb(readPort) & EOC) >> 5) == 0) {}
				
	//	10 uS CS settle, CS Low & 2 uS delay settle time
	mS_Delay(.01);
	outb(0,writePort);
	mS_Delay(.002);
	
	//	Get conversion results from previous read in
	for (i=0;i<12;i++) {

		//	output the address MSB, also makes I/O Clock low
		//	address is 1000 
		outb((Channel & address),writePort);

		//	read in data bits MSB first (shift right so data bit is 1 or 0
		//	then shift Value left 1 and add in next bit)
		Value = (((inb(readPort) & data) >> 6) | (Value << 1));
		
		//	Tap I/O Clock high so it is ready for the next bits
		outb(ioclk,writePort);
	}	
	

	//	CS High & 60 uS settle time
	mS_Delay(.06);
	outb(cs,writePort);

	
	//	Convert Value to volts & return results
	return (((Value / 1.024)/1000) * 5);

}	// End function getSample





//	Test function to sample once from each channel
int main() {

  	//	Request access to Read/Write Port
	//	Only needed in Linux Systems
	ioperm(writePort, 1, 1);
	ioperm(readPort, 1, 1);
	//	Done requesting access
	
	
	// Initialize the time functions and set offset
	gettimeofday(&tv,NULL);
	offsetTime = (unsigned long)tv.tv_sec;  // Declare the inital offset time (so getmS isn't REAL big!)
	
	
	printf("\n\n");
	
	//	Print channel readings from 1 - 11
	int ch;
	for (ch=1; ch<=11; ch++) {
		printf("\tTest Input From Channel %d: %f\n",ch,getSample(ch));
	}
	


	printf("\n");
	

	//	Return access to Read/Write Port
	//	Only needed in Linux Systems
	ioperm(writePort, 1, 0);
	ioperm(readPort, 1, 0);
	//	Done returning access

	return 1;
	
} // End method main

