diff options
| author | Timo Kröger <[email protected]> | 2024-03-07 18:35:05 +0100 |
|---|---|---|
| committer | Timo Kröger <[email protected]> | 2024-03-12 08:14:42 +0100 |
| commit | 36a99189210bf15c93198a4bf9a0d2ab732c8bf8 (patch) | |
| tree | 1bcdf15955fc8423b397e460a9a59750479a0452 | |
| parent | 984d5bbc720dfe9acf22400d2462c0ffce52a7cb (diff) | |
[UCPD] Implement PD transmitter
| -rw-r--r-- | embassy-stm32/src/ucpd.rs | 81 |
1 files changed, 80 insertions, 1 deletions
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index f3f225d0c..0a6f7df71 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs | |||
| @@ -24,7 +24,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 24 | 24 | ||
| 25 | use crate::dma::{AnyChannel, Request, Transfer, TransferOptions}; | 25 | use crate::dma::{AnyChannel, Request, Transfer, TransferOptions}; |
| 26 | use crate::interrupt; | 26 | use crate::interrupt; |
| 27 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk}; | 27 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; |
| 28 | pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState}; | 28 | pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState}; |
| 29 | use crate::rcc::RccPeripheral; | 29 | use crate::rcc::RccPeripheral; |
| 30 | 30 | ||
| @@ -194,6 +194,9 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 194 | w.set_phyrxen(true); | 194 | w.set_phyrxen(true); |
| 195 | }); | 195 | }); |
| 196 | 196 | ||
| 197 | // TODO: Currently only SOP messages are supported. | ||
| 198 | r.tx_ordsetr().write(|w| w.set_txordset(0b10001_11000_11000_11000)); | ||
| 199 | |||
| 197 | into_ref!(rx_dma, tx_dma); | 200 | into_ref!(rx_dma, tx_dma); |
| 198 | let rx_dma_req = rx_dma.request(); | 201 | let rx_dma_req = rx_dma.request(); |
| 199 | let tx_dma_req = tx_dma.request(); | 202 | let tx_dma_req = tx_dma.request(); |
| @@ -308,6 +311,13 @@ impl<'d, T: Instance> PdRx<'d, T> { | |||
| 308 | } | 311 | } |
| 309 | } | 312 | } |
| 310 | 313 | ||
| 314 | /// Transmit Error. | ||
| 315 | #[derive(Debug, Clone, Copy)] | ||
| 316 | pub enum TxError { | ||
| 317 | /// Concurrent receive in progress or excessive noise on the line. | ||
| 318 | Discarded, | ||
| 319 | } | ||
| 320 | |||
| 311 | /// Power Delivery (PD) Transmitter. | 321 | /// Power Delivery (PD) Transmitter. |
| 312 | pub struct PdTx<'d, T: Instance> { | 322 | pub struct PdTx<'d, T: Instance> { |
| 313 | _ucpd: &'d Ucpd<'d, T>, | 323 | _ucpd: &'d Ucpd<'d, T>, |
| @@ -315,6 +325,68 @@ pub struct PdTx<'d, T: Instance> { | |||
| 315 | dma_req: Request, | 325 | dma_req: Request, |
| 316 | } | 326 | } |
| 317 | 327 | ||
| 328 | impl<'d, T: Instance> PdTx<'d, T> { | ||
| 329 | /// Transmits a PD message. | ||
| 330 | pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), TxError> { | ||
| 331 | let r = T::REGS; | ||
| 332 | |||
| 333 | // When a previous transmission was dropped before it had finished it | ||
| 334 | // might still be running because there is no way to abort an ongoing | ||
| 335 | // message transmission. Wait for it to finish but ignore errors. | ||
| 336 | if r.cr().read().txsend() { | ||
| 337 | let _ = self.wait_tx_done().await; | ||
| 338 | } | ||
| 339 | |||
| 340 | // Clear the TX interrupt flags. | ||
| 341 | T::REGS.icr().write(|w| { | ||
| 342 | w.set_txmsgdisccf(true); | ||
| 343 | w.set_txmsgsentcf(true); | ||
| 344 | }); | ||
| 345 | |||
| 346 | // Start the DMA and let it do its thing in the background. | ||
| 347 | let _dma = unsafe { | ||
| 348 | Transfer::new_write( | ||
| 349 | &self.dma_ch, | ||
| 350 | self.dma_req, | ||
| 351 | buf, | ||
| 352 | r.txdr().as_ptr() as *mut u8, | ||
| 353 | TransferOptions::default(), | ||
| 354 | ) | ||
| 355 | }; | ||
| 356 | |||
| 357 | // Configure and start the transmission. | ||
| 358 | r.tx_payszr().write(|w| w.set_txpaysz(buf.len() as _)); | ||
| 359 | r.cr().modify(|w| { | ||
| 360 | w.set_txmode(Txmode::PACKET); | ||
| 361 | w.set_txsend(true); | ||
| 362 | }); | ||
| 363 | |||
| 364 | self.wait_tx_done().await | ||
| 365 | } | ||
| 366 | |||
| 367 | async fn wait_tx_done(&self) -> Result<(), TxError> { | ||
| 368 | poll_fn(|cx| { | ||
| 369 | let r = T::REGS; | ||
| 370 | if r.sr().read().txmsgdisc() { | ||
| 371 | Poll::Ready(Err(TxError::Discarded)) | ||
| 372 | } else if r.sr().read().txmsgsent() { | ||
| 373 | Poll::Ready(Ok(())) | ||
| 374 | } else { | ||
| 375 | // Enable transmit interrupts. | ||
| 376 | T::waker().register(cx.waker()); | ||
| 377 | r.imr().modify(|w| { | ||
| 378 | w.set_txmsgdiscie(true); | ||
| 379 | w.set_txmsgsentie(true); | ||
| 380 | }); | ||
| 381 | Poll::Pending | ||
| 382 | } | ||
| 383 | }) | ||
| 384 | .await | ||
| 385 | } | ||
| 386 | |||
| 387 | fn clear_tx_flags(&self) {} | ||
| 388 | } | ||
| 389 | |||
| 318 | /// Interrupt handler. | 390 | /// Interrupt handler. |
| 319 | pub struct InterruptHandler<T: Instance> { | 391 | pub struct InterruptHandler<T: Instance> { |
| 320 | _phantom: PhantomData<T>, | 392 | _phantom: PhantomData<T>, |
| @@ -336,6 +408,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 336 | r.imr().modify(|w| w.set_rxmsgendie(false)); | 408 | r.imr().modify(|w| w.set_rxmsgendie(false)); |
| 337 | } | 409 | } |
| 338 | 410 | ||
| 411 | if sr.txmsgdisc() || sr.txmsgsent() { | ||
| 412 | r.imr().modify(|w| { | ||
| 413 | w.set_txmsgdiscie(false); | ||
| 414 | w.set_txmsgsentie(false); | ||
| 415 | }); | ||
| 416 | } | ||
| 417 | |||
| 339 | // Wake the task to clear and re-enabled interrupts. | 418 | // Wake the task to clear and re-enabled interrupts. |
| 340 | T::waker().wake(); | 419 | T::waker().wake(); |
| 341 | } | 420 | } |
