// ----------------------------------------------------------------------------------------
//
//  avr/device/uart.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  This file provides the implementation for the UART library.
//
//  Copyright (C) 2008 Bob Cook
//
//      This program 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 2 of the License, or (at your
//      option) any later version.
//    
//      This program 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 this program; if not, write to the Free Software Foundation, Inc., 
//      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// ----------------------------------------------------------------------------------------


#include "uart.h"

#include <avr/interrupt.h>

#include "hwdefs.h"
#include "project_defs.h"

#include "packages/common/util/cbuffer.h"


// ----------------------------------------------------------------------------------------
//  Project-specific definitions
//

#if !defined( HW_HAS_UART0 )
#if defined( PRJ_UART0_USE_INTERRUPT_MODE ) || defined( PRJ_UART0_USE_POLLED_MODE )
#error Target HW has no UART0
#endif
#if defined( PRJ_UART0_EXTENDED_READ_WRITE )
#error Target HW has no UART0
#endif
#endif

#if !defined( HW_HAS_UART1 )
#if defined( PRJ_UART1_USE_INTERRUPT_MODE ) || defined( PRJ_UART1_USE_POLLED_MODE )
#error Target HW has no UART1
#endif
#if defined( PRJ_UART1_EXTENDED_READ_WRITE )
#error Target HW has no UART1
#endif
#endif

#if !defined( PRJ_UART0_BUFFER_SIZE )
#define PRJ_UART0_BUFFER_SIZE 16
#endif

#if ( PRJ_UART0_BUFFER_SIZE == 0 )
#error PRJ_UART0_BUFFER_SIZE cannot be zero!
#endif

#if !defined( PRJ_UART1_BUFFER_SIZE )
#define PRJ_UART1_BUFFER_SIZE 16
#endif

#if ( PRJ_UART1_BUFFER_SIZE == 0 )
#error PRJ_UART1_BUFFER_SIZE cannot be zero!
#endif

#if defined( PRJ_UART0_USE_INTERRUPT_MODE ) || defined( PRJ_UART1_USE_INTERRUPT_MODE )
#if ( ( 2 * PRJ_UART0_BUFFER_SIZE + 2 * PRJ_UART1_BUFFER_SIZE ) > HW_RAM_SIZE )
#error Target HW has insufficient RAM for specified buffer sizes
#endif
#endif

#if !defined( PRJ_UART0_USE_INTERRUPT_MODE )
#undef PRJ_UART0_BUFFER_SIZE
#endif

#if !defined( PRJ_UART1_USE_INTERRUPT_MODE )
#undef PRJ_UART1_BUFFER_SIZE
#endif


// ----------------------------------------------------------------------------------------
//  Note: none of the hardware-specific checks below will error on unrecognized chips
//  so make that check here.
//

#if !defined( __AVR_ATmega8__ ) \
    && !defined( __AVR_ATmega16__ ) \
    && !defined( __AVR_ATmega88__ ) \
    && !defined( __AVR_ATmega168__ ) \
    && !defined( __AVR_ATmega128__ )
#error Unsupported Hardware
#endif


// ----------------------------------------------------------------------------------------

#if defined( PRJ_UART0_USE_INTERRUPT_MODE ) || defined( PRJ_UART0_USE_POLLED_MODE )


// ----------------------------------------------------------------------------------------

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

static cbytebuffer< PRJ_UART0_BUFFER_SIZE > uart0_rx_buffer;
static cbytebuffer< PRJ_UART0_BUFFER_SIZE > uart0_tx_buffer;

#endif


// ----------------------------------------------------------------------------------------

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

ISR( USART_RXC_vect )

#elif defined( __AVR_ATmega88__ ) || defined( __AVR_ATmega168__ )

ISR( USART_RX_vect )

#elif defined( __AVR_ATmega128__ )

ISR( USART0_RX_vect )

#endif
{

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

    uint8_t data = UDR;

#elif defined( __AVR_ATmega88__ ) \
      || defined( __AVR_ATmega168__ ) \
      || defined( __AVR_ATmega128__ )

    uint8_t data = UDR0;

#endif

    // TODO: error checking in the USRC register, after reading UDR

    if ( uart0_rx_buffer.is_full() )
    {
        // overflow error

        // TODO
    }
    else
    {
        uart0_rx_buffer.push( data );
    }
}

#endif


// ----------------------------------------------------------------------------------------

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

ISR( USART_UDRE_vect )

#elif defined( __AVR_ATmega88__ ) || defined( __AVR_ATmega168__ )

