Skip to content

Commit 0e97bcb

Browse files
matthijskooijmancmaglie
authored andcommitted
Put each HardwareSerial instance in its own .cpp file
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.
1 parent 8e43c1a commit 0e97bcb

File tree

5 files changed

+229
-92
lines changed

5 files changed

+229
-92
lines changed

Diff for: hardware/arduino/avr/cores/arduino/HardwareSerial.cpp

+13-92
Original file line numberDiff line numberDiff line change
@@ -84,105 +84,46 @@
8484
#error "Not all bit positions for UART3 are the same as for UART0"
8585
#endif
8686

87+
// SerialEvent functions are weak, so when the user doesn't define them,
88+
// the linker just sets their address to 0 (which is checked below).
89+
// The Serialx_available is just a wrapper around Serialx.available(),
90+
// but we can refer to it weakly so we don't pull in the entire
91+
// HardwareSerial instance if the user doesn't also refer to it.
8792
#if defined(HAVE_HWSERIAL0)
8893
void serialEvent() __attribute__((weak));
89-
void serialEvent() {}
90-
#if defined(USART_RX_vect)
91-
ISR(USART_RX_vect)
92-
#elif defined(USART0_RX_vect)
93-
ISR(USART0_RX_vect)
94-
#elif defined(USART_RXC_vect)
95-
ISR(USART_RXC_vect) // ATmega8
96-
#else
97-
#error "Don't know what the Data Received vector is called for the first UART"
98-
#endif
99-
{
100-
Serial._rx_complete_irq();
101-
}
94+
bool Serial0_available() __attribute__((weak));
10295
#endif
10396

10497
#if defined(HAVE_HWSERIAL1)
10598
void serialEvent1() __attribute__((weak));
106-
void serialEvent1() {}
107-
ISR(USART1_RX_vect)
108-
{
109-
Serial1._rx_complete_irq();
110-
}
99+
bool Serial1_available() __attribute__((weak));
111100
#endif
112101

113102
#if defined(HAVE_HWSERIAL2)
114103
void serialEvent2() __attribute__((weak));
115-
void serialEvent2() {}
116-
ISR(USART2_RX_vect)
117-
{
118-
Serial2._rx_complete_irq();
119-
}
104+
bool Serial2_available() __attribute__((weak));
120105
#endif
121106

122107
#if defined(HAVE_HWSERIAL3)
123108
void serialEvent3() __attribute__((weak));
124-
void serialEvent3() {}
125-
ISR(USART3_RX_vect)
126-
{
127-
Serial3._rx_complete_irq();
128-
}
109+
bool Serial3_available() __attribute__((weak));
129110
#endif
130111

131112
void serialEventRun(void)
132113
{
133114
#if defined(HAVE_HWSERIAL0)
134-
if (Serial.available()) serialEvent();
115+
if (Serial0_available && serialEvent && Serial0_available()) serialEvent();
135116
#endif
136117
#if defined(HAVE_HWSERIAL1)
137-
if (Serial1.available()) serialEvent1();
118+
if (Serial1_available && serialEvent1 && Serial1_available()) serialEvent1();
138119
#endif
139120
#if defined(HAVE_HWSERIAL2)
140-
if (Serial2.available()) serialEvent2();
121+
if (Serial2_available && serialEvent2 && Serial2_available()) serialEvent2();
141122
#endif
142123
#if defined(HAVE_HWSERIAL3)
143-
if (Serial3.available()) serialEvent3();
144-
#endif
145-
}
146-
147-
148-
#if defined(HAVE_HWSERIAL0)
149-
#if defined(UART0_UDRE_vect)
150-
ISR(UART0_UDRE_vect)
151-
#elif defined(UART_UDRE_vect)
152-
ISR(UART_UDRE_vect)
153-
#elif defined(USART0_UDRE_vect)
154-
ISR(USART0_UDRE_vect)
155-
#elif defined(USART_UDRE_vect)
156-
ISR(USART_UDRE_vect)
157-
#else
158-
#error "Don't know what the Data Register Empty vector is called for the first UART"
159-
#endif
160-
{
161-
Serial._tx_udr_empty_irq();
162-
}
124+
if (Serial3_available && serialEvent2 && Serial3_available()) serialEvent3();
163125
#endif
164-
165-
#if defined(HAVE_HWSERIAL1)
166-
ISR(USART1_UDRE_vect)
167-
{
168-
Serial1._tx_udr_empty_irq();
169126
}
170-
#endif
171-
172-
#if defined(HAVE_HWSERIAL2)
173-
ISR(USART2_UDRE_vect)
174-
{
175-
Serial2._tx_udr_empty_irq();
176-
}
177-
#endif
178-
179-
#if defined(HAVE_HWSERIAL3)
180-
ISR(USART3_UDRE_vect)
181-
{
182-
Serial3._tx_udr_empty_irq();
183-
}
184-
#endif
185-
186127

