このコーナーはEZ-KIT Liteの簡単な紹介のつもりですので、前回までで一通り目的は果たしました。が、もう一つだけ便利なIOの説明をして終わりましょう。それはタイマーIOです。
ADSP-2191はタイマーを3本持っています。Timer0, 1, 2と名づけられ、それぞれ同じ機能です。このタイマーは三つのモードを持っています。
詳細は省略しますが今回触ってみるのはPWM (Puls Width Modulation )です。これは外部ピンに対して任意の周期、任意の幅のパルスを出力するモードです。さらに、1周期ごとにプログラムに対して割り込みを書けることが出来ます。
パルス周期を決めるレジスタです。周期は32ビットで指定し、TPRDHxが上位16ビット、TPRDLxが下位16ビットを指定します。xは2191の場合は0から2までで、それぞれタイマー0,1,2に対応します。
T_PRDHx, T_PRDLxに設定された値はPWMモードではパルス周期を表します。パルスのクロックはHCLK(HR 12-2)から供給されます。これは設定にもよりますがEZ-KIT Liteの場合は90MHzです。したがって、T_PRDHx、T_PRDLxに値Nを設定すると、パルス周波数は(90/N)MHzとなります。
設定方法の注意はT_WDHxとT_WDLxを参照してください。
この二つのレジスタはパルス幅を決めます。
値の設定には決まった順番があります。PWMモードで動作させる場合、タイマーがイネーブルかどうかにかかわらずいつパルス周期、パルス幅の指定を変えてもいいことになっています。ただし、書き込みの順番は決まっています。
まず、周期を変更する必要があれば値を書き込みます。このときHLの順序は任意です。つぎにT_WHRxを書き込み、ついでT_WLRxを書き込みます。最後のT_WLRxの書き込みでこれまで書き込んだ値が正式にレジスタに書き込まれます。つまり、T_WLRxを書き込むまでは、他の値はバッファされているだけです。
T_PRDyx、T_WyRxのいずれも、1周期ごとにタイマーに再ロードされます。したがって、変更が反映されるのはタイマー周期の終わりです。
これはタイマーの動作モードを定義するレジスタです。先のPWM, キャプチャー、カウントのどのモードを使うか、割り込みを許すかどうかなどを指定します。
T_GSRx( Timer Global Status & Sticky Register )は、ステータスと言う名前がついているものの、もっぱら制御に使われます。
制御に使うのは次の3種類のビットです。
これらのビットはそれぞれのタイマーから割り込みが発生したかどうかをラッチします。このラッチは割り込みの表示だけではなく、その割り込みをクリアするために使います。1を書き込むとクリアです。タイマー割り込みはこのビットをクリアするまで発生しつづけますので、処理が終わったら必ずクリアします。
これらのビットはタイマーをイネーブルします。1を書き込むとイネーブルです。
これらのビットはタイマーをディセーブルします。1を書き込むとディセーブルです。
プログラムを下に示します。最初の頃に比べると随分長くなりましたが、実は流れはまったく変わっていません。割り込みハンドラを設定し、IOを設定して割り込みを待つだけです。
最初にdef2191bit.hというヘッダーファイルをインクルードしています。このファイルはVisualDSP++のファイルではなく、アナログデバイセズの日本語技術情報ページからできるものです。同ページへはリンクページからジャンプしてください。ANJ-017のサンプルファイルの中に入っています。
setupTimer()関数ではフラグI0の下位4ビット(LED8-11)を出力にし、タイマーの設定を行います。タイマーは設定前にまずT_GSR0を使ってタイマー0を制止します。これは保険のようなものです。次にT_CFGR0を使ってPWMモード、割り込み可能にします。
周期については0.5秒周期の割り込みとします。幅はどうでもいいので1を設定します。全部終わったらタイマー0をイネーブルにします。タイマーはイネーブルになると同時に周期と幅をレジスタからロードするので、ここから0.5秒後に割り込みがはいります。
割り込みハンドラでは二つの処理をします。後半のLEDの処理はどうでもいいとして、重要なのはタイマー割り込みのクリアです。T_GSR0のTIMIL0ビットに1を書き込むと、タイマー0の割り込みをクリアーすることが出来ます。これを怠るとひっきりなしに割り込みが入りつづけて泣くことになりますので気をつけてください。
#include <sysreg.h>
#include <signal.h>
#include <def2191bit.h>
#include <def2191.h>
void timerHandler( int intr );
void setUpTimer( void );
int main(void)
{
    interrupt( SIG_INT13, timerHandler );  // タイマー0割り込み
    enable_interrupts();
    setUpTimer();
    while (1) 
        asm("idle;");
}
void setUpTimer( void )
{
    long period = 40000000L;        // 0.5 sec at 90MHz HCLCK
    sysreg_write(sysreg_IOPG, General_Purpose_IO);
    io_space_write(DIR, 0xf );      // set flag 0 & 1 output
    sysreg_write(sysreg_IOPG, Timer_Page );
    io_space_write(T_GSR0, TIMDIS0);        // タイマー0停止
    io_space_write(T_CFGR0, (IRQ_ENA | TMODE_PWMOUT | PERIOD_CNT ));
    io_space_write(T_PRDH0, (int)( period >> 16) );   // 周期の上位
    io_space_write(T_PRDL0, (int)( period & 0xffff) );// 周期の下位
    io_space_write(T_WHR0, 0 );             // 幅の上位
    io_space_write(T_WLR0, 1 );             // 幅の下位
    io_space_write(T_GSR0, TIMEN0);         // enable timer 0
}
void timerHandler( int intr )
{
    static int odd = 0;
    int iopg;
    iopg = sysreg_read( sysreg_IOPG );
                               // handling timer
    sysreg_write(sysreg_IOPG, Timer_Page );
    io_space_write(T_GSR0, TIMIL0);  // タイマー割り込みクリア
                               // blinking LED
    sysreg_write(sysreg_IOPG, General_Purpose_IO);
    if (odd) 
        io_space_write(FLAGC, 0xf );
    else
        io_space_write(FLAGS, 0xf );
    odd=!odd;
    sysreg_write(sysreg_IOPG,  iopg );
}