ISR( USART_UDRE_vect )

#elif defined( __AVR_ATmega128__ )

ISR( USART0_UDRE_vect )

#endif
{
    if ( ! uart0_tx_buffer.is_empty() )
    {

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

        UDR = uart0_tx_buffer.pop();

#elif defined( __AVR_ATmega88__ ) \
      || defined( __AVR_ATmega168__ ) \
      || defined( __AVR_ATmega128__ )

        UDR0 = uart0_tx_buffer.pop();

#endif

    }
    else
    {
        // tx buffer empty, disable the interrupt

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

        UCSRB &= ~( 1 << UDRIE );

#elif defined( __AVR_ATmega88__ ) \
      || defined( __AVR_ATmega168__ ) \
      || defined( __AVR_ATmega128__ )

        UCSR0B &= ~( 1 << UDRIE0 );

#endif

    }
}

#endif


// ----------------------------------------------------------------------------------------

void uart0_init()
{

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

    UCSRB = 0

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

          | ( 1 << RXCIE )                      // enable receive complete interrupts
          | ( 0 << TXCIE )                      // no transmit complete interrupts
          | ( 0 << UDRIE )                      // no data register empty interrupts

#else // defined( PRJ_UART0_USE_POLLED_MODE )

          | ( 0 << RXCIE )                      // no interrupts
          | ( 0 << TXCIE )
          | ( 0 << UDRIE )

#endif
        
          | ( 1 << RXEN  )                      // receiver enabled
          | ( 1 << TXEN  )                      // transmitter enabled
          | ( 0 << UCSZ2 )                      // eight data bits
          | ( 0 << RXB8  )                      // rx ninth-bit (ro)
          | ( 0 << TXB8  )                      // tx ninth-bit
          ;
    
    UCSRC = 0

          | ( 1 << URSEL )                      // writing the UCSCR register
          | ( 0 << UMSEL )                      // asynchronous mode
          | ( 0 << UPM1  )                      // parity mode disabled
          | ( 0 << UPM0  )
          | ( 0 << USBS  )                      // one stop bit
          | ( 1 << UCSZ1 )                      // eight data bits
          | ( 1 << UCSZ0 )
          | ( 0 << UCPOL )                      // unused in async mode
          ;

    //-------------------------------------------------------------------------------------

#elif defined( __AVR_ATmega88__ ) || defined( __AVR_ATmega168__ )

    UCSR0B = 0

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

           | ( 1 << RXCIE0 )                    // enable receive complete interrupts
           | ( 0 << TXCIE0 )                    // no transmit complete interrupts
           | ( 0 << UDRIE0 )                    // no data register empty interrupts

#else // defined( PRJ_UART0_USE_POLLED_MODE )

           | ( 0 << RXCIE0 )                    // no interrupts
           | ( 0 << TXCIE0 )
           | ( 0 << UDRIE0 )

#endif

           | ( 1 << RXEN0  )                    // receiver enabled
           | ( 1 << TXEN0  )                    // transmitter enabled
           | ( 0 << UCSZ02 )                    // eight data bits
           | ( 0 << RXB80  )                    // rx ninth-bit (ro)
           | ( 0 << TXB80  )                    // tx ninth-bit
           ;
        
    UCSR0C = 0
        
           | ( 1 << UMSEL01 )                   // asynchronous mode
           | ( 1 << UMSEL00 )
           | ( 0 << UPM01  )                    // parity mode disabled
           | ( 0 << UPM00  )
           | ( 0 << USBS0  )                    // one stop bit
           | ( 1 << UCSZ01 )                    // eight data bits
           | ( 1 << UCSZ00 )
           | ( 0 << UCPOL0 )                    // unused in async mode
           ;
        

    //-------------------------------------------------------------------------------------

#elif defined( __AVR_ATmega128__ )

    UCSR0A = 0;

    UCSR0B = 0

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

           | ( 1 << RXCIE0 )                    // enable receive complete interrupts
           | ( 0 << TXCIE0 )                    // no transmit complete interrupts
           | ( 0 << UDRIE0 )                    // no data register empty interrupts

#else // defined( PRJ_UART0_USE_POLLED_MODE )

           | ( 0 << RXCIE0 )                    // no interrupts
           | ( 0 << TXCIE0 )
           | ( 0 << UDRIE0 )

#endif

           | ( 1 << RXEN0  )                    // receiver enabled
           | ( 1 << TXEN0  )                    // transmitter enabled
           | ( 0 << UCSZ02 )                    // eight data bits
           | ( 0 << RXB80  )                    // rx ninth-bit (ro)
           | ( 0 << TXB80  )                    // tx ninth-bit
           ;
        
    UCSR0C = 0
        
           | ( 0 << UMSEL0 )                    // asynchronous mode
           | ( 0 << UPM01  )                    // parity mode disabled
           | ( 0 << UPM00  )
           | ( 0 << USBS0  )                    // one stop bit
           | ( 1 << UCSZ01 )                    // eight data bits
           | ( 1 << UCSZ00 )
           | ( 0 << UCPOL0 )                    // unused in async mode
           ;
        

#endif  // #if defined( ...processor selection... )

#if defined( PRJ_UART0_INITIAL_BAUDRATE )

    uart0_set_baudrate( PRJ_UART0_INITIAL_BAUDRATE );

#endif

}


