/* ----------------------------------------------------------------------------
 *         Ambulantní monitor srdečního rytmu
 * ----------------------------------------------------------------------------
 * Peter Majerík
 * VUT Brno
 * FEKT
 * UTKO
 * ----------------------------------------------------------------------------
 */

//------------------------------------------------------------------------------
//         Headers
//------------------------------------------------------------------------------

#include <board.h>
#include <aic/aic.h>
#include <pio/pio.h>
#include <pio/pio_it.h>
#include <pit/pit.h>
#include <tc/tc.h>
#include <rtt/rtt.h>
#include <dbgu/dbgu.h>
#include <sdmmc/sdspi.h>
#include <utility/assert.h>
#include <utility/trace.h>
#include <sdmmc/sdmmc_spi.h>
#include <adc/adc.h>

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

#define BOARD_ADC_FREQ 5000000
#define ADC_VREF       3300  // 3.3 * 1000

//------------------------------------------------------------------------------
//         Local variables
//------------------------------------------------------------------------------

/// Pio pins to configure.
#ifdef PINS_ADC
static const Pin pinsADC[] = {PINS_ADC};
#endif

/// SPI driver instance.
static SdSpi sdSpiDrv;

/// SDCard driver instance.
static SdCard sdDrv;

/// SD card pins.
static const Pin pinsSd[] = {BOARD_SD_SPI_PINS};
 
//------------------------------------------------------------------------------
//         Local define
//------------------------------------------------------------------------------

/// Definice ADC kanalu a EOC registru

#define ADC_NUM_1  ADC_CHANNEL_0
#define ADC_NUM_2  ADC_CHANNEL_1
#define ADC_NUM_3  ADC_CHANNEL_2
#define ADC_NUM_4  ADC_CHANNEL_3
//#define ADC_NUM_5  ADC_CHANNEL_4

#define EOC1  AT91C_ADC_EOC0
#define EOC2  AT91C_ADC_EOC1
#define EOC3  AT91C_ADC_EOC2
#define EOC4  AT91C_ADC_EOC3
//#define EOC5  AT91C_ADC_EOC4

/// Definice multibloku posilanych na SD kartu = 5 (5 * 512)
#define NB_MULTI_BLOCKS 10

/// SPI clock frequency, in Hz.
#define SPCK        10000000

/// Define AT91C_BASE_RTTC if not already done
#if !defined(AT91C_BASE_RTTC)
    #define AT91C_BASE_RTTC     AT91C_BASE_RTTC0
#endif


//------------------------------------------------------------------------------
//         Local functions
//------------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/// Convert a digital value in milivolt
/// /param valueToconvert Value to convert in milivolt
//-----------------------------------------------------------------------------
static unsigned int ConvHex2mV( unsigned int valueToConvert )
{
    return( (ADC_VREF * valueToConvert)/0xFF);
}

//-----------------------------------------------------------------------------
/// Zaznamena data urciteho kanalu
/// /param channel na ulozeni dat
/// /param id_channel identifikace kanalu, z ktereho jsou data zaznamenana
/// /param EOC registr pro ukonceni konverze kanalu
//-----------------------------------------------------------------------------
//static void ADCConv(unsigned int channel, unsigned int id_channel, unsigned int EOC)
unsigned int ADCConv(unsigned int id_channel, unsigned int EOC)
{	  
	 unsigned int channel; 	 
	 ADC_EnableChannel(AT91C_BASE_ADC, id_channel);
	 ADC_StartConversion(AT91C_BASE_ADC);
	 while ((AT91C_BASE_ADC->ADC_SR & EOC) == 0);
	 
	 channel = ADC_GetConvertedData(AT91C_BASE_ADC, id_channel);
	                 
	 ADC_DisableChannel(AT91C_BASE_ADC, id_channel);

	 return channel;
}

//------------------------------------------------------------------------------
/// SPI0 interrupt handler. Forwards the event to the SPI driver handler.
//------------------------------------------------------------------------------
void ISR_Spi0(void)
{
    SDSPI_Handler(&sdSpiDrv);
}


