Playing with Arduino
A page to record my playing with Arduino
HardwareSerial::write()

Abstract

The Serial.write()/HardwareSerial::write() wites binary data to serial port.

Source Code

The HardwareSerial::write() is defined in hardware/arduino/avr/cores/arduino/HardwareSerial.cpp as below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
}

The input is uint8_t and returns size_t.

1
2
size_t HardwareSerial::write(uint8_t c)
{

Make _written true to record that write() has been called. This value is refered in HardwareSerial::flush().

3
  _written = true;

Calculate the position to write next data in the transmit buffer.

 4
 5
 6
 7
 8
 9
10
11
12
// 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;
}

Checks if _tx_buffer_head is same as _tx_buffer_tail, and UDRE0 bit of _ucsra(UCSR0A) is 1 using bit_is_set()

If _tx_buffer_head is same as _tx_buffer_tail, it means the buffer is empty. If UDRE0 bit is 1, it means the transmit buffer of ATmega328P is available.

In this case, write data to _udr(UDR0) and set TXC0 bit of _ucsra(UCSR0A) to send data.

13
  tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;

Get the last position in the transmit buffer and hold it in i.

15
16
17
  // 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 the i is same as _tx_buffer_tail, it means the transmit buffer is full. So wait for the data to transmit.

18
19
20
21
22
23
24
25
26
27
28
  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
  }
}

Check the SREG_I bit of SREG using bit_is_clear(). If the SRG_I bit is 0, interrupt is globally disabled. In this case if the UDRE0 bit of _ucsra(UCSR0A) is 1, the transmit buffer is vacant, call HardwareSerial::_tx_udr_empty_irq() to send data.

30
31
32
33
34
35
36
  _tx_buffer[_tx_buffer_head] = c;
  _tx_buffer_head = i;
     
  sbi(*_ucsrb, UDRIE0);
   
  return 1;
}

Add the data to trasmit, c, to _tx_buffer.

Then set the UDRIE0 bit of _ucsrb(UCSR0B) 1 to enable interrupt.

Finally returns 1 to show, 1 byte data has been written to the buffer.

Version

Arduino 1.8.13

Last Update

June 19, 2020

inserted by FC2 system