SVX日記
2005-06-21(Tue) PICのTimer0を理解する
今日は、残業でキーボードをバコバコと叩きまくっていて帰りが遅くなってしまった……のだが、別に家に帰ったトコロでキーボードをバコバコと叩きまくっているオイラなのであった。もし手元からPCが無くなったりしたら、窒息死するに違いない。これは、保障つきで間違いない。
で、おもむろに昨日のタイマ割り込みの間隔が短すぎる問題の解消を図りだすのである。実はオイラ、タイマ割り込みを利用するのは初めてではないのだが、やっぱりちゃんと勉強しなおしておいた方がよいだろうというコトで、PICのデータシートの一部を翻訳してみるコトにした。ちゅーワケで、PIC12F629/675のデータシート、4章Timer0モジュールの訳をどうぞ。
あ、そうそう、例によって各文末の句点などにマウスの乗せると原文がポップアップするようにしてある。このヘッポコ訳ッ!! と思ったら、原文を確認すれッ!!
4.0 TIMER0モジュール_
Timer0タイマ/カウンタモジュールには、以下の特徴があります:
・8ビットのタイマ/カウンタ_
・読み書き可能_
・ソフトウェアで選択可能な8ビットのプリスケーラ(分周器)_
・内部/外部クロックを選択可能_
・オーバフロー(FFh→00h)発生時に割り込みを発生可能_
・外部クロックのエッジ(立ち上がり/立ち下がり)を選択可能_
図4-1はTimer0モジュールとプリスケーラ(WDTと共用)の模式図です。
4.1 Timer0の操作方法_
タイマモードは、T0CSビット(OPTION_REG<5>)のクリアにより選択されます。このモードでは、インストラクションサイクル毎(1命令実行毎)にTimer0がカウントアップされます(プリスケーラ非使用の場合)。TMR0に書き込みがなされた場合、続く2インストラクションサイクルの間、TMR0はカウントアップされません。ユーザはこれを考慮の上、TMR0レジスタに値をセットすることで、意図する動きを実現可能です。
カウンタモードは、T0CSビット(OPTION_REG<5>)のセットにより選択されます。このモードでは、ピンGP2/T0CKIの変化のエッジ(立ち上がり/立ち下がり)でTimer0がカウントアップされます。カウントアップするエッジの方向は、ソースエッジ(T0SE)の選択ビット(OPTION_REG<4>)で指定可能で、T0SEビットがクリアに設定してあれば、立ち上がりでカウントアップされます。
4.2 Timer0割り込み_
TMR0タイマ/カウンタレジスタがオーバフロー(FFh→00h)すると、Timer0割り込みが発生します。このオーバフローによりT0IFビットがセットされます。T0IEビット(INTCON<5>)をクリアすることで、この割り込みを禁止することもできます。次の割り込みを有効にするためには、Timer0割り込みルーチンの中でT0IFビット(INTCON<2>)をソフトウェア的にクリアする必要があります。タイマがSLEEP中にカウントアップすることはないので、Timer0の割り込みにより、CPUをSLEEPから復帰させることはできません。
4.3 Timer0を外部クロックで使用する_
プリスケーラを利用しない場合、外部クロック入力はプリスケーラの出力と同じになります。内部クロックとT0CKIの同期は、内部クロックのQ2とQ4サイクルにプリスケーラ出力をサンプリングすることによって、実行されます。よって、T0CKIをHIGHと認識するには最低2TOSC(+20nsのRC遅れ)、LOWと認識するには最低2TOSC(+20nsのRC遅れ)が必要です。外部装置の電気的特性を確認してください。
注意:入出力ピンをデジタル入力モードに設定したい場合、ANSEL(9Fh)およびCMCON(19h)レジスタを初期化する必要があります。アナログ入力モードに設定されていると、常に'0'が返ります。なお、ANSELレジスタはPIC12F675用です。
4.4 プリスケーラ(分周器)_
この8ビットのカウンタは、Timer0モジュールのためのプリスケーラとして、またはウォッチドッグタイマのためのポストスケーラとして利用可能です(……が、以下このカウンタを一概にプリスケーラと呼ぶことにします)。プリスケーラの設定はソフトウェア的にコントロールビットPSA(OPTION_REG<3>)で行えます。PSAビットをクリアに設定すると、プリスケーラはTimer0に割り当てられるます。プリスケーラ値はPS2:PS0ビット(OPTION_REG<2:0>)により設定可能です。
プリスケーラは読み書き不可能です。プリスケーラがTimer0に割り当てられている場合、TMR0レジスタへの書き込み命令(CLRF 1、MOVWF 1、BSF 1などなど)実行の都度、プリスケーラはクリアされます。プリスケーラがWDTに割り当てられている場合、CLRWDT命令実行の際、ウォッチドッグタイマのクリアと同時にプリスケーラもクリアされます。
4.4.1 プリスケーラの設定切り替え_
プリスケーラの設定はすべてソフトウェア的に行います(つまり、プログラム実行中にホイッと変えられます)。Timer0からWDTへプリスケーラの割り当てを変える場合、以下の命令順序(例 4-1)で実行すれば、意図しないデバイスリセットを避けられます。
逆に、WDTからTimer0へプリスケーラの割り当てを変える場合、以下の命令順序(例 4-2)を使用します。例えWDTが無効にしてあっても、この方法を利用すべきです。
でだ、訳すウチに気づいてしまったのだ。それは「次の割り込みを有効にするためには、Timer0割り込みルーチンの中でT0IFビット(INTCON<2>)をソフトウェア的にクリアする必要があります」というトコロ。今回、気づかないうちに、そのコードをコメントアウトしていたんだよね。
通常、割り込みルーチンには、割り込み処理をそのまま記述するのでなく、起こった割り込みの種類をイベントフラグに記録する処理だけを記述する。そんでもって、割り込み処理自体はメインルーチンの中で行うようにするのが常套手段である。なぜなら、割り込み処理中に再び割り込みイベントが発生すると、二重に割り込みが発生することはないので、後者のイベントを取りこぼしてしまうからである。そーゆーコトではどーしよーもないので、極力取りこぼしたくないならば、極力割り込み処理自体を短時間で完了する必要があるのだ。ところが今回、割り込み処理のテストとして行ったLEDの点滅処理は非常に軽いので、割り込みルーチンの中に直接書いてしまったのが災いした。LEDの点滅処理を追加する際に、元から記述してあったT0IFビットをクリアする処理をコメントアウトしてしまったのである。これが昨日の失敗の理由だ。理解したので、サクッと修正する。