『Raspberry Piで赤外線リモコンを学習し発信する』で読み取った、赤外線リモコンの信号データを使って、簡単な電子回路で送信実験をしてみる。
Raspberry Piで読み取ったリモコンの信号例
YAMAHAのAVアンプのリモコン信号
begin remote name yamaha_rxs600 flags RAW_CODES|CONST_LENGTH eps 30 aeps 100 gap 107686 begin raw_codes name volup 9062 4378 702 427 676 1594 650 495 649 1556 672 1595 760 1464 672 1599 650 450 700 1569 651 451 699 1547 724 400 723 401 725 398 779 349 700 1570 778 326 673 1574 727 397 726 1524 677 1602 677 424 675 450 700 425 722 1524 724 401 752 1501 775 364 729 372 700 1571 649 1596 679 1569 655 〜 以下略 〜 end raw_codes end remote
赤外線リモコンのエンコード方式は、次のようなものがあり
・NEC方式
・家電協方式
・SONY方式などメーカー独自方式
今回のものはNECフォーマットなので、次のようなデータエンコードになっているはず…
具体的にはt=560マイクロ秒として、
Hi | Lo | |
---|---|---|
リーダ(スタートビット) | 16t = 9000us | 8t = 4500us |
1 | t = 560us | 3t = 1690us |
0 | t = 560us | t = 560us |
トレーラ(ストップビット) | t = 560us |
なお、1フレーム(1つのボタンの送信)は108 ミリ秒の間隔のため、60〜70ミリ秒でデータ送信が終わったとしても、その後に無通信状態を保持しなければならない。
詳しくは、ルネサスの仕様書 『その他の通信機能 : 赤外線リモコンの信号はどうなっているのですか?』に詳しく書かれている。
Raspberry Piでキャプチャされたデータで、赤で着色したところが「リーダ」と「トレーラ」、青と緑の部分が「カスタムコード」と呼ばれる機器で統一された固有ID、オレンジの部分が「データ」、その後ろは「データのXOR」となっている。
カスタムコード H = 0b01011110 (0x5e)
カスタムコード L = 0b10100001 (0xA1)
データ = 0b01011000 (0x58)
というように解読できる。
回路作成
PIC 12F1822 を利用して、次のような回路を作成
当初、単純にRA2ポートを560マイクロ秒Hi、… のようなプログラムを作成したが、受信側の危機が全く反応しない。 『秋月電子 : リモコンフォーマット ※参考資料』や『赤外線リモコンの通信フォーマット』を読んでみると、単純な560マイクロ秒のHi信号ではなく、38kHzのキャリアに乗せて送信を行うらしい…
ひたすら試行錯誤しても、38kHzのキャリア信号に気づかなければ、無駄な苦労だった。
トランジスタ増幅回路周りの抵抗値の計算 ■ コレクタ抵抗(LED側) LED電流値 IF = 70mA (許容最大値 50〜100) LED順方向電圧 VF = 1.5V 駆動電圧 Vcc = 5.0V オームの法則より Rc = (5 - 1.5) / 0.070 = 80 オーム ■ ベース側スイッチング抵抗 ベース電圧 V = 5.0V or 3.3V 半導体損失 0.6V コレクタ電流 Ic = 70mA Hfe = 200 → 安全率3として Hfe = 67 オームの法則より Rb = (5 - 0.6) / (0.070/67 ) = 4211 オーム オームの法則より Rb = (3.3 - 0.6) / (0.070/67 ) = 2584 オーム どちらでも可能なように、2.4kの抵抗を採用する
送信信号をオシロスコープで観察してみる
横軸1メモリが1ミリ秒
0,1,0,1 … のNECフォーマットの信号。0データのHiが642マイクロ秒、Loが470マイクロ秒というYAMAHAの解析値に近いタイミングにカスタマイズしている。
一部分を拡大してみる。横軸が100マイクロ秒
Hiの部分(642マイクロ秒)が、38kHzのキャリア信号で表現されている。今回は、PICで簡単に実現するため、PWMの方形波を用いている。
更に拡大してみる。横軸が5マイクロ秒
1秒/38,000Hz = 26マイクロ秒 という計算にだいたいあっている。少し短いように感じるのは、PWMのPR2値設定の時に、計算上27のものを25に丸めたため。
PICのプログラム ソースコード
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <xc.h> /* PIC Configuration 1 */ __CONFIG(FOSC_INTOSC & // INTOSC oscillator: I/O function on CLKIN pin WDTE_OFF & // WDT(Watchdog Timer) disabled PWRTE_ON & // PWRT(Power-up Timer) disabled MCLRE_OFF & // MCLR pin function is digital input CP_OFF & // Program memory code protection is disabled CPD_OFF & // Data memory code protection is disabled BOREN_OFF & // BOR(Brown-out Reset) disabled CLKOUTEN_OFF & // CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin IESO_OFF & // Internal/External Switchover mode is disabled FCMEN_OFF); // Fail-Safe Clock Monitor is disabled /* PIC Configuration 2 */ __CONFIG(WRT_OFF & // Flash Memory Self-Write Protection : OFF VCAPEN_OFF & // VDDCORE pin functionality is disabled PLLEN_OFF & // 4x PLL disabled STVREN_ON & // Stack Overflow or Underflow will not cause a Reset BORV_HI & // Brown-out Reset Voltage Selection : High Voltage DEBUG_OFF & // In-Circuit Debugger disabled, ICSPCLK and ICSPDAT are general purpose I/O pins LVP_OFF); // Low-voltage programming : disable #ifndef _XTAL_FREQ /* 例:4MHzの場合、4000000 をセットする */ #define _XTAL_FREQ 4000000 #endif unsigned char custom_code_1 = 0b01011110; /* 0x5E */ unsigned char custom_code_2 = 0b10100001; /* 0xA1 */ unsigned char cmd = 0b01011000; /* 0x58 */ //待ち時間が変数設定できるarg_delay_ms()関数を定義 //(__delay_ms()は引数に変数設定が出来ない為) void arg_delay_ms(unsigned int x) { while(x) { __delay_ms(1); x--; } } void arg_delay_us(unsigned int x) { while(x) { __delay_us(1); x--; } } void decode_bit(unsigned char c){ for(int i=0; i<8; i++){ if(c & (0x80 >> i)){ // PORTAbits.RA2 = 1; // ← 単なる矩形波ではダメな例 CCP1CON = 0b00001100; __delay_us(560); // PORTAbits.RA2 = 0; CCP1CON = 0b00000000; __delay_us(1690); } else{ CCP1CON = 0b00001100; __delay_us(560); CCP1CON = 0b00000000; __delay_us(560); } } } int main(int argc, char** argv) { // 基本機能の設定 OSCCON = 0b01101010; // 内部オシレーター 4MHz TRISA = 0b00101011; // IOポートRA0(AN0),RA1(SCL),RA5(RX)を入力モード(RA3は入力専用)、RA2(SDA),RA4(TX)を出力モード APFCONbits.RXDTSEL = 1; // シリアルポート RXをRA5ピンに割付 APFCONbits.TXCKSEL = 1; // シリアルポート TXをRA4ピンに割付 ANSELA = 0b00000000; // A/D変換をAN0,AN1,AN2,AN4を無効 PORTA = 0; CCP1SEL = 0; // RA2をCCP1(PWM)のP1Aで用いる CCP1CON = 0b00000000; // PWM(シングル) T2CON = 0b00000000; // TMR2プリスケーラ値を1倍に設定, Timer2はこの時点ではOFF // PR2 = Fosc / (Fpwm * 4 * Prescale) + 1 = 4000000 / (38000 * 4 * 1) + 1 = 27 PR2 = 0x19; // 27→25 CCPR1L = 0x0c; // PR2=0x19の場合デューティ値は6bitで、デューティ比50%の設定 CCPR1H = 0; TMR2 = 0; // Timer2カウンタを0 // PWM開始 TMR2ON = 1; // 0.5秒待つ __delay_ms(2000); // スタートビット(リーダ) CCP1CON = 0b00001100; __delay_us(9000); CCP1CON = 0b00000000; __delay_us(4500); // 送信データ本体 decode_bit(custom_code_1); decode_bit(custom_code_2); decode_bit(cmd); decode_bit(cmd ^ 0xff); // ストップビット(トレーラ) CCP1CON = 0b00001100; __delay_us(560); CCP1CON = 0b00000000; __delay_us(560); /* 終了 (無限ループでこれ以降何もしない) */ while(1){ __delay_ms(1000); } return (EXIT_SUCCESS); }