« FRマイコン基板でQステアをコントロール(その1) | トップページ | OPENBDM用にボードをアレンジ »

2008年11月24日 (月曜日)

FRマイコン基板でQステアをコントロール(その2)

という事で前回に引き続き、コードの詳細と動作の様子について紹介。

完成したシステムはこんな感じです。
100_0039 FR60の特長でもあるUSBホスト機能を活用しない手は無いので、USBゲームパッドを接続しました。USBゲームパッドからのデータ取得はインターフェース2008年6月号のサンプルで紹介されているインターフェイス社のITF-USBHostの試用版を使っています。キーアサインは十字キーの左右をステアリングに、ボタンは真ん中と左をそれぞれ前進、後進に割り当て、上段/下段でダッシュあり/なしとしています。Qステアをお持ちの方はご存知かと思いますが、

標準のリモコンは「ダッシュ」ボタンを押しながらステアリング操作をするのが非常に辛く、今回の制作の目的の半分はこれを解消した状態で操縦してみたいと言う欲望だったりもします。

今回の作例と動作の様子を動画にて。

なお、使用したビデオリモコン(?)の赤外線LEDの出力があまり強くないらしく(*1)、常にQステアに発光部を向けていないと反応しなくなります。

*1 当初、フォトカプラ経由で5vでもドライブしてみたのですがあまり変化が無かったので3.3v駆動にしてあります。今後、他の赤外線LEDでも試してみるつもりです。

そしてコードですが、前回にも書いたように、DoJaの影響を強く受けています。定数や構造体定義はこんな感じ。

/*****************************************************************************/
/* irremotecontroller.h:IRリモコン用ヘッダファイル                           */
/*****************************************************************************/
// 送信コードのパターン
#define LH 0
#define HL 1
// 最大送信長
#define MAX 8
// 送信データサイズ
#define DATA_SIZE MAX * 2 + 2
// キャリアOn/Off情報
#define OFF 0
#define ON 1
// 送信繰り返し
#define NO_LOOP 0
#define LOOP 1
// 論理パルス構造体
struct LogicalPulse{
    char pattern;    // パターン(LH or HL)
    short highduration;    // High送信時間
    short lowduration;    // Low送信時間
} typedef PULSE;
// フレーム構造体
struct Frame{
    short duration;    // 送信時間(10usec)
    char data;    // 送信データ(MSB→LSB)
    short length;    // 送信ビット数
    short startduration; // ヘッダデータ送信時間(10usec)
    short interval;    // 再送時インターバル時間(10usec)
} typedef FRAME;
// キャリアOn/Off制御データ構造体
struct CareerControl{
    char status;    // ON/OFF
    short duration; // 制御時間
} typedef CAREER_CONTROL;
// 関数プロトタイプ
void setCode0(PULSE);
void setCode1(PULSE);
void send(FRAME, char);
void timer_stop(void);
void PPG2_init(void);
void RLT1_init(void);

そして本体のコード

