lynxeyedの電音鍵盤

MBDとFPGAと車載で使うデバイスの備忘録

NXPのARMプロセッサのDMAを理解したい

LPC1768/LPC1769を使っていて、是非使いたい機能の中にDMAがあります。しかし、GPDMAとあるように汎用DMAになると特定のペリフェラルに特化していないDMAとなるため、はじめてNXPマイコンを使う人に取っては設定項目の多さ、初期化手順の混沌といった問題にぶつかります。

以前製作したMTM06の時に展示したロスレスオーディオデコーダではユーザRAMに数kBのバッファをソフトウェアで用意し、割り込みを使用してI2Sペリフェラルにデータを渡していました。8ワード分のバッファがあるにせよ44.1kHzステレオ、16bitの音声ですのでそれなりのオーバーヘッドが生じます。DMAを使えば割り込みのオーバーヘッドが無くなりはしないものの大きく低減できますので、是非使いたいところ*1です。
ところがいざこの準備をしようとするとsirius506氏も仰る通りかなり大変です。【注:PDFです】ユーザーマニュアルのI2SとGPDMAの項目に精通していなければなりません。

id:suikan 氏が最近TOPPERS/ASPによるTalkThroughプログラムをリリースされました。こちらも参考にしながら、DMAを実装していこうと思いました。
しかし、mbed IDE上でレジスタを直接*2叩くとおかしな挙動をする事があるようです*3。mbedライブラリはCMSISレイヤの上にある高位なライブラリですので、mbedライブラリに存在しないLPC17xxのペリフェラルを操作する際にはなるべくCMSIS越しに操作した方が良いのかもしれません。NXPが用意しているCMSISドライバライブラリを参考にします。
I2S DMAでは音声データを途切れる事無くDMAに渡すためLLIを使用します。今回は勉強のためI2Sの機能を省いたLLIのテストコードを使用しました。
このサンプルもGPDMAフォルダにある(Link_list)ので流用しました。
このサンプルはワンショットLLIとなっているため、LLIがリングバッファを形成するように(無限ループになるように)書き換えます。

	
    /* Init GPDMA link list */
    DMA_LLI_Struct[0].SrcAddr = (uint32_t)&DMASrc_Buffer1;
    DMA_LLI_Struct[0].DstAddr = (uint32_t)&DMADest_Buffer;
    DMA_LLI_Struct[0].NextLLI = (uint32_t)&DMA_LLI_Struct[1];
    DMA_LLI_Struct[0].Control = (DMA_SIZE/2)
	| (2<<18)	//source width 32 bit
	| (2<<21)	//dest. width 32 bit
	| (1<<26)	//source increment
 	| (1<<27)
	| (1<<31)	//trigger interrupt	<- これを追記
	;
    DMA_LLI_Struct[1].SrcAddr = (uint32_t)&DMASrc_Buffer2;
    DMA_LLI_Struct[1].DstAddr = ((uint32_t)&DMADest_Buffer) + (DMA_SIZE/2)*4;
    DMA_LLI_Struct[1].NextLLI = (uint32_t)&DMA_LLI_Struct[0]; // 	<- これを追記
    DMA_LLI_Struct[1].Control = (DMA_SIZE/2)
	| (2<<18)	//source width 32 bit
 	| (2<<21)	//dest. width 32 bit
	| (1<<26)	//source increment
	| (1<<27)
	| (1<<31)	 //trigger interrupt  				<- これを追記
	;

こちらをコンパイルしたものをmbed.org上にuploadしてあります。Buffer1,2それぞれデータを送出した後にTC割り込みを発生させます。確認のためprintfで"hogehoge"とUART0越しに喋ります。


参考:todotani氏のブログPS3とLinux、電子工作も::mbedでDMAの実験

*1:他のタスクをより多く行える

*2:gcc使えと言う話だが、mbedライブラリを作りたいのでこんな事をしている

*3:未確認だが、PCLKやCCLK回りを弄ると強制的に書き戻したり、mbedが止まったりする