スパイス  組み込み制御装置の受注製作

自社標準仕様の製作
平成27年12月 13日

 汎用の高速シリアル通信機能(24)  SPI仕様の検討

 現在の題名で書き始めた当初はRS-485を使って1Mbpsの速度で通信を行うことを目的にしていたのですが、デバッグのやり易さを考慮して実験基板の作成を決めました。ブレッドボードでは実験環境を維持するのが面倒になったためでした。
どうせ基板を作るのなら他の実験も出来るようにとCANやSPI, USBなどの通信機能を含めた基板を作りました。
 これと平行してプログラムの記述性の問題から従来使用してきたC言語からC++言語への移行を考えるようになり、現在は基板の動作確認とC++言語の習得を平行して進めています。
本来は、題名を改めて書いていくべきですが、あまり細かなことは気に掛けず気長に進めていきます。

 RS-845通信プログラムの前にSPI通信をプログラムします。こちらの方がハードウェア仕様が単純なので、その分プログラムの見通しも良くなります。
SPI通信の特徴は下記になります。  最大のメリットはその通信速度です。また複数デバイスと通信できることと合わせて、少ないピン数で高い通信レートを稼ぐことが出来ます。
反面ノイズに起因する通信エラーに関しては全く考慮されていないので、回路設計上はそれなりに気を使う通信仕様でもあります。
特に隣接する基板間で通信する場合は基板の出入り口でノイズを拾い易いため、それなりの対策が必要です(SPI通信で誤動作するのは大半が信号をケーブルに引き出しています)。
 通信エラーに関しては主として回路的な対策を行ない、可能であればソフト的にCRCデータを付加するなど補完的な対策を行います。

 SPI通信の通信相手としては下記を想定しています。  SPIスレーブ仕様デバイスは世の中に多数あります。今回は手元にあるRTCとフラッシュROMを使用します。
これらは送受信を開始する段階で送信および受信する文字数が決まっています(決めることが出来る)。これをBinaryモードとします。

 対してCPU同士をSPI通信で接続する場合には、両者の間で通信仕様を取り決める必要があります。
幾つかの方法がありますが、デバイスの仕様から特定のデータを送受信した段階で割り込みを発生させ通信を終了するパターン一致モードを使用します。特定データが通信の終端を示すため、通信開始前に通信文字数が不確定でも通信が成立します。当然、通信バッファのサイズからくる最大通信文字数の制約はありますが。
このモードをAsciiモードと呼ぶことにします。
 Asciiモードの実装には、ハードウェアとして特定の通信データを検出して通信を終了させ割り込みを発生させる機能が必要です。
この機能を持っていないマイコンでは通信時CPUに大きな負担がかかります。一般にスレーブ側は処理負担が少ないのですが、出来るだけ上記機能を有するPIC32を使用することが望まれます。これらをソフトウェア処理で行う場合はポーリングが必要になると思われます。

 Binaryモードを使用する多くのデバイスでは、SPIのマスタとなるCPUからデバイスに対して数バイトのコマンドを送った後、その結果を受信します。
従って、通信方法としては、送信のみ・受信のみ・送信後受信の3方法を用意します。これらについてもう少し細かく検討してみます。

1.送信のみ
送信の手順は以下のようになります。 
  1. 全てのデバイスのCS信号をネガティブの状態にします。
  2. 通信したい通信モード(SPIには信号の極性の組み合わせからモード0〜3までの通信モードがあります)に設定する
  3. 通信したいデバイスのCS信号をアクティブに設定する。
  4. 送信を開始して指定された文字数だけ送信する
  5. 送信された文字が全てSPIから送信されたのを確認してCS信号をネガティブにする
 SPIの送受信レジスタへの読み書きはDMAが行います。従って一旦動作を開始すればCPUは指定された文字数の転送が終了したときの割り込みを待てば良いだけです。
しかし面倒なのは、送信側のDMA転送が終了するのは最後の送信データをSPI送信バッファに送った直後です。まだ、SPIからは送信できていません。
  @ : 最終送信文字の転送タイミング
  A : CS信号ネガティブのタイミング
 時間→
−−−−−−−−|−−−−−−−−−−−−−−−−−
送信側DMA    @ ← この直後ではまだAの処理は出来ない
SPI送受信       @--@
受信側DMA           @
CS信号                 A


このため、送信側DMAの送信終了で割り込みを掛けても、その後SPIが送信を完了するまでCPUを割り込み処理関数内部で待たせるか、SPIの送信完了割り込みを有効に設定して一旦割り込みを抜け、その後SPI送信終了割り込み処理の中でCS信号をネガティブにする必要があります。
組み込み装置で割り込み処理関数内で処理を待つというのはいかにも間抜けな方法ですし、PIC32mxでは割り込みによるレジスタの退避・復帰ペナルティはそれなりに大きいので、2度も割り込みを起こすのは気が引けます。
 このため送信処理でも同時に受信を行うことにします。受信を行っても受信データを無視すればいいだけですし、受信側なら指定された文字数を受信完了した段階で、SPIの送信処理も終わっています。従って、送信側DMAでは指定された文字数を送信し(割り込みは使用せず)、同時に受信側DMAが指定文字数を受信した段階で割り込みを発生させ、必要な処理をした後でデバイスのCS信号をネガティブに設定します。
受信データ分の無駄なDMA転送が発生しますが、一度の送信での文字数は大半が数〜十数バイト程度ですので、この方法が最も有利と判断します。

2.受信のみ
 基本的にはBinaryモードで受信のみの処理が必要になることはないのですが、基本処理としては作っておく必要があります。
SPI通信でのマスタ側では受信するときでも同期クロックを供給する必要があります。同期クロックを供給するにはSPI送信バッファにデータを書き込む必要があるので、事実上送信処理が必要です。受信文字数分だけ送信データを送ると同時に受信側で受信データを受け取ります。なお、このときの送信データは受信するデバイス側で無視されますのでデータ値は何でも良いです。

3.送信後受信
 上記の二つの処理を順番に呼ぶだけで、何も面倒なことはありません。

 このようにしてみると、Binaryデータの送信処理と受信処理は極めて似通った処理になります。実際のプログラムでも一つの関数として実装しています。

 次はAsciiモードについて考えますが、長くなるので日を改めます。


目次へ  前へ  次へ