/*****************************************************************************/
/* fr_irremotecontroller.c:                                                  */
/* PPGタイマとリロードタイマを用いた赤外線リモコン信号生成                */
/*****************************************************************************
#include "_fr.h"    /* 周辺機能のレジスタをC言語の変数として宣言・定義 */
#include "irremotecontroller.h"
// 内部管理用変数群
PULSE code[2];    // 0/1を表現するパルスデータ
// キャリア制御データ
CAREER_CONTROL careerControl[DATA_SIZE];
int rlcount;    // リロードタイマ1割り込みカウント
short datacount;    // キャリア制御データ数
char isloop;    // ループ有無
short currdatacount;    // 現在送信中のキャリア制御データ番号0->
short currduration;    // 現在送信中のキャリアの長さ
// setCode0
// 0を表現する論理パルス
void setCode0(PULSE logicalPulse){
    code[0] = logicalPulse;
}
// setCode1
// 1を表現する論理パルス
void setCode1(PULSE logicalPulse){
    code[1] = logicalPulse;
}
// データ送信
void send(FRAME frame, char loop){
    char i;
    char mask = 0x80;
    char data;
    CAREER_CONTROL* controldata;
    // 送信中のものがあれば中止
    timer_stop();
    // 送信データリセット
    memset( &careerControl[0], 0x00, (sizeof(CAREER_CONTROL) * DATA_SIZE));
    controldata = &careerControl[0];
    datacount = 0;
    // ヘッダデータ
    controldata->status = ON;
    controldata->duration = frame.startduration;
    controldata++;
    datacount++;
    // frameを元に送信データを生成して送信
    // 最大の送信データは8bitとする
    for(i = 0; i < 8; i++){
        if(i == frame.length){
            // lengthに達したらbreak
            break;
        }
        // frame.dataを上位ビットから順に取得
        if( ((mask >> i) & frame.data) == 0){
            data = OFF;
        }
        else{
            data = ON;
        }
        if(code[data].pattern == LH){
            // LH
            controldata->status = OFF;
            controldata->duration = code[data].lowduration;
            controldata++;
            datacount++;
            controldata->status = ON;
            controldata->duration = code[data].highduration;
            controldata++;
            datacount++;
        }
        else{
            // HL
            controldata->status = ON;
            controldata->duration = code[data].highdration;
            controldata++;
            datacount++;
            controldata->status = OFF;
            controldata->duration = code[data].lowduration;
            controldata++;
            datacount++;
        }
    }
    // interval
    controldata->status = OFF;
    controldata->duration = frame.interval;
    datacount++;
    // 繰り返し送信設定
    isloop = loop;
    // frame.durationとframe.startdurationの合計値
    currduration = careerControl[0].duration;
    currdatacount = 0;
    // PPGとリロードタイマを初期化
    PPG2_init();
    RLT1_init();
}
/************************************************************************/
/* PPG2、リロードタイマ1を停止                                          */
/************************************************************************/
void timer_stop(){
    IO_TMCSR1.bit.CNTE = 0;    // カウンタ動作禁止
    IO_PCN2.bit.IREN = 0;        /* PPG割り込み不許可               */
    IO_PCN2.bit.CNTE = 0;        /* PPGタイマ動作不許可             */
    IO_PORT.IO_DDR4.bit.P46 = 0;    /* P44出力不許可   */               
    IO_PORT.IO_PFR3.bit.PPGE2 = 0;    /* P44汎用I/Oポート設定          */
}
/************************************************************************/
/* PPG2初期化関数(キャリア:38kHz/デューティ比3:1 生成)                */
/************************************************************************/
void PPG2_init(void){
    // P46(PPG2)の出力許可設定
    IO_PORT.IO_DDR4.bit.P46 = 1;    /* P46出力許可                   */
    IO_PORT.IO_PFR3.bit.PPGE2 = 1;    /* P46汎用I/Oポート設定          */
    __set_il(0x1F);            /* CPU割り込みレベルILM=31       */
    __EI();                /* CPU割り込み受付許可 Iフラグ=1 */
    IO_ICR[25].byte = 20;        /* PPG2割り込みレベルICR25    */
    IO_PCN2.bit.MDSE = 0;        /* PWM動作                       */
    IO_PCN2.bit.IRS  = 3;        /* 周期/デューティ一致割り込み   */
    IO_PCN2.bit.CKS  = 1;        /* 4分周 20MHz/4=5MHz */
    IO_PCSR2 = 131 - 1;        /* PPG周期 0.2u * 131 =  26.2u       */
    IO_PDUT2 = 44 - 1;        /* PPGデュティー30% 0.2u * 44 = 8.8u */
    IO_PCN2.bit.IRQF = 0;        /* PPG割り込み要因クリア         */
    IO_PCN2.bit.IREN = 0;        /* PPG割り込み不許可               */
    IO_PCN2.bit.CNTE = 1;        /* PPGタイマ動作許可             */
    IO_PCN2.bit.STGR = 1;        /* ソフトウェア起動              */
}
/*****************************************************************************/
/* リロードタイマ初期化関数                                                  */
/*****************************************************************************/
void RLT1_init(void){
    IO_TMCSR1.bit.CNTE = 0;    // カウンタ動作禁止
    IO_TMRLR1 = 100 - 1;    // 10u秒(割り込み処理の処理時間を考慮する必要があります)
    IO_ICR[9].byte = 19;    // 割り込みの優先レベル
    IO_TMCSR1.bit.CSL = 0;    // CLKP 20MHzの1/2に設定 1カウント=0.1u
    IO_TMCSR1.bit.RELD = 1;    // リロード許可
    IO_TMCSR1.bit.INTE = 1;    // 割り込み許可
    IO_TMCSR1.bit.CNTE = 1;    // カウント動作許可
    IO_TMCSR1.bit.TRG = 1;    // カウント開始指示
}
/*****************************************************************************/
/* リロードタイマ割り込み関数                                                */
/*****************************************************************************/
__interrupt void RLT1_int(void){
    // 送信するframeとモードが指定されて呼び出される
    rlcount++;
    if(rlcount > currduration){
        // 指定音長に到達
        rlcount = 0;
        currdatacount++;
        if( currdatacount > (datacount - 1) ){
            // 全データ送信
            if(isloop == LOOP){
                // ループ
                currduration = careerControl[0].duation;
                currdatacount = 0;
            }
            else{
                timer_stop();
                return;
            }
        }
        // 次のデータ
        currduration = careerControl[currdatacount].duration;
        if(careerControl[currdatacount].status == ON){
            IO_PCN2.bit.CNTE = 1;        /* PPGタイマ動作許可             */
            IO_PCN2.bit.STGR = 1;        /* ソフトウェア起動              */
        }
        else{
            IO_PCN2.bit.CNTE = 0;        /* PPGタイマ動作停止             */
        }
    }
    IO_TMCSR1.bit.UF = 0;    /* 割り込みフラグクリア */
}

実際の使用方法としてはこんな感じ

void some_process()
{
    // 送信フレームデータ
    FRAME frame;
    PULSE data0, data1;
    // リモコンデータの初期化
    data0.pattern = LH;
    data0.highduration = 43;
    data0.lowduration = 41;
    setCode0(data0);
    data1.pattern = LH;
    data1.highduration = 90;
    data1.lowduration = 41;
    setCode1(data1);
    // バンドC、前進信号を送信
    frame.duration = 7910;
    // バンドC:2 前進:1
    frame.data = getSendData(2, 1);
    frame.length = 6;
    frame.startduration = 173;
    frame.interval = 4800;
    send(frame, NO_LOOP);
}
// QSTEERのリモコンデータ生成
char getSendData(char ch, char command){
    char result;   
    result = ((ch << 4 | command) << 2);
    return result;
}

実際のアプリはUSBゲームパッド以外にも、UARTで受け取った文字に応じて信号が出せるようになっているのでPCから制御する事も可能です。今後、もう3台購入して4台同時に扱えるようにしてみたいと思います。

|

« FRマイコン基板でQステアをコントロール(その1) | トップページ | OPENBDM用にボードをアレンジ »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/2790/43212463

この記事へのトラックバック一覧です: FRマイコン基板でQステアをコントロール(その2):

« FRマイコン基板でQステアをコントロール(その1) | トップページ | OPENBDM用にボードをアレンジ »