platform: esp32: first attempt at an i2s implementation, and fix rmt flickering with thread magic
This commit is contained in:
parent
ea75d7a2ee
commit
b20c562b27
@ -1,3 +1,4 @@
|
|||||||
|
use core::borrow::BorrowMut;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
@ -8,30 +9,81 @@ use chrono::Utc;
|
|||||||
use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System};
|
use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System};
|
||||||
use esp_idf_svc::hal::modem::Modem;
|
use esp_idf_svc::hal::modem::Modem;
|
||||||
use esp_idf_svc::hal::prelude::Peripherals;
|
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::netif::IpEvent;
|
||||||
use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault};
|
use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault};
|
||||||
use esp_idf_svc::sntp::EspSntp;
|
use esp_idf_svc::sntp::EspSntp;
|
||||||
use esp_idf_svc::sys::esp_efuse_mac_get_default;
|
use esp_idf_svc::sys::esp_efuse_mac_get_default;
|
||||||
use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent};
|
use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent};
|
||||||
|
|
||||||
use rgb::Rgb;
|
use rgb::Rgb;
|
||||||
|
|
||||||
|
use super::smart_leds_lib::rmt::FastWs2812Esp32Rmt;
|
||||||
|
use super::smart_leds_lib::StrideOutput;
|
||||||
use super::Board;
|
use super::Board;
|
||||||
|
|
||||||
use crate::buffers::BufferedSurfacePool;
|
use crate::buffers::BufferedSurfacePool;
|
||||||
use crate::task::FixedSizeScheduler;
|
|
||||||
use crate::task::Task;
|
|
||||||
use crate::buffers::Pixbuf;
|
use crate::buffers::Pixbuf;
|
||||||
use crate::mappings::StrideMapping;
|
use crate::mappings::StrideMapping;
|
||||||
use crate::platform::smart_leds_lib::StrideOutput;
|
use crate::task::FixedSizeScheduler;
|
||||||
use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt;
|
use crate::task::Task;
|
||||||
use crate::time::Periodically;
|
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 {
|
pub struct Esp32Board {
|
||||||
output: Option<<Self as Board>::Output>,
|
|
||||||
sys_loop: EspSystemEventLoop,
|
sys_loop: EspSystemEventLoop,
|
||||||
modem: Option<Modem>,
|
modem: Option<Modem>,
|
||||||
surfaces: BufferedSurfacePool
|
surfaces: BufferedSurfacePool,
|
||||||
|
output: Option<<Self as Board>::Output>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Board for Esp32Board {
|
impl Board for Esp32Board {
|
||||||
@ -50,43 +102,61 @@ impl Board for Esp32Board {
|
|||||||
let peripherals = Peripherals::take().unwrap();
|
let peripherals = Peripherals::take().unwrap();
|
||||||
let sys_loop = EspSystemEventLoop::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];
|
let mut chip_id: [u8; 8] = [0; 8];
|
||||||
unsafe {
|
unsafe {
|
||||||
esp_efuse_mac_get_default(&mut chip_id as *mut u8);
|
esp_efuse_mac_get_default(&mut chip_id as *mut u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Setting up for chip ID {:?}", chip_id);
|
log::info!("Setting up output for chip ID {:x?}", 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())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const POWER_VOLTS : u32 = 5;
|
const POWER_VOLTS : u32 = 5;
|
||||||
const POWER_MA : u32 = 500;
|
const POWER_MA : u32 = 500;
|
||||||
const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
|
const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
|
||||||
|
let pins = peripherals.pins;
|
||||||
|
|
||||||
let output = StrideOutput::new(
|
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(),
|
Pixbuf::new(),
|
||||||
pixmap,
|
StrideMapping::new_panel(),
|
||||||
target,
|
std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(),
|
||||||
MAX_POWER_MW
|
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 {
|
Esp32Board {
|
||||||
output: Some(output),
|
|
||||||
modem: Some(peripherals.modem),
|
modem: Some(peripherals.modem),
|
||||||
sys_loop: sys_loop.clone(),
|
sys_loop: sys_loop.clone(),
|
||||||
surfaces: BufferedSurfacePool::new()
|
surfaces: BufferedSurfacePool::new(),
|
||||||
|
output: Some(output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +204,7 @@ struct WifiTask {
|
|||||||
|
|
||||||
impl WifiTask {
|
impl WifiTask {
|
||||||
fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition<NvsDefault>) -> Self {
|
fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition<NvsDefault>) -> Self {
|
||||||
|
log::info!("Installing wifi driver");
|
||||||
let mut wifi = EspWifi::new(
|
let mut wifi = EspWifi::new(
|
||||||
modem,
|
modem,
|
||||||
sys_loop.clone(),
|
sys_loop.clone(),
|
||||||
@ -178,8 +249,6 @@ impl Task for WifiTask {
|
|||||||
fn start(&mut self) {
|
fn start(&mut self) {
|
||||||
log::info!("Starting wifi!");
|
log::info!("Starting wifi!");
|
||||||
|
|
||||||
log::info!("waiting for loop");
|
|
||||||
|
|
||||||
let wifi_state = self.state.clone();
|
let wifi_state = self.state.clone();
|
||||||
self.wifi_sub = Some(self.sys_loop.subscribe::<WifiEvent, _>( move |evt| {
|
self.wifi_sub = Some(self.sys_loop.subscribe::<WifiEvent, _>( move |evt| {
|
||||||
log::warn!("wifi event {:?}", evt);
|
log::warn!("wifi event {:?}", evt);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user