/**************************************************************************************
*	File:			DeviceDriver.h										*
*	Titel:			ATmega128 DeviceDriver for the UART						*
*	Autor:		Jan Helber, Florian Steinle, Lars Mildner						*
*	Datum:		17.10.2006											*
*	used Software:	Win-AVR-GCC und AVR Studio 4							*
*																*
*	Description:													*
*		ATmega128 DEviceDriver provides a set of routines for the 2 internal UARTS:		*
*																*
*		Following routines are provided:									*
*			io_init()												*
*			uart0_init(UART_BAUD_RATE, databits, stopbits, parity, FREQ_CPU)		*
*			uart0_send(text, size)										*
*			char uart0_get_char()										*
*			size uart0_get_chararray(chararray)							*
*			size uart0_get_chararray_command(chararray)						*
*			uart0_waitack()											*
*			uart0_recv_complete()										*
*			uart0_trans_complete()										*
*																*
*	Infos:														*
*		PD3 INT3/TXD1 (External Interrupt3 Input or UART1 Transmit Pin)			*
*		PD2 INT2/RXD1 (External Interrupt2 Input or UART1 Receive Pin)				*
*		XCK1, TXD1, RXD1, SDA, and SCL not applicable in ATmega103 compatibility mode.	*
*																*
*		PE1 PDO/TXD0 (Programming Data Output or UART0 Transmit Pin)				*
*		PE0 PDI/RXD0 (Programming Data Input or UART0 Receive Pin)				*
*		TXD0, UART0 Transmit pin.										*
*																*
*	Edition History:													*
*		10.10.2006: Getting started - jh									*
**************************************************************************************/

#include <avr\io.h>
#include <avr\interrupt.h>
#include <stdlib.h> 
#include <string.h>

enum parity{ DISABLED, EVEN, ODD };
unsigned char in_val;

static volatile unsigned char	*send_buf;
static volatile unsigned char	send_counter;

unsigned char recv_buf[40];
unsigned char recv_size = 0;
unsigned char recv_counter = 0;
unsigned char cr = 0;

/********************************** Funktionsrümpfe **********************************************/

void uart0_init(long UART_BAUD_RATE, long databits, long stopbits, unsigned char parity, long FREQ_CPU);// initialisiert UART-Schnittstelle mit
void io_init(void);																						// Stellt alle Ports auf Eingang
void uart0_send(unsigned char *buf, unsigned char size);												// sendet Daten über UART
unsigned char uart0_get_chararray(unsigned char *destination);											// Liefert eingegebenen String zurück
unsigned char uart0_get_chararray_command(unsigned char *destination);									// Wartet auf RETURN und liefert eigegebenen String zurück
unsigned char uart0_get_char();																			// Lese ein Zeichen (UART)
void uart0_waitack();																					// Warte auf "RETURN"-Taste
unsigned char uart0_recv_complete();																	// Datenempfang über UART vollständig
unsigned char uart0_trans_complete();																	// Daten über UART vollständig versendet

/************************************** Interruptserviceroutinen *********************************/

SIGNAL(SIG_UART0_TRANS) {											// Interrupt für UART Byte gesendet
    send_buf++;
    if (--send_counter) UDR0 = *send_buf;							// solange send_counter > 0, nächstes Datenbyte
}

SIGNAL(SIG_UART0_RECV) {											// Interrupt für UART Byte empfangen
	recv_buf[recv_counter] = UDR0;									// schreibe eingegebenes Zeichen in Empfangspuffer
	if (recv_buf[recv_counter] == 0x0d || recv_counter > 0x1F){		// Es wurde ein RETURN eingegeben
		recv_buf[recv_counter] = '\0';								// 	Terminierende 0 von Character Array
		cr = 1;														// 	habe ein Return entdeckt (CR)
		recv_size = recv_counter;									//	kopiere Pufferlänge
		recv_counter = 0;											//	lösche Empfangspuffer
	}
	else {															// Es wurde ein "normales" Zeichen eingegeben
		recv_counter++;												//	inkrementiere Empfangspufferzähler
		cr = 0;
	}
}

