RS-485

長距離通信が必要となったので、RS-485を検討しています。

●マスタ-スレーブ

多数の機器を接続するため、マスタ-スレーブ方式を使います。マルチマスタという方法もありますが、アービトレーション処理が面倒なのでやめます。

CANだとマルチマスタと同じで、アービトレーションも自動で行われるので楽なんですが、長距離だと不安があります。

マスタ-スレーブ方式の場合、マスタ1つ、スレーブ多数という形態になります。それぞれのスレーブには固有のID(アドレス)を割り当てて、それをマスタが指定するようにします。

マスタがスレーブへ通信する場合は簡単ですが、スレーブが自律的に送信を開始することはできません。そのため、マスタが頻繁にスレーブに問合せする必要があります(ポーリング方式)。それが面倒なので、RS-485は使いたくなかったのですが、そうもいかなくなりました。

スレーブはすべての通信を受信して、自分のアドレスと一致したときだけ何等かのアクションを起こすというように作る必要があります。

このアドレスはI2Cなどのように、通信フォーマットで決められているわけではないので、通信データの一部にアドレスを含めるなどして、送受信それぞれのアプリで管理する必要があります。

●全二重と半二重

RS-485でも、2つの機器を全二重でつなぐだけなら、RS-232Cの通常のシリアル通信となんらかわりがありません。長距離対応のRS-232Cみたいな感じです。問題は3つ以上の機器をつなげる場合です。回線は1つなので、各機器が自分勝手に通信を始めるわけにはいきません。

前述のようにマスタは各スレーブへ問合せして、指名されたスレーブは何等かのアクションを起こすわけですが、全二重の場合は、マスタは問合せをして、スレーブからの応答を待ちます。スレーブはマスタからの指名に対してデータを送信します。単純にはそれだけです。

で、半二重の場合もやり取りは同じなのですが、問題は送信ラインと受信ラインが兼用になってるため、マスタは受信を開始する時は送信ドライバを無効化して受信ドライバを有効化、スレーブは受信ドライバを無効化して送信ドライバを有効化する必要があります。通信が終わったら、その逆で、マスタは受信バッファを無効にして送信ドライバを有効化、逆にスレーブは送信ドライバを無効化して受信ドライバを有効化するという、ドライバの送受信を切り替える必要があります。これが面倒です。

通信が終わったタイミングで切り替えればよいのですが、ArduinoのSerialドライバでは、送信が完了したことが判断できません。送受信にリングバッファが使われていますが、送信の場合、Serial.print()など実行したあと、関数を抜けたときに通信が終わっているわけではなく、実際はリングバッファに未送信のデータがたまっていて、順次送信されていきます。なので、送信が完了したタイミングが判断できません。1バイトの送信完了ならレジスタを監視すればわかるのですが、リングバッファはドライバが管理しているメモリエリアなので、それを覗かない限り判断できません。

Serialのソース修正してリングバッファが空かどうか読み出す処理を追加するのは簡単なんですが、あまり、システム部分をいじりたくないし、継承して新しいドライバ作るにもちょっと手間だし、どうしたものかと。

PIC使って自分でシリアル通信のドライバ作るなら、このへんは問題ないですね。

で結局、送受信の切り替えが不要な全二重を使うことにしました。処理が単純にでき、タイミングのずれなどで通信不良になるなどのトラブルも少なくなります。

全二重の場合、通信ラインが半二重の2倍、ツイストケーブルの2ペアが必要になります。その分、ケーブルのコストが上昇しますが、もともと6芯3ペアのツイストを使う予定なので今回は問題なしとしています。

●マスタ、スレーブの入れ替え

半二重の場合、何等かのタイミングを見計らって、マスタとスレーブの機器を入れ替えるということもできます。

しかし、全二重の場合は、配線上、マスタとスレーブで送受信の向きがきまってしまうため、途中でマスタとスレーブを入れ替えることはできません。スレーブ間はストレート結線ですが、マスタとスレーブ間はクロス結線にする必要があります。これが、マスタとスレーブを入れ替えることができない理由です。

●RS-485トランシーバ

現在、秋月では半二重のトランシーバしか販売されていませんので、DigKeyやRSコンポーネンツなどで購入する必要がありますが、Analog Devices社のADM4856ARZ (SOIC 8pin)を使おうかと考えています。

コメントは受け付けていません。