/*

    v1.0
	
    "int" is used for the ADC and DAC data although it should be unsigned, 
    it should be not a problem at all as this data are limited to 12bits...



*/

#include<ADuC7020.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>

void My_IRQ_Handler(void);

short i = 0;
unsigned char Byte_addr = 0;
int first = 1;
int i2c_cnt = 0;

#define BIGDAT_SZ 256
unsigned short BigDat[BIGDAT_SZ];
char text[512];

unsigned short Vout[4];

unsigned short start, stop,start_gnd,stop_gnd,start2,stop2,Vset,wf_len, step_max,N;

unsigned char transf,enab_gnd;

unsigned int trigg_cnt;

float Gain;


//char format	= 0;


unsigned char* pbuff;

unsigned char* plist[256];



void delay (int length)
{
	while (length >=0)
    	length--;
}


// conversion of the read value into it corresponding 12bits integer
int ADCtoDAT(unsigned long ADC)
{
	return (ADC&0xFFF0000)>>16;
}

unsigned long DATtoADC(int DAT)
{
	unsigned long ADC;
	ADC=DAT;
	return ADC<<16;
}

unsigned long DATtoDAC(unsigned short DAT)
{
	unsigned int ADC;
	ADC=DAT;
	return ADC<<16;
}


int Read_Digital(int n)
{	
    return ((GP0DAT&0x000000FF)>>n)&0x1;
}

void Write_Digital(int n, int state)
{	
    if(state==1)
	  GP2DAT=(0x00000001<<(n+16))|GP2DAT;
	else 
		GP2DAT=~((0x00000001<<(n+16))|(~GP2DAT));
}


void ADCpoweron(int time)
{
	ADCCON = 0x620;	 			// power-on the ADC
	while (time >=0)	  		// wait for ADC to be fully powered on
    time--;
}




void get_DACs(void)
{
	Vout[0]=DAC0DAT>>16;
	Vout[1]=DAC1DAT>>16;
	Vout[2]=DAC2DAT>>16;
	Vout[3]=DAC3DAT>>16;	
}

void set_DACs(void)
{
	DAC0DAT=DATtoDAC(Vout[0]);
	DAC1DAT=DATtoDAC(Vout[1]);
	DAC2DAT=DATtoDAC(Vout[2]);
	DAC3DAT=DATtoDAC(Vout[3]);
}

void lock_StabPulse_i2c(void)
{
	// define variables
	unsigned int cnt_N, tg_cnt;
	double sum,sum_gnd;
	unsigned short Vout2;
	double Vin,Vin_gnd;
	int Vmean;
	int step = 100;
	short armed; // this is used to detect the trigger : 
				 // when the trigger input is low armed is set to 1
				 // when a measurement start it is set to 0
	
	int k;
	unsigned short Data[256];


	POWKEY1 = 0x01;
	POWCON = 0x00;		   				// 41.78MHz
	POWKEY2 = 0xF4;
	 

	//GP1CON = 0x00000000;    // IO initialization
	//GP1DAT = 0xFF000000;	// set P1.n as digital output
	
	GP0CON = 0x00000000;	// IO initialization
	//GP0DAT = 0x00000000;	// set P0.n as digital input



	// ADC&DAC setting
	ADCpoweron(20000); // power on ADC
	REFCON = 0x01;	   // internal 2.5V reference
	DAC0CON = 0x12;	   // AGND-ARef range 0x12 2.5V
	DAC1CON = 0x12;	   // AGND-ARef range 0x12 2.5V
	DAC2CON = 0x12;	   // AGND-ARef range 0x12 2.5V
	DAC3CON = 0x12;	   // AGND-ARef range 0x12 2.5V
	ADCCP = 0x03;	   // conversion on ADC0
	ADCCON = 0x3E4;    // continuous conversion

	// IO setting
    GP2CON = 0x00000000;    // IO initialization
	GP2DAT = 0xFF000000;	// set P2.n as digital output
	GP0CON = 0x00000000;	// IO initialization
	GP0DAT = 0x00000000;	// set P0.n as digital input
	

	// locking parameters initialization
//	cnt = 0;      // 
	N = 50;       // number of measume,ts for averaging
	Vin = 0;      // initialize the voltage of first step
	Vmean = 2000;
	start = 20;
	stop = 30;
	start_gnd = 0;
	stop_gnd = 10;
	wf_len = 256;
	Vset = 10;
	step = 50;
	step_max = 100;
	Gain = 1;


	// I2C on P1.0 and P1.1
 	GP1CON = 0x22;

	IRQ = My_IRQ_Handler;
	IRQEN = 0x200;					// I2C0 Slave Interupt
	
	I2C0CFG = 0x04001;
	// Slave ID		  		
	I2C0ID0 = (0x50 + (((GP0DAT&0x000000FF)>>5)&0x1)+(((GP0DAT&0x000000FF)>>7)&0x1)*2)<<1;
	I2C0STX = 0x00;
	I2C0STX = 0x00;

	// assignation of the different pointers for the I2C exchange of data
	for (k=0;k<16;k++){
		plist[k] = (unsigned char*)(BigDat+k*16);
		plist[k+50] = (unsigned char*)(text+k*32);
	}
	
	for(i=0;i<BIGDAT_SZ;i++)
		BigDat[i]=0;

	sprintf(text,"pulse stabilization => %s\ncompiled: %s\nbecause we can!",__func__,__DATE__);
	
	for (k=0;k<4;k++){
		plist[100+k] = (unsigned char*)(Vout+k);
	}
	// 104
	// 105
	
	plist[120] = (unsigned char*)&I2C0ID0;
	plist[121] = (unsigned char*)&ADCCP;
	plist[122] = (unsigned char*)&transf;
	plist[123] = (unsigned char*)&wf_len;
	plist[124] = (unsigned char*)&trigg_cnt;

	plist[125] = (unsigned char*)&Vset;
	plist[126] = (unsigned char*)&N;
	plist[127] = (unsigned char*)&start;
	plist[128] = (unsigned char*)&stop;
	plist[129] = (unsigned char*)&start2;
	plist[130] = (unsigned char*)&stop2;
	plist[131] = (unsigned char*)&start_gnd;
	plist[132] = (unsigned char*)&stop_gnd;
	plist[133] = (unsigned char*)&enab_gnd;
	plist[134] = (unsigned char*)&Gain;
	plist[135] = (unsigned char*)&step_max;
	
	
		
	DAC0DAT = DATtoADC(10);
	DAC1DAT = DATtoADC(20);
	DAC2DAT = DATtoADC(2000);
	DAC3DAT = DATtoADC(40);
	
	Vset=0;

	Vout[3] = 111;
	set_DACs();

	transf = 0;
	trigg_cnt = 0;
	tg_cnt = 0;
	cnt_N = 0;
	enab_gnd = 0;


	// main loop for the locking
	while(1){
	
		// trigg in is on p0.3 => we check that it is low first (rising edge detection)
		if((((GP0DAT&0x000000FF)>>3)&0x1)==0){
			armed = 1;
		}		

		// now p0.3 is high => this is our rising edge
		if((((GP0DAT&0x000000FF)>>3)&0x1)==1 && armed==1){
			
			armed = 0;	
			//*** aquisition of the waveform  ***
			for(k=0;k<wf_len;k++){
				while(!ADCSTA){}	// wait for the end of ADC conversion
				Data[k]= (ADCDAT&0xFFF0000)>>16; // read voltage from ADC0
			}
			tg_cnt++;
			//*** copy for the i2c ***			
			while(transf==1){} // we make sure that the data are not changed during the transfer
			memcpy(BigDat,Data,256*sizeof(short));
			trigg_cnt = tg_cnt;
			
			if (cnt_N==0){
				sum = 0; // initialization	of the measurement
				sum_gnd = 0;
			}
			cnt_N++;
						

			//sum of the data
			for(k=start;k<stop;k++){
				sum += Data[k];
				//cnt++;
			}
			for(k=start_gnd;k<stop_gnd;k++){
				sum_gnd += Data[k];
				//cnt_gnd++;
			}
			if(cnt_N>=N){
				Vin = sum/(cnt_N*(stop-start));	// calculate average value
				Vin_gnd = sum_gnd/(cnt_N*(stop_gnd-start_gnd));	// calculate average value
			}

			//*** feedback *** (mode is on pin p0.6)

			// LOCK MODE
			if((((GP0DAT&0x000000FF)>>6)&0x1)==1){
				if(cnt_N>=N){
					cnt_N=0;
					//cnt_gnd=0;
					step = (Vset-Vin+Vin_gnd*enab_gnd)*Gain;
					if (step>step_max)
						step = step_max;
					else if (step<-step_max)
						step = -step_max;
					Vout2 = Vout2 + step;
					if(Vout2>4095){
						Write_Digital(0,1);
						Vout2 = 4090;
					}
					else{
						Write_Digital(0,0);
					}	 		
				}
			}

			// LEARN MODE
			// we set the outputs to the average voltage
			// and save the current input level as the set point of the next locking enable
			else{
				if(cnt_N>=N){
					Vset = Vin-Vin_gnd*enab_gnd;
					cnt_N=0;
				}
				Vout2 = Vmean;
			}

			//this line could also be inserted in the if conditions and thus not set every loops
			DAC2DAT = DATtoADC(Vout2); // output voltage
		}
		
	}
}




int main(void)
{ 
	
	lock_StabPulse_i2c();

	return 0;
}





/*************************************************/
/*************************************************/
/************	IRQ Service Routine  *************/
/*************************************************/
/*************************************************/



void My_IRQ_Handler()
{
	int status = I2C0SSTA;
	
	// Slave Recieve
	if ((status & 0x08)==0x08)   // Slave Recieve IRQ
	{			
			if(first==1){
				first=0;
				Byte_addr=I2C0SRX;
				I2C0FSTA|= 1 << 8;

				i2c_cnt = 0;
				if(Byte_addr==122)
					transf = 1;
				if(Byte_addr==104)
					get_DACs();
				if(Byte_addr==105)
					set_DACs();

				Write_Digital(2,0);
				pbuff = plist[Byte_addr];
				I2C0STX = pbuff[0];	
			
			}
			else {
				pbuff[i2c_cnt] = I2C0SRX;
				i2c_cnt++;	
			}		
	}
	
	// Slave Transmit
	else if ((status & 0x04)==0x04)   // Slave Transmit IRQ
	{
		i2c_cnt ++;
		I2C0STX = pbuff[i2c_cnt];
		I2C0ADR = 0xA1;
		//if(Byte_addr>=110 && Byte_addr<=113 && i2c_cnt==1)
			//set_DACs();	   			   		
	}
	
	else if((status & 0x0400)==0x0400)   // 
	{
		 first = 1;
		 //Write_Digital(2,1);
	}
	
	// either we transfer a waveform or some parameters
	// and when it is done we should put back transf = 0
	/*if((i2c_cnt>31 && Byte_addr==15) || (i2c_cnt>1 && Byte_addr>15)){
		Write_Digital(2,1);
		transf = 0;
	} */
	/*if(i2c_cnt>31 && Byte_addr==15){
		Write_Digital(2,1);
		transf = 0;
	}*/
}
