//! # Parallel IO (PARL_IO) //! //! ## Overview //! The Parallel IO peripheral is a general purpose parallel interface that can //! be used to connect to external devices such as LED matrix, LCD display, //! Printer and Camera. The peripheral has independent TX and RX units. Each //! unit can have up to 8 or 16 data signals (depending on your target hardware) //! plus 1 or 2 clock signals. //! //! ## Configuration //! The driver uses DMA (Direct Memory Access) for efficient data transfer. //! //! ## Examples //! ### Initialization for RX //! See the [Parallel IO RX] example to learn how to initialize the RX and //! reading data. //! //! [Parallel IO RX]: https://github.com/esp-rs/esp-hal/blob/main/examples/src/bin/parl_io_rx.rs //! //! ### Initialization for TX //! See the [Parallel IO TX] example to learn how to initialize the TX and //! transferring data. //! //! [Parallel IO TX]: https://github.com/esp-rs/esp-hal/blob/main/examples/src/bin/parl_io_tx.rs use enumset::{EnumSet, EnumSetType}; use fugit::HertzU32; use peripheral::PeripheralRef; use private::*; use crate::{ dma::{ dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, Channel, ChannelRx, ChannelTx, DescriptorChain, DmaChannelConvert, DmaDescriptor, DmaEligible, DmaError, DmaPeripheral, DmaTransferRx, DmaTransferTx, ReadBuffer, Rx, Tx, WriteBuffer, }, gpio::{ interconnect::{InputConnection, OutputConnection, PeripheralInput, PeripheralOutput}, NoPin, }, interrupt::InterruptHandler, peripheral::{self, Peripheral}, peripherals::{self, Interrupt, PARL_IO}, system::PeripheralClockControl, Async, Blocking, InterruptConfigurable, Mode, }; #[allow(unused)] const MAX_DMA_SIZE: usize = 32736; /// Interrupts generated by the peripheral #[derive(Debug, EnumSetType)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParlIoInterrupt { /// Triggered when TX FIFO is empty. This interrupt indicates that there /// might be error in the data sent by TX. TxFifoReEmpty, /// Triggered when RX FIFO is full. This interrupt indicates that there /// might be error in the data received by RX. RxFifoWOvf, /// Triggered when TX finishes sending a complete frame of data. TxEof, } /// Parallel IO errors #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// General DMA error DmaError(DmaError), /// Maximum transfer size (32736) exceeded MaxDmaTransferSizeExceeded, /// Trying to use an impossible clock rate UnreachableClockRate, } impl From for Error { fn from(value: DmaError) -> Self { Error::DmaError(value) } } /// Parallel IO sample edge #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SampleEdge { /// Positive edge Normal = 0, /// Negative edge Invert = 1, } /// Parallel IO bit packing order #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BitPackOrder { /// Bit pack order: MSB Msb = 0, /// Bit pack order: LSB Lsb = 1, } #[cfg(esp32c6)] /// Enable Mode #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum EnableMode { /// Enable at high level HighLevel, /// Enable at low level LowLevel, /// Positive pulse start (data bit included) & Positive pulse end (data bit /// included) PulseMode1, /// Positive pulse start (data bit included) & Positive pulse end (data bit /// excluded) PulseMode2, /// Positive pulse start (data bit excluded) & Positive pulse end (data bit /// included) PulseMode3, /// Positive pulse start (data bit excluded) & Positive pulse end (data bit /// excluded) PulseMode4, /// Positive pulse start (data bit included) & Length end PulseMode5, /// Positive pulse start (data bit excluded) & Length end PulseMode6, /// Negative pulse start (data bit included) & Negative pulse end(data bit /// included) PulseMode7, /// Negative pulse start (data bit included) & Negative pulse end (data bit /// excluded) PulseMode8, /// Negative pulse start (data bit excluded) & Negative pulse end (data bit /// included) PulseMode9, /// Negative pulse start (data bit excluded) & Negative pulse end (data bit /// excluded) PulseMode10, /// Negative pulse start (data bit included) & Length end PulseMode11, /// Negative pulse start (data bit excluded) & Length end PulseMode12, } #[cfg(esp32c6)] impl EnableMode { fn pulse_submode_sel(&self) -> Option { match self { EnableMode::PulseMode1 => Some(0), EnableMode::PulseMode2 => Some(1), EnableMode::PulseMode3 => Some(2), EnableMode::PulseMode4 => Some(3), EnableMode::PulseMode5 => Some(4), EnableMode::PulseMode6 => Some(5), EnableMode::PulseMode7 => Some(6), EnableMode::PulseMode8 => Some(7), EnableMode::PulseMode9 => Some(8), EnableMode::PulseMode10 => Some(9), EnableMode::PulseMode11 => Some(10), EnableMode::PulseMode12 => Some(11), _ => None, } } fn level_submode_sel(&self) -> Option { match self { EnableMode::HighLevel => Some(0), EnableMode::LowLevel => Some(1), _ => None, } } fn smp_model_sel(&self) -> Option { match self { EnableMode::HighLevel => Some(self::private::SampleMode::ExternalLevel), EnableMode::LowLevel => Some(self::private::SampleMode::ExternalLevel), _ => Some(self::private::SampleMode::ExternalPulse), } } } #[cfg(esp32h2)] /// Enable Mode #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum EnableMode { /// Enable at high level HighLevel, /// Positive pulse start (data bit included) & Positive pulse end (data bit /// included) PulseMode1, /// Positive pulse start (data bit included) & Positive pulse end (data bit /// excluded) PulseMode2, /// Positive pulse start (data bit excluded) & Positive pulse end (data bit /// included) PulseMode3, /// Positive pulse start (data bit excluded) & Positive pulse end (data bit /// excluded) PulseMode4, /// Positive pulse start (data bit included) & Length end PulseMode5, /// Positive pulse start (data bit excluded) & Length end PulseMode6, } #[cfg(esp32h2)] impl EnableMode { fn pulse_submode_sel(&self) -> Option { match self { EnableMode::PulseMode1 => Some(0), EnableMode::PulseMode2 => Some(1), EnableMode::PulseMode3 => Some(2), EnableMode::PulseMode4 => Some(3), EnableMode::PulseMode5 => Some(4), EnableMode::PulseMode6 => Some(5), _ => None, } } fn level_submode_sel(&self) -> Option { match self { EnableMode::HighLevel => Some(0), _ => None, } } fn smp_model_sel(&self) -> Option { match self { EnableMode::HighLevel => Some(self::private::SampleMode::ExternalLevel), _ => Some(self::private::SampleMode::ExternalPulse), } } } /// Generation of GDMA SUC EOF #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum EofMode { /// Generate GDMA SUC EOF by data byte length ByteLen, /// Generate GDMA SUC EOF by the external enable signal EnableSignal, } /// Used to configure no pin as clock output impl TxClkPin for NoPin { fn configure(&mut self) { crate::gpio::OutputSignal::PARL_TX_CLK.connect_to(self); } } impl RxClkPin for NoPin { fn configure(&mut self) { crate::gpio::InputSignal::PARL_RX_CLK.connect_to(self); } } /// Wraps a GPIO pin which will be used as the clock output signal pub struct ClkOutPin<'d> { pin: PeripheralRef<'d, OutputConnection>, } impl<'d> ClkOutPin<'d> { /// Create a ClkOutPin pub fn new(pin: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(pin); Self { pin } } } impl TxClkPin for ClkOutPin<'_> { fn configure(&mut self) { self.pin.set_to_push_pull_output(crate::private::Internal); crate::gpio::OutputSignal::PARL_TX_CLK.connect_to(&mut self.pin); } } /// Wraps a GPIO pin which will be used as the TX clock input signal pub struct ClkInPin<'d> { pin: PeripheralRef<'d, InputConnection>, } impl<'d> ClkInPin<'d> { /// Create a new ClkInPin pub fn new(pin: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(pin); Self { pin } } } impl TxClkPin for ClkInPin<'_> { fn configure(&mut self) { let pcr = unsafe { &*crate::peripherals::PCR::PTR }; pcr.parl_clk_tx_conf() .modify(|_, w| unsafe { w.parl_clk_tx_sel().bits(3).parl_clk_tx_div_num().bits(0) }); // PAD_CLK_TX, no divider self.pin .init_input(crate::gpio::Pull::None, crate::private::Internal); crate::gpio::InputSignal::PARL_TX_CLK.connect_to(&mut self.pin); } } /// Wraps a GPIO pin which will be used as the RX clock input signal pub struct RxClkInPin<'d> { pin: PeripheralRef<'d, InputConnection>, sample_edge: SampleEdge, } impl<'d> RxClkInPin<'d> { /// Create a new RxClkInPin pub fn new( pin: impl Peripheral

