diff options
| author | Andres Vahter <[email protected]> | 2023-11-10 15:49:31 +0200 |
|---|---|---|
| committer | Andres Vahter <[email protected]> | 2023-11-10 16:04:25 +0200 |
| commit | 3b33cc46915f0b400a2a9dd87cfe51eca9f7e434 (patch) | |
| tree | a4dcddb350847c19b1d607cc4ed3b7e449d58d6c /embassy-stm32 | |
| parent | b3367be9c8f0eb54d500dc2e6f652ade05859088 (diff) | |
i2c: expose async api without needing time
This exposes I2C async API without needing "time" feature. With "time" feature additional async API with timeouts is exposed.
Diffstat (limited to 'embassy-stm32')
| -rw-r--r-- | embassy-stm32/src/i2c/v2.rs | 207 |
1 files changed, 175 insertions, 32 deletions
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index fc6dcd6ed..7049affe8 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -1,21 +1,14 @@ | |||
| 1 | use core::cmp; | 1 | use core::cmp; |
| 2 | #[cfg(feature = "time")] | 2 | use core::future::{poll_fn, Future}; |
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 5 | #[cfg(feature = "time")] | ||
| 6 | use core::task::Poll; | 4 | use core::task::Poll; |
| 7 | 5 | ||
| 8 | use embassy_embedded_hal::SetConfig; | 6 | use embassy_embedded_hal::SetConfig; |
| 9 | #[cfg(feature = "time")] | ||
| 10 | use embassy_hal_internal::drop::OnDrop; | 7 | use embassy_hal_internal::drop::OnDrop; |
| 11 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 13 | #[cfg(feature = "time")] | ||
| 14 | use embassy_time::{Duration, Instant}; | ||
| 15 | 10 | ||
| 16 | use crate::dma::NoDma; | 11 | use crate::dma::{NoDma, Transfer}; |
| 17 | #[cfg(feature = "time")] | ||
| 18 | use crate::dma::Transfer; | ||
| 19 | use crate::gpio::sealed::AFType; | 12 | use crate::gpio::sealed::AFType; |
| 20 | use crate::gpio::Pull; | 13 | use crate::gpio::Pull; |
| 21 | use crate::i2c::{Error, Instance, SclPin, SdaPin}; | 14 | use crate::i2c::{Error, Instance, SclPin, SdaPin}; |
| @@ -24,6 +17,92 @@ use crate::pac::i2c; | |||
| 24 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 25 | use crate::{interrupt, Peripheral}; | 18 | use crate::{interrupt, Peripheral}; |
| 26 | 19 | ||
| 20 | /// # Async I2C Operations with Optional Timeouts | ||
| 21 | /// | ||
| 22 | /// This module provides compatibility for async I2C operations with timeout-based APIs, | ||
| 23 | /// even when the "time" feature is not enabled. In the absence of the "time" feature, | ||
| 24 | /// operations effectively never time out. | ||
| 25 | /// | ||
| 26 | /// ## Usage Scenario | ||
| 27 | /// This is particularly useful in scenarios such as when using RTIC, where a user might | ||
| 28 | /// have their own monotonic timer and thus choose not to enable the "time" feature. | ||
| 29 | /// In such cases, this module allows the use of async I2C APIs without actual timeout | ||
| 30 | /// handling. | ||
| 31 | /// | ||
| 32 | /// ## Functionality | ||
| 33 | /// - When the "time" feature is disabled, `Duration` and `Instant` types are provided | ||
| 34 | /// as dummy implementations, and timeout functions do not perform real timing but | ||
| 35 | /// simply mimic the required interfaces. | ||
| 36 | /// - When the "time" feature is enabled, `Duration` and `Instant` from the `embassy_time` | ||
| 37 | /// are used, and timeouts are handled as expected. | ||
| 38 | #[cfg(not(feature = "time"))] | ||
| 39 | mod dummy_time { | ||
| 40 | use core::ops::Sub; | ||
| 41 | |||
| 42 | use super::{Error, Future}; | ||
| 43 | |||
| 44 | #[derive(Copy, Clone)] | ||
| 45 | pub struct Duration; | ||
| 46 | |||
| 47 | impl Duration { | ||
| 48 | pub fn dummy_duration() -> Duration { | ||
| 49 | Duration | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | pub struct Instant; | ||
| 54 | |||
| 55 | impl Instant { | ||
| 56 | pub fn now() -> Self { | ||
| 57 | Self | ||
| 58 | } | ||
| 59 | |||
| 60 | pub fn duration_since(&self, _since: Instant) -> Duration { | ||
| 61 | Duration | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | impl Sub for Duration { | ||
| 66 | type Output = Duration; | ||
| 67 | |||
| 68 | fn sub(self, _rhs: Duration) -> Duration { | ||
| 69 | Duration | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Timeout that never times out. | ||
| 74 | pub fn timeout_fn(_timeout: Duration) -> impl Fn() -> Result<(), Error> { | ||
| 75 | move || Ok(()) | ||
| 76 | } | ||
| 77 | |||
| 78 | /// This is compatible with `embassy_time::with_timeout` however it never times out. | ||
| 79 | pub async fn with_timeout<F: Future>(_timeout: Duration, fut: F) -> Result<F::Output, ()> { | ||
| 80 | Ok(fut.await) | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | #[cfg(not(feature = "time"))] | ||
| 85 | use dummy_time::{timeout_fn, with_timeout, Duration, Instant}; | ||
| 86 | #[cfg(feature = "time")] | ||
| 87 | use embassy_time::{Duration, Instant}; | ||
| 88 | |||
| 89 | #[cfg(feature = "time")] | ||
| 90 | fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { | ||
| 91 | let deadline = Instant::now() + timeout; | ||
| 92 | move || { | ||
| 93 | if Instant::now() > deadline { | ||
| 94 | Err(Error::Timeout) | ||
| 95 | } else { | ||
| 96 | Ok(()) | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | #[cfg(feature = "time")] | ||
| 102 | async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, embassy_time::TimeoutError> { | ||
| 103 | embassy_time::with_timeout(timeout, fut).await | ||
| 104 | } | ||
| 105 | |||
| 27 | /// Interrupt handler. | 106 | /// Interrupt handler. |
| 28 | pub struct InterruptHandler<T: Instance> { | 107 | pub struct InterruptHandler<T: Instance> { |
| 29 | _phantom: PhantomData<T>, | 108 | _phantom: PhantomData<T>, |
| @@ -437,7 +516,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 437 | result | 516 | result |
| 438 | } | 517 | } |
| 439 | 518 | ||
| 440 | #[cfg(feature = "time")] | ||
| 441 | async fn write_dma_internal( | 519 | async fn write_dma_internal( |
| 442 | &mut self, | 520 | &mut self, |
| 443 | address: u8, | 521 | address: u8, |
| @@ -528,7 +606,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 528 | Ok(()) | 606 | Ok(()) |
| 529 | } | 607 | } |
| 530 | 608 | ||
| 531 | #[cfg(feature = "time")] | ||
| 532 | async fn read_dma_internal( | 609 | async fn read_dma_internal( |
| 533 | &mut self, | 610 | &mut self, |
| 534 | address: u8, | 611 | address: u8, |
| @@ -616,7 +693,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 616 | where | 693 | where |
| 617 | TXDMA: crate::i2c::TxDma<T>, | 694 | TXDMA: crate::i2c::TxDma<T>, |
| 618 | { | 695 | { |
| 619 | self.write_timeout(address, write, self.timeout).await | 696 | self.write_timeout_internal(address, write, self.timeout).await |
| 697 | } | ||
| 698 | |||
| 699 | #[cfg(not(feature = "time"))] | ||
| 700 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> | ||
| 701 | where | ||
| 702 | TXDMA: crate::i2c::TxDma<T>, | ||
| 703 | { | ||
| 704 | self.write_timeout_internal(address, write, Duration::dummy_duration()) | ||
| 705 | .await | ||
| 620 | } | 706 | } |
| 621 | 707 | ||
| 622 | #[cfg(feature = "time")] | 708 | #[cfg(feature = "time")] |
| @@ -624,10 +710,17 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 624 | where | 710 | where |
| 625 | TXDMA: crate::i2c::TxDma<T>, | 711 | TXDMA: crate::i2c::TxDma<T>, |
| 626 | { | 712 | { |
| 713 | self.write_timeout_internal(address, write, timeout).await | ||
| 714 | } | ||
| 715 | |||
| 716 | async fn write_timeout_internal(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> | ||
| 717 | where | ||
| 718 | TXDMA: crate::i2c::TxDma<T>, | ||
| 719 | { | ||
| 627 | if write.is_empty() { | 720 | if write.is_empty() { |
| 628 | self.write_internal(address, write, true, timeout_fn(timeout)) | 721 | self.write_internal(address, write, true, timeout_fn(timeout)) |
| 629 | } else { | 722 | } else { |
| 630 | embassy_time::with_timeout( | 723 | with_timeout( |
| 631 | timeout, | 724 | timeout, |
| 632 | self.write_dma_internal(address, write, true, true, timeout_fn(timeout)), | 725 | self.write_dma_internal(address, write, true, true, timeout_fn(timeout)), |
| 633 | ) | 726 | ) |
| @@ -641,7 +734,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 641 | where | 734 | where |
| 642 | TXDMA: crate::i2c::TxDma<T>, | 735 | TXDMA: crate::i2c::TxDma<T>, |
| 643 | { | 736 | { |
| 644 | self.write_vectored_timeout(address, write, self.timeout).await | 737 | self.write_vectored_timeout_internal(address, write, self.timeout).await |
| 738 | } | ||
| 739 | |||
| 740 | #[cfg(not(feature = "time"))] | ||
| 741 | pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> | ||
| 742 | where | ||
| 743 | TXDMA: crate::i2c::TxDma<T>, | ||
| 744 | { | ||
| 745 | self.write_vectored_timeout_internal(address, write, Duration::dummy_duration()) | ||
| 746 | .await | ||
| 645 | } | 747 | } |
| 646 | 748 | ||
| 647 | #[cfg(feature = "time")] | 749 | #[cfg(feature = "time")] |
| @@ -649,6 +751,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 649 | where | 751 | where |
| 650 | TXDMA: crate::i2c::TxDma<T>, | 752 | TXDMA: crate::i2c::TxDma<T>, |
| 651 | { | 753 | { |
| 754 | self.write_vectored_timeout_internal(address, write, timeout).await | ||
| 755 | } | ||
| 756 | |||
| 757 | async fn write_vectored_timeout_internal( | ||
| 758 | &mut self, | ||
| 759 | address: u8, | ||
| 760 | write: &[&[u8]], | ||
| 761 | timeout: Duration, | ||
| 762 | ) -> Result<(), Error> | ||
| 763 | where | ||
| 764 | TXDMA: crate::i2c::TxDma<T>, | ||
| 765 | { | ||
| 652 | if write.is_empty() { | 766 | if write.is_empty() { |
| 653 | return Err(Error::ZeroLengthTransfer); | 767 | return Err(Error::ZeroLengthTransfer); |
| 654 | } | 768 | } |
| @@ -660,7 +774,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 660 | let next = iter.next(); | 774 | let next = iter.next(); |
| 661 | let is_last = next.is_none(); | 775 | let is_last = next.is_none(); |
| 662 | 776 | ||
| 663 | embassy_time::with_timeout( | 777 | with_timeout( |
| 664 | timeout, | 778 | timeout, |
| 665 | self.write_dma_internal(address, c, first, is_last, timeout_fn(timeout)), | 779 | self.write_dma_internal(address, c, first, is_last, timeout_fn(timeout)), |
| 666 | ) | 780 | ) |
| @@ -677,7 +791,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 677 | where | 791 | where |
| 678 | RXDMA: crate::i2c::RxDma<T>, | 792 | RXDMA: crate::i2c::RxDma<T>, |
| 679 | { | 793 | { |
| 680 | self.read_timeout(address, buffer, self.timeout).await | 794 | self.read_timeout_internal(address, buffer, self.timeout).await |
| 795 | } | ||
| 796 | |||
| 797 | #[cfg(not(feature = "time"))] | ||
| 798 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> | ||
| 799 | where | ||
| 800 | RXDMA: crate::i2c::RxDma<T>, | ||
| 801 | { | ||
| 802 | self.read_timeout_internal(address, buffer, Duration::dummy_duration()) | ||
| 803 | .await | ||
| 681 | } | 804 | } |
| 682 | 805 | ||
| 683 | #[cfg(feature = "time")] | 806 | #[cfg(feature = "time")] |
| @@ -685,10 +808,17 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 685 | where | 808 | where |
| 686 | RXDMA: crate::i2c::RxDma<T>, | 809 | RXDMA: crate::i2c::RxDma<T>, |
| 687 | { | 810 | { |
| 811 | self.read_timeout_internal(address, buffer, timeout).await | ||
| 812 | } | ||
| 813 | |||
| 814 | async fn read_timeout_internal(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> | ||
| 815 | where | ||
| 816 | RXDMA: crate::i2c::RxDma<T>, | ||
| 817 | { | ||
| 688 | if buffer.is_empty() { | 818 | if buffer.is_empty() { |
| 689 | self.read_internal(address, buffer, false, timeout_fn(timeout)) | 819 | self.read_internal(address, buffer, false, timeout_fn(timeout)) |
| 690 | } else { | 820 | } else { |
| 691 | embassy_time::with_timeout( | 821 | with_timeout( |
| 692 | timeout, | 822 | timeout, |
| 693 | self.read_dma_internal(address, buffer, false, timeout_fn(timeout)), | 823 | self.read_dma_internal(address, buffer, false, timeout_fn(timeout)), |
| 694 | ) | 824 | ) |
| @@ -703,7 +833,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 703 | TXDMA: super::TxDma<T>, | 833 | TXDMA: super::TxDma<T>, |
| 704 | RXDMA: super::RxDma<T>, | 834 | RXDMA: super::RxDma<T>, |
| 705 | { | 835 | { |
| 706 | self.write_read_timeout(address, write, read, self.timeout).await | 836 | self.write_read_timeout_internal(address, write, read, self.timeout) |
| 837 | .await | ||
| 838 | } | ||
| 839 | |||
| 840 | #[cfg(not(feature = "time"))] | ||
| 841 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> | ||
| 842 | where | ||
| 843 | TXDMA: super::TxDma<T>, | ||
| 844 | RXDMA: super::RxDma<T>, | ||
| 845 | { | ||
| 846 | self.write_read_timeout_internal(address, write, read, Duration::dummy_duration()) | ||
| 847 | .await | ||
| 707 | } | 848 | } |
| 708 | 849 | ||
| 709 | #[cfg(feature = "time")] | 850 | #[cfg(feature = "time")] |
| @@ -718,12 +859,26 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 718 | TXDMA: super::TxDma<T>, | 859 | TXDMA: super::TxDma<T>, |
| 719 | RXDMA: super::RxDma<T>, | 860 | RXDMA: super::RxDma<T>, |
| 720 | { | 861 | { |
| 862 | self.write_read_timeout_internal(address, write, read, timeout).await | ||
| 863 | } | ||
| 864 | |||
| 865 | async fn write_read_timeout_internal( | ||
| 866 | &mut self, | ||
| 867 | address: u8, | ||
| 868 | write: &[u8], | ||
| 869 | read: &mut [u8], | ||
| 870 | timeout: Duration, | ||
| 871 | ) -> Result<(), Error> | ||
| 872 | where | ||
| 873 | TXDMA: super::TxDma<T>, | ||
| 874 | RXDMA: super::RxDma<T>, | ||
| 875 | { | ||
| 721 | let start_instant = Instant::now(); | 876 | let start_instant = Instant::now(); |
| 722 | let check_timeout = timeout_fn(timeout); | 877 | let check_timeout = timeout_fn(timeout); |
| 723 | if write.is_empty() { | 878 | if write.is_empty() { |
| 724 | self.write_internal(address, write, false, &check_timeout)?; | 879 | self.write_internal(address, write, false, &check_timeout)?; |
| 725 | } else { | 880 | } else { |
| 726 | embassy_time::with_timeout( | 881 | with_timeout( |
| 727 | timeout, | 882 | timeout, |
| 728 | self.write_dma_internal(address, write, true, true, &check_timeout), | 883 | self.write_dma_internal(address, write, true, true, &check_timeout), |
| 729 | ) | 884 | ) |
| @@ -736,7 +891,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 736 | if read.is_empty() { | 891 | if read.is_empty() { |
| 737 | self.read_internal(address, read, true, &check_timeout)?; | 892 | self.read_internal(address, read, true, &check_timeout)?; |
| 738 | } else { | 893 | } else { |
| 739 | embassy_time::with_timeout( | 894 | with_timeout( |
| 740 | time_left_until_timeout, | 895 | time_left_until_timeout, |
| 741 | self.read_dma_internal(address, read, true, &check_timeout), | 896 | self.read_dma_internal(address, read, true, &check_timeout), |
| 742 | ) | 897 | ) |
| @@ -1201,15 +1356,3 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> { | |||
| 1201 | Ok(()) | 1356 | Ok(()) |
| 1202 | } | 1357 | } |
| 1203 | } | 1358 | } |
| 1204 | |||
| 1205 | #[cfg(feature = "time")] | ||
| 1206 | fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { | ||
| 1207 | let deadline = Instant::now() + timeout; | ||
| 1208 | move || { | ||
| 1209 | if Instant::now() > deadline { | ||
| 1210 | Err(Error::Timeout) | ||
| 1211 | } else { | ||
| 1212 | Ok(()) | ||
| 1213 | } | ||
| 1214 | } | ||
| 1215 | } | ||
