There are a lot of addressable LEDs out there. The chief variant is NeoPixel .. NeoPixel and Dotstar. Our two types of addressable LEDs are NeoPixel and Dotstar .. and TM1814....
(you get it right? It's like the Spanish Inquisition skit, but about LEDs?)
Long story short: TM1814 addressable LEDs can work with CircuitPython on the RP2040, but you'll have to use some custom code to talk to them. The TM1814PixelBackground class is a PixelBuf, though, so you can use it with the LED animation library no problem.
There's one big difference with TM1814 LEDs: The logic levels are inverted compared to NeoPixels. And, the first 64 bits are an overall brightness value. Two differences. The bits are inverted, and the timing is subtly different, and there's an overall brightness value. And if you don't continuously update the pixels, they enter a test mode. Four. Four main differences. And often the strips are powered from a higher voltage like 12V.
You have one last chance left to show me the source code
But first! You'll want to make sure you're using CircuitPython 9.2, at least 9.2.0-beta.0 or newer. It seems there was a teensy, tiny bug in CircuitPython that you'd encounter if you used this code. So with that out of the way
Two, I'll allow you two last chances to show me the source code
Grab the code from below (or from github) and place it on your CircuitPython device as code.py. Then, hook up your TM1814 strip to CircuitPython's GND and A0 pins. Hook up appropriate power to the TM1814 strip (mine was labeled "12V", so I used a 12V supply. 5V definitely didn't work! And definitely don't cross-connect the LED strip's supply into the RP2040's VCC or +5V!).
When the CircuitPython LED code is not running (or if it malfunctions) you get an obnoxious test pattern generated by the TM1814 LEDs themselves. Sorry, there's nothing we can do about it.
Once the code's running you'll get a comparatively soothing rainbow LED animation instead.
If you won't show us the source code I'm afraid we'll have to put you in the Comfy Chair
# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""Demonstrate background writing with TM1814 pixels
The class defined here is largely compatible with the
standard NeoPixel class, except that it always is
writing data to the LEDs in the background.
Not only do TM1814 pixels require continuous updating,
writing the LED data in the background will allow more time for your
Python code to run.
Because the pixelbuf storage is also being written out 'live', it is possible
to experience tearing, where the LEDs are a combination of old and new values
at the same time.
The demonstration code, under ``if __name__ == '__main__':`` is intended
for a 150-pixel strand of TM1814 pixels.
"""
import struct
import time
import adafruit_pixelbuf
from rp2pio import StateMachine
from adafruit_pioasm import Program
# Pixel stream is very similar to NeoPixel WS2812B, but inverted.
#
# Pixel time is 1.25us (800kHz)
#
# Datasheet low times for a "0" bit are 310 (min) 360 (typ) 410 (max) ns
# Datasheet high times for a "1" bit are 650 (min) 720 (typ) 1000 (max) ns
#
# Operating PIO at 14x the bit clock lets us achieve nominal 357ns and 714ns
DEBUG = 0 # Set to 1 to output OPPOSITE signal on the next higher pin
_program = Program(
f"""
.side_set {2 if DEBUG else 1}
.wrap_target
pull block side 1
out y, 32 side 1 ; get count of pixel bits
bitloop:
pull ifempty side 1 ; drive low
out x 1 side 1 [4]
jmp !x do_zero side {2 if DEBUG else 0} [3] ; drive low and branch depending on bit val
jmp y--, bitloop side {2 if DEBUG else 0} [3] ; drive low for a one (long pulse)
jmp end_sequence side 1 ; sequence is over
do_zero:
jmp y--, bitloop side 1 [3] ; drive high for a zero (short pulse)
end_sequence:
pull block side 1 ; get fresh delay value
out y, 32 side 1 ; get delay count
wait_reset:
jmp y--, wait_reset side 1 ; wait until delay elapses
.wrap
"""
)
def convert_brightness(x):
x = int(x * 63) + 13
x |= (x << 8)
return x | (x << 16)
class TM1814PixelBackground( # pylint: disable=too-few-public-methods
adafruit_pixelbuf.PixelBuf
):
def __init__(
self, pin, n, *, brightness=1.0
):
pixel_order = "RGBW"
n = max(n, 4) # minimum of 4 pixels
byte_count = 4 * n
bit_count = byte_count * 8 + 64 # count the 64 brightness bits
padding_count = -byte_count % 4
self._brightness = brightness
raw_brightness = convert_brightness(brightness)
print(f"{raw_brightness=:08x}")
# backwards, so that dma byteswap corrects it!
header = struct.pack(">LLL", bit_count - 1, raw_brightness, raw_brightness ^ 0xffffffff)
trailer = b"\0" * padding_count + struct.pack(">L", 38400) # Delay is about 3ms
self._sm = StateMachine(
_program.assembled,
auto_pull=False,
first_sideset_pin=pin,
out_shift_right=False,
pull_threshold=32,
frequency=800_000 * 14,
**_program.pio_kwargs,
)
self._buf = None
super().__init__(
n,
brightness=1.0,
byteorder=pixel_order,
auto_write=False,
header=header,
trailer=trailer,
)
self.show()
@property
def auto_write(self):
return True
@property
def brightness(self):
return self._brightness
@brightness.setter
def brightness(self, brightness):
raw_brightness = convert_brightness(brightness)
self._brightness = brightness
struct.pack_into("<LL", self._buf, 4, raw_brightness, raw_brightness ^ 0xffffffff)
def _transmit(self, buf):
self._buf = buf
self._sm.background_write(loop=memoryview(buf).cast("L"), swap=True)
if __name__ == "__main__":
import board
import rainbowio
import supervisor
NEOPIXEL = board.A0
NUM_PIXELS = 150
pixels = TM1814PixelBackground(NEOPIXEL, NUM_PIXELS, brightness=0.1)
buf = memoryview(pixels._buf).cast('L')
for _ in range(1000):
# Around 1 cycle per second
pixels.fill(rainbowio.colorwheel(supervisor.ticks_ms() // 4))
print(*(f"{pixel:08x}" for pixel in buf[:8]), "...", f"{buf[-1]:08x}")
To use this code as a library, simply rename it tm1814.py using the file manager and then use from tm1814 import TM1814PixelBackground in your code.
This page (TM1814 LEDs with CircuitPython & RP2040) was last updated on October 28, 2024.