use esp_hal::dma::DmaChannelFor;
use esp_hal::dma::DmaDescriptor;
use esp_hal::gpio::AnyPin;
use esp_hal::gpio::NoPin;
use esp_hal::i2s::parallel::I2sParallel;
use esp_hal::i2s::parallel::TxEightBits;
use esp_hal::i2s::parallel::TxPins;
use esp_hal::i2s::parallel::TxSixteenBits;
use esp_hal::i2s::AnyI2s;
use esp_hal::peripherals::Interrupt;
use esp_hal::time::Rate;
use esp_hal::Blocking;
#[cfg(not(feature = "circular-dma"))]
use crate::bcm::linear::BcmBuf;
#[cfg(feature = "circular-dma")]
use crate::bcm::circular::CircularBcmBuf;
pub use crate::isr::Hub75;
use crate::Hub75Error;
use crate::Hub75Pins;
use crate::Hub75Pins16;
use crate::Hub75Pins8;
pub trait I2sHub75Instance: esp_hal::i2s::parallel::Instance {
#[doc(hidden)]
fn bind_and_enable_isr();
#[doc(hidden)]
fn clear_interrupt();
}
impl I2sHub75Instance for esp_hal::peripherals::I2S0<'_> {
fn bind_and_enable_isr() {
#[cfg(not(feature = "circular-dma"))]
unsafe {
esp_hal::interrupt::bind_handler(Interrupt::I2S0, crate::isr::hub75_isr);
let stolen = esp_hal::peripherals::I2S0::steal();
let reg = stolen.register_block();
reg.int_ena().modify(|_, w| w.out_total_eof().set_bit());
}
#[cfg(feature = "circular-dma")]
unsafe {
esp_hal::interrupt::bind_handler(
Interrupt::I2S0,
crate::isr::hub75_frame_count_isr,
);
let stolen = esp_hal::peripherals::I2S0::steal();
let reg = stolen.register_block();
reg.int_ena().modify(|_, w| w.out_eof().set_bit());
}
}
fn clear_interrupt() {
unsafe {
let stolen = esp_hal::peripherals::I2S0::steal();
let reg = stolen.register_block();
#[cfg(not(feature = "circular-dma"))]
reg.int_clr()
.write(|w| w.out_total_eof().clear_bit_by_one());
#[cfg(feature = "circular-dma")]
reg.int_clr().write(|w| w.out_eof().clear_bit_by_one());
}
}
}
impl I2sHub75Instance for esp_hal::peripherals::I2S1<'_> {
fn bind_and_enable_isr() {
#[cfg(not(feature = "circular-dma"))]
unsafe {
esp_hal::interrupt::bind_handler(Interrupt::I2S1, crate::isr::hub75_isr);
let stolen = esp_hal::peripherals::I2S1::steal();
let reg = stolen.register_block();
reg.int_ena().modify(|_, w| w.out_total_eof().set_bit());
}
#[cfg(feature = "circular-dma")]
unsafe {
esp_hal::interrupt::bind_handler(
Interrupt::I2S1,
crate::isr::hub75_frame_count_isr,
);
let stolen = esp_hal::peripherals::I2S1::steal();
let reg = stolen.register_block();
reg.int_ena().modify(|_, w| w.out_eof().set_bit());
}
}
fn clear_interrupt() {
unsafe {
let stolen = esp_hal::peripherals::I2S1::steal();
let reg = stolen.register_block();
#[cfg(not(feature = "circular-dma"))]
reg.int_clr()
.write(|w| w.out_total_eof().clear_bit_by_one());
#[cfg(feature = "circular-dma")]
reg.int_clr().write(|w| w.out_eof().clear_bit_by_one());
}
}
}
#[cfg(not(feature = "circular-dma"))]
impl<DM: esp_hal::DriverMode, FB: crate::framebuffer::FrameBuffer + 'static> Hub75<DM, FB> {
fn new_internal<
T: TxPins<'static> + 'static,
P: Hub75Pins<'static, T, Word = FB::Word>,
I: I2sHub75Instance + 'static,
>(
i2s: I,
hub75_pins: P,
channel: impl DmaChannelFor<AnyI2s<'static>>,
tx_descriptors: &'static mut [DmaDescriptor],
frequency: Rate,
fb: &'static FB,
) -> Result<Self, 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(i2s, channel, frequency, pins, clock_pin);
I::bind_and_enable_isr();
let buf = BcmBuf::new(tx_descriptors);
crate::isr::init_isr_state(i2s_parallel, buf);
crate::isr::start_internal(fb)?;
Ok(Self::from_phantom())
}
}
#[cfg(feature = "circular-dma")]
impl<DM: esp_hal::DriverMode, FB: crate::framebuffer::FrameBuffer + 'static> Hub75<DM, FB> {
fn new_internal<
T: TxPins<'static> + 'static,
P: Hub75Pins<'static, T, Word = FB::Word>,
I: I2sHub75Instance + 'static,
>(
i2s: I,
hub75_pins: P,
channel: impl DmaChannelFor<AnyI2s<'static>>,
tx_descriptors: &'static mut [DmaDescriptor],
frequency: Rate,
fb: &'static FB,
) -> Result<Self, 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(i2s, channel, frequency, pins, clock_pin);
I::bind_and_enable_isr();
let buf = CircularBcmBuf::new(tx_descriptors, fb);
let desc_ptr = buf.descriptors_ptr();
let desc_count = buf.desc_count();
let fb_ptr = fb as *const _ as *const ();
let xfer = i2s_parallel
.send(buf)
.map_err(|(err, _tx, _buf)| Hub75Error::Dma(err))?;
crate::isr::store_circular_state(xfer, desc_ptr, desc_count, fb_ptr);
crate::isr::store_clear_interrupt(I::clear_interrupt);
Ok(Self::from_phantom())
}
}
impl<FB: crate::framebuffer::FrameBuffer + 'static> Hub75<Blocking, FB> {
pub fn new<
T: TxPins<'static> + 'static,
P: Hub75Pins<'static, T, Word = FB::Word>,
I: I2sHub75Instance + 'static,
>(
i2s: I,
hub75_pins: P,
channel: impl DmaChannelFor<AnyI2s<'static>>,
tx_descriptors: &'static mut [DmaDescriptor],
frequency: Rate,
fb: &'static FB,
) -> Result<Self, Hub75Error> {
Self::new_internal(i2s, hub75_pins, channel, tx_descriptors, frequency, fb)
}
}
impl<FB: crate::framebuffer::FrameBuffer + 'static> Hub75<esp_hal::Async, FB> {
pub fn new_async<
T: TxPins<'static> + 'static,
P: Hub75Pins<'static, T, Word = FB::Word>,
I: I2sHub75Instance + 'static,
>(
i2s: I,
hub75_pins: P,
channel: impl DmaChannelFor<AnyI2s<'static>>,
tx_descriptors: &'static mut [DmaDescriptor],
frequency: Rate,
fb: &'static FB,
) -> Result<Self, Hub75Error> {
Self::new_internal(i2s, hub75_pins, channel, tx_descriptors, frequency, fb)
}
}
impl<'d> crate::Hub75Pins<'d, TxSixteenBits<'d>> for Hub75Pins16<'d> {
type Word = u16;
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> {
type Word = u8;
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)
}
}