はじめに
ここではdsPIC33FのTIMER機能と割り込みについて説明します。
※このページで紹介する内容はあくまでも一例です。個別の作成のご相談ご質問はお答えできませんのでご了承下さい。このページと同じ内容についてのご質問についてはロボット掲示板にてお願いいたします。
※以下の情報は2007年12月現在のものです。ご注意ください。
※このページではC言語の基本的な部分を理解している方向けに書いてあります。また、何回かMPLABの操作を行った方を対象にしています。それ以外の方はまず基本編を読んで下さい。
SFR(Special Function Register:特殊レジスタ)
C言語にはマイコン特有の機能を直接操作する命令はありませんので、マイコン開発が始めてという方は、C言語を勉強しただけでどうしてマイコンが動かせるかわからないかと思います。
マイコンに特有な機能を使うには、SFRというレジスタ(小さい記憶装置のようなもの)に値を設定したり、その値を読んだりして使います。
dsPIC33FのSFR
dsPIC33Fを含む多くのマイコンはSFRがメモリ空間内にあり、メモリにアクセスするのとおなじようにSFRにもアクセスできます。アクセスできるということは値を書いたり読んだりできる、ということです。
これは、dsPIC33Fのデータシートの3章の記述です。これにより、各SFRはメモリ0x0000番地〜0x07FF番地に配置されています。メモリ空間に配置されることをマッピングといいます。
アセンブラ(マシン語)でプログラムを書く場合は、これらのメモリのSFR領域に直接読み書きして特殊機能を使います。C言語の場合は、ヘッダファイルp33FJ256GP710.hでSFRの名前を定義していますので、ソースプログラム内ではSFRの名前を書くだけでアクセスすることができます。
ちなみに、上の図はRAM(データの計算や一時保存で使われる)の部分です。dsPIC33Fの場合は、データ領域とプログラム領域は分かれていますので、上の表にはプログラム領域は入っていません。
上の図で、XデータやYデータとありますが、dsPIC33FはRAM領域を2つに分けて、高速に掛け算などを実行する仕組みがあります。C言語でプログラムを書く場合はdspという機能を使わなければこのあたりは特に意識する必要はありません。dspについては機会があれば説明します。dspは音声処理などの高度な計算を速く実行する仕組みです。
TIMER
マイコンの特殊機能の基本的なものにTIMER(タイマー)があります。TIMER機能はTIMER機能に入力されるクロックが何回あったかを数える(カウント、記憶、リセットなどする)もので、時間の測定に使われます。ほとんどのマイコンにはTIMER機能がついています。タイマーに入力されるクロックは、外部から入力してもよいし、CPUのクロックを使ってもよいです。
カウンタは大きさによってカウントできる数が決まります。たとえば8ビットだったら0〜255、16ビットだったら0〜65535、というようになります。クロックが高速の場合には短い時間しか測定できませんので、大抵のTIMERには「??クロックで一回カウントする」という設定ができるようになっています。このように数クロックで1カウントするものを「プリスケーラ(分周器)」と言います。
入力パルスが一定の間隔であった場合(大抵は一定)は、カウンタの値を測定することは時間を測定することにもなります。
dsPIC33FのTIMER
dsPIC33Fには9個の16ビットTIMERがついています。そのうち、2と3、4と5、6と7、8と9は2つを合わせて32ビットタイマとして使うこともできます。TIMER1だけは1つの16ビットタイマとしてしか使えません。
TIMER1
TIMER1だけは、1つの16ビットタイマーとしてだけ動作します。又、他のタイマーと違って、リアルタイムクロックの入力端子(SOSCI端子とSOSCO端子をセットで使う)があって、32.768KHzの発信クリスタル入力をすれば、時計と同じように実時間をカウントすることもできます。
<クロック入力>
クロックは外部からのクロック入力端子T1CKからのクロックか、CPUの動作クロックFcyを選択できます。上の図でTcyとなっているものがFcyと同じ意味です。Fcyの設定はクロックの説明ページにあります。そのページと同じように設定した場合(内部クロック使用で最高速度)は36.85MHzになります。
<カウンタ部>
TMR1がカウンタです。C言語でアクセスする場合はTMR1と書きます。
<コンペア>
TMR1がある値になったらなにかをさせる、ということもできます。この時、その比較する値をPR1に入れておきます。この動作は33Fデータシートや30Fリファレンスマニュアルではタイマーのところではなく、「出力比較モジュール(Output
Conpare:OC)」のところに書かれています。出力比較についてはこのページではなく、後のDCモータコントロールのページで詳しく説明します。
TIMER1の制御は、上の図でその他に書かれているTCKPSやTONなどで行います。これらはT1CONという特殊レジスタの中の各ビットに割り当てられています。
<T1CONレジスタ>
TIMER1の制御レジスタは次のようになっています。(33Fデータシートの11章より抜粋)
データシートの特殊レジスタの表の見方ですが、例えば上のT1CONの表は、1行目に特性と初期値、2行目に機能の名前、3行目にビット番号が書いてあります。bit15は「TON」という名前で、特性がR/W−0となっています。判例を見るとわかりますが、R/Wは読み込みも書き込みも可、という意味です。−0は初期値が0という意味です。
基本的な使い方は、
1) TCKPSでプリスケーラを選択し、
2) TMR1に開始時の値を入れ、
3) TONを1にするとカウントを開始
という感じで使います。
16ビットタイマーなので、カウント値は65535までしかカウントできません。それを超えると0に戻りカウントが続けられます。
TIMER2〜9
TIMER2〜9はそれぞれを16ビットタイマーとして使う場合はTIMER1と同じです。レジスタの名称もTIMER2のカウンタはTMR2、制御レジスタはT2CON、というように、TIMER1の説明の1の部分を各番号に変えるだけです。
2つを合わせて32ビットカウンタとして使う場合の構成は次のようになります。(データシートより抜粋)
上はTIMER2/3の図ですが、4/5、6/7、8/9も同様です。 2つが合体すると小さい番号の方が買い16ビット、大きい番号の方が上位16ビットになります。
各16ビットか、2つで32ビットかの設定は、小さい方の番号の制御レジスタにT32というのがありますので、そこを1にすると32ビットタイマーとして動作します。また、プリスケーラの設定や、開始/停止の操作も”小さい”番号の方の制御レジスタを操作することで行います。
注意点ですが、割り込み(後で説明します)を使う場合は、32ビットタイマに設定している場合は”大きいほう”の番号の特殊レジスタを設定します。たとえば、TIMER2/3をセットで32ビットタイマとして使用する場合は、割り込み許可はT3IEをセットします。
TIMERで1秒を作ってみる
では実際にTIMERを使ってみましょう。とりあえず、TIMER1の16ビットタイマーを使ってみます。
まず、どのようにすれば1秒をカウントできるかを考えます。基本クロックはクロックの説明ページでやったように、36.85MHzとします。
Tcy(Fcy) = 36.85MHz
36.85MHzは一秒間に36.85百万回のクロックパルスがあるということですので、1クロック1カウントで考えると36,850,000カウントが1秒になります。
16ビットでカウントできるのは65,535までですので、このままではカウントできません。
プリスケーラを使った場合を考えます。TIMER1のプリスケーラは1/1、1/8、1/64、1/256の4パターンに設定できます。最大の1/256にした場合、36,850,000カウントを1/256すると約143,945となり、またしても16ビットの範囲にはおさまりません。この十分の一でしたら約14,394となって16ビットに収まります。1秒の十分の一は100mSということになります。
よって、今回は100mSをカウントしてそれを10回繰り返すことで1秒を作ってみます。
実際のプログラムは次のようになりました。
ソースはこちらになります。(右クリックで対象を保存)
TIMER.c
行27まではいままでやった部分なので割愛します。また、プロジェクトの作成やプログラム転送、デバック実行についても前の方のページを参照願います。
行30と31がTIMER1の設定部分です。コメントにもあるとおり、プリスケーラを設定してTIMERを起動しているだけです。単純にクロックをカウントするだけならば、その他の設定(T1CON)は初期値のままでOKです。
特殊レジスタの設定方法ですが、行31のように、 レジスタ名bits.ビット名 でアクセスできます。この特殊レジスタの名称はヘッダファイル内に定義されています。行31のTONのように1ビットで設定するものは0か1を入れます。行30のTCKPSは2ビットで設定するもので、このような場合はそのビットに合った数値を入れます。
今回、1/256に設定したいので2進数で 11 の設定になります。C言語では2進数は表せませんので16進数で 0x03 とするか、10進数で3とすると、TCKPSの2つのビットがどちらも 11 になります。
行36〜39は、1/256で設定したTIMER1を14394カウントx10回行って1秒を作り出しています。
while(TMR1<14394);
は、事前にTMR1を0にしているので、カウントが14394より小さい場合はなにもしない、つまり14394カウント待つ、つまり100mS待つということになります。
RA0はLEDに接続されています。(LEDの点灯のページを参照)
実行の結果、LEDが1秒ごとに付いたり消えたりとなりました。
割り込み
実生活では場合によっては、ある作業をしている時に、ある条件になったら別な作業をしたいということも出てきます。たとえば、パソコンで仕事をしている最中に、「電話が鳴る」という条件になったら一旦パソコンを中断して電話に出て、そのあとパソコンの仕事にもどる、ということがあります。マイコンでもこのように「ある条件になったら一旦処理を中断してなにかをする」こをと「割り込み」と言います。英語ではInterrupt(インタラプト)と言います。
マイコンの割り込みには「TIMERがある値になったら発生する割り込み」や「外部の決まった端子が変化したら発生する割り込み」など、マイコンの特殊機能に変化がおきた時に発生する割り込みがあります。各機能の割り込みを発生させるかどうかは特殊レジスタで設定します。また、ある機能の割り込みが発生しているかも特殊レジスタの値を見ることでわかるようになってます。
割り込み条件になった場合、「割り込みベクタ」というテーブル(表のようなもの)があり、そこに割り込みが発生した時に起動するプログラムのアドレスを書いておきます。
dsPIC33Fの割り込み
dsPIC33Fの場合、各機能でどのような条件になったらどのような割り込みが発生するかはデータシートの各機能の説明のところに書いてあります(つまりほぼ全ページにわたって割り込みの説明がところどころにある)。よって、どのような条件で割り込みが発生するかはデータシートの各機能を詳しく読む必要があります。
割り込み後のどのような処理をするかですが、これは大体やり方(プログラムの書き方)が決まっていますが、詳しくはC30ユーザーガイドの「7章割り込み」に書いてあります。(下のリンクを右クリックで対象を保存)
C30UserGuid_J.pdf
しかし、このドキュメントはdsPIC33Fがリリースされる前に書いてあるようなのでMPLABver8だと記述が少し変わるのと、ビギナーの方はここを読んでもやり方がさっぱりわからないと思いますのでここでMPLAB+C30の環境でのC言語の割り込みプログラムの書き方を説明します。
<割り込みプログラムの書き方>
割り込みプログラムを作る場合は、以下のフォーマットで関数を作ります。
void __attribute__((interrupt, no_auto_psv)) _????????Interrupt(void)
{
//ここに割り込み時のプログラムを書く
}
すると、割り込み条件になった時に、他のプログラムは一時停止され、ここのプログラムが実行されます。
attributeの前後のアンダーバーはアンダーバー2個づつですので注意して下さい(つながって見えるのでわかりづらい)。??????の部分には割り込みベクタの名前を入れます。割り込みベクタの名称はdsPIC33Fデータシートの6章の表6−1に書いてある割り込み要因の名称になります。
これは表の一部ですが、例えばタイマ1割り込みが起きたときのプログラムを書きたい場合は
void __attribute__((interrupt, no_auto_psv)) _T1Interrupt(void)
{
//ここにTIMER1割り込み時のプログラムを書く
}
のようになります。
<割り込みの許可>
割り込みは、初期設定では発生しないようになっていますので、各機能の割り込みを使うかどうかを設定する必要があります。割り込みを行うかどうかは
IEC0〜IEC4 の5つの特殊レジスタで設定します。
これはデータシートの6章のIEC0の記述です。ビット3がT1IEとなっていますが、このビットを1にするとTIMER1の割り込みを使うという設定になります。初期設定は全て0で、割り込みを使用しない設定になっています。
<割り込みの状態>
割り込みの状態は IFS0〜IFS4 の5つの特殊レジスタで確認できます。下はIFS0の例です。
IFSxは割り込みが発生した時に書くビットが1になります。これらのビットは自動的に0には戻りませんので、割り込みが発生したら、割り込みプログラムの中で各ビットを0にクリアする必要があります。
<割り込みの優先順位>
割り込み中に他の割り込み条件が発生する場合があります。この場合は各割り込みに優先順位をつけることができます。これはIPC0〜IPC17の18個の特殊レジスタで設定します。
IPCxはこのように3ビットで1つの割り込みの順位を設定します。7(111)が最高で1(001)が最低です。0だと割り込み禁止になります。
また、外部割込み(ある端子の変化によって発生する割り込み)の要因を立ち上がり/立下りどちらかに設定する INTCON2 があります。
これらの他に、CPU内部での動きで割り込みを制御する SR CORCON INTCON1 などの特殊レジスタがあります。また、今割り込みを待っている割り込みの番号を表す
INTTREG などがあります。これらはC言語でプログラムを書いている場合はほとんど使いませんので説明を割愛します。
TIMER1の割り込み
割り込みの例としてTIMER1の割り込みについて説明します。
TIMER1の割り込みについてはdsPIC30Fリファレンスマニュアルの12章タイマーの6節タイマー割り込みのところに書いてあります。割り込みが起きる条件は
1) 外部ゲート信号の立下りエッジで発生(今回説明を割愛)
2) 周期レジスタとの値の一致で発生
となっています。周期レジスタとは、各カウンタに対応した比較レジスタ(33Fと名称が違うので注意)つまり、TIMER1の場合はカウンタTMR1に対するPR1と値が一致したときに割り込みが発生します。
また、33Fデータシートの割り込みの説明を読むと、TIMER1の場合は割り込みを許可する場合はIEC0レジスタのT1IEを1にすると割り込み許可になります。
割り込みプログラムは先の説明のように
void __attribute__((interrupt, no_auto_psv)) _T1Interrupt(void)
{
//ここにTIMER1割り込み時のプログラムを書く
}
この形式になります。
割り込みが発生するとIFS0のT1IFフラグが1になります。このビットは自動でクリア(0)になりませんので、割り込みプログラムの中で0にする必要があります。
※以後、他の機能の割り込みを使う場合には以上のようにドキュメントを読んで理解してください。
割り込みで正確な1秒を作ってみる例
では実際に割り込みを使ったプログラム例を説明します。今回、常にモニタに「Hello」を表示させ、割り込みによって1秒ごとにLEDを変化させます。モニタ表示についてはこちらのページを参照してください。
とりあえず、ソースプログラムはこちらになります。(右クリックで対象を保存)
TIMER2.c
まずはメイン関数部分から説明します。
今回はUART1を使います(PCへ文字を送る)ので、行48〜50のようにUART1を設定します。printf関数を使うので「Project」->「Build Options」->「Project」の「MPLAB
LINK30」タブで「Heap sice」を設定するのを忘れないで下さい。値は適当に128ぐらいでOKです。また、上の画面のさらに上にありますが、stdio.h
uart.h のヘッダファイルもインクルードします(行5、6)。
まず、行53でTIMER1割り込みを有効にしています。
また、行55〜59でTIMER1を設定しています。比較レジスタPR1には100mSになるカウンタの値 14394 を入れています。この状態でTIMER1をスタートさせることによって、100mS間隔で割り込みが発生します。
行63〜65では、printf文でHello!!を表示させていますが、ウェイトがまったくないので非常に高速にHello!!が表示されつづけます。
割り込みプログラムは次のようになります。
1秒を作るのに、100mSが10回発生したかを、グローバル変数 LedCount でカウントしています。行29の判定で、10回目にLEDの値を変化させています。
LEDを点灯させるにはRA0を0に、消灯するには1にしますが、RA0は1ビットなので単純に1を足せば、1+1=0 0+1=1 と、前の状態の逆になります(行30)。
これをビルドしてプログラムを転送し、デバック実行させるます。ハイパーターミナルでモニタするとHello!!が表示され続けています。これには関係なく、正確に1秒ごとにLEDが点滅しました。
割り込みを使うと、割り込み条件が発生した瞬間に割り込みプログラムが動きますので、比較的正確な時間間隔でなにかをさせることができます。
おわりに
今回はLEDを駆動しましたが、この割り込みとTIMERを使えば正確なパルス波形を作ることもできます。RCサーボはパルス波形によって動きます。次はRCサーボの駆動について説明します。
2007年12月3日
|