Use constants for register bit positions in HardwareSerial

Previously, the constants to use for the bit positions of the various
UARTs were passed to the HardwareSerial constructor. However, this
meant that whenever these values were used, the had to be indirectly
loaded, resulting in extra code overhead. Additionally, since there is
no instruction to shift a value by a variable amount, the 1 << x
expressions (inside _BV and sbi() / cbi()) would be compiled as a loop
instead of being evaluated at compiletime.

Now, the HardwareSerial class always uses the constants for the bit
positions of UART 0 (and some code is present to make sure these
constants exist, even for targets that only have a single unnumbered
UART or start at UART1).

This was already done for the TXC0 constant, for some reason. For the
actual register addresses, this approach does not work, since these are
of course different between the different UARTs on a single chip.

Of course, always using the UART 0 constants is only correct when the
constants are actually identical for the different UARTs. It has been
verified that this is currently the case for all targets supported by
avr-gcc 4.7.2, and the code contains compile-time checks to verify this
for the current target, in case a new target is added for which this
does not hold. This verification was done using:

for i in TXC RXEN TXEN RXCIE UDRIE U2X UPE; do echo $i; grep --no-filename -r "#define $i[0-9]\? " /usr/lib/avr/include/avr/io* | sed "s/#define $i[0-9]\?\s*\(\S\)\+\s*\(\/\*.*\*\/\)\?$/\1/" | sort | uniq ; done

This command shows that the above constants are identical for all uarts
on all platforms, except for TXC, which is sometimes 6 and sometimes 0.
Further investigation shows that it is always 6, except in io90scr100.h,
but that file defines TXC0 with value 6 for the UART and uses TXC with
value 0 for some USB-related register.

This commit reduces program size on the uno by around 120 bytes.
This commit is contained in:
Matthijs Kooijman 2013-04-18 14:17:47 +02:00 committed by Cristian Maglie
parent 494929495e
commit 3babfc2a85
2 changed files with 57 additions and 34 deletions

View File

