LEDの制御に時間の概念を取り入れる:S08ではじめるマイコン制御プログラミング(4)(3/3 ページ)
少しずつ本格的になってきたマイコン制御。今回はタイマーと割り込みの考え方について、LEDの制御を例にしながら学ぼう。
ハードウェア・タイマー
わざわざ、「ソフトウェア」タイマーというのですから、それならば「ハードウェア」タイマーもあるのではないかと考えた人。鋭いです。世の中の仕組みが良く分かった人ですね。
実はQE128マイコンにもハードウェア・タイマーがあります。タイマーといっても時計ではありませんよ。正確な周期で数を数える、多機能なカウンタです。QE128マイコンでは8ビットのRTCと16ビットのTPMが搭載されています。今回は比較的使い方が簡単なRTCを用います。
では、0.5秒毎にLEDの表示を更新するようにしましょう。リファレンス・マニュアルでRTC(Real-Time Counter)の章を見てみます。今回用いるのはRTCSCレジスタだけです(図3)。
RTCを使う前にRTCLKSとRTCPSの値を決めます。リファレンス・マニュアルに対応表(図4)が出ていますので、これを使うと便利です。0.5秒になる組み合わせを探せばいいのです。今回はRTCLKS=00b、RTCPS=1110bの組み合わせを使います。RTIEを1にすれば、割り込み許可になり、0.5秒毎にRTC割り込みがかかるようになります。
プログラムを見てみます。main関数では、LED表示部分も割り込み処理で行い、周辺I/Fや変数の初期化を行うだけになっています。カウンタ変数cも外部変数とし、割り込み処理内で操作できるようにしました。これまで使っていたソフトウェア・タイマーのwait関数も不要ですので取り除きます(リスト5)。
mcu_init関数にRTC初期化の設定を追加します。RTCSC_RTIEで割り込みが開始され、0.5秒毎にRTC割り込みがかかることになります(リスト6)。
0.5秒間隔で呼び出されるRTC割り込み処理int_timer関数を定義します。RTC割り込みのベクター・テーブルエントリー番号は24で、定義名は「VectorNumber_Vrtc」です。最初にRTIFに1を書き込み、ビット・クリアをしたあと、これまでmain関数で行っていたLED表示の更新をします。
プログラムをビルド、デモボードにダウンロードして動作を確認してみてください。きちんと0.5秒間隔でLEDが更新します。多少、早くまたは遅く見えるかもしれませんが気のせいです……。
すいません、白状します。実は今回RTCのクロック・ソースに使った1kHzのLPOはあまり精度がよくありません。条件によりますが、最大±30%程度の周期の誤差があります。ただ、使い方が簡単なので今回はこれで説明しました。精度が必要であれば、トリミングという処理がされたIRCLKを使うか、外部クロックERCLKを使うと精度を上げることが可能です。余裕があるようでしたら、リファレンス・マニュアルで調べてみてください。
いかがでしたか? 今回説明した割り込みとタイマーの考え方は、制御プログラムではよく使われるものです。LEDのアップ/ダウン・カウンタのプログラムも、前回で作ったものと機能は変わりませんが、中身がだいぶアップグレードされてきたと実感してきているのではないでしょうか? 念のため、今回の最終版プログラムの全ソースリストを示します(リスト8)。
#include <hidef.h> /* for EnableInterrupts macro */ #include "derivative.h" /* include peripheral declarations */ void mcu_init(void); void putbyte_led(unsigned char); char step; unsigned char c; void main(void) { EnableInterrupts; /* enable interrupts */ /* include your code here */ mcu_init(); c = 0; step = 0; for(;;) ; /* loop forever */ /* please make sure that you never leave main */ } void mcu_init(void){ SOPT1_COPE = 0; PTCDD_PTCDD0 = 1; PTCDD_PTCDD1 = 1; PTCDD_PTCDD2 = 1; PTCDD_PTCDD3 = 1; PTCDD_PTCDD4 = 1; PTCDD_PTCDD5 = 1; PTEDD_PTEDD6 = 1; PTEDD_PTEDD7 = 1; KBI1PE_KBIPE2 = 1; PTAPE_PTAPE2 = 1; KBI1PE_KBIPE3 = 1; PTAPE_PTAPE3 = 1; KBI2PE_KBIPE2= 1; PTDPE_PTDPE2 = 1; KBI1SC_KBIE = 1; KBI2SC_KBIE = 1; RTCSC_RTCLKS = 0; RTCSC_RTCPS = 0xe; RTCSC_RTIE = 1; return ; } void putbyte_led(unsigned char c){ c = ~c; PTED_PTED7 = c & 0x01; PTED_PTED6 = (c >> 1) & 0x01; PTCD_PTCD5 = (c >> 2) & 0x01; PTCD_PTCD4 = (c >> 3) & 0x01; PTCD_PTCD3 = (c >> 4) & 0x01; PTCD_PTCD2 = (c >> 5) & 0x01; PTCD_PTCD1 = (c >> 6) & 0x01; PTCD_PTCD0 = (c >> 7) & 0x01; return ; } interrupt VectorNumber_Vkeyboard void chk_key_stat(void){ if (KBI1SC_KBF) { KBI1SC_KBACK = 1; if (!PTAD_PTAD2) { step = 1; } else if (!PTAD_PTAD3) { step = -1; } } if (KBI2SC_KBF) { KBI2SC_KBACK = 1; if (!PTDD_PTDD2) { step = 0; } } return ; } interrupt VectorNumber_Vrtc void int_timer(void){ RTCSC_RTIF = 1; putbyte_led(c); c += step; return ; }
次回はいよいよアナログ・データを扱いますよ。第5回をお楽しみに!(次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.