From f9c62d4f1da1c4a38219056be3f658c34d0031af Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 29 Sep 2022 09:09:35 +0200 Subject: [PATCH 01/31] Add flowcontrol to UART --- embassy-stm32/src/usart/mod.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6c266874..22de6d18 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -214,7 +214,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { 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); @@ -241,6 +240,29 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } + pub fn new_with_rtscts( + _inner: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: 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); + + 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, rx, tx, tx_dma, rx_dma, config) + } + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> where TxDma: crate::usart::TxDma, From c2404ee8ca6200d9037f096eee3f0eab98711778 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Tue, 6 Sep 2022 21:39:23 +0300 Subject: [PATCH 02/31] Initial generic timer queue impl --- embassy-time/Cargo.toml | 5 + embassy-time/src/lib.rs | 1 + embassy-time/src/queue.rs | 197 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 embassy-time/src/queue.rs diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c51a71d0..0e3391d1 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,6 +26,9 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] +# TODO: Doc +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. @@ -111,9 +114,11 @@ embedded-hal-async = { version = "=0.1.0-alpha.2", 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 } diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 4edc883f..0457a657 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -11,6 +11,7 @@ mod delay; pub mod driver; mod duration; mod instant; +pub mod queue; mod tick; mod timer; diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs new file mode 100644 index 00000000..7e84090b --- /dev/null +++ b/embassy-time/src/queue.rs @@ -0,0 +1,197 @@ +//! Generic timer queue implementation +use core::cell::RefCell; +use core::cmp::Ordering; +use core::task::Waker; + +use atomic_polyfill::{AtomicBool, Ordering as AtomicOrdering}; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use embassy_sync::blocking_mutex::Mutex; +use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; + +use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; +use crate::Instant; + +#[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: SortedLinkedList, + alarm_at: Instant, + alarm: Option, +} + +impl InnerQueue { + const fn new() -> Self { + Self { + queue: SortedLinkedList::new_u8(), + alarm_at: Instant::MAX, + alarm: None, + } + } + + fn schedule(&mut self, at: Instant, waker: &Waker) { + self.queue + .find_mut(|timer| timer.waker.will_wake(waker)) + .map(|mut timer| { + timer.waker = waker.clone(); + timer.at = at; + + timer.finish(); + }) + .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) { + let now = Instant::now(); + + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + self.update_alarm(); + } + + fn update_alarm(&mut self) { + if let Some(timer) = self.queue.peek() { + let new_at = timer.at; + + if self.alarm_at != new_at { + self.alarm_at = new_at; + set_alarm(self.alarm.unwrap(), new_at.as_ticks()); + } + } else { + self.alarm_at = Instant::MAX; + } + } + + fn handle_alarm(&mut self) { + self.alarm_at = Instant::MAX; + + self.dispatch(); + } +} + +/// TODO: Doc +pub struct Queue { + initialized: AtomicBool, + inner: Mutex>>, +} + +impl Queue { + /// TODO: Doc + pub const fn new() -> Self { + Self { + initialized: AtomicBool::new(false), + inner: Mutex::new(RefCell::new(InnerQueue::::new())), + } + } + + /// TODO: Doc + pub unsafe fn initialize(&'static self) { + if self.initialized.load(AtomicOrdering::SeqCst) { + panic!("Queue already initialized"); + } + + let handle = allocate_alarm().unwrap(); + self.inner.lock(|inner| inner.borrow_mut().alarm = Some(handle)); + + set_alarm_callback(handle, Self::handle_alarm, self as *const _ as _); + + self.initialized.store(true, AtomicOrdering::SeqCst); + } + + /// TODO: Doc + pub fn schedule(&'static self, at: Instant, waker: &Waker) { + self.check_initialized(); + + self.inner.lock(|inner| inner.borrow_mut().schedule(at, waker)); + } + + fn check_initialized(&self) { + if !self.initialized.load(AtomicOrdering::SeqCst) { + panic!("Queue is not initialized"); + } + } + + fn handle_alarm(ctx: *mut ()) { + let this = unsafe { (ctx as *const Self).as_ref().unwrap() }; + + this.check_initialized(); + this.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + } +} + +/// TODO: Doc +pub unsafe fn initialize() { + extern "Rust" { + fn _embassy_time_generic_queue_initialize(); + } + + _embassy_time_generic_queue_initialize(); +} + +/// TODO: Doc +#[macro_export] +macro_rules! generic_queue { + (static $name:ident: $t: ty = $val:expr) => { + static $name: $t = $val; + + #[no_mangle] + fn _embassy_time_generic_queue_initialize() { + unsafe { + $crate::queue::Queue::initialize(&$name); + } + } + + #[no_mangle] + fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) { + $crate::queue::Queue::schedule(&$name, at, waker); + } + }; +} + +#[cfg(feature = "generic-queue")] +generic_queue!(static QUEUE: Queue = Queue::new()); From ba6e452cc5d6c33029f34d7cfb5cd5ea846979bd Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Tue, 20 Sep 2022 20:23:56 +0300 Subject: [PATCH 03/31] Documentation and initial testing framework Add mock waker First simple test Tests & documentation --- embassy-time/Cargo.toml | 6 +- embassy-time/src/lib.rs | 2 +- embassy-time/src/queue.rs | 486 +++++++++++++++++++++++++++++++++++--- 3 files changed, 459 insertions(+), 35 deletions(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0e3391d1..4fbf97f0 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,7 +26,8 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] -# TODO: Doc +# Create a global queue that can be used with any executor +# To use this you must have a time driver provided. generic-queue = [] # Set the `embassy_time` tick rate. @@ -124,3 +125,6 @@ heapless = "0.7" 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" diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 0457a657..50f437ba 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)] diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs index 7e84090b..56ad5af8 100644 --- a/embassy-time/src/queue.rs +++ b/embassy-time/src/queue.rs @@ -1,9 +1,53 @@ //! Generic timer queue implementation -use core::cell::RefCell; +//! +//! This module provides a timer queue that works with any executor. +//! +//! In terms of performance, this queue will likely be less efficient in comparison to executor-native queues, +//! like the one provided with e.g. the `embassy-executor` crate. +//! +//! # Enabling the queue +//! - Enable the Cargo feature `generic-queue`. This will automatically instantiate the queue. +//! +//! # Initializing the queue +//! - Call ```unsafe { embassy_time::queue::initialize(); }``` early on in your program, before any of your futures that utilize `embassy-time` are polled. +//! +//! # Customizing the queue +//! - It is possible to customize two aspects of the queue: +//! - Queue size: +//! By default, the queue can hold up to 128 timer schedules and their corresponding wakers. While it will not crash if more timer schedules are added, +//! the performance will degrade, as one of the already added wakers will be awoken, thus making room for the new timer schedule and its waker. +//! - The mutex (i.e. the [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation) utilized by the queue: +//! By default, the utilized [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation is [`CriticalSectionRawMutex`](embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex) +//! which is provided by the `critical-section` crate. This should work just fine, except in a few niche cases like running on +//! top of an RTOS which provides a [`Driver`](crate::driver::Driver) implementation that will call-back directly from an ISR. As the +//! `critical-section` implementation for RTOS-es will likely provide an RTOS mutex which cannot be locked from an ISR, user needs to instead +//! configure the queue with a "disable-all-interrupts" style of mutex. +//! - To customize any of these queue aspects, don't enable the `generic-queue` Cargo feature and instead instantiate the queue with the [`generic_queue`](crate::generic_queue) +//! macro, as per the example below. +//! +//! +//! # Example +//! +//! ```ignore +//! use embassy_time::queue::Queue; +//! +//! // You only need to invoke this macro in case you need to customize the queue. +//! // +//! // Otherwise, just depend on the `embassy-time` crate with feature `generic-queue` enabled, +//! // and the queue instantiation will be done for you behind the scenes. +//! embassy_time::generic_queue!(static QUEUE: Queue<200, MyCustomRawMutex> = Queue::new()); +//! +//! fn main() { +//! unsafe { +//! embassy_time::queue::initialize(); +//! } +//! } +//! ``` +use core::cell::{Cell, RefCell}; use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicBool, Ordering as AtomicOrdering}; +use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; use embassy_sync::blocking_mutex::Mutex; use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; @@ -40,7 +84,6 @@ impl Ord for Timer { struct InnerQueue { queue: SortedLinkedList, alarm_at: Instant, - alarm: Option, } impl InnerQueue { @@ -48,11 +91,10 @@ impl InnerQueue { Self { queue: SortedLinkedList::new_u8(), alarm_at: Instant::MAX, - alarm: None, } } - fn schedule(&mut self, at: Instant, waker: &Waker) { + fn schedule(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { @@ -81,90 +123,128 @@ impl InnerQueue { // dispatch all timers that are already due // // Then update the alarm if necessary - self.dispatch(); + self.dispatch(alarm_schedule); } - fn dispatch(&mut self) { + fn dispatch(&mut self, alarm_schedule: &AtomicU64) { let now = Instant::now(); while self.queue.peek().filter(|timer| timer.at <= now).is_some() { self.queue.pop().unwrap().waker.wake(); } - self.update_alarm(); + self.update_alarm(alarm_schedule); } - fn update_alarm(&mut self) { + fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { if let Some(timer) = self.queue.peek() { let new_at = timer.at; if self.alarm_at != new_at { self.alarm_at = new_at; - set_alarm(self.alarm.unwrap(), new_at.as_ticks()); + alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); } } else { self.alarm_at = Instant::MAX; + alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); } } - fn handle_alarm(&mut self) { + fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { self.alarm_at = Instant::MAX; - self.dispatch(); + self.dispatch(alarm_schedule); } } -/// TODO: Doc +/// The generic queue implementation pub struct Queue { - initialized: AtomicBool, inner: Mutex>>, + alarm: Cell>, + alarm_schedule: AtomicU64, } impl Queue { - /// TODO: Doc + /// Create a Queue pub const fn new() -> Self { Self { - initialized: AtomicBool::new(false), inner: Mutex::new(RefCell::new(InnerQueue::::new())), + alarm: Cell::new(None), + alarm_schedule: AtomicU64::new(u64::MAX), } } - /// TODO: Doc + /// Initialize the queue + /// + /// This method is called from [`initialize`](crate::queue::initialize), so you are not expected to call it directly. + /// Call [`initialize`](crate::queue::initialize) instead. + /// + /// # Safety + /// It is UB call this function more than once, or to call it after any of your + /// futures that use `embassy-time` are polled already. pub unsafe fn initialize(&'static self) { - if self.initialized.load(AtomicOrdering::SeqCst) { - panic!("Queue already initialized"); + if self.alarm.get().is_some() { + panic!("Queue is already initialized"); } let handle = allocate_alarm().unwrap(); - self.inner.lock(|inner| inner.borrow_mut().alarm = Some(handle)); + self.alarm.set(Some(handle)); - set_alarm_callback(handle, Self::handle_alarm, self as *const _ as _); - - self.initialized.store(true, AtomicOrdering::SeqCst); + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); } - /// TODO: Doc + /// Schedule a new waker to be awoken at moment `at` + /// + /// This method is called internally by [`embassy-time`](crate), so you are not expected to call it directly. pub fn schedule(&'static self, at: Instant, waker: &Waker) { self.check_initialized(); - self.inner.lock(|inner| inner.borrow_mut().schedule(at, waker)); + self.inner + .lock(|inner| inner.borrow_mut().schedule(at, waker, &self.alarm_schedule)); + + self.update_alarm(); } fn check_initialized(&self) { - if !self.initialized.load(AtomicOrdering::SeqCst) { - panic!("Queue is not initialized"); + if self.alarm.get().is_none() { + panic!("Queue is not initialized yet"); } } - fn handle_alarm(ctx: *mut ()) { - let this = unsafe { (ctx as *const Self).as_ref().unwrap() }; + fn update_alarm(&self) { + // Need to set the alarm when we are *not* holding the mutex on the inner queue + // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately + // call us back if the timestamp is in the past. + let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - this.check_initialized(); - this.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + if alarm_at < u64::MAX { + set_alarm(self.alarm.get().unwrap(), alarm_at); + } + } + + fn handle_alarm(&self) { + self.check_initialized(); + self.inner + .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); + + self.update_alarm(); + } + + fn handle_alarm_callback(ctx: *mut ()) { + unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); } } -/// TODO: Doc +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} + +/// Initialize the queue +/// +/// Call this function early on in your program, before any of your futures that utilize `embassy-time` are polled. +/// +/// # Safety +/// It is UB call this function more than once, or to call it after any of your +/// futures that use `embassy-time` are polled already. pub unsafe fn initialize() { extern "Rust" { fn _embassy_time_generic_queue_initialize(); @@ -173,7 +253,12 @@ pub unsafe fn initialize() { _embassy_time_generic_queue_initialize(); } -/// TODO: Doc +/// Instantiates a global, generic (as in executor-agnostic) timer queue. +/// +/// Unless you plan to customize the queue (size or mutex), prefer +/// instantiating the queue via the `generic-queue` feature. +/// +/// See the module documentation for an example. #[macro_export] macro_rules! generic_queue { (static $name:ident: $t: ty = $val:expr) => { @@ -195,3 +280,338 @@ macro_rules! generic_queue { #[cfg(feature = "generic-queue")] generic_queue!(static QUEUE: Queue = Queue::new()); + +#[cfg(test)] +mod tests { + use core::cell::Cell; + use core::sync::atomic::Ordering; + use core::task::{RawWaker, RawWakerVTable, Waker}; + use std::rc::Rc; + use std::sync::Mutex; + + use embassy_sync::blocking_mutex::raw::RawMutex; + use serial_test::serial; + + use super::InnerQueue; + use crate::driver::{AlarmHandle, Driver}; + 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) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if timestamp <= inner.now { + Some((inner.callback, inner.ctx)) + } else { + inner.alarm = timestamp; + None + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + 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) }, + } + } + } + + // TODO: This impl should be part of `embassy-sync`, hidden behind the "std" feature gate + pub struct StdRawMutex(std::sync::Mutex<()>); + + unsafe impl RawMutex for StdRawMutex { + const INIT: Self = StdRawMutex(std::sync::Mutex::new(())); + + fn lock(&self, f: impl FnOnce() -> R) -> R { + let _guard = self.0.lock().unwrap(); + + f() + } + } + + const QUEUE_MAX_LEN: usize = 8; + + crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); + crate::generic_queue!(static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new()); + + fn setup() { + DRIVER.reset(); + + QUEUE.alarm.set(None); + QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); + QUEUE.inner.lock(|inner| { + *inner.borrow_mut() = InnerQueue::new(); + }); + + unsafe { super::initialize() }; + } + + fn queue_len() -> usize { + QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) + } + + #[test] + #[serial] + #[should_panic(expected = "Queue is not initialized yet")] + fn test_not_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + } + + #[test] + #[serial] + fn test_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + assert!(QUEUE.alarm.get().is_none()); + + unsafe { QUEUE.initialize() }; + + assert!(QUEUE.alarm.get().is_some()); + } + + #[test] + #[serial] + #[should_panic(expected = "Queue is already initialized")] + fn test_already_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + unsafe { QUEUE.initialize() }; + + assert!(QUEUE.alarm.get().is_some()); + + unsafe { QUEUE.initialize() }; + } + + #[test] + #[serial] + fn test_schedule() { + setup(); + + assert_eq!(queue_len(), 0); + + let waker = TestWaker::new(); + + QUEUE.schedule(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(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule(Instant::from_secs(100), &waker.waker); + + assert_eq!(queue_len(), 1); + + let waker2 = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(100), &waker2.waker); + + assert_eq!(queue_len(), 2); + } + + #[test] + #[serial] + fn test_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule(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(Instant::from_secs(100), &waker.waker); + + DRIVER.set_now(Instant::from_secs(50).as_ticks()); + + let waker2 = TestWaker::new(); + + QUEUE.schedule(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..QUEUE_MAX_LEN { + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(310), &waker.waker); + + assert_eq!(queue_len(), i); + assert!(!waker.awoken.get()); + } + + let first_waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(300), &first_waker.waker); + + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(!first_waker.awoken.get()); + + let second_waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(305), &second_waker.waker); + + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(first_waker.awoken.get()); + + QUEUE.schedule(Instant::from_secs(320), &TestWaker::new().waker); + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(second_waker.awoken.get()); + } +} From 53608a87ac4b6c8c60b5508551d12f5ba76ca2f6 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 26 Sep 2022 13:46:15 +0300 Subject: [PATCH 04/31] Address feedback after code review --- embassy-time/Cargo.toml | 16 +- embassy-time/src/lib.rs | 2 + embassy-time/src/queue.rs | 613 ++---------------------------- embassy-time/src/queue_generic.rs | 474 +++++++++++++++++++++++ 4 files changed, 518 insertions(+), 587 deletions(-) create mode 100644 embassy-time/src/queue_generic.rs diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 4fbf97f0..e1ad4b9d 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,10 +26,22 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] -# Create a global queue that can be used with any executor +# 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. @@ -128,3 +140,5 @@ 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/lib.rs b/embassy-time/src/lib.rs index 50f437ba..586aa28d 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -19,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 index 56ad5af8..c6f8b440 100644 --- a/embassy-time/src/queue.rs +++ b/embassy-time/src/queue.rs @@ -1,617 +1,58 @@ -//! Generic timer queue implementation +//! Timer queue implementation //! -//! This module provides a timer queue that works with any executor. +//! This module defines the interface a timer queue needs to implement to power the `embassy_time` module. //! -//! In terms of performance, this queue will likely be less efficient in comparison to executor-native queues, -//! like the one provided with e.g. the `embassy-executor` crate. +//! # Implementing a timer queue //! -//! # Enabling the queue -//! - Enable the Cargo feature `generic-queue`. This will automatically instantiate the queue. +//! - Define a struct `MyTimerQueue` +//! - Implement [`TimerQueue`] for it +//! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). //! -//! # Initializing the queue -//! - Call ```unsafe { embassy_time::queue::initialize(); }``` early on in your program, before any of your futures that utilize `embassy-time` are polled. +//! # Linkage details //! -//! # Customizing the queue -//! - It is possible to customize two aspects of the queue: -//! - Queue size: -//! By default, the queue can hold up to 128 timer schedules and their corresponding wakers. While it will not crash if more timer schedules are added, -//! the performance will degrade, as one of the already added wakers will be awoken, thus making room for the new timer schedule and its waker. -//! - The mutex (i.e. the [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation) utilized by the queue: -//! By default, the utilized [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation is [`CriticalSectionRawMutex`](embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex) -//! which is provided by the `critical-section` crate. This should work just fine, except in a few niche cases like running on -//! top of an RTOS which provides a [`Driver`](crate::driver::Driver) implementation that will call-back directly from an ISR. As the -//! `critical-section` implementation for RTOS-es will likely provide an RTOS mutex which cannot be locked from an ISR, user needs to instead -//! configure the queue with a "disable-all-interrupts" style of mutex. -//! - To customize any of these queue aspects, don't enable the `generic-queue` Cargo feature and instead instantiate the queue with the [`generic_queue`](crate::generic_queue) -//! macro, as per the example below. +//! 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 //! -//! ```ignore -//! use embassy_time::queue::Queue; +//! ``` +//! use core::task::Waker; //! -//! // You only need to invoke this macro in case you need to customize the queue. -//! // -//! // Otherwise, just depend on the `embassy-time` crate with feature `generic-queue` enabled, -//! // and the queue instantiation will be done for you behind the scenes. -//! embassy_time::generic_queue!(static QUEUE: Queue<200, MyCustomRawMutex> = Queue::new()); +//! use embassy_time::Instant; +//! use embassy_time::queue::{TimerQueue}; //! -//! fn main() { -//! unsafe { -//! embassy_time::queue::initialize(); +//! 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::cell::{Cell, RefCell}; -use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; -use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; -use embassy_sync::blocking_mutex::Mutex; -use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; - -use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; use crate::Instant; -#[derive(Debug)] -struct Timer { - at: Instant, - waker: Waker, +/// 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); } -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: SortedLinkedList, - alarm_at: Instant, -} - -impl InnerQueue { - const fn new() -> Self { - Self { - queue: SortedLinkedList::new_u8(), - alarm_at: Instant::MAX, - } - } - - fn schedule(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { - self.queue - .find_mut(|timer| timer.waker.will_wake(waker)) - .map(|mut timer| { - timer.waker = waker.clone(); - timer.at = at; - - timer.finish(); - }) - .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(alarm_schedule); - } - - fn dispatch(&mut self, alarm_schedule: &AtomicU64) { - let now = Instant::now(); - - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); - } - - self.update_alarm(alarm_schedule); - } - - fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { - if let Some(timer) = self.queue.peek() { - let new_at = timer.at; - - if self.alarm_at != new_at { - self.alarm_at = new_at; - alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); - } - } else { - self.alarm_at = Instant::MAX; - alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); - } - } - - fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { - self.alarm_at = Instant::MAX; - - self.dispatch(alarm_schedule); - } -} - -/// The generic queue implementation -pub struct Queue { - inner: Mutex>>, - alarm: Cell>, - alarm_schedule: AtomicU64, -} - -impl Queue { - /// Create a Queue - pub const fn new() -> Self { - Self { - inner: Mutex::new(RefCell::new(InnerQueue::::new())), - alarm: Cell::new(None), - alarm_schedule: AtomicU64::new(u64::MAX), - } - } - - /// Initialize the queue - /// - /// This method is called from [`initialize`](crate::queue::initialize), so you are not expected to call it directly. - /// Call [`initialize`](crate::queue::initialize) instead. - /// - /// # Safety - /// It is UB call this function more than once, or to call it after any of your - /// futures that use `embassy-time` are polled already. - pub unsafe fn initialize(&'static self) { - if self.alarm.get().is_some() { - panic!("Queue is already initialized"); - } - - let handle = allocate_alarm().unwrap(); - self.alarm.set(Some(handle)); - - set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); - } - - /// Schedule a new waker to be awoken at moment `at` - /// - /// This method is called internally by [`embassy-time`](crate), so you are not expected to call it directly. - pub fn schedule(&'static self, at: Instant, waker: &Waker) { - self.check_initialized(); - - self.inner - .lock(|inner| inner.borrow_mut().schedule(at, waker, &self.alarm_schedule)); - - self.update_alarm(); - } - - fn check_initialized(&self) { - if self.alarm.get().is_none() { - panic!("Queue is not initialized yet"); - } - } - - fn update_alarm(&self) { - // Need to set the alarm when we are *not* holding the mutex on the inner queue - // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately - // call us back if the timestamp is in the past. - let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - - if alarm_at < u64::MAX { - set_alarm(self.alarm.get().unwrap(), alarm_at); - } - } - - fn handle_alarm(&self) { - self.check_initialized(); - self.inner - .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); - - self.update_alarm(); - } - - fn handle_alarm_callback(ctx: *mut ()) { - unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); - } -} - -unsafe impl Send for Queue {} -unsafe impl Sync for Queue {} - -/// Initialize the queue -/// -/// Call this function early on in your program, before any of your futures that utilize `embassy-time` are polled. -/// -/// # Safety -/// It is UB call this function more than once, or to call it after any of your -/// futures that use `embassy-time` are polled already. -pub unsafe fn initialize() { - extern "Rust" { - fn _embassy_time_generic_queue_initialize(); - } - - _embassy_time_generic_queue_initialize(); -} - -/// Instantiates a global, generic (as in executor-agnostic) timer queue. -/// -/// Unless you plan to customize the queue (size or mutex), prefer -/// instantiating the queue via the `generic-queue` feature. +/// Set the TimerQueue implementation. /// /// See the module documentation for an example. #[macro_export] -macro_rules! generic_queue { +macro_rules! timer_queue_impl { (static $name:ident: $t: ty = $val:expr) => { static $name: $t = $val; - #[no_mangle] - fn _embassy_time_generic_queue_initialize() { - unsafe { - $crate::queue::Queue::initialize(&$name); - } - } - #[no_mangle] fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) { - $crate::queue::Queue::schedule(&$name, at, waker); + <$t as $crate::queue::TimerQueue>::schedule_wake(&$name, at, waker); } }; } - -#[cfg(feature = "generic-queue")] -generic_queue!(static QUEUE: Queue = Queue::new()); - -#[cfg(test)] -mod tests { - use core::cell::Cell; - use core::sync::atomic::Ordering; - use core::task::{RawWaker, RawWakerVTable, Waker}; - use std::rc::Rc; - use std::sync::Mutex; - - use embassy_sync::blocking_mutex::raw::RawMutex; - use serial_test::serial; - - use super::InnerQueue; - use crate::driver::{AlarmHandle, Driver}; - 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) { - let notify = { - let mut inner = self.0.lock().unwrap(); - - if timestamp <= inner.now { - Some((inner.callback, inner.ctx)) - } else { - inner.alarm = timestamp; - None - } - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); - } - } - } - - 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) }, - } - } - } - - // TODO: This impl should be part of `embassy-sync`, hidden behind the "std" feature gate - pub struct StdRawMutex(std::sync::Mutex<()>); - - unsafe impl RawMutex for StdRawMutex { - const INIT: Self = StdRawMutex(std::sync::Mutex::new(())); - - fn lock(&self, f: impl FnOnce() -> R) -> R { - let _guard = self.0.lock().unwrap(); - - f() - } - } - - const QUEUE_MAX_LEN: usize = 8; - - crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); - crate::generic_queue!(static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new()); - - fn setup() { - DRIVER.reset(); - - QUEUE.alarm.set(None); - QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); - QUEUE.inner.lock(|inner| { - *inner.borrow_mut() = InnerQueue::new(); - }); - - unsafe { super::initialize() }; - } - - fn queue_len() -> usize { - QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) - } - - #[test] - #[serial] - #[should_panic(expected = "Queue is not initialized yet")] - fn test_not_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - } - - #[test] - #[serial] - fn test_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - assert!(QUEUE.alarm.get().is_none()); - - unsafe { QUEUE.initialize() }; - - assert!(QUEUE.alarm.get().is_some()); - } - - #[test] - #[serial] - #[should_panic(expected = "Queue is already initialized")] - fn test_already_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - unsafe { QUEUE.initialize() }; - - assert!(QUEUE.alarm.get().is_some()); - - unsafe { QUEUE.initialize() }; - } - - #[test] - #[serial] - fn test_schedule() { - setup(); - - assert_eq!(queue_len(), 0); - - let waker = TestWaker::new(); - - QUEUE.schedule(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(Instant::from_secs(1), &waker.waker); - - assert_eq!(queue_len(), 1); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - - assert_eq!(queue_len(), 1); - - QUEUE.schedule(Instant::from_secs(100), &waker.waker); - - assert_eq!(queue_len(), 1); - - let waker2 = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(100), &waker2.waker); - - assert_eq!(queue_len(), 2); - } - - #[test] - #[serial] - fn test_trigger() { - setup(); - - let waker = TestWaker::new(); - - QUEUE.schedule(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(Instant::from_secs(100), &waker.waker); - - DRIVER.set_now(Instant::from_secs(50).as_ticks()); - - let waker2 = TestWaker::new(); - - QUEUE.schedule(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..QUEUE_MAX_LEN { - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(310), &waker.waker); - - assert_eq!(queue_len(), i); - assert!(!waker.awoken.get()); - } - - let first_waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(300), &first_waker.waker); - - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(!first_waker.awoken.get()); - - let second_waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(305), &second_waker.waker); - - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(first_waker.awoken.get()); - - QUEUE.schedule(Instant::from_secs(320), &TestWaker::new().waker); - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(second_waker.awoken.get()); - } -} diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs new file mode 100644 index 00000000..1c4e5398 --- /dev/null +++ b/embassy-time/src/queue_generic.rs @@ -0,0 +1,474 @@ +use core::cell::RefCell; +use core::cmp::Ordering; +use core::task::Waker; + +use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; + +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: SortedLinkedList, + alarm: Option, + alarm_at: Instant, +} + +impl InnerQueue { + const fn new() -> Self { + Self { + queue: SortedLinkedList::new_u8(), + alarm: None, + alarm_at: Instant::MAX, + } + } + + fn schedule_wake(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { + self.queue + .find_mut(|timer| timer.waker.will_wake(waker)) + .map(|mut timer| { + timer.at = at; + timer.finish(); + }) + .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(alarm_schedule); + } + + fn dispatch(&mut self, alarm_schedule: &AtomicU64) { + let now = Instant::now(); + + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + self.update_alarm(alarm_schedule); + } + + fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { + if let Some(timer) = self.queue.peek() { + let new_at = timer.at; + + if self.alarm_at != new_at { + self.alarm_at = new_at; + alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); + } + } else { + self.alarm_at = Instant::MAX; + alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); + } + } + + fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { + self.alarm_at = Instant::MAX; + + self.dispatch(alarm_schedule); + } +} + +struct Queue { + inner: Mutex>, + alarm_schedule: AtomicU64, +} + +impl Queue { + const fn new() -> Self { + Self { + inner: Mutex::new(RefCell::new(InnerQueue::new())), + alarm_schedule: AtomicU64::new(u64::MAX), + } + } + + fn schedule_wake(&'static self, at: Instant, waker: &Waker) { + self.inner.lock(|inner| { + let mut inner = inner.borrow_mut(); + + if inner.alarm.is_none() { + let handle = unsafe { allocate_alarm() }.unwrap(); + inner.alarm = Some(handle); + + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); + } + + inner.schedule_wake(at, waker, &self.alarm_schedule) + }); + + self.update_alarm(); + } + + fn update_alarm(&self) { + // Need to set the alarm when we are *not* holding the mutex on the inner queue + // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately + // call us back if the timestamp is in the past. + let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); + + if alarm_at < u64::MAX { + set_alarm(self.inner.lock(|inner| inner.borrow().alarm.unwrap()), alarm_at); + } + } + + fn handle_alarm(&self) { + self.inner + .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); + + self.update_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::sync::atomic::Ordering; + 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) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if timestamp <= inner.now { + Some((inner.callback, inner.ctx)) + } else { + inner.alarm = timestamp; + None + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + 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.alarm_schedule.store(u64::MAX, Ordering::SeqCst); + 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()); + } +} From 4d5550070fe5e80ff2296a71239c568c774b9ceb Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 09:17:43 +0300 Subject: [PATCH 05/31] Change time Driver contract to never fire the alarm synchronously --- embassy-executor/src/raw/mod.rs | 78 +++++++++++++++++-------------- embassy-nrf/src/time_driver.rs | 19 ++++---- embassy-rp/src/timer.rs | 17 +++---- embassy-stm32/src/time_driver.rs | 18 +++---- embassy-time/src/driver.rs | 13 +++--- embassy-time/src/driver_std.rs | 4 +- embassy-time/src/driver_wasm.rs | 4 +- embassy-time/src/queue_generic.rs | 77 +++++++++++------------------- 8 files changed, 113 insertions(+), 117 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e1258ebb..5bcb1e6e 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")] diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index c32a4463..0d03ad52 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -243,20 +243,19 @@ 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 t = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= t { + return false; + } + 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(); // If it hasn't triggered yet, setup it in the compare channel. @@ -287,6 +286,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-rp/src/timer.rs b/embassy-rp/src/timer.rs index 5215c0c0..8f280f55 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -68,9 +68,16 @@ 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 now = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= now { + return false; + } + let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(timestamp); @@ -80,13 +87,7 @@ impl Driver for TimerDriver { // it is checked if the alarm time has passed. 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); - } + true }) } } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index ed3225c5..e4c266e7 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -292,21 +292,21 @@ 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 t = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= t { + return false; + } + let r = T::regs_gp16(); let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); - let t = self.now(); - if timestamp <= t { - unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) }; - self.trigger_alarm(n, cs); - return; - } - let safe_timestamp = timestamp.max(t + 3); // Write the CCR value regardless of whether we're going to enable it now or not. @@ -317,6 +317,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-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..d7a6b0d8 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -81,13 +81,15 @@ impl Driver for TimeDriver { } } - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.closure.replace(Closure::new(move || { callback(ctx); })); + + true } fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 1c4e5398..83f73484 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -2,7 +2,6 @@ use core::cell::RefCell; use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; @@ -71,7 +70,7 @@ impl InnerQueue { } } - fn schedule_wake(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { + fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { @@ -98,50 +97,54 @@ impl InnerQueue { // dispatch all timers that are already due // // Then update the alarm if necessary - self.dispatch(alarm_schedule); + self.dispatch(); } - fn dispatch(&mut self, alarm_schedule: &AtomicU64) { - let now = Instant::now(); + fn dispatch(&mut self) { + loop { + let now = Instant::now(); - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + if self.update_alarm() { + break; + } } - - self.update_alarm(alarm_schedule); } - fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { + fn update_alarm(&mut self) -> bool { if let Some(timer) = self.queue.peek() { let new_at = timer.at; if self.alarm_at != new_at { self.alarm_at = new_at; - alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); + + return set_alarm(self.alarm.unwrap(), self.alarm_at.as_ticks()); } } else { self.alarm_at = Instant::MAX; - alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); } + + true } - fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { + fn handle_alarm(&mut self) { self.alarm_at = Instant::MAX; - self.dispatch(alarm_schedule); + self.dispatch(); } } struct Queue { inner: Mutex>, - alarm_schedule: AtomicU64, } impl Queue { const fn new() -> Self { Self { inner: Mutex::new(RefCell::new(InnerQueue::new())), - alarm_schedule: AtomicU64::new(u64::MAX), } } @@ -156,28 +159,12 @@ impl Queue { set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); } - inner.schedule_wake(at, waker, &self.alarm_schedule) + inner.schedule_wake(at, waker) }); - - self.update_alarm(); - } - - fn update_alarm(&self) { - // Need to set the alarm when we are *not* holding the mutex on the inner queue - // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately - // call us back if the timestamp is in the past. - let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - - if alarm_at < u64::MAX { - set_alarm(self.inner.lock(|inner| inner.borrow().alarm.unwrap()), alarm_at); - } } fn handle_alarm(&self) { - self.inner - .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); - - self.update_alarm(); + self.inner.lock(|inner| inner.borrow_mut().handle_alarm()); } fn handle_alarm_callback(ctx: *mut ()) { @@ -196,7 +183,6 @@ crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); #[cfg(test)] mod tests { use core::cell::Cell; - use core::sync::atomic::Ordering; use core::task::{RawWaker, RawWakerVTable, Waker}; use std::rc::Rc; use std::sync::Mutex; @@ -282,20 +268,14 @@ mod tests { inner.ctx = ctx; } - fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { - let notify = { - let mut inner = self.0.lock().unwrap(); + fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool { + let mut inner = self.0.lock().unwrap(); - if timestamp <= inner.now { - Some((inner.callback, inner.ctx)) - } else { - inner.alarm = timestamp; - None - } - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); + if timestamp <= inner.now { + false + } else { + inner.alarm = timestamp; + true } } } @@ -344,7 +324,6 @@ mod tests { fn setup() { DRIVER.reset(); - QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); QUEUE.inner.lock(|inner| { *inner.borrow_mut() = InnerQueue::new(); }); From f78c706b89feed71a7e0a3eaf332f55813698c7f Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 11:10:59 +0300 Subject: [PATCH 06/31] Address review feedback --- embassy-nrf/src/time_driver.rs | 18 +++++++++++------- embassy-rp/src/timer.rs | 20 ++++++++++++-------- embassy-stm32/src/time_driver.rs | 18 +++++++++++------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 0d03ad52..bc2c8a3c 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -245,19 +245,23 @@ impl Driver for RtcDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { - let t = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= t { - return false; - } - let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); 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. diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 8f280f55..80efd779 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -71,13 +71,6 @@ impl Driver for TimerDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { let n = alarm.id() as usize; critical_section::with(|cs| { - let now = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= now { - return false; - } - let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(timestamp); @@ -87,7 +80,18 @@ impl Driver for TimerDriver { // it is checked if the alarm time has passed. unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; - true + let now = self.now(); + if timestamp <= now { + // 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-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index e4c266e7..ab55cc08 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -294,19 +294,23 @@ impl Driver for RtcDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { - let t = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= t { - return false; - } - let r = T::regs_gp16(); let n = alarm.id() as _; 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)) }; + + alarm.timestamp.set(u64::MAX); + + return false; + } + let safe_timestamp = timestamp.max(t + 3); // Write the CCR value regardless of whether we're going to enable it now or not. From 4ce4131f8bad84ab38f595ac02dc5292611aaac0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 11:30:04 +0300 Subject: [PATCH 07/31] Implement i2cv1 timeout --- embassy-stm32/src/i2c/mod.rs | 3 + embassy-stm32/src/i2c/timeout.rs | 132 +++++++++++++++++++++++++++++++ embassy-stm32/src/i2c/v1.rs | 101 +++++++++++++++++------ examples/stm32f4/src/bin/i2c.rs | 30 +++++++ 4 files changed, 243 insertions(+), 23 deletions(-) create mode 100644 embassy-stm32/src/i2c/timeout.rs create mode 100644 examples/stm32f4/src/bin/i2c.rs diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 9d314f41..f4f64992 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,6 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; +mod timeout; +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..12319af8 --- /dev/null +++ b/embassy-stm32/src/i2c/timeout.rs @@ -0,0 +1,132 @@ +use embassy_time::{Duration, Instant}; + +use super::{Error, I2c, Instance}; + +pub struct TimeoutI2c<'d, T: Instance> { + i2c: &'d mut I2c<'d, T>, + 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> TimeoutI2c<'d, T> { + pub fn new(i2c: &'d mut I2c<'d, T>, timeout: Duration) -> Self { + Self { i2c, 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)) + } + + pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, buffer, self.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)) + } + + pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, bytes, self.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)) + } + + 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> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> { + type Error = Error; + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(addr, buffer) + } +} + +impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T> { + type Error = Error; + + fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(addr, bytes) + } +} + +impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T> { + 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> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T> { + type Error = Error; + } + + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T> { + 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..92a89882 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -141,7 +141,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 +154,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 +164,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 +174,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 +206,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 +243,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 +281,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 +295,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/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs new file mode 100644 index 00000000..99e3cecf --- /dev/null +++ b/examples/stm32f4/src/bin/i2c.rs @@ -0,0 +1,30 @@ +#![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::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 mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); + 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), + } +} From e3cf4255c6395ff5174432ab332189adf1ef431a Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 11:31:54 +0300 Subject: [PATCH 08/31] Help compiler with type inference --- embassy-stm32/src/time_driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index ab55cc08..8e84570a 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -296,7 +296,7 @@ impl Driver for RtcDriver { 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); From d99841fea90caccfd95c2dad8f233ab3198f7371 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 11:38:15 +0300 Subject: [PATCH 09/31] Implement time feature --- embassy-stm32/Cargo.toml | 5 ++++- embassy-stm32/src/i2c/mod.rs | 2 ++ embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/subghz/timeout.rs | 1 + embassy-stm32/src/subghz/tx_params.rs | 1 + 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9194ae78..c598604b 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -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/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f4f64992..f898fcc8 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,7 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; +#[cfg(feature = "time")] mod timeout; +#[cfg(feature = "time")] pub use timeout::*; use crate::peripherals; 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/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 { From 545cc9326b47efc27549a60b3539e93ea0d04d70 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 23 Oct 2022 16:31:10 -0500 Subject: [PATCH 10/31] stm32/adc: Remove voltage and temperature conversions --- embassy-stm32/src/adc/f1.rs | 21 ------------------ embassy-stm32/src/adc/v2.rs | 23 ------------------- embassy-stm32/src/adc/v3.rs | 39 --------------------------------- embassy-stm32/src/adc/v4.rs | 14 ------------ examples/stm32f1/src/bin/adc.rs | 12 ++++++++-- examples/stm32f4/src/bin/adc.rs | 27 ++++++++++++++++++++--- examples/stm32f7/src/bin/adc.rs | 12 +++++++++- 7 files changed, 45 insertions(+), 103 deletions(-) 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/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 2d6b4a0e..3521d06b 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -17,10 +17,18 @@ async fn main(_spawner: Spawner) { let mut pin = p.PB1; let mut vref = adc.enable_vref(&mut Delay); - adc.calibrate(&mut vref); + let vref_sample = adc.read(&mut vref); + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/CD00161566.pdf + // 5.3.4 Embedded reference voltage + const VREF_MV: u32 = 1200; + + (u32::from(sample) * VREF_MV / u32::from(vref_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/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 1d030f7d..5e036bb4 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -24,19 +24,40 @@ 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 vref_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 VREF_MILLIVOLTS: u32 = 1210; // mV + + (u32::from(sample) * VREF_MILLIVOLTS / u32::from(vref_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 + }; + 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: {} ({} mV)", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index 80fad8c4..d932f8b3 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 vref = adc.enable_vrefint(); + let vref_sample = adc.read_internal(&mut vref); + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00273119.pdf + // 6.3.27 Reference voltage + const VREF_MV: u32 = 1210; + + (u32::from(sample) * VREF_MV / u32::from(vref_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; } } From 516f4ce94684d9b3f9310c9972f878dbf883600c Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 12:15:53 +0300 Subject: [PATCH 11/31] Fix embassy-time wasm build and fix a bug in wasm time driver --- embassy-time/src/driver_wasm.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index d7a6b0d8..63d04989 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -81,26 +81,32 @@ impl Driver for TimeDriver { } } - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) -> bool { + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.closure.replace(Closure::new(move || { callback(ctx); })); - - true } - 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 + } } } From 1bed02296cf15013e0149b36c3ddedb3278e9b88 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:33:17 +0300 Subject: [PATCH 12/31] i2cv2 timeouts --- embassy-stm32/src/i2c/v2.rs | 204 ++++++++++++++++++++++---------- examples/stm32h7/src/bin/i2c.rs | 41 +++++++ 2 files changed, 182 insertions(+), 63 deletions(-) create mode 100644 examples/stm32h7/src/bin/i2c.rs diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 89b52da9..40413b69 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,19 @@ 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 +217,16 @@ 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 +238,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 +262,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 +280,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 +303,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 +326,12 @@ 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 +347,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 +371,7 @@ 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 +385,14 @@ 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 +400,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 +408,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 +422,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 +474,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 +489,34 @@ 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 +557,7 @@ 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 +565,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(()) } @@ -561,18 +593,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> + pub async fn write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { if bytes.is_empty() { - self.write_internal(address, bytes, true) + self.write_internal(address, bytes, true, &check_timeout) } else { - self.write_dma_internal(address, bytes, true, true).await + self.write_dma_internal(address, bytes, true, true, &check_timeout).await } } - pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> + pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_timeout(address, bytes, || Ok(())).await + } + + pub async fn write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -587,63 +626,97 @@ 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, &check_timeout).await?; first = false; current = next; } Ok(()) } - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_vectored_timeout(address, bytes, || Ok(())).await + } + + pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { if buffer.is_empty() { - self.read_internal(address, buffer, false) + self.read_internal(address, buffer, false, &check_timeout) } else { - self.read_dma_internal(address, buffer, false).await + self.read_dma_internal(address, buffer, false, &check_timeout).await } } + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.read_timeout(address, buffer, || Ok(())).await + } + + pub async fn write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + if bytes.is_empty() { + self.write_internal(address, bytes, false, &check_timeout)?; + } else { + self.write_dma_internal(address, bytes, true, true, &check_timeout).await?; + } + + if buffer.is_empty() { + self.read_internal(address, buffer, true, &check_timeout)?; + } else { + self.read_dma_internal(address, buffer, true, &check_timeout).await?; + } + + Ok(()) + } + pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> where TXDMA: super::TxDma, RXDMA: super::RxDma, { - if bytes.is_empty() { - self.write_internal(address, bytes, false)?; - } else { - self.write_dma_internal(address, bytes, true, true).await?; - } - - if buffer.is_empty() { - self.read_internal(address, buffer, true)?; - } else { - self.read_dma_internal(address, buffer, true).await?; - } - - Ok(()) + self.write_read_timeout(address, bytes, buffer, || Ok(())).await } // ========================= // 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 +730,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 +747,7 @@ 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 +755,7 @@ 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 +763,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 +774,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/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs new file mode 100644 index 00000000..7a314b99 --- /dev/null +++ b/examples/stm32h7/src/bin/i2c.rs @@ -0,0 +1,41 @@ +#![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(), + ); + 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), + } +} From 5f02bee388c1754a95e5cab95338aef9c5605c9a Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:34:55 +0300 Subject: [PATCH 13/31] Gate TimeoutI2c behind i2cv1 --- embassy-stm32/src/i2c/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f898fcc8..1dffbd10 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,9 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; -#[cfg(feature = "time")] +#[cfg(all(i2c_v1, feature = "time"))] mod timeout; -#[cfg(feature = "time")] +#[cfg(all(i2c_v1, feature = "time"))] pub use timeout::*; use crate::peripherals; From 9b209ffe1c9048f296213c7050919ccbcd7ded1b Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:39:47 +0300 Subject: [PATCH 14/31] Add docs --- embassy-stm32/src/i2c/timeout.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 12319af8..c4c035b4 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -2,6 +2,7 @@ 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. pub struct TimeoutI2c<'d, T: Instance> { i2c: &'d mut I2c<'d, T>, timeout: Duration, @@ -23,22 +24,27 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { 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, @@ -50,6 +56,7 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { .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) } From ca8afacfd07b24c27441f5a4ea1d66719fd50038 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:11:15 +0300 Subject: [PATCH 15/31] Implement TimeoutI2c for i2cv2 --- embassy-stm32/src/i2c/mod.rs | 4 ++-- embassy-stm32/src/i2c/timeout.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 1dffbd10..f898fcc8 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,9 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; -#[cfg(all(i2c_v1, feature = "time"))] +#[cfg(feature = "time")] mod timeout; -#[cfg(all(i2c_v1, feature = "time"))] +#[cfg(feature = "time")] pub use timeout::*; use crate::peripherals; diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index c4c035b4..fd9a753a 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -3,8 +3,8 @@ 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. -pub struct TimeoutI2c<'d, T: Instance> { - i2c: &'d mut I2c<'d, T>, +pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { + i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration, } @@ -19,8 +19,8 @@ fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { } } -impl<'d, T: Instance> TimeoutI2c<'d, T> { - pub fn new(i2c: &'d mut I2c<'d, T>, timeout: Duration) -> Self { +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 } } @@ -62,7 +62,7 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> { +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> { @@ -70,7 +70,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T> { +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> { @@ -78,7 +78,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T> { +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> { @@ -90,11 +90,11 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<' mod eh1 { use super::*; - impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T> { + impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T> { + 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) } From 6062978d58915e1d0c7db103365f0048f836babc Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:22:20 +0300 Subject: [PATCH 16/31] Remove weird async timeouts --- embassy-stm32/src/i2c/v2.rs | 73 +++++++++++-------------------------- 1 file changed, 22 insertions(+), 51 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 40413b69..876f6223 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -593,25 +593,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - pub async fn write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - if bytes.is_empty() { - self.write_internal(address, bytes, true, &check_timeout) - } else { - self.write_dma_internal(address, bytes, true, true, &check_timeout).await - } - } - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { - self.write_timeout(address, bytes, || Ok(())).await + if bytes.is_empty() { + self.write_internal(address, bytes, true, || Ok(())) + } else { + self.write_dma_internal(address, bytes, true, true, || Ok(())).await + } } - pub async fn write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> + pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -626,56 +619,22 @@ 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, &check_timeout).await?; + self.write_dma_internal(address, c, first, is_last, || Ok(())).await?; first = false; current = next; } Ok(()) } - pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - self.write_vectored_timeout(address, bytes, || Ok(())).await - } - - pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { - if buffer.is_empty() { - self.read_internal(address, buffer, false, &check_timeout) - } else { - self.read_dma_internal(address, buffer, false, &check_timeout).await - } - } - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { - self.read_timeout(address, buffer, || Ok(())).await - } - - pub async fn write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - TXDMA: super::TxDma, - RXDMA: super::RxDma, - { - if bytes.is_empty() { - self.write_internal(address, bytes, false, &check_timeout)?; - } else { - self.write_dma_internal(address, bytes, true, true, &check_timeout).await?; - } - if buffer.is_empty() { - self.read_internal(address, buffer, true, &check_timeout)?; + self.read_internal(address, buffer, false, || Ok(())) } else { - self.read_dma_internal(address, buffer, true, &check_timeout).await?; + self.read_dma_internal(address, buffer, false, || Ok(())).await } - - Ok(()) } pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> @@ -683,7 +642,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { TXDMA: super::TxDma, RXDMA: super::RxDma, { - self.write_read_timeout(address, bytes, buffer, || Ok(())).await + if bytes.is_empty() { + self.write_internal(address, bytes, false, || Ok(()))?; + } else { + self.write_dma_internal(address, bytes, true, true, || Ok(())).await?; + } + + if buffer.is_empty() { + self.read_internal(address, buffer, true, || Ok(()))?; + } else { + self.read_dma_internal(address, buffer, true, || Ok(())).await?; + } + + Ok(()) } // ========================= From 33f75419e542ef52d7d6a1403c9e3dbfd1c39abe Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:34:10 +0300 Subject: [PATCH 17/31] Unify i2cv1 definition with i2cv2 --- embassy-stm32/src/i2c/v1.rs | 22 +++++++++++++++++----- examples/stm32f4/src/bin/i2c.rs | 14 +++++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 92a89882..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 { diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 99e3cecf..12965d2b 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -4,7 +4,9 @@ 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 _}; @@ -17,7 +19,17 @@ async fn main(_spawner: Spawner) -> ! { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, Hertz(100_000), 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(), + ); let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); let mut data = [0u8; 1]; From ac61e0ee9fcec2792c73806afe4c998019b0db75 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:39:13 +0300 Subject: [PATCH 18/31] fmt --- embassy-stm32/src/i2c/v2.rs | 106 ++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 18 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 876f6223..aa4e6bb0 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -189,7 +189,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + 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 @@ -221,7 +227,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn master_continue(length: usize, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + 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() { @@ -331,7 +341,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> 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 @@ -347,7 +363,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Stop::Automatic, last_chunk_idx != 0, restart, - &check_timeout + &check_timeout, )?; } @@ -371,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, check_timeout: impl Fn() -> Result<(), Error>) -> 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 @@ -385,7 +407,13 @@ 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, &check_timeout)?; + Self::master_write( + address, + bytes.len().min(255), + Stop::Software, + last_chunk_idx != 0, + &check_timeout, + )?; } for (number, chunk) in bytes.chunks(255).enumerate() { @@ -422,7 +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> + check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -474,7 +502,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { total_len.min(255), Stop::Software, (total_chunks != 1) || !last_slice, - &check_timeout + &check_timeout, )?; } } else { @@ -516,7 +544,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> 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, { @@ -557,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, &check_timeout)?; + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_chunks != 1, + restart, + &check_timeout, + )?; } poll_fn(|cx| { @@ -573,7 +614,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { - return Poll::Ready(Err(e)) + return Poll::Ready(Err(e)); } T::regs().cr1().modify(|w| w.set_tcie(true)); } @@ -660,7 +701,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Blocking public API - pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + 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 } @@ -669,7 +715,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { 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> { + 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) } @@ -677,7 +728,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.blocking_write_timeout(address, bytes, || Ok(())) } - pub fn blocking_write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + 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 @@ -687,7 +744,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { 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> { + 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); } @@ -701,7 +763,7 @@ 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 + &check_timeout, )?; } @@ -718,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), &check_timeout)?; + Self::master_continue( + slice_len.min(255), + (idx != last_slice_index) || (slice_len > 255), + &check_timeout, + )?; } } @@ -726,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), &check_timeout)?; + Self::master_continue( + chunk.len(), + (number != last_chunk_idx) || (idx != last_slice_index), + &check_timeout, + )?; } } From 52c03cf0a4ae5a7a6374e6acac123670b83860fe Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:48:40 +0300 Subject: [PATCH 19/31] Add more docs --- embassy-stm32/src/i2c/timeout.rs | 3 +++ examples/stm32f4/src/bin/i2c.rs | 3 +++ examples/stm32h7/src/bin/i2c.rs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index fd9a753a..4fca1ca2 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -3,6 +3,9 @@ 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, diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 12965d2b..6e51c211 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -30,6 +30,9 @@ async fn main(_spawner: Spawner) -> ! { 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]; diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs index 7a314b99..d44319ae 100644 --- a/examples/stm32h7/src/bin/i2c.rs +++ b/examples/stm32h7/src/bin/i2c.rs @@ -29,6 +29,9 @@ async fn main(_spawner: Spawner) -> ! { 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]; From 7a6732adcfd09d72f5335f85cbe4e263234849e7 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Mon, 24 Oct 2022 15:01:16 -0500 Subject: [PATCH 20/31] Improve examples --- examples/stm32f1/src/bin/adc.rs | 8 ++++---- examples/stm32f4/src/bin/adc.rs | 12 ++++++++---- examples/stm32f7/src/bin/adc.rs | 8 ++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 3521d06b..ed59e279 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -16,14 +16,14 @@ 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); - let vref_sample = adc.read(&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 VREF_MV: u32 = 1200; + const VREFINT_MV: u32 = 1200; // mV - (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; loop { diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 5e036bb4..1c9a0b35 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -24,14 +24,14 @@ 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 vref_sample = adc.read_internal(&mut vrefint); + 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 VREF_MILLIVOLTS: u32 = 1210; // mV + const VREFINT_MV: u32 = 1210; // mV - (u32::from(sample) * VREF_MILLIVOLTS / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; let convert_to_celcius = |sample| { @@ -45,6 +45,10 @@ async fn main(_spawner: Spawner) { (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); @@ -57,7 +61,7 @@ async fn main(_spawner: Spawner) { // Read internal voltage reference let v = adc.read_internal(&mut vrefint); - info!("VrefInt: {} ({} mV)", v, convert_to_millivolts(v)); + info!("VrefInt: {}", v); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index d932f8b3..70b3b2a7 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -16,14 +16,14 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PA3; - let mut vref = adc.enable_vrefint(); - let vref_sample = adc.read_internal(&mut vref); + 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 VREF_MV: u32 = 1210; + const VREFINT_MV: u32 = 1210; // mV - (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; loop { From ea868920e6859aac924a6266968f790a76d6b160 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Wed, 26 Oct 2022 09:13:26 +0200 Subject: [PATCH 21/31] Update nrf pacs --- embassy-nrf/Cargo.toml | 20 ++++++++++---------- embassy-nrf/src/chips/nrf52805.rs | 4 ++-- embassy-nrf/src/chips/nrf52810.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52811.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52832.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52833.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52840.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf5340_app.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf9160.rs | 16 ++++++++-------- 9 files changed, 68 insertions(+), 68 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 5459bc90..dca5fd1b 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -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/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; From 0c9ec8dc362efb3a569b4b85c6fcd97a7f9d7860 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Wed, 26 Oct 2022 10:39:29 +0200 Subject: [PATCH 22/31] Update usb --- embassy-nrf/src/usb.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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()); From 66611a80caea216241d58cc3b848fe55b8865b49 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 26 Oct 2022 11:51:37 +0200 Subject: [PATCH 23/31] Introduce shared new_inner for uart instantiation --- embassy-stm32/src/usart/mod.rs | 63 +++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 07406121..3f5b9952 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -222,10 +222,48 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { rx_dma: impl Peripheral

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

