#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <stdio.h>
#include <string.h>
#include <util/crc16.h>

// ************************
// * Makra 
// ************************

// systemove
#define FOSC 	16000000		// rychlost CPU
#define BAUD 	38400			// rychlost prenosu
#define MYUBRR 	FOSC/16/BAUD-1
	
// LEDky
#define LED1_ON		PORTB &= ~0x40	//zelena	
#define LED2_ON		PORTB &= ~0x80	//lutá
#define LED3_ON		PORTB &= ~0x20	//červena
#define LED1_OFF    PORTB |= 0x40
#define LED2_OFF	PORTB |= 0x80
#define LED3_OFF	PORTB |= 0x20

// externi SRAM
#define LATCH_ON	PORTB |= 0x10
#define LATCH_OFF	PORTB &= ~0x10
#define MASKA		0x01
#define CE_ON		PORTA |= _BV(PA7)
#define CE_OFF		PORTA &= ~_BV(PA7)
#define OE_ON		PORTC |= _BV(PC7)
#define OE_OFF		PORTC &= ~_BV(PC7)
#define WE_ON		PORTC |= _BV(PC6)
#define WE_OFF		PORTC &= ~_BV(PC6)

// ostatni
#define PTT_ON		PORTB |= 0x01
#define PTT_OFF     PORTB &= ~0x01

// FX614
#define	TRANSMIT_ENABLE		PORTA |= _BV(PA1)	
#define	TRANSMIT_DISABLE	PORTA &= ~_BV(PA1)
#define CLOCK				_BV(PA5)
#define TXD_LOG1			PORTA |= _BV(PA6)
#define TXD_LOG0			PORTA &= ~_BV(PA6)
#define TXD_IN				PORTA & _BV(PA6)

// CRC
#define CRC_START		0xFFFF
#define CRC_REMAINDER	0xF0B8

// sekce externi RAM
#define RX_BASE			0x0000
#define RX_BUF_SIZE		1024
#define RX_BUF_CNT		2

// vysilaci stavy
#define TX_KISS_INACTIVE	1
#define TX_KISS_CTRL		2
#define TX_KISS_DATA		3
#define TX_KISS_PARAM		4

#define TX_TRA_INACTIVE		1
#define TX_TRA_START_FLAG	2
#define TX_TRA_DATA			3
#define TX_TRA_STOP_FLAG	4


// ************************
// * Promenne
// ************************

//promenne pro prijem
unsigned short RxCrc = CRC_START;
unsigned char RxRcvByte = 0;
unsigned char RxRcvBitPos = 1;
int RxRcvInt = 0;
int RxRcvT1Int = 0;
int RxRcvDcd = 0;
int RxRcvDataStart = 0;
int RxRcvOnesCntr = 0;
int RxRcvIdx = 0;
int RxRcvPos = 0;
int RxKissIdx = 1;
int RxKissPos = 0;
int RxKissCnt = 0;
int RxKissInt = 0;

//promenne pro vysilani
unsigned char TxBuffer[1024];
unsigned short TxCrc = CRC_START;
int TxKissState = TX_KISS_INACTIVE;
int TxKissEscaped = 0;
int TxKissPos = 0;
int TxKissInt = 0;
volatile unsigned char TxKissByte;
unsigned char TxKissParam;
int TxTraState = TX_TRA_INACTIVE;
int TxTraPos = 0;
int TxTraCnt = 0;
int TxTraInt = 0;
int TxTraOnesCntr = 0;
int TxTraInsertZero = 0;
int TxTraFlagCntr = 0;
int TxTraBit = 1;
unsigned char TxTraBitPos = 1;


// ************************
// * Inicializace USART
// ************************
void USART_Init(unsigned int ubrr)
{
	// Set baud rate 
	UBRRH = (unsigned char)(ubrr>>8);
	UBRRL = (unsigned char)ubrr;
	
	// Enable receiver and transmitter **
	UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE) | (1<<TXCIE);
	
	// Set frame format: 8data, 1stop bit **
	UCSRC = (1<<URSEL) | (1<<UCSZ0) | (1<<UCSZ1) ;
}

