PIC12F629 TIMER1の研究

私のIOT

PIC12F629においてTIMER1はSLEEPからの復帰が可能なタイマーです。
TIMER0は8ビットでTIMER1は16ビットということもあり、タイマー割り込みはほとんどタイマー1を使います。
省電力化しようとするとTimer1について、しょっちゅうマニュアルを見るので日本語でまとめておきます。主にデータシート、Developer Helpとやって確認したことのまとめです。

TIMER1モジュールとゲートコントロール

PIC12F629/675は16ビットのタイマーをもってます。
以下がブロックダイアグラムです。(後閑大先生の本の引用)

いろんなサイトを見ても、ほとんど言及されていませんが32.768KHzのLPクリスタルを接続した時、TImer1はこのクリスタルを入力ソースにできます。
この時、本体は内部クロックINTOSCで動かすことになります。(だってクリスタルを別につなぐ場所がないから)
それぞれの発振は同期しないので、T1CONで非同期を指定します。

タイマー1の割り込みで本体にやりたいことをさせてすぐにSLEEPさせるのです。
Lチカくらいなら割り込みルーチン内でやればいいので、main.cでは

while(1){
   SLEEP();
   NOP();
}

でよくなります。
これがPICで作る、もっとも省電力なデバイスの制作方法だと思います。

Timer1には以下の機能があります。

  • 16ビットカウンター(TMR1H:TMR1L)
  • 読み書き可能
  • プリスケーラー
  • 内部クロックか外部クロックか選べる
  • 専用の32KHzオシレーター用回路
  • 同期か非同期で動作
  • 0xFFFF(65535)から0x0000に変わった時に割り込みが発生
  • SLEEPからの復帰はオーバーフロー時(非同期モードで)
  • 外部からのインプットでも可能
  • LPオシレーターでも可能

タイマー1コントロールレジスター(T1CON)でタイマー1をコントロールします

Timer1モジュールの動作

タイマー1は以下の3つのモードのどれかで動作します。

  • プリスケーラー付き16ビットタイマー
  • 16ビット同期カウンター
  • 16ビット非同期カウンター

タイマーの時はインストラクションサイクルごとに増えます。(クロックの1/4ということですね)
カウンターの時はタイマー1はT1CKIの外部クロック入力の立ち上がりエッジの時に増えます。
さらにカウンターモードのクロックはマイクロコントローラーのシステムクロックと同期することもできるし、非同期にすることもできます。

カウンターモードでもタイマーモードでもカウンター/タイマークロックはnT1Gインプットを選択できます。

もし外部クロックオシレーターが必要な場合(かつ、マイクロコントローラーがCLKOUTなしのINTOSCを使っている場合)タイマー1はLPオシレーターをクロックのソースとして使うことができます
NOTE: カウンターモードでは立ち下がりエッジが、最初の立ち上がりエッジより先に、カウンターにより検知されます。

タイマー1のクロックソース

  • タイマー1のクロックソースとは、タイマー1カウンターを増加する入力信号のことです。
    クロックのソースはT1CON内のTMR1CSビットで選びます。
    四つのソースがあります。
  • Fosc/4命令クロック
  • Foscシステムクロック
  • 外部クロック
  • 四番目(機種による)のクロック(LF内部オシレーターなど)

PIC12F629のような古いタイプはTMR1CS相当はT!CONの中のT1OSCENの1ビットです。

Fosc/4命令クロックソース(00)

命令クロック(Fosc/4)はTMR1CS<1:0> = 00)を選ぶと、Timer1は命令サイクルと同じオシレーターによりTMR1H:TMR1Lは立ち上がりエッジでインクリメントします。

Foscシステムクロックソース(01)

FoscのシステムオシレーターがTimer1のクロックソースとして使われます。(TMR1CS)
もし、TMR1H:TMR1Lの値を読むことがあるなら、Fosc/4のサイクルで読み取られるということです。

Fosc内部クロックが選ばれた場合、値は命令サイクルで更新されます。このため最下位の2ビットは精度がエラーである場合があります。

外部クロックソース(10)

外部クロックソースが選ばれた時(TMR1CS=10)タイマー1オシレーター可能ビット(T1OSCEN)が外部ソースとして選ばれます。タイマー1モジュールはI/Oピンか、外部の32KHzクロックオシレーターをm入力ソースとして使います。

T1CKI I/Oピン

カウンターを使いたい時T1OSCENビットでT1CKI入力をタイマー1のオシレーターとして使えます。
タイマー1はT1CK1入力の立ち上がりエッジでインクリメントされます。
これはマイクロコントローラーシステムクロックと同期することもできるし、非同期で動かすこともできます。

外部32KHzクロックオシレーター

タイマーをクロックオシレーターと別にしたい場合、T1OSCENビットはクリスタルオシレーターモードとします。このモードが選ばれるとTimer1は32.768KHzオシレーター回路(I/OピンのSOSCIとSOSCSO)を動かします。この内部サーキットには32.768KHzクロッククリスタルを接続します。タイマー1はリアルタイムクロックとして動作します。

タイマー1割り込み

タイマー1のレジスターペア(TMR1H:TMR1L)は0xFFFFを超えたら0x0000に戻ります。タイマー1が戻った時にタイマー1は割り込みフラグビット(PIR1<0>)をセットします。
割り込みを検知可能とするためには以下の3つのビットをセットします。

  • タイマー1割り込み可能ビット(PIE1<0>)
  • PEIEビット(INTCON<6>)
  • GIEビット(INTCON<7>)

