#![no_std]
use esp_hal::Blocking;
use esp_hal::gpio::Level;
use esp_hal::gpio::interconnect::PeripheralOutput;
use esp_hal::rmt::{
Channel, ConfigError, Error as RmtError, PulseCode, Tx, TxChannelConfig, TxChannelCreator,
};
pub use smart_leds_trait::RGB8;
#[derive(Clone, Copy, Debug)]
pub struct Timing {
pub t0h: u16,
pub t0l: u16,
pub t1h: u16,
pub t1l: u16,
pub reset_half: u16,
}
impl Timing {
pub const WS2812_AT_12_5NS_TICK_ESP32C6: Timing = Timing {
t0h: 28,
t0l: 64,
t1h: 56,
t1l: 48,
reset_half: 2000,
};
pub const WS2812B_AT_12_5NS_TICK_ESP32C3: Timing = Timing {
t0h: 32,
t0l: 68,
t1h: 64,
t1l: 36,
reset_half: 2000,
};
}
pub const fn buffer_len(pixel_count: usize) -> usize {
pixel_count * 24 + 2
}
pub struct Ws2812<'d, const BUFFER_LEN: usize> {
channel: Option<Channel<'d, Blocking, Tx>>,
timing: Timing,
}
impl<'d, const BUFFER_LEN: usize> Ws2812<'d, BUFFER_LEN> {
pub fn new<Ch>(
channel: Ch,
pin: impl PeripheralOutput<'d>,
timing: Timing,
clk_divider: u8,
) -> Result<Self, ConfigError>
where
Ch: TxChannelCreator<'d, Blocking>,
{
let config = TxChannelConfig::default()
.with_clk_divider(clk_divider)
.with_idle_output_level(Level::Low)
.with_idle_output(true)
.with_carrier_modulation(false);
let channel = channel.configure_tx(&config)?.with_pin(pin);
Ok(Self {
channel: Some(channel),
timing,
})
}
pub fn write(&mut self, pixels: &[RGB8]) -> Result<(), RmtError> {
assert_eq!(
BUFFER_LEN,
buffer_len(pixels.len()),
"Ws2812::write: pixels.len() doesn't match this driver's BUFFER_LEN"
);
let buf = self.encode(pixels);
let channel = self
.channel
.take()
.expect("channel is always Some between write() calls");
match channel.transmit(&buf) {
Ok(txn) => match txn.wait() {
Ok(channel) => {
self.channel = Some(channel);
Ok(())
}
Err((e, channel)) => {
self.channel = Some(channel);
Err(e)
}
},
Err((e, channel)) => {
self.channel = Some(channel);
Err(e)
}
}
}
fn encode(&self, pixels: &[RGB8]) -> [PulseCode; BUFFER_LEN] {
let zero = PulseCode::new(Level::High, self.timing.t0h, Level::Low, self.timing.t0l);
let one = PulseCode::new(Level::High, self.timing.t1h, Level::Low, self.timing.t1l);
let mut buf = [PulseCode::end_marker(); BUFFER_LEN];
let mut i = 0;
for pixel in pixels {
for byte in [pixel.g, pixel.r, pixel.b] {
for bit in (0..8).rev() {
buf[i] = if byte & (1 << bit) != 0 { one } else { zero };
i += 1;
}
}
}
buf[i] = PulseCode::new(
Level::Low,
self.timing.reset_half,
Level::Low,
self.timing.reset_half,
);
buf
}
}