lyricalmagical が 2023年01月29日18時34分41秒 に編集
初版
タイトルの変更
[Arduino]Serial.print()は送信完了まで待ってくれない
タグの変更
Arduino
メイン画像の変更
記事種類の変更
セットアップや使用方法
本文の変更
半二重通信(一つの伝送路で送信と受信を行うので、送受信が同時にできない通信手段。RS485等)を行うためには、自身の送信終了タイミングを正確に知る必要があります。 ということでSerial.print()が完了するタイミングがいつなのか調べてみました。 ※ちなみにRS485通信をするのであれば、既存のライブラリはあるようですがせっかくなので調べてみました。 # データ取り では実際に色々データ取りをしてみた結果です。 ## 成功パターン1 ```arduino:成功パターン1 void setup() { Serial.begin (1200); pinMode(13, OUTPUT); } void loop() { digitalWrite( 13, HIGH ); Serial.print("U"); Serial.flush(); digitalWrite( 13, LOW ); delay(1000); } ```  上の波形(黄色)がTXD、下の波形(水色)がPIN13です。 "U"のコードは0x55なので、TXDはわかりやすい波形になりますね。 また、Serial.Print()の前にPIN13をHIGHにして、Serial.print()、Serial.Flush()が終わった後にPIN13をLOWにしています。 波形を見てわかる通り、TXDにすべてbitを送信し終わってからPIN13がLOWに戻っていますね。 ## 成功パターン2 同様に複数バイトを送った場合も見てみましょう。 ```arduino:L成功パターン2 void setup() { Serial.begin (1200); pinMode(13, OUTPUT); } void loop() { digitalWrite( 13, HIGH ); Serial.print("UUU"); Serial.flush(); digitalWrite( 13, LOW ); delay(1000); } ```  こちらもきちんと3バイト分のデータを送信し終わってからPIN13がLOWになっています。 ## 単純にやってダメだった例 では、Serial.flush()しないとどうなるかを試してみましょう。 ```arduino:ダメな例1 void setup() { Serial.begin (1200); pinMode(13, OUTPUT); } void loop() { digitalWrite( 13, HIGH ); Serial.print("UUU"); digitalWrite( 13, LOW ); delay(1000); } ```  Arduino自体がUART FIFO(転送データを一時的にためておく場所)を持っていますので、FIFOに3バイト書き込む処理をする瞬間だけPIN13がHIGHになっています。 これでは全く意味がありませんね。 ## FIFOの空きを監視したかったけどダメな例1 というわけでSerial.fulsh()で問題は解決するのですが、Serial.flush()はArduinoの旧バージョンでは送信完了を待つのではなく、バッファをクリアする仕様のようです。今更旧バージョン使うこともないとは思いますが、別の方法でできないかなと試行した結果ダメだった例です。 ```arduino:FIFO監視したかったけどダメだった例1(40) int count; void setup() { Serial.begin (1200); pinMode(13, OUTPUT); count=Serial.availableForWrite(); Serial.println(); Serial.print(count); } void loop() { digitalWrite( 13, HIGH ); Serial.print("UUU"); while(Serial.availableForWrite()!=count){}; digitalWrite( 13, LOW ); delay(1000); } ```  Serial.availableForWrite()でFIFOの空きバイト数がわかるので、空きを監視して送信完了を検出しようという試みです。 どういうわけか、1バイト分だけ待たされて、1バイト分送信完了した時点でPIN13がLOWになっています。 おそらくリングバッファの管理の都合、1バイト余分に確保されるのと、UARTのシリアライザー自体にFIFOとは別に1バイト分のバッファがあるのでしょう。 とりあえずこの方式ではダメなようです。 ## FIFOの空きを監視したかったけどダメな例2 というわけでダメだということは確定したのですが、何かの役に立つかもしれませんので、2バイト以下の場合はどういう動きをするのかもデータ取りしてみました。 ```arduino:FIFO監視したかったけどダメだった例2 int count; void setup() { Serial.begin (1200); pinMode(13, OUTPUT); count=Serial.availableForWrite(); Serial.println(); Serial.print(count); } void loop() { digitalWrite( 13, HIGH ); Serial.print("UU"); while(Serial.availableForWrite()!=count){}; digitalWrite( 13, LOW ); delay(1000); } ```  単純に、1バイト分の時間が無くなっただけです。 ということは、Serial.availableForWrite()で取得される空きの他に、見えないFIFO(のようなもの)が2バイト分あることになりますね。 # 結論 Serial.flush(); で送信完了を待ちましょう。 旧バージョンでの対応方法は結局不明です。