To do anything non-trivial needs DMA, which is work in progress, but had a quick moment at accessing RAM from µPython and turns out to be pretty easy…
from uctypes import addressof
from machine import mem16
from array import array
# initialise array
scratch = array("H", [0xffff for _ in range(1024)])
# grab address and overwrite
address = addressof(scratch)
for j in range(0, 2048, 2):
mem16[address + j] = j // 2
for j, s in enumerate(scratch):
assert j == s
Useful if you are driving stuff from e.g. ADC to DMA by writing the right registers… driving the ADC for one shot is at least relatively simple:
from machine import mem32
# prepare the pin (set the function to NULL which is 0b11111, needed for ADC)
IO_BANK_BASE = 0x40014000
GPIO26_CTRL = IO_BANK_BASE + 0xD4
mem32[GPIO26_CTRL] = 0b11111
# configure the ADC inc. FIFO - registers
ADC_BASE = 0x4004C000
ADC_CS = ADC_BASE + 0x0
ADC_RESULT = ADC_BASE + 0x4
# trigger ADC for one read
mem32[ADC_CS] = 0x4 + 0x1
# read value
result = mem32[ADC_RESULT]
# report the value remembering that the range is 12 but not 16
print(result)
Now this is where things get a whole bunch more involved. Much more involved: FIFO control and status register, enabling DREQ for DMA, not chaining (because I don’t want to go that fast) and setting up a scratch array to copy the data into.
Job list:
I think that should be enough…
Easy -> as above, has to be type uint16_t also known as “H” in µPython. Just make sure that this is allocated to be sufficiently large for however many measurements I will need. Looks like using ~ 100kB will be fine as gc.mem_free() shows about 170kB.
Register pointers:
# configure the ADC inc. FIFO - registers
ADC_BASE = 0x4004c000
ADC_CS = ADC_BASE + 0x0
ADC_FCS = ADC_BASE + 0x8
ADC_FIFO = ADC_BASE + 0xC
N.B. for this using DREQ not IRQ so want to operate in “quiet” mode. The ADC DREQ value is 0x24. Need to set the FIFO threshold to 1, enable the DREQ, enable the FIFO. ADC CS register needs to be set to 9: START_MANY|EN. Set up the DMA to read from the ADC_FIFO address.
Should really do something simple with this first like using DMA to fill an array with a specific byte pattern from a single word -> do that next. Setting that up should be simple and equivalent and … easy to debug. No DREQ as will be as fast as possible => 0x3f. INCR_WRITE 👍 but INCR_READ 👎.
from machine import mem32
from uctypes import addressof
from array import array
COUNT = 1024
target = array("H", [0 for j in range(COUNT)])
source = array("H", [0xFFFF])
# DMA registers
DMA_BASE = 0x50000000
CH0_READ_ADDR = DMA_BASE + 0x0
CH0_WRITE_ADDR = DMA_BASE + 0x4
CH0_TRANS_COUNT = DMA_BASE + 0x8
CH0_CTRL_TRIG = DMA_BASE + 0xC
QUIET = 0x1 << 21
DREQ = 0x3F << 15
WRITE_INCR = 0x1 << 5
DATA_SIZE = 0x1 << 2
ENABLE = 1
mem32[CH0_READ_ADDR] = addressof(source)
mem32[CH0_WRITE_ADDR] = addressof(target)
mem32[CH0_TRANS_COUNT] = COUNT
mem32[CH0_CTRL_TRIG] = QUIET + DREQ + WRITE_INCR + DATA_SIZE + ENABLE
BUSY = 0x1 << 24
while mem32[CH0_CTRL_TRIG] & BUSY:
continue
for t in target:
assert t == 0xFFFF
That seems easy enough (lots of 65535). Rest will wait until tomorrow.