Skip to content

Add SPI driven NeoPixel. #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 9, 2019
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 86 additions & 2 deletions neopixel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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, 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)
# 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.RESET)

def _transmogrify(self):
"""Turn every BIT of buf into a special BYTE pattern."""
out_buf = bytearray()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we can pre-alloc the size of the bytearray instead of using append, that will be much faster :)

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