PIC16F87xのタイマ1の使い方

【概要】

PIC16F877のタイマ1の使い方の説明です。32.768kHzの水晶振動子を
使った外部発振回路モードでの使い方を例に、CCS社のCコンパイラで
動かす方法です。
CCS社Cコンパイラのヘッダーファイルに誤りがあり、割込みが入らない
のを修正しました。

【タイマ1の機能】

 タイマ1は、ちょっと高機能なミッドレンジシリーズのPICに内蔵されている
機能で、タイマ0と同様の機能を持っていますが、大きな特徴はカウンタが
16ビットカウンタとなっているところです。
 また、タイマ1は単独の使い方以外に、キャプチャ機能やコンパレータ機能
と組合わせることで、時間測定や、プログラムに影響されない一定のインター
バルの繰り返しなど、さらに高機能な使い方が出来ます。

実際のタイマ1の内部構成は下図のようになっています。
このタイマ1の動作を決めるのは、タイマ1専用の制御用レジスタである
T1CONレジスタで行います。
 タイマ1の動作は、入力は外部入力、内部クロック、専用発振回路の3つの
内のいずれかを切替えて使います。
内部クロックの時はクロックの1/4の周波数となっています。
そしてその後に専用の3ビットのプリスケーラを通したあと、内部クロックとの
同期回路を通して、16ビットのTMR1レジスタでカウントします。
TMR1レジスタは8ビットずつのTMR1HとTMR1Lの2つのレジスタとなっていて、
プログラムで読み書きが自由に出来ます。TMR1もカウントがオーバーフロー
したときTMR1IFフラグがOnとなって割込みを発生します。





【レジスタの詳細内容】

 タイマ1の内部の設定切替はT1CONレジスタに設定することで可能となります
が、このT1CONレジスタの詳細は下図のようになっています。




タイマ1の動作としては、タイマ0と同じ内部クロックか、専用発振回路による
タイマモードと、外部入力によるカウンタモードの3種類があります。
その指定方法はT1CONレジスタの中で下表のようにして行います。

入力種別

T1OSCEN

TMR1CS

^T1SYNC

内部クロック

専用発振回路

外部入力(非同期)

外部入力(同期)



@ 外部入力カウンタモード
  カウンタモードの時には、内部クロックに同期をとることが出来るようになって
  おり、この同期をとっておくと、TMR1の読み出し最中にカウンタがカウント
  アップ動作をすることを避けられるため正確な値を読み出すことが可能となり
  ます。
  普通は同期させるようにしますが、逆に、クロックより高い周波数の時には
  同期が正確には取れなくなりますから、そのような場合には同期をしないよう
  にすれば、外部入力信号がそのままカウンタの入力となります。

  タイマ1は16ビットカウンタであるため、カウントさせながらカウンタ値を読み
  込むのには、TMR1HとTMR1Lを2回に分けて読み込む動作が必要となります。
  こうすると問題が起きることがあります。つまり読み込み中にカウントアップ動作
  をしてしまうことが有りうるからです。
  これを避けるため次のような手順を踏んで読み込む必要があります。

   まず上位のTMR1Hを読み込みレジスタに保存します。
   次に下位のTMR1Lを読み込んで保存します。
   そしてもう一度上位を読み込んで前のと同じかどうかを確認します。
   同じなら正常で完了ですが、異なっていた時には、再度読み込みをしなお
   します。

A 内部クロックモード
  タイマ1はプリスケーラも合わせると全部で19ビットのカウンタとなりますから、
  内部クロックが20MHzの時でも、最長約105msecという長いインターバルタイマ
  を作ることが出来ます。

B 専用発振回路モード
  タイマ1についている専用発振回路は、システムクロック発振回路のLPモードと
  同じ回路になっていて、最高200kHzまでのクリスタル発振回路用となっています。
  これは、時計用の32kHzのクリスタル発振回路を想定したもので、実際の例として、
  32.768kHzのクリスタルを使った時の、タイマ1にセットする値とオーバーフロー
  割込み発生までの時間を一覧表にすると下表のようになり、扱いやすい値の
  インターバルが出来るようになっています。
  但し、この時のプリスケーラの値は1で、TMR1Lは0のままです。

