[UCPD] Implement PD transmitter
This commit is contained in:
parent
984d5bbc72
commit
36a9918921
1 changed files with 80 additions and 1 deletions
|
@ -24,7 +24,7 @@ use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use crate::dma::{AnyChannel, Request, Transfer, TransferOptions};
|
use crate::dma::{AnyChannel, Request, Transfer, TransferOptions};
|
||||||
use crate::interrupt;
|
use crate::interrupt;
|
||||||
use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk};
|
use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode};
|
||||||
pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState};
|
pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState};
|
||||||
use crate::rcc::RccPeripheral;
|
use crate::rcc::RccPeripheral;
|
||||||
|
|
||||||
|
@ -194,6 +194,9 @@ impl<'d, T: Instance> Ucpd<'d, T> {
|
||||||
w.set_phyrxen(true);
|
w.set_phyrxen(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: Currently only SOP messages are supported.
|
||||||
|
r.tx_ordsetr().write(|w| w.set_txordset(0b10001_11000_11000_11000));
|
||||||
|
|
||||||
into_ref!(rx_dma, tx_dma);
|
into_ref!(rx_dma, tx_dma);
|
||||||
let rx_dma_req = rx_dma.request();
|
let rx_dma_req = rx_dma.request();
|
||||||
let tx_dma_req = tx_dma.request();
|
let tx_dma_req = tx_dma.request();
|
||||||
|
@ -308,6 +311,13 @@ impl<'d, T: Instance> PdRx<'d, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transmit Error.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum TxError {
|
||||||
|
/// Concurrent receive in progress or excessive noise on the line.
|
||||||
|
Discarded,
|
||||||
|
}
|
||||||
|
|
||||||
/// Power Delivery (PD) Transmitter.
|
/// Power Delivery (PD) Transmitter.
|
||||||
pub struct PdTx<'d, T: Instance> {
|
pub struct PdTx<'d, T: Instance> {
|
||||||
_ucpd: &'d Ucpd<'d, T>,
|
_ucpd: &'d Ucpd<'d, T>,
|
||||||
|
@ -315,6 +325,68 @@ pub struct PdTx<'d, T: Instance> {
|
||||||
dma_req: Request,
|
dma_req: Request,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> PdTx<'d, T> {
|
||||||
|
/// Transmits a PD message.
|
||||||
|
pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), TxError> {
|
||||||
|
let r = T::REGS;
|
||||||
|
|
||||||
|
// When a previous transmission was dropped before it had finished it
|
||||||
|
// might still be running because there is no way to abort an ongoing
|
||||||
|
// message transmission. Wait for it to finish but ignore errors.
|
||||||
|
if r.cr().read().txsend() {
|
||||||
|
let _ = self.wait_tx_done().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the TX interrupt flags.
|
||||||
|
T::REGS.icr().write(|w| {
|
||||||
|
w.set_txmsgdisccf(true);
|
||||||
|
w.set_txmsgsentcf(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the DMA and let it do its thing in the background.
|
||||||
|
let _dma = unsafe {
|
||||||
|
Transfer::new_write(
|
||||||
|
&self.dma_ch,
|
||||||
|
self.dma_req,
|
||||||
|
buf,
|
||||||
|
r.txdr().as_ptr() as *mut u8,
|
||||||
|
TransferOptions::default(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure and start the transmission.
|
||||||
|
r.tx_payszr().write(|w| w.set_txpaysz(buf.len() as _));
|
||||||
|
r.cr().modify(|w| {
|
||||||
|
w.set_txmode(Txmode::PACKET);
|
||||||
|
w.set_txsend(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.wait_tx_done().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_tx_done(&self) -> Result<(), TxError> {
|
||||||
|
poll_fn(|cx| {
|
||||||
|
let r = T::REGS;
|
||||||
|
if r.sr().read().txmsgdisc() {
|
||||||
|
Poll::Ready(Err(TxError::Discarded))
|
||||||
|
} else if r.sr().read().txmsgsent() {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
// Enable transmit interrupts.
|
||||||
|
T::waker().register(cx.waker());
|
||||||
|
r.imr().modify(|w| {
|
||||||
|
w.set_txmsgdiscie(true);
|
||||||
|
w.set_txmsgsentie(true);
|
||||||
|
});
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_tx_flags(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
/// Interrupt handler.
|
/// Interrupt handler.
|
||||||
pub struct InterruptHandler<T: Instance> {
|
pub struct InterruptHandler<T: Instance> {
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
|
@ -336,6 +408,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||||
r.imr().modify(|w| w.set_rxmsgendie(false));
|
r.imr().modify(|w| w.set_rxmsgendie(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sr.txmsgdisc() || sr.txmsgsent() {
|
||||||
|
r.imr().modify(|w| {
|
||||||
|
w.set_txmsgdiscie(false);
|
||||||
|
w.set_txmsgsentie(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Wake the task to clear and re-enabled interrupts.
|
// Wake the task to clear and re-enabled interrupts.
|
||||||
T::waker().wake();
|
T::waker().wake();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue