/*
 * RS485_lib.c
 *
 * Created: 1.3.2012 17:21:43
 *  Author: JIRKO
 */ 

#include <avr/io.h>
#include <util/delay.h>			// knižnica pre oneskorenia
#include <avr/interrupt.h>		// Prerusenia
#include <string.h>				// praca so stringom
#include "deff_packet_lib.h"
#include "RS485_lib.h"			// praca so RS485


	volatile	uint8_t		flags_1;									// flagi pre komunikaciu RS485
// pre RX
	volatile	uint8_t		RX_buf_incomingData[MAX_PACKET_LENGHT];		// premenna kde sa ukladaju priate data USARTom
	volatile	uint8_t		counter_accepted_byte;						// pocita priate bity RS485_lib.h
	volatile	uint8_t		RX_calc_crc;								// pocita crc z priatych bytov RS485_lib.h
	volatile	uint8_t		t_last_accepted_B;							// cas od posledneho priateho/vyslaneho bite
	
// pre TX
	volatile	uint8_t		TX_calc_crc;								// premena pre vypocet CRC
	volatile	uint8_t		len_TX_packet;								// dlzka
	volatile	uint8_t		index_TX_packet;							// index na odosielany byte s packetu 
	volatile	uint8_t		TX_sennding_buff_cell;						// prave odosielana bunka	
// tx buffer
	volatile	t_TX_buff	TX_buff[SIZE_TX_BUFF];						// TX buffer 									

// inicializacia pre tuto kniznicu		
	void	RS485_init(void)
	{
		RS485_IN;												// nastavi HW na priem 
		RS485_LISTEN;											// signalizacia pre casovac 
		REDY_TO_ACCEPT_PACKET;									// nespracuva sa ziaden packet moze sa primat
	
		//****************   Nastavenia USARTU   *****************************************************************
		UBRRL=  3;                                    // prenosova rychlost 115k2 -pre kristal 7.3728MHz
		UCSRC= (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);     // Set frame format: 8 data 2stop
		DIS_TX_INT;
		
		// TX
		uint8_t i;
		for(i=0;i<SIZE_TX_BUFF;i++)
		{
			TX_buff[i].cell_send_reddy= 0;		// vsetky bunky su volne
		}
	
		// RX
		counter_accepted_byte= 0;
		RX_calc_crc= 0;
		t_last_accepted_B=0;							// stav linky free bude za dohodnuty cas
		
		//****************   Nastavenia Casovaca0 na 200us  ***********************************************
	
		//nastavenie C0			pre kristal 7.3728MHz
		TCCR0= 0b00000010;			//rezim normalny s preddelickou /8
		TIMSK= (1<<TOIE0);			//povolene prerusenie pri preteceni		
		SET_TCNT0;					//prerusenie za 200us

		//--------------------------------------------------------------------------------------------------
	}	

// kontroluje ci prisiel niaky packet a kopiruje ho do pola na ktore dostava smernik
	uint8_t isPacketAccepted(uint8_t *packet)
	{
		if IS_PACKET_ACCEPTED		// packet bol priaty
		{
			memcpy( packet, (uint8_t *)RX_buf_incomingData, MAX_PACKET_LENGHT );
			REDY_TO_ACCEPT_PACKET;
			return 0;
		}
		else return 1;				// nebol priaty ziaden packet
	}

// casovanie 200us pre RX
	void I_TIMER0_RS485(void)						// funkcia sa vola pri vyprsani casovaca TIMER 0 zabespeceuje detkciu konca packetu a prori kolizni cas 	
	{
		if( t_last_accepted_B <= (TIMEOUT_END_OF_PACKET + (TIMEOUT_RS485_FREE * my_ID()) ) )	// ak je to viac tak uz nema zmysle inkrementovat lebo vsetko prebehlo
		{
			t_last_accepted_B++;

			if IS_RS485_MODE_ACCEPTING_DATA								// tu sa caka ci bol ukonceny packet (neprisiel posliednich 200us ziaden byte) 
			{
				if ( t_last_accepted_B >= TIMEOUT_END_OF_PACKET )		// bol priaty posledny packet vyprsalo 200us
				{
					if ( ( RX_calc_crc == 0 ) && ( counter_accepted_byte <= MAX_PACKET_LENGHT ) && ( counter_accepted_byte >=PACKET_LENGHT_WITHAUT_DATA) )	// CRC, max a min dlzka sedia
					{
						PACKET_ACCEPTED;					
					}
					else									// CRC alebo blzka nesedi packet sa zahodi
					{
						REDY_TO_ACCEPT_PACKET;
					}			
					RS485_LISTEN;							// zapnem mod cakania na dalsi packet pre mna 
					// resteujem premenne
					RX_calc_crc= 0;		
					counter_accepted_byte= 0;
				}	
			}
			else
			{
				if ( ( TIMEOUT_END_OF_PACKET + (TIMEOUT_RS485_FREE * my_ID()) ) <= t_last_accepted_B )		// zbernica je volna mozem vysialat
				{
					RS485_FREE;		
				}
			}	
		}		
	}

