#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++; // オーバーフローカウンタをインクリ
}
}
__CONFIGstatic 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 ビット幅しかないため、長い時間をカウントするのは不向きです。そこで、プリスケーラやソフトウェア カウンタを利用することで、更に長い時間で割り込みをかけられるようになります。ミリ秒オーダーの割り込みであれば、これで十分かと。ただ、動作クロックの精度にも影響を受けるので、あまり正確な時間を刻むには注意が必要になります。シリアル通信など、データ送受信のタイミングが重要なアプリケーションでは、外部クリスタルなどを利用した方がよさそうです。