/*!
 * \brief
 *
 * \author Jan Oleksiewicz <jnk0le@hotmail.com>
 * \license SPDX-License-Identifier: MIT
 */

#ifndef _USART_H_
#define _USART_H_

#include "usart_config.h"
#include <avr/io.h> // for inline func

#ifndef F_CPU
  #warning F_CPU is undefined, USART may not work correctly without this
#endif

#if __AVR_ARCH__ == 103
  // avrxmega3 (attiny/mega-0) async BAUD = 64*F_CPU/(S*baud), S=16 normal / 8
  // double. Compute rounded without truncating F_CPU/x first (the naive
  // (F_CPU/x)*S loses precision for non-integer ratios, e.g. 20MHz/1.2Mbps).
  #define BAUD_CALC(x)      (uint16_t)((64UL * (F_CPU) + 8UL * (x)) / (16UL * (x)))
  #define BAUD_CALC_FAST(x) BAUD_CALC(x)
  // macro calculating BAUD value for double speed
  #define DOUBLE_BAUD_CALC(x)                                                   \
    (uint16_t)((64UL * (F_CPU) + 4UL * (x)) / (8UL * (x)))
#else
  // macro calculating precise UBRR value
  #define BAUD_CALC(x)        ((F_CPU + (x) * 8UL) / (16UL * (x)) - 1UL)
  // for faster real time calculations ? // not recommended
  #define BAUD_CALC_FAST(x)   ((F_CPU) / ((x) * 16UL) - 1)
  // macro calculating UBRR value for double speed
  #define DOUBLE_BAUD_CALC(x) ((F_CPU + (x) * 4UL) / (8UL * (x)) - 1UL)
#endif

#if !defined(__OPTIMIZE__) && !defined(USART_NO_ABI_BREAKING_PREMATURES)
  #warning Compiler optimizations disabled; functions from usart.h might not work as designed
#endif

#ifdef DEBUG
  #define USART_NO_ABI_BREAKING_PREMATURES
#endif

#ifndef __AVR_ARCH__ // compiler fault ?
  #define USART_NO_ABI_BREAKING_PREMATURES
#endif

// avrxmega3 (tinyAVR-1/megaAVR-0, __AVR_ARCH__==103) is an AVRxt core where RETI
// does NOT restore SREG.I outside an ISR. The "ABI-breaking premature" trick
// re-enables interrupts by ending non-ISR helpers (e.g. uart0_putc) with `reti`
// after a `cli`; on AVRxt that leaves interrupts disabled forever, so the TX
// (UDRE) interrupt never fires. Force the safe ATOMIC_BLOCK (cli + SREG-restore)
// path on this architecture.
#if __AVR_ARCH__ == 103
  #define USART_NO_ABI_BREAKING_PREMATURES
#endif

#ifndef RX_BUFFER_SIZE
  #define RX_BUFFER_SIZE 32 // Size of the ring buffers, must be power of 2
#endif

#ifndef TX_BUFFER_SIZE
  #define TX_BUFFER_SIZE 32 // Size of the ring buffers, must be power of 2
#endif

#ifndef TX0_BUFFER_SIZE
  #define TX0_BUFFER_SIZE TX_BUFFER_SIZE
#endif
#ifndef RX0_BUFFER_SIZE
  #define RX0_BUFFER_SIZE RX_BUFFER_SIZE
#endif
#ifndef TX1_BUFFER_SIZE
  #define TX1_BUFFER_SIZE TX_BUFFER_SIZE
#endif
#ifndef RX1_BUFFER_SIZE
  #define RX1_BUFFER_SIZE RX_BUFFER_SIZE
#endif
#ifndef TX2_BUFFER_SIZE
  #define TX2_BUFFER_SIZE TX_BUFFER_SIZE
#endif
#ifndef RX2_BUFFER_SIZE
  #define RX2_BUFFER_SIZE RX_BUFFER_SIZE
#endif
#ifndef TX3_BUFFER_SIZE
  #define TX3_BUFFER_SIZE TX_BUFFER_SIZE
#endif
#ifndef RX3_BUFFER_SIZE
  #define RX3_BUFFER_SIZE RX_BUFFER_SIZE
#endif

#define TX0_BUFFER_MASK (TX0_BUFFER_SIZE - 1)
#define RX0_BUFFER_MASK (RX0_BUFFER_SIZE - 1)

#define TX1_BUFFER_MASK (TX1_BUFFER_SIZE - 1)
#define RX1_BUFFER_MASK (RX1_BUFFER_SIZE - 1)

#define TX2_BUFFER_MASK (TX2_BUFFER_SIZE - 1)
#define RX2_BUFFER_MASK (RX2_BUFFER_SIZE - 1)

#define TX3_BUFFER_MASK (TX3_BUFFER_SIZE - 1)
#define RX3_BUFFER_MASK (RX3_BUFFER_SIZE - 1)

enum { COMPLETED = 1, BUFFER_EMPTY = 0, BUFFER_FULL = 0 };

#if defined(URSEL) || defined(URSEL0) || defined(URSEL1) || defined(URSEL2) || \
    defined(URSEL3)

  #define USART_XCK_RISING_EDGE  0x80
  #define USART_XCK_FALLING_EDGE 0x81

  #define USART_5BIT_DATA 0x80
  #define USART_6BIT_DATA 0x82
  #define USART_7BIT_DATA 0x84
  #define USART_8BIT_DATA 0x86

  #define USART_1STOP_BIT  0x80
  #define USART_2STOP_BITS 0x88

  #define USART_NO_PARITY   0x80
  #define USART_EVEN_PARITY 0xA0
  #define USART_ODD_PARITY  0xB0

  #define USART_ASYNC_MODE 0x80
  #define USART_SYNC_MODE  0xC0
#elif __AVR_ARCH__ == 103
  #define USART_XCK_RISING_EDGE  0x00
  #define USART_XCK_FALLING_EDGE 0x01

  #define USART_5BIT_DATA USART_CHSIZE_5BIT_gc
  #define USART_6BIT_DATA USART_CHSIZE_6BIT_gc
  #define USART_7BIT_DATA USART_CHSIZE_7BIT_gc
  #define USART_8BIT_DATA USART_CHSIZE_8BIT_gc

  #define USART_1STOP_BIT  USART_SBMODE_1BIT_gc
  #define USART_2STOP_BITS USART_SBMODE_2BIT_gc

  #define USART_NO_PARITY   USART_PMODE_DISABLED_gc
  #define USART_EVEN_PARITY USART_PMODE_EVEN_gc
  #define USART_ODD_PARITY  USART_PMODE_ODD_gc

  #define USART_ASYNC_MODE USART_CMODE_ASYNCHRONOUS_gc
  #define USART_SYNC_MODE  USART_CMODE_SYNCHRONOUS_gc
  #define USART_MSPI_MODE  USART_CMODE_MSPI_gc
#else
  #define USART_XCK_RISING_EDGE  0x00
  #define USART_XCK_FALLING_EDGE 0x01

  #define USART_5BIT_DATA 0x00
  #define USART_6BIT_DATA 0x02
  #define USART_7BIT_DATA 0x04
  #define USART_8BIT_DATA 0x06

  #define USART_1STOP_BIT  0x00
  #define USART_2STOP_BITS 0x08

  #define USART_NO_PARITY   0x00
  #define USART_EVEN_PARITY 0x20
  #define USART_ODD_PARITY  0x30

  #define USART_ASYNC_MODE 0x00
  #define USART_SYNC_MODE  0x40
  #define USART_MSPI_MODE  0xC0
#endif

#define USART_8N1 (USART_8BIT_DATA | USART_NO_PARITY | USART_1STOP_BIT)
#define USART_8N2 (USART_8BIT_DATA | USART_NO_PARITY | USART_2STOP_BITS)
#define USART_8E1 (USART_8BIT_DATA | USART_EVEN_PARITY | USART_1STOP_BIT)
#define USART_8E2 (USART_8BIT_DATA | USART_EVEN_PARITY | USART_2STOP_BITS)
#define USART_8O1 (USART_8BIT_DATA | USART_ODD_PARITY | USART_1STOP_BIT)
#define USART_8O2 (USART_8BIT_DATA | USART_ODD_PARITY | USART_2STOP_BITS)
#define USART_7N1 (USART_7BIT_DATA | USART_NO_PARITY | USART_1STOP_BIT)
#define USART_7N2 (USART_7BIT_DATA | USART_NO_PARITY | USART_2STOP_BITS)
#define USART_7E1 (USART_7BIT_DATA | USART_EVEN_PARITY | USART_1STOP_BIT)
#define USART_7E2 (USART_7BIT_DATA | USART_EVEN_PARITY | USART_2STOP_BITS)
#define USART_7O1 (USART_7BIT_DATA | USART_ODD_PARITY | USART_1STOP_BIT)
#define USART_7O2 (USART_7BIT_DATA | USART_ODD_PARITY | USART_2STOP_BITS)

#ifdef NO_USART_RX // remove all RX interrupts
  #define NO_RX0_INTERRUPT
  #define NO_RX1_INTERRUPT
  #define NO_RX2_INTERRUPT
  #define NO_RX3_INTERRUPT
#endif

#ifdef NO_USART_TX // remove all TX interrupts
  #define NO_TX0_INTERRUPT
  #define NO_TX1_INTERRUPT
  #define NO_TX2_INTERRUPT
  #define NO_TX3_INTERRUPT
#endif

#ifdef USE_DOUBLE_SPEED
  #define USART0_U2X_SPEED
  #define USART1_U2X_SPEED
  #define USART2_U2X_SPEED
  #define USART3_U2X_SPEED
#endif

#ifdef RX_GETC_ECHO
  #define RX0_GETC_ECHO
  #define RX1_GETC_ECHO
  #define RX2_GETC_ECHO
  #define RX3_GETC_ECHO
#endif

#ifdef PUTC_CONVERT_LF_TO_CRLF
  #define PUTC0_CONVERT_LF_TO_CRLF
  #define PUTC1_CONVERT_LF_TO_CRLF
  #define PUTC2_CONVERT_LF_TO_CRLF
  #define PUTC3_CONVERT_LF_TO_CRLF
#endif

#ifdef USART_EXTEND_RX_BUFFER
  #define USART0_EXTEND_RX_BUFFER
  #define USART1_EXTEND_RX_BUFFER
  #define USART2_EXTEND_RX_BUFFER
  #define USART3_EXTEND_RX_BUFFER
#endif

#ifdef USART_PUTC_FAST_INSERTIONS
  #define USART0_PUTC_FAST_INSERTIONS
  #define USART1_PUTC_FAST_INSERTIONS
  #define USART2_PUTC_FAST_INSERTIONS
  #define USART3_PUTC_FAST_INSERTIONS
#endif

#ifdef USART_MPCM_MODE
  #define USART0_MPCM_MODE
  #define USART1_MPCM_MODE
  #define USART2_MPCM_MODE
  #define USART3_MPCM_MODE
#endif

#if defined(CTS0_DDR) && defined(CTS0_PORT) && defined(CTS0_PIN) &&            \
    defined(CTS0_IONUM)
  #define USART0_USE_SOFT_CTS
#endif
#if defined(CTS1_DDR) && defined(CTS1_PORT) && defined(CTS1_PIN) &&            \
    defined(CTS1_IONUM)
  #define USART1_USE_SOFT_CTS
#endif
#if defined(CTS2_DDR) && defined(CTS2_PORT) && defined(CTS2_PIN) &&            \
    defined(CTS2_IONUM)
  #define USART2_USE_SOFT_CTS
#endif
#if defined(CTS3_DDR) && defined(CTS3_PORT) && defined(CTS3_PIN) &&            \
    defined(CTS3_IONUM)
  #define USART3_USE_SOFT_CTS
#endif

#if defined(RTS0_DDR) && defined(RTS0_PORT) && defined(RTS0_PIN) &&            \
    defined(RTS0_IONUM)
  #define USART0_USE_SOFT_RTS
#endif
#if defined(RTS1_DDR) && defined(RTS1_PORT) && defined(RTS1_PIN) &&            \
    defined(RTS1_IONUM)
  #define USART1_USE_SOFT_RTS
#endif
#if defined(RTS2_DDR) && defined(RTS2_PORT) && defined(RTS2_PIN) &&            \
    defined(RTS2_IONUM)
  #define USART2_USE_SOFT_RTS
#endif
#if defined(RTS3_DDR) && defined(RTS3_PORT) && defined(RTS3_PIN) &&            \
    defined(RTS3_IONUM)
  #define USART3_USE_SOFT_RTS
#endif

#if defined(RS485_CONTROL0_DDR) && defined(RS485_CONTROL0_PORT) &&             \
    defined(RS485_CONTROL0_PIN) && defined(RS485_CONTROL0_IONUM)
  #define USART0_RS485_MODE
#endif
#if defined(RS485_CONTROL1_DDR) && defined(RS485_CONTROL1_PORT) &&             \
    defined(RS485_CONTROL1_PIN) && defined(RS485_CONTROL1_IONUM)
  #define USART1_RS485_MODE
#endif
#if defined(RS485_CONTROL2_DDR) && defined(RS485_CONTROL2_PORT) &&             \
    defined(RS485_CONTROL2_PIN) && defined(RS485_CONTROL2_IONUM)
  #define USART2_RS485_MODE
#endif
#if defined(RS485_CONTROL3_DDR) && defined(RS485_CONTROL3_PORT) &&             \
    defined(RS485_CONTROL3_PIN) && defined(RS485_CONTROL3_IONUM)
  #define USART3_RS485_MODE
#endif

#ifdef USART0_USE_SOFT_RTS
  #ifndef USART0_EXTEND_RX_BUFFER
    #define USART0_EXTEND_RX_BUFFER
  #endif
#endif

#ifdef USART1_USE_SOFT_RTS
  #ifndef USART1_EXTEND_RX_BUFFER
    #define USART1_EXTEND_RX_BUFFER
  #endif
#endif

#ifdef USART2_USE_SOFT_RTS
  #ifndef USART2_EXTEND_RX_BUFFER
    #define USART2_EXTEND_RX_BUFFER
  #endif
#endif

#ifdef USART3_USE_SOFT_RTS
  #ifndef USART3_EXTEND_RX_BUFFER
    #define USART3_EXTEND_RX_BUFFER
  #endif
