mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-30 22:01:11 +00:00
UART using DMA (via UHCI) (#3871)
* working rx * very sketchy working tx * uart uhci qa example * move it * cleanout * read working great, write outputs the whole buffer * tx working but rx can freeze * fix freezes * dont use vec * cleaning * async weird and not working * async working? * into async * Apply suggested changes * it compiles * it also works with 'd * split into seperate structs * Add uart config configuration after uhci consumed it * working Uhci normal * again aaa sorry * Apply suggested changes (That I knew how to implement) * Add proper publicity, reimplement wrappers for internal functions using a macro, repair examples * Moved to config, uhci normal example doesn't echo * still not working * hacky works normal * apply suggested changes * Initial docs * fix messing up rx tx, still doesn't work * Workaround, working but well * change back to not pub (Maybe redo to public later, for now just to not mess with the workaround) * now its working * forgot to push * Revert 2 commits, implement drop to showcase problem * apply some suggested changes * apply more suggested fixes (async/blocking) * Everything works * clearing out warnings * handle errors * merge from upstream, make it compile (not sure) * Apply almost all suggestions * re add wait for done * Move errors out of wait_for_done, formating, changelog * Apply suggested changes * apply lint suggestions * docs * No longer public * hil tests, rx tx transfer * implement split * forgot to format * apply suggested changes * Add top level module doc * cicd now working? * format, cicd maybe now * apply suggestion * format fix * modify to write
This commit is contained in:
parent
3f29f0571c
commit
9fe02b819c
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- `aes::{AesBackend, AesContext, dma::AesDmaBackend}`: Work-queue based AES driver (#3880, #3897)
|
- `aes::{AesBackend, AesContext, dma::AesDmaBackend}`: Work-queue based AES driver (#3880, #3897)
|
||||||
- `aes::cipher_modes`, `aes::CipherState` for constructing `AesContext`s (#3895)
|
- `aes::cipher_modes`, `aes::CipherState` for constructing `AesContext`s (#3895)
|
||||||
- `aes::dma::DmaCipherState` so that `AesDma` can properly support cipher modes that require state (IV, nonce, etc.) (#3897)
|
- `aes::dma::DmaCipherState` so that `AesDma` can properly support cipher modes that require state (IV, nonce, etc.) (#3897)
|
||||||
|
- `uart::Uhci`: for UART with DMA using the UHCI peripheral (#3871)
|
||||||
- Align `I8080` driver pin configurations with latest guidelines (#3997)
|
- Align `I8080` driver pin configurations with latest guidelines (#3997)
|
||||||
- Expose cache line configuration (#3946)
|
- Expose cache line configuration (#3946)
|
||||||
- ESP32: Expose `psram_vaddr_mode` via `PsramConfig` (#3990)
|
- ESP32: Expose `psram_vaddr_mode` via `PsramConfig` (#3990)
|
||||||
|
@ -41,6 +41,11 @@
|
|||||||
//! [embedded-hal-async]: embedded_hal_async
|
//! [embedded-hal-async]: embedded_hal_async
|
||||||
//! [embedded-io-async]: embedded_io_async
|
//! [embedded-io-async]: embedded_io_async
|
||||||
|
|
||||||
|
/// UHCI wrapper around UART (Only esp32c6)
|
||||||
|
#[cfg(esp32c6)]
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
pub mod uhci;
|
||||||
|
|
||||||
use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll};
|
use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll};
|
||||||
|
|
||||||
#[cfg(feature = "unstable")]
|
#[cfg(feature = "unstable")]
|
708
esp-hal/src/uart/uhci.rs
Normal file
708
esp-hal/src/uart/uhci.rs
Normal file
@ -0,0 +1,708 @@
|
|||||||
|
#![cfg_attr(docsrs, procmacros::doc_replace)]
|
||||||
|
//! ## Usage
|
||||||
|
//! ```rust, no_run
|
||||||
|
//! #![no_std]
|
||||||
|
//! #![no_main]
|
||||||
|
//!
|
||||||
|
//! #[panic_handler]
|
||||||
|
//! fn panic(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
//! loop {}
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! use esp_hal::{
|
||||||
|
//! clock::CpuClock,
|
||||||
|
//! dma::{DmaRxBuf, DmaTxBuf},
|
||||||
|
//! dma_buffers,
|
||||||
|
//! main,
|
||||||
|
//! rom::software_reset,
|
||||||
|
//! uart,
|
||||||
|
//! uart::{RxConfig, Uart, uhci, uhci::Uhci},
|
||||||
|
//! };
|
||||||
|
//!
|
||||||
|
//! #[main]
|
||||||
|
//! fn main() -> ! {
|
||||||
|
//! let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
|
||||||
|
//! let peripherals = esp_hal::init(config);
|
||||||
|
//! let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
|
||||||
|
//! let peripherals = esp_hal::init(config);
|
||||||
|
//!
|
||||||
|
//! let config = uart::Config::default()
|
||||||
|
//! .with_rx(RxConfig::default().with_fifo_full_threshold(64))
|
||||||
|
//! .with_baudrate(115200);
|
||||||
|
//!
|
||||||
|
//! let uart = Uart::new(peripherals.UART1, config)
|
||||||
|
//! .unwrap()
|
||||||
|
//! .with_tx(peripherals.GPIO2)
|
||||||
|
//! .with_rx(peripherals.GPIO3);
|
||||||
|
//!
|
||||||
|
//! let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4092);
|
||||||
|
//! let dma_rx = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
|
||||||
|
//! let mut dma_tx = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
|
||||||
|
//!
|
||||||
|
//! let mut uhci = Uhci::new(uart, peripherals.UHCI0, peripherals.DMA_CH0);
|
||||||
|
//! uhci.apply_config(&uhci::Config::default().with_chunk_limit(dma_rx.len() as u16))
|
||||||
|
//! .unwrap();
|
||||||
|
//!
|
||||||
|
//! let config = uart::Config::default()
|
||||||
|
//! .with_rx(RxConfig::default().with_fifo_full_threshold(64))
|
||||||
|
//! .with_baudrate(9600);
|
||||||
|
//! uhci.set_uart_config(&config).unwrap();
|
||||||
|
//!
|
||||||
|
//! let (uhci_rx, uhci_tx) = uhci.split();
|
||||||
|
//! // Waiting for message
|
||||||
|
//! let transfer = uhci_rx
|
||||||
|
//! .read(dma_rx)
|
||||||
|
//! .unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
//! let (err, _uhci_rx, dma_rx) = transfer.wait();
|
||||||
|
//! err.unwrap();
|
||||||
|
//!
|
||||||
|
//! let received = dma_rx.number_of_received_bytes();
|
||||||
|
//! // println!("Received dma bytes: {}", received);
|
||||||
|
//!
|
||||||
|
//! let rec_slice = &dma_rx.as_slice()[0..received];
|
||||||
|
//! if received > 0 {
|
||||||
|
//! match core::str::from_utf8(&rec_slice) {
|
||||||
|
//! Ok(x) => {
|
||||||
|
//! // println!("Received DMA message: \"{}\"", x);
|
||||||
|
//! dma_tx.as_mut_slice()[0..received].copy_from_slice(&rec_slice);
|
||||||
|
//! dma_tx.set_length(received);
|
||||||
|
//! let transfer = uhci_tx
|
||||||
|
//! .write(dma_tx)
|
||||||
|
//! .unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
//! let (err, _uhci, _dma_tx) = transfer.wait();
|
||||||
|
//! err.unwrap();
|
||||||
|
//! }
|
||||||
|
//! Err(x) => panic!("Error string: {}", x),
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! software_reset()
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use core::{
|
||||||
|
mem::ManuallyDrop,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
|
|
||||||
|
use embassy_embedded_hal::SetConfig;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Async,
|
||||||
|
Blocking,
|
||||||
|
DriverMode,
|
||||||
|
dma::{
|
||||||
|
AnyGdmaRxChannel,
|
||||||
|
AnyGdmaTxChannel,
|
||||||
|
Channel,
|
||||||
|
ChannelRx,
|
||||||
|
ChannelTx,
|
||||||
|
DmaChannelFor,
|
||||||
|
DmaEligible,
|
||||||
|
DmaError,
|
||||||
|
DmaRxBuffer,
|
||||||
|
DmaTxBuffer,
|
||||||
|
PeripheralDmaChannel,
|
||||||
|
asynch::{DmaRxFuture, DmaTxFuture},
|
||||||
|
},
|
||||||
|
pac::uhci0,
|
||||||
|
peripherals,
|
||||||
|
uart::{self, TxError, Uart, UartRx, UartTx, uhci::Error::AboveReadLimit},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
/// Uhci specific errors
|
||||||
|
pub enum Error {
|
||||||
|
/// set_chunk_limit() argument is above what's possible by the hardware. It cannot exceed 4095
|
||||||
|
/// (12 bits), above this value it will simply also split the readings
|
||||||
|
AboveReadLimit,
|
||||||
|
/// DMA originating error
|
||||||
|
Dma(DmaError),
|
||||||
|
/// UART Tx originating error
|
||||||
|
Tx(TxError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DmaError> for Error {
|
||||||
|
fn from(value: DmaError) -> Self {
|
||||||
|
Error::Dma(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TxError> for Error {
|
||||||
|
fn from(value: TxError) -> Self {
|
||||||
|
Error::Tx(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::any_peripheral! {
|
||||||
|
pub peripheral AnyUhci<'d> {
|
||||||
|
Uhci0(crate::peripherals::UHCI0<'d>),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> DmaEligible for AnyUhci<'d> {
|
||||||
|
#[cfg(gdma)]
|
||||||
|
type Dma = crate::dma::AnyGdmaChannel<'d>;
|
||||||
|
|
||||||
|
fn dma_peripheral(&self) -> crate::dma::DmaPeripheral {
|
||||||
|
match &self.0 {
|
||||||
|
any::Inner::Uhci0(_) => crate::dma::DmaPeripheral::Uhci0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyUhci<'_> {
|
||||||
|
/// Opens the enum into the peripheral below
|
||||||
|
fn give_uhci(&self) -> &peripherals::UHCI0<'_> {
|
||||||
|
match &self.0 {
|
||||||
|
any::Inner::Uhci0(x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A configuration error.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ConfigError {
|
||||||
|
/// chunk_limit is above 4095, this is not allowed (hardware limit)
|
||||||
|
AboveReadLimit,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UHCI Configuration
|
||||||
|
#[derive(Debug, Clone, Copy, procmacros::BuilderLite)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Config {
|
||||||
|
/// If this is set to true UHCI will end the payload receiving process when UART has been in
|
||||||
|
/// idle state.
|
||||||
|
idle_eof: bool,
|
||||||
|
/// If this is set to true UHCI decoder receiving payload data ends when the receiving
|
||||||
|
/// byte count has reached the specified value (in len_eof).
|
||||||
|
/// If this is set to false UHCI decoder receiving payload data is end when 0xc0 is received.
|
||||||
|
len_eof: bool,
|
||||||
|
/// The limit of how much to read in a single read call. It cannot be higher than the dma
|
||||||
|
/// buffer size, otherwise uart/dma/uhci will freeze. It cannot exceed 4095 (12 bits), above
|
||||||
|
/// this value it will simply also split the readings
|
||||||
|
chunk_limit: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
idle_eof: true,
|
||||||
|
len_eof: true,
|
||||||
|
// This is the default in the register at boot, still should be changed!
|
||||||
|
chunk_limit: 128,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::error::Error for ConfigError {}
|
||||||
|
|
||||||
|
impl core::fmt::Display for ConfigError {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ConfigError::AboveReadLimit => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"The requested read limit is not possible. The max is 4095 (12 bits)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Dm> embassy_embedded_hal::SetConfig for Uhci<'_, Dm>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
{
|
||||||
|
type Config = Config;
|
||||||
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
|
self.apply_config(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UHCI (To use with UART over DMA)
|
||||||
|
pub struct Uhci<'d, Dm>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
{
|
||||||
|
uart: Uart<'d, Dm>,
|
||||||
|
uhci: AnyUhci<'static>,
|
||||||
|
channel: Channel<Dm, PeripheralDmaChannel<AnyUhci<'d>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, Dm> Uhci<'d, Dm>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
{
|
||||||
|
fn init(&self) {
|
||||||
|
self.clean_turn_on();
|
||||||
|
self.reset();
|
||||||
|
self.conf_uart();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean_turn_on(&self) {
|
||||||
|
// General conf registers
|
||||||
|
let reg: &uhci0::RegisterBlock = self.uhci.give_uhci().register_block();
|
||||||
|
reg.conf0().modify(|_, w| w.clk_en().set_bit());
|
||||||
|
reg.conf0().write(|w| {
|
||||||
|
unsafe { w.bits(0) };
|
||||||
|
w.clk_en().set_bit()
|
||||||
|
});
|
||||||
|
reg.conf1().modify(|_, w| unsafe { w.bits(0) });
|
||||||
|
|
||||||
|
// For TX
|
||||||
|
reg.escape_conf().modify(|_, w| unsafe { w.bits(0) });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&self) {
|
||||||
|
let reg: &uhci0::RegisterBlock = self.uhci.give_uhci().register_block();
|
||||||
|
reg.conf0().modify(|_, w| w.rx_rst().set_bit());
|
||||||
|
reg.conf0().modify(|_, w| w.rx_rst().clear_bit());
|
||||||
|
|
||||||
|
reg.conf0().modify(|_, w| w.tx_rst().set_bit());
|
||||||
|
reg.conf0().modify(|_, w| w.tx_rst().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn conf_uart(&self) {
|
||||||
|
let reg: &uhci0::RegisterBlock = self.uhci.give_uhci().register_block();
|
||||||
|
|
||||||
|
// Idk if there is a better way to check it, but it works
|
||||||
|
match &self.uart.tx.uart.0 {
|
||||||
|
super::any::Inner::Uart0(_) => {
|
||||||
|
info!("Uhci will use uart0");
|
||||||
|
reg.conf0().modify(|_, w| w.uart0_ce().set_bit());
|
||||||
|
}
|
||||||
|
super::any::Inner::Uart1(_) => {
|
||||||
|
info!("Uhci will use uart1");
|
||||||
|
reg.conf0().modify(|_, w| w.uart1_ce().set_bit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn set_chunk_limit(&self, limit: u16) -> Result<(), Error> {
|
||||||
|
let reg: &uhci0::RegisterBlock = self.uhci.give_uhci().register_block();
|
||||||
|
// let val = reg.pkt_thres().read().pkt_thrs().bits();
|
||||||
|
// info!("Read limit value: {} to set: {}", val, limit);
|
||||||
|
|
||||||
|
// limit is 12 bits
|
||||||
|
// Above this value, it will probably split the messages, anyway, the point is below (the
|
||||||
|
// dma buffer length) it it will not freeze itself
|
||||||
|
if limit > 4095 {
|
||||||
|
return Err(AboveReadLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.pkt_thres().write(|w| unsafe { w.bits(limit as u32) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the config the the consumed UART
|
||||||
|
pub fn set_uart_config(&mut self, uart_config: &uart::Config) -> Result<(), uart::ConfigError> {
|
||||||
|
self.uart.set_config(uart_config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the config to the UHCI peripheral
|
||||||
|
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
|
let reg: &uhci0::RegisterBlock = self.uhci.give_uhci().register_block();
|
||||||
|
|
||||||
|
reg.conf0()
|
||||||
|
.modify(|_, w| w.uart_idle_eof_en().bit(config.idle_eof));
|
||||||
|
|
||||||
|
reg.conf0()
|
||||||
|
.modify(|_, w| w.len_eof_en().bit(config.len_eof));
|
||||||
|
|
||||||
|
if self.set_chunk_limit(config.chunk_limit).is_err() {
|
||||||
|
return Err(ConfigError::AboveReadLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the Uhci into UhciRx and UhciTx
|
||||||
|
pub fn split(self) -> (UhciRx<'d, Dm>, UhciTx<'d, Dm>) {
|
||||||
|
let (uart_rx, uart_tx) = self.uart.split();
|
||||||
|
(
|
||||||
|
UhciRx {
|
||||||
|
uhci: unsafe { self.uhci.clone_unchecked() },
|
||||||
|
uart_rx,
|
||||||
|
channel_rx: self.channel.rx,
|
||||||
|
},
|
||||||
|
UhciTx {
|
||||||
|
uhci: self.uhci,
|
||||||
|
uart_tx,
|
||||||
|
channel_tx: self.channel.tx,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Uhci<'d, Blocking> {
|
||||||
|
/// Creates a new instance of UHCI
|
||||||
|
pub fn new(
|
||||||
|
uart: Uart<'d, Blocking>,
|
||||||
|
uhci: peripherals::UHCI0<'static>,
|
||||||
|
channel: impl DmaChannelFor<AnyUhci<'d>>,
|
||||||
|
) -> Self {
|
||||||
|
let channel = Channel::new(channel.degrade());
|
||||||
|
channel.runtime_ensure_compatible(&uhci);
|
||||||
|
|
||||||
|
let uhci = Uhci {
|
||||||
|
uart,
|
||||||
|
uhci: uhci.into(),
|
||||||
|
channel,
|
||||||
|
};
|
||||||
|
|
||||||
|
uhci.init();
|
||||||
|
uhci
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance in [crate::Async] mode.
|
||||||
|
pub fn into_async(self) -> Uhci<'d, Async> {
|
||||||
|
Uhci {
|
||||||
|
uart: self.uart.into_async(),
|
||||||
|
uhci: self.uhci,
|
||||||
|
channel: self.channel.into_async(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Uhci<'d, Async> {
|
||||||
|
/// Create a new instance in [crate::Blocking] mode.
|
||||||
|
pub fn into_blocking(self) -> Uhci<'d, Blocking> {
|
||||||
|
Uhci {
|
||||||
|
uart: self.uart.into_blocking(),
|
||||||
|
uhci: self.uhci,
|
||||||
|
channel: self.channel.into_blocking(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Splitted Uhci structs, Tx part for sending data
|
||||||
|
pub struct UhciTx<'d, Dm>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
{
|
||||||
|
uhci: AnyUhci<'static>,
|
||||||
|
uart_tx: UartTx<'d, Dm>,
|
||||||
|
channel_tx: ChannelTx<Dm, AnyGdmaTxChannel<'d>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, Dm> UhciTx<'d, Dm>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
{
|
||||||
|
/// Starts the write DMA transfer and returns the instance of UhciDmaTxTransfer
|
||||||
|
pub fn write<Buf: DmaTxBuffer>(
|
||||||
|
mut self,
|
||||||
|
mut tx_buffer: Buf,
|
||||||
|
) -> Result<UhciDmaTxTransfer<'d, Dm, Buf>, (Error, Self, Buf)> {
|
||||||
|
let res = unsafe {
|
||||||
|
self.channel_tx
|
||||||
|
.prepare_transfer(self.uhci.dma_peripheral(), &mut tx_buffer)
|
||||||
|
};
|
||||||
|
if let Err(err) = res {
|
||||||
|
return Err((err.into(), self, tx_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = self.channel_tx.start_transfer();
|
||||||
|
if let Err(err) = res {
|
||||||
|
return Err((err.into(), self, tx_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(UhciDmaTxTransfer::new(self, tx_buffer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Splitted Uhci structs, Rx part for receiving data
|
||||||
|
pub struct UhciRx<'d, Dm>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
{
|
||||||
|
uhci: AnyUhci<'static>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
uart_rx: UartRx<'d, Dm>,
|
||||||
|
channel_rx: ChannelRx<Dm, AnyGdmaRxChannel<'d>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, Dm> UhciRx<'d, Dm>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
{
|
||||||
|
/// Starts the read DMA transfer and returns the instance of UhciDmaRxTransfer
|
||||||
|
pub fn read<Buf: DmaRxBuffer>(
|
||||||
|
mut self,
|
||||||
|
mut rx_buffer: Buf,
|
||||||
|
) -> Result<UhciDmaRxTransfer<'d, Dm, Buf>, (Error, Self, Buf)> {
|
||||||
|
{
|
||||||
|
let res = unsafe {
|
||||||
|
self.channel_rx
|
||||||
|
.prepare_transfer(self.uhci.dma_peripheral(), &mut rx_buffer)
|
||||||
|
};
|
||||||
|
if let Err(err) = res {
|
||||||
|
return Err((err.into(), self, rx_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = self.channel_rx.start_transfer();
|
||||||
|
if let Err(err) = res {
|
||||||
|
return Err((err.into(), self, rx_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(UhciDmaRxTransfer::new(self, rx_buffer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A structure representing a DMA transfer for UHCI/UART.
|
||||||
|
///
|
||||||
|
/// This structure holds references to the UHCI instance, DMA buffers, and
|
||||||
|
/// transfer status.
|
||||||
|
pub struct UhciDmaTxTransfer<'d, Dm, Buf>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
Buf: DmaTxBuffer,
|
||||||
|
{
|
||||||
|
uhci: ManuallyDrop<UhciTx<'d, Dm>>,
|
||||||
|
dma_buf: ManuallyDrop<Buf::View>,
|
||||||
|
done: bool,
|
||||||
|
saved_err: Result<(), Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, Buf: DmaTxBuffer, Dm: DriverMode> UhciDmaTxTransfer<'d, Dm, Buf> {
|
||||||
|
fn new(uhci: UhciTx<'d, Dm>, dma_buf: Buf) -> Self {
|
||||||
|
Self {
|
||||||
|
uhci: ManuallyDrop::new(uhci),
|
||||||
|
dma_buf: ManuallyDrop::new(dma_buf.into_view()),
|
||||||
|
done: false,
|
||||||
|
saved_err: Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true when [Self::wait] will not block.
|
||||||
|
pub fn is_done(&self) -> bool {
|
||||||
|
self.uhci.channel_tx.is_done()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancels the DMA transfer.
|
||||||
|
pub fn cancel(mut self) -> (UhciTx<'d, Dm>, Buf::Final) {
|
||||||
|
self.uhci.channel_tx.stop_transfer();
|
||||||
|
|
||||||
|
let retval = unsafe {
|
||||||
|
(
|
||||||
|
ManuallyDrop::take(&mut self.uhci),
|
||||||
|
Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
core::mem::forget(self);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Waits for the DMA transfer to complete.
|
||||||
|
///
|
||||||
|
/// This method blocks until the transfer is finished and returns the
|
||||||
|
/// `Uhci` instance and the associated buffer.
|
||||||
|
pub fn wait(mut self) -> (Result<(), Error>, UhciTx<'d, Dm>, Buf::Final) {
|
||||||
|
if let Err(err) = self.saved_err {
|
||||||
|
return (
|
||||||
|
Err(err),
|
||||||
|
unsafe { ManuallyDrop::take(&mut self.uhci) },
|
||||||
|
unsafe { Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)) },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.done {
|
||||||
|
let res = self.uhci.uart_tx.flush();
|
||||||
|
if let Err(err) = res {
|
||||||
|
return (
|
||||||
|
Err(err.into()),
|
||||||
|
unsafe { ManuallyDrop::take(&mut self.uhci) },
|
||||||
|
unsafe { Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)) },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
while !self.is_done() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.uhci.channel_tx.stop_transfer();
|
||||||
|
let retval = unsafe {
|
||||||
|
(
|
||||||
|
Result::<(), Error>::Ok(()),
|
||||||
|
ManuallyDrop::take(&mut self.uhci),
|
||||||
|
Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
core::mem::forget(self);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, Buf: DmaTxBuffer> UhciDmaTxTransfer<'d, Async, Buf> {
|
||||||
|
/// Waits for the DMA transfer to complete, but async. After that, you still need to wait()
|
||||||
|
pub async fn wait_for_done(&mut self) {
|
||||||
|
// Workaround for an issue when it doesn't actually wait for the transfer to complete. I'm
|
||||||
|
// lost at this point, this is the only thing that worked
|
||||||
|
let res = self.uhci.uart_tx.flush_async().await;
|
||||||
|
if let Err(err) = res {
|
||||||
|
self.saved_err = Err(err.into());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = DmaTxFuture::new(&mut self.uhci.channel_tx).await;
|
||||||
|
if let Err(err) = res {
|
||||||
|
self.saved_err = Err(err.into());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Dm: DriverMode, Buf: DmaTxBuffer> Deref for UhciDmaTxTransfer<'_, Dm, Buf> {
|
||||||
|
type Target = Buf::View;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.dma_buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Dm: DriverMode, Buf: DmaTxBuffer> DerefMut for UhciDmaTxTransfer<'_, Dm, Buf> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.dma_buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Dm, Buf> Drop for UhciDmaTxTransfer<'_, Dm, Buf>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
Buf: DmaTxBuffer,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.uhci.channel_tx.stop_transfer();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ManuallyDrop::drop(&mut self.uhci);
|
||||||
|
drop(Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A structure representing a DMA transfer for UHCI/UART.
|
||||||
|
///
|
||||||
|
/// This structure holds references to the UHCI instance, DMA buffers, and
|
||||||
|
/// transfer status.
|
||||||
|
pub struct UhciDmaRxTransfer<'d, Dm, Buf>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
Buf: DmaRxBuffer,
|
||||||
|
{
|
||||||
|
uhci: ManuallyDrop<UhciRx<'d, Dm>>,
|
||||||
|
dma_buf: ManuallyDrop<Buf::View>,
|
||||||
|
done: bool,
|
||||||
|
saved_err: Result<(), Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, Buf: DmaRxBuffer, Dm: DriverMode> UhciDmaRxTransfer<'d, Dm, Buf> {
|
||||||
|
fn new(uhci: UhciRx<'d, Dm>, dma_buf: Buf) -> Self {
|
||||||
|
Self {
|
||||||
|
uhci: ManuallyDrop::new(uhci),
|
||||||
|
dma_buf: ManuallyDrop::new(dma_buf.into_view()),
|
||||||
|
done: false,
|
||||||
|
saved_err: Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true when [Self::wait] will not block.
|
||||||
|
pub fn is_done(&self) -> bool {
|
||||||
|
self.uhci.channel_rx.is_done()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancels the DMA transfer.
|
||||||
|
pub fn cancel(mut self) -> (UhciRx<'d, Dm>, Buf::Final) {
|
||||||
|
self.uhci.channel_rx.stop_transfer();
|
||||||
|
|
||||||
|
let retval = unsafe {
|
||||||
|
(
|
||||||
|
ManuallyDrop::take(&mut self.uhci),
|
||||||
|
Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
core::mem::forget(self);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Waits for the DMA transfer to complete.
|
||||||
|
///
|
||||||
|
/// This method blocks until the transfer is finished and returns the
|
||||||
|
/// `Uhci` instance and the associated buffer.
|
||||||
|
pub fn wait(mut self) -> (Result<(), Error>, UhciRx<'d, Dm>, Buf::Final) {
|
||||||
|
if let Err(err) = self.saved_err {
|
||||||
|
return (
|
||||||
|
Err(err),
|
||||||
|
unsafe { ManuallyDrop::take(&mut self.uhci) },
|
||||||
|
unsafe { Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)) },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.done {
|
||||||
|
while !self.is_done() {}
|
||||||
|
}
|
||||||
|
self.uhci.channel_rx.stop_transfer();
|
||||||
|
|
||||||
|
let retval = unsafe {
|
||||||
|
(
|
||||||
|
Result::<(), Error>::Ok(()),
|
||||||
|
ManuallyDrop::take(&mut self.uhci),
|
||||||
|
Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
core::mem::forget(self);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, Buf: DmaRxBuffer> UhciDmaRxTransfer<'d, Async, Buf> {
|
||||||
|
/// Waits for the DMA transfer to complete, but async. After that, you still need to wait()
|
||||||
|
pub async fn wait_for_done(&mut self) {
|
||||||
|
let res = DmaRxFuture::new(&mut self.uhci.channel_rx).await;
|
||||||
|
if let Err(err) = res {
|
||||||
|
self.saved_err = Err(err.into());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Dm: DriverMode, Buf: DmaRxBuffer> Deref for UhciDmaRxTransfer<'_, Dm, Buf> {
|
||||||
|
type Target = Buf::View;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.dma_buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Dm: DriverMode, Buf: DmaRxBuffer> DerefMut for UhciDmaRxTransfer<'_, Dm, Buf> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.dma_buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Dm, Buf> Drop for UhciDmaRxTransfer<'_, Dm, Buf>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
Buf: DmaRxBuffer,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.uhci.channel_rx.stop_transfer();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ManuallyDrop::drop(&mut self.uhci);
|
||||||
|
drop(Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -173,6 +173,11 @@ name = "uart_async"
|
|||||||
harness = false
|
harness = false
|
||||||
required-features = ["embassy"]
|
required-features = ["embassy"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "uart_uhci"
|
||||||
|
harness = false
|
||||||
|
required-features = ["embassy"]
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "uart_regression"
|
name = "uart_regression"
|
||||||
harness = false
|
harness = false
|
||||||
|
156
hil-test/tests/uart_uhci.rs
Normal file
156
hil-test/tests/uart_uhci.rs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
//! UART UHCI test, async
|
||||||
|
|
||||||
|
//% CHIPS: esp32c6
|
||||||
|
//% FEATURES: unstable embassy
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use esp_hal::{
|
||||||
|
Blocking,
|
||||||
|
dma::{DmaRxBuf, DmaTxBuf},
|
||||||
|
dma_buffers,
|
||||||
|
uart::{self, Uart, uhci::Uhci},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
uhci: Uhci<'static, Blocking>,
|
||||||
|
dma_rx: DmaRxBuf,
|
||||||
|
dma_tx: DmaTxBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
async fn init() -> Context {
|
||||||
|
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||||
|
|
||||||
|
let (rx, tx) = hil_test::common_test_pins!(peripherals);
|
||||||
|
|
||||||
|
let uart = Uart::new(peripherals.UART0, uart::Config::default())
|
||||||
|
.unwrap()
|
||||||
|
.with_tx(tx)
|
||||||
|
.with_rx(rx);
|
||||||
|
|
||||||
|
let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4092);
|
||||||
|
let dma_rx = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
|
||||||
|
let dma_tx = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
|
||||||
|
|
||||||
|
let mut uhci = Uhci::new(uart, peripherals.UHCI0, peripherals.DMA_CH0);
|
||||||
|
uhci.apply_config(&uart::uhci::Config::default().with_chunk_limit(dma_rx.len() as u16))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Context {
|
||||||
|
uhci,
|
||||||
|
dma_rx,
|
||||||
|
dma_tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_send_receive(mut ctx: Context) {
|
||||||
|
const SEND: &[u8] = b"Hello ESP32";
|
||||||
|
ctx.dma_tx.as_mut_slice()[0..SEND.len()].copy_from_slice(&SEND);
|
||||||
|
ctx.dma_tx.set_length(SEND.len());
|
||||||
|
|
||||||
|
let (uhci_rx, uhci_tx) = ctx.uhci.split();
|
||||||
|
let transfer_rx = uhci_rx
|
||||||
|
.read(ctx.dma_rx)
|
||||||
|
.unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
let transfer_tx = uhci_tx
|
||||||
|
.write(ctx.dma_tx)
|
||||||
|
.unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
let (res, _uhci_tx, _dma_tx) = transfer_tx.wait();
|
||||||
|
res.unwrap();
|
||||||
|
let (res, _uhci_rx, dma_rx) = transfer_rx.wait();
|
||||||
|
res.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&dma_rx.as_slice()[0..dma_rx.number_of_received_bytes()],
|
||||||
|
SEND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_long_strings(mut ctx: Context) {
|
||||||
|
const LONG_TEST_STRING: &str = "Loremipsumdolorsitamet,consecteturadipiscingelit.Suspendissemetusnisl,pretiumsedeuismodeget,bibendumeusem.Donecaccumsanrisusnibh,etefficiturnisivehiculatempus.Etiamegestasenimatduieleifendmaximus.Nuncinsemperest.Etiamvelodioultrices,interdumeratsed,dignissimmetus.Phasellusexleo,eleifendquisexid,laciniavenenatisneque.Sednuncdiam,molestieveltinciduntnec,ornareetnisi.Maecenasetmolestietortor.Nullaeupulvinarquam.Aeneanmolestieliberoquistortorviverralobortis.Praesentlaoreetlectusattinciduntscelerisque.Suspendisseegeterateleifend,posuerenuncvenenatis,faucibusdolor.Nuncvitaeluctusmetus.Nullamultriciesarcuvitaeestfermentumeleifend.Suspendisselaoreetmaximuslacus,utlaoreetnisiiaculisvitae.Nullamscelerisqueporttitorpulvinar.Intinciduntipsummauris,velaliquetmetusdictumut.Nunceratelit,suscipitacnisiac,volutpatporttitormauris.Aliquampretiumnisidiam,molestietemporlacusplaceratid.Mauristinciduntmattisturpis,velconvallisurnatempusnon.Integermattismetusnoneuismodcursus.Namideratetmassapretiumfinibus.Praesentfermentumnuncurna,quissagittismaurisimperdieteu.InLoremipsumdolorsitamet,consecteturadipiscingelit.Suspendissemetusnisl,pretiumsedeuismodeget,bibendumeusem.Donecaccumsanrisusnibh,etefficiturnisivehiculatempus.Etiamegestasenimatduieleifendmaximus.Nuncinsemperest.Etiamvelodioultrices,interdumeratsed,dignissimmetus.Phasellusexleo,eleifendquisexid,laciniavenenatisneque.Sednuncdiam,molestieveltinciduntnec,ornareetnisi.Maecenasetmolestietortor.Nullaeupulvinarquam.Aeneanmolestieliberoquistortorviverralobortis.Praesentlaoreetlectusattinciduntscelerisque.Suspendisseegeterateleifend,posuerenuncvenenatis,faucibusdolor.Nuncvitaeluctusmetus.Nullamultriciesarcuvitaeestfermentumeleifend.Suspendisselaoreetmaximuslacus,utlaoreetnisiiaculisvitae.Nullamscelerisqueporttitorpulvinar.Intinciduntipsummauris,velaliquetmetusdictumut.Nunceratelit,suscipitacnisiac,volutpatporttitormauris.Aliquampretiumnisidiam,molestietemporlacusplaceratid.Mauristinciduntmattisturpis,velconvallisurnatempusnon.Integermattismetusnoneuismodcursus.Namideratetmassapretiumfinibus.Praesentfermentumnuncurna,quissagittismaurisimperdieteu.Inefficituraliquamdui.Phasellussempermaurisacconvallismollis.Suspendisseintellusanuncvariusiaculisutegetlibero.Inmalesuada,nislquisconsecteturposuere,nullaipsumfringillaeros,egetrhoncussapienarcunecenim.Proinvenenatistortorveltristiquealiquam.Utelementumtellusligula,velauctorexfermentuma.Vestibulummaximusanteinvulputateornare.Sedquisnislaligulaporttitorfacilisismattissedmi.Crasconsecteturexegetsagittisfeugiat.Invenenatisminectinciduntaliquet.Sedcommodonecorciidvenenatis.Vestibulumanteipsumprimisinfaucibusorciluctusetultricesposuerecubiliacurae;Phasellusinterdumorcefficituraliquamdui.Phasellussempermaurisacconvallismollis.Suspendisseintellusanuncvariusiaculisutegetlibero.Inmalesuada,nislquisconsecteturposuere,nullaipsumfringillaeros,egetrhoncussapienarcunecenim.Proinvenenatistortorveltristiquealiquam.Utelementumtellusligula,velauctorexfermentuma.Vestibulummaximusanteinvulputateornare.Sedquisnislaligulaporttitorfacilisismattissedmi.Crasconsecteturexegetsagittisfeugiat.Invenenatisminectinciduntaliquet.Sedcommodonecorciidvenenatis.Vestibulumanteipsumprimisinfaucibusorciluctusetultricesposuerecubiliacurae;Phasellusinterdumorcrtis.Praesentlaoreetlectusattinciduntscelerisque.Suspendisseegeterateleifend,posuerenuncvenenatis,faucibusdolor.Nuncvitaeluctusmetus.Nullamultriciesarcuvitaeestfermentumeleifend.Suspendisselaoreetmaximuslacus,utlaoreetnisiiaculisvitae.Nullamscelerisqueporttitorpulvinar.Intinciduntipsummauris,velaliquetmetusdictumut.Nunceratelit,suscipitacnisiac,volutpatporttitormauris.Aliquampretiumnisidiam,molestietemporlacusplaceratid.Mauristinciduntmattisturpis,velconvallisurnatempusnon.Integermattismetusnoneuismodcursus.Namideratetmassapretiumfinibus.Praesentfermentumnuncurna,quissagittismaurisimperdieteu.Inefficituraliquamdui.Phasellussempermaurisacconvallismollis.Suspendisseintellusanuncvariusiaculisutegetlibero.Inmalesuada,nislquisconsecteturposuere,nullaipsumfringillaeros,egetrhoncussapienarcunece";
|
||||||
|
ctx.dma_tx.as_mut_slice()[0..LONG_TEST_STRING.len()]
|
||||||
|
.copy_from_slice(&LONG_TEST_STRING.as_bytes());
|
||||||
|
ctx.dma_tx.set_length(LONG_TEST_STRING.len());
|
||||||
|
|
||||||
|
let (uhci_rx, uhci_tx) = ctx.uhci.split();
|
||||||
|
let transfer_rx = uhci_rx
|
||||||
|
.read(ctx.dma_rx)
|
||||||
|
.unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
let transfer_tx = uhci_tx
|
||||||
|
.write(ctx.dma_tx)
|
||||||
|
.unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
let (res, _uhci_tx, _dma_tx) = transfer_tx.wait();
|
||||||
|
res.unwrap();
|
||||||
|
let (res, _uhci_rx, dma_rx) = transfer_rx.wait();
|
||||||
|
res.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&dma_rx.as_slice()[0..dma_rx.number_of_received_bytes()],
|
||||||
|
LONG_TEST_STRING.as_bytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_send_receive_async(mut ctx: Context) {
|
||||||
|
let uhci = ctx.uhci.into_async();
|
||||||
|
const SEND: &[u8] = b"Hello ESP32";
|
||||||
|
ctx.dma_tx.as_mut_slice()[0..SEND.len()].copy_from_slice(&SEND);
|
||||||
|
ctx.dma_tx.set_length(SEND.len());
|
||||||
|
|
||||||
|
let (uhci_rx, uhci_tx) = uhci.split();
|
||||||
|
let mut transfer_rx = uhci_rx
|
||||||
|
.read(ctx.dma_rx)
|
||||||
|
.unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
let mut transfer_tx = uhci_tx
|
||||||
|
.write(ctx.dma_tx)
|
||||||
|
.unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
transfer_tx.wait_for_done().await;
|
||||||
|
let (res, _uhci_tx, _dma_tx) = transfer_tx.wait();
|
||||||
|
res.unwrap();
|
||||||
|
transfer_rx.wait_for_done().await;
|
||||||
|
let (res, _uhci_rx, dma_rx) = transfer_rx.wait();
|
||||||
|
res.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&dma_rx.as_slice()[0..dma_rx.number_of_received_bytes()],
|
||||||
|
SEND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_long_strings_async(mut ctx: Context) {
|
||||||
|
let uhci = ctx.uhci.into_async();
|
||||||
|
const LONG_TEST_STRING: &str = "Loremipsumdolorsitamet,consecteturadipiscingelit.Suspendissemetusnisl,pretiumsedeuismodeget,bibendumeusem.Donecaccumsanrisusnibh,etefficiturnisivehiculatempus.Etiamegestasenimatduieleifendmaximus.Nuncinsemperest.Etiamvelodioultrices,interdumeratsed,dignissimmetus.Phasellusexleo,eleifendquisexid,laciniavenenatisneque.Sednuncdiam,molestieveltinciduntnec,ornareetnisi.Maecenasetmolestietortor.Nullaeupulvinarquam.Aeneanmolestieliberoquistortorviverralobortis.Praesentlaoreetlectusattinciduntscelerisque.Suspendisseegeterateleifend,posuerenuncvenenatis,faucibusdolor.Nuncvitaeluctusmetus.Nullamultriciesarcuvitaeestfermentumeleifend.Suspendisselaoreetmaximuslacus,utlaoreetnisiiaculisvitae.Nullamscelerisqueporttitorpulvinar.Intinciduntipsummauris,velaliquetmetusdictumut.Nunceratelit,suscipitacnisiac,volutpatporttitormauris.Aliquampretiumnisidiam,molestietemporlacusplaceratid.Mauristinciduntmattisturpis,velconvallisurnatempusnon.Integermattismetusnoneuismodcursus.Namideratetmassapretiumfinibus.Praesentfermentumnuncurna,quissagittismaurisimperdieteu.InLoremipsumdolorsitamet,consecteturadipiscingelit.Suspendissemetusnisl,pretiumsedeuismodeget,bibendumeusem.Donecaccumsanrisusnibh,etefficiturnisivehiculatempus.Etiamegestasenimatduieleifendmaximus.Nuncinsemperest.Etiamvelodioultrices,interdumeratsed,dignissimmetus.Phasellusexleo,eleifendquisexid,laciniavenenatisneque.Sednuncdiam,molestieveltinciduntnec,ornareetnisi.Maecenasetmolestietortor.Nullaeupulvinarquam.Aeneanmolestieliberoquistortorviverralobortis.Praesentlaoreetlectusattinciduntscelerisque.Suspendisseegeterateleifend,posuerenuncvenenatis,faucibusdolor.Nuncvitaeluctusmetus.Nullamultriciesarcuvitaeestfermentumeleifend.Suspendisselaoreetmaximuslacus,utlaoreetnisiiaculisvitae.Nullamscelerisqueporttitorpulvinar.Intinciduntipsummauris,velaliquetmetusdictumut.Nunceratelit,suscipitacnisiac,volutpatporttitormauris.Aliquampretiumnisidiam,molestietemporlacusplaceratid.Mauristinciduntmattisturpis,velconvallisurnatempusnon.Integermattismetusnoneuismodcursus.Namideratetmassapretiumfinibus.Praesentfermentumnuncurna,quissagittismaurisimperdieteu.Inefficituraliquamdui.Phasellussempermaurisacconvallismollis.Suspendisseintellusanuncvariusiaculisutegetlibero.Inmalesuada,nislquisconsecteturposuere,nullaipsumfringillaeros,egetrhoncussapienarcunecenim.Proinvenenatistortorveltristiquealiquam.Utelementumtellusligula,velauctorexfermentuma.Vestibulummaximusanteinvulputateornare.Sedquisnislaligulaporttitorfacilisismattissedmi.Crasconsecteturexegetsagittisfeugiat.Invenenatisminectinciduntaliquet.Sedcommodonecorciidvenenatis.Vestibulumanteipsumprimisinfaucibusorciluctusetultricesposuerecubiliacurae;Phasellusinterdumorcefficituraliquamdui.Phasellussempermaurisacconvallismollis.Suspendisseintellusanuncvariusiaculisutegetlibero.Inmalesuada,nislquisconsecteturposuere,nullaipsumfringillaeros,egetrhoncussapienarcunecenim.Proinvenenatistortorveltristiquealiquam.Utelementumtellusligula,velauctorexfermentuma.Vestibulummaximusanteinvulputateornare.Sedquisnislaligulaporttitorfacilisismattissedmi.Crasconsecteturexegetsagittisfeugiat.Invenenatisminectinciduntaliquet.Sedcommodonecorciidvenenatis.Vestibulumanteipsumprimisinfaucibusorciluctusetultricesposuerecubiliacurae;Phasellusinterdumorcrtis.Praesentlaoreetlectusattinciduntscelerisque.Suspendisseegeterateleifend,posuerenuncvenenatis,faucibusdolor.Nuncvitaeluctusmetus.Nullamultriciesarcuvitaeestfermentumeleifend.Suspendisselaoreetmaximuslacus,utlaoreetnisiiaculisvitae.Nullamscelerisqueporttitorpulvinar.Intinciduntipsummauris,velaliquetmetusdictumut.Nunceratelit,suscipitacnisiac,volutpatporttitormauris.Aliquampretiumnisidiam,molestietemporlacusplaceratid.Mauristinciduntmattisturpis,velconvallisurnatempusnon.Integermattismetusnoneuismodcursus.Namideratetmassapretiumfinibus.Praesentfermentumnuncurna,quissagittismaurisimperdieteu.Inefficituraliquamdui.Phasellussempermaurisacconvallismollis.Suspendisseintellusanuncvariusiaculisutegetlibero.Inmalesuada,nislquisconsecteturposuere,nullaipsumfringillaeros,egetrhoncussapienarcunece";
|
||||||
|
ctx.dma_tx.as_mut_slice()[0..LONG_TEST_STRING.len()]
|
||||||
|
.copy_from_slice(&LONG_TEST_STRING.as_bytes());
|
||||||
|
ctx.dma_tx.set_length(LONG_TEST_STRING.len());
|
||||||
|
|
||||||
|
let (uhci_rx, uhci_tx) = uhci.split();
|
||||||
|
let mut transfer_rx = uhci_rx
|
||||||
|
.read(ctx.dma_rx)
|
||||||
|
.unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
let mut transfer_tx = uhci_tx
|
||||||
|
.write(ctx.dma_tx)
|
||||||
|
.unwrap_or_else(|x| panic!("Something went horribly wrong: {:?}", x.0));
|
||||||
|
transfer_tx.wait_for_done().await;
|
||||||
|
let (res, _uhci_tx, _dma_tx) = transfer_tx.wait();
|
||||||
|
res.unwrap();
|
||||||
|
transfer_rx.wait_for_done().await;
|
||||||
|
let (res, _uhci_rx, dma_rx) = transfer_rx.wait();
|
||||||
|
res.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&dma_rx.as_slice()[0..dma_rx.number_of_received_bytes()],
|
||||||
|
LONG_TEST_STRING.as_bytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user