SSブログ
English Version

PIC24FJで4足ロボットの製作(その8) [PIC]

 今回はリモート制御のための赤外線コントロールについて書いてみます。

 ハードウェアを簡略化するために 赤外線でのコントロール実験 の記事で紹介した安価な家電用赤外線受信ユニット(PL-IRM0208-A538)を使用します。通信方式は通信速度を極力高速化(家電方式の約20倍)した独自方式です。
 概要を 赤外線コントロールの実験 (←上記とは違うWebページ)にまとめていますので参考にしてください。

 上記の記事では受信側を PIC16F88 を使ってエッジ(信号状態変化)割込みとタイマー割り込みを使って実現しましたが、受信処理内容を改めて見てみると割込み処理内での処理時間まで考慮した結構複雑な処理を行っています。
 今回は PIC24FJ を32MHzで使うので割込み処理内の時間は考慮しなくても大丈夫と思います。また、上記 URL では受信処理内容について細かく記載していませんでしたので再度整理しました。
  1. 制御信号
     制御信号は上記URLのページにも書いてありますが下記のようになります。
    • PCM信号はLSB側から出力する。
    • ビット1は変調信号送信(オン)、ビット0は変調信号オフとする。
    • 1ビット分の送信で
      • ビット1:38KHzの変調波を6サイクル(156us)送信
      • ビット0:6サイクルの時間38KHz信号をオフ。但し直前の送信ビットが1の場合、12サイクルの時間オフにする。

  2. 制御信号のフォーマット
    [1+ID(4)+01] + [0ff-ffff] + [1ss-ssss] + [0tt-tttt] + [1oo-oooo] + [0cc-cccc]
    f,s,t,oはそれぞれ1~4chの値で6ビット長なので64段階の制御可能
    cは制御信号のチェックサム値で誤り検出用
     構成としては 7bit x 6 でエッジ同期でディコードするのでエッジなし最長時間を保証するため、各データの最上位に同期用のビットを付加し、この同期用ビットは交互に反転しています。

  3. 制御信号例
     制御信号の波形例(コントロール信号の値は 0x4d, 0x00, 0x40, 0x20, 0x40)を下図に示します。黄色が赤外線受信ユニットの出力波形で紫色がタイマー割込み毎にトグルで変化させた波形(ディバッグのために一時的に出力させたモニタ信号)です。

    制御信号例

  4. タイマー割込みのタイマー値
     赤外線受信ユニットの出力は赤外線有の時のパルス幅(Low出力)が実際の赤外線出力時間よりも長くなり、逆に赤外線なしの場合のパルス幅(High出力)は赤外線がない時間よりも短くなります。
     このため、ディコード処理が少々面倒で、エッジ割込みとタイマー割込みを使って処理しています。
     タイマー割込みのタイマー値は条件に応じて変更する必要があり、条件分けとしてはエッジ割込み時のタイマー設定とタイマー割込み時のタイマー設定のそれぞれについて赤外線受信ユニット信号の値(0または1)で場合分け(2x2=4通り)する必要があります。
     それぞれの条件でのタイマー設定値は下記のようになります。

     尚、下表でのタイマー値設定欄で記載している記号の意味は次の通りです。
    • Tb : 1bit長(156 us)
    • Ta : 赤外線有時の赤外線受信ユニットの出力パルス幅の増加分
    • Tm : エッジ変化可能性ありの時刻に加算するタイマー値(= Tb / 2 )

    No割込み処理赤外線有無タイマー設定値下図対応個所
    1エッジ割込みTb + Ta + Tm
    2エッジ割込み2 * Tb - Ta + Tm
    3タイマー割込みTb
    4タイマー割込みTb

    タイマー割込み設定値のサンプルケース

  5. Ta の値について
     赤外線受信ユニットの出力での赤外線有の時のパルス幅(Low出力)の増加時間について再度確認してみました。
     コントロール信号が 0x4d, 0x18. 0x4e, 0x20, 0x40, 0x3b の波形を用いてシンクロで赤外線ユニットの出力信号幅を実測し、赤外線の出力時間との差異を確認しました。
     結果が下表で差異を平均すると 44.7 us という結果になりました。この結果から前項での Ta の値を 50 us にしたところ、赤外線受信処理でのディコードが安定するようになりました。

    赤外線受信ユニットの0/1パルス幅の非対称性

  6. 割込み処理及び初期化
     フローチャート化等すればベターなのでしょうが、上記の赤外線受信処理のエッセンスから実装可能と思います。
     参考に初期化と割込み処理のソースを付けておきます。
     尚、赤外線受信ユニットの出力信号は _RA1 で読み込んでいます(下記ソースでは IR_BIT )

    /*
     * First IR control receiver
     *  for PIC24FJ64GA
     *   Ver 0.01 by skyriver 2016/08/21
     */
    
    
    #include "io.h"
    #include "IrRcv.h"
    
    /* timing parameter definition */
    #define TM4_LENGTH      4   // timer4 clock [uSec]
    
    #define TM_1BIT_LEN     156 // one bit length[uSec]
    #define TM_1_ADD        50  // 1(low) bit additional time [us]
    #define TM_MARGIN       (TM_1BIT_LEN / 2 ) // time up timing
    #define TM_UNSENCE      10000       // unsense timer(wait next signal) [us]
    #define TM_SET(x)       PR4 = ( (x) / TM4_LENGTH )
    
    IRDAT_TYP IrDat;
    #define RcvCmpBit   IrDat.RcvFlg.bits.b0   // receive data exist
    #define RcvRcvingBit IrDat.RcvFlg.bits.b2  // receiving flag
    #define RcvPreBit   IrDat.RcvFlg.bits.b3   // receive privious bit
    #define RcvNoRcvBit IrDat.RcvFlg.bits.b4   // no receive bit
    
    uint8_t *RcvBufPnt;
    BYTE_BIT RcvData;       // received control data
    
    #define RcvDataMSB RcvData.bits.b7
    uint8_t RcvBitCnt;      // receive data bit counter
    uint8_t RcvByteCnt; // receive data byte counter
    
    uint8_t RcvChkSum;      // receive data check sum
    uint8_t RcvID;          // receive ID No
    
    #define ENABLE_TMR4     T4CONbits.TON = 1
    #define DISABLE_TMR4    T4CONbits.TON = 0
    
    void CheckEnd( void );
    
    
    void __attribute__((interrupt, no_auto_psv)) _CNInterrupt( void )
    {
        CN_MON ^= 1;        // ___ for debug
        if( RcvNoRcvBit == 0 ) {    // if can receive
            if( RcvCmpBit == 0 ) {  // if receive data doesn't exist
                if( RcvRcvingBit == 0 ) {   // if not receiving
                    if( IR_BIT == IR_BIT1VAL ) {    // if IR signal is active
                        TMR4 = 0;
                        TM_SET( TM_1BIT_LEN + TM_1_ADD + TM_MARGIN );
                        ENABLE_TMR4;
    
                        RcvRcvingBit = 1;
                        RcvPreBit = 1;
    
                        RcvByteCnt = RCVDATALEN;    // set receive byte counter
                        RcvBitCnt = RCVDATABLEN;    // set bit counter
                        RcvBufPnt = &IrDat.RcvDataBuf[ 0 ];
                        RcvData.byte = 0;
                        RcvChkSum = 0;
                    }
                } else {
                    TMR4 = 0;
                    if( IR_BIT == IR_BIT0VAL ) {
                        TM_SET( 2 * TM_1BIT_LEN - TM_1_ADD + TM_MARGIN );
                    } else {
                        TM_SET( TM_1BIT_LEN + TM_1_ADD + TM_MARGIN );
                    }
                    RcvDataMSB = RcvPreBit;
                    RcvData.byte >>= 1;
                    RcvPreBit ^= 1;
                    CheckEnd();     // check byte end
                }
            }
        }
        _CNIF = 0;
    }
    
    
    void __attribute__((interrupt, no_auto_psv)) _T4Interrupt( void )
    {
        TM0_MON ^= 1;   // ___ for debug
        if( RcvNoRcvBit == 0) {     // if not inhibit to receive(=receiving)
            TMR4 = 0;
            TM_SET( TM_1BIT_LEN );
            RcvDataMSB = RcvPreBit;     // receive same as previous data
            RcvData.byte >>= 1;
            CheckEnd();     // check byte end
        } else {
            RcvNoRcvBit = 0;
            DISABLE_TMR4;
            _CNIE = 1;      // enable CN int
        }
        _T4IF = 0;
    }
    
    
    /* check byte end */
    void CheckEnd( void )
    {
        if( --RcvBitCnt == 0 ) {    // if byte data receive completely
            RcvBitCnt = RCVDATABLEN;
            *RcvBufPnt++ = RcvData.byte;
            RcvChkSum ^=  RcvData.byte;
            if( --RcvByteCnt == 0 ) {
                RcvRcvingBit = 0;
                if( RcvChkSum == RCVDATMSB ) {  // if check sum is ok
                    RcvCmpBit = 1;  // set receive complate flag
                    RcvNoconBit = 0;
                }
            } else {
                if( RcvByteCnt == ( RCVDATALEN - 1 ) ) {    // if first byte
                    if( RcvData.byte != RcvID ) {   // if not my ID
                        RcvRcvingBit = 0;
                    }
                }
                else if( RcvByteCnt & 1 ) { // if first, third or  fifth byte
                    if( RcvData.bits.b6 == 0 ) {    // if format error
                        RcvRcvingBit = 0;
                    }
                }
                else if(  RcvData.bits.b6 == 1 ) {    // if format error
                    RcvRcvingBit = 0;
                }
            }
            RcvData.byte = 0;
            if( RcvRcvingBit == 0 ) {   // if receive proc end
                RcvNoRcvBit = 1;
                TMR4 = 0;
                TM_SET( TM_UNSENCE );
                _CNIE = 0;      // disable CN int
            }
        }
    }
    
    
    // IR receive initial setting
    void IrRcvIni( void )
    {
        CNPU1 = 0x0008;         // set IRbit PullUp(CN3:RA1)
        CNEN1 = 0x0008;         // enable IRbit(CN3:RA1) change interrupt
        _CNIF = 0;              // clear CN int flag
        _CNIE = 1;              // enable CN interrupt
        T4CON = 0x2020;         // TON:OFF TSIDL 1/64(4us clock)
        _T4IE = 1;              // enable time4 interrupt
        IrDat.RcvFlg.byte = 0;
        RcvID = (DEFAULT_CH << 2) | 0x41;
    }
    

[TOP] [ 前へ ] 連載記事 [ 次へ ]
nice!(1)  コメント(0)  トラックバック(3) 
共通テーマ:趣味・カルチャー

nice! 1

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 3