起きた問題
ある製品のRS485が客先でコケた。現象発生後、その機器は一切の指令に応答しなくなった。
実験室で繰り返しテストした。短絡、バスを完全に占有してフレームを乱発、長時間ハイ/ローが続く異常などを試しても、どれも再現できず、機器の通信はかなり頑健だった。2日間調べて、この部分に関する自分の実装にはますます自信がついてきた。それでも客先では確かに問題が起きている。現場へ行くしかなかった。その時点で、私は現場の差動線に問題があると思い込んでいた。
いや、違った。本当に自分の問題だった。要するに、調査の結果、機器はRS485 ICが送信状態に張り付いた状態になっていたことが分かった。
何が原因だったのか
送信すべきデータがあるときは、まず送信状態に切り替え、送信が終わったら受信状態に戻す。
一見もっともらしいが、実際はそうではない。この機器はUARTの管理にDMAを使っておらず、送受信データはすべて周辺回路の割り込みに依存している。ICの状態切り替えも割り込みが担っている。何らかの事情で、物理世界のありとあらゆる外乱が割り込みの発生/処理を妨げた。あるいは、特殊な異常が周辺回路の再起動を引き起こしたのかもしれない。とにかく、「送信終了」を割り込みで検知できなくなり、そのままICは送信状態に張り付いた。しかも、この機器は自分から送信データを生成することはないため、受信もできないし送信もされない。さらに、バスを送信状態で占有したままになる。

正常時の波形

異常発生時の能動側の波形
チャネルの考え方
半二重通信とDMAには、実は似たところがある。どちらも、バラバラにやってくるデータを集めて、1本のチャネルを作り、一括して転送する必要がある。
これまでに大量出荷された機器は、すべてDMA+RS485方式を採用していた。DMAの割り込みは周辺回路に依存しないので、物理世界で何が起ころうと、DMAのチャネル転送が完了すれば割り込みが発生する。だから、このような問題は起きなかった。
今回は後からRS485機能を追加したため、手抜きしたのと、安定性を考えて変更をできるだけ小さくしたかったので、DMAを使わなかった。また一つ勉強になった。
非チャネルの考え方
割り込み+レジスタ直接操作でストリームデータのインターフェースを管理する以上、この方式は非チャネルだ。しかし、物理層のRS485はチャネル形式だ。ではどうするか。
非チャネル方式に頼ってチャネルを管理すべきではないと思う。たとえば、ICをハイ/ローに切り替える処理を非チャネル方式の割り込み内で行うのは不合理だ。チャネルの方向は、専用のポーリングで管理すべきだ。周期的に送信状態かどうかを監視し、送信バッファが空か、バスがアイドルかといった条件を組み合わせて判定する。そして、送信状態でいる必要がない時間が一定以上続いたら、強制的にチャネルを受信状態へ切り替える。
(周辺回路のフロー制御ピンを使えばいい、という話もあるが、それはタイミングが制御不能だし、IC共通の仕様でもない。)
今後、Dataflowライブラリにこの仕組みを追加して、非チャネル方式のアプリケーションでも使えるようにする予定だ。
RS485の怪