/*****************************************************************************
** avr_uart.c    Interupt gesteuert, keine loops 
**
** Transfer startet wenn die routine uart_putstring aufgerufen wird.
** Wenn uart_putc verwendet wird, muss der Transfer mit start_tx()
** gestartet werden, da der AVR UART sofort nach dem ersten byte das
** 2. byte anfordert, deshalb erst buffer fuellen und dann starten.
**
** 01.07.2009 li Schoenheits Korrektur  
** 08.07.2009 li korrektur
** 09.07.2009 li Fehler trace
** 10.07.2009 li uart_putstring korrigiert, 0x00 unsinnigerweise gesendet
** 13.07.2009 li UART_BUFSIZE_HW muss 2er Potenz zwischen 16 und 256 sein 
** 16.03.2010 li Anpassung an Atmega168  
** 24.03.2010 li Benutze asynchon, synchron erfordert clock !!!
**               Fehlwert 8N1 8Bit, kein Parity, 1 Stop Bit
**               Die Datenübertragung muss ueber ein Prüfzeichen gesichert werden 
** 03.10.2012 li fehlerhafte Zeichen ausgabe geändert
*/

#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <avr/io.h>
#include "avr_uart.h"
                                 // FIFO Struct 
typedef struct
{
   unsigned char *buffer;              // buffer Adresse
   unsigned char volatile count;       // Anzahl Zeichen im Puffer
   unsigned char volatile read;        // Lese position
   unsigned char volatile write;       // Schreib position
} S_FIFO;

/*
  #define BAUDRATE 600UL         // Definition als unsigned long, sonst gibt es Fehler in der Berechnung
                                 // jetzt in der makefile definiert 
*/

/*------------------------------------------------
**  FIFO UART_BUFSIZE_HW check, 2**n Werte  
*/
#undef UARTIG 
#if UART_BUFSIZE_HW == 8 
#define UARTIG
#endif
#if UART_BUFSIZE_HW == 16 
#define UARTIG
#endif
#if UART_BUFSIZE_HW == 32 
#define UARTIG
#endif
#if UART_BUFSIZE_HW == 64
#define UARTIG
#endif
#if UART_BUFSIZE_HW == 128 
#define UARTIG
#endif
#if UART_BUFSIZE_HW == 256 
#define UARTIG
#endif

#ifdef UARTIG               // UART_BUFSIZE_HW ok ?          #endif siehe Ende 

S_UART us;                  // Stuktur der flags 

S_FIFO rx_fifo;             // Stuktur FIFO empfangene Zeichen 
S_FIFO tx_fifo;             // Stuktur FIFO zu sendende Zeichen

unsigned char rxbuffer[ UART_BUFSIZE_HW ];
unsigned char txbuffer[ UART_BUFSIZE_HW ];

static void           fifo_init (S_FIFO*, unsigned char* buf, const unsigned char size);
static unsigned char  fifo_put(S_FIFO*, const unsigned char data);
static int            fifo_get(S_FIFO *f);