//------------------------------------------------------------------------------
/// Nastavi RTT na 1/ 4936 sekundy.
//------------------------------------------------------------------------------
void ConfigureRtt(void)
{
    unsigned int previousTime;

    // Configure RTT for a 1 second tick interrupt
    RTT_SetPrescaler(AT91C_BASE_RTTC, 6);
    previousTime = RTT_GetTime(AT91C_BASE_RTTC);
    while (previousTime == RTT_GetTime(AT91C_BASE_RTTC));
}

//------------------------------------------------------------------------------
//         Optional: SD card detection
//------------------------------------------------------------------------------

#ifdef BOARD_SD_PIN_CD

/// SD card detection pin instance.
static const Pin pinSdCardDetect = BOARD_SD_PIN_CD;

//------------------------------------------------------------------------------
/// Waits for a SD card to be connected.
//------------------------------------------------------------------------------
void WaitSdConn(void)
{
    PIO_Configure(&pinSdCardDetect, 1);
    printf("-I- Please connect a SD card ...\n\r");
    while (PIO_Get(&pinSdCardDetect) != 0);
    printf("-I- SD card connection detected\n\r");
}

#else

//------------------------------------------------------------------------------
/// Dummy function.
//------------------------------------------------------------------------------
void WaitSdConn(void)
{
    printf("-I- SD card detection not available, assuming card is present\n\r");
}

#endif

//------------------------------------------------------------------------------
//         Optional: Write protection status
//------------------------------------------------------------------------------

#ifdef BOARD_SD_PIN_WP

/// Write protection status pin instance.
static const Pin pinSdWriteProtect = BOARD_SD_PIN_WP;

//------------------------------------------------------------------------------
/// Checks if the device is write protected.
//------------------------------------------------------------------------------
void CheckProtection(void)
{
    PIO_Configure(&pinSdWriteProtect, 1);
    if (PIO_Get(&pinSdWriteProtect) != 0) {

        printf("-I- SD card is write-protected\n\r");
    }
    else {

        printf("-I- SD card is NOT write-protected.\n\r");
    }
}

#else

//------------------------------------------------------------------------------
/// Dummy implementation.
//------------------------------------------------------------------------------
void CheckProtection(void)
{
    printf("-I- Cannot check if SD card is write-protected\n\r");
}

#endif
//------------------------------------------------------------------------------
//         Global functions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Zpozdeni
// \param zp - pocet cyklu kvuli zpozdeni
//------------------------------------------------------------------------------

void tDelay (unsigned int zp){ 
int	x = 0;
while (x < zp) x++;
}

//------------------------------------------------------------------------------
//Pocitadlo sekund
// \param sec - sekundy
//------------------------------------------------------------------------------

static void timeCounter (unsigned int sec, unsigned int min, unsigned int hour, unsigned int block){
 	 	
	 printf ("%u : %u : %u *** %u\n\r", hour, min, sec, block);
	
}

//------------------------------------------------------------------------------
// Nastaveni casu mereni
// \param logCH - znak prijaty z PC
// \param logINT - prideleni casu podle prijateho znaku
//------------------------------------------------------------------------------
unsigned int logTime ( unsigned char logCH )
{
	unsigned int logINT = 0;

	if (logCH != '1' && logCH != '2' && logCH != '3' && logCH != '4' && logCH != '5') logCH = '5';
	switch(logCH) {
        case '1': logINT = 1; break;
        case '2': logINT = 5; break;
		case '3': logINT = 10; break;
		case '4': logINT = 15; break;
		case '5': logINT = 255; break;
    }
	return logINT;
}