#endif

#ifdef RX_NEWLINE_MODE

  #if (RX_NEWLINE_MODE == 0)
    #define RX_NEWLINE_MODE_R
  #elif (RX_NEWLINE_MODE == 1)
    #define RX_NEWLINE_MODE_N
  #else // RX_NEWLINE_MODE == 2
    #define RX_NEWLINE_MODE_RN
  #endif
#else
  #define RX_NEWLINE_MODE_RN // 2
#endif

#ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
register uint8_t USART_SREG_SAVE_REG_NAME asm(
    USART_SREG_SAVE_REG_NUM); // have to be defined separately in every
                              // compilation unit
#endif

#ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
register uint16_t USART_Z_SAVE_REG_NAME asm(
    USART_Z_SAVE_REG_NUM); // have to be defined separately in every compilation
                           // unit
#endif

#if defined(USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE) &&                      \
    defined(USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE)
  #define USART_REG_SAVE_LIST                                                  \
    [sreg_save] "+r"(USART_SREG_SAVE_REG_NAME), [z_save] "+r"(                 \
                                                    USART_Z_SAVE_REG_NAME)

#elif defined(USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE)
  #define USART_REG_SAVE_LIST [z_save] "+r"(USART_Z_SAVE_REG_NAME)

#elif defined(USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE)
  #define USART_REG_SAVE_LIST [sreg_save] "+r"(USART_SREG_SAVE_REG_NAME)
#else
  #define USART_REG_SAVE_LIST
#endif

#if defined(__usbdrv_h_included__) && !defined(USART_UNSAFE_RX_INTERRUPT)
  #warning "usb may not work with uart's RX ISR"
#endif

#if defined(__usbdrv_h_included__) && !defined(USART_UNSAFE_TX_INTERRUPT)
  #warning "usb may not work with uart's TX ISR"
#endif

#if (defined(USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE) ||                     \
     defined(USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE)) &&                       \
    (defined(USART_UNSAFE_TX_INTERRUPT) || defined(USART_UNSAFE_RX_INTERRUPT))
  #warning                                                                     \
      "using globally reserved save registers with interruptable interrupts might create special conditions"
#endif

#if defined(__AVR_ATtiny102__) || defined(__AVR_ATtiny104__) ||                \
    defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny2313A__)
  #define USART_USE_TINY_MEMORY_MODEL
#endif

#if defined(__AVR_ATtiny102__) || defined(__AVR_ATtiny104__)

  #if (TX0_BUFFER_SIZE > 8) || (RX0_BUFFER_SIZE > 8)
    #warning "TX or RX buffer may be too large for this mcu"
  #endif

  #define USART0_IN_UPPER_IO_ADDRESS_SPACE

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RXC_vect
    #define TXC0_INTERRUPT  USART0_TXC_vect
    #define UDRE0_INTERRUPT USART0_DRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0
#endif

#if defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny2313A__) ||             \
    defined(__AVR_ATtiny4313)

  #define USART0_IN_IO_ADDRESS_SPACE

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART_RX_vect
    #define TXC0_INTERRUPT  USART_TX_vect
    #define UDRE0_INTERRUPT USART_UDRE_vect
    #define UDR0_REGISTER   UDR
    #define UBRR0L_REGISTER UBRRL
    #define UBRR0H_REGISTER UBRRH
    #define UCSR0A_REGISTER UCSRA
    #define UCSR0B_REGISTER UCSRB
    #define UCSR0C_REGISTER UCSRC
    #define TXCIE0_BIT      TXCIE
    #define UDRIE0_BIT      UDRIE
    #define RXCIE0_BIT      RXCIE
    #define TXEN0_BIT       TXEN
    #define RXEN0_BIT       RXEN
    #define UDRE0_BIT       UDRE
    #define RXC0_BIT        RXC
    #define U2X0_BIT        U2X
    #define MPCM0_BIT       MPCM
    #define UCSZ02_BIT      UCSZ2
    #define TXB80_BIT       TXB8

  #endif // NO_USART0
#endif

#if defined(__AVR_ATtiny1634__)

  #define USART0_IN_UPPER_IO_ADDRESS_SPACE

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RX_vect
    #define TXC0_INTERRUPT  USART0_TX_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0

  #ifndef NO_USART1
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RX_vect
    #define TXC1_INTERRUPT  USART1_TX_vect
    #define UDRE1_INTERRUPT USART1_UDRE_vect
    #define UDR1_REGISTER   UDR1
    #define UBRR1L_REGISTER UBRR1L
    #define UBRR1H_REGISTER UBRR1H
    #define UCSR1A_REGISTER UCSR1A
    #define UCSR1B_REGISTER UCSR1B
    #define UCSR1C_REGISTER UCSR1C
    #define TXCIE1_BIT      TXCIE1
    #define UDRIE1_BIT      UDRIE1
    #define RXCIE1_BIT      RXCIE1
    #define TXEN1_BIT       TXEN1
    #define RXEN1_BIT       RXEN1
    #define UDRE1_BIT       UDRE1
    #define RXC1_BIT        RXC1
    #define U2X1_BIT        U2X1
    #define MPCM1_BIT       MPCM1
    #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT       TXB81

  #endif // NO_USART1
#endif

#if defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny841__)

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RX_vect
    #define TXC0_INTERRUPT  USART0_TX_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0

  #ifndef NO_USART1
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RX_vect
    #define TXC1_INTERRUPT  USART1_TX_vect
    #define UDRE1_INTERRUPT USART1_UDRE_vect
    #define UDR1_REGISTER   UDR1
    #define UBRR1L_REGISTER UBRR1L
    #define UBRR1H_REGISTER UBRR1H
    #define UCSR1A_REGISTER UCSR1A
    #define UCSR1B_REGISTER UCSR1B
    #define UCSR1C_REGISTER UCSR1C
    #define TXCIE1_BIT      TXCIE1
    #define UDRIE1_BIT      UDRIE1
    #define RXCIE1_BIT      RXCIE1
    #define TXEN1_BIT       TXEN1
    #define RXEN1_BIT       RXEN1
    #define UDRE1_BIT       UDRE1
    #define RXC1_BIT        RXC1
    #define U2X1_BIT        U2X1
    #define MPCM1_BIT       MPCM1
    #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT       TXB81

  #endif // NO_USART1
#endif

#if defined(__AVR_ATmega48__) || defined(__AVR_ATmega48P__) ||                 \
    defined(__AVR_ATmega48PA__) || defined(__AVR_ATmega48PB__) ||              \
    defined(__AVR_ATmega88__) || defined(__AVR_ATmega88P__) ||                 \
    defined(__AVR_ATmega88PA__) || defined(__AVR_ATmega88PB__) ||              \
    defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) ||               \
    defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega168PB__) ||            \
    defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART_RX_vect
    #define TXC0_INTERRUPT  USART_TX_vect
    #define UDRE0_INTERRUPT USART_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0
#endif

#if defined(__AVR_ATmega328PB__)

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RX_vect
    #define TXC0_INTERRUPT  USART0_TX_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0

  #ifndef NO_USART1
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RX_vect
    #define TXC1_INTERRUPT  USART1_TX_vect
    #define UDRE1_INTERRUPT USART1_UDRE_vect
    #define UDR1_REGISTER   UDR1
    #define UBRR1L_REGISTER UBRR1L
    #define UBRR1H_REGISTER UBRR1H
    #define UCSR1A_REGISTER UCSR1A
    #define UCSR1B_REGISTER UCSR1B
    #define UCSR1C_REGISTER UCSR1C
    #define TXCIE1_BIT      TXCIE1
    #define UDRIE1_BIT      UDRIE1
    #define RXCIE1_BIT      RXCIE1
    #define TXEN1_BIT       TXEN1
    #define RXEN1_BIT       RXEN1
    #define UDRE1_BIT       UDRE1
    #define RXC1_BIT        RXC1
    #define U2X1_BIT        U2X1
    #define MPCM1_BIT       MPCM1
    #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT       TXB81

  #endif // NO_USART1
#endif

#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8P__) ||                   \
    defined(__AVR_ATmega16__) || defined(__AVR_ATmega16A__) ||                 \
    defined(__AVR_ATmega32__) || defined(__AVR_ATmega32A__) ||                 \
    defined(__AVR_ATmega8A__) || defined(__AVR_ATmega8L__)

  #define USART0_IN_IO_ADDRESS_SPACE

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART_RXC_vect
    #define TXC0_INTERRUPT  USART_TXC_vect
    #define UDRE0_INTERRUPT USART_UDRE_vect
    #define UDR0_REGISTER   UDR
    #define UBRR0L_REGISTER UBRRL
    #define UBRR0H_REGISTER UBRRH
    #define UCSR0A_REGISTER UCSRA
    #define UCSR0B_REGISTER UCSRB
    #define UCSR0C_REGISTER UCSRC
    #define TXCIE0_BIT      TXCIE
    #define UDRIE0_BIT      UDRIE
    #define RXCIE0_BIT      RXCIE
    #define TXEN0_BIT       TXEN
    #define RXEN0_BIT       RXEN
    #define UDRE0_BIT       UDRE
    #define RXC0_BIT        RXC
    #define U2X0_BIT        U2X
    #define MPCM0_BIT       MPCM
    #define UCSZ02_BIT      UCSZ2
    #define TXB80_BIT       TXB8

  #endif // NO_USART0
#endif

#if defined(__AVR_ATmega8515__) || defined(__AVR_ATmega8515L__)

  #define USART0_IN_IO_ADDRESS_SPACE

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART_RX_vect
    #define TXC0_INTERRUPT  USART_TX_vect
    #define UDRE0_INTERRUPT USART_UDRE_vect
    #define UDR0_REGISTER   UDR
    #define UBRR0L_REGISTER UBRRL
    #define UBRR0H_REGISTER UBRRH
    #define UCSR0A_REGISTER UCSRA
    #define UCSR0B_REGISTER UCSRB
    #define UCSR0C_REGISTER UCSRC
    #define TXCIE0_BIT      TXCIE
    #define UDRIE0_BIT      UDRIE
    #define RXCIE0_BIT      RXCIE
    #define TXEN0_BIT       TXEN
    #define RXEN0_BIT       RXEN
    #define UDRE0_BIT       UDRE
    #define RXC0_BIT        RXC
    #define U2X0_BIT        U2X
    #define MPCM0_BIT       MPCM
    #define UCSZ02_BIT      UCSZ2
    #define TXB80_BIT       TXB8

  #endif // NO_USART0
#endif

#if defined(__AVR_ATmega162__)

  #define USART0_IN_IO_ADDRESS_SPACE
  #define USART1_IN_IO_ADDRESS_SPACE

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RXC_vect
    #define TXC0_INTERRUPT  USART0_TXC_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0

  #ifndef NO_USART1
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RXC_vect
    #define TXC1_INTERRUPT  USART1_TXC_vect
    #define UDRE1_INTERRUPT USART1_UDRE_vect
    #define UDR1_REGISTER   UDR1
    #define UBRR1L_REGISTER UBRR1L
    #define UBRR1H_REGISTER UBRR1H
    #define UCSR1A_REGISTER UCSR1A
    #define UCSR1B_REGISTER UCSR1B
    #define UCSR1C_REGISTER UCSR1C
    #define TXCIE1_BIT      TXCIE1
    #define UDRIE1_BIT      UDRIE1
    #define RXCIE1_BIT      RXCIE1
    #define TXEN1_BIT       TXEN1
    #define RXEN1_BIT       RXEN1
    #define UDRE1_BIT       UDRE1
    #define RXC1_BIT        RXC1
    #define U2X1_BIT        U2X1
    #define MPCM1_BIT       MPCM1
    #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT       TXB81

  #endif // NO_USART1
#endif

#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega128A__) ||               \
    defined(__AVR_ATmega64__) || defined(__AVR_ATmega64A__)
  #define USART0_IN_IO_ADDRESS_SPACE
#endif

#if defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) ||               \
    defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega1284__) ||             \
    defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega128__) ||              \
    defined(__AVR_ATmega128A__) || defined(__AVR_ATmega64__) ||                \
    defined(__AVR_ATmega64A__) || defined(__AVR_ATmega1281__) ||               \
    defined(__AVR_ATmega2561__) || defined(__AVR_ATmega640__) ||               \
    defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) ||              \
    defined(__AVR_ATmega164P__) || defined(__AVR_ATmega324P__) ||              \
    defined(__AVR_ATmega324A__) || defined(__AVR_ATmega324PA__) ||             \
    defined(__AVR_ATmega324PB__)

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RX_vect
    #define TXC0_INTERRUPT  USART0_TX_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif

  #if !defined(NO_USART1) && !defined(__AVR_ATmega644__)
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RX_vect
    #define TXC1_INTERRUPT  USART1_TX_vect
    #define UDRE1_INTERRUPT USART1_UDRE_vect
    #define UDR1_REGISTER   UDR1
    #define UBRR1L_REGISTER UBRR1L
    #define UBRR1H_REGISTER UBRR1H
    #define UCSR1A_REGISTER UCSR1A
    #define UCSR1B_REGISTER UCSR1B
    #define UCSR1C_REGISTER UCSR1C
    #define TXCIE1_BIT      TXCIE1
    #define UDRIE1_BIT      UDRIE1
    #define RXCIE1_BIT      RXCIE1
    #define TXEN1_BIT       TXEN1
    #define RXEN1_BIT       RXEN1
    #define UDRE1_BIT       UDRE1
    #define RXC1_BIT        RXC1
    #define U2X1_BIT        U2X1
    #define MPCM1_BIT       MPCM1
    #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT       TXB81

  #endif // NO_USART1 && 644

#endif

