All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 5m29s
Check code formatting / Check-C-Format (push) Successful in 9s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 4s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
The python and C modules that are supposed to be built into the firmware image (i.e. those that are in manifest.py or in USER_C_MODULES) have been moved to the software/modules directory. The software/src directory should now only contain python scripts and other files that should be installed to the Picos flash filesystem. The idea is that these should be those scripts that implement the application behaviour, as these are the ones that a user who does not want to build the whole firmware themself wants to modify.
85 lines
2.9 KiB
Python
85 lines
2.9 KiB
Python
# (mostly) drop-in replacement for micropython-lib NeoPixel driver using the RP2 PIO
|
|
# Makes rainbows go faster
|
|
# MIT license; Copyright (c) 2016 Damien P. George, 2021 Jim Mussared, 2024 Matthias Blankertz
|
|
|
|
from array import array
|
|
from asyncio import ThreadSafeFlag
|
|
from rp2 import StateMachine, asm_pio, PIO, DMA
|
|
|
|
|
|
# Based on pico-examples/pio/ws2812/ws2812.pio
|
|
# Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
T1 = 2
|
|
T2 = 5
|
|
T3 = 3
|
|
|
|
|
|
@asm_pio(sideset_init=(PIO.OUT_LOW), fifo_join=PIO.JOIN_TX, autopull=True,
|
|
out_shiftdir=PIO.SHIFT_LEFT)
|
|
def _ws2812_pio(T1=T1, T2=T2, T3=T3):
|
|
label("bitloop") # noqa:F821
|
|
out(x, 1).side(0).delay(T3-1) # noqa:F821
|
|
jmp(not_x, "do_zero").side(1).delay(T1-1) # noqa:F821
|
|
label("do_one") # noqa:F821
|
|
jmp("bitloop").side(1).delay(T2-1) # noqa:F821
|
|
label("do_zero") # noqa:F821
|
|
nop().side(0).delay(T2-1) # noqa:F821
|
|
wrap() # noqa:F821
|
|
|
|
|
|
class NeoPixel:
|
|
# G R B W
|
|
ORDER = (2, 3, 1, 0)
|
|
|
|
# User must set 'sm' to id of an unused PIO statemachine (range 0 to 7), as unfortunately the micropython API does
|
|
# not expose the pico sdks claim functionality
|
|
def __init__(self, pin, n, bpp=3, timing=1, sm=0):
|
|
# Timing arg can be either 1 for 800 kHz, 0 for 400 kHz or a number x > 1 for x Hz
|
|
if not isinstance(timing, int):
|
|
raise ValueError("user-specified timing not supported")
|
|
self.pin = pin
|
|
self.n = n
|
|
self.bpp = bpp
|
|
self.buf = array('I', [0] * n)
|
|
bitrate = (800000 if timing == 1 else
|
|
400000 if timing == 0 else
|
|
timing)
|
|
self.statemachine = StateMachine(sm, _ws2812_pio,
|
|
freq=bitrate*(T1+T2+T3),
|
|
sideset_base=pin,
|
|
pull_thresh=(32 if bpp == 4 else 24))
|
|
self.statemachine.active(1)
|
|
self.dma = DMA()
|
|
self.dma_ctrl = self.dma.pack_ctrl(inc_write=False, treq_sel=(sm if sm <= 3 else 4+sm), irq_quiet=False)
|
|
self.dma.irq(handler=self._interrupt)
|
|
self.interrupt_flag = ThreadSafeFlag()
|
|
|
|
def _interrupt(self, _):
|
|
self.interrupt_flag.set()
|
|
|
|
def __len__(self):
|
|
return self.n
|
|
|
|
def __setitem__(self, i, v):
|
|
self.buf[i] = 0
|
|
for b in range(self.bpp):
|
|
self.buf[i] |= v[b] << (self.ORDER[b] * 8)
|
|
|
|
def __getitem__(self, i):
|
|
return tuple((self.buf[i] >> (self.ORDER[b] * 8)) & 0xff for b in range(self.bpp))
|
|
|
|
def fill(self, v):
|
|
val = 0
|
|
for b in range(self.bpp):
|
|
val |= v[b] << (self.ORDER[b] * 8)
|
|
self.buf = array('I', [val]*self.n)
|
|
|
|
def write(self):
|
|
self.statemachine.put(self.buf)
|
|
|
|
async def async_write(self):
|
|
self.dma.config(read=self.buf, write=self.statemachine, count=self.n, ctrl=self.dma_ctrl, trigger=True)
|
|
while self.dma.active():
|
|
await self.interrupt_flag.wait()
|