This is all great, but you probably have existing code using the Animations library that you'd like to use with NeoPIO. How can we make the two work together? By implementing a subclass of PixelBuf! Select parts of the code are listed below.

Instead of taking a single I/O pin, the constructor takes three: data, clock, and strobe. It also requires that you specify the number of strands (num_strands), from 2 to 8. The first step is some necessary error checking:

class NeoPIO(adafruit_pixelbuf.PixelBuf):
    def __init__(
        self, data, clock, strobe, n, *, num_strands=8, bpp=3, brightness=1.0, auto_write=True, pixel_order=None
    ):
        if not _pin_directly_follows(data, clock):
            raise ValueError("clock pin must directly follow data pin")
        if not _pin_directly_follows(clock, strobe):
            raise ValueError("strobe pin must directly follow clock pin")

        if n % num_strands:
            raise ValueError("Length must be a multiple of num_strands")

Next, the steps to construct the underlying PixelBuf object, just like NeoPixel:

…
        if not pixel_order:
            pixel_order = GRB if bpp == 3 else GRBW
        else:
            if isinstance(pixel_order, tuple):
                order_list = [RGBW[order] for order in pixel_order]
                pixel_order = "".join(order_list)

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

Last, we need to store the number of strands so we can use that number later, create some scratch memory for the transposed bits, and then (the interesting part!) create the PIO state machine:

…
        self._transposed = bytearray(bpp*n*8//num_strands)
        self._num_strands = num_strands

        self._sm = rp2pio.StateMachine(
            _assembled,
            frequency=800_000 * 52,
            init=adafruit_pioasm.assemble("set pindirs 7"),
            first_out_pin=data,
            out_pin_count=1,
            first_set_pin=data,
            set_pin_count=3,
            first_sideset_pin=clock,
            sideset_pin_count=2,
            auto_pull=True,
            out_shift_right=False,
            pull_threshold=8,
        )

There are some other definitions to supply that are identical to NeoPixel, or are not very interesting, such as the method deinit and the property num_strands.

The last thing to provide is the _transmit method, to blast out those pixel values. There are two steps—transposing the bits, then writing them out—yet It's almost anticlimactic how simple this is:

…
    def _transmit(self, buffer):
        bitops.bit_transpose(buffer, self._transposed, self._num_strands)
        self._sm.write(self._transposed)

This guide was first published on Feb 23, 2021. It was last updated on 2021-02-23 12:32:27 -0500.

This page (Code Walkthrough: Being a PixelBuf) was last updated on Oct 15, 2021.

Text editor powered by tinymce.