TMR1の値

オーバーフローまでの時間

8000H

1sec

C000H

0.5sec

E000H

0.25sec

F000H

0.125sec


【C言語による使い方】

CCS Cコンパイラでタイマ1を使うためには、専用の組込み関数を使います。
この関数を使うことで、割込みも容易に扱うことが出来ます。タイマ1用の組込み関数
としては下表が用意されています

組込み関数書式

内   容

SETUP_TIMER_1(mode) タイマー1の初期設定を行う。
複数の設定はORで行う。

modeの値は下記を使用する。
 T1_DISABLED タイマ1を使用しない
 T1_INTERNAL 内部クロックモード指定
 T1_EXTERNAL 外部入力、非同期モード指定
 T1_EXTERNAL_SYNC 外部入力同期モード指定
 T1_CLK_OUT 内蔵発振回路を使う指定
 T1_DIV_BY_1 プリスケール値1
 T1_DIV_BY_2  〃     2
 T1_DIV?BY_4  〃     4
 T1_DIV_BY_8  〃     8

例 setup_timer_1(T1_DISABLED);
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
  setup_timer_1(T1_INTERNAL | T1_DIV_BY_8)
GET_TIMER1( ) 現在のTMR1の内容を返す。
16ビットなのでlong変数で扱う必要がある。

例 while (get_timer1( ) != 0) 
SET_TIMER1(value) TMR1レジスタにvalueの値をセットする

例 if(get_timer1( )=1000)
set_timer1(0);

【ヘッダファイルの修正】

CCS社CコンパイラのPIC16F87x用のヘッダファイルに誤りがあり、周辺割込みの
許可がされず、タイマ1の割込みが受け付けられません。
そこで下記部分を修正します。

≪オリジナル≫(間違っている)
#undef GLOBAL
#define GLOBAL      0x0B80  // Used for ENABLE/DISABLE INTERRUPTS
#define INT_EEPROM   0x0B40  // Used for ENABLE/DISABLE INTERRUPTS

≪修正後≫
(2行削除)
#define INT_EEPROM   0x8D10  // Used for ENABLE/DISABLE INTERRUPTS


【プログラム例】

実際にタイマ1を使ったプログラム例を紹介します。
下記はタイマ1を外部発振回路で動作させ、1秒毎に発光ダイオードを点滅させる
プログラムです。
mainの中で同時にプログラムタイマのdelay_ms関数を使って、やはり1秒周期で
別の発光ダイオードの点滅もしています。

この時の回路図は下図のようになっています。





≪プログラムリスト≫ 下記には漢字スペースを含んでいます。
 
//////////////////////////////////////////////
// Timer1 of 16F877 test program.
// Use 32.768kHz crystal and set 1sec period.
// Each interval interrupt,LED is controled.
//////////////////////////////////////////////
#include <16f877.h>
#use delay(CLOCK=10000000)     //10MHz

#int_timer1
intval() {
 int flag1;
 set_timer1(0x8000);       //1sec
 if(flag1 == 0) {
   output_high(PIN_B7);     //LED off
   flag1 = 1;
 }
 else {
   output_low(PIN_B7);     //LED on
   flag1 = 0;
 }
}

main() {
 int flag;
 set_tris_b(0);         //all output
 output_high(PIN_B7);      //LED off

 setup_timer_1(T1_EXTERNAL_SYNC | T1_CLK_OUT | T1_DIV_BY_1);
 set_timer1(0x8000);      //initial set
 enable_interrupts(INT_TIMER1);
 enable_interrupts(GLOBAL);

 while(1) {
   if(flag == 0) {       //key input
    output_high(PIN_B6);   //LED2 on
    flag = 1;
   }
   else {
    output_low(PIN_B6);    //LED2 off
    flag = 0;
   }
   delay_ms(1000);
 }
}



  目次ページに戻る