/*******************************************************
Project					: LiFePO4 Balancer
Chip type               : ATtiny412
Program type            : Application
AVR Core Clock frequency: 10,000000 MHz
File					: usart.c
*******************************************************/

/*_____ I N C L U D E S ____________________________________________________*/

#include <avr/io.h>
#include <avr/interrupt.h>

#include "main.h"
#include "usart.h"

/*______D E C L A R A T I O N ______________________________________________*/

uint8_t device_id;
uint8_t broadcast_id;

// USART Transmitter
uint8_t tx_buffer[TX_BUFFER_SIZE];
uint8_t tx_index = 0;
uint8_t tx_count = 0;

// USART Receiver
uint8_t rx_buffer[RX_BUFFER_SIZE];
uint8_t rx_index = 0;
t_rxFlgs rx_flags = USART_FLG_NO_DATA;

/*----- Functions ----------------------------------------------------------*/

uint16_t crc16( uint8_t *buf, uint8_t len )
{
	uint16_t crc = 0xFFFF;
	for (uint8_t pos = 0; pos < len; pos++)
	{
		crc ^= (uint16_t)buf[pos];			// XOR byte into least sig. byte of crc

		for (uint8_t i = 8; i != 0; i--) {	// Loop over each bit
			if ((crc & 0x0001) != 0) {		// If the LSB is set
				crc >>= 1;                  // Shift right and XOR 0xA001
				crc ^= 0xA001;
			}
			else                            // Else LSB is not set
				crc >>= 1;                  // Just shift right
		}
	}
	
	return crc;
}


void usart_txStart( uint8_t bytes ) 
{
	if(bytes > TX_BUFFER_SIZE)
		return;
	
	uint16_t crc = crc16(tx_buffer, bytes);
	tx_buffer[bytes] = _BYTEX(crc, 0);
	tx_buffer[bytes+1] = _BYTEX(crc, 1);
	
	tx_count = bytes+1;							// bytes + 2 (CRC) - 1 (already transmitted byte)
	USART_TX(tx_buffer[0]);
	USART_DREI_ON();
}

uint8_t usart_rxEnd( uint8_t check_crc ) 
{
	uint8_t retVal;
	
	USART_TIMOUT_STOP();
	USART_TIMOUT_RST();
	USART_MPCM_ON();
	
	if(check_crc)
		retVal = !crc16(rx_buffer, rx_index);
	else
		retVal = 0;
	
	rx_index = 0;
	return retVal;
}

/*----- Interrupts ---------------------------------------------------------*/

ISR( TCB0_INT_vect )			// rx timeout
{			
	usart_rxEnd(0);				// clear rx_buffer without checking CRC
	USART_TIMOUT_FLG_CLR();
}

ISR( USART0_DRE_vect ) 
{
	if(tx_count) 
	{
		USART_TX(tx_buffer[++tx_index]);
		tx_count--;
	}
	
	else 
	{
		tx_index = 0;
		USART_DREI_OFF();
	}
}

ISR( USART0_RXC_vect ) 
	{
	// Receiver is not ready yet
	if(USART_RX_FLAGS | (rx_flags)) {							// Skip if error or buffer isn't processed yet
		rx_buffer[BUF_POS_TRASH] = USART_RX_DATA;				// Read data to clear flags
		usart_rxEnd(0);											// clear rx_buffer without checking CRC
	}
	
	// Received data
	else if(!USART_RX_ADDR) 
	{
		USART_TIMOUT_RST();										// Reset Timeout Timer
		rx_buffer[rx_index++] = USART_RX_DATA;					// Save data
		
		// Invalid function code
		if( (rx_index == BUF_POS_CMD) && rx_buffer[BUF_POS_CMD] >= CMD_ENUM_COUNT) 
		{
			usart_rxEnd(0);										// clear rx_buffer without checking CRC
		}
		
		// Transfer competed
		else if( rx_index >= rx_buffer[BUF_POS_DATALEN] )		// Rx Data completed
		{	
			if(usart_rxEnd(1))									// Check CRC
				rx_flags = USART_FLG_RX_COMPLETED;				// Set Rx compete flag
		}
	}
	
	// Received address
	else 
	{
		uint8_t rx_addr = USART_RX_DATA;
		
		// Check if address is valid
		if( (rx_addr == device_id) || (rx_addr == broadcast_id) ) 
		{
			USART_MPCM_OFF();							// Disable multi-processor mode
			USART_TIMOUT_START();						// Start timeout timer	
			rx_buffer[rx_index++] = rx_addr;			// Save data
		}
	}
}