From 1d4f729c1ad98457834599726ae73e32a037c4d4 Mon Sep 17 00:00:00 2001 From: caternuson Date: Fri, 4 Oct 2019 16:25:56 -0700 Subject: [PATCH 1/5] add NeoPixel_SPI class --- neopixel.py | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/neopixel.py b/neopixel.py index 9840700..a9f2a1a 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: + # 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,82 @@ 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 + + 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 spi: + try: + # get actual SPI frequency + freq = spi.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) + # 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.""" + 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._transmogrify(self.buf) + self.RESET) + + def _transmogrify(self, buf): + """Turn every BIT of buf into a special BYTE pattern.""" + out_buf = bytearray() + for byte in self.buf: + # MSB first + for i in range(7, -1, -1): + if byte >> i & 0x01: + out_buf.append(0b11110000) # A NeoPixel 1 bit + else: + out_buf.append(0b11000000) # A NeoPixel 0 bit + return out_buf \ No newline at end of file From 6d5085c51179267eb43757834e5a8e1f90107ca8 Mon Sep 17 00:00:00 2001 From: caternuson Date: Fri, 4 Oct 2019 16:29:53 -0700 Subject: [PATCH 2/5] lint --- neopixel.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/neopixel.py b/neopixel.py index a9f2a1a..fc21eb7 100644 --- a/neopixel.py +++ b/neopixel.py @@ -34,7 +34,7 @@ # imports needed for main NeoPixel class import digitalio from neopixel_write import neopixel_write -except: +except ModuleNotFoundError: # silently accept this, can still use NeoPixel SPI class pass @@ -259,7 +259,7 @@ class NeoPixel_SPI(NeoPixel): pixels = neopixel.NeoPixel_SPI(board.SPI(), 10) pixels.fill(0xff0000) """ - #pylint: disable=invalid-name + #pylint: disable=invalid-name, super-init-not-called FREQ = 6400000 # 800kHz * 8, actual may be different TRST = 80e-6 # Reset code low level time @@ -267,10 +267,10 @@ class NeoPixel_SPI(NeoPixel): 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 spi: + with self._spi as spibus: try: # get actual SPI frequency - freq = spi.frequency + freq = spibus.frequency except AttributeError: # use nominal freq = self.FREQ @@ -313,4 +313,4 @@ def _transmogrify(self, buf): out_buf.append(0b11110000) # A NeoPixel 1 bit else: out_buf.append(0b11000000) # A NeoPixel 0 bit - return out_buf \ No newline at end of file + return out_buf From 1b9b34093898534db01f4df9dbc589a6b7f1ebdb Mon Sep 17 00:00:00 2001 From: caternuson Date: Fri, 4 Oct 2019 16:36:41 -0700 Subject: [PATCH 3/5] and more lints --- neopixel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neopixel.py b/neopixel.py index fc21eb7..9e2afc1 100644 --- a/neopixel.py +++ b/neopixel.py @@ -34,7 +34,7 @@ # imports needed for main NeoPixel class import digitalio from neopixel_write import neopixel_write -except ModuleNotFoundError: +except NotImplementedError: # silently accept this, can still use NeoPixel SPI class pass @@ -301,9 +301,9 @@ def show(self): 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._transmogrify(self.buf) + self.RESET) + spi.write(self.RESET + self._transmogrify() + self.RESET) - def _transmogrify(self, buf): + def _transmogrify(self): """Turn every BIT of buf into a special BYTE pattern.""" out_buf = bytearray() for byte in self.buf: From adca4fa0d922d93178088b52cecd6095d1557197 Mon Sep 17 00:00:00 2001 From: caternuson Date: Fri, 4 Oct 2019 17:51:40 -0700 Subject: [PATCH 4/5] pre-allocate spi buffer --- neopixel.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/neopixel.py b/neopixel.py index 9e2afc1..94ec683 100644 --- a/neopixel.py +++ b/neopixel.py @@ -283,6 +283,7 @@ def __init__(self, spi, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_orde 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 @@ -298,19 +299,20 @@ def deinit(self): 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._transmogrify() + self.RESET) + spi.write(self.RESET + self.spibuf + self.RESET) def _transmogrify(self): """Turn every BIT of buf into a special BYTE pattern.""" - out_buf = bytearray() + k = 0 for byte in self.buf: # MSB first for i in range(7, -1, -1): if byte >> i & 0x01: - out_buf.append(0b11110000) # A NeoPixel 1 bit + self.spibuf[k] = 0b11110000 # A NeoPixel 1 bit else: - out_buf.append(0b11000000) # A NeoPixel 0 bit - return out_buf + self.spibuf[k] = 0b11000000 # A NeoPixel 0 bit + k += 1 From 163bd47a8385b993309a4b9100bb5956b2066a1d Mon Sep 17 00:00:00 2001 From: caternuson Date: Wed, 9 Oct 2019 07:49:31 -0700 Subject: [PATCH 5/5] fix brightness --- neopixel.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neopixel.py b/neopixel.py index 94ec683..09bb300 100644 --- a/neopixel.py +++ b/neopixel.py @@ -309,6 +309,7 @@ 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: