Knowledge
   

割り込みでLEDを点滅させてみる

やっぱ制御系ソフトの基本ということで、割り込みにチャレンジしてみました。いや、見た目の動作自体はたいした事無いです。はい。


2005/10/24

PSoC・割り込みでLEDを点滅させてみる。

タイマと割り込みでLEDを点滅
今回は、LEDを割り込みで1秒毎に点灯/消灯させようと思います。やっぱこういうのには「タイマ」という気がするのですが、実は、 「カウンタとタイマ、どっちがいいんだろう?」なんて悩んでます。ドキュメントを見るとすごくよく似ています。 ま、単純なプロジェクトなので、どっちを使ってもよさそうということで、素直にタイマにします。

さて、今回、このタイマ、ちょっと調べて見たら「32kHz」を入力できるようです。また、「32kHz」は内臓しているとのこと。 精度は、内臓24MHzのクロックが±2.5%とのことから、たぶん、これも±2.5%かな。内部低速発振器(ILO)を使っているとのことなので、 きっとそうでしょう。 この32kHzというクロック、時計などではよく使われるクロックで、正確には「32.768kHz」だったと思います。 PSoCの内臓32kHzの場合は、32000、32768、どっちなんだろう?ま、一般的と言う事で、32768ということで進めてみます。 またアプリケーションノートあたりを探してみます。

SCREEN SHOT
クリックで拡大します。
まずはプロジェクトを作ります。今回は、「LedFlicker」としました。
SCREEN SHOT
クリックで拡大します。
それではさっそく「Timer16」を選択します。今回、32768をカウントして1秒を作り出すため、16ビットカウンタを選択します。 8ビットカウンタの場合、「0〜255」までしかカウントできませんが、16ビットカウンタならば「0〜65535」までカウントでき、 ばっちりです。
SCREEN SHOT
クリックで拡大します。
「Device Editor」の「Selection」画面から「Interconnect」画面に切り替えます。そして「Timer16_1」を「Place」します。
SCREEN SHOT
クリックで拡大します。
それでは、「Timer16_1」の設定をします。今回、「Global Resources」の設定は、すべてデフォルトで問題ありません。 まずは「Clock」を「CPU_32_KHz」にします。そして「Caputure」は「Low」(なんか、有効/無効をHigh/Lowで表現しているみたいです)。 「Period」は、タイマの期間ですので「32768」から「-1」した値である「32767」を設定します。 他のサンプルとかみると、こう「-1」して書いて「Compare Type」を「Less than Or Equal」にしていたので、 同じようにしています。

あと大事なのが「ClockSync」です。必ず「Use SysClk Direct」以外を選択してください。これ、厳密にどんな機能か、 まだ理解していないのですが、「Use SysClk Direct」を選択すると、さっきの32kHzの設定が無視されて、 SysClkが使用されるようなことがマニュアルに書いてありました。実は、最初、「なんでLEDが点滅しないんだろ〜!?」と、 はまりました。

というわけで、「Generate Application」しましょう。

 

SCREEN SHOT
クリックで拡大します。
さってと、いよいよ割り込み処理です。まずは「Application Editor」に切り替えます。サンプルを見てみると… モジュール起動や割り込み許可の処理は書いてあっても、なんと、肝心な割り込み処理のサンプルがありません〜。 ひょ〜。というわけで、ごにょごにょと調べると…(って、本に書いてありますね)、自動生成されたソースコード、 「boot.asm」に割り込みベクタテーブルが書いてあり(割り込みが発生した時に参照されて、 必要な処理に移るためのテーブル)、ジャンプ先は、これまた自動生成されたソースコードの「timer16_1int.asm」 の中に定義されていました。「Library Source」の中にあります。 どうやらここに直接コードを書くか、C言語の関数を作っておいて、割り込み関数の宣言をしておけばよさそうです。

 

SCREEN SHOT
クリックで拡大します。
せっかくC言語が使えるし、そんなにスピードも気になりませんから、今回はC言語で割り込みを記述します。

まずは、いつものメイン処理を記述します。これはマニュアルを読むと載っているので、ほとんどそのまま使います。 まずは「Timer16_1」の割り込み用初期化をし、その後、CPU全体の割り込み許可をして、最後にモジュールをスタートかけるようです。 で、プログラムが終わると、そのままPSoCもとまってしまいますので、最後に、永久ループ(って言うのかな?)として、 ぐるぐる回りの一行を入れておきます。

  Timer16_1_EnableInt();
  M8C_EnableGInt;
  Timer16_1_Start();

  while( 1 );
				
本題の割り込み処理ですが、C言語で割り込み処理を記述する場合、あるおまじないをします。 というわけでいきなりでてきたのが「#pragma」。これは…よくわからないので、お作法とて覚えておくことにします。 この行の一番最後は関数名で、次の行(次の行である必要はありませんが)に関数の本体が定義してあります。 こうやって記述すると、割り込み処理として認識してくれるようです。関数名は好きなものをつけて頂けばOKです。 中身は、単にPORT2の[0]〜[3]をひっくり返す、というものです。
#pragma interrupt_handler int_timer16_1
void int_timer16_1()
{
  PRT2DR ^= 0x0F;
}
				

 

SCREEN SHOT
クリックで拡大します。
それでは、Application Editor上から「Library Source」フォルダを開いて、「timer16_1int.asm」の編集をします。 「asm」と拡張子がついているだけあって、アセンブラです。え?アセンブラ、わかりません?大丈夫です。 SISOもわかりません。「boot.asm」の方を見て、ごくごく簡単に書きました。

書いたのは、先ほど作った割り込み処理の「int_timer16_1_TC()」を呼び出す処理です。 「ljmp」っていうのはきっと「ジャンプする命令」だろうと。で、あと、割と定番なんですが、 アセンブラからC言語を呼び出す時には、関数名の頭に「_」(アンダーバー)をつけるようです。 というわけでできたのが、次の一行。

  ljmp  _int_timer16_1
				

 

SCREEN SHOT
クリックで拡大します。
ビルドしてみると…何やら警告がでてきてしまいました。あらら。ま、よくわからないので、とりあえず無視しておきます。 後は「PSoC Programmer」を起動してPSoCに書き込めばおしまいです。「PSoC Programmer」から、MiniProg1の電源をONしてやれば、 見事にチカチカとLEDが4つ同時に点滅すると思います。1秒に一度、点灯と消灯を切り替えていますので、 点滅周期は2秒になります。
PROCESSING
クリックで再生します。
それでは、動作中のPSoCの動画をお楽しみください(動画だけ見ても楽しくないかもしれませんが、 自分でプログラムしたものが、思ったとおりに動作するのは楽しいですよ!)。 う〜ん、心なしか1秒よりちょっと遅い感じ。

ここで疑問なんですが、「割り込み処理の呼び出しは、boot.asmの方に呼び出しを書いたほうが速いんじゃないの?」 って思っちゃいますよね〜。というわけで試してみました。「ljmp _int_timer16_1_ISR」と書いてあるところを、 「_int_timer16_1」としたら、ちゃんと動作しました。ところが、ちょっと困った事が。 「Generate Application」を再度したら、コードが初期化されました。やっぱり、 ちゃんと「ここにコード書いてね〜」とあるところに書かないとダメですね〜。

 


SISO-LAB Knowledge