fix: switch WS2812 driver to blocking RMT transmit
Async RMT had peripheral lifetime issues causing silent failures. Blocking transmit is ~31µs per update (negligible) and avoids the async complexity. Also removes the ManuallyDrop/unsafe workaround.
This commit is contained in:
2 files changed
+25
-19
No files matched your search
@@ -77,18 +77,10 @@ impl LoRaBoard for Board {
|
|||||||
let i2c = mcu::init_i2c(p.I2C0, p.GPIO17, p.GPIO18);
|
let i2c = mcu::init_i2c(p.I2C0, p.GPIO17, p.GPIO18);
|
||||||
let display = Some(DisplayParts { i2c });
|
let display = Some(DisplayParts { i2c });
|
||||||
|
|
||||||
// WS2812B RGB LED on GPIO38 via RMT channel 0.
|
// WS2812B RGB LED on GPIO38 via RMT channel 0 (blocking, ~31µs per update)
|
||||||
// Wrap in ManuallyDrop so the Rmt peripheral isn't disabled when
|
let rmt = Rmt::new(p.RMT, Rate::from_mhz(80)).expect("RMT init");
|
||||||
// the struct is dropped after we take channel0.
|
let led_channel = rmt
|
||||||
let rmt = core::mem::ManuallyDrop::new(
|
.channel0
|
||||||
Rmt::new(p.RMT, Rate::from_mhz(80))
|
|
||||||
.expect("RMT init")
|
|
||||||
.into_async(),
|
|
||||||
);
|
|
||||||
// SAFETY: we're reading channel0 out of the ManuallyDrop wrapper.
|
|
||||||
// The Rmt won't be dropped, so the peripheral stays enabled.
|
|
||||||
let channel0 = unsafe { core::ptr::read(&raw const rmt.channel0) };
|
|
||||||
let led_channel = channel0
|
|
||||||
.configure_tx(
|
.configure_tx(
|
||||||
p.GPIO38,
|
p.GPIO38,
|
||||||
TxChannelConfig::default()
|
TxChannelConfig::default()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Encodes RGB values as GRB PulseCodes and transmits via RMT.
|
//! Encodes RGB values as GRB PulseCodes and transmits via RMT.
|
||||||
//! At 80 MHz with divider=1, each tick is 12.5 ns.
|
//! At 80 MHz with divider=1, each tick is 12.5 ns.
|
||||||
|
//! Uses blocking transmit (~31µs per LED update).
|
||||||
|
|
||||||
use esp_hal::gpio::Level;
|
use esp_hal::gpio::Level;
|
||||||
use esp_hal::rmt::{Channel, PulseCode, Tx};
|
use esp_hal::rmt::{Channel, PulseCode, Tx};
|
||||||
@@ -14,14 +15,16 @@ const BIT_0: PulseCode = PulseCode::new(Level::High, 32, Level::Low, 68);
|
|||||||
/// WS2812 1-bit: High 800ns (64 ticks), Low 450ns (36 ticks).
|
/// WS2812 1-bit: High 800ns (64 ticks), Low 450ns (36 ticks).
|
||||||
const BIT_1: PulseCode = PulseCode::new(Level::High, 64, Level::Low, 36);
|
const BIT_1: PulseCode = PulseCode::new(Level::High, 64, Level::Low, 36);
|
||||||
|
|
||||||
/// WS2812B single-LED driver over RMT.
|
/// WS2812B single-LED driver over RMT (blocking).
|
||||||
pub struct Ws2812 {
|
pub struct Ws2812 {
|
||||||
channel: Channel<'static, esp_hal::Async, Tx>,
|
channel: Option<Channel<'static, esp_hal::Blocking, Tx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ws2812 {
|
impl Ws2812 {
|
||||||
pub fn new(channel: Channel<'static, esp_hal::Async, Tx>) -> Self {
|
pub fn new(channel: Channel<'static, esp_hal::Blocking, Tx>) -> Self {
|
||||||
Self { channel }
|
Self {
|
||||||
|
channel: Some(channel),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode 3 bytes (GRB order) + end marker into PulseCodes.
|
/// Encode 3 bytes (GRB order) + end marker into PulseCodes.
|
||||||
@@ -35,7 +38,6 @@ impl Ws2812 {
|
|||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// data[24] is already end_marker
|
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,8 +45,20 @@ impl Ws2812 {
|
|||||||
impl RgbLed for Ws2812 {
|
impl RgbLed for Ws2812 {
|
||||||
async fn set_rgb(&mut self, r: u8, g: u8, b: u8) {
|
async fn set_rgb(&mut self, r: u8, g: u8, b: u8) {
|
||||||
let data = Self::encode(r, g, b);
|
let data = Self::encode(r, g, b);
|
||||||
if let Err(e) = self.channel.transmit(&data).await {
|
if let Some(ch) = self.channel.take() {
|
||||||
defmt::warn!("WS2812 transmit error: {}", e);
|
match ch.transmit(&data) {
|
||||||
|
Ok(tx) => match tx.wait() {
|
||||||
|
Ok(ch) => self.channel = Some(ch),
|
||||||
|
Err((e, ch)) => {
|
||||||
|
defmt::warn!("WS2812 wait error: {}", e);
|
||||||
|
self.channel = Some(ch);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
defmt::warn!("WS2812 transmit error: {}", e);
|
||||||
|
// Channel consumed on transmit error — LED is lost
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user