// ----------------------------------------------------------------------------------------

void uart0_set_baudregisters( uint8_t highbyte, uint8_t lowbyte )
{

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

    UBRRH = highbyte;   // the caller is required to mask off the top bits
    UBRRL = lowbyte;
    
    //-------------------------------------------------------------------------------------

#elif defined( __AVR_ATmega88__ ) \
      || defined( __AVR_ATmega168__ ) \
      || defined( __AVR_ATmega128__ )

    UBRR0H = highbyte;  // the caller is required to mask off the top bits
    UBRR0L = lowbyte;
    
#endif

}


// ----------------------------------------------------------------------------------------

bool uart0_is_char_available()
{

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

    return ! uart0_rx_buffer.is_empty();

#else   // defined( PRJ_UART0_USE_POLLED_MODE )

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

        return ( UCSRA & ( 1 << RXC ) ) != 0;


#elif defined( __AVR_ATmega88__ ) \
      || defined( __AVR_ATmega168__ ) \
      || defined( __AVR_ATmega128__ )

        return ( UCSR0A & ( 1 << RXC0 ) ) != 0;

#endif

#endif  // #if defined( PRJ_UART0_USE_INTERRUPT_MODE )

}


// ----------------------------------------------------------------------------------------
    
uint8_t uart0_read()
{

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

    return uart0_rx_buffer.wait_and_pop();
    
#else   // defined( PRJ_UART0_USE_POLLED_MODE )

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

    while ( ! ( UCSRA & ( 1 << RXC ) ) ) ;
    return UDR;

#elif defined( __AVR_ATmega88__ ) \
      || defined( __AVR_ATmega168__ ) \
      || defined( __AVR_ATmega128__ )

    while ( ! ( UCSR0A & ( 1 << RXC0 ) ) ) ;
    return UDR0;

#endif

#endif  // #if defined( PRJ_UART0_USE_INTERRUPT_MODE )

}


// ----------------------------------------------------------------------------------------
    
#if defined( PRJ_UART0_EXTENDED_READ_WRITE )

void uart0_read( uint8_t* data, uint8_t len )
{
    while ( len > 0 )
    {
        *data++ = uart0_read();
        --len;
    }
}

#endif  // PRJ_UART0_EXTENDED_READ_WRITE


// ----------------------------------------------------------------------------------------
    
void uart0_write( uint8_t data )
{

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

    //--    Wait for space in the buffer.

    uart0_tx_buffer.wait_and_push( data );

    //--    Enable UDRE interrupt, its handler will stuff bytes to the UART h/w.

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

    UCSRB |= ( 1 << UDRIE );

#elif defined( __AVR_ATmega88__ ) \
      || defined( __AVR_ATmega168__ ) \
      || defined( __AVR_ATmega128__ )

    UCSR0B |= ( 1 << UDRIE0 );

#endif

#else   // defined( PRJ_UART0_USE_POLLED_MODE )

#if defined( __AVR_ATmega8__ ) || defined( __AVR_ATmega16__ )

    while ( ! ( UCSRA & ( 1 << UDRE ) ) ) ;
    UDR = data;

#elif defined( __AVR_ATmega88__ ) \
      || defined( __AVR_ATmega168__ ) \
      || defined( __AVR_ATmega128__ )

    while ( ! ( UCSR0A & ( 1 << UDRE0 ) ) ) ;
    UDR0 = data;

#endif

#endif  // #if defined( PRJ_UART0_USE_INTERRUPT_MODE )

}


// ----------------------------------------------------------------------------------------
    
#if defined( PRJ_UART0_EXTENDED_READ_WRITE )

