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.