[PIC16F88]HI-TECH CでTimer1割り込みを使用する

PICの16F88はTimer0,1,2という3つのタイマーモジュールを持っており、それぞれのタイマーは役割が異なっています。

今回は、16F88でHI-TECH Cを使用してTimer1割り込みを利用し、0.5秒周期でLEDを点灯させてみます。
クロックは16F88の内部オシレータを8MHzで駆動させます。

#include <stdio.h>
#include <htc.h>
 
#define TIMER_INTERVAL (0xffff - 20000) // TMRモジュールのカウント初期値
                                        // 8MHz, 1/1プリスケーラで、10msecごとに割り込みが入る
 
 
void main( void )
{
    __CONFIG ( FOSC_INTOSCIO &  // 内部クロック
              WDTE_OFF &        // watchdogオフ
              PWRTE_OFF &       // Power-up Timer
              MCLRE_OFF &       // MCLRピンを汎用I/Oとして使用
              BOREN_OFF &       // 電圧降下によるリセットオフ
              LVP_OFF &         // Low-Voltage Programmingオフ
              CPD_OFF &         // EEPROM  プロテクトオフ
              WRT_OFF &         // FrashROMプロテクトオフ
              DEBUG_ON &        // ICDデバッグを有効にする
              CCPMX_RB0 &       // RB0をCCPとして使用
              CP_OFF );         // コードプロテクトオフ
 
    OSCCONbits.IRCF = 7;        // 内部クロックを8MHz駆動にする
    TRISB  = 0x00;          // PortBをすべて出力にする
 
 
    INTCONbits.GIE  = 1;    // Grobal interruptを有効にする
    INTCONbits.PEIE = 1;    // 外部割込みを有効にする
 
    PIR1bits.TMR1IF = 0;    // Timer1割り込みフラグを落としておく
    PIE1bits.TMR1IE = 1;    // Timer1割り込みを有効
 
 
    T1CONbits.T1CKPS = 0;   // プリスケーラを1/1にする
    TMR1 = TIMER_INTERVAL;  // タイマー値をセットします
 
    T1CONbits.TMR1ON = 1;   // Timer1を有効にする
 
    // 割り込みベースで動作させるので何もしない
    while( 1 ) {
        NOP();
    }
 
}
 
 
// 割り込み処理(10msec周期)
static void interrupt intr( void )
{
    volatile static int intr_counter;
 
    //------------------------------------
    // Timer1割り込みが発生したときの処理
    //------------------------------------
    if ( PIR1bits.TMR1IF == 1 ) {
        TMR1 = TIMER_INTERVAL;  // タイマーをリセット
 
        intr_counter++;
        if ( intr_counter >= 100 ) {
            intr_counter = 0;
        }
 
        // 0.5sec周期でRB0を点滅させる。    
        if ( intr_counter >= 50 ) {
            PORTBbits.RB0 = 0;
        } else {
            PORTBbits.RB0 = 1;
        }
 
        // 10msec周期でRB1を点滅させる。    
        // (点滅が速すぎて目視では確認できませんが...)
        if ( intr_counter % 2 == 0 ) {
            PORTBbits.RB1 = 0;
        } else {
            PORTBbits.RB1 = 1;
        }
 
        PIR1bits.TMR1IF = 0;    // 割り込みフラグを落とす
    }   
 
    return;
 
}



main()ではまず内部オシレータを使用させています。

    __CONFIG ( FOSC_INTOSCIO & ... ); // 内部クロック
    OSCCONbits.IRCF = 7;        // 内部クロックを8MHz駆動にする


__CONFIG()でFOSC_INTOSCIOを指定する事で内部オシレータを使用します。
内部オシレータはデフォルトでは31.25kHzという非常に遅い周波数になっているため、OSCCONレジスタのIRCFビット(以後OSCCON.IRCFと表記します)を7にセットし、8MHz駆動に変更します。


次にタイマー割り込みの設定を行います。

    INTCONbits.GIE  = 1;    // Grobal interruptを有効にする
    INTCONbits.PEIE = 1;    // 外部割込みを有効にする
 
    PIE1bits.TMR1IE = 1;    // Timer1割り込みを有効
    PIR1bits.TMR1IF = 0;    // Timer1割り込みフラグを落としておく


タイマー割り込みは、INTCON.GIE、INTCON.PEIE、PIE1.TMR1IEの2ビットを立てることで有効になります。
タイマー割り込みが発生すると、PIR1.TMR1IFビットが立つので、0で初期化します。


以下のコードは、タイマー割り込みの周期を設定しています。

#define TIMER_INTERVAL (0xffff - 20000) // TMRモジュールのカウント初期値
                                        // 8MHz, 1/1プリスケーラで、10msecごとに割り込みが入る
...
 
    T1CONbits.T1CKPS = 0;   // プリスケーラを1/1にする 0:1/1 1:1/2 2:1/4 3:1/8
    TMR1 = TIMER_INTERVAL;  // タイマー値をセットします
 
    T1CONbits.TMR1ON = 1;   // Timer1を有効にする


T1CON.T1CKPSでプリスケーラを1/1にセットします。これによって4クロック毎にタイマーが1増える事になります。もしプリスケーラを他の値、例えば1/2にセットすると分周がかかるので8クロック毎にタイマー値が加算されます


次にTMR1レジスタにタイマーの周期をセットします。
TMR1は16bitのレジスタで、Timer1はTMR1が0xffffからオーバーフローした時に割り込みが掛かります。
今回は,タイマー割り込みを10msec周期でかけるようにしてみます。

今までの設定で、8MHz駆動、4クロック毎にカウンタインクリメントさせているので…

0.001(msec) = 1/8000000(hz) * 4 * x
x = 20000


となり、カウンタが20000回る毎に割り込みが発生すればよい事がわかります。TMR1はアップカウンタなので、結果的にTMR1には0xffff – 20000セットすれば良いです。




タイマーの事前準備が出来たら、次は割り込み処理です。
HI-TECH Cではinterruptをつけた関数が割り込み関数になります。

// 割り込み処理(10msec周期)
static void interrupt intr( void )
{
    volatile static int intr_counter;
 
    //------------------------------------
    // Timer1割り込みが発生したときの処理
    //------------------------------------
    if ( PIR1bits.TMR1IF == 1 ) {
        TMR1 = TIMER_INTERVAL;  // タイマーをリセット
 
        intr_counter++;
        if ( intr_counter >= 100 ) {
            intr_counter = 0;
        }
 
        // 0.5sec周期でRB0を点滅させる。    
        if ( intr_counter >= 50 ) {
            PORTBbits.RB0 = 0;
        } else {
            PORTBbits.RB0 = 1;
        }
 
        PIR1bits.TMR1IF = 0;    // 割り込みフラグを落とす
    }   
 
    return;
 
}



割り込みハンドラは、Timer0に限らずどんな割り込みでもここに飛んでくるので、PIR1.TMR1IFを見て、Timer0割り込みが発生したかをまずチェックします。0.5msec毎に点滅させるのが目的だったので、割り込み関数内でカウンタを回して50回ごとにRB0の値をトグルさせています。

割り込みハンドラ内では、PIR1.TMR1IFを落とし忘れないように注意してください。
これを落とし忘れると、直ぐに再度割り込み処理が走ってしまい、想定した振る舞いになりません。

関連記事

コメントを残す

メールアドレスが公開されることはありません。