The next example program introduces new concepts: the pull
instruction, which receives data from the CircuitPython program; and registers
, which are similar to variables in that they can store values and some simple operations can be performed on those values. However, the uses of PIO registers are much more restricted than the use of variables in Python.
# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries # # SPDX-License-Identifier: MIT # # Adapted from the example https://github.com/raspberrypi/pico-examples/tree/master/pio/pio_blink import array import time import board import rp2pio import adafruit_pioasm blink = adafruit_pioasm.assemble( """ .program blink pull block ; These two instructions take the blink duration out y, 32 ; and store it in y forever: mov x, y set pins, 1 ; Turn LED on lp1: jmp x-- lp1 ; Delay for (x + 1) cycles, x is a 32 bit number mov x, y set pins, 0 ; Turn LED off lp2: jmp x-- lp2 ; Delay for the same number of cycles again jmp forever ; Blink forever! """ ) while True: for freq in [5, 8, 30]: with rp2pio.StateMachine( blink, frequency=125_000_000, first_set_pin=board.LED, wait_for_txstall=False, ) as sm: data = array.array("I", [sm.frequency // freq]) sm.write(data) time.sleep(3) time.sleep(0.5)
pull block ; These two instructions take the blink duration out y, 32 ; and store it in y
The instruction "pull block" says to wait until a value sent from CircuitPython is available ("block"), and then to pull that value into a holding area known as OSR
, or Output Shift Register. Then, all 32 bits of OSR are stored in the register called Y
. Later, the CircuitPython program will send in a number that represents how long the LED spends in an on or off state, so remember that this is what the register Y
now holds.
forever: mov x, y set pins, 1 ; Turn LED on lp1: jmp x-- lp1 ; Delay for (x + 1) cycles, x is a 32 bit number
The next few lines' purpose is to turn the LED on, then wait for the desired length of time.
Working up from the bottom of this section of code, the last two lines create a loop that delays for (x+1) cycles. lp1:
is a label, and a jmp
instruction can skip to it instead of continuing to the next instruction. In this case, the jump is conditional on x--
, which means "if X is not zero, jump to lp1. In any case, decrease X by 1." Because the delay numbers are large and because the program will to change the delay value without changing the PIO program itself, it cannot use the [#]
notation to delay as in the previous program.
The set
instruction to turn on the LED should be familiar by now.
The mov
instruction takes the value in Y and copies it to X. If not, and the loop used jmp y--
, then it would lose the original delay value. But the value is needed each time the program has a delay. Happily, there are two register X and Y so the program can just take a copy of the original delay value each time.
Remember that there's a forever:
label here, it will be used later.
mov x, y set pins, 0 ; Turn LED off lp2: jmp x-- lp2 ; Delay for the same number of cycles again jmp forever ; Blink forever!
The next block is very much like the previous block, except that set is used to turn the LED off before the delay.
The final line, jmp forever
, sends us back to the first delay. If it instead relied on the automatic wrap back to the first instruction, there would only be a single on-off blink before the program went back to the pull block instruction and waited for a new blink duration to be sent in, which would not give the desired result.
while True: for freq in [5, 8, 30]: with rp2pio.StateMachine( blink, frequency=125_000_000, first_set_pin=board.LED, wait_for_txstall=False, ) as sm: data = array.array("I", [sm.frequency // freq]) sm.write(data) time.sleep(3) time.sleep(0.5)
The Python forever-loop repeatedly cycles through the frequencies 5, 8, and 30.
For each frequency, it creates a state machine with our program, calculates and send the required delay value to it, and waits 3 seconds. Then, before continuing with the next blink pattern, it delays a half a second.
Of course, your CircuitPython program doesn't need to sleep while PIO is making the LED blink, it could be doing calculations, updating an LCD display, reading button presses. The PIO program keeps running independently of what CircuitPython is doing.
In many applications of PIO, such as sending data out to NeoPixels, a write
call needs to wait until the PIO program has completed. Since PIO programs run endlessly, there needs to be some definition of "completed". The usual definition is "the PIO program (through a pull instruction) requested fresh data from CircuitPython, but none was available". This is called a "transmit stall" or "txstall". Thus, the line wait_for_txstall=False
means that CircuitPython does not wait for this condition before the sm.write(data)
returns.
Text editor powered by tinymce.