Skip to content

Commit

Permalink
Put each HardwareSerial instance in its own .cpp file
Browse files Browse the repository at this point in the history
By putting the ISRs and HardwareSerial instance for each instance in a
separate compilation unit, the compile will only consider them for
linking when the instance is actually used. The ISR is always referenced
by the compiler runtime and the Serialx_available() function is always
referenced by SerialEventRun(), but both references are weak and thus do
not cause the compilation to be included in the link by themselves.

The effect of this is that when multiple HardwareSerial ports are
available, but not all are used, buffers are only allocated and ISRs are
only included for the serial ports that are used. On the mega, this
lowers memory usage from 653 bytes to just 182 when only using the first
serial port.

On boards with just a single port, there is no change, since the code
and memory was already left out when no serial port was used at all.

This fixes #1425 and fixes #1259.
  • Loading branch information
matthijskooijman authored and cmaglie committed Jan 22, 2014
1 parent 8e43c1a commit 0e97bcb
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 92 deletions.
105 changes: 13 additions & 92 deletions hardware/arduino/avr/cores/arduino/HardwareSerial.cpp
Expand Up @@ -84,105 +84,46 @@
#error "Not all bit positions for UART3 are the same as for UART0"
#endif

// SerialEvent functions are weak, so when the user doesn't define them,
// the linker just sets their address to 0 (which is checked below).
// The Serialx_available is just a wrapper around Serialx.available(),
// but we can refer to it weakly so we don't pull in the entire
// HardwareSerial instance if the user doesn't also refer to it.
#if defined(HAVE_HWSERIAL0)
void serialEvent() __attribute__((weak));
void serialEvent() {}
#if defined(USART_RX_vect)
ISR(USART_RX_vect)
#elif defined(USART0_RX_vect)
ISR(USART0_RX_vect)
#elif defined(USART_RXC_vect)
ISR(USART_RXC_vect) // ATmega8
#else
#error "Don't know what the Data Received vector is called for the first UART"
#endif
{
Serial._rx_complete_irq();
}
bool Serial0_available() __attribute__((weak));
#endif

#if defined(HAVE_HWSERIAL1)
void serialEvent1() __attribute__((weak));
void serialEvent1() {}
ISR(USART1_RX_vect)
{
Serial1._rx_complete_irq();
}
bool Serial1_available() __attribute__((weak));
#endif

#if defined(HAVE_HWSERIAL2)
void serialEvent2() __attribute__((weak));
void serialEvent2() {}
ISR(USART2_RX_vect)
{
Serial2._rx_complete_irq();
}
bool Serial2_available() __attribute__((weak));
#endif

#if defined(HAVE_HWSERIAL3)
void serialEvent3() __attribute__((weak));
void serialEvent3() {}
ISR(USART3_RX_vect)
{
Serial3._rx_complete_irq();
}
bool Serial3_available() __attribute__((weak));
#endif

void serialEventRun(void)
{
#if defined(HAVE_HWSERIAL0)
if (Serial.available()) serialEvent();
if (Serial0_available && serialEvent && Serial0_available()) serialEvent();
#endif
#if defined(HAVE_HWSERIAL1)
if (Serial1.available()) serialEvent1();
if (Serial1_available && serialEvent1 && Serial1_available()) serialEvent1();
#endif
#if defined(HAVE_HWSERIAL2)
if (Serial2.available()) serialEvent2();
if (Serial2_available && serialEvent2 && Serial2_available()) serialEvent2();
#endif
#if defined(HAVE_HWSERIAL3)
if (Serial3.available()) serialEvent3();
#endif
}


#if defined(HAVE_HWSERIAL0)
#if defined(UART0_UDRE_vect)
ISR(UART0_UDRE_vect)
#elif defined(UART_UDRE_vect)
ISR(UART_UDRE_vect)
#elif defined(USART0_UDRE_vect)
ISR(USART0_UDRE_vect)
#elif defined(USART_UDRE_vect)
ISR(USART_UDRE_vect)
#else
#error "Don't know what the Data Register Empty vector is called for the first UART"
#endif
{
Serial._tx_udr_empty_irq();
}
if (Serial3_available && serialEvent2 && Serial3_available()) serialEvent3();
#endif

