PIC ことはじめ ~ タイマ割り込み

Posted by
ぴろり
Posted at
2011/12/28 17:54
Trackbacks
関連記事 (0)
Post Comment
コメントできます
Category
電子工作 カテゴリ
カバーイメージ
  • timer
  • Photo by Andy Price
    • Attribution
    • ShareAlike

 PIC のタイマ割り込みの習作。LED フラッシャーでは、メインループ内でディレイを実施していましたが、こちらはディレイを明示せず、タイマによる割り込みで所望の時間間隔を得る方法です。

このエントリーをはてなブックマークに追加  

概要

  • PIC16F819 を用いてタイマ割り込みの方法を習得する
  • デジタル出力ポートに接続した LED を 1 秒間隔で点滅させる

ソースコード

#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++;                  // オーバーフローカウンタをインクリ
    }
}

説明

__CONFIG
 PIC12F675 の時と比べると、項目が増えていたり、定数名が変更になっていたりする。このあたりは、データシートをじっくり読むか、デバイス毎のヘッダ ファイルを直接参照して確認するしかないみたいだ。
static void interrupt isr (void)
 割り込みが発生した時に呼ばれる関数で、関数名は特に何でもよいらしい。interrupt が指定されることで、関数が割り込みベクタに配置されるようになるっぽい。割り込みを利用するプログラム中に一つだけ存在すること。タイマ以外にも様々な割り込みがあるけれども、あらゆる割り込みはこの関数に飛び込んでくる。
 割り込みごとに関数を作成して、割り込みイベントと関数ポインタを関連付けるのかなぁ? とかの先入観があったので、最初何をしているのかわからなかった(苦笑)
if (INTCONbits.TMR0IF)
 割り込みが発生した際に、何が原因で割り込みが発生したのか、個々に割り込みフラグを判断して処理を行うことになる。Timer0 による割り込みが発生した場合には、INTCONbits.TMR0IF が立つことでこれを判別できる。
INTCONbits.TMR0IF = 0;
 割り込みフラグをクリアするのはプログラムの仕事なので忘れないようにする。

時間の指定方法

 このプログラムを走らせると、RA0 に接続された LED が約 1 秒間隔、正確には 1.024 秒間隔でオン/オフを繰り返します*1。この動作時間を求めるには以下の段階を経ています。

  1. 内蔵クロックの動作周波数(Fosc)は 4MHz (25行目)
  2. Timer0 は 4 動作クロック(CLKO=Fosc/4)で 1 カウントアップされる
  3. プリスケーラの設定比は 1:32 (25行目)
  4. Timer0 は 160 カウントするとオーバーフローして割り込みを発生する(33行目、58行目)
  5. Timer0 が 200 回オーバーフローしたら、出力を反転する(44行目)

1 秒 ÷ 4MHz × 4 クロック × プリスケーラ 32 倍 × 160 タイマーカウント × 200 オーバーフローカウント = 1.024 秒

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

このエントリーをはてなブックマークに追加  

  1. *1 内蔵クロックの誤差が無いと仮定した場合

この記事を読んだ人はこんな記事も読んでいます記事リコメンデーションについて

カバー画像:PIC ことはじめ ~ 外部割り込み

関連記事/トラックバック

関連記事/トラックバックはまだありません

この記事にトラックバックを送るには?

コメントを投稿する

 
 (必須, 匿名可, 公開, トリップが使えます)
 (必須, 匿名可, 非公開, Gravatar に対応しています)
 (必須)
スパム コメント防止のため「投稿確認」欄に ランダムな数字 CAPTCHAについて を入力してから送信してください。お手数ですがご協力のほど宜しくお願いいたします。