Skip to content

Commit e40cf5b

Browse files
matthijskooijmancmaglie
authored andcommitted
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.
1 parent 6ac8185 commit e40cf5b

File tree

2 files changed

+57
-34
lines changed

2 files changed

+57
-34
lines changed

hardware/arduino/avr/cores/arduino/HardwareSerial.cpp

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,54 @@
3434

3535
#include "HardwareSerial.h"
3636

37-
/*
38-
* on ATmega8, the uart and its bits are not numbered, so there is no "TXC0"
39-
* definition.
40-
*/
37+
// Ensure that the various bit positions we use are available with a 0
38+
// postfix, so we can always use the values for UART0 for all UARTs. The
39+
// alternative, passing the various values for each UART to the
40+
// HardwareSerial constructor also works, but makes the code bigger and
41+
// slower.
4142
#if !defined(TXC0)
4243
#if defined(TXC)
44+
// On ATmega8, the uart and its bits are not numbered, so there is no TXC0 etc.
4345
#define TXC0 TXC
46+
#define RXEN0 RXEN
47+
#define TXEN0 TXEN
48+
#define RXCIE0 RXCIE
49+
#define UDRIE0 UDRIE
50+
#define U2X0 U2X
51+
#define UPE0 UPE
52+
#define UDRE0 UDRE
4453
#elif defined(TXC1)
4554
// Some devices have uart1 but no uart0
4655
#define TXC0 TXC1
56+
#define RXEN0 RXEN1
57+
#define TXEN0 TXEN1
58+
#define RXCIE0 RXCIE1
59+
#define UDRIE0 UDRIE1
60+
#define U2X0 U2X1
61+
#define UPE0 UPE1
62+
#define UDRE0 UDRE1
4763
#else
48-
#error TXC0 not definable in HardwareSerial.h
64+
#error No UART found in HardwareSerial.cpp
65+
#endif
66+
#endif // !defined TXC0
67+
68+
// Check at compiletime that it is really ok to use the bit positions of
69+
// UART0 for the other UARTs as well, in case these values ever get
70+
// changed for future hardware.
71+
#if defined(TXC1) && (TXC1 != TXC0 || RXEN1 != RXEN0 || RXCIE1 != RXCIE0 || \
72+
UDRIE1 != UDRIE0 || U2X1 != U2X0 || UPE1 != UPE0 || \
73+
UDRE1 != UDRE0)
74+
#error "Not all bit positions for UART1 are the same as for UART0"
75+
#endif
76+
#if defined(TXC2) && (TXC2 != TXC0 || RXEN2 != RXEN0 || RXCIE2 != RXCIE0 || \
77+
UDRIE2 != UDRIE0 || U2X2 != U2X0 || UPE2 != UPE0 || \
78+
UDRE2 != UDRE0)
79+
#error "Not all bit positions for UART2 are the same as for UART0"
4980
#endif
81+
#if defined(TXC3) && (TXC3 != TXC0 || RXEN3 != RXEN0 || RXCIE3 != RXCIE0 || \
82+
UDRIE3 != UDRIE0 || U3X3 != U3X0 || UPE3 != UPE0 || \
83+
UDRE3 != UDRE0)
84+
#error "Not all bit positions for UART3 are the same as for UART0"
5085
#endif
5186

5287
inline void store_char(unsigned char c, HardwareSerial *s)
@@ -261,8 +296,7 @@ ISR(USART3_UDRE_vect)
261296
HardwareSerial::HardwareSerial(
262297
volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
263298
volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
264-
volatile uint8_t *ucsrc, volatile uint8_t *udr,
265-
uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x)
299+
volatile uint8_t *ucsrc, volatile uint8_t *udr)
266300
{
267301
_tx_buffer_head = _tx_buffer_tail = 0;
268302
_rx_buffer_head = _rx_buffer_tail = 0;
@@ -272,11 +306,6 @@ HardwareSerial::HardwareSerial(
272306
_ucsrb = ucsrb;
273307
_ucsrc = ucsrc;
274308
_udr = udr;
275-
_rxen = rxen;
276-
_txen = txen;
277-
_rxcie = rxcie;
278-
_udrie = udrie;
279-
_u2x = u2x;
280309
}
281310

282311
// Public Methods //////////////////////////////////////////////////////////////
@@ -285,7 +314,7 @@ void HardwareSerial::begin(unsigned long baud, byte config)
285314
{
286315
// Try u2x mode first
287316
uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2;
288-
*_ucsra = 1 << _u2x;
317+
*_ucsra = 1 << U2X0;
289318

290319
// hardcoded exception for 57600 for compatibility with the bootloader
291320
// shipped with the Duemilanove and previous boards and the firmware
@@ -308,10 +337,10 @@ void HardwareSerial::begin(unsigned long baud, byte config)
308337
#endif
309338
*_ucsrc = config;
310339

311-
sbi(*_ucsrb, _rxen);
312-
sbi(*_ucsrb, _txen);
313-
sbi(*_ucsrb, _rxcie);
314-
cbi(*_ucsrb, _udrie);
340+
sbi(*_ucsrb, RXEN0);
341+
sbi(*_ucsrb, TXEN0);
342+
sbi(*_ucsrb, RXCIE0);
343+
cbi(*_ucsrb, UDRIE0);
315344
}
316345

317346
void HardwareSerial::end()
@@ -320,10 +349,10 @@ void HardwareSerial::end()
320349
while (_tx_buffer_head != _tx_buffer_tail)
321350
;
322351

323-
cbi(*_ucsrb, _rxen);
324-
cbi(*_ucsrb, _txen);
325-
cbi(*_ucsrb, _rxcie);
326-
cbi(*_ucsrb, _udrie);
352+
cbi(*_ucsrb, RXEN0);
353+
cbi(*_ucsrb, TXEN0);
354+
cbi(*_ucsrb, RXCIE0);
355+
cbi(*_ucsrb, UDRIE0);
327356

328357
// clear any received data
329358
_rx_buffer_head = _rx_buffer_tail;
@@ -375,7 +404,7 @@ size_t HardwareSerial::write(uint8_t c)
375404
_tx_buffer[_tx_buffer_head] = c;
376405
_tx_buffer_head = i;
377406

378-
sbi(*_ucsrb, _udrie);
407+
sbi(*_ucsrb, UDRIE0);
379408
// clear the TXC bit -- "can be cleared by writing a one to its bit location"
380409
transmitting = true;
381410
sbi(*_ucsra, TXC0);
@@ -386,23 +415,23 @@ size_t HardwareSerial::write(uint8_t c)
386415
// Preinstantiate Objects //////////////////////////////////////////////////////
387416

388417
#if defined(UBRRH) && defined(UBRRL)
389-
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X);
418+
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
390419
#elif defined(UBRR0H) && defined(UBRR0L)
391-
HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0);
420+
HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
392421
#elif defined(USBCON)
393422
// do nothing - Serial object and buffers are initialized in CDC code
394423
#else
395424
#error no serial port defined (port 0)
396425
#endif
397426

398427
#if defined(UBRR1H)
399-
HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1, RXEN1, TXEN1, RXCIE1, UDRIE1, U2X1);
428+
HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1);
400429
#endif
401430
#if defined(UBRR2H)
402-
HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2, RXEN2, TXEN2, RXCIE2, UDRIE2, U2X2);
431+
HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2);
403432
#endif
404433
#if defined(UBRR3H)
405-
HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3, RXEN3, TXEN3, RXCIE3, UDRIE3, U2X3);
434+
HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3);
406435
#endif
407436

408437
#endif // whole file

hardware/arduino/avr/cores/arduino/HardwareSerial.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,6 @@ class HardwareSerial : public Stream
7272
volatile uint8_t *_ucsrb;
7373
volatile uint8_t *_ucsrc;
7474
volatile uint8_t *_udr;
75-
uint8_t _rxen;
76-
uint8_t _txen;
77-
uint8_t _rxcie;
78-
uint8_t _udrie;
79-
uint8_t _u2x;
8075
bool transmitting;
8176

8277
public:
@@ -94,8 +89,7 @@ class HardwareSerial : public Stream
9489
HardwareSerial(
9590
volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
9691
volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
97-
volatile uint8_t *ucsrc, volatile uint8_t *udr,
98-
uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x);
92+
volatile uint8_t *ucsrc, volatile uint8_t *udr);
9993
void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
10094
void begin(unsigned long, uint8_t);
10195
void end();

0 commit comments

Comments
 (0)