lyricalmagicalのアイコン画像
lyricalmagical 2023年01月29日作成 (2023年01月30日更新)
セットアップや使用方法 セットアップや使用方法 閲覧数 1328
lyricalmagical 2023年01月29日作成 (2023年01月30日更新) セットアップや使用方法 セットアップや使用方法 閲覧数 1328

[Arduino]Serial.print()は送信完了まで待ってくれない

[Arduino]Serial.print()は送信完了まで待ってくれない

半二重通信(一つの伝送路で送信と受信を行うので、送受信が同時にできない通信手段。RS485等)を行うためには、自身の送信終了タイミングを正確に知る必要があります。
ということでSerial.print()が完了するタイミングがいつなのか調べてみました。
※ちなみにRS485通信をするのであれば、既存のライブラリはあるようですがせっかくなので調べてみました。

データ取り

では実際に色々データ取りをしてみた結果です。

成功パターン1

成功パターン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

同様に複数バイトを送った場合も見てみましょう。

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()しないとどうなるかを試してみましょう。

ダメな例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.flush()で問題は解決するのですが、Serial.flush()はArduinoの旧バージョンでは送信完了を待つのではなく、バッファをクリアする仕様のようです。今更旧バージョン使うこともないとは思いますが、別の方法でできないかなと試行した結果ダメだった例です。

FIFO監視したかったけどダメだった例1

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バイト以下の場合はどういう動きをするのかもデータ取りしてみました。

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();
で送信完了を待ちましょう。
旧バージョンでの対応方法は結局不明です。

2
lyricalmagicalのアイコン画像
電子デバイスはとってもりりかるなの
ログインしてコメントを投稿する