平成27年12月 18日
汎用の高速シリアル通信機能(27) SPI仕様の検討(3)
SPI通信でのAsciiモード受信の仕様検討に誤りがありました。
当初の基本的なアイデアは下記の通りです。
- マスタの受信側は終端文字'\0'が送られるまで受信を続け、'\0'の受信を持って受信割り込みを起こして受信を終了する
- マスタの送信側はSCK信号を派生させるためダミーの送信処理を継続し、受信側の'\0'受信割り込み発生を検出してダミー送信処理を終了する。
通常の送受信であればこれで問題は無いのですが、Asciiモードでの通信では通信相手はCPUが想定されています。
相手がCPUでの問題は、受信開始の段階で相手側の返信処理が終わっていない可能性があります。この場合当初の基本的なアイデアでは、スレーブ側SPIの送信バッファが空の状態で送信が行われるため、SPIの仕様に従ってデータ0が送信されます。データ0は終端文字であるため、マスタ側はスレーブ側の処理が終わっていないことを認識できます。この場合は少し時間をおいて再実行します。
これで上手くいくと思ったのですが甘かった。
Asciiモードでの受信処理の制約に受信データの終わりを示す終端文字+1文字分の送受信が行われるというのがあります。つまり、終端文字の'\0'の次の文字までが読み出されるが、この最後の文字は読み捨てられます。
もし、マスタ側が受信開始した段階ではスレーブ側の返信処理が間に合わずデータ'\0'が返信されている途中に、スレーブの返信処理が終わり返信データをSPIの送信バッファに送った場合、返信データの最初の文字は上記の制約に従って読み捨てられます。次にマスタが再度の受信処理を行った場合にはスレーブからは2バイト目以降のデータが送信されることになり、通信は破綻します。
簡単なタイミング図を下記に示します。最初にCS信号がLになった所がマスタ側からスレーブへの返信要求です、この段階ではまだ返信処理が終わっておらず、データ0が返信されます。マスタ側はデータ0の受信を持ってデータ受信を中断しますが、中断処理が行われるのは次のデータをダミー受信した後になります。このダミー受信(図中ではXと表記したデータ)の所にスレーブ側の返信データが入る、つまりこの段階でスレーブ側の返信処理が終了し、1バイト目の返信データが送信バッファに送られると、1バイト目のデータは無視されてしまいます。次の返信要求ではスレーブは2バイト目以降を順次返信してしまいます。
CS信号 HHHLLHH HHHLLLL....
スレーブ送信 ----0X-- ---23456....
で、対策を考えたのですが、
- マスタ・スレーブ間の送受信を確実に行うため、ハンドシェイク信号を追加する
- 終端文字による動的な送受信を諦めて他の方法に移行する
の二通りを考えました。
ハンドシェイク信号の追加はハードウェアの負担もあり、また今回のSPI通信では隣接する基板間での通信をも想定しているため、信号線の追加は多くのリスクをはらみます。最後の手段として残しておきます。
他の方式の検討では、幾つかの方法が思いつくのですが、最も簡単に受信する文字数を最初に受信することにします。つまり、マスタがスレーブに対して返信要求するとき、最初は1バイトだけの受信を行います。従来どおりDMAを使ってもいいですが、1バイトだけなので直接CPUからSPIにデータを送ってもいいでしょう。このときの返信データを文字数として2回目の受信処理を文字数指定で行います。最初の返信が0ならまだスレーブの返信処理が終わっていないし、それ以外ならすぐに返信処理にかかれます。この仕様では一度の返信文字数が255文字以内に限定されますが、これだけあれば大半は十分な値です。それ以上のデータ量がある用途では通信書式側で工夫することにします。