class DMA – RP2040’ın DMA denetleyicisine erişim¶
DMA sınıfı, RP2040’ın Doğrudan Bellek Erişimi (DMA) denetleyicisine erişim sunar; bellek blokları ve/veya GÇ yazmaçları arasında veri taşıma yeteneği sağlar. DMA denetleyicisinin veri yolu yapısına kendine ait, ayrı okuma ve yazma veri yolu yöneticisi bağlantıları vardır ve her DMA kanalı bağımsız olarak bir adresten veri okuyup başka bir adrese geri yazabilir; isteğe bağlı olarak işaretçilerden birini veya her ikisini artırarak, işlemci başka görevler yürütürken veya düşük güç durumuna girerken işlemci adına aktarımlar gerçekleştirmesine olanak tanır. RP2040’ın DMA denetleyicisinin eşzamanlı çalışabilen 12 bağımsız DMA kanalı vardır. RP2040’ın DMA sisteminin tüm ayrıntıları için RP2040 Veri Sayfası belgesinin 2.5 bölümüne bakın.
Örnekler¶
DMA denetleyicisinin en basit kullanımı, verileri bir bellek bloğundan başka bir bloğa taşımaktır. Bu, aşağıdaki kodla gerçekleştirilebilir:
a = bytearray(32*1024)
b = bytearray(32*1024)
d = rp2.DMA()
c = d.pack_ctrl() # Just use the default control value.
# The count is in 'transfers', which defaults to four-byte words, so divide length by 4
d.config(read=a, write=b, count=len(a)//4, ctrl=c, trigger=True)
# Wait for completion
while d.active():
pass
Bu örneğin aktarımın tamamlanmasını beklerken bir boşta döngüde durduğunu, ancak programın bu süre içinde yararlı bir iş de yapabileceğini unutmayın.
DMA denetleyicisinin belki de daha yaygın olan bir başka kullanımı, bellek ile bir GÇ çevre birimi arasında aktarım yapmaktır. Bu durumda GÇ yazmacının adresi her aktarım için değişmez, ancak bellek adresinin artırılması gerekir. Ayrıca, verileri bir çevre birimi tarafından kabul edilmeden önce yazmamak veya veri hazır olmadan önce okumamak için aktarımın hızını kontrol etmek de gereklidir ve bu, DMA kanalının kontrol yazmacının treq_sel alanıyla kontrol edilebilir. Her DMA kanalının kontrol yazmacının çeşitli alanları DMA.pack_ctrl() yöntemi kullanılarak paketlenebilir ve DMA.unpack_ctrl() statik yöntemi kullanılarak çözülebilir. Bir bayt dizisinden bir PIO durum makinesinin TX FIFO’suna her seferinde bir bayt veri aktaran kod şöyle görünür:
# pio_num is index of the PIO block being used, sm_num is the state machine in that block.
# my_state_machine is an rp2.PIO() instance.
DATA_REQUEST_INDEX = (pio_num << 3) + sm_num
src_data = bytearray(1024)
d = rp2.DMA()
# Transfer bytes, rather than words, don't increment the write address and pace the transfer.
c = d.pack_ctrl(size=0, inc_write=False, treq_sel=DATA_REQUEST_INDEX)
d.config(
read=src_data,
write=my_state_machine,
count=len(src_data),
ctrl=c,
trigger=True
)
Bu örnekte yazma adresi için verilen değerin yalnızca verileri gönderdiğimiz PIO durum makinesi olduğunu unutmayın. Bu çalışır çünkü PIO durum makineleri arabellek protokolünü sunarak veri FIFO yazmaçlarına doğrudan erişime izin verir.
Kurucu¶
- class rp2.DMA¶
DMA denetleyicisi kanallarından birini özel kullanım için talep eder.
- config(read: 'int | _AnyReadableBuf | None' = None, write: 'int | _AnyWritableBuf | None' = None, count: int | None = None, ctrl: int | None = None, trigger: bool = False) None¶
Kanal için DMA yazmaçlarını yapılandırır ve isteğe bağlı olarak aktarımı başlatır. Parametreler şunlardır:
read: DMA denetleyicisinin veri okumaya başlayacağı adres veya okunacak verileri sağlayacak bir nesne. Bir tam sayı veya arabellek protokolünü destekleyen herhangi bir nesne olabilir.
write: DMA denetleyicisinin yazmaya başlayacağı adres veya verinin yazılacağı bir nesne. Bir tam sayı veya arabellek protokolünü destekleyen herhangi bir nesne olabilir.
count: Bu kanal durmadan önce yürütülecek veri yolu aktarımlarının sayısı. Bunun bayt sayısı değil, aktarım sayısı olduğunu unutmayın. Aktarımlar 2 veya 4 bayt genişliğindeyse, taşınan toplam veri miktarının (ve dolayısıyla gereken arabelleğin boyutunun) buna göre çarpılması gerekir.
ctrl: DMA kontrol yazmacının değeri. Bu, tipik olarak
DMA.pack_ctrl()kullanılarak paketlenen bir tam sayı değeridir.trigger: İsteğe bağlı olarak aktarımı hemen başlatır.
- irq(handler: Callable[[DMA], None] | None = None, hard: bool = False) Callable¶
Bu DMA kanalı için IRQ nesnesini döndürür ve isteğe bağlı olarak yapılandırır.
- close() None¶
Temel DMA kanalı üzerindeki talebi serbest bırakır ve kesme işleyicisini boşaltır. Bu işlemden sonra
DMAnesnesi kullanılamaz.
- pack_ctrl(default: int | None = None, **kwargs) int¶
Anahtar kelime bağımsız değişkenlerinde sağlanan değerleri yeni bir kontrol yazmacı değerinin adlandırılmış alanlarına paketler. Sağlanmayan herhangi bir alan varsayılan bir değere ayarlanır. Varsayılan değer ya sağlanan
defaultdeğerinden alınır ya da bu verilmezse mevcut kanal için uygun bir varsayılandan alınır; bunuDMA.ctrlözniteliğinin geçerli değerine ayarlamak, alanların bir alt kümesini geçersiz kılmanın kolay bir yolunu sağlar.Anahtar kelime bağımsız değişkenleri için anahtarlar,
DMA.unpack_ctrl()yöntemi tarafından döndürülen herhangi bir anahtar olabilir. Yazılabilir değerler şunlardır:enable:
boolKanalı etkinleştirmek için ayarlayın (varsayılan:True).high_pri:
boolBu kanalın veri yolu trafiğini yüksek öncelikli yapar (varsayılan:False).size:
intAktarım boyutu: 0=bayt, 1=yarım sözcük, 2=sözcük (varsayılan: 2).inc_read:
boolHer aktarımdan sonra okuma adresini artırır (varsayılan:True).inc_write:
boolHer aktarımdan sonra yazma adresini artırır (varsayılan:True).ring_size:
intSıfır değilse, bir adres yazmacının yalnızca en alttakiring_sizebiti bir adres artırıldığında değişir ve adresin bir sonraki1 << ring_sizebayt sınırında sarmalanmasına neden olur. Hangi adresin sarmalandığıring_selbayrağıyla kontrol edilir. Sıfır değer adres sarmalamayı devre dışı bırakır.ring_sel:
boolring_sizedeğerinin okuma adresine uygulanması içinFalse, yazma adresine uygulanması içinTrueolarak ayarlayın.chain_to:
intBu aktarım tamamlandıktan sonra tetiklenecek bir kanalın kanal numarası. Bu değeri bu DMA nesnesinin kendi kanal numarasına ayarlamak zincirlemeyi devre dışı bırakır (bu varsayılandır).treq_sel:
intBir Aktarım İsteği sinyali seçer. Ayrıntılar için RP2040 veri sayfasındaki 2.5.3 bölümüne bakın.irq_quiet:
boolHer aktarımın sonunda kesme oluşturmaz. Bunun yerine, tetik yazmacına sıfır değer yazıldığında kesmeler oluşturulur ve bu, zincirlenmiş aktarımlar dizisini durdurur (varsayılan:True).bswap:
boolDoğru olarak ayarlanırsa, sözcüklerdeki veya yarım sözcüklerdeki baytlar yazılmadan önce ters çevrilir (varsayılan:True).sniff_en:
boolVerilere yongaların sniff donanımı tarafından erişilmesine izin vermek içinTrueolarak ayarlayın (varsayılan:False).write_err:
boolBunuTrueolarak ayarlamak, daha önce bildirilen bir yazma hatasını temizler.read_err:
boolBunuTrueolarak ayarlamak, daha önce bildirilen bir okuma hatasını temizler.
Tüm bu alanların ayrıntıları için RP2040 veri sayfasının 2.5.7 bölümündeki
CH0_CTRL_TRIGyazmacının açıklamasına bakın.
- unpack_ctrl(value: int) dict¶
Bir DMA kanalı kontrol yazmacının bir değerini, kontrol yazmacındaki alanların her biri için anahtar/değer çiftleri içeren bir sözlüğe çözer. value, çözülecek
ctrlyazmacı değeridir.Bu yöntem,
DMA.pack_ctrlifadesine aktarılabilecek tüm anahtarlar için değerler döndürür. Buna ek olarak, kontrol yazmacındaki salt okunur bayrakları da döndürür: bir aktarım başladığında yükselen ve bittiğinde düşenbusyveread_errilewrite_errbayraklarının mantıksal VEYA’sı olanahb_err. Bu değerler paketleme sırasında yok sayılır, böylece bir kontrol yazmacının çözülmesiyle oluşturulan sözlük doğrudan paketleme için anahtar kelime bağımsız değişkenleri olarak kullanılabilir.
- active(value: bool | None = None, /) bool¶
DMA kanalının şu anda çalışıp çalışmadığını alır veya ayarlar.
>>> sm.active() 0 >>> sm.active(1) >>> while sm.active(): ... pass
- read: int¶
Bu öznitelik, bir sonraki veri yolu aktarımının okuyacağı adresi yansıtır. Bir tam sayı veya arabellek protokolünü destekleyen bir nesneyle yazılabilir ve bunu yapmak anında etki eder.
- write: int¶
Bu öznitelik, bir sonraki veri yolu aktarımının yazacağı adresi yansıtır. Bir tam sayı veya arabellek protokolünü destekleyen bir nesneyle yazılabilir ve bunu yapmak anında etki eder.
- count: int¶
Bu özniteliği okumak, geçerli aktarım dizisindeki kalan veri yolu aktarımlarının sayısını döndürür. Bu özniteliği yazmak, toplam aktarım sayısını sonraki aktarım dizisi olarak ayarlar.
- ctrl: int¶
Bu öznitelik, DMA kanalı kontrol yazmacını yansıtır. Tipik olarak
DMA.pack_ctrl()yöntemi kullanılarak paketlenmiş bir tam sayıyla yazılır. Döndürülen yazmaç değeriDMA.unpack_ctrl()yöntemi kullanılarak çözülebilir.
- channel: int¶
DMA kanalının kanal numarası. Bu, DMA zincirlemesine izin vermek için başka bir kanalda
DMA.pack_ctrl()ifadesininchain_tobağımsız değişkeninde aktarılabilir.
- registers: 'memoryview'¶
Bu öznitelik, DMA kanalının yazmaçlarına doğrudan erişime izin veren dizi benzeri bir nesnedir. Dizin bayt yerine sözcük cinsindendir, dolayısıyla yazmaç dizinleri yazmaç adres ofsetlerinin 4’e bölünmüş halidir. Yazmaç ayrıntıları için RP2040 veri sayfasına bakın.
Zincirleme ve tetik yazmacı erişimi¶
RP2040’taki DMA denetleyicisi, bir DMA kanalının başka bir kanalda bir aktarım başlatmasına olanak tanıyan birkaç gelişmiş özellik sunar. Bunlardan biri kontrol yazmacındaki chain_to değerinin kullanılması, diğeri ise DMA kanalının tetik etkisi olan yazmaçlarından birine yazmaktır. Bir DMA kanalının başka bir kanalın DMA.registers yazmaçlarına doğrudan yazma yeteneğiyle birleştiğinde, bu, herhangi bir CPU müdahalesi olmadan karmaşık işlemlerin gerçekleştirilmesine olanak tanır.
Aşağıda, birden çok veri bloğunun tek bir hedefe toplanmasını uygulamak için hem zincirleme hem de yazmaç tetiklemesinin kullanılmasına dair bir örnek bulunmaktadır. Bu özelliklerin tüm ayrıntıları RP2040 veri sayfasının 2.5 bölümünde bulunabilir ve aşağıdaki kod, 2.5.6.2 alt bölümündeki örneğin Python’cu bir sürümüdür.
from rp2 import DMA
from uctypes import addressof
from array import array
def gather_strings(string_list, buf):
# We use two DMA channels. The first sends lengths and source addresses from the gather
# list to the registers of the second. The second copies the data itself.
gather_dma = DMA()
buffer_dma = DMA()
# Pack up length/address pairs to be sent to the registers.
gather_list = array("I")
for s in string_list:
gather_list.append(len(s))
gather_list.append(addressof(s))
gather_list.append(0)
gather_list.append(0)
# When writing to the registers of the second DMA channel, we need to wrap the
# write address on an 8-byte (1<<3 bytes) boundary. We write to the ``TRANS_COUNT``
# and ``READ_ADD_TRIG`` registers in the last register alias (registers 14 and 15).
gather_ctrl = gather_dma.pack_ctrl(ring_size=3, ring_sel=True)
gather_dma.config(
read=gather_list, write=buffer_dma.registers[14:16],
count=2, ctrl=gather_ctrl
)
# When copying the data, the transfer size is single bytes, and when completed we need
# to chain back to the start another gather DMA transaction.
buffer_ctrl = buffer_dma.pack_ctrl(size=0, chain_to=gather_dma.channel)
# The read and count values will be set by the other DMA channel.
buffer_dma.config(write=buf, ctrl=buffer_ctrl)
# Set the transfer in motion.
gather_dma.active(1)
# Wait until all the register values have been sent
end_address = addressof(gather_list) + 4 * len(gather_list)
while gather_dma.read != end_address:
pass
input = ["This is ", "a ", "test", " of the scatter", " gather", " process"]
output = bytearray(64)
print(output)
gather_strings(input, output)
print(output)
Bu örnek, aktarımın tamamlanmasını beklerken boşta bekler; alternatif olarak bir kesme işleyicisi ayarlayıp hemen geri dönebilirdi.