赤外線リモコン受信器
テレビ用の赤外線リモコンを受信する実験をしました。

以前にPICでWebの制作例どおりに作りhexファイルをそのまま書き込んで作ったことがあります。 それは学習型の立派なものでした。もちろん何もわからずにトレースしただけです。次に なる研さんのページに90S2313を使ったソニーリモコンを受信する 制作例がありましたのでトレースしてみましたところうまく動きました。

受信のための解説がされていましたので、少し工夫してそれを元に前回に用意したmega48 にWINAVRで書いてみようと考えたものです。赤外線の検出器は秋月で購入した「赤外線リモコン受信モジュールTSOP1738」を使うことにします。これは受光部だけ のように見えますが38kHzの搬送波を検出して、データをTTLレベルで出力してくれる優れものです。

なる研さんのページにある解説を自分なりにまとめてWINAVRでコーディングしましたがなかなか思うようには動いてくれませんでした。私の読み違いや、誤解が あったのでしょう。解説はそのページを見てください。詳しい図入りで説明されています。
次に私の試行錯誤の結果をまとめてみます。
 @ 信号の立ち下がりで割込を発生させる。このために検出器の入力は INT1 を使います。 INT0 は PD2として使います。
 A 300μs(マイクロ秒)待ってデータを読む。この時間を待つのはデータの中間付近を取り出すためです。
 B 続けて600μsごとに合計6回読み込む。
 C 6回分が「LLLLHL」なら正しいスタート状態だから、次へ進む。そうでないときは割込を終了して@に戻る。
 D 600μs待って次の7ビットデータの0bit目を読む。
 E Hのときは1200μs待って次を読む。Lのときは1800μs待って次を読む。
 F これで得た7ビットデータが各ビットが反転したメーカーコード(?)を表す。(と思います)
 G 同様に次の8ビットデータを読む。リモコンのボタンのコード(ビット反転している)を示します。
 H 続けて次の4ビットデータを取り出す。リモコン個体のコードのようです。反転しています。

わからないことが多いまま、プログラムを書くことになるわけですが、特に今回はC言語との悪戦苦闘になりました。良く知っている人から見れば おかしいかもしれませんがわからない私には無我夢中です。何度か挫折しそうになりながら頑張りました。

まず割込関係です。例によってデータシートとメモ用紙を用意して、
入力は INT0 か INT1 を使うことになりますが、INT0 の端子はモニタ用として使うことにしていますから、INT1 を使うことになります。
関係レジスタは、
 EICRA (External Interrupt Register A)bit3,2を10とするとINT1の立ち下がりで割込発生になります。0x08を書き込みます。
 EIMSK (External Interrupt Mask Register) bit1が外部割込1許可となります。 0x02を書き込みます。
 sei() 総割込元の許可を書きます。

次に、600μs、300μsのwait時間です。コンパイルされた.lstファイルから時間を計算したのですがどうにもうまくいきません。そこでforループを 作って実測することにしました。モニタ用のPORTD2にwait時間ごとにONとOFFを繰り返すプログラムを書きました。  
 PORTD=0x04;
 wait600( );
 PORTD=0x00;
 wait600( );

ここに周波数カウンタをつなぎます。833Hzになりましたら600μsということになります。wait関数の中のループ変数は詳しく調べていませんが 8ビット整数を使っても、16ビット整数を使ってもうまく調整できません。肝心なところで不連続に変わります。演算レジスタの都合でコンパイルの結果が 変わるのでしょう。また、どうしてもうまく動かなかったので、クロックも1MHz内蔵RC発振を断念して、12MHz水晶の8分周で動かすことになりました。12MHz は手持ちの関係です。600μsのためにはクロックが遅いほうが良いので内部で8分周のヒューズビットを選びましたからクロックは1.5MHzになります。 waitルーチンは何度も試行錯誤の上プログラムの数値に落ち着きました。300μsの方は累積誤差がでませんのでおよそ半分にしています。

割込の関数名はコンパイラで決まっています。割込中に割込が入っては困りますから SIGNAL() を使いますが、引数の名は調べねばなりません。 mega48の場合はwinavr\avr\include\avr\iomx8.hにある SIG_INTERRUPT1 を使います。

次に、読み込みのプログラムです。長くなっても単純な方が間違いが少ない、動いたら短くする。というのが私の持論で動くまでは こんな形で書いています。不細工ですが自分では見やすいと思っています。
動くことが確認できてから少しカッコを付けて整理しました。shiftを使えば良いのでしょうが不勉強で使っていません。液晶表示のために itoa()関数を使います。intは16ビット、uint8_tは8ビットですがそのまま代入しても不都合はなかったのでそのままにしてあります。ただ、 すべてのコードがビット反転しています。本来の形で表示しようとしてチルダを付けると8ビット、16ビットの不都合が出ますので反転したままにしています。 今後、on、offに使うとしても「チャネル1ボタンを押すと7fになる」と考えればよいと思います。

このプログラムで表示すると2行目に、「 2 1f 7f f 」と出ます。 2はリーダー部が正常であることを表し、次の 1fはSONYのコード(?)、 7fはチャネル1のデータ、最後のfはリモコンのコードのようです。もう1つのリモコンでは最後が cになりました。