#if defined(__AVR_ATmega640__) || defined(__AVR_ATmega1280__) ||               \
    defined(__AVR_ATmega2560__) || defined(__AVR_ATmega324PB__)

  #ifndef NO_USART2
    #define USE_USART2

    #define RX2_INTERRUPT   USART2_RX_vect
    #define TXC2_INTERRUPT  USART2_TX_vect
    #define UDRE2_INTERRUPT USART2_UDRE_vect
    #define UDR2_REGISTER   UDR2
    #define UBRR2L_REGISTER UBRR2L
    #define UBRR2H_REGISTER UBRR2H
    #define UCSR2A_REGISTER UCSR2A
    #define UCSR2B_REGISTER UCSR2B
    #define UCSR2C_REGISTER UCSR2C
    #define TXCIE2_BIT      TXCIE2
    #define UDRIE2_BIT      UDRIE2
    #define RXCIE2_BIT      RXCIE2
    #define TXEN2_BIT       TXEN2
    #define RXEN2_BIT       RXEN2
    #define UDRE2_BIT       UDRE2
    #define RXC2_BIT        RXC2
    #define U2X2_BIT        U2X2
    #define MPCM2_BIT       MPCM2
    #define UCSZ22_BIT      UCSZ22
    #define TXB82_BIT       TXB82

  #endif // NO_USART2

  #if !defined(NO_USART3) && !defined(__AVR_ATmega324PB__)
    #define USE_USART3

    #define RX3_INTERRUPT   USART3_RX_vect
    #define TXC3_INTERRUPT  USART3_TX_vect
    #define UDRE3_INTERRUPT USART3_UDRE_vect
    #define UDR3_REGISTER   UDR3
    #define UBRR3L_REGISTER UBRR3L
    #define UBRR3H_REGISTER UBRR3H
    #define UCSR3A_REGISTER UCSR3A
    #define UCSR3B_REGISTER UCSR3B
    #define UCSR3C_REGISTER UCSR3C
    #define TXCIE3_BIT      TXCIE3
    #define UDRIE3_BIT      UDRIE3
    #define RXCIE3_BIT      RXCIE3
    #define TXEN3_BIT       TXEN3
    #define RXEN3_BIT       RXEN3
    #define UDRE3_BIT       UDRE3
    #define RXC3_BIT        RXC3
    #define U2X3_BIT        U2X3
    #define MPCM3_BIT       MPCM3
    #define UCSZ32_BIT      UCSZ32
    #define TXB83_BIT       TXB83

  #endif // NO_USART3
#endif   // 640/1280/2560 usart 2 & 3

#if defined(__AVR_ATmega8U2__) || defined(__AVR_ATmega16U2__) ||               \
    defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U2__) ||              \
    defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32U6__) ||              \
    defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__)

  #define USART1_HARDWARE_FLOW_CONTROL_AVAILABLE

  #ifndef NO_USART1 // we will call the only usart, as an usart0
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RX_vect
    #define TXC1_INTERRUPT  USART1_TX_vect
    #define UDRE1_INTERRUPT USART1_UDRE_vect
    #define UDR1_REGISTER   UDR1
    #define UBRR1L_REGISTER UBRR1L
    #define UBRR1H_REGISTER UBRR1H
    #define UCSR1A_REGISTER UCSR1A
    #define UCSR1B_REGISTER UCSR1B
    #define UCSR1C_REGISTER UCSR1C
    #define UCSR1D_REGISTER UCSR1D
    #define TXCIE1_BIT      TXCIE1
    #define UDRIE1_BIT      UDRIE1
    #define RXCIE1_BIT      RXCIE1
    #define TXEN1_BIT       TXEN1
    #define RXEN1_BIT       RXEN1
    #define UDRE1_BIT       UDRE1
    #define RXC1_BIT        RXC1
    #define U2X1_BIT        U2X1
    #define MPCM1_BIT       MPCM1
    #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT       TXB81
    #define CTSEN1_BIT      CTSEN
    #define RTSEN1_BIT      RTSEN

  #endif // NO_USART1
#endif

#if defined(__AVR_ATmega169A__) || defined(__AVR_ATmega169__) ||               \
    defined(__AVR_ATmega169P__) || defined(__AVR_ATmega169PA__)

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RX_vect
    #define TXC0_INTERRUPT  USART0_TX_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR
    #define UBRR0L_REGISTER UBRRL
    #define UBRR0H_REGISTER UBRRH
    #define UCSR0A_REGISTER UCSRA
    #define UCSR0B_REGISTER UCSRB
    #define UCSR0C_REGISTER UCSRC
    #define TXCIE0_BIT      TXCIE
    #define UDRIE0_BIT      UDRIE
    #define RXCIE0_BIT      RXCIE
    #define TXEN0_BIT       TXEN
    #define RXEN0_BIT       RXEN
    #define UDRE0_BIT       UDRE
    #define RXC0_BIT        RXC
    #define U2X0_BIT        U2X
    #define MPCM0_BIT       MPCM
    #define UCSZ02_BIT      UCSZ2
    #define TXB80_BIT       TXB8

  #endif // NO_USART0
#endif

#if defined(__AVR_ATmega329__) || defined(__AVR_ATmega329P__) ||               \
    defined(__AVR_ATmega329PA__) || defined(__AVR_ATmega329A__) ||             \
    defined(__AVR_ATmega649__) || defined(__AVR_ATmega649A__) ||               \
    defined(__AVR_ATmega649P__) || defined(__AVR_ATmega169P__) ||              \
    defined(__AVR_ATmega169PA__) || defined(__AVR_ATmega325__) ||              \
    defined(__AVR_ATmega325A__) || defined(__AVR_ATmega325P__) ||              \
    defined(__AVR_ATmega325PA__) || defined(__AVR_ATmega645__) ||              \
    defined(__AVR_ATmega645A__) || defined(__AVR_ATmega645P__)

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RX_vect
    #define TXC0_INTERRUPT  USART0_TX_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0
#endif

#if defined(__AVR_ATmega3290__) || defined(__AVR_ATmega6490__) ||              \
    defined(__AVR_ATmega3290P__) || defined(__AVR_ATmega3290PA__) ||           \
    defined(__AVR_ATmega3290A__) || defined(__AVR_ATmega6490A__) ||            \
    defined(__AVR_ATmega6490P__) || defined(__AVR_ATmega3250__) ||             \
    defined(__AVR_ATmega3250A__) || defined(__AVR_ATmega3250P__) ||            \
    defined(__AVR_ATmega3250PA__) || defined(__AVR_ATmega6450__) ||            \
    defined(__AVR_ATmega6450A__) || defined(__AVR_ATmega6450P__)

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART_RX_vect // wtf
    #define TXC0_INTERRUPT  USART0_TX_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0
#endif

#if defined(__AVR_AT90CAN32__) || defined(__AVR_AT90CAN64__) ||                \
    defined(__AVR_AT90CAN128__) || defined(__AVR_ATmega64RFR2__) ||            \
    defined(__AVR_ATmega128RFR2__) || defined(__AVR_ATmega256RFR2__) ||        \
    defined(__AVR_ATmega644RFR2__) || defined(__AVR_ATmega1284RFR2__) ||       \
    defined(__AVR_ATmega2564RFR2__) || defined(__AVR_ATmega128RFA1__)

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RX_vect
    #define TXC0_INTERRUPT  USART0_TX_vect
    #define UDRE0_INTERRUPT USART0_UDRE_vect
    #define UDR0_REGISTER   UDR0
    #define UBRR0L_REGISTER UBRR0L
    #define UBRR0H_REGISTER UBRR0H
    #define UCSR0A_REGISTER UCSR0A
    #define UCSR0B_REGISTER UCSR0B
    #define UCSR0C_REGISTER UCSR0C
    #define TXCIE0_BIT      TXCIE0
    #define UDRIE0_BIT      UDRIE0
    #define RXCIE0_BIT      RXCIE0
    #define TXEN0_BIT       TXEN0
    #define RXEN0_BIT       RXEN0
    #define UDRE0_BIT       UDRE0
    #define RXC0_BIT        RXC0
    #define U2X0_BIT        U2X0
    #define MPCM0_BIT       MPCM0
    #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT       TXB80

  #endif // NO_USART0

  #ifndef NO_USART1
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RX_vect
    #define TXC1_INTERRUPT  USART1_TX_vect
    #define UDRE1_INTERRUPT USART1_UDRE_vect
    #define UDR1_REGISTER   UDR1
    #define UBRR1L_REGISTER UBRR1L
    #define UBRR1H_REGISTER UBRR1H
    #define UCSR1A_REGISTER UCSR1A
    #define UCSR1B_REGISTER UCSR1B
    #define UCSR1C_REGISTER UCSR1C
    #define TXCIE1_BIT      TXCIE1
    #define UDRIE1_BIT      UDRIE1
    #define RXCIE1_BIT      RXCIE1
    #define TXEN1_BIT       TXEN1
    #define RXEN1_BIT       RXEN1
    #define UDRE1_BIT       UDRE1
    #define RXC1_BIT        RXC1
    #define U2X1_BIT        U2X1
    #define MPCM1_BIT       MPCM1
    #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT       TXB81

  #endif // NO_USART1
#endif

#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) ||              \
    defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)

  #ifndef NO_USART1
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RX_vect
    #define TXC1_INTERRUPT  USART1_TX_vect
    #define UDRE1_INTERRUPT USART1_UDRE_vect
    #define UDR1_REGISTER   UDR1
    #define UBRR1L_REGISTER UBRR1L
    #define UBRR1H_REGISTER UBRR1H
    #define UCSR1A_REGISTER UCSR1A
    #define UCSR1B_REGISTER UCSR1B
    #define UCSR1C_REGISTER UCSR1C
    #define TXCIE1_BIT      TXCIE1
    #define UDRIE1_BIT      UDRIE1
    #define RXCIE1_BIT      RXCIE1
    #define TXEN1_BIT       TXEN1
    #define RXEN1_BIT       RXEN1
    #define UDRE1_BIT       UDRE1
    #define RXC1_BIT        RXC1
    #define U2X1_BIT        U2X1
    #define MPCM1_BIT       MPCM1
    #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT       TXB81

  #endif // NO_USART1
#endif

#if __AVR_ARCH__ == 103

  #ifndef NO_USART0
    #define USE_USART0

    #define RX0_INTERRUPT   USART0_RXC_vect
    #define TXC0_INTERRUPT  USART0_TXC_vect
    #define UDRE0_INTERRUPT USART0_DRE_vect
    #define UDR0_REGISTER   USART0_TXDATAL
    #define UDR0H_REGISTER  USART0_TXDATAH
    #define RX0_REGISTER    USART0_RXDATAL
    #define RX0H_REGISTER   USART0_RXDATAH
    #define UBRR0L_REGISTER USART0_BAUDL
    #define UBRR0H_REGISTER USART0_BAUDH
    #define USR0_REGISTER   USART0_STATUS
    #define UCSR0A_REGISTER USART0_CTRLA
    #define UCSR0B_REGISTER USART0_CTRLB
    #define UCSR0C_REGISTER USART0_CTRLC
    #define TXCIE0_BIT      USART_TXCIE_bp  // CTRLA
    #define UDRIE0_BIT      USART_DREIE_bp  // CTRLA
    #define RXCIE0_BIT      USART_RXCIE_bp  // CTRLA
    #define TXEN0_BIT       USART_TXEN_bp   // CTRLB
    #define RXEN0_BIT       USART_RXEN_bp   // CTRLB
    #define UDRE0_BIT       USART_DREIF_bp  // STATUS
    #define RXC0_BIT        USART_RXCIF_bp  // STATUS
    #define U2X0_BIT        USART_RXMODE_gp // CTRLB
    #define MPCM0_BIT       USART_MPCM_bp   // CTRLB
    // #define UCSZ02_BIT      UCSZ02
    #define TXB80_BIT USART_DATA8_bp

  #endif // NO_USART0

  #if !defined(NO_USART1) && defined(USART1)
    #define USE_USART1

    #define RX1_INTERRUPT   USART1_RXC_vect
    #define TXC1_INTERRUPT  USART1_TXC_vect
    #define UDRE1_INTERRUPT USART1_DRE_vect
    #define UDR1_REGISTER   USART1_TXDATAL
    #define UDR1H_REGISTER  USART1_TXDATAH
    #define RX1_REGISTER    USART1_RXDATAL
    #define RX1H_REGISTER   USART1_RXDATAH
    #define UBRR1L_REGISTER USART1_BAUDL
    #define UBRR1H_REGISTER USART1_BAUDH
    #define USR1_REGISTER   USART1_STATUS
    #define UCSR1A_REGISTER USART1_CTRLA
    #define UCSR1B_REGISTER USART1_CTRLB
    #define UCSR1C_REGISTER USART1_CTRLC
    #define TXCIE1_BIT      USART_TXCIE_bp                        // CTRLA
    #define UDRIE1_BIT      USART_DREIE_bp                        // CTRLA
    #define RXCIE1_BIT      USART_RXCIE_bp                        // CTRLA
    #define TXEN1_BIT       USART_TXEN_bp                         // CTRLB
    #define RXEN1_BIT       USART_RXEN_bp                         // CTRLB
    #define UDRE1_BIT       USART_DREIF_bp                        // STATUS
    #define RXC1_BIT        USART_RXCIF_bp                        // RXDATAH
    #define U2X1_BIT        (USART_RXMODE_gp - USART_RXMODE_0_bp) // CTRLB
    #define MPCM1_BIT       USART_MPCM_bp                         // CTRLB
    // #define UCSZ12_BIT      UCSZ12
    #define TXB81_BIT USART_DATA8_bp

  #endif // NO_USART1
#endif

#if defined(USART_REMAP_LAST_INTERFACE) && !defined(USE_USART0) &&             \
    defined(USE_USART1) && !defined(USE_USART2) && !defined(USE_USART3)
  #undef USE_USART1
  #define USE_USART0

  #ifdef USART1_HARDWARE_FLOW_CONTROL_AVAILABLE
    #define USART0_HARDWARE_FLOW_CONTROL_AVAILABLE

    #define UCSR0D_REGISTER UCSR1D_REGISTER
    #define CTSEN0_BIT      CTSEN1_BIT
    #define RTSEN0_BIT      RTSEN1_BIT
  #endif

  #define RX0_INTERRUPT   RX1_INTERRUPT
  #define TXC0_INTERRUPT  TXC1_INTERRUPT
  #define UDRE0_INTERRUPT UDRE1_INTERRUPT
  #define UDR0_REGISTER   UDR1_REGISTER
  #define UBRR0L_REGISTER UBRR1L_REGISTER
  #define UBRR0H_REGISTER UBRR1H_REGISTER
  #define UCSR0A_REGISTER UCSR1A_REGISTER
  #define UCSR0B_REGISTER UCSR1B_REGISTER
  #define UCSR0C_REGISTER UCSR1C_REGISTER
  #define TXCIE0_BIT      TXCIE1_BIT
  #define UDRIE0_BIT      UDRIE1_BIT
  #define RXCIE0_BIT      RXCIE1_BIT
  #define TXEN0_BIT       TXEN1_BIT
  #define RXEN0_BIT       RXEN1_BIT
  #define UDRE0_BIT       UDRE1_BIT
  #define RXC0_BIT        RXC1_BIT
  #define U2X0_BIT        U2X1_BIT
  #define MPCM0_BIT       MPCM1_BIT
  #define UCSZ02_BIT      UCSZ12_BIT
  #define TXB80_BIT       TXB81_BIT
