Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
Serial.write()/HardwareSerial::write()

Serial.write()/HardwareSerial::write()

概要

Serial.write()/HardwareSerial::write()は、バイナリデータをシリアルポートに書き込みます。リファレンスはこちら

ソースコード

Serial.write()/HardwareSerial::write()は、hardware/arduino/avr/cores/arduino/HardwareSerial.cppに定義されています。以下に全ソースコードを示します。

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;
}

入力はuint8_tで、戻り値はsize_tです。

size_t HardwareSerial::write(uint8_t c)
{

送信バッファ内の次にデータを書き込む場所を求めます。

  _written = true;

_writtenをtrueにして、write()が呼ばれたことを記録しておきます。この値はflush()で参照します。

  // 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_headと_tx_buffer_tailが同じかどうかと、bit_is_set()を使って_ucsra(UCSR0A)のUDRE0ビットが1かを調べます。

_tx_buffer_headと_tx_buffer_tailが同じということは、バッファが空であることを示しています。また、_ucsraのUDRE0ビットが1ということは、ATmega328Pのシリアル送信バッファにデータを書き込み可能であることを示しています。

この場合は、シリアル送信バッファの_udr(UDR0)にデータを書き込み、sbi()を使って、_ucsra(UCSR0A)のTXC0ビットを1にすると、TXC0ビットがクリアされ、データを送信します。

その後、送信したバイト数である1を返却します。

  tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;

送信バッファの最後尾の次の位置を取得し、iに代入します。

  // 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(送信バッファの先頭)と等しい間は、出力バッファが満杯なので、データが送信されるのを待ちます。

    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であれば、送信バッファが空いているので_tx_udr_empty_irq()を呼び出し、データを送信します。

  _tx_buffer[_tx_buffer_head] = c;
  _tx_buffer_head = i;
	
  sbi(*_ucsrb, UDRIE0);
  
  return 1;
}

送信対象のデータcを_tx_bufferに追加し、_tx_buffer_headを更新します。

その後、sbi()を使って、_ucsrb(UCSR0B)のUDRIE0ビットを1にし、送信データレジスタ空き割り込みを許可します。

最後に、バッファに書き込んだ1を返却します。

バージョン

Arduino 1.8.3



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2017 garretlab all rights reserved.
inserted by FC2 system