Porównaj commity

...

14 Commity

Autor SHA1 Wiadomość Data
blaz-r a288867b12 Merge branch 'pio_timer' of https://github.com/mcarlson/pi_pico_neopixel into pio_timer 2024-04-01 13:19:25 +02:00
blaz-r b02b657555 Remove unused imports 2024-04-01 13:19:18 +02:00
Blaž Rolih 4a895e2fd7
Merge branch 'main' into pio_timer 2024-04-01 13:17:34 +02:00
blaz-r d281ce4f1a Optimize and add WS2821 support 2024-04-01 13:16:48 +02:00
Blaž Rolih 7e59515bf9
Merge pull request #18 from mcarlson/fix_glitching
Update default delay to 300 microseconds per Adafruit
2024-03-31 22:49:51 +02:00
Blaž Rolih 15f27b80cc
Merge branch 'main' into fix_glitching 2024-03-31 22:49:15 +02:00
Blaž Rolih 70a84ba843
Merge branch 'main' into pio_timer 2024-03-31 19:55:56 +02:00
Blaž Rolih 7c97c8b15a
Merge pull request #19 from mcarlson/Add-length,-getter-and-array-replacement-for-better-list/array-like-support
Add array/list-like methods
2024-03-31 19:52:38 +02:00
blaz-r 89b06f4383 Decouple color and pixel indexing 2024-03-31 19:46:20 +02:00
Blaž Rolih 8f158e6390
Merge branch 'main' into Add-length,-getter-and-array-replacement-for-better-list/array-like-support 2024-03-31 19:15:27 +02:00
Blaž Rolih 30f8314330
Merge pull request #21 from blaz-r/improve_show
Improve data transfer in show()
2024-03-31 19:00:51 +02:00
Max Carlson 9e956e518d Add array/list-like methods 2024-02-02 16:34:28 +02:00
Max Carlson 093b30ea17 Update default delay to 300 microseconds per Adafruit
https://adafruit.github.io/Adafruit_NeoPixel/html/class_adafruit___neo_pixel.html says we need 300 microseconds delay. This definitely helped glitching on my setup!
2024-02-02 15:58:53 +02:00
blaz-r aef5b0db81 Improve data transfer in show() 2023-07-03 13:47:50 +02:00
1 zmienionych plików z 84 dodań i 35 usunięć

Wyświetl plik

