ページ

2014/07/24

[nrf51]今のFeliCa Plugの動き

現在実装できているところまでのFeliCa Plugがどう動くか、簡単に説明しておこう。
ソースファイルなどは、こちら。
https://github.com/hirokuma/nrf51822_felicaplug/tree/e43719514679f0515670656c582036a15e37e2af

ドキュメントフォルダの、FeliCa Plugの説明をがんばってるつもり。
Excelファイルは、これから作ろうとしているものについてなので、astaファイルを見るのがよいだろう。
だいたい、どういうことをしないといけないかとか、どこでどのくらい待つとかがわかるんじゃなかろうか。

これを実装して、FeliCa Plugを接続し、R/Wで読み込みを行うと、こういうデータになる。

image

注意としては、IDmだろうか。
私はスイッチサイエンスさんから購入したFeliCa Plugを使っているので、FeliCa Plugへの初期パラメータはこうしている。

static const uint8_t k_InitVal[] = {
    /* 各種設定パラメータ */
    0x03,                    /* データ転送設定 : FT転送設定        */
                            /* システムコード : 0x12FC            */
    /* 最大応答時間パラメータ */
    0xff, 0xff,                /* Read w/o Enctyption  : 255        */
                            /* Write w/o Enctyption : 255        */
    /* データフォーマットコード */
    0x00, 0x1c,                /* Switch Science社から購入の場合    */
    /* ユーザー設定パラメータ */
    'k', 'u', 'm', 'a'
};

データフォーマットコードが、スイッチサイエンスさんの場合は上記の値になっている。
そして、ユーザー設定パラメータは、とりあえず「kuma」にしてみた。

だから、IDmが

  • 03 FE : FeliCa Plugの値
  • 00 1C : データフォーマットコード
  • 6B 75 6D 61 : kuma

になっている。
まあ、03 FEで始まるのはFeliCa Plugだけということになってるから、気になる人はユーザー設定パラメータはランダム値にしておいてもいいかも。

Read w/o Encryptionで返す値は、無条件で0x00~0x0Fまでになっている。
これはfelica_plug.cのrecv_rwe()を見てもらえばわかるだろう。
Write w/o Encryptionしても、かならずステータスフラグは正常を返すようにしている。同じくrecv_wwe()。

ここまで動けば、あとはBLEを使うだけだ。
さてさて、どう料理しましょうかね。

2014/07/23

[nrf51]バグにも、よいバグと悪いバグがあってな・・・

FeliCa PlugからのSPI受信ができずに悩み続けた私。
原因を突き止めるべく、ロジアナの購入にまで踏み切った。
しかし、購入までには時間があるため、もちろんデバッグを続けていた。

のろのろ考えていると、隅にいた老人が声をかけてきた。
彼は紐を持った震える指で複雑で美しい結び目を作りながら、静かに話すのだった。

「バグには・・・バグにも、といった方がよいかな。
 バグにも、よいバグと悪いバグがあってな。」

私は彼が何を言い出すのかさっぱりわからなかったが、この薄気味悪い老人と話すのがよいか逃げるのがよいか決めかねていた。
というのも、薄気味悪い見かけとは違い、目はとても知的で澄んでいたためだ。
それがわかっただけに、さらに私は彼を薄気味悪く感じた。

そんな様子に気付いたかどうかわからなかったが、彼は続けた。
「よいバグなんてない、そう思ってるだろう?
 バグはすべて悪だ、と。
 わしも、若い頃にはそう思ったもんじゃ。
 君と違ってわしは血気盛んな方でな。
 まあ、今のこの姿からは想像もつかんと思うが」
そういうと、予想した以上に不気味な笑い声を静かに上げた。
立ち去ろうと思えばその瞬間だったのだろうが、私は彼が何を言い出すのか気になり、足を止めてしまった。

「でも、な。
 『これは悪いバグだ!』というのはあるじゃろう?
 見た瞬間に身の毛が総立つような、腐臭が漂ってくるような、そんなバグじゃ。

 ということは、じゃ。
 相対的に、よいバグというものもある、という証明にならんじゃろうか?」
