PIC24Fタイマーの使い方(MCCを使わない方法)

関連投稿
PIC24FJ64GB002のTimer

関連する投稿と被ってけど、MCC(Microchip Code Configurator)を使わないで、直接レジスタ操作して使う方法に関する備忘録を残しておく。下記はPIC24FJ64GB002の使用を前提としている。ほかのPIC24F系でもほとんと同じではないかと。

PIC24F タイマー(日本語)

PIC24FJ64GB002のタイマー

pic24f_timer001

  • このPICでは、16bitタイマーモジュールが合計5個(Timer1~5)内蔵されており、それぞれ独立してカウントすることができる。基本的な使い方はどれも同じ。
  • Timer1だけTSYNCレジスタがあり、TCS=1(外部クロックをカウント源とする)のときだけ、同期させる機能がある。(これの有効な使い方は不明)
  • Timer2と3、Timer4と5のカウンターが隣り合っており、くっつけて32bitタイマーとしても利用できる。(その場合Timer3やTimer5は独立して使えない)

タイマーの初期設定

TxCONが挙動を決めるレジスタになっている。

タイマーの周期について

FOSC/2の1クロックで1カウントする。32MHzで動作させると、62.5ns周期で1カウントすることになる。

16bitタイマーの場合、65535までしかカウントできないで、62.5ns * 65535 = 4.095msとなる。このままだと約4ms周期までのタイマーとしてしか機能しない。これでは使えないケースが出てくるので、プリスケーラというクロックを分周させる機能がある。簡単にいうとカウンタを係数倍してたくさんカウントできるようにする。1:256が最大だがこれを指定すると周期を256倍することができる。4.095ms * 256 = 1048ms = 1.048s約1秒ぐらいまでの周期を指定できる。

1秒以上の周期を選択したい場合は、動作クロックを遅くする(1カウントの周期が遅くなるので)か、32bitタイマーを使うしかない。

周期が決まったら、動作クロックを基にカウント数を計算して、PRxに代入する。タイマーのカウントが始まるとTMRxをカウントアップしていってPRxに達するとタイマーが発動(フラグがON)する仕組みになっている。なおPRxの値は、PICが稼働している間でも変更できる。

以下の例は、0.25sec周期で動作するTimer1の初期設定になる。(割り込みなし)

// とりあえずT1CONレジスタをクリア
T1CON = 0;

// プリスケーラを1:256に設定する(00 1:1, 01 1:8, 10 1:64, 11 1:256)
T1CONbits.TCKPS0 = 1;
T1CONbits.TCKPS1 = 1;

// 動作クロック32MHz、0.25s周期のときのカウンタを計算する
// FOSC/2 = 16MHz, Period = 0.25s
// 0.25 / ((1/16000000) * 256) = 15625
PR1 = 15625;

// カウンタを初期化
TMR1 = 0;

// Timer1 割り込み設定オフ
IEC0bits.T1IE = 0;

// Timer1開始
T1CONbits.TON = 1;

割り込みなしのときのタイマーの確認方法

タイマーが発動したかどうかを利用する側に伝える方法は2つある。1つは自分でフラグを参照する方法と、割り込みを発生させて、割り込み関数をコールさせて認識する。なので割り込みさせるかどうかを初期設定時に設定する。割り込みのON/OFFは、PIC稼働中に変更できる。

割り込みなしの場合、設定したカウント数にカウンタが達しているか確認する必要がある。達した場合、割り込みなしの場合でも、Timer1の割り込みフラグ(IFS0bits.T1IF)がONになるので、これを確認すればよい。

注意点としては、割り込みフラグ'T1IF'は、内部モジュールがONしてくれるが、自動的にOFFされない。自分でフラグをクリアしないと、タイマーONしっぱなしになるので、検知したらクリアする必要がある。

int main(void){
  :
  while(1){
    if( IFS0bits.T1IF == 1 ){
      // フラグをクリアする
      IFS0bits.T1IF = 0;
      // タイマーカウントが規定数に達したときの処理
    }
  }
  return 1;
}

カウンタTMR1について

周期ごとにTMR1の値が+1されていって、PR1と内部で常に比較されている。MPLABX上でデバッグしても、プログラムのカウンタと違って+1されて増えている様は見れなかった。また、T1IFがONしたときにTMR1はゼロクリアされているようなので、後始末する必要はなさそう。勝手にクリアされているようなので、T1IFを使わずに

if( TMR1 >= PR1 ){
  // タイマーON
  :
}

という判定処理はしないほうが良さそう。(勝手にクリアされて、評価タイミングではif文の中に入らないタイミングが発生しそう。

割り込み処理を使う場合

割り込み処理内でタイマー処理を行いたい場合は、IEC0bits.T1IEをONして、割り込み関数を定義&実装すればよい。割り込み関数名は、使用するPICごとに決まっている。PIC24FJ64GB002の場合は、_TxInterrupt()(xはタイマーの番号)を実装すると、コールされるようになる。

void __attribute__ ( ( interrupt, no_auto_psv ) ) _T1Interrupt()
{
    IFS0bits.T1IF = 0;
    // タイマーカウントが規定数に達したときの処理
}

int main(void){
  :
  // 初期処理
  :
  // Timer1 割り込みを有効にする
  IEC0bits.T1IE = 1;
  :
  while(1){
  }
  return 1;
}
-->