【遊戯王真DM】チートリモコンを作ってみた~ソフトウェア編④:NECフォーマット~【封印されし記憶】

遊戯王真デュエルモンスターズ~封印されし記憶~のポケットステーションで強力なカードを入手するためのツール:チートリモコンを製作した。
今回はチートリモコンのソフトウェア設計④:NECフォーマットの作成を行う。



<チートリモコン作ってみたシリーズ>
封印されし記憶を語る
概要編
ハードウェア編
ソフトウェア編①:動作確認
ソフトウェア編②:キー操作
ソフトウェア編③:チャタリング対策
ソフトウェア編④:NECフォーマット ←いまここ
ソースコード一覧
入手カード一覧




以下の内容は、ある程度PICやC言語を知っている前提となります。
何をしているのか分からない部分があったら、参考書やwebのPIC入門などを参照してください。




今回がチートリモコン製作のラストで、赤外線LEDの設計を行う。
今回設計するのはNECフォーマットで、以下のページを参考にした。
elm-chan.org




IrREMlib.cというソースファイルを作り、最初に以下のincludeやdefineを行う。

#include "xc.h"

#define _XTAL_FREQ 40000000

#define LED_ON  LATB1 = 1
#define LED_OFF LATB1 = 0

上記はmain.cにも同じのを書いているけど、別ファイルなので再定義が必要。




まずは、LED ONの38kHzのサブキャリアと変調単位T(562us)の設計を行う。
理想はPWMを使って38kHzのサブキャリアを作り、タイマー割り込みで変調単位Tを作るのだが、色々と設計が面倒だし、PWMで38kHzを正確に作るのは難しいので、単純なON/OFFとディレイだけで作ることにした。
38kHzの周期は約26.3usであり、チートリモコンは0.1us単位で動作するので、±0.1us以内にしたいところ。
デューティー比1/3はあくまで目安で、ON期間が短い方が消費電力をちょっと抑えられる。
また、変調単位Tは562usであり、26.3usを21回と9.7usのディレイで丁度よくなる。
という訳で、とりあえず以下のように作ってみた。

void NecGenLedTon(void){
  for(int i=0; i<21; i++){
    LED_ON;
    __delay_us(9);
    LED_OFF;
    __delay_us(17);
  }
  __delay_us(10);
}  

上記のプログラムでは、LED_ONとLED_OFF、for文のループ判定がそれぞれ1サイクル(0.1us)で動作すると仮定している。
そのため、理想は1周期26.3usで、最後のディレイ時間を10usとした。




理想通りに動作しているか確認してみた。
これが実験の様子。

まずは38kHzサブキャリア。

次は562us変調単位T。

どちらも理想より長くなっている。
理由は明白で、LED_ONとLED_OFFの処理やfor文のループに数サイクル要しており、その分を考慮していなかったせい。
上で書いた通り、チートリモコンは0.1us単位で動作するので、ディレイ時間を調整してみた。
これが調整後のプログラム。

void NecGenLedTon(void){
  for(int i=0; i<21; i++){
    LED_ON;
    __delay_us(8);
    _delay(7);
    LED_OFF;
    __delay_us(16);
  }
  __delay_us(8);
}


今度こそ理想通りに動作しているか確認してみた。
まずは38kHzサブキャリア。

次は562us変調単位T。

これだけの精度があれば十分だろう。
ちなみに、未調整でも受信できたので、ある程度の誤差は許容できるっぽい。
今回は、PICマイコンを40MHzで動作させているため未調整でも大丈夫だったが、これが10MHz以下だったら受信できるか危うかったと思われる。




変調単位TのLED OFFはこんなもんで大丈夫。

void NecGenLedToff(void){
  LED_OFF;
  __delay_us(562);
}

更に言うと、ON関数はLED_OFFで終わるため、ディレイ関数だけでも大丈夫。




次はデータビット'1'と'0'の送信関数の設計だけど、上で変調単位TのONとOFFを作ったので、以下のようにすればいい。
データビット'1'

void NecGenBitHigh(void){
  NecGenLedTon();
  NecGenLedToff();
  NecGenLedToff();
  NecGenLedToff();
}

データビット'0'

void NecGenBitLow(void){
  NecGenLedTon();
  NecGenLedToff();
}




これをベースに、本命であるNECフォーマットのフレームを設計してみた。

void NecSend(unsigned char custom, unsigned char data){
  int i;
  // Leader Code
  for(i=0; i<16; i++){
    NecGenLedTon();
  }
  for(i=0; i<8; i++){
    NecGenLedToff();
  }
  // Custom Code
  for(i=0; i<8; i++){
    if(custom>>i & 1){
      NecGenBitHigh();
    }
    else{
      NecGenBitLow();
    }
  }
  // Custom Code(invert)
  for(i=0; i<8; i++){
    if(custom>>i & 1){
      NecGenBitLow();
    }
    else{
      NecGenBitHigh();
    }
  }
  // Data Code
  for(i=0; i<8; i++){
    if(data>>i & 1){
      NecGenBitHigh();
    }
    else{
      NecGenBitLow();
    }
  }
  // Data Code(invert)
  for(i=0; i<8; i++){
    if(data>>i & 1){
      NecGenBitLow();
    }
    else{
      NecGenBitHigh();
    }
  }
  // Stop Bit
  NecGenLedTon();
}

まずはリーダーコードで、LED ONを16T、LED OFFを8Tのため、簡単なforループで作る。
次はCustomコードで、ビットシフトを使って1桁ずつ'1'か'0'かを判定し、データビットを送信している。
この手のコードはMSB(桁が高い方)からというイメージだが、NECフォーマットはLSB(桁が低い方)からのため注意。
後はその反転とDataコードを作り、最後にストップビット(LED ONが1T)を送って終了。
リピートコードは使用せず、フレームを108ms間隔で送信するようにした。




ここまでがIrREMlib.cで、includeするためにIrREMlib.hというヘッダーファイルを作る。

#ifndef IRREMLIB_H
#define	IRREMLIB_H

// NEC Format
void NecSend(unsigned char custom, unsigned char data);

#endif

ヘッダーファイル内ではNecSendだけを定義する。
こうすることで、他のソースコードからはNecSend以外を使用できないようにした。
後は前回作ったkeys.cの頭に#include "IrREMlib.h"を追加し、NecSend(custom, data);のコメントを取ればチートリモコンのプログラムが完成!




これをチートリモコンに書き込み、波形を調べてみた。
Cuntomコードを0xCB、Dataコードを0x53にした結果は以下の通り。


狙い通りにできているので、カードをゲットしてみた。

この動画では、ブルーアイズ、完全究極体グレートモス、ブラックデーモンズドラゴン、サンダーボルト、光の護封剣、巨大化、シャインキャッスルの順に入手している。
もちろんこれ以外にも698/722種類のカードを入手することができるため、図鑑を一気に埋めることができる!!
これを使って最強デッキを入手しよう!!!




最後に、ソースコードは長いので別記事を用意します。