SSブログ

ネットワーク電飾 (2) [ColdFire V2]このエントリーを含むはてなブックマーク#

2068980

ネットワーク電飾 (1)では、ソフトウェアのポーリングでLEDの点灯タイミングを作っていました。 今回は、ハードウェアの力をちょっと借りて、LEDを点灯させます。

DMAでLEDを点灯させる

先のプログラムでは、ループの中に通信を受け持つ部分とLEDを点灯させる部分が混在していました。 どうも、すっきりしないので、これら二つの部分を分けたいと思います。

とはいえ、SilentCでマルチタスクなプログラムが書けるのかどうか、疑問です。 そこで、LEDを点灯させる部分をハードウェアに任せます。 具体的には、DMAを使うことが出来ます。

DMAモジュール自体は、時間稼ぎをしてくれるモジュールでは無いので、そのままではLEDをピカピカさせることは出来ません。 しかし、DMAには、「DMA Timer」という強い味方がいます。 そこで、「DMA Timer」でタイミングを作って、DMAを叩き、LEDを点灯させます。

DMA1の設定

DMA1は、DTIM0によって周期的に要求を受け、要求のたびに1バイトのデータをPORTUBに書き込みます。 転送元アドレスは、modulo-16で動作させて、繰り返しパターンが出てくるようにします。 また、膨大な総転送バイト数を入れることによって、半永久的にDMA転送要求が続きます。

DMA1のレジスタ設定
レジスタ設定値備考
DMAREQC [DMAC1]0100 (DTIM0) DMAタイマ#0によって、転送要求が発生します。
SAR1pat データの転送元になるSilentCが確保したRAMのアドレスを指定します。 この領域は、SilentCが動作しているときに限り使用可能な領域なので、プログラムの実行が停止するときには、DMAの動作を止めなくてはなりません。 まあ、読み出し側なので、副作用は無いとは思いますが。
DAR10x4010_0012 (PORTUB) 転送先にPORTUBを指定します。
BCR1 [DSR]0xFF DONEフラグをクリアします。 DONEフラグは、割り込みの確認をするのでなければ、特にクリアする必要も無いのですが、隣のBCR1[BCR]と同時に書き込む必要があることから、ついでにクリアしてしまいます。
BCR1 [BCR]0xFFFFF0 半永久的に転送を続けるために、総転送バイト数には、大きな値を指定します。
DCR1 [INT]0 (disable) 割り込みは、使いません。
DCR1 [EEXT]1 (enable) DMAタイマによって、DMAの転送要求が発行されるので、外部要求は許可します。
DCR1 [CS]1 (cycle-steal) このアプリケーションでは、DMA要求1回当たり1バイトの転送を行います。 このような使い方をサイクルスチールモードと呼び、CSビットをセットして設定します。 転送一回あたりのデータ長は、DCR1[SSIZE]とDCR1[DSIZE]で指定されるデータ長の大きいほうです。
DCR1 [AA]0 (disable) MCF52233は、32ビット幅のバスに8,16,32ビットのデータを流すため、必要に応じてデータを分断したり結合したりして転送を行います。 この作業を自動的に行うのが、auto-alignという機能です。 このアプリケーションでは、1バイト単位の転送しか行わないので、どちらでもかまいませんが、ディセーブルにしておきます。
DCR1 [BWC]001 (16KB) DMAがデータを転送するときに使用する最大ブロック長を指定します。 これによって、DMAがバスを占有しすぎないようにすることができます。 このアプリケーションでは、設定が正常であれば1バイトしか転送が行われないのですが、保険として最小の16Kバイトを指定しておきます。
DCR1 [SINC]1 (increment) 転送元アドレスレジスタ(SAR1)を自動的にインクリメントするかどうかを決定します。 このアプリケーションでは、転送元にあるパターンを次々に変更しながらPORTUBに送り込むので、インクリメントの機能が必要です。
DCR1 [SSIZE]01 (Byte) 転送元から読み出されるデータ長は、1バイトです。
DCR1 [DINC]0 (not-increment) 転送先アドレスレジスタ(DAR1)は、常にPORTUBを指していてほしいので、インクリメントはさせません。
DCR1 [DSIZE]01 (Byte) 転送先に書き込まれるデータ長は、1バイトです。
DCR1 [START]0 (inactive) このビットは、プログラムから転送の開始を要求するときにセットします。 このアプリケーションでは、DMAタイマによって要求を発行するので、プログラムではセットしません。
DCR1 [SMOD]0001 (16Bytes)