@ -1,10 +1,10 @@
import array, time, struct
import array
from machine import Pin
import rp2
# based on https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython/advanced-using-pio-to-drive-neopixels-in-the-background by https://learn.adafruit.com/u/jepler
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=False, pull_thresh=32)
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=False)
def sk6812():
wrap_target()
pull(block) .side(0) # get fresh NeoPixel bit count value
@ -29,6 +29,37 @@ def sk6812():
wrap()
# based on https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython/advanced-using-pio-to-drive-neopixels-in-the-background by https://learn.adafruit.com/u/jepler
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=False)
def ws2812():
wrap_target()
pull(block) .side(0) # get fresh NeoPixel bit count value
out(y, 32) .side(0) # get count of NeoPixel bits
label("bitloop")
jmp(not_osre, "bit_out") .side(0) # if OSR still contains data, skip this section
pull(ifempty) .side(0) # pull new 32bits of RGB data
out(x, 8) .side(0) # skip first 8 as RGB is 24bits
label("bit_out")
pull(ifempty) .side(0) # drive low
out(x, 1) .side(0) [5]
jmp(not_x, "do_zero") .side(1) [3] # drive high and branch depending on bit val
jmp(y_dec, "bitloop") .side(1) [4] # drive high for a one (long pulse)
jmp("end_sequence") .side(0) # sequence is over
label("do_zero")
jmp(y_dec, "bitloop") .side(0) [4] # drive low for a zero (short pulse)
label("end_sequence")
pull(block) .side(0) # get fresh delay value
out(y, 32) .side(0) # get delay count
label("wait_reset")
jmp(y_dec, "wait_reset") .side(0) # wait until delay elapses
wrap()
# we need this because Micropython can't construct slice objects directly, only by
# way of supporting slice notation.
# So, e.g. slice_maker[1::4] gives a slice(1,None,4) object.
@ -54,12 +85,14 @@ class Neopixel:
# to describe the data members...
# __slots__ = [
# 'num_leds', # number of LEDs
# 'pixels', # array.array('I') of raw data for LEDs
# 'pixels', # array.array('I') of raw data for LEDs (with header and trailer)
# 'mode', # mode 'RGB' etc
# 'W_in_mode', # bool: is 'W' in mode
# 'sm', # state machine
# 'shift', # shift amount for each component, in a tuple for (R,B,G,W)
# 'brightnessvalue', # brightness scale factor 1..255
# 'header', # header - num of data bits in the data stream
# 'trailer', # trailer - delay at the end
# ]
def __init__(self, num_leds, state_machine, pin, mode="RGB"):
@ -72,7 +105,6 @@ class Neopixel:
:param mode: [default: "RGB"] mode and order of bits representing the color value.
This can be any order of RGB or RGBW (neopixels are usually GRB)
"""
# self.pixels_out = array.array("I", [0] * num_leds)
self.mode = mode
self.W_in_mode = 'W' in mode
bpp = 3
@ -84,7 +116,7 @@ class Neopixel:
(mode.index('B') ^ 3) * 8, (mode.index('W') ^ 3) * 8)
bpp = 4
else:
self.sm = rp2.StateMachine(state_machine, sk6812, freq=12_800_000, sideset_base=Pin(pin))
self.sm = rp2.StateMachine(state_machine, ws2812, freq=12_800_000, sideset_base=Pin(pin))
self.shift = (((mode.index('R') ^ 3) - 1) * 8, ((mode.index('G') ^ 3) - 1) * 8,
((mode.index('B') ^ 3) - 1) * 8, 0)
self.sm.active(1)
@ -94,26 +126,15 @@ class Neopixel:
# from https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython/advanced-using-pio-to-drive-neopixels-in-the-background
byte_count = bpp * num_leds
bit_count = byte_count * 8
padding_count = -byte_count % 4
# use a single array
self.pixels = array.array("I")
# send number of bits to read
self.pixels.append(bit_count - 1)
# store offset into pixels array for get/set later
self.offset = len(self.pixels)
# add starting values for each pixel
pix = array.array("I", [0] * num_leds)
self.pixels.extend(pix)
# send number of cycles to delay
self.pixels.append(3840)
# use a memoryview to
self.mv = memoryview(self.pixels)
# array of 32bit integers, with header and trailer at the beginning and end
self.pixels = array.array("I", [0] * (num_leds + 2))
# header - num of bits in the data stream
self.header = bit_count - 1
# trailer - delay at the end (12.8 Mhz clk -> 1 period = 7.8125e-8, 3840 * 7.8125e-8 = 0.0003s delay)
self.trailer = 3840
self.pixels[0] = self.header
self.pixels[-1] = self.trailer
def brightness(self, brightness=None):
"""
@ -205,13 +226,14 @@ class Neopixel:
white = round(rgb_w[3] * bratio)
pix_value = white << sh_W | blue << sh_B | red << sh_R | green << sh_G
offset = self.offset
# set some subset, if pixel_num is a slice:
if type(pixel_num) is slice:
for i in range(*pixel_num.indices(self.num_leds)):
self.pixels[i + offset] = pix_value
# offset of 1 due to header
self.pixels[i + 1] = pix_value
else:
self.pixels[pixel_num + offset] = pix_value
# offset of 1 due to header
self.pixels[pixel_num + 1] = pix_value
def get_pixel(self, pixel_num):
"""
@ -220,7 +242,9 @@ class Neopixel:
:param pixel_num: Index of pixel to be set
:return rgb_w: Tuple of form (r, g, b) or (r, g, b, w) representing color to be used
"""
balance = self.pixels[pixel_num + self.offset]
# offset of 1 due to header
balance = self.pixels[pixel_num + 1]
sh_R, sh_G, sh_B, sh_W = self.shift
if self.W_in_mode:
w = (balance >> sh_W) & 255
@ -243,14 +267,29 @@ class Neopixel:
npix[15:21] = (255,0,0) # <- sets 16,17 .. 20 to red
npix[21:29:2] = (0,0,255) # <- sets 21,23,25,27 to blue
npix[1::2] = (0,0,0) # <- sets all odd pixels to 'off'
npix[:] = [(0,5,0),(0,5,0)] # <- replaces all pixels with those from the array
(the 'slice' cases pass idx as a 'slice' object, and
set_pixel processes the slice)
:param idx: Index can either be indexing number or slice
:param rgb_w: Tuple of form (r, g, b) or (r, g, b, w) representing color to be used
:return:
:param rgb_w: Tuple (or list of tuples) of form (r, g, b) or (r, g, b, w) representing color to be used
:return: None
"""
self.set_pixel(idx, rgb_w)
if type(rgb_w) is list:
# set some subset, if idx is a slice:
if type(idx) is slice:
for rgb_i, pixel_i in enumerate(range(*idx.indices(self.num_leds))):
self.set_pixel(pixel_i, rgb_w[rgb_i])
else:
raise ValueError("Index must be a slice when setting multiple pixels as list")
else:
self.set_pixel(idx, rgb_w)
def __len__(self):
return self.num_leds
def __getitem__(self, idx):
return self.get_pixel(idx)
def colorHSV(self, hue, sat, val):
"""
@ -316,7 +355,10 @@ class Neopixel:
"""
if num_of_pixels is None:
num_of_pixels = 1
self.pixels = self.pixels[num_of_pixels:] + self.pixels[:num_of_pixels]
# offset of 1 due to header
num_of_pixels += 1
#self.pixels = self.pixels[num_of_pixels:] + self.pixels[:num_of_pixels]
self.pixels = array.array("I", [self.pixels[0]] + list(self.pixels[num_of_pixels:-1]) + list(self.pixels[1:num_of_pixels]) + [self.pixels[-1]])
def rotate_right(self, num_of_pixels=None):
"""
@ -327,8 +369,11 @@ class Neopixel:
"""
if num_of_pixels is None:
num_of_pixels = 1
# offset of 1 due to header
num_of_pixels += 1
num_of_pixels = -1 * num_of_pixels
self.pixels = self.pixels[num_of_pixels:] + self.pixels[:num_of_pixels]
#self.pixels = self.pixels[num_of_pixels:] + self.pixels[:num_of_pixels]
self.pixels = array.array("I", [self.pixels[0]] + list(self.pixels[num_of_pixels:-1]) + list(self.pixels[1:num_of_pixels]) + [self.pixels[-1]])
def show(self):
"""
@ -336,7 +381,9 @@ class Neopixel:
This method should be used after every method that changes the state of leds or after a chain of changes.
:return: None
"""
self.sm.put(self.mv)
# put entire pixel data along with header and trailer into TX FIFO
self.sm.put(memoryview(self.pixels))
def fill(self, rgb_w, how_bright=None):
"""
@ -355,4 +402,6 @@ class Neopixel:
:return: None
"""
self.pixels = array.array("I", [0] * self.num_leds)
self.pixels = array.array("I", [0] * (self.num_leds + 2))
self.pixels[0] = self.header
self.pixels[-1] = self.trailer