#if defined(HAVE_HWSERIAL1)
ISR(USART1_UDRE_vect)
{
Serial1._tx_udr_empty_irq();
}
#endif

#if defined(HAVE_HWSERIAL2)
ISR(USART2_UDRE_vect)
{
Serial2._tx_udr_empty_irq();
}
#endif

#if defined(HAVE_HWSERIAL3)
ISR(USART3_UDRE_vect)
{
Serial3._tx_udr_empty_irq();
}
#endif


// Actual interrupt handlers //////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -371,25 +312,5 @@ size_t HardwareSerial::write(uint8_t c)
return 1;
}

// Preinstantiate Objects //////////////////////////////////////////////////////

#if defined(HAVE_HWSERIAL0)
#if defined(UBRRH) && defined(UBRRL)
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
#else
HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
#endif
#endif

#if defined(HAVE_HWSERIAL1)
HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1);
#endif
#if defined(HAVE_HWSERIAL2)
HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2);
#endif
#if defined(HAVE_HWSERIAL3)
HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3);
#endif

#endif // whole file

54 changes: 54 additions & 0 deletions hardware/arduino/avr/cores/arduino/HardwareSerial0.cpp
@@ -0,0 +1,54 @@
#include "Arduino.h"
#include "HardwareSerial.h"

// Each HardwareSerial is defined in its own file, sine the linker pulls
// in the entire file when any element inside is used. --gc-sections can
// additionally cause unused symbols to be dropped, but ISRs have the
// "used" attribute so are never dropped and they keep the
// HardwareSerial instance in as well. Putting each instance in its own
// file prevents the linker from pulling in any unused instances in the
// first place.

#if defined(HAVE_HWSERIAL0)

#if defined(USART_RX_vect)
ISR(USART_RX_vect)
#elif defined(USART0_RX_vect)
ISR(USART0_RX_vect)
#elif defined(USART_RXC_vect)
ISR(USART_RXC_vect) // ATmega8
#else
#error "Don't know what the Data Received vector is called for the first UART"
#endif
{
Serial._rx_complete_irq();
}

#if defined(UART0_UDRE_vect)
ISR(UART0_UDRE_vect)
#elif defined(UART_UDRE_vect)
ISR(UART_UDRE_vect)
#elif defined(USART0_UDRE_vect)
ISR(USART0_UDRE_vect)
#elif defined(USART_UDRE_vect)
ISR(USART_UDRE_vect)
#else
#error "Don't know what the Data Register Empty vector is called for the first UART"
#endif
{
Serial._tx_udr_empty_irq();
}

#if defined(UBRRH) && defined(UBRRL)
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
#else
HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
#endif

// Function that can be weakly referenced by serialEventRun to prevent
// pulling in this file if it's not otherwise used.
bool Serial0_available() {
return Serial.available();
}

#endif // HAVE_HWSERIAL0
54 changes: 54 additions & 0 deletions hardware/arduino/avr/cores/arduino/HardwareSerial1.cpp
@@ -0,0 +1,54 @@
#include "Arduino.h"
#include "HardwareSerial.h"

// Each HardwareSerial is defined in its own file, sine the linker pulls
// in the entire file when any element inside is used. --gc-sections can
// additionally cause unused symbols to be dropped, but ISRs have the
// "used" attribute so are never dropped and they keep the
// HardwareSerial instance in as well. Putting each instance in its own
// file prevents the linker from pulling in any unused instances in the
// first place.

#if defined(HAVE_HWSERIAL1)

#if defined(USART_RX_vect)
ISR(USART_RX_vect)
#elif defined(USART1_RX_vect)
ISR(USART1_RX_vect)
#elif defined(USART_RXC_vect)
ISR(USART_RXC_vect) // ATmega8
#else
#error "Don't know what the Data Received vector is called for the first UART"
#endif
{
Serial1._rx_complete_irq();
}

#if defined(UART1_UDRE_vect)
ISR(UART1_UDRE_vect)
#elif defined(UART_UDRE_vect)
ISR(UART_UDRE_vect)
#elif defined(USART1_UDRE_vect)
ISR(USART1_UDRE_vect)
#elif defined(USART_UDRE_vect)
ISR(USART_UDRE_vect)
#else
#error "Don't know what the Data Register Empty vector is called for the first UART"
#endif
{
Serial1._tx_udr_empty_irq();
}

