#![cfg_attr(
feature = "doc-images",
doc = ::embed_doc_image::embed_image!("led2d1", "docs/assets/led2d1.png"),
doc = ::embed_doc_image::embed_image!("led2d2", "docs/assets/led2d2.png")
)]
#[doc(hidden)]
pub use paste;
pub mod layout {
pub use device_envoy_core::led2d::layout::*;
}
pub use device_envoy_core::led2d::Led2d;
pub use device_envoy_core::led2d::{
Frame2d, Led2dFont, Led2dStripAdapter, Led2dStripBacked, Point, Size, bit_matrix3x4_font,
render_text_to_frame,
};
pub use layout::LedLayout;
pub mod led2d_generated;
#[doc(hidden)]
pub type Led2dRp<'a, const N: usize, S> = Led2dStripAdapter<'a, N, S>;
#[doc = include_str!("docs/current_limiting_and_gamma.md")]
#[macro_export]
#[cfg(not(feature = "host"))]
#[doc(hidden)]
macro_rules! led2d {
($($tt:tt)*) => { $crate::__led2d_impl! { $($tt)* } };
}
#[doc(hidden)] #[macro_export]
#[cfg(not(feature = "host"))]
macro_rules! __led2d_impl {
(
$name:ident,
$($fields:tt)*
) => {
$crate::__led2d_impl! { pub $name, $($fields)* }
};
(
$vis:vis $name:ident,
$($fields:tt)*
) => {
$crate::__validate_keyword_fields_expr! {
macro_name: "led2d!",
allowed_macro: $crate::__led2d_allowed_field,
fields: [ $($fields)* ]
}
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: PIO0,
pin: _UNSET_,
dma: DMA_CH0,
led_layout: _UNSET_,
max_current: _UNSET_,
gamma: $crate::led_strip::GAMMA_DEFAULT,
max_frames: $crate::led_strip::MAX_FRAMES_DEFAULT,
font: _UNSET_,
fields: [ $($fields)* ]
}
};
(
$name:ident {
$($fields:tt)*
}
) => {
$crate::__validate_keyword_fields_expr! {
macro_name: "led2d!",
allowed_macro: $crate::__led2d_allowed_field,
fields: [ $($fields)* ]
}
$crate::__led2d_impl! {
@__fill_defaults
vis: pub(self),
name: $name,
pio: PIO0,
pin: _UNSET_,
dma: DMA_CH0,
led_layout: _UNSET_,
max_current: _UNSET_,
gamma: $crate::led_strip::GAMMA_DEFAULT,
max_frames: $crate::led_strip::MAX_FRAMES_DEFAULT,
font: _UNSET_,
fields: [ $($fields)* ]
}
};
(
$vis:vis $name:ident {
$($fields:tt)*
}
) => {
$crate::__validate_keyword_fields_expr! {
macro_name: "led2d!",
allowed_macro: $crate::__led2d_allowed_field,
fields: [ $($fields)* ]
}
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: PIO0,
pin: _UNSET_,
dma: DMA_CH0,
led_layout: _UNSET_,
max_current: _UNSET_,
gamma: $crate::led_strip::GAMMA_DEFAULT,
max_frames: $crate::led_strip::MAX_FRAMES_DEFAULT,
font: _UNSET_,
fields: [ $($fields)* ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ pio: $new_pio:ident $(, $($rest:tt)* )? ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $new_pio,
pin: $pin,
dma: $dma,
led_layout: $led_layout,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
font: $font_variant,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ pin: $new_pin:ident $(, $($rest:tt)* )? ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $pio,
pin: $new_pin,
dma: $dma,
led_layout: $led_layout,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
font: $font_variant,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ dma: $new_dma:ident $(, $($rest:tt)* )? ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $pio,
pin: $pin,
dma: $new_dma,
led_layout: $led_layout,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
font: $font_variant,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ led_layout: $new_led_layout:tt $(, $($rest:tt)* )? ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $pio,
pin: $pin,
dma: $dma,
led_layout: $new_led_layout,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
font: $font_variant,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ max_current: $new_max_current:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $pio,
pin: $pin,
dma: $dma,
led_layout: $led_layout,
max_current: $new_max_current,
gamma: $gamma,
max_frames: $max_frames,
font: $font_variant,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ gamma: $new_gamma:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $pio,
pin: $pin,
dma: $dma,
led_layout: $led_layout,
max_current: $max_current,
gamma: $new_gamma,
max_frames: $max_frames,
font: $font_variant,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ max_frames: $new_max_frames:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $pio,
pin: $pin,
dma: $dma,
led_layout: $led_layout,
max_current: $max_current,
gamma: $gamma,
max_frames: $new_max_frames,
font: $font_variant,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:tt,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ font: $new_font_variant:expr $(, $($rest:tt)* )? ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $pio,
pin: $pin,
dma: $dma,
led_layout: $led_layout,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
font: $new_font_variant,
fields: [ $($($rest)*)? ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: _UNSET_,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:tt,
fields: [ ]
) => {
$crate::__led2d_impl! {
@__fill_defaults
vis: $vis,
name: $name,
pio: $pio,
pin: $pin,
dma: $dma,
led_layout: $led_layout,
max_current: $crate::led_strip::MAX_CURRENT_DEFAULT,
gamma: $gamma,
max_frames: $max_frames,
font: $font_variant,
fields: [ ]
}
};
(@__fill_defaults
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:tt,
dma: $dma:ident,
led_layout: $led_layout:tt,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:expr,
fields: [ ]
) => {
$crate::__led2d_impl! {
@__expand
vis: $vis,
name: $name,
pio: $pio,
pin: $pin,
dma: $dma,
led_layout: $led_layout,
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
font: $font_variant
}
};
(@__expand
vis: $vis:vis,
name: $name:ident,
pio: $pio:ident,
pin: $pin:ident,
dma: $dma:ident,
led_layout: $led_layout:expr,
max_current: $max_current:expr,
gamma: $gamma:expr,
max_frames: $max_frames:expr,
font: $font_variant:expr
) => {
$crate::led2d::paste::paste! {
const [<$name:upper _LAYOUT>]: $crate::led2d::LedLayout<
{ $led_layout.len() },
{ $led_layout.width() },
{ $led_layout.height() }
> = $led_layout;
$crate::__led_strips_impl! {
@__with_frame_alias
frame_alias: __SKIP_FRAME_ALIAS__,
pio: $pio,
vis: $vis,
[<$name Strips>] {
[<$name LedStrip>]: {
dma: $dma,
pin: $pin,
len: { [<$name:upper _LAYOUT>].len() },
max_current: $max_current,
gamma: $gamma,
max_frames: $max_frames,
}
}
}
const [<$name:upper _MAX_FRAMES>]: usize = [<$name LedStrip>]::MAX_FRAMES;
const _: () = assert!([<$name:upper _LAYOUT>].index_to_xy().len() == [<$name LedStrip>]::LEN);
$crate::led2d::led2d_from_strip! {
@__from_layout_const
$vis $name,
strip_type: [<$name LedStrip>],
led_layout_const: [<$name:upper _LAYOUT>],
font: $font_variant,
max_frames_const: [<$name:upper _MAX_FRAMES>],
}
#[allow(non_snake_case, dead_code)]
impl [<$name>] {
#[allow(non_upper_case_globals)]
$vis fn new(
pin: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>,
pio: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pio>,
dma: ::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$dma>,
spawner: ::embassy_executor::Spawner,
) -> $crate::Result<Self> {
let (sm0, _sm1, _sm2, _sm3) = [<$pio:lower _split>](pio);
let led_strip = [<$name LedStrip>]::new(
sm0,
pin,
dma,
spawner
)?;
[<$name>]::from_strip(led_strip)
}
}
}
};
}
#[cfg(not(feature = "host"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __led2d_allowed_field {
(pio, $macro_name:literal) => {};
(pin, $macro_name:literal) => {};
(dma, $macro_name:literal) => {};
(led_layout, $macro_name:literal) => {};
(max_current, $macro_name:literal) => {};
(gamma, $macro_name:literal) => {};
(max_frames, $macro_name:literal) => {};
(font, $macro_name:literal) => {};
($field:ident, $macro_name:literal) => {
compile_error!(concat!(
$macro_name,
" unknown field; expected `pio`, `pin`, `dma`, `led_layout`, `max_current`, `gamma`, `max_frames`, or `font`"
));
};
}
#[doc(hidden)] #[macro_export]
#[cfg(not(feature = "host"))]
macro_rules! led2d_from_strip {
(
$vis:vis $name:ident,
strip_type: $strip_type:ident,
width: $width:expr,
height: $height:expr,
led_layout: serpentine_column_major,
font: $font_variant:expr $(,)?
) => {
$crate::led2d::paste::paste! {
const [<$name:upper _LED_LAYOUT>]: $crate::led2d::LedLayout<{ $width * $height }, { $width }, { $height }> =
$crate::led2d::LedLayout::<{ $width * $height }, { $width }, { $height }>::serpentine_column_major();
const [<$name:upper _MAX_FRAMES>]: usize = $strip_type::MAX_FRAMES;
const _: () = assert!([<$name:upper _LED_LAYOUT>].index_to_xy().len() == $strip_type::LEN);
$crate::led2d::led2d_from_strip!(
@common $vis, $name, $strip_type, [<$name:upper _LED_LAYOUT>],
$font_variant,
[<$name:upper _MAX_FRAMES>]
);
}
};
(
$vis:vis $name:ident,
strip_type: $strip_type:ident,
width: $width:expr,
height: $height:expr,
led_layout: $led_layout:expr,
font: $font_variant:expr $(,)?
) => {
$crate::led2d::paste::paste! {
const [<$name:upper _LED_LAYOUT>]: $crate::led2d::LedLayout<{ $width * $height }, { $width }, { $height }> = $led_layout;
const [<$name:upper _MAX_FRAMES>]: usize = $strip_type::MAX_FRAMES;
const _: () = assert!([<$name:upper _LED_LAYOUT>].index_to_xy().len() == $strip_type::LEN);
$crate::led2d::led2d_from_strip!(
@common $vis, $name, $strip_type, [<$name:upper _LED_LAYOUT>],
$font_variant,
[<$name:upper _MAX_FRAMES>]
);
}
};
(
@__from_layout_const
$vis:vis $name:ident,
strip_type: $strip_type:ident,
led_layout_const: $led_layout_const:ident,
font: $font_variant:expr,
max_frames_const: $max_frames_const:ident $(,)?
) => {
$crate::led2d::led2d_from_strip!(
@common $vis, $name, $strip_type, $led_layout_const,
$font_variant,
$max_frames_const
);
};
(
@common $vis:vis,
$name:ident,
$strip_type:ident,
$led_layout_const:ident,
$font_variant:expr,
$max_frames_const:ident
) => {
$crate::led2d::paste::paste! {
$vis struct [<$name>] {
led2d: $crate::led2d::Led2dRp<'static, { $led_layout_const.len() }, $strip_type>,
}
#[allow(non_snake_case, dead_code)]
impl [<$name>] {
pub const MAX_FRAMES: usize = $max_frames_const;
pub const MAX_BRIGHTNESS: u8 = $strip_type::MAX_BRIGHTNESS;
pub const FONT: $crate::led2d::Led2dFont = $font_variant;
pub const WIDTH: usize = $led_layout_const.width();
pub const HEIGHT: usize = $led_layout_const.height();
pub const LEN: usize = $led_layout_const.len();
pub const SIZE: $crate::led2d::Size =
$crate::led2d::Frame2d::<{ $led_layout_const.width() }, { $led_layout_const.height() }>::SIZE;
pub const TOP_LEFT: $crate::led2d::Point =
$crate::led2d::Frame2d::<{ $led_layout_const.width() }, { $led_layout_const.height() }>::TOP_LEFT;
pub const TOP_RIGHT: $crate::led2d::Point =
$crate::led2d::Frame2d::<{ $led_layout_const.width() }, { $led_layout_const.height() }>::TOP_RIGHT;
pub const BOTTOM_LEFT: $crate::led2d::Point =
$crate::led2d::Frame2d::<{ $led_layout_const.width() }, { $led_layout_const.height() }>::BOTTOM_LEFT;
pub const BOTTOM_RIGHT: $crate::led2d::Point =
$crate::led2d::Frame2d::<{ $led_layout_const.width() }, { $led_layout_const.height() }>::BOTTOM_RIGHT;
#[doc(hidden)]
$vis fn from_strip(
led_strip: &'static $strip_type,
) -> $crate::Result<Self> {
let led2d = $crate::led2d::Led2dRp::new(
led_strip,
&$led_layout_const,
);
defmt::info!("Led2dRp::new: device created successfully");
Ok(Self { led2d })
}
}
impl $crate::led2d::Led2d<{ $led_layout_const.width() }, { $led_layout_const.height() }>
for [<$name>]
{
const MAX_FRAMES: usize = $max_frames_const;
const MAX_BRIGHTNESS: u8 = $strip_type::MAX_BRIGHTNESS;
const FONT: $crate::led2d::Led2dFont = $font_variant;
fn write_frame(
&self,
frame2d: $crate::led2d::Frame2d<{ $led_layout_const.width() }, { $led_layout_const.height() }>,
) {
$crate::led2d::Led2dStripBacked::write_frame(&self.led2d, frame2d);
}
fn animate<I>(&self, frames: I)
where
I: IntoIterator,
I::Item: core::borrow::Borrow<(
$crate::led2d::Frame2d<{ $led_layout_const.width() }, { $led_layout_const.height() }>,
embassy_time::Duration,
)>,
{
$crate::led2d::Led2dStripBacked::animate(&self.led2d, frames);
}
}
}
};
}
#[cfg(not(feature = "host"))]
#[doc(inline)]
pub use led2d;
#[cfg(not(feature = "host"))]
#[doc(hidden)] pub use led2d_from_strip;