size_tHardwareSerial::write(uint8_tc){_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)){// 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);}return1;}tx_buffer_index_ti=(_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;// make atomic to prevent execution of ISR between setting the
// head pointer and setting the interrupt flag resulting in buffer
// retransmission
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){_tx_buffer_head=i;sbi(*_ucsrb,UDRIE0);}return1;}
The input is uint8_t and returns size_t.
1
2
size_tHardwareSerial::write(uint8_tc){
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.
// 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)){// 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);}return1;}
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 U2X0 and MPCM0 bits of _ucsra(UCSR0A). And setting TXC0 bit of _ucsra to send data.
Get the last position in the transmit buffer and hold it in i.
25
26
27
// 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.
28
29
30
31
32
33
34
35
36
37
38
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.
40
41
42
43
44
45
46
47
48
49
50
51
_tx_buffer[_tx_buffer_head]=c;// make atomic to prevent execution of ISR between setting the
// head pointer and setting the interrupt flag resulting in buffer
// retransmission
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){_tx_buffer_head=i;sbi(*_ucsrb,UDRIE0);}return1;}
Add the data to trasmit, c, to _tx_buffer.
Then set the UDRIE0 bit of _ucsrb(UCSR0B) 1 to enable interrupt, by using sbi().
Finally returns 1 to show, 1 byte data has been written to the buffer.