#if defined(UBRRH) && defined(UBRRL)
HardwareSerial Serial1(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
#else
HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1);
#endif

// Function that can be weakly referenced by serialEventRun to prevent
// pulling in this file if it's not otherwise used.
bool Serial1_available() {
return Serial1.available();
}

#endif // HAVE_HWSERIAL1
54 changes: 54 additions & 0 deletions hardware/arduino/avr/cores/arduino/HardwareSerial2.cpp
@@ -0,0 +1,54 @@
#include "Arduino.h"
#include "HardwareSerial.h"

// Each HardwareSerial is defined in its own file, sine the linker pulls
// in the entire file when any element inside is used. --gc-sections can
// additionally cause unused symbols to be dropped, but ISRs have the
// "used" attribute so are never dropped and they keep the
// HardwareSerial instance in as well. Putting each instance in its own
// file prevents the linker from pulling in any unused instances in the
// first place.

#if defined(HAVE_HWSERIAL2)

#if defined(USART_RX_vect)
ISR(USART_RX_vect)
#elif defined(USART2_RX_vect)
ISR(USART2_RX_vect)
#elif defined(USART_RXC_vect)
ISR(USART_RXC_vect) // ATmega8
#else
#error "Don't know what the Data Received vector is called for the first UART"
#endif
{
Serial2._rx_complete_irq();
}

#if defined(UART2_UDRE_vect)
ISR(UART2_UDRE_vect)
#elif defined(UART_UDRE_vect)
ISR(UART_UDRE_vect)
#elif defined(USART2_UDRE_vect)
ISR(USART2_UDRE_vect)
#elif defined(USART_UDRE_vect)
ISR(USART_UDRE_vect)
#else
#error "Don't know what the Data Register Empty vector is called for the first UART"
#endif
{
Serial2._tx_udr_empty_irq();
}

#if defined(UBRRH) && defined(UBRRL)
HardwareSerial Serial2(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
#else
HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2);
#endif

// Function that can be weakly referenced by serialEventRun to prevent
// pulling in this file if it's not otherwise used.
bool Serial2_available() {
return Serial2.available();
}

#endif // HAVE_HWSERIAL2
54 changes: 54 additions & 0 deletions hardware/arduino/avr/cores/arduino/HardwareSerial3.cpp
@@ -0,0 +1,54 @@
#include "Arduino.h"
#include "HardwareSerial.h"

// Each HardwareSerial is defined in its own file, sine the linker pulls
// in the entire file when any element inside is used. --gc-sections can
// additionally cause unused symbols to be dropped, but ISRs have the
// "used" attribute so are never dropped and they keep the
// HardwareSerial instance in as well. Putting each instance in its own
// file prevents the linker from pulling in any unused instances in the
// first place.

#if defined(HAVE_HWSERIAL3)

#if defined(USART_RX_vect)
ISR(USART_RX_vect)
#elif defined(USART3_RX_vect)
ISR(USART3_RX_vect)
#elif defined(USART_RXC_vect)
ISR(USART_RXC_vect) // ATmega8
#else
#error "Don't know what the Data Received vector is called for the first UART"
#endif
{
Serial3._rx_complete_irq();
}

#if defined(UART3_UDRE_vect)
ISR(UART3_UDRE_vect)
#elif defined(UART_UDRE_vect)
ISR(UART_UDRE_vect)
#elif defined(USART3_UDRE_vect)
ISR(USART3_UDRE_vect)
#elif defined(USART_UDRE_vect)
ISR(USART_UDRE_vect)
#else
#error "Don't know what the Data Register Empty vector is called for the first UART"
#endif
{
Serial3._tx_udr_empty_irq();
}

#if defined(UBRRH) && defined(UBRRL)
HardwareSerial Serial3(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
#else
HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3);
#endif

// Function that can be weakly referenced by serialEventRun to prevent
// pulling in this file if it's not otherwise used.
bool Serial3_available() {
return Serial3.available();
}

#endif // HAVE_HWSERIAL3

0 comments on commit 0e97bcb

Please sign in to comment.