/*
 * Copyright 2015 Mario Richter, Peter Stephan
 * This file is part of NeMo.
 *
 * NeMo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * NeMo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with NeMo.  If not, see <http://www.gnu.org/licenses/>.
 */

/*!
 * \file usart.c
 * \ingroup	usart
 * \brief	Routinen für die serielle Kommunikation über RS232 
 */

/*!
 * \defgroup usart Serielle Kommunikation (RS232) 
 */
 
#include "main.h"

ringbuf send_buf;
ringbuf recv_buf;

/* ------------------------------------------------------------------------------------ */

/*!
 * \ingroup usart
 * \brief Initialisierung der seriellen Schnittstelle.
 * 
 * Die Funktion initialisiert die serielle Schnittstelle. Es wird die Baudrate eingestellt
 * und Sender und Empfänger aktiviert. Außerdem wird der Interrupt für den Empfang eines
 * neuen Zeichens freigegeben.
 * \note Die Interrupts müssen noch separat global aktiviert werden (sei()). 
 * \sa usart_new_char, usart_scheduler
 */

void usart_init(void)
{
  UBRR0H = 0;
	UBRR0L = 8;			// 115k Baud @ 16 MHz
	UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0);			// RX interrupt, RX und TX aktivieren
	UCSR0C = (3 << UCSZ00);						                    // 8 bits, keine Parität, 1 Stopbit
}
        

/*!
 * \ingroup usart
 * \brief USART-Scheduler zum Senden von Zeichen aus dem Buffer.
 * 
 * Diese Funktion wird zyklisch aufgerufen. Sie überprüft, ob neue zu sendende Zeichen im Buffer
 * enthalten sind und sendet diese über die USART (wenn der Sender bereit ist).
 * \sa usart_init, usart_new_char
 */

void usart_scheduler(void)
{
	if((send_buf.read != send_buf.writ) && USART_TX_READY)			// Sind im Buffer zu sendende Zeichen und ist Sender bereit?
	{
		USART_SEND_CHAR(send_buf.buf[send_buf.read]);				// Nächstes Zeichen senden
		
		if(++send_buf.read >= BUFLEN)								// Buffer-Leseposition weiterziehen bzw. auf 0 bei Überlauf
			send_buf.read = 0;
	}
}


/*!
 * \ingroup usart
 * \brief Routine zur Behandlung eines neuen empfangenen Zeichens.
 * 
 * Diese Funktion wird aus dem Interrupt aufgerufen, wenn ein neues Zeichen über die serielle
 * Schnittstelle empfangen wurde. Das Zeichen wird aus dem USART-Empfangsregister in den Empfangsbuffer
 * übernommen und dessen Schreibzeiger weitergezogen. Der Buffer enthält neue Zeichen, wenn die Leseposition
 * ungleich der Schreibposition ist.
 * \sa usart_init, usart_scheduler
 */
 
void usart_new_char(void)
{
	recv_buf.buf[recv_buf.writ] = USART_RECV_CHAR();						// Neu empfangenes Zeichen in Empfangsbuffer eintragen
	
	if(++recv_buf.writ >= BUFLEN)																// Buffer-Schreibposition weiterziehen bzw. auf 0 bei Überlauf
		recv_buf.writ = 0;
}


/*!
 * \ingroup usart
 * \brief Senden eines einzelnen Zeichens über die serielle Schnittstelle.
 * 
 * Die Funktion trägt das zu sendende Zeichen in den Sendebuffer ein und zieht die Schreibposition
 * weiter. Die Zeichen werden dann, falls die USART bereit ist, über den \ref usart_scheduler an die USART übergeben
 * und gesendet.
 * \param data Zu sendendes Zeichen.
 * \sa usart_send_string, usart_scheduler
 */
 
void usart_send_char(char data)
{
	send_buf.buf[send_buf.writ] = data;													// Neu zu sendendes Zeichen in Sendebuffer eintragen
	
	if(++send_buf.writ >= BUFLEN)																// Buffer-Schreibposition weiterziehen bzw. auf 0 bei Überlauf
		send_buf.writ = 0;
}


/*!
 * \ingroup usart
 * \brief Senden eines Strings über die serielle Schnittstelle (RAM).
 *
 * Die Funktion sendet einen String aus dem RAM über die serielle Schnittstelle. Dabei wird für jedes Zeichen
 * einzeln \ref usart_send_char aufgerufen, bis das Ende des Strings erreicht wurde (NULL-Character).
 * \param str Zeiger auf den zu sendenden String.
 * \sa usart_send_char, usart_scheduler
 */

void usart_send_string(char * str)
{
	while(*str != '\0')																					// Jedes Zeichen im String an usart_send_char() übergeben
		usart_send_char(*str++);
}


/*!
 * \ingroup usart
 * \brief Senden eines Strings über die serielle Schnittstelle (Flash).
 * 
 * Die Funktion sendet einen String aus dem Flash über die serielle Schnittstelle. Dabei wird für jedes Zeichen
 * einzeln \ref usart_send_char aufgerufen, bis das Ende des Strings erreicht wurde (NULL-Character).
 * \param str Zeiger auf den zu sendenden String.
 * \sa usart_send_char, usart_scheduler
 */
 