このアプリケーションでは、転送元にある16バイトのデータを順にPORTUBに送り出します。 そのため、転送元のアドレスは、16バイトインクリメントするごとに元に戻さなくてはなりません。

このレジスタビットを0000以外の値にすると、アドレスレジスタの下位ビット(たとえば、16バイト単位であれば、下位4ビット)だけをインクリメントしてくれるので、事実上16バイトごとに元に戻すという機能が実現できます。 ただし、下位ビットだけをインクリメントしていることから、転送元のバッファのアドレスは、下位ビットが0になるアドレスから始まる必要があります。

SilentCでは、MemoryAllocで確保された領域が何処から始まるか指定できないので、大き目の領域を確保し、その中から下位ビットが0になるアドレスから始まる16バイトの領域を使用します。

DCR1 [DMOD]0000 (disable) 転送先アドレスは、インクリメントしないので、この設定には意味がありません。
DCR1 [D_REQ]1 (enable) 総転送バイトカウンタ(BCR1)が0になったらDCR1[EEXT]ビットをクリアします。 まあ、バイトカウンタが0になるのは1000時間後のことです。
DCR1 [LNKCC]00 (no link) 複数のDMAチャネルを連続して動作させるための設定です。 このアプリケーションでは、DMA1しか使用しません。 LCH1とLCH2も00にしておきます。

DMA1の設定を考えたので、ここでいったん、ソフトウェアでDMAに要求を出すプログラムを作成して、DMAの動作を確認しておきます。

main() {
  char *pubpar  = 0x40100072;
  char *portub  = 0x40100012;
  char *ddrub   = 0x4010002a;
  char *dmareqc = 0x40000014;
  long *sar1    = 0x40000110;
  long *dar1    = 0x40000114;
  long *bcr1    = 0x40000118;
  long *dcr1    = 0x4000011c;
  
  char *alloc;
  char *pat;

  alloc = MemoryAlloc(32);
  pat = (alloc+15)&0xFFFFFFF0;
  pat[0]=1;pat[1]=3;pat[2]=11;pat[3]=15;
  pat[4]=14;pat[5]=12;pat[6]=4;pat[7]=0;
  pat[8]=pat[10]=pat[12]=15;
  pat[9]=pat[11]=pat[13]=pat[14]=pat[15]=0;

  *pubpar = 0x00; // PUBPAR[3:0]=0000
  *portub = 0x00; // PORTUB[3:0]=0000
  *ddrub  = 0x0F; // DDRUB[3:0]=1111
  *bcr1   = 0x01000000; // clear DONE
  *sar1   = pat;    // source
  *dar1   = portub; // destination
  *bcr1   = 0x00FFFFF0; // byte count
  *dcr1   = 0x62521080; // See table
  #stop 0
  for(;;){
    if (Getc(0)=='q')break;
    PrHex(*dar1);PrStr(" ");
    PrHex(*sar1);PrStr(" ");
    PrHex(*bcr1);PrStr("\r\n");
    *dcr1  |= 0x00010000; // START=1
    Sleep(20);
  }
  *bcr1   = 0x00000000; // reset byte count
  MemoryFree(alloc);
}

BCR1レジスタは、DSRとBCRを一度に設定しようと考えていたのですが、先にフラグをクリアしないとBCRが設定できない仕様になってるようで、うまく動作しませんでした。