#endif

#if defined(USART_REMAP_LAST_INTERFACE) && !defined(USE_USART0) &&             \
    !defined(USE_USART1) && defined(USE_USART2) && !defined(USE_USART3)
  #undef USE_USART2
  #define USE_USART0

  #define RX0_INTERRUPT   RX2_INTERRUPT
  #define TXC0_INTERRUPT  TXC2_INTERRUPT
  #define UDRE0_INTERRUPT UDRE2_INTERRUPT
  #define UDR0_REGISTER   UDR2_REGISTER
  #define UBRR0L_REGISTER UBRR2L_REGISTER
  #define UBRR0H_REGISTER UBRR2H_REGISTER
  #define UCSR0A_REGISTER UCSR2A_REGISTER
  #define UCSR0B_REGISTER UCSR2B_REGISTER
  #define UCSR0C_REGISTER UCSR2C_REGISTER
  #define TXCIE0_BIT      TXCIE2_BIT
  #define UDRIE0_BIT      UDRIE2_BIT
  #define RXCIE0_BIT      RXCIE2_BIT
  #define TXEN0_BIT       TXEN2_BIT
  #define RXEN0_BIT       RXEN2_BIT
  #define UDRE0_BIT       UDRE2_BIT
  #define RXC0_BIT        RXC2_BIT
  #define U2X0_BIT        U2X2_BIT
  #define MPCM0_BIT       MPCM2_BIT
  #define UCSZ02_BIT      UCSZ22_BIT
  #define TXB80_BIT       TXB82_BIT
#endif

#if defined(USART_REMAP_LAST_INTERFACE) && !defined(USE_USART0) &&             \
    !defined(USE_USART1) && !defined(USE_USART2) && defined(USE_USART3)
  #undef USE_USART3
  #define USE_USART0

  #define RX0_INTERRUPT   RX3_INTERRUPT
  #define TXC0_INTERRUPT  TXC3_INTERRUPT
  #define UDRE0_INTERRUPT UDRE3_INTERRUPT
  #define UDR0_REGISTER   UDR3_REGISTER
  #define UBRR0L_REGISTER UBRR3L_REGISTER
  #define UBRR0H_REGISTER UBRR3H_REGISTER
  #define UCSR0A_REGISTER UCSR3A_REGISTER
  #define UCSR0B_REGISTER UCSR3B_REGISTER
  #define UCSR0C_REGISTER UCSR3C_REGISTER
  #define TXCIE0_BIT      TXCIE3_BIT
  #define UDRIE0_BIT      UDRIE3_BIT
  #define RXCIE0_BIT      RXCIE3_BIT
  #define TXEN0_BIT       TXEN3_BIT
  #define RXEN0_BIT       RXEN3_BIT
  #define UDRE0_BIT       UDRE3_BIT
  #define RXC0_BIT        RXC3_BIT
  #define U2X0_BIT        U2X3_BIT
  #define MPCM0_BIT       MPCM3_BIT
  #define UCSZ02_BIT      UCSZ32_BIT
  #define TXB80_BIT       TXB83_BIT
#endif

#if !defined(USE_USART0) && !defined(USE_USART1) && !defined(USE_USART2) &&    \
    !defined(USE_USART3)
  #warning "USART not available or unknown mcu"
#endif

#ifndef USE_USART0
  #define NO_TX0_INTERRUPT
  #define NO_RX0_INTERRUPT
#endif

#ifndef USE_USART1
  #define NO_TX1_INTERRUPT
  #define NO_RX1_INTERRUPT
#endif

#ifndef USE_USART2
  #define NO_TX2_INTERRUPT
  #define NO_RX2_INTERRUPT
#endif

#ifndef USE_USART3
  #define NO_TX3_INTERRUPT
  #define NO_RX3_INTERRUPT
#endif

#if (defined(USE_USART0) && !defined(USE_USART1) && !defined(USE_USART2) &&    \
     !defined(USE_USART3))
  #ifdef NO_TX0_INTERRUPT
    #define NO_USART_TX
  #endif

  #ifdef NO_RX0_INTERRUPT
    #define NO_USART_RX
  #endif
#endif

#if defined(USART0_RS485_MODE) && !defined(NO_TX0_INTERRUPT)
  #define USART0_USE_TXC_INTERRUPT
#endif

#if defined(USART1_RS485_MODE) && !defined(NO_TX1_INTERRUPT)
  #define USART1_USE_TXC_INTERRUPT
#endif

#if defined(USART2_RS485_MODE) && !defined(NO_TX2_INTERRUPT)
  #define USART2_USE_TXC_INTERRUPT
#endif

#if defined(USART3_RS485_MODE) && !defined(NO_TX3_INTERRUPT)
  #define USART3_USE_TXC_INTERRUPT
#endif

#ifndef USART0_CONFIG_B // set config bytes for UCSR0B_REGISTER

  #if __AVR_ARCH__ == 103
    #ifdef USART0_U2X_SPEED
      #define USART0_CONFIG_B_SPEED | (1 << U2X0_BIT)
    #else
      #define USART0_CONFIG_B_SPEED | (0 << U2X0_BIT)
    #endif

    #ifdef USART0_MPCM_MODE
      #define USART0_CONFIG_B_MPCM | (1 << MPCM0_BIT)
    #else
      #define USART0_CONFIG_B_MPCM | (0 << MPCM0_BIT)
    #endif

    #if defined(NO_RX0_INTERRUPT)
      #define USART0_CONFIG_B                                                  \
        (1 << TXEN0_BIT) USART0_CONFIG_B_MPCM USART0_CONFIG_B_SPEED

    #elif defined(NO_TX0_INTERRUPT)
      #define USART0_CONFIG_B                                                  \
        (1 << RXEN0_BIT) USART0_CONFIG_B_MPCM USART0_CONFIG_B_SPEED
    #else
      #define USART0_CONFIG_B                                                  \
        (1 << TXEN0_BIT) | (1 << RXEN0_BIT)                                    \
                               USART0_CONFIG_B_MPCM USART0_CONFIG_B_SPEED
    #endif
  #else
    #ifdef USART0_MPCM_MODE
      #if defined(NO_RX0_INTERRUPT)
        #define USART0_CONFIG_B (1 << TXEN0_BIT) | (1 << UCSZ02_BIT)

      #elif defined(NO_TX0_INTERRUPT)
        #define USART0_CONFIG_B                                                \
          (1 << RXEN0_BIT) | (1 << RXCIE0_BIT) | (1 << UCSZ02_BIT)
      #else
        #ifdef USART0_RS485_MODE
          #define USART0_CONFIG_B                                              \
            (1 << TXEN0_BIT) | (1 << TXCIE0_BIT) | (1 << RXEN0_BIT) |          \
                (1 << RXCIE0_BIT) | (1 << UCSZ02_BIT)
        #else
          #define USART0_CONFIG_B                                              \
            (1 << TXEN0_BIT) | (1 << RXEN0_BIT) | (1 << RXCIE0_BIT) |          \
                (1 << UCSZ02_BIT)
        #endif
      #endif
    #else
      #if defined(NO_RX0_INTERRUPT)
        #define USART0_CONFIG_B (1 << TXEN0_BIT)

      #elif defined(NO_TX0_INTERRUPT)
        #define USART0_CONFIG_B (1 << RXEN0_BIT) | (1 << RXCIE0_BIT)
      #else
        #ifdef USART0_RS485_MODE
          #define USART0_CONFIG_B                                              \
            (1 << TXEN0_BIT) | (1 << TXCIE0_BIT) | (1 << RXEN0_BIT) |          \
                (1 << RXCIE0_BIT)
        #else
          #define USART0_CONFIG_B                                              \
            (1 << TXEN0_BIT) | (1 << RXEN0_BIT) | (1 << RXCIE0_BIT)
        #endif
      #endif
    #endif
  #endif
#endif // USART0_CONFIG

#ifndef USART1_CONFIG_B // set config bytes for UCSR1B_REGISTER

  #ifdef USART1_MPCM_MODE
    #if defined(NO_RX1_INTERRUPT)
      #define USART1_CONFIG_B (1 << TXEN1_BIT) | (1 << UCSZ12_BIT)

    #elif defined(NO_TX1_INTERRUPT)
      #define USART1_CONFIG_B                                                  \
        (1 << RXEN1_BIT) | (1 << RXCIE1_BIT) | (1 << UCSZ12_BIT)
    #else
      #ifdef USART1_RS485_MODE
        #define USART1_CONFIG_B                                                \
          (1 << TXEN1_BIT) | (1 << TXCIE1_BIT) | (1 << RXEN1_BIT) |            \
              (1 << RXCIE1_BIT) | (1 << UCSZ12_BIT)
      #else
        #define USART1_CONFIG_B                                                \
          (1 << TXEN1_BIT) | (1 << RXEN1_BIT) | (1 << RXCIE1_BIT) |            \
              (1 << UCSZ12_BIT)
      #endif
    #endif
  #else
    #if defined(NO_RX1_INTERRUPT)
      #define USART1_CONFIG_B (1 << TXEN1_BIT)

    #elif defined(NO_TX1_INTERRUPT)
      #define USART1_CONFIG_B (1 << RXEN1_BIT) | (1 << RXCIE1_BIT)
    #else
      #ifdef USART1_RS485_MODE
        #define USART1_CONFIG_B                                                \
          (1 << TXEN1_BIT) | (1 << TXCIE1_BIT) | (1 << RXEN1_BIT) |            \
              (1 << RXCIE1_BIT)
      #else
        #define USART1_CONFIG_B                                                \
          (1 << TXEN1_BIT) | (1 << RXEN1_BIT) | (1 << RXCIE1_BIT)
      #endif
    #endif
  #endif
#endif // USART1_CONFIG

#ifndef USART2_CONFIG_B // set config bytes for UCSR2B_REGISTER

  #ifdef USART2_MPCM_MODE
    #if defined(NO_RX2_INTERRUPT)
      #define USART2_CONFIG_B (1 << TXEN2_BIT) | (1 << UCSZ22_BIT)

    #elif defined(NO_TX2_INTERRUPT)
      #define USART2_CONFIG_B                                                  \
        (1 << RXEN2_BIT) | (1 << RXCIE2_BIT) | (1 << UCSZ22_BIT)
    #else
      #ifdef USART2_RS485_MODE
        #define USART2_CONFIG_B                                                \
          (1 << TXEN2_BIT) | (1 << TXCIE2_BIT) | (1 << RXEN2_BIT) |            \
              (1 << RXCIE2_BIT) | (1 << UCSZ22_BIT)
      #else
        #define USART2_CONFIG_B                                                \
          (1 << TXEN2_BIT) | (1 << RXEN2_BIT) | (1 << RXCIE2_BIT) |            \
              (1 << UCSZ22_BIT)
      #endif
    #endif
  #else
    #if defined(NO_RX2_INTERRUPT)
      #define USART2_CONFIG_B (1 << TXEN2_BIT)

    #elif defined(NO_TX2_INTERRUPT)
      #define USART2_CONFIG_B (1 << RXEN2_BIT) | (1 << RXCIE2_BIT)
    #else
      #ifdef USART2_RS485_MODE
        #define USART2_CONFIG_B                                                \
          (1 << TXEN2_BIT) | (1 << TXCIE2_BIT) | (1 << RXEN2_BIT) |            \
              (1 << RXCIE2_BIT)
      #else
        #define USART2_CONFIG_B                                                \
          (1 << TXEN2_BIT) | (1 << RXEN2_BIT) | (1 << RXCIE2_BIT)
      #endif
    #endif
  #endif
#endif // USART2_CONFIG

#ifndef USART3_CONFIG_B // set config bytes for UCSR3B_REGISTER

  #ifdef USART3_MPCM_MODE
    #if defined(NO_RX3_INTERRUPT)
      #define USART3_CONFIG_B (1 << TXEN3_BIT) | (1 << UCSZ32_BIT)

    #elif defined(NO_TX3_INTERRUPT)
      #define USART3_CONFIG_B                                                  \
        (1 << RXEN3_BIT) | (1 << RXCIE3_BIT) | (1 << UCSZ32_BIT)
    #else
      #ifdef USART3_RS485_MODE
        #define USART3_CONFIG_B                                                \
          (1 << TXEN3_BIT) | (1 << TXCIE3_BIT) | (1 << RXEN3_BIT) |            \
              (1 << RXCIE3_BIT) | (1 << UCSZ32_BIT)
      #else
        #define USART3_CONFIG_B                                                \
          (1 << TXEN3_BIT) | (1 << RXEN3_BIT) | (1 << RXCIE3_BIT) |            \
              (1 << UCSZ32_BIT)
      #endif
    #endif
  #else
    #if defined(NO_RX3_INTERRUPT)
      #define USART3_CONFIG_B (1 << TXEN3_BIT)

    #elif defined(NO_TX3_INTERRUPT)
      #define USART3_CONFIG_B (1 << RXEN3_BIT) | (1 << RXCIE3_BIT)
    #else
      #ifdef USART3_RS485_MODE
        #define USART3_CONFIG_B                                                \
          (1 << TXEN3_BIT) | (1 << TXCIE3_BIT) | (1 << RXEN3_BIT) |            \
              (1 << RXCIE3_BIT)
      #else
        #define USART3_CONFIG_B                                                \
          (1 << TXEN3_BIT) | (1 << RXEN3_BIT) | (1 << RXCIE3_BIT)
      #endif
    #endif
  #endif
#endif // USART3_CONFIG

