esp-hal/esp-hal-common/src/parl_io.rs
Jesse Braham 0064766ef2
No longer publicly expose the PeripheralClockControl struct (#817)
* No longer publicly expose the `PeripheralClockControl` struct

* Update examples as needed to get things building again

* Update CHANGELOG.md

* Address review feedback, fix a warning

* Use a critical section for all devices other than the ESP32-C6/H2, as they modify multiple registers

* Rebase and update `etm` driver to fix build errors
2023-09-26 09:07:57 -07:00

2129 lines
59 KiB
Rust

//! # Parallel 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.
//!
//! The driver uses DMA (Direct Memory Access) for efficient data transfer.
//!
//! ## Examples
//!
//! ### Initialization for TX
//! ```no_run
//! // configure the data pins to use
//! let tx_pins = TxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4);
//!
//! // configure the valid pin which will be driven high during a TX transfer
//! let pin_conf = TxPinConfigWithValidPin::new(tx_pins, io.pins.gpio5);
//!
//! let mut parl_io = ParlIoTxOnly::new(
//! peripherals.PARL_IO,
//! dma_channel.configure(
//! false,
//! &mut tx_descriptors,
//! &mut rx_descriptors,
//! DmaPriority::Priority0,
//! ),
//! 1u32.MHz(),
//! &clocks,
//! )
//! .unwrap();
//!
//! // configure a pin for the clock signal
//! let cp = ClkOutPin::new(io.pins.gpio6);
//!
//! let mut parl_io_tx = parl_io
//! .tx
//! .with_config(pin_conf, cp, 0, SampleEdge::Normal, BitPackOrder::Msb)
//! .unwrap();
//! ```
//!
//! ### Start TX transfer
//! ```no_run
//! let transfer = parl_io_tx.write_dma(buffer).unwrap();
//!
//! // the buffer and driver is moved into the transfer and we can get it back via
//! // `wait`
//! (buffer, parl_io_tx) = transfer.wait().unwrap();
//! ```
//!
//! ### Initialization for RX
//! ```no_run
//! let rx_pins = RxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4);
//!
//! let parl_io = ParlIoRxOnly::new(
//! peripherals.PARL_IO,
//! dma_channel.configure(
//! false,
//! &mut tx_descriptors,
//! &mut rx_descriptors,
//! DmaPriority::Priority0,
//! ),
//! 1u32.MHz(),
//! &clocks,
//! )
//! .unwrap();
//!
//! let mut parl_io_rx = parl_io
//! .rx
//! .with_config(rx_pins, NoClkPin, BitPackOrder::Msb, Some(0xfff))
//! .unwrap();
//! ```
//!
//! ### Start RX transfer
//! ```no_run
//! let transfer = parl_io_rx.read_dma(buffer).unwrap();
//!
//! // the buffer and driver is moved into the transfer and we can get it back via
//! // `wait`
//! (buffer, parl_io_rx) = transfer.wait().unwrap();
//! ```
use core::mem;
use embedded_dma::{ReadBuffer, WriteBuffer};
use fugit::HertzU32;
use peripheral::PeripheralRef;
use private::*;
use crate::{
clock::Clocks,
dma::{Channel, ChannelTypes, DmaError, DmaPeripheral, ParlIoPeripheral, RxPrivate, TxPrivate},
gpio::{InputPin, OutputPin},
peripheral::{self, Peripheral},
peripherals,
system::PeripheralClockControl,
};
#[allow(unused)]
const MAX_DMA_SIZE: usize = 32736;
/// 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<DmaError> 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<u8> {
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<u8> {
match self {
EnableMode::HighLevel => Some(0),
EnableMode::LowLevel => Some(1),
_ => None,
}
}
fn smp_model_sel(&self) -> Option<self::private::SampleMode> {
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<u8> {
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<u8> {
match self {
EnableMode::HighLevel => Some(0),
_ => None,
}
}
fn smp_model_sel(&self) -> Option<self::private::SampleMode> {
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
pub struct NoClkPin;
impl TxClkPin for NoClkPin {
fn configure(&mut self) {
// nothing
}
}
impl RxClkPin for NoClkPin {
fn configure(&mut self) {
// nothing
}
}
/// Wraps a GPIO pin which will be used as the clock output signal
pub struct ClkOutPin<'d, P>
where
P: OutputPin,
{
pin: PeripheralRef<'d, P>,
}
impl<'d, P> ClkOutPin<'d, P>
where
P: OutputPin,
{
pub fn new(pin: impl Peripheral<P = P> + 'd) -> Self {
crate::into_ref!(pin);
Self { pin }
}
}
impl<'d, P> TxClkPin for ClkOutPin<'d, P>
where
P: OutputPin,
{
fn configure(&mut self) {
self.pin
.set_to_push_pull_output()
.connect_peripheral_to_output(crate::gpio::OutputSignal::PARL_TX_CLK);
}
}
/// Wraps a GPIO pin which will be used as the TX clock input signal
pub struct ClkInPin<'d, P>
where
P: InputPin,
{
pin: PeripheralRef<'d, P>,
}
impl<'d, P> ClkInPin<'d, P>
where
P: InputPin,
{
pub fn new(pin: impl Peripheral<P = P> + 'd) -> Self {
crate::into_ref!(pin);
Self { pin }
}
}
impl<'d, P> TxClkPin for ClkInPin<'d, P>
where
P: InputPin,
{
fn configure(&mut self) {
let pcr = unsafe { &*crate::peripherals::PCR::PTR };
pcr.parl_clk_tx_conf.modify(|_, w| {
w.parl_clk_tx_sel()
.variant(3)
.parl_clk_tx_div_num()
.variant(0)
}); // PAD_CLK_TX, no divider
self.pin
.set_to_input()
.connect_input_to_peripheral(crate::gpio::InputSignal::PARL_TX_CLK);
}
}
/// Wraps a GPIO pin which will be used as the RX clock input signal
pub struct RxClkInPin<'d, P>
where
P: InputPin,
{
pin: PeripheralRef<'d, P>,
sample_edge: SampleEdge,
}
impl<'d, P> RxClkInPin<'d, P>
where
P: InputPin,
{
pub fn new(pin: impl Peripheral<P = P> + 'd, sample_edge: SampleEdge) -> Self {
crate::into_ref!(pin);
Self { pin, sample_edge }
}
}
impl<'d, P> RxClkPin for RxClkInPin<'d, P>
where
P: InputPin,
{
fn configure(&mut self) {
let pcr = unsafe { &*crate::peripherals::PCR::PTR };
pcr.parl_clk_rx_conf.modify(|_, w| {
w.parl_clk_rx_sel()
.variant(3)
.parl_clk_rx_div_num()
.variant(0)
}); // PAD_CLK_TX, no divider
self.pin
.set_to_input()
.connect_input_to_peripheral(crate::gpio::InputSignal::PARL_RX_CLK);
Instance::set_rx_clk_edge_sel(self.sample_edge);
}
}
/// Pin configuration with an additional pin for the valid signal.
pub struct TxPinConfigWithValidPin<'d, P, VP>
where
P: NotContainsValidSignalPin + TxPins + ConfigurePins,
VP: OutputPin,
{
tx_pins: P,
valid_pin: PeripheralRef<'d, VP>,
}
impl<'d, P, VP> TxPinConfigWithValidPin<'d, P, VP>
where
P: NotContainsValidSignalPin + TxPins + ConfigurePins,
VP: OutputPin,
{
pub fn new(tx_pins: P, valid_pin: impl Peripheral<P = VP> + 'd) -> Self {
crate::into_ref!(valid_pin);
Self { tx_pins, valid_pin }
}
}
impl<'d, P, VP> TxPins for TxPinConfigWithValidPin<'d, P, VP>
where
P: NotContainsValidSignalPin + TxPins + ConfigurePins,
VP: OutputPin,
{
}
impl<'d, P, VP> ConfigurePins for TxPinConfigWithValidPin<'d, P, VP>
where
P: NotContainsValidSignalPin + TxPins + ConfigurePins,
VP: OutputPin,
{
fn configure(&mut self) -> Result<(), Error> {
self.tx_pins.configure()?;
self.valid_pin
.set_to_push_pull_output()
.connect_peripheral_to_output(Instance::tx_valid_pin_signal());
Instance::set_tx_hw_valid_en(true);
Ok(())
}
}
/// Pin configuration where the pin for the valid signal is the MSB pin.
pub struct TxPinConfigIncludingValidPin<P>
where
P: ContainsValidSignalPin + TxPins + ConfigurePins,
{
tx_pins: P,
}
impl<P> TxPinConfigIncludingValidPin<P>
where
P: ContainsValidSignalPin + TxPins + ConfigurePins,
{
pub fn new(tx_pins: P) -> Self {
Self { tx_pins }
}
}
impl<P> TxPins for TxPinConfigIncludingValidPin<P> where
P: ContainsValidSignalPin + TxPins + ConfigurePins
{
}
impl<P> ConfigurePins for TxPinConfigIncludingValidPin<P>
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_ $pin:lower >] : PeripheralRef<'d, $pin>,
)+
}
impl<'d, $($pin),+> $name<'d, $($pin),+>
where
$($pin: OutputPin),+
{
pub fn new(
$(
[< pin_ $pin:lower >] : impl Peripheral<P = $pin> + 'd,
)+
) -> Self {
crate::into_ref!($( [< pin_ $pin:lower >] ),+);
Self { $( [< pin_ $pin:lower >] ),+ }
}
}
impl<'d, $($pin),+> ConfigurePins for $name<'d, $($pin),+>
where
$($pin: OutputPin),+
{
fn configure(&mut self) -> Result<(), Error>{
$(
self.[< pin_ $pin:lower >]
.set_to_push_pull_output()
.connect_peripheral_to_output(crate::gpio::OutputSignal::$signal);
)+
private::Instance::set_tx_bit_width( private::WidSel::[< Bits $width >]);
Ok(())
}
}
impl<'d, $($pin),+> TxPins for $name<'d, $($pin),+> {}
}
};
}
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<'d, P0> FullDuplex for TxOneBit<'d, P0> {}
impl<'d, P0, P1> FullDuplex for TxTwoBits<'d, P0, P1> {}
impl<'d, P0, P1, P2, P3> FullDuplex for TxFourBits<'d, P0, P1, P2, P3> {}
impl<'d, P0, P1, P2, P3, P4, P5, P6, P7> FullDuplex
for TxEightBits<'d, P0, P1, P2, P3, P4, P5, P6, P7>
{
}
impl<'d, P0> NotContainsValidSignalPin for TxOneBit<'d, P0> {}
impl<'d, P0, P1> NotContainsValidSignalPin for TxTwoBits<'d, P0, P1> {}
impl<'d, P0, P1, P2, P3> NotContainsValidSignalPin for TxFourBits<'d, P0, P1, P2, P3> {}
#[cfg(esp32c6)]
impl<'d, P0, P1, P2, P3, P4, P5, P6, P7> NotContainsValidSignalPin
for TxEightBits<'d, P0, P1, P2, P3, P4, P5, P6, P7>
{
}
#[cfg(esp32h2)]
impl<'d, P0, P1, P2, P3, P4, P5, P6, P7> ContainsValidSignalPin
for TxEightBits<'d, P0, P1, P2, P3, P4, P5, P6, P7>
{
}
#[cfg(esp32c6)]
impl<'d, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15>
ContainsValidSignalPin
for TxSixteenBits<'d, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15>
{
}
/// Pin configuration with an additional pin for the valid signal.
pub struct RxPinConfigWithValidPin<'d, P, VP>
where
P: NotContainsValidSignalPin + RxPins + ConfigurePins,
VP: InputPin,
{
rx_pins: P,
valid_pin: PeripheralRef<'d, VP>,
enable_mode: EnableMode,
eof_mode: EofMode,
}
impl<'d, P, VP> RxPinConfigWithValidPin<'d, P, VP>
where
P: NotContainsValidSignalPin + RxPins + ConfigurePins,
VP: InputPin,
{
pub fn new(
rx_pins: P,
valid_pin: impl Peripheral<P = VP> + 'd,
enable_mode: EnableMode,
eof_mode: EofMode,
) -> Self {
crate::into_ref!(valid_pin);
Self {
rx_pins,
valid_pin,
enable_mode,
eof_mode,
}
}
}
impl<'d, P, VP> RxPins for RxPinConfigWithValidPin<'d, P, VP>
where
P: NotContainsValidSignalPin + RxPins + ConfigurePins,
VP: InputPin,
{
}
impl<'d, P, VP> ConfigurePins for RxPinConfigWithValidPin<'d, P, VP>
where
P: NotContainsValidSignalPin + RxPins + ConfigurePins,
VP: InputPin,
{
fn configure(&mut self) -> Result<(), Error> {
self.rx_pins.configure()?;
self.valid_pin
.set_to_input()
.connect_input_to_peripheral(Instance::rx_valid_pin_signal());
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<P>
where
P: ContainsValidSignalPin + RxPins + ConfigurePins,
{
rx_pins: P,
enable_mode: EnableMode,
eof_mode: EofMode,
}
impl<P> RxPinConfigIncludingValidPin<P>
where
P: ContainsValidSignalPin + RxPins + ConfigurePins,
{
pub fn new(rx_pins: P, enable_mode: EnableMode, eof_mode: EofMode) -> Self {
Self {
rx_pins,
enable_mode,
eof_mode,
}
}
}
impl<P> RxPins for RxPinConfigIncludingValidPin<P> where
P: ContainsValidSignalPin + RxPins + ConfigurePins
{
}
impl<P> ConfigurePins for RxPinConfigIncludingValidPin<P>
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_ $pin:lower >] : PeripheralRef<'d, $pin>,
)+
}
impl<'d, $($pin),+> $name<'d, $($pin),+>
where
$($pin: InputPin),+
{
pub fn new(
$(
[< pin_ $pin:lower >] : impl Peripheral<P = $pin> + 'd,
)+
) -> Self {
crate::into_ref!($( [< pin_ $pin:lower >] ),+);
Self { $( [< pin_ $pin:lower >] ),+ }
}
}
impl<'d, $($pin),+> ConfigurePins for $name<'d, $($pin),+>
where
$($pin: InputPin),+
{
fn configure(&mut self) -> Result<(), Error> {
$(
self.[< pin_ $pin:lower >]
.set_to_input()
.connect_input_to_peripheral(crate::gpio::InputSignal::$signal);
)+
private::Instance::set_rx_bit_width( private::WidSel::[< Bits $width >]);
Ok(())
}
}
impl<'d, $($pin),+> RxPins for $name<'d, $($pin),+> {}
}
};
}
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<'d, P0> FullDuplex for RxOneBit<'d, P0> {}
impl<'d, P0, P1> FullDuplex for RxTwoBits<'d, P0, P1> {}
impl<'d, P0, P1, P2, P3> FullDuplex for RxFourBits<'d, P0, P1, P2, P3> {}
impl<'d, P0, P1, P2, P3, P4, P5, P6, P7> FullDuplex
for RxEightBits<'d, P0, P1, P2, P3, P4, P5, P6, P7>
{
}
impl<'d, P0> NotContainsValidSignalPin for RxOneBit<'d, P0> {}
impl<'d, P0, P1> NotContainsValidSignalPin for RxTwoBits<'d, P0, P1> {}
impl<'d, P0, P1, P2, P3> NotContainsValidSignalPin for RxFourBits<'d, P0, P1, P2, P3> {}
#[cfg(esp32c6)]
impl<'d, P0, P1, P2, P3, P4, P5, P6, P7> NotContainsValidSignalPin
for RxEightBits<'d, P0, P1, P2, P3, P4, P5, P6, P7>
{
}
#[cfg(esp32h2)]
impl<'d, P0, P1, P2, P3, P4, P5, P6, P7> ContainsValidSignalPin
for RxEightBits<'d, P0, P1, P2, P3, P4, P5, P6, P7>
{
}
#[cfg(esp32c6)]
impl<'d, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15>
ContainsValidSignalPin
for RxSixteenBits<'d, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15>
{
}
trait RegisterAccess {}
impl<'d, CH> TxCreatorFullDuplex<'d, CH>
where
CH: ChannelTypes,
{
pub fn with_config<P, CP>(
self,
mut tx_pins: P,
mut clk_pin: CP,
idle_value: u16,
sample_edge: SampleEdge,
bit_order: BitPackOrder,
) -> Result<ParlIoTx<'d, CH, P, CP>, 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,
_pins: tx_pins,
_clk_pin: clk_pin,
})
}
}
impl<'d, CH> TxCreator<'d, CH>
where
CH: ChannelTypes,
{
pub fn with_config<P, CP>(
self,
mut tx_pins: P,
mut clk_pin: CP,
idle_value: u16,
sample_edge: SampleEdge,
bit_order: BitPackOrder,
) -> Result<ParlIoTx<'d, CH, P, CP>, 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,
_pins: tx_pins,
_clk_pin: clk_pin,
})
}
}
/// Parallel IO TX channel
pub struct ParlIoTx<'d, CH, P, CP>
where
CH: ChannelTypes,
P: TxPins + ConfigurePins,
CP: TxClkPin,
{
tx_channel: CH::Tx<'d>,
_pins: P,
_clk_pin: CP,
}
impl<'d, CH, P, CP> core::fmt::Debug for ParlIoTx<'d, CH, P, CP>
where
CH: ChannelTypes,
P: TxPins + ConfigurePins,
CP: TxClkPin,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ParlIoTx").finish()
}
}
impl<'d, CH> RxCreatorFullDuplex<'d, CH>
where
CH: ChannelTypes,
{
pub fn with_config<P, CP>(
self,
mut rx_pins: P,
mut clk_pin: CP,
bit_order: BitPackOrder,
timeout_ticks: Option<u16>,
) -> Result<ParlIoRx<'d, CH, P, CP>, 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,
_pins: rx_pins,
_clk_pin: clk_pin,
})
}
}
impl<'d, CH> RxCreator<'d, CH>
where
CH: ChannelTypes,
{
pub fn with_config<P, CP>(
self,
mut rx_pins: P,
mut clk_pin: CP,
bit_order: BitPackOrder,
timeout_ticks: Option<u16>,
) -> Result<ParlIoRx<'d, CH, P, CP>, 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,
_pins: rx_pins,
_clk_pin: clk_pin,
})
}
}
/// Parallel IO RX channel
pub struct ParlIoRx<'d, CH, P, CP>
where
CH: ChannelTypes,
P: RxPins + ConfigurePins,
CP: RxClkPin,
{
rx_channel: CH::Rx<'d>,
_pins: P,
_clk_pin: CP,
}
impl<'d, CH, P, CP> core::fmt::Debug for ParlIoRx<'d, CH, P, CP>
where
CH: ChannelTypes,
P: RxPins + ConfigurePins,
CP: RxClkPin,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ParlIoTx").finish()
}
}
/// Parallel IO in full duplex mode
///
/// Full duplex mode might limit the maximum possible bit width.
pub struct ParlIoFullDuplex<'d, CH>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
{
_parl_io: PeripheralRef<'d, peripherals::PARL_IO>,
pub tx: TxCreatorFullDuplex<'d, CH>,
pub rx: RxCreatorFullDuplex<'d, CH>,
}
impl<'d, CH> ParlIoFullDuplex<'d, CH>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
{
pub fn new(
parl_io: impl Peripheral<P = peripherals::PARL_IO> + 'd,
mut dma_channel: Channel<'d, CH>,
frequency: HertzU32,
_clocks: &Clocks,
) -> Result<Self, Error> {
crate::into_ref!(parl_io);
internal_init(&mut dma_channel, frequency)?;
Ok(Self {
_parl_io: parl_io,
tx: TxCreatorFullDuplex {
tx_channel: dma_channel.tx,
},
rx: RxCreatorFullDuplex {
rx_channel: dma_channel.rx,
},
})
}
}
/// Parallel IO in half duplex / TX only mode
pub struct ParlIoTxOnly<'d, CH>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
{
_parl_io: PeripheralRef<'d, peripherals::PARL_IO>,
pub tx: TxCreator<'d, CH>,
}
impl<'d, CH> ParlIoTxOnly<'d, CH>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
{
pub fn new(
parl_io: impl Peripheral<P = peripherals::PARL_IO> + 'd,
mut dma_channel: Channel<'d, CH>,
frequency: HertzU32,
_clocks: &Clocks,
) -> Result<Self, Error> {
crate::into_ref!(parl_io);
internal_init(&mut dma_channel, frequency)?;
Ok(Self {
_parl_io: parl_io,
tx: TxCreator {
tx_channel: dma_channel.tx,
},
})
}
}
/// Parallel IO in half duplex / RX only mode
pub struct ParlIoRxOnly<'d, CH>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
{
_parl_io: PeripheralRef<'d, peripherals::PARL_IO>,
pub rx: RxCreator<'d, CH>,
}
impl<'d, CH> ParlIoRxOnly<'d, CH>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
{
pub fn new(
parl_io: impl Peripheral<P = peripherals::PARL_IO> + 'd,
mut dma_channel: Channel<'d, CH>,
frequency: HertzU32,
_clocks: &Clocks,
) -> Result<Self, Error> {
crate::into_ref!(parl_io);
internal_init(&mut dma_channel, frequency)?;
Ok(Self {
_parl_io: parl_io,
rx: RxCreator {
rx_channel: dma_channel.rx,
},
})
}
}
fn internal_init<'d, CH>(
dma_channel: &mut Channel<'d, CH>,
frequency: HertzU32,
) -> Result<(), Error>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
{
if frequency.raw() > 40_000_000 {
return Err(Error::UnreachableClockRate);
}
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| {
w.parl_clk_tx_en()
.set_bit()
.parl_clk_tx_sel()
.variant(1) // PLL
.parl_clk_tx_div_num()
.variant(divider)
});
pcr.parl_clk_rx_conf.modify(|_, w| {
w.parl_clk_rx_en()
.set_bit()
.parl_clk_rx_sel()
.variant(1) // PLL
.parl_clk_rx_div_num()
.variant(divider)
});
Instance::set_rx_sw_en(true);
Instance::set_rx_sample_mode(SampleMode::InternalSoftwareEnable);
dma_channel.tx.init_channel();
dma_channel.rx.init_channel();
Ok(())
}
impl<'d, CH, P, CP> ParlIoTx<'d, CH, P, CP>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
P: TxPins + ConfigurePins,
CP: TxClkPin,
{
/// Perform a DMA write.
///
/// This will return a [DmaTransfer] owning the buffer(s) and the driver
/// instance.
///
/// The maximum amount of data to be sent is 32736 bytes.
pub fn write_dma<TXBUF>(
mut self,
words: TXBUF,
) -> Result<DmaTransfer<'d, CH, TXBUF, P, CP>, Error>
where
TXBUF: ReadBuffer<Word = u8>,
{
let (ptr, len) = unsafe { words.read_buffer() };
if len > MAX_DMA_SIZE {
return Err(Error::MaxDmaTransferSizeExceeded);
}
self.start_write_bytes_dma(ptr, len)?;
Ok(DmaTransfer {
instance: self,
buffer: words,
})
}
fn start_write_bytes_dma<'w>(&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());
pcr.parl_clk_tx_conf
.modify(|_, w| w.parl_tx_rst_en().clear_bit());
Instance::clear_tx_interrupts();
Instance::set_tx_bytes(len as u16);
self.tx_channel.is_done();
self.tx_channel
.prepare_transfer(DmaPeripheral::ParlIo, false, ptr, len)?;
loop {
if Instance::is_tx_ready() {
break;
}
}
Instance::set_tx_start(true);
Ok(())
}
}
/// An in-progress DMA transfer.
pub struct DmaTransfer<'d, C, BUFFER, P, CP>
where
C: ChannelTypes,
C::P: ParlIoPeripheral,
P: TxPins + ConfigurePins,
CP: TxClkPin,
{
instance: ParlIoTx<'d, C, P, CP>,
buffer: BUFFER,
}
impl<'d, C, BUFFER, P, CP> DmaTransfer<'d, C, BUFFER, P, CP>
where
C: ChannelTypes,
C::P: ParlIoPeripheral,
P: TxPins + ConfigurePins,
CP: TxClkPin,
{
/// Wait for the DMA transfer to complete and return the buffers and the
/// SPI instance.
pub fn wait(
self,
) -> Result<(BUFFER, ParlIoTx<'d, C, P, CP>), (DmaError, BUFFER, ParlIoTx<'d, C, P, CP>)> {
loop {
if Instance::is_tx_eof() {
break;
}
}
Instance::set_tx_start(false);
// `DmaTransfer` needs to have a `Drop` implementation, because we accept
// managed buffers that can free their memory on drop. Because of that
// we can't move out of the `DmaTransfer`'s fields, so we use `ptr::read`
// and `mem::forget`.
//
// NOTE(unsafe) There is no panic branch between getting the resources
// and forgetting `self`.
unsafe {
let buffer = core::ptr::read(&self.buffer);
let payload = core::ptr::read(&self.instance);
let err = (&self).instance.tx_channel.has_error();
mem::forget(self);
if err {
Err((DmaError::DescriptorError, buffer, payload))
} else {
Ok((buffer, payload))
}
}
}
/// Check if the DMA transfer is complete
pub fn is_done(&self) -> bool {
let ch = &self.instance.tx_channel;
ch.is_done()
}
}
impl<'d, CH, P, CP> ParlIoRx<'d, CH, P, CP>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
P: RxPins + ConfigurePins,
CP: RxClkPin,
{
/// Perform a DMA read.
///
/// This will return a [RxDmaTransfer] owning the buffer(s) and the driver
/// instance.
///
/// 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<RXBUF>(
mut self,
mut words: RXBUF,
) -> Result<RxDmaTransfer<'d, CH, RXBUF, P, CP>, Error>
where
RXBUF: WriteBuffer<Word = u8>,
{
let (ptr, len) = unsafe { words.write_buffer() };
if !Instance::is_suc_eof_generated_externally() {
if len > MAX_DMA_SIZE {
return Err(Error::MaxDmaTransferSizeExceeded);
}
}
self.start_receive_bytes_dma(ptr, len)?;
Ok(RxDmaTransfer {
instance: self,
buffer: words,
})
}
fn start_receive_bytes_dma<'w>(&mut self, 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);
self.rx_channel
.prepare_transfer(false, DmaPeripheral::ParlIo, ptr, len)?;
Instance::set_rx_reg_update();
Instance::set_rx_start(true);
Ok(())
}
}
/// An in-progress DMA transfer.
pub struct RxDmaTransfer<'d, C, BUFFER, P, CP>
where
C: ChannelTypes,
C::P: ParlIoPeripheral,
P: RxPins + ConfigurePins,
CP: RxClkPin,
{
instance: ParlIoRx<'d, C, P, CP>,
buffer: BUFFER,
}
impl<'d, C, BUFFER, P, CP> RxDmaTransfer<'d, C, BUFFER, P, CP>
where
C: ChannelTypes,
C::P: ParlIoPeripheral,
P: RxPins + ConfigurePins,
CP: RxClkPin,
{
/// Wait for the DMA transfer to complete and return the buffers and the
/// SPI instance.
pub fn wait(
self,
) -> Result<(BUFFER, ParlIoRx<'d, C, P, CP>), (DmaError, BUFFER, ParlIoRx<'d, C, P, CP>)> {
loop {
if self.is_done() || self.is_eof_error() {
break;
}
}
Instance::set_rx_start(false);
// `DmaTransfer` needs to have a `Drop` implementation, because we accept
// managed buffers that can free their memory on drop. Because of that
// we can't move out of the `DmaTransfer`'s fields, so we use `ptr::read`
// and `mem::forget`.
//
// NOTE(unsafe) There is no panic branch between getting the resources
// and forgetting `self`.
unsafe {
let buffer = core::ptr::read(&self.buffer);
let payload = core::ptr::read(&self.instance);
let err = (&self).instance.rx_channel.has_error();
mem::forget(self);
if err {
Err((DmaError::DescriptorError, buffer, payload))
} else {
Ok((buffer, payload))
}
}
}
/// Check if the DMA transfer is complete
pub fn is_done(&self) -> bool {
let ch = &self.instance.rx_channel;
ch.is_done()
}
/// Check if the DMA transfer is completed by buffer full or source EOF
/// error
pub fn is_eof_error(&self) -> bool {
let ch = &self.instance.rx_channel;
ch.has_eof_error() || ch.has_dscr_empty_error()
}
}
#[cfg(feature = "async")]
pub mod asynch {
use core::task::Poll;
use embassy_sync::waitqueue::AtomicWaker;
use super::{
private::{ConfigurePins, Instance, RxClkPin, RxPins, TxClkPin, TxPins},
Error,
ParlIoRx,
ParlIoTx,
MAX_DMA_SIZE,
};
use crate::{
dma::{asynch::DmaRxDoneChFuture, ChannelTypes, ParlIoPeripheral},
macros::interrupt,
};
static TX_WAKER: AtomicWaker = AtomicWaker::new();
pub 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<Self::Output> {
TX_WAKER.register(cx.waker());
if Instance::is_listening_tx_done() {
Poll::Pending
} else {
Poll::Ready(())
}
}
}
#[cfg(esp32c6)]
#[interrupt]
fn PARL_IO() {
if Instance::is_tx_done_set() {
Instance::clear_is_tx_done();
Instance::unlisten_tx_done();
TX_WAKER.wake()
}
}
#[cfg(esp32h2)]
#[interrupt]
fn PARL_IO_TX() {
if Instance::is_tx_done_set() {
Instance::clear_is_tx_done();
Instance::unlisten_tx_done();
TX_WAKER.wake()
}
}
impl<'d, CH, P, CP> ParlIoTx<'d, CH, P, CP>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
P: TxPins + ConfigurePins,
CP: TxClkPin,
{
/// Perform a DMA write.
///
/// The maximum amount of data to be sent is 32736 bytes.
pub async fn write_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
let (ptr, len) = (words.as_ptr(), words.len());
if len > MAX_DMA_SIZE {
return Err(Error::MaxDmaTransferSizeExceeded);
}
self.start_write_bytes_dma(ptr, len)?;
TxDoneFuture::new().await;
Ok(())
}
}
impl<'d, CH, P, CP> ParlIoRx<'d, CH, P, CP>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
P: RxPins + ConfigurePins,
CP: RxClkPin,
{
/// Perform a DMA write.
///
/// The maximum amount of data to be sent is 32736 bytes.
pub async fn read_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
let (ptr, len) = (words.as_mut_ptr(), words.len());
if !Instance::is_suc_eof_generated_externally() {
if len > MAX_DMA_SIZE {
return Err(Error::MaxDmaTransferSizeExceeded);
}
}
self.start_receive_bytes_dma(ptr, len)?;
DmaRxDoneChFuture::new(&mut self.rx_channel).await;
Ok(())
}
}
}
mod private {
use super::{BitPackOrder, EofMode, Error, SampleEdge};
use crate::dma::ChannelTypes;
pub trait FullDuplex {}
pub trait NotContainsValidSignalPin {}
pub trait ContainsValidSignalPin {}
pub trait ValidSignalPin {}
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>;
}
pub struct TxCreator<'d, CH>
where
CH: ChannelTypes,
{
pub tx_channel: CH::Tx<'d>,
}
pub struct RxCreator<'d, CH>
where
CH: ChannelTypes,
{
pub rx_channel: CH::Rx<'d>,
}
pub struct TxCreatorFullDuplex<'d, CH>
where
CH: ChannelTypes,
{
pub tx_channel: CH::Tx<'d>,
}
pub struct RxCreatorFullDuplex<'d, CH>
where
CH: ChannelTypes,
{
pub rx_channel: CH::Rx<'d>,
}
#[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| w.tx_bus_wid_sel().variant(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| w.tx_idle_value().variant(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().variant(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().variant(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_int_clr()
.set_bit()
.tx_eof_int_clr()
.set_bit()
});
}
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| w.tx_bytelen().variant(len as u16));
}
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_int_raw().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| w.rx_bus_wid_sel().variant(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_wfull_int_clr().set_bit());
}
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| w.rx_data_bytelen().variant(len as u16));
}
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| w.rx_smp_mode_sel().variant(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().variant(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| w.rx_pulse_submode_sel().variant(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().variant(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().variant(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().variant(value as u8 == 1));
}
pub fn set_rx_timeout_ticks(value: Option<u16>) {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };
reg_block.rx_cfg1.modify(|_, w| {
w.rx_timeout_en()
.bit(value.is_some())
.rx_timeout_threshold()
.variant(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()
}
#[cfg(feature = "async")]
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_int_ena().set_bit());
}
#[cfg(feature = "async")]
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_int_ena().clear_bit());
}
#[cfg(feature = "async")]
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_int_ena().bit()
}
#[cfg(feature = "async")]
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_int_raw().bit()
}
#[cfg(feature = "async")]
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_int_clr().set_bit());
}
}
#[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| w.tx_bus_wid_sel().variant(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| w.tx_idle_value().variant(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().variant(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_int_clr()
.set_bit()
.tx_eof_int_clr()
.set_bit()
});
}
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| w.tx_bitlen().variant((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_int_raw().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| w.rx_bus_wid_sel().variant(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_int_clr().set_bit());
}
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| w.rx_bitlen().variant((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| w.rx_smp_mode_sel().variant(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().variant(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| w.rx_pulse_submode_sel().variant(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().variant(value as u8 == 1));
}
pub fn set_rx_timeout_ticks(value: Option<u16>) {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };
reg_block.rx_genrl_cfg.modify(|_, w| {
w.rx_timeout_en()
.bit(value.is_some())
.rx_timeout_thres()
.variant(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()
}
#[cfg(feature = "async")]
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_int_ena().set_bit());
}
#[cfg(feature = "async")]
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_int_ena().clear_bit());
}
#[cfg(feature = "async")]
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_int_ena().bit()
}
#[cfg(feature = "async")]
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_int_raw().bit()
}
#[cfg(feature = "async")]
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_int_clr().set_bit());
}
}
}