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:
1 parent
239e72d0a8
commit
eb880d03f6
2 files changed
+25
-19
No files matched your search
+4
-12
@@ -77,18 +77,10 @@ impl LoRaBoard for Board {
|
||||
let i2c = mcu::init_i2c(p.I2C0, p.GPIO17, p.GPIO18);
|
||||
let display = Some(DisplayParts { i2c });
|
||||
|
||||
// WS2812B RGB LED on GPIO38 via RMT channel 0.
|
||||
// Wrap in ManuallyDrop so the Rmt peripheral isn't disabled when
|
||||
// the struct is dropped after we take channel0.
|
||||
let rmt = core::mem::ManuallyDrop::new(
|
||||
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
|
||||
// WS2812B RGB LED on GPIO38 via RMT channel 0 (blocking, ~31µs per update)
|
||||
let rmt = Rmt::new(p.RMT, Rate::from_mhz(80)).expect("RMT init");
|
||||
let led_channel = rmt
|
||||
.channel0
|
||||
.configure_tx(
|
||||
p.GPIO38,
|
||||
TxChannelConfig::default()
|
||||
|
||||
+21
-7
@@ -2,6 +2,7 @@
|
||||
//!
|
||||
//! Encodes RGB values as GRB PulseCodes and transmits via RMT.
|
||||
//! 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::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).
|
||||
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 {
|
||||
channel: Channel<'static, esp_hal::Async, Tx>,
|
||||
channel: Option<Channel<'static, esp_hal::Blocking, Tx>>,
|
||||
}
|
||||
|
||||
impl Ws2812 {
|
||||
pub fn new(channel: Channel<'static, esp_hal::Async, Tx>) -> Self {
|
||||
Self { channel }
|
||||
pub fn new(channel: Channel<'static, esp_hal::Blocking, Tx>) -> Self {
|
||||
Self {
|
||||
channel: Some(channel),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode 3 bytes (GRB order) + end marker into PulseCodes.
|
||||
@@ -35,7 +38,6 @@ impl Ws2812 {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
// data[24] is already end_marker
|
||||
data
|
||||
}
|
||||
}
|
||||
@@ -43,8 +45,20 @@ impl Ws2812 {
|
||||
impl RgbLed for Ws2812 {
|
||||
async fn set_rgb(&mut self, r: u8, g: u8, b: u8) {
|
||||
let data = Self::encode(r, g, b);
|
||||
if let Err(e) = self.channel.transmit(&data).await {
|
||||
defmt::warn!("WS2812 transmit error: {}", e);
|
||||
if let Some(ch) = self.channel.take() {
|
||||
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