use esp_hal::{
Async,
dma::{DmaRxBuf, DmaTxBuf},
gpio::{AnyPin, Level, Output, OutputConfig},
spi::{
Mode,
master::{AnySpi, Config as SpiConfig, ConfigError, Spi, SpiDmaBus},
},
time::Rate,
};
pub type SpiBusType = SpiDmaBus<'static, Async>;
pub fn sd_init_config() -> SpiConfig {
SpiConfig::default()
.with_frequency(Rate::from_khz(400))
.with_mode(Mode::_0)
}
pub fn display_config() -> SpiConfig {
sd_init_config().with_frequency(Rate::from_khz(40_000))
}
#[cfg(feature = "cores3")]
pub struct Spi2Resources<'a> {
pub spi2: AnySpi<'a>,
pub spi2_dma: esp_hal::dma::AnyGdmaChannel<'a>,
pub sck: AnyPin<'a>,
pub mosi: AnyPin<'a>,
pub miso_dc: AnyPin<'a>,
pub display_cs: AnyPin<'a>,
pub card_cs: AnyPin<'a>,
}
#[cfg(feature = "fire27")]
pub struct Spi2Resources<'a> {
pub spi2: AnySpi<'a>,
pub spi2_dma: esp_hal::dma::AnySpiDmaChannel<'a>,
pub sck: AnyPin<'a>,
pub mosi: AnyPin<'a>,
pub miso: AnyPin<'a>,
pub display_cs: AnyPin<'a>,
pub display_dc: AnyPin<'a>,
pub display_rst: AnyPin<'a>,
pub display_bl: AnyPin<'a>,
pub card_cs: AnyPin<'a>,
}
pub struct Spi2Parts {
pub bus: SpiBusType,
display_cs: Output<'static>,
#[cfg(feature = "fire27")]
display_dc: Output<'static>,
#[cfg(feature = "fire27")]
display_rst: Output<'static>,
#[cfg(feature = "fire27")]
display_bl: Output<'static>,
}
#[cfg(feature = "cores3")]
impl Spi2Resources<'static> {
pub fn into_parts(
self,
dma_rx_buf: DmaRxBuf,
dma_tx_buf: DmaTxBuf,
) -> Result<(Spi2Parts, Output<'static>), ConfigError> {
let bus = Spi::new(self.spi2, sd_init_config())?
.with_sck(self.sck)
.with_mosi(self.mosi)
.with_miso(self.miso_dc)
.with_dma(self.spi2_dma)
.with_buffers(dma_rx_buf, dma_tx_buf)
.into_async();
let display_cs = Output::new(self.display_cs, Level::High, OutputConfig::default());
let card_cs = Output::new(self.card_cs, Level::High, OutputConfig::default());
Ok((Spi2Parts { bus, display_cs }, card_cs))
}
}
#[cfg(feature = "fire27")]
impl Spi2Resources<'static> {
pub fn into_parts(
self,
dma_rx_buf: DmaRxBuf,
dma_tx_buf: DmaTxBuf,
) -> Result<(Spi2Parts, Output<'static>), ConfigError> {
let bus = Spi::new(self.spi2, sd_init_config())?
.with_sck(self.sck)
.with_mosi(self.mosi)
.with_miso(self.miso)
.with_dma(self.spi2_dma)
.with_buffers(dma_rx_buf, dma_tx_buf)
.into_async();
let display_cs = Output::new(self.display_cs, Level::High, OutputConfig::default());
let card_cs = Output::new(self.card_cs, Level::High, OutputConfig::default());
let display_dc = Output::new(self.display_dc, Level::Low, OutputConfig::default());
let display_rst = Output::new(self.display_rst, Level::Low, OutputConfig::default());
let display_bl = Output::new(self.display_bl, Level::Low, OutputConfig::default());
Ok((
Spi2Parts { bus, display_cs, display_dc, display_rst, display_bl },
card_cs,
))
}
}
#[cfg(feature = "display")]
pub use devices::*;
#[cfg(feature = "display")]
mod devices {
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDeviceWithConfig;
use embassy_sync::mutex::Mutex;
use embedded_hal::digital::{ErrorType, OutputPin};
use esp_hal::{
dma::{DmaRxBuf, DmaTxBuf},
gpio::{Level, Output, OutputConfig},
spi::master::Spi,
};
use esp_sync::RawMutex;
use lcd_async::interface::{Interface, SpiInterface};
use static_cell::StaticCell;
use super::{Spi2Parts, Spi2Resources, SpiBusType, display_config, sd_init_config};
use crate::board::display::{self, Ili9342c};
pub type SpiDeviceType<'a> = SpiDeviceWithConfig<'a, RawMutex, SpiBusType, Output<'a>>;
pub type CardSpiDevice<CS> = SpiDeviceWithConfig<'static, RawMutex, SpiBusType, CS>;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CardPresence {
Detect,
ForceAbsent,
}
pub struct PresenceCs<CS> {
pin: CS,
frozen: bool,
}
impl<CS: ErrorType> ErrorType for PresenceCs<CS> {
type Error = CS::Error;
}
impl<CS: OutputPin> OutputPin for PresenceCs<CS> {
fn set_low(&mut self) -> Result<(), Self::Error> {
if self.frozen {
Ok(()) } else {
self.pin.set_low()
}
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.pin.set_high() }
}
pub struct PreparedCard<CS>(CardSpiDevice<PresenceCs<CS>>);
impl<CS> PreparedCard<CS> {
pub fn into_inner(self) -> CardSpiDevice<PresenceCs<CS>> {
self.0
}
}
#[cfg(feature = "cores3")]
pub type DisplayInterface =
SpiInterface<SpiDeviceType<'static>, crate::board::cores3::Gpio35Dc>;
#[cfg(feature = "fire27")]
pub type DisplayInterface = SpiInterface<SpiDeviceType<'static>, Output<'static>>;
#[cfg(feature = "cores3")]
pub type DisplayType = Ili9342c<DisplayInterface>;
#[cfg(feature = "fire27")]
pub type DisplayType = Ili9342c<DisplayInterface, Output<'static>>;
pub type DisplayInitError =
lcd_async::InitError<<DisplayInterface as Interface>::Error, core::convert::Infallible>;
pub type DisplayOnlyInterface = SpiInterface<SpiDeviceType<'static>, Output<'static>>;
#[cfg(feature = "cores3")]
pub type DisplayOnlyType = Ili9342c<DisplayOnlyInterface>;
#[cfg(feature = "fire27")]
pub type DisplayOnlyType = Ili9342c<DisplayOnlyInterface, Output<'static>>;
pub type DisplayOnlyInitError =
lcd_async::InitError<<DisplayOnlyInterface as Interface>::Error, core::convert::Infallible>;
pub struct DisplayBus {
pub display: DisplayOnlyType,
#[cfg(feature = "fire27")]
pub backlight: Output<'static>,
}
pub struct DisplayDriver {
pub display: DisplayType,
#[cfg(feature = "fire27")]
bl: Output<'static>,
}
#[cfg(feature = "fire27")]
impl DisplayDriver {
pub fn bl_on(&mut self) {
self.bl.set_high();
}
pub fn bl_off(&mut self) {
self.bl.set_low();
}
}
static SPI_BUS: StaticCell<Mutex<RawMutex, SpiBusType>> = StaticCell::new();
impl Spi2Parts {
pub async fn finish<CS>(
self,
card_cs: CS,
) -> Result<(DisplayDriver, CardSpiDevice<CS>), DisplayInitError> {
let bus = SPI_BUS.init(Mutex::new(self.bus));
let display_device =
SpiDeviceWithConfig::new(bus, self.display_cs, display_config());
#[cfg(feature = "cores3")]
let driver = {
let di = SpiInterface::new(display_device, crate::board::cores3::Gpio35Dc);
let display = display::init_ili9342c(di).await?;
crate::board::cores3::gpio35_disable_output();
DisplayDriver { display }
};
#[cfg(feature = "fire27")]
let driver = {
let di = SpiInterface::new(display_device, self.display_dc);
let display =
display::init_ili9342c_with_reset(di, self.display_rst).await?;
let mut driver = DisplayDriver { display, bl: self.display_bl };
driver.bl_on();
driver
};
let card_device = SpiDeviceWithConfig::new(bus, card_cs, sd_init_config());
Ok((driver, card_device))
}
pub async fn finish_sd<CS>(
mut self,
card_cs: CS,
presence: CardPresence,
) -> Result<(DisplayDriver, PreparedCard<CS>), DisplayInitError>
where
CS: OutputPin,
{
if let Err(e) = self.bus.write(&[0xFF; 10]) {
warn!("SD power-up idle clock failed: {e:?}");
}
let card_cs = PresenceCs {
pin: card_cs,
frozen: matches!(presence, CardPresence::ForceAbsent),
};
let (driver, card_device) = self.finish(card_cs).await?;
Ok((driver, PreparedCard(card_device)))
}
}
#[cfg(feature = "cores3")]
impl Spi2Resources<'static> {
pub async fn into_display_only(
self,
dma_rx_buf: DmaRxBuf,
dma_tx_buf: DmaTxBuf,
) -> Result<DisplayBus, DisplayOnlyInitError> {
let spi = Spi::new(self.spi2, display_config())
.expect("SPI2 display config")
.with_sck(self.sck)
.with_mosi(self.mosi)
.with_dma(self.spi2_dma)
.with_buffers(dma_rx_buf, dma_tx_buf)
.into_async();
let bus = SPI_BUS.init(Mutex::new(spi));
let display_cs = Output::new(self.display_cs, Level::High, OutputConfig::default());
let dc = Output::new(self.miso_dc, Level::Low, OutputConfig::default());
let device = SpiDeviceWithConfig::new(bus, display_cs, display_config());
let di = SpiInterface::new(device, dc);
let display = display::init_ili9342c(di).await?;
Ok(DisplayBus { display })
}
}
#[cfg(feature = "fire27")]
impl Spi2Resources<'static> {
pub async fn into_display_only(
self,
dma_rx_buf: DmaRxBuf,
dma_tx_buf: DmaTxBuf,
) -> Result<DisplayBus, DisplayOnlyInitError> {
let spi = Spi::new(self.spi2, display_config())
.expect("SPI2 display config")
.with_sck(self.sck)
.with_mosi(self.mosi)
.with_miso(self.miso)
.with_dma(self.spi2_dma)
.with_buffers(dma_rx_buf, dma_tx_buf)
.into_async();
let bus = SPI_BUS.init(Mutex::new(spi));
let display_cs = Output::new(self.display_cs, Level::High, OutputConfig::default());
let dc = Output::new(self.display_dc, Level::Low, OutputConfig::default());
let rst = Output::new(self.display_rst, Level::Low, OutputConfig::default());
let mut backlight = Output::new(self.display_bl, Level::Low, OutputConfig::default());
let device = SpiDeviceWithConfig::new(bus, display_cs, display_config());
let di = SpiInterface::new(device, dc);
let display = display::init_ili9342c_with_reset(di, rst).await?;
backlight.set_high();
Ok(DisplayBus { display, backlight })
}
}
}