diff --git a/neopixel.py b/neopixel.py index 9840700..09bb300 100644 --- a/neopixel.py +++ b/neopixel.py @@ -30,8 +30,13 @@ import math -import digitalio -from neopixel_write import neopixel_write +try: + # imports needed for main NeoPixel class + import digitalio + from neopixel_write import neopixel_write +except NotImplementedError: + # silently accept this, can still use NeoPixel SPI class + pass __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel.git" @@ -230,3 +235,85 @@ def show(self): neopixel_write(self.pin, self.buf) else: neopixel_write(self.pin, bytearray([int(i * self.brightness) for i in self.buf])) + +class NeoPixel_SPI(NeoPixel): + """ + A sequence of neopixels. + + :param ~busio.SPI spi: The SPI bus to output neopixel data on. + :param int n: The number of neopixels in the chain + :param int bpp: Bytes per pixel. 3 for RGB and 4 for RGBW pixels. + :param float brightness: Brightness of the pixels between 0.0 and 1.0 where 1.0 is full + brightness + :param bool auto_write: True if the neopixels should immediately change when set. If False, + `show` must be called explicitly. + :param tuple pixel_order: Set the pixel color channel order. GRBW is set by default. + + Example: + + .. code-block:: python + + import board + import neopixel + + pixels = neopixel.NeoPixel_SPI(board.SPI(), 10) + pixels.fill(0xff0000) + """ + #pylint: disable=invalid-name, super-init-not-called + + FREQ = 6400000 # 800kHz * 8, actual may be different + TRST = 80e-6 # Reset code low level time + + def __init__(self, spi, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None): + from adafruit_bus_device.spi_device import SPIDevice + self._spi = SPIDevice(spi, baudrate=self.FREQ) + with self._spi as spibus: + try: + # get actual SPI frequency + freq = spibus.frequency + except AttributeError: + # use nominal + freq = self.FREQ + self.RESET = bytes([0]*round(freq*self.TRST)) + self.n = n + if pixel_order is None: + self.order = GRBW + self.bpp = bpp + else: + self.order = pixel_order + self.bpp = len(self.order) + self.buf = bytearray(self.n * self.bpp) + self.spibuf = bytearray(8*len(self.buf)) + # Set auto_write to False temporarily so brightness setter does _not_ + # call show() while in __init__. + self.auto_write = False + self.brightness = brightness + self.auto_write = auto_write + + def deinit(self): + """Blank out the NeoPixels.""" + for i in range(len(self.buf)): + self.buf[i] = 0 + self.show() + + def show(self): + """Shows the new colors on the pixels themselves if they haven't already + been autowritten.""" + self._transmogrify() + with self._spi as spi: + # write out special byte sequence surrounded by RESET + # leading RESET needed for cases where MOSI rests HI + spi.write(self.RESET + self.spibuf + self.RESET) + + def _transmogrify(self): + """Turn every BIT of buf into a special BYTE pattern.""" + k = 0 + for byte in self.buf: + byte = int(byte * self.brightness) + # MSB first + for i in range(7, -1, -1): + if byte >> i & 0x01: + self.spibuf[k] = 0b11110000 # A NeoPixel 1 bit + else: + self.spibuf[k] = 0b11000000 # A NeoPixel 0 bit + k += 1