クモの時計コレクション
STATUS: 故障中x2
USBSPYDER08のCPU、MC9S08QG4(コア:HCS08)には2種類のタイマーがあります。
単純なタイマーの「モジュロ・タイマ(S08MTIMV1)」、高精度なクロック、PWM制御、外部ピンへの出力機能などをもつ高機能な「タイマ/ パルス幅モジュレータ(S08TPMV2)」。
とりあえずこの二つをタイマー(割り込み発生)として使うことが出来るようになりました。
それぞれのタイマーは、どちらも大元のクロック源を分周して使います。
クロック源としては、バスクロック(CPUのクロックのもと)や外部に接続したクロックなどを選べるのですが、回路を増やさなくてよいという意味ではバスクロックを使うのが一番お手軽です。
でも、バスクロックは8MHz(ですよね?※)もあるので、そのままではでは早すぎます。1秒間に800万回も割り込みが起きても困るというか処理しきれないし。
そこで、何分の1かに分周して(クロックが何回か来たら1とするようにして、もとのクロックを間引いて遅くする)使います。
クロックの発生から割り込みが起きるまでは以下のように2段構えになっています。
1.「プリスケーラ」でだいたいのところまで落とす(1/16とか、1/64とか、1/256とか)。
タイマーの中には「カウンター」があり、ここで指定した回数の分クロックがきたら、カウンターを1増やすという処理が行われています。たとえば、プリスケーラに「1/64」を指定すると、16回クロックがきて初めてカウンターが1増加します。
2.カウンターが「モジュロ値」を超えたら割り込み発生
モジュロ値はもっと詳細に指定できる値(モジュロ・タイマでは1〜255、タイマ/パルス幅モジュレータでは、1〜65535)で、数値が小さいほど割り込みは発生する感覚が短くなります。
● モジュロ・タイマ(S08MTIMV1)
こちらは単純なほうのタイマーです。
プログラムの初期化部分では、以下のように書きます。
// M TIMER // 54 CLKS = 00 : クロック源としてバスクロックを選択 (00はBUSLCKの意味。データシート P.170参照) // 3210 PS = 1000 : プリスケーラを1/256を選択 (1000は1/256という意味。P.170参照) MTIMCLK = 0b00001000; MTIMMOD = 100; // モジュロ値(1-255で選択可)として、100を指定 // 7 TOF : オーバーフローフラグ // 6 TOIE = 0->1 : Interrupt Enable , 割り込み許可 // 5 TRST = 1->0 : Reset TOI , TOFリセット // 4 TSTP = 0 : MTIM Counter Active MTIMSC = 0b00100000; // TOFリセット MTIMSC = 0b01000000; // タイマースタート
これで、タイマーが一定周期で割り込みを発生するようになります。
割り込み周期は、上記のプログラムでは…
・クロック元 = バスクロック → 8Mhz = 秒間8000000回
・プリスケーラ = 1/256 → 8Mhzを1/256にするので、秒間約31250回、カウンターが加算される
・モジュロ値 = 100 → カウンターが100を超えるごとに割り込み発生(カウンターは100を超えると0に戻る) → 秒間約312回割り込み発生
ということで、秒間312回割り込みが発生する設定です。
で、割り込みを実際に処理するルーチンは以下のように書きます。
interrupt 12 void MTimerOverflow (void) { MTIMSC_TOF=0; // TOFをリセット ※忘れると大変なことになるよ n3++; if ( n3>=100 ) { PTAD ^= 0b00000001; // LEDのON/OFF n3=0; } }
ここでは、割り込みでLEDのON/OFFを切り替えています。
(PTADのbit0をOn/offしている。事前に、PTADDのbit0を1にしておくこと)
ただ、毎回ON/OFFすると高速すぎて人間が認識できないので、変数を加算していき100になったら処理することにしています。
これで、1/3.1秒ごとにLEDのON/OFFがかわることになります。
ちなみに、「interrupt 12」という記述は、ベクター12番の割り込みルーチンだよーという指定をしています。(専用の文法)
ベクター番号は、割り込みの発生源ごとに決まっていて、モジュラ・タイマの場合は、12と決まっています。
割り込みベクタの一覧は、データシートの、64ページに記載されています。
そうだ!大事なことを書き忘れてた。
割り込みルーチンの銭湯には、「MTIMSC_TOF=0;」と書いておきましょう。
こうやってTOFをリセットしないと、次の割り込みが発生しないばかりか、CPUがハングアップしちゃいますよー。
これ入れ忘れててかな〜り悩みました。
(ハングアップすると約1秒間隔で、WatchDogで強制リセットされるので、びみょーな動きになった)
まあ、これさえ忘れなければ難しいところはないでしょう。
詳しくはデータシートの13章「モジュロ・タイマ(S08MTIMV1)」を読んでください。
※ 上記記事では、バスクロック=8Mhzということで書きましたが、デフォルトっていくつでしたっけ? 実測するとバスクロック=4Mhzになってるような気がするー。ここまで書いたのは、だいたいの考え方ということで、実体にあわせて値を設定してくださいな(って、いいかげんな…)
● タイマ/ パルス幅モジュレータ(S08TPMV2)
こっちは機能がいっぱいなのでまだ全部の機能は使っていません。
PWMの出力をちゃんと使えば、LEDの明るさ制御もチップ任せでできちゃうんだろうなー
とりあえずタイマーとして使ってます。
これはちょっと複雑で、メインのカウンターが一つあって、そこにサブのカウンターが複数個(MC9S08QGでは0と1の計2チャンネル)つながってPWMなどの制御を行います。
ここでは一番単純に、メインのカウンターだけを利用してみます。
// TPM // 7 TOF : (ReadOnly)オーバーフローフラグ // 6 TOIE = 1 : Interrupt Enable (割り込み許可) // 5 CPWMS = 0 : // 43 CLKS = 01 : クロック源としてバスクロックを選択 (00はBUSLCKの意味。データシート P.211参照) // 210 PS = 111 : プリスケーラを1/128に指定 TPMSC = 0b01001111; TPMMOD = 20000; // モジュロ値(1-65535で選択可)として、20000を指定
初期化は、こんな感じ。
メインのカウンターでは、クロック源にバスクロックを選択。
それをプリスケーラで、1/128にして、 そのカウントがモジュロ値で指定した20000をになったら、カウンターがオーバーフローして0にもどります。この際に割り込みが発生します。
割り込み処理ルーチンはこんな感じ。
interrupt 7 void Timer1Overflow (void) { TPMSC_TOF=0; // TOFをリセット ※忘れると大変なことになるよ PTAD ^= 0b00000001; }
割り込みベクタ番号は7番なので、「interrupt 7」と指定してあります。
ベクタ番号が重要なので、関数名はなんでもいいです(最初のモジュロタイマーのほうも同様)。
こちらは、割り込み発生間隔を細かく指定できるので、割り込みのたびにLEDをON/OFFしています。
タイマーはこんな感じ。
割り込みの処理のしかたもわかったしよきかな。