/************************************** Funktionen ***********************************************/
void uart0_init(long UART_BAUD_RATE, long databits, long stopbits, unsigned char parity, long FREQ_CPU) {
	// Intern: 1MHz (extern bis 16MHz)
	// Baud rate (in bits per second, bps)
    UCSR0B = ((1<<RXCIE0)|(1<<TXCIE0)|(1<<RXEN0)|(1<<TXEN0));		// Interrupts enable für RxD/TxD (Datenblatt: S.191!!)
	//UCSR0C = ((3<<UCSZ00)|(0<<UPM0)|(0<<USBS0));					// Set frame format: 8data, keine Parität, 1 stop bit (S.192-193!)

	switch(parity) { // Datenblatt: S.192-193!!
		case DISABLED:
       	UCSR0C = (0<<UPM0);											// Set frame format: keine Parität
		break;
		case EVEN:
       	UCSR0C = (2<<UPM0);											// Set frame format: EVEN (gerade)
		break;
		case ODD:
       	UCSR0C = (3<<UPM0);											// Set frame format: ODD (ungerade)
		break;
		default:
		UCSR0C = (0<<UPM0);											// Set frame format: keine Parität
	}

	switch(stopbits) { //(S.193!!)
		case 1:
       	UCSR0C = (0<<USBS0);										// Set frame format: 1 stop bit
		break;
		case 2:
       	UCSR0C = (1<<USBS0);										// Set frame format: 2 stop bit
		break;
		default:
		UCSR0C = (0<<USBS0);										// Set frame format: 1 stop bit
	}
	
	switch(databits) { //(S.193!!)
		case 5:
       	UCSR0C = (0<<UCSZ00);										// Set frame format: 5data
		break;
		case 6:
       	UCSR0C = (1<<UCSZ00);										// Set frame format: 6data
		break;
		case 7:
       	UCSR0C = (2<<UCSZ00);										// Set frame format: 7data
		break;
		case 8:
       	UCSR0C = (3<<UCSZ00);										// Set frame format: 8data
		break;
		case 9:
       	UCSR0C = (7<<UCSZ00);										// Set frame format: 9data
		break;
		default:
		UCSR0C = (3<<UCSZ00);										// Set frame format: 8data
	}
	
	long UART_BAUD_SELECT = (long)(FREQ_CPU/(UART_BAUD_RATE*16)-1);	// Datenblatt: S.175!!
	UBRR0L = ((unsigned char)(UART_BAUD_SELECT));					// Baudrate festlegen (LOW-Byte) (S.194!)
	UBRR0H = 0;														// Baudrate festlegen (HIGH-Byte) (S.194!)
	sei();															// generelle Interruptfreigabe
}

void io_init(void) {
	DDRA = 0x00;													// alle Pins von Port A auf Eingabe
	DDRB = 0x00;													// alle Pins von Port B auf Eingabe
	DDRC = 0x00;													// alle Pins von Port C auf Eingabe
	DDRD = 0x00;													// alle Pins von Port D auf Eingabe
	DDRE = 0x00;													// alle Pins von Port E auf Eingabe
	DDRF = 0x00;													// alle Pins von Port F auf Eingabe
}

void uart0_send(unsigned char *buf, unsigned char size){			// sendet Daten über UART
	if (send_counter == 0) {
		send_buf = buf;
		send_counter = size;
		UDR0 = buf[0];
	}
}

void uart0_send_wait(unsigned char *buf, unsigned char size){		// sendet Daten über UART sobald möglich
	while (uart0_trans_complete() == 0) asm volatile ("nop");		// warten auf leeren Puffer
	if (send_counter == 0) {
		send_buf = buf;
		send_counter = size;
		UDR0 = buf[0];
	}
}

unsigned char uart0_get_chararray(unsigned char *destination){		//Lese eingegebenen String von UART
	char *s = recv_buf;
	char *d = destination;
	int i=0;
	for(i=0; i<recv_size; i++) {									// kopiere alle Zeichen von UART nach 'destination'
		*d++ = *s++;
	}
	*d = '\0';														// terminierungsstring anhängen
	return recv_size;												// gebe Stringlänge zurück
}

unsigned char uart0_get_chararray_command(unsigned char *destination){	// Wie uart0_get_chararray(), wartet auf RETURN
	while(!cr || recv_size == 0) {
		asm volatile ("nop");
	}
	cr=0;
	char *s = recv_buf;
	char *d = destination;
	int i=0;
	for(i=0; i<recv_size; i++) {
		*d++ = *s++;
	}
	*d = '\0';
   return recv_size;
}

unsigned char uart0_get_char() {									// liefert letztes eingegebenes Zeichen
	return recv_buf[recv_size-1];
}

void uart0_waitack() {												// auf Eingabetaste (RETURN) warten
	while(!cr || recv_size == 0) {
		asm volatile ("nop");
	}
	cr=0;
}

unsigned char uart0_recv_complete() {								// Datenempfang über UART vollständig
	if (recv_size==0) return 1;
	return 0;
}

unsigned char uart0_trans_complete() {								// Datenversand über UART vollständig
	if (send_counter==0) return 1;
	return 0;
}
