As of CircuitPython 7.3, it is possible to send data to one or more PIO peripherals while Python code keeps running. Depending on how the background_write
function is called, it can send a block of data just once, or repeatedly until it's directed otherwise.
To introduce this capability, the following example shows morse code messages on the board's LED. It works on any RP2040 board with board.LED
. You can also use it on a Raspberry Pi Pico, but you'll have to add an external LED and series resistor, then modify the code to use the right board.GP##
connection.
# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries # # SPDX-License-Identifier: MIT """Demonstrate background writing, including loop writing, with morse code. On any rp2040 board with board.LED, this will alternately send 'SOS' and 'TEST' via the LED, demonstrating that Python code continues to run while the morse code data is transmitted. Alternately, change one line below to make it send 'TEST' forever in a loop, again while Python code continues to run. The combination of "LED status" and duration is sent to the PIO as 16-bit number: The top bit is 1 if the LED is turned on and 0 otherwise. The other 15 bits form a delay value from 1 to 32767. A subset of the morse code 'alphabit' is created, with everthing based on the 'DIT duration' of about 128ms (1MHz / 32 / 4000). https://en.wikipedia.org/wiki/Morse_code """ import array import time from board import LED from rp2pio import StateMachine from adafruit_pioasm import Program # This program turns the LED on or off depending on the first bit of the value, # then delays a length of time given by the next 15 bits of the value. # By correctly choosing the durations, a message in morse code can be sent. pio_code = Program( """ out x, 1 mov pins, x out x, 15 busy_wait: jmp x--, busy_wait [31] """ ) # The top bit of the command is the LED value, on or off LED_ON = 0x8000 LED_OFF = 0x0000 # The other 15 bits are a delay duration. # It must be the case that 4 * DIT_DURATION < 32768 DIT_DURATION = 4000 DAH_DURATION = 3 * DIT_DURATION # Build up some elements of morse code, based on the wikipedia article. DIT = array.array("H", [LED_ON | DIT_DURATION, LED_OFF | DIT_DURATION]) DAH = array.array("H", [LED_ON | DAH_DURATION, LED_OFF | DIT_DURATION]) # That is, two more DAH-length gaps for a total of three LETTER_SPACE = array.array("H", [LED_OFF | (2 * DAH_DURATION)]) # That is, four more DAH-length gaps (after a letter space) for a total of seven WORD_SPACE = array.array("H", [LED_OFF | (4 * DIT_DURATION)]) # Letters and words can be created by concatenating ("+") the elements E = DIT + LETTER_SPACE O = DAH + DAH + DAH + LETTER_SPACE S = DIT + DIT + DIT + LETTER_SPACE T = DAH + LETTER_SPACE SOS = S + O + S + WORD_SPACE TEST = T + E + S + T + WORD_SPACE sm = StateMachine( pio_code.assembled, frequency=1_000_000, first_out_pin=LED, pull_threshold=16, auto_pull=True, out_shift_right=False, **pio_code.pio_kwargs, ) # To simply repeat 'TEST' forever, change to 'if True': if False: # pylint: disable=using-constant-test print("Sending out TEST forever", end="") sm.background_write(loop=TEST) while True: print(end=".") time.sleep(0.1) # But instead, let's alternate SOS and TEST, forever: while True: for plain, morse in ( ("SOS", SOS), ("TEST", TEST), ): print(f"Sending out {plain}", end="") sm.background_write(morse) sm.clear_txstall() while not sm.txstall: print(end=".") time.sleep(0.1) print() print("Message all sent to StateMachine (including emptying FIFO)") print()
Now to focus on some specific parts of the program. First, take a look at the PIO program itself. This program reads the new LED state first, sets it on the pin, reads a delay, then loops until the delay has been completed:
pio_code = Program( """ out x, 1 mov pins, x out x, 15 busy_wait: jmp x--, busy_wait [31] """ )
Next, according to the necessary arrangement of bits, a small vocabulary in Morse code is built up:
# Letters and words can be created by concatenating ("+") the elements E = DAH + LETTER_SPACE O = DAH + DAH + DAH + LETTER_SPACE S = DIT + DIT + DIT + LETTER_SPACE T = DIT + LETTER_SPACE SOS = S + O + S + WORD_SPACE TEST = T + E + S + T + WORD_SPACE
Now, of course the Python code could simply write the data to the State Machine and wait for it to finish with sm.write(SOS)
. However, by using this method, control won't return to the Python code until all the data is handled. In the case where this is undesirable, use a background write so that the operation continues in the background while CircuitPython code continues working in the foreground:
sm.background_write(morse) sm.clear_txstall() while not sm.txstall: print(end=".") time.sleep(0.1) print() print("Message all sent to StateMachine (including emptying FIFO)") print()
By clearing and then monitoring the txstall
flag, the code waits for the whole message to be played before continuing.
Using the loop
keyword argument, a single message can be made to loop forever:
print("Sending out TEST forever", end="") sm.background_write(loop=TEST) while True: print(end=".") time.sleep(0.1)
That's one LED, but what if you have greater ambitions? Head to the next page: Driving 7-segment displays with Background Writes.
Text editor powered by tinymce.