// *********************************
// * Inicializace
// *********************************
void ioinit (void)
{
	// adresy ex. pameti
	DDRA |= _BV(PA4);
	DDRC |= _BV(PC5) | _BV(PC4);
	DDRD |= _BV(PD3) | _BV(PD4) | _BV(PD5) | _BV(PD6) | _BV(PD7);	
	
	// ovládání externí pameti
	DDRC |= _BV(PC6) | _BV(PC7);	// OE, WE
	DDRB |= _BV(PB4);				// latch
	DDRA |= _BV(PA7);				// OE

	// inicializace externi pamet
	CE_ON;
	OE_ON;
	WE_ON;

	//ininicializace USARTu
	USART_Init(MYUBRR);

	// inicializace FX614
	DDRA |= _BV(PA1) | _BV(PA5) | _BV(PA6); // M0, CLK, Transmit
	PORTA |= CLOCK;

	// inicializace LED
	DDRB |=0xE0;
	LED1_OFF;
	LED2_OFF;
	LED3_OFF;

	//povoleni externiho preruseni - jakakoliv zmena na INT0
	MCUCR |= 0x01;
	GICR |= 0x40;

	//T0 pro vysilani
	TCCR0 = 0x0B;	//CTC mod, clock/64
	OCR0 = 208;		//1200 Hz
	TIMSK |= 0x02;	//aktivace OCIE0

	//zakázaní JTAG
	MCUCSR = 0x80;
	MCUCSR = 0x80;

	//globalni povoleni preruseni
    sei();
}

// *********************************
// * Funkce nastaveni adresy
// *********************************
void Set_Address(unsigned int adr){
	
	// porty reset
	PORTC &= ~0x30;
	PORTD &= ~0xF8;
	PORTA &= ~0x10;	

	LATCH_ON;	
	
	// porty nastaveni dolni adresy
	PORTC |= ((adr>>0)&MASKA)<<5;	//A0
	PORTD |= ((adr>>1)&MASKA)<<7;	//A1
	PORTD |= ((adr>>2)&MASKA)<<6;	//A2
	PORTD |= ((adr>>3)&MASKA)<<5;	//A3
	PORTC |= ((adr>>4)&MASKA)<<4;	//A4
	PORTD |= ((adr>>5)&MASKA)<<4;	//A5
	PORTD |= ((adr>>6)&MASKA)<<3;	//A6
	PORTA |= ((adr>>7)&MASKA)<<4;	//A7

	LATCH_OFF;		
	
	PORTC &= ~0x30;
	PORTD &= ~0xF8;
	PORTA &= ~0x10;	

	// porty nastaveni horni adresy
	PORTC |= ((adr>>8)&MASKA)<<5;	//A8
	PORTD |= ((adr>>9)&MASKA)<<7;	//A9
	PORTD |= ((adr>>10)&MASKA)<<6;	//A10
	PORTD |= ((adr>>11)&MASKA)<<5;	//A11
	PORTC |= ((adr>>12)&MASKA)<<4;	//A12
	PORTD |= ((adr>>13)&MASKA)<<4;	//A13
	PORTD |= ((adr>>14)&MASKA)<<3;	//A14
	
}

// ***************************************************************
// * zapis DAT do SRAM
// ***************************************************************
void Store_Data(unsigned int addr, unsigned char data)
{
	cli();

	// nastaveni vystupu
	DDRC |= 0x0F;
	DDRB |= 0x0E;
	DDRA |= 0x01;	

	// reset
	PORTC &= ~0x0F;
	PORTB &= ~0x0E;
	PORTA &= ~0x01;	

	// nastaveni adresy
	Set_Address(addr);

	CE_OFF;	
	WE_OFF;

	//	zapis dat
	PORTC |= (data&0x0F);	
	PORTB |= ((data>>4)&MASKA)<<3;	
	PORTB |= ((data>>5)&MASKA)<<2;
	PORTB |= ((data>>6)&MASKA)<<1;
	PORTA |= ((data>>7)&MASKA)<<0;

	WE_ON;
	CE_ON;

	sei();
}

// *********************************
// * Cteni DAT z SRAM
// *********************************
unsigned char Load_Data(unsigned int addr)
{
	unsigned char data;

	cli();

	// nastaveni vstup;
	DDRC &= ~0x0F;
	DDRB &= ~0x0E;
	DDRA &= ~0x01;

	WE_ON;

	// odpojeni pull-up
	PORTC &= ~0x0F;
	PORTB &= ~0x0E;
	PORTA &= ~0x01;

	// nastaveni adresy
	Set_Address(addr);

	CE_OFF;
	OE_OFF;

	// zpozdeni

	// cteni z pameti	
	data = (PINA&MASKA)<<7 ;
	data += ((PINB>>1)&MASKA)<<6;
	data += ((PINB>>2)&MASKA)<<5;
	data += ((PINB>>3)&MASKA)<<4;
	data += PINC&0x0F;

	CE_ON;
	OE_ON;

	sei();

	// vrat data
	return data;
}

// *********************************
// * Ulozi prijaty bajt do bufferu
// *********************************
void Rx_Store_Byte(unsigned char data)
{
	Store_Data(RX_BASE + RX_BUF_SIZE*RxRcvIdx + RxRcvPos,data);
	RxRcvPos++;
}

void Rx_Store_Data( unsigned char data )
{
	if (RxRcvDataStart)
	{
		Rx_Store_Byte(0x00);
		RxRcvDataStart = 0;
	}
	switch (data)
	{
		case 0xC0:
			Rx_Store_Byte(0xDB);
			Rx_Store_Byte(0xDC);
			break;

		case 0xDB:
			Rx_Store_Byte(0xDB);
			Rx_Store_Byte(0xDD);
			break;
		
		default:
			Rx_Store_Byte(data);
			break;
	}
	RxCrc = _crc_ccitt_update(RxCrc,data);
}


// *********************************
// * Vysle znak v KISS modu
// *********************************
unsigned char Rx_Load_Byte()
{
	unsigned char data = Load_Data(RX_BASE + RX_BUF_SIZE*RxKissIdx + RxKissPos);
	RxKissPos++;
	return data;
}

void Rx_Kiss_Transmit()
{
	if (RxKissPos < RxKissCnt)
	{
		//odeslani bajtu dat
		UDR = Rx_Load_Byte();

		//preskoceni CRC
		if (RxKissPos + 3 == RxKissCnt)
		{
			RxKissPos += 2;
		}
	}
}

// *********************************
// * Odstrani KISS escape znaky
// *********************************
int TxKissEscape()
{
	if (TxKissEscaped)
	{
		TxKissEscaped = 0;
		if (TxKissByte == 0xDC)
		{
			TxKissByte = 0xC0;
		}
		else if (TxKissByte == 0xDD)
		{
			TxKissByte = 0xDB;
		}
		else
		{
			TxKissState = TX_KISS_INACTIVE;
			return 0;
		}
	}
	if (TxKissByte == 0xDB)
	{
		TxKissEscaped = 1;
		return 0;
	}

	return 1;
}

