
私は ATMEL 組み込みコントローラー (at91sam9x25e) とそのシリアル ポートを使用して、RS485 Modbus RTU 経由で送受信しています。通常の通信は問題なく動作しますが、書き込み時に、一部のパッケージがメッセージ内の 2 つの文字間で約 3.5 ミリ秒の遅延で分割され、新しいメッセージとして検出されることがあります。
設定:
- モノ 3.2.8
- Modbus RTU 19.2 kボー
- RS485
- 自動RTS
パッケージ全体を一度に送ります:
try
{
Thread.Sleep(_timePreSend);
_serialPort.Write(buffer, offset, count);
} catch(Exception err)
{
log.Error(err.Message);
} finally
{
Thread.Sleep(2);
}
以前、RTS を手動で有効化/無効化していたときに 2 ミリ秒を使用して、バッファ (UART または FIFO) から最後のビットを送信しました。
オシロスコープからのキャプチャ:
120318000100020032000403200000001C0005000000000000000003D <- ギャップ -> B6
sttyからの出力:
stty -F /dev/ttyS4 -a
speed 19200 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke
このギャップの原因は何でしょうか? これを回避するにはどうすればよいでしょうか?
答え1
原因を正確に特定することは困難です。残念ながら、Linux シリアル ポートはタイミングを保証しません。また、通常の UART プロトコルは、このような厳しいタイミング要件を持つようにはなっていません。プロトコルでこのようなタイミング保証が必要な場合は、カーネル ドライバーを調べて、おそらく割り込みを使用して UART バッファーが常にタイムリーに補充されるようにし、すべてのバイトをギャップなしでアトミックに配信することを保証する独自のドライバーを作成する必要があります。カーネルの助けがなければ、ユーザー空間からこのような保証を行うことはできません。
そうは言っても、分割の原因を突き止め、おそらくそれを回避し、実際に目的にかなうほど信頼性が高いことを経験的に判断することはできます。遅延は常に最後のバイトの前に発生しますか? もしそうなら、スタック内の何かが何らかの理由で文字をバッファリングしているようです。おそらく、複数のバイトを一度にハードウェアに配信し、小さなタイムアウトの後に 1 バイトのみを配信することでスループットを最適化するためです。まず、ユーザー空間アプリが適切に動作していることを確認する必要があります。実行してシステム コールをチェックしstrace
、パケットが 1 回のシステム コールでカーネルに書き込まれていることを確認しますwrite
。そうであれば、UART ドライバーのカーネル ソースを読んで、奇妙なバッファリングが関係していないかどうかを確認します。
もう 1 つの可能性は、カーネル ドライバーがカーネル スレッド (またはプロセス コンテキスト内) でデータを処理しているだけであり、発生しているギャップは別のプロセスがスケジュールされているために発生している可能性があります。その場合、これを回避する唯一の方法は、カーネル ドライバーを変更するか、独自のドライバーを作成して、他のプロセスが優先されることなく厳密にリアルタイムで転送を処理することです (つまり、すべてを割り込みコンテキストで実行します)。
答え2
ようやくこの動作の原因が分かりました。通信ループでビジーウェイトを使用していたため、CPUに負荷がかかっていました。そこで、次のようなSerialPortコマンドを使用すると、BytesToRead と Readまったく理想的ではありません: .NET System.IO.Port.Serialportを使用する必要がある場合
そこでByteStreamの非同期関数を使ってみましたReadAsync と WriteAsync私の新しい質問で説明したようにシリアルポートとReadAsyncでタイムアウトを使用する適切な方法CPU 使用率が大幅に低下し、ギャップもまったくなくなりました。