Skip to content

RP2 DMA Class #10704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ user.props

# MacOS desktop metadata files
.DS_Store

# VScode config
.vscode
1 change: 1 addition & 0 deletions docs/rp2/general.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ The peripherals include:
* 16 PWM channels
* USB 1.1 controller
* 8 PIO state machines
* 12 DMA channels and 4 pacing timers
44 changes: 44 additions & 0 deletions docs/rp2/quickref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,50 @@ See :ref:`machine.WDT <machine.WDT>`. ::

The maximum value for timeout is 8388 ms.

DMA (Direct memory access)
--------------------------

The RP2040 has a DMA subsytem that has 12 hardware controlled DMA channels and 4 pacing timers.

Example using DMA to fade an LED::

from rp2 import DMA, Timer
from machine import Pin, PWM

led = Pin(16, Pin.OUT)
pwm1 = PWM(led)
pwm1.freq(1000)
pwm1.duty_u16(0)

# PWM 0A for GPIO16. Lower 16 bits of CC
dest = 0x4005000e
dma = DMA()
dma.claim()
t = Timer()

t.claim()
# go as slowly as possible
t.set(0x0001, 0xffff)

data_size = dma.DMA_SIZE_16
buffer_size = 32768
from_buffer = bytearray(buffer_size)
for i in range(0, buffer_size, 2):
from_buffer[i]= i & 0xff
from_buffer[i+1]= (i>>8) & 0xff

dma.write_from(from_buffer=from_buffer,
to_address=dest,
transfer_count=buffer_size>>data_size,
dreq=dreq,
data_size=data_size)
while dma.isbusy():
time.sleep_ms(1)

t.unclaim()
dma.unclaim()


OneWire driver
--------------

Expand Down
59 changes: 59 additions & 0 deletions examples/rp2/dma_write_from.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import time
from rp2 import DMA, Timer
from machine import Pin, PWM


def led():
print("DMA write_from demo")
led = Pin(16, Pin.OUT)
pwm1 = PWM(led)
pwm1.freq(1000)
pwm1.duty_u16(0)

# pwm 0A attached to GPIO 16
dest = 0x4005000E
dma = DMA()
dma.claim()
t = Timer()

X = 0x0001
Y = 0xFFFF
t.claim()
t.set(X, Y)

print(f"Starting\nDMA:{dma}\nTimer:{t}")

dreq = t.dreq()

data_size = dma.DMA_SIZE_16
buffer_size = 32768
from_buffer = bytearray(buffer_size)

# create a 16 bit fade
for i in range(0, buffer_size, 2):
from_buffer[i] = i & 0xFF
from_buffer[i + 1] = (i >> 8) & 0xFF

start = time.ticks_ms()

dma.write_from(
from_buffer=from_buffer,
to_address=dest,
transfer_count=buffer_size >> data_size,
dreq=dreq,
data_size=data_size,
)
while dma.isbusy():
time.sleep_ms(100)

end = time.ticks_ms()

t.unclaim()
dma.unclaim()
print(f"Write from fade took {end-start}mS")
print("Done")
pwm1.duty_u16(0)


if __name__ == "__main__":
led()
77 changes: 77 additions & 0 deletions examples/rp2/dma_write_from_irq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import uasyncio
import time
from rp2 import DMA, Timer
from machine import Pin, PWM


async def led():
print("DMA write_from demo")
led = Pin(16, Pin.OUT)
pwm1 = PWM(led)
pwm1.freq(1000)
pwm1.duty_u16(0)

# pwm 0A attached to GPIO 16
dest = 0x4005000E
dma = DMA()
dma.claim()
t = Timer()

# slow the timer to it's slowest rate
X = 0x0001
Y = 0xFFFF
t.claim()
t.set(X, Y)

print(f"Starting\nDMA:{dma}\nTimer:{t}")

dreq = t.dreq()

data_size = dma.DMA_SIZE_16
buffer_size = 32768
from_buffer = bytearray(buffer_size)

# create a 16 bit fade
for i in range(0, buffer_size, 2):
from_buffer[i] = i & 0xFF
from_buffer[i + 1] = (i >> 8) & 0xFF

start = time.ticks_ms()

e = uasyncio.ThreadSafeFlag()

def _callback(dma_status):
print("callback")
e.set()

dma.write_from(
from_buffer=from_buffer,
to_address=dest,
transfer_count=buffer_size >> data_size,
dreq=dreq,
data_size=data_size,
handler=_callback,
)

await e.wait()

end = time.ticks_ms()

t.unclaim()
dma.unclaim()
print(f"Write from fade took {end-start}mS")
print("Done")
pwm1.duty_u16(0)


async def async_led():
t = uasyncio.create_task(led())
await t


def main():
uasyncio.run(async_led())


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions ports/rp2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ set(MICROPY_SOURCE_PORT
fatfs_port.c
machine_adc.c
machine_bitstream.c
dma.c
machine_i2c.c
machine_i2s.c
machine_pin.c
Expand Down Expand Up @@ -140,6 +141,7 @@ set(MICROPY_SOURCE_QSTR
${MICROPY_DIR}/shared/runtime/mpirq.c
${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c
${PROJECT_SOURCE_DIR}/machine_adc.c
${PROJECT_SOURCE_DIR}/dma.c
${PROJECT_SOURCE_DIR}/machine_i2c.c
${PROJECT_SOURCE_DIR}/machine_i2s.c
${PROJECT_SOURCE_DIR}/machine_pin.c
Expand Down
2 changes: 1 addition & 1 deletion ports/rp2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Currently supported features are:
- `uos` module with VFS support.
- `machine` module with the following classes: `Pin`, `ADC`, `PWM`, `I2C`, `SPI`,
`SoftI2C`, `SoftSPI`, `Timer`, `UART`, `WDT`.
- `rp2` module with programmable IO (PIO) support.
- `rp2` module with programmable IO (PIO) support and DMA module.

See the `examples/rp2/` directory for some example code.

Expand Down
Loading