なんとなく彼の言い分がわかったので、思わず聞いてしまった。
「爺さんが見たことある悪いバグって、どんなのだい?」

その声を聞いた瞬間だった。
老人から漂っていた不気味な雰囲気は消え去り、代わりに別の雰囲気をまとっていた。
こういえばよかろうか、悪魔的な、と。
予想できる以上に悪魔的な笑い声を続けた後、聞こえるか聞こえないかの声で囁いた。
「これじゃよ」

それは、私が見ていたディスプレイを指していた。
そしてその目線は、GPIOの割り込みハンドラ処理に注がれていた。
気になってそこを見ると・・・。

「あああああああああああ!!!!」

私は叫び声かうめき声かわからない音を放ちながら崩れ落ち、血の涙を流し始めた。
画面には、GPIOからの割込が立ったビットを受ける変数をビットマスク値で&&している様子が浮かび上がっていた。
それも、全部の条件式に。

血の涙を流し終わったときには、隅の老人は消えていた。悪魔的な雰囲気だけを残して。


えーっと、『隅の老人』はこんなお話じゃないのでご安心を。
では、バグの説明に入ります。

まずバグのコードを。

static void gpiote_event_handler(uint32_t event_pins_low_to_high, uint32_t event_pins_high_to_low)
{
    //立ち上がり
    if (event_pins_low_to_high && GPIOTE_MASK_IRQ) {
        fp_irq_assert();
    }
    if (event_pins_low_to_high && GPIOTE_MASK_RFDET) {
        LED_OFF(PIN_LED);
        fp_stop();
    }
    //立ち下がり
    if (event_pins_high_to_low && GPIOTE_MASK_RFDET) {
        LED_ON(PIN_LED);
        fp_rfdet_assert();
    }
}

これはGPIOTEの割り込みハンドラ。
GPIO Task Eventという名前なのだが、登録したGPIOピンに変化があったとき、event_pins_low_to_highには立ち上がりのピンが、event_pins_high_to_lowには立ち下がりのピンが、それぞれビットで入ってくる。
0x00000014だったら、bit2とbit4に1が立っているので、GPIO2とGPIO4に変化があった、という見方だ。

FeliCa PlugからのGPIO制御で割込監視するものとしては、/RFDETとIRQがある。
/RFDETはアクティブLOWで、搬送波検知を示す。
IRQはアクティブHIGHで、受信データ要求を示す。

おおよその使い方としては、こうなる。

  1. 搬送波が来るまで、省電力で待つ
  2. nRFDETがアサートしたら、FeliCa Plugの初期設定をして、コマンド待ち受け状態にする
  3. IRQがアサートしたら、FeliCa Plugからコマンド受信する(コマンド受信中にIRQはネゲートする)
  4. コマンドがRead w/o EncryptionかWrite w/o Encryptionか判断し、戻り値を返す
  5. 搬送波が無くなるまで、3~5を繰り返す

そして、上記の実装で動いていたのは、3のアサートしたら、までだった。
というのも、IRQが立ち上がってevent_pins_low_to_highに値が入ると、まず最初のif文に入り、fp_irq_assert()が実行される。
これは(if文の書き方はさておき)期待通りの処理だ。
しかし次のif文、ここにも入ってしまい、fp_stop()が呼ばれ、すべてのFeliCa Plugへの処理を取りやめてしまっていた。
入る理由は簡単で、&&でつなげていて、右も左も値が入っているからだ。
あーー、恥ずかしい・・・。

正解は、こう。

static void gpiote_event_handler(uint32_t event_pins_low_to_high, uint32_t event_pins_high_to_low)
{
    //立ち上がり
    if (event_pins_low_to_high & GPIOTE_MASK_IRQ) {
        fp_irq_assert();
    }
    if (event_pins_low_to_high & GPIOTE_MASK_RFDET) {
        LED_OFF(PIN_LED);
        fp_stop();
    }
    //立ち下がり
    if (event_pins_high_to_low & GPIOTE_MASK_RFDET) {
        LED_ON(PIN_LED);
        fp_rfdet_assert();
    }
}