//------------------------------------------------------------------------------
// Nastaveni casu periody
// \param perCH - znak prijaty z PC
// \param perINT - prideleni casu podle prijateho znaku
//------------------------------------------------------------------------------ 
unsigned int perTime ( unsigned char perCH )
{
	unsigned int perINT = 0;

	if (perCH != '1' && perCH != '2' && perCH != '3' && perCH != '4' && perCH != '5') perCH = '5';
	switch(perCH) {
        case '1': perINT = 5; break;
        case '2': perINT = 20; break;
		case '3': perINT = 30; break;
		case '4': perINT = 60; break;
		case '5': perINT = 255; break;
    }
	return 	perINT;
}

//------------------------------------------------------------------------------
// Nastaveni celkoveho casu
// \param totCH - znak prijaty z PC
// \param totINT - prideleni casu podle prijateho znaku
//------------------------------------------------------------------------------
unsigned int totTime ( unsigned char totCH )
{
	unsigned int totINT = 0;

	if (totCH != '1' && totCH != '2' && totCH != '3' && totCH != '4') totCH = '1';
	switch(totCH) {
        case '1': totINT = 6; break;
        case '2': totINT = 12; break;
		case '3': totINT = 24; break;
		case '4': totINT = 48; break;		
    }
	return totINT;
}

