mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-30 05:40:39 +00:00
PARL_IO TX driver (#733)
* PARL_IO TX driver * Update CHANGELOG.md * Update esp-hal-common/src/dma/mod.rs Co-authored-by: Jesse Braham <jessebraham@users.noreply.github.com> --------- Co-authored-by: Jesse Braham <jessebraham@users.noreply.github.com>
This commit is contained in:
parent
9acb915fb9
commit
95a1255c3b
@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Add GPIO (output) and delay functionality to `esp32c6-lp-hal` (#715)
|
||||
- Implement RTCIO pullup, pulldown and hold control for Xtensa MCUs (#684)
|
||||
- Add GPIO input support and implement additional `embedded-hal` output traits for the C6's LP core [#720]
|
||||
- Add PARL_IO TX driver for ESP32-C6 / ESP32-H2 (#733)
|
||||
- Implement `ufmt_write::uWrite` trait for USB Serial JTAG (#751)
|
||||
|
||||
### Changed
|
||||
|
@ -479,6 +479,8 @@ macro_rules! impl_channel {
|
||||
impl I2sPeripheral for [<SuitablePeripheral $num>] {}
|
||||
impl I2s0Peripheral for [<SuitablePeripheral $num>] {}
|
||||
impl I2s1Peripheral for [<SuitablePeripheral $num>] {}
|
||||
#[cfg(parl_io)]
|
||||
impl ParlIoPeripheral for [<SuitablePeripheral $num>] {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -140,6 +140,8 @@ pub enum DmaPeripheral {
|
||||
Adc = 8,
|
||||
#[cfg(esp32s3)]
|
||||
Rmt = 9,
|
||||
#[cfg(parl_io)]
|
||||
ParlIo = 9,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, PartialOrd)]
|
||||
@ -253,6 +255,8 @@ pub trait I2s0Peripheral: I2sPeripheral + PeripheralMarker {}
|
||||
/// Marks channels as useable for I2S1
|
||||
pub trait I2s1Peripheral: I2sPeripheral + PeripheralMarker {}
|
||||
|
||||
pub trait ParlIoPeripheral: PeripheralMarker {}
|
||||
|
||||
/// DMA Rx
|
||||
pub trait Rx: RxPrivate {}
|
||||
|
||||
|
@ -105,6 +105,8 @@ pub mod ledc;
|
||||
pub mod mcpwm;
|
||||
#[cfg(usb0)]
|
||||
pub mod otg_fs;
|
||||
#[cfg(parl_io)]
|
||||
pub mod parl_io;
|
||||
#[cfg(pcnt)]
|
||||
pub mod pcnt;
|
||||
pub mod peripheral;
|
||||
|
935
esp-hal-common/src/parl_io.rs
Normal file
935
esp-hal-common/src/parl_io.rs
Normal file
@ -0,0 +1,935 @@
|
||||
//! # 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.
|
||||
//!
|
||||
//! At the moment, the Parallel IO driver only supports TX mode. The RX feature
|
||||
//! is still working in progress.
|
||||
//!
|
||||
//! The driver uses DMA (Direct Memory Access) for efficient data transfer.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ### Initialization
|
||||
//! ```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(),
|
||||
//! &mut system.peripheral_clock_control,
|
||||
//! &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);
|
||||
//! ```
|
||||
|
||||
use core::mem;
|
||||
|
||||
use embedded_dma::ReadBuffer;
|
||||
use fugit::HertzU32;
|
||||
use peripheral::PeripheralRef;
|
||||
use private::*;
|
||||
|
||||
use crate::{
|
||||
clock::Clocks,
|
||||
dma::{Channel, ChannelTypes, DmaError, DmaPeripheral, ParlIoPeripheral, 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)]
|
||||
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)]
|
||||
pub enum SampleEdge {
|
||||
/// Positive edge
|
||||
Normal = 0,
|
||||
/// Negative edge
|
||||
Invert = 1,
|
||||
}
|
||||
|
||||
/// Parallel IO bit packing order
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BitPackOrder {
|
||||
/// Bit pack order: MSB
|
||||
Msb = 0,
|
||||
/// Bit pack order: LSB
|
||||
Lsb = 1,
|
||||
}
|
||||
|
||||
/// Used to configure no pin as clock output
|
||||
pub struct NoClkPin;
|
||||
impl TxClkPin 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 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
self.tx_pins.configure();
|
||||
Instance::set_tx_hw_valid_en(true);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
$(
|
||||
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 >]);
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
{
|
||||
}
|
||||
|
||||
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,
|
||||
) -> ParlIoTx<'d, CH, P, CP>
|
||||
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);
|
||||
|
||||
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,
|
||||
) -> ParlIoTx<'d, CH, P, CP>
|
||||
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);
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
_clocks: &Clocks,
|
||||
) -> Result<Self, Error> {
|
||||
crate::into_ref!(parl_io);
|
||||
internal_init(&mut dma_channel, frequency, peripheral_clock_control)?;
|
||||
|
||||
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,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
_clocks: &Clocks,
|
||||
) -> Result<Self, Error> {
|
||||
crate::into_ref!(parl_io);
|
||||
internal_init(&mut dma_channel, frequency, peripheral_clock_control)?;
|
||||
|
||||
Ok(Self {
|
||||
_parl_io: parl_io,
|
||||
tx: TxCreator {
|
||||
tx_channel: dma_channel.tx,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn internal_init<'d, CH>(
|
||||
dma_channel: &mut Channel<'d, CH>,
|
||||
frequency: HertzU32,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
CH: ChannelTypes,
|
||||
CH::P: ParlIoPeripheral,
|
||||
{
|
||||
if frequency.raw() > 40_000_000 {
|
||||
return Err(Error::UnreachableClockRate);
|
||||
}
|
||||
|
||||
peripheral_clock_control.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)
|
||||
});
|
||||
|
||||
dma_channel.tx.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);
|
||||
return 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()
|
||||
}
|
||||
}
|
||||
|
||||
mod private {
|
||||
use super::{BitPackOrder, SampleEdge};
|
||||
use crate::dma::ChannelTypes;
|
||||
|
||||
pub trait FullDuplex {}
|
||||
|
||||
pub trait NotContainsValidSignalPin {}
|
||||
|
||||
pub trait ContainsValidSignalPin {}
|
||||
|
||||
pub trait ValidSignalPin {}
|
||||
|
||||
pub trait TxPins {}
|
||||
|
||||
pub trait TxClkPin {
|
||||
fn configure(&mut self);
|
||||
}
|
||||
|
||||
pub trait ConfigurePins {
|
||||
fn configure(&mut self);
|
||||
}
|
||||
|
||||
pub struct TxCreator<'d, CH>
|
||||
where
|
||||
CH: ChannelTypes,
|
||||
{
|
||||
pub tx_channel: CH::Tx<'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) 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));
|
||||
}
|
||||
}
|
||||
|
||||
#[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));
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,8 @@ pub(crate) mod constants {
|
||||
pub const RMT_CLOCK_SRC: u8 = 1;
|
||||
pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80);
|
||||
|
||||
pub const PARL_IO_SCLK: u32 = 240_000_000;
|
||||
|
||||
pub const SOC_DRAM_LOW: u32 = 0x4080_0000;
|
||||
pub const SOC_DRAM_HIGH: u32 = 0x4088_0000;
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ pub(crate) mod constants {
|
||||
pub const RMT_CLOCK_SRC: bool = false;
|
||||
pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(32);
|
||||
|
||||
pub const PARL_IO_SCLK: u32 = 96_000_000;
|
||||
|
||||
pub const SOC_DRAM_LOW: u32 = 0x4080_0000;
|
||||
pub const SOC_DRAM_HIGH: u32 = 0x4085_0000;
|
||||
}
|
||||
|
@ -102,6 +102,8 @@ pub enum Peripheral {
|
||||
Uart2,
|
||||
#[cfg(rsa)]
|
||||
Rsa,
|
||||
#[cfg(parl_io)]
|
||||
ParlIo,
|
||||
}
|
||||
|
||||
pub struct SoftwareInterruptControl {
|
||||
@ -519,6 +521,14 @@ impl PeripheralClockControl {
|
||||
system.rsa_conf.modify(|_, w| w.rsa_rst_en().clear_bit());
|
||||
system.rsa_pd_ctrl.modify(|_, w| w.rsa_mem_pd().clear_bit());
|
||||
}
|
||||
#[cfg(parl_io)]
|
||||
Peripheral::ParlIo => {
|
||||
system.parl_io_conf.modify(|_, w| w.parl_clk_en().set_bit());
|
||||
system.parl_io_conf.modify(|_, w| w.parl_rst_en().set_bit());
|
||||
system
|
||||
.parl_io_conf
|
||||
.modify(|_, w| w.parl_rst_en().clear_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
119
esp32c6-hal/examples/parl_io_tx.rs
Normal file
119
esp32c6-hal/examples/parl_io_tx.rs
Normal file
@ -0,0 +1,119 @@
|
||||
//! This shows using Parallel IO to output 4 bit parallel data at 1MHz clock
|
||||
//! rate.
|
||||
//!
|
||||
//! Uses GPIO 1, 2, 3 and 4 as the data pins.
|
||||
//! GPIO 5 as the "valid pin" (driven high during an active transfer) and GPIO
|
||||
//! 6 as the clock signal output.
|
||||
//!
|
||||
//! You can use a logic analyzer to see how the pins are used.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp32c6_hal::{
|
||||
clock::ClockControl,
|
||||
dma::DmaPriority,
|
||||
gdma::Gdma,
|
||||
gpio::IO,
|
||||
parl_io::{
|
||||
BitPackOrder,
|
||||
ClkOutPin,
|
||||
ParlIoTxOnly,
|
||||
SampleEdge,
|
||||
TxFourBits,
|
||||
TxPinConfigWithValidPin,
|
||||
},
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let mut system = peripherals.PCR.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
// Disable the watchdog timers.
|
||||
let mut rtc = Rtc::new(peripherals.LP_CLKRST);
|
||||
let timer_group0 = TimerGroup::new(
|
||||
peripherals.TIMG0,
|
||||
&clocks,
|
||||
&mut system.peripheral_clock_control,
|
||||
);
|
||||
let mut wdt0 = timer_group0.wdt;
|
||||
let timer_group1 = TimerGroup::new(
|
||||
peripherals.TIMG1,
|
||||
&clocks,
|
||||
&mut system.peripheral_clock_control,
|
||||
);
|
||||
let mut wdt1 = timer_group1.wdt;
|
||||
|
||||
rtc.swd.disable();
|
||||
rtc.rwdt.disable();
|
||||
wdt0.disable();
|
||||
wdt1.disable();
|
||||
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
let mut tx_descriptors = [0u32; 8 * 3];
|
||||
let mut rx_descriptors = [0u32; 8 * 3];
|
||||
|
||||
let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control);
|
||||
let dma_channel = dma.channel0;
|
||||
|
||||
let tx_pins = TxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4);
|
||||
|
||||
let pin_conf = TxPinConfigWithValidPin::new(tx_pins, io.pins.gpio5);
|
||||
|
||||
let parl_io = ParlIoTxOnly::new(
|
||||
peripherals.PARL_IO,
|
||||
dma_channel.configure(
|
||||
false,
|
||||
&mut tx_descriptors,
|
||||
&mut rx_descriptors,
|
||||
DmaPriority::Priority0,
|
||||
),
|
||||
1u32.MHz(),
|
||||
&mut system.peripheral_clock_control,
|
||||
&clocks,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let clock_pin = ClkOutPin::new(io.pins.gpio6);
|
||||
|
||||
let mut parl_io_tx = parl_io.tx.with_config(
|
||||
pin_conf,
|
||||
clock_pin,
|
||||
0,
|
||||
SampleEdge::Normal,
|
||||
BitPackOrder::Msb,
|
||||
);
|
||||
|
||||
let mut buffer = dma_buffer();
|
||||
for i in 0..buffer.len() {
|
||||
buffer[i] = (i % 255) as u8;
|
||||
}
|
||||
|
||||
let mut delay = Delay::new(&clocks);
|
||||
|
||||
loop {
|
||||
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();
|
||||
println!("Transferred {} bytes", buffer.len());
|
||||
|
||||
delay.delay_ms(500u32);
|
||||
}
|
||||
}
|
||||
|
||||
fn dma_buffer() -> &'static mut [u8; 4092 * 4] {
|
||||
static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4];
|
||||
unsafe { &mut BUFFER }
|
||||
}
|
119
esp32h2-hal/examples/parl_io_tx.rs
Normal file
119
esp32h2-hal/examples/parl_io_tx.rs
Normal file
@ -0,0 +1,119 @@
|
||||
//! This shows using Parallel IO to output 4 bit parallel data at 1MHz clock
|
||||
//! rate.
|
||||
//!
|
||||
//! Uses GPIO 1, 2, 3 and 4 as the data pins.
|
||||
//! GPIO 5 as the "valid pin" (driven high during an active transfer) and GPIO
|
||||
//! 10 as the clock signal output.
|
||||
//!
|
||||
//! You can use a logic analyzer to see how the pins are used.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp32h2_hal::{
|
||||
clock::ClockControl,
|
||||
dma::DmaPriority,
|
||||
gdma::Gdma,
|
||||
gpio::IO,
|
||||
parl_io::{
|
||||
BitPackOrder,
|
||||
ClkOutPin,
|
||||
ParlIoTxOnly,
|
||||
SampleEdge,
|
||||
TxFourBits,
|
||||
TxPinConfigWithValidPin,
|
||||
},
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let mut system = peripherals.PCR.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
// Disable the watchdog timers.
|
||||
let mut rtc = Rtc::new(peripherals.LP_CLKRST);
|
||||
let timer_group0 = TimerGroup::new(
|
||||
peripherals.TIMG0,
|
||||
&clocks,
|
||||
&mut system.peripheral_clock_control,
|
||||
);
|
||||
let mut wdt0 = timer_group0.wdt;
|
||||
let timer_group1 = TimerGroup::new(
|
||||
peripherals.TIMG1,
|
||||
&clocks,
|
||||
&mut system.peripheral_clock_control,
|
||||
);
|
||||
let mut wdt1 = timer_group1.wdt;
|
||||
|
||||
rtc.swd.disable();
|
||||
rtc.rwdt.disable();
|
||||
wdt0.disable();
|
||||
wdt1.disable();
|
||||
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
let mut tx_descriptors = [0u32; 8 * 3];
|
||||
let mut rx_descriptors = [0u32; 8 * 3];
|
||||
|
||||
let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control);
|
||||
let dma_channel = dma.channel0;
|
||||
|
||||
let tx_pins = TxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4);
|
||||
|
||||
let pin_conf = TxPinConfigWithValidPin::new(tx_pins, io.pins.gpio5);
|
||||
|
||||
let parl_io = ParlIoTxOnly::new(
|
||||
peripherals.PARL_IO,
|
||||
dma_channel.configure(
|
||||
false,
|
||||
&mut tx_descriptors,
|
||||
&mut rx_descriptors,
|
||||
DmaPriority::Priority0,
|
||||
),
|
||||
1u32.MHz(),
|
||||
&mut system.peripheral_clock_control,
|
||||
&clocks,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let clock_pin = ClkOutPin::new(io.pins.gpio10);
|
||||
|
||||
let mut parl_io_tx = parl_io.tx.with_config(
|
||||
pin_conf,
|
||||
clock_pin,
|
||||
0,
|
||||
SampleEdge::Normal,
|
||||
BitPackOrder::Msb,
|
||||
);
|
||||
|
||||
let mut buffer = dma_buffer();
|
||||
for i in 0..buffer.len() {
|
||||
buffer[i] = (i % 255) as u8;
|
||||
}
|
||||
|
||||
let mut delay = Delay::new(&clocks);
|
||||
|
||||
loop {
|
||||
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();
|
||||
println!("Transferred {} bytes", buffer.len());
|
||||
|
||||
delay.delay_ms(500u32);
|
||||
}
|
||||
}
|
||||
|
||||
fn dma_buffer() -> &'static mut [u8; 4092 * 4] {
|
||||
static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4];
|
||||
unsafe { &mut BUFFER }
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user