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を落とし忘れないように注意してください。
これを落とし忘れると、直ぐに再度割り込み処理が走ってしまい、想定した振る舞いになりません。
関連記事
コメントを残す