#include <htc.h> __CONFIG ( FOSC_INTOSCIO // 内蔵オシレータ & WDTE_OFF // WDT 無効 & PWRTE_OFF // PWRT 無効 & MCLRE_OFF // MCLR 使用しない & BOREN_OFF // BOD 無効 & LVP_OFF // Low-Voltage Programming 使用しない & CPD_OFF // Data memory code protection 無効 & DEBUG_OFF // In-Circuit Debugger 使用しない & CP_OFF // Program Memory code protection 無効 ); // Timer0 のオーバーフローをカウントする static unsigned int cnt = 0; // メインルーチン void main () { // RA0 をデジタル出力に設定 TRISB0 = 0b0; // OSCILLATOR CONTROL REGISTER OSCCONbits.IRCF = 0b110; // 内蔵クロックを 4MHz に設定 // OPTION REGISTER OPTION_REGbits.T0CS = 0b0; // Timer0 は CLKO(=Fosc/4) 基準で動作 OPTION_REGbits.PSA = 0b0; // プリスケーラを Timer0 にアサイン OPTION_REGbits.PS = 0b100; // プリスケーラ比は 1:32 // カウントアップしていって 0xFF → 0x00 で割り込みを発生させる TMR0 = -160; // 割り込み許可 INTCONbits.TMR0IE = 0b1; // Timer0 の割り込みを許可 INTCONbits.GIE = 0b1; // グローバル割り込み許可 // ポート初期化、最初 LED は点灯している PORTB = 0b00000001; // メインループ while (1) if (cnt == 200) { RB0 = ~RB0; // 出力ポートを反転させる cnt = 0; // オーバーフローカウンタをリセット } } // 割り込みルーチン static void interrupt isr (void) { // Timer0 による割り込みの場合 if (INTCONbits.TMR0IF) { INTCONbits.TMR0IF = 0; // 割り込みフラグをクリアするのはプログラムの仕事 TMR0 = -160; // Timer0 を再設定 cnt++; // オーバーフローカウンタをインクリ } }
__CONFIG
static void interrupt isr (void)
interrupt
が指定されることで、関数が割り込みベクタに配置されるようになるっぽい。割り込みを利用するプログラム中に一つだけ存在すること。タイマ以外にも様々な割り込みがあるけれども、あらゆる割り込みはこの関数に飛び込んでくる。if (INTCONbits.TMR0IF)
INTCONbits.TMR0IF
が立つことでこれを判別できる。INTCONbits.TMR0IF = 0;
このプログラムを走らせると、RA0 に接続された LED が約 1 秒間隔、正確には 1.024 秒間隔でオン/オフを繰り返します*1。この動作時間を求めるには以下の段階を経ています。
1 秒 ÷ 4MHz × 4 クロック × プリスケーラ 32 倍 × 160 タイマーカウント × 200 オーバーフローカウント = 1.024 秒
Timer0 は 8 ビット幅しかないため、長い時間をカウントするのは不向きです。そこで、プリスケーラやソフトウェア カウンタを利用することで、更に長い時間で割り込みをかけられるようになります。ミリ秒オーダーの割り込みであれば、これで十分かと。ただ、動作クロックの精度にも影響を受けるので、あまり正確な時間を刻むには注意が必要になります。シリアル通信など、データ送受信のタイミングが重要なアプリケーションでは、外部クリスタルなどを利用した方がよさそうです。