Skip to content

Commit b94f06d

Browse files
authored
Merge pull request #53 from caternuson/spi_neopixel
Add SPI driven NeoPixel.
2 parents c0bdd8b + 163bd47 commit b94f06d

File tree

1 file changed

+89
-2
lines changed

1 file changed

+89
-2
lines changed

neopixel.py

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@
3030

3131
import math
3232

33-
import digitalio
34-
from neopixel_write import neopixel_write
33+
try:
34+
# imports needed for main NeoPixel class
35+
import digitalio
36+
from neopixel_write import neopixel_write
37+
except NotImplementedError:
38+
# silently accept this, can still use NeoPixel SPI class
39+
pass
3540

3641
__version__ = "0.0.0-auto.0"
3742
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel.git"
@@ -230,3 +235,85 @@ def show(self):
230235
neopixel_write(self.pin, self.buf)
231236
else:
232237
neopixel_write(self.pin, bytearray([int(i * self.brightness) for i in self.buf]))
238+
239+
class NeoPixel_SPI(NeoPixel):
240+
"""
241+
A sequence of neopixels.
242+
243+
:param ~busio.SPI spi: The SPI bus to output neopixel data on.
244+
:param int n: The number of neopixels in the chain
245+
:param int bpp: Bytes per pixel. 3 for RGB and 4 for RGBW pixels.
246+
:param float brightness: Brightness of the pixels between 0.0 and 1.0 where 1.0 is full
247+
brightness
248+
:param bool auto_write: True if the neopixels should immediately change when set. If False,
249+
`show` must be called explicitly.
250+
:param tuple pixel_order: Set the pixel color channel order. GRBW is set by default.
251+
252+
Example:
253+
254+
.. code-block:: python
255+
256+
import board
257+
import neopixel
258+
259+
pixels = neopixel.NeoPixel_SPI(board.SPI(), 10)
260+
pixels.fill(0xff0000)
261+
"""
262+
#pylint: disable=invalid-name, super-init-not-called
263+
264+
FREQ = 6400000 # 800kHz * 8, actual may be different
265+
TRST = 80e-6 # Reset code low level time
266+
267+
def __init__(self, spi, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None):
268+
from adafruit_bus_device.spi_device import SPIDevice
269+
self._spi = SPIDevice(spi, baudrate=self.FREQ)
270+
with self._spi as spibus:
271+
try:
272+
# get actual SPI frequency
273+
freq = spibus.frequency
274+
except AttributeError:
275+
# use nominal
276+
freq = self.FREQ
277+
self.RESET = bytes([0]*round(freq*self.TRST))
278+
self.n = n
279+
if pixel_order is None:
280+
self.order = GRBW
281+
self.bpp = bpp
282+
else:
283+
self.order = pixel_order
284+
self.bpp = len(self.order)
285+
self.buf = bytearray(self.n * self.bpp)
286+
self.spibuf = bytearray(8*len(self.buf))
287+
# Set auto_write to False temporarily so brightness setter does _not_
288+
# call show() while in __init__.
289+
self.auto_write = False
290+
self.brightness = brightness
291+
self.auto_write = auto_write
292+
293+
def deinit(self):
294+
"""Blank out the NeoPixels."""
295+
for i in range(len(self.buf)):
296+
self.buf[i] = 0
297+
self.show()
298+
299+
def show(self):
300+
"""Shows the new colors on the pixels themselves if they haven't already
301+
been autowritten."""
302+
self._transmogrify()
303+
with self._spi as spi:
304+
# write out special byte sequence surrounded by RESET
305+
# leading RESET needed for cases where MOSI rests HI
306+
spi.write(self.RESET + self.spibuf + self.RESET)
307+
308+
def _transmogrify(self):
309+
"""Turn every BIT of buf into a special BYTE pattern."""
310+
k = 0
311+
for byte in self.buf:
312+
byte = int(byte * self.brightness)
313+
# MSB first
314+
for i in range(7, -1, -1):
315+
if byte >> i & 0x01:
316+
self.spibuf[k] = 0b11110000 # A NeoPixel 1 bit
317+
else:
318+
self.spibuf[k] = 0b11000000 # A NeoPixel 0 bit
319+
k += 1

0 commit comments

Comments
 (0)