/*
 * DPv3.c
 *
 * Created: 19. 10. 2015 19:13:36
 * Author : Lukas Patocka
 */ 

#include <stdio.h>
#include <string.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include <LUFA/Platform/Platform.h>
#include <LUFA/Drivers/USB/USB.h>

#include "DPv4.h"
#include "Descriptors.h"

// CPU clock
#define F_CPU			32000000UL
// length of array with sine wave in program memory
#define TABLE_LENGTH	512

// buffer size in EEPROM
uint16_t EEMEM bufferSize;
// divider in EEPROM
uint16_t EEMEM divider;
// offset in EEPROM
uint16_t EEMEM dcoffset;
// waveform buffer in EEPROM
uint16_t EEMEM Data[512];

uint16_t tmp_Data[512];

// buffer size in SRAM
uint16_t BSIZE;
// divider in SRAM
uint16_t DIV;
// offset in SRAM
uint16_t DCOFFSET;

// static array with sine wave in program memory
extern const uint16_t wave[TABLE_LENGTH] PROGMEM;

// informations of DAC to send to application
// count of DAC bits
const uint32_t PROGMEM DA_bits = 12;
// sample rate
const uint32_t PROGMEM DA_freq = 160000;
// dividing rate
const uint32_t PROGMEM DA_devs = 256;
// memory size
const uint32_t PROGMEM DA_mems = 512;

// static memory of processed bytes
static uint16_t processedInBytes = 0;
static uint16_t processedOutBytes = 0;

// static index of sample in array
static uint16_t index = 0;

int main(void)
{
	initDevice();

	sei();
	
	//LedBlink();

	while (1)
	{
		USB_USBTask();
		
		Endpoint_SelectEndpoint(EP_BULK_OUT);
		
		if(Endpoint_IsOUTReceived())
		{
			//LedBlinkGreen();
			DACoff();
			_delay_ms(10);
			SetGenerator();	
			DACon();
		}
		
/*		Endpoint_SelectEndpoint(EP_ISO_OUT);
		
		if(Endpoint_IsOUTReceived())
		{
			LedBlinkGreen();
		}*/
	}
}



void initDevice(void)
{
	// nastaveni vystupu pro LED
	PORTA.DIR = PIN0_bm | PIN1_bm | PIN2_bm | PIN3_bm;	

	// nastavenii casovani
	XMEGACLK_StartPLL(CLOCK_SRC_INT_RC2MHZ, 2000000, F_CPU);
	XMEGACLK_SetCPUClockSource(CLOCK_SRC_PLL);

	// casovani pro USB
	XMEGACLK_StartInternalOscillator(CLOCK_SRC_INT_RC32MHZ);
	XMEGACLK_StartDFLL(CLOCK_SRC_INT_RC32MHZ, DFLL_REF_INT_USBSOF, F_USB);

	PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;

	// inicializace knihovny LUFA
	USB_Init();

	// nacteni dat z EEPROM pameti do SRAM
	BSIZE = eeprom_read_word(&bufferSize);
	DIV = eeprom_read_word(&divider);
	DCOFFSET = eeprom_read_word(&dcoffset);

	// nastaveni casovani DA prevodniku
	TCC0.CTRLA = TC_CLKSEL_DIV1_gc ; // 1:1 with main clock
	if(DIV <= 0)
		TCC0.PER = 204;
	else
		TCC0.PER = 204 * DIV;
	TCC0.INTCTRLA = TC_OVFINTLVL_LO_gc ; // set overflow interrupt to lo

	// Povoleni a nastaveni DA prevodniku
	DACB.CTRLA = DAC_CH0EN_bm | DAC_CH1EN_bm | DAC_ENABLE_bm ;
	DACB.CTRLB = DAC_CHSEL_DUAL_gc ; // oba kanaly
	DACB.CTRLC = DAC_REFSEL_AVCC_gc; // referencni napeti = napajeci
}

void DACon(void)
{
	DACB.CTRLA = DAC_CH0EN_bm | DAC_CH1EN_bm | DAC_ENABLE_bm ;
}

void DACoff(void)
{
	DACB.CTRLA = 0;
}

void LedBlinkGreen(void)
{
	PORTA.OUTSET = PIN0_bm;
	_delay_ms(40);
	PORTA.OUTCLR = PIN0_bm;
	_delay_ms(20);
}

void LedBlinkRed(void)
{
	PORTA.OUTSET = PIN1_bm;
	_delay_ms(50);
	PORTA.OUTCLR = PIN1_bm;
	//_delay_ms(20);
}

void LedBlinkYellow(void)
{
	PORTA.OUTSET = PIN2_bm;
	_delay_ms(10);
	PORTA.OUTCLR = PIN2_bm;
	//_delay_ms(20);
}

void LedYOn(void)
{
	PORTA.OUTSET = PIN2_bm;
}

void LedYOff(void)
{
	PORTA.OUTCLR = PIN2_bm;
}