void usart_send_string_P(const char * str)
{
  while(pgm_read_byte(str) != '\0')                         // Jedes Zeichen im String an usart_send_char() übergeben
    usart_send_char(pgm_read_byte(str++));
}


/*!
 * \ingroup usart
 * \brief Senden eines Strings über die serielle Schnittstelle (RAM). 
 * 
 * Die Funktion sendet einen String aus dem RAM über die serielle Schnittstelle. Dabei wird für jedes Zeichen
 * einzeln \ref usart_send_char aufgerufen, bis das Ende des Strings erreicht wurde (NULL-Character).
 * \param str Zeiger auf den zu sendenden String.
 * \sa usart_send_char, usart_scheduler
 */
 
void usart_send_line(char * str)
{
	while(*str != '\0')																					// Jedes Zeichen im String an usart_send_char() übergeben
		usart_send_char(*str++);

	usart_send_char('\r');
	usart_send_char('\n');
}


/*!
 * \ingroup usart
 * \brief Senden eines Strings über die serielle Schnittstelle (Flash).
 *
 * Die Funktion sendet einen String aus dem Flash über die serielle Schnittstelle. Dabei wird für jedes Zeichen
 * einzeln \ref usart_send_char aufgerufen, bis das Ende des Strings erreicht wurde (NULL-Character).
 * \param str Zeiger auf den zu sendenden String.
 * \sa usart_send_char, usart_scheduler
 */

void usart_send_line_P(const char * str)
{
  while(pgm_read_byte(str) != '\0')                         // Jedes Zeichen im String an usart_send_char() übergeben
    usart_send_char(pgm_read_byte(str++));

  usart_send_char('\r');
  usart_send_char('\n');
 }


/*!
 * \ingroup usart
 * \brief Senden einer Zahl als Hex-String über die serielle Schnittstelle.
 * 
 * Die Funktion sendet eine übergebene Zahl (8 Bit, 0-255) als Hex-String an die serielle Schnittstelle.
 * Dabei werden zwei Zeichen ausgegeben, für die jeweils \ref usart_send_char aufgerufen wird.
 * \param hex Die auszugebende Zahl (1 Byte, 0-255).
 * \param upper Gibt an, ob Groß- (1) oder Kleinbuchstaben (0) verwendet werden sollen.
 * \sa usart_send_char
 */
 
void usart_send_hexnumber(uint8_t hex, uint8_t upper)
{
	uint8_t tmp;
	
	tmp = (hex & 0xF0) >> 4;																		// Obere 4 Bits holen
	
	if(tmp <= 9)
		usart_send_char(tmp + 0x30);															// Zahl 0-9
	else
		usart_send_char(upper ? (tmp + 0x37) : (tmp + 0x57));			// Groß- oder Kleinbuchstabe A-F / a-f
	
	tmp = (hex & 0x0F);																					// Untere 4 Bits holen
	
	if(tmp <= 9)
		usart_send_char(tmp + 0x30);															// Zahl 0-9
	else
		usart_send_char(upper ? (tmp + 0x37) : (tmp + 0x57));			// Groß- oder Kleinbuchstabe A-F / a-f
}

void usart_send_big_hexnumberLF(uint8_t hexl,uint8_t hexh, uint8_t upper)
{
	usart_send_hexnumber(hexl,upper);
	usart_send_hexnumber(hexh,upper);
	usart_send_line("");
}


/*!
 * \ingroup usart
 * \brief Senden aller Zeichen im Sendebuffer
 * 
 * Diese Funktion sendet alle noch im Sendebuffer vorhandenen Zeichen direkt über die USART und wartet,
 * bis alle Zeichen gesendet wurden. Sie ist nützlich, um einen Bufferüberlauf zu verhindern, wenn
 * größere Datenmengen gesendet werden sollen.
 * \sa usart_send_char, usart_send_string
 */

void usart_wait_send(void)
{
  while(send_buf.read != send_buf.writ)			                  // Sind im Buffer noch zu sendende Zeichen?
	{
		if(USART_TX_READY)                                        // Wenn Sender bereit, nächstes Zeichen raus
    {
      USART_SEND_CHAR(send_buf.buf[send_buf.read]);
		
      if(++send_buf.read >= BUFLEN)								            // Buffer-Leseposition weiterziehen
        send_buf.read = 0;
    }
	}
}


/*!
 * \ingroup usart
 * \brief Lesen eines empfangenen Zeichens aus dem USART-Empfangsbuffer.
 * 
 * Diese Funktion liest ein Zeichen aus dem USART-Empfangsbuffer und zieht die Leseposition des Buffers
 * weiter. Ist kein neues Zeichen vorhanden, wird 0 zurückgegeben und die Leseposition bleibt unverändert.
 * \return Neues Zeichen aus dem Buffer, bzw. 0, falls kein neues Zeichen verfügbar. 
 * \sa usart_send_char, usart_scheduler
 */
 
char usart_get_char(void)
{
	char c;
	
	if(USART_NEW_CHAR())
	{
		c = recv_buf.buf[recv_buf.read];
		
		if(++recv_buf.read >= BUFLEN)
			recv_buf.read = 0;
	}
	else
		c = 0;
	
	return c;
}
