Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 8041

MicroPython • Experiments in chaining DMA to PIO - strange error and room for improvement?

$
0
0
I've written some Python to drive a LED bar (https://thepihut.com/products/bi-color- ... -pack-of-2) from a PIO program fed by a chained pair of DMAs. The LEDs are multiplexed so only 1/3 can be driven at any one time. The PIO+DMA combo takes care of switching the signals without any CPU involvement.

I do have it working, but a few questions have come up...

Q1. If the first version of the PIO program is fine, why does the second one raise a PIOASMError: delay too large error (on the line with the comment)?

Good:

Code:

@rp2.asm_pio(out_init=(rp2.PIO.OUT_LOW,) * 8,             sideset_init=(rp2.PIO.OUT_LOW,) * 3,             autopull=True,             pull_thresh=8)def cycle_multiplex():    out(pins, 8).side(1) [3]    out(pins, 8).side(2) [3]    out(pins, 8).side(4) [3]
Bad:

Code:

@rp2.asm_pio(out_init=(rp2.PIO.OUT_LOW,) * 8,             sideset_init=(rp2.PIO.OUT_LOW,) * 3,             autopull=True,             pull_thresh=8)def cycle_multiplex():    out(pins, 8).side(1) [3]# Now I'm only allowed a single bit of wait    nop() [3]    out(pins, 8).side(2) [3]    nop() [3]    out(pins, 8).side(4) [3]    nop() [3]
I don't actually need the additional delay - the loop is already suitably slow, but the same error came up in an earlier version of the program when I was using pull(block) and mov(pins) (I think), and I'd like to understand why a previous/subsequent instruction alters the number of wait bits available to other instructions.

Q2. For the chaining, I need the secondary DMA to be configured to reset the read address of the data-mover DMA. The result I've ended up with feels quite clunky - I need to pass the address of a location that contains the address of the start of the source data. I'm sure there must be a cleaner way than what I have here, but it eludes me:

Code:

move = rp2.DMA()ctrl = rp2.DMA()self.values = bytearray(3 * 4)pointer = bytearray(4)addr = addressof(self.values)# Q. Is there a nicer way of doing this?pointer[0] = addr & 0xffpointer[1] = (addr >> 8) & 0xffpointer[2] = (addr >> 16) & 0xffpointer[3] = (addr >> 24) & 0xffDATA_REQUEST_INDEX = ((self.sm // 4) << 3) + (self.sm % 4)# Configure the 'move' channel to:# - transfer 32-bit words# - incrementing its read address# - *not* incrementing its write address# - chaining to the control channel# - using the DREQ for the state machinemove_cfg = move.pack_ctrl(        chain_to = ctrl.channel,        size = 2,        inc_read = True,        inc_write = False,        treq_sel = DATA_REQUEST_INDEX    )move.config(        read = self.values,        write = self.multiplexer,        count = len(self.values) // 4,        ctrl = move_cfg,        trigger = False    )# Configure the 'ctrl' channel to:# - transfer 32-bit words# - *not* incrementing either address# - chaining to the move channelctrl_cfg = ctrl.pack_ctrl(        chain_to = move.channel,        size = 2,        inc_read = False,        inc_write = False    )ctrl.config(        read = pointer,        write = addressof(move.registers[0:1]),        count = 1,        ctrl = ctrl_cfg,        trigger = False    )# And finally, start one of the channels# (chaining will star the other)ctrl.active(1)

Statistics: Posted by chrisjl — Wed Sep 10, 2025 6:18 pm



Viewing all articles
Browse latest Browse all 8041

Trending Articles