// *********************************
// * hlavni funkce 
// ********************************* 
int main( void )
{
	//inicializace
	ioinit ();

	//indikace uspesne inicializace
	LED1_ON;

	//hlavni smycka
	while(1)
	{
		//*********************
		// detekce DCD
		//*********************
		if(bit_is_set(PINA,3))
		{
			LED2_ON;
			RxRcvDcd = 1;
		}
		else
		{
			LED2_OFF;
			RxRcvDcd = 0;
		}
		
		//*********************
		// prijem dat
		//*********************
		if (RxRcvDcd)
		{
			//zmena na vstupnim signalu
			if (RxRcvInt)
			{
				//vymazani priznaku zmeny
				RxRcvInt = 0;

				//start T1 na 1.5 x perioda
				TCCR1B = 0;	//zastavit
				TIMSK |= 0x04;	//povoleni int
				TCNT1 = 45536;	//1.5x perioda
				TCCR1A = 0;	//normalni mod
				TCCR1B = 1;	//spustit clkI/O 1x

				//ulozeni log. 0 do Rx registru
				RxRcvByte &= ~RxRcvBitPos;
				RxRcvBitPos <<= 1;
			
				//odeslani prijateho bajtu, bit stuffing, synchronizace na 7E
				if (RxRcvOnesCntr <= 4)
				{
					if (RxRcvBitPos == 0)
					{
						Rx_Store_Data(RxRcvByte);
						RxRcvBitPos = 1;
					}
				}
				else if (RxRcvOnesCntr == 5)
				{
					RxRcvBitPos >>= 1;
					if (RxRcvBitPos == 0)
					{
						RxRcvBitPos = 1;
					}
				}
				else if (RxRcvOnesCntr == 6)
				{
					if (RxRcvDataStart == 0)
					{
						Rx_Store_Byte(0xC0);
						RxRcvDataStart = 1;

						//kontrola CRC
						if (RxCrc == CRC_REMAINDER)
						{
							//odvysilani paketu do PC
							RxKissIdx = RxRcvIdx;
							RxKissCnt = RxRcvPos;
							RxKissPos = 0;
							Rx_Kiss_Transmit();

							//prepnuti na druhy buffer
							RxRcvIdx = RxRcvIdx ? 0 : 1;
							RxRcvPos = 0;
						}
						else
						{
							//znovu prijem do tehoz bufferu
							RxRcvPos = 0;
						}
						
						//reset pocitadla CRC
						RxCrc = CRC_START;

						//vlozeni pocatecniho KISS flagu
						Rx_Store_Byte(0xC0);
					}
		  			RxRcvBitPos = 1;
				}
				else
				{
					RxRcvBitPos = 1;
				}

				//zacatek hledani nove sekvence jednicek
				RxRcvOnesCntr = 0;
			}

			//vyprseni casovace detektoru
			if (RxRcvT1Int)
			{
				//vymazani priznaku vyprseni casovace
				RxRcvT1Int = 0;

				//start T1 na 1.0 x perioda
				TCCR1B = 0;	//zastavit
				TIMSK |= 0x04;	//povoleni int
				TCNT1 = 52203;	//1.0x perioda
				TCCR1A = 0;	//normalni mod
				TCCR1B = 1;	//spustit clkI/O 1x

				//ulozeni log. 1 do Rx registru
				RxRcvByte |= RxRcvBitPos;
				RxRcvBitPos <<= 1; 
				RxRcvOnesCntr++;		

				//odeslani prijateho bajtu v Rx registru
				if (RxRcvBitPos == 0)
				{
					Rx_Store_Data(RxRcvByte);
					RxRcvBitPos = 1;
				} 
			}
		}

		//*********************
		// vysilani dat
		//*********************
		if (TxTraInt)
		{
			//vymazani priznaku pripravy bitu na odvysilani
			TxTraInt = 0;

			//vyslani bud vlozene nuly nebo bitu dat
			if (TxTraInsertZero)
			{
				//vymazani priznaku vlozeni nuly
				TxTraInsertZero = 0;

				//vysle bit 0
				TxTraBit = 0;
			}
			else
			{
				switch (TxTraState)
				{
					case TX_TRA_INACTIVE :
						break;

					case TX_TRA_START_FLAG :
						//posle bit flagu
						TxTraBit = TxTraBitPos & 0x7E;
				
						//posun a kontrola konce flagu
						TxTraBitPos <<= 1;
						if (TxTraBitPos == 0)
						{
							TxTraBitPos = 1;
							if (++TxTraFlagCntr == 10)
							{
								TxTraFlagCntr = 0;
								TxTraState = TX_TRA_DATA;
							}
						}
						break;

					case TX_TRA_DATA :
						//posle bit dat
						TxTraBit = TxTraBitPos & TxBuffer[TxTraPos];

						//kontrola poctu jednicek
						if (TxTraBit)
						{
							if (++TxTraOnesCntr == 5)
							{
								TxTraOnesCntr = 0;
								TxTraInsertZero = 1;
							}
						}
						else
						{
							TxTraOnesCntr = 0;
						}

						//posun a kontrola konce bajtu
						TxTraBitPos <<= 1;
						if (TxTraBitPos == 0)
						{
							TxTraBitPos = 1;
							if (++TxTraPos == TxTraCnt)
							{
								TxTraState = TX_TRA_STOP_FLAG;
								TxTraBitPos = 1;
							}
						}
								
						break;

					case TX_TRA_STOP_FLAG :
						//posle bit flagu
						TxTraBit = TxTraBitPos & 0x7E;
				
						//posun a kontrola konce flagu
						TxTraBitPos <<= 1;
						if (TxTraBitPos == 0)
						{
							TxTraBitPos = 1;
							if (++TxTraFlagCntr == 10)
							{
								TxTraFlagCntr = 0;
								TxTraState = TX_TRA_INACTIVE;
								TRANSMIT_DISABLE;
								PTT_OFF;
								LED3_OFF;
							}
						}
						break;
				}
			}
		}

		//*********************
		// vysilani pres kiss
		//*********************
		if (RxKissInt)
		{
			//vymaz priznak odeslaneho bajtu pres kiss
			RxKissInt = 0;

			//posli dalsi bajt KISS ramce
			Rx_Kiss_Transmit();
		}

		//*********************
		// prijem z kissu
		//*********************
		if (TxKissInt)
		{
			//vymaz priznak prijateho bajtu na kissu
			TxKissInt = 0;
			
			//zpracovani prijateho bajtu
			switch (TxKissState)
			{
				case TX_KISS_INACTIVE:
					if (TxKissByte == 0xC0)
					{
						TxKissState = TX_KISS_CTRL;
						TxKissEscaped = 0;
					}
					break;

				case TX_KISS_CTRL:
					if (TxKissByte == 0xC0)
					{
						TxKissState = TX_KISS_INACTIVE;
					}
					else
					{
						if (TxKissEscape())
						{
							if (TxKissByte == 0x00)
							{
								TxKissState = TX_KISS_DATA;
								TxKissPos = 0;
								TxCrc = CRC_START;
							}
							else
							{
								TxKissState = TX_KISS_PARAM;
							}
						}
					}
					break;

				case TX_KISS_DATA:
					if (TxKissByte == 0xC0)
					{
						//ulozeni CRC
						TxBuffer[TxKissPos++] = (unsigned char)(~TxCrc);
						TxBuffer[TxKissPos++] = (unsigned char)((~TxCrc)>>8);

						//odvysilani datoveho paketu
						TxTraState = TX_TRA_START_FLAG;
						TxTraPos = 0;
						TxTraCnt = TxKissPos;
						TRANSMIT_ENABLE;
						PTT_ON;
						LED3_ON;

						//prijem dalsiho paketu
						TxKissState = TX_KISS_INACTIVE;
					}
					else
					{
						//ulozeni bajtu
						if (TxKissEscape())
						{
							TxBuffer[TxKissPos++] = TxKissByte;
							TxCrc = _crc_ccitt_update(TxCrc,TxKissByte);
						}
					}
					break;

				case TX_KISS_PARAM:
					if (TxKissByte == 0xC0)
					{
						//zpracovani ridiciho paketu

						//prijem dalsiho paketu
						TxKissState = TX_KISS_INACTIVE;
						
					}
					else
					{
						//ulozeni parametru
						if (TxKissEscape())
						{
							TxKissParam = TxKissByte;
						}
					}
					break;
			}
		}
	}

	return 0;

}

// *********************************
// * Preruseni pri odvysilani KISS
// *********************************
ISR(USART_TXC_vect)
{
	//pockej na ready
	while (!(UCSRA & (1<<UDRE)));
	RxKissInt = 1;
}

// *********************************
// * Presruseni pro prijem KISS
// *********************************
ISR(USART_RXC_vect)
{
	//precti byte pokud prijat
	if (UCSRA & (1<<RXC))
	{
		TxKissByte = UDR;
		TxKissInt = 1;
	}
}

// *********************************
// * Preruseni pri zmene Rx signalu
// *********************************
ISR (INT0_vect)
{
	RxRcvInt = 1;
}


// *********************************
// * Preruseni casovace Rx detektoru
// *********************************
ISR (TIMER1_OVF_vect)
{
	RxRcvT1Int = 1;
}

// *********************************
// * Preruseni casovace vysilace
// *********************************
ISR (TIMER0_COMP_vect)
{
	//NRZI enkodovani a vyslani
	if (TxTraBit == 0)
	{
		if (TXD_IN)
		{
			TXD_LOG0;
		}
		else
		{
			TXD_LOG1;
		}
	}

	TxTraInt = 1;
}