//------------------------------------------------------------------------------
/// Main function
//------------------------------------------------------------------------------
int main(void)
{
	//promenne pro nahrani casovych hodnot
	unsigned char times, logID, perID, totID, logHelp = 0, perHelp = 0, totHelp = 0;
	unsigned int  log = 0, period = 0, total = 0;
	unsigned int cont = 0;
	unsigned char blockZero[SD_BLOCK_SIZE];
	unsigned char blockHelp[SD_BLOCK_SIZE];
	unsigned int Zero = 0;
	memset(blockZero, 0, (SD_BLOCK_SIZE));
	memset(blockHelp, 0, (SD_BLOCK_SIZE));	
	//promenne pro zapis na SD kartu
    unsigned char pBuffer[SD_BLOCK_SIZE * NB_MULTI_BLOCKS];
    unsigned int block, start;
    unsigned int i, j;
	unsigned int sec = 0, min = 0, hour = 0;
	unsigned int count = 0, countLog = 0, countPer = 0, turnOff = 0;
    unsigned char error;

	memset(pBuffer, 0, (SD_BLOCK_SIZE * NB_MULTI_BLOCKS));
	start = Zero + 1;
	block = start;

    TRACE_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK);
    printf("-- Basic SD-Card SPI Mode Project %s --\n\r", SOFTPACK_VERSION);
    printf("-- %s\n\r", BOARD_NAME);
    printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__);

	// konfigurace pinu pro LED
	const Pin pinPA0 = PIN_LED_DS1;
	const Pin pinPA1 = PIN_LED_DS2;
	const Pin pinPA2 = PIN_LED_DS3;
	const Pin pinPB  = PIN_PUSHBUTTON_3;
	//PIN configuration
	PIO_Configure( &pinPA0, 1 );
	PIO_Configure( &pinPA1, 1 );
	PIO_Configure( &pinPA2, 1 );
	PIO_Configure( &pinPB, 1 ); //presunuto zakonfiguraci ADC pinu
	
	
	//konfigurace ADC
	#ifdef PINS_ADC
    	PIO_Configure(pinsADC, PIO_LISTSIZE(pinsADC));
	#endif	

    ADC_Initialize( AT91C_BASE_ADC,
                    AT91C_ID_ADC,
                    AT91C_ADC_TRGEN_DIS,
                    0,
                    AT91C_ADC_SLEEP_NORMAL_MODE,
                    AT91C_ADC_LOWRES_8_BIT,
                    BOARD_MCK,
                    BOARD_ADC_FREQ,
                    10,
                    1200);

	 while (1) 
	 {
	 	//cyklus dokud nebudeprijaty znak anebo zarizeni bude zapnuto
	 	while((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_RXRDY) == 0 && PIO_Get(&pinPB) == 1);
		//while(PIO_Get(&pinPB) == 1);
		times = AT91C_BASE_DBGU->DBGU_RHR;

		//------------------------------------------------------------------
		//Nahrani casovych dat do nulteho bloku SD karty
		//------------------------------------------------------------------
		if ( times == 'x' ){

			memset(blockHelp, 0, (SD_BLOCK_SIZE));	//vynulovani pomocneho bloku
			memset(blockZero, 0, (SD_BLOCK_SIZE)); 	//vynulovani bloku pro nulty blok
			block = start;

			logID = DBGU_GetChar();	 //prideli znak pro nastaveni delky jednoho zaznamu
			perID = DBGU_GetChar();	 //prideli znak pro nastaveni delky jedne periody
			totID = DBGU_GetChar();	 //prideli znak pro nastaveni delky celeho mereni

			log = logTime ( logID ); //funkce podle znaku nastavi cas (255 pro nepretrzite mereni)
			period = perTime ( perID ); //funkce podle znaku nastavi cas(255 pro nepretrzite mereni)
			total = totTime ( totID ); //funkce podle znaku nastavi cas

			//podminka prideli zaznamu i periode nepretrzite nahravani v pripade
			//ze zazanm je delsi jak perioda nebo jedno je nepretrzite a druhe ne
			if ( log >= period || log == 255 || period == 255){
				log = 255;
				period = 255;
			}
				//do bloku se nahrajou parametry
				memset(blockHelp, 0, (SD_BLOCK_SIZE));
				blockHelp[0] = log;
				blockHelp[1] = period;
				blockHelp[2] = total;
										
				// konfigurace pinu SD karty
    			PIO_Configure(pinsSd, PIO_LISTSIZE(pinsSd));      			  
    			// Inicializace SPI driver
    			AIC_ConfigureIT(BOARD_SD_SPI_ID,  AT91C_AIC_PRIOR_LOWEST, ISR_Spi0);
    			SDSPI_Configure(&sdSpiDrv, BOARD_SD_SPI_BASE, BOARD_SD_SPI_ID);
    			AIC_EnableIT(BOARD_SD_SPI_ID);
    			//SDSPI_ConfigureCS(&sdSpiDrv, BOARD_SD_NPCS, SDSPI_CSR(BOARD_MCK / SPCK));
    			SDSPI_ConfigureCS(&sdSpiDrv, BOARD_SD_NPCS, AT45_CSR(BOARD_MCK, SPCK));

				//Inicializace SD karty 				
				for ( i = 0; i < 5; i++){
			 		if (SD_Init(&sdDrv, (SdDriver *)&sdSpiDrv)){				 	
						cont = 0;
					}
					else {
						cont = 1;
						i = 10;					
					}
				}
				
				//v pripade, ze inicializace SD karty byla uspesna probehne zapsania vycteni 
				if (cont == 1){
					for (j = 0; j < 5; j++){	
						tDelay (1000000); //zpozdeni 										
						error = SD_WriteBlock(&sdDrv, Zero, 1, blockHelp); //zapsani
						tDelay (1000000);//zpozdeni
						memset(blockZero, 0, (SD_BLOCK_SIZE));
						error = SD_ReadBlock(&sdDrv, Zero, 1, blockZero); //vycteni
						//porovnani zapsanych a vyctenych dat
						if (blockZero[0] == log &&
							blockZero[1] == period &&
							blockZero[2] == total) {
							 j = 10;					
							 logHelp = blockZero[0];
							 perHelp = blockZero[1];
							 totHelp = blockZero[2];
							  printf ( "%u,%u,%u", 
						 		((unsigned int *)logHelp), 
						 		((unsigned int *)perHelp), 
						 		((unsigned int *)totHelp));
						}
					} 								  
				}
				
				SD_Stop(&sdDrv, (SdDriver *)&sdSpiDrv);
			
			if ( j == 5 ) printf("Chyba dat\n\r"); //vypsani chyby v pripade, ze se data neshoduji
					
			if (cont == 0){
			 	printf("Chyba SD karty\n\r"); //vypsani chyby v pripade, ze neprobehla inicializace SD karty
			}
			 
		}

		//------------------------------------------------------------------
		//Spusteni monitorovani dat a zapisovani na SD kartu
		//------------------------------------------------------------------
		if ( PIO_Get(&pinPB) == 0 ){

			PIO_Clear(&pinPA1);
			memset(blockZero, 0, (SD_BLOCK_SIZE));
			memset(blockHelp, 0, (SD_BLOCK_SIZE));

			// Konfigurace pinu SD karty
    		PIO_Configure(pinsSd, PIO_LISTSIZE(pinsSd));

    		// Wait for SD card connection (if supported)
    		//WaitSdConn();

    		// Check if card is write-protected (if supported)
    		//CheckProtection();

    		// Inicializace SPI driver
    		AIC_ConfigureIT(BOARD_SD_SPI_ID,  AT91C_AIC_PRIOR_LOWEST, ISR_Spi0);
    		SDSPI_Configure(&sdSpiDrv, BOARD_SD_SPI_BASE, BOARD_SD_SPI_ID);
    		AIC_EnableIT(BOARD_SD_SPI_ID);

    		//SDSPI_ConfigureCS(&sdSpiDrv, BOARD_SD_NPCS, SDSPI_CSR(BOARD_MCK / SPCK));
    		SDSPI_ConfigureCS(&sdSpiDrv, BOARD_SD_NPCS, AT45_CSR(BOARD_MCK, SPCK));

			//	Inicializace SD card driver
			for ( i = 0; i < 10; i++){
			 	if (SD_Init(&sdDrv, (SdDriver *)&sdSpiDrv)){				 	
					cont = 0;
				}
				else {
					cont = 1;
					i = 10;					
				}
			}
			//Jestli probehla inicializace SD karty spravne, vyctou se parametry z nulteho bloku
			//Jestli ne rozsviti se cervena LED az dokud se nevypne spinac
			if ( cont == 1 ){
				error = SD_ReadBlock(&sdDrv, Zero, 1, blockZero);
			}
			else{
			 	while ( PIO_Get(&pinPB) == 0 )PIO_Clear(&pinPA2); 
				PIO_Set(&pinPA2); 
			}

			//V pripade, ze bylo nahravani preruseno se podminka nespusti
			//Jinak nastavi a zkontroluje parametry
			if (block == start){
				log = blockZero[0];
				period = blockZero[1];
				total = blockZero[2];

				if ( log != 1 && log != 5 && log != 10 && log != 15 && log != 255 ) total = 0;
				if ( period != 5 && period != 20 && period != 30 && period != 60 && period != 255 ) total = 0;
				if ( total != 6 && total != 12 && total != 24 && total != 48 ) total = 0;
				if ( (log == 255 && period != 255) || (log != 255 && period == 255) ) total = 0;
				printf ("\n\r%u, %u, %u \n\r", log, period, total);			

			 	if ( log == 255 ) log = total * 60;
			 	if ( period == 255 ) period = total * 60;

			 
			 	hour = 0;
			 	sec = 0;
			 	min = 0; 
			 }
			 //Konfigurace RTT
			 ConfigureRtt();
			 //Cyklus celkoveho mereni
			 //Podminky cyklu - dokud neprobehne cele mereni nebo se nevypne spinac nebo nebude konec SD-HC karty
			 while ( hour < total && PIO_Get(&pinPB) == 0 && (block + NB_MULTI_BLOCKS) < (SD_TOTAL_BLOCK(&sdDrv))){		 	

				countLog = 0;
				if (turnOff == 0){
					count = 0;
				}
				//Cyklus mereni
				//Podminky cyklu - neprobehne cas nastaveny promereni nebo dokud neprobehne cele mereni 
				//nebo se nevypne spinac nebo nebude konec SD-HC karty
			 	while ( count < log && hour < total && PIO_Get(&pinPB) == 0 && (block + NB_MULTI_BLOCKS) < (SD_TOTAL_BLOCK(&sdDrv))){
					//Vynulovani bufferu pro ukladani dat
					memset(pBuffer, 0, SD_BLOCK_SIZE * NB_MULTI_BLOCKS);
					//Prvni 4 byty pro zapsani casove stopy
					// 0 - identifikator, 1 - hodina, 2 - minuta, 3 - sekunda
					pBuffer[0] = 255;
					pBuffer[1] = hour;
					pBuffer[2] = min;
					pBuffer[3] = sec;					
					//Cyklus pro vycteni hodnot z ADC a zapsani do bufferu
					for (i=4; i < SD_BLOCK_SIZE * NB_MULTI_BLOCKS;){											
						pBuffer[i] = ADCConv(ADC_NUM_1, EOC1);
						i++;
						pBuffer[i] = ADCConv(ADC_NUM_2, EOC2);
						i++;						
						pBuffer[i] = ADCConv(ADC_NUM_3, EOC3);
						i++;
						pBuffer[i] = ADCConv(ADC_NUM_4, EOC4); 
						i++;						
				 		while (RTT_GetTime(AT91C_BASE_RTTC)< (1 + countLog)); // cekani dokud se neinkrementuje RTT
						countLog++;
						//pocitani casu a blikani diody
				 		if (countLog == 4936){
							sec++;
							if (sec == 60){
								min++;
								count++;
								sec = 0; 									
							}
							if (min == 60){
								hour++;
								min = 0; 								
							}
							if (PIO_Get(&pinPA1) == 1){
		 						PIO_Clear(&pinPA1); 
							} else PIO_Set(&pinPA1);
							timeCounter (sec, min, hour, block);
							ConfigureRtt();
							countLog = 0;
				 		}
						
					}
					//----------------------------------------------------------------------------
					//kod pro ukladani na kartu
					//----------------------------------------------------------------------------
					error = SD_WriteBlock(&sdDrv, block, NB_MULTI_BLOCKS, pBuffer);
					block+= NB_MULTI_BLOCKS;
					turnOff = 0;
					if (PIO_Get(&pinPB) == 1){
					 	turnOff = 1;
					}
				}

				if (turnOff == 0){
					countPer = 0;
				}				
				//Cyklus cekani do konce periody
				//Podminky cyklu - dokud nenastane rozdil periody a zaznamu nebo dokud neprobehne cele mereni
				//nebo se nevypne  spinac	
				while ( countPer < (period - log) && hour < total && PIO_Get(&pinPB) == 0){
					while (RTT_GetTime(AT91C_BASE_RTTC)< (4936));
					sec++;
					if (sec == 60){
							min++;
							countPer++;
							sec = 0; 								
						}
						if (min == 60){
							hour++;
							min = 0; 								
						}
						if (PIO_Get(&pinPA1) == 1){
		 						PIO_Clear(&pinPA1); 
						} else PIO_Set(&pinPA1);
						timeCounter (sec, min, hour, block);
						ConfigureRtt();	
						turnOff = 0;
					    if (PIO_Get(&pinPB) == 1){
					 		turnOff = 1;
						}					
				}
			 }
			PIO_Clear(&pinPA1);		
			tDelay (100000);
			SD_Stop(&sdDrv, (SdDriver *)&sdSpiDrv);
			// V pripade chyby parametru se nastavi celkove mereni = 0 a rozsviti se cervena LED
			if (total == 0){
				PIO_Set(&pinPA1); 
				while ( PIO_Get(&pinPB) == 0 ) PIO_Clear(&pinPA2); 				
			}
			PIO_Set(&pinPA2);			 
			
			PIO_Set(&pinPA1);			
			tDelay (100000);
			if ( PIO_Get(&pinPB) == 0 ) PIO_Clear(&pinPA0);	 // Na uspesnem konci se rozsviti zelena LED
			while ( PIO_Get(&pinPB) == 0 );			
			tDelay (100000); //Cekani kvuli zakmitunapeti na spinaci
			PIO_Set(&pinPA0);

		}	
	}
}