// funkcia odosiela packet dofronty na odoslanie
	uint8_t send_packet(const t_packet * packet)
	{
		uint8_t i, temp;
		temp= 255;
		for (i=0;i<SIZE_TX_BUFF;i++)
		{
			if( TX_buff[i].cell_send_reddy == 0 )	// hlada volnu bunku
			{
				temp= i;							// bola najdena volna bunka vyskoc s for
				break;
			}
		}
		if (temp > 10)	return 1;					// nenasla sa ziadna volna bunka vrat chybu 1

		TX_buff[temp].cell_send_reddy= 1;									// oznacujem bunku ako obsadenu	a pripravenu na odoslatie
	
		memcpy((uint8_t*)TX_buff[temp].data , packet, packet->lenght);		// kopirujem do buffera
	
		return 0;															// packet bol uspesne pridany do fronty
	}
	
// kontroluje ci neni niaky packet na odoslatie ako je tak sa ho pokusi odolat
	uint8_t I_check_TX_RS485(void)
	{
		t_packet *p_packet;
		uint8_t i, temp;
		
	// ********************* hladanie packetu na odoslanie*********************
		temp= 255;											// ak je temp po vykonani foru 255 neni ziaden packet pripraveny na odoslatie
		for (i = 0; i < SIZE_TX_BUFF; i++)
		{
			 if ( TX_buff[i].cell_send_reddy == 1 )			// bunka je pripravena na odoslatie odoslem ju
			 {	
				 temp=i;
				 break;
			 }
		}	
	
		if ( temp > SIZE_TX_BUFF )							// nieje nic na odoslanie vrat 
		{
			return 1;
		}

	// ************** kontrola ci je linka volna pre odosielanie **************
		if IS_RS485_BUSSY			
		{
			return 1;				//ak je bussy nic neodosles 
		}
		RS485_BUSSY;				// nastavim ze nema k RS485 nic pristupovat
		RS485_OUT;					// vysielaci rezim RS485

	// *************** odosielanie packetu  ***********************************	

		TX_sennding_buff_cell= temp;				// ulozim ukazovatel na odosielanu bunku buffera
		p_packet= (t_packet *)TX_buff[temp].data;	// ulozim pointer na odosielany packet

		index_TX_packet= 0;							// index na prave odosielany bite
		TX_calc_crc= 0;								// nulujem premennu pre vypocet crc	
		len_TX_packet= p_packet->lenght;			// ukladam dlzku

		TX_calc_crc= TX_buff[TX_sennding_buff_cell].data[index_TX_packet];		// vypocet crc
		UARTSEND(TX_buff[TX_sennding_buff_cell].data[index_TX_packet]);			// odoslanie 1. bite
		
		index_TX_packet++;							// dalsia pozicia na odoslanie
		EN_TX_UDRIE_INT;							// povolujem prerusenie 
		return 0;
	}

// prerusenie pri vyprazneni registru UDR
	void I_USART_UDRE_vect(void)				// funkcia sa vola pri vyprazdneni UDR a zabespecuje odosielanie dat
	{
		RS485_BUSSY;
		SET_TCNT0;								// prerusenie T0 za 200us
		t_last_accepted_B= 0;
		if ( index_TX_packet + 1  > len_TX_packet )		// odoslal som posledny byte
		{	
			RS485_LISTEN;
			RS485_BUSSY;						// nastavim ze nema k RS485 nic pristupovat
			EN_TX_TXCIE_INT;					// vypinam prerusenie zapinam tx aj rx

			TX_buff[TX_sennding_buff_cell].cell_send_reddy= 0;	// bunka je zas volna
			// RX485_IN;	toto sa robi v prerusenie TX		// koli prepnutiu na vstup
		}
		else
		{
			if (index_TX_packet + 1  == len_TX_packet)			// caka na posledny prvok to je crc
			{
				UARTSEND(TX_calc_crc);							// odosiela crc
				index_TX_packet++;	
			} 
			else
			{
				UARTSEND(TX_buff[TX_sennding_buff_cell].data[index_TX_packet]);
				TX_calc_crc= TX_calc_crc^TX_buff[TX_sennding_buff_cell].data[index_TX_packet];
				index_TX_packet++;
			}			
		}	
	}	

// prerusenie ked USART prime byte
	void	I_USART_RXC_vect(void)			// funkcia sa vola pri priati byte a zabespecuje priem dat
	{
		uint8_t temp;
		temp=UDR;							// ulozim priaty byte
		SET_TCNT0;							// prerusenie T0 za 200us
		
		// prima data
		if IS_RS485_IN		
		{
			RS485_BUSSY;
			t_last_accepted_B=0;			// nulujem cas od posledneho priateho bajtu rutina time out nastava po uplinuti TIMEOUT_END_OF_PACKET
		
			if IS_REDY_TO_ACCEPT_PACKET		///! ak sa nespracuva packet mozes primat
			{
				RS485_ACCEPTING_DATA;	
				if( PACKET_DATA_MAX_LENGHT >= counter_accepted_byte )	// ak je packet dlksi ako maximalna dlzka packetu tak uz neprimaj potom sa zahodi 
				{
					RX_buf_incomingData[counter_accepted_byte]= temp;
					RX_calc_crc= RX_calc_crc ^ temp;					// vypocer crc CRC sa xorne s originalnym crc vo visledku by RX_calc_crc malo vyt nula
					counter_accepted_byte++;	
				}					
			}
		}
	}

// prerusenie ked USART odosle posedny znak
	void	I_USART_TXC_vect(void)
	{
		DIS_TX_INT;		// povolim prerusnie iba RX
		RS485_IN;		// koli prepnutiu na vstup
	}