PICマイコンを使って測定ツールを作ってみよう(2) ―― クロック周波数やモータの回転数を測れるカウンタを作る(後編)

中西 紫朗

●割り込み処理は二つのルーチンを用意

 リスト4には高位割り込みルーチンと低位割り込みルーチンがあります.PIC16F877Aなどでは割り込みの種類は1種類だけで,Z80などと比べても貧弱です.PBポートの状態変化割り込みまで含めると,割り込みピン数は十分なのですが,割り込みアドレスが一つだけなので,どういう割り込みかを判別する処理が必要になり,割り込み処理時間の増大を招きます.

リスト4 割り込みルーチンの記述(user.cの571~680行目)

/*********************************************************
   優先順位を使った複数割込み
    高レベル:タイマ1  低レベル:タイマ0
  機能
    タイマ0   :外部クロックのカウント
    タイマ1   :25msごとの割込み、100msごとのタイマ0値読み出し
********************************************************/ 
//****** 割込みの宣言 優先順位使用
#pragma interrupt isr save = PROD
#pragma interruptlow tmr0 save = WREG,BSR,STATUS,PROD
 
//***** 割込みベクタジャンプ命令セット
#pragma code isrcode = 0x8
void isr_direct(void)
{
    _asm
    goto isr
    _endasm
}
#pragma code lowcode = 0x18
void low_direct(void)
{   _asm
    goto tmr0
    _endasm
}
//**** 高レベル 割込み処理関数
#pragma code
void isr(void)                      // 割り込み関数
{
           INTCONbits.GIEH=0;          // 高レベル禁止
           INTCONbits.GIEL=0;          // 低レベル禁止
                            INTCONbits.T0IE = 0;                    // Timer0割り込み禁止
                            PIE1bits.TMR1IE = 0;                    // Timer1割り込み禁止
    if(PIR1bits.TMR1IF){            // タイマ1割り込み25ms=4x8x37500 @48MHz
        PIR1bits.TMR1IF=0;          // タイマ1割り込みフラグを0にする
                            TMR1H = 0x6D;
                            TMR1L = 0x84-10;                                        // 0x6D84=655368-37500
        if(--cnt1==0){              // cnt1を-1して結果が0?
            cnt1=4;                // cnt1にLEDの更新周期を書き戻す
            if(PORTCbits.RC2)                  //    100m秒@20MHz(4x25mS)
                PORTCbits.RC2=0;    //LED YELLOWを0.5秒間隔で点滅
            else
                PORTCbits.RC2=1;
                                          TMR0L1=TMR0L;
                                          TMR0H1=TMR0H;
                                          cnt01=cnt0;
                                          TMR0H=0;
                                          dsp_on =1;
                                          cnt0=0;
                                          TMR0L=0;
                                INTCONbits.GIEL=1;          // 低レベル許可
        }
              } else if(PIR1bits.SSPIF){               //I2C割り込み?
//                          PORTAbits.RA0 = 1;
                            PIR1bits.SSPIF=0;
                            switch(I2C_state){
                                          case 0:
                                                                      if(SSPSTATbits.BF){
                                                                                    I2C_DA=SSPBUF;
                                                                                    I2C_state=1;
                                                                      }
                                                                      break;
                                          case 1:
                                                                      if(SSPSTATbits.BF){
                                                                                    I2C_RA=SSPBUF;
                                                                                    I2C_state=2;
                                                                      }
                                                                      break;
                                          case 2:
                                                                      if(SSPSTATbits.BF){
                                                                                    if(SSPSTATbits.R_W==1){
                                                                                                  I2C_state=3;
                                                                                    } else {
                                                                                                  I2C_MDATA=SSPBUF;
                                                                                                  I2C_state=0;
                                                                                    }
                                                                      }
                                                                      break;
                                          case 3:
                                                                      if(!SSPSTATbits.BF){
                                                                                    SSPBUF=I2C_SDATA;
                                                                                    I2C_state=0;
                                                                      }
                                                                      break;
                                          }                                                      
                            PIE1bits.SSPIE=1;
                            } else if(PIR1bits.RCIF){                 //USART割り込み?
                                          PIR1bits.RCIF=0;
                                          RCVFLG=1;
                                          a=RCREG;
                                          UART_buffer[uart_ptr++]=a;
                                          if(uart_ptr>uart_ptr_max) uart_ptr=0;
                            }
    INTCONbits.GIEH=1;          // 高レベル許可
    INTCONbits.GIEL=1;          // 低レベル許可
              INTCONbits.T0IE = 1;
              PIE1bits.TMR1IE = 1;
 
}                                  
//***** 低レベル割込み処理関数
void tmr0(void)                     // 割り込み関数
{
    if(INTCONbits.T0IF){            // タイマ0割り込み?
        INTCONbits.T0IF=0;          // タイマ0割り込みフラグを0にする
                                          cnt0++;
        if(--cnt==0){               // cntを-1して結果が0?
            cnt=10;                  // cntにLEDの更新周期を書き戻す
        }
    }
}

 


 今回のように,USBの割り込みフラグ・チェックが1ms周期というゆるやかなタスクでは問題ありませんが,タスク切り替え時間が数十ns以下のリアルタイムOSになると状況が厳しくなってきます.PIC24Fシリーズなどでは,I/O一つ一つに割り込みアドレスが割り付けられており,より迅速な処理が行えます.PIC18Fシリーズでは2種類の割り込みアドレスで十分,と考えたのでしょう.PIC24Fという16ビット・マイコンのシリーズでようやく他社のマイコン,例えばMSP430などと肩を並べたともいえます.MSP430はほかに低消費電力という特徴があるので,手ごわいライバルかもしれません.ただし開発ツールが何十万円もするので,シロウト向けではありません.

 台湾や中国では昔ながらの8051がよく使われており,無償のKeilという環境でソフトウェアが開発されています.8051のコアはFPGA向けに提供されているので,コストを抑えたい開発にはうってつけです.それに30年にわたる8051のソフトウェア資産があります.商業利用での地位はなかなか揺るぎそうもありません.

 PICマイコンは出荷量では1位,2位と言われますが,まだまだ安泰ではありません.今後もいろいろと機能の豊富なマイコンがでてきます.それらと伍して戦うためには,開発ツールの整備といまの安価な価格体系を維持することが必要だと思います.また,豊富なI/Oモジュール群も魅力です.

 本題に戻りましょう.カウンタに使う割り込みは,タイマ1を高位割り込みに,タイマ0を低位割り込みに割り付けています.両方高位に割り付けても動作上は変わりませんが,両方の割り込みが同時に起こったとき,優先順位をつけておいたほうがデバッグしやすい,ということもあります.

 タイマ1は基本クロックが48MHzなので,その4分周されたクロックがプリスケーラに入力されます.1:8プリスケーラ出力は1.5MHzとなり,周期にすると0.66μs程度になり,16ビット・カウンタでは43msまでしかカウントできません.そこで,100msを生成しやすい25msをカウンタの周期にしました.

 具体的には割り込みサービス・ルーチン(ISR)で4回カウントして100msにして,タイマ0の読み出しを行います.タイマ0は16ビット読み出しをセットしているので,TMR0Lを読み出すとTMR0Hもいっしょにラッチされます.そのあとTMR0Hを読み出しても,先にTMR0Lを読み出したときのTMR0H値が保持されています.今回の回路ではそのようなことはないのですが,TMR0Hを読み出している間にTMR0Hの値が変化することはあり得ます.タイマの値としては16ビット一括の値を読み出したいので,こういう16ビット読み出しの工夫がしてあるとありがたいものです.

 同時にLEDを点滅させています.この点滅を確認することで,タイマが順調に動作しているかどうかが分かります.

組み込みキャッチアップ

お知らせ 一覧を見る

電子書籍の最新刊! FPGAマガジン No.12『ARMコアFPGA×Linux初体験』好評発売中

FPGAマガジン No.11『性能UP! アルゴリズム×手仕上げHDL』好評発売中! PDF版もあります

PICK UP用語

EV(電気自動車)

関連記事

EnOcean

関連記事

Android

関連記事

ニュース 一覧を見る
Tech Villageブログ

渡辺のぼるのロボコン・プロモータ日記

2年ぶりのブログ更新w

2016年10月 9日

Hamana Project

Hamana-8最終打ち上げ報告(その2)

2012年6月26日