NFC Shield用ライブラリのソースコード(PN532_SPI)を読む(2)
体感時間として一瞬で半月すぎてしまっていたことにびっくりしつつ、一方まだ新年度入ってから半年も経っていないことに驚きつつ。濃いですね。濃いからこそあっという間にすぎる的な。
基底にあるArduino標準SPIライブラリ
まず前提として、このライブラリは内部でもArduinoの標準SPIライブラリを使っています。PN532.hの83行目のSPIClassなんかがそうで、PN532クラス自体、これを継承してるらしいことがPN532クラスのコンストラクタからも分かります(PN532.cppの29行目付近)(コロン1つは継承らしい)。
コンストラクタでは、_csというprivate変数の設定が行われています。_csはSPIのチップセレクトのことみたいですね。お約束、といったところでしょうか。
サンプルスケッチから流れを読む
実際のスケッチでは、クラスが宣言された後まずsetup()関数内でPN532.cppのbeginメソッドが呼ばれていることが分かります。その後、getFirmwareVersionメソッドを呼び出しています。PN532のライブラリにはいくつかサンプルスケッチがありますが、この二点は大体呼ばれているようです。ということで中身を見てみます。
beginメソッド
この中ではSPI関係の初期化(PN532のライブラリ自体が、ArduinoのSPIライブラリを参照している)と、Not exactly sure why but we have to send a dummy command to get synced upとの怪しげなコメントを添えてダミーパケットの送信を行っています(sendCommandCheckAckのところ)。
getFirmwareVersionメソッド
サンプルスケッチではこのメソッドを呼び出して、ちゃんとした返り値が得られることをPN53xボードの正当性としているようです。メソッド自体は内部でPN532のgetFirmwareVersionコマンドを呼び出して、そのレスポンスを返り値としています。
では、どういう時にちゃんとした返り値が得られないでしょうか。コードを見ると、return 0となる場所は2カ所で、一カ所はsendCommandCheckAckメソッドの戻り値が偽となる場合、もう一カ所はレスポンスの最初6バイトが設定したものと違っていた場合です。
より詳しくいうと、前者はコマンドを送ったのにAckが返ってこなかった、後者は期待したレスポンスを得られなかったということになります。
これらの関数の中では、sendCommandCheckAckやreadといったメソッドが使われています。以下では、さらに深く読んでいく意味でこれらメソッドの中を探っていきます。
sendCommandCheckAckメソッド
PN532コマンド(getFirmwareVersionなど)を実行する場合、対応したメソッド内でsendCommandCheckが呼ばれています。次はこのsendCommandCheckAckメソッドの実装を探っていきましょう。
このメソッドは実際にコマンドを送って、ACKが返ってくるかどうかの処理を受け持っています。引数には送るコマンドへのポインタとその長さを渡します。オプションでタイムアウト値を渡すこともできます。戻り値はコマンドが正しく送信できたかどうかです。
コマンドの送信については、内部的に後述するwriteCommandメソッドを使用しています。ユーザーマニュアルにもありますが、ホストコントローラーからコマンドを受信したら、PN532はとりあえずACKを返し、その後にコマンドレスポンスを返します。コード上では、SPI通信の状態が準備完了(PN532_SPI_READY)になるまでループを回しています。その際、タイムアウトを設定して、それを超えたら偽を返す実装になっています。
待ちの処理についても、タイムアウトで設定した数値になるまで、10ミリ秒ごとにデータリンク層で規定されているACKをみるという素朴な実装がされています。
PN532のホストコマンドを実装する際は、このメソッドを利用すると良さそうです。
privateなメソッドたち
ここで取り上げるメソッドはライブラリの外からアクセスできないメソッド、つまり、ライブラリ内で使われることを前提としたメソッドです。内部では、こういうメソッドが使われているのだという参考までに取り上げます。
- write/read
- readSpiStatusメソッド
- checkSpiAckメソッド
- 拡張readメソッド
- writeCommandメソッド
これらprivateなメソッドは、PN532.cppのコメントで、低・中・高にレベル分けされています。以下にそれぞれのレベルに属するメソッドと、その処理の概要を説明します。
低レベル
write/readメソッドがこれにあたります。内部的にSPIインタフェースを使ってデバイス(PN532)に直接、データを送ったり、逆に受け取ったりしているようです。
中レベル
readSpiStatusメソッドがこれにあたります。SPIの状態を読み取る処理を実装しています。
高レベル
読み取ったデータを任意バイト数、任意のバッファに格納できるよう拡張されたreadメソッドと、writeCommandメソッドがこれにあたります。拡張readメソッドは低レベルのreadメソッドとメソッド名自体は同じですが、引数が違います。いわゆるオーバーロードというやつで、内部で低レベルのreadメソッドを利用しています。writeCommandメソッドは内部でwriteメソッドを利用して、「お約束」の処理をカプセル化しています。