背景:DMAでいろいろややこしい使い方をしようとしているので、、沼に落ちるのを避けるため簡単なサンプルコード作成と、転送速度を測る
取り組み:まずは1chだけのDMAを使ってGPIOのPinをLHした時の周期を測定
結果:システムクロックの速度(150MHz)でPinをLHできるのかと単純に思っていました。実際計測すると37MHzの速度に留まっていました。これはデータ取り込み等のオーバーヘッドがあるためらしいです*1
詳細:
この方法がベストかどうか分かりませんが、、GPIOのレジスタをDMAでL/H設定する。
テストコード
#
# DMA speed test simple
# dma0
# dma0 L->H->L->H... 27ns 37MHz
#
#
from rp2 import DMA
from uctypes import addressof
from array import array
from machine import Pin
pin0 = Pin(0,Pin.OUT)
pin0.low()
BASE_IO_BANK0 = 0x40028000
OFFSET_GPIO0_CTRL = 0x4
GPIO0_CTRL_REG_ADDR = BASE_IO_BANK0 + OFFSET_GPIO0_CTRL
buf_HL = array("I", [0] * 50)
for i in range(0, len(buf_HL), 2):
buf_HL[i] = (3 << 12) + 5
buf_HL[i+1] = (2 << 12) + 5
dma0 = DMA()
def dma0_handler(arg):
print('dmq0 fin')
SIZE_WORD=2
dma0_ctrl = dma0.pack_ctrl(enable = True, size=SIZE_WORD, inc_write=0, inc_read=True, irq_quiet=False)
transfer_count = 10
dma0.irq(dma0_handler)
while True:
dma0.config(write=GPIO0_CTRL_REG_ADDR, read=buf_HL, count=transfer_count, ctrl=dma0_ctrl, trigger=False)
print('start address')
hex(addressof(buf_HL))
machine.mem32[GPIO0_CTRL_REG_ADDR] = (2 << 12) + 5
dma0.active(1)
time.sleep(0.1)
print('read:', hex(dma0.read))
print('count:', dma0.count)
print(dma0.unpack_ctrl(dma0.ctrl))
# dma0 L->H->L->H... 27ns 37MHz山->谷にかかった時間をオシロで測定

山ー>谷にかかった時間は、27ns (37.04MHz)であった。これが正しい結果なのか?をAIに質問、回答は以下


Claudeに相談すると、データの取り込みやDMAでの送り出しを考慮すると、4サイクル(27ns (37.04MHz))で1データ(32bit)送り出すのは理にかなっているとの回答
■ご参考
別途、DMAを2つ使ってピンポンで送信した場合の速度も調べました。DMA0による送信、DMA0->DMA1の切り替え、DMA1の送信までの時間を測ると、47ns(21.28MHz)でした。ですので単純に引き算で計算すると、2つのDMA間での切り替えにかかるオーバーヘッドは、20nsで、約3サイクルかかっている計算になります。
2つのDMAをピンポンで動かすサンプルコード
#
# DMA speed test
# dma0 <--> dma1 pin pon archi tecture
#
# dma0 -> dma1 pin pon 21.28MHz 47ns (time for H->L)
#
from rp2 import DMA
from uctypes import addressof
from array import array
BASE_IO_BANK0 = 0x40028000
OFFSET_GPIO0_CTRL = 0x4
GPIO0_CTRL_REG_ADDR = BASE_IO_BANK0 + OFFSET_GPIO0_CTRL
from machine import Pin
pin0 = Pin(0,Pin.OUT)
pin0.low()
# data set for GPIO to H and GPIO to L
buf_H = array("I", ((3 << 12) + 5,))
buf_L = array("I", ((2 << 12) + 5,))
dma0 = DMA()
dma1 = DMA()
# data size (WORD)
TRANSFER_SIZE_WORD = 2
transfer_size = TRANSFER_SIZE_WORD
# transfer count
transfer_count = 1
dma0_ctrl = dma0.pack_ctrl(enable = True, size=transfer_size, inc_write=0, inc_read=0, irq_quiet=True, chain_to=dma1.channel)
dma0.config(write=GPIO0_CTRL_REG_ADDR, read=buf_H, count=transfer_count, ctrl=dma0_ctrl, trigger=False)
dma1_ctrl = dma1.pack_ctrl(enable = True, size=transfer_size, inc_write=0, inc_read=0, irq_quiet=True, chain_to=dma0.channel)
dma1.config(write=GPIO0_CTRL_REG_ADDR, read=buf_L, count=transfer_count, ctrl=dma1_ctrl, trigger=False)
dma0.active(1)