|
| 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> |
| 37 | +# and ones as <700 ns hi, 556 ns lo>. |
| 38 | +_program = Program( |
| 39 | + """ |
| 40 | +.side_set 1 opt |
| 41 | +.wrap_target |
| 42 | + pull block side 0 |
| 43 | + out y, 16 side 0 ; get count of NeoPixel bits |
| 44 | +
|
| 45 | +bitloop: |
| 46 | + pull ifempty side 0 ; drive low |
| 47 | + out x 1 side 0 [5] |
| 48 | + jmp !x do_zero side 1 [3] ; drive high and branch depending on bit val |
| 49 | + jmp y--, bitloop side 1 [4] ; drive high for a one (long pulse) |
| 50 | + jmp end_sequence side 0 ; sequence is over |
| 51 | +
|
| 52 | +do_zero: |
| 53 | + jmp y--, bitloop side 0 [4] ; drive low for a zero (short pulse) |
| 54 | +
|
| 55 | +end_sequence: |
| 56 | + pull block side 0 ; get fresh 16 bit delay value |
| 57 | + out y, 16 side 0 ; get delay count |
| 58 | +wait_reset: |
| 59 | + jmp y--, wait_reset side 0 ; wait until delay elapses |
| 60 | +.wrap |
| 61 | + """ |
| 62 | +) |
| 63 | + |
| 64 | + |
| 65 | +class NeoPixelBackground( # pylint: disable=too-few-public-methods |
| 66 | + adafruit_pixelbuf.PixelBuf |
| 67 | +): |
| 68 | + def __init__( |
| 69 | + self, pin, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None |
| 70 | + ): |
| 71 | + if not pixel_order: |
| 72 | + pixel_order = GRB if bpp == 3 else GRBW |
| 73 | + elif isinstance(pixel_order, tuple): |
| 74 | + order_list = [RGBW[order] for order in pixel_order] |
| 75 | + pixel_order = "".join(order_list) |
| 76 | + |
| 77 | + byte_count = bpp * n |
| 78 | + bit_count = byte_count * 8 |
| 79 | + padding_count = byte_count % 2 |
| 80 | + |
| 81 | + if bit_count > 65536: |
| 82 | + raise ValueError("Too many pixels") |
| 83 | + |
| 84 | + # backwards, so that ulab byteswap corrects it! |
| 85 | + header = struct.pack(">H", (bit_count - 1) & 0xFFFF) |
| 86 | + trailer = b"\0" * padding_count + struct.pack(">H", 3840) |
| 87 | + |
| 88 | + self._sm = StateMachine( |
| 89 | + _program.assembled, |
| 90 | + auto_pull=False, |
| 91 | + first_sideset_pin=pin, |
| 92 | + out_shift_right=False, |
| 93 | + pull_threshold=16, |
| 94 | + frequency=12_800_000, |
| 95 | + **_program.pio_kwargs, |
| 96 | + ) |
| 97 | + |
| 98 | + super().__init__( |
| 99 | + n, |
| 100 | + brightness=brightness, |
| 101 | + byteorder=pixel_order, |
| 102 | + auto_write=auto_write, |
| 103 | + header=header, |
| 104 | + trailer=trailer, |
| 105 | + ) |
| 106 | + |
| 107 | + def _transmit(self, buf): |
| 108 | + self._sm.background_write(np.frombuffer(buf, dtype=np.uint16).byteswap()) |
| 109 | + |
| 110 | + |
| 111 | +if __name__ == "__main__": |
| 112 | + import board |
| 113 | + import rainbowio |
| 114 | + import time |
| 115 | + |
| 116 | + NEOPIXEL = board.NEOPIXEL |
| 117 | + NUM_PIXELS = 12 |
| 118 | + pixels = NeoPixelBackground(NEOPIXEL, NUM_PIXELS) |
| 119 | + i = 0 |
| 120 | + while True: |
| 121 | + pixels.fill(rainbowio.colorwheel(i := (i + 1) % 256)) |
| 122 | + time.sleep(0.01) |
0 commit comments