トップページ > dsPIC入門 > タイマーについて

タイマーについて

はじめに

最初に作成したテストプログラムは、for文で時間稼ぎをしていて 残念な感じでした(汗)次はちゃんと時間を測って動作するような、実用的なものをタイマーを使ってで実現します。 まずは、いろいろ細かい設定は置いといて(またか)てっとり早くタイマーの使い方を覚えます。

タイマー割り込み

dsPICの内部モジュール“タイマー”は、独立してカウンターの値をインクリメント(+1する)していきます。 その値が設定値に達すると、コンパイラ側で決められた名前を持つ関数(内容は自分で記述する)が呼び出されます。 その際、それまで行っていた処理は中断されます。これが「タイマー割り込み」。割り込み関数からreturnすると また元の処理に戻ります。

いきなりソースコード

例の「LEDナイトライダー」のプログラムをタイマー割り込みを使ったものに書き換えてみます。 前のプログラムでは「メイン関数内でLEDをシフト→時間稼ぎ」という感じでした。 今回は、最初にメイン関数内で初期設定を済ませて、後は何もしません。 設定した時間が経過するとタイマー割り込みが発生するので、その関数内でLEDのシフトを行うようにします。 …すると、一定時間ごとにLEDがシフトしているように見えます。割り込み処理の中に、再度割り込みが発生 できるように記述することで、繰り返し割り込みが発生できるようにします。

実際のところ、あのプログラムではタイマーのありがたみを感じることは無いとおもいます。 ただ、今後赤外線通信や音声のサンプリングなんかを実装する際には非常に重宝するので、簡単な例で 使い方を確認しよう…ということです。


//********************************************************************
//LEDナイトライダー(タイマーで) @dsPIC30F4012
//********************************************************************


//********************************************************************
//ヘッダファイル
#include "p30f4012.h"
#include "timer.h"



//********************************************************************
//コンフィグ(変更なし)

_FOSC(XT_PLL8 & CSW_FSCM_OFF);
_FWDT(WDT_OFF);
_FBORPOR(PBOR_ON & BORV_20 & PWRT_64 & MCLR_EN);
_FGS(CODE_PROT_OFF);


//********************************************************************
//グローバル変数

int flag = 0;
int cnt=0;
char a = 0x01;


//********************************************************************
//タイマー割り込み関数

void _ISR _T1Interrupt(void)
{
 //タイマー割り込みフラグをOFFにしておく。(同時にカウンタもリセットされる)
 IFS0bits.T1IF = 0;


 //===================================================================
 //PORTBで6回シフト
 if(flag==0)
 {
  //PORTEは消しとく
  PORTE=0;

  //表示して、シフト
  PORTB = a;
  a = a << 1;

  //シフト回数をカウント
  cnt++;
  
  //もしPORTBの左ハジまでいったら…
  if(cnt==6)
  {
   cnt=0;	  
   a=0x01;

   //flagの値を変えて、次に呼ばれた時は別な処理を実行する
   flag=1;
   return;
  }
  
  return;
 }
 
 //===================================================================
 //PORTEの0ビット点灯
 if(flag==1)
 {
  PORTB=0;
  PORTE = 0x01;

  //flagの値を変えて、次の処理へ
  flag=2;
  return;
 }

 //===================================================================
 //PORTEの1ビット点灯
  
 if(flag==2)
 {
  PORTE=0x02;

  //flagの処理を変えて、次の処理へ
  flag=0;
  return;
 }
}



//********************************************************************
//メイン関数

int main(void)
{
 //===================================================================
 //入出力ポートの設定
 TRISB = 0x00;
 TRISE = 0x00;
 PORTB = 0x00;
 PORTE = 0x00;	


 //===================================================================
 //タイマーの設定&タイマー割り込みを許可
 OpenTimer1(T1_ON & T1_GATE_OFF & T1_PS_1_64 & T1_SYNC_EXT_OFF & T1_SOURCE_INT,10000);
 ConfigIntTimer1(T1_INT_PRIOR_5 & T1_INT_ON);



 //===================================================================
 //今回は、本処理で何もしない
 while(1)
 {
 
 }
}


//********************************************************************



…それでは、新しく追加された事項を見ていくことにします。ソースコードの上の行から順番にいきます。

ヘッダファイル

タイマーを使うためには、

#include "timer.h"

の文を追加してタイマーライブラリのヘッダファイルをインクルードする必要があります。

割り込み関数の書式

void _ISR _T1Interrupt(void) { 処理内容 }

…という感じで記述します。導入で使った30F4012には計5個のタイマーモジュールが載っています。 今回は“タイマー1”だけを使用していますが、基本的なタイマーとしての使い方であれば、他のタイマーも同様です。 注意点は、以下のとおりです。

関数名の“_ISR”はコンパイラに登録されているマクロで“Interrupt Service Routine”の略。汎用の割り込み関数の頭にはコレが付きます。 “_T1Interrupt”は、そのまんまの意味です。「タイマー1の割り込み」です。この2つの修飾辞(?)の間には半角スペースが入ります。注意。

