#![cfg_attr(
feature = "doc-images",
doc = ::embed_doc_image::embed_image!(
"led_strip_simple",
"docs/assets/led_strip_simple.png"
),
doc = ::embed_doc_image::embed_image!(
"led_strip_gpio0",
"docs/assets/led_strip_gpio0.png"
),
doc = ::embed_doc_image::embed_image!(
"led_strip_gogo",
"docs/assets/led2d2.png"
),
doc = ::embed_doc_image::embed_image!(
"led_strip_animated",
"docs/assets/led_strip_animated.png"
)
)]
pub use device_envoy_core::led_strip::*;
pub mod led_strip_generated;
#[cfg(not(feature = "host"))]
use core::cell::RefCell;
#[cfg(not(feature = "host"))]
use embassy_futures::select::{Either, select};
#[cfg(not(feature = "host"))]
use embassy_rp::pio::{Common, Instance};
#[cfg(not(feature = "host"))]
use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
#[cfg(not(feature = "host"))]
use embassy_sync::blocking_mutex::Mutex;
#[cfg(not(feature = "host"))]
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
#[cfg(not(feature = "host"))]
#[cfg(not(feature = "host"))]
use embassy_sync::once_lock::OnceLock;
#[cfg(not(feature = "host"))]
use embassy_time::{Duration, Timer};
#[cfg(not(feature = "host"))]
use heapless::Vec;
#[doc(hidden)]
pub struct LedStripRp<const N: usize, const MAX_FRAMES: usize> {
command_signal: &'static LedStripCommandSignal<N, MAX_FRAMES>,
}
impl<const N: usize, const MAX_FRAMES: usize> LedStripRp<N, MAX_FRAMES> {
#[doc(hidden)]
pub const fn new_static() -> LedStripStatic<N, MAX_FRAMES> {
LedStripStatic::new_static()
}
#[doc(hidden)]
pub fn new(led_strip_static: &'static LedStripStatic<N, MAX_FRAMES>) -> Self {
Self {
command_signal: led_strip_static.command_signal(),
}
}
#[doc(hidden)]
pub fn __command_signal(&self) -> &'static LedStripCommandSignal<N, MAX_FRAMES> {
self.command_signal
}
}
#[cfg(not(feature = "host"))]
#[doc(hidden)] pub struct PioBusStateMachine<PIO: Instance + 'static, const SM: usize> {
pio_bus: &'static PioBus<'static, PIO>,
state_machine: embassy_rp::pio::StateMachine<'static, PIO, SM>,
}
#[cfg(not(feature = "host"))]
impl<PIO: Instance + 'static, const SM: usize> PioBusStateMachine<PIO, SM> {
#[doc(hidden)]
pub fn new(
pio_bus: &'static PioBus<'static, PIO>,
state_machine: embassy_rp::pio::StateMachine<'static, PIO, SM>,
) -> Self {
Self {
pio_bus,
state_machine,
}
}
#[doc(hidden)]
pub fn pio_bus(&self) -> &'static PioBus<'static, PIO> {
self.pio_bus
}
#[doc(hidden)]
pub fn into_parts(
self,
) -> (
&'static PioBus<'static, PIO>,
embassy_rp::pio::StateMachine<'static, PIO, SM>,
) {
(self.pio_bus, self.state_machine)
}
}
#[cfg(not(feature = "host"))]
#[doc(hidden)] pub struct PioBus<'d, PIO: Instance> {
common: Mutex<CriticalSectionRawMutex, RefCell<Common<'d, PIO>>>,
ws2812_program: OnceLock<PioWs2812Program<'d, PIO>>,
}
#[cfg(not(feature = "host"))]
impl<'d, PIO: Instance> PioBus<'d, PIO> {
pub fn new(common: Common<'d, PIO>) -> Self {
Self {
common: Mutex::new(RefCell::new(common)),
ws2812_program: OnceLock::new(),
}
}
pub fn get_program(&'static self) -> &'static PioWs2812Program<'d, PIO> {
self.ws2812_program.get_or_init(|| {
self.common.lock(|common_cell: &RefCell<Common<'d, PIO>>| {
let mut common = common_cell.borrow_mut();
PioWs2812Program::new(&mut *common)
})
})
}
pub fn with_common<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut Common<'d, PIO>) -> R,
{
self.common.lock(|common_cell: &RefCell<Common<'d, PIO>>| {
let mut common = common_cell.borrow_mut();
f(&mut *common)
})
}
}
#[cfg(not(feature = "host"))]
#[doc(hidden)] pub async fn led_strip_device_loop<
PIO,
const SM: usize,
const N: usize,
const MAX_FRAMES: usize,
ORDER,
>(
mut driver: PioWs2812<'static, PIO, SM, N, ORDER>,
command_signal: &'static LedStripCommandSignal<N, MAX_FRAMES>,
combo_table: &'static [u8; 256],
) -> !
where
PIO: Instance,
ORDER: embassy_rp::pio_programs::ws2812::RgbColorOrder,
{
loop {
let mut command = command_signal.wait().await;
command_signal.reset();
loop {
match command {
Command::DisplayStatic(mut frame) => {
apply_correction(&mut frame, combo_table);
driver.write(&frame).await;
break;
}
Command::Animate(frames) => {
command =
run_frame_animation(&mut driver, frames, command_signal, combo_table).await;
}
}
}
}
}
#[cfg(not(feature = "host"))]
async fn run_frame_animation<PIO, const SM: usize, const N: usize, const MAX_FRAMES: usize, ORDER>(
driver: &mut PioWs2812<'static, PIO, SM, N, ORDER>,
mut frames: Vec<(Frame1d<N>, Duration), MAX_FRAMES>,
command_signal: &'static LedStripCommandSignal<N, MAX_FRAMES>,
combo_table: &'static [u8; 256],
) -> Command<N, MAX_FRAMES>
where
PIO: Instance,
ORDER: embassy_rp::pio_programs::ws2812::RgbColorOrder,
{
frames
.iter_mut()
.for_each(|(frame, _)| apply_correction(frame, combo_table));
loop {
for (frame, duration) in &frames {
driver.write(frame).await;
match select(command_signal.wait(), Timer::after(*duration)).await {
Either::First(new_command) => {
command_signal.reset();
return new_command;
}
Either::Second(()) => continue,
}
}
}
}
#[doc = include_str!("docs/current_limiting_and_gamma.md")]
#[cfg_attr(
feature = "doc-images",
doc = ::embed_doc_image::embed_image!(
"led_strip_gpio0",
"docs/assets/led_strip_gpio0.png"
)
)]
#[cfg_attr(
feature = "doc-images",
doc = ::embed_doc_image::embed_image!(
"led_strip_simple",
"docs/assets/led_strip_simple.png"
)
)]
#[cfg_attr(
feature = "doc-images",
doc = ::embed_doc_image::embed_image!(
"led_strip_gogo",
"docs/assets/led2d2.png"
)
)]
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! led_strips {
($($tt:tt)*) => { $crate::__led_strips_impl! { $($tt)* } };
}
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strips_impl {
(@__expand
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips: [
$(
$label:ident {
sm: $sm_index:expr,
dma: $dma:ident,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
$(,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
$(max_frames: $led2d_max_frames:expr,)?
font: $led2d_font:expr $(,)?
}
)?
}
),+ $(,)?
]
) => {
paste::paste! {
#[allow(non_upper_case_globals)]
static [<$pio _BUS>]: ::static_cell::StaticCell<
$crate::led_strip::PioBus<'static, ::embassy_rp::peripherals::$pio>
> = ::static_cell::StaticCell::new();
#[allow(dead_code)]
pub fn [<$pio:lower _split>](
pio: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pio>,
) -> (
$crate::led_strip::PioBusStateMachine<::embassy_rp::peripherals::$pio, 0>,
$crate::led_strip::PioBusStateMachine<::embassy_rp::peripherals::$pio, 1>,
$crate::led_strip::PioBusStateMachine<::embassy_rp::peripherals::$pio, 2>,
$crate::led_strip::PioBusStateMachine<::embassy_rp::peripherals::$pio, 3>,
) {
let ::embassy_rp::pio::Pio { common, sm0, sm1, sm2, sm3, .. } =
::embassy_rp::pio::Pio::new(
pio,
<::embassy_rp::peripherals::$pio as $crate::pio_irqs::PioIrqMap>::irqs(),
);
let pio_bus = [<$pio _BUS>].init_with(|| {
$crate::led_strip::PioBus::new(common)
});
(
$crate::led_strip::PioBusStateMachine::new(pio_bus, sm0),
$crate::led_strip::PioBusStateMachine::new(pio_bus, sm1),
$crate::led_strip::PioBusStateMachine::new(pio_bus, sm2),
$crate::led_strip::PioBusStateMachine::new(pio_bus, sm3),
)
}
$(
$crate::__led_strips_impl!(
@__define_strip
vis: $vis,
group: $group,
pio: $pio,
label: $label,
sm: $sm_index,
dma: $dma,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
$(,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
$(max_frames: $led2d_max_frames,)?
font: $led2d_font,
}
)?
);
)+
#[allow(missing_docs)]
$vis struct $group;
#[allow(missing_docs)]
impl $group {
#[allow(clippy::too_many_arguments)]
pub fn new(
pio: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pio>>,
$(
[<$label:snake _pin>]: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>>,
[<$label:snake _dma>]: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>>,
)+
spawner: ::embassy_executor::Spawner,
) -> $crate::Result<(
$(
$crate::__led_strips_impl!(
@__strip_return_type
$label
$(,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
font: $led2d_font,
}
)?
),
)+
)> {
let pio_peri = pio.into();
let ::embassy_rp::pio::Pio { common, sm0, sm1, sm2, sm3, .. } =
::embassy_rp::pio::Pio::new(
pio_peri,
<::embassy_rp::peripherals::$pio as $crate::pio_irqs::PioIrqMap>::irqs(),
);
let pio_bus = [<$pio _BUS>].init_with(|| {
$crate::led_strip::PioBus::new(common)
});
#[allow(unused_variables)]
let sm0_wrapped = $crate::led_strip::PioBusStateMachine::new(pio_bus, sm0);
#[allow(unused_variables)]
let sm1_wrapped = $crate::led_strip::PioBusStateMachine::new(pio_bus, sm1);
#[allow(unused_variables)]
let sm2_wrapped = $crate::led_strip::PioBusStateMachine::new(pio_bus, sm2);
#[allow(unused_variables)]
let sm3_wrapped = $crate::led_strip::PioBusStateMachine::new(pio_bus, sm3);
Ok((
$(
$crate::__led_strips_impl!(
@__strip_return_value
label: $label,
state_machine: $crate::__led_strips_impl!(
@__select_sm
$sm_index,
sm0_wrapped,
sm1_wrapped,
sm2_wrapped,
sm3_wrapped
),
pin: [<$label:snake _pin>],
dma: [<$label:snake _dma>],
spawner: spawner
$(,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
font: $led2d_font,
}
)?
),
)+
))
}
}
}
};
(@__select_sm 0, $sm0:ident, $sm1:ident, $sm2:ident, $sm3:ident) => { $sm0 };
(@__select_sm 1, $sm0:ident, $sm1:ident, $sm2:ident, $sm3:ident) => { $sm1 };
(@__select_sm 2, $sm0:ident, $sm1:ident, $sm2:ident, $sm3:ident) => { $sm2 };
(@__select_sm 3, $sm0:ident, $sm1:ident, $sm2:ident, $sm3:ident) => { $sm3 };
(@__define_strip
vis: $vis:vis,
group: $group:ident,
pio: $pio:ident,
label: $label:ident,
sm: $sm_index:expr,
dma: $dma:ident,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
) => {
paste::paste! {
#[doc = concat!(
"LED strip wrapper generated by [`led_strips!`].\n\n",
"Implements the [`LedStrip`](crate::led_strip::LedStrip) trait for LED control methods. ",
"Created with [`", stringify!($group), "::new`]. ",
"See the [led_strip module documentation](mod@crate::led_strip) for a similar example."
)]
$vis struct $label {
strip: $crate::led_strip::LedStripRp<{ $len }, { $max_frames }>,
}
#[allow(missing_docs)]
impl $label {
pub const LEN: usize = $len;
pub const MAX_FRAMES: usize = $max_frames;
const WORST_CASE_MA: u32 = ($len as u32) * 60;
pub const MAX_BRIGHTNESS: u8 =
$max_current.max_brightness(Self::WORST_CASE_MA);
const COMBO_TABLE: [u8; 256] = $crate::led_strip::generate_combo_table($gamma, Self::MAX_BRIGHTNESS);
pub(crate) const fn new_static() -> $crate::led_strip::LedStripStatic<{ $len }, { $max_frames }> {
$crate::led_strip::LedStripRp::new_static()
}
pub fn new(
state_machine: $crate::led_strip::PioBusStateMachine<::embassy_rp::peripherals::$pio, $sm_index>,
pin: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>>,
dma: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>>,
spawner: ::embassy_executor::Spawner,
) -> $crate::Result<&'static Self> {
static STRIP_STATIC: $crate::led_strip::LedStripStatic<{ $len }, { $max_frames }> =
$label::new_static();
static STRIP_CELL: ::static_cell::StaticCell<$label> = ::static_cell::StaticCell::new();
let pin = pin.into();
let dma = dma.into();
let (bus, sm) = state_machine.into_parts();
let token = [<$label:snake _device_task>](
bus,
sm,
dma,
pin,
STRIP_STATIC.command_signal(),
);
spawner.spawn(token.map_err($crate::Error::TaskSpawn)?);
let strip = $crate::led_strip::LedStripRp::new(&STRIP_STATIC);
let instance = STRIP_CELL.init(Self { strip });
Ok(instance)
}
}
#[cfg(not(feature = "host"))]
impl AsRef<$crate::led_strip::LedStripRp<{ $len }, { $max_frames }>> for $label {
fn as_ref(&self) -> &$crate::led_strip::LedStripRp<{ $len }, { $max_frames }> {
&self.strip
}
}
impl $crate::led_strip::LedStrip<{ $len }> for $label {
const MAX_FRAMES: usize = $max_frames;
const MAX_BRIGHTNESS: u8 = Self::MAX_BRIGHTNESS;
fn write_frame(&self, frame: $crate::led_strip::Frame1d<{ $len }>) {
$crate::led_strip::__write_frame(self.strip.__command_signal(), frame);
}
fn animate<I>(&self, frames: I)
where
I: IntoIterator,
I::Item: ::core::borrow::Borrow<(
$crate::led_strip::Frame1d<{ $len }>,
embassy_time::Duration,
)>,
{
$crate::led_strip::__animate(self.strip.__command_signal(), frames);
}
}
#[::embassy_executor::task]
async fn [<$label:snake _device_task>](
bus: &'static $crate::led_strip::PioBus<'static, ::embassy_rp::peripherals::$pio>,
sm: ::embassy_rp::pio::StateMachine<'static, ::embassy_rp::peripherals::$pio, $sm_index>,
dma: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>,
pin: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>,
command_signal: &'static $crate::led_strip::LedStripCommandSignal<{ $len }, { $max_frames }>,
) -> ! {
let program = bus.get_program();
let driver = bus.with_common(|common| {
::embassy_rp::pio_programs::ws2812::PioWs2812::<
::embassy_rp::peripherals::$pio,
$sm_index,
{ $len },
_
>::new(
common,
sm,
dma,
<::embassy_rp::peripherals::$dma as $crate::pio_irqs::DmaIrqMap>::irqs(),
pin,
program,
)
});
$crate::led_strip::led_strip_device_loop::<
::embassy_rp::peripherals::$pio,
$sm_index,
{ $len },
{ $max_frames },
_
>(driver, command_signal, &$label::COMBO_TABLE).await
}
}
};
(@__define_strip
vis: $vis:vis,
group: $group:ident,
pio: $pio:ident,
label: $label:ident,
sm: $sm_index:expr,
dma: $dma:ident,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
$(max_frames: $led2d_max_frames:expr,)?
font: $led2d_font:expr $(,)?
}
) => {
paste::paste! {
#[doc = concat!(
"LED strip wrapper generated by [`led_strips!`].\n\n",
"Implements the [`LedStrip`](crate::led_strip::LedStrip) trait for LED control methods. ",
"Created with [`", stringify!($group), "::new`]. ",
"See the [led_strip module documentation](mod@crate::led_strip) for a similar example."
)]
#[allow(missing_docs)]
struct [<$label:camel LedStrip>] {
strip: $crate::led_strip::LedStripRp<{ $len }, { $max_frames }>,
}
#[allow(missing_docs)]
impl [<$label:camel LedStrip>] {
pub const LEN: usize = $len;
pub const MAX_FRAMES: usize = $max_frames;
const WORST_CASE_MA: u32 = ($len as u32) * 60;
pub const MAX_BRIGHTNESS: u8 =
$max_current.max_brightness(Self::WORST_CASE_MA);
const COMBO_TABLE: [u8; 256] = $crate::led_strip::generate_combo_table($gamma, Self::MAX_BRIGHTNESS);
pub(crate) const fn new_static() -> $crate::led_strip::LedStripStatic<{ $len }, { $max_frames }> {
$crate::led_strip::LedStripRp::new_static()
}
pub fn new(
state_machine: $crate::led_strip::PioBusStateMachine<::embassy_rp::peripherals::$pio, $sm_index>,
pin: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>>,
dma: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>>,
spawner: ::embassy_executor::Spawner,
) -> $crate::Result<&'static Self> {
static STRIP_STATIC: $crate::led_strip::LedStripStatic<{ $len }, { $max_frames }> =
[<$label:camel LedStrip>]::new_static();
static STRIP_CELL: ::static_cell::StaticCell<[<$label:camel LedStrip>]> = ::static_cell::StaticCell::new();
let pin = pin.into();
let dma = dma.into();
let (bus, sm) = state_machine.into_parts();
let token = [<$label:snake _led_strip _device_task>](
bus,
sm,
dma,
pin,
STRIP_STATIC.command_signal(),
);
spawner.spawn(token.map_err($crate::Error::TaskSpawn)?);
let strip = $crate::led_strip::LedStripRp::new(&STRIP_STATIC);
let instance = STRIP_CELL.init(Self { strip });
Ok(instance)
}
}
#[cfg(not(feature = "host"))]
impl AsRef<$crate::led_strip::LedStripRp<{ $len }, { $max_frames }>> for [<$label:camel LedStrip>] {
fn as_ref(&self) -> &$crate::led_strip::LedStripRp<{ $len }, { $max_frames }> {
&self.strip
}
}
impl $crate::led_strip::LedStrip<{ $len }> for [<$label:camel LedStrip>] {
const MAX_FRAMES: usize = $max_frames;
const MAX_BRIGHTNESS: u8 = Self::MAX_BRIGHTNESS;
fn write_frame(&self, frame: $crate::led_strip::Frame1d<{ $len }>) {
$crate::led_strip::__write_frame(self.strip.__command_signal(), frame);
}
fn animate<I>(&self, frames: I)
where
I: IntoIterator,
I::Item: ::core::borrow::Borrow<(
$crate::led_strip::Frame1d<{ $len }>,
embassy_time::Duration,
)>,
{
$crate::led_strip::__animate(self.strip.__command_signal(), frames);
}
}
#[::embassy_executor::task]
async fn [<$label:snake _led_strip _device_task>](
bus: &'static $crate::led_strip::PioBus<'static, ::embassy_rp::peripherals::$pio>,
sm: ::embassy_rp::pio::StateMachine<'static, ::embassy_rp::peripherals::$pio, $sm_index>,
dma: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>,
pin: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>,
command_signal: &'static $crate::led_strip::LedStripCommandSignal<{ $len }, { $max_frames }>,
) -> ! {
let program = bus.get_program();
let driver = bus.with_common(|common| {
::embassy_rp::pio_programs::ws2812::PioWs2812::<
::embassy_rp::peripherals::$pio,
$sm_index,
{ $len },
_
>::new(
common,
sm,
dma,
<::embassy_rp::peripherals::$dma as $crate::pio_irqs::DmaIrqMap>::irqs(),
pin,
program,
)
});
$crate::led_strip::led_strip_device_loop::<
::embassy_rp::peripherals::$pio,
$sm_index,
{ $len },
{ $max_frames },
_
>(driver, command_signal, &[<$label:camel LedStrip>]::COMBO_TABLE).await
}
#[cfg(not(feature = "host"))]
$crate::led2d::led2d_from_strip! {
$vis $label,
strip_type: [<$label:camel LedStrip>],
width: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?.width(),
height: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?.height(),
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
font: $led2d_font,
}
#[cfg(not(feature = "host"))]
impl [<$label:camel LedStrip>] {
pub fn new_led2d(
state_machine: $crate::led_strip::PioBusStateMachine<::embassy_rp::peripherals::$pio, $sm_index>,
pin: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>>,
dma: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>>,
spawner: ::embassy_executor::Spawner,
) -> $crate::Result<$label> {
let strip = Self::new(state_machine, pin, dma, spawner)?;
$label::from_strip(strip)
}
}
}
};
(@__strip_return_type $label:ident) => {
&'static $label
};
(@__strip_return_type $label:ident, led2d: { $($led2d_fields:tt)* }) => {
$label
};
(@__strip_return_value
label: $label:ident,
state_machine: $state_machine:expr,
pin: $pin:ident,
dma: $dma:ident,
spawner: $spawner:ident
) => {
$label::new($state_machine, $pin, $dma, $spawner)?
};
(@__strip_return_value
label: $label:ident,
state_machine: $state_machine:expr,
pin: $pin:ident,
dma: $dma:ident,
spawner: $spawner:ident,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
$(max_frames: $led2d_max_frames:expr,)?
font: $led2d_font:expr $(,)?
}
) => {
paste::paste! {{
let [<$label:snake _led_strip>] =
[<$label:camel LedStrip>]::new($state_machine, $pin, $dma, $spawner)?;
$label::from_strip([<$label:snake _led_strip>])?
}}
};
(@__with_frame_alias
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
$group:ident {
$( $label:ident: { $($fields:tt)* } ),+ $(,)?
}
) => {
$crate::__led_strips_impl! {
@__with_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
sm_counter: 0,
strips_out: [],
strips_in: [ $( $label: { $($fields)* } ),+ ]
}
};
(
pio: $pio:ident,
$vis:vis $group:ident {
$( $label:ident: { $($fields:tt)* } ),+ $(,)?
}
) => {
$crate::__led_strips_impl! {
@__with_defaults
frame_alias: __WITH_FRAME_ALIAS__,
pio: $pio,
vis: $vis,
group: $group,
sm_counter: 0,
strips_out: [],
strips_in: [ $( $label: { $($fields)* } ),+ ]
}
};
(
$vis:vis $group:ident {
$( $label:ident: { $($fields:tt)* } ),+ $(,)?
}
) => {
$crate::__led_strips_impl! {
@__with_defaults
frame_alias: __WITH_FRAME_ALIAS__,
pio: PIO0,
vis: $vis,
group: $group,
sm_counter: 0,
strips_out: [],
strips_in: [ $( $label: { $($fields)* } ),+ ]
}
};
(@__with_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_in: [ $label:ident: { $($fields:tt)* } $(, $($rest:tt)* )? ]
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: $sm,
strips_out: [ $($out)* ],
strips_remaining: [ $($($rest)*)? ],
label: $label,
group: $group,
pin: __MISSING_PIN__,
dma: __DEFAULT_DMA__,
len: __MISSING_LEN__,
max_current: __MISSING_MAX_CURRENT__,
gamma: $crate::led_strip::GAMMA_DEFAULT,
max_frames: $crate::led_strip::MAX_FRAMES_DEFAULT,
led2d: __NONE__,
fields: [ $($fields)* ]
}
};
(@__with_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_in: []
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [],
strips_in: [ $($out)* ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: []
) => {
$crate::__led_strips_impl! {
@__expand
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips: [ $($out)* ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: 0,
dma: __DEFAULT_DMA__,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
$(,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
$(max_frames: $led2d_max_frames:expr,)?
font: $led2d_font:expr $(,)?
}
)?
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: 0,
dma: DMA_CH0,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
$(,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
font: $led2d_font,
}
)?
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: 0,
dma: __DEFAULT_DMA__,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
font: $led2d_font:expr $(,)?
}
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: 0,
dma: DMA_CH0,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
max_frames: $max_frames,
font: $led2d_font,
}
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: 1,
dma: __DEFAULT_DMA__,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
font: $led2d_font:expr $(,)?
}
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: 1,
dma: DMA_CH1,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
max_frames: $max_frames,
font: $led2d_font,
}
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: 1,
dma: __DEFAULT_DMA__,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
$(,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
$(max_frames: $led2d_max_frames:expr,)?
font: $led2d_font:expr $(,)?
}
)?
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: 1,
dma: DMA_CH1,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
$(,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
font: $led2d_font,
}
)?
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: 2,
dma: __DEFAULT_DMA__,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
font: $led2d_font:expr $(,)?
}
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: 2,
dma: DMA_CH2,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
max_frames: $max_frames,
font: $led2d_font,
}
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: 3,
dma: __DEFAULT_DMA__,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
font: $led2d_font:expr $(,)?
}
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: 3,
dma: DMA_CH3,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
max_frames: $max_frames,
font: $led2d_font,
}
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: 2,
dma: __DEFAULT_DMA__,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
$(,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
$(max_frames: $led2d_max_frames:expr,)?
font: $led2d_font:expr $(,)?
}
)?
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: 2,
dma: DMA_CH2,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
$(,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
font: $led2d_font,
}
)?
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: 3,
dma: __DEFAULT_DMA__,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
$(,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
$(max_frames: $led2d_max_frames:expr,)?
font: $led2d_font:expr $(,)?
}
)?
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: 3,
dma: DMA_CH3,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
$(,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
font: $led2d_font,
}
)?
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__resolve_default_dma
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
group: $group:ident,
strips_out: [ $($out:tt)* ],
strips_in: [
$label:ident {
sm: $sm_index:expr,
dma: $dma:ident,
pin: $pin:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr
$(,
led2d: {
led_layout: $led2d_led_layout:ident $( ( $($led2d_led_layout_args:tt)* ) )?,
$(max_frames: $led2d_max_frames:expr,)?
font: $led2d_font:expr $(,)?
}
)?
}
$(, $($rest:tt)* )?
]
) => {
$crate::__led_strips_impl! {
@__resolve_default_dma
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
strips_out: [
$($out)*
$label {
sm: $sm_index,
dma: $dma,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
$(,
led2d: {
led_layout: $led2d_led_layout $( ( $($led2d_led_layout_args)* ) )?,
font: $led2d_font,
}
)?
},
],
strips_in: [ $($($rest)*)? ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: [ pin: $new_pin:ident $(, $($rest:tt)* )? ]
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: $sm,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $new_pin,
dma: $dma,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: [ $($($rest)*)? ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: [ dma: $new_dma:ident $(, $($rest:tt)* )? ]
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: $sm,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: $new_dma,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: [ $($($rest)*)? ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: [ len: $new_len:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: $sm,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: $dma,
len: $new_len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: [ $($($rest)*)? ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: [ max_current: $new_max_current:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: $sm,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: $dma,
len: $len,
max_current: $new_max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: [ $($($rest)*)? ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: [ gamma: $new_gamma:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: $sm,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: $dma,
len: $len,
max_current: $max_current,
gamma: $new_gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: [ $($($rest)*)? ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: [ max_frames: $new_max_frames:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: $sm,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: $dma,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $new_max_frames,
led2d: $led2d,
fields: [ $($($rest)*)? ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: __NONE__,
fields: [ led2d: { $($led2d_fields:tt)* } $(, $($rest:tt)* )? ]
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: $sm,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: $dma,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: __HAS_LED2D__ { $($led2d_fields)* },
fields: [ $($($rest)*)? ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: 0,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:ident,
dma: __DEFAULT_DMA__,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: []
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: 0,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: DMA_CH0,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: []
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: 1,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:ident,
dma: __DEFAULT_DMA__,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: []
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: 1,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: DMA_CH1,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: []
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: 2,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:ident,
dma: __DEFAULT_DMA__,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: []
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: 2,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: DMA_CH2,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: []
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: 3,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:ident,
dma: __DEFAULT_DMA__,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: $led2d:tt,
fields: []
) => {
$crate::__led_strips_impl! {
@__fill_strip_defaults
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
sm_counter: 3,
strips_out: [ $($out)* ],
strips_remaining: [ $($remaining)* ],
label: $label,
group: $group,
pin: $pin,
dma: DMA_CH3,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: $led2d,
fields: []
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:ident,
dma: $dma:ident,
len: $len:expr,
max_current: __MISSING_MAX_CURRENT__,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: __NONE__,
fields: []
) => {
compile_error!("led_strips!: max_current is required for every strip");
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:ident,
dma: $dma:ident,
len: $len:expr,
max_current: __MISSING_MAX_CURRENT__,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: __HAS_LED2D__ { $($led2d_fields:tt)* },
fields: []
) => {
compile_error!("led_strips!: max_current is required for every strip");
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:ident,
dma: $dma:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: __NONE__,
fields: []
) => {
$crate::__led_strips_impl! {
@__inc_counter
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
sm: $sm,
strips_out: [
$($out)*
$label {
sm: $sm,
dma: $dma,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames
},
],
strips_in: [ $($remaining)* ]
}
};
(@__fill_strip_defaults
frame_alias: $frame_alias:tt,
pio: $pio:ident,
vis: $vis:vis,
sm_counter: $sm:tt,
strips_out: [ $($out:tt)* ],
strips_remaining: [ $($remaining:tt)* ],
label: $label:ident,
group: $group:ident,
pin: $pin:ident,
dma: $dma:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
led2d: __HAS_LED2D__ { $($led2d_fields:tt)* },
fields: []
) => {
$crate::__led_strips_impl! {
@__inc_counter
frame_alias: $frame_alias,
pio: $pio,
vis: $vis,
group: $group,
sm: $sm,
strips_out: [
$($out)*
$label {
sm: $sm,
dma: $dma,
pin: $pin,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
led2d: { $($led2d_fields)* }
},
],
strips_in: [ $($remaining)* ]
}
};
(@__inc_counter frame_alias: $frame_alias:tt, pio: $pio:ident, vis: $vis:vis, group: $group:ident, sm: 0, strips_out: [$($out:tt)*], strips_in: [$($in:tt)*]) => {
$crate::__led_strips_impl! { @__with_defaults frame_alias: $frame_alias, pio: $pio, vis: $vis, group: $group, sm_counter: 1, strips_out: [$($out)*], strips_in: [$($in)*] }
};
(@__inc_counter frame_alias: $frame_alias:tt, pio: $pio:ident, vis: $vis:vis, group: $group:ident, sm: 1, strips_out: [$($out:tt)*], strips_in: [$($in:tt)*]) => {
$crate::__led_strips_impl! { @__with_defaults frame_alias: $frame_alias, pio: $pio, vis: $vis, group: $group, sm_counter: 2, strips_out: [$($out)*], strips_in: [$($in)*] }
};
(@__inc_counter frame_alias: $frame_alias:tt, pio: $pio:ident, vis: $vis:vis, group: $group:ident, sm: 2, strips_out: [$($out:tt)*], strips_in: [$($in:tt)*]) => {
$crate::__led_strips_impl! { @__with_defaults frame_alias: $frame_alias, pio: $pio, vis: $vis, group: $group, sm_counter: 3, strips_out: [$($out)*], strips_in: [$($in)*] }
};
(@__inc_counter frame_alias: $frame_alias:tt, pio: $pio:ident, vis: $vis:vis, group: $group:ident, sm: 3, strips_out: [$($out:tt)*], strips_in: [$($in:tt)*]) => {
$crate::__led_strips_impl! { @__with_defaults frame_alias: $frame_alias, pio: $pio, vis: $vis, group: $group, sm_counter: 4, strips_out: [$($out)*], strips_in: [$($in)*] }
};
}
#[doc = include_str!("docs/current_limiting_and_gamma.md")]
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! led_strip {
($($tt:tt)*) => { $crate::__led_strip_impl! { $($tt)* } };
}
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_impl {
(
$name:ident {
$($fields:tt)*
}
) => {
$crate::__validate_keyword_fields_expr! {
macro_name: "led_strip!",
allowed_macro: $crate::__led_strip_allowed_field,
fields: [ $($fields)* ]
}
$crate::__led_strip_impl! {
@__fill_defaults
vis: pub(self),
pio: PIO0,
name: $name,
pin: _UNSET_,
dma: DMA_CH0,
len: _UNSET_,
max_current: _UNSET_,
gamma: $crate::led_strip::GAMMA_DEFAULT,
max_frames: $crate::led_strip::MAX_FRAMES_DEFAULT,
fields: [ $($fields)* ]
}
};
(
$vis:vis $name:ident {
$($fields:tt)*
}
) => {
$crate::__validate_keyword_fields_expr! {
macro_name: "led_strip!",
allowed_macro: $crate::__led_strip_allowed_field,
fields: [ $($fields)* ]
}
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: PIO0,
name: $name,
pin: _UNSET_,
dma: DMA_CH0,
len: _UNSET_,
max_current: _UNSET_,
gamma: $crate::led_strip::GAMMA_DEFAULT,
max_frames: $crate::led_strip::MAX_FRAMES_DEFAULT,
fields: [ $($fields)* ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: [ pio: $new_pio:ident $(, $($rest:tt)* )? ]
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $new_pio,
name: $name,
pin: $pin,
dma: $dma,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: [ pin: $new_pin:ident $(, $($rest:tt)* )? ]
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $pio,
name: $name,
pin: $new_pin,
dma: $dma,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: [ dma: $new_dma:ident $(, $($rest:tt)* )? ]
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $pio,
name: $name,
pin: $pin,
dma: $new_dma,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: [ len: { $new_len:expr } $(, $($rest:tt)* )? ]
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $pio,
name: $name,
pin: $pin,
dma: $dma,
len: { $new_len },
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: [ len: $new_len:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $pio,
name: $name,
pin: $pin,
dma: $dma,
len: $new_len,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: [ max_current: $new_max_current:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $pio,
name: $name,
pin: $pin,
dma: $dma,
len: $len,
max_current: $new_max_current,
gamma: $gamma,
max_frames: $max_frames,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: [ gamma: $new_gamma:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $pio,
name: $name,
pin: $pin,
dma: $dma,
len: $len,
max_current: $max_current,
gamma: $new_gamma,
max_frames: $max_frames,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:tt,
dma: $dma:ident,
len: $len:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: [ max_frames: $new_max_frames:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $pio,
name: $name,
pin: $pin,
dma: $dma,
len: $len,
max_current: $max_current,
gamma: $gamma,
max_frames: $new_max_frames,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:ident,
dma: $dma:ident,
len: $len:expr,
max_current: _UNSET_,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: []
) => {
$crate::__led_strip_impl! {
@__fill_defaults
vis: $vis,
pio: $pio,
name: $name,
pin: $pin,
dma: $dma,
len: $len,
max_current: $crate::led_strip::MAX_CURRENT_DEFAULT,
gamma: $gamma,
max_frames: $max_frames,
fields: []
}
};
(@__fill_defaults
vis: $vis:vis,
pio: $pio:ident,
name: $name:ident,
pin: $pin:ident,
dma: $dma:ident,
len: $len:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
fields: []
) => {
::paste::paste! {
#[allow(non_upper_case_globals)]
static [<$name:snake _ $pio _BUS>]: ::static_cell::StaticCell<
$crate::led_strip::PioBus<'static, ::embassy_rp::peripherals::$pio>
> = ::static_cell::StaticCell::new();
#[allow(dead_code)]
fn [<$name:snake _split_sm0>](
pio: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pio>,
) -> $crate::led_strip::PioBusStateMachine<::embassy_rp::peripherals::$pio, 0> {
let ::embassy_rp::pio::Pio { common, sm0, .. } =
::embassy_rp::pio::Pio::new(
pio,
<::embassy_rp::peripherals::$pio as $crate::pio_irqs::PioIrqMap>::irqs(),
);
let pio_bus = [<$name:snake _ $pio _BUS>].init_with(|| {
$crate::led_strip::PioBus::new(common)
});
$crate::led_strip::PioBusStateMachine::new(pio_bus, sm0)
}
#[doc = concat!(
"LED strip generated by [`led_strip!`] or [`led_strips!`](crate::led_strips!).\n\n",
"See the [led_strip module documentation](mod@crate::led_strip) for usage and examples."
)]
$vis struct $name {
strip: $crate::led_strip::LedStripRp<{ $len }, { $max_frames }>,
}
impl $name {
pub const LEN: usize = $len;
pub const MAX_FRAMES: usize = $max_frames;
const WORST_CASE_MA: u32 = ($len as u32) * 60;
pub const MAX_BRIGHTNESS: u8 =
$max_current.max_brightness(Self::WORST_CASE_MA);
const COMBO_TABLE: [u8; 256] = $crate::led_strip::generate_combo_table($gamma, Self::MAX_BRIGHTNESS);
pub fn new(
pin: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>>,
pio: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pio>,
dma: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>>,
spawner: ::embassy_executor::Spawner,
) -> $crate::Result<&'static Self> {
static STRIP_STATIC: $crate::led_strip::LedStripStatic<{ $len }, { $max_frames }> =
$crate::led_strip::LedStripRp::new_static();
static STRIP_CELL: ::static_cell::StaticCell<$name> = ::static_cell::StaticCell::new();
let pin = pin.into();
let dma = dma.into();
let sm0 = [<$name:snake _split_sm0>](pio);
let (bus, sm) = sm0.into_parts();
let token = [<$name:snake _device_task>](
bus,
sm,
dma,
pin,
STRIP_STATIC.command_signal(),
);
spawner.spawn(token.map_err($crate::Error::TaskSpawn)?);
let strip = $crate::led_strip::LedStripRp::new(&STRIP_STATIC);
let instance = STRIP_CELL.init($name { strip });
Ok(instance)
}
}
#[cfg(not(feature = "host"))]
impl AsRef<$crate::led_strip::LedStripRp<{ $len }, { $max_frames }>> for $name {
fn as_ref(&self) -> &$crate::led_strip::LedStripRp<{ $len }, { $max_frames }> {
&self.strip
}
}
impl $crate::led_strip::LedStrip<{ $len }> for $name {
const MAX_FRAMES: usize = $max_frames;
const MAX_BRIGHTNESS: u8 = Self::MAX_BRIGHTNESS;
fn write_frame(&self, frame: $crate::led_strip::Frame1d<{ $len }>) {
$crate::led_strip::__write_frame(self.strip.__command_signal(), frame);
}
fn animate<I>(&self, frames: I)
where
I: IntoIterator,
I::Item: ::core::borrow::Borrow<(
$crate::led_strip::Frame1d<{ $len }>,
embassy_time::Duration,
)>,
{
$crate::led_strip::__animate(self.strip.__command_signal(), frames);
}
}
#[::embassy_executor::task]
async fn [<$name:snake _device_task>](
bus: &'static $crate::led_strip::PioBus<'static, ::embassy_rp::peripherals::$pio>,
sm: ::embassy_rp::pio::StateMachine<'static, ::embassy_rp::peripherals::$pio, 0>,
dma: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>,
pin: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>,
command_signal: &'static $crate::led_strip::LedStripCommandSignal<{ $len }, { $max_frames }>,
) -> ! {
let program = bus.get_program();
let driver = bus.with_common(|common| {
::embassy_rp::pio_programs::ws2812::PioWs2812::<
::embassy_rp::peripherals::$pio,
0,
{ $len },
_
>::new(
common,
sm,
dma,
<::embassy_rp::peripherals::$dma as $crate::pio_irqs::DmaIrqMap>::irqs(),
pin,
program,
)
});
$crate::led_strip::led_strip_device_loop
::<
::embassy_rp::peripherals::$pio,
0,
{ $len },
{ $max_frames },
_
>(driver, command_signal, &$name::COMBO_TABLE).await
}
}
};
}
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_allowed_field {
(pin, $macro_name:literal) => {};
(len, $macro_name:literal) => {};
(pio, $macro_name:literal) => {};
(dma, $macro_name:literal) => {};
(max_current, $macro_name:literal) => {};
(gamma, $macro_name:literal) => {};
(max_frames, $macro_name:literal) => {};
($field:ident, $macro_name:literal) => {
compile_error!(concat!(
$macro_name,
" unknown field; expected `pin`, `len`, `pio`, `dma`, `max_current`, `gamma`, or `max_frames`"
));
};
}
#[doc(hidden)]
#[cfg(not(feature = "host"))]
#[macro_export]
macro_rules! pio_split {
($p:ident . PIO0) => {
pio0_split($p.PIO0)
};
($p:ident . PIO1) => {
pio1_split($p.PIO1)
};
($p:ident . PIO2) => {
pio2_split($p.PIO2)
};
}
#[cfg(not(feature = "host"))]
pub use pio_split;
#[cfg(not(feature = "host"))]
#[doc(inline)]
pub use led_strip;
#[cfg(not(feature = "host"))]
#[doc(inline)]
pub use led_strips;
#[doc(hidden)]
pub const MAX_CURRENT_DEFAULT: Current = Current::Milliamps(250);