Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2165 lines
63 KiB
Rust
2165 lines
63 KiB
Rust
//! # 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<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
|
|
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<P = impl PeripheralOutput> + '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<P = impl PeripheralInput> + '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<P = impl PeripheralInput> + '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<P = impl PeripheralOutput> + 'd) -> Self {
|
|
crate::into_mapped_ref!(valid_pin);
|
|
Self { tx_pins, valid_pin }
|
|
}
|
|
}
|
|
|
|
impl<P> TxPins for TxPinConfigWithValidPin<'_, P> where
|
|
P: NotContainsValidSignalPin + TxPins + ConfigurePins
|
|
{
|
|
}
|
|
|
|
impl<P> 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<P>
|
|
where
|
|
P: ContainsValidSignalPin + TxPins + ConfigurePins,
|
|
{
|
|
tx_pins: P,
|
|
}
|
|
|
|
impl<P> TxPinConfigIncludingValidPin<P>
|
|
where
|
|
P: ContainsValidSignalPin + TxPins + ConfigurePins,
|
|
{
|
|
/// Create a new [TxPinConfigIncludingValidPin]
|
|
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: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<P = impl PeripheralOutput > + '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<P = impl PeripheralInput> + 'd,
|
|
enable_mode: EnableMode,
|
|
eof_mode: EofMode,
|
|
) -> Self {
|
|
crate::into_mapped_ref!(valid_pin);
|
|
Self {
|
|
rx_pins,
|
|
valid_pin,
|
|
enable_mode,
|
|
eof_mode,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<P> RxPins for RxPinConfigWithValidPin<'_, P> where
|
|
P: NotContainsValidSignalPin + RxPins + ConfigurePins
|
|
{
|
|
}
|
|
|
|
impl<P> 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<P>
|
|
where
|
|
P: ContainsValidSignalPin + RxPins + ConfigurePins,
|
|
{
|
|
rx_pins: P,
|
|
enable_mode: EnableMode,
|
|
eof_mode: EofMode,
|
|
}
|
|
|
|
impl<P> RxPinConfigIncludingValidPin<P>
|
|
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<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: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<P = impl PeripheralInput > + '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<P, CP>(
|
|
self,
|
|
mut tx_pins: P,
|
|
mut clk_pin: CP,
|
|
idle_value: u16,
|
|
sample_edge: SampleEdge,
|
|
bit_order: BitPackOrder,
|
|
) -> Result<ParlIoTx<'d, DM>, 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<P, CP>(
|
|
self,
|
|
tx_pins: &'d mut P,
|
|
clk_pin: &'d mut CP,
|
|
idle_value: u16,
|
|
sample_edge: SampleEdge,
|
|
bit_order: BitPackOrder,
|
|
) -> Result<ParlIoTx<'d, DM>, 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, <PARL_IO as DmaEligible>::Dma>,
|
|
tx_chain: DescriptorChain,
|
|
}
|
|
|
|
impl<DM> 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<P, CP>(
|
|
self,
|
|
rx_pins: &'d mut P,
|
|
clk_pin: &'d mut CP,
|
|
bit_order: BitPackOrder,
|
|
timeout_ticks: Option<u16>,
|
|
) -> Result<ParlIoRx<'d, DM>, 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<P, CP>(
|
|
self,
|
|
rx_pins: &'d mut P,
|
|
clk_pin: &'d mut CP,
|
|
bit_order: BitPackOrder,
|
|
timeout_ticks: Option<u16>,
|
|
) -> Result<ParlIoRx<'d, DM>, 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, <PARL_IO as DmaEligible>::Dma>,
|
|
rx_chain: DescriptorChain,
|
|
}
|
|
|
|
impl<DM> 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<ParlIoInterrupt>, 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<ParlIoInterrupt> {
|
|
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<ParlIoInterrupt>) {
|
|
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<CH, DM>(
|
|
_parl_io: impl Peripheral<P = peripherals::PARL_IO> + 'd,
|
|
dma_channel: Channel<'d, DM, CH>,
|
|
tx_descriptors: &'static mut [DmaDescriptor],
|
|
rx_descriptors: &'static mut [DmaDescriptor],
|
|
frequency: HertzU32,
|
|
) -> Result<Self, Error>
|
|
where
|
|
DM: Mode,
|
|
CH: DmaChannelConvert<<PARL_IO as DmaEligible>::Dma>,
|
|
Channel<'d, Blocking, CH>: From<Channel<'d, DM, CH>>,
|
|
{
|
|
let dma_channel = Channel::<Blocking, CH>::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<EnumSet<ParlIoInterrupt>>) {
|
|
internal_listen(interrupts.into(), true);
|
|
}
|
|
|
|
/// Unlisten the given interrupts
|
|
pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<ParlIoInterrupt>>) {
|
|
internal_listen(interrupts.into(), false);
|
|
}
|
|
|
|
/// Gets asserted interrupts
|
|
pub fn interrupts(&mut self) -> EnumSet<ParlIoInterrupt> {
|
|
internal_interrupts()
|
|
}
|
|
|
|
/// Resets asserted interrupts
|
|
pub fn clear_interrupts(&mut self, interrupts: EnumSet<ParlIoInterrupt>) {
|
|
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<CH>(
|
|
_parl_io: impl Peripheral<P = peripherals::PARL_IO> + 'd,
|
|
dma_channel: Channel<'d, DM, CH>,
|
|
descriptors: &'static mut [DmaDescriptor],
|
|
frequency: HertzU32,
|
|
) -> Result<Self, Error>
|
|
where
|
|
CH: DmaChannelConvert<<PARL_IO as DmaEligible>::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<EnumSet<ParlIoInterrupt>>) {
|
|
internal_listen(interrupts.into(), true);
|
|
}
|
|
|
|
/// Unlisten the given interrupts
|
|
pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<ParlIoInterrupt>>) {
|
|
internal_listen(interrupts.into(), false);
|
|
}
|
|
|
|
/// Gets asserted interrupts
|
|
pub fn interrupts(&mut self) -> EnumSet<ParlIoInterrupt> {
|
|
internal_interrupts()
|
|
}
|
|
|
|
/// Resets asserted interrupts
|
|
pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<ParlIoInterrupt>>) {
|
|
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<CH>(
|
|
_parl_io: impl Peripheral<P = peripherals::PARL_IO> + 'd,
|
|
dma_channel: Channel<'d, DM, CH>,
|
|
descriptors: &'static mut [DmaDescriptor],
|
|
frequency: HertzU32,
|
|
) -> Result<Self, Error>
|
|
where
|
|
CH: DmaChannelConvert<<PARL_IO as DmaEligible>::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<EnumSet<ParlIoInterrupt>>) {
|
|
internal_listen(interrupts.into(), true);
|
|
}
|
|
|
|
/// Unlisten the given interrupts
|
|
pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<ParlIoInterrupt>>) {
|
|
internal_listen(interrupts.into(), false);
|
|
}
|
|
|
|
/// Gets asserted interrupts
|
|
pub fn interrupts(&mut self) -> EnumSet<ParlIoInterrupt> {
|
|
internal_interrupts()
|
|
}
|
|
|
|
/// Resets asserted interrupts
|
|
pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<ParlIoInterrupt>>) {
|
|
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<DM> 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<DmaTransferTx<'t, Self>, 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<DM> 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, <PARL_IO as DmaEligible>::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<DmaTransferRx<'t, Self>, 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, <PARL_IO as DmaEligible>::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<DM> 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, <PARL_IO as DmaEligible>::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, <PARL_IO as DmaEligible>::Dma>,
|
|
descriptors: &'static mut [DmaDescriptor],
|
|
}
|
|
|
|
/// Creates a RX channel
|
|
pub struct RxCreator<'d, DM>
|
|
where
|
|
DM: Mode,
|
|
{
|
|
rx_channel: ChannelRx<'d, DM, <PARL_IO as DmaEligible>::Dma>,
|
|
descriptors: &'static mut [DmaDescriptor],
|
|
}
|
|
|
|
/// Creates a TX channel
|
|
pub struct TxCreatorFullDuplex<'d, DM>
|
|
where
|
|
DM: Mode,
|
|
{
|
|
tx_channel: ChannelTx<'d, DM, <PARL_IO as DmaEligible>::Dma>,
|
|
descriptors: &'static mut [DmaDescriptor],
|
|
}
|
|
|
|
/// Creates a RX channel
|
|
pub struct RxCreatorFullDuplex<'d, DM>
|
|
where
|
|
DM: Mode,
|
|
{
|
|
rx_channel: ChannelRx<'d, DM, <PARL_IO as DmaEligible>::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<Self::Output> {
|
|
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<TXBUF>(&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<u16>) {
|
|
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<u16>) {
|
|
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());
|
|
}
|
|
}
|
|
}
|