use esp_hal::dma::DmaDescriptor;
use esp_hal::dma::DmaError;
use esp_hal::dma::DmaTxBuf;
use esp_hal::dma::TxChannelFor;
use esp_hal::gpio::NoPin;
use esp_hal::lcd_cam::lcd::i8080;
use esp_hal::lcd_cam::lcd::i8080::Command;
use esp_hal::lcd_cam::lcd::i8080::I8080Transfer;
use esp_hal::lcd_cam::lcd::i8080::I8080;
#[cfg(feature = "invert-clock")]
use esp_hal::lcd_cam::lcd::ClockMode;
#[cfg(feature = "invert-clock")]
use esp_hal::lcd_cam::lcd::Phase;
#[cfg(feature = "invert-clock")]
use esp_hal::lcd_cam::lcd::Polarity;
use esp_hal::lcd_cam::LcdCam;
use esp_hal::peripherals::LCD_CAM;
#[cfg(feature = "iram")]
use esp_hal::ram;
use esp_hal::time::Rate;
use crate::framebuffer::FrameBuffer;
use crate::framebuffer::WordSize;
use crate::Hub75Error;
use crate::Hub75Pins;
use crate::Hub75Pins16;
use crate::Hub75Pins8;
pub struct Hub75<'d, DM: esp_hal::DriverMode> {
i8080: I8080<'d, DM>,
tx_descriptors: &'static mut [DmaDescriptor],
}
impl<'d> Hub75<'d, esp_hal::Blocking> {
pub fn new(
lcd_cam: LCD_CAM<'d>,
hub75_pins: impl Hub75Pins<'d>,
channel: impl TxChannelFor<LCD_CAM<'d>>,
tx_descriptors: &'static mut [DmaDescriptor],
frequency: Rate,
) -> Result<Self, Hub75Error> {
let lcd_cam = LcdCam::new(lcd_cam);
Self::new_internal(lcd_cam, hub75_pins, channel, tx_descriptors, frequency)
}
}
impl<'d> Hub75<'d, esp_hal::Async> {
pub fn new_async(
lcd_cam: LCD_CAM<'d>,
hub75_pins: impl Hub75Pins<'d>,
channel: impl TxChannelFor<LCD_CAM<'d>>,
tx_descriptors: &'static mut [DmaDescriptor],
frequency: Rate,
) -> Result<Self, Hub75Error> {
let lcd_cam = LcdCam::new(lcd_cam).into_async();
Self::new_internal(lcd_cam, hub75_pins, channel, tx_descriptors, frequency)
}
}
impl<'d, DM: esp_hal::DriverMode> Hub75<'d, DM> {
fn new_internal(
lcd_cam: LcdCam<'d, DM>,
hub75_pins: impl Hub75Pins<'d>,
channel: impl TxChannelFor<LCD_CAM<'d>>,
tx_descriptors: &'static mut [DmaDescriptor],
frequency: Rate,
) -> Result<Self, Hub75Error> {
#[allow(unused_mut)]
let mut config = i8080::Config::default().with_frequency(frequency);
#[cfg(feature = "invert-clock")]
let config = config.with_clock_mode(ClockMode {
polarity: Polarity::IdleLow,
phase: Phase::ShiftHigh,
});
let i8080 = I8080::new(lcd_cam.lcd, channel, config).map_err(Hub75Error::I8080)?;
let i8080 = hub75_pins.apply(i8080);
Ok(Self {
i8080,
tx_descriptors,
})
}
#[cfg_attr(feature = "iram", ram)]
pub fn render<
const ROWS: usize,
const COLS: usize,
const NROWS: usize,
const BITS: u8,
const FRAME_COUNT: usize,
>(
self,
fb: &impl FrameBuffer<ROWS, COLS, NROWS, BITS, FRAME_COUNT>,
) -> Result<Hub75Transfer<'d, DM>, (Hub75Error, Self)> {
let i8080 = self.i8080;
let tx_descriptors = self.tx_descriptors;
let tx_buffer = unsafe {
let (ptr, len) = fb.read_buffer();
core::slice::from_raw_parts_mut(ptr as *mut u8, len)
};
let tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).expect("failed to create DmaTxBuf!");
let result = match fb.get_word_size() {
WordSize::Eight => i8080.send(Command::<u8>::None, 0, tx_buf),
WordSize::Sixteen => i8080.send(Command::<u16>::None, 0, tx_buf),
};
let xfer = result.map_err(|(e, i8080, buf)| {
let (tx_descriptors, _) = buf.split();
(
e.into(),
Self {
i8080,
tx_descriptors,
},
)
})?;
Ok(Hub75Transfer { xfer })
}
}
pub struct Hub75Transfer<'d, DM: esp_hal::DriverMode> {
xfer: I8080Transfer<'d, DmaTxBuf, DM>,
}
impl<'d, DM: esp_hal::DriverMode> Hub75Transfer<'d, DM> {
#[cfg_attr(feature = "iram", ram)]
pub fn is_done(&self) -> bool {
self.xfer.is_done()
}
#[cfg_attr(feature = "iram", ram)]
pub fn wait(self) -> (Result<(), DmaError>, Hub75<'d, DM>) {
let (result, i8080, tx_buf) = self.xfer.wait();
let (tx_descriptors, _) = tx_buf.split();
match result {
Ok(()) => (
Ok(()),
Hub75 {
i8080,
tx_descriptors,
},
),
Err(e) => (
Err(e),
Hub75 {
i8080,
tx_descriptors,
},
),
}
}
}
impl Hub75Transfer<'_, esp_hal::Async> {
#[cfg_attr(feature = "iram", ram)]
pub async fn wait_for_done(&mut self) -> Result<(), DmaError> {
self.xfer.wait_for_done().await;
Ok(())
}
}
impl<'d> crate::Hub75Pins<'d> for Hub75Pins16<'d> {
fn apply<DM: esp_hal::DriverMode>(self, i8080: I8080<'d, DM>) -> I8080<'d, DM> {
let (_, blank) = unsafe { self.blank.split() };
i8080
.with_wrx(self.clock)
.with_data0(self.addr0)
.with_data1(self.addr1)
.with_data2(self.addr2)
.with_data3(self.addr3)
.with_data4(self.addr4)
.with_data5(self.latch)
.with_data6(NoPin)
.with_data7(NoPin)
.with_data8(blank.with_output_inverter(true))
.with_data9(self.red1)
.with_data10(self.grn1)
.with_data11(self.blu1)
.with_data12(self.red2)
.with_data13(self.grn2)
.with_data14(self.blu2)
.with_data15(NoPin)
}
}
impl<'d> crate::Hub75Pins<'d> for Hub75Pins8<'d> {
fn apply<DM: esp_hal::DriverMode>(self, i8080: I8080<'d, DM>) -> I8080<'d, DM> {
let (_, blank) = unsafe { self.blank.split() };
#[cfg(feature = "invert-blank")]
let blank = blank.with_output_inverter(true);
i8080
.with_wrx(self.clock)
.with_data0(self.red1)
.with_data1(self.grn1)
.with_data2(self.blu1)
.with_data3(self.red2)
.with_data4(self.grn2)
.with_data5(self.blu2)
.with_data6(self.latch)
.with_data7(blank)
}
}