概要
Serial.write()/HardwareSerial::write()は、バイナリデータをシリアルポートに書き込みます。リファレンスはこちら}。
ソースコード
HardwareSerial::write()は、hardware/arduino/avr/cores/arduino/HardwareSerial.cppに定義されています。以下に全ソースコードを示します。
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
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)) {
// 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;
// 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);
}
return 1;
}
|
入力はuint8_tで、戻り値はsize_tです。
1
2
|
size_t HardwareSerial::write(uint8_t c)
{
|
_writtenをtrueにして、write()が呼ばれたことを記録しておきます。この値はHardwareSerial::flush()で参照します。
送信バッファ内の次にデータを書き込む場所を求めます。
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// 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);
}
return 1;
}
|
_tx_buffer_headと_tx_buffer_tailが同じかどうかと、bit_is_set()を使って_ucsra(UCSR0A)のUDRE0ビットが1かを調べます。
_tx_buffer_headと_tx_buffer_tailが同じということは、バッファが空であることを示しています。また、_ucsraのUDRE0ビットが1ということは、ATmega328Pのシリアル送信バッファにデータを書き込み可能であることを示しています。
この場合は、シリアル送信バッファの_udr(UDR0)にデータを書き込み、_ucsra(UCSR0A)のU2X0ビットとMPCM0ビットを設定します。さらにTXC0ビットを1にすると、TXC0ビットがクリアされ、データを送信します。最後に1を返します。
23
|
tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;
|
送信バッファの最後尾の次の位置を取得し、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) {
|
i(送信バッファの最後尾の次)が_tx_buffer_tail(送信バッファの先頭)と等しい間は、出力バッファが満杯なので、データが送信されるのを待ちます。
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
}
}
|
bit_is_clear()は、第1引数の第2引数ビット目が0かどうかを調べるマクロです。SREGのSREG_Iビットが0の場合はグローバル割り込み禁止状態を示します。このときは、_ucsra(UCSR0A)のUDRE0ビットが1であれば、送信バッファが空いているのでHardwareSerial::_tx_udr_empty_irq()を呼び出し、データを送信します。
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);
}
return 1;
}
|
送信対象のデータcを_tx_bufferに追加し、_tx_buffer_headを更新します。
その後、sbi()を使って、_ucsrb(UCSR0B)のUDRIE0ビットを1にし、送信データレジスタ空き割り込みを許可します。
最後に、バッファに書き込んだバイト数1を返却します。
バージョン
Arduino AVR Boards 1.8.6
最終更新日
March 21, 2023