diff options
| -rw-r--r-- | embassy-stm32/src/i2c/v2.rs | 110 |
1 files changed, 49 insertions, 61 deletions
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 39e6702e5..853bc128f 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | use core::cmp; | 1 | use core::cmp; |
| 2 | use core::future::poll_fn; | 2 | use core::future::poll_fn; |
| 3 | use core::sync::atomic::{AtomicUsize, Ordering}; | ||
| 4 | use core::task::Poll; | 3 | use core::task::Poll; |
| 5 | 4 | ||
| 6 | use embassy_embedded_hal::SetConfig; | 5 | use embassy_embedded_hal::SetConfig; |
| @@ -35,14 +34,12 @@ impl Default for Config { | |||
| 35 | 34 | ||
| 36 | pub struct State { | 35 | pub struct State { |
| 37 | waker: AtomicWaker, | 36 | waker: AtomicWaker, |
| 38 | chunks_transferred: AtomicUsize, | ||
| 39 | } | 37 | } |
| 40 | 38 | ||
| 41 | impl State { | 39 | impl State { |
| 42 | pub(crate) const fn new() -> Self { | 40 | pub(crate) const fn new() -> Self { |
| 43 | Self { | 41 | Self { |
| 44 | waker: AtomicWaker::new(), | 42 | waker: AtomicWaker::new(), |
| 45 | chunks_transferred: AtomicUsize::new(0), | ||
| 46 | } | 43 | } |
| 47 | } | 44 | } |
| 48 | } | 45 | } |
| @@ -130,10 +127,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 130 | let isr = regs.isr().read(); | 127 | let isr = regs.isr().read(); |
| 131 | 128 | ||
| 132 | if isr.tcr() || isr.tc() { | 129 | if isr.tcr() || isr.tc() { |
| 133 | let state = T::state(); | 130 | T::state().waker.wake(); |
| 134 | let transferred = state.chunks_transferred.load(Ordering::Relaxed); | ||
| 135 | state.chunks_transferred.store(transferred + 1, Ordering::Relaxed); | ||
| 136 | state.waker.wake(); | ||
| 137 | } | 131 | } |
| 138 | // The flag can only be cleared by writting to nbytes, we won't do that here, so disable | 132 | // The flag can only be cleared by writting to nbytes, we won't do that here, so disable |
| 139 | // the interrupt | 133 | // the interrupt |
| @@ -457,12 +451,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 457 | TXDMA: crate::i2c::TxDma<T>, | 451 | TXDMA: crate::i2c::TxDma<T>, |
| 458 | { | 452 | { |
| 459 | let total_len = write.len(); | 453 | let total_len = write.len(); |
| 460 | let completed_chunks = total_len / 255; | ||
| 461 | let total_chunks = if completed_chunks * 255 == total_len { | ||
| 462 | completed_chunks | ||
| 463 | } else { | ||
| 464 | completed_chunks + 1 | ||
| 465 | }; | ||
| 466 | 454 | ||
| 467 | let dma_transfer = unsafe { | 455 | let dma_transfer = unsafe { |
| 468 | let regs = T::regs(); | 456 | let regs = T::regs(); |
| @@ -480,7 +468,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 480 | }; | 468 | }; |
| 481 | 469 | ||
| 482 | let state = T::state(); | 470 | let state = T::state(); |
| 483 | state.chunks_transferred.store(0, Ordering::Relaxed); | ||
| 484 | let mut remaining_len = total_len; | 471 | let mut remaining_len = total_len; |
| 485 | 472 | ||
| 486 | let on_drop = OnDrop::new(|| { | 473 | let on_drop = OnDrop::new(|| { |
| @@ -495,33 +482,35 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 495 | } | 482 | } |
| 496 | }); | 483 | }); |
| 497 | 484 | ||
| 498 | // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers | ||
| 499 | if first_slice { | ||
| 500 | unsafe { | ||
| 501 | Self::master_write( | ||
| 502 | address, | ||
| 503 | total_len.min(255), | ||
| 504 | Stop::Software, | ||
| 505 | (total_chunks != 1) || !last_slice, | ||
| 506 | &check_timeout, | ||
| 507 | )?; | ||
| 508 | } | ||
| 509 | } else { | ||
| 510 | unsafe { | ||
| 511 | Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?; | ||
| 512 | T::regs().cr1().modify(|w| w.set_tcie(true)); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | poll_fn(|cx| { | 485 | poll_fn(|cx| { |
| 517 | state.waker.register(cx.waker()); | 486 | state.waker.register(cx.waker()); |
| 518 | let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); | ||
| 519 | 487 | ||
| 520 | if chunks_transferred == total_chunks { | 488 | let isr = unsafe { T::regs().isr().read() }; |
| 489 | if remaining_len == total_len { | ||
| 490 | // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers | ||
| 491 | if first_slice { | ||
| 492 | unsafe { | ||
| 493 | Self::master_write( | ||
| 494 | address, | ||
| 495 | total_len.min(255), | ||
| 496 | Stop::Software, | ||
| 497 | (total_len > 255) || !last_slice, | ||
| 498 | &check_timeout, | ||
| 499 | )?; | ||
| 500 | } | ||
| 501 | } else { | ||
| 502 | unsafe { | ||
| 503 | Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?; | ||
| 504 | T::regs().cr1().modify(|w| w.set_tcie(true)); | ||
| 505 | } | ||
| 506 | } | ||
| 507 | } else if !(isr.tcr() || isr.tc()) { | ||
| 508 | // poll_fn was woken without an interrupt present | ||
| 509 | return Poll::Pending; | ||
| 510 | } else if remaining_len == 0 { | ||
| 521 | return Poll::Ready(Ok(())); | 511 | return Poll::Ready(Ok(())); |
| 522 | } else if chunks_transferred != 0 { | 512 | } else { |
| 523 | remaining_len = remaining_len.saturating_sub(255); | 513 | let last_piece = (remaining_len <= 255) && last_slice; |
| 524 | let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice; | ||
| 525 | 514 | ||
| 526 | // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers | 515 | // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers |
| 527 | unsafe { | 516 | unsafe { |
| @@ -531,6 +520,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 531 | T::regs().cr1().modify(|w| w.set_tcie(true)); | 520 | T::regs().cr1().modify(|w| w.set_tcie(true)); |
| 532 | } | 521 | } |
| 533 | } | 522 | } |
| 523 | |||
| 524 | remaining_len = remaining_len.saturating_sub(255); | ||
| 534 | Poll::Pending | 525 | Poll::Pending |
| 535 | }) | 526 | }) |
| 536 | .await?; | 527 | .await?; |
| @@ -559,12 +550,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 559 | RXDMA: crate::i2c::RxDma<T>, | 550 | RXDMA: crate::i2c::RxDma<T>, |
| 560 | { | 551 | { |
| 561 | let total_len = buffer.len(); | 552 | let total_len = buffer.len(); |
| 562 | let completed_chunks = total_len / 255; | ||
| 563 | let total_chunks = if completed_chunks * 255 == total_len { | ||
| 564 | completed_chunks | ||
| 565 | } else { | ||
| 566 | completed_chunks + 1 | ||
| 567 | }; | ||
| 568 | 553 | ||
| 569 | let dma_transfer = unsafe { | 554 | let dma_transfer = unsafe { |
| 570 | let regs = T::regs(); | 555 | let regs = T::regs(); |
| @@ -580,7 +565,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 580 | }; | 565 | }; |
| 581 | 566 | ||
| 582 | let state = T::state(); | 567 | let state = T::state(); |
| 583 | state.chunks_transferred.store(0, Ordering::Relaxed); | ||
| 584 | let mut remaining_len = total_len; | 568 | let mut remaining_len = total_len; |
| 585 | 569 | ||
| 586 | let on_drop = OnDrop::new(|| { | 570 | let on_drop = OnDrop::new(|| { |
| @@ -593,27 +577,29 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 593 | } | 577 | } |
| 594 | }); | 578 | }); |
| 595 | 579 | ||
| 596 | // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers | ||
| 597 | unsafe { | ||
| 598 | Self::master_read( | ||
| 599 | address, | ||
| 600 | total_len.min(255), | ||
| 601 | Stop::Software, | ||
| 602 | total_chunks != 1, | ||
| 603 | restart, | ||
| 604 | &check_timeout, | ||
| 605 | )?; | ||
| 606 | } | ||
| 607 | |||
| 608 | poll_fn(|cx| { | 580 | poll_fn(|cx| { |
| 609 | state.waker.register(cx.waker()); | 581 | state.waker.register(cx.waker()); |
| 610 | let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); | ||
| 611 | 582 | ||
| 612 | if chunks_transferred == total_chunks { | 583 | let isr = unsafe { T::regs().isr().read() }; |
| 584 | if remaining_len == total_len { | ||
| 585 | // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers | ||
| 586 | unsafe { | ||
| 587 | Self::master_read( | ||
| 588 | address, | ||
| 589 | total_len.min(255), | ||
| 590 | Stop::Software, | ||
| 591 | total_len > 255, | ||
| 592 | restart, | ||
| 593 | &check_timeout, | ||
| 594 | )?; | ||
| 595 | } | ||
| 596 | } else if !(isr.tcr() || isr.tc()) { | ||
| 597 | // poll_fn was woken without an interrupt present | ||
| 598 | return Poll::Pending; | ||
| 599 | } else if remaining_len == 0 { | ||
| 613 | return Poll::Ready(Ok(())); | 600 | return Poll::Ready(Ok(())); |
| 614 | } else if chunks_transferred != 0 { | 601 | } else { |
| 615 | remaining_len = remaining_len.saturating_sub(255); | 602 | let last_piece = remaining_len <= 255; |
| 616 | let last_piece = chunks_transferred + 1 == total_chunks; | ||
| 617 | 603 | ||
| 618 | // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers | 604 | // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers |
| 619 | unsafe { | 605 | unsafe { |
| @@ -623,6 +609,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 623 | T::regs().cr1().modify(|w| w.set_tcie(true)); | 609 | T::regs().cr1().modify(|w| w.set_tcie(true)); |
| 624 | } | 610 | } |
| 625 | } | 611 | } |
| 612 | |||
| 613 | remaining_len = remaining_len.saturating_sub(255); | ||
| 626 | Poll::Pending | 614 | Poll::Pending |
| 627 | }) | 615 | }) |
| 628 | .await?; | 616 | .await?; |