+ 'd, + rx: impl Peripheral

> + 'd, + tx: 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(); + + 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(_inner, rx, tx, tx_dma, rx_dma, config) + } + + fn new_inner( + _inner: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: 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); + let pclk_freq = T::frequency(); // TODO: better calculation, including error checking and OVER8 if possible. @@ -264,29 +302,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } - pub fn new_with_rtscts( - _inner: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: 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); - - 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, rx, tx, tx_dma, rx_dma, config) - } - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> where TxDma: crate::usart::TxDma, From eeb072d9cbc457892c58670ca6fefacf8c80a32b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 16:47:29 +0200 Subject: [PATCH 24/31] Update Rust nightly. --- embassy-embedded-hal/Cargo.toml | 2 +- embassy-lora/Cargo.toml | 2 +- embassy-net/Cargo.toml | 2 +- embassy-net/src/tcp.rs | 18 +++++++++--------- embassy-nrf/Cargo.toml | 4 ++-- embassy-nrf/src/buffered_uarte.rs | 16 ++++++++-------- embassy-rp/Cargo.toml | 4 ++-- embassy-rp/src/uart/buffered.rs | 16 ++++++++-------- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/sdmmc/mod.rs | 12 ++++++------ embassy-stm32/src/usart/buffered.rs | 16 ++++++++-------- embassy-sync/Cargo.toml | 2 +- embassy-sync/src/pipe.rs | 18 +++++++++--------- embassy-time/Cargo.toml | 2 +- examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 4 ++-- examples/std/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 4 ++-- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- tests/rp/Cargo.toml | 4 ++-- tests/stm32/Cargo.toml | 2 +- 26 files changed, 74 insertions(+), 74 deletions(-) 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-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 dca5fd1b..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 } 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-rp/Cargo.toml b/embassy-rp/Cargo.toml index c5685841..d2196be7 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -53,12 +53,12 @@ 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.0", features = ["async"], optional = true } +embedded-io = { version = "0.3.1", 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/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..0b88e89c 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" 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/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 2a711bc0..c30bbc20 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -283,7 +283,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 +293,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 +303,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 +317,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 +331,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 +339,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 +349,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 +357,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-sync/Cargo.toml b/embassy-sync/Cargo.toml index 584d5ba9..cd6ff07d 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -32,7 +32,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..0fd4645a 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -107,7 +107,7 @@ 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"} 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 747dde51..ec9896b7 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"] } static_cell = "1.0.0" [profile.release] 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/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/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/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/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/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/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 d6770d6e..e1a6dce4 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"] } [profile.dev] debug = 2 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] From ac6995f9e656a724d92590e722ac0c25f417893b Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 26 Oct 2022 17:48:22 +0300 Subject: [PATCH 25/31] Fix a bug identified during code review --- embassy-time/src/queue_generic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 83f73484..6769d6a5 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use core::cmp::Ordering; +use core::cmp::{min, Ordering}; use core::task::Waker; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -74,7 +74,7 @@ impl InnerQueue { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { - timer.at = at; + timer.at = min(timer.at, at); timer.finish(); }) .unwrap_or_else(|| { From 560eecdb737642e6aba132408039195c7d6b6dd8 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 26 Oct 2022 18:05:27 +0300 Subject: [PATCH 26/31] Remove the _embassy_time_schedule_wake magic --- embassy-executor/src/raw/mod.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 5bcb1e6e..181dabe8 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -444,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() { From 9cac649fcf5a633a89aa1b6e550d641270d14956 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Wed, 26 Oct 2022 18:58:22 +0200 Subject: [PATCH 27/31] stm32: Add support for read_until_idle on UART --- embassy-stm32/src/usart/buffered.rs | 34 +- embassy-stm32/src/usart/mod.rs | 402 +++++++++++++++++++-- examples/stm32f3/src/bin/usart_dma.rs | 4 +- examples/stm32f4/src/bin/usart.rs | 4 +- examples/stm32f4/src/bin/usart_buffered.rs | 15 +- examples/stm32f4/src/bin/usart_dma.rs | 4 +- examples/stm32f7/src/bin/usart_dma.rs | 4 +- examples/stm32h7/src/bin/usart.rs | 4 +- examples/stm32h7/src/bin/usart_dma.rs | 4 +- examples/stm32h7/src/bin/usart_split.rs | 4 +- examples/stm32l0/src/bin/usart_dma.rs | 4 +- examples/stm32l0/src/bin/usart_irq.rs | 12 +- examples/stm32l4/src/bin/usart.rs | 4 +- examples/stm32l4/src/bin/usart_dma.rs | 4 +- tests/stm32/src/bin/usart.rs | 17 +- tests/stm32/src/bin/usart_dma.rs | 48 ++- 16 files changed, 500 insertions(+), 68 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index c30bbc20..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); }); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 3f5b9952..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,13 +317,202 @@ 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, @@ -225,13 +520,14 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::enable(); T::reset(); - Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } pub fn new_with_rtscts( - _inner: impl Peripheral

