#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>

#include "peekpoke.h"
#include "eeprog.h"
#include "ep93xx_adc.h"

#define DATA_PAGE 0x80840000
#define CALIB_LOC    2027          //location of calibration values

/* Prototypes */
static void read_7xxx_adc(int adc_result[5][10]);
void print_ADC_channels();

/* globals */
static unsigned long dr_page, adc_page, syscon_page, pld_page;
	
int main(void)
{
	int devmem = open("/dev/mem", O_RDWR|O_SYNC);
	assert(devmem != -1);

	dr_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE
		, MAP_SHARED, devmem, DATA_PAGE);
	assert(&dr_page != MAP_FAILED);
	
	spistart = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
		MAP_SHARED, devmem, SPI_PAGE);
	assert(&spistart != MAP_FAILED);

	adc_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
		MAP_SHARED, devmem, ADC_PAGE);
	assert(&adc_page != MAP_FAILED);

	syscon_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE
		, MAP_SHARED, devmem, SYSCON_PAGE);
	assert(&syscon_page != MAP_FAILED);

	pld_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
		MAP_SHARED, devmem, PLD_PAGE);
	assert(&pld_page != MAP_FAILED);

	alarm(10);  //this program has 10 seconds to complete

	init_ADC(adc_page, syscon_page);
	print_ADC_channels(); 

	close(devmem);

	return 0;
}

/************************************************************************
 *DESCRIPTION: Read all five of the EP93xx onboard ADC. Discard the first
 *two samples then save the next 10.
 ***********************************************************************/
static void read_7xxx_adc(int adc_result[5][10])
{
	int i, j, cur_ch;

	for(i = 0; i < 5; i++) 
	{
		switch(i)
		{
			case 0:
				cur_ch = ADC_CH0;
			break;
			case 1:
				cur_ch = ADC_CH1;
			break;
			case 2:
				cur_ch = ADC_CH2;
			break;
			case 3:
				cur_ch = ADC_CH3;
			break;
			case 4:
				cur_ch = ADC_CH4;
			break;
		}

		//discard first two samples
		read_channel(adc_page, cur_ch);

		read_channel(adc_page, cur_ch);

		//read 10 more samples
		for(j = 0; j < 10; j++)
		{
			usleep(10000); 
			adc_result[i][j] = read_channel(adc_page, cur_ch);
		}
	}
}

/******************************************************************************
*DESCRIPTION: In eeprom 2027->2046 are the calibration values. 2027->2036
*are the zero volt calibration values and 2037->2046 are the 2.5V calibration
*values. Each calibration value is 16 bits written using little endian
*******************************************************************************/
void print_ADC_channels()
{
	double val, full_scale;
	int i, j, avg, ch, addr;
	int adc_result[5][10];
	int stored_cal[5][2];  //stored calibration values
	int virgin = TRUE;
	char buffer[20];

	/* intialize the eeprom */
	POKE16(pld_page, (PEEK16(pld_page) & ~CS_MSK)); //disable CS
	POKE32((spistart + SSPCR1), 0x10);  //turn on transmit
	while ((PEEK32(spistart + 0xc) & 0x10) == 0x10); // wait for unbusy
	while ((PEEK32(spistart + 0xc) & 0x5) != 0x1);   // empty FIFO's
	POKE16(pld_page, (PEEK16(pld_page) | CS_MSK));   //enable CS

	POKE32(spistart, 0xc7);                          // set SPI mode 3, 8bit
	POKE32(spistart + 0x10, 0x2);                    // divide clk by 2
	POKE32(spistart + 0x4, 0x0);                     // stop transmit

	while((ee_rdsr(dr_page) & 0x1) == 0x1);//wait for unbusy

	ee_wren(dr_page);
	ee_wrsr(dr_page, 0x0);              // make eeprom R/W

	while((ee_rdsr(dr_page) & 0x1) == 0x1);//wait for unbusy

	/* Read in the calibration values */
	printf("Calibration Values = \n[ ");

	addr = CALIB_LOC;
	for(i = 0; i < 20; i++)
	{
		ee_read_byte(dr_page, addr, &buffer[i]);
		
		//check if board has stored calibration values
		if(buffer[i] != 0xFF)
			virgin = FALSE;
		addr++;
	}
	
	//convert to 16 bit values
	j = 0;
	ch = 0;
	for(i = 0; i < 20; i = i + 2)
	{
		if(i == 10)
		{
			ch = 0;
			j = 1;
		}

		stored_cal[ch][j] = (buffer[i] | (buffer[i+1] << 8));
		printf("0x%x ", stored_cal[ch][j]);
		ch++;
	}
	printf(" ]\n");

	//Read the adc values for all 5 channels
	read_7xxx_adc(adc_result);

	if(virgin == TRUE)
		printf("No calibration values found...\n");
	else
		printf("Board has been calibrated by Technologic Systems...\n");


	//Convert to voltage
	for(i = 0; i < 5; i++)
	{
		avg = 0;
		
		full_scale = (((((double)(stored_cal[i][1] + 0x10000) 
			- stored_cal[i][0]) / 2.5 ) * 3.3 )); 

		for(j = 0; j < 10; j++)
			avg = adc_result[i][j] + avg;

		avg = avg / 10;
			
		if(avg < 0x7000)
			avg = avg + 0x10000;

		if(virgin == TRUE)  //use approximation
		{
			avg = avg - 0x9E58;
			val = ((double)avg * 3.3) / 0xC350;
		}
		else                //use calibration values
		{
			avg = avg - stored_cal[i][0];
			val = ((double)avg * 3.3) / full_scale;
		}
	
		printf("Channel %d: %3.3fV\n", i, val);
	}
		
	//make eeprom RO
	ee_wren(dr_page);
	ee_wrsr(dr_page, 0x1c);
	
}