/*****************************************************************************
** Empfangene Zeichen im Eingangs-buffer speichern  PE entfällt
** 3.10.2012 li fehlerhafte Zeichen ausgabe geändert
*/
#if defined (__AVR_ATmega644__)
ISR ( USART0_RX_vect )
{
   unsigned char status, data;
   //unsigned char data2;
   
   status = UCSR0A;                 
   data = UDR0;
   
   if ( status & ( (1<<FE0) + (1<<DOR0 ) ))   // Error ??
#else
#if defined (__AVR_ATmega168__)
ISR ( USART_RX_vect )
{
   unsigned char status, data;
   //unsigned char data2;
   
   status = UCSR0A;                 
   data = UDR0;
   
   if ( status & ( (1<<FE0) + (1<<DOR0 ) ))   // Error ??
#else
ISR ( USART_RXC_vect )
{
   unsigned char status, data;
   //unsigned char data2;
   
   status = UCSRA;
   data = UDR;
 
   if ( status & ( (1<<FE) + (1<<DOR ) ))   // Error ??
#endif   
#endif
   { 
/**********   
      fifo_put( &rx_fifo, '#' );                      // zeichen hexadezimal
      data2 = data >> 4;
      if ( data2 > 0x09 )
         data2 += 0x06;
      fifo_put( &rx_fifo, data2 + 0x30 );             // hex  
      data2 = data & 0x0F;
      if ( data2 > 0x09 )
         data2 += 0x06;
      fifo_put( &rx_fifo, data2 + 0x30 );             // hex
      
      us.rx_error = 1;                                // error flag setzen          
   }
   else
   {   
      fifo_put( &rx_fifo, data );
   }
**************/
      us.rx_error = 1;                                // error flag setzen
      data = 0xFE;      
   }
   fifo_put( &rx_fifo, data );
}



/*****************************************************************************
** tx leitung frei
*/
#if defined (__AVR_ATmega644__)
ISR ( USART0_TX_vect )
#else 
#if defined (__AVR_ATmega168__) 
ISR ( USART_TX_vect )
#else
ISR ( USART_TXC_vect )
#endif
#endif
{     
   us.tx_busy = 0;           // echtes Ende der Uebertragung
}



/*****************************************************************************
** Ein Zeichen senden
** Ist das Zeichen fertig ausgegeben, wird ein neuer SIG_UART_DATA-IRQ getriggert
** Ist die FIFO leer, deaktiviert die ISR ihren eigenen IRQ.
*/
#if defined (__AVR_ATmega644__) 
ISR ( USART0_UDRE_vect )
{
   int data;

   data = fifo_get( &tx_fifo );
   if ( data == -1 )
   {
      UCSR0B &= ~(1 << UDRIE0);    // Fifo leer, Int ausschalten
   }
   else
   {
      UDR0 = (unsigned char)data;
   }   
}
#else
#if defined (__AVR_ATmega168__)
ISR ( USART_UDRE_vect )
{
   int data;

   data = fifo_get( &tx_fifo );
   if ( data == -1 )
   {
      UCSR0B &= ~(1 << UDRIE0);    // Fifo leer, Int ausschalten
   }
   else
   {
      UDR0 = (unsigned char)data;
   }   
}
#else
ISR ( USART_UDRE_vect )
{
   int data;

   data = fifo_get( &tx_fifo );
   if ( data == -1 )
   {
      UCSRB &= ~(1 << UDRIE);    // Fifo leer, Int ausschalten
   }
   else
   {
      UDR = (unsigned char)data;
   }   
}
#endif
#endif


/*****************************************************************************
**  Start Transfer
*/
void start_tx( void )
{
   us.tx_busy = 1;
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega644__) 
   UCSR0B |= (1 << UDRIE0);      // DATA Register Empty Int einschalten
#else   
   UCSRB |= (1 << UDRIE);        // DATA Register Empty Int einschalten      
#endif
}



/*****************************************************************************
**  Put Char
*/
int uart_putc( const unsigned char c)
{
   if ( us.tx_busy )
     return -2;
   return fifo_put( &tx_fifo, c );   // Zeichen in den sende buffer
}



/*****************************************************************************
** Wenn nichts im read Buffer, return -1
*/
int uart_getc( void )
{
   return fifo_get( &rx_fifo );
}



/*****************************************************************************
** Einen 0-terminierten String uebertragen.
** rc =  0  String noch nicht komplett im Buffer
** rc =  1  Transfer gestartet
** rc = -2  Transfer nicht frei 
*/
int usart_write_str( char *s )
{
   int rc;
   
   if ( us.tx_busy )          // transfer frei ?
      return -2;              // --> nein
   
   do
   {
      rc = uart_putc (*s);
      if ( rc == 0 )         // Puffer voll ??
        break;
      s++;
   } while( *s );
   
   if ( rc == 1 )            // Wenn alles im Puffer, transfer starten
      start_tx();
   
   return rc;
}



#ifdef USART_WRITE_P
/*****************************************************************************
** Varibaler String, like printf.
** rc =  0  String noch nicht komplett im Buffer
** rc =  1  Transfer gestartet
** rc = -2  Transfer nicht frei 
*/
void usart_write_P(const char *Buffer,...)
{
   va_list ap;
   va_start (ap, Buffer);
      
   for(;;)           // wait for free
   {
      if ( us.tx_busy == 0)
         break;
   }    

   int format_flag;
   char str_buffer[10];
   char str_null_buffer[10];
   char move = 0;
   char Base = 0;
   int tmp = 0;
   char by;
   char *ptr;
   char *s;
   
   if (us.tx_busy )          // transfer frei ?
   {
     _delay_ms(2);
   }
   if (us.tx_busy )          // transfer frei ?
   {
     _delay_ms(4);
   }   
   if (us.tx_busy )          // transfer frei ?
   {
     _delay_ms(8);
   }   
   if (us.tx_busy )          // transfer frei ?
   {
     _delay_ms(16);
   }   
   
                                         //Ausgabe der Zeichen
   for(;;)
   {
      by = pgm_read_byte(Buffer++);
      if( by == 0 )
         break;                         // end of format string

      if (by == '%')
      {
         by = pgm_read_byte(Buffer++);

         if ( isdigit(by) > 0)
         {
            str_null_buffer[0] = by;
            str_null_buffer[1] = '\0';
            move = atoi(str_null_buffer);
            by = pgm_read_byte(Buffer++);
         }

         switch (by)
         {
           case 's':                            // string
            ptr = va_arg(ap,char *);
            while(*ptr)
            {
               uart_putc(*ptr++);
            }
            break;

           case 'b':                            // binary
            Base = 2;
            goto ConversionLoop;

           case 'c':                            // Int to char
            format_flag = va_arg(ap,int);
            uart_putc (format_flag++);
            break;

           case 'i':                            // integer
            Base = 10;
            goto ConversionLoop;

           case 'o':                            // oktal
            Base = 8;
            goto ConversionLoop;

           case 'x':                            // hex
            Base = 16;
ConversionLoop:
            itoa(va_arg(ap,int),str_buffer,Base);
            int b=0;
            while (str_buffer[b++] != 0){};
            b--;
            if ( b < move)
            {
               move -=b;
               for ( tmp = 0; tmp < move; tmp++)
               {
                  str_null_buffer[tmp] = '0';
               }
               //tmp ++;
               str_null_buffer[tmp] = '\0';
               strcat(str_null_buffer,str_buffer);
               strcpy(str_buffer,str_null_buffer);
            }
            //usart_write_str(str_buffer);
            s = str_buffer;
            do
            {
               uart_putc (*s);
               s++;
            } while( *s );
            move =0;
            break;
         }

      }
      else
      {
         uart_putc( by );
      }
   }
   va_end(ap);
   start_tx();
}    

#endif


/*****************************************************************************
** uart init, Data mode asynchron, 8Bit, kein Parity, 1 Stop Bit 
*/
void uart_init( void )
{
   unsigned char dummy;   
   unsigned int  ubrr = (unsigned int) ((uint32_t) F_CPU/(16*BAUDRATE) - 1);
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega644__)
   
   UBRR0H = (unsigned char) (ubrr>>8);   // baudrate einstellen
   UBRR0L = (unsigned char) (ubrr);

                   // rx und tx enable
                   // rx complete int enable 
                   // tx complete int enable                    
                   // in start_tx() UCSRB |= (1 << UDRIE); // DATA Register Empty Int einschalten

   UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0) | (1 << TXCIE0);
   
                   // Data mode asynchron, 8Bit, no Parity, 1 Stop Bit ist der Fehlwert      
   
   while (UCSR0A & (1 << RXC0)) dummy = UDR0 ;  // dummy read = clear
                  
   UCSR0A &= ~(1 << TXC0);                     // reset Transmit Complete-Flag                                   