void uart0_write( const uint8_t* data, uint8_t len )
{
    while ( len > 0 )
    {
        uart0_write( *data++ );
        --len;
    }
}

void uart0_write( const char* str )
{
    while ( *str != '\0' )
    {
        uart0_write( static_cast< uint8_t >( *str++ ) );
    }
}

#endif  // PRJ_UART0_EXTENDED_READ_WRITE


// ----------------------------------------------------------------------------------------
//  These functions use pgm_read_byte. Two things to know about pgm_read_byte:
//
//  1. it's a macro so don't use inline post/pre-increment
//  2. it's only able to access the lower 64K; see <avr/pgmspace.h> for more info
//

#if defined( PRJ_UART0_EXTENDED_READ_WRITE )

void uart0_write_pgm( const prog_uint8_t* data, uint8_t len )
{

    while ( len > 0 )
    {
        uart0_write( pgm_read_byte( data ) );
        data++;
        --len;
    }
}

void uart0_write_pgm( const prog_char* str )
{
    char c;

    while ( ( c = static_cast< char >( pgm_read_byte( str ) ) ) != '\0' )
    {
        uart0_write( c );
        str++;
    }
}

#endif  // PRJ_UART0_EXTENDED_READ_WRITE


// ----------------------------------------------------------------------------------------

#endif  // PRJ_UART0_USE_INTERRUPT_MODE || PRJ_UART0_USE_POLLED_MODE


// ----------------------------------------------------------------------------------------

#if defined( PRJ_UART1_USE_INTERRUPT_MODE ) || defined( PRJ_UART1_USE_POLLED_MODE )


// TODO ...
// ----------------------------------------------------------------------------------------

#if defined( PRJ_UART1_USE_INTERRUPT_MODE )

static cbytebuffer< PRJ_UART1_BUFFER_SIZE > uart1_rx_buffer;
static cbytebuffer< PRJ_UART1_BUFFER_SIZE > uart1_tx_buffer;

#endif


// ----------------------------------------------------------------------------------------

#if defined( PRJ_UART1_USE_INTERRUPT_MODE )

#if defined( __AVR_ATmega128__ )

ISR( USART1_RX_vect )

#endif
{

#if defined( __AVR_ATmega128__ )

    uint8_t data = UDR1;

#endif

    // TODO: error checking in the USRC register, after reading UDR

    if ( uart1_rx_buffer.is_full() )
    {
        // overflow error

        // TODO
    }
    else
    {
        uart1_rx_buffer.push( data );
    }
}

#endif


// ----------------------------------------------------------------------------------------

#if defined( PRJ_UART1_USE_INTERRUPT_MODE )

#if defined( __AVR_ATmega128__ )

ISR( USART1_UDRE_vect )

#endif

{
    if ( ! uart1_tx_buffer.is_empty() )
    {

#if defined( __AVR_ATmega128__ )

        UDR1 = uart1_tx_buffer.pop();

#endif

    }
    else
    {
        // tx buffer empty, disable the interrupt

#if defined( __AVR_ATmega128__ )

        UCSR1B &= ~( 1 << UDRIE1 );

#endif

    }
}

#endif


// ----------------------------------------------------------------------------------------

void uart1_init()
{

#if defined( __AVR_ATmega128__ )
    
    UCSR1A = 0;

    UCSR1B = 0

#if defined( PRJ_UART1_USE_INTERRUPT_MODE )

           | ( 1 << RXCIE1 )                    // enable receive complete interrupts
           | ( 0 << TXCIE1 )                    // no transmit complete interrupts
           | ( 0 << UDRIE1 )                    // no data register empty interrupts

#else // defined( PRJ_UART1_USE_POLLED_MODE )

           | ( 0 << RXCIE1 )                    // no interrupts
           | ( 0 << TXCIE1 )
           | ( 0 << UDRIE1 )

#endif

           | ( 1 << RXEN1  )                    // receiver enabled
           | ( 1 << TXEN1  )                    // transmitter enabled
           | ( 0 << UCSZ12 )                    // eight data bits
           | ( 0 << RXB81  )                    // rx ninth-bit (ro)
           | ( 0 << TXB81  )                    // tx ninth-bit
           ;
        
    UCSR0C = 0
        
           | ( 0 << UMSEL1 )                    // asynchronous mode
           | ( 0 << UPM11  )                    // parity mode disabled
           | ( 0 << UPM10  )
           | ( 0 << USBS1  )                    // one stop bit
           | ( 1 << UCSZ11 )                    // eight data bits
           | ( 1 << UCSZ10 )
           | ( 0 << UCPOL1 )                    // unused in async mode
           ;
        

#endif  // #if defined( ...processor selection... )

#if defined( PRJ_UART1_INITIAL_BAUDRATE )

    uart1_set_baudrate( PRJ_UART1_INITIAL_BAUDRATE );

#endif

}