+ 'd, sample_edge: SampleEdge, ) -> Self { crate::into_mapped_ref!(pin); Self { pin, sample_edge } } } impl RxClkPin for RxClkInPin<'_> { fn configure(&mut self) { let pcr = unsafe { &*crate::peripherals::PCR::PTR }; pcr.parl_clk_rx_conf() .modify(|_, w| unsafe { w.parl_clk_rx_sel().bits(3).parl_clk_rx_div_num().bits(0) }); // PAD_CLK_TX, no divider self.pin .init_input(crate::gpio::Pull::None, crate::private::Internal); crate::gpio::InputSignal::PARL_RX_CLK.connect_to(&mut self.pin); Instance::set_rx_clk_edge_sel(self.sample_edge); } } /// Pin configuration with an additional pin for the valid signal. pub struct TxPinConfigWithValidPin<'d, P> where P: NotContainsValidSignalPin + TxPins + ConfigurePins, { tx_pins: P, valid_pin: PeripheralRef<'d, OutputConnection>, } impl<'d, P> TxPinConfigWithValidPin<'d, P> where P: NotContainsValidSignalPin + TxPins + ConfigurePins, { /// Create a [TxPinConfigWithValidPin] pub fn new(tx_pins: P, valid_pin: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(valid_pin); Self { tx_pins, valid_pin } } } impl

TxPins for TxPinConfigWithValidPin<'_, P> where P: NotContainsValidSignalPin + TxPins + ConfigurePins { } impl

ConfigurePins for TxPinConfigWithValidPin<'_, P> where P: NotContainsValidSignalPin + TxPins + ConfigurePins, { fn configure(&mut self) -> Result<(), Error> { self.tx_pins.configure()?; self.valid_pin .set_to_push_pull_output(crate::private::Internal); Instance::tx_valid_pin_signal().connect_to(&mut self.valid_pin); Instance::set_tx_hw_valid_en(true); Ok(()) } } /// Pin configuration where the pin for the valid signal is the MSB pin. pub struct TxPinConfigIncludingValidPin

where P: ContainsValidSignalPin + TxPins + ConfigurePins, { tx_pins: P, } impl

TxPinConfigIncludingValidPin

where P: ContainsValidSignalPin + TxPins + ConfigurePins, { /// Create a new [TxPinConfigIncludingValidPin] pub fn new(tx_pins: P) -> Self { Self { tx_pins } } } impl

TxPins for TxPinConfigIncludingValidPin

where P: ContainsValidSignalPin + TxPins + ConfigurePins { } impl

ConfigurePins for TxPinConfigIncludingValidPin

where P: ContainsValidSignalPin + TxPins + ConfigurePins, { fn configure(&mut self) -> Result<(), Error> { self.tx_pins.configure()?; Instance::set_tx_hw_valid_en(true); Ok(()) } } macro_rules! tx_pins { ($name:ident, $width:literal, $($pin:ident = $signal:ident),+ ) => { paste::paste! { #[doc = "Data pin configuration for "] #[doc = stringify!($width)] #[doc = "bit output mode"] pub struct $name<'d> { $( [< pin_ $pin:lower >] : PeripheralRef<'d, OutputConnection>, )+ } impl<'d> $name<'d> { /// Create a new TX pin #[allow(clippy::too_many_arguments)] pub fn new( $( [< pin_ $pin:lower >] : impl Peripheral

+ 'd, )+ ) -> Self { crate::into_mapped_ref!($( [< pin_ $pin:lower >] ),+); Self { $( [< pin_ $pin:lower >] ),+ } } } impl ConfigurePins for $name<'_> { fn configure(&mut self) -> Result<(), Error>{ $( self.[< pin_ $pin:lower >].set_to_push_pull_output(crate::private::Internal); crate::gpio::OutputSignal::$signal.connect_to(&mut self.[< pin_ $pin:lower >]); )+ private::Instance::set_tx_bit_width( private::WidSel::[< Bits $width >]); Ok(()) } } impl TxPins for $name<'_> {} } }; } tx_pins!(TxOneBit, 1, P0 = PARL_TX_DATA0); tx_pins!(TxTwoBits, 2, P0 = PARL_TX_DATA0, P1 = PARL_TX_DATA1); tx_pins!( TxFourBits, 4, P0 = PARL_TX_DATA0, P1 = PARL_TX_DATA1, P2 = PARL_TX_DATA2, P3 = PARL_TX_DATA3 ); tx_pins!( TxEightBits, 8, P0 = PARL_TX_DATA0, P1 = PARL_TX_DATA1, P2 = PARL_TX_DATA2, P3 = PARL_TX_DATA3, P4 = PARL_TX_DATA4, P5 = PARL_TX_DATA5, P6 = PARL_TX_DATA6, P7 = PARL_TX_DATA7 ); #[cfg(esp32c6)] tx_pins!( TxSixteenBits, 16, P0 = PARL_TX_DATA0, P1 = PARL_TX_DATA1, P2 = PARL_TX_DATA2, P3 = PARL_TX_DATA3, P4 = PARL_TX_DATA4, P5 = PARL_TX_DATA5, P6 = PARL_TX_DATA6, P7 = PARL_TX_DATA7, P8 = PARL_TX_DATA8, P9 = PARL_TX_DATA9, P10 = PARL_TX_DATA10, P11 = PARL_TX_DATA11, P12 = PARL_TX_DATA12, P13 = PARL_TX_DATA13, P14 = PARL_TX_DATA14, P15 = PARL_TX_DATA15 ); impl FullDuplex for TxOneBit<'_> {} impl FullDuplex for TxTwoBits<'_> {} impl FullDuplex for TxFourBits<'_> {} impl FullDuplex for TxEightBits<'_> {} impl NotContainsValidSignalPin for TxOneBit<'_> {} impl NotContainsValidSignalPin for TxTwoBits<'_> {} impl NotContainsValidSignalPin for TxFourBits<'_> {} #[cfg(esp32c6)] impl NotContainsValidSignalPin for TxEightBits<'_> {} #[cfg(esp32h2)] impl ContainsValidSignalPin for TxEightBits<'_> {} #[cfg(esp32c6)] impl ContainsValidSignalPin for TxSixteenBits<'_> {} /// Pin configuration with an additional pin for the valid signal. pub struct RxPinConfigWithValidPin<'d, P> where P: NotContainsValidSignalPin + RxPins + ConfigurePins, { rx_pins: P, valid_pin: PeripheralRef<'d, InputConnection>, enable_mode: EnableMode, eof_mode: EofMode, } impl<'d, P> RxPinConfigWithValidPin<'d, P> where P: NotContainsValidSignalPin + RxPins + ConfigurePins, { /// Create a new [RxPinConfigWithValidPin] pub fn new( rx_pins: P, valid_pin: impl Peripheral

+ 'd, enable_mode: EnableMode, eof_mode: EofMode, ) -> Self { crate::into_mapped_ref!(valid_pin); Self { rx_pins, valid_pin, enable_mode, eof_mode, } } } impl

RxPins for RxPinConfigWithValidPin<'_, P> where P: NotContainsValidSignalPin + RxPins + ConfigurePins { } impl

ConfigurePins for RxPinConfigWithValidPin<'_, P> where P: NotContainsValidSignalPin + RxPins + ConfigurePins, { fn configure(&mut self) -> Result<(), Error> { self.rx_pins.configure()?; self.valid_pin .init_input(crate::gpio::Pull::None, crate::private::Internal); Instance::rx_valid_pin_signal().connect_to(&mut self.valid_pin); Instance::set_rx_sw_en(false); if let Some(sel) = self.enable_mode.pulse_submode_sel() { Instance::set_rx_pulse_submode_sel(sel); } if let Some(sel) = self.enable_mode.level_submode_sel() { Instance::set_rx_level_submode_sel(sel); } if let Some(sel) = self.enable_mode.smp_model_sel() { Instance::set_rx_sample_mode(sel); } Instance::set_eof_gen_sel(self.eof_mode); Ok(()) } } /// Pin configuration where the pin for the valid signal is the MSB pin. pub struct RxPinConfigIncludingValidPin

where P: ContainsValidSignalPin + RxPins + ConfigurePins, { rx_pins: P, enable_mode: EnableMode, eof_mode: EofMode, } impl

RxPinConfigIncludingValidPin

where P: ContainsValidSignalPin + RxPins + ConfigurePins, { /// Create a new [RxPinConfigIncludingValidPin] pub fn new(rx_pins: P, enable_mode: EnableMode, eof_mode: EofMode) -> Self { Self { rx_pins, enable_mode, eof_mode, } } } impl

RxPins for RxPinConfigIncludingValidPin

where P: ContainsValidSignalPin + RxPins + ConfigurePins { } impl

ConfigurePins for RxPinConfigIncludingValidPin

where P: ContainsValidSignalPin + RxPins + ConfigurePins, { fn configure(&mut self) -> Result<(), Error> { self.rx_pins.configure()?; Instance::set_rx_sw_en(false); if let Some(sel) = self.enable_mode.pulse_submode_sel() { Instance::set_rx_pulse_submode_sel(sel); } if let Some(sel) = self.enable_mode.level_submode_sel() { Instance::set_rx_level_submode_sel(sel); } if let Some(sel) = self.enable_mode.smp_model_sel() { Instance::set_rx_sample_mode(sel); } Instance::set_eof_gen_sel(self.eof_mode); Ok(()) } } macro_rules! rx_pins { ($name:ident, $width:literal, $($pin:ident = $signal:ident),+ ) => { paste::paste! { #[doc = "Data pin configuration for "] #[doc = stringify!($width)] #[doc = "bit input mode"] pub struct $name<'d> { $( [< pin_ $pin:lower >] : PeripheralRef<'d, InputConnection>, )+ } impl<'d> $name<'d> { /// Create a new RX pin #[allow(clippy::too_many_arguments)] pub fn new( $( [< pin_ $pin:lower >] : impl Peripheral

+ 'd, )+ ) -> Self { crate::into_mapped_ref!($( [< pin_ $pin:lower >] ),+); Self { $( [< pin_ $pin:lower >] ),+ } } } impl ConfigurePins for $name<'_> { fn configure(&mut self) -> Result<(), Error> { $( self.[< pin_ $pin:lower >].init_input(crate::gpio::Pull::None, crate::private::Internal); crate::gpio::InputSignal::$signal.connect_to(&mut self.[< pin_ $pin:lower >]); )+ private::Instance::set_rx_bit_width( private::WidSel::[< Bits $width >]); Ok(()) } } impl RxPins for $name<'_> {} } }; } rx_pins!(RxOneBit, 1, P0 = PARL_RX_DATA0); rx_pins!(RxTwoBits, 2, P0 = PARL_RX_DATA0, P1 = PARL_RX_DATA1); rx_pins!( RxFourBits, 4, P0 = PARL_RX_DATA0, P1 = PARL_RX_DATA1, P2 = PARL_RX_DATA2, P3 = PARL_RX_DATA3 ); rx_pins!( RxEightBits, 8, P0 = PARL_RX_DATA0, P1 = PARL_RX_DATA1, P2 = PARL_RX_DATA2, P3 = PARL_RX_DATA3, P4 = PARL_RX_DATA4, P5 = PARL_RX_DATA5, P6 = PARL_RX_DATA6, P7 = PARL_RX_DATA7 ); #[cfg(esp32c6)] rx_pins!( RxSixteenBits, 16, P0 = PARL_RX_DATA0, P1 = PARL_RX_DATA1, P2 = PARL_RX_DATA2, P3 = PARL_RX_DATA3, P4 = PARL_RX_DATA4, P5 = PARL_RX_DATA5, P6 = PARL_RX_DATA6, P7 = PARL_RX_DATA7, P8 = PARL_RX_DATA8, P9 = PARL_RX_DATA9, P10 = PARL_RX_DATA10, P11 = PARL_RX_DATA11, P12 = PARL_RX_DATA12, P13 = PARL_RX_DATA13, P14 = PARL_RX_DATA14, P15 = PARL_RX_DATA15 ); impl FullDuplex for RxOneBit<'_> {} impl FullDuplex for RxTwoBits<'_> {} impl FullDuplex for RxFourBits<'_> {} impl FullDuplex for RxEightBits<'_> {} impl NotContainsValidSignalPin for RxOneBit<'_> {} impl NotContainsValidSignalPin for RxTwoBits<'_> {} impl NotContainsValidSignalPin for RxFourBits<'_> {} #[cfg(esp32c6)] impl NotContainsValidSignalPin for RxEightBits<'_> {} #[cfg(esp32h2)] impl ContainsValidSignalPin for RxEightBits<'_> {} #[cfg(esp32c6)] impl ContainsValidSignalPin for RxSixteenBits<'_> {} impl<'d, DM> TxCreatorFullDuplex<'d, DM> where DM: Mode, { /// Configure TX to use the given pins and settings pub fn with_config( self, mut tx_pins: P, mut clk_pin: CP, idle_value: u16, sample_edge: SampleEdge, bit_order: BitPackOrder, ) -> Result, Error> where P: FullDuplex + TxPins + ConfigurePins, CP: TxClkPin, { tx_pins.configure()?; clk_pin.configure(); Instance::set_tx_idle_value(idle_value); Instance::set_tx_sample_edge(sample_edge); Instance::set_tx_bit_order(bit_order); Ok(ParlIoTx { tx_channel: self.tx_channel, tx_chain: DescriptorChain::new(self.descriptors), }) } } impl<'d, DM> TxCreator<'d, DM> where DM: Mode, { /// Configure TX to use the given pins and settings pub fn with_config( self, tx_pins: &'d mut P, clk_pin: &'d mut CP, idle_value: u16, sample_edge: SampleEdge, bit_order: BitPackOrder, ) -> Result, Error> where P: TxPins + ConfigurePins, CP: TxClkPin, { tx_pins.configure()?; clk_pin.configure(); Instance::set_tx_idle_value(idle_value); Instance::set_tx_sample_edge(sample_edge); Instance::set_tx_bit_order(bit_order); Ok(ParlIoTx { tx_channel: self.tx_channel, tx_chain: DescriptorChain::new(self.descriptors), }) } } /// Parallel IO TX channel pub struct ParlIoTx<'d, DM> where DM: Mode, { tx_channel: ChannelTx<'d, DM, ::Dma>, tx_chain: DescriptorChain, } impl core::fmt::Debug for ParlIoTx<'_, DM> where DM: Mode, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("ParlIoTx").finish() } } impl<'d, DM> RxCreatorFullDuplex<'d, DM> where DM: Mode, { /// Configure RX to use the given pins and settings pub fn with_config( self, rx_pins: &'d mut P, clk_pin: &'d mut CP, bit_order: BitPackOrder, timeout_ticks: Option, ) -> Result, Error> where P: FullDuplex + RxPins + ConfigurePins, CP: RxClkPin, { rx_pins.configure()?; clk_pin.configure(); Instance::set_rx_bit_order(bit_order); Instance::set_rx_timeout_ticks(timeout_ticks); Ok(ParlIoRx { rx_channel: self.rx_channel, rx_chain: DescriptorChain::new(self.descriptors), }) } } impl<'d, DM> RxCreator<'d, DM> where DM: Mode, { /// Configure RX to use the given pins and settings pub fn with_config( self, rx_pins: &'d mut P, clk_pin: &'d mut CP, bit_order: BitPackOrder, timeout_ticks: Option, ) -> Result, Error> where P: RxPins + ConfigurePins, CP: RxClkPin, { rx_pins.configure()?; clk_pin.configure(); Instance::set_rx_bit_order(bit_order); Instance::set_rx_timeout_ticks(timeout_ticks); Ok(ParlIoRx { rx_channel: self.rx_channel, rx_chain: DescriptorChain::new(self.descriptors), }) } } /// Parallel IO RX channel pub struct ParlIoRx<'d, DM> where DM: Mode, { rx_channel: ChannelRx<'d, DM, ::Dma>, rx_chain: DescriptorChain, } impl core::fmt::Debug for ParlIoRx<'_, DM> where DM: Mode, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("ParlIoTx").finish() } } fn internal_set_interrupt_handler(handler: InterruptHandler) { #[cfg(esp32c6)] { for core in crate::Cpu::other() { crate::interrupt::disable(core, Interrupt::PARL_IO); } internal_listen(EnumSet::all(), false); internal_clear_interrupts(EnumSet::all()); unsafe { PARL_IO::steal() }.bind_parl_io_interrupt(handler.handler()); unwrap!(crate::interrupt::enable( Interrupt::PARL_IO, handler.priority() )); } #[cfg(esp32h2)] { for core in crate::Cpu::other() { crate::interrupt::disable(core, Interrupt::PARL_IO_RX); crate::interrupt::disable(core, Interrupt::PARL_IO_TX); } internal_listen(EnumSet::all(), false); internal_clear_interrupts(EnumSet::all()); unsafe { PARL_IO::steal() }.bind_parl_io_tx_interrupt(handler.handler()); unsafe { PARL_IO::steal() }.bind_parl_io_rx_interrupt(handler.handler()); unwrap!(crate::interrupt::enable( Interrupt::PARL_IO_TX, handler.priority(), )); unwrap!(crate::interrupt::enable( Interrupt::PARL_IO_RX, handler.priority(), )); } } fn internal_listen(interrupts: EnumSet, enable: bool) { let parl_io = unsafe { PARL_IO::steal() }; parl_io.int_ena().write(|w| { for interrupt in interrupts { match interrupt { ParlIoInterrupt::TxFifoReEmpty => w.tx_fifo_rempty().bit(enable), ParlIoInterrupt::RxFifoWOvf => w.rx_fifo_wovf().bit(enable), ParlIoInterrupt::TxEof => w.tx_eof().bit(enable), }; } w }); } fn internal_interrupts() -> EnumSet { let mut res = EnumSet::new(); let parl_io = unsafe { PARL_IO::steal() }; let ints = parl_io.int_st().read(); if ints.tx_fifo_rempty().bit() { res.insert(ParlIoInterrupt::TxFifoReEmpty); } if ints.rx_fifo_wovf().bit() { res.insert(ParlIoInterrupt::RxFifoWOvf); } if ints.tx_eof().bit() { res.insert(ParlIoInterrupt::TxEof); } res } fn internal_clear_interrupts(interrupts: EnumSet) { let parl_io = unsafe { PARL_IO::steal() }; parl_io.int_clr().write(|w| { for interrupt in interrupts { match interrupt { ParlIoInterrupt::TxFifoReEmpty => w.tx_fifo_rempty().clear_bit_by_one(), ParlIoInterrupt::RxFifoWOvf => w.rx_fifo_wovf().clear_bit_by_one(), ParlIoInterrupt::TxEof => w.tx_eof().clear_bit_by_one(), }; } w }); } /// Parallel IO in full duplex mode /// /// Full duplex mode might limit the maximum possible bit width. pub struct ParlIoFullDuplex<'d, DM> where DM: Mode, { /// The transmitter (TX) channel responsible for handling DMA transfers in /// the parallel I/O full-duplex operation. pub tx: TxCreatorFullDuplex<'d, DM>, /// The receiver (RX) channel responsible for handling DMA transfers in the /// parallel I/O full-duplex operation. pub rx: RxCreatorFullDuplex<'d, DM>, } impl<'d> ParlIoFullDuplex<'d, Blocking> { /// Create a new instance of [ParlIoFullDuplex] pub fn new( _parl_io: impl Peripheral

+ 'd, dma_channel: Channel<'d, DM, CH>, tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, ) -> Result where DM: Mode, CH: DmaChannelConvert<::Dma>, Channel<'d, Blocking, CH>: From>, { let dma_channel = Channel::::from(dma_channel); internal_init(frequency)?; Ok(Self { tx: TxCreatorFullDuplex { tx_channel: dma_channel.tx.degrade(), descriptors: tx_descriptors, }, rx: RxCreatorFullDuplex { rx_channel: dma_channel.rx.degrade(), descriptors: rx_descriptors, }, }) } /// Convert to an async version. pub fn into_async(self) -> ParlIoFullDuplex<'d, Async> { ParlIoFullDuplex { tx: TxCreatorFullDuplex { tx_channel: self.tx.tx_channel.into_async(), descriptors: self.tx.descriptors, }, rx: RxCreatorFullDuplex { rx_channel: self.rx.rx_channel.into_async(), descriptors: self.rx.descriptors, }, } } /// Sets the interrupt handler, enables it with /// [crate::interrupt::Priority::min()] /// /// Interrupts are not enabled at the peripheral level here. pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { internal_set_interrupt_handler(handler); } /// Listen for the given interrupts pub fn listen(&mut self, interrupts: impl Into>) { internal_listen(interrupts.into(), true); } /// Unlisten the given interrupts pub fn unlisten(&mut self, interrupts: impl Into>) { internal_listen(interrupts.into(), false); } /// Gets asserted interrupts pub fn interrupts(&mut self) -> EnumSet { internal_interrupts() } /// Resets asserted interrupts pub fn clear_interrupts(&mut self, interrupts: EnumSet) { internal_clear_interrupts(interrupts); } } impl crate::private::Sealed for ParlIoFullDuplex<'_, Blocking> {} impl InterruptConfigurable for ParlIoFullDuplex<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { ParlIoFullDuplex::set_interrupt_handler(self, handler); } } impl<'d> ParlIoFullDuplex<'d, Async> { /// Convert to a blocking version. pub fn into_blocking(self) -> ParlIoFullDuplex<'d, Blocking> { ParlIoFullDuplex { tx: TxCreatorFullDuplex { tx_channel: self.tx.tx_channel.into_blocking(), descriptors: self.tx.descriptors, }, rx: RxCreatorFullDuplex { rx_channel: self.rx.rx_channel.into_blocking(), descriptors: self.rx.descriptors, }, } } } /// Parallel IO in half duplex / TX only mode pub struct ParlIoTxOnly<'d, DM> where DM: Mode, { /// The transmitter (TX) channel responsible for handling DMA transfers in /// the parallel I/O operation. pub tx: TxCreator<'d, DM>, } impl<'d, DM> ParlIoTxOnly<'d, DM> where DM: Mode, { /// Create a new [ParlIoTxOnly] // TODO: only take a TX DMA channel? pub fn new( _parl_io: impl Peripheral

+ 'd, dma_channel: Channel<'d, DM, CH>, descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, ) -> Result where CH: DmaChannelConvert<::Dma>, { internal_init(frequency)?; Ok(Self { tx: TxCreator { tx_channel: dma_channel.tx.degrade(), descriptors, }, }) } } impl ParlIoTxOnly<'_, Blocking> { /// Sets the interrupt handler, enables it with /// [crate::interrupt::Priority::min()] /// /// Interrupts are not enabled at the peripheral level here. pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { internal_set_interrupt_handler(handler); } /// Listen for the given interrupts pub fn listen(&mut self, interrupts: impl Into>) { internal_listen(interrupts.into(), true); } /// Unlisten the given interrupts pub fn unlisten(&mut self, interrupts: impl Into>) { internal_listen(interrupts.into(), false); } /// Gets asserted interrupts pub fn interrupts(&mut self) -> EnumSet { internal_interrupts() } /// Resets asserted interrupts pub fn clear_interrupts(&mut self, interrupts: impl Into>) { internal_clear_interrupts(interrupts.into()); } } impl crate::private::Sealed for ParlIoTxOnly<'_, Blocking> {} impl InterruptConfigurable for ParlIoTxOnly<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { ParlIoTxOnly::set_interrupt_handler(self, handler); } } /// Parallel IO in half duplex / RX only mode pub struct ParlIoRxOnly<'d, DM> where DM: Mode, { /// The receiver (RX) channel responsible for handling DMA transfers in the /// parallel I/O operation. pub rx: RxCreator<'d, DM>, } impl<'d, DM> ParlIoRxOnly<'d, DM> where DM: Mode, { /// Create a new [ParlIoRxOnly] instance // TODO: only take a RX DMA channel? pub fn new( _parl_io: impl Peripheral

+ 'd, dma_channel: Channel<'d, DM, CH>, descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, ) -> Result where CH: DmaChannelConvert<::Dma>, { internal_init(frequency)?; Ok(Self { rx: RxCreator { rx_channel: dma_channel.rx.degrade(), descriptors, }, }) } } impl ParlIoRxOnly<'_, Blocking> { /// Sets the interrupt handler, enables it with /// [crate::interrupt::Priority::min()] /// /// Interrupts are not enabled at the peripheral level here. pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { internal_set_interrupt_handler(handler); } /// Listen for the given interrupts pub fn listen(&mut self, interrupts: impl Into>) { internal_listen(interrupts.into(), true); } /// Unlisten the given interrupts pub fn unlisten(&mut self, interrupts: impl Into>) { internal_listen(interrupts.into(), false); } /// Gets asserted interrupts pub fn interrupts(&mut self) -> EnumSet { internal_interrupts() } /// Resets asserted interrupts pub fn clear_interrupts(&mut self, interrupts: impl Into>) { internal_clear_interrupts(interrupts.into()); } } impl crate::private::Sealed for ParlIoRxOnly<'_, Blocking> {} impl InterruptConfigurable for ParlIoRxOnly<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { ParlIoRxOnly::set_interrupt_handler(self, handler); } } fn internal_init(frequency: HertzU32) -> Result<(), Error> { if frequency.raw() > 40_000_000 { return Err(Error::UnreachableClockRate); } PeripheralClockControl::reset(crate::system::Peripheral::ParlIo); PeripheralClockControl::enable(crate::system::Peripheral::ParlIo); let pcr = unsafe { &*crate::peripherals::PCR::PTR }; let divider = crate::soc::constants::PARL_IO_SCLK / frequency.raw(); if divider > 0xffff { return Err(Error::UnreachableClockRate); } let divider = divider as u16; pcr.parl_clk_tx_conf().modify(|_, w| unsafe { w.parl_clk_tx_en().set_bit(); w.parl_clk_tx_sel().bits(1); // PLL w.parl_clk_tx_div_num().bits(divider) }); pcr.parl_clk_rx_conf().modify(|_, w| unsafe { w.parl_clk_rx_en().set_bit(); w.parl_clk_rx_sel().bits(1); // PLL w.parl_clk_rx_div_num().bits(divider) }); Instance::set_rx_sw_en(true); Instance::set_rx_sample_mode(SampleMode::InternalSoftwareEnable); Ok(()) } impl ParlIoTx<'_, DM> where DM: Mode, { /// Perform a DMA write. /// /// This will return a [DmaTransferTx] /// /// The maximum amount of data to be sent is 32736 bytes. pub fn write_dma<'t, TXBUF>( &'t mut self, words: &'t TXBUF, ) -> Result, Error> where TXBUF: ReadBuffer, { let (ptr, len) = unsafe { words.read_buffer() }; if len > MAX_DMA_SIZE { return Err(Error::MaxDmaTransferSizeExceeded); } self.start_write_bytes_dma(ptr, len)?; Ok(DmaTransferTx::new(self)) } fn start_write_bytes_dma(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { let pcr = unsafe { &*crate::peripherals::PCR::PTR }; pcr.parl_clk_tx_conf() .modify(|_, w| w.parl_tx_rst_en().set_bit()); Instance::clear_tx_interrupts(); Instance::set_tx_bytes(len as u16); self.tx_channel.is_done(); unsafe { self.tx_chain.fill_for_tx(false, ptr, len)?; self.tx_channel .prepare_transfer_without_start(DmaPeripheral::ParlIo, &self.tx_chain) .and_then(|_| self.tx_channel.start_transfer())?; } while !Instance::is_tx_ready() {} Instance::set_tx_start(true); pcr.parl_clk_tx_conf() .modify(|_, w| w.parl_tx_rst_en().clear_bit()); Ok(()) } } impl DmaSupport for ParlIoTx<'_, DM> where DM: Mode, { fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { while !Instance::is_tx_eof() {} Instance::set_tx_start(false); } fn peripheral_dma_stop(&mut self) { unreachable!("unsupported") } } impl<'d, DM> DmaSupportTx for ParlIoTx<'d, DM> where DM: Mode, { type TX = ChannelTx<'d, DM, ::Dma>; fn tx(&mut self) -> &mut Self::TX { &mut self.tx_channel } fn chain(&mut self) -> &mut DescriptorChain { &mut self.tx_chain } } impl<'d, DM> ParlIoRx<'d, DM> where DM: Mode, { /// Perform a DMA read. /// /// This will return a [DmaTransferRx] /// /// The maximum amount of data is 32736 bytes when using [EofMode::ByteLen]. /// /// It's only limited by the size of the DMA buffer when using /// [EofMode::EnableSignal]. pub fn read_dma<'t, RXBUF>( &'t mut self, words: &'t mut RXBUF, ) -> Result, Error> where RXBUF: WriteBuffer, { let (ptr, len) = unsafe { words.write_buffer() }; if !Instance::is_suc_eof_generated_externally() && len > MAX_DMA_SIZE { return Err(Error::MaxDmaTransferSizeExceeded); } Self::start_receive_bytes_dma(&mut self.rx_channel, &mut self.rx_chain, ptr, len)?; Ok(DmaTransferRx::new(self)) } fn start_receive_bytes_dma( rx_channel: &mut ChannelRx<'d, DM, ::Dma>, rx_chain: &mut DescriptorChain, ptr: *mut u8, len: usize, ) -> Result<(), Error> { let pcr = unsafe { &*crate::peripherals::PCR::PTR }; pcr.parl_clk_rx_conf() .modify(|_, w| w.parl_rx_rst_en().set_bit()); pcr.parl_clk_rx_conf() .modify(|_, w| w.parl_rx_rst_en().clear_bit()); Instance::clear_rx_interrupts(); Instance::set_rx_bytes(len as u16); unsafe { rx_chain.fill_for_rx(false, ptr, len)?; rx_channel .prepare_transfer_without_start(DmaPeripheral::ParlIo, rx_chain) .and_then(|_| rx_channel.start_transfer())?; } Instance::set_rx_reg_update(); Instance::set_rx_start(true); Ok(()) } } impl DmaSupport for ParlIoRx<'_, DM> where DM: Mode, { fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { loop { if self.rx_channel.is_done() || self.rx_channel.has_eof_error() || self.rx_channel.has_dscr_empty_error() { break; } } Instance::set_rx_start(false); } fn peripheral_dma_stop(&mut self) { unreachable!("unsupported") } } impl<'d, DM> DmaSupportRx for ParlIoRx<'d, DM> where DM: Mode, { type RX = ChannelRx<'d, DM, ::Dma>; fn rx(&mut self) -> &mut Self::RX { &mut self.rx_channel } fn chain(&mut self) -> &mut DescriptorChain { &mut self.rx_chain } } /// Creates a TX channel pub struct TxCreator<'d, DM> where DM: Mode, { tx_channel: ChannelTx<'d, DM, ::Dma>, descriptors: &'static mut [DmaDescriptor], } /// Creates a RX channel pub struct RxCreator<'d, DM> where DM: Mode, { rx_channel: ChannelRx<'d, DM, ::Dma>, descriptors: &'static mut [DmaDescriptor], } /// Creates a TX channel pub struct TxCreatorFullDuplex<'d, DM> where DM: Mode, { tx_channel: ChannelTx<'d, DM, ::Dma>, descriptors: &'static mut [DmaDescriptor], } /// Creates a RX channel pub struct RxCreatorFullDuplex<'d, DM> where DM: Mode, { rx_channel: ChannelRx<'d, DM, ::Dma>, descriptors: &'static mut [DmaDescriptor], } #[doc(hidden)] pub mod asynch { use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; use procmacros::handler; use super::{private::Instance, Error, ParlIoRx, ParlIoTx, MAX_DMA_SIZE}; use crate::{ dma::{asynch::DmaRxFuture, ReadBuffer, WriteBuffer}, peripherals::Interrupt, }; static TX_WAKER: AtomicWaker = AtomicWaker::new(); #[must_use = "futures do nothing unless you `.await` or poll them"] struct TxDoneFuture {} impl TxDoneFuture { pub fn new() -> Self { Instance::listen_tx_done(); Self {} } } impl core::future::Future for TxDoneFuture { type Output = (); fn poll( self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> Poll { let mut parl_io = unsafe { crate::peripherals::PARL_IO::steal() }; #[cfg(esp32c6)] { parl_io.bind_parl_io_interrupt(interrupt_handler.handler()); crate::interrupt::enable(Interrupt::PARL_IO, interrupt_handler.priority()).unwrap(); } #[cfg(esp32h2)] { parl_io.bind_parl_io_tx_interrupt(interrupt_handler.handler()); crate::interrupt::enable(Interrupt::PARL_IO_TX, interrupt_handler.priority()) .unwrap(); } TX_WAKER.register(cx.waker()); if Instance::is_listening_tx_done() { Poll::Pending } else { Poll::Ready(()) } } } #[handler] fn interrupt_handler() { if Instance::is_tx_done_set() { Instance::clear_is_tx_done(); Instance::unlisten_tx_done(); TX_WAKER.wake() } } impl ParlIoTx<'_, crate::Async> { /// Perform a DMA write. /// /// The maximum amount of data to be sent is 32736 bytes. pub async fn write_dma_async(&mut self, words: &TXBUF) -> Result<(), Error> where TXBUF: ReadBuffer, { let (ptr, len) = unsafe { words.read_buffer() }; if len > MAX_DMA_SIZE { return Err(Error::MaxDmaTransferSizeExceeded); } let future = TxDoneFuture::new(); self.start_write_bytes_dma(ptr, len)?; future.await; Ok(()) } } impl ParlIoRx<'_, crate::Async> { /// Perform a DMA write. /// /// The maximum amount of data to be sent is 32736 bytes. pub async fn read_dma_async<'t, RXBUF>( &'t mut self, words: &'t mut RXBUF, ) -> Result<(), Error> where RXBUF: WriteBuffer, { let (ptr, len) = unsafe { words.write_buffer() }; if !Instance::is_suc_eof_generated_externally() && len > MAX_DMA_SIZE { return Err(Error::MaxDmaTransferSizeExceeded); } let future = DmaRxFuture::new(&mut self.rx_channel); Self::start_receive_bytes_dma(future.rx, &mut self.rx_chain, ptr, len)?; future.await?; Ok(()) } } } mod private { use super::{BitPackOrder, EofMode, Error, SampleEdge}; pub trait FullDuplex {} pub trait NotContainsValidSignalPin {} pub trait ContainsValidSignalPin {} pub trait TxPins {} pub trait RxPins {} pub trait TxClkPin { fn configure(&mut self); } pub trait RxClkPin { fn configure(&mut self); } pub trait ConfigurePins { fn configure(&mut self) -> Result<(), Error>; } #[cfg(esp32c6)] pub(super) enum WidSel { Bits16 = 0, Bits8 = 1, Bits4 = 2, Bits2 = 3, Bits1 = 4, } #[cfg(esp32h2)] pub(super) enum WidSel { Bits8 = 3, Bits4 = 2, Bits2 = 1, Bits1 = 0, } pub(super) enum SampleMode { ExternalLevel = 0, ExternalPulse = 1, InternalSoftwareEnable = 2, } pub(super) struct Instance; #[cfg(esp32c6)] impl Instance { pub fn set_tx_bit_width(width: WidSel) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_cfg0() .modify(|_, w| unsafe { w.tx_bus_wid_sel().bits(width as u8) }); } pub fn set_tx_idle_value(value: u16) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_cfg1() .modify(|_, w| unsafe { w.tx_idle_value().bits(value) }); } pub fn set_tx_sample_edge(value: SampleEdge) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_cfg0() .modify(|_, w| w.tx_smp_edge_sel().bit(value as u8 == 1)); } pub fn set_tx_bit_order(value: BitPackOrder) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_cfg0() .modify(|_, w| w.tx_bit_unpack_order().bit(value as u8 == 1)); } pub fn clear_tx_interrupts() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_clr().write(|w| { w.tx_fifo_rempty() .clear_bit_by_one() .tx_eof() .clear_bit_by_one() }); } pub fn set_tx_bytes(len: u16) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_cfg0() .modify(|_, w| unsafe { w.tx_bytelen().bits(len) }); } pub fn is_tx_ready() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.st().read().tx_ready().bit_is_set() } pub fn set_tx_start(value: bool) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.tx_cfg0().modify(|_, w| w.tx_start().bit(value)); } pub fn is_tx_eof() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_raw().read().tx_eof().bit_is_set() } pub fn tx_valid_pin_signal() -> crate::gpio::OutputSignal { crate::gpio::OutputSignal::PARL_TX_DATA15 } pub fn set_tx_hw_valid_en(value: bool) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_cfg0() .modify(|_, w| w.tx_hw_valid_en().bit(value)); } pub fn set_rx_bit_width(width: WidSel) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg0() .modify(|_, w| unsafe { w.rx_bus_wid_sel().bits(width as u8) }); } pub fn rx_valid_pin_signal() -> crate::gpio::InputSignal { crate::gpio::InputSignal::PARL_RX_DATA15 } pub fn set_rx_sw_en(value: bool) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.rx_cfg0().modify(|_, w| w.rx_sw_en().bit(value)); } pub fn clear_rx_interrupts() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .int_clr() .write(|w| w.rx_fifo_wovf().clear_bit_by_one()); } pub fn set_rx_bytes(len: u16) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg0() .modify(|_, w| unsafe { w.rx_data_bytelen().bits(len) }); } pub fn set_rx_sample_mode(sample_mode: SampleMode) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg0() .modify(|_, w| unsafe { w.rx_smp_mode_sel().bits(sample_mode as u8) }); } pub fn set_eof_gen_sel(mode: EofMode) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg0() .modify(|_, w| w.rx_eof_gen_sel().bit(mode == EofMode::EnableSignal)); } pub fn set_rx_pulse_submode_sel(sel: u8) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg0() .modify(|_, w| unsafe { w.rx_pulse_submode_sel().bits(sel) }); } pub fn set_rx_level_submode_sel(sel: u8) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg0() .modify(|_, w| w.rx_level_submode_sel().bit(sel == 1)); } pub fn set_rx_clk_edge_sel(edge: SampleEdge) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg0() .modify(|_, w| w.rx_clk_edge_sel().bit(edge as u8 == 1)); } pub fn set_rx_start(value: bool) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.rx_cfg0().modify(|_, w| w.rx_start().bit(value)); } pub fn set_rx_reg_update() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg1() .modify(|_, w| w.rx_reg_update().bit(true)); } pub fn set_rx_bit_order(value: BitPackOrder) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_cfg0() .modify(|_, w| w.rx_bit_pack_order().bit(value as u8 == 1)); } pub fn set_rx_timeout_ticks(value: Option) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.rx_cfg1().modify(|_, w| unsafe { w.rx_timeout_en() .bit(value.is_some()) .rx_timeout_threshold() .bits(value.unwrap_or(0xfff)) }); } pub fn is_suc_eof_generated_externally() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.rx_cfg0().read().rx_eof_gen_sel().bit_is_set() } pub fn listen_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_ena().modify(|_, w| w.tx_eof().set_bit()); } pub fn unlisten_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_ena().modify(|_, w| w.tx_eof().clear_bit()); } pub fn is_listening_tx_done() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_ena().read().tx_eof().bit() } pub fn is_tx_done_set() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_raw().read().tx_eof().bit() } pub fn clear_is_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_clr().write(|w| w.tx_eof().clear_bit_by_one()); } } #[cfg(esp32h2)] impl Instance { pub fn set_tx_bit_width(width: WidSel) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_data_cfg() .modify(|_, w| unsafe { w.tx_bus_wid_sel().bits(width as u8) }); } pub fn set_tx_idle_value(value: u16) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_genrl_cfg() .modify(|_, w| unsafe { w.tx_idle_value().bits(value) }); } pub fn set_tx_sample_edge(value: SampleEdge) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.tx_clk_cfg().modify(|_, w| { w.tx_clk_i_inv() .bit(value == SampleEdge::Invert) .tx_clk_o_inv() .bit(value == SampleEdge::Invert) }); } pub fn set_tx_bit_order(value: BitPackOrder) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_data_cfg() .modify(|_, w| w.tx_data_order_inv().bit(value as u8 == 1)); } pub fn clear_tx_interrupts() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_clr().write(|w| { w.tx_fifo_rempty() .clear_bit_by_one() .tx_eof() .clear_bit_by_one() }); } pub fn set_tx_bytes(len: u16) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_data_cfg() .modify(|_, w| unsafe { w.tx_bitlen().bits((len as u32) * 8) }); } pub fn is_tx_ready() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.st().read().tx_ready().bit_is_set() } pub fn set_tx_start(value: bool) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_start_cfg() .modify(|_, w| w.tx_start().bit(value)); } pub fn is_tx_eof() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_raw().read().tx_eof().bit_is_set() } pub fn tx_valid_pin_signal() -> crate::gpio::OutputSignal { crate::gpio::OutputSignal::PARL_TX_DATA7 } pub fn set_tx_hw_valid_en(value: bool) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .tx_genrl_cfg() .modify(|_, w| w.tx_valid_output_en().bit(value)); } pub fn set_rx_bit_width(width: WidSel) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_data_cfg() .modify(|_, w| unsafe { w.rx_bus_wid_sel().bits(width as u8) }); } pub fn rx_valid_pin_signal() -> crate::gpio::InputSignal { crate::gpio::InputSignal::PARL_RX_DATA7 } pub fn set_rx_sw_en(value: bool) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_mode_cfg() .modify(|_, w| w.rx_sw_en().bit(value)); } pub fn clear_rx_interrupts() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .int_clr() .write(|w| w.rx_fifo_wovf().clear_bit_by_one()); } pub fn set_rx_bytes(len: u16) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_data_cfg() .modify(|_, w| unsafe { w.rx_bitlen().bits((len as u32) * 8) }); } pub fn set_rx_sample_mode(sample_mode: SampleMode) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_mode_cfg() .modify(|_, w| unsafe { w.rx_smp_mode_sel().bits(sample_mode as u8) }); } pub fn set_eof_gen_sel(mode: EofMode) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_genrl_cfg() .modify(|_, w| w.rx_eof_gen_sel().bit(mode == EofMode::EnableSignal)); } pub fn set_rx_pulse_submode_sel(sel: u8) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_mode_cfg() .modify(|_, w| unsafe { w.rx_pulse_submode_sel().bits(sel) }); } pub fn set_rx_level_submode_sel(_sel: u8) { // unsupported, always high } pub fn set_rx_clk_edge_sel(value: SampleEdge) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.rx_clk_cfg().modify(|_, w| { w.rx_clk_i_inv() .bit(value == SampleEdge::Invert) .rx_clk_o_inv() .bit(value == SampleEdge::Invert) }); } pub fn set_rx_start(value: bool) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_start_cfg() .modify(|_, w| w.rx_start().bit(value)); } pub fn set_rx_reg_update() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .reg_update() .write(|w| w.rx_reg_update().bit(true)); } pub fn set_rx_bit_order(value: BitPackOrder) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_data_cfg() .modify(|_, w| w.rx_data_order_inv().bit(value as u8 == 1)); } pub fn set_rx_timeout_ticks(value: Option) { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.rx_genrl_cfg().modify(|_, w| unsafe { w.rx_timeout_en() .bit(value.is_some()) .rx_timeout_thres() .bits(value.unwrap_or(0xfff)) }); } pub fn is_suc_eof_generated_externally() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block .rx_genrl_cfg() .read() .rx_eof_gen_sel() .bit_is_set() } pub fn listen_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_ena().modify(|_, w| w.tx_eof().set_bit()); } pub fn unlisten_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_ena().modify(|_, w| w.tx_eof().clear_bit()); } pub fn is_listening_tx_done() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_ena().read().tx_eof().bit() } pub fn is_tx_done_set() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_raw().read().tx_eof().bit() } pub fn clear_is_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; reg_block.int_clr().write(|w| w.tx_eof().clear_bit_by_one()); } } }