#else
      
   UBRRH = (unsigned char) (ubrr>>8);   // baudrate einstellen
   UBRRL = (unsigned char) (ubrr);

                   // rx und tx enable
                   // rx complete int enable 
                   // tx complete int enable                    
                   // in start_tx() UCSRB |= (1 << UDRIE); // DATA Register Empty Int einschalten

   UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE) | (1 << TXCIE);
   
                   // Data mode asynchron, 8Bit, no Parity, 1 Stop Bit ist der Fehlwert                       

   while (UCSRA & (1 << RXC)) dummy = UDR ;  // dummy read = clear
                  
   UCSRA &= ~(1 << TXC);                     // reset Transmit Complete-Flag
#endif   
                                             // FIFOs initialisieren
   fifo_init( &rx_fifo, rxbuffer, UART_BUFSIZE_HW );
   fifo_init( &tx_fifo, txbuffer, UART_BUFSIZE_HW );
   us.tx_busy = 0;
   us.rx_error = 0;
}



/*****************************************************************************
** fifo struct initialisieren
*/
static void fifo_init (S_FIFO *f, unsigned char *buffer, const unsigned char size)
{
   f->count = 0;
   f->read  = 0;
   f->write = 0;
   f->buffer = buffer;
}



/*****************************************************************************
** zeichen in den fifo buffer ablegen
*/
static unsigned char fifo_put( S_FIFO *f, const unsigned char data )
{
   if ( f->count >= UART_BUFSIZE_HW )
      return 0;
   
   f->write &= UART_BUFSIZE_HW-1;   // wrap around
   f->buffer[ f->write++ ] = data;
   f->count++;   
   return 1;
}



/*****************************************************************************
** zeichen aus dem fifo buffer holen
*/
static int fifo_get(S_FIFO *f)
{
   unsigned char data;

   if ( f->count == 0 )
      return -1 ;
      
   f->read &= UART_BUFSIZE_HW-1;   // wrap around 
   data = f->buffer[ f->read++ ];
   f->count--;
   
   return data;
}

#endif             // siehe Anfang
