Support multiple strips in one Python program
Previously, a necessary delay at the end of pixel transmission was not enforced. The new test program is intended for a braincraft hat with a 96-pixel LED strip on each of the two GPIO connectors, but can be modified for other uses. Closes: #3
This commit is contained in:
parent
7643c3f0d1
commit
f8847a5aac
2 changed files with 85 additions and 7 deletions
55
examples/dual_animation.py
Normal file
55
examples/dual_animation.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
import adafruit_pixelbuf
|
||||
import board
|
||||
from adafruit_led_animation.animation.rainbow import Rainbow
|
||||
from adafruit_led_animation.animation.rainbowchase import RainbowChase
|
||||
from adafruit_led_animation.animation.rainbowcomet import RainbowComet
|
||||
from adafruit_led_animation.animation.rainbowsparkle import RainbowSparkle
|
||||
from adafruit_led_animation.sequence import AnimationSequence
|
||||
from adafruit_raspberry_pi5_neopixel_write import neopixel_write
|
||||
|
||||
NEOPIXEL1 = board.D13
|
||||
NEOPIXEL2 = board.D12
|
||||
num_pixels = 96
|
||||
|
||||
class Pi5Pixelbuf(adafruit_pixelbuf.PixelBuf):
|
||||
def __init__(self, pin, size, **kwargs):
|
||||
self._pin = pin
|
||||
super().__init__(size=size, **kwargs)
|
||||
|
||||
def _transmit(self, buf):
|
||||
neopixel_write(self._pin, buf)
|
||||
|
||||
pixels1 = Pi5Pixelbuf(NEOPIXEL1, num_pixels, auto_write=True, byteorder="BGR")
|
||||
pixels2 = Pi5Pixelbuf(NEOPIXEL2, num_pixels, auto_write=True, byteorder="BGR")
|
||||
|
||||
def make_animation(pixels):
|
||||
rainbow = Rainbow(pixels, speed=0.02, period=2)
|
||||
rainbow_chase = RainbowChase(pixels, speed=0.02, size=5, spacing=3)
|
||||
rainbow_comet = RainbowComet(pixels, speed=0.02, tail_length=7, bounce=True)
|
||||
rainbow_sparkle = RainbowSparkle(pixels, speed=0.02, num_sparkles=15)
|
||||
|
||||
|
||||
animations = AnimationSequence(
|
||||
rainbow,
|
||||
rainbow_chase,
|
||||
rainbow_comet,
|
||||
rainbow_sparkle,
|
||||
advance_interval=5,
|
||||
auto_clear=True,
|
||||
random_order=True,
|
||||
)
|
||||
return animations
|
||||
|
||||
animation1 = make_animation(pixels1)
|
||||
animation2 = make_animation(pixels2)
|
||||
|
||||
try:
|
||||
while True:
|
||||
animation1.animate()
|
||||
animation2.animate()
|
||||
finally:
|
||||
pixels1.fill(0)
|
||||
pixels1.show()
|
||||
pixels2.fill(0)
|
||||
pixels2.show()
|
||||
35
src/main.cpp
35
src/main.cpp
|
|
@ -1,4 +1,5 @@
|
|||
#include <iostream>
|
||||
#include <time.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include "piolib.h"
|
||||
|
|
@ -14,6 +15,19 @@ static int sm{-1};
|
|||
static int offset{-1};
|
||||
static int last_gpio{-1};
|
||||
static size_t last_size{};
|
||||
static struct timespec deadline;
|
||||
|
||||
constexpr auto NS_PER_SECOND = 1'000'000'000l;
|
||||
constexpr auto NS_PER_MS = 1'000'000l;
|
||||
|
||||
static void timespec_add_ns(struct timespec &out, const struct timespec &in, long ns) {
|
||||
out = in;
|
||||
out.tv_nsec += ns;
|
||||
if(out.tv_nsec > NS_PER_SECOND) {
|
||||
out.tv_nsec -= NS_PER_SECOND;
|
||||
out.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void neopixel_write(py::object gpio_obj, py::buffer buf) {
|
||||
int gpio = py::getattr(gpio_obj, "_pin", gpio_obj).attr("id").cast<int>();
|
||||
|
|
@ -39,15 +53,15 @@ static void neopixel_write(py::object gpio_obj, py::buffer buf) {
|
|||
offset = pio_add_program(pio, &ws2812_program);
|
||||
|
||||
pio_sm_clear_fifos(pio, sm);
|
||||
pio_sm_set_clkdiv(pio, sm, 1.0);
|
||||
|
||||
last_gpio = -1;
|
||||
}
|
||||
|
||||
ws2812_program_init(pio, sm, offset, gpio, 800000.0, true);
|
||||
} else {
|
||||
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL);
|
||||
if (gpio != last_gpio) {
|
||||
ws2812_program_init(pio, sm, offset, gpio, 800000.0, true);
|
||||
last_gpio = gpio;
|
||||
}
|
||||
}
|
||||
last_gpio = gpio;
|
||||
|
||||
|
||||
size_t size = info.size * info.itemsize;
|
||||
|
||||
|
|
@ -75,6 +89,15 @@ static void neopixel_write(py::object gpio_obj, py::buffer buf) {
|
|||
if (pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, data_size, &vec[0])) {
|
||||
throw std::runtime_error("pio_sm_xfer_data() failed");
|
||||
}
|
||||
|
||||
// Track the earliest time at which we can start a fresh neopixel transmission.
|
||||
// This needs to be long enough that all bits have been clocked out (the FIFO can
|
||||
// contain 16 entries of 32 bits each, taking 640us to transmit) plus the ws2812
|
||||
// required idle time ("RET code") of 50usmin. These sum to around 700us, so the 1ms
|
||||
// delay is generous.
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
timespec_add_ns(deadline, ts, NS_PER_MS);
|
||||
}
|
||||
|
||||
static void free_pio(void) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue