/*
 * UART_LinkLayer_V10.c
 *
 * Created: 30.8.2015 19:06:54
 *  Author: Chenda
 */ 


#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <stdbool.h>
#include "conf_uart.h"
#include "conf_brd.h"
#include "../../Common/PeipherialFunctions.h"
#include "../../lib_mcu/uart/uart_drv.h"
#include "UART_LinkLayer_V10.h"


/************************************************************************/
/* Global variables                                                     */
/************************************************************************/
static volatile uint8_t uiTmpInterrupt;
static struct stLL_UARTx stLL_UART1;
static struct stLL_UARTx *spLL_UART1 = &stLL_UART1;

/************************************************************************/
/* Private functions prototypes                                         */
/************************************************************************/
void UART1_decode(void);
void UART1_encode(void);
uint16_t UART_Chksum_Calc (uint8_t *pDataBuffer,uint8_t Len);
void PeripheryLauncher(void);

/************************************************************************/
/* Public function                                                      */
/************************************************************************/
struct stLL_UARTx* UART_LL_U1_INT (void)
{
	uint16_t ui16Tmp;
	uint8_t uiBrLow;
	uint8_t uiBrHigh;

	// timer 1 - Master bus RX TimeOut
	mTimer1Mode_CTC();
	mTimer1Clear();
	mTimer1CompReg_Upd(TIMER_CalculateValue(DEF_UART1_RX_TOUT));
	mTimer1IntOcrA_Enab();

	// UART1 setting 
	spLL_UART1->LL.DisTxWhileIdle = DEF_UART1_DIS_TX_IDLE;
	spLL_UART1->LL.RS485 = DEF_UART1_RS485_BUS;
	spLL_UART1->LL.DisRxWhileTxOperate = DEF_UART1_DIS_RX;
	spLL_UART1->LL.IsMasterPeriphery = DEF_UART1_MASTER_BUS;
	spLL_UART1->LL.RX.TickForToutError = DEF_UART1_RX_MSG_TOUT;
	
	// periphery setup
	if (DEF_UART1_RS485_BUS) {
		mU1BusRs485Dir_Enab();
		mU1BusRs485Dir_In();
	}
	ui16Tmp = UART_CalculateBaudRate(DEF_UART1_BAUD_RATE);
	uiBrLow = (uint8_t)ui16Tmp;
	uiBrHigh = (uint8_t)(ui16Tmp>>8);
	mUart1BaudRate(uiBrLow,uiBrHigh);
	mUart1NoBit_8();
	mUart1Rx_Enab();
	mUart1Tx_Enab();
	mUart1IntRx_Enab();

	return spLL_UART1;
}

void UART_LL_U1 (void)
{
	// Link layer receiver executions
	if (!spLL_UART1->LL.RX.E) {
		if (!spLL_UART1->LL.RX.FrameReceiving) {
			spLL_UART1->LL_Int.RX.Busy = false;
			if (spLL_UART1->LL.RX.FrameReceived) {
				// checking correct parameters
				if (spLL_UART1->LL.RX.Index < UART_FRAME_LEN_MIN) {
					spLL_UART1->LL_Int.RX.E_Nr = UART_E_RX_LOW_FRM_LEN;
					spLL_UART1->LL.RX.Index = 0;
				}
				else {
					UART1_decode();
					if (!spLL_UART1->LL.RX.E) {
						spLL_UART1->LL_Int.RX.PacketReceived = true;
					}
				}
				spLL_UART1->LL.RX.FrameReceived = false;
			}
		} 
		else {
			spLL_UART1->LL_Int.RX.Busy = true;
		}
	}
	if (spLL_UART1->LL.RX.E) {
		// passing error to higher layer
		spLL_UART1->LL_Int.RX.E_Nr = spLL_UART1->LL.RX.E_Nr;
		spLL_UART1->LL_Int.RX.MissedRx = spLL_UART1->LL.RX.MissedRxWhileException;
		// clearing variable
		spLL_UART1->LL.RX.E_Nr = 0;
		spLL_UART1->LL.RX.MissedRxWhileException = 0;
		spLL_UART1->LL.RX.Index = 0;
		spLL_UART1->LL.RX.FrameReceived = false;
		spLL_UART1->LL.RX.FrameReceiving = false;
		spLL_UART1->LL.RX.CurrentTicks = 0;
		// Error clearing - last step because of interruption
		spLL_UART1->LL.RX.E = false;
	}
	// Link layer transmitter executions
	if (!spLL_UART1->LL.TX.FrameTransmitting) {
		spLL_UART1->LL_Int.TX.Busy = false;
		if (spLL_UART1->LL.TX.FrameTransmitted) {
			spLL_UART1->LL.TX.FrameTransmitted = false;
			spLL_UART1->LL_Int.TX.PacketTransmitted = true;
			spLL_UART1->LL.RX.CurrentTicks = 0;
			if (spLL_UART1->LL.RX.ExpectRespondMsg) {
				mTimer1Run_Clk_64();
			}
		}
		if (spLL_UART1->LL_Int.TX.InitiateTransmition) {
			spLL_UART1->LL_Int.TX.InitiateTransmition = false;
			// checking correct parameters
			if (spLL_UART1->LL_Int.TX.PacketLength > UART_PACKET_LEN_MAX) {
				spLL_UART1->LL_Int.TX.E_Nr = UART_E_TX_HIG_PAC_LEN;
			}
			else if (spLL_UART1->LL_Int.TX.PacketLength < UART_PACKET_LEN_MIN) {
				spLL_UART1->LL_Int.TX.E_Nr = UART_E_TX_LOW_PAC_LEN;
			}
			else {	
				// packet encoding
				UART1_encode();
				spLL_UART1->LL.TX.LaunchIt = true;
				spLL_UART1->LL.TX.Index = 0;
				spLL_UART1->LL.RX.ExpectRespondMsg = spLL_UART1->LL_Int.TX.ExpectRespond;
				spLL_UART1->LL_Int.TX.ExpectRespond = false;
				spLL_UART1->LL.TX.FrameTransmitting = true;
			}
		}
	} 
	if (spLL_UART1->LL.TX.FrameTransmitting) {
		spLL_UART1->LL_Int.TX.Busy = true;
	}	
	PeripheryLauncher();
}

/************************************************************************/
/* Private functions                                                    */
/************************************************************************/
void PeripheryLauncher(void)
{
	if (spLL_UART1->LL.TX.LaunchIt) {
		spLL_UART1->LL.TX.LaunchIt = false;
		if(spLL_UART1->LL.DisTxWhileIdle) {
			mUart1Tx_Enab();
		}
		if(spLL_UART1->LL.RS485) {
			mU1BusRs485Dir_Out();
		}
		if(spLL_UART1->LL.DisRxWhileTxOperate) {
			mUart1Rx_Dis();
		}
		mUart1IntUdrEmp_Enab();
	}
}

void UART1_decode(void)
{
	/* remove start mark and and mark and make decoding stored into interface buffer */
	uint8_t PacketLength = 0;
	uint16_t Checksum_Calc;

	for (uint8_t i = 1; i < (spLL_UART1->LL.RX.Index - 1); i++) {
		if(spLL_UART1->LL.RX.Buff[i] == UART_FRAME_SUBST_MARK) {
			/* Decoding mark found */
			i++;
			switch(spLL_UART1->LL.RX.Buff[i]) {
			case UART_FRAME_SUBST_0X80:
				spLL_UART1->LL_Int.RX.Buff[PacketLength++] = UART_FRAME_SUBST_MARK;
				break;
			case UART_FRAME_SUBST_0XAB:
				spLL_UART1->LL_Int.RX.Buff[PacketLength++] = UART_FRAME_START_MARK;
				break;
			case UART_FRAME_SUBST_0XBA:
				spLL_UART1->LL_Int.RX.Buff[PacketLength++] = UART_FRAME_STOP_MARK;
				break;
			default:
				spLL_UART1->LL.RX.E = true;
				spLL_UART1->LL.RX.E_Nr = UART_E_RX_DECODING;
				spLL_UART1->LL_Int.RX.Buff[PacketLength++] = 0;
			}
		}
		else {
			spLL_UART1->LL_Int.RX.Buff[PacketLength++] = spLL_UART1->LL.RX.Buff[i];
		}
	}
	if ((PacketLength % 2) == 0) {
		Checksum_Calc = UART_Chksum_Calc(spLL_UART1->LL_Int.RX.Buff,PacketLength);
		if (Checksum_Calc != 0) {
			spLL_UART1->LL.RX.E = true;
			spLL_UART1->LL.RX.E_Nr = UART_E_RX_CHKSUM; 
		}
	}
	else {
		spLL_UART1->LL.RX.E = true;
		spLL_UART1->LL.RX.E_Nr = UART_E_RX_EVEN_LEN;
	}
	spLL_UART1->LL_Int.RX.PacketLength = PacketLength - 2;		// removing last 2 bytes - checksum	
}

