前回は1サンプルだけ受信するプログラムでした。今度は連続的にどんどん受信するプログラムを作りましょう。連続的に受信するには送り込まれるデータをどんどんためていてはいけません。そんなことをするとあっという間にメモリーがあふれます。そこで、二つのバッファを交互に使うことにします。
ダブル・バッファ方式は比較的よく用いられる手法です。この方法をDMAで実装するには、二つのデスクリプタでリンクリストを作り、それぞれのデスクリプタが異なるバッファを指し示すようにします。今回は大きなバッファを二分して二つのバッファとして使用します。
この方法は前回の構成とそんなに変わりませんが、前回は二つのデスクリプタで動作を停止していたのが、今回は二つ目の次に一つ目のデスクリプタを処理することが異なります。このようにリンク・リストを輪にすることで二つのバッファを交互に使用することができます。
注意が必要なビットはCFG(Configuration)フィールドのDOWNビットです。前回も説明しましたが、DMAはあるデスクリプタの転送が終了すると、そのデスクリプタのDOWNビットを0にします。このビットは次回デスクリプタが読み込まれる前にセットしなければ、DMAが停止しますので気をつけてください。
今回のプログラムを下に示します。大まかな流れはすでに説明したとおりですが、細かい点がいくつか前回と異なります。まず、AC97コーデックAD1885のリセット・ルーチンを組み込みました。これはPF7を1μ秒の間だけLにすることで実現します。時間の測り方はいいかげんですが、きちんと働きます。AC97コーデックのハードウェア・リセットは内部キャリブレーションまで行うため、本来周到な準備が必要ですがここでは考えないことにしました。
また、今回は前回と異なり1サンプル(フレーム)に6ワードしか割り当てていません。これは実際のデータ転送が先頭の6ワード(DSPのワード)だけで行われるためで、不要な後ろの10ワードは受信しないことにしました。この点の制御はSP0_MRCS0への書き込みビットマップを0x003Fに変更して実現しています。
DOWNのセットは割り込みハンドラで行います。このセットを行うにはどちらのデスクリプタのDOWNを立てるべきか判断しなければなりません。これを行う一つの方法はDOWN自身を調べるというものです。本来そのためのDOWNではありますが…。いずれにせよ、DOWNが0になっていたら、そのデスクリプタは転送が終了していますので早速DOWNを1にします。なを、このDOWNを0のまま放っておくとデスクリプタが2回だけ処理されて終わります。count変数で割り込みの数を確かめられますので実験してみるといいでしょう。
今回のプログラムはきちんとコーデックをリセットしているため、バッファの中にデータがたまっているのを目にすることができます。今回もプログラムをダウンロードできるように用意しておきます。
#include <def2191.h> #include "def2191bit.h" #include <sysreg.h> #include <signal.h> #define BUFSIZE 6 struct TDMADescriptor { int configuration, startPage, startAddress, count; struct TDMADescriptor * nextDescriptor; }; int rxBuffer[BUFSIZE*2]; struct TDMADescriptor rxTCB1, rxTCB2; void sportRxHandler( int sig ); void setupSport( void ); void setupTCB( void ); void reset1885( void ); int main(void) { reset1885( ); interrupt( SIG_INT5, sportRxHandler ); // default interupt SPORT0 RX enable_interrupts(); // グローバル割り込みイネーブル setupTCB(); // TCB Configuratoin setupSport(); // SPORT0 RX Configuration while ( 1 ) ; } void setupSport( void ) { sysreg_write( sysreg_IOPG, SPORT0_Controller_Page ); io_space_write( SP0DR_CFG, 0 ); // RX DMA Disable io_space_write( SP0_RCR, 0 ); // RX disable io_space_write( SP0_TCR, 0 ); // TX disable io_space_write( SP0_RFSDIV, 255 ); // 256 bit per frame io_space_write( SP0_MRCS0, 0x003f ); // only 6 channel io_space_write( SP0_MRCS1, 0 ); io_space_write( SP0_MRCS2, 0 ); io_space_write( SP0_MRCS3, 0 ); io_space_write( SP0_MRCS4, 0 ); io_space_write( SP0_MRCS5, 0 ); io_space_write( SP0_MRCS6, 0 ); io_space_write( SP0_MRCS7, 0 ); io_space_write( SP0_MCMC1, 0 << WOFF_OFST | 1 << WSIZE_OFST | 1 << MFD_OFST | MCM ); io_space_write( SP0_MCMC2, MCDRXPE ); io_space_write( SP0DR_CP, (int) &rxTCB1 ); // set chain pointer io_space_write( SP0DR_CPR, 1 ); // ready descriptor pointer io_space_write( SP0DR_CFG, DEN ); io_space_write( SP0_RCR, IRFS | SLEN_16 | RSPEN ); } void setupTCB( void ) { // setting up the DMA TCB1 rxTCB1.configuration = DOWN | DCOME | TRAN | DEN; rxTCB1.startPage = 0; rxTCB1.startAddress = (int)rxBuffer; rxTCB1.count = BUFSIZE; rxTCB1.nextDescriptor = &rxTCB2; // setting up the DMA TCB2 rxTCB2.configuration = DOWN | DCOME | TRAN | DEN; rxTCB2.startPage = 0; rxTCB2.startAddress = (int)&rxBuffer[BUFSIZE]; rxTCB2.count = BUFSIZE; rxTCB2.nextDescriptor = &rxTCB1; } int count = 0; void sportRxHandler( int sig ) { sysreg_write( sysreg_IOPG, SPORT0_Controller_Page ); io_space_write( SP0DR_IRQ, 1 ); // clear interrupt from SP0 RX if ( !( rxTCB1.configuration & DOWN ) ) // Finish? rxTCB1.configuration |= DOWN; // give ownership of TCB1 to DMA else rxTCB2.configuration |= DOWN; // give ownership of TCB2 to DMA count ++; } void reset1885( void ) { int curDIR, curIOPG, i; curIOPG = sysreg_read( sysreg_IOPG ); sysreg_write( sysreg_IOPG, General_Purpose_IO ); curDIR = io_space_read( DIR ); // get current Dir io_space_write( DIR, curDIR | 0x0080 ); // PF7 : AD1885 /reset io_space_write( FLAGC, 0x0080 ); // asseart /reset_ad1885 for ( i=0; i<160; i++ ) asm volatile( "nop;" ); io_space_write( FLAGS, 0x0080 ); // deasseart /reset_ad1885 sysreg_write( sysreg_IOPG, curIOPG ); }
⇒次は送信