/* * kuro_rs-pseudo_firmware.c * * KURO-RS実機の挙動から推測した、KURO-RSファームウェアの疑似ソース * ※コンパイルは通りません。説明のために作成した、疑似ソースです。 * * * Tue Nov 27 22:51:32 JST 2007 Naoyuki Sawa * - 1st リリース。 */ /**************************************************************************** * 別途定義されていると仮定するサブルーチン ****************************************************************************/ extern int recv_from_pc(); /* PCから1バイト受信する。(0x00〜0xFF = 受信データ, -1 = 受信データ無し) */ extern void send_to_pc(int data); /* PCへ1バイト送信する。 */ extern int get_0_1ms_timer(); /* 現在時刻を0.1ミリ秒単位で取得する。 */ extern int pd_stat(); /* 本体の左側面に内蔵されている『受光部』(Photo Diode)の受光状態を取得する。(0 = 受光していない, 1 = 受光している) */ extern void ir_on(int port, int on); /* 赤外線送信部の先に付いている『発光部』の発光状態を設定する。(0 = 消光させる, 1 = 発光させる) */ extern void access_blink(int type); /* 本体の右上に内蔵されている『アクセスランプ』を点滅させる。(type = 点滅パターン) */ /**************************************************************************** * ローカル関数宣言 ****************************************************************************/ static void rx_mode(); /* 学習モード */ static void tx_mode(); /* 送信モード */ static void id_mode(); /* 識別モード */ /**************************************************************************** * モード監視状態 ****************************************************************************/ void main() { int c; for(;;) { c = recv_from_pc(); if(c >= 0) { switch(c) { case 'r': rx_mode(); /* 学習モード */ break; case 't': tx_mode(); /* 送信モード */ break; case 'i': id_mode(); /* 識別モード */ break; } } } } /**************************************************************************** * 学習モード ****************************************************************************/ static void rx_mode() { int c; int i; int j; int t; unsigned char buf[240]; /* PCへ'Y'を送信する。 */ send_to_pc('Y'); /* 赤外線を受光するか、キャンセルされるまで待ちます。 */ while(!pd_stat()) { if(recv_from_pc() >= 0) { /* PCへ'Y'を送信し、モード監視状態に戻ります。 * ※『赤外線コントロール仕様書』では、キャンセルコマンドは'c'となっていますが、 * 実際には、'c'でも'c'以外でも何でもキャンセルコマンドと見なされるようです。 * ※キャンセル時の'Y'の送信の有無が、受信モードと送信モードで異なるようです。 */ send_to_pc('Y'); return; } } /* 0.1ミリ秒間隔で受光状態をサンプリングし、リモコンデータとして格納します。 */ t = get_0_1ms_timer(); for(i = 0; i < 240; i++) { buf[i] = 0; for(j = 0; j < 8; j++) { if(pd_stat()) { buf[i] |= (1 << j); /* LSB先行 */ } /* 0.1ミリ秒間隔のタイミング待ち。 */ while(get_0_1ms_timer() == t) { /** no job **/ } t++; } } /* PCへ'S'を送信します。 */ send_to_pc('S'); /* PCへリモコンデータを送信します。 */ for(i = 0; i < 240; i++) { send_to_pc(buf[i]); } /* アクセスランプをゆっくり3回点滅させます。 * ※PCへ'E'を送信する前にアクセスランプを点滅させます。点滅が完了するまで'E'は送信しません。 * 送信モードや識別モードの場合は、すべての送信が完了したあとにアクセスランプを点滅させて、 * 点滅が終了したら直ちににモード監視状態に戻っているのと較べると、学習モードだけ特殊です。 */ access_blink(3); /* PCへ'E'を送信します。 */ send_to_pc('E'); /* モード監視状態に戻ります。 */ } /**************************************************************************** * 送信モード ****************************************************************************/ static void tx_mode() { int c; int port; int i; int j; int t; unsigned char buf[240 + 2]; /* ※「+2」はKURO-RS実機の挙動から推測しました。以下のコメントを参照してください。 */ /* PCへ'Y'を送信する。 */ send_to_pc('Y'); /* PCからポート番号を受信します。 */ for(;;) { c = recv_from_pc(); if(c >= 0) { if((c >= '1') && (c <= '4')) { port = c - '0'; /* PCへ'Y'を送信し、リモコンデータ受信待ちへ移行します。 */ send_to_pc('Y'); break; } else { /* PCへ何も送信せずに、モード監視状態に戻ります。 * ※『赤外線コントロール仕様書』では、キャンセルコマンドは'c'となっていますが、 * 実際には、'1'〜'4'以外ならば何でもキャンセルコマンドと見なされるようです。 * ※キャンセル時の'Y'の送信の有無が、受信モードと送信モードで異なるようです。 */ return; } } } /* ※これ以降、送信モードを終了するまで、もうキャンセルはできません。 * PCは、リモコンデータを240バイト送信して、正規の手順で送信モードを完了させるしかありません。 * もし発光させたくないならば、0x00を240バイト送信すれば発光しないはずなので、実質キャンセルできるかも知れません。(未検証) */ /* PCからリモコンデータを240バイト受信します。 * ※KURO-RSにバグがあり、243バイト以上受信すると、ハングアップしてしまうようです。 * 239バイト以下 -> 合計240バイト以上になるまで、次の受信を待ちます。 * 240バイト -> 受信を完了します。 * 241バイト -> 余分に受信した1バイトを捨てて、受信を完了します。 * 242バイト -> 余分に受信した2バイトを捨てて、受信を完了します。 * 243バイト以上 -> ハングアップします。KURO-RSをつなぎ直さないと、復旧しません。 */ i = 0; for(;;) { c = recv_from_pc(); if(c >= 0) { buf[i++] = c; continue; /* ※バグ(KURO-RS実機の挙動から推測)。この「continue;」を削れば、正しい動作になります。 */ } if(i >= 240) { break; } } /* リモコンデータに従って、0.1ミリ秒間隔で発光、または、消光します。 */ t = get_0_1ms_timer(); for(i = 0; i < 240; i++) { for(j = 0; j < 8; j++) { if(buf[i] & (1 << j)) { /* LSB先行 */ ir_on(port, 1); } else { ir_on(port, 0); } /* 0.1ミリ秒間隔のタイミング待ち。 */ while(get_0_1ms_timer() == t) { /** no job **/ } t++; } } /* 確実に発光部を消光します。 */ ir_on(port, 0); /* PCへ'E'を送信します。 */ send_to_pc('E'); /* アクセスランプをすばやく2回点滅させます。 */ access_blink(2); /* モード監視状態に戻ります。 */ } /**************************************************************************** * 識別モード ****************************************************************************/ static void id_mode() { /* PCへ'O'を送信します。 */ send_to_pc('O'); /* アクセスランプをゆっくり1回点滅させます。 */ access_blink(1); /* モード監視状態に戻ります。 */ }