2014年8月29日金曜日

リモコン受信 AVRマイコン編 その1

以前RaspberryPiでリモコン受信するやり方を書きましたが、今回はHA2module上のAVRマイコンでリモコン受信してコード化する方法について書きます。色々とあって長いので3回に分けて書きます。

赤外線リモコンのフォーマットは色々あって全てに対応するのは難しいですが、国内の一般的な赤外線リモコンはサブキャリアが38kHz前後で、AV機器は数十bit程度の長さが多いようです。エアコンの中にはかなり長く300bitを超えるものも存在しています。
赤外線受信モジュールはサブキャリアをフィルターしてパルスデータだけを出力してくれるので、GPIOで受けてパルスの変化のタイミングの間隔を見ていけばデコードできます。今回はAVRのPinChangeInterruptでTimerのCounter値を読んで前回の変化からの時間を記録するようにしました。
最初は自宅にある機器をメインに考えていたためAEHA,NEC,SONYのリモコンだけ対応すればよかったので、この割込みの処理の中でフォーマットの解釈もする実装にしていました。
ところが色々なリモコンを検討し始めたところ、このやり方ではダイキンやパナソニックのエアコンコードなどが対応出来ないため再度実装しなおしました。
新しい実装では、まずはじめにPinChange割込み処理中にHigh区間幅、Low区間幅のペアの頻度統計を取りながらコード化して記録します。
全体を記録し終えたところで押し続けることによる繰り返しパターンの判別をし、AEHA,NEC,SONYのフォーマットを解釈していずれかであればそのフォーマット、何れでもなければ記録したフォーマットをXBee経由で送るようにしています。
以下、実際の処理の詳細です。

□PinChange割込み中の処理
パルスをHigh区間、Low区間(実際の受光モジュールの出力は逆ですが、わかりやすいように無信号状態をLowとしています)をそれぞれ76kHzのカウント値で数えペアの値とします。このペアの値をPatternTableを検索し近い値があればそのPatternとして、なければ新規に登録して、そのPattern番号を求めます。
その番号を記録バッファに記録していくのですがそのまま番号を記録していくと4bit単位で記録していったとしても300bit越えのエアコンフォーマットだと余裕をみて196byte位必要になってしまいます。
普通のリモコンコードはデータを表すパターン2種類が0と1を表していて頻出するはずなのでこの2つを1bitの値として扱い、それ以外のパターンを特殊扱いすれば全体として短くすることが出来るはずです。ところがリモコンを受信し始めたタイミングではどのパターンがデータを表す2種類のパターンなのかは判りません。
そこで出現頻度を判断できるように先頭の64パルスを圧縮処理を遅らせるためにFIFOに登録しながら統計をとり、64パルス揃ったところで出現率の多いPatternを2つ選び、2つのうちHigh+Lowが小さい、等しい場合はHighが小さい方をパターン番号0、大きい方をパターン番号1とします。それ以外のパターンは出現順にパターン番号2から順番に登録します。普通のリモコンコードの場合、大体5〜6種類程度に収まります。
64パルス目以降はFIFOにパルス幅を登録しながらFIFOの先頭から順番に値を取り出します。
この時64パルス目のタイミングでそれまでのデータをまとめて処理してしまうと割込み中の処理が重すぎて取りこぼしが発生するので、FIFOでずらしたその割込みの64パルス前のパルスの処理だけを行うようにしています。
この説明だけでは判りにくいかと思いますが、あとで実際のエアコンのフォーマットを使って説明します。

□Timeout割込み
PinChange割込みのたびにTimerに100msのTimeoutを登録、更新します。
最後のPinChange割込みから100ms経つとTimeout割込みが入り、最終ビットパターンの処理を行い、通常Threadに処理を渡します。

長くなってきたので、一旦ここで切ります。
次回は通常Threadで後処理をするところからです。

2014年8月21日木曜日

子供用のスイッチボックス

1歳の息子が壁の電気のスイッチに興味をもって、頻繁にスイッチを押すために抱っこをせがむので子供用にスイッチボックスを作りました。 



よく、自治体の子育て施設とかに行くと壁にスイッチを沢山つけたものとか設置してあるので、子供は興味を持つものなのですね。
押してもなにも反応しないとすぐ飽きられてしまうので、中にモジュールをいれて単1電池で電源を取るようにしました。
これをコントロールサーバーとXBee経由で接続しLEDテープをランダムな色で光らせるようにしました。
中身はこんな感じです。


XBeeを間欠モードで動かすことで消費電力を抑えているので多分半年ほどは電池交換なしで行けるかと思います。
投げられても電池が外れないように固定して、新聞紙で間を埋めてガムテープで箱を閉じてます。
一見、ただ段ボール箱にスイッチを付けただけでどこにも繋がってないように見えるのに実際にスイッチとして動くのが面白いかと思ったのですが1歳の子供には理解できないですね。


2014年8月17日日曜日

LEDTape

PHILIPSのHueのライトリボンをみて作ってみようと思い部品を探してみました。
スイッチサイエンスにフルカラーシリアルLEDがあったので試しに購入。簡単に点くかと思ったら意外とタイミングが厳しく、8MHz動作のAVRの場合割り込み禁止してアセンブラでクロック数を数えながら無駄なく書かないとうまく点きませんでした。
写真は明るいキッチンカウンターの上でとっていますが、LEDが強すぎてLED以外の部分が真っ暗になってしまってうまく撮れなかったので見た目に合わせて色補正かけています。実際に見るとかなり眩しいです。



このLEDは1m60個のフルカラーLEDで、今回は半分に切って50cm30個で使っています。
本当は1灯ずつフルカラーで色を指定できるのですが、実際に使うとなると全体を同じ色で光らせれば十分なのでLEDの個数とR,G,Bを指定してPORTC4につないだシリアルLED を光らせる関数を作りました。
この関数を呼ぶと30us x LEDの個数分割り込み禁止になります。
1m品の場合60個なので1.8msの間blockされます。UARTでフロー制御していない場合、AVRはFIFOが1段しかないので4800bps以下に設定しないと通信を取りこぼします。
HA2moduleで接続しているので、XBeeを4800bpsで接続してコントロールサーバー経由で制御するようにしています。

void SetLED(unsigned char count, unsigned char r, unsigned char g, unsigned char b) {

  asm volatile(
               "  in __tmp_reg__,__SREG__ \n" // interrupt block 30us x count
               "  cli                     \n"
               "  in r6, %[port]          \n"
               "  or r6, %[tmp]           \n"
               "  mov r5, r6              \n"
               "  eor r5, %[tmp]          \n"
               "  mov r2, %[byteg]        \n"
               "loop:                     \n"
               "  ldi %[tmp], 8           \n" // 1
               "loopg:                    \n"
               "  out %[port], r5         \n" // 1
               "  nop                     \n" // 1
               "  sbrs r2, 7              \n" // 1 - 2
               "  out %[port], r6         \n" // 1
               "  lsl r2                  \n" // 1
               "  mov r3, %[byter]        \n" // 1
               "  dec %[tmp]              \n" // 1
               "  out %[port], r6         \n" // 1
               "  brne loopg              \n" // 2
               "                          \n"
               "  ldi %[tmp], 8           \n" // 1
               "loopr:                    \n"
               "  out %[port], r5         \n" // 1
               "  nop                     \n" // 1
               "  sbrs r3, 7              \n" // 1 - 2
               "  out %[port], r6         \n" // 1
               "  lsl r3                  \n" // 1
               "  mov r4 , %[byteb]       \n" // 1
               "  dec %[tmp]              \n" // 1
               "  out %[port], r6         \n" // 1
               "  brne loopr              \n" // 2
               "                          \n"
               "  ldi %[tmp], 8           \n" // 1
               "loopb:                    \n"
               "  out %[port], r5         \n" // 1
               "  nop                     \n" // 1
               "  sbrs r4, 7              \n" // 1 - 2
               "  out %[port], r6         \n" // 1
               "  lsl r4                  \n" // 1
               "  mov r2, %[byteg]        \n" // 1
               "  dec %[tmp]              \n" // 1
               "  out %[port], r6         \n" // 1
               "  brne loopb              \n" // 2
               "  dec %[count]            \n" // 1
               "  brne loop               \n" // 2
               "  out __SREG__,__tmp_reg__\n"
               : 
               : [count] "r" (count),
                 [byter]  "r" (r),
                 [byteg]  "r" (g),
                 [byteb]  "r" (b),
                 [tmp]    "r" ((1 << PORTC4)),
                 [port]   "I" (_SFR_IO_ADDR(PORTC))
               : "r0", "r2", "r3", "r4", "r5", "r6");
}

2014年8月9日土曜日

bootの書き込みツール(AVR USB-ISP)



HA2モジュールは大きさの関係で特殊なコネクタを使用していますが、コネクタを変換すれば3.3V対応のAVR用のwriterを使用できます。
AtmelのAVR ISP mkIIが一番無難ですが、嵩張るのでaitendo AVR-USBasp-Bを改造して(5V->3.3V)使用していました。最近もう1つ必要で購入しようとしたら在庫切れで買えなくなっていたので、同じくaitendoのUSB-ASPの方を買って改造しました。




このUSB-ASPはFWが公開されておらずそのままではMacで使えないので、こちらのページ

を参考にFWを書き換え、B面に3端子レギュレータを載せて5VとVCCをつないでいるパターンをカットして3.3Vに変更しています。小型でなかなか便利なので最近の愛用品です。