赤外線リモコンがどのようにしてデータを送り、受信側はどのようにデータを受け取っているのか、全くもって知らないので1から調べることにしました。
今回を番外編(電飾パート1.5)ということにして、調べた内容などを1つずつ淡々と自分用のメモとしてメモっていきます。
書いていくうちに訳が分からなくなっていきましたが数日かけて修正していきます。
図解等が無い部分も後日追加していく予定です。
最終目的地は
- 電飾コントロール用の赤外線リモコンを自作
- ヘッドライトやウインカーなどの電飾を赤外線リモコンによって別々にコントロールする
※メモの内容は”私が個人的に実現してみたい電飾赤外線コントロール”に必要な内容に絞っている為、「赤外線リモコンは絶対にこういう仕組みで動作している」という意味で解釈されないようにお願いします。
また、電子工作素人なのでメモの内容は間違っている可能性大…というか間違っていると思ってください。
赤外線リモコンのフォーマット
周期について
「1」を送る期間
ここまでのまとめ
プログラムを書く
実際に組み込むPICはPIC16F15325になる予定なので最終的にプログラムの内容も変わってしまいますが、とりあえず練習ってことで。
ちなみに受信に使う赤外線リモコン受信モジュールはSHARP製の”GP1UXC41QS”です。
秋月で1個\50とお手軽です。http://akizukidenshi.com/catalog/g/gI-06487/
ということでまずは送信機側のプログラムを書いていきます。
12F675の設定(重要な部分のみ)
- 内蔵クロック
- 動作周波数4MHz
- GP0~GP2を入力(3つのタクトスイッチに接続)
- GP4は出力(赤外線LEDを制御するトランジスタに接続)
38kHz変調の関数を作る
使用する赤外線リモコン受信モジュールのデータシートによるとハイレベルパルス幅の最小値が600㎲となっていたので余裕をもって780㎲の周期で作ることにしました。(このあたりはよく分かりません)
- void l1() //点灯関数(38kHz変調)
- {
- for(b=0;b<30;b++){ //780us周期、26usを30回
- GP4=1; //点灯
- __delay_us(13);
- GP4=0; //消灯
- __delay_us(13);
- }
- }
- void l0() //消灯関数(780us待つ)
- {
- GP4=0; //消灯
- __delay_us(780); //780usの間
- }
"0"に関しては消灯するだけでいいので780㎲の間消灯しているだけです。
"1"は38kHz(26㎲周期)の点滅を作らなければいけないのでfor文のループを使います。
点灯13㎲+消灯13㎲=26㎲
変数"b=0"に1を足していき"b=30"になると終了するループを作ることで30回の点滅(780㎲)を作る。
26㎲×30ループ=780㎲
ということでこれで38kHz変調の関数が完成かと思いきや、これでは全く上手く送受信できません。
何故かといいますと、マイコンの命令サイクルの時間を考慮していない為です。
1命令にかかる時間は4MHzの場合1㎲
1命令に1㎲ですから例えば1ループに10命令かかっていたとして、それが30回行われればそれだけで300㎲です。
780㎲を作りたいのに結果出来上がったのは1080㎲というとんでもない誤差になります。
アセンブラ言語であれば正確にこの命令サイクルが分かるようですが、C言語で正確な命令サイクルを求めることは難しいため、実際に何パターンも点灯消灯の長さを変えたりして試行錯誤する必要があります。
私が見た作例では「点灯10㎲ 消灯10㎲」や「点灯12㎲ 消灯11㎲」の場合があったり様々でした。
この点灯消灯の時間については終盤に解決します。
2進数を送信するプログラム
関数が出来上がったので次は2進数を送信するプログラムです。まず送りたい2進数を決めます。
- 1001
- 1011
- 0110
- 11001
- 11011
- 10110
- 1100110101
- 1101110101
- 1011010101
プログラムを書きます。
3つのデータを3つのスイッチ(GP0~GP2)に割り振りスイッチが押されると実行されるようになっています。(タクトスイッチはプルアップ接続しています)
- while(1){
- a=1;
- switch(a){
- case 1:
- if(GP0==0){ //GP0のスイッチが0(オン)なら開始
- l1(); //スタートビット
- l1();l0();l0();l1(); //送信する2進数(1001)
- l1();l0();l1();l0();l1(); //ストップビット(10101)
- GP4 = 0;
- __delay_ms(3000); //3秒停止
- a=2;
- break;
- }
- a=2;
- case 2:
- if(GP1==0){ //GP1のスイッチが0(オン)なら開始
- l1(); //スタートビット
- l1();l0();l1();l1(); //送信する2進数(1011)
- l1();l0();l1();l0();l1(); //ストップビット(10101)
- GP4 = 0;
- __delay_ms(3000); //3秒停止
- a=3;
- break;
- }
- a=3;
- case 3:
- if(GP2==0){ //GP2のスイッチが0(オン)なら開始
- l1(); //スタートビット
- l0();l1();l1();l0(); //送信する2進数(0110)
- l1();l0();l1();l0();l1(); //ストップビット(10101)
- GP4 = 0;
- __delay_ms(3000); //3秒停止
- a=1;
- break;
- }
- a=1;
- } //case
- } //while
受信側のプログラムを作る
動作クロックは送信側と同じ4MHzです。
- void main() {
- //設定
- CMCON = 0x07 ; //下位3bitコンパレータオフ
- ANSEL = 0x00 ; //デジタルIOに設定
- TRISIO = 0x04 ; //GP2を入力に設定
- GPIO= 0x00 ; //IOの初期化
- //割り込み設定
- IOC = 0x04; //GP2割り込み検知
- INTE = 1; //GP2割り込み検出の有効化
- INTEDG = 0; // 割込み条件を立下がりエッジにする
- INTF = 0; //GP2外部割込みフラグ、ソフトウェアで解除が必要
- GIE = 1; //全体割り込みを許可
続いて受信処理(割り込み)です。
- void __interrupt () isr (void){
- GIE = 0; //全体割り込み不許可
- if (INTF == 1) { //外部割込みが1の場合実行
- while(1){
- a=0;b=0;c=0;d=0;e=0; //2進数用データ
- /*受信中スタートビットもしくはストップビッドが一致しなかった場合は処理を
- * 中断しリセット*/
- if(GP2==0) //スタートビット検出
- __delay_us(100);
- else break;
- if(GP2==0) //確認
- __delay_us(780);
- else break;
- if(GP2==0) //受信する2進数1ケタ目
- b = 1; //1なら1を代入
- __delay_us(780);
- if(GP2==0) //受信する2進数2ケタ目
- c = 5; //1なら5を代入
- __delay_us(780);
- if(GP2==0) //受信する2進数2ケタ目
- d = 10; //1なら10を代入
- __delay_us(780);
- if(GP2==0) //受信する2進数2ケタ目
- e = 20; //1なら20を代入
- __delay_us(780);
- if(GP2==0) //ストップビット1
- __delay_us(780);
- else break;
- if(GP2==1)//ストップビット0
- __delay_us(780);
- else break;
- if(GP2==0) //ストップビット1
- __delay_us(780);
- else break;
- if(GP2==1)//ストップビット0
- __delay_us(780);
- else break;
- if(GP2==0) //ストップビット1
- a=b+c+d+e; //受信した2進数の代入値を合計
- else break;
- break;
- /*受信処理ここまで*/
- } //end of while
- } //end of if
- __delay_ms(30);
- INTF = 0 ; // 割り込みフラグをクリア
- GIE = 1; //全体割り込みを許可
- } //end of 外部割り込み
スタートビットとストップビットが一致しなければ処理は中断され割り込みは終了されます。
受信するスタートビットとストップビットを除く4bitの2進数については、「0」を受信した場合は何もせず、「1」の場合にのみ対応する桁の変数に「b+1」「c+5」「d+10」「e+20」を実行。
最後に「a=b+c+d+e」を実行することで変数aに合計値を代入。
1001=1+0+0+20="21"=a
1011=1+0+10+20="31"=a
0110=0+5+10+0="15"=a
変数aの数値によって受信した2進数がどれなのかを判別します。
受信した2進数に対応したプログラムを実行
- while(1){
- switch(a){
- case 21: //2進数1001を受信した時に実行
- GP0 = !GP0;
- __delay_ms(30);
- a=0;b=0;c=0;d=0;e=0;
- break;
- case 31: //2進数1011を受信した時に実行
- GP1 = !GP1;
- __delay_ms(30);
- a=0;b=0;c=0;d=0;e=0;
- break;
- case 15: //2進数0110を受信した時に実行
- GP4 = !GP4;
- __delay_ms(30);
- a=0;b=0;c=0;d=0;e=0;
- break;
- default: //代入値が不正の場合何もせずリセット
- break;
- }//end of switch
- }//end of while
実際に動かしてみる
![]() |
左が送信機、右が受信機 |
- void l1() //点灯関数(38kHz変調)
- {
- for(b=0;b<30;b++){ //780us周期、26usを30回
- GP4=1; //点灯
- __delay_us(13);
- GP4=0; //消灯
- __delay_us(13);
- }
- }
この作業、2日かかりました。
結果は…失敗。
というかこのままやっても成功に辿り着くまでに時間がかかりすぎます。
「DSO138」という自分で組み立てて(はんだ付け含む)使えるお手軽なオシロスコープです。
早速まずは38kHz変調を調節してみました。
37.990kHzと限りなく38kHzに近い数値を出すことに成功。
次にオン期間が780㎲になるように調節しますが、これは目で見て合わせました。
![]() |
1101 |
画像ではまだオン期間が長いようです。
このようにオシロスコープを使って調節した結果がこれです。
- void l1() //点灯関数(38kHz変調)
- {
- for(b=0;b<24;b++){ //30回から24回に
- GP4=1; //点灯
- __delay_us(4); //13から4に
- GP4=0; //消灯
- __delay_us(4); //13から4に
- }
- }
オシロスコープ無しでこの数値まで辿り着くのは至難の業だと思います…
オシロスコープ様様です。
上記のようにプログラムを書き換えて実際に動かしてみるとすんなりと動作するもんなんですよね~すごい。
動画です⇩
ということで以上です。
0 件のコメント :
コメントを投稿 (クリックでコメント入力欄を表示)