AVRによる疑似DMA


2002. 06. 1


LCDコントローラ

AVRファミリの中には外部メモリを接続できるものがあり、内蔵SRAMでは容量の不足するアプリケーションにとても重宝します。しかし、DMAコントローラを持っていないので、周辺回路との間で高速にデータを転送することは困難です。でも、ちょっとしたグルーロジックの追加だけで意外に高速なデータ転送も可能だったりします。今回説明するテクニックは同様な問題を抱える他のマイコン(i8051等)にもそのまま適用できます。

疑似DMAの回路はとても簡単で、次のIMGに示すような感じとなります。AVRが外部メモリにアクセスするときのRD/WRパルスをそのまま周辺デバイスへのストローブ信号にして転送されるデータを横取りしてしまうのです。これによりCPUを介して周辺デバイスとの間の転送をするのに比べて倍の速度で転送できるわけです。このときAVR自体は単にアドレスジェネレータとしての働きだけでよく、読み書きされるデータに対しては Don't Careです。

疑似DMA転送のロジック

疑似DMAの回路

(a)はSRAM上のデータを外部へ送り出す場合の回路です。ポート出力で出力デバイスへのストローブ信号を許可して、必要なデータをAVRで一気に読み込みます。するとAVRのRDパルスが同時に出力デバイスへのストローブ信号となり、読み出されたデータが直接出力デバイスへ書き込まれます。32KB以下の小容量メモリの場合は、余ったアドレス信号からDMA Enable信号を作ることもできます。

(b)は外部からSRAMへデータを取り込む場合の回路です。出力と同様、WRパルスが入力デバイスへのストローブ信号となり、入力デバイスからの出力がバスに乗ってSRAMへ直接書き込まれます。AVRが書き込むデータは Don't Care($FFを推奨)ですが、AVRの出力と入力デバイスの出力との衝突を防ぐためデータバスにRs(1kΩ程度)やバススイッチを挿入する必要があります。この抵抗は、タイミングマージンや耐ノイズ性に影響を与える場合があるので値の選定には十分な吟味が必要です。また、WRパルス幅がとても狭いので1WS追加する必要があるかも知れません。

このように、DMAといっても結局はプログラムで駆動されるため、転送方式は一度にまとまった量のデータを転送するバーストモード転送でないと効率が著しく低下します。AVRではロード/ストア命令で外部メモリへアクセスしたときの実行時間は最短で3クロックサイクルとなります。したがって、8MHz動作なら 2.66MB/sec(理論値)の転送速度が得られることになります。

疑似DMA転送の応用例

;---------------------------------------------------;
; TC0 overflow ISR (10kHz)

tc0ov:
      push  r16           ;Save regs.
      in    r16, SREG     ;
      push  r16           ;
      push  r17           ;
      push  YL            ;
      push  YH            ;/
      outi  TCNT0, -11    ;Restore TCNT0

      ldsw  Y, Vaddr      ;Load DMA pointer

      cbi   PORTD, DMAEN  ;Enable pseudo DMA
      ldi   r17, 40/10    ;Transfer 40 bytes of line data
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      ld    r16, Y+       ;
      dec   r17           ;
      brne  PC-11         ;/
      sbi   PORTD, DMAEN  ;Disable pseudo DMA

      lds   r16, Line     ;Load line counter
      tst   r16           ;If top line, assert YD.
      brne  PC+2          ;
      cbi   PORTD, YD     ;/
      inc   r16           ;Next line
      cpi   r16, 200      ;If bottom line, re-initialize
      brcs  PC+4          ; DMA pointer and line counter.
      clr   r16           ;
      ldiw  Y, Vram       ;/
      cbi   PORTD, LP     ;Strobe (LP) the line data.
      sts   Line, r16     ; Save DMA pointer and line ctr.
      stsw  Vaddr, Y      ;
      sbi   PORTD, LP     ;
      sbi   PORTD, YD     ;/

      pop   YH            ;Restore regs.
      pop   YL            ;
      pop   r17           ;
      pop   r16           ;
      out   SREG, r16     ;
      pop   r16           ;/
      reti

疑似DMA転送の応用例として、AVRにグラフィックLCDを接続した例を示します。このページのトップにあるIMGがそうです。回路図はこちら。キャラクタLCDモジュールと違って、ある程度以上の解像度のグラフィックLCDモジュールはバッファメモリを持たないタイプが多く、スチール表示させるために外部から絶えずイメージデータを送ってやらなければなりません。要はCRTディスプレイと同じ考え方です。

この例で使用したLCDモジュールは、320×200ドットの白黒です。1画面分のデータ量は 320*200/8 = 8000と、8KB になります。また、LCDの垂直リフレッシュ周波数が 50Hzとなっていることから、要求されるデータ転送速度は平均 400KB/secとなります。

LCDへのデータ転送は水平ライン毎に一定周期で行われるので、タイマ割り込みで 100μs毎に1水平ライン分のデータ(40バイト)をLCDに送ってやる必要があります。1回の割り込み処理に要するクロック数は約 200クロックです。このことから計算すると 7.37MHz動作におけるLCD表示動作のプロセッサ負荷率は 30%足らずで済んでしまうわけです。640x200のLCDなら 45%程になります。いやぁ、余裕ですね。

当たり前ですが、DMA Enable が自動生成でない(ポート制御)場合、疑似DMAを行っている間は他の割り込みが入らないようにしなければなりません。この間に割り込みが入って外部SRAMへのリードアクセスがあると意図しないデータが転送されてしまいます。

フレームバッファがローカルメモリ上にあるので、描画が高速なのはもちろん、マルチプレーンや仮想フレームバッファなどの機能も容易に付けられるでしょう。