void EVENT_USB_Device_ControlRequest()
{
	{
		switch(USB_ControlRequest.bRequest)
		{
			case(GET_DA_BITS):
				if(USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE))
				{
					Endpoint_ClearSETUP();
					Endpoint_Write_32_LE(DA_bits);
					Endpoint_ClearIN();
					LedBlinkYellow();
				}
				break;

			case(GET_DA_FREQ):
				if(USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE))
				{
					Endpoint_ClearSETUP();
					Endpoint_Write_32_LE(DA_freq);
					Endpoint_ClearIN();
					LedBlinkYellow();
				}
				break;

			case(GET_DA_DEVS):
				if(USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE))
				{
					Endpoint_ClearSETUP();
					Endpoint_Write_32_LE(DA_devs);
					Endpoint_ClearIN();
					LedBlinkYellow();
				}
				break;

			case(GET_DA_MEMS):
				if(USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE))
				{
					Endpoint_ClearSETUP();
					Endpoint_Write_32_LE(DA_mems);
					Endpoint_ClearIN();
					LedBlinkYellow();
				}
				break;

			case(SET_BF_LENGTH):
				if(USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_VENDOR | REQREC_DEVICE))
				{
					Endpoint_ClearSETUP();
					Endpoint_Read_Control_Stream_LE(&BSIZE, sizeof(BSIZE));
					Endpoint_ClearOUT();
					LedBlinkYellow();
					eeprom_write_word(&bufferSize, BSIZE);
					processedInBytes = 0;
					processedOutBytes = 0;
				}
				break;

			case(SET_DIVIDER):
				if(USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_VENDOR | REQREC_DEVICE))
				{
					Endpoint_ClearSETUP();
					Endpoint_Read_Control_Stream_LE(&DIV, sizeof(DIV));
					Endpoint_ClearOUT();
					LedBlinkYellow();
					eeprom_write_word(&divider, DIV);
					
					if(DIV <= 0)
						TCC0.PER = 204;
					else
						TCC0.PER = 204 * DIV;
				}
				break;

			case(SET_DCOFFSET):
				if(USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_VENDOR | REQREC_DEVICE))
				{
					Endpoint_ClearSETUP();
					Endpoint_Read_Control_Stream_LE(&DCOFFSET, sizeof(DCOFFSET));
					Endpoint_ClearOUT();
					LedBlinkYellow();
					eeprom_write_word(&dcoffset, DCOFFSET);
				}
				break;
		}
	}
}



ISR( TCC0_OVF_vect )
{
	//if (index >= TABLE_LENGTH) index -= TABLE_LENGTH;
	if (index >= BSIZE) index -= BSIZE;
	//DACB.CH0DATA = Data2[index];
	DACB.CH0DATA = eeprom_read_word(&Data[index++]);
	//pgm_read_word( &wave[index] ) ;
	DACB.CH1DATA = DCOFFSET;
}




// bulk transfer and start generating
void SetGenerator(void)
{
	uint8_t result;
	//uint16_t size = Endpoint_BytesInEndpoint();	
	uint16_t ready = 0;
	
	LedYOn();
	// read data
	while((result = Endpoint_Read_Stream_LE(tmp_Data, sizeof(uint16_t) * BSIZE, &ready)) == ENDPOINT_RWSTREAM_IncompleteTransfer);
	
	// clear endpoint buffer
	Endpoint_ClearOUT();
	LedYOff();
	
	// wrong result signal
	if(result != ENDPOINT_RWSTREAM_NoError)
	{
		LedBlinkRed();
	}
	
	for(int i = 0; i < BSIZE; i++)
	{
		do
		{
			eeprom_write_word(&Data[i], tmp_Data[i]);
		} while (tmp_Data[i] != eeprom_read_word(&Data[i]));
	}

	/*
	do 
	{
		eeprom_write_block(tmp_Data, Data, sizeof(uint16_t) * BSIZE);
	} while (compare_data() > 0);
	*/
	
	// clear index
	index = 0;
	
	
	//---------------------------------------- kontrolni odeslani dat zpet
	/*
	Endpoint_SelectEndpoint(EP_BULK_IN);
	
	ready = 0;
	
	// write data
	while((result = Endpoint_Write_EStream_LE(Data, sizeof(uint16_t) * BSIZE, &ready)) == ENDPOINT_RWSTREAM_IncompleteTransfer);
	
	// clear endpoint buffer
	Endpoint_ClearIN();

	// wrong result signal
	if(result != ENDPOINT_RWSTREAM_NoError)
	{
		LedBlinkRed();
	}
	*/
}
/*
uint16_t ByteToUint(uint8_t low, uint8_t high)
{
	return (((uint16_t)high) << 8 | low);
}

uint16_t compare_data()
{
	for(size_t i = 0; i < BSIZE; i++)
	{
		if(tmp_Data[i] != eeprom_read_word(&Data[i]))
		{
			return i;
		}
	}
	return 0;
}
*/
