diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index d0be6d19..85ee856a 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -20,7 +20,7 @@ nightly = ["embedded-hal-async", "embedded-storage-async"] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true } +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } nb = "1.0.0" diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e1258ebb..181dabe8 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -354,46 +354,54 @@ impl Executor { /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. pub unsafe fn poll(&'static self) { - #[cfg(feature = "integrated-timers")] - self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); + loop { + #[cfg(feature = "integrated-timers")] + self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); - self.run_queue.dequeue_all(|p| { - let task = p.as_ref(); + self.run_queue.dequeue_all(|p| { + let task = p.as_ref(); + + #[cfg(feature = "integrated-timers")] + task.expires_at.set(Instant::MAX); + + let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); + if state & STATE_SPAWNED == 0 { + // If task is not running, ignore it. This can happen in the following scenario: + // - Task gets dequeued, poll starts + // - While task is being polled, it gets woken. It gets placed in the queue. + // - Task poll finishes, returning done=true + // - RUNNING bit is cleared, but the task is already in the queue. + return; + } + + #[cfg(feature = "rtos-trace")] + trace::task_exec_begin(p.as_ptr() as u32); + + // Run the task + task.poll_fn.read()(p as _); + + #[cfg(feature = "rtos-trace")] + trace::task_exec_end(); + + // Enqueue or update into timer_queue + #[cfg(feature = "integrated-timers")] + self.timer_queue.update(p); + }); #[cfg(feature = "integrated-timers")] - task.expires_at.set(Instant::MAX); - - let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); - if state & STATE_SPAWNED == 0 { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - return; + { + // If this is already in the past, set_alarm might return false + // In that case do another poll loop iteration. + let next_expiration = self.timer_queue.next_expiration(); + if driver::set_alarm(self.alarm, next_expiration.as_ticks()) { + break; + } } - #[cfg(feature = "rtos-trace")] - trace::task_exec_begin(p.as_ptr() as u32); - - // Run the task - task.poll_fn.read()(p as _); - - #[cfg(feature = "rtos-trace")] - trace::task_exec_end(); - - // Enqueue or update into timer_queue - #[cfg(feature = "integrated-timers")] - self.timer_queue.update(p); - }); - - #[cfg(feature = "integrated-timers")] - { - // If this is already in the past, set_alarm will immediately trigger the alarm. - // This will cause `signal_fn` to be called, which will cause `poll()` to be called again, - // so we immediately do another poll loop iteration. - let next_expiration = self.timer_queue.next_expiration(); - driver::set_alarm(self.alarm, next_expiration.as_ticks()); + #[cfg(not(feature = "integrated-timers"))] + { + break; + } } #[cfg(feature = "rtos-trace")] @@ -436,14 +444,21 @@ pub unsafe fn wake_task(task: NonNull) { } #[cfg(feature = "integrated-timers")] -#[no_mangle] -unsafe fn _embassy_time_schedule_wake(at: Instant, waker: &core::task::Waker) { - let task = waker::task_from_waker(waker); - let task = task.as_ref(); - let expires_at = task.expires_at.get(); - task.expires_at.set(expires_at.min(at)); +struct TimerQueue; + +#[cfg(feature = "integrated-timers")] +impl embassy_time::queue::TimerQueue for TimerQueue { + fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { + let task = waker::task_from_waker(waker); + let task = unsafe { task.as_ref() }; + let expires_at = task.expires_at.get(); + task.expires_at.set(expires_at.min(at)); + } } +#[cfg(feature = "integrated-timers")] +embassy_time::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); + #[cfg(feature = "rtos-trace")] impl rtos_trace::RtosTraceOSCallbacks for Executor { fn task_list() { diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index ea2c3fe6..dc200417 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -32,7 +32,7 @@ embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 967ef26a..76217075 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -42,7 +42,7 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embedded-io = { version = "0.3.0", optional = true } +embedded-io = { version = "0.3.1", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.7.5", default-features = false } diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index f8fff3e2..f3bd2361 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -292,7 +292,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -302,7 +302,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -310,7 +310,7 @@ mod embedded_io_impls { self.io.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -324,7 +324,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpReader<'d> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -338,7 +338,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -346,7 +346,7 @@ mod embedded_io_impls { self.io.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -445,7 +445,7 @@ pub mod client { impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read for TcpConnection<'d, N, TX_SZ, RX_SZ> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -457,7 +457,7 @@ pub mod client { impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write for TcpConnection<'d, N, TX_SZ, RX_SZ> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -465,7 +465,7 @@ pub mod client { self.socket.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 5459bc90..67b6bec4 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -75,8 +75,8 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} -embedded-io = { version = "0.3.0", features = ["async"], optional = true } +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-io = { version = "0.3.1", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -90,13 +90,13 @@ embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } cfg-if = "1.0.0" -nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52811-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52820-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52832-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52833-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52840-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf5340-app-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf5340-net-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf9160-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } +nrf52805-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52810-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52811-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52820-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52832-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52833-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52840-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf5340-app-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf5340-net-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf9160-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 6e85a159..9c8fe65f 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -341,7 +341,7 @@ impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUar } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -351,7 +351,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -361,7 +361,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read f } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -375,7 +375,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for Bu } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -389,7 +389,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRea } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -397,7 +397,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff self.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -407,7 +407,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -415,7 +415,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write self.inner.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index d078fa0a..dec31a84 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -193,8 +193,8 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 3e500098..e57a4a38 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -211,14 +211,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 25c7c0d9..918404cf 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -212,14 +212,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 18b8eda6..81e66c19 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -225,14 +225,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 3b33907d..92499e3c 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -271,14 +271,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index ae59f8b2..4beadfba 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -276,14 +276,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index edf800ef..7845d4a8 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -458,14 +458,14 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -impl_saadc_input!(P0_13, ANALOGINPUT0); -impl_saadc_input!(P0_14, ANALOGINPUT1); -impl_saadc_input!(P0_15, ANALOGINPUT2); -impl_saadc_input!(P0_16, ANALOGINPUT3); -impl_saadc_input!(P0_17, ANALOGINPUT4); -impl_saadc_input!(P0_18, ANALOGINPUT5); -impl_saadc_input!(P0_19, ANALOGINPUT6); -impl_saadc_input!(P0_20, ANALOGINPUT7); +impl_saadc_input!(P0_13, ANALOG_INPUT0); +impl_saadc_input!(P0_14, ANALOG_INPUT1); +impl_saadc_input!(P0_15, ANALOG_INPUT2); +impl_saadc_input!(P0_16, ANALOG_INPUT3); +impl_saadc_input!(P0_17, ANALOG_INPUT4); +impl_saadc_input!(P0_18, ANALOG_INPUT5); +impl_saadc_input!(P0_19, ANALOG_INPUT6); +impl_saadc_input!(P0_20, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index f8ed11e0..b5a53ed8 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -339,14 +339,14 @@ impl_ppi_channel!(PPI_CH13, 13 => configurable); impl_ppi_channel!(PPI_CH14, 14 => configurable); impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_saadc_input!(P0_13, ANALOGINPUT0); -impl_saadc_input!(P0_14, ANALOGINPUT1); -impl_saadc_input!(P0_15, ANALOGINPUT2); -impl_saadc_input!(P0_16, ANALOGINPUT3); -impl_saadc_input!(P0_17, ANALOGINPUT4); -impl_saadc_input!(P0_18, ANALOGINPUT5); -impl_saadc_input!(P0_19, ANALOGINPUT6); -impl_saadc_input!(P0_20, ANALOGINPUT7); +impl_saadc_input!(P0_13, ANALOG_INPUT0); +impl_saadc_input!(P0_14, ANALOG_INPUT1); +impl_saadc_input!(P0_15, ANALOG_INPUT2); +impl_saadc_input!(P0_16, ANALOG_INPUT3); +impl_saadc_input!(P0_17, ANALOG_INPUT4); +impl_saadc_input!(P0_18, ANALOG_INPUT5); +impl_saadc_input!(P0_19, ANALOG_INPUT6); +impl_saadc_input!(P0_20, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index c32a4463..bc2c8a3c 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -243,22 +243,25 @@ impl Driver for RtcDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); - let t = self.now(); - - // If alarm timestamp has passed, trigger it instantly. - if timestamp <= t { - self.trigger_alarm(n, cs); - return; - } - let r = rtc(); + let t = self.now(); + if timestamp <= t { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); + + alarm.timestamp.set(u64::MAX); + + return false; + } + // If it hasn't triggered yet, setup it in the compare channel. // Write the CC value regardless of whether we're going to enable it now or not. @@ -287,6 +290,8 @@ impl Driver for RtcDriver { // It will be setup later by `next_period`. r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); } + + true }) } } diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 00da5c9d..eb1472fa 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -313,7 +313,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } }) .await; - regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear. + regs.eventcause.write(|w| w.ready().clear_bit_by_one()); errata::post_enable(); @@ -367,24 +367,24 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { let r = regs.eventcause.read(); if r.isooutcrc().bit() { - regs.eventcause.write(|w| w.isooutcrc().set_bit()); + regs.eventcause.write(|w| w.isooutcrc().detected()); trace!("USB event: isooutcrc"); } if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().set_bit()); + regs.eventcause.write(|w| w.usbwuallowed().allowed()); trace!("USB event: usbwuallowed"); } if r.suspend().bit() { - regs.eventcause.write(|w| w.suspend().set_bit()); + regs.eventcause.write(|w| w.suspend().detected()); regs.lowpower.write(|w| w.lowpower().low_power()); return Poll::Ready(Event::Suspend); } if r.resume().bit() { - regs.eventcause.write(|w| w.resume().set_bit()); + regs.eventcause.write(|w| w.resume().detected()); return Poll::Ready(Event::Resume); } if r.ready().bit() { - regs.eventcause.write(|w| w.ready().set_bit()); + regs.eventcause.write(|w| w.ready().ready()); trace!("USB event: ready"); } @@ -512,7 +512,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } else if r.resume().bit() { Poll::Ready(()) } else if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().set_bit()); + regs.eventcause.write(|w| w.usbwuallowed().allowed()); regs.dpdmvalue.write(|w| w.state().resume()); regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 2f9c9b9a..04b0c13c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -53,13 +53,13 @@ cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } chrono = { version = "0.4", default-features = false, optional = true } +embedded-io = { version = "0.3.1", features = ["async"], optional = true } embedded-storage = { version = "0.3" } -embedded-io = { version = "0.3.0", features = ["async"], optional = true } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 5215c0c0..80efd779 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -68,7 +68,7 @@ impl Driver for TimerDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { let n = alarm.id() as usize; critical_section::with(|cs| { let alarm = &self.alarms.borrow(cs)[n]; @@ -81,11 +81,16 @@ impl Driver for TimerDriver { unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; let now = self.now(); - - // If alarm timestamp has passed, trigger it instantly. - // This disarms it. if timestamp <= now { - self.trigger_alarm(n, cs); + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } + + alarm.timestamp.set(u64::MAX); + + false + } else { + true } }) } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 87e16f0e..4f0a5553 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -355,7 +355,7 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -376,7 +376,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -397,7 +397,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -419,7 +419,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -441,7 +441,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -455,7 +455,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { }) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -465,7 +465,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -479,7 +479,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> }) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9194ae78..6b00518a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -44,7 +44,7 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} embedded-storage = "0.3.0" @@ -67,7 +67,7 @@ nb = "1.0.0" stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" -embedded-io = { version = "0.3.0", features = ["async"], optional = true } +embedded-io = { version = "0.3.1", features = ["async"], optional = true } [build-dependencies] proc-macro2 = "1.0.36" @@ -82,9 +82,12 @@ memory-x = ["stm32-metapac/memory-x"] subghz = [] exti = [] +# Enables additional driver features that depend on embassy-time +time = ["dep:embassy-time"] + # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. -_time-driver = ["dep:embassy-time"] +_time-driver = ["time"] time-driver-any = ["_time-driver"] time-driver-tim2 = ["_time-driver"] time-driver-tim3 = ["_time-driver"] diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 50d4f9bf..c5b317ce 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -86,7 +86,6 @@ pub use sample_time::SampleTime; pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - calibrated_vdda: u32, phantom: PhantomData<&'d mut T>, } @@ -122,7 +121,6 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), - calibrated_vdda: VDDA_CALIB_MV, phantom: PhantomData, } } @@ -162,29 +160,10 @@ impl<'d, T: Instance> Adc<'d, T> { Temperature {} } - /// Calculates the system VDDA by sampling the internal VREF channel and comparing - /// to the expected value. If the chip's VDDA is not stable, run this before each ADC - /// conversion. - pub fn calibrate(&mut self, vref: &mut Vref) -> u32 { - let old_sample_time = self.sample_time; - self.sample_time = SampleTime::Cycles239_5; - - let vref_samp = self.read(vref); - self.sample_time = old_sample_time; - - self.calibrated_vdda = (ADC_MAX * VREF_INT) / u32::from(vref_samp); - self.calibrated_vdda - } - pub fn set_sample_time(&mut self, sample_time: SampleTime) { self.sample_time = sample_time; } - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.calibrated_vdda) / ADC_MAX) as u16 - } - /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 4fe4ad1f..53419c7f 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -80,15 +80,6 @@ impl super::sealed::InternalChannel for Temperature { } impl Temperature { - /// Converts temperature sensor reading in millivolts to degrees celcius - pub fn to_celcius(sample_mv: u16) -> f32 { - // From 6.3.22 Temperature sensor characteristics - const V25: i32 = 760; // mV - const AVG_SLOPE: f32 = 2.5; // mV/C - - (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0 - } - /// Time needed for temperature sensor readings to stabilize pub fn start_time_us() -> u32 { 10 @@ -172,7 +163,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -200,7 +190,6 @@ where Self { sample_time: Default::default(), resolution: Resolution::default(), - vref_mv: VREF_DEFAULT_MV, phantom: PhantomData, } } @@ -213,18 +202,6 @@ where self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /// Enables internal voltage reference and returns [VrefInt], which can be used in /// [Adc::read_internal()] to perform conversion. pub fn enable_vrefint(&self) -> VrefInt { diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 0f109088..816feeac 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -205,7 +205,6 @@ pub use sample_time::SampleTime; pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -244,7 +243,6 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), resolution: Resolution::default(), - vref_mv: VREF_DEFAULT_MV, phantom: PhantomData, } } @@ -285,31 +283,6 @@ impl<'d, T: Instance> Adc<'d, T> { Vbat {} } - /// Calculates the system VDDA by sampling the internal VREFINT channel and comparing - /// the result with the value stored at the factory. If the chip's VDDA is not stable, run - /// this before each ADC conversion. - #[cfg(not(stm32g0))] // TODO is this supposed to be public? - #[allow(unused)] // TODO is this supposed to be public? - fn calibrate(&mut self, vrefint: &mut VrefInt) { - #[cfg(stm32l5)] - let vrefint_cal: u32 = todo!(); - #[cfg(not(stm32l5))] - let vrefint_cal = unsafe { crate::pac::VREFINTCAL.data().read().value() }; - let old_sample_time = self.sample_time; - - // "Table 24. Embedded internal voltage reference" states that the sample time needs to be - // at a minimum 4 us. With 640.5 ADC cycles we have a minimum of 8 us at 80 MHz, leaving - // some headroom. - self.sample_time = SampleTime::Cycles640_5; - - // This can't actually fail, it's just in a result to satisfy hal trait - let vrefint_samp = self.read(vrefint); - - self.sample_time = old_sample_time; - - self.vref_mv = (VREF_CALIB_MV * u32::from(vrefint_cal)) / u32::from(vrefint_samp); - } - pub fn set_sample_time(&mut self, sample_time: SampleTime) { self.sample_time = sample_time; } @@ -318,18 +291,6 @@ impl<'d, T: Instance> Adc<'d, T> { self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /* /// Convert a raw sample from the `Temperature` to deg C pub fn to_degrees_centigrade(sample: u16) -> f32 { diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index eda2b2a7..2b8f1053 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -314,7 +314,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -352,7 +351,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { let mut s = Self { sample_time: Default::default(), - vref_mv: VREF_DEFAULT_MV, resolution: Resolution::default(), phantom: PhantomData, }; @@ -459,18 +457,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 9d314f41..f898fcc8 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,6 +7,11 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; +#[cfg(feature = "time")] +mod timeout; +#[cfg(feature = "time")] +pub use timeout::*; + use crate::peripherals; #[derive(Debug)] diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs new file mode 100644 index 00000000..4fca1ca2 --- /dev/null +++ b/embassy-stm32/src/i2c/timeout.rs @@ -0,0 +1,142 @@ +use embassy_time::{Duration, Instant}; + +use super::{Error, I2c, Instance}; + +/// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods. +/// +/// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state. +/// A regular [I2c] would freeze until condition is removed. +pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { + i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, + timeout: Duration, +} + +fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { + let deadline = Instant::now() + timeout; + move || { + if Instant::now() > deadline { + Err(Error::Timeout) + } else { + Ok(()) + } + } +} + +impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { + pub fn new(i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { + Self { i2c, timeout } + } + + /// Blocking read with a custom timeout + pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout)) + } + + /// Blocking read with default timeout, provided in [`TimeoutI2c::new()`] + pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, buffer, self.timeout) + } + + /// Blocking write with a custom timeout + pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout)) + } + + /// Blocking write with default timeout, provided in [`TimeoutI2c::new()`] + pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, bytes, self.timeout) + } + + /// Blocking write-read with a custom timeout + pub fn blocking_write_read_timeout( + &mut self, + addr: u8, + bytes: &[u8], + buffer: &mut [u8], + timeout: Duration, + ) -> Result<(), Error> { + self.i2c + .blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout)) + } + + /// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`] + pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout) + } +} + +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { + type Error = Error; + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(addr, buffer) + } +} + +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { + type Error = Error; + + fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(addr, bytes) + } +} + +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { + type Error = Error; + + fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(addr, bytes, buffer) + } +} + +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> { + type Error = Error; + } + + impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } + + fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, buffer) + } + + fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator, + { + todo!(); + } + + fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> + where + B: IntoIterator, + { + todo!(); + } + + fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, wr_buffer, rd_buffer) + } + + fn transaction<'a>( + &mut self, + _address: u8, + _operations: &mut [embedded_hal_1::i2c::Operation<'a>], + ) -> Result<(), Self::Error> { + todo!(); + } + + fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>, + { + todo!(); + } + } +} diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index f39a37df..f140e2b0 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -1,8 +1,9 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::into_ref; +use embassy_hal_common::{into_ref, PeripheralRef}; +use crate::dma::NoDma; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; @@ -34,19 +35,26 @@ impl State { } } -pub struct I2c<'d, T: Instance> { +pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { phantom: PhantomData<&'d mut T>, + #[allow(dead_code)] + tx_dma: PeripheralRef<'d, TXDMA>, + #[allow(dead_code)] + rx_dma: PeripheralRef<'d, RXDMA>, } -impl<'d, T: Instance> I2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub fn new( _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, + _irq: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, freq: Hertz, config: Config, ) -> Self { - into_ref!(scl, sda); + into_ref!(scl, sda, tx_dma, rx_dma); T::enable(); T::reset(); @@ -99,7 +107,11 @@ impl<'d, T: Instance> I2c<'d, T> { }); } - Self { phantom: PhantomData } + Self { + phantom: PhantomData, + tx_dma, + rx_dma, + } } unsafe fn check_and_clear_error_flags(&self) -> Result { @@ -141,7 +153,12 @@ impl<'d, T: Instance> I2c<'d, T> { Ok(sr1) } - unsafe fn write_bytes(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + unsafe fn write_bytes( + &mut self, + addr: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { // Send a START condition T::regs().cr1().modify(|reg| { @@ -149,7 +166,9 @@ impl<'d, T: Instance> I2c<'d, T> { }); // Wait until START condition was generated - while !self.check_and_clear_error_flags()?.start() {} + while !self.check_and_clear_error_flags()?.start() { + check_timeout()?; + } // Also wait until signalled we're master and everything is waiting for us while { @@ -157,7 +176,9 @@ impl<'d, T: Instance> I2c<'d, T> { let sr2 = T::regs().sr2().read(); !sr2.msl() && !sr2.busy() - } {} + } { + check_timeout()?; + } // Set up current address, we're trying to talk to T::regs().dr().write(|reg| reg.set_dr(addr << 1)); @@ -165,26 +186,30 @@ impl<'d, T: Instance> I2c<'d, T> { // Wait until address was sent // Wait for the address to be acknowledged // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. - while !self.check_and_clear_error_flags()?.addr() {} + while !self.check_and_clear_error_flags()?.addr() { + check_timeout()?; + } // Clear condition by reading SR2 let _ = T::regs().sr2().read(); // Send bytes for c in bytes { - self.send_byte(*c)?; + self.send_byte(*c, &check_timeout)?; } // Fallthrough is success Ok(()) } - unsafe fn send_byte(&self, byte: u8) -> Result<(), Error> { + unsafe fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { // Wait until we're ready for sending while { // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. !self.check_and_clear_error_flags()?.txe() - } {} + } { + check_timeout()?; + } // Push out a byte of data T::regs().dr().write(|reg| reg.set_dr(byte)); @@ -193,24 +218,33 @@ impl<'d, T: Instance> I2c<'d, T> { while { // Check for any potential error conditions. !self.check_and_clear_error_flags()?.btf() - } {} + } { + check_timeout()?; + } Ok(()) } - unsafe fn recv_byte(&self) -> Result { + unsafe fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result { while { // Check for any potential error conditions. self.check_and_clear_error_flags()?; !T::regs().sr1().read().rxne() - } {} + } { + check_timeout()?; + } let value = T::regs().dr().read().dr(); Ok(value) } - pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read_timeout( + &mut self, + addr: u8, + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { if let Some((last, buffer)) = buffer.split_last_mut() { // Send a START condition and set ACK bit unsafe { @@ -221,27 +255,33 @@ impl<'d, T: Instance> I2c<'d, T> { } // Wait until START condition was generated - while unsafe { !T::regs().sr1().read().start() } {} + while unsafe { !self.check_and_clear_error_flags()?.start() } { + check_timeout()?; + } // Also wait until signalled we're master and everything is waiting for us while { let sr2 = unsafe { T::regs().sr2().read() }; !sr2.msl() && !sr2.busy() - } {} + } { + check_timeout()?; + } // Set up current address, we're trying to talk to unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) } // Wait until address was sent // Wait for the address to be acknowledged - while unsafe { !self.check_and_clear_error_flags()?.addr() } {} + while unsafe { !self.check_and_clear_error_flags()?.addr() } { + check_timeout()?; + } // Clear condition by reading SR2 let _ = unsafe { T::regs().sr2().read() }; // Receive bytes into buffer for c in buffer { - *c = unsafe { self.recv_byte()? }; + *c = unsafe { self.recv_byte(&check_timeout)? }; } // Prepare to send NACK then STOP after next byte @@ -253,10 +293,12 @@ impl<'d, T: Instance> I2c<'d, T> { } // Receive last byte - *last = unsafe { self.recv_byte()? }; + *last = unsafe { self.recv_byte(&check_timeout)? }; // Wait for the STOP to be sent. - while unsafe { T::regs().cr1().read().stop() } {} + while unsafe { T::regs().cr1().read().stop() } { + check_timeout()?; + } // Fallthrough is success Ok(()) @@ -265,25 +307,50 @@ impl<'d, T: Instance> I2c<'d, T> { } } - pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, buffer, || Ok(())) + } + + pub fn blocking_write_timeout( + &mut self, + addr: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { unsafe { - self.write_bytes(addr, bytes)?; + self.write_bytes(addr, bytes, &check_timeout)?; // Send a STOP condition T::regs().cr1().modify(|reg| reg.set_stop(true)); // Wait for STOP condition to transmit. - while T::regs().cr1().read().stop() {} + while T::regs().cr1().read().stop() { + check_timeout()?; + } }; // Fallthrough is success Ok(()) } - pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - unsafe { self.write_bytes(addr, bytes)? }; - self.blocking_read(addr, buffer)?; + pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, bytes, || Ok(())) + } + + pub fn blocking_write_read_timeout( + &mut self, + addr: u8, + bytes: &[u8], + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { + unsafe { self.write_bytes(addr, bytes, &check_timeout)? }; + self.blocking_read_timeout(addr, buffer, &check_timeout)?; Ok(()) } + + pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, bytes, buffer, || Ok(())) + } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 89b52da9..aa4e6bb0 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -147,14 +147,23 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - unsafe fn master_read(address: u8, length: usize, stop: Stop, reload: bool, restart: bool) { + unsafe fn master_read( + address: u8, + length: usize, + stop: Stop, + reload: bool, + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256); if !restart { // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while T::regs().cr2().read().start() {} + while T::regs().cr2().read().start() { + check_timeout()?; + } } // Set START and prepare to receive bytes into @@ -176,15 +185,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_autoend(stop.autoend()); w.set_reload(reload); }); + + Ok(()) } - unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) { + unsafe fn master_write( + address: u8, + length: usize, + stop: Stop, + reload: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256); // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while T::regs().cr2().read().start() {} + while T::regs().cr2().read().start() { + check_timeout()?; + } let reload = if reload { i2c::vals::Reload::NOTCOMPLETED @@ -204,12 +223,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_autoend(stop.autoend()); w.set_reload(reload); }); + + Ok(()) } - unsafe fn master_continue(length: usize, reload: bool) { + unsafe fn master_continue( + length: usize, + reload: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256 && length > 0); - while !T::regs().isr().read().tcr() {} + while !T::regs().isr().read().tcr() { + check_timeout()?; + } let reload = if reload { i2c::vals::Reload::NOTCOMPLETED @@ -221,6 +248,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_nbytes(length as u8); w.set_reload(reload); }); + + Ok(()) } fn flush_txdr(&self) { @@ -243,7 +272,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { //} } - fn wait_txe(&self) -> Result<(), Error> { + fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -261,10 +290,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn wait_rxne(&self) -> Result<(), Error> { + fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -282,10 +313,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn wait_tc(&self) -> Result<(), Error> { + fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -303,10 +336,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error> { + fn read_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { let completed_chunks = buffer.len() / 255; let total_chunks = if completed_chunks * 255 == buffer.len() { completed_chunks @@ -322,20 +363,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Stop::Automatic, last_chunk_idx != 0, restart, - ); + &check_timeout, + )?; } for (number, chunk) in buffer.chunks_mut(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), number != last_chunk_idx); + Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; } } for byte in chunk { // Wait until we have received something - self.wait_rxne()?; + self.wait_rxne(&check_timeout)?; unsafe { *byte = T::regs().rxdr().read().rxdata(); @@ -345,7 +387,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + fn write_internal( + &mut self, + address: u8, + bytes: &[u8], + send_stop: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { let completed_chunks = bytes.len() / 255; let total_chunks = if completed_chunks * 255 == bytes.len() { completed_chunks @@ -359,14 +407,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ST SAD+W // NOTE(unsafe) We have &mut self unsafe { - Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0); + Self::master_write( + address, + bytes.len().min(255), + Stop::Software, + last_chunk_idx != 0, + &check_timeout, + )?; } for (number, chunk) in bytes.chunks(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), number != last_chunk_idx); + Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; } } @@ -374,7 +428,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe()?; + self.wait_txe(&check_timeout)?; unsafe { T::regs().txdr().write(|w| w.set_txdata(*byte)); @@ -382,7 +436,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - self.wait_tc()?; + self.wait_tc(&check_timeout)?; if send_stop { self.master_stop(); @@ -396,6 +450,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { bytes: &[u8], first_slice: bool, last_slice: bool, + check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -447,11 +502,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { total_len.min(255), Stop::Software, (total_chunks != 1) || !last_slice, - ); + &check_timeout, + )?; } } else { unsafe { - Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice); + Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?; T::regs().cr1().modify(|w| w.set_tcie(true)); } } @@ -461,32 +517,40 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); if chunks_transferred == total_chunks { - return Poll::Ready(()); + return Poll::Ready(Ok(())); } else if chunks_transferred != 0 { remaining_len = remaining_len.saturating_sub(255); let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice; // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers unsafe { - Self::master_continue(remaining_len.min(255), !last_piece); + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + return Poll::Ready(Err(e)); + } T::regs().cr1().modify(|w| w.set_tcie(true)); } } Poll::Pending }) - .await; + .await?; dma_transfer.await; if last_slice { // This should be done already - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); } Ok(()) } - async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error> + async fn read_dma_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { @@ -527,7 +591,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { - Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart); + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_chunks != 1, + restart, + &check_timeout, + )?; } poll_fn(|cx| { @@ -535,25 +606,27 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); if chunks_transferred == total_chunks { - return Poll::Ready(()); + return Poll::Ready(Ok(())); } else if chunks_transferred != 0 { remaining_len = remaining_len.saturating_sub(255); let last_piece = chunks_transferred + 1 == total_chunks; // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { - Self::master_continue(remaining_len.min(255), !last_piece); + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + return Poll::Ready(Err(e)); + } T::regs().cr1().modify(|w| w.set_tcie(true)); } } Poll::Pending }) - .await; + .await?; dma_transfer.await; // This should be done already - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); Ok(()) } @@ -566,9 +639,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { TXDMA: crate::i2c::TxDma, { if bytes.is_empty() { - self.write_internal(address, bytes, true) + self.write_internal(address, bytes, true, || Ok(())) } else { - self.write_dma_internal(address, bytes, true, true).await + self.write_dma_internal(address, bytes, true, true, || Ok(())).await } } @@ -587,7 +660,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let next = iter.next(); let is_last = next.is_none(); - self.write_dma_internal(address, c, first, is_last).await?; + self.write_dma_internal(address, c, first, is_last, || Ok(())).await?; first = false; current = next; } @@ -599,9 +672,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { RXDMA: crate::i2c::RxDma, { if buffer.is_empty() { - self.read_internal(address, buffer, false) + self.read_internal(address, buffer, false, || Ok(())) } else { - self.read_dma_internal(address, buffer, false).await + self.read_dma_internal(address, buffer, false, || Ok(())).await } } @@ -611,15 +684,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { RXDMA: super::RxDma, { if bytes.is_empty() { - self.write_internal(address, bytes, false)?; + self.write_internal(address, bytes, false, || Ok(()))?; } else { - self.write_dma_internal(address, bytes, true, true).await?; + self.write_dma_internal(address, bytes, true, true, || Ok(())).await?; } if buffer.is_empty() { - self.read_internal(address, buffer, true)?; + self.read_internal(address, buffer, true, || Ok(()))?; } else { - self.read_dma_internal(address, buffer, true).await?; + self.read_dma_internal(address, buffer, true, || Ok(())).await?; } Ok(()) @@ -628,22 +701,55 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Blocking public API - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.read_internal(address, buffer, false) + pub fn blocking_read_timeout( + &mut self, + address: u8, + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { + self.read_internal(address, buffer, false, &check_timeout) // Automatic Stop } + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(address, buffer, || Ok(())) + } + + pub fn blocking_write_timeout( + &mut self, + address: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { + self.write_internal(address, bytes, true, &check_timeout) + } + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - self.write_internal(address, bytes, true) + self.blocking_write_timeout(address, bytes, || Ok(())) } - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.write_internal(address, bytes, false)?; - self.read_internal(address, buffer, true) + pub fn blocking_write_read_timeout( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { + self.write_internal(address, bytes, false, &check_timeout)?; + self.read_internal(address, buffer, true, &check_timeout) // Automatic Stop } - pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(address, bytes, buffer, || Ok(())) + } + + pub fn blocking_write_vectored_timeout( + &mut self, + address: u8, + bytes: &[&[u8]], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { if bytes.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -657,7 +763,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), - ); + &check_timeout, + )?; } for (idx, slice) in bytes.iter().enumerate() { @@ -673,7 +780,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if idx != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255)); + Self::master_continue( + slice_len.min(255), + (idx != last_slice_index) || (slice_len > 255), + &check_timeout, + )?; } } @@ -681,7 +792,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index)); + Self::master_continue( + chunk.len(), + (number != last_chunk_idx) || (idx != last_slice_index), + &check_timeout, + )?; } } @@ -689,7 +804,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe()?; + self.wait_txe(&check_timeout)?; // Put byte on the wire //self.i2c.txdr.write(|w| w.txdata().bits(*byte)); @@ -700,11 +815,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); Ok(()) } + + pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { + self.blocking_write_vectored_timeout(address, bytes, || Ok(())) + } } mod eh02 { diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0392e808..bcf2feee 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -52,7 +52,7 @@ pub mod sdmmc; pub mod spi; #[cfg(usart)] pub mod usart; -#[cfg(usb)] +#[cfg(all(usb, feature = "time"))] pub mod usb; #[cfg(any(otgfs, otghs))] pub mod usb_otg; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index a8bc6385..c91f3c8b 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1534,14 +1534,14 @@ mod sdmmc_rs { impl<'d, T: Instance, P: Pins> BlockDevice for Sdmmc<'d, T, P> { type Error = Error; - type ReadFuture<'a> + + type ReadFuture<'a> = impl Future> + 'a where - Self: 'a, - = impl Future> + 'a; - type WriteFuture<'a> + Self: 'a; + + type WriteFuture<'a> = impl Future> + 'a where - Self: 'a, - = impl Future> + 'a; + Self: 'a; fn read<'a>( &'a mut self, diff --git a/embassy-stm32/src/subghz/timeout.rs b/embassy-stm32/src/subghz/timeout.rs index 28b3b0c2..0ae49dd9 100644 --- a/embassy-stm32/src/subghz/timeout.rs +++ b/embassy-stm32/src/subghz/timeout.rs @@ -439,6 +439,7 @@ impl From for [u8; 3] { } } +#[cfg(feature = "time")] impl From for embassy_time::Duration { fn from(to: Timeout) -> Self { embassy_time::Duration::from_micros(to.as_micros().into()) diff --git a/embassy-stm32/src/subghz/tx_params.rs b/embassy-stm32/src/subghz/tx_params.rs index cede6f2c..03bdb1ea 100644 --- a/embassy-stm32/src/subghz/tx_params.rs +++ b/embassy-stm32/src/subghz/tx_params.rs @@ -44,6 +44,7 @@ impl From for core::time::Duration { } } +#[cfg(feature = "time")] impl From for embassy_time::Duration { fn from(rt: RampTime) -> Self { match rt { diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index ed3225c5..8e84570a 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -292,19 +292,23 @@ impl Driver for RtcDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { let r = T::regs_gp16(); - let n = alarm.id() as _; + let n = alarm.id() as usize; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); let t = self.now(); if timestamp <= t { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) }; - self.trigger_alarm(n, cs); - return; + + alarm.timestamp.set(u64::MAX); + + return false; } let safe_timestamp = timestamp.max(t + 3); @@ -317,6 +321,8 @@ impl Driver for RtcDriver { let diff = timestamp - t; // NOTE(unsafe) We're in a critical section unsafe { r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)) }; + + true }) } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 2a711bc0..3be0677b 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -46,16 +46,44 @@ impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( state: &'d mut State<'d, T>, - _uart: Uart<'d, T, NoDma, NoDma>, + _peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], + config: Config, ) -> BufferedUart<'d, T> { - into_ref!(irq); + into_ref!(_peri, rx, tx, irq); + + T::enable(); + T::reset(); let r = T::regs(); + + configure(r, &config, T::frequency(), T::MULTIPLIER); + unsafe { - r.cr1().modify(|w| { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + r.cr2().write(|_w| {}); + r.cr3().write(|_w| {}); + r.cr1().write(|w| { + w.set_ue(true); + w.set_te(true); + w.set_re(true); + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); w.set_rxneie(true); w.set_idleie(true); }); @@ -283,7 +311,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { } impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -293,7 +321,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -303,7 +331,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, } impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -317,7 +345,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -331,7 +359,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<' } impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -339,7 +367,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { self.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -349,7 +377,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -357,7 +385,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, self.inner.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index a152a0c1..dee466b2 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1,7 +1,11 @@ #![macro_use] +use core::future::poll_fn; use core::marker::PhantomData; +use core::task::Poll; +use atomic_polyfill::{compiler_fence, Ordering}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::NoDma; @@ -10,6 +14,7 @@ use crate::gpio::sealed::AFType; use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; #[cfg(not(any(lpuart_v1, lpuart_v2)))] use crate::pac::usart::{regs, vals, Usart as Regs}; +use crate::time::Hertz; use crate::{peripherals, Peripheral}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -44,6 +49,10 @@ pub struct Config { pub data_bits: DataBits, pub stop_bits: StopBits, pub parity: Parity, + /// if true, on read-like method, if there is a latent error pending, + /// read will abort, the error reported and cleared + /// if false, the error is ignored and cleared + pub detect_previous_overrun: bool, } impl Default for Config { @@ -53,6 +62,8 @@ impl Default for Config { data_bits: DataBits::DataBits8, stop_bits: StopBits::STOP1, parity: Parity::ParityNone, + // historical behavior + detect_previous_overrun: false, } } } @@ -70,10 +81,11 @@ pub enum Error { Overrun, /// Parity check error Parity, + /// Buffer too large for DMA + BufferTooLong, } pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { - phantom: PhantomData<&'d mut T>, tx: UartTx<'d, T, TxDma>, rx: UartRx<'d, T, RxDma>, } @@ -84,8 +96,9 @@ pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> { } pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { - phantom: PhantomData<&'d mut T>, + _peri: PeripheralRef<'d, T>, rx_dma: PeripheralRef<'d, RxDma>, + detect_previous_overrun: bool, } impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { @@ -135,10 +148,112 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { - fn new(rx_dma: PeripheralRef<'d, RxDma>) -> Self { + /// usefull if you only want Uart Rx. It saves 1 pin and consumes a little less power + pub fn new( + peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, irq, rx, rx_dma); + + T::enable(); + T::reset(); + + let r = T::regs(); + + configure(r, &config, T::frequency(), T::MULTIPLIER); + + unsafe { + rx.set_as_af(rx.af_num(), AFType::Input); + + r.cr2().write(|_w| {}); + r.cr3().write(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + }); + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + // enable receiver + w.set_re(true); + // configure word size + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); + } + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + // create state once! + let _s = T::state(); + Self { + _peri: peri, rx_dma, - phantom: PhantomData, + detect_previous_overrun: config.detect_previous_overrun, + } + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; + + let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); + + if has_errors { + // clear all interrupts and DMA Rx Request + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + + compiler_fence(Ordering::SeqCst); + + s.rx_waker.wake(); + } else if cr1.idleie() && sr.idle() { + // IDLE detected: no more data will come + unsafe { + r.cr1().modify(|w| { + // disable idle line detection + w.set_idleie(false); + }); + + r.cr3().modify(|w| { + // disable DMA Rx Request + w.set_dmar(false); + }); + } + compiler_fence(Ordering::SeqCst); + + s.rx_waker.wake(); } } @@ -146,17 +261,8 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { where RxDma: crate::usart::RxDma, { - let ch = &mut self.rx_dma; - let request = ch.request(); - unsafe { - T::regs().cr3().modify(|reg| { - reg.set_dmar(true); - }); - } - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); - transfer.await; + self.inner_read(buffer, false).await?; + Ok(()) } @@ -211,35 +317,259 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } Ok(()) } + + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result + where + RxDma: crate::usart::RxDma, + { + self.inner_read(buffer, true).await + } + + async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result + where + RxDma: crate::usart::RxDma, + { + if buffer.is_empty() { + return Ok(0); + } else if buffer.len() > 0xFFFF { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + + let buffer_len = buffer.len(); + + let ch = &mut self.rx_dma; + let request = ch.request(); + + // SAFETY: The only way we might have a problem is using split rx and tx + // here we only modify or read Rx related flags, interrupts and DMA channel + unsafe { + // Start USART DMA + // will not do anything yet because DMAR is not yet set + ch.start_read(request, rdr(r), buffer, Default::default()); + + // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer + if !self.detect_previous_overrun { + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + } + + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // enable parity interrupt if not ParityNone + w.set_peie(w.pce()); + }); + + r.cr3().modify(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + // enable DMA Rx Request + w.set_dmar(true); + }); + + compiler_fence(Ordering::SeqCst); + + // In case of errors already pending when reception started, interrupts may have already been raised + // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts + // have been disabled in interrupt handler and DMA Rx Request has been disabled. + + let cr3 = r.cr3().read(); + + if !cr3.dmar() { + // something went wrong + // because the only way to get this flag cleared is to have an interrupt + + // abort DMA transfer + ch.request_stop(); + while ch.is_running() {} + + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + if sr.pe() { + return Err(Error::Parity); + } + if sr.fe() { + return Err(Error::Framing); + } + if sr.ne() { + return Err(Error::Noise); + } + if sr.ore() { + return Err(Error::Overrun); + } + + unreachable!(); + } + + // clear idle flag + if enable_idle_line_detection { + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); + } + } + + compiler_fence(Ordering::SeqCst); + + let res = poll_fn(move |cx| { + let s = T::state(); + + ch.set_waker(cx.waker()); + s.rx_waker.register(cx.waker()); + + // SAFETY: read only and we only use Rx related flags + let sr = unsafe { sr(r).read() }; + + // SAFETY: only clears Rx related flags + unsafe { + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + } + + compiler_fence(Ordering::SeqCst); + + let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); + + if has_errors { + // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler + + // stop dma transfer + ch.request_stop(); + while ch.is_running() {} + + if sr.pe() { + return Poll::Ready(Err(Error::Parity)); + } + if sr.fe() { + return Poll::Ready(Err(Error::Framing)); + } + if sr.ne() { + return Poll::Ready(Err(Error::Noise)); + } + if sr.ore() { + return Poll::Ready(Err(Error::Overrun)); + } + } + + if sr.idle() { + // Idle line + + // stop dma transfer + ch.request_stop(); + while ch.is_running() {} + + let n = buffer_len - (ch.remaining_transfers() as usize); + + return Poll::Ready(Ok(n)); + } else if !ch.is_running() { + // DMA complete + return Poll::Ready(Ok(buffer_len)); + } + + Poll::Pending + }) + .await; + + // clear all interrupts and DMA Rx Request + // SAFETY: only clears Rx related flags + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + + res + } } impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { pub fn new( - _inner: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(_inner, rx, tx, tx_dma, rx_dma); + T::enable(); + T::reset(); + + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) + } + + pub fn new_with_rtscts( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cts, rts); T::enable(); T::reset(); - let pclk_freq = T::frequency(); - // TODO: better calculation, including error checking and OVER8 if possible. - let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * T::MULTIPLIER; + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); + } + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, rx, tx, irq, tx_dma, rx_dma); let r = T::regs(); + configure(r, &config, T::frequency(), T::MULTIPLIER); + unsafe { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); r.cr2().write(|_w| {}); - r.cr3().write(|_w| {}); - r.brr().write_value(regs::Brr(div)); r.cr1().write(|w| { w.set_ue(true); w.set_te(true); @@ -258,10 +588,20 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }); } + irq.set_handler(UartRx::::on_interrupt); + irq.unpend(); + irq.enable(); + + // create state once! + let _s = T::state(); + Self { tx: UartTx::new(tx_dma), - rx: UartRx::new(rx_dma), - phantom: PhantomData {}, + rx: UartRx { + _peri: peri, + rx_dma, + detect_previous_overrun: config.detect_previous_overrun, + }, } } @@ -295,6 +635,13 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.rx.blocking_read(buffer) } + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result + where + RxDma: crate::usart::RxDma, + { + self.rx.read_until_idle(buffer).await + } + /// Split the Uart into a transmitter and receiver, which is /// particuarly useful when having two tasks correlating to /// transmitting and receiving. @@ -303,6 +650,15 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } +fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32) { + // TODO: better calculation, including error checking and OVER8 if possible. + let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier; + + unsafe { + r.brr().write_value(regs::Brr(div)); + } +} + mod eh02 { use super::*; @@ -352,6 +708,7 @@ mod eh1 { Self::Noise => embedded_hal_1::serial::ErrorKind::Noise, Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, + Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, } } } @@ -536,13 +893,30 @@ unsafe fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { } pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + use super::*; + pub struct State { + pub rx_waker: AtomicWaker, + pub tx_waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + } + } + } + pub trait BasicInstance: crate::rcc::RccPeripheral { const MULTIPLIER: u32; type Interrupt: crate::interrupt::Interrupt; fn regs() -> Regs; + fn state() -> &'static State; } pub trait FullInstance: BasicInstance { @@ -550,7 +924,7 @@ pub(crate) mod sealed { } } -pub trait BasicInstance: sealed::BasicInstance {} +pub trait BasicInstance: Peripheral

+ sealed::BasicInstance + 'static + Send {} pub trait FullInstance: sealed::FullInstance {} @@ -572,6 +946,11 @@ macro_rules! impl_lpuart { fn regs() -> Regs { Regs(crate::pac::$inst.0) } + + fn state() -> &'static crate::usart::sealed::State { + static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new(); + &STATE + } } impl BasicInstance for peripherals::$inst {} diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 584d5ba9..b7fe1643 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -19,6 +19,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-sync/ features = ["nightly"] target = "thumbv7em-none-eabi" +[package.metadata.docs.rs] +features = ["nightly"] + [features] nightly = ["embedded-io/async"] std = [] @@ -32,7 +35,7 @@ atomic-polyfill = "1.0.1" critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" -embedded-io = "0.3.0" +embedded-io = "0.3.1" [dev-dependencies] futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 7d64b648..cd577f34 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -361,7 +361,7 @@ mod io_impls { } impl embedded_io::asynch::Read for Pipe { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -371,7 +371,7 @@ mod io_impls { } impl embedded_io::asynch::Write for Pipe { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -379,7 +379,7 @@ mod io_impls { Pipe::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -393,7 +393,7 @@ mod io_impls { } impl embedded_io::asynch::Read for &Pipe { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -403,7 +403,7 @@ mod io_impls { } impl embedded_io::asynch::Write for &Pipe { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -411,7 +411,7 @@ mod io_impls { Pipe::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -425,7 +425,7 @@ mod io_impls { } impl embedded_io::asynch::Read for Reader<'_, M, N> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -439,7 +439,7 @@ mod io_impls { } impl embedded_io::asynch::Write for Writer<'_, M, N> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -447,7 +447,7 @@ mod io_impls { Writer::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c51a71d0..9487003c 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -2,8 +2,16 @@ name = "embassy-time" version = "0.1.0" edition = "2021" +description = "Instant and Duration for embedded no-std systems, with async timer support" +repository = "https://github.com/embassy-rs/embassy" +readme = "README.md" license = "MIT OR Apache-2.0" - +categories = [ + "embedded", + "no-std", + "concurrency", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-v$VERSION/embassy-time/src/" @@ -11,6 +19,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time/ features = ["nightly", "defmt", "unstable-traits", "std"] target = "x86_64-unknown-linux-gnu" +[package.metadata.docs.rs] +features = ["nightly", "defmt", "unstable-traits", "std"] + [features] std = ["tick-hz-1_000_000"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] @@ -26,6 +37,22 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] +# Create a global, generic queue that can be used with any executor +# To use this you must have a time driver provided. +generic-queue = [] + +# Set the number of timers for the generic queue. +# +# At most 1 `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. +# +# When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the +# end user to pick. +generic-queue-8 = ["generic-queue"] +generic-queue-16 = ["generic-queue"] +generic-queue-32 = ["generic-queue"] +generic-queue-64 = ["generic-queue"] +generic-queue-128 = ["generic-queue"] + # Set the `embassy_time` tick rate. # # At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. @@ -107,15 +134,21 @@ log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} futures-util = { version = "0.3.17", default-features = false } -embassy-macros = { version = "0.1.0", path = "../embassy-macros"} +embassy-sync = { version = "0.1", path = "../embassy-sync" } atomic-polyfill = "1.0.1" critical-section = "1.1" cfg-if = "1.0.0" +heapless = "0.7" # WASM dependencies wasm-bindgen = { version = "0.2.81", optional = true } js-sys = { version = "0.3", optional = true } wasm-timer = { version = "0.2.5", optional = true } + +[dev-dependencies] +serial_test = "0.9" +critical-section = { version = "1.1", features = ["std"] } + diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs index 79ae14b9..5c2ad3b2 100644 --- a/embassy-time/src/driver.rs +++ b/embassy-time/src/driver.rs @@ -105,20 +105,21 @@ pub trait Driver: Send + Sync + 'static { /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm /// timestamp, the provided callback function will be called. /// - /// If `timestamp` is already in the past, the alarm callback must be immediately fired. - /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. + /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`. + /// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set, + /// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously. /// /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. /// /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; } extern "Rust" { fn _embassy_time_now() -> u64; fn _embassy_time_allocate_alarm() -> Option; fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); + fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool; } /// See [`Driver::now`] @@ -139,7 +140,7 @@ pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ( } /// See [`Driver::set_alarm`] -pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) { +pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool { unsafe { _embassy_time_set_alarm(alarm, timestamp) } } @@ -167,7 +168,7 @@ macro_rules! time_driver_impl { } #[no_mangle] - fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) { + fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) -> bool { <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp) } }; diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index 2ddb2e60..fc7fd197 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -127,12 +127,14 @@ impl Driver for TimeDriver { alarm.ctx = ctx; } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.timestamp = timestamp; unsafe { self.signaler.as_ref() }.signal(); + + true } } diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index e4497e6a..63d04989 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -90,15 +90,23 @@ impl Driver for TimeDriver { })); } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; - let timeout = (timestamp - self.now()) as u32; if let Some(token) = alarm.token { clearTimeout(token); } - alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); + + let now = self.now(); + if timestamp <= now { + false + } else { + let timeout = (timestamp - now) as u32; + alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); + + true + } } } diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 4edc883f..586aa28d 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] +#![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] #![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] @@ -11,6 +11,7 @@ mod delay; pub mod driver; mod duration; mod instant; +pub mod queue; mod tick; mod timer; @@ -18,6 +19,8 @@ mod timer; mod driver_std; #[cfg(feature = "wasm")] mod driver_wasm; +#[cfg(feature = "generic-queue")] +mod queue_generic; pub use delay::{block_for, Delay}; pub use duration::Duration; diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs new file mode 100644 index 00000000..c6f8b440 --- /dev/null +++ b/embassy-time/src/queue.rs @@ -0,0 +1,58 @@ +//! Timer queue implementation +//! +//! This module defines the interface a timer queue needs to implement to power the `embassy_time` module. +//! +//! # Implementing a timer queue +//! +//! - Define a struct `MyTimerQueue` +//! - Implement [`TimerQueue`] for it +//! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). +//! +//! # Linkage details +//! +//! Check the documentation of the [`driver`](crate::driver) module for more information. +//! +//! Similarly to driver, if there is none or multiple timer queues in the crate tree, linking will fail. +//! +//! # Example +//! +//! ``` +//! use core::task::Waker; +//! +//! use embassy_time::Instant; +//! use embassy_time::queue::{TimerQueue}; +//! +//! struct MyTimerQueue{}; // not public! +//! embassy_time::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); +//! +//! impl TimerQueue for MyTimerQueue { +//! fn schedule_wake(&'static self, at: Instant, waker: &Waker) { +//! todo!() +//! } +//! } +//! ``` +use core::task::Waker; + +use crate::Instant; + +/// Timer queue +pub trait TimerQueue { + /// Schedules a waker in the queue to be awoken at moment `at`. + /// If this moment is in the past, the waker might be awoken immediately. + fn schedule_wake(&'static self, at: Instant, waker: &Waker); +} + +/// Set the TimerQueue implementation. +/// +/// See the module documentation for an example. +#[macro_export] +macro_rules! timer_queue_impl { + (static $name:ident: $t: ty = $val:expr) => { + static $name: $t = $val; + + #[no_mangle] + fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) { + <$t as $crate::queue::TimerQueue>::schedule_wake(&$name, at, waker); + } + }; +} diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs new file mode 100644 index 00000000..20ae7e6c --- /dev/null +++ b/embassy-time/src/queue_generic.rs @@ -0,0 +1,449 @@ +use core::cell::RefCell; +use core::cmp::{min, Ordering}; +use core::task::Waker; + +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use heapless::Vec; + +use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; +use crate::queue::TimerQueue; +use crate::Instant; + +#[cfg(feature = "generic-queue-8")] +const QUEUE_SIZE: usize = 8; +#[cfg(feature = "generic-queue-16")] +const QUEUE_SIZE: usize = 16; +#[cfg(feature = "generic-queue-32")] +const QUEUE_SIZE: usize = 32; +#[cfg(feature = "generic-queue-64")] +const QUEUE_SIZE: usize = 32; +#[cfg(feature = "generic-queue-128")] +const QUEUE_SIZE: usize = 128; +#[cfg(not(any( + feature = "generic-queue-8", + feature = "generic-queue-16", + feature = "generic-queue-32", + feature = "generic-queue-64", + feature = "generic-queue-128" +)))] +const QUEUE_SIZE: usize = 64; + +#[derive(Debug)] +struct Timer { + at: Instant, + waker: Waker, +} + +impl PartialEq for Timer { + fn eq(&self, other: &Self) -> bool { + self.at == other.at + } +} + +impl Eq for Timer {} + +impl PartialOrd for Timer { + fn partial_cmp(&self, other: &Self) -> Option { + self.at.partial_cmp(&other.at) + } +} + +impl Ord for Timer { + fn cmp(&self, other: &Self) -> Ordering { + self.at.cmp(&other.at) + } +} + +struct InnerQueue { + queue: Vec, + alarm: AlarmHandle, +} + +impl InnerQueue { + fn schedule_wake(&mut self, at: Instant, waker: &Waker) { + self.queue + .iter_mut() + .find(|timer| timer.waker.will_wake(waker)) + .map(|mut timer| { + timer.at = min(timer.at, at); + }) + .unwrap_or_else(|| { + let mut timer = Timer { + waker: waker.clone(), + at, + }; + + loop { + match self.queue.push(timer) { + Ok(()) => break, + Err(e) => timer = e, + } + + self.queue.pop().unwrap().waker.wake(); + } + }); + + // Don't wait for the alarm callback to trigger and directly + // dispatch all timers that are already due + // + // Then update the alarm if necessary + self.dispatch(); + } + + fn dispatch(&mut self) { + loop { + let now = Instant::now(); + + let mut next_alarm = Instant::MAX; + + let mut i = 0; + while i < self.queue.len() { + let timer = &self.queue[i]; + if timer.at <= now { + let timer = self.queue.swap_remove(i); + timer.waker.wake(); + } else { + next_alarm = min(next_alarm, timer.at); + i += 1; + } + } + + if self.update_alarm(next_alarm) { + break; + } + } + } + + fn update_alarm(&mut self, next_alarm: Instant) -> bool { + if next_alarm == Instant::MAX { + true + } else { + set_alarm(self.alarm, next_alarm.as_ticks()) + } + } + + fn handle_alarm(&mut self) { + self.dispatch(); + } +} + +struct Queue { + inner: Mutex>>, +} + +impl Queue { + const fn new() -> Self { + Self { + inner: Mutex::new(RefCell::new(None)), + } + } + + fn schedule_wake(&'static self, at: Instant, waker: &Waker) { + self.inner.lock(|inner| { + let mut inner = inner.borrow_mut(); + + if inner.is_none() {} + + inner + .get_or_insert_with(|| { + let handle = unsafe { allocate_alarm() }.unwrap(); + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); + InnerQueue { + queue: Vec::new(), + alarm: handle, + } + }) + .schedule_wake(at, waker) + }); + } + + fn handle_alarm(&self) { + self.inner + .lock(|inner| inner.borrow_mut().as_mut().unwrap().handle_alarm()); + } + + fn handle_alarm_callback(ctx: *mut ()) { + unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); + } +} + +impl TimerQueue for Queue { + fn schedule_wake(&'static self, at: Instant, waker: &Waker) { + Queue::schedule_wake(self, at, waker); + } +} + +crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); + +#[cfg(test)] +mod tests { + use core::cell::Cell; + use core::task::{RawWaker, RawWakerVTable, Waker}; + use std::rc::Rc; + use std::sync::Mutex; + + use serial_test::serial; + + use super::InnerQueue; + use crate::driver::{AlarmHandle, Driver}; + use crate::queue_generic::QUEUE; + use crate::Instant; + + struct InnerTestDriver { + now: u64, + alarm: u64, + callback: fn(*mut ()), + ctx: *mut (), + } + + impl InnerTestDriver { + const fn new() -> Self { + Self { + now: 0, + alarm: u64::MAX, + callback: Self::noop, + ctx: core::ptr::null_mut(), + } + } + + fn noop(_ctx: *mut ()) {} + } + + unsafe impl Send for InnerTestDriver {} + + struct TestDriver(Mutex); + + impl TestDriver { + const fn new() -> Self { + Self(Mutex::new(InnerTestDriver::new())) + } + + fn reset(&self) { + *self.0.lock().unwrap() = InnerTestDriver::new(); + } + + fn set_now(&self, now: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if inner.now < now { + inner.now = now; + + if inner.alarm <= now { + inner.alarm = u64::MAX; + + Some((inner.callback, inner.ctx)) + } else { + None + } + } else { + panic!("Going back in time?"); + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + impl Driver for TestDriver { + fn now(&self) -> u64 { + self.0.lock().unwrap().now + } + + unsafe fn allocate_alarm(&self) -> Option { + Some(AlarmHandle::new(0)) + } + + fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + let mut inner = self.0.lock().unwrap(); + + inner.callback = callback; + inner.ctx = ctx; + } + + fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool { + let mut inner = self.0.lock().unwrap(); + + if timestamp <= inner.now { + false + } else { + inner.alarm = timestamp; + true + } + } + } + + struct TestWaker { + pub awoken: Rc>, + pub waker: Waker, + } + + impl TestWaker { + fn new() -> Self { + let flag = Rc::new(Cell::new(false)); + + const VTABLE: RawWakerVTable = RawWakerVTable::new( + |data: *const ()| { + unsafe { + Rc::increment_strong_count(data as *const Cell); + } + + RawWaker::new(data as _, &VTABLE) + }, + |data: *const ()| unsafe { + let data = data as *const Cell; + data.as_ref().unwrap().set(true); + Rc::decrement_strong_count(data); + }, + |data: *const ()| unsafe { + (data as *const Cell).as_ref().unwrap().set(true); + }, + |data: *const ()| unsafe { + Rc::decrement_strong_count(data); + }, + ); + + let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE); + + Self { + awoken: flag.clone(), + waker: unsafe { Waker::from_raw(raw) }, + } + } + } + + crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); + + fn setup() { + DRIVER.reset(); + + QUEUE.inner.lock(|inner| { + *inner.borrow_mut() = InnerQueue::new(); + }); + } + + fn queue_len() -> usize { + QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) + } + + #[test] + #[serial] + fn test_schedule() { + setup(); + + assert_eq!(queue_len(), 0); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert!(!waker.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_schedule_same() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + assert_eq!(queue_len(), 1); + + let waker2 = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker2.waker); + + assert_eq!(queue_len(), 2); + } + + #[test] + #[serial] + fn test_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + assert!(!waker.awoken.get()); + + DRIVER.set_now(Instant::from_secs(99).as_ticks()); + + assert!(!waker.awoken.get()); + + assert_eq!(queue_len(), 1); + + DRIVER.set_now(Instant::from_secs(100).as_ticks()); + + assert!(waker.awoken.get()); + + assert_eq!(queue_len(), 0); + } + + #[test] + #[serial] + fn test_immediate_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + DRIVER.set_now(Instant::from_secs(50).as_ticks()); + + let waker2 = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(40), &waker2.waker); + + assert!(!waker.awoken.get()); + assert!(waker2.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_queue_overflow() { + setup(); + + for i in 1..super::QUEUE_SIZE { + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(310), &waker.waker); + + assert_eq!(queue_len(), i); + assert!(!waker.awoken.get()); + } + + let first_waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(300), &first_waker.waker); + + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(!first_waker.awoken.get()); + + let second_waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(305), &second_waker.waker); + + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(first_waker.awoken.get()); + + QUEUE.schedule_wake(Instant::from_secs(320), &TestWaker::new().waker); + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(second_waker.awoken.get()); + } +} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 6949042e..c633f82f 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -17,7 +17,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } -embedded-io = "0.3.0" +embedded-io = "0.3.1" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 38355bbf..31f68830 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -28,8 +28,8 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "0.1.0-alpha.1" } -embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +embedded-hal-async = { version = "0.1.0-alpha.3" } +embedded-io = { version = "0.3.1", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = "1.0.0" diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index b9bd1e71..79025838 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["lo embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } -embedded-io = { version = "0.3.0", features = ["async", "std", "futures"] } +embedded-io = { version = "0.3.1", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } async-io = "1.6.0" diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 2d6b4a0e..ed59e279 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -16,11 +16,19 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PB1; - let mut vref = adc.enable_vref(&mut Delay); - adc.calibrate(&mut vref); + let mut vrefint = adc.enable_vref(&mut Delay); + let vrefint_sample = adc.read(&mut vrefint); + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/CD00161566.pdf + // 5.3.4 Embedded reference voltage + const VREFINT_MV: u32 = 1200; // mV + + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 + }; + loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 3bc5a287..47121acf 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, p.DMA1_CH4, NoDma, config); + let irq = interrupt::take!(USART1); + let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, irq, p.DMA1_CH4, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 6d4f09fb..b05457ea 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = "0.3" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-io = "0.3.0" +embedded-io = "0.3.1" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 1d030f7d..1c9a0b35 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -24,19 +24,44 @@ async fn main(_spawner: Spawner) { // Startup delay can be combined to the maximum of either delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); + let vrefint_sample = adc.read_internal(&mut vrefint); + + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00071990.pdf + // 6.3.24 Reference voltage + const VREFINT_MV: u32 = 1210; // mV + + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 + }; + + let convert_to_celcius = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00071990.pdf + // 6.3.22 Temperature sensor characteristics + const V25: i32 = 760; // mV + const AVG_SLOPE: f32 = 2.5; // mV/C + + let sample_mv = convert_to_millivolts(sample) as i32; + + (sample_mv - V25) as f32 / AVG_SLOPE + 25.0 + }; + + info!("VrefInt: {}", vrefint_sample); + const MAX_ADC_SAMPLE: u16 = (1 << 12) - 1; + info!("VCCA: {} mV", convert_to_millivolts(MAX_ADC_SAMPLE)); + loop { // Read pin let v = adc.read(&mut pin); - info!("PC1: {} ({} mV)", v, adc.to_millivolts(v)); + info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); // Read internal temperature let v = adc.read_internal(&mut temp); - let celcius = Temperature::to_celcius(adc.to_millivolts(v)); + let celcius = convert_to_celcius(v); info!("Internal temp: {} ({} C)", v, celcius); // Read internal voltage reference let v = adc.read_internal(&mut vrefint); - info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v)); + info!("VrefInt: {}", v); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs new file mode 100644 index 00000000..6e51c211 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c.rs @@ -0,0 +1,45 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dma::NoDma; +use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::interrupt; +use embassy_stm32::time::Hertz; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + NoDma, + NoDma, + Hertz(100_000), + Default::default(), + ); + + // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. + // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. + let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); + + let mut data = [0u8; 1]; + + match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { + Ok(()) => info!("Whoami: {}", data[0]), + Err(Error::Timeout) => error!("Operation timed out"), + Err(e) => error!("I2c Error: {:?}", e), + } +} diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 90ad882b..8f41bb6c 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs @@ -5,6 +5,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); + let irq = interrupt::take!(USART3); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index 7bcecbd2..dd171fe1 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -4,9 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; +use embassy_stm32::usart::{BufferedUart, Config, State}; use embedded_io::asynch::BufRead; use {defmt_rtt as _, panic_probe as _}; @@ -16,13 +15,21 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); let mut state = State::new(); let irq = interrupt::take!(USART3); let mut tx_buf = [0u8; 32]; let mut rx_buf = [0u8; 32]; - let mut buf_usart = BufferedUart::new(&mut state, usart, irq, &mut tx_buf, &mut rx_buf); + let mut buf_usart = BufferedUart::new( + &mut state, + p.USART3, + p.PD9, + p.PD8, + irq, + &mut tx_buf, + &mut rx_buf, + config, + ); loop { let buf = buf_usart.fill_buf().await.unwrap(); diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index bb41b8b4..78baeaa0 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, p.DMA1_CH3, NoDma, config); + let irq = interrupt::take!(USART3); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, p.DMA1_CH3, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index dad92c0f..b14afd2f 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } -embedded-io = { version = "0.3.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index 80fad8c4..70b3b2a7 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -16,9 +16,19 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PA3; + let mut vrefint = adc.enable_vrefint(); + let vrefint_sample = adc.read_internal(&mut vrefint); + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00273119.pdf + // 6.3.27 Reference voltage + const VREFINT_MV: u32 = 1210; // mV + + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 + }; + loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index 07270479..4827c52a 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, p.DMA1_CH1, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, irq, p.DMA1_CH1, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 1a05b9ec..0dccff6e 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } -embedded-io = { version = "0.3.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } defmt = "0.3" defmt-rtt = "0.3" @@ -19,7 +19,7 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } embedded-nal-async = "0.2.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs new file mode 100644 index 00000000..d44319ae --- /dev/null +++ b/examples/stm32h7/src/bin/i2c.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::interrupt; +use embassy_stm32::time::Hertz; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + p.DMA1_CH4, + p.DMA1_CH5, + Hertz(100_000), + Default::default(), + ); + + // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. + // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. + let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); + + let mut data = [0u8; 1]; + + match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { + Ok(()) => info!("Whoami: {}", data[0]), + Err(Error::Timeout) => error!("Operation timed out"), + Err(e) => error!("I2c Error: {:?}", e), + } +} diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index 87c2b125..405f18ec 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs @@ -6,6 +6,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, NoDma, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index 3adffcbe..6e3491e5 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs @@ -8,6 +8,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use static_cell::StaticCell; @@ -18,7 +19,8 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index df2b600f..f97176ec 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -5,6 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::peripherals::{DMA1_CH1, UART7}; use embassy_stm32::usart::{Config, Uart, UartRx}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; @@ -31,7 +32,8 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, p.DMA1_CH1, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, p.DMA1_CH1, config); unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); let (mut tx, rx) = usart.split(); diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 7e1120f4..8b00773b 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -22,7 +22,7 @@ defmt = "0.3" defmt-rtt = "0.3" embedded-storage = "0.3.0" -embedded-io = "0.3.0" +embedded-io = "0.3.1" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l0/src/bin/usart_dma.rs b/examples/stm32l0/src/bin/usart_dma.rs index 66657d0f..c307f857 100644 --- a/examples/stm32l0/src/bin/usart_dma.rs +++ b/examples/stm32l0/src/bin/usart_dma.rs @@ -4,13 +4,15 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, p.DMA1_CH2, p.DMA1_CH3, Config::default()); + let irq = interrupt::take!(USART1); + let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH2, p.DMA1_CH3, Config::default()); usart.write(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 0e223738..8e84cd09 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -4,9 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; +use embassy_stm32::usart::{BufferedUart, Config, State}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -21,15 +20,18 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 9600; - let usart = Uart::new(p.USART2, p.PA3, p.PA2, NoDma, NoDma, config); let mut state = State::new(); + let irq = interrupt::take!(USART2); let mut usart = unsafe { BufferedUart::new( &mut state, - usart, - interrupt::take!(USART2), + p.USART2, + p.PA3, + p.PA2, + irq, &mut TX_BUFFER, &mut RX_BUFFER, + config, ) }; diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 657605eb..83d456b2 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -20,7 +20,7 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 4a4b46c5..7d874d9d 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; @@ -14,7 +15,8 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, NoDma, NoDma, config); + let irq = interrupt::take!(UART4); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index 72890689..452bede3 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, p.DMA1_CH3, NoDma, config); + let irq = interrupt::take!(UART4); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, p.DMA1_CH3, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 63eac3ed..848723f8 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -26,5 +26,5 @@ embedded-hal = "0.2.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } rand_core = { version = "0.6.3", default-features = false } -embedded-io = { version = "0.3.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } static_cell = "1.0" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1ec19e58..c05aac3f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2022-09-22" +channel = "nightly-2022-10-25" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 48c88e85..069b7fb8 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -18,10 +18,10 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } -embedded-io = { version = "0.3.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } embedded-storage = { version = "0.3" } [profile.dev] diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bebbf557..602c1fb5 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -26,7 +26,7 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev] diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 7673bfe6..af55867f 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -7,6 +7,7 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use example_common::*; @@ -18,22 +19,22 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart) = (p.PA9, p.PA10, p.USART1); + let (tx, rx, usart, irq) = (p.PA9, p.PA10, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart) = (p.PC4, p.PC5, p.USART1); + let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart) = (p.PC4, p.PC5, p.USART1); + let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart) = (p.PG14, p.PG9, p.USART6); + let (tx, rx, usart, irq) = (p.PG14, p.PG9, p.USART6, interrupt::take!(USART6)); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart) = (p.PA2, p.PA3, p.LPUART1); + let (tx, rx, usart, irq) = (p.PA2, p.PA3, p.LPUART1, interrupt::take!(LPUART1)); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart) = (p.PB6, p.PB7, p.USART1); + let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart) = (p.PD8, p.PD9, p.USART3); + let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, NoDma, NoDma, config); + let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index e0389446..d12605a9 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -6,6 +6,7 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use example_common::*; @@ -17,22 +18,53 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PA9, p.PA10, p.USART1, p.DMA1_CH4, p.DMA1_CH5); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PA9, + p.PA10, + p.USART1, + interrupt::take!(USART1), + p.DMA1_CH4, + p.DMA1_CH5, + ); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PG14, p.PG9, p.USART6, p.DMA2_CH6, p.DMA2_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PG14, + p.PG9, + p.USART6, + interrupt::take!(USART6), + p.DMA2_CH6, + p.DMA2_CH1, + ); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PA2, p.PA3, p.LPUART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PA2, + p.PA3, + p.LPUART1, + interrupt::take!(LPUART1), + p.DMA1_CH1, + p.DMA1_CH2, + ); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH0, p.DMA1_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH0, p.DMA1_CH1); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PD8, p.PD9, p.USART3, p.GPDMA1_CH0, p.GPDMA1_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PD8, + p.PD9, + p.USART3, + interrupt::take!(USART3), + p.GPDMA1_CH0, + p.GPDMA1_CH1, + ); let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, tx_dma, rx_dma, config); + let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time.