#ifdef __cplusplus
extern "C" {
#endif

/************************************************************************************
 *                                   Initializers *
 ************************************************************************************/

// functions smaller then calling overhead (including context saves) or executed
// once are inline for reducing flash memory usage (eg. if() statements routines
// can be executed during compilation)

#ifdef USE_USART0
// for runtime reinitialization of uart
void uart0_reinit(uint16_t ubrr_value);

// have to be called once at startup
static inline void uart0_init(uint16_t ubrr_value)
    __attribute__((always_inline));
static inline void uart0_init(uint16_t ubrr_value) {
  #ifdef USART0_RS485_MODE
  RS485_CONTROL0_DDR |= (1 << RS485_CONTROL0_IONUM); // default pin state is low
  #endif

  UBRR0L_REGISTER = (uint8_t)ubrr_value;

  #ifdef USART_SKIP_UBRRH_IF_ZERO
  if (__builtin_constant_p(ubrr_value))
    // requires -Os flag - do not use in non-inline functions
    if (((ubrr_value >> 8) != 0))
  #endif
      UBRR0H_REGISTER = (ubrr_value >> 8);

  #if __AVR_ARCH__ == 103
    #if (!defined(NO_RX0_INTERRUPT) && !defined(NO_TX0_INTERRUPT))
      #ifdef USART0_RS485_MODE
  UCSR0A_REGISTER = (1 << RXCIE0_BIT) | (1 << TXCIE0_BIT) | (1 << RS485_BIT);
      #else
  UCSR0A_REGISTER = (1 << RXCIE0_BIT);
      #endif
    #endif
  #else
    #ifdef USART0_U2X_SPEED
      #ifdef USART0_MPCM_MODE
  UCSR0A_REGISTER = (1 << U2X0_BIT) | (1 << MPCM0_BIT);
      #else
  UCSR0A_REGISTER = (1 << U2X0_BIT); // enable double speed
      #endif
    #elif defined(USART0_MPCM_MODE)
  UCSR0A_REGISTER |= (1 << MPCM0_BIT);
    #endif
  #endif

  UCSR0B_REGISTER = USART0_CONFIG_B;
  // 8n1 is set by default, setting UCSRC is not needed
  #if defined(USART0_MPCM_MODE) && __AVR_ARCH__ == 103
  UCSR0C_REGISTER = USART_CHSIZE_9BITH_gc;
  #endif

  #ifdef USART0_USE_SOFT_RTS
  RTS0_DDR |= (1 << RTS0_IONUM);
  #endif
}

// UCSRC_reg can be used to set other than 8n1 transmission
static inline void uart0_set_FrameFormat(uint8_t UCSRC_reg)
    __attribute__((always_inline));
static inline void uart0_set_FrameFormat(uint8_t UCSRC_reg) {
  UCSR0C_REGISTER = UCSRC_reg;
}

// use instead of USE_DOUBLE_SPEED
static inline void uart0_set_U2X(void) __attribute__((always_inline));
static inline void uart0_set_U2X(void) {
  #if __AVR_ARCH__ == 103
  UCSR0B_REGISTER = (1 << U2X0_BIT);
  #else
  UCSR0A_REGISTER |= (1 << U2X0_BIT);
  #endif
}

// enable UDRE interrupt
static inline void uart0_enable_UDREI(void) __attribute__((always_inline));
static inline void uart0_enable_UDREI(void) {
  #if __AVR_ARCH__ == 103
  UCSR0A_REGISTER |= (1 << UDRIE0_BIT);
  #else
  UCSR0B_REGISTER |= (1 << UDRIE0_BIT);
  #endif
}

  #ifdef USART0_MPCM_MODE
// return slave to mpcm idle mode (wait for own addres frame)
static inline void uart0_mpcm_slave_return_idle(void)
    __attribute__((always_inline));
static inline void uart0_mpcm_slave_return_idle(void) {
    #if __AVR_ARCH__ == 103
  UCSR0B_REGISTER = (1 << MPCM0_BIT);
    #else
  UCSR0A_REGISTER |= (1 << MPCM0_BIT);
    #endif
}
  #endif

  #ifdef USART0_HARDWARE_FLOW_CONTROL_AVAILABLE
// pass true to enable
void uart0_hardware_flow_control_init(uint8_t ctsenable, uint8_t rtsenable)
    __attribute__((always_inline));
void uart0_hardware_flow_control_init(uint8_t ctsenable, uint8_t rtsenable) {
  // -Os dependent, do not use in non-inline functions
  if (ctsenable && rtsenable)
    UCSR0D_REGISTER = (1 << CTSEN0_BIT) | (1 << RTSEN0_BIT);
  else if (ctsenable)
    UCSR0D_REGISTER = (1 << CTSEN0_BIT);
  else
    UCSR0D_REGISTER = (1 << RTSEN0_BIT);
}
    #define uart_hardware_flow_control_init(__ctsenable, __rtsenable)          \
      uart0_hardware_flow_control_init(__ctsenable, __rtsenable)
  #endif

  #define uart_init(__ubrr)             uart0_init(__ubrr)
  #define uart_reinit(__ubrr)           uart0_reinit(__ubrr)
  #define uart_set_FrameFormat(__ucsrc) uart0_set_FrameFormat(__ucsrc)
  #define uart_set_U2X()                uart0_set_U2X()
  #define uart_mpcm_slave_return_idle() uart0_mpcm_slave_return_idle()
#endif // USE_USART0

#ifdef USE_USART1
// for runtime reinitialization of uart
void uart1_reinit(uint16_t ubrr_value);

// have to be called once at startup
static inline void uart1_init(uint16_t ubrr_value)
    __attribute__((always_inline));
static inline void uart1_init(uint16_t ubrr_value) {
  #ifdef USART1_RS485_MODE
  RS485_CONTROL1_DDR |= (1 << RS485_CONTROL1_IONUM); // default pin state is low
  #endif

  UBRR1L_REGISTER = (uint8_t)ubrr_value;

  #ifdef USART_SKIP_UBRRH_IF_ZERO
  if (__builtin_constant_p(ubrr_value))
    // requires -Os flag - do not use in non-inline functions
    if (((ubrr_value >> 8) != 0))
  #endif
      UBRR1H_REGISTER = (ubrr_value >> 8);

  #if __AVR_ARCH__ == 103
    #if (!defined(NO_RX1_INTERRUPT) && !defined(NO_TX1_INTERRUPT))
      #ifdef USART1_RS485_MODE
  UCSR1A_REGISTER = (1 << RXCIE1_BIT) | (1 << TXCIE1_BIT) | (1 << RS485_BIT);
      #else
  UCSR1A_REGISTER = (1 << RXCIE1_BIT);
      #endif
    #endif
  #else
    #ifdef USART1_U2X_SPEED
      #ifdef USART1_MPCM_MODE
  UCSR1A_REGISTER = (1 << U2X1_BIT) | (1 << MPCM1_BIT);
      #else
  UCSR1A_REGISTER = (1 << U2X1_BIT); // enable double speed
      #endif
    #elif defined(USART1_MPCM_MODE)
  UCSR1A_REGISTER |= (1 << MPCM1_BIT);
    #endif
  #endif

  UCSR1B_REGISTER = USART1_CONFIG_B;
    // 8n1 is set by default, setting UCSRC is not needed
  #if defined(USART1_MPCM_MODE) && __AVR_ARCH__ == 103
  UCSR1C_REGISTER = USART_CHSIZE_9BITH_gc;
  #endif

  #ifdef USART1_USE_SOFT_RTS
  RTS1_DDR |= (1 << RTS1_IONUM);
  #endif
}

// UCSRC_reg can be used to set other than 8n1 transmission
static inline void uart1_set_FrameFormat(uint8_t UCSRC_reg)
    __attribute__((always_inline));
static inline void uart1_set_FrameFormat(uint8_t UCSRC_reg) {
  UCSR1C_REGISTER = UCSRC_reg;
}

// use instead of USE_DOUBLE_SPEED
static inline void uart1_set_U2X(void) __attribute__((always_inline));
static inline void uart1_set_U2X(void) {
  #if __AVR_ARCH__ == 103
  UCSR1B_REGISTER = (1 << U2X1_BIT);
  #else
  UCSR1A_REGISTER |= (1 << U2X1_BIT);
  #endif
}

// enable UDRE interrupt
static inline void uart1_enable_UDREI(void) __attribute__((always_inline));
static inline void uart1_enable_UDREI(void) {
  #if __AVR_ARCH__ == 103
  UCSR1A_REGISTER |= (1 << UDRIE1_BIT);
  #else
  UCSR1B_REGISTER |= (1 << UDRIE1_BIT);
  #endif
}

  #ifdef USART1_MPCM_MODE
// return slave to mpcm idle mode (wait for own addres frame)
static inline void uart1_mpcm_slave_return_idle(void)
    __attribute__((always_inline));
static inline void uart1_mpcm_slave_return_idle(void) {
    #if __AVR_ARCH__ == 103
  UCSR1B_REGISTER = (1 << MPCM1_BIT);
    #else
  UCSR1A_REGISTER |= (1 << MPCM1_BIT);
    #endif
}
  #endif

  #ifdef USART1_HARDWARE_FLOW_CONTROL_AVAILABLE
// pass true to enable
static inline void uart1_hardware_flow_control_init(uint8_t ctsenable,
                                                    uint8_t rtsenable)
    __attribute__((always_inline));
static inline void uart1_hardware_flow_control_init(uint8_t ctsenable,
                                                    uint8_t rtsenable) {
  // -Os dependent, do not use in non-inline functions
  if (ctsenable && rtsenable)
    UCSR1D_REGISTER = (1 << CTSEN1_BIT) | (1 << RTSEN1_BIT);
  else if (ctsenable)
    UCSR1D_REGISTER = (1 << CTSEN1_BIT);
  else
    UCSR1D_REGISTER = (1 << RTSEN1_BIT);
}
  #endif

#endif // USE_USART1

#ifdef USE_USART2
// for runtime reinitialization of uart
void uart2_reinit(uint16_t ubrr_value);

// have to be called once at startup
static inline void uart2_init(uint16_t ubrr_value)
    __attribute__((always_inline));
static inline void uart2_init(uint16_t ubrr_value) {
  #ifdef USART2_RS485_MODE
  RS485_CONTROL2_DDR |= (1 << RS485_CONTROL2_IONUM); // default pin state is low
  #endif

  UBRR2L_REGISTER = (uint8_t)ubrr_value;

  #ifdef USART_SKIP_UBRRH_IF_ZERO
  if (__builtin_constant_p(ubrr_value))
    // requires -Os flag - do not use in non-inline functions
    if (((ubrr_value >> 8) != 0))
  #endif
      UBRR2H_REGISTER = (ubrr_value >> 8);

  #if __AVR_ARCH__ == 103
    #if (!defined(NO_RX2_INTERRUPT) && !defined(NO_TX2_INTERRUPT))
      #ifdef USART2_RS485_MODE
  UCSR2A_REGISTER = (1 << RXCIE2_BIT) | (1 << TXCIE2_BIT) | (1 << RS485_BIT);
      #else
  UCSR2A_REGISTER = (1 << RXCIE2_BIT);
      #endif
    #endif
  #else
    #ifdef USART2_U2X_SPEED
      #ifdef USART2_MPCM_MODE
  UCSR2A_REGISTER = (1 << U2X2_BIT) | (1 << MPCM2_BIT);
      #else
  UCSR2A_REGISTER = (1 << U2X2_BIT); // enable double speed
      #endif
    #elif defined(USART2_MPCM_MODE)
  UCSR2A_REGISTER |= (1 << MPCM2_BIT);
    #endif
  #endif

  UCSR2B_REGISTER = USART2_CONFIG_B;
    // 8n1 is set by default, setting UCSRC is not needed
  #if defined(USART2_MPCM_MODE) && __AVR_ARCH__ == 103
  UCSR2C_REGISTER = USART_CHSIZE_9BITH_gc;
  #endif

  #ifdef USART2_USE_SOFT_RTS
  RTS2_DDR |= (1 << RTS2_IONUM);
  #endif
}

// UCSRC_reg can be used to set other than 8n1 transmission
static inline void uart2_set_FrameFormat(uint8_t UCSRC_reg)
    __attribute__((always_inline));
static inline void uart2_set_FrameFormat(uint8_t UCSRC_reg) {
  UCSR2C_REGISTER = UCSRC_reg;
}

// use instead of USE_DOUBLE_SPEED
static inline void uart2_set_U2X(void) __attribute__((always_inline));
static inline void uart2_set_U2X(void) {
  #if __AVR_ARCH__ == 103
  UCSR2B_REGISTER = (1 << U2X2_BIT);
  #else
  UCSR2A_REGISTER |= (1 << U2X2_BIT);
  #endif
}

// enable UDRE interrupt
static inline void uart2_enable_UDREI(void) __attribute__((always_inline));
static inline void uart2_enable_UDREI(void) {
  #if __AVR_ARCH__ == 103
  UCSR2A_REGISTER |= (1 << UDRIE2_BIT);
  #else
  UCSR2B_REGISTER |= (1 << UDRIE2_BIT);
  #endif
}

  #ifdef USART2_MPCM_MODE
// return slave to mpcm idle mode (wait for own addres frame)
static inline void uart2_mpcm_slave_return_idle(void)
    __attribute__((always_inline));
static inline void uart2_mpcm_slave_return_idle(void) {
    #if __AVR_ARCH__ == 103
  UCSR2B_REGISTER = (1 << MPCM2_BIT);
    #else
  UCSR2A_REGISTER |= (1 << MPCM2_BIT);
    #endif
}
  #endif
#endif // USE_USART2

#ifdef USE_USART3
// for runtime reinitialization of uart
void uart3_reinit(uint16_t ubrr_value);

// have to be called once at startup
static inline void uart3_init(uint16_t ubrr_value)
    __attribute__((always_inline));
static inline void uart3_init(uint16_t ubrr_value) {
  #ifdef USART3_RS485_MODE
  RS485_CONTROL3_DDR |= (1 << RS485_CONTROL3_IONUM); // default pin state is low
  #endif

  UBRR3L_REGISTER = (uint8_t)ubrr_value;

  #ifdef USART_SKIP_UBRRH_IF_ZERO
  if (__builtin_constant_p(ubrr_value))
    if (((ubrr_value >> 8) !=
         0)) // requires -Os flag - do not use in non-inline functions
  #endif
      UBRR3H_REGISTER = (ubrr_value >> 8);

  #if __AVR_ARCH__ == 103
    #if (!defined(NO_RX3_INTERRUPT) && !defined(NO_TX3_INTERRUPT))
      #ifdef USART3_RS485_MODE
  UCSR3A_REGISTER = (1 << RXCIE3_BIT) | (1 << TXCIE3_BIT) | (1 << RS485_BIT);
      #else
  UCSR3A_REGISTER = (1 << RXCIE3_BIT);
      #endif
    #endif
  #else
    #ifdef USART3_U2X_SPEED
      #ifdef USART3_MPCM_MODE
  UCSR3A_REGISTER = (1 << U2X3_BIT) | (1 << MPCM3_BIT);
      #else
  UCSR3A_REGISTER = (1 << U2X3_BIT); // enable double speed
      #endif
    #elif defined(USART3_MPCM_MODE)
  UCSR3A_REGISTER |= (1 << MPCM3_BIT);
    #endif
  #endif

  UCSR3B_REGISTER = USART3_CONFIG_B;
  // 8n1 is set by default, setting UCSRC is not needed
  #if defined(USART3_MPCM_MODE) && __AVR_ARCH__ == 103
  UCSR3C_REGISTER = USART_CHSIZE_9BITH_gc;
  #endif

  #ifdef USART3_USE_SOFT_RTS
  RTS3_DDR |= (1 << RTS3_IONUM);
  #endif
}

// UCSRC_reg can be used to set other than 8n1 transmission
static inline void uart3_set_FrameFormat(uint8_t UCSRC_reg)
    __attribute__((always_inline));
static inline void uart3_set_FrameFormat(uint8_t UCSRC_reg) {
  UCSR3C_REGISTER = UCSRC_reg;
}

// use instead of USE_DOUBLE_SPEED
static inline void uart3_set_U2X(void) __attribute__((always_inline));
static inline void uart3_set_U2X(void) {
  #if __AVR_ARCH__ == 103
  UCSR3B_REGISTER = (1 << U2X3_BIT);
  #else
  UCSR3A_REGISTER |= (1 << U2X3_BIT);
  #endif
}

// enable UDRE interrupt
static inline void uart3_enable_UDREI(void) __attribute__((always_inline));
static inline void uart3_enable_UDREI(void) {
  #if __AVR_ARCH__ == 103
  UCSR3A_REGISTER |= (1 << UDRIE3_BIT);
  #else
  UCSR3B_REGISTER |= (1 << UDRIE3_BIT);
  #endif
}

  #ifdef USART3_MPCM_MODE
// return slave to mpcm idle mode (wait for own addres frame)
static inline void uart3_mpcm_slave_return_idle(void)
    __attribute__((always_inline));
static inline void uart3_mpcm_slave_return_idle(void) {
    #if __AVR_ARCH__ == 103
  UCSR3B_REGISTER = (1 << MPCM3_BIT);
    #else
  UCSR3A_REGISTER |= (1 << MPCM3_BIT);
    #endif
}
  #endif
#endif // USE_USART3

/************************************************************************************
 *                              Transmitter functions *
 ************************************************************************************/
#ifndef NO_USART_TX

  #ifndef NO_TX0_INTERRUPT
    #ifdef USART_NO_ABI_BREAKING_PREMATURES
void uart0_putc(char data);
inline char uart0_putc_(char data) __attribute__((always_inline));
inline char uart0_putc_(char data) {
  uart0_putc(data);
  return data;
}
    #else
// alias for uart_putc that returns passed argument unaffected by omitting any
// existent rule
void uart0_putc(char data) __attribute__((naked, noinline));
char uart0_putc_(char data) __attribute__((noinline));
    #endif

// returns BUFFER_FULL (false) if buffer is full and character cannot be sent at
// the moment
uint8_t uart0_putc_noblock(char data);

// send string from the memory buffer stops when NULL byte is hit (NULL byte is
// not included into transmission)
void uart0_putstr(char *string);

// send specified number of bytes from the pointed buffer (up to 255 bytes)
void uart0_putstrl(char *string, uint8_t BytesToWrite);

    // for deprecated usage only (wastes SRAM data memory to keep all string
    // constants), instead of this try to use puts_P
    #ifdef __cplusplus
      // macro to avoid const char* conversion restrictions
      #define uart0_puts(str) uart0_putstr((const char *)(str))
    #else
      #define uart0_puts(str) uart0_putstr(str)
    #endif

    // avrxmega3 has unified memory
    #if __AVR_ARCH__ != 103
      #ifdef __cplusplus
// send string from flash memory
void uart0_puts_p(const char *string);
      #else
// send string from flash memory
void uart0_puts_p(const __flash char *string);
      #endif

      // macro to automatically put a string constant into flash
      #define uart0_puts_P(__strP) uart0_puts_p(PSTR(__strP))
    #endif

void uart0_putint(int16_t data);
void uart0_putintr(int16_t data, uint8_t radix);

void uart0_putuint(uint16_t data);
void uart0_putuintr(uint16_t data, uint8_t radix);

void uart0_puthex(uint8_t data);

void uart0_putlong(int32_t data);
void uart0_putlongr(int32_t data, uint8_t radix);

void uart0_putulong(uint32_t data);
void uart0_putulongr(uint32_t data, uint8_t radix);

void uart0_putfloat(float data);
void uart0_fputfloat(float data, uint8_t precision);

void uart0_flush(void); // flush tx buffer

extern volatile uint8_t tx0_Head, tx0_Tail;
inline uint8_t uart0_BytesToSend(void) {
  return (tx0_Head - tx0_Tail - 1) & TX0_BUFFER_MASK;
}
    // returns number of bytes waiting in the transmit buffer

    #ifdef USART0_MPCM_MODE
void uart0_mpcm_transmit_addres_Frame(uint8_t dat);
    #endif

    #define uart_putc(__data)            uart0_putc(__data)
    #define uart_putc_(__data)           uart0_putc_(__data)
    #define uart_putc_noblock(__data)    uart0_putc_noblock(__data)
    #define uart_putstrl(__str, __limit) uart0_putstrl(__str, __limit)
    #define uart_putstr(__str)           uart0_putstr(__str)
    #define uart_puts(__str)             uart0_puts(__str)
    #if __AVR_ARCH__ != 103
      #define uart_puts_p(__strP) uart0_puts_p(__strP)
      #define uart_puts_P(__str)  uart0_puts_P(__str)
    #endif
    #define uart_putint(__data)             uart0_putint(__data)
    #define uart_putintr(__data, __radix)   uart0_putintr(__data, __radix)
    #define uart_putuint(__data)            uart0_putuint(__data)
    #define uart_putuintr(__data, __radix)  uart0_putuintr(__data, __radix)
    #define uart_puthex(__data)             uart0_puthex(__data)
    #define uart_putlong(__data)            uart0_putlong(__data)
    #define uart_putlongr(__data, __radix)  uart0_putlongr(__data, __radix)
    #define uart_putulong(__data)           uart0_putulong(__data)
    #define uart_putulongr(__data, __radix) uart0_putulongr(__data, __radix)
    #define uart_putfloat(__data)           uart0_putfloat(__data)
    #define uart_fputfloat(__data, __precision)                                \
      uart0_fputfloat(__data, __precision)
    #define uart_flush() uart0_flush()

    #ifdef USART0_MPCM_MODE
      #define uart_mpcm_transmit_addres_Frame(__data)                          \
        uart0_mpcm_transmit_addres_Frame(__data)
    #endif
  #endif // NO_TX0_INTERRUPT

  #ifndef NO_TX1_INTERRUPT
    #ifdef USART_NO_ABI_BREAKING_PREMATURES
void uart1_putc(char data);
inline char uart1_putc_(char data) __attribute__((always_inline));
inline char uart1_putc_(char data) {
  uart1_putc(data);
  return data;
}
    #else
// alias for uart_putc that returns passed argument unaffected by omitting any
// existent rule
void uart1_putc(char data) __attribute__((naked, noinline));
char uart1_putc_(char data) __attribute__((noinline));
    #endif

// returns BUFFER_FULL (false) if buffer is full and character cannot be sent at
// the moment
uint8_t uart1_putc_noblock(char data);

// send string from the memory buffer stops when NULL byte is hit (NULL byte is
// not included into transmission)
void uart1_putstr(char *string);

// send specified number of bytes from the pointed buffer (up to 255 bytes)
void uart1_putstrl(char *string, uint8_t BytesToWrite);

    // for deprecated usage only (wastes SRAM data memory to keep all string
    // constants), instead of this try to use puts_P
    #ifdef __cplusplus
      // macro to avoid const char* conversion restrictions
      #define uart1_puts(str) uart1_putstr((const char *)(str))
    #else
      #define uart1_puts(str) uart1_putstr(str)
    #endif

    // avrxmega3 has unified memory
    #if __AVR_ARCH__ != 103
      // macro to automatically put a string constant into flash
      #ifdef __cplusplus
// send string from flash memory
void uart1_puts_p(const char *string);
      #else
// send string from flash memory
void uart1_puts_p(const __flash char *string);
      #endif
      #define uart1_puts_P(__s) uart1_puts_p(PSTR(__s))
    #endif

void uart1_putint(int16_t data);
void uart1_putintr(int16_t data, uint8_t radix);

void uart1_putuint(uint16_t data);
void uart1_putuintr(uint16_t data, uint8_t radix);

void uart1_puthex(uint8_t data);

void uart1_putlong(int32_t data);
void uart1_putlongr(int32_t data, uint8_t radix);

void uart1_putulong(uint32_t data);
void uart1_putulongr(uint32_t data, uint8_t radix);

void uart1_putfloat(float data);
void uart1_fputfloat(float data, uint8_t precision);

void uart1_flush(void); // flush tx buffer

extern volatile uint8_t tx1_Head, tx1_Tail;

// returns number of bytes waiting in the transmit buffer
inline uint8_t uart1_BytesToSend(void) {
  return (tx1_Head - tx1_Tail - 1) & TX1_BUFFER_MASK;
}

    #ifdef USART1_MPCM_MODE
void uart1_mpcm_transmit_addres_Frame(uint8_t dat);
    #endif
  #endif // NO_TX1_INTERRUPT

  #ifndef NO_TX2_INTERRUPT
    #ifdef USART_NO_ABI_BREAKING_PREMATURES
void uart2_putc(char data);
inline char uart2_putc_(char data) __attribute__((always_inline));
inline char uart2_putc_(char data) {
  uart2_putc(data);
  return data;
}
    #else
// alias for uart_putc that returns passed argument unaffected by omitting any
// existent rule
void uart2_putc(char data) __attribute__((naked, noinline));
char uart2_putc_(char data) __attribute__((noinline));
    #endif

// returns BUFFER_FULL (false) if buffer is full and character cannot be sent at
// the moment
uint8_t uart2_putc_noblock(char data);

// send string from the memory buffer stops when NULL byte is hit (NULL byte is
// not included into transmission)
void uart2_putstr(char *string);

// send specified number of bytes from the pointed buffer (up to 255 bytes)
void uart2_putstrl(char *string, uint8_t BytesToWrite);

    // for deprecated usage only (wastes SRAM data memory to keep all string
    // constants), instead of this try to use puts_P
    #ifdef __cplusplus
      // macro to avoid const char* conversion restrictions
      #define uart2_puts(str) uart2_putstr((const char *)(str))
    #else
      #define uart2_puts(str) uart2_putstr(str)
    #endif

    // avrxmega3 has unified memory
    #if __AVR_ARCH__ != 103
      // macro to automatically put a string constant into flash
      #ifdef __cplusplus
// send string from flash memory
void uart2_puts_p(const char *string);
      #else
// send string from flash memory
void uart2_puts_p(const __flash char *string);
      #endif
      #define uart2_puts_P(__s) uart2_puts_p(PSTR(__s))
    #endif

void uart2_putint(int16_t data);
void uart2_putintr(int16_t data, uint8_t radix);

void uart2_putuint(uint16_t data);
void uart2_putuintr(uint16_t data, uint8_t radix);

void uart2_puthex(uint8_t data);

void uart2_putlong(int32_t data);
void uart2_putlongr(int32_t data, uint8_t radix);

void uart2_putulong(uint32_t data);
void uart2_putulongr(uint32_t data, uint8_t radix);

void uart2_putfloat(float data);
void uart2_fputfloat(float data, uint8_t precision);

void uart2_flush(void); // flush tx buffer

extern volatile uint8_t tx2_Head, tx2_Tail;

// returns number of bytes waiting in the transmit buffer
inline uint8_t uart2_BytesToSend(void) {
  return (tx2_Head - tx2_Tail - 1) & TX2_BUFFER_MASK;
}

    #ifdef USART2_MPCM_MODE
void uart2_mpcm_transmit_addres_Frame(uint8_t dat);
    #endif
  #endif // NO_TX2_INTERRUPT

  #ifndef NO_TX3_INTERRUPT
    #ifdef USART_NO_ABI_BREAKING_PREMATURES
void uart3_putc(char data);
inline char uart3_putc_(char data) __attribute__((always_inline));
inline char uart3_putc_(char data) {
  uart3_putc(data);
  return data;
}
    #else
// alias for uart_putc that returns passed argument unaffected by omitting any
// existent rule
void uart3_putc(char data) __attribute__((naked, noinline));
char uart3_putc_(char data) __attribute__((noinline));
    #endif

// returns BUFFER_FULL (false) if buffer is full and character cannot be sent at
// the moment
uint8_t uart3_putc_noblock(char data);

// send string from the memory buffer stops when NULL byte is hit (NULL byte is
// not included into transmission)
void uart3_putstr(char *string);

// send specified number of bytes from the pointedbuffer (up to 255 bytes)
void uart3_putstrl(char *string, uint8_t BytesToWrite);

    // for deprecated usage only (wastes SRAM data memory to keep all string
    // constants), instead of this try to use puts_P
    #ifdef __cplusplus
      // macro to avoid const char* conversion restrictions
      #define uart3_puts(str) uart3_putstr((const char *)(str))
    #else
      #define uart3_puts(str) uart3_putstr(str)
    #endif

    #if __AVR_ARCH__ != 103
      // macro to automatically put a string constant into flash
      #ifdef __cplusplus
// send string from flash memory
void uart3_puts_p(const char *string);
      #else
// send string from flash memory
void uart3_puts_p(const __flash char *string);
      #endif
      #define uart3_puts_P(__s) uart3_puts_p(PSTR(__s))
    #endif

void uart3_putint(int16_t data);
void uart3_putintr(int16_t data, uint8_t radix);

void uart3_putuint(uint16_t data);
void uart3_putuintr(uint16_t data, uint8_t radix);

void uart3_puthex(uint8_t data);

void uart3_putlong(int32_t data);
void uart3_putlongr(int32_t data, uint8_t radix);

void uart3_putulong(uint32_t data);
void uart3_putulongr(uint32_t data, uint8_t radix);

void uart3_putfloat(float data);
void uart3_fputfloat(float data, uint8_t precision);

void uart3_flush(void); // flush tx buffer

extern volatile uint8_t tx3_Head, tx3_Tail;

// returns number of bytes waiting in the transmit buffer
inline uint8_t uart3_BytesToSend(void) {
  return (tx3_Head - tx3_Tail - 1) & TX3_BUFFER_MASK;
}

    #ifdef USART3_MPCM_MODE
void uart3_mpcm_transmit_addres_Frame(uint8_t dat);
    #endif
  #endif // NO_TX0_INTERRUPT

#endif // NO_USART_TX

/************************************************************************************
 *                                Receiver functions *
 ************************************************************************************/
#ifndef NO_USART_RX

  #ifndef NO_RX0_INTERRUPT
// get character from receiver ring buffer
char uart0_getc(void);

// reads whole receiver buffer or bufferlimit-1 characters
// newline terminator will not be cut
// adds NULL byte at the end of string
void uart0_gets(char *buffer, uint8_t bufferlimit);

// reads one line from the buffer
// waits for newline terminator or reached bufferlimit
// adds NULL byte at the end of string
void uart0_getln(char *buffer, uint8_t bufferlimit);

// read one line to the first whitespace after the string
// cuts all whitespaces before string and one after the string
void uart0_getlnToFirstWhiteSpace(char *buffer, uint8_t bufferlimit);

// returns first nonspace character found in the buffer
inline char uart0_skipWhiteSpaces(void) {
  char c;
  do {
    c = uart0_getc();
  } while (c <= 32);
  return c;
}

int16_t uart0_getint(void);
int32_t uart0_getlong(void);
float uart0_getfloat(void);

// reads single byte from a buffer
// returns negative value if buffer is empty (upper byte is non zero)
int16_t uart0_getData(void);

// reads single byte from a buffer and loads it into *data byte
// in case of empty buffers returned flag is set to BUFFER_EMPTY - NULL
uint8_t uart0_LoadData(uint8_t *data);

extern volatile uint8_t rx0_Head, rx0_Tail;

// returns number of bytes waiting in the receiver buffer
inline uint8_t uart0_AvailableBytes(void) {
  return (rx0_Head - rx0_Tail) & RX0_BUFFER_MASK;
}

// returns next byte from buffer
// returned byte is invalid if there is nothing to read
uint8_t uart0_peek(void);

    #define uart_getc()                    uart0_getc()
    #define uart_gets(__buffptr, __limit)  uart0_gets(__buffptr, __limit)
    #define uart_getln(__buffptr, __limit) uart0_getln(__buffptr, __limit)
    #define uart_getlnToFirstWhiteSpace(__buffptr, __limit)                    \
      uart0_getlnToFirstWhiteSpace(__buffptr, __limit)
    #define uart_skipWhiteSpaces()   uart0_skipWhiteSpaces()
    #define uart_getint()            uart0_getint()
    #define uart_getlong()           uart0_getlong()
    #define uart_getfloat()          uart0_getfloat()
    #define uart_getData()           uart0_getData()
    #define uart_LoadData(__dataptr) uart0_LoadData(__dataptr)
    #define uart_AvailableBytes()    uart0_AvailableBytes()
    #define uart_peek()              uart0_peek()
  #endif // NO_RX0_INTERRUPT

  #ifndef NO_RX1_INTERRUPT
// get character from receiver ring buffer
char uart1_getc(void);

// reads whole receiver buffer or bufferlimit-1 characters
// newline terminator will not be cut
// adds NULL byte at the end of string
void uart1_gets(char *buffer, uint8_t bufferlimit);

// reads one line from the buffer
// waits for newline terminator or reached bufferlimit
// adds NULL byte at the end of string
void uart1_getln(char *buffer, uint8_t bufferlimit);

// read one line to the first whitespace after the string
// cuts all whitespaces before string and one after the string
void uart1_getlnToFirstWhiteSpace(char *buffer, uint8_t bufferlimit);

// returns first nonspace character found in the buffer
inline char uart1_skipWhiteSpaces(void) {
  char c;
  do {
    c = uart1_getc();
  } while (c <= 32);
  return c;
}

int16_t uart1_getint(void);
int32_t uart1_getlong(void);
float uart1_getfloat(void);

// reads single byte from a buffer
// returns negative value if buffer is empty (upper byte is non zero)
int16_t uart1_getData(void);

// reads single byte from a buffer and loads it into *data byte
// in case of empty buffers returned flag is set to BUFFER_EMPTY - NULL
uint8_t uart1_LoadData(uint8_t *data);

extern volatile uint8_t rx1_Head, rx1_Tail;

// returns number of bytes waiting in the receiver buffer
inline uint8_t uart1_AvailableBytes(void) {
  return (rx1_Head - rx1_Tail) & RX1_BUFFER_MASK;
}

// returns next byte from buffer
// returned byte is invalid if there is nothing to read
uint8_t uart1_peek(void);
  #endif // NO_RX0_INTERRUPT

  #ifndef NO_RX2_INTERRUPT
// get character from receiver ring buffer
char uart2_getc(void);

// reads whole receiver buffer or bufferlimit-1 characters
// newline terminator will not be cut
// adds NULL byte at the end of string
void uart2_gets(char *buffer, uint8_t bufferlimit);

// reads one line from the buffer
// waits for newline terminator or reached bufferlimit
// adds NULL byte at the end of string
void uart2_getln(char *buffer, uint8_t bufferlimit);

// read one line to the first whitespace after the string
// cuts all whitespaces before string and one after the string
void uart2_getlnToFirstWhiteSpace(char *buffer, uint8_t bufferlimit);

// returns first nonspace character found in the buffer
inline char uart2_skipWhiteSpaces(void) {
  char c;
  do {
    c = uart2_getc();
  } while (c <= 32);
  return c;
}

int16_t uart2_getint(void);
int32_t uart2_getlong(void);
float uart2_getfloat(void);

// reads single byte from a buffer
// returns negative value if buffer is empty (upper byte is non zero)
int16_t uart2_getData(void);

// reads single byte from a buffer and loads it into *data byte
// in case of empty buffers returned flag is set to BUFFER_EMPTY - NULL
uint8_t uart2_LoadData(uint8_t *data);

extern volatile uint8_t rx2_Head, rx2_Tail;

// returns number of bytes waiting in the receiver buffer
inline uint8_t uart2_AvailableBytes(void) {
  return (rx2_Head - rx2_Tail) & RX2_BUFFER_MASK;
}

// returns next byte from buffer
// returned byte is invalid if there is nothing to read
uint8_t uart2_peek(void);
  #endif // NO_RX0_INTERRUPT

  #ifndef NO_RX3_INTERRUPT
// get character from receiver ring buffer
char uart3_getc(void);

// reads whole receiver buffer or bufferlimit-1 characters
// newline terminator will not be cut
// adds NULL byte at the end of string
void uart3_gets(char *buffer, uint8_t bufferlimit);

// reads one line from the buffer
// waits for newline terminator or reached bufferlimit
// adds NULL byte at the end of string
void uart3_getln(char *buffer, uint8_t bufferlimit);

// read one line to the first whitespace after the string
// cuts all whitespaces before string and one after the string
void uart3_getlnToFirstWhiteSpace(char *buffer, uint8_t bufferlimit);

// returns first nonspace character found in the buffer
inline char uart3_skipWhiteSpaces(void) {
  char c;
  do {
    c = uart3_getc();
  } while (c <= 32);
  return c;
}

int16_t uart3_getint(void);
int32_t uart3_getlong(void);
float uart3_getfloat(void);

// reads single byte from a buffer
// returns negative value if buffer is empty (upper byte is non zero)
int16_t uart3_getData(void);

// reads single byte from a buffer and  loads it into *data byte
// in case of empty buffers returned flag is set to BUFFER_EMPTY - NULL
uint8_t uart3_LoadData(uint8_t *data);

extern volatile uint8_t rx3_Head, rx3_Tail;

// returns number of bytes waiting in the receiver buffer
inline uint8_t uart3_AvailableBytes(void) {
  return (rx3_Head - rx3_Tail) & RX3_BUFFER_MASK;
}

// returns next byte from buffer
// returned byte is invalid if there is nothing to read
uint8_t uart3_peek(void);
  #endif // NO_RX0_INTERRUPT

#endif // NO_USART_RX

/************************************************************************************
 *                       soft flow control interrupt handlers *
 ************************************************************************************/

#if defined(USART0_USE_SOFT_CTS) && !defined(NO_TX0_INTERRUPT)
extern volatile uint8_t tx0_Tail, tx0_Head;

static inline void cts0_isr_handler(void) __attribute__((always_inline));
static inline void cts0_isr_handler(void) {
  #if defined(USART0_IN_IO_ADDRESS_SPACE)
  if (CTS0_PIN & (1 << CTS0_IONUM)) {
    UCSR0B_REGISTER &= ~(1 << UDRIE0_BIT);
  } else if (tx0_Tail != tx0_Head) {
    UCSR0B_REGISTER |= (1 << UDRIE0_BIT);
  }
  #else
    #if __AVR_ARCH__ == 103
  uint8_t tmp = UCSR0A_REGISTER;
    #else
  uint8_t tmp = UCSR0B_REGISTER;
    #endif

  if (CTS0_PIN & (1 << CTS0_IONUM)) {
    tmp &= ~(1 << UDRIE0_BIT);
  } else if (tx0_Tail != tx0_Head) {
    tmp |= (1 << UDRIE0_BIT);
  }
    #if __AVR_ARCH__ == 103
  UCSR0A_REGISTER = tmp;
    #else
  UCSR0B_REGISTER = tmp;
    #endif
  #endif
}

static inline void naked_cts0_isr_handler(void) __attribute__((always_inline));
static inline void naked_cts0_isr_handler(void) {
  asm volatile("\n\t"

  #if defined(USART0_IN_IO_ADDRESS_SPACE)
               "cbi %M[UDRIE_reg_IO], %M[UDRIE_bit] \n\t"
               "sbic %M[cts_port], %M[cts_pin] \n\t"
               "reti \n\t"

    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "lds %B[z_save], (tx0_Tail) \n\t"
               "lds %A[z_save], (tx0_Head) \n\t"
               "cpse %B[z_save], %A[z_save] \n\t"
               "sbi %M[UDRIE_reg_IO], %M[UDRIE_bit] \n\t"
    #elif defined(USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE)
               "push r25 \n\t"

               "lds r25, (tx0_Tail) \n\t"
               "lds %[sreg_save], (tx0_Head) \n\t"
               "cpse r25, %[sreg_save] \n\t"
               "sbi %M[UDRIE_reg_IO], %M[UDRIE_bit] \n\t"

               "pop r25 \n\t"
    #else
               "push r24 \n\t"
               "push r25 \n\t"

               "lds r25, (tx0_Tail) \n\t"
               "lds r24, (tx0_Head) \n\t"
               "cpse r25, r24 \n\t"
               "sbi %M[UDRIE_reg_IO], %M[UDRIE_bit] \n\t"

               "pop r25 \n\t"
               "pop r24 \n\t"
    #endif

               "reti \n\t"

  #else // USART not in IO

    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
               "in %[sreg_save], __SREG__ \n\t"
    #else
               "push r16 \n\t"
               "in r16,__SREG__ \n\t"
    #endif

    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
      #ifdef __AVR_HAVE_MOVW__
               "movw %[z_save], r24 \n\t"
      #else // in this case only 4 cycles are prematured out
               "mov %A[z_save], r24 \n\t"
               "mov %B[z_save], r25 \n\t"
      #endif
    #else
               "push r24 \n\t"
    #endif

    #ifdef USART0_IN_UPPER_IO_ADDRESS_SPACE
               "in r24, %M[UDRIE_reg_IO] \n\t"
    #else
               "lds r24, %M[UDRIE_reg] \n\t"
    #endif

               "andi r24, ~(1<<%M[UDRIE_bit]) \n\t"
               "sbic %M[cts_port], %M[cts_pin]\n\t"
               "rjmp cts_apply_%= \n\t"

    #ifndef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "push r25 \n\t"
    #endif
               "lds r24, (tx0_Head) \n\t"
               "lds r25, (tx0_Tail) \n\t"
               "cp r25, r24 \n\t"

    #ifndef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "pop r25 \n\t" // branch after poping
    #endif
               "breq cts_exit_%= \n\t" // UDRIE should be disabled in this case
                                       // - no need to clear it

    #ifdef USART0_IN_UPPER_IO_ADDRESS_SPACE
               "in r24, %M[UDRIE_reg_IO] \n\t"
    #else
               "lds r24, %M[UDRIE_reg] \n\t"
    #endif
               "ori r24, (1<<%M[UDRIE_bit]) \n\t"

               "cts_apply_%=:"
    #ifdef USART0_IN_UPPER_IO_ADDRESS_SPACE
               "out %M[UDRIE_reg_IO], r24 \n\t"
    #else
               "sts %M[UDRIE_reg], r24 \n\t"
    #endif

               "cts_exit_%=:"
    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
      #ifdef __AVR_HAVE_MOVW__
               "movw r24, %[z_save] \n\t"
      #else // in this case only 4 cycles are prematured out
               "mov r25, %B[z_save] \n\t"
               "mov r24, %A[z_save] \n\t"
      #endif
    #else
               "pop r24 \n\t"
    #endif

    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
               "out __SREG__, %[sreg_save] \n\t"
    #else
               "out __SREG__, r16 \n\t"
               "pop r16 \n\t"
    #endif

               "reti \n\t"
  #endif
               : /* output operands */
               USART_REG_SAVE_LIST
               : /* input operands */
  #if __AVR_ARCH__ == 103
               [UDRIE_reg] "n"(_SFR_MEM_ADDR(UCSR0A_REGISTER)),
               [UDRIE_reg_IO] "M"(_SFR_IO_ADDR(UCSR0A_REGISTER)),
  #else
               [UDRIE_reg] "n"(_SFR_MEM_ADDR(UCSR0B_REGISTER)),
               [UDRIE_reg_IO] "M"(_SFR_IO_ADDR(UCSR0B_REGISTER)),
  #endif
               [UDRIE_bit] "M"(UDRIE0_BIT),
               [cts_port] "M"(_SFR_IO_ADDR(CTS0_PIN)), [cts_pin] "M"(CTS0_IONUM)
               : /* clobbers */
  );
}
#endif

#if defined(USART1_USE_SOFT_CTS) && !defined(NO_TX1_INTERRUPT)
extern volatile uint8_t tx1_Tail, tx1_Head;

static inline void cts1_isr_handler(void) __attribute__((always_inline));
static inline void cts1_isr_handler(void) {
  #if defined(USART1_IN_IO_ADDRESS_SPACE)
  if (CTS1_PIN & (1 << CTS1_IONUM)) {
    UCSR1B_REGISTER &= ~(1 << UDRIE1_BIT);
  } else if (tx1_Tail != tx1_Head) {
    UCSR1B_REGISTER |= (1 << UDRIE1_BIT);
  }
  #else
  uint8_t tmp = UCSR1B_REGISTER;
  if (CTS1_PIN & (1 << CTS1_IONUM)) {
    tmp &= ~(1 << UDRIE1_BIT);
  } else if (tx1_Tail != tx1_Head) {
    tmp |= (1 << UDRIE1_BIT);
  }
  UCSR1B_REGISTER = tmp;
  #endif
}

static inline void naked_cts1_isr_handler(void) __attribute__((always_inline));
static inline void naked_cts1_isr_handler(void) {
  asm volatile("\n\t"

  #if defined(USART1_IN_IO_ADDRESS_SPACE)
               "cbi %M[UDRIE_reg_IO], %M[UDRIE_bit] \n\t"
               "sbic %M[cts_port], %M[cts_pin] \n\t"
               "reti \n\t"

    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "lds %B[z_save], (tx1_Tail) \n\t"
               "lds %A[z_save], (tx1_Head) \n\t"
               "cpse %B[z_save], %A[z_save] \n\t"
               "sbi %M[UDRIE_reg_IO], %M[UDRIE_bit] \n\t"

    #elif defined(USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE)
               "push r25 \n\t"

               "lds r25, (tx1_Tail) \n\t"
               "lds %[sreg_save], (tx1_Head) \n\t"
               "cpse r25, %[sreg_save] \n\t"
               "sbi %M[UDRIE_reg_IO], %M[UDRIE_bit] \n\t"

               "pop r25 \n\t"
    #else
               "push r24 \n\t"
               "push r25 \n\t"

               "lds r25, (tx1_Tail) \n\t"
               "lds r24, (tx1_Head) \n\t"
               "cpse r25, r24 \n\t"
               "sbi %M[UDRIE_reg_IO], %M[UDRIE_bit] \n\t"

               "pop r25 \n\t"
               "pop r24 \n\t"
    #endif

               "reti \n\t"

  #else // USART not in IO
    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
               "in %[sreg_save], __SREG__ \n\t"
    #else
               "push r16 \n\t"
               "in r16,__SREG__ \n\t"
    #endif

    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "movw %[z_save], r24 \n\t"
    #else
               "push r24 \n\t"
    #endif

               "lds r24, %M[UDRIE_reg] \n\t"
               "andi r24, ~(1<<%M[UDRIE_bit]) \n\t"
               "sbic %M[cts_port], %M[cts_pin]\n\t"
               "rjmp cts_apply_%= \n\t"

    #ifndef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "push r25 \n\t"
    #endif
               "lds r24, (tx1_Head) \n\t"
               "lds r25, (tx1_Tail) \n\t"
               "cp r25, r24 \n\t"

    #ifndef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "pop r25 \n\t"
    #endif
               "breq cts_exit_%= \n\t" // UDRIE should be disabled in this case
                                       // - no need to clear it

               "lds r24, %M[UDRIE_reg] \n\t"
               "ori r24, (1<<%M[UDRIE_bit]) \n\t"
               "cts_apply_%=:"
               "sts %M[UDRIE_reg], r24 \n\t"

               "cts_exit_%=:"
    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "movw r24, %[z_save] \n\t"
    #else
               "pop r24 \n\t"
    #endif

    #ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
               "out __SREG__, %[sreg_save] \n\t"
    #else
               "out __SREG__, r16 \n\t"
               "pop r16 \n\t"
    #endif

               "reti \n\t"
  #endif
               : /* output operands */
               USART_REG_SAVE_LIST
               : /* input operands */
  #if __AVR_ARCH__ == 103
               [UDRIE_reg] "n"(_SFR_MEM_ADDR(UCSR1A_REGISTER)),
               [UDRIE_reg_IO] "M"(_SFR_IO_ADDR(UCSR1A_REGISTER)),
  #else
               [UDRIE_reg] "n"(_SFR_MEM_ADDR(UCSR1B_REGISTER)),
               [UDRIE_reg_IO] "M"(_SFR_IO_ADDR(UCSR1B_REGISTER)),
  #endif
               [UDRIE_bit] "M"(UDRIE1_BIT),
               [cts_port] "M"(_SFR_IO_ADDR(CTS1_PIN)), [cts_pin] "M"(CTS1_IONUM)
               : /* clobbers */
  );
}
#endif

#if defined(USART2_USE_SOFT_CTS) && !defined(NO_TX2_INTERRUPT)
extern volatile uint8_t tx2_Tail, tx2_Head;

static inline void cts2_isr_handler(void) __attribute__((always_inline));
static inline void cts2_isr_handler(void) {
  uint8_t tmp = UCSR2B_REGISTER;
  if (CTS2_PIN & (1 << CTS2_IONUM)) {
    tmp &= ~(1 << UDRIE2_BIT);
  } else if (tx2_Tail != tx2_Head) {
    tmp |= (1 << UDRIE2_BIT);
  }
  UCSR2B_REGISTER = tmp;
}

static inline void naked_cts2_isr_handler(void) __attribute__((always_inline));
static inline void naked_cts2_isr_handler(void) {
  asm volatile("\n\t"

  #ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
               "in %[sreg_save], __SREG__ \n\t"
  #else
               "push r16 \n\t"
               "in r16,__SREG__ \n\t"
  #endif

  #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "movw %[z_save], r24 \n\t"
  #else
               "push r24 \n\t"
  #endif

               "lds r24, %M[UDRIE_reg] \n\t"
               "andi r24, ~(1<<%M[UDRIE_bit]) \n\t"
               "sbic %M[cts_port], %M[cts_pin]\n\t"
               "rjmp cts_apply_%= \n\t"

  #ifndef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "push r25 \n\t"
  #endif
               "lds r24, (tx2_Head) \n\t"
               "lds r25, (tx2_Tail) \n\t"
               "cp r25, r24 \n\t"

  #ifndef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "pop r25 \n\t"
  #endif
               "breq cts_exit_%= \n\t" // UDRIE should be disabled in this case
                                       // - no need to clear it

               "lds r24, %M[UDRIE_reg] \n\t"
               "ori r24, (1<<%M[UDRIE_bit]) \n\t"
               "cts_apply_%=:"
               "sts %M[UDRIE_reg], r24 \n\t"

               "cts_exit_%=:"
  #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "movw r24, %[z_save] \n\t"
  #else
               "pop r24 \n\t"
  #endif

  #ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
               "out __SREG__, %[sreg_save] \n\t"
  #else
               "out __SREG__, r16 \n\t"
               "pop r16 \n\t"
  #endif

               "reti \n\t"
               : /* output operands */
               USART_REG_SAVE_LIST
               : /* input operands */
  #if __AVR_ARCH__ == 103
               [UDRIE_reg] "n"(_SFR_MEM_ADDR(UCSR2A_REGISTER)),
  #else
               [UDRIE_reg] "n"(_SFR_MEM_ADDR(UCSR2B_REGISTER)),
  #endif
               [UDRIE_bit] "M"(UDRIE2_BIT),
               [cts_port] "M"(_SFR_IO_ADDR(CTS2_PIN)), [cts_pin] "M"(CTS2_IONUM)
               : /* clobbers */
  );
}
#endif

#if defined(USART3_USE_SOFT_CTS) && !defined(NO_TX3_INTERRUPT)
extern volatile uint8_t tx3_Tail, tx3_Head;

static inline void cts3_isr_handler(void) __attribute__((always_inline));
static inline void cts3_isr_handler(void) {
  uint8_t tmp = UCSR3B_REGISTER;
  if (CTS3_PIN & (1 << CTS3_IONUM)) {
    tmp &= ~(1 << UDRIE3_BIT);
  } else if (tx3_Tail != tx3_Head) {
    tmp |= (1 << UDRIE3_BIT);
  }
  UCSR3B_REGISTER = tmp;
}

static inline void naked_cts3_isr_handler(void) __attribute__((always_inline));
static inline void naked_cts3_isr_handler(void) {
  asm volatile("\n\t"

  #ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
               "in %[sreg_save], __SREG__ \n\t"
  #else
               "push r16 \n\t"
               "in r16,__SREG__ \n\t"
  #endif

  #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "movw %[z_save], r24 \n\t"
  #else
               "push r24 \n\t"
  #endif

               "lds r24, %M[UDRIE_reg] \n\t"
               "andi r24, ~(1<<%M[UDRIE_bit]) \n\t"
               "sbic %M[cts_port], %M[cts_pin]\n\t"
               "rjmp cts_apply_%= \n\t"

  #ifndef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "push r25 \n\t"
  #endif
               "lds r24, (tx3_Head) \n\t"
               "lds r25, (tx3_Tail) \n\t"
               "cp r25, r24 \n\t"

  #ifndef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "pop r25 \n\t"
  #endif
               "breq cts_exit_%= \n\t" // UDRIE should be disabled in this case
                                       // - no need to clear it

               "lds r24, %M[UDRIE_reg] \n\t"
               "ori r24, (1<<%M[UDRIE_bit]) \n\t"
               "cts_apply_%=:"
               "sts %M[UDRIE_reg], r24 \n\t"

               "cts_exit_%=:"
  #ifdef USART_USE_GLOBALLY_RESERVED_ISR_Z_SAVE
               "movw r24, %[z_save] \n\t"
  #else
               "pop r24 \n\t"
  #endif

  #ifdef USART_USE_GLOBALLY_RESERVED_ISR_SREG_SAVE
               "out __SREG__, %[sreg_save] \n\t"
  #else
               "out __SREG__, r16 \n\t"
               "pop r16 \n\t"
  #endif

               "reti \n\t"
               : /* output operands */
               USART_REG_SAVE_LIST
               : /* input operands */
  #if __AVR_ARCH__ == 103
               [UDRIE_reg] "n"(_SFR_MEM_ADDR(UCSR3A_REGISTER)),
  #else
               [UDRIE_reg] "n"(_SFR_MEM_ADDR(UCSR3B_REGISTER)),
  #endif
               [UDRIE_bit] "M"(UDRIE3_BIT),
               [cts_port] "M"(_SFR_IO_ADDR(CTS3_PIN)), [cts_pin] "M"(CTS3_IONUM)
               : /* clobbers */
  );
}
#endif

/************************************************************************************
 *                              stdio.h stuff *
 ************************************************************************************/
#include <stdio.h> // avoid compilation errors

#if defined(USE_USART1) || defined(USE_USART2) || defined(USE_USART3)

  // wrapper of stdio.h FDEV_SETUP_STREAM to allow setting udata; udata is
  // used to store info about port ID
  #define FDEV_SETUP_STREAM_U(p, g, f, u)                                      \
    { .put = p, .get = g, .flags = f, .udata = u, }

  #ifndef NO_USART_TX
int uart_putchar(char data, FILE *stream);
  #endif

  #ifndef NO_USART_RX
int uart_getchar(FILE *stream);
  #endif

  #ifdef USE_USART0

    #if defined(NO_RX0_INTERRUPT)
extern FILE uart0_out;

    #elif defined(NO_TX0_INTERRUPT)
extern FILE uart0_in;
    #else
extern FILE uart0_io;
extern FILE uart0_in;
extern FILE uart0_out;
    #endif

  #endif // USE_USART0

  #ifdef USE_USART1

    #if defined(NO_RX1_INTERRUPT)
extern FILE uart1_out;

    #elif defined(NO_TX1_INTERRUPT)
extern FILE uart1_in;
    #else
extern FILE uart1_io;
extern FILE uart1_in;
extern FILE uart1_out;
    #endif

  #endif // USE_USART1

  #ifdef USE_USART2

    #if defined(NO_RX2_INTERRUPT)
extern FILE uart2_out;

    #elif defined(NO_TX2_INTERRUPT)
extern FILE uart2_in;
    #else
extern FILE uart2_io;
extern FILE uart2_in;
extern FILE uart2_out;
    #endif

  #endif // USE_USART2

  #ifdef USE_USART3

    #if defined(NO_RX3_INTERRUPT)
extern FILE uart3_out;

    #elif defined(NO_TX3_INTERRUPT)
extern FILE uart3_in;
    #else
extern FILE uart3_io;
extern FILE uart3_in;
extern FILE uart3_out;
    #endif

  #endif // USE_USART3

#else // single USART mcu

  #ifndef NO_TX0_INTERRUPT
int uart_putchar(char data, FILE *stream);
  #endif

  #ifndef NO_RX0_INTERRUPT
int uart_getchar(FILE *stream);
  #endif

  #if defined(NO_RX0_INTERRUPT)
extern FILE uart0_out;

  #elif defined(NO_TX0_INTERRUPT)
extern FILE uart0_in;
  #else
extern FILE uart0_io;
extern FILE uart0_in;
extern FILE uart0_out;
  #endif

#endif // single/multi USART

#ifdef __cplusplus
}
#endif

#endif // _USART_H_