OK
run
40100012 20003770 00fffff0
40100012 20003771 11ffffef
40100012 20003772 11ffffee
40100012 20003773 11ffffed
40100012 20003774 11ffffec
40100012 20003775 11ffffeb
40100012 20003776 11ffffea
40100012 20003777 11ffffe9
40100012 20003778 11ffffe8

表示されているのは、DAR1, SAR1, BCR1の各レジスタの値です。 この表示で、DMA転送の様子が分かるのですが、PORTUBには反応がありません。 BCR1[DSR]のBED (Bus Error on Destination) がセットされている、つまりエラーが発生しているようです。

freescale.comFAQを探したところ、DMAでのアクセスを制限するレジスタの存在を教えてもらいました。 一連のレジスタは、0x4000_0020から配置されています。

40000020  03 00 00 08 00 00 00 00  ........
40000028  00 00 00 00 00 00 00 00  ........
40000030  00 00 00 00 00 00 00 00  ........

いずれもリセット後のデフォルト値です。 設定は、以下のとおりです。

  1. DMAのアクセスは、Userモードで行われます。
  2. GPIOへのアクセスは、Supervisorモードのみ許可されます。

そのため、DMAによるGPIOモジュールへのアクセスは、禁止されます。 これを解決するためには、UserモードでもGPIOへのRead/Writeアクセスを許可します。 GPACR0[ACCESS_CTRL]を0100 (U/S両モードでRead/Write可能) に変更します。

main() {
  char *pubpar  = 0x40100072;
  char *portub  = 0x40100012;
  char *ddrub   = 0x4010002a;
  char *dmareqc = 0x40000014;
  long *sar1    = 0x40000110;
  long *dar1    = 0x40000114;
  long *bcr1    = 0x40000118;
  long *dcr1    = 0x4000011c;
  char *gpacr0  = 0x40000030;
  
  char *alloc;
  char *pat;

  alloc = MemoryAlloc(32);
  pat = (alloc+15)&0xFFFFFFF0;
  pat[0]=1;pat[1]=3;pat[2]=11;pat[3]=15;
  pat[4]=14;pat[5]=12;pat[6]=4;pat[7]=0;
  pat[8]=pat[10]=pat[12]=15;
  pat[9]=pat[11]=pat[13]=pat[14]=pat[15]=0;

  *gpacr0 = 0x04; // ACCESS_CTRL=0100
  *pubpar = 0x00; // PUBPAR[3:0]=0000
  *portub = 0x00; // PORTUB[3:0]=0000
  *ddrub  = 0x0F; // DDRUB[3:0]=1111
  *bcr1   = 0x01000000; // clear DONE
  *sar1   = pat;    // source
  *dar1   = portub; // destination
  *bcr1   = 0x00FFFFF0; // byte count
  *dcr1   = 0x62521080; // See table
  #stop 0
  for(;;){
    if (Getc(0)=='q')break;
    PrHex(*dar1);PrStr(" ");
    PrHex(*sar1);PrStr(" ");
    PrHex(*bcr1);PrStr("\r\n");
    *dcr1  |= 0x00010000; // START=1
    Sleep(20);
  }
  *bcr1   = 0x00000000; // reset byte count
  MemoryFree(alloc);
}

実行すると、DSR1[BSY]ビットだけがセットされ、LEDの点滅も始まり、正常に転送が行われていることが確認できます。

OK
run
40100012 20003ad0 00fffff0
40100012 20003ad1 02ffffef
40100012 20003ad2 02ffffee
40100012 20003ad3 02ffffed
40100012 20003ad4 02ffffec
40100012 20003ad5 02ffffeb
40100012 20003ad6 02ffffea
40100012 20003ad7 02ffffe9
40100012 20003ad8 02ffffe8
40100012 20003ad9 02ffffe7
40100012 20003ada 02ffffe6

またまた、つづく

参考文献

Interface (インターフェース) 2008年 09月号 [雑誌]

Interface (インターフェース) 2008年 09月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2008/07/25
  • メディア: 雑誌

nice!(0)  コメント(0)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。