use embassy_time::Timer;
use fixed::types::U24F8;
use smart_leds::{RGB8, RGBW};
use crate::clocks::clk_sys_freq;
use crate::pio::{
Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
};
use crate::{Peri, dma, interrupt};
const T1: u8 = 2; const T2: u8 = 5; const T3: u8 = 3; const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
pub trait RgbColorOrder {
fn pack(color: RGB8) -> u32;
}
pub struct Grb;
impl RgbColorOrder for Grb {
fn pack(color: RGB8) -> u32 {
(u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8)
}
}
pub struct Rgb;
impl RgbColorOrder for Rgb {
fn pack(color: RGB8) -> u32 {
(u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8)
}
}
pub trait RgbwColorOrder {
fn pack(color: RGBW<u8>) -> u32;
}
pub struct Grbw;
impl RgbwColorOrder for Grbw {
fn pack(color: RGBW<u8>) -> u32 {
(u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8) | u32::from(color.a.0)
}
}
pub struct Rgbw;
impl RgbwColorOrder for Rgbw {
fn pack(color: RGBW<u8>) -> u32 {
(u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8) | u32::from(color.a.0)
}
}
pub struct PioWs2812Program<'a, PIO: Instance> {
prg: LoadedProgram<'a, PIO>,
}
impl<'a, PIO: Instance> PioWs2812Program<'a, PIO> {
pub fn new(common: &mut Common<'a, PIO>) -> Self {
let side_set = pio::SideSet::new(false, 1, false);
let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
let mut wrap_target = a.label();
let mut wrap_source = a.label();
let mut do_zero = a.label();
a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
a.bind(&mut wrap_target);
a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
a.bind(&mut do_zero);
a.nop_with_delay_and_side_set(T2 - 1, 0);
a.bind(&mut wrap_source);
let prg = a.assemble_with_wrap(wrap_source, wrap_target);
let prg = common.load_program(&prg);
Self { prg }
}
}
pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER>
where
ORDER: RgbColorOrder,
{
dma: dma::Channel<'d>,
sm: StateMachine<'d, P, S>,
_order: core::marker::PhantomData<ORDER>,
}
impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N, Grb> {
pub fn new<D: dma::ChannelInstance>(
pio: &mut Common<'d, P>,
sm: StateMachine<'d, P, S>,
dma: Peri<'d, D>,
irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,
pin: Peri<'d, impl PioPin>,
program: &PioWs2812Program<'d, P>,
) -> Self {
Self::with_color_order(pio, sm, dma, irq, pin, program)
}
}
impl<'d, P: Instance, const S: usize, const N: usize, ORDER> PioWs2812<'d, P, S, N, ORDER>
where
ORDER: RgbColorOrder,
{
pub fn with_color_order<D: dma::ChannelInstance>(
pio: &mut Common<'d, P>,
mut sm: StateMachine<'d, P, S>,
dma: Peri<'d, D>,
irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,
pin: Peri<'d, impl PioPin>,
program: &PioWs2812Program<'d, P>,
) -> Self {
let mut cfg = Config::default();
let out_pin = pio.make_pio_pin(pin);
cfg.set_out_pins(&[&out_pin]);
cfg.set_set_pins(&[&out_pin]);
cfg.use_program(&program.prg, &[&out_pin]);
let clock_freq = U24F8::from_num(clk_sys_freq() / 1000);
let ws2812_freq = U24F8::from_num(800);
let bit_freq = ws2812_freq * CYCLES_PER_BIT;
cfg.clock_divider = clock_freq / bit_freq;
cfg.fifo_join = FifoJoin::TxOnly;
cfg.shift_out = ShiftConfig {
auto_fill: true,
threshold: 24,
direction: ShiftDirection::Left,
};
sm.set_config(&cfg);
sm.set_enable(true);
Self {
dma: dma::Channel::new(dma, irq),
sm,
_order: core::marker::PhantomData,
}
}
pub async fn write(&mut self, colors: &[RGB8; N]) {
let mut words = [0u32; N];
for i in 0..N {
words[i] = ORDER::pack(colors[i]);
}
self.sm.tx().dma_push(&mut self.dma, &words, false).await;
Timer::after_micros(55).await;
}
}
pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER>
where
ORDER: RgbwColorOrder,
{
dma: dma::Channel<'d>,
sm: StateMachine<'d, P, S>,
_order: core::marker::PhantomData<ORDER>,
}
impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N, Grbw> {
pub fn new<D: dma::ChannelInstance>(
pio: &mut Common<'d, P>,
sm: StateMachine<'d, P, S>,
dma: Peri<'d, D>,
irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,
pin: Peri<'d, impl PioPin>,
program: &PioWs2812Program<'d, P>,
) -> Self {
Self::with_color_order(pio, sm, dma, irq, pin, program)
}
}
impl<'d, P: Instance, const S: usize, const N: usize, ORDER> RgbwPioWs2812<'d, P, S, N, ORDER>
where
ORDER: RgbwColorOrder,
{
pub fn with_color_order<D: dma::ChannelInstance>(
pio: &mut Common<'d, P>,
mut sm: StateMachine<'d, P, S>,
dma: Peri<'d, D>,
irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,
pin: Peri<'d, impl PioPin>,
program: &PioWs2812Program<'d, P>,
) -> Self {
let mut cfg = Config::default();
let out_pin = pio.make_pio_pin(pin);
cfg.set_out_pins(&[&out_pin]);
cfg.set_set_pins(&[&out_pin]);
cfg.use_program(&program.prg, &[&out_pin]);
let clock_freq = U24F8::from_num(clk_sys_freq() / 1000);
let ws2812_freq = U24F8::from_num(800);
let bit_freq = ws2812_freq * CYCLES_PER_BIT;
cfg.clock_divider = clock_freq / bit_freq;
cfg.fifo_join = FifoJoin::TxOnly;
cfg.shift_out = ShiftConfig {
auto_fill: true,
threshold: 32,
direction: ShiftDirection::Left,
};
sm.set_config(&cfg);
sm.set_enable(true);
Self {
dma: dma::Channel::new(dma, irq),
sm,
_order: core::marker::PhantomData,
}
}
pub async fn write(&mut self, colors: &[RGBW<u8>; N]) {
let mut words = [0u32; N];
for i in 0..N {
words[i] = ORDER::pack(colors[i]);
}
self.sm.tx().dma_push(&mut self.dma, &words, false).await;
Timer::after_micros(55).await;
}
}