void UART1_encode(void)
{
	/* add start mark and end mark and make encoding into frame buffer*/
	uint8_t FrameLength = 0;
	uint16_t Checksum_Calc;
	uint8_t PacketLength;
	
	PacketLength = spLL_UART1->LL_Int.TX.PacketLength;
	if ((PacketLength % 2) != 0) {
		// data length must be even because of 16 bit checksum
		spLL_UART1->LL_Int.TX.Buff[PacketLength++] = 0;
	}
	Checksum_Calc = UART_Chksum_Calc(spLL_UART1->LL_Int.TX.Buff,PacketLength);
	// adding 2 bytes - checksum
	spLL_UART1->LL_Int.TX.Buff[PacketLength++] = (uint8_t)(Checksum_Calc>>8);
	spLL_UART1->LL_Int.TX.Buff[PacketLength++] = (uint8_t)Checksum_Calc;
	spLL_UART1->LL.TX.Buff[FrameLength++] = UART_FRAME_START_MARK;
	for (uint8_t i = 0; i < PacketLength; i++) {
		switch(spLL_UART1->LL_Int.TX.Buff[i]) {
		case UART_FRAME_START_MARK:
			spLL_UART1->LL.TX.Buff[FrameLength++] = UART_FRAME_SUBST_MARK;
			spLL_UART1->LL.TX.Buff[FrameLength++] = UART_FRAME_SUBST_0XAB;
			break;
		case UART_FRAME_STOP_MARK:
			spLL_UART1->LL.TX.Buff[FrameLength++] = UART_FRAME_SUBST_MARK;
			spLL_UART1->LL.TX.Buff[FrameLength++] = UART_FRAME_SUBST_0XBA;
			break;
		case UART_FRAME_SUBST_MARK:
			spLL_UART1->LL.TX.Buff[FrameLength++] = UART_FRAME_SUBST_MARK;
			spLL_UART1->LL.TX.Buff[FrameLength++] = UART_FRAME_SUBST_0X80;
			break;
		default:	
			spLL_UART1->LL.TX.Buff[FrameLength++] = spLL_UART1->LL_Int.TX.Buff[i];
		}
	}
	spLL_UART1->LL.TX.Buff[FrameLength++] = UART_FRAME_STOP_MARK;
}

// move to data function
uint16_t UART_Chksum_Calc (uint8_t *pDataBuffer,uint8_t Len)
{
	/* return calculated One's complement additional checksum */
	uint32_t TempChecksum;
	uint16_t Checksum = 0x0000;
	uint8_t i = 0;
	
	while(i < Len) {
		TempChecksum = (uint32_t)((pDataBuffer[i++]<<8) + Checksum);
		if(i<Len) {
			TempChecksum += (uint32_t)(pDataBuffer[i++]); 
		}
		Checksum = (uint16_t)(TempChecksum & 0x0FFFF); //Discard the carry if any
		TempChecksum = (TempChecksum>>16);     //Keep the carryout for value exceeding 16 Bit
		Checksum = (uint16_t)(TempChecksum + Checksum); //Add the carryout if any
	}

	Checksum = ~Checksum;
	//Checksum++;		// Two's complement additional checksum
	return Checksum;
}

/************************************************************************/
/* Interrupt periphery                                                  */
/************************************************************************/
// timer 1 - UART1 RX TimeOut
ISR(TIMER1_COMPA_vect)
{
	if (spLL_UART1->LL.RX.FrameReceiving) {
		spLL_UART1->LL.RX.E = true;
		spLL_UART1->LL.RX.E_Nr = UART_E_RX_TIMEOUT;
		mTimer1Stop();
	}
	else if (spLL_UART1->LL.RX.ExpectRespondMsg) {
		spLL_UART1->LL.RX.CurrentTicks++;
		if(spLL_UART1->LL.RX.CurrentTicks == spLL_UART1->LL.RX.TickForToutError) {
			spLL_UART1->LL.RX.E = true;
			spLL_UART1->LL.RX.E_Nr = UART_E_RX_MSG_TOUT;
			mTimer1Stop();
		}
	}
}

/* UART 1 interrupt handling */
ISR(USART1_RX_vect)
{
	uiTmpInterrupt = UDR1;
	if (!spLL_UART1->LL.RX.E && !spLL_UART1->LL.RX.FrameReceived) {
		switch (uiTmpInterrupt) {
			case UART_FRAME_START_MARK:
				if (!spLL_UART1->LL.RX.FrameReceiving) {
					mTimer1Stop();
					spLL_UART1->LL.RX.FrameReceiving = true;
					spLL_UART1->LL.RX.Index = 0;
					spLL_UART1->LL.RX.Buff[spLL_UART1->LL.RX.Index++] = uiTmpInterrupt;
					mTimer1Clear();
					mTimer1Run_Clk_64();
				}
				else {
					spLL_UART1->LL.RX.E = true;
					spLL_UART1->LL.RX.E_Nr = UART_E_RX_START_MARK;
					mTimer1Stop();
				}
				break;
			case UART_FRAME_STOP_MARK:
				if (spLL_UART1->LL.RX.FrameReceiving) {
					spLL_UART1->LL.RX.Buff[spLL_UART1->LL.RX.Index++] = uiTmpInterrupt;
					spLL_UART1->LL.RX.FrameReceived = true;
					spLL_UART1->LL.RX.FrameReceiving = false;
				}
				else {
					spLL_UART1->LL.RX.E = true;
					spLL_UART1->LL.RX.E_Nr = UART_E_RX_STOP_MARK;
				}
				mTimer1Stop();
				break;
			default:
				if (spLL_UART1->LL.RX.FrameReceiving) {
					spLL_UART1->LL.RX.Buff[spLL_UART1->LL.RX.Index++] = uiTmpInterrupt;
					mTimer1Clear();
				}
				else {
					spLL_UART1->LL.RX.E = true;
					spLL_UART1->LL.RX.E_Nr = UART_E_RX_UNEXP_DATA;
					mTimer1Stop();
				}
				break;
		}
		if (spLL_UART1->LL.RX.Index > UART_FRAME_LEN_MAX) {
			spLL_UART1->LL.RX.E = true;
			spLL_UART1->LL.RX.E_Nr = UART_E_RX_HIG_FRM_LEN;
			mTimer1Stop();
		}
	}
	else {
		spLL_UART1->LL.RX.MissedRxWhileException++;
	}
}

ISR(USART1_TX_vect)
{
	mUart1IntTx_Dis();
	if(spLL_UART1->LL.RS485) {
		mU1BusRs485Dir_In();
	}
	if(spLL_UART1->LL.DisTxWhileIdle) {
		mUart1Tx_Dis();
	}
	if(spLL_UART1->LL.DisRxWhileTxOperate) {
		mUart1Rx_Enab();
	}
	spLL_UART1->LL.TX.FrameTransmitted = true;
	spLL_UART1->LL.TX.Index = 0;
	spLL_UART1->LL.TX.FrameTransmitting = false;
}

ISR(USART1_UDRE_vect)
{
	uiTmpInterrupt = spLL_UART1->LL.TX.Buff[spLL_UART1->LL.TX.Index++];
	UDR1 = uiTmpInterrupt;
	//spLL_UART1->LL.TX.FrameTransmitting = true;
	if (uiTmpInterrupt == UART_FRAME_STOP_MARK) {
		mUart1IntUdrEmp_Dis();
		mUart1TxFlag_Clr();
		mUart1IntTx_Enab();
	}
}