aboutsummaryrefslogtreecommitdiff
path: root/cores/arduino
diff options
context:
space:
mode:
Diffstat (limited to 'cores/arduino')
-rw-r--r--cores/arduino/HardwareSerial.cpp14
1 files changed, 12 insertions, 2 deletions
diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp
index e6ccdef..ecd7918 100644
--- a/cores/arduino/HardwareSerial.cpp
+++ b/cores/arduino/HardwareSerial.cpp
@@ -226,8 +226,18 @@ size_t HardwareSerial::write(uint8_t c)
// significantly improve the effective datarate at high (>
// 500kbit/s) bitrates, where interrupt overhead becomes a slowdown.
if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) {
- *_udr = c;
- *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0);
+ // If TXC is cleared before writing UDR and the previous byte
+ // completes before writing to UDR, TXC will be set but a byte
+ // is still being transmitted causing flush() to return too soon.
+ // So writing UDR must happen first.
+ // Writing UDR and clearing TC must be done atomically, otherwise
+ // interrupts might delay the TXC clear so the byte written to UDR
+ // is transmitted (setting TXC) before clearing TXC. Then TXC will
+ // be cleared when no bytes are left, causing flush() to hang
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+ *_udr = c;
+ *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0);
+ }
return 1;
}
tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;