Skip to content

Commit 387abbb

Browse files
committed
Add neopixel background example
1 parent 3a9a028 commit 387abbb

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

examples/pioasm_neopixel_bg.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""Demonstrate background writing with NeoPixels
6+
7+
The NeoPixelBackground class defined here is largely compatible with the
8+
standard NeoPixel class, except that the ``show()`` method returns immediately,
9+
writing data to the LEDs in the background.
10+
11+
Writing the LED data in the background will allow more time for your
12+
Python code to run, so it may be possible to slightly increase the refresh
13+
rate of your LEDs or do more complicated processing.
14+
15+
The demonstration code, under ``if __name__ == '__main__':`` is intended
16+
for the Adafruit MacroPad, with 12 NeoPixel LEDs. It shows a cycling rainbow
17+
pattern across all the LEDs.
18+
"""
19+
20+
import struct
21+
import adafruit_pixelbuf
22+
from ulab import numpy as np
23+
from rp2pio import StateMachine
24+
from adafruit_pioasm import Program
25+
26+
# Pixel color order constants
27+
RGB = "RGB"
28+
"""Red Green Blue"""
29+
GRB = "GRB"
30+
"""Green Red Blue"""
31+
RGBW = "RGBW"
32+
"""Red Green Blue White"""
33+
GRBW = "GRBW"
34+
"""Green Red Blue White"""
35+
36+
# NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> and ones
37+
# and ones as <700 ns hi, 556 ns lo>.
38+
# cycle. The first two instructions always run while only one of the two final
39+
# instructions run per bit. We start with the low period because it can be
40+
# longer while waiting for more data.
41+
_program = Program(
42+
"""
43+
.side_set 1 opt
44+
.wrap_target
45+
pull block side 0
46+
out y, 16 side 0 ; get count of NeoPixel bits
47+
48+
bitloop:
49+
pull ifempty side 0 ; drive low
50+
out x 1 side 0 [5]
51+
jmp !x do_zero side 1 [3] ; drive high and branch depending on bit val
52+
jmp y--, bitloop side 1 [4] ; drive high for a one (long pulse)
53+
jmp end_sequence side 0 ; sequence is over
54+
55+
do_zero:
56+
jmp y--, bitloop side 0 [4] ; drive low for a zero (short pulse)
57+
58+
end_sequence:
59+
pull block side 0 ; get fresh 16 bit delay value
60+
out y, 16 side 0 ; get delay count
61+
wait_reset:
62+
jmp y--, wait_reset side 0 ; wait until delay elapses
63+
.wrap
64+
"""
65+
)
66+
67+
68+
class NeoPixelBackground( # pylint: disable=too-few-public-methods
69+
adafruit_pixelbuf.PixelBuf
70+
):
71+
def __init__(
72+
self, pin, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None
73+
):
74+
if not pixel_order:
75+
pixel_order = GRB if bpp == 3 else GRBW
76+
elif isinstance(pixel_order, tuple):
77+
order_list = [RGBW[order] for order in pixel_order]
78+
pixel_order = "".join(order_list)
79+
80+
byte_count = bpp * n
81+
bit_count = byte_count * 8
82+
padding_count = byte_count % 2
83+
84+
if bit_count > 65536:
85+
raise ValueError("Too many pixels")
86+
87+
# backwards, so that ulab byteswap corrects it!
88+
header = struct.pack(">H", (bit_count - 1) & 0xFFFF)
89+
trailer = b"\0" * padding_count + struct.pack(">H", 3840)
90+
91+
self._sm = StateMachine(
92+
_program.assembled,
93+
auto_pull=False,
94+
first_sideset_pin=pin,
95+
out_shift_right=False,
96+
pull_threshold=16,
97+
frequency=12_800_000,
98+
**_program.pio_kwargs,
99+
)
100+
101+
super().__init__(
102+
n,
103+
brightness=brightness,
104+
byteorder=pixel_order,
105+
auto_write=auto_write,
106+
header=header,
107+
trailer=trailer,
108+
)
109+
110+
def _transmit(self, buf):
111+
self._sm.background_write(np.frombuffer(buf, dtype=np.uint16).byteswap())
112+
113+
114+
if __name__ == "__main__":
115+
import board
116+
import rainbowio
117+
import time
118+
119+
NEOPIXEL = board.NEOPIXEL
120+
NUM_PIXELS = 12
121+
pixels = NeoPixelBackground(NEOPIXEL, NUM_PIXELS)
122+
i = 0
123+
while True:
124+
pixels.fill(rainbowio.colorwheel(i := (i + 1) % 256))
125+
time.sleep(0.01)

0 commit comments

Comments
 (0)