STM32でSDI-12

 ワンワイヤーのシリアル通信規格のSDI-12をSTM32につなげたいお話です。屋外設置の市販センサーに使われている規格で、 仕様書(sdi-12.org)によると初版が1988年となっていますのでそこそこ古いことが分かります。

f:id:jun930:20220417101110p:plain

通信速度とフレームフォーマット

 通信の仕様は、1200bps, 7ビット, 偶数パリティ, ストップビット1と普通のシリアル通信でOKそうですが、

f:id:jun930:20220417101411p:plain

シリアルデータライン

 信号の電圧が通常のTTLシリアルと反転しているうえに、spacingが3.5~5.5(V)となっており3.3V系のCPUには直接接続できないです。

f:id:jun930:20220417101842p:plain

 更に、コマンドを送る前に少なくとも12msecのブレーク信号を出せと書いてあります。

 これに関してOSSを探してみるとArduinoのライブラリが何種類かヒットしました。コードを読んでみると。タイマーと割り込み使って送受信しています。

github.com ATMega328Pはシリアルが一個なのでこの実装で納得ですし、5V系のArduinoでしたらピンに直結できることが本当に楽で、手持ちのArduinoで試してみたところあっさり通信できました。

 一方STM32の場合はシリアルが多チャネルあります。今回実装しようとしているSTM32L476はペリフェラルの機能としてシリアル信号を反転させこともでき、GPタイマを使う必要もなさそうなのでArduinoの実装例はあまり参考にならないと思いました。

 手始めにペリフェラルでシリアル信号を反転させてお決まりの双方向レベル変換回路を使おうと考えてみたのですが、やってみたら動きませんでした。

f:id:jun930:20220417104427p:plain

お決まりのレベル変換回路

 この回路はセンサー側が受信状態(ハイインピーダンス)のとき5Vが出続けますので使えません。ここで高価な双方向レベル変換ICを使うことに負けを感じたことをきっかけに、Arduinoのように直結とまでは行かないまでもなるべく簡易な回路にならないものか?と思い始めました。

f:id:jun930:20220417105114p:plain

仕様書のおすすめ回路

 仕様書では送信側は3ステートバッファを使えとなっていますが、今日は虫の居所が悪いのか、これも気に食わない。CPU側TX,RXの2線を使うのであれば、送受信の切り替えなどしなくて良い気がするのです。

 なるべく簡単に制作することを念頭に使用する部品も秋月電子さんで入手可能なもので作ることにしました。

f:id:jun930:20220417111358p:plain

STM32用簡易接続回路1

 送信側はインバータ、受信側はFETで信号の反転を行うことにしました。送受信の切り替えを行わないため送信側の出力インピーダンスを大きく(10kΩ)します。使用するセンサーの出力インピーダンスより十分大きくすることで、受信用FETのゲートでセンサー出力が優先されるようにしました。お決まりレベル変換の失敗を経てこの回路はうまく動きました。

f:id:jun930:20220417113345p:plain

 受信側に送信コマンドのエコーとリザルトが順次返ってきます。このような場合でも、DMAを設定するだけで通信中に受信処理をしなくても取りこぼしが起きないところがSTM32の良いところだと思います。

 ここで、ブレイク信号の送信についてです。

f:id:jun930:20220417114348p:plain

 STM32のHALにHAL_LIN_SendBreak()なる関数があり、これを呼ぶとシリアルにブレイク信号を出すことができますが、ロジアナで信号を見ると8msec強しかありませんでした。手持ちのセンサーは動作するものの仕様的には最低でも12msecですので、UARTに割り当てたピンを一度GPIOにしてLOW出力、少し待ってピンをUARTに戻す方がお行儀が良いと思いました。


 STM32のシリアルは送受信のピンを別々に反転することができます。STM32用簡易接続回路1では受信側をFETで反転していましたが、受信のピンを反転すれば抵抗に置き換えることもできそうです。

f:id:jun930:20220417115033p:plain

STM32用簡易接続回路2(受信側反転なし)

 これも同様に動作しました。これであれば、実装面積も少ないし普通のシリアル通信とほぼ同じ使い勝手のコードで納得したので一連の実験はこれで終わりです。

 STM32にSDI-12のセンサーを接続するには、秋月電子さんで販売されているインバータ(TC7S14F)一個と抵抗3つで出来ることが分かりました。