use esp_hal::gpio::AnyPin;
use esp_hal::gpio::NoPin;
use esp_hal::i2s::parallel::I2sParallel;
use esp_hal::i2s::parallel::I2sParallelDmaChannel;
use esp_hal::i2s::parallel::Instance;
use esp_hal::i2s::parallel::TxEightBits;
use esp_hal::i2s::parallel::TxPins;
use esp_hal::i2s::parallel::TxSixteenBits;
use esp_hal::interrupt::InterruptHandler;
use esp_hal::peripherals::Interrupt;
use crate::Hub75Config;
use crate::Hub75DmaDescriptors;
static SELECTED_I2S: AtomicU8 = AtomicU8::new(0);
#[doc(hidden)]
pub trait I2sInterruptBinding: Instance {
fn bind_and_enable_isr(handler: InterruptHandler);
#[cfg_attr(not(feature = "circular-dma"), allow(dead_code))]
fn clear_frame_interrupt();
}
impl I2sInterruptBinding for esp_hal::peripherals::I2S0<'_> {
fn bind_and_enable_isr(handler: InterruptHandler) {
SELECTED_I2S.store(1, Ordering::Relaxed);
unsafe {
esp_hal::interrupt::bind_handler(Interrupt::I2S0, handler);
esp_hal::peripherals::I2S0::steal()
.register_block()
.int_ena()
.modify(|_, w| w.out_total_eof().set_bit());
}
}
fn clear_frame_interrupt() {
unsafe {
esp_hal::peripherals::I2S0::steal()
.register_block()
.int_clr()
.write(|w| w.out_total_eof().clear_bit_by_one());
}
}
}
impl I2sInterruptBinding for esp_hal::peripherals::I2S1<'_> {
fn bind_and_enable_isr(handler: InterruptHandler) {
SELECTED_I2S.store(2, Ordering::Relaxed);
unsafe {
esp_hal::interrupt::bind_handler(Interrupt::I2S1, handler);
esp_hal::peripherals::I2S1::steal()
.register_block()
.int_ena()
.modify(|_, w| w.out_total_eof().set_bit());
}
}
fn clear_frame_interrupt() {
unsafe {
esp_hal::peripherals::I2S1::steal()
.register_block()
.int_clr()
.write(|w| w.out_total_eof().clear_bit_by_one());
}
}
}
#[cfg_attr(not(feature = "circular-dma"), allow(dead_code))]
pub(crate) fn clear_frame_interrupt() {
match SELECTED_I2S.load(Ordering::Relaxed) {
1 => <esp_hal::peripherals::I2S0<'_> as I2sInterruptBinding>::clear_frame_interrupt(),
2 => <esp_hal::peripherals::I2S1<'_> as I2sInterruptBinding>::clear_frame_interrupt(),
_ => {}
}
}
use core::sync::atomic::AtomicU8;
use core::sync::atomic::Ordering;
use crate::Hub75Backend;
use crate::Hub75Error;
use crate::Hub75Pins;
use crate::Hub75Pins8;
use crate::Hub75Pins16;
use crate::framebuffer::WordSize;
use crate::isr::BcmBuf;
impl<FB, P, CH, I> Hub75Backend<FB, P, CH> for I
where
FB: crate::framebuffer::FrameBuffer + 'static,
P: Hub75Pins<Word = FB::Word> + I2sPins<'static>,
CH: I2sParallelDmaChannel<'static, I>,
I: Instance + I2sInterruptBinding + 'static,
{
fn construct<const N: usize>(
self,
hub75_pins: P,
channel: CH,
tx_descriptors: &'static mut Hub75DmaDescriptors<FB, N>,
config: Hub75Config,
) -> Result<(), Hub75Error> {
let (pins, clock_pin) = hub75_pins.convert_pins();
#[cfg(not(feature = "invert-clock"))]
let clock_pin = clock_pin.into_output_signal().with_output_inverter(true);
let i2s_parallel = I2sParallel::new(self, channel, config.frequency, pins, clock_pin);
I::bind_and_enable_isr(crate::isr::handler_with_priority(
crate::isr::isr,
config.interrupt_priority,
));
let buf = BcmBuf::new(tx_descriptors.as_slice());
crate::isr::init_state(i2s_parallel, buf);
Ok(())
}
}
impl crate::Hub75Pins for Hub75Pins16<'_> {
type Word = u16;
fn word_size(&self) -> WordSize {
WordSize::Sixteen
}
}
impl crate::Hub75Pins for Hub75Pins8<'_> {
type Word = u8;
fn word_size(&self) -> WordSize {
WordSize::Eight
}
}
#[doc(hidden)]
pub trait I2sPins<'d> {
type Pins: TxPins<'d> + 'd;
fn convert_pins(self) -> (Self::Pins, AnyPin<'d>);
}
impl<'d> I2sPins<'d> for Hub75Pins16<'d> {
type Pins = TxSixteenBits<'d>;
fn convert_pins(self) -> (TxSixteenBits<'d>, AnyPin<'d>) {
let blank = self.blank.into_output_signal();
#[cfg(feature = "invert-blank")]
let blank = blank.with_output_inverter(true);
let pins = TxSixteenBits::new(
self.addr0, self.addr1, self.addr2, self.addr3, self.addr4, self.latch, NoPin, NoPin,
blank, self.red1, self.grn1, self.blu1, self.red2, self.grn2, self.blu2, NoPin,
);
(pins, self.clock)
}
}
impl<'d> I2sPins<'d> for Hub75Pins8<'d> {
type Pins = TxEightBits<'d>;
fn convert_pins(self) -> (TxEightBits<'d>, AnyPin<'d>) {
let blank = self.blank.into_output_signal();
#[cfg(feature = "invert-blank")]
let blank = blank.with_output_inverter(true);
let pins = TxEightBits::new(
self.red1, self.grn1, self.blu1, self.red2, self.grn2, self.blu2, self.latch, blank,
);
(pins, self.clock)
}
}