use esp_hal::dma::DmaChannelFor;
use esp_hal::dma::DmaDescriptor;
use esp_hal::dma::DmaError;
use esp_hal::dma::DmaTxBuffer;
use esp_hal::gpio::AnyPin;
use esp_hal::gpio::NoPin;
use esp_hal::i2s::parallel::I2sParallel;
use esp_hal::i2s::parallel::I2sParallelTransfer;
use esp_hal::i2s::parallel::TxEightBits;
use esp_hal::i2s::parallel::TxPins;
use esp_hal::i2s::parallel::TxSixteenBits;
use esp_hal::i2s::AnyI2s;
#[cfg(feature = "iram")]
use esp_hal::ram;
use esp_hal::time::Rate;
use crate::framebuffer::FrameBuffer;
use crate::Hub75Error;
use crate::Hub75Pins;
use crate::Hub75Pins16;
use crate::Hub75Pins8;
pub struct Hub75<'d, DM: esp_hal::DriverMode> {
i2s: I2sParallel<'d, DM>,
tx_descriptors: &'static mut [DmaDescriptor],
}
impl<'d> Hub75<'d, esp_hal::Blocking> {
pub fn new<T: TxPins<'d>>(
i2s: AnyI2s<'d>,
hub75_pins: impl Hub75Pins<'d, T>,
channel: impl DmaChannelFor<AnyI2s<'d>>,
tx_descriptors: &'static mut [DmaDescriptor],
frequency: Rate,
) -> Result<Self, Hub75Error> {
let (pins, clock) = hub75_pins.convert_pins();
#[cfg(not(feature = "invert-clock"))]
let clock = clock.into_output_signal().with_output_inverter(true);
let i2s = I2sParallel::new(i2s, channel, frequency, pins, clock);
Ok(Self {
i2s,
tx_descriptors,
})
}
pub fn into_async(self) -> Hub75<'d, esp_hal::Async> {
Hub75 {
i2s: self.i2s.into_async(),
tx_descriptors: self.tx_descriptors,
}
}
}
impl<'d, DM: esp_hal::DriverMode> Hub75<'d, DM> {
#[cfg_attr(feature = "iram", ram)]
pub fn render(
self,
fb: &impl FrameBuffer,
) -> Result<Hub75Transfer<'d, crate::bcm_dma_buf::BcmTxDmaBuf, DM>, (Hub75Error, Self)> {
let i2s = self.i2s;
let tx_descriptors = self.tx_descriptors;
let tx_buf = crate::bcm_dma_buf::BcmTxDmaBuf::new(tx_descriptors, fb);
let xfer = i2s.send(tx_buf).map_err(|(e, i2s, buf)| {
let tx_descriptors = buf.split();
(
e.into(),
Self {
i2s,
tx_descriptors,
},
)
})?;
Ok(Hub75Transfer {
xfer,
tx_descriptors: None,
})
}
#[cfg_attr(feature = "iram", ram)]
pub fn render_buf<BUF: DmaTxBuffer>(
self,
buf: BUF,
) -> Result<Hub75Transfer<'d, BUF, DM>, (Hub75Error, Self, BUF)> {
let i2s = self.i2s;
let tx_descriptors = self.tx_descriptors;
match i2s.send(buf) {
Ok(xfer) => Ok(Hub75Transfer {
xfer,
tx_descriptors: Some(tx_descriptors),
}),
Err((e, i2s, buf)) => Err((
e.into(),
Self {
i2s,
tx_descriptors,
},
buf,
)),
}
}
}
pub struct Hub75Transfer<'d, BUF: DmaTxBuffer, DM: esp_hal::DriverMode> {
xfer: I2sParallelTransfer<'d, BUF, DM>,
tx_descriptors: Option<&'static mut [DmaDescriptor]>,
}
impl<'d, BUF: DmaTxBuffer, DM: esp_hal::DriverMode> Hub75Transfer<'d, BUF, DM> {
#[cfg_attr(feature = "iram", ram)]
pub fn is_done(&self) -> bool {
self.xfer.is_done()
}
#[cfg_attr(feature = "iram", ram)]
pub fn wait_with_buf(self) -> (Result<(), DmaError>, BUF::Final, Hub75<'d, DM>) {
let Hub75Transfer {
xfer,
tx_descriptors,
} = self;
let (i2s, final_buf) = xfer.wait();
let tx_descriptors = tx_descriptors
.expect("wait_with_buf is only supported for render_buf()-started transfers");
(
Ok(()),
final_buf,
Hub75 {
i2s,
tx_descriptors,
},
)
}
}
impl<'d, DM: esp_hal::DriverMode> Hub75Transfer<'d, crate::bcm_dma_buf::BcmTxDmaBuf, DM> {
#[cfg_attr(feature = "iram", ram)]
pub fn wait(self) -> (Result<(), DmaError>, Hub75<'d, DM>) {
let (i2s, bcm_buf) = self.xfer.wait();
let tx_descriptors = bcm_buf.split();
(
Ok(()),
Hub75 {
i2s,
tx_descriptors,
},
)
}
}
impl<BUF: DmaTxBuffer> Hub75Transfer<'_, BUF, esp_hal::Async> {
#[cfg_attr(feature = "iram", ram)]
pub async fn wait_for_done(&mut self) -> Result<(), DmaError> {
self.xfer.wait_for_done().await
}
}
impl<'d> crate::Hub75Pins<'d, TxSixteenBits<'d>> for Hub75Pins16<'d> {
fn convert_pins(self) -> (TxSixteenBits<'d>, AnyPin<'d>) {
let (_, blank) = unsafe { self.blank.split() };
let pins = TxSixteenBits::new(
self.addr0,
self.addr1,
self.addr2,
self.addr3,
self.addr4,
self.latch,
NoPin,
NoPin,
blank.with_output_inverter(true),
self.red1,
self.grn1,
self.blu1,
self.red2,
self.grn2,
self.blu2,
NoPin,
);
(pins, self.clock)
}
}
impl<'d> crate::Hub75Pins<'d, TxEightBits<'d>> for Hub75Pins8<'d> {
fn convert_pins(self) -> (TxEightBits<'d>, AnyPin<'d>) {
let (_, blank) = unsafe { self.blank.split() };
let pins = TxEightBits::new(
self.red1,
self.grn1,
self.blu1,
self.red2,
self.grn2,
self.blu2,
self.latch,
#[cfg(feature = "invert-blank")]
blank.with_output_inverter(true),
#[cfg(not(feature = "invert-blank"))]
blank,
);
(pins, self.clock)
}
}