「||」と「|」は間違えないけど、「&&」と「&」は間違えやすい気がする。
なんでだろう?
「if (abc & MASK == 0)」みたいな優先順位の間違いはしなくなったのにねぇ。

2014/07/22

[nrf51]初期パラメータ送信まではいけるが、受信ができない

FeliCa PlugのIRQは、有線データができたときにもHになるけど、SWをLにしたときもハイインピーダンスになるためかHに見えてしまう。
だから、IRQの立ち上がりだけで処理をするようにしていると、期待していない動作になってしまう。

というメモを見つけたので実装し直したら、搬送波検知をし直すたびにFeliCaランチャーが動作してくれるようになった。
ここまではいいのだが、その後がまだ動かない。

この後は、IRQ割込によってFeliCa Plugが持っているR/Wからの無線コマンドを取得するのだが、IRQ割込は来てるけれども、SPIの受信完了が上がってこない。

うーむ。。。
FeliCa Plugって、SPIっぽい通信ではあるのだけど、独自性が強いのよね。。。
データが送信と受信で同じポートになってるのだ。
クロックマスターはホスト側だから、FeliCa Plugの制御がちゃんとできていようといまいと、好きなだけクロックを出してポートからがんがんデータを吸えば終わりだと思うのだけど、終わった通知が来ない。


ということは、何かわからないけど、nRF51 SDKが期待している「終わり」になっていないということだ。
文章にしてみてようやく整理できたので、明日からはそこを見直そう。

2014/07/21

[nrf51]スケジューラを使ってGPIOハンドラからSPI転送するとHardFaultになった

nRF51822で、ボタンが押されたら、SPI転送をするような処理を書こうとしていた。
が、SPI転送の開始APIを呼ぶと、その中でHardFaultが発生していた。

発生しているのは、SPI転送spi_master.cの仲で割込を禁止しようとしているところのようだったが、原因がわからない。
つらつらとnRF51 SDKのHTMLドキュメントを見ていると「GPIOTEのハンドラは、スケジューラを使っていると直接GPIOTEの割り込みハンドラから呼ばれますよ」と書いてある。

割込禁止にしているところは、SVCALL()を呼び出しているところだった。
つまり、割込コンテキストからさらに割り込みを発生させようとしていたのでHardFaultになったということか?

調べてないけど、ありえそうな気がしてきたので、SPI転送をハンドラからメインループに移動させると動くようになった。
すっきりはしてないが、検索する語彙がわからないので、もういいや。


さて、そうしてようやくFeliCa Plugとお話しする環境ができてきたのだが、うまく動いてくれない。
初期パラメータの転送がうまくいってないのか、どこか待つべきタイミングを待ててないのか・・・。
というのも、デバッガで止めたりしているとFeliCaランチャーが反応することがあるからだ。
反応しないと言うことは、初期パラメータがうまく送信できてないのか・・・。
でも、nRF51822に電源を入れた直後だけは成功しているような気がするので、搬送波がOFFになったときの処理を間違っているだけかもしれない。

悩んでいてもしょうが無いからロジアナで波形を取ろうとしたのだが・・・使っていたカメレオンUSBのロジアナに入っているCypressのチップが最近では対応していなさそうで、ドライバが認識してくれなかった。
いろいろと動かせるようにしている人もいたのだが、すまん、私にはその根性が無かった・・・。
ロジアナの買い換えも検討していたところではあったので、これを機に買うことにした。
http://akizukidenshi.com/catalog/g/gM-04426/

来るまでの間に動いてしまうと残念かもしれないが、まあやれるだけはやってみよう。

[nrf51]待たせる

ちょっと待ちたい、ということがあると思う。
デバイスが安定するまで、とかなので、それほど正確じゃ無くてもよいけど、だいたいの精度を持ってくれてればよい、というくらいの待ち方。
タイマーをわざわざ使うまでも無いけど、じゃあ自分でループを作るかってのも面倒だし・・・。

FeliCa Plugの場合、搬送波検知した後にポートの設定をするのだが、そこから50usec待つことになっている。
その時間をどうやって待たせようか悩んでいた。

nrf_delay.h
static void __INLINE nrf_delay_us(uint32_t volatile number_of_us)

作る前でよかった。