割り込みサービスルーチンの中でTMR1IFはクリアーしてから次の割り込みを受け付けてください。

タイマー1 プリスケーラー

タイマー1は4つのプリスケーラー(その数で割る)1/1,1/2,1/4,1/8でクロック入力を分周することができます。T1CKPSビット(T1CON<5:4>)で設定します。プリスケーラーカウンター自身を読み書きすることはできません。しかしTMR1HかTMR1Lに書き込みを行うとクリアされます。

T1CONレジスター

 

ビット7:いつも0

ビット6: TMR1GE: ゲート使用ビット 使わないなら0

ビット5-4: T1CKPS:T1CKPS0 プリスケーラー設定ビット

11 = 1/8プリスケーラー
10 = 1/4プリスケーラー
01 = 1/2プリスケーラー
00 = 1/1プリスケーラー

ビット3: T1OSCEN: LPオシレーター入力ビット
もしCLKOUTなしのINTOSCをアクティブにしているならば、1ではTimer1クロックとする、0ではオシレーターオフ(TMR1CSと関係する)
もしINTOSCでないならば関係ない

ビット2:nT1SYNC: タイマー1外部クロック入力と同期とるか 1=とらない 0=とる

ビット1: TMR1CS:タイマー1クロックのソース選択ビット 1:外部クロック 0:通常のFoSC/4

ビット0: タイマー1オン 1=オン、0=ストップ

非同期カウンターモードのタイマー1

外部クロックモードnT1SYNC(T1CON<2>)を1にすると同期は取られません。0にするとFosc/4と同期されます。タイマーはインターナルフェーズクロックとは同期せずにインクリメントされます。
非同期のタイマーはSLEEP中であっても動作し続け、オーバーフロー割り込みを発生します。それはプロセッサーを復帰させます。しかしタイマーの読み書きには注意が必要です。

タイマーが外部非同期のクロックで動いている時に、TMR1HやTMR1Lを読むと正しい読み方を意識しないといけません。ユーザーは16ビットタイマーが2つの8ビット値からなり、読んでいる最中にオーバーフローすると問題が起きます。

書き込み時にはユーザーは単純にタイマーを止めてのぞみの値を書き込むべきです。タイマーが値を増加している時には整合性が取れないことがあります。

タイマー1のオシレーター

OSC1, OSC2端子にはクリスタルオシレーター回路が設定されていて、T1OSCEN(T1CON<3>)をセットすれば使用可能となります。オシレーターは37KHz程度の弱いパワーのオシレーターが想定されています。表9-2にはタイマー1オシレーターの適切なコンデンサーが示されています。(まったく動きません。37KHzクリスタルの場合、15PF程度でないと動作しません!)

タイマー1オシレーターはシステムのLPオシレーターを共用することができます。内部オシレーターをシステムクロックとしている場合です。システムのLPオシレーターと共用する場合、ユーザーはオシレーターがきちんとスタートアップするまでソフトウェアで時間を与える必要があります。(数秒かかります。作るものによっては無反応としたほうがいいでしょう。)

TRISIO4とTRISIO5をえらんでいる場合、GP4とGP5がセットされるため、読み取っても0で、TRISIO4,TRISIO5は1(入力)となります。

SLEEP中のタイマー1

非同期カウンターモードにセットした時、TIMER1はSLEEP中でも動作します。このモードの時、外部クリスタルかクロックはカウンターのソースとして使われます。タイマーにより復帰させるには

  • タイマー1はオン(T1CON<0>)をセット
  • TTMR1IEビット(PIE1<0>)をセット
  • PEIEビット(INTCON<6>)をセット

オーバーフローが起きるとデバイスは復帰します。GIEビット(INTCON<7>)がセットされていたらデバイスは動作し始め。オーバーフローした時に割り込みサービスルーチンへジャンプします。

関連レジスタ

サンプルコード



// PIC12F629 Configuration Bit Settings

// 'C' source line config statements

// CONFIG
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-Up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = ON          // Code Protection bit (Program Memory code protection is enabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include 

#define _XTAL_FREQ 4000000
// __delay_ms()
#define SW R3  // pin 4
#define GRN_LED GP2  // pin5 
#define RED_LED GP1  // pin6

#define HIVALUE 0x00
#define LOWVALUE 0x01

static void setup(void){
    CMCON = 0x07; // do not use comparator
    TRISIO = 0b00010000; // all output except GP3 always in
    T1CON =  0b00111000; // 7th. bit timer on/off TMR1ON
    INTCONbits.PEIE = 1; // timer 1 intterupt
    INTCONbits.GIE = 1;
    PIE1bits.TMR1IE = 1; 
    PIR1bits.TMR1IF = 0; // reset interruption flag
    GPIO = 0; // once reset
    TMR1H = HIVALUE;  // 5 sec
    TMR1L = LOWVALUE; 

}

void __interrupt() ISR(void){
    if (PIR1bits.TMR1IF){
        T1CONbits.TMR1ON = 0; // stop timer
        PIR1bits.TMR1IF = 0;
        // 
        GRN_LED = ~GRN_LED;
        TMR1H = HIVALUE;  // 5 sec
        TMR1L = LOWVALUE; 
        T1CONbits.TMR1ON = 1; // start timer
    }
}

void main(void){
    setup();
    T1CONbits.TMR1ON =1;
    
    while(1){
    }   
}

コメント