25
25
#include < stdio.h>
26
26
#include < string.h>
27
27
#include < inttypes.h>
28
+ #include < util/atomic.h>
29
+ #include < avr/cpufunc.h>
28
30
#include " Arduino.h"
29
31
#include " wiring_private.h"
30
32
@@ -245,27 +247,33 @@ void HardwareSerial::end()
245
247
246
248
int HardwareSerial::available (void )
247
249
{
248
- return (unsigned int )(SERIAL_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail) % SERIAL_BUFFER_SIZE;
250
+ ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
251
+ return (unsigned int )(SERIAL_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail) % SERIAL_BUFFER_SIZE;
252
+ }
249
253
}
250
254
251
255
int HardwareSerial::peek (void )
252
256
{
253
- if (_rx_buffer_head == _rx_buffer_tail) {
254
- return -1 ;
255
- } else {
256
- return _rx_buffer[_rx_buffer_tail];
257
+ ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
258
+ if (_rx_buffer_head == _rx_buffer_tail) {
259
+ return -1 ;
260
+ } else {
261
+ return _rx_buffer[_rx_buffer_tail];
262
+ }
257
263
}
258
264
}
259
265
260
266
int HardwareSerial::read (void )
261
267
{
262
- // if the head isn't ahead of the tail, we don't have any characters
263
- if (_rx_buffer_head == _rx_buffer_tail) {
264
- return -1 ;
265
- } else {
266
- unsigned char c = _rx_buffer[_rx_buffer_tail];
267
- _rx_buffer_tail = (unsigned int )(_rx_buffer_tail + 1 ) % SERIAL_BUFFER_SIZE;
268
- return c;
268
+ ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
269
+ // if the head isn't ahead of the tail, we don't have any characters
270
+ if (_rx_buffer_head == _rx_buffer_tail) {
271
+ return -1 ;
272
+ } else {
273
+ unsigned char c = _rx_buffer[_rx_buffer_tail];
274
+ _rx_buffer_tail = (unsigned int )(_rx_buffer_tail + 1 ) % SERIAL_BUFFER_SIZE;
275
+ return c;
276
+ }
269
277
}
270
278
}
271
279
@@ -291,30 +299,44 @@ void HardwareSerial::flush()
291
299
292
300
size_t HardwareSerial::write (uint8_t c)
293
301
{
294
- int i = (_tx_buffer_head + 1 ) % SERIAL_BUFFER_SIZE;
295
-
296
- // If the output buffer is full, there's nothing for it other than to
297
- // wait for the interrupt handler to empty it a bit
298
- while (i == _tx_buffer_tail) {
299
- if (bit_is_clear (SREG, SREG_I)) {
300
- // Interrupts are disabled, so we'll have to poll the data
301
- // register empty flag ourselves. If it is set, pretend an
302
- // interrupt has happened and call the handler to free up
303
- // space for us.
304
- if (bit_is_set (*_ucsra, UDRE0))
305
- _tx_udr_empty_irq ();
306
- } else {
307
- // nop, the interrupt handler will free up space for us
302
+ bool interrupts_enabled = bit_is_set (SREG, SREG_I);
303
+ ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
304
+ int i = (_tx_buffer_head + 1 ) % SERIAL_BUFFER_SIZE;
305
+
306
+ // If the output buffer is full, there's nothing for it other than to
307
+ // wait for the interrupt handler to empty it a bit
308
+ while (i == _tx_buffer_tail) {
309
+ if (!interrupts_enabled) {
310
+ // Interrupts were disabled at the start of this function, so we
311
+ // can't just enable them (that would allow other interrupts to
312
+ // trigger as well!). We'll have to poll the data register empty
313
+ // flag ourselves. If it is set, pretend an interrupt has happened
314
+ // and call the handler to free up space for us.
315
+ if (bit_is_set (*_ucsra, UDRE0))
316
+ _tx_udr_empty_irq ();
317
+ } else {
318
+ // Interrupts were enabled, so we can temporarily enable them so
319
+ // our tx interrupt can fire and free up some space. Note that we
320
+ // can't just use the UDRE polling approach here instead of
321
+ // enabling interrupts, since that would prevent other
322
+ // interrupts from triggering while we wait for room.
323
+ NONATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
324
+ _NOP (); // Any pending interrupts will trigger here
325
+ }
326
+ // Recalculate i, since other interrupt handlers might have
327
+ // written to our buffer
328
+ i = (_tx_buffer_head + 1 ) % SERIAL_BUFFER_SIZE;
329
+ }
308
330
}
309
- }
310
331
311
- _tx_buffer[_tx_buffer_head] = c;
312
- _tx_buffer_head = i;
313
-
314
- sbi (*_ucsrb, UDRIE0);
315
- _written = true ;
316
-
317
- return 1 ;
332
+ _tx_buffer[_tx_buffer_head] = c;
333
+ _tx_buffer_head = i;
334
+
335
+ sbi (*_ucsrb, UDRIE0);
336
+ _written = true ;
337
+
338
+ return 1 ;
339
+ }
318
340
}
319
341
320
342
HardwareSerial::operator bool () {
0 commit comments