SSブログ

MC9S08JS8 でLEDマトリックスを駆動する ~ ソフトウェア編 (1) ~ [HCS08]このエントリーを含むはてなブックマーク#

2813417

ハードウェアは、ひとまず放っておいて、ソフトウェアの話を書きます。

LATCH*信号をトグルするプロトコル

ハードウェアを工夫する事によって、SPIモジュールが使えるようになったのですが、実際に 16×16 LED マトリックス基板を使うには、もう一工夫必要です。 理由は、この基板のプロトコルが純粋なSPIではないからです。

通常のSPIプロトコルは、通信の前に Slave Select (SS) 信号をアサートして、データの転送が終わったら SS 信号をネゲートして通信の終了を知らせます。 ところが、この 16×16 LED マトリックス基板では、通信の終了時に LATCH* 信号を一回だけトグルするプロトコルが使われています。 また、データ通信の単位は、接続する 16×16 LED マトリックス基板の枚数に依存していて、今回のように3枚の基板を使用した場合には144ビット (18バイト)の通信が必要です。

LedMatrixUsbTiming1.png

この図では、24ビット(3バイト)のデータを送信する場合のタイミングを示しています。 SPIを使用して1バイトのデータを3回送信してからLATCH*パルスを発生させています。

SPIは、送受信バッファを持っている

MC9S8JS8のSPIモジュールは、1バイトずつの送受信バッファを持っています。 また、送信バッファが空になったことを示す SPTEF (SPI Transmit Buffer Empty Flag) というフラグと受信バッファにデータが到着した事を示す SPRF (SPI Read Buffer Full Flag) というフラグがあり、ソフトウェアは、これらのフラグを調べて送信データを送信バッファに書き込み、受信データを受信バッファから読み込みます。

LedMatrixUsbSequence1.png

今回、LATCH*パルスを発生させるために採用したのは、SPRFフラグだけに着目する方式です。 SPRFフラグは、SPIの通信が終了したときにセットされます。 そのため、確実に通信が終了してからLATCH*パルスを発生させることができます。


LedMatrixUsbSequence2.png

この時、SPTEFフラグも併用して、送信バッファを有効に使うことができないかと考えたのですが、うまくいかない事がわかってきました。 理由は、SPRFフラグを確実に検出しないと、SPRFフラグ検出の間に二つ以上のSPI通信が入ってしまう可能性があるからです。 この例では、メインループの動作が間に合わなかった場合を図示しています。 一番目のSPRFフラグの処理に手間取ったため、二番目のSPRFフラグ(赤い枠を付けたもの)がクリアされてしまいます。 そのため、メインループは、二番目のSPRFフラグの発生を検知することができません。 このような状況が発生してしまうと、SPI通信の終了が正しく検出できなくなり、LATCH*パルスを発生させる直前でプログラムの実行が止まってしまいます。

SPRFフラグとSPTEFフラグを併用する方法は、もちろんあります。 それは、割り込みを使用する方法です。 フラグの発生と同時に処理を開始すれば、フラグの取りこぼしはほぼなくなると思います。 ところが、このアプリケーションでは、USBインターフェースで優先的に割り込み処理を行うために、他のモジュールが発生する割り込みはなるべく使いたくありません。 このような事情から、SPRFフラグだけをメインループで監視する方法を取り入れました。

ノンプリエンプティブなステートマシン

SPIにデータを送り込む処理は、ステートマシンで記述できます。 また、ステートマシンの動作は、割り込みを使わずにメインループに取り込みます。 そこで、ステートマシンの処理単位を細切れにして、自発的にプログラム実行件を返納する「ノンプリエンプティブ」なステートマシンとして実装しました。

LedMatrixUsbState1.png

この状態遷移図が、データ送信を行うためのステートマシンです。 まず、SPIの送信バッファが空いていることを確認します。。 そして、SPRFフラグを監視しながら、16ビットのデータを9回送信し、LATCH*をトグルします。 これで、1フレーム分の処理が終わりです。 最後に次のフレームに備えて、RTCフラグを待ちます。 この処理を延々と繰り返すのが、ステートマシンの仕事です。

ステートマシンの状態コードは、このように割り当てました。

状態状態コードお仕事
Generate ROW data0LEDマトリックスに送る行指定データ"row"を作成します。
Confirm TX buffer empty1送信バッファが開いている事をあらかじめ確認します。
Send column 525桁目のデータを送信します。
Wait for column 5 transfer ends3通信の終了を待ちます。
Send column 444桁目のデータを送信します。
Wait for column 4 transfer ends5通信の終了を待ちます。
Send row for column 4 and 564、5桁目の行指定データを送信します。
Wait for row transfer for column 4, 5 ends7通信の終了を待ちます。
Send column 383桁目のデータを送信します。
Wait for column 3 transfer ends9通信の終了を待ちます。
Send column 2102桁目のデータを送信します。
Wait for column 2 transfer ends11通信の終了を待ちます。
Send row for column 2 and 3122、3桁目の行指定データを送信します。
Wait for row transfer for column 2, 3 ends13通信の終了を待ちます。
Send column 1141桁目のデータを送信します。
Wait for column 1 transfer ends15通信の終了を待ちます。
Send column 0162桁目のデータを送信します。
Wait for column 1 transfer ends17通信の終了を待ちます。
Send row for column 0 and 1180、1桁目の行指定データを送信します。
Wait for row transfer for column 0, 1 ends19通信の終了を待ちます。
Assert LATCH20LATCH*出力をアサートします。
Negate LATCH21LATCH*出力をネゲートします。
Assert STROBE22STROBE出力をアサートします。
Go to next line23行カウンタを進めます。
Prepare for 1msec timer24RTCフラグをクリアします。
Wait for 1msec timer25RTCフラグを監視し、フレームの開始タイミングを待ちます。
Return to state 0>25最初の状態#0に戻ります。

たとえば、それぞれの状態での処理は、こんな風になります。

    case 3:
      // Wait for a transfer ends
      if (SPIS_SPRF) {
        vwDummy = SPID16;
        st++;
      }
      break;

SPRFフラグが検出されたら、フラグをクリアし、次の状態に遷移します。 検出されなければ、今の状態に留まります。

    case 4:
      // Send column 4
      SPID16 = buf[line][4];
      st++;
      break;

次の16ビット・データの送信を指示し、次の状態に遷移します。

次回予告

次回は、最初のテスト・プログラムのご紹介と実行の様子をごらんに入れます。


nice!(0)  コメント(0)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました