platform: esp32: first attempt at an i2s implementation, and fix rmt flickering with thread magic

This commit is contained in:
Victoria Fischer 2024-12-06 18:24:22 +01:00
parent ea75d7a2ee
commit b20c562b27

View File

@ -1,3 +1,4 @@
use core::borrow::BorrowMut;
use std::sync::Arc;
use std::fmt::Debug;
use std::sync::Mutex;
@ -8,30 +9,81 @@ use chrono::Utc;
use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System};
use esp_idf_svc::hal::modem::Modem;
use esp_idf_svc::hal::prelude::Peripherals;
use esp_idf_svc::hal::task::thread::ThreadSpawnConfiguration;
use esp_idf_svc::netif::IpEvent;
use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault};
use esp_idf_svc::sntp::EspSntp;
use esp_idf_svc::sys::esp_efuse_mac_get_default;
use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent};
use rgb::Rgb;
use super::smart_leds_lib::rmt::FastWs2812Esp32Rmt;
use super::smart_leds_lib::StrideOutput;
use super::Board;
use crate::buffers::BufferedSurfacePool;
use crate::task::FixedSizeScheduler;
use crate::task::Task;
use crate::buffers::Pixbuf;
use crate::mappings::StrideMapping;
use crate::platform::smart_leds_lib::StrideOutput;
use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt;
use crate::task::FixedSizeScheduler;
use crate::task::Task;
use crate::time::Periodically;
pub mod i2s {
use esp_idf_svc::hal::i2s::*;
use rgb::ComponentBytes;
use rgb::Rgb;
use crate::mappings::*;
use crate::buffers::Pixbuf;
use crate::render::Output;
use crate::render::Sample;
pub struct I2SOutput<'d> {
driver: I2sDriver<'d, I2sTx>,
pixbuf: [Rgb<u8>; 310],
pixmap: StrideMapping,
}
impl<'d> I2SOutput<'d> {
fn new(driver: I2sDriver<'d, I2sTx>) -> Self {
I2SOutput {
driver,
pixbuf: Pixbuf::new(),
pixmap: StrideMapping::new_jar()
}
}
}
impl<'d> Output for I2SOutput<'d> {
fn blank(&mut self) {
self.pixbuf.blank();
}
fn commit(&mut self) {
let bytes = self.pixbuf.as_bytes();
let mut written = self.driver.preload_data(bytes).unwrap();
self.driver.tx_enable().unwrap();
while written < bytes.len() {
let next = &bytes[written..];
written += self.driver.write(next, 0).unwrap();
}
self.driver.tx_disable().unwrap();
}
}
impl<'d> Sample for I2SOutput<'d> {
type Pixel = Rgb<u8>;
fn sample(&mut self, rect: &crate::geometry::Rectangle<crate::geometry::Virtual>) -> impl crate::render::PixelView<Pixel = Self::Pixel> {
StrideSampler::new(&mut self.pixbuf, self.pixmap.select(rect))
}
}
}
pub struct Esp32Board {
output: Option<<Self as Board>::Output>,
sys_loop: EspSystemEventLoop,
modem: Option<Modem>,
surfaces: BufferedSurfacePool
surfaces: BufferedSurfacePool,
output: Option<<Self as Board>::Output>
}
impl Board for Esp32Board {
@ -50,43 +102,61 @@ impl Board for Esp32Board {
let peripherals = Peripherals::take().unwrap();
let sys_loop = EspSystemEventLoop::take().unwrap();
let channel = peripherals.rmt.channel0;
let pins = peripherals.pins;
let mut chip_id: [u8; 8] = [0; 8];
unsafe {
esp_efuse_mac_get_default(&mut chip_id as *mut u8);
}
log::info!("Setting up for chip ID {:?}", chip_id);
let (pixmap, target) = match chip_id {
[72, 202, 67, 89, 145, 204, 0, 0] => {
(StrideMapping::new_panel(), FastWs2812Esp32Rmt::new(channel, pins.gpio5).unwrap())
},
[140, 170, 181, 131, 95, 116, 0, 0] => {
(StrideMapping::new_jar(), FastWs2812Esp32Rmt::new(channel, pins.gpio14).unwrap())
},
_ => {
(StrideMapping::new(), FastWs2812Esp32Rmt::new(channel, pins.gpio5).unwrap())
}
};
log::info!("Setting up output for chip ID {:x?}", chip_id);
const POWER_VOLTS : u32 = 5;
const POWER_MA : u32 = 500;
const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
let pins = peripherals.pins;
let output = StrideOutput::new(
Pixbuf::new(),
pixmap,
target,
MAX_POWER_MW
);
ThreadSpawnConfiguration {
pin_to_core: Some(esp_idf_svc::hal::cpu::Core::Core1),
..Default::default()
}.set().unwrap();
// Wifi driver creates too many interrupts on core0, so we need to use RMT on core1.
// But the implementation spawns a thread based on the core the driver was created in,
// so we create the driver in another thread briefly.
// Fun stuff.
let output = match chip_id {
[72, 202, 67, 89, 145, 204, 0, 0] => {
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new_panel(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
},
[0x8C, 0xAA, 0xB5, 0x83, 0x5f, 0x74, 0x0, 0x0] => {
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new_jar(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio14).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
},
_ => {
StrideOutput::new(
Pixbuf::new(),
StrideMapping::new(),
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
MAX_POWER_MW
)
}
};
ThreadSpawnConfiguration {
..Default::default()
}.set().unwrap();
Esp32Board {
output: Some(output),
modem: Some(peripherals.modem),
sys_loop: sys_loop.clone(),
surfaces: BufferedSurfacePool::new()
surfaces: BufferedSurfacePool::new(),
output: Some(output)
}
}
@ -134,6 +204,7 @@ struct WifiTask {
impl WifiTask {
fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition<NvsDefault>) -> Self {
log::info!("Installing wifi driver");
let mut wifi = EspWifi::new(
modem,
sys_loop.clone(),
@ -178,8 +249,6 @@ impl Task for WifiTask {
fn start(&mut self) {
log::info!("Starting wifi!");
log::info!("waiting for loop");
let wifi_state = self.state.clone();
self.wifi_sub = Some(self.sys_loop.subscribe::<WifiEvent, _>( move |evt| {
log::warn!("wifi event {:?}", evt);