#![cfg_attr(docsrs, procmacros::doc_replace)]
use core::{
fmt::Formatter,
marker::PhantomData,
mem::{ManuallyDrop, size_of},
ops::{Deref, DerefMut},
};
use crate::{
Blocking,
DriverMode,
dma::{ChannelTx, DmaError, DmaPeripheral, DmaTxBuffer, PeripheralTxChannel, TxChannelFor},
gpio::{OutputConfig, OutputSignal, interconnect::PeripheralOutput},
lcd_cam::{
BitOrder,
ByteOrder,
ClockError,
Instance,
LCD_DONE_WAKER,
Lcd,
calculate_clkm,
lcd::{ClockMode, DelayMode, Phase, Polarity},
},
pac,
peripherals::LCD_CAM,
soc::clocks::ClockTree,
system::{self, GenericPeripheralGuard},
time::Rate,
};
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigError {
Clock(ClockError),
}
pub struct I8080<'d, Dm: DriverMode> {
lcd_cam: LCD_CAM<'d>,
tx_channel: ChannelTx<Blocking, PeripheralTxChannel<LCD_CAM<'d>>>,
_guard: GenericPeripheralGuard<{ system::Peripheral::LcdCam as u8 }>,
_mode: PhantomData<Dm>,
}
impl<'d, Dm> I8080<'d, Dm>
where
Dm: DriverMode,
{
pub fn new(
lcd: Lcd<'d, Dm>,
channel: impl TxChannelFor<LCD_CAM<'d>>,
config: Config,
) -> Result<Self, ConfigError> {
let tx_channel = ChannelTx::new(channel.degrade());
let mut this = Self {
lcd_cam: lcd.lcd_cam,
tx_channel,
_guard: lcd._guard,
_mode: PhantomData,
};
this.apply_config(&config)?;
Ok(this)
}
fn regs(&self) -> &pac::lcd_cam::RegisterBlock {
self.lcd_cam.register_block()
}
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
let (i, divider) = ClockTree::with(|clocks| {
calculate_clkm(
(config.frequency.as_hz() * 2) as _,
&[
crate::soc::clocks::xtal_clk_frequency(clocks) as usize,
crate::soc::clocks::pll_d2_frequency(clocks) as usize,
crate::soc::clocks::crypto_pwm_clk_frequency(clocks) as usize,
],
)
})
.map_err(ConfigError::Clock)?;
self.regs().lcd_clock().write(|w| unsafe {
w.clk_en().set_bit();
w.lcd_clk_sel().bits((i + 1) as _);
w.lcd_clkm_div_num().bits(divider.div_num as _);
w.lcd_clkm_div_b().bits(divider.div_b as _);
w.lcd_clkm_div_a().bits(divider.div_a as _); w.lcd_clk_equ_sysclk().clear_bit();
w.lcd_clkcnt_n().bits(2 - 1); w.lcd_ck_idle_edge()
.bit(config.clock_mode.polarity == Polarity::IdleHigh);
w.lcd_ck_out_edge()
.bit(config.clock_mode.phase == Phase::ShiftHigh)
});
self.regs()
.lcd_ctrl()
.write(|w| w.lcd_rgb_mode_en().clear_bit());
self.regs()
.lcd_rgb_yuv()
.write(|w| w.lcd_conv_bypass().clear_bit());
self.regs().lcd_user().modify(|_, w| {
w.lcd_8bits_order().bit(false);
w.lcd_bit_order().bit(false);
w.lcd_byte_order().bit(false);
w.lcd_2byte_en().bit(false)
});
self.regs().lcd_misc().write(|w| unsafe {
w.lcd_afifo_threshold_num().bits(0);
w.lcd_vfk_cyclelen()
.bits(config.setup_cycles.saturating_sub(1) as _);
w.lcd_vbk_cyclelen()
.bits(config.hold_cycles.saturating_sub(1) as _);
w.lcd_next_frame_en().clear_bit();
w.lcd_bk_en().set_bit();
w.lcd_cd_data_set()
.bit(config.cd_data_edge != config.cd_idle_edge);
w.lcd_cd_dummy_set()
.bit(config.cd_dummy_edge != config.cd_idle_edge);
w.lcd_cd_cmd_set()
.bit(config.cd_cmd_edge != config.cd_idle_edge);
w.lcd_cd_idle_edge().bit(config.cd_idle_edge)
});
self.regs()
.lcd_dly_mode()
.write(|w| unsafe { w.lcd_cd_mode().bits(config.cd_mode as u8) });
self.regs().lcd_data_dout_mode().write(|w| unsafe {
w.dout0_mode().bits(config.output_bit_mode as u8);
w.dout1_mode().bits(config.output_bit_mode as u8);
w.dout2_mode().bits(config.output_bit_mode as u8);
w.dout3_mode().bits(config.output_bit_mode as u8);
w.dout4_mode().bits(config.output_bit_mode as u8);
w.dout5_mode().bits(config.output_bit_mode as u8);
w.dout6_mode().bits(config.output_bit_mode as u8);
w.dout7_mode().bits(config.output_bit_mode as u8);
w.dout8_mode().bits(config.output_bit_mode as u8);
w.dout9_mode().bits(config.output_bit_mode as u8);
w.dout10_mode().bits(config.output_bit_mode as u8);
w.dout11_mode().bits(config.output_bit_mode as u8);
w.dout12_mode().bits(config.output_bit_mode as u8);
w.dout13_mode().bits(config.output_bit_mode as u8);
w.dout14_mode().bits(config.output_bit_mode as u8);
w.dout15_mode().bits(config.output_bit_mode as u8)
});
self.regs()
.lcd_user()
.modify(|_, w| w.lcd_update().set_bit());
Ok(())
}
pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self {
let is_inverted = byte_order != ByteOrder::default();
self.regs()
.lcd_user()
.modify(|_, w| w.lcd_byte_order().bit(is_inverted));
self
}
pub fn set_8bits_order(&mut self, byte_order: ByteOrder) -> &mut Self {
let is_inverted = byte_order != ByteOrder::default();
self.regs()
.lcd_user()
.modify(|_, w| w.lcd_8bits_order().bit(is_inverted));
self
}
pub fn set_bit_order(&mut self, bit_order: BitOrder) -> &mut Self {
self.regs()
.lcd_user()
.modify(|_, w| w.lcd_bit_order().bit(bit_order != BitOrder::default()));
self
}
pub fn with_cs(self, cs: impl PeripheralOutput<'d>) -> Self {
let cs = cs.into();
cs.apply_output_config(&OutputConfig::default());
cs.set_output_enable(true);
OutputSignal::LCD_CS.connect_to(&cs);
self
}
pub fn with_dc(self, dc: impl PeripheralOutput<'d>) -> Self {
let dc = dc.into();
dc.apply_output_config(&OutputConfig::default());
dc.set_output_enable(true);
OutputSignal::LCD_DC.connect_to(&dc);
self
}
pub fn with_wrx(self, wrx: impl PeripheralOutput<'d>) -> Self {
let wrx = wrx.into();
wrx.apply_output_config(&OutputConfig::default());
wrx.set_output_enable(true);
OutputSignal::LCD_PCLK.connect_to(&wrx);
self
}
fn with_data_pin(self, signal: OutputSignal, pin: impl PeripheralOutput<'d>) -> Self {
let pin = pin.into();
pin.apply_output_config(&OutputConfig::default());
pin.set_output_enable(true);
signal.connect_to(&pin);
self
}
pub fn with_data0(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_0, pin)
}
pub fn with_data1(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_1, pin)
}
pub fn with_data2(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_2, pin)
}
pub fn with_data3(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_3, pin)
}
pub fn with_data4(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_4, pin)
}
pub fn with_data5(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_5, pin)
}
pub fn with_data6(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_6, pin)
}
pub fn with_data7(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_7, pin)
}
pub fn with_data8(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_8, pin)
}
pub fn with_data9(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_9, pin)
}
pub fn with_data10(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_10, pin)
}
pub fn with_data11(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_11, pin)
}
pub fn with_data12(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_12, pin)
}
pub fn with_data13(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_13, pin)
}
pub fn with_data14(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_14, pin)
}
pub fn with_data15(self, pin: impl PeripheralOutput<'d>) -> Self {
self.with_data_pin(OutputSignal::LCD_DATA_15, pin)
}
pub fn send<W: Into<u16> + Copy, BUF: DmaTxBuffer>(
mut self,
cmd: impl Into<Command<W>>,
dummy: u8,
mut data: BUF,
) -> Result<I8080Transfer<'d, BUF, Dm>, (DmaError, Self, BUF)> {
let cmd = cmd.into();
self.regs()
.lcd_user()
.modify(|_, w| w.lcd_reset().set_bit());
self.regs()
.lcd_misc()
.modify(|_, w| w.lcd_afifo_reset().set_bit());
match cmd {
Command::None => {
self.regs()
.lcd_user()
.modify(|_, w| w.lcd_cmd().clear_bit());
}
Command::One(value) => {
self.regs().lcd_user().modify(|_, w| {
w.lcd_cmd().set_bit();
w.lcd_cmd_2_cycle_en().clear_bit()
});
self.regs()
.lcd_cmd_val()
.write(|w| unsafe { w.lcd_cmd_value().bits(value.into() as _) });
}
Command::Two(first, second) => {
self.regs().lcd_user().modify(|_, w| {
w.lcd_cmd().set_bit();
w.lcd_cmd_2_cycle_en().set_bit()
});
let cmd = first.into() as u32 | ((second.into() as u32) << 16);
self.regs()
.lcd_cmd_val()
.write(|w| unsafe { w.lcd_cmd_value().bits(cmd) });
}
}
let is_2byte_mode = size_of::<W>() == 2;
self.regs().lcd_user().modify(|_, w| unsafe {
if dummy > 0 {
w.lcd_dummy()
.set_bit()
.lcd_dummy_cyclelen()
.bits((dummy - 1) as _)
} else {
w.lcd_dummy().clear_bit()
}
.lcd_2byte_en()
.bit(is_2byte_mode)
});
self.regs()
.lcd_user()
.modify(|_, w| w.lcd_always_out_en().set_bit().lcd_dout().set_bit());
let result = unsafe {
self.tx_channel
.prepare_transfer(DmaPeripheral::LcdCam, &mut data)
}
.and_then(|_| self.tx_channel.start_transfer());
if let Err(err) = result {
return Err((err, self, data));
}
self.regs()
.lc_dma_int_clr()
.write(|w| w.lcd_trans_done_int_clr().set_bit());
self.regs().lcd_user().modify(|_, w| {
w.lcd_update().set_bit();
w.lcd_start().set_bit()
});
Ok(I8080Transfer {
i8080: ManuallyDrop::new(self),
buf_view: ManuallyDrop::new(data.into_view()),
})
}
}
impl<Dm: DriverMode> core::fmt::Debug for I8080<'_, Dm> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("I8080").finish()
}
}
pub struct I8080Transfer<'d, BUF: DmaTxBuffer, Dm: DriverMode> {
i8080: ManuallyDrop<I8080<'d, Dm>>,
buf_view: ManuallyDrop<BUF::View>,
}
impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> I8080Transfer<'d, BUF, Dm> {
pub fn is_done(&self) -> bool {
self.i8080
.regs()
.lcd_user()
.read()
.lcd_start()
.bit_is_clear()
}
pub fn cancel(mut self) -> (I8080<'d, Dm>, BUF::Final) {
self.stop_peripherals();
let (_, i8080, buf) = self.wait();
(i8080, buf)
}
pub fn wait(mut self) -> (Result<(), DmaError>, I8080<'d, Dm>, BUF::Final) {
while !self.is_done() {}
self.i8080
.regs()
.lc_dma_int_clr()
.write(|w| w.lcd_trans_done_int_clr().set_bit());
let (i8080, view) = unsafe {
let i8080 = ManuallyDrop::take(&mut self.i8080);
let view = ManuallyDrop::take(&mut self.buf_view);
core::mem::forget(self);
(i8080, view)
};
let result = if i8080.tx_channel.has_error() {
Err(DmaError::DescriptorError)
} else {
Ok(())
};
(result, i8080, BUF::from_view(view))
}
fn stop_peripherals(&mut self) {
self.i8080
.regs()
.lcd_user()
.modify(|_, w| w.lcd_start().clear_bit());
self.i8080.tx_channel.stop_transfer();
}
}
impl<BUF: DmaTxBuffer, Dm: DriverMode> Deref for I8080Transfer<'_, BUF, Dm> {
type Target = BUF::View;
fn deref(&self) -> &Self::Target {
&self.buf_view
}
}
impl<BUF: DmaTxBuffer, Dm: DriverMode> DerefMut for I8080Transfer<'_, BUF, Dm> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buf_view
}
}
impl<BUF: DmaTxBuffer> I8080Transfer<'_, BUF, crate::Async> {
pub async fn wait_for_done(&mut self) {
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
struct LcdDoneFuture {}
impl Future for LcdDoneFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if Instance::is_lcd_done_set() {
Poll::Ready(())
} else {
LCD_DONE_WAKER.register(cx.waker());
Instance::listen_lcd_done();
Poll::Pending
}
}
}
impl Drop for LcdDoneFuture {
fn drop(&mut self) {
Instance::unlisten_lcd_done();
}
}
LcdDoneFuture {}.await
}
}
impl<BUF: DmaTxBuffer, Dm: DriverMode> Drop for I8080Transfer<'_, BUF, Dm> {
fn drop(&mut self) {
self.stop_peripherals();
let view = unsafe {
ManuallyDrop::drop(&mut self.i8080);
ManuallyDrop::take(&mut self.buf_view)
};
let _ = BUF::from_view(view);
}
}
#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Config {
clock_mode: ClockMode,
frequency: Rate,
setup_cycles: usize,
hold_cycles: usize,
cd_idle_edge: bool,
cd_cmd_edge: bool,
cd_dummy_edge: bool,
cd_data_edge: bool,
cd_mode: DelayMode,
output_bit_mode: DelayMode,
}
impl Default for Config {
fn default() -> Self {
Self {
clock_mode: Default::default(),
frequency: Rate::from_mhz(20),
setup_cycles: 1,
hold_cycles: 1,
cd_idle_edge: false,
cd_cmd_edge: false,
cd_dummy_edge: false,
cd_data_edge: true,
cd_mode: Default::default(),
output_bit_mode: Default::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Command<T> {
None,
One(T),
Two(T, T),
}
impl From<u8> for Command<u8> {
fn from(value: u8) -> Self {
Command::One(value)
}
}
impl From<u16> for Command<u16> {
fn from(value: u16) -> Self {
Command::One(value)
}
}