@ -34,19 +34,54 @@
#include "HardwareSerial.h"
/*
* on ATmega8, the uart and its bits are not numbered, so there is no "TXC0"
* definition.
*/
// Ensure that the various bit positions we use are available with a 0
// postfix, so we can always use the values for UART0 for all UARTs. The
// alternative, passing the various values for each UART to the
// HardwareSerial constructor also works, but makes the code bigger and
// slower.
#if !defined(TXC0)
#if defined(TXC)
// On ATmega8, the uart and its bits are not numbered, so there is no TXC0 etc.
#define TXC0 TXC
#define RXEN0 RXEN
#define TXEN0 TXEN
#define RXCIE0 RXCIE
#define UDRIE0 UDRIE
#define U2X0 U2X
#define UPE0 UPE
#define UDRE0 UDRE
#elif defined(TXC1)
// Some devices have uart1 but no uart0
#define TXC0 TXC1
#define RXEN0 RXEN1
#define TXEN0 TXEN1
#define RXCIE0 RXCIE1
#define UDRIE0 UDRIE1
#define U2X0 U2X1
#define UPE0 UPE1
#define UDRE0 UDRE1
#else
#error TXC0 not definable in HardwareSerial.h
#error No UART found in HardwareSerial.cpp
#endif
#endif // !defined TXC0
// Check at compiletime that it is really ok to use the bit positions of
// UART0 for the other UARTs as well, in case these values ever get
// changed for future hardware.
#if defined(TXC1) && (TXC1 != TXC0 || RXEN1 != RXEN0 || RXCIE1 != RXCIE0 || \
UDRIE1 != UDRIE0 || U2X1 != U2X0 || UPE1 != UPE0 || \
UDRE1 != UDRE0)
#error "Not all bit positions for UART1 are the same as for UART0"
#endif
#if defined(TXC2) && (TXC2 != TXC0 || RXEN2 != RXEN0 || RXCIE2 != RXCIE0 || \
UDRIE2 != UDRIE0 || U2X2 != U2X0 || UPE2 != UPE0 || \
UDRE2 != UDRE0)
#error "Not all bit positions for UART2 are the same as for UART0"
#endif
#if defined(TXC3) && (TXC3 != TXC0 || RXEN3 != RXEN0 || RXCIE3 != RXCIE0 || \
UDRIE3 != UDRIE0 || U3X3 != U3X0 || UPE3 != UPE0 || \
UDRE3 != UDRE0)
#error "Not all bit positions for UART3 are the same as for UART0"
#endif
inline void store_char(unsigned char c, HardwareSerial *s)
@ -261,8 +296,7 @@ ISR(USART3_UDRE_vect)
HardwareSerial::HardwareSerial(
volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
volatile uint8_t *ucsrc, volatile uint8_t *udr,
uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x)
volatile uint8_t *ucsrc, volatile uint8_t *udr)
{
_tx_buffer_head = _tx_buffer_tail = 0;
_rx_buffer_head = _rx_buffer_tail = 0;
@ -272,11 +306,6 @@ HardwareSerial::HardwareSerial(
_ucsrb = ucsrb;
_ucsrc = ucsrc;
_udr = udr;
_rxen = rxen;
_txen = txen;
_rxcie = rxcie;
_udrie = udrie;
_u2x = u2x;
}
// Public Methods //////////////////////////////////////////////////////////////
@ -285,7 +314,7 @@ void HardwareSerial::begin(unsigned long baud, byte config)
{
// Try u2x mode first
uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2;
*_ucsra = 1 << _u2x;
*_ucsra = 1 << U2X0;
// hardcoded exception for 57600 for compatibility with the bootloader
// shipped with the Duemilanove and previous boards and the firmware
@ -308,10 +337,10 @@ void HardwareSerial::begin(unsigned long baud, byte config)
#endif
*_ucsrc = config;
sbi(*_ucsrb, _rxen);
sbi(*_ucsrb, _txen);
sbi(*_ucsrb, _rxcie);
cbi(*_ucsrb, _udrie);
sbi(*_ucsrb, RXEN0);
sbi(*_ucsrb, TXEN0);
sbi(*_ucsrb, RXCIE0);
cbi(*_ucsrb, UDRIE0);
}
void HardwareSerial::end()
@ -320,10 +349,10 @@ void HardwareSerial::end()
while (_tx_buffer_head != _tx_buffer_tail)
;
cbi(*_ucsrb, _rxen);
cbi(*_ucsrb, _txen);
cbi(*_ucsrb, _rxcie);
cbi(*_ucsrb, _udrie);
cbi(*_ucsrb, RXEN0);
cbi(*_ucsrb, TXEN0);
cbi(*_ucsrb, RXCIE0);
cbi(*_ucsrb, UDRIE0);
// clear any received data
_rx_buffer_head = _rx_buffer_tail;
@ -375,7 +404,7 @@ size_t HardwareSerial::write(uint8_t c)
_tx_buffer[_tx_buffer_head] = c;
_tx_buffer_head = i;
sbi(*_ucsrb, _udrie);
sbi(*_ucsrb, UDRIE0);
// clear the TXC bit -- "can be cleared by writing a one to its bit location"
transmitting = true;
sbi(*_ucsra, TXC0);
@ -386,9 +415,9 @@ size_t HardwareSerial::write(uint8_t c)
// Preinstantiate Objects //////////////////////////////////////////////////////
#if defined(UBRRH) && defined(UBRRL)
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X);
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
#elif defined(UBRR0H) && defined(UBRR0L)
HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0);
HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
#elif defined(USBCON)
// do nothing - Serial object and buffers are initialized in CDC code
#else
@ -396,13 +425,13 @@ size_t HardwareSerial::write(uint8_t c)
#endif
#if defined(UBRR1H)
HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1, RXEN1, TXEN1, RXCIE1, UDRIE1, U2X1);
HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1);
#endif
#if defined(UBRR2H)
HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2, RXEN2, TXEN2, RXCIE2, UDRIE2, U2X2);
HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2);
#endif
#if defined(UBRR3H)
HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3, RXEN3, TXEN3, RXCIE3, UDRIE3, U2X3);
HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3);
#endif
#endif // whole file

View File

@ -72,11 +72,6 @@ class HardwareSerial : public Stream
volatile uint8_t *_ucsrb;
volatile uint8_t *_ucsrc;
volatile uint8_t *_udr;
uint8_t _rxen;
uint8_t _txen;
uint8_t _rxcie;
uint8_t _udrie;
uint8_t _u2x;
bool transmitting;
public:
@ -94,8 +89,7 @@ class HardwareSerial : public Stream
HardwareSerial(
volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
volatile uint8_t *ucsrc, volatile uint8_t *udr,
uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x);
volatile uint8_t *ucsrc, volatile uint8_t *udr);
void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
void begin(unsigned long, uint8_t);
void end();