+ 'd, + 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, @@ -251,32 +547,29 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { w.set_ctse(true); }); } - Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } fn new_inner( - _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); - - 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; + 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.brr().write_value(regs::Brr(div)); r.cr1().write(|w| { w.set_ue(true); w.set_te(true); @@ -295,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, + }, } } @@ -332,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. @@ -340,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::*; @@ -389,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, } } } @@ -573,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 { @@ -587,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 {} @@ -609,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/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/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/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/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/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/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/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. From 4976cbbe6040d5e147e7c42bd29b72d6223b05b0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 20:02:58 +0200 Subject: [PATCH 28/31] time/generic-queue: ensure queue goes in .bss instead of .data --- embassy-time/src/queue_generic.rs | 38 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 6769d6a5..8a355b32 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -57,19 +57,11 @@ impl Ord for Timer { struct InnerQueue { queue: SortedLinkedList, - alarm: Option, + alarm: AlarmHandle, alarm_at: Instant, } impl InnerQueue { - const fn new() -> Self { - Self { - queue: SortedLinkedList::new_u8(), - alarm: None, - alarm_at: Instant::MAX, - } - } - fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) @@ -121,7 +113,7 @@ impl InnerQueue { if self.alarm_at != new_at { self.alarm_at = new_at; - return set_alarm(self.alarm.unwrap(), self.alarm_at.as_ticks()); + return set_alarm(self.alarm, self.alarm_at.as_ticks()); } } else { self.alarm_at = Instant::MAX; @@ -138,13 +130,13 @@ impl InnerQueue { } struct Queue { - inner: Mutex>, + inner: Mutex>>, } impl Queue { const fn new() -> Self { Self { - inner: Mutex::new(RefCell::new(InnerQueue::new())), + inner: Mutex::new(RefCell::new(None)), } } @@ -152,19 +144,25 @@ impl Queue { self.inner.lock(|inner| { let mut inner = inner.borrow_mut(); - if inner.alarm.is_none() { - let handle = unsafe { allocate_alarm() }.unwrap(); - inner.alarm = Some(handle); + if inner.is_none() {} - set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); - } - - inner.schedule_wake(at, waker) + 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: SortedLinkedList::new_u8(), + alarm: handle, + alarm_at: Instant::MAX, + } + }) + .schedule_wake(at, waker) }); } fn handle_alarm(&self) { - self.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + self.inner + .lock(|inner| inner.borrow_mut().as_mut().unwrap().handle_alarm()); } fn handle_alarm_callback(ctx: *mut ()) { From f9da6271cea7035b2c9f27cfe479aa81889168d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 21:00:50 +0200 Subject: [PATCH 29/31] time/generic_queue: use Vec instead of SortedLinkedList --- embassy-time/src/queue_generic.rs | 46 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 8a355b32..20ae7e6c 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -4,7 +4,7 @@ use core::task::Waker; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; -use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; +use heapless::Vec; use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; use crate::queue::TimerQueue; @@ -56,18 +56,17 @@ impl Ord for Timer { } struct InnerQueue { - queue: SortedLinkedList, + queue: Vec, alarm: AlarmHandle, - alarm_at: Instant, } impl InnerQueue { fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue - .find_mut(|timer| timer.waker.will_wake(waker)) + .iter_mut() + .find(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { timer.at = min(timer.at, at); - timer.finish(); }) .unwrap_or_else(|| { let mut timer = Timer { @@ -96,35 +95,35 @@ impl InnerQueue { loop { let now = Instant::now(); - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); + 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() { + if self.update_alarm(next_alarm) { break; } } } - fn update_alarm(&mut self) -> bool { - if let Some(timer) = self.queue.peek() { - let new_at = timer.at; - - if self.alarm_at != new_at { - self.alarm_at = new_at; - - return set_alarm(self.alarm, self.alarm_at.as_ticks()); - } + fn update_alarm(&mut self, next_alarm: Instant) -> bool { + if next_alarm == Instant::MAX { + true } else { - self.alarm_at = Instant::MAX; + set_alarm(self.alarm, next_alarm.as_ticks()) } - - true } fn handle_alarm(&mut self) { - self.alarm_at = Instant::MAX; - self.dispatch(); } } @@ -151,9 +150,8 @@ impl Queue { let handle = unsafe { allocate_alarm() }.unwrap(); set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); InnerQueue { - queue: SortedLinkedList::new_u8(), + queue: Vec::new(), alarm: handle, - alarm_at: Instant::MAX, } }) .schedule_wake(at, waker) From d2246ae693d15187f3af9c263bc02809ba096b80 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 22:11:08 +0200 Subject: [PATCH 30/31] Release embassy-sync, embassy-time v0.1.0 --- embassy-sync/Cargo.toml | 3 +++ embassy-time/Cargo.toml | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index cd6ff07d..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 = [] diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 9ed4d57e..e3d2cbcd 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -11,6 +11,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"] @@ -126,7 +129,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optiona 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" From 61560e740dea1b4c7ca036dafd66c834a1ff92e2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 22:18:10 +0200 Subject: [PATCH 31/31] time: add missing cargo manifest fields. --- embassy-time/Cargo.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index e3d2cbcd..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/"