Skip to content

match the latest core code #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions examples/pioasm_neopixel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@
import microcontroller
import adafruit_pioasm

# NeoPixels are 800khz bit streams. Zeroes are 1/3 duty cycle (~416ns) and ones
# are 2/3 duty cycle (~833ns).
# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo>
# and ones as <700 ns hi, 556 ns lo>.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a repetition of "and ones"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhalbert will want to fix this in the core, I think I faithfully copy-pasted it 😀

# The first two instructions always run while only one of the two final
# instructions run per bit. We start with the low period because it can be
# longer while waiting for more data.
program = """
.program ws2812
.side_set 1
.wrap_target
bitloop:
out x 1 side 0 [1]; Side-set still takes place when instruction stalls
jmp !x do_zero side 1 [1]; Branch on the bit we shifted out. Positive pulse
do_one:
jmp bitloop side 1 [1]; Continue driving high, for a long pulse
do_zero:
nop side 0 [1]; Or drive low, for a short pulse
out x 1 side 0 [6]; Drive low. Side-set still takes place before instruction stalls.
jmp !x do_zero side 1 [3]; Branch on the bit we shifted out previous delay. Drive high.
do_one:
jmp bitloop side 1 [4]; Continue driving high, for a one (long pulse)
do_zero:
nop side 0 [4]; Or drive low, for a zero (short pulse)
.wrap
"""

Expand All @@ -35,7 +38,7 @@

sm = rp2pio.StateMachine(
assembled,
frequency=800000 * 6, # 800khz * 6 clocks per bit
frequency=12_800_000, # to get appropriate sub-bit times in PIO program
first_sideset_pin=NEOPIXEL,
auto_pull=True,
out_shift_right=False,
Expand Down
122 changes: 122 additions & 0 deletions examples/pioasm_neopixel_bg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""Demonstrate background writing with NeoPixels

The NeoPixelBackground class defined here is largely compatible with the
standard NeoPixel class, except that the ``show()`` method returns immediately,
writing data to the LEDs in the background.

Writing the LED data in the background will allow more time for your
Python code to run, so it may be possible to slightly increase the refresh
rate of your LEDs or do more complicated processing.

The demonstration code, under ``if __name__ == '__main__':`` is intended
for the Adafruit MacroPad, with 12 NeoPixel LEDs. It shows a cycling rainbow
pattern across all the LEDs.
"""

import struct
import adafruit_pixelbuf
from ulab import numpy as np
from rp2pio import StateMachine
from adafruit_pioasm import Program

# Pixel color order constants
RGB = "RGB"
"""Red Green Blue"""
GRB = "GRB"
"""Green Red Blue"""
RGBW = "RGBW"
"""Red Green Blue White"""
GRBW = "GRBW"
"""Green Red Blue White"""

# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo>
# and ones as <700 ns hi, 556 ns lo>.
_program = Program(
"""
.side_set 1 opt
.wrap_target
pull block side 0
out y, 16 side 0 ; get count of NeoPixel bits

bitloop:
pull ifempty side 0 ; drive low
out x 1 side 0 [5]
jmp !x do_zero side 1 [3] ; drive high and branch depending on bit val
jmp y--, bitloop side 1 [4] ; drive high for a one (long pulse)
jmp end_sequence side 0 ; sequence is over

do_zero:
jmp y--, bitloop side 0 [4] ; drive low for a zero (short pulse)

end_sequence:
pull block side 0 ; get fresh 16 bit delay value
out y, 16 side 0 ; get delay count
wait_reset:
jmp y--, wait_reset side 0 ; wait until delay elapses
.wrap
"""
)


class NeoPixelBackground( # pylint: disable=too-few-public-methods
adafruit_pixelbuf.PixelBuf
):
def __init__(
self, pin, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None
):
if not pixel_order:
pixel_order = GRB if bpp == 3 else GRBW
elif isinstance(pixel_order, tuple):
order_list = [RGBW[order] for order in pixel_order]
pixel_order = "".join(order_list)

byte_count = bpp * n
bit_count = byte_count * 8
padding_count = byte_count % 2

if bit_count > 65536:
raise ValueError("Too many pixels")

# backwards, so that ulab byteswap corrects it!
header = struct.pack(">H", (bit_count - 1) & 0xFFFF)
trailer = b"\0" * padding_count + struct.pack(">H", 3840)

self._sm = StateMachine(
_program.assembled,
auto_pull=False,
first_sideset_pin=pin,
out_shift_right=False,
pull_threshold=16,
frequency=12_800_000,
**_program.pio_kwargs,
)

super().__init__(
n,
brightness=brightness,
byteorder=pixel_order,
auto_write=auto_write,
header=header,
trailer=trailer,
)

def _transmit(self, buf):
self._sm.background_write(np.frombuffer(buf, dtype=np.uint16).byteswap())


if __name__ == "__main__":
import board
import rainbowio
import time

NEOPIXEL = board.NEOPIXEL
NUM_PIXELS = 12
pixels = NeoPixelBackground(NEOPIXEL, NUM_PIXELS)
i = 0
while True:
pixels.fill(rainbowio.colorwheel(i := (i + 1) % 256))
time.sleep(0.01)