2

I have explored Google and the Arduino site and this site and cannot find a mention of this, so my guess is that serial is handled by a separate component to the core processing and so different serial communication rates will not affect code performance.

Is this correct?

Where can I find information about how serial passing is handled? For instance, when I call Serial.prinlnt("test"), how does that get to the serial line?

2 Answers 2

6

It depends. Serial data is received into, or sent from, buffers (one for sending and one for receiving). If you have a processor with 1024 bytes of RAM or more, then the buffer size is 64 bytes, otherwise it is 16 bytes. (See the HardwareSerial.h file in your distribution).

If the buffer fills up when sending, it will block (that is, the code will wait for the buffer to empty). If it fill up when receiving the extra bytes will be discarded.

If the buffer does not fill up, then the baud rate will not affect performance. That is because data is sent, or received, by an interrupt service routine, in the background.

As a rule of thumb, you can expect data to be sent or received at the baud rate divided by 10. That is because each byte is 8 bits, plus a start and a stop bit (10 bits in total).

So, for example, at 9600 baud you can send 960 characters per second. If you are sending something simple, like a temperature which is 20 bytes, every second, then the baud rate won't affect performance at all. If you are trying to send thousands of bytes per second, it will affect performance, because the Serial.println will probably block once the buffer fills up.


For instance, when I call Serial.prinlnt("test"), how does that get to the serial line?

If the buffer is not full, then "t" "e" "s" "t" are copied into the buffer, and the code returns immediately. If the buffer is full then it waits for it to become not-full.

As an efficiency measure, if you are sending a single byte, and the buffer is empty, then the byte is placed in the hardware register immediately (saving a small amount of time). In this case, if the buffer was empty, "t" would be sent immediately, and "est" would be sent afterwards, as a "byte has been sent" interrupt is raised.1

This is described in the code comments:

size_t HardwareSerial::write(uint8_t c)
{
  _written = true;
  // If the buffer and the data register is empty, just write the byte
  // to the data register and be done. This shortcut helps
  // 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;
    sbi(*_ucsra, TXC0);
    return 1;
  }
  tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;

  // If the output buffer is full, there's nothing for it other than to 
  // wait for the interrupt handler to empty it a bit
  while (i == _tx_buffer_tail) {
    if (bit_is_clear(SREG, SREG_I)) {
      // Interrupts are disabled, so we'll have to poll the data
      // register empty flag ourselves. If it is set, pretend an
      // interrupt has happened and call the handler to free up
      // space for us.
      if(bit_is_set(*_ucsra, UDRE0))
    _tx_udr_empty_irq();
    } else {
      // nop, the interrupt handler will free up space for us
    }
  }

  _tx_buffer[_tx_buffer_head] = c;
  _tx_buffer_head = i;

  sbi(*_ucsrb, UDRIE0);

  return 1;
}

Notice that the byte is not placed in the buffer at all if it is empty, and nothing is being sent.


For more information see my question and answer about serial processing.


  1. Strictly speaking, after discussing this in the comments with Majenko, the interrupt that is raised is really that the hardware "send" buffer is empty. When the send buffer empties then the interrupt is called so that the interrupt code can put a new byte into it.
7
  • As an efficiency measure, if you are sending a single byte, and the buffer is empty, then the byte is placed in the hardware register immediately (saving a small amount of time). - actually that's not for the sake of efficiency - that's done so that a TX interrupt can be raised - otherwise you put stuff in the buffer and the TX interrupt never triggers (because there is nothing in the TX FIFO to send) and so the buffer never empties. You need that first manual entry in the TX FIFO to start the ball rolling.
    – Majenko
    Sep 27, 2016 at 9:44
  • That's not what the comments in the code say (see amended answer). However I agree that a side-effect is that if you go to write another byte, you now know there will be an interrupt from writing the first one. Although I'm wondering now if there could be a race condition. If the code fails that first test (ie. something is being sent) but the sending completes, and the interrupt is processed before the byte is put into the buffer, then that byte may not be sent.
    – Nick Gammon
    Sep 27, 2016 at 22:03
  • Nah. It enables the Data Register Empty interrupt at the end of write() which, if the transmit buffer is empty triggers the ISR to fill it. If it has triggered during the first if and the buffer has been filled with the last byte in the ring buffer the IF will fail, it will fall through to placing the byte into the ring buffer, and the next time the interrupt fires it will be picked up. If it was the last time the interrupt would fire (empty ring buffer) then the interrupt is re-enabled anyway and fires because the TX buffer is empty.
    – Majenko
    Sep 27, 2016 at 22:22
  • I'm used to chips with a proper multi-level hardware FIFO, so there's no messing with these silly ring buffers anyway...
    – Majenko
    Sep 27, 2016 at 22:23
  • Ah, OK, I was looking at the specs for the wrong flag (TXCIEn). What you say makes sense.
    – Nick Gammon
    Sep 27, 2016 at 22:43
1

No, it affects code performance always. For how much is dependent on used data types, baudrate, tx buffer size.

Data types:

  • AVR doesn't have hardware floating point operation support. Software support is super slow, so if you want to print some floats, it will be affecting performance a lot (but it's same as just using it).
  • There is no integer division either (except for bit shifts/rotates which is division by powers of two). So printing bytes, integers, longs in DEC base takes some performance.

Baud rate settings and transmit buffer size:

Sending data over Serial is limited by its speed (baud rate). So if you use common 9600 baud rate you'll be limited by this speed. If you're sending data at higher rate. Transmit buffer gets filled and next write into Serial will be blocked until another character from buffer is sent.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.