view main/packages/avr/device/uart.cpp @ 255:0703e0c8ce9d main

Correct the behavior for interrupt-driven UART on the 16M1/64M1 hardware. Remove spurios debugging port twiddles.
author Bob Cook <bob@bobcookdev.com>
date Mon, 07 Sep 2015 20:23:23 -0700
parents 84746acd2e0e
children
line wrap: on
line source

// ----------------------------------------------------------------------------------------
//
//  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) 2010-2015 Bob Cook
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
// ----------------------------------------------------------------------------------------

#include "uart.h"

#include <avr/interrupt.h>

#include "hwdefs.h"
#include "int_helpers.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_ATmega328P__ ) \
    && !defined( __AVR_ATmega128__ ) \
    && !defined( __AVR_ATmega16M1__ ) \
    && !defined( __AVR_ATmega64M1__ )
#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 ) && !defined( __AVR_ATmega16M1__ ) && !defined( __AVR_ATmega64M1__ )

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

ISR( USART_RXC_vect )

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

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_ATmega328P__ ) \
      || defined( __AVR_ATmega128__ )

    uint8_t data = UDR0;

#elif defined( __AVR_ATmega16M1__ ) || defined( __AVR_ATmega64M1__ )

    uint8_t data = LINDAT;

#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 ) && !defined( __AVR_ATmega16M1__ ) && !defined( __AVR_ATmega64M1__ )

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

ISR( USART_UDRE_vect )

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

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_ATmega328P__ ) \
      || 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_ATmega328P__ ) \
      || defined( __AVR_ATmega128__ )

        UCSR0B &= ~( 1 << UDRIE0 );

#endif

    }
}

#endif

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

#if defined( PRJ_UART0_USE_INTERRUPT_MODE ) && ( defined( __AVR_ATmega16M1__ ) || defined( __AVR_ATmega64M1__ ) )

ISR( LIN_TC_vect )
{
    if ( LINSIR & ( 1 << LRXOK ) )
    {
        if ( uart0_rx_buffer.is_full() )
        {
            // overflow error

            // TODO
        }
        else
        {
            uart0_rx_buffer.push( static_cast< uint8_t >( LINDAT ) );
        }
    }

    if ( LINSIR & ( 1 << LTXOK ) )
    {
        if ( ! uart0_tx_buffer.is_empty() )
        {
            LINDAT = uart0_tx_buffer.pop();
        }
        else
        {
            LINENIR &= ~( 1 << LENTXOK ); // tx buffer empty, disable the interrupt
        }
    }
}

#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__ ) \
      || defined( __AVR_ATmega328P__ )

    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 << UMSEL01 )                   // asynchronous mode
           | ( 0 << 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
           ;
        
    //-------------------------------------------------------------------------------------

#elif defined( __AVR_ATmega16M1__ ) || defined( __AVR_ATmega64M1__ )

    // reset the LIN/UART module

    LINCR = ( 1 << LSWRES );
    while ( LINSIR & ( 1 << LBUSY ) ) ;

    // switch to UART mode, then enable TX & RX separately

    LINCR = 0

          | ( 0 << LCONF1 ) | ( 0 << LCONF0 )   // 8-bit, no parity, 1 stop bit
          | ( 1 << LENA )                       // enable
          | ( 1 << LCMD2 )                      // uart enable
          ;

    LINCR |= ( 1 << LCMD1 ) | ( 1 << LCMD0 );   // tx & rx output enable

    LINENIR = 0

#if defined( PRJ_UART0_USE_INTERRUPT_MODE )

           | ( 0 << LENERR )                    // no error handler interrupts
           | ( 0 << LENTXOK )                   // no transmit complete interrupts
           | ( 1 << LENRXOK )                   // enable receive complete interrupts
           ;

#else // defined( PRJ_UART0_USE_POLLED_MODE )

           | ( 0 << LENERR )                    // no interrupts
           | ( 0 << LENTXOK )
           | ( 0 << LENRXOK )
           ;

#endif

#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_ATmega328P__ ) \
      || defined( __AVR_ATmega128__ )

    UBRR0H = highbyte;  // the caller is required to mask off the top bits
    UBRR0L = lowbyte;
    
#elif defined( __AVR_ATmega16M1__ ) || defined( __AVR_ATmega64M1__ )

    // the original computation (for non-M1 devices) uses a divsor of 16 rather than 32,
    // so divide the input value by 2

    LINBTR = 32;
    LINBRR = ( ( highbyte << 8 ) + ( lowbyte ) ) / 2;

#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_ATmega328P__ ) \
      || defined( __AVR_ATmega128__ )

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

#elif defined( __AVR_ATmega16M1__ ) || defined( __AVR_ATmega64M1__ )

    return ( LINSIR & ( 1 << LRXOK ) ) != 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_ATmega328P__ ) \
      || defined( __AVR_ATmega128__ )

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

#elif defined( __AVR_ATmega16M1__ ) || defined( __AVR_ATmega64M1__ )

    while ( ! ( LINSIR & ( 1 << LRXOK ) ) ) ;
    return LINDAT;

#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 ) && !defined( __AVR_ATmega16M1__ ) && !defined( __AVR_ATmega64M1__ )

    //--    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_ATmega328P__ ) \
      || defined( __AVR_ATmega128__ )

    UCSR0B |= ( 1 << UDRIE0 );

#endif // non-16M1 transmission

#elif defined( PRJ_UART0_USE_INTERRUPT_MODE ) && ( defined( __AVR_ATmega16M1__ ) || defined( __AVR_ATmega64M1__ ) )

    //--    If transmission is already in progress, all we need to do is add the
    //      new data to the TX buffer. Otherwise we have to write the data directly.

    if ( LINENIR & ( 1 << LTXOK ) )
    {
        uart0_tx_buffer.wait_and_push( data );
    }
    else
    {
        LINENIR |= ( 1 << LENTXOK );
        while ( ( LINSIR & ( 1 << LBUSY ) ) ) ;
        LINDAT = data;
    }

#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_ATmega328P__ ) \
      || defined( __AVR_ATmega128__ )

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

#elif defined( __AVR_ATmega16M1__ ) || defined( __AVR_ATmega64M1__ )

    while ( ( LINSIR & ( 1 << LBUSY ) ) ) ;
    LINDAT = data;
    //while ( LINSIR & ( 1 << LTXOK ) ) ;

#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 )

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

#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

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