割り込み制御レジスタ

割り込みが発生すると、dsPIC内で割り込みに関して使用されるレジスタの値が書き変わります。「割り込み制御レジスタ」と呼ばれる ものですが、このレジスタをプログラム中で自由に書き換えることで、割り込み動作を制御することができます。 今回のプログラムでは…

IFS0bits.T1IF = 0;

という構文で割り込みフラグをクリアする際にアクセスしています。
割り込みが発生すると、”IFS(Interrupt Flag Status)”レジスタが書きかえられます。 割り込みフラグが立つ…とか言うやつです。IFSレジスタは実際にはIFS0〜IFS2まで3つあり、各ビットが割り込みを発生する内蔵モジュールへ 割り当てられています。今回使用しているタイマー1のフラグは「IFS0」の中の1ビットです。T1IFビット(Timer 1 Interrupt Flag-bit)というそのまんま の名前です。

この割り込み制御レジスタの値は、割り込み関数が終了した後でも値が保存され続けてしまいます。よって、割り込み処理内でクリアしてあげないと 割り込み関数を抜けた直後にまた割り込み関数が呼ばれて永久ループになってしまいます。 割り込み処理の頭にフラグクリア…というのは常套句のようです。



デバイスファイルにて、このレジスタが共用体として登録されているようなので、“.T1IF”で深く考えずにタイマー1用のビットへアクセスできます。 アセンブラではないので、何ビット目かまで覚えておく必要はありません…。(アセンブラでもマクロ登録すればいいのか…というのは、私がPICアセンブラから 離れた後で知ったことです 汗)…一度Cになれると、なかなかアセンブラに戻れませんね。

通常C言語では“レジスタ”を意識することなくプログラムを記述することができますが、C30コンパイラでは用意されているマクロを使って 制御レジスタへ直接ビット単位でアクセスすることもできます。今回は「割り込みフラグレジスタの特定のビットだけをクリアしたい」というかなり ピンポイントな話なので、この手の記述ができると便利です。

割り込み関数の中身

その場でテキトーに書いたので、洗練されていないのは目をつぶってください。まず、グローバル変数使うな…というのは自分でも気になっています。 割り込み関数内でstaticとかつけて宣言すればいいのでしょうけど。あと、PORTBが6bitしかないというのがなんともアレです。 PORTEの2ビットを巻き込んでいるので、どうしてもゴチャゴチャしますね。もはや、1Byteが1単位だ!っていうのは古いのでしょうか?組み込み系だし…。 dsPICでデータバス相手の処理ってのも、ヘンな感じするし…。ブツブツ…。

タイマーの初期設定

割り込み関数の書式はスッキリでしたが、初期設定は結構ゴチャゴチャします。機能が多くなった分だけしょうがないのかもしれませんが…。 とりあえず初期設定としては、タイマーそのものの動作モードの設定・タイマーの割り込みレベルの設定・タイマー割り込みの許可を考えます。


OpenTimer1(unsigned int config,unsigned int period)

名前のとおり、タイマーを動作開始させる関数です。タイマ用ライブラリ内に含まれてます。 便利ですねー。もしタイマー2を使うなら、「OpenTimer2()」とかになります。
引数は2つあります。1つ目はconfig…コンフィグレーションについてのところでやったような、 マクロを&でつないで記述するアレが引数としてズラーっと並びます。今回関係あるパラメータのみ書き出すと、

タイマのON・OFF


ゲートタイマーモードのON・OFF(外部信号−IC1ピンとかIC2ピンとか−がHighである時間幅の測定に用いる)
プリスケーラ(分周器の設定)
クロック同期設定(これは今回使用するタイマー1のみ有効)
クロック源
すべて、タイマー1の場合です。ほかの番号のタイマーを使うなら、「T2_…」「T3_…」等になります。


2つ目の引数は、カウンタの上限値の設定です。つまり、カウンタがインクリメントされていってこの値に達すると 割り込みが発生する…というタイマー時間の設定のようなものです。今回は10000です。テキトーです。すいません。

もう一度、今回のプログラムの設定を見てみます。

OpenTimer1(T1_ON & T1_GATE_OFF & T1_PS_1_64 & T1_SYNC_EXT_OFF & T1_SOURCE_INT,10000);

なんとなく、やってる事が理解できたような気がします。

ConfigIntTimer1(unsigned int config)

これはタイマーの割り込みに関する設定をする関数です。また引数がconfigになっています。またマクロを羅列します。煩雑ですいません。


割り込みレベルを設定 割り込み許可
もう一度、今回のプログラムの設定を見てみます。

ConfigIntTimer1(T1_INT_PRIOR_5 & T1_INT_ON);

コンパイルする際の注意

MPLABでprojectへライブラリーファイルを追加しないと、コンパイルエラーとなります。使用するデバイスと同じ名前のライブラリファイル (拡張子は“.a”)を入れます。ディレクトリはC:/Program Files/Microchip/MPLAB C30/libとかです。今回は30F4012を使用しているので、 “libp30F4012-coff.a”を選択します。




dsPIC入門へ