長い長い前振りでした。そろそろ飽きられる気もしますのでこれまで説明したことをもとに実際にプログラムを組んでみましょう。作るのは次のようなプログラムにします。
プログラム言語はCを使います。DMAの終了は割り込みを使ってもいいのですが今回は実験ですのでとくに終了検出はせず、手動終了させることにします。AC97コーデックの1サンプルは256ビットのフレームですので、これを16ビット×16ワード構成で受信します。この方法はAC97コーデックのデータ型に合致しませんが実験には十分です。AC97規格より、SPORTの設定は
この条件でSPORT0の設定レジスタであるSP0_RCRがどうなるか見てみましょう。HRの9-14およびB-33ページも参照してください。
マルチチャンネル動作時のチャンネル数はどこで設定するのでしょうか。実はチャンネル数を明示的に設定するレジスタはありません。その代わりにフレーム同期信号の周期を設定することができます。1周期をワードサイズで割った商がチャンネル数です。
SP0_RFSDIV = ( チャンネル数 * 受信ワード長 ) - 1
今回は16ビット長で16チャンネルなのでSP0_RFSDIVには255を設定します。
さて、今回ばかりは書いている私にとっても少々タフです。設定はこれに終わりません。さらにマルチチャンネル設定とDMAの設定が続くのです。がんばりましょう。MCMの設定はSP0_MCMC0とSP0_MCMC1で行います。HRの9-30ページとB-41ページも参照してください。まずはSP0_MCMC0から。
続いてSP0_MCMC1の設定です。HRのB-43を参照してください。
SP0_MRTCSxは、最大128チャンネル構成を許すSPORTに対して実際にはどのチャンネルを受信するかビットマップで指定します。SP0_MRTCS0は最初の16チャンネル、SP0_MRTCS1は次の16チャンネルといった具合にすべてのチャンネルに1ビットずつ割り当てられています。詳細はHR B-38を参照してください。
今回は16チャンネル全部を受信しますので、SP0_MRTCS0を0xFFFFとし、あとは0にします。
DMA転送を行うときに重要なことは、あらかじめバッファとデスクリプタが構成するリンクをよく理解しておくことです。今回の構成を下の図に示します。
今回のプログラムは1フレームだけ受信して終わりですので、デスクリプタのリンク・リストは簡単です。図にはrxTCBおよびterminationTCBという二つのデスクリプタからなるリンクリストが示されています。このうちterminationTCBはそのとおりDMAを停止させるためのTCBであり、目印としてconfigurationフィールドが0になっています。DMAはこのデスクリプタを読み込むと動作を停止します。実際にはconfigurationフィールドを読み込んだ時点で値が0なら動作を停止しますので、startPage以降は冗長です。
実際の転送はrxTCBデスクリプタに記述してあります。このデスクリプタは転送数16、転送バッファのページ番号0を決め打ちしており、startPageにデータを転送すべきバッファ(rxBuffer)のアドレスを格納しています。
configurationフィールドにはSP0_DRCFGレジスタにロードする設定値を書いておきます。なお、デスクリプタを使用する場合、以下のビットの相当数が読み出し専用になっています。そのため、レジスタには直接書き込めず、デスクリプタのCFGフィールドに値を書き込んでロードします。SPO_DRCFGの定義は次のとおりです。
DMAはなかなか開始手続きが複雑です。詳細はHR 6-6を見てもらうとして、下に簡単に手続きを説明します。
一度DMA転送が始まると、DMAエンジンは自分自身でデスクリプタのリンク・リストを手繰っていきますのでソフトウェアの介入は不要です。転送量、転送場所、転送終了後の割り込みの有無はすべてデスクリプタに書いてあるため、ソフトウェアはこれらを自由に設定して手放しでデータ転送を行えます。
お待たせしました。ようやくプログラムを披露できます。以下のプログラムはこれまで説明したことを全部盛り込んでいます。SPORTとデスクリプタの設定について改めて説明は不要でしょう。割り込みハンドラに関しては、アーキテクチャーの割り込みの構造ページやEZ-KIT Liteの割り込みを使おうページを参照してください。
#include <def2191.h> #include "def2191bit.h" #include <sysreg.h> #include <signal.h> #define BUFSIZE 16 struct TDMADescriptor { int configuration, startPage, startAddress, count; struct TDMADescriptor * nextDescriptor; }; int rxBuffer[BUFSIZE]; struct TDMADescriptor terminationTCB, rxTCB; void sportRxHandler( int sig ); void setupSport( void ); void setupTCB( void ); int main(void) { interrupt( SIG_INT5, sportRxHandler ); // default interupt for 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, 0xffff ); 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) &rxTCB ); // 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 TCB rxTCB.configuration = DOWN | DCOME | TRAN | DEN; rxTCB.startPage = 0; rxTCB.startAddress = (int)rxBuffer; rxTCB.count = BUFSIZE; rxTCB.nextDescriptor = &terminationTCB; // setting up the DMA TCB for termination terminationTCB.configuration = 0; terminationTCB.startPage = 0; terminationTCB.startAddress = 0; terminationTCB.count = 0; terminationTCB.nextDescriptor = ( struct TDMADescriptor * )0; } void sportRxHandler( int sig ) { sysreg_write( sysreg_IOPG, SPORT0_Controller_Page ); io_space_write( SP0DR_IRQ, 1 ); // clear interrupt from SP0 RX }
インクルードしているdef2191bit.hは、アナログデバイセズの日本語技術資料ページのANJ-017のサンプルに入っています。当該ページへはリンクページから飛んでください。
メインプログラムは、
プログラムの終了はVisualDSP++から行ってください。終了の仕方は先の割り込みを使おうページの末尾に説明してあります。このプログラムを走らせると、SPORTのDMAはAC97コーデックAD1885から1サンプル文のデータを1フレーム16チャンネルに受け取ります。そのデータはrxBuffer中に展開されます。私が実行した結果を以下に示します。
rxTCBを見ると、最後のフィールド(nextDescriptor)がterminationTCBのアドレスを指していることがわかります。また、3番目のフィールド(startAddress)はrxBufferを指しています。rxBufferには受信データが格納されています。
1ワード目はAC97のSLOTチャンネルです。このチャンネルは続くチャンネルのうち、どのチャンネルが有効かを示します。詳しくはAC97の規格を参照してもらうとして、9800とは、SLOTチャンネル、左チャンネル、右チャンネルが有効であるということです。
SLOTチャンネル以降はチャンネルあたり20ビットであることに気をつけてください。従って2ワード目以降は少しずつずらして考えなければなりません。上の絵を分解してみると、
となっています。
最後に上記プログラムをプロジェクトごと圧縮したファイルをダウンロードできるようにしておきました。興味のある方は動かしてみてください。
このプログラムはAC97コーデックがすでに動作状態にあることを前提にしています。AC97コーデックの状態は直前に実行したプログラムや、ブートしたファームウェアに強く依存します。そのため、必ずしも上のようにrxBufferにデータが格納されるとは限りません。まったくデータがない場合もあります。どのような場合も正しく動くプログラムは次回紹介します。
⇒次は連続受信