One of the things I really liked in the rp2040 is almost every (or actually every?) register was aliased to another copy which could be toggled, rather than going through a read / modify / write cycle. Reading through the SAMD51 data sheet just now in the PORT controller chapter, found a section on toggle registers for the output ports -> win.
LED on the board is on pin D13, which is port B (offset 0x80
) - poking 0x2
at OUTTGL (+0x1c
) will toggle an already configured pin, so:
from machine import Pin
import time
led = Pin("D13")
@micropython.asm_thumb
def fast_blink(r0):
mov(r1, 2)
# move a big value into r2, say 1<<20
data(2, 0b11110_1_0_0010_0_1111, 0b0_001_0010_10000000)
label(start)
str(r1, [r0, 0])
sub(r2, r2, 1)
cmp(r2, 0)
bgt(start)
def nop(arg):
pass
BASE=0x41008000+0x80+0x1C
t0 = time.ticks_us()
nop(BASE)
t1 = time.ticks_us()
t_nop = t1 - t0
t0 = time.ticks_us()
fast_blink(BASE)
t1 = time.ticks_us()
t_blink = t1 - t0
print(f"Extra time: {t_blink - t_nop:d}µs")
Set up the D13 pin using the usual µPython then 0x100000 (1048576) times toggle the LED on and off. This turns out to be surprisingly quick - executing it suggests an overhead of ~ 52500µs, which makes for around 0.05µs / sample. This time looks like about 6 clock ticks… which is about right? str
is two ticks, sub
, cmp
one and then bgt
takes two ticks if taken, one otherwise.
500,000 blinks in 50ms - it’ll do, fast-wise.