Using DMA as a beginner
-
- Posts: 4
- Joined: Fri Jun 18, 2021 3:29 pm
Using DMA as a beginner
Hello!
Am attempting to construct a triggerable function (envelope for synthesisers) generator using a Pi Pico and Micropython. The Pico has a 7bit R2R DAC across GPIO 16-22.
Have been successful using while loops and utime.sleep to play back lists of voltages but I find that there is an annoyingly low limit to the sample rate I can achieve, even though the unit is clocked at 125 MHz? I would like to get close to that value.
Have seen some posts that suggest using DMA is the answer but my Python is pretty basic and I don't know where to start.
Any help would be hugely appreciated!!!
Am attempting to construct a triggerable function (envelope for synthesisers) generator using a Pi Pico and Micropython. The Pico has a 7bit R2R DAC across GPIO 16-22.
Have been successful using while loops and utime.sleep to play back lists of voltages but I find that there is an annoyingly low limit to the sample rate I can achieve, even though the unit is clocked at 125 MHz? I would like to get close to that value.
Have seen some posts that suggest using DMA is the answer but my Python is pretty basic and I don't know where to start.
Any help would be hugely appreciated!!!
Re: Using DMA as a beginner
I have been investigating DMA myself for a couple of days and Roberthh's code is what got me going -
viewtopic.php?f=21&t=9697&start=10#p55104
This is my proof of concept version of that which outputs as a 7-bit DAC using 32-bit data. It's cobbled together from more generic code intended for something else so you can optimise out the "len(pioPins)", and use Roberthh's code to figure out how to use 8-bit data -
Edit : Changed 'set_init' and 'set_base' to 'out_init' and 'out_base'.
viewtopic.php?f=21&t=9697&start=10#p55104
This is my proof of concept version of that which outputs as a 7-bit DAC using 32-bit data. It's cobbled together from more generic code intended for something else so you can optimise out the "len(pioPins)", and use Roberthh's code to figure out how to use 8-bit data -
Edit : Changed 'set_init' and 'set_base' to 'out_init' and 'out_base'.
Code: Select all
from machine import Pin
from rp2 import asm_pio, PIO, StateMachine
import array
pioPins = []
pioPins.append(Pin(16, Pin.OUT))
pioPins.append(Pin(17, Pin.OUT))
pioPins.append(Pin(18, Pin.OUT))
pioPins.append(Pin(19, Pin.OUT))
pioPins.append(Pin(20, Pin.OUT))
pioPins.append(Pin(21, Pin.OUT))
pioPins.append(Pin(22, Pin.OUT))
@asm_pio(out_init = [PIO.OUT_LOW] * len(pioPins),
autopull = True,
pull_thresh = (32 - ((32 // len(pioPins)) * len(pioPins))),
out_shiftdir = PIO.SHIFT_RIGHT
)
def PioDac(LEN=len(pioPins)):
out(x, LEN) # Get the bits to output, lsb's of pulled 32-bit word
mov(pins, x) # Output the bits
if True:
mov(isr, x) # Report what was sent for debugging
push(block)
sm = StateMachine(0, PioDac, out_base=pioPins[0])
sm.active(1)
# Credit : Roberthh
# https://forum.micropython.org/viewtopic.php?f=21&t=9697&start=10#p55104
import uctypes
@micropython.viper
def Dma():
channel = uint(0)
DMA_BASE = const(0x50000000)
READ_ADDR = const(0)
WRITE_ADDR = const(1)
TRANS_COUNT = const(2)
CTRL_TRIG = const(3)
AL1_CTRL = const(4)
PIO0_BASE = const(0x50200000)
PIO0_BASE_TXF0 = const(PIO0_BASE + 0x10)
dma = ptr32(uint(DMA_BASE) + channel * 0x40)
dma[READ_ADDR] = uint(ptr32(wave32)) # Buffer to write
dma[WRITE_ADDR] = uint(PIO0_BASE_TXF0) # Where to write to
dma[TRANS_COUNT] = uint(len(wave32)) # Items to transfer
IRQ_QUIET = const(0x1) # No interrupt
TREQ_SEL = const(0x00) # Wait for PIO0_TX0
CHAIN_TO = const(0) # Do not chain
RING_SEL = const(0)
RING_SIZE = const(0) # No wrapping
INCR_WRITE = const(0) # For write to array
INCR_READ = const(1) # For read from array
DATA_SIZE = const(2) # 32-bit transfer
HIGH_PRIORITY = const(1) # High priority
ENABLE = const(1) # Enabled
TRIG = const((IRQ_QUIET << 21) |
(TREQ_SEL << 15) |
(CHAIN_TO << 11) |
(RING_SEL << 10) |
(RING_SIZE << 9) |
(INCR_WRITE << 5) |
(INCR_READ << 4) |
(DATA_SIZE << 2) |
(HIGH_PRIORITY << 1) |
(ENABLE << 0))
dma[CTRL_TRIG] = uint(TRIG)
wave32 = array.array("L")
wave32.append(0b0000000)
wave32.append(0b0000001)
wave32.append(0b0000011)
wave32.append(0b0000111)
wave32.append(0b0001111)
wave32.append(0b0011111)
wave32.append(0b0111111)
wave32.append(0b1111111)
Dma()
count = 0
while sm.rx_fifo() > 0:
count += 1
print("{:>2} : {:0>7}".format(count, bin(sm.get())[2:]))
Last edited by hippy on Thu Jun 24, 2021 9:14 am, edited 1 time in total.
Re: Using DMA as a beginner
Did you see this post: viewtopic.php?f=21&t=9945&p=55659&hilit ... tor#p55659
With links to https://www.instructables.com/Arbitrary ... ry-Pi-Pic/ and
viewtopic.php?f=21&t=9697
With links to https://www.instructables.com/Arbitrary ... ry-Pi-Pic/ and
viewtopic.php?f=21&t=9697
-
- Posts: 4
- Joined: Fri Jun 18, 2021 3:29 pm
Re: Using DMA as a beginner
V helpful thanks!
I'll try and get my head around it
I was aware of that post, my design is partially based off the instructable!
I'll try and get my head around it
I was aware of that post, my design is partially based off the instructable!
-
- Posts: 4
- Joined: Fri Jun 18, 2021 3:29 pm
Re: Using DMA as a beginner
Been going through this for a while and am still unsure how to get it to actually send the voltages to the pins in hippy's code- it just seems to print the values.
I assume PioDac is the key but it doesn't behave like any function I've come across before.
I assume PioDac is the key but it doesn't behave like any function I've come across before.
Re: Using DMA as a beginner
If it is printing the values then that is good news because it means PIO is running and being driven by DMA, and the "mov(pins, x)" should be setting the pins to the desired values before it pushes those values for MicroPython to print.ndclarkehall wrote: ↑Wed Jun 23, 2021 12:27 pmBeen going through this for a while and am still unsure how to get it to actually send the voltages to the pins in hippy's code- it just seems to print the values.
I don't have a Pico configured to verify what's actually being set on the output pins. I would have expected it to work but maybe it doesn't.
Update : Seems it doesn't - Please accept my apologies. Have I mentioned that I love the idea of PIO, hate the way they are implemented, and configured using '@asm_pio' and 'StateMachine' ?
Change 'set_init=' and 'set_base=' to 'out_init=' and 'out_base=' and I think everything should work ...
Code: Select all
@asm_pio(out_init = [PIO.OUT_LOW] * len(pioPins),
Code: Select all
sm = StateMachine(0, PioDac, out_base=pioPins[0])
Code: Select all
from machine import mem32
SIO_BASE = 0xD0000000
GPIO_IN = SIO_BASE + 0x004
inputBits = mem32[GPIO_IN]
-
- Posts: 4
- Joined: Fri Jun 18, 2021 3:29 pm
Re: Using DMA as a beginner
That's working now! Thanks
Is there a way to reload the DMA without rerunning the script?
The brief needs the program to run as a standalone where triggers on a GPIO pin play back wave32 from the beginning.
Is there a way to reload the DMA without rerunning the script?
The brief needs the program to run as a standalone where triggers on a GPIO pin play back wave32 from the beginning.
Re: Using DMA as a beginner
Untested but I imagine you could do something like ...
Code: Select all
btn = Pin(PIN_NUMBER, Pin.IN, Pin.PULL_UP) # Short to 0V when pushed button
while True:
while btn.value() == 0 : pass # Wait for button to be released
while btn.value() != 0 : pass # Wait for button to be pushed
Dma()