// ----------------------------------------------------------------------------------------

void uart1_set_baudregisters( uint8_t highbyte, uint8_t lowbyte )
{

#if defined( __AVR_ATmega128__ )

    UBRR1H = highbyte;  // the caller is required to mask off the top bits
    UBRR1L = lowbyte;
    
#endif

}


// ----------------------------------------------------------------------------------------

bool uart1_is_char_available()
{

#if defined( PRJ_UART1_USE_INTERRUPT_MODE )

    return ! uart1_rx_buffer.is_empty();

#else   // defined( PRJ_UART1_USE_POLLED_MODE )

#if defined( __AVR_ATmega128__ )

        return ( UCSR1A & ( 1 << RXC1 ) ) != 0;

#endif

#endif  // #if defined( PRJ_UART1_USE_INTERRUPT_MODE )

}


// ----------------------------------------------------------------------------------------
    
uint8_t uart1_read()
{

#if defined( PRJ_UART1_USE_INTERRUPT_MODE )

    return uart1_rx_buffer.wait_and_pop();
    
#else   // defined( PRJ_UART1_USE_POLLED_MODE )

#if defined( __AVR_ATmega128__ )

    while ( ! ( UCSR1A & ( 1 << RXC1 ) ) ) ;
    return UDR1;

#endif

#endif  // #if defined( PRJ_UART1_USE_INTERRUPT_MODE )

}


// ----------------------------------------------------------------------------------------
    
#if defined( PRJ_UART1_EXTENDED_READ_WRITE )

void uart1_read( uint8_t* data, uint8_t len )
{
    while ( len > 0 )
    {
        *data++ = uart1_read();
        --len;
    }
}

#endif  // PRJ_UART1_EXTENDED_READ_WRITE


// ----------------------------------------------------------------------------------------
    
void uart1_write( uint8_t data )
{

#if defined( PRJ_UART1_USE_INTERRUPT_MODE )

    //--    Wait for space in the buffer.

    uart1_tx_buffer.wait_and_push( data );

    //--    Enable UDRE interrupt, its handler will stuff bytes to the UART h/w.

#if defined( __AVR_ATmega128__ )

    UCSR1B |= ( 1 << UDRIE1 );

#endif

#else   // defined( PRJ_UART1_USE_POLLED_MODE )

#if defined( __AVR_ATmega128__ )

    while ( ! ( UCSR1A & ( 1 << UDRE1 ) ) ) ;
    UDR1 = data;

#endif

#endif  // #if defined( PRJ_UART1_USE_INTERRUPT_MODE )

}


// ----------------------------------------------------------------------------------------
    
#if defined( PRJ_UART1_EXTENDED_READ_WRITE )

void uart1_write( const uint8_t* data, uint8_t len )
{
    while ( len > 0 )
    {
        uart1_write( *data++ );
        --len;
    }
}

void uart1_write( const char* str )
{
    while ( *str != '\0' )
    {
        uart1_write( static_cast< uint8_t >( *str++ ) );
    }
}

#endif  // PRJ_UART1_EXTENDED_READ_WRITE


// ----------------------------------------------------------------------------------------
//  These functions use pgm_read_byte. Two things to know about pgm_read_byte:
//
//  1. it's a macro so don't use inline post/pre-increment
//  2. it's only able to access the lower 64K; see <avr/pgmspace.h> for more info
//

#if defined( PRJ_UART1_EXTENDED_READ_WRITE )

void uart1_write_pgm( const prog_uint8_t* data, uint8_t len )
{

    while ( len > 0 )
    {
        uart1_write( pgm_read_byte( data ) );
        data++;
        --len;
    }
}

void uart1_write_pgm( const prog_char* str )
{
    char c;

    while ( ( c = static_cast< char >( pgm_read_byte( str ) ) ) != '\0' )
    {
        uart1_write( c );
        str++;
    }
}

#endif  // PRJ_UART1_EXTENDED_READ_WRITE


// ----------------------------------------------------------------------------------------

#endif  // PRJ_UART1_USE_INTERRUPT_MODE || PRJ_UART1_USE_POLLED_MODE


// ----------------------------------------------------------------------------------------
//  end of uart.cpp
// ----------------------------------------------------------------------------------------