ボタンのデータの一部は次のとおりです。くどいようですがビット反転しています。
電源 CH1 CH2 CH3 CH4 CH5 CH6  CH7 CH8 CH9 CH10 CH11 CH12 
6a7f7e7d7c7b7a 797877767574

リモコンは本来、スイッチの切り替えですから、ポートのon,offを追加しました。実際のスイッチ回路の替わりにLEDの点滅でスイッチ状態を確認しています。
PORTBの1,2,3にLEDを接続して、Hで点灯しています。とりあえずチャンネル1,2,3ボタンで、3回路をトグルでonn,offとしました。ボタンが引き続き押されているのか、 一度離されて再び押されてものかを判断するためにずいぶん時間を要しました(不勉強の証)。プログラムでは v[ ] と w[ ] の変数を使って判断しています。もっと スマートな方法があるのかもしれませんが、ここへたどりつくのが精一杯でした。動作上は満足のいく結果となりました。1のボタンでon、2のボタンでoffというのが 最も簡単です。

トグル動作のために考えたこと: リモコンボタンを押し続けると、1秒間に数回の割込が入るようです。押されたボタンを検出す るとLEDが反転するようにプログラムすると、押し続けによってLEDが点滅します。タイミング良くボタンを短く押すとon,offのトグル動作となりますが、押す時間が長い と再び反転してしまします。
そこで、ボタンが押された過去の状態を保存して、現在の状態と比べることにしました。これが v[ ] と w[ ] の変数です。1つのボタンに v と w の2つの グローバル変数を用意しています。w が現在の状態、v が前回の状態です。
@ プログラムがスタートするとメインルーチンですべてを0に初期化しています。
A 割込が入ると割込ルーチンの中で、まず全wを全vに代入して前回の割込の状態とします。
B 押されたボタン(例えばCH1)を検出するとそのボタンのwを1にします。
C 続いて、CH1の v と w の値を調べます。前回が0で今回が1だとボタンが押されたとしてLEDを反転させます。1と1なら前回から押し続けていますので反転しません。
D この状態で、押し続けても点滅はしないのですが、一度ボタンをoffにしても前回の1がwに残ったままになりますから応答してくれません。ボタンが離されたことを 調べるために、メインルーチンの中にウエイトを作り、割込から戻ると時間を計らせます。60mSの間に割込がないとリモコンボタンが離されたと判断して全wを0としています。 whileループを使って、割込が入るたびにカウンタを0にしています。
これで思い通りの動作となりました。ここにくるまで、寝床のなかの思案も含めるとかなりの時間になりました。チップの書き換えも今までの最高でした。


私の身の回りにはリモコンを必要とする機器がありませんので実用に組み込む予定ははありません。
ハードウエアでは回路を変更しやすいように赤外線受光ICとデカップリングのRとCを小さな基板に半田づけしてそれを両面テープで貼り付けています。同様に LED3個も電流制限抵抗とともに別の基板に取り付けてあります。

ソフトでは、外部割込とswitch文が初めての使用です。今回は別項のLCD表示回路を含めて、極めて多いROMの書き換えをしています。書換保証が90Sシリーズのように 千回だったら気にしていたかもしれません。1万回ですから気楽に実験ができました。

プログラム(写真撮影の後に1行目の表示を変えました。)下記の最終のプログラムをWinAVR20050214対応に修正しましたので、このプログラムは 修正していません。使うなら、includeファイルの記述と、sbi()、cbi()マクロを修正する必要があります。
回路図

追記
一応の成功(?)を見てから、再び内部RC発振による動作に挑戦しました。600μsの設定には成功したのですが、やはり受信はできませんでした。得られるデータが とても不安定でかつ、間違ったデータを検出します。さらに、セラミックレゾネータ(12MHzの手持ちがありましたのでそれを使いました)でもRCと同様な結果に終わ りました。セラミックならいけると思ったのですが残念でした。冒頭に書きましたPICのものも、なる研さんのページのものもともにセラミックで正常に作動しているので 解せないのですが、実際には動きませんでした。理由は現在わかりません。ご存じの方がおられたらお教え願いたいと思います。

デジットで「光リモコン用受光基板ユニット」が出ていたので購入しました。(写真) 上で使ったセンサICより嵩が高いので使いにくいと思いましたが100円だから よしとしました。持ち帰って値札をめくると下から1,700円の文字。昔はこんなに高かったのかなぁ。上記のセンサICをこれに代えてみました。何の問題もなく同様に 反応してくれました。組み込む場所があればこれでもokです。いつもあるものではないのですが。78L05は取り外したので別に使います。

後になってインラインアセンブラでwaitルーチンを書くと最適化によって左右されないルーチンとなることを  まろりさんのページ で知りました。説明を読んで おぼろげにわかる程度ですが定数の値を変えて、実測で300μsのwaitルーチンとしました。関数呼び出しの引数が1で300μs、2で600μs、4で1200μsとなります。数値 を代えて実験しましたがこのwaitルーチンの誤差が蓄積されていくのでかなり厳密に設定しないといけないようです。
インラインアセンブラを使った最終のプログラムです。lcd.h と lcd.c をここに置きます。 lcd.hファイルはサーバーの都合で lcd.txtに拡張子を変更してあります。使うときはlcd.hに戻してください。


はじめのページへもどる
inserted by FC2 system