/** \file ******************************************************************
* \brief Implementation of USART Stack
* 
* \author Dominic Rathje (dominic.rathje@uni-ulm.de)
* \version 1.0
* \note
* - Compiler          : WinAVR 20100110
* - Supported devices : ATMega8
*
*****************************************************************************/

#define _UART_C_ 1

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

uint8_t rx_buffer[UART_RX_BUFFER_SIZE];		///< Memory for the Receive FIFO
uint8_t tx_buffer[UART_TX_BUFFER_SIZE];		///< Memory for the Transmit FIFO

volatile uint8_t rx_head=0; 				///< head index of the Receive FIFO
volatile uint8_t rx_tail=0;					///< tail index of the Receive FIFO
volatile uint8_t tx_head=0; 				///< head index of the Transmit FIFO
volatile uint8_t tx_tail=0;					///< tail index of the Transmit FIFO


void uart_init(void) {

		UART_DDR |= TXPIN_bm;
		UART_DDR &= ~RXPIN_bm;
		
		UBRRH = (unsigned char)(UBRR_val>>8);
		UBRRL = (unsigned char)(UBRR_val);
		
		//UCSRA |= (1<<U2X);
		UCSRB = (1<<RXEN) | (1<<TXEN);
		UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
		
		#if UART_CHRSIZE_9BIT==1
		UCSRB |= (1<<UCSZ2); //9-Bit Character Size
		#endif
		
		uart_rxc_int_en();

}


void uart_sendbyte(uint8_t data) {
	while(tx_head == tx_tail-1) {}
	tx_buffer[tx_head++] = data;
	tx_head &= (UART_TX_BUFFER_SIZE-1);
	uart_udre_int_en();
}


uint8_t uart_senddata(uint8_t data){
	if(!(UCSRA & (1<<UDRE))) { return 0; }
	UCSRB &= ~(1<<TXB8);
	UART_DATA = data;
	return 1;
}


uint8_t uart_sendid(uint8_t data){
	if(!(UCSRA & (1<<UDRE))) { return 0; }
	UCSRB |= (1<<TXB8);
	UART_DATA = data;
	return 1;
}


uint8_t uart_getbyte(uint8_t *data) {
	if(rx_tail==rx_head) {
		return 0;
	} else {
		*data=rx_buffer[rx_tail++];
		rx_tail &= (UART_RX_BUFFER_SIZE-1);
		return 1;
	}
}

#if UART_MPCM_ENABLE == 1
uint8_t uart_senddata(uint8_t data){
	if(!(UCSRA & (1<<UDRE))) { return 0; }
	UCSRB &= ~(1<<TXB8);
	UART_DATA = data;
	return 1;
}


uint8_t uart_sendid(uint8_t data){
	if(!(UCSRA & (1<<UDRE))) { return 0; }
	UCSRB |= (1<<TXB8);
	UART_DATA = data;
	return 1;
}
#endif

#if UART_PRINTF_COMPATIBILITY == 1

int uart_putchar(char c, FILE *sream) {
	while(tx_head == tx_tail-1) {}
	tx_buffer[tx_head++] = c;
	tx_head &= (UART_TX_BUFFER_SIZE-1);
	uart_udre_int_en();
	return 0;
}


int uart_getchar(FILE *stream) {
	int data;
	if(rx_tail==rx_head) {
		return _FDEV_EOF;
	} else {
		data= (int)rx_buffer[rx_tail++];
		rx_tail &= (UART_RX_BUFFER_SIZE-1);
		return data;
	}

}

#endif

/** \brief Interrupt-Service-Routine for the RX-Complete Interrupt
 * 
 * If a RX-Complete Interrupt occurs: 
 *  - checkt if there is any space left in the FIFO Buffer 
 *  - copy the received byte into the buffer 
 */
ISR(RXC_vect) {
	
	if(rx_head != ((rx_tail-1) & (UART_RX_BUFFER_SIZE-1)) ) {
		rx_buffer[rx_head++] = UART_DATA;
		rx_head &= (UART_RX_BUFFER_SIZE-1);
	}
}

/** \brief Interrupt-Service-Routine for the Data-Register-Empty Interrupt
 * 
 * If a Data-Register-Empty Interrupt occurs:
 * - check if there is pending data in the FIFO
 *  - if yes: send the next byte
 *  - if no: disable URDE Interrupt
 */ 
ISR(DRE_vect) {
	if(tx_tail==tx_head) {
		uart_udre_int_dis();
	} else {
		UART_DATA = tx_buffer[tx_tail++];
		tx_tail &= (UART_TX_BUFFER_SIZE-1);
	}
}
