Graeme Winter

µPython for real time

Day 2: output - blink example “tidied up” so rather than using nop() everywhere use proper counting. Still some scope to polish this (e.g. by preloading the osr) but works nicely for generating 1MHz square wave output for 100s or so:

import time

from machine import Pin
import rp2

p0 = Pin(0)

# counter program using side-set to control output
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW)
def square():
    wrap_target()
    mov(x, osr).side(1)
    label("high")
    jmp(x_dec, "high")
    mov(x, osr).side(0)
    label("low")
    jmp(x_dec, "low")
    wrap()


# because 50% duty cycle square wave, need frequency to be even
# in here: clock divider of 12.5 is fine
sm = rp2.StateMachine(0, square, freq=10_000_000, sideset_base=p0)

# counts: two fewer than you think:
#
# mov() takes one instruction
# jmp x_dec only jumps when x_dec comes in as zero
#
# clock into the osr before program starts
sm.put(5 - 2)
sm.exec("pull()")

sm.active(1)
time.sleep(100)
sm.active(0)

# tidy
p0.off()

Useful for testing code which would e.g. use the PIO for pulse counting as can send in a well determined number of pulses with given width. Combining this with the timer from yesterday (running on different state machines) could be interesting. Trace demonstrating that this is ~ 1MHz and 2 x 500ns:

Oscilloscope trace

Code above could easily have different periods for high / low by clocking a low count into the isr as a second scratch register.

Working in this code with the counter from yesterday it looks like the planets align: two state machines, one running counter (which is started first) then the other running this square wave nicely shows that the timing appears to work:

import time

from machine import Pin
import rp2

p0 = Pin(0)

# counter program using side-set to control output
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW)
def square():
    wrap_target()
    mov(x, osr).side(1)
    label("high")
    jmp(x_dec, "high")
    mov(x, osr).side(0)
    label("low")
    jmp(x_dec, "low")
    wrap()


# counter for how many ticks we are high
@rp2.asm_pio()
def count_high():
    mov(x, invert(null))
    wait(1, pin, 0)
    label("high")
    jmp(x_dec, "next")
    label("next")
    jmp(pin, "high")
    mov(isr, x)
    push()


sm0 = rp2.StateMachine(0, square, sideset_base=p0)
sm1 = rp2.StateMachine(1, count_high, jmp_pin=p0)

# half a second - so should report 500,000,000ns
sm0.put(62500000 - 2)
sm0.exec("pull()")

# start the reader first as we are waiting on high
sm1.active(1)
sm0.active(1)

for j in range(10):
    high = 16 * (0xFFFFFFFF - sm1.get())
    print(high)

sm0.active(0)
sm1.active(0)

# tidy
p0.off()

Output is indeed printing 500,000,000 every second… 🥸 but nice.

Previous Next