From b20c562b271c662bc8d7115e4fd6341100f3bde2 Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Fri, 6 Dec 2024 18:24:22 +0100 Subject: [PATCH] platform: esp32: first attempt at an i2s implementation, and fix rmt flickering with thread magic --- src/platform/esp32.rs | 133 ++++++++++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 32 deletions(-) diff --git a/src/platform/esp32.rs b/src/platform/esp32.rs index 9e15fdf..2f7b3eb 100644 --- a/src/platform/esp32.rs +++ b/src/platform/esp32.rs @@ -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; 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; + fn sample(&mut self, rect: &crate::geometry::Rectangle) -> impl crate::render::PixelView { + StrideSampler::new(&mut self.pixbuf, self.pixmap.select(rect)) + } + } +} + pub struct Esp32Board { - output: Option<::Output>, sys_loop: EspSystemEventLoop, modem: Option, - surfaces: BufferedSurfacePool + surfaces: BufferedSurfacePool, + output: Option<::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) -> 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::( move |evt| { log::warn!("wifi event {:?}", evt);