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)」みたいな優先順位の間違いはしなくなったのにねぇ。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。