mcarlson 2024-04-01 13:19:46 +02:00 zatwierdzone przez GitHub
commit 9b9778cd6e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
1 zmienionych plików z 87 dodań i 46 usunięć

Wyświetl plik

@ -1,37 +1,62 @@
import array, time
import array
from machine import Pin
import rp2
# PIO state machine for RGB. Pulls 24 bits (rgb -> 3 * 8bit) automatically
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24)
def ws2812():
T1 = 2
T2 = 5
T3 = 3
# 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 sk6812():
wrap_target()
pull(block) .side(0) # get fresh NeoPixel bit count value
out(y, 32) .side(0) # get count of NeoPixel bits
label("bitloop")
out(x, 1) .side(0) [T3 - 1]
jmp(not_x, "do_zero") .side(1) [T1 - 1]
jmp("bitloop") .side(1) [T2 - 1]
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")
nop() .side(0) [T2 - 1]
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()
# PIO state machine for RGBW. Pulls 32 bits (rgbw -> 4 * 8bit) automatically
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=32)
def sk6812():
T1 = 2
T2 = 5
T3 = 3
# 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")
out(x, 1) .side(0) [T3 - 1]
jmp(not_x, "do_zero") .side(1) [T1 - 1]
jmp("bitloop") .side(1) [T2 - 1]
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")
nop() .side(0) [T2 - 1]
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()
@ -46,10 +71,6 @@ class slice_maker_class:
slice_maker = slice_maker_class()
# Delay here is the reset time. You need a pause to reset the LED strip back to the initial LED
# however, if you have quite a bit of processing to do before the next time you update the strip
# you could put in delay=0 (or a lower delay)
#
# Class supports different order of individual colors (GRB, RGB, WRGB, GWRB ...). In order to achieve
# this, we need to flip the indexes: in 'RGBW', 'R' is on index 0, but we need to shift it left by 3 * 8bits,
# so in it's inverse, 'WBGR', it has exactly right index. Since micropython doesn't have [::-1] and recursive rev()
@ -64,16 +85,17 @@ 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)
# 'delay', # delay amount
# '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", delay=0.0003):
def __init__(self, num_leds, state_machine, pin, mode="RGB"):
"""
Constructor for library class
@ -82,26 +104,38 @@ class Neopixel:
:param pin: pin on which data line to led-strip is connected
: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)
:param delay: [default: 0.0001] delay used for latching of leds when sending data
"""
self.pixels = array.array("I", [0] * num_leds)
self.mode = mode
self.W_in_mode = 'W' in mode
bpp = 3
if self.W_in_mode:
# RGBW uses different PIO state machine configuration
self.sm = rp2.StateMachine(state_machine, sk6812, freq=8000000, sideset_base=Pin(pin))
self.sm = rp2.StateMachine(state_machine, sk6812, freq=12_800_000, sideset_base=Pin(pin))
# tuple of values required to shift bit into position (check class desc.)
self.shift = ((mode.index('R') ^ 3) * 8, (mode.index('G') ^ 3) * 8,
(mode.index('B') ^ 3) * 8, (mode.index('W') ^ 3) * 8)
bpp = 4
else:
self.sm = rp2.StateMachine(state_machine, ws2812, freq=8000000, 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)
self.num_leds = num_leds
self.delay = delay
self.brightnessvalue = 255
# 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
# 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):
"""
Set the overall value to adjust brightness when updating leds
@ -195,9 +229,11 @@ class Neopixel:
# 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] = pix_value
# offset of 1 due to header
self.pixels[i + 1] = pix_value
else:
self.pixels[pixel_num] = pix_value
# offset of 1 due to header
self.pixels[pixel_num + 1] = pix_value
def get_pixel(self, pixel_num):
"""
@ -206,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]
# 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
@ -317,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):
"""
@ -328,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):
"""
@ -337,14 +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
"""
# If mode is RGB, we cut 8 bits of, otherwise we keep all 32
cut = 8
if self.W_in_mode:
cut = 0
self.sm.put(self.pixels, cut)
# put entire pixel data along with header and trailer into TX FIFO
self.sm.put(memoryview(self.pixels))
time.sleep(self.delay)
def fill(self, rgb_w, how_bright=None):
"""
@ -363,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