187128
// Actual interrupt handlers //////////////////////////////////////////////////////////////
188129

@@ -371,25 +312,5 @@ size_t HardwareSerial::write(uint8_t c)
371312
return 1;
372313
}
373314

374-
// Preinstantiate Objects //////////////////////////////////////////////////////
375-
376-
#if defined(HAVE_HWSERIAL0)
377-
#if defined(UBRRH) && defined(UBRRL)
378-
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
379-
#else
380-
HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
381-
#endif
382-
#endif
383-
384-
#if defined(HAVE_HWSERIAL1)
385-
HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1);
386-
#endif
387-
#if defined(HAVE_HWSERIAL2)
388-
HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2);
389-
#endif
390-
#if defined(HAVE_HWSERIAL3)
391-
HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3);
392-
#endif
393315

394316
#endif // whole file
395-
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "Arduino.h"
2+
#include "HardwareSerial.h"
3+
4+
// Each HardwareSerial is defined in its own file, sine the linker pulls
5+
// in the entire file when any element inside is used. --gc-sections can
6+
// additionally cause unused symbols to be dropped, but ISRs have the
7+
// "used" attribute so are never dropped and they keep the
8+
// HardwareSerial instance in as well. Putting each instance in its own
9+
// file prevents the linker from pulling in any unused instances in the
10+
// first place.
11+
12+
#if defined(HAVE_HWSERIAL0)
13+
14+
#if defined(USART_RX_vect)
15+
ISR(USART_RX_vect)
16+
#elif defined(USART0_RX_vect)
17+
ISR(USART0_RX_vect)
18+
#elif defined(USART_RXC_vect)
19+
ISR(USART_RXC_vect) // ATmega8
20+
#else
21+
#error "Don't know what the Data Received vector is called for the first UART"
22+
#endif
23+
{
24+
Serial._rx_complete_irq();
25+
}
26+
27+
#if defined(UART0_UDRE_vect)
28+
ISR(UART0_UDRE_vect)
29+
#elif defined(UART_UDRE_vect)
30+
ISR(UART_UDRE_vect)
31+
#elif defined(USART0_UDRE_vect)
32+
ISR(USART0_UDRE_vect)
33+
#elif defined(USART_UDRE_vect)
34+
ISR(USART_UDRE_vect)
35+
#else
36+
#error "Don't know what the Data Register Empty vector is called for the first UART"
37+
#endif
38+
{
39+
Serial._tx_udr_empty_irq();
40+
}
41+
42+
#if defined(UBRRH) && defined(UBRRL)
43+
HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
44+
#else
45+
HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
46+
#endif
47+
48+
// Function that can be weakly referenced by serialEventRun to prevent
49+
// pulling in this file if it's not otherwise used.
50+
bool Serial0_available() {
51+
return Serial.available();
52+
}
53+
54+
#endif // HAVE_HWSERIAL0
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "Arduino.h"
2+
#include "HardwareSerial.h"
3+
4+
// Each HardwareSerial is defined in its own file, sine the linker pulls
5+
// in the entire file when any element inside is used. --gc-sections can
6+
// additionally cause unused symbols to be dropped, but ISRs have the
7+
// "used" attribute so are never dropped and they keep the
8+
// HardwareSerial instance in as well. Putting each instance in its own
9+
// file prevents the linker from pulling in any unused instances in the
10+
// first place.
11+
12+
#if defined(HAVE_HWSERIAL1)
13+
14+
#if defined(USART_RX_vect)
15+
ISR(USART_RX_vect)
16+
#elif defined(USART1_RX_vect)
17+
ISR(USART1_RX_vect)
18+
#elif defined(USART_RXC_vect)
19+
ISR(USART_RXC_vect) // ATmega8
20+
#else
21+
#error "Don't know what the Data Received vector is called for the first UART"
22+
#endif
23+
{
24+
Serial1._rx_complete_irq();
25+
}
26+
27+
#if defined(UART1_UDRE_vect)
28+
ISR(UART1_UDRE_vect)
29+
#elif defined(UART_UDRE_vect)
30+
ISR(UART_UDRE_vect)
31+
#elif defined(USART1_UDRE_vect)
32+
ISR(USART1_UDRE_vect)
33+
#elif defined(USART_UDRE_vect)
34+
ISR(USART_UDRE_vect)
35+
#else
36+
#error "Don't know what the Data Register Empty vector is called for the first UART"
37+
#endif
38+
{
39+
Serial1._tx_udr_empty_irq();
40+
}
41+
42+
#if defined(UBRRH) && defined(UBRRL)
43+
HardwareSerial Serial1(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
44+
#else
45+
HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1);
46+
#endif
47+
48+
// Function that can be weakly referenced by serialEventRun to prevent
49+
// pulling in this file if it's not otherwise used.
50+
bool Serial1_available() {
51+
return Serial1.available();
52+
}
53+
54+
#endif // HAVE_HWSERIAL1
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "Arduino.h"
2+
#include "HardwareSerial.h"
3+
4+
// Each HardwareSerial is defined in its own file, sine the linker pulls
5+
// in the entire file when any element inside is used. --gc-sections can
6+
// additionally cause unused symbols to be dropped, but ISRs have the
7+
// "used" attribute so are never dropped and they keep the
8+
// HardwareSerial instance in as well. Putting each instance in its own
9+
// file prevents the linker from pulling in any unused instances in the
10+
// first place.
11+
12+
#if defined(HAVE_HWSERIAL2)
13+
14+
#if defined(USART_RX_vect)
15+
ISR(USART_RX_vect)
16+
#elif defined(USART2_RX_vect)
17+
ISR(USART2_RX_vect)
18+
#elif defined(USART_RXC_vect)
19+
ISR(USART_RXC_vect) // ATmega8
20+
#else
21+
#error "Don't know what the Data Received vector is called for the first UART"
22+
#endif
23+
{
24+
Serial2._rx_complete_irq();
25+
}
26+
27+
#if defined(UART2_UDRE_vect)
28+
ISR(UART2_UDRE_vect)
29+
#elif defined(UART_UDRE_vect)
30+
ISR(UART_UDRE_vect)
31+
#elif defined(USART2_UDRE_vect)
32+
ISR(USART2_UDRE_vect)
33+
#elif defined(USART_UDRE_vect)
34+
ISR(USART_UDRE_vect)
35+
#else
36+
#error "Don't know what the Data Register Empty vector is called for the first UART"
37+
#endif
38+
{
39+
Serial2._tx_udr_empty_irq();
40+
}
41+
42+
#if defined(UBRRH) && defined(UBRRL)
43+
HardwareSerial Serial2(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
44+
#else
45+
HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2);
46+
#endif
47+
48+
// Function that can be weakly referenced by serialEventRun to prevent
49+
// pulling in this file if it's not otherwise used.
50+
bool Serial2_available() {
51+
return Serial2.available();
52+
}
53+
54+
#endif // HAVE_HWSERIAL2
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "Arduino.h"
2+
#include "HardwareSerial.h"
3+
4+
// Each HardwareSerial is defined in its own file, sine the linker pulls
5+
// in the entire file when any element inside is used. --gc-sections can
6+
// additionally cause unused symbols to be dropped, but ISRs have the
7+
// "used" attribute so are never dropped and they keep the
8+
// HardwareSerial instance in as well. Putting each instance in its own
9+
// file prevents the linker from pulling in any unused instances in the
10+
// first place.
11+
12+
#if defined(HAVE_HWSERIAL3)
13+
14+
#if defined(USART_RX_vect)
15+
ISR(USART_RX_vect)
16+
#elif defined(USART3_RX_vect)
17+
ISR(USART3_RX_vect)
18+
#elif defined(USART_RXC_vect)
19+
ISR(USART_RXC_vect) // ATmega8
20+
#else
21+
#error "Don't know what the Data Received vector is called for the first UART"
22+
#endif
23+
{
24+
Serial3._rx_complete_irq();
25+
}
26+
27+
#if defined(UART3_UDRE_vect)
28+
ISR(UART3_UDRE_vect)
29+
#elif defined(UART_UDRE_vect)
30+
ISR(UART_UDRE_vect)
31+
#elif defined(USART3_UDRE_vect)
32+
ISR(USART3_UDRE_vect)
33+
#elif defined(USART_UDRE_vect)
34+
ISR(USART_UDRE_vect)
35+
#else
36+
#error "Don't know what the Data Register Empty vector is called for the first UART"
37+
#endif
38+
{
39+
Serial3._tx_udr_empty_irq();
40+
}
41+
42+
#if defined(UBRRH) && defined(UBRRL)
43+
HardwareSerial Serial3(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
44+
#else
45+
HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3);
46+
#endif
47+
48+
// Function that can be weakly referenced by serialEventRun to prevent
49+
// pulling in this file if it's not otherwise used.
50+
bool Serial3_available() {
51+
return Serial3.available();
52+
}
53+
54+
#endif // HAVE_HWSERIAL3

0 commit comments

Comments
 (0)