diff options
| author | sander <[email protected]> | 2023-03-30 14:37:51 +0200 |
|---|---|---|
| committer | sander <[email protected]> | 2023-03-30 14:37:51 +0200 |
| commit | 6b2aaacf830d69fcb05f9611d3780f56b4ae82bc (patch) | |
| tree | a6e4d7628cd5153bbfd122b902a598b0862feeb9 | |
| parent | ba9afbc26d06ab38065cbff5b17a7f76db297ad4 (diff) | |
| parent | 754bb802ba377c19be97d092c4b2afe542de20b5 (diff) | |
Update embassy
Merge commit '9dd3719f09835f646e3a8f3abaa33726a1e3f9ca'
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 179 | ||||
| -rw-r--r-- | embassy-executor/src/raw/timer_queue.rs | 14 | ||||
| -rw-r--r-- | embassy-executor/src/raw/util.rs | 29 | ||||
| -rw-r--r-- | embassy-executor/src/spawner.rs | 12 | ||||
| -rw-r--r-- | embassy-rp/src/dma.rs | 1 | ||||
| -rw-r--r-- | embassy-rp/src/spi.rs | 45 | ||||
| -rw-r--r-- | embassy-rp/src/uart/buffered.rs | 74 | ||||
| -rw-r--r-- | embassy-stm32/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-stm32/build.rs | 7 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/sample_time.rs | 13 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 3 | ||||
| -rw-r--r-- | embassy-stm32/src/qspi/enums.rs | 294 | ||||
| -rw-r--r-- | embassy-stm32/src/qspi/mod.rs | 338 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/buffered.rs | 99 | ||||
| -rw-r--r-- | embassy-sync/src/pipe.rs | 71 | ||||
| -rw-r--r-- | embassy-usb/src/builder.rs | 8 | ||||
| -rw-r--r-- | embassy-usb/src/class/hid.rs | 3 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 37 | ||||
| -rw-r--r-- | embassy-usb/src/msos.rs | 5 | ||||
| -rw-r--r-- | tests/rp/src/bin/spi_async.rs | 64 |
20 files changed, 1153 insertions, 147 deletions
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 42bd82262..15ff18fc8 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -13,11 +13,12 @@ mod timer_queue; | |||
| 13 | pub(crate) mod util; | 13 | pub(crate) mod util; |
| 14 | mod waker; | 14 | mod waker; |
| 15 | 15 | ||
| 16 | use core::cell::Cell; | ||
| 17 | use core::future::Future; | 16 | use core::future::Future; |
| 17 | use core::marker::PhantomData; | ||
| 18 | use core::mem; | 18 | use core::mem; |
| 19 | use core::pin::Pin; | 19 | use core::pin::Pin; |
| 20 | use core::ptr::NonNull; | 20 | use core::ptr::NonNull; |
| 21 | use core::sync::atomic::AtomicPtr; | ||
| 21 | use core::task::{Context, Poll}; | 22 | use core::task::{Context, Poll}; |
| 22 | 23 | ||
| 23 | use atomic_polyfill::{AtomicU32, Ordering}; | 24 | use atomic_polyfill::{AtomicU32, Ordering}; |
| @@ -30,7 +31,7 @@ use embassy_time::Instant; | |||
| 30 | use rtos_trace::trace; | 31 | use rtos_trace::trace; |
| 31 | 32 | ||
| 32 | use self::run_queue::{RunQueue, RunQueueItem}; | 33 | use self::run_queue::{RunQueue, RunQueueItem}; |
| 33 | use self::util::UninitCell; | 34 | use self::util::{SyncUnsafeCell, UninitCell}; |
| 34 | pub use self::waker::task_from_waker; | 35 | pub use self::waker::task_from_waker; |
| 35 | use super::SpawnToken; | 36 | use super::SpawnToken; |
| 36 | 37 | ||
| @@ -46,11 +47,11 @@ pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; | |||
| 46 | pub(crate) struct TaskHeader { | 47 | pub(crate) struct TaskHeader { |
| 47 | pub(crate) state: AtomicU32, | 48 | pub(crate) state: AtomicU32, |
| 48 | pub(crate) run_queue_item: RunQueueItem, | 49 | pub(crate) run_queue_item: RunQueueItem, |
| 49 | pub(crate) executor: Cell<Option<&'static Executor>>, | 50 | pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>, |
| 50 | poll_fn: Cell<Option<unsafe fn(TaskRef)>>, | 51 | poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, |
| 51 | 52 | ||
| 52 | #[cfg(feature = "integrated-timers")] | 53 | #[cfg(feature = "integrated-timers")] |
| 53 | pub(crate) expires_at: Cell<Instant>, | 54 | pub(crate) expires_at: SyncUnsafeCell<Instant>, |
| 54 | #[cfg(feature = "integrated-timers")] | 55 | #[cfg(feature = "integrated-timers")] |
| 55 | pub(crate) timer_queue_item: timer_queue::TimerQueueItem, | 56 | pub(crate) timer_queue_item: timer_queue::TimerQueueItem, |
| 56 | } | 57 | } |
| @@ -61,6 +62,9 @@ pub struct TaskRef { | |||
| 61 | ptr: NonNull<TaskHeader>, | 62 | ptr: NonNull<TaskHeader>, |
| 62 | } | 63 | } |
| 63 | 64 | ||
| 65 | unsafe impl Send for TaskRef where &'static TaskHeader: Send {} | ||
| 66 | unsafe impl Sync for TaskRef where &'static TaskHeader: Sync {} | ||
| 67 | |||
| 64 | impl TaskRef { | 68 | impl TaskRef { |
| 65 | fn new<F: Future + 'static>(task: &'static TaskStorage<F>) -> Self { | 69 | fn new<F: Future + 'static>(task: &'static TaskStorage<F>) -> Self { |
| 66 | Self { | 70 | Self { |
| @@ -115,12 +119,12 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 115 | raw: TaskHeader { | 119 | raw: TaskHeader { |
| 116 | state: AtomicU32::new(0), | 120 | state: AtomicU32::new(0), |
| 117 | run_queue_item: RunQueueItem::new(), | 121 | run_queue_item: RunQueueItem::new(), |
| 118 | executor: Cell::new(None), | 122 | executor: SyncUnsafeCell::new(None), |
| 119 | // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` | 123 | // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` |
| 120 | poll_fn: Cell::new(None), | 124 | poll_fn: SyncUnsafeCell::new(None), |
| 121 | 125 | ||
| 122 | #[cfg(feature = "integrated-timers")] | 126 | #[cfg(feature = "integrated-timers")] |
| 123 | expires_at: Cell::new(Instant::from_ticks(0)), | 127 | expires_at: SyncUnsafeCell::new(Instant::from_ticks(0)), |
| 124 | #[cfg(feature = "integrated-timers")] | 128 | #[cfg(feature = "integrated-timers")] |
| 125 | timer_queue_item: timer_queue::TimerQueueItem::new(), | 129 | timer_queue_item: timer_queue::TimerQueueItem::new(), |
| 126 | }, | 130 | }, |
| @@ -170,9 +174,15 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 170 | // it's a noop for our waker. | 174 | // it's a noop for our waker. |
| 171 | mem::forget(waker); | 175 | mem::forget(waker); |
| 172 | } | 176 | } |
| 173 | } | ||
| 174 | 177 | ||
| 175 | unsafe impl<F: Future + 'static> Sync for TaskStorage<F> {} | 178 | #[doc(hidden)] |
| 179 | #[allow(dead_code)] | ||
| 180 | fn _assert_sync(self) { | ||
| 181 | fn assert_sync<T: Sync>(_: T) {} | ||
| 182 | |||
| 183 | assert_sync(self) | ||
| 184 | } | ||
| 185 | } | ||
| 176 | 186 | ||
| 177 | struct AvailableTask<F: Future + 'static> { | 187 | struct AvailableTask<F: Future + 'static> { |
| 178 | task: &'static TaskStorage<F>, | 188 | task: &'static TaskStorage<F>, |
| @@ -279,29 +289,10 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> { | |||
| 279 | } | 289 | } |
| 280 | } | 290 | } |
| 281 | 291 | ||
| 282 | /// Raw executor. | 292 | pub(crate) struct SyncExecutor { |
| 283 | /// | ||
| 284 | /// This is the core of the Embassy executor. It is low-level, requiring manual | ||
| 285 | /// handling of wakeups and task polling. If you can, prefer using one of the | ||
| 286 | /// [higher level executors](crate::Executor). | ||
| 287 | /// | ||
| 288 | /// The raw executor leaves it up to you to handle wakeups and scheduling: | ||
| 289 | /// | ||
| 290 | /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks | ||
| 291 | /// that "want to run"). | ||
| 292 | /// - You must supply a `signal_fn`. The executor will call it to notify you it has work | ||
| 293 | /// to do. You must arrange for `poll()` to be called as soon as possible. | ||
| 294 | /// | ||
| 295 | /// `signal_fn` can be called from *any* context: any thread, any interrupt priority | ||
| 296 | /// level, etc. It may be called synchronously from any `Executor` method call as well. | ||
| 297 | /// You must deal with this correctly. | ||
| 298 | /// | ||
| 299 | /// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates | ||
| 300 | /// the requirement for `poll` to not be called reentrantly. | ||
| 301 | pub struct Executor { | ||
| 302 | run_queue: RunQueue, | 293 | run_queue: RunQueue, |
| 303 | signal_fn: fn(*mut ()), | 294 | signal_fn: fn(*mut ()), |
| 304 | signal_ctx: *mut (), | 295 | signal_ctx: AtomicPtr<()>, |
| 305 | 296 | ||
| 306 | #[cfg(feature = "integrated-timers")] | 297 | #[cfg(feature = "integrated-timers")] |
| 307 | pub(crate) timer_queue: timer_queue::TimerQueue, | 298 | pub(crate) timer_queue: timer_queue::TimerQueue, |
| @@ -309,14 +300,8 @@ pub struct Executor { | |||
| 309 | alarm: AlarmHandle, | 300 | alarm: AlarmHandle, |
| 310 | } | 301 | } |
| 311 | 302 | ||
| 312 | impl Executor { | 303 | impl SyncExecutor { |
| 313 | /// Create a new executor. | 304 | pub(crate) fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { |
| 314 | /// | ||
| 315 | /// When the executor has work to do, it will call `signal_fn` with | ||
| 316 | /// `signal_ctx` as argument. | ||
| 317 | /// | ||
| 318 | /// See [`Executor`] docs for details on `signal_fn`. | ||
| 319 | pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { | ||
| 320 | #[cfg(feature = "integrated-timers")] | 305 | #[cfg(feature = "integrated-timers")] |
| 321 | let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; | 306 | let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; |
| 322 | #[cfg(feature = "integrated-timers")] | 307 | #[cfg(feature = "integrated-timers")] |
| @@ -325,7 +310,7 @@ impl Executor { | |||
| 325 | Self { | 310 | Self { |
| 326 | run_queue: RunQueue::new(), | 311 | run_queue: RunQueue::new(), |
| 327 | signal_fn, | 312 | signal_fn, |
| 328 | signal_ctx, | 313 | signal_ctx: AtomicPtr::new(signal_ctx), |
| 329 | 314 | ||
| 330 | #[cfg(feature = "integrated-timers")] | 315 | #[cfg(feature = "integrated-timers")] |
| 331 | timer_queue: timer_queue::TimerQueue::new(), | 316 | timer_queue: timer_queue::TimerQueue::new(), |
| @@ -346,19 +331,10 @@ impl Executor { | |||
| 346 | trace::task_ready_begin(task.as_ptr() as u32); | 331 | trace::task_ready_begin(task.as_ptr() as u32); |
| 347 | 332 | ||
| 348 | if self.run_queue.enqueue(cs, task) { | 333 | if self.run_queue.enqueue(cs, task) { |
| 349 | (self.signal_fn)(self.signal_ctx) | 334 | (self.signal_fn)(self.signal_ctx.load(Ordering::Relaxed)) |
| 350 | } | 335 | } |
| 351 | } | 336 | } |
| 352 | 337 | ||
| 353 | /// Spawn a task in this executor. | ||
| 354 | /// | ||
| 355 | /// # Safety | ||
| 356 | /// | ||
| 357 | /// `task` must be a valid pointer to an initialized but not-already-spawned task. | ||
| 358 | /// | ||
| 359 | /// It is OK to use `unsafe` to call this from a thread that's not the executor thread. | ||
| 360 | /// In this case, the task's Future must be Send. This is because this is effectively | ||
| 361 | /// sending the task to the executor thread. | ||
| 362 | pub(super) unsafe fn spawn(&'static self, task: TaskRef) { | 338 | pub(super) unsafe fn spawn(&'static self, task: TaskRef) { |
| 363 | task.header().executor.set(Some(self)); | 339 | task.header().executor.set(Some(self)); |
| 364 | 340 | ||
| @@ -370,24 +346,11 @@ impl Executor { | |||
| 370 | }) | 346 | }) |
| 371 | } | 347 | } |
| 372 | 348 | ||
| 373 | /// Poll all queued tasks in this executor. | ||
| 374 | /// | ||
| 375 | /// This loops over all tasks that are queued to be polled (i.e. they're | ||
| 376 | /// freshly spawned or they've been woken). Other tasks are not polled. | ||
| 377 | /// | ||
| 378 | /// You must call `poll` after receiving a call to `signal_fn`. It is OK | ||
| 379 | /// to call `poll` even when not requested by `signal_fn`, but it wastes | ||
| 380 | /// energy. | ||
| 381 | /// | ||
| 382 | /// # Safety | 349 | /// # Safety |
| 383 | /// | 350 | /// |
| 384 | /// You must NOT call `poll` reentrantly on the same executor. | 351 | /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. |
| 385 | /// | 352 | pub(crate) unsafe fn poll(&'static self) { |
| 386 | /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you | 353 | #[allow(clippy::never_loop)] |
| 387 | /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to | ||
| 388 | /// somehow schedule for `poll()` to be called later, at a time you know for sure there's | ||
| 389 | /// no `poll()` already running. | ||
| 390 | pub unsafe fn poll(&'static self) { | ||
| 391 | loop { | 354 | loop { |
| 392 | #[cfg(feature = "integrated-timers")] | 355 | #[cfg(feature = "integrated-timers")] |
| 393 | self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); | 356 | self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); |
| @@ -441,6 +404,84 @@ impl Executor { | |||
| 441 | #[cfg(feature = "rtos-trace")] | 404 | #[cfg(feature = "rtos-trace")] |
| 442 | trace::system_idle(); | 405 | trace::system_idle(); |
| 443 | } | 406 | } |
| 407 | } | ||
| 408 | |||
| 409 | /// Raw executor. | ||
| 410 | /// | ||
| 411 | /// This is the core of the Embassy executor. It is low-level, requiring manual | ||
| 412 | /// handling of wakeups and task polling. If you can, prefer using one of the | ||
| 413 | /// [higher level executors](crate::Executor). | ||
| 414 | /// | ||
| 415 | /// The raw executor leaves it up to you to handle wakeups and scheduling: | ||
| 416 | /// | ||
| 417 | /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks | ||
| 418 | /// that "want to run"). | ||
| 419 | /// - You must supply a `signal_fn`. The executor will call it to notify you it has work | ||
| 420 | /// to do. You must arrange for `poll()` to be called as soon as possible. | ||
| 421 | /// | ||
| 422 | /// `signal_fn` can be called from *any* context: any thread, any interrupt priority | ||
| 423 | /// level, etc. It may be called synchronously from any `Executor` method call as well. | ||
| 424 | /// You must deal with this correctly. | ||
| 425 | /// | ||
| 426 | /// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates | ||
| 427 | /// the requirement for `poll` to not be called reentrantly. | ||
| 428 | #[repr(transparent)] | ||
| 429 | pub struct Executor { | ||
| 430 | pub(crate) inner: SyncExecutor, | ||
| 431 | |||
| 432 | _not_sync: PhantomData<*mut ()>, | ||
| 433 | } | ||
| 434 | |||
| 435 | impl Executor { | ||
| 436 | pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self { | ||
| 437 | mem::transmute(inner) | ||
| 438 | } | ||
| 439 | /// Create a new executor. | ||
| 440 | /// | ||
| 441 | /// When the executor has work to do, it will call `signal_fn` with | ||
| 442 | /// `signal_ctx` as argument. | ||
| 443 | /// | ||
| 444 | /// See [`Executor`] docs for details on `signal_fn`. | ||
| 445 | pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { | ||
| 446 | Self { | ||
| 447 | inner: SyncExecutor::new(signal_fn, signal_ctx), | ||
| 448 | _not_sync: PhantomData, | ||
| 449 | } | ||
| 450 | } | ||
| 451 | |||
| 452 | /// Spawn a task in this executor. | ||
| 453 | /// | ||
| 454 | /// # Safety | ||
| 455 | /// | ||
| 456 | /// `task` must be a valid pointer to an initialized but not-already-spawned task. | ||
| 457 | /// | ||
| 458 | /// It is OK to use `unsafe` to call this from a thread that's not the executor thread. | ||
| 459 | /// In this case, the task's Future must be Send. This is because this is effectively | ||
| 460 | /// sending the task to the executor thread. | ||
| 461 | pub(super) unsafe fn spawn(&'static self, task: TaskRef) { | ||
| 462 | self.inner.spawn(task) | ||
| 463 | } | ||
| 464 | |||
| 465 | /// Poll all queued tasks in this executor. | ||
| 466 | /// | ||
| 467 | /// This loops over all tasks that are queued to be polled (i.e. they're | ||
| 468 | /// freshly spawned or they've been woken). Other tasks are not polled. | ||
| 469 | /// | ||
| 470 | /// You must call `poll` after receiving a call to `signal_fn`. It is OK | ||
| 471 | /// to call `poll` even when not requested by `signal_fn`, but it wastes | ||
| 472 | /// energy. | ||
| 473 | /// | ||
| 474 | /// # Safety | ||
| 475 | /// | ||
| 476 | /// You must NOT call `poll` reentrantly on the same executor. | ||
| 477 | /// | ||
| 478 | /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you | ||
| 479 | /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to | ||
| 480 | /// somehow schedule for `poll()` to be called later, at a time you know for sure there's | ||
| 481 | /// no `poll()` already running. | ||
| 482 | pub unsafe fn poll(&'static self) { | ||
| 483 | self.inner.poll() | ||
| 484 | } | ||
| 444 | 485 | ||
| 445 | /// Get a spawner that spawns tasks in this executor. | 486 | /// Get a spawner that spawns tasks in this executor. |
| 446 | /// | 487 | /// |
| @@ -483,8 +524,10 @@ impl embassy_time::queue::TimerQueue for TimerQueue { | |||
| 483 | fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { | 524 | fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { |
| 484 | let task = waker::task_from_waker(waker); | 525 | let task = waker::task_from_waker(waker); |
| 485 | let task = task.header(); | 526 | let task = task.header(); |
| 486 | let expires_at = task.expires_at.get(); | 527 | unsafe { |
| 487 | task.expires_at.set(expires_at.min(at)); | 528 | let expires_at = task.expires_at.get(); |
| 529 | task.expires_at.set(expires_at.min(at)); | ||
| 530 | } | ||
| 488 | } | 531 | } |
| 489 | } | 532 | } |
| 490 | 533 | ||
diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 57d6d3cda..dc71c95b1 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs | |||
| @@ -1,28 +1,32 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use core::cmp::min; | 1 | use core::cmp::min; |
| 3 | 2 | ||
| 4 | use atomic_polyfill::Ordering; | 3 | use atomic_polyfill::Ordering; |
| 5 | use embassy_time::Instant; | 4 | use embassy_time::Instant; |
| 6 | 5 | ||
| 7 | use super::{TaskRef, STATE_TIMER_QUEUED}; | 6 | use super::{TaskRef, STATE_TIMER_QUEUED}; |
| 7 | use crate::raw::util::SyncUnsafeCell; | ||
| 8 | 8 | ||
| 9 | pub(crate) struct TimerQueueItem { | 9 | pub(crate) struct TimerQueueItem { |
| 10 | next: Cell<Option<TaskRef>>, | 10 | next: SyncUnsafeCell<Option<TaskRef>>, |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | impl TimerQueueItem { | 13 | impl TimerQueueItem { |
| 14 | pub const fn new() -> Self { | 14 | pub const fn new() -> Self { |
| 15 | Self { next: Cell::new(None) } | 15 | Self { |
| 16 | next: SyncUnsafeCell::new(None), | ||
| 17 | } | ||
| 16 | } | 18 | } |
| 17 | } | 19 | } |
| 18 | 20 | ||
| 19 | pub(crate) struct TimerQueue { | 21 | pub(crate) struct TimerQueue { |
| 20 | head: Cell<Option<TaskRef>>, | 22 | head: SyncUnsafeCell<Option<TaskRef>>, |
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | impl TimerQueue { | 25 | impl TimerQueue { |
| 24 | pub const fn new() -> Self { | 26 | pub const fn new() -> Self { |
| 25 | Self { head: Cell::new(None) } | 27 | Self { |
| 28 | head: SyncUnsafeCell::new(None), | ||
| 29 | } | ||
| 26 | } | 30 | } |
| 27 | 31 | ||
| 28 | pub(crate) unsafe fn update(&self, p: TaskRef) { | 32 | pub(crate) unsafe fn update(&self, p: TaskRef) { |
diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index 2b1f6b6f3..e2e8f4df8 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs | |||
| @@ -25,3 +25,32 @@ impl<T> UninitCell<T> { | |||
| 25 | ptr::drop_in_place(self.as_mut_ptr()) | 25 | ptr::drop_in_place(self.as_mut_ptr()) |
| 26 | } | 26 | } |
| 27 | } | 27 | } |
| 28 | |||
| 29 | unsafe impl<T> Sync for UninitCell<T> {} | ||
| 30 | |||
| 31 | #[repr(transparent)] | ||
| 32 | pub struct SyncUnsafeCell<T> { | ||
| 33 | value: UnsafeCell<T>, | ||
| 34 | } | ||
| 35 | |||
| 36 | unsafe impl<T: Sync> Sync for SyncUnsafeCell<T> {} | ||
| 37 | |||
| 38 | impl<T> SyncUnsafeCell<T> { | ||
| 39 | #[inline] | ||
| 40 | pub const fn new(value: T) -> Self { | ||
| 41 | Self { | ||
| 42 | value: UnsafeCell::new(value), | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | pub unsafe fn set(&self, value: T) { | ||
| 47 | *self.value.get() = value; | ||
| 48 | } | ||
| 49 | |||
| 50 | pub unsafe fn get(&self) -> T | ||
| 51 | where | ||
| 52 | T: Copy, | ||
| 53 | { | ||
| 54 | *self.value.get() | ||
| 55 | } | ||
| 56 | } | ||
diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 7c0a0183c..2b6224045 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs | |||
| @@ -92,6 +92,7 @@ impl Spawner { | |||
| 92 | poll_fn(|cx| { | 92 | poll_fn(|cx| { |
| 93 | let task = raw::task_from_waker(cx.waker()); | 93 | let task = raw::task_from_waker(cx.waker()); |
| 94 | let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; | 94 | let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; |
| 95 | let executor = unsafe { raw::Executor::wrap(executor) }; | ||
| 95 | Poll::Ready(Self::new(executor)) | 96 | Poll::Ready(Self::new(executor)) |
| 96 | }) | 97 | }) |
| 97 | .await | 98 | .await |
| @@ -130,9 +131,7 @@ impl Spawner { | |||
| 130 | /// spawner to other threads, but the spawner loses the ability to spawn | 131 | /// spawner to other threads, but the spawner loses the ability to spawn |
| 131 | /// non-Send tasks. | 132 | /// non-Send tasks. |
| 132 | pub fn make_send(&self) -> SendSpawner { | 133 | pub fn make_send(&self) -> SendSpawner { |
| 133 | SendSpawner { | 134 | SendSpawner::new(&self.executor.inner) |
| 134 | executor: self.executor, | ||
| 135 | } | ||
| 136 | } | 135 | } |
| 137 | } | 136 | } |
| 138 | 137 | ||
| @@ -145,14 +144,11 @@ impl Spawner { | |||
| 145 | /// If you want to spawn non-Send tasks, use [Spawner]. | 144 | /// If you want to spawn non-Send tasks, use [Spawner]. |
| 146 | #[derive(Copy, Clone)] | 145 | #[derive(Copy, Clone)] |
| 147 | pub struct SendSpawner { | 146 | pub struct SendSpawner { |
| 148 | executor: &'static raw::Executor, | 147 | executor: &'static raw::SyncExecutor, |
| 149 | } | 148 | } |
| 150 | 149 | ||
| 151 | unsafe impl Send for SendSpawner {} | ||
| 152 | unsafe impl Sync for SendSpawner {} | ||
| 153 | |||
| 154 | impl SendSpawner { | 150 | impl SendSpawner { |
| 155 | pub(crate) fn new(executor: &'static raw::Executor) -> Self { | 151 | pub(crate) fn new(executor: &'static raw::SyncExecutor) -> Self { |
| 156 | Self { executor } | 152 | Self { executor } |
| 157 | } | 153 | } |
| 158 | 154 | ||
diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 05adcecdd..ba07a88df 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | //! Direct Memory Access (DMA) | ||
| 1 | use core::future::Future; | 2 | use core::future::Future; |
| 2 | use core::pin::Pin; | 3 | use core::pin::Pin; |
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::sync::atomic::{compiler_fence, Ordering}; |
diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 584370d56..ebd621ecf 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | //! Serial Peripheral Interface | ||
| 1 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 2 | 3 | ||
| 3 | use embassy_embedded_hal::SetConfig; | 4 | use embassy_embedded_hal::SetConfig; |
| @@ -383,21 +384,33 @@ impl<'d, T: Instance> Spi<'d, T, Async> { | |||
| 383 | } | 384 | } |
| 384 | 385 | ||
| 385 | async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> { | 386 | async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> { |
| 386 | let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr); | 387 | let (_, tx_len) = crate::dma::slice_ptr_parts(tx_ptr); |
| 387 | let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); | 388 | let (_, rx_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); |
| 388 | assert_eq!(from_len, to_len); | 389 | |
| 389 | unsafe { | 390 | unsafe { |
| 390 | self.inner.regs().dmacr().write(|reg| { | 391 | self.inner.regs().dmacr().write(|reg| { |
| 391 | reg.set_rxdmae(true); | 392 | reg.set_rxdmae(true); |
| 392 | reg.set_txdmae(true); | 393 | reg.set_txdmae(true); |
| 393 | }) | 394 | }) |
| 394 | }; | 395 | }; |
| 395 | let tx_ch = self.tx_dma.as_mut().unwrap(); | 396 | |
| 396 | let tx_transfer = unsafe { | 397 | let mut tx_ch = self.tx_dma.as_mut().unwrap(); |
| 397 | // If we don't assign future to a variable, the data register pointer | 398 | // If we don't assign future to a variable, the data register pointer |
| 398 | // is held across an await and makes the future non-Send. | 399 | // is held across an await and makes the future non-Send. |
| 399 | crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) | 400 | let tx_transfer = async { |
| 401 | let p = self.inner.regs(); | ||
| 402 | unsafe { | ||
| 403 | crate::dma::write(&mut tx_ch, tx_ptr, p.dr().ptr() as *mut _, T::TX_DREQ).await; | ||
| 404 | |||
| 405 | if rx_len > tx_len { | ||
| 406 | let write_bytes_len = rx_len - tx_len; | ||
| 407 | // write dummy data | ||
| 408 | // this will disable incrementation of the buffers | ||
| 409 | crate::dma::write_repeated(tx_ch, p.dr().ptr() as *mut u8, write_bytes_len, T::TX_DREQ).await | ||
| 410 | } | ||
| 411 | } | ||
| 400 | }; | 412 | }; |
| 413 | |||
| 401 | let rx_ch = self.rx_dma.as_mut().unwrap(); | 414 | let rx_ch = self.rx_dma.as_mut().unwrap(); |
| 402 | let rx_transfer = unsafe { | 415 | let rx_transfer = unsafe { |
| 403 | // If we don't assign future to a variable, the data register pointer | 416 | // If we don't assign future to a variable, the data register pointer |
| @@ -405,6 +418,22 @@ impl<'d, T: Instance> Spi<'d, T, Async> { | |||
| 405 | crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) | 418 | crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) |
| 406 | }; | 419 | }; |
| 407 | join(tx_transfer, rx_transfer).await; | 420 | join(tx_transfer, rx_transfer).await; |
| 421 | |||
| 422 | // if tx > rx we should clear any overflow of the FIFO SPI buffer | ||
| 423 | if tx_len > rx_len { | ||
| 424 | let p = self.inner.regs(); | ||
| 425 | unsafe { | ||
| 426 | while p.sr().read().bsy() {} | ||
| 427 | |||
| 428 | // clear RX FIFO contents to prevent stale reads | ||
| 429 | while p.sr().read().rne() { | ||
| 430 | let _: u16 = p.dr().read().data(); | ||
| 431 | } | ||
| 432 | // clear RX overrun interrupt | ||
| 433 | p.icr().write(|w| w.set_roric(true)); | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 408 | Ok(()) | 437 | Ok(()) |
| 409 | } | 438 | } |
| 410 | } | 439 | } |
diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 32e5ddf14..1a573b311 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs | |||
| @@ -124,7 +124,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { | |||
| 124 | } | 124 | } |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 127 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<usize, Error> { |
| 128 | self.tx.blocking_write(buffer) | 128 | self.tx.blocking_write(buffer) |
| 129 | } | 129 | } |
| 130 | 130 | ||
| @@ -132,7 +132,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { | |||
| 132 | self.tx.blocking_flush() | 132 | self.tx.blocking_flush() |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 135 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { |
| 136 | self.rx.blocking_read(buffer) | 136 | self.rx.blocking_read(buffer) |
| 137 | } | 137 | } |
| 138 | 138 | ||
| @@ -201,7 +201,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { | |||
| 201 | }) | 201 | }) |
| 202 | } | 202 | } |
| 203 | 203 | ||
| 204 | pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<(), Error> { | 204 | pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { |
| 205 | loop { | 205 | loop { |
| 206 | let state = T::state(); | 206 | let state = T::state(); |
| 207 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | 207 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| @@ -222,7 +222,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { | |||
| 222 | }); | 222 | }); |
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | return Ok(()); | 225 | return Ok(n); |
| 226 | } | 226 | } |
| 227 | } | 227 | } |
| 228 | } | 228 | } |
| @@ -326,7 +326,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { | |||
| 326 | }) | 326 | }) |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | pub fn blocking_write(&mut self, buf: &[u8]) -> Result<(), Error> { | 329 | pub fn blocking_write(&mut self, buf: &[u8]) -> Result<usize, Error> { |
| 330 | loop { | 330 | loop { |
| 331 | let state = T::state(); | 331 | let state = T::state(); |
| 332 | let mut tx_writer = unsafe { state.tx_buf.writer() }; | 332 | let mut tx_writer = unsafe { state.tx_buf.writer() }; |
| @@ -342,7 +342,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { | |||
| 342 | // FIFO was empty we have to manually pend the interrupt to shovel | 342 | // FIFO was empty we have to manually pend the interrupt to shovel |
| 343 | // TX data from the buffer into the FIFO. | 343 | // TX data from the buffer into the FIFO. |
| 344 | unsafe { T::Interrupt::steal() }.pend(); | 344 | unsafe { T::Interrupt::steal() }.pend(); |
| 345 | return Ok(()); | 345 | return Ok(n); |
| 346 | } | 346 | } |
| 347 | } | 347 | } |
| 348 | } | 348 | } |
| @@ -533,6 +533,38 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> | |||
| 533 | } | 533 | } |
| 534 | } | 534 | } |
| 535 | 535 | ||
| 536 | impl<'d, T: Instance + 'd> embedded_io::blocking::Read for BufferedUart<'d, T> { | ||
| 537 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 538 | self.rx.blocking_read(buf) | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | impl<'d, T: Instance + 'd> embedded_io::blocking::Read for BufferedUartRx<'d, T> { | ||
| 543 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 544 | self.blocking_read(buf) | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 | impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUart<'d, T> { | ||
| 549 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 550 | self.tx.blocking_write(buf) | ||
| 551 | } | ||
| 552 | |||
| 553 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 554 | self.tx.blocking_flush() | ||
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 | impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUartTx<'d, T> { | ||
| 559 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 560 | self.blocking_write(buf) | ||
| 561 | } | ||
| 562 | |||
| 563 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 564 | self.blocking_flush() | ||
| 565 | } | ||
| 566 | } | ||
| 567 | |||
| 536 | mod eh02 { | 568 | mod eh02 { |
| 537 | use super::*; | 569 | use super::*; |
| 538 | 570 | ||
| @@ -566,8 +598,15 @@ mod eh02 { | |||
| 566 | impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx<'d, T> { | 598 | impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx<'d, T> { |
| 567 | type Error = Error; | 599 | type Error = Error; |
| 568 | 600 | ||
| 569 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | 601 | fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { |
| 570 | self.blocking_write(buffer) | 602 | while !buffer.is_empty() { |
| 603 | match self.blocking_write(buffer) { | ||
| 604 | Ok(0) => panic!("zero-length write."), | ||
| 605 | Ok(n) => buffer = &buffer[n..], | ||
| 606 | Err(e) => return Err(e), | ||
| 607 | } | ||
| 608 | } | ||
| 609 | Ok(()) | ||
| 571 | } | 610 | } |
| 572 | 611 | ||
| 573 | fn bflush(&mut self) -> Result<(), Self::Error> { | 612 | fn bflush(&mut self) -> Result<(), Self::Error> { |
| @@ -586,8 +625,15 @@ mod eh02 { | |||
| 586 | impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUart<'d, T> { | 625 | impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUart<'d, T> { |
| 587 | type Error = Error; | 626 | type Error = Error; |
| 588 | 627 | ||
| 589 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | 628 | fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { |
| 590 | self.blocking_write(buffer) | 629 | while !buffer.is_empty() { |
| 630 | match self.blocking_write(buffer) { | ||
| 631 | Ok(0) => panic!("zero-length write."), | ||
| 632 | Ok(n) => buffer = &buffer[n..], | ||
| 633 | Err(e) => return Err(e), | ||
| 634 | } | ||
| 635 | } | ||
| 636 | Ok(()) | ||
| 591 | } | 637 | } |
| 592 | 638 | ||
| 593 | fn bflush(&mut self) -> Result<(), Self::Error> { | 639 | fn bflush(&mut self) -> Result<(), Self::Error> { |
| @@ -620,7 +666,7 @@ mod eh1 { | |||
| 620 | 666 | ||
| 621 | impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { | 667 | impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { |
| 622 | fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | 668 | fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { |
| 623 | self.blocking_write(buffer) | 669 | self.blocking_write(buffer).map(drop) |
| 624 | } | 670 | } |
| 625 | 671 | ||
| 626 | fn flush(&mut self) -> Result<(), Self::Error> { | 672 | fn flush(&mut self) -> Result<(), Self::Error> { |
| @@ -630,7 +676,7 @@ mod eh1 { | |||
| 630 | 676 | ||
| 631 | impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { | 677 | impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { |
| 632 | fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { | 678 | fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { |
| 633 | self.blocking_write(&[char]).map_err(nb::Error::Other) | 679 | self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) |
| 634 | } | 680 | } |
| 635 | 681 | ||
| 636 | fn flush(&mut self) -> nb::Result<(), Self::Error> { | 682 | fn flush(&mut self) -> nb::Result<(), Self::Error> { |
| @@ -646,7 +692,7 @@ mod eh1 { | |||
| 646 | 692 | ||
| 647 | impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { | 693 | impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { |
| 648 | fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | 694 | fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { |
| 649 | self.blocking_write(buffer) | 695 | self.blocking_write(buffer).map(drop) |
| 650 | } | 696 | } |
| 651 | 697 | ||
| 652 | fn flush(&mut self) -> Result<(), Self::Error> { | 698 | fn flush(&mut self) -> Result<(), Self::Error> { |
| @@ -656,7 +702,7 @@ mod eh1 { | |||
| 656 | 702 | ||
| 657 | impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { | 703 | impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { |
| 658 | fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { | 704 | fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { |
| 659 | self.blocking_write(&[char]).map_err(nb::Error::Other) | 705 | self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) |
| 660 | } | 706 | } |
| 661 | 707 | ||
| 662 | fn flush(&mut self) -> nb::Result<(), Self::Error> { | 708 | fn flush(&mut self) -> nb::Result<(), Self::Error> { |
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b66d724d5..14ec3d70a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -60,7 +60,7 @@ sdio-host = "0.5.0" | |||
| 60 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } | 60 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } |
| 61 | critical-section = "1.1" | 61 | critical-section = "1.1" |
| 62 | atomic-polyfill = "1.0.1" | 62 | atomic-polyfill = "1.0.1" |
| 63 | stm32-metapac = { version = "1", features = ["rt"] } | 63 | stm32-metapac = { version = "2", features = ["rt"] } |
| 64 | vcell = "0.1.3" | 64 | vcell = "0.1.3" |
| 65 | bxcan = "0.7.0" | 65 | bxcan = "0.7.0" |
| 66 | nb = "1.0.0" | 66 | nb = "1.0.0" |
| @@ -72,7 +72,7 @@ embedded-io = { version = "0.4.0", features = ["async"], optional = true } | |||
| 72 | [build-dependencies] | 72 | [build-dependencies] |
| 73 | proc-macro2 = "1.0.36" | 73 | proc-macro2 = "1.0.36" |
| 74 | quote = "1.0.15" | 74 | quote = "1.0.15" |
| 75 | stm32-metapac = { version = "1", default-features = false, features = ["metadata"]} | 75 | stm32-metapac = { version = "2", default-features = false, features = ["metadata"]} |
| 76 | 76 | ||
| 77 | [features] | 77 | [features] |
| 78 | defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] | 78 | defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index dbfc1370d..3780c5a40 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -427,6 +427,12 @@ fn main() { | |||
| 427 | (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)), | 427 | (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)), |
| 428 | (("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)), | 428 | (("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)), |
| 429 | (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)), | 429 | (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)), |
| 430 | (("quadspi", "BK1_IO0"), quote!(crate::qspi::D0Pin)), | ||
| 431 | (("quadspi", "BK1_IO1"), quote!(crate::qspi::D1Pin)), | ||
| 432 | (("quadspi", "BK1_IO2"), quote!(crate::qspi::D2Pin)), | ||
| 433 | (("quadspi", "BK1_IO3"), quote!(crate::qspi::D3Pin)), | ||
| 434 | (("quadspi", "CLK"), quote!(crate::qspi::SckPin)), | ||
| 435 | (("quadspi", "BK1_NCS"), quote!(crate::qspi::NSSPin)), | ||
| 430 | ].into(); | 436 | ].into(); |
| 431 | 437 | ||
| 432 | for p in METADATA.peripherals { | 438 | for p in METADATA.peripherals { |
| @@ -507,6 +513,7 @@ fn main() { | |||
| 507 | (("dcmi", "PSSI"), quote!(crate::dcmi::FrameDma)), | 513 | (("dcmi", "PSSI"), quote!(crate::dcmi::FrameDma)), |
| 508 | // SDMMCv1 uses the same channel for both directions, so just implement for RX | 514 | // SDMMCv1 uses the same channel for both directions, so just implement for RX |
| 509 | (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), | 515 | (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), |
| 516 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), | ||
| 510 | ] | 517 | ] |
| 511 | .into(); | 518 | .into(); |
| 512 | 519 | ||
diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 60ba80048..bc5fb1d6f 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | macro_rules! impl_sample_time { | 1 | macro_rules! impl_sample_time { |
| 2 | ($default_doc:expr, $default:ident, $pac:ty, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { | 2 | ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { |
| 3 | #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] | 3 | #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] |
| 4 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] | 4 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] |
| 5 | pub enum SampleTime { | 5 | pub enum SampleTime { |
| @@ -9,10 +9,10 @@ macro_rules! impl_sample_time { | |||
| 9 | )* | 9 | )* |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | impl From<SampleTime> for $pac { | 12 | impl From<SampleTime> for crate::pac::adc::vals::SampleTime { |
| 13 | fn from(sample_time: SampleTime) -> $pac { | 13 | fn from(sample_time: SampleTime) -> crate::pac::adc::vals::SampleTime { |
| 14 | match sample_time { | 14 | match sample_time { |
| 15 | $(SampleTime::$variant => <$pac>::$pac_variant),* | 15 | $(SampleTime::$variant => crate::pac::adc::vals::SampleTime::$pac_variant),* |
| 16 | } | 16 | } |
| 17 | } | 17 | } |
| 18 | } | 18 | } |
| @@ -29,7 +29,6 @@ macro_rules! impl_sample_time { | |||
| 29 | impl_sample_time!( | 29 | impl_sample_time!( |
| 30 | "1.5", | 30 | "1.5", |
| 31 | Cycles1_5, | 31 | Cycles1_5, |
| 32 | crate::pac::adc::vals::SampleTime, | ||
| 33 | ( | 32 | ( |
| 34 | ("1.5", Cycles1_5, CYCLES1_5), | 33 | ("1.5", Cycles1_5, CYCLES1_5), |
| 35 | ("7.5", Cycles7_5, CYCLES7_5), | 34 | ("7.5", Cycles7_5, CYCLES7_5), |
| @@ -46,7 +45,6 @@ impl_sample_time!( | |||
| 46 | impl_sample_time!( | 45 | impl_sample_time!( |
| 47 | "3", | 46 | "3", |
| 48 | Cycles3, | 47 | Cycles3, |
| 49 | crate::pac::adc::vals::Smp, | ||
| 50 | ( | 48 | ( |
| 51 | ("3", Cycles3, CYCLES3), | 49 | ("3", Cycles3, CYCLES3), |
| 52 | ("15", Cycles15, CYCLES15), | 50 | ("15", Cycles15, CYCLES15), |
| @@ -63,7 +61,6 @@ impl_sample_time!( | |||
| 63 | impl_sample_time!( | 61 | impl_sample_time!( |
| 64 | "2.5", | 62 | "2.5", |
| 65 | Cycles2_5, | 63 | Cycles2_5, |
| 66 | crate::pac::adc::vals::SampleTime, | ||
| 67 | ( | 64 | ( |
| 68 | ("2.5", Cycles2_5, CYCLES2_5), | 65 | ("2.5", Cycles2_5, CYCLES2_5), |
| 69 | ("6.5", Cycles6_5, CYCLES6_5), | 66 | ("6.5", Cycles6_5, CYCLES6_5), |
| @@ -80,7 +77,6 @@ impl_sample_time!( | |||
| 80 | impl_sample_time!( | 77 | impl_sample_time!( |
| 81 | "1.5", | 78 | "1.5", |
| 82 | Cycles1_5, | 79 | Cycles1_5, |
| 83 | crate::pac::adc::vals::SampleTime, | ||
| 84 | ( | 80 | ( |
| 85 | ("1.5", Cycles1_5, CYCLES1_5), | 81 | ("1.5", Cycles1_5, CYCLES1_5), |
| 86 | ("3.5", Cycles3_5, CYCLES3_5), | 82 | ("3.5", Cycles3_5, CYCLES3_5), |
| @@ -97,7 +93,6 @@ impl_sample_time!( | |||
| 97 | impl_sample_time!( | 93 | impl_sample_time!( |
| 98 | "1.5", | 94 | "1.5", |
| 99 | Cycles1_5, | 95 | Cycles1_5, |
| 100 | crate::pac::adc::vals::Smp, | ||
| 101 | ( | 96 | ( |
| 102 | ("1.5", Cycles1_5, CYCLES1_5), | 97 | ("1.5", Cycles1_5, CYCLES1_5), |
| 103 | ("2.5", Cycles2_5, CYCLES2_5), | 98 | ("2.5", Cycles2_5, CYCLES2_5), |
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index eeaa04f67..8dc4df2dc 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -48,6 +48,8 @@ pub mod crc; | |||
| 48 | ))] | 48 | ))] |
| 49 | pub mod flash; | 49 | pub mod flash; |
| 50 | pub mod pwm; | 50 | pub mod pwm; |
| 51 | #[cfg(quadspi)] | ||
| 52 | pub mod qspi; | ||
| 51 | #[cfg(rng)] | 53 | #[cfg(rng)] |
| 52 | pub mod rng; | 54 | pub mod rng; |
| 53 | #[cfg(sdmmc)] | 55 | #[cfg(sdmmc)] |
| @@ -60,7 +62,6 @@ pub mod usart; | |||
| 60 | pub mod usb; | 62 | pub mod usb; |
| 61 | #[cfg(otg)] | 63 | #[cfg(otg)] |
| 62 | pub mod usb_otg; | 64 | pub mod usb_otg; |
| 63 | |||
| 64 | #[cfg(iwdg)] | 65 | #[cfg(iwdg)] |
| 65 | pub mod wdg; | 66 | pub mod wdg; |
| 66 | 67 | ||
diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs new file mode 100644 index 000000000..2dbe2b061 --- /dev/null +++ b/embassy-stm32/src/qspi/enums.rs | |||
| @@ -0,0 +1,294 @@ | |||
| 1 | #[allow(dead_code)] | ||
| 2 | #[derive(Copy, Clone)] | ||
| 3 | pub(crate) enum QspiMode { | ||
| 4 | IndirectWrite, | ||
| 5 | IndirectRead, | ||
| 6 | AutoPolling, | ||
| 7 | MemoryMapped, | ||
| 8 | } | ||
| 9 | |||
| 10 | impl Into<u8> for QspiMode { | ||
| 11 | fn into(self) -> u8 { | ||
| 12 | match self { | ||
| 13 | QspiMode::IndirectWrite => 0b00, | ||
| 14 | QspiMode::IndirectRead => 0b01, | ||
| 15 | QspiMode::AutoPolling => 0b10, | ||
| 16 | QspiMode::MemoryMapped => 0b11, | ||
| 17 | } | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | #[allow(dead_code)] | ||
| 22 | #[derive(Copy, Clone)] | ||
| 23 | pub enum QspiWidth { | ||
| 24 | NONE, | ||
| 25 | SING, | ||
| 26 | DUAL, | ||
| 27 | QUAD, | ||
| 28 | } | ||
| 29 | |||
| 30 | impl Into<u8> for QspiWidth { | ||
| 31 | fn into(self) -> u8 { | ||
| 32 | match self { | ||
| 33 | QspiWidth::NONE => 0b00, | ||
| 34 | QspiWidth::SING => 0b01, | ||
| 35 | QspiWidth::DUAL => 0b10, | ||
| 36 | QspiWidth::QUAD => 0b11, | ||
| 37 | } | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | #[derive(Copy, Clone)] | ||
| 42 | pub enum MemorySize { | ||
| 43 | _1KiB, | ||
| 44 | _2KiB, | ||
| 45 | _4KiB, | ||
| 46 | _8KiB, | ||
| 47 | _16KiB, | ||
| 48 | _32KiB, | ||
| 49 | _64KiB, | ||
| 50 | _128KiB, | ||
| 51 | _256KiB, | ||
| 52 | _512KiB, | ||
| 53 | _1MiB, | ||
| 54 | _2MiB, | ||
| 55 | _4MiB, | ||
| 56 | _8MiB, | ||
| 57 | _16MiB, | ||
| 58 | _32MiB, | ||
| 59 | _64MiB, | ||
| 60 | _128MiB, | ||
| 61 | _256MiB, | ||
| 62 | _512MiB, | ||
| 63 | _1GiB, | ||
| 64 | _2GiB, | ||
| 65 | _4GiB, | ||
| 66 | Other(u8), | ||
| 67 | } | ||
| 68 | |||
| 69 | impl Into<u8> for MemorySize { | ||
| 70 | fn into(self) -> u8 { | ||
| 71 | match self { | ||
| 72 | MemorySize::_1KiB => 9, | ||
| 73 | MemorySize::_2KiB => 10, | ||
| 74 | MemorySize::_4KiB => 11, | ||
| 75 | MemorySize::_8KiB => 12, | ||
| 76 | MemorySize::_16KiB => 13, | ||
| 77 | MemorySize::_32KiB => 14, | ||
| 78 | MemorySize::_64KiB => 15, | ||
| 79 | MemorySize::_128KiB => 16, | ||
| 80 | MemorySize::_256KiB => 17, | ||
| 81 | MemorySize::_512KiB => 18, | ||
| 82 | MemorySize::_1MiB => 19, | ||
| 83 | MemorySize::_2MiB => 20, | ||
| 84 | MemorySize::_4MiB => 21, | ||
| 85 | MemorySize::_8MiB => 22, | ||
| 86 | MemorySize::_16MiB => 23, | ||
| 87 | MemorySize::_32MiB => 24, | ||
| 88 | MemorySize::_64MiB => 25, | ||
| 89 | MemorySize::_128MiB => 26, | ||
| 90 | MemorySize::_256MiB => 27, | ||
| 91 | MemorySize::_512MiB => 28, | ||
| 92 | MemorySize::_1GiB => 29, | ||
| 93 | MemorySize::_2GiB => 30, | ||
| 94 | MemorySize::_4GiB => 31, | ||
| 95 | MemorySize::Other(val) => val, | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | #[derive(Copy, Clone)] | ||
| 101 | pub enum AddressSize { | ||
| 102 | _8Bit, | ||
| 103 | _16Bit, | ||
| 104 | _24bit, | ||
| 105 | _32bit, | ||
| 106 | } | ||
| 107 | |||
| 108 | impl Into<u8> for AddressSize { | ||
| 109 | fn into(self) -> u8 { | ||
| 110 | match self { | ||
| 111 | AddressSize::_8Bit => 0b00, | ||
| 112 | AddressSize::_16Bit => 0b01, | ||
| 113 | AddressSize::_24bit => 0b10, | ||
| 114 | AddressSize::_32bit => 0b11, | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | #[derive(Copy, Clone)] | ||
| 120 | pub enum ChipSelectHightTime { | ||
| 121 | _1Cycle, | ||
| 122 | _2Cycle, | ||
| 123 | _3Cycle, | ||
| 124 | _4Cycle, | ||
| 125 | _5Cycle, | ||
| 126 | _6Cycle, | ||
| 127 | _7Cycle, | ||
| 128 | _8Cycle, | ||
| 129 | } | ||
| 130 | |||
| 131 | impl Into<u8> for ChipSelectHightTime { | ||
| 132 | fn into(self) -> u8 { | ||
| 133 | match self { | ||
| 134 | ChipSelectHightTime::_1Cycle => 0, | ||
| 135 | ChipSelectHightTime::_2Cycle => 1, | ||
| 136 | ChipSelectHightTime::_3Cycle => 2, | ||
| 137 | ChipSelectHightTime::_4Cycle => 3, | ||
| 138 | ChipSelectHightTime::_5Cycle => 4, | ||
| 139 | ChipSelectHightTime::_6Cycle => 5, | ||
| 140 | ChipSelectHightTime::_7Cycle => 6, | ||
| 141 | ChipSelectHightTime::_8Cycle => 7, | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | #[derive(Copy, Clone)] | ||
| 147 | pub enum FIFOThresholdLevel { | ||
| 148 | _1Bytes, | ||
| 149 | _2Bytes, | ||
| 150 | _3Bytes, | ||
| 151 | _4Bytes, | ||
| 152 | _5Bytes, | ||
| 153 | _6Bytes, | ||
| 154 | _7Bytes, | ||
| 155 | _8Bytes, | ||
| 156 | _9Bytes, | ||
| 157 | _10Bytes, | ||
| 158 | _11Bytes, | ||
| 159 | _12Bytes, | ||
| 160 | _13Bytes, | ||
| 161 | _14Bytes, | ||
| 162 | _15Bytes, | ||
| 163 | _16Bytes, | ||
| 164 | _17Bytes, | ||
| 165 | _18Bytes, | ||
| 166 | _19Bytes, | ||
| 167 | _20Bytes, | ||
| 168 | _21Bytes, | ||
| 169 | _22Bytes, | ||
| 170 | _23Bytes, | ||
| 171 | _24Bytes, | ||
| 172 | _25Bytes, | ||
| 173 | _26Bytes, | ||
| 174 | _27Bytes, | ||
| 175 | _28Bytes, | ||
| 176 | _29Bytes, | ||
| 177 | _30Bytes, | ||
| 178 | _31Bytes, | ||
| 179 | _32Bytes, | ||
| 180 | } | ||
| 181 | |||
| 182 | impl Into<u8> for FIFOThresholdLevel { | ||
| 183 | fn into(self) -> u8 { | ||
| 184 | match self { | ||
| 185 | FIFOThresholdLevel::_1Bytes => 0, | ||
| 186 | FIFOThresholdLevel::_2Bytes => 1, | ||
| 187 | FIFOThresholdLevel::_3Bytes => 2, | ||
| 188 | FIFOThresholdLevel::_4Bytes => 3, | ||
| 189 | FIFOThresholdLevel::_5Bytes => 4, | ||
| 190 | FIFOThresholdLevel::_6Bytes => 5, | ||
| 191 | FIFOThresholdLevel::_7Bytes => 6, | ||
| 192 | FIFOThresholdLevel::_8Bytes => 7, | ||
| 193 | FIFOThresholdLevel::_9Bytes => 8, | ||
| 194 | FIFOThresholdLevel::_10Bytes => 9, | ||
| 195 | FIFOThresholdLevel::_11Bytes => 10, | ||
| 196 | FIFOThresholdLevel::_12Bytes => 11, | ||
| 197 | FIFOThresholdLevel::_13Bytes => 12, | ||
| 198 | FIFOThresholdLevel::_14Bytes => 13, | ||
| 199 | FIFOThresholdLevel::_15Bytes => 14, | ||
| 200 | FIFOThresholdLevel::_16Bytes => 15, | ||
| 201 | FIFOThresholdLevel::_17Bytes => 16, | ||
| 202 | FIFOThresholdLevel::_18Bytes => 17, | ||
| 203 | FIFOThresholdLevel::_19Bytes => 18, | ||
| 204 | FIFOThresholdLevel::_20Bytes => 19, | ||
| 205 | FIFOThresholdLevel::_21Bytes => 20, | ||
| 206 | FIFOThresholdLevel::_22Bytes => 21, | ||
| 207 | FIFOThresholdLevel::_23Bytes => 22, | ||
| 208 | FIFOThresholdLevel::_24Bytes => 23, | ||
| 209 | FIFOThresholdLevel::_25Bytes => 24, | ||
| 210 | FIFOThresholdLevel::_26Bytes => 25, | ||
| 211 | FIFOThresholdLevel::_27Bytes => 26, | ||
| 212 | FIFOThresholdLevel::_28Bytes => 27, | ||
| 213 | FIFOThresholdLevel::_29Bytes => 28, | ||
| 214 | FIFOThresholdLevel::_30Bytes => 29, | ||
| 215 | FIFOThresholdLevel::_31Bytes => 30, | ||
| 216 | FIFOThresholdLevel::_32Bytes => 31, | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | #[derive(Copy, Clone)] | ||
| 222 | pub enum DummyCycles { | ||
| 223 | _0, | ||
| 224 | _1, | ||
| 225 | _2, | ||
| 226 | _3, | ||
| 227 | _4, | ||
| 228 | _5, | ||
| 229 | _6, | ||
| 230 | _7, | ||
| 231 | _8, | ||
| 232 | _9, | ||
| 233 | _10, | ||
| 234 | _11, | ||
| 235 | _12, | ||
| 236 | _13, | ||
| 237 | _14, | ||
| 238 | _15, | ||
| 239 | _16, | ||
| 240 | _17, | ||
| 241 | _18, | ||
| 242 | _19, | ||
| 243 | _20, | ||
| 244 | _21, | ||
| 245 | _22, | ||
| 246 | _23, | ||
| 247 | _24, | ||
| 248 | _25, | ||
| 249 | _26, | ||
| 250 | _27, | ||
| 251 | _28, | ||
| 252 | _29, | ||
| 253 | _30, | ||
| 254 | _31, | ||
| 255 | } | ||
| 256 | |||
| 257 | impl Into<u8> for DummyCycles { | ||
| 258 | fn into(self) -> u8 { | ||
| 259 | match self { | ||
| 260 | DummyCycles::_0 => 0, | ||
| 261 | DummyCycles::_1 => 1, | ||
| 262 | DummyCycles::_2 => 2, | ||
| 263 | DummyCycles::_3 => 3, | ||
| 264 | DummyCycles::_4 => 4, | ||
| 265 | DummyCycles::_5 => 5, | ||
| 266 | DummyCycles::_6 => 6, | ||
| 267 | DummyCycles::_7 => 7, | ||
| 268 | DummyCycles::_8 => 8, | ||
| 269 | DummyCycles::_9 => 9, | ||
| 270 | DummyCycles::_10 => 10, | ||
| 271 | DummyCycles::_11 => 11, | ||
| 272 | DummyCycles::_12 => 12, | ||
| 273 | DummyCycles::_13 => 13, | ||
| 274 | DummyCycles::_14 => 14, | ||
| 275 | DummyCycles::_15 => 15, | ||
| 276 | DummyCycles::_16 => 16, | ||
| 277 | DummyCycles::_17 => 17, | ||
| 278 | DummyCycles::_18 => 18, | ||
| 279 | DummyCycles::_19 => 19, | ||
| 280 | DummyCycles::_20 => 20, | ||
| 281 | DummyCycles::_21 => 21, | ||
| 282 | DummyCycles::_22 => 22, | ||
| 283 | DummyCycles::_23 => 23, | ||
| 284 | DummyCycles::_24 => 24, | ||
| 285 | DummyCycles::_25 => 25, | ||
| 286 | DummyCycles::_26 => 26, | ||
| 287 | DummyCycles::_27 => 27, | ||
| 288 | DummyCycles::_28 => 28, | ||
| 289 | DummyCycles::_29 => 29, | ||
| 290 | DummyCycles::_30 => 30, | ||
| 291 | DummyCycles::_31 => 31, | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs new file mode 100644 index 000000000..f33319620 --- /dev/null +++ b/embassy-stm32/src/qspi/mod.rs | |||
| @@ -0,0 +1,338 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | pub mod enums; | ||
| 4 | |||
| 5 | use embassy_hal_common::{into_ref, PeripheralRef}; | ||
| 6 | use enums::*; | ||
| 7 | |||
| 8 | use crate::dma::TransferOptions; | ||
| 9 | use crate::gpio::sealed::AFType; | ||
| 10 | use crate::gpio::AnyPin; | ||
| 11 | use crate::pac::quadspi::Quadspi as Regs; | ||
| 12 | use crate::rcc::RccPeripheral; | ||
| 13 | use crate::{peripherals, Peripheral}; | ||
| 14 | |||
| 15 | pub struct TransferConfig { | ||
| 16 | /// Instraction width (IMODE) | ||
| 17 | pub iwidth: QspiWidth, | ||
| 18 | /// Address width (ADMODE) | ||
| 19 | pub awidth: QspiWidth, | ||
| 20 | /// Data width (DMODE) | ||
| 21 | pub dwidth: QspiWidth, | ||
| 22 | /// Instruction Id | ||
| 23 | pub instruction: u8, | ||
| 24 | /// Flash memory address | ||
| 25 | pub address: Option<u32>, | ||
| 26 | /// Number of dummy cycles (DCYC) | ||
| 27 | pub dummy: DummyCycles, | ||
| 28 | /// Length of data | ||
| 29 | pub data_len: Option<usize>, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl Default for TransferConfig { | ||
| 33 | fn default() -> Self { | ||
| 34 | Self { | ||
| 35 | iwidth: QspiWidth::NONE, | ||
| 36 | awidth: QspiWidth::NONE, | ||
| 37 | dwidth: QspiWidth::NONE, | ||
| 38 | instruction: 0, | ||
| 39 | address: None, | ||
| 40 | dummy: DummyCycles::_0, | ||
| 41 | data_len: None, | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | pub struct Config { | ||
| 47 | /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. | ||
| 48 | /// If you need other value the whose predefined use `Other` variant. | ||
| 49 | pub memory_size: MemorySize, | ||
| 50 | /// Address size (8/16/24/32-bit) | ||
| 51 | pub address_size: AddressSize, | ||
| 52 | /// Scalar factor for generating CLK [0-255] | ||
| 53 | pub prescaler: u8, | ||
| 54 | /// Number of bytes to trigger FIFO threshold flag. | ||
| 55 | pub fifo_threshold: FIFOThresholdLevel, | ||
| 56 | /// Minimum number of cycles that chip select must be high between issued commands | ||
| 57 | pub cs_high_time: ChipSelectHightTime, | ||
| 58 | } | ||
| 59 | |||
| 60 | impl Default for Config { | ||
| 61 | fn default() -> Self { | ||
| 62 | Self { | ||
| 63 | memory_size: MemorySize::Other(0), | ||
| 64 | address_size: AddressSize::_24bit, | ||
| 65 | prescaler: 128, | ||
| 66 | fifo_threshold: FIFOThresholdLevel::_17Bytes, | ||
| 67 | cs_high_time: ChipSelectHightTime::_5Cycle, | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | #[allow(dead_code)] | ||
| 73 | pub struct Qspi<'d, T: Instance, Dma> { | ||
| 74 | _peri: PeripheralRef<'d, T>, | ||
| 75 | sck: Option<PeripheralRef<'d, AnyPin>>, | ||
| 76 | d0: Option<PeripheralRef<'d, AnyPin>>, | ||
| 77 | d1: Option<PeripheralRef<'d, AnyPin>>, | ||
| 78 | d2: Option<PeripheralRef<'d, AnyPin>>, | ||
| 79 | d3: Option<PeripheralRef<'d, AnyPin>>, | ||
| 80 | nss: Option<PeripheralRef<'d, AnyPin>>, | ||
| 81 | dma: PeripheralRef<'d, Dma>, | ||
| 82 | config: Config, | ||
| 83 | } | ||
| 84 | |||
| 85 | impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { | ||
| 86 | pub fn new( | ||
| 87 | peri: impl Peripheral<P = T> + 'd, | ||
| 88 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | ||
| 89 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | ||
| 90 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | ||
| 91 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | ||
| 92 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | ||
| 93 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | ||
| 94 | dma: impl Peripheral<P = Dma> + 'd, | ||
| 95 | config: Config, | ||
| 96 | ) -> Self { | ||
| 97 | into_ref!(peri, d0, d1, d2, d3, sck, nss); | ||
| 98 | |||
| 99 | unsafe { | ||
| 100 | sck.set_as_af(sck.af_num(), AFType::OutputPushPull); | ||
| 101 | sck.set_speed(crate::gpio::Speed::VeryHigh); | ||
| 102 | nss.set_as_af(nss.af_num(), AFType::OutputPushPull); | ||
| 103 | nss.set_speed(crate::gpio::Speed::VeryHigh); | ||
| 104 | d0.set_as_af(d0.af_num(), AFType::OutputPushPull); | ||
| 105 | d0.set_speed(crate::gpio::Speed::VeryHigh); | ||
| 106 | d1.set_as_af(d1.af_num(), AFType::OutputPushPull); | ||
| 107 | d1.set_speed(crate::gpio::Speed::VeryHigh); | ||
| 108 | d2.set_as_af(d2.af_num(), AFType::OutputPushPull); | ||
| 109 | d2.set_speed(crate::gpio::Speed::VeryHigh); | ||
| 110 | d3.set_as_af(d3.af_num(), AFType::OutputPushPull); | ||
| 111 | d3.set_speed(crate::gpio::Speed::VeryHigh); | ||
| 112 | } | ||
| 113 | |||
| 114 | Self::new_inner( | ||
| 115 | peri, | ||
| 116 | Some(d0.map_into()), | ||
| 117 | Some(d1.map_into()), | ||
| 118 | Some(d2.map_into()), | ||
| 119 | Some(d3.map_into()), | ||
| 120 | Some(sck.map_into()), | ||
| 121 | Some(nss.map_into()), | ||
| 122 | dma, | ||
| 123 | config, | ||
| 124 | ) | ||
| 125 | } | ||
| 126 | |||
| 127 | fn new_inner( | ||
| 128 | peri: impl Peripheral<P = T> + 'd, | ||
| 129 | d0: Option<PeripheralRef<'d, AnyPin>>, | ||
| 130 | d1: Option<PeripheralRef<'d, AnyPin>>, | ||
| 131 | d2: Option<PeripheralRef<'d, AnyPin>>, | ||
| 132 | d3: Option<PeripheralRef<'d, AnyPin>>, | ||
| 133 | sck: Option<PeripheralRef<'d, AnyPin>>, | ||
| 134 | nss: Option<PeripheralRef<'d, AnyPin>>, | ||
| 135 | dma: impl Peripheral<P = Dma> + 'd, | ||
| 136 | config: Config, | ||
| 137 | ) -> Self { | ||
| 138 | into_ref!(peri, dma); | ||
| 139 | |||
| 140 | T::enable(); | ||
| 141 | unsafe { | ||
| 142 | T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); | ||
| 143 | |||
| 144 | while T::REGS.sr().read().busy() {} | ||
| 145 | |||
| 146 | T::REGS.cr().write(|w| { | ||
| 147 | w.set_prescaler(config.prescaler); | ||
| 148 | w.set_en(true); | ||
| 149 | }); | ||
| 150 | T::REGS.dcr().write(|w| { | ||
| 151 | w.set_fsize(config.memory_size.into()); | ||
| 152 | w.set_csht(config.cs_high_time.into()); | ||
| 153 | w.set_ckmode(false); | ||
| 154 | }); | ||
| 155 | } | ||
| 156 | |||
| 157 | Self { | ||
| 158 | _peri: peri, | ||
| 159 | sck, | ||
| 160 | d0, | ||
| 161 | d1, | ||
| 162 | d2, | ||
| 163 | d3, | ||
| 164 | nss, | ||
| 165 | dma, | ||
| 166 | config, | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | pub fn command(&mut self, transaction: TransferConfig) { | ||
| 171 | unsafe { | ||
| 172 | T::REGS.cr().modify(|v| v.set_dmaen(false)); | ||
| 173 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | ||
| 174 | |||
| 175 | while !T::REGS.sr().read().tcf() {} | ||
| 176 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { | ||
| 181 | unsafe { | ||
| 182 | T::REGS.cr().modify(|v| v.set_dmaen(false)); | ||
| 183 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | ||
| 184 | |||
| 185 | if let Some(len) = transaction.data_len { | ||
| 186 | let current_ar = T::REGS.ar().read().address(); | ||
| 187 | T::REGS.ccr().modify(|v| { | ||
| 188 | v.set_fmode(QspiMode::IndirectRead.into()); | ||
| 189 | }); | ||
| 190 | T::REGS.ar().write(|v| { | ||
| 191 | v.set_address(current_ar); | ||
| 192 | }); | ||
| 193 | |||
| 194 | for idx in 0..len { | ||
| 195 | while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} | ||
| 196 | buf[idx] = *(T::REGS.dr().ptr() as *mut u8); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | while !T::REGS.sr().read().tcf() {} | ||
| 201 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { | ||
| 206 | unsafe { | ||
| 207 | T::REGS.cr().modify(|v| v.set_dmaen(false)); | ||
| 208 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | ||
| 209 | |||
| 210 | if let Some(len) = transaction.data_len { | ||
| 211 | T::REGS.ccr().modify(|v| { | ||
| 212 | v.set_fmode(QspiMode::IndirectWrite.into()); | ||
| 213 | }); | ||
| 214 | |||
| 215 | for idx in 0..len { | ||
| 216 | while !T::REGS.sr().read().ftf() {} | ||
| 217 | *(T::REGS.dr().ptr() as *mut u8) = buf[idx]; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | while !T::REGS.sr().read().tcf() {} | ||
| 222 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) | ||
| 227 | where | ||
| 228 | Dma: QuadDma<T>, | ||
| 229 | { | ||
| 230 | unsafe { | ||
| 231 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | ||
| 232 | |||
| 233 | let request = self.dma.request(); | ||
| 234 | let options = TransferOptions::default(); | ||
| 235 | |||
| 236 | T::REGS.ccr().modify(|v| { | ||
| 237 | v.set_fmode(QspiMode::IndirectRead.into()); | ||
| 238 | }); | ||
| 239 | let current_ar = T::REGS.ar().read().address(); | ||
| 240 | T::REGS.ar().write(|v| { | ||
| 241 | v.set_address(current_ar); | ||
| 242 | }); | ||
| 243 | |||
| 244 | self.dma | ||
| 245 | .start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options); | ||
| 246 | |||
| 247 | T::REGS.cr().modify(|v| v.set_dmaen(true)); | ||
| 248 | |||
| 249 | while self.dma.is_running() {} | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) | ||
| 254 | where | ||
| 255 | Dma: QuadDma<T>, | ||
| 256 | { | ||
| 257 | unsafe { | ||
| 258 | self.setup_transaction(QspiMode::IndirectWrite, &transaction); | ||
| 259 | |||
| 260 | let request = self.dma.request(); | ||
| 261 | let options = TransferOptions::default(); | ||
| 262 | |||
| 263 | T::REGS.ccr().modify(|v| { | ||
| 264 | v.set_fmode(QspiMode::IndirectWrite.into()); | ||
| 265 | }); | ||
| 266 | |||
| 267 | self.dma | ||
| 268 | .start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options); | ||
| 269 | |||
| 270 | T::REGS.cr().modify(|v| v.set_dmaen(true)); | ||
| 271 | |||
| 272 | while self.dma.is_running() {} | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { | ||
| 277 | unsafe { | ||
| 278 | T::REGS.fcr().modify(|v| { | ||
| 279 | v.set_csmf(true); | ||
| 280 | v.set_ctcf(true); | ||
| 281 | v.set_ctef(true); | ||
| 282 | v.set_ctof(true); | ||
| 283 | }); | ||
| 284 | |||
| 285 | while T::REGS.sr().read().busy() {} | ||
| 286 | |||
| 287 | if let Some(len) = transaction.data_len { | ||
| 288 | T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); | ||
| 289 | } | ||
| 290 | |||
| 291 | T::REGS.ccr().write(|v| { | ||
| 292 | v.set_fmode(fmode.into()); | ||
| 293 | v.set_imode(transaction.iwidth.into()); | ||
| 294 | v.set_instruction(transaction.instruction); | ||
| 295 | v.set_admode(transaction.awidth.into()); | ||
| 296 | v.set_adsize(self.config.address_size.into()); | ||
| 297 | v.set_dmode(transaction.dwidth.into()); | ||
| 298 | v.set_abmode(QspiWidth::NONE.into()); | ||
| 299 | v.set_dcyc(transaction.dummy.into()); | ||
| 300 | }); | ||
| 301 | |||
| 302 | if let Some(addr) = transaction.address { | ||
| 303 | T::REGS.ar().write(|v| { | ||
| 304 | v.set_address(addr); | ||
| 305 | }); | ||
| 306 | } | ||
| 307 | } | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | pub(crate) mod sealed { | ||
| 312 | use super::*; | ||
| 313 | |||
| 314 | pub trait Instance { | ||
| 315 | const REGS: Regs; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} | ||
| 320 | |||
| 321 | pin_trait!(SckPin, Instance); | ||
| 322 | pin_trait!(D0Pin, Instance); | ||
| 323 | pin_trait!(D1Pin, Instance); | ||
| 324 | pin_trait!(D2Pin, Instance); | ||
| 325 | pin_trait!(D3Pin, Instance); | ||
| 326 | pin_trait!(NSSPin, Instance); | ||
| 327 | |||
| 328 | dma_trait!(QuadDma, Instance); | ||
| 329 | |||
| 330 | foreach_peripheral!( | ||
| 331 | (quadspi, $inst:ident) => { | ||
| 332 | impl sealed::Instance for peripherals::$inst { | ||
| 333 | const REGS: Regs = crate::pac::$inst; | ||
| 334 | } | ||
| 335 | |||
| 336 | impl Instance for peripherals::$inst {} | ||
| 337 | }; | ||
| 338 | ); | ||
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index a27fcc1ca..cd7d72f91 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -197,6 +197,40 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { | |||
| 197 | .await | 197 | .await |
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | fn inner_blocking_read(&self, buf: &mut [u8]) -> Result<usize, Error> { | ||
| 201 | loop { | ||
| 202 | let mut do_pend = false; | ||
| 203 | let mut inner = self.inner.borrow_mut(); | ||
| 204 | let n = inner.with(|state| { | ||
| 205 | compiler_fence(Ordering::SeqCst); | ||
| 206 | |||
| 207 | // We have data ready in buffer? Return it. | ||
| 208 | let data = state.rx.pop_buf(); | ||
| 209 | if !data.is_empty() { | ||
| 210 | let len = data.len().min(buf.len()); | ||
| 211 | buf[..len].copy_from_slice(&data[..len]); | ||
| 212 | |||
| 213 | if state.rx.is_full() { | ||
| 214 | do_pend = true; | ||
| 215 | } | ||
| 216 | state.rx.pop(len); | ||
| 217 | |||
| 218 | return len; | ||
| 219 | } | ||
| 220 | |||
| 221 | 0 | ||
| 222 | }); | ||
| 223 | |||
| 224 | if do_pend { | ||
| 225 | inner.pend(); | ||
| 226 | } | ||
| 227 | |||
| 228 | if n > 0 { | ||
| 229 | return Ok(n); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 200 | async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> { | 234 | async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> { |
| 201 | poll_fn(move |cx| { | 235 | poll_fn(move |cx| { |
| 202 | let mut inner = self.inner.borrow_mut(); | 236 | let mut inner = self.inner.borrow_mut(); |
| @@ -236,6 +270,39 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { | |||
| 236 | .await | 270 | .await |
| 237 | } | 271 | } |
| 238 | 272 | ||
| 273 | fn inner_blocking_write(&self, buf: &[u8]) -> Result<usize, Error> { | ||
| 274 | loop { | ||
| 275 | let mut inner = self.inner.borrow_mut(); | ||
| 276 | let (n, empty) = inner.with(|state| { | ||
| 277 | let empty = state.tx.is_empty(); | ||
| 278 | let tx_buf = state.tx.push_buf(); | ||
| 279 | if tx_buf.is_empty() { | ||
| 280 | return (0, empty); | ||
| 281 | } | ||
| 282 | |||
| 283 | let n = core::cmp::min(tx_buf.len(), buf.len()); | ||
| 284 | tx_buf[..n].copy_from_slice(&buf[..n]); | ||
| 285 | state.tx.push(n); | ||
| 286 | |||
| 287 | (n, empty) | ||
| 288 | }); | ||
| 289 | if empty { | ||
| 290 | inner.pend(); | ||
| 291 | } | ||
| 292 | if n != 0 { | ||
| 293 | return Ok(n); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | fn inner_blocking_flush(&self) -> Result<(), Error> { | ||
| 299 | loop { | ||
| 300 | if !self.inner.borrow_mut().with(|state| state.tx.is_empty()) { | ||
| 301 | return Ok(()); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 239 | async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { | 306 | async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { |
| 240 | poll_fn(move |cx| { | 307 | poll_fn(move |cx| { |
| 241 | self.inner.borrow_mut().with(|state| { | 308 | self.inner.borrow_mut().with(|state| { |
| @@ -419,3 +486,35 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, | |||
| 419 | self.inner.inner_flush().await | 486 | self.inner.inner_flush().await |
| 420 | } | 487 | } |
| 421 | } | 488 | } |
| 489 | |||
| 490 | impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { | ||
| 491 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 492 | self.inner_blocking_read(buf) | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> { | ||
| 497 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 498 | self.inner.inner_blocking_read(buf) | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 | impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { | ||
| 503 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 504 | self.inner_blocking_write(buf) | ||
| 505 | } | ||
| 506 | |||
| 507 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 508 | self.inner_blocking_flush() | ||
| 509 | } | ||
| 510 | } | ||
| 511 | |||
| 512 | impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> { | ||
| 513 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 514 | self.inner.inner_blocking_write(buf) | ||
| 515 | } | ||
| 516 | |||
| 517 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 518 | self.inner.inner_blocking_flush() | ||
| 519 | } | ||
| 520 | } | ||
diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 1977005fb..ee27cdec8 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs | |||
| @@ -32,16 +32,16 @@ impl<'p, M, const N: usize> Writer<'p, M, N> | |||
| 32 | where | 32 | where |
| 33 | M: RawMutex, | 33 | M: RawMutex, |
| 34 | { | 34 | { |
| 35 | /// Writes a value. | 35 | /// Write some bytes to the pipe. |
| 36 | /// | 36 | /// |
| 37 | /// See [`Pipe::write()`] | 37 | /// See [`Pipe::write()`] |
| 38 | pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> { | 38 | pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> { |
| 39 | self.pipe.write(buf) | 39 | self.pipe.write(buf) |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | /// Attempt to immediately write a message. | 42 | /// Attempt to immediately write some bytes to the pipe. |
| 43 | /// | 43 | /// |
| 44 | /// See [`Pipe::write()`] | 44 | /// See [`Pipe::try_write()`] |
| 45 | pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> { | 45 | pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> { |
| 46 | self.pipe.try_write(buf) | 46 | self.pipe.try_write(buf) |
| 47 | } | 47 | } |
| @@ -95,16 +95,16 @@ impl<'p, M, const N: usize> Reader<'p, M, N> | |||
| 95 | where | 95 | where |
| 96 | M: RawMutex, | 96 | M: RawMutex, |
| 97 | { | 97 | { |
| 98 | /// Reads a value. | 98 | /// Read some bytes from the pipe. |
| 99 | /// | 99 | /// |
| 100 | /// See [`Pipe::read()`] | 100 | /// See [`Pipe::read()`] |
| 101 | pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> { | 101 | pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> { |
| 102 | self.pipe.read(buf) | 102 | self.pipe.read(buf) |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | /// Attempt to immediately read a message. | 105 | /// Attempt to immediately read some bytes from the pipe. |
| 106 | /// | 106 | /// |
| 107 | /// See [`Pipe::read()`] | 107 | /// See [`Pipe::try_read()`] |
| 108 | pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> { | 108 | pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> { |
| 109 | self.pipe.try_read(buf) | 109 | self.pipe.try_read(buf) |
| 110 | } | 110 | } |
| @@ -221,12 +221,11 @@ impl<const N: usize> PipeState<N> { | |||
| 221 | } | 221 | } |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | /// A bounded pipe for communicating between asynchronous tasks | 224 | /// A bounded byte-oriented pipe for communicating between asynchronous tasks |
| 225 | /// with backpressure. | 225 | /// with backpressure. |
| 226 | /// | 226 | /// |
| 227 | /// The pipe will buffer up to the provided number of messages. Once the | 227 | /// The pipe will buffer up to the provided number of bytes. Once the |
| 228 | /// buffer is full, attempts to `write` new messages will wait until a message is | 228 | /// buffer is full, attempts to `write` new bytes will wait until buffer space is freed up. |
| 229 | /// read from the pipe. | ||
| 230 | /// | 229 | /// |
| 231 | /// All data written will become available in the same order as it was written. | 230 | /// All data written will become available in the same order as it was written. |
| 232 | pub struct Pipe<M, const N: usize> | 231 | pub struct Pipe<M, const N: usize> |
| @@ -277,40 +276,56 @@ where | |||
| 277 | Reader { pipe: self } | 276 | Reader { pipe: self } |
| 278 | } | 277 | } |
| 279 | 278 | ||
| 280 | /// Write a value, waiting until there is capacity. | 279 | /// Write some bytes to the pipe. |
| 280 | /// | ||
| 281 | /// This method writes a nonzero amount of bytes from `buf` into the pipe, and | ||
| 282 | /// returns the amount of bytes written. | ||
| 283 | /// | ||
| 284 | /// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full, | ||
| 285 | /// this method will wait until it is. See [`try_write`](Self::try_write) for a variant that | ||
| 286 | /// returns an error instead of waiting. | ||
| 281 | /// | 287 | /// |
| 282 | /// Writeing completes when the value has been pushed to the pipe's queue. | 288 | /// It is not guaranteed that all bytes in the buffer are written, even if there's enough |
| 283 | /// This doesn't mean the value has been read yet. | 289 | /// free space in the pipe buffer for all. In other words, it is possible for `write` to return |
| 290 | /// without writing all of `buf` (returning a number less than `buf.len()`) and still leave | ||
| 291 | /// free space in the pipe buffer. You should always `write` in a loop, or use helpers like | ||
| 292 | /// `write_all` from the `embedded-io` crate. | ||
| 284 | pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> { | 293 | pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> { |
| 285 | WriteFuture { pipe: self, buf } | 294 | WriteFuture { pipe: self, buf } |
| 286 | } | 295 | } |
| 287 | 296 | ||
| 288 | /// Attempt to immediately write a message. | 297 | /// Attempt to immediately write some bytes to the pipe. |
| 289 | /// | 298 | /// |
| 290 | /// This method differs from [`write`](Pipe::write) by returning immediately if the pipe's | 299 | /// This method will either write a nonzero amount of bytes to the pipe immediately, |
| 291 | /// buffer is full, instead of waiting. | 300 | /// or return an error if the pipe is empty. See [`write`](Self::write) for a variant |
| 292 | /// | 301 | /// that waits instead of returning an error. |
| 293 | /// # Errors | ||
| 294 | /// | ||
| 295 | /// If the pipe capacity has been reached, i.e., the pipe has `n` | ||
| 296 | /// buffered values where `n` is the argument passed to [`Pipe`], then an | ||
| 297 | /// error is returned. | ||
| 298 | pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> { | 302 | pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> { |
| 299 | self.lock(|c| c.try_write(buf)) | 303 | self.lock(|c| c.try_write(buf)) |
| 300 | } | 304 | } |
| 301 | 305 | ||
| 302 | /// Receive the next value. | 306 | /// Read some bytes from the pipe. |
| 307 | /// | ||
| 308 | /// This method reads a nonzero amount of bytes from the pipe into `buf` and | ||
| 309 | /// returns the amount of bytes read. | ||
| 310 | /// | ||
| 311 | /// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty, | ||
| 312 | /// this method will wait until it is. See [`try_read`](Self::try_read) for a variant that | ||
| 313 | /// returns an error instead of waiting. | ||
| 303 | /// | 314 | /// |
| 304 | /// If there are no messages in the pipe's buffer, this method will | 315 | /// It is not guaranteed that all bytes in the buffer are read, even if there's enough |
| 305 | /// wait until a message is written. | 316 | /// space in `buf` for all. In other words, it is possible for `read` to return |
| 317 | /// without filling `buf` (returning a number less than `buf.len()`) and still leave bytes | ||
| 318 | /// in the pipe buffer. You should always `read` in a loop, or use helpers like | ||
| 319 | /// `read_exact` from the `embedded-io` crate. | ||
| 306 | pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> { | 320 | pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> { |
| 307 | ReadFuture { pipe: self, buf } | 321 | ReadFuture { pipe: self, buf } |
| 308 | } | 322 | } |
| 309 | 323 | ||
| 310 | /// Attempt to immediately read a message. | 324 | /// Attempt to immediately read some bytes from the pipe. |
| 311 | /// | 325 | /// |
| 312 | /// This method will either read a message from the pipe immediately or return an error | 326 | /// This method will either read a nonzero amount of bytes from the pipe immediately, |
| 313 | /// if the pipe is empty. | 327 | /// or return an error if the pipe is empty. See [`read`](Self::read) for a variant |
| 328 | /// that waits instead of returning an error. | ||
| 314 | pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> { | 329 | pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> { |
| 315 | self.lock(|c| c.try_read(buf)) | 330 | self.lock(|c| c.try_read(buf)) |
| 316 | } | 331 | } |
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 305dfa02e..6b68bcd7b 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs | |||
| @@ -201,6 +201,14 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 201 | self.config_descriptor.end_configuration(); | 201 | self.config_descriptor.end_configuration(); |
| 202 | self.bos_descriptor.end_bos(); | 202 | self.bos_descriptor.end_bos(); |
| 203 | 203 | ||
| 204 | // Log the number of allocator bytes actually used in descriptor buffers | ||
| 205 | info!("USB: device_descriptor used: {}", self.device_descriptor.position()); | ||
| 206 | info!("USB: config_descriptor used: {}", self.config_descriptor.position()); | ||
| 207 | info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); | ||
| 208 | #[cfg(feature = "msos-descriptor")] | ||
| 209 | info!("USB: msos_descriptor used: {}", msos_descriptor.len()); | ||
| 210 | info!("USB: control_buf size: {}", self.control_buf.len()); | ||
| 211 | |||
| 204 | UsbDevice::build( | 212 | UsbDevice::build( |
| 205 | self.driver, | 213 | self.driver, |
| 206 | self.config, | 214 | self.config, |
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 974268c62..03e4c1dbb 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs | |||
| @@ -458,6 +458,9 @@ impl<'d> Handler for Control<'d> { | |||
| 458 | return None; | 458 | return None; |
| 459 | } | 459 | } |
| 460 | 460 | ||
| 461 | // This uses a defmt-specific formatter that causes use of the `log` | ||
| 462 | // feature to fail to build, so leave it defmt-specific for now. | ||
| 463 | #[cfg(feature = "defmt")] | ||
| 461 | trace!("HID control_out {:?} {=[u8]:x}", req, data); | 464 | trace!("HID control_out {:?} {=[u8]:x}", req, data); |
| 462 | match req.request { | 465 | match req.request { |
| 463 | HID_REQ_SET_IDLE => { | 466 | HID_REQ_SET_IDLE => { |
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index bfeccd5fe..3016b81cb 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs | |||
| @@ -165,6 +165,25 @@ struct Interface { | |||
| 165 | num_alt_settings: u8, | 165 | num_alt_settings: u8, |
| 166 | } | 166 | } |
| 167 | 167 | ||
| 168 | /// A report of the used size of the runtime allocated buffers | ||
| 169 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] | ||
| 170 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 171 | pub struct UsbBufferReport { | ||
| 172 | /// Number of device descriptor bytes used | ||
| 173 | pub device_descriptor_used: usize, | ||
| 174 | /// Number of config descriptor bytes used | ||
| 175 | pub config_descriptor_used: usize, | ||
| 176 | /// Number of bos descriptor bytes used | ||
| 177 | pub bos_descriptor_used: usize, | ||
| 178 | /// Number of msos descriptor bytes used | ||
| 179 | /// | ||
| 180 | /// Will be `None` if the "msos-descriptor" feature is not active. | ||
| 181 | /// Otherwise will return Some(bytes). | ||
| 182 | pub msos_descriptor_used: Option<usize>, | ||
| 183 | /// Size of the control buffer | ||
| 184 | pub control_buffer_size: usize, | ||
| 185 | } | ||
| 186 | |||
| 168 | /// Main struct for the USB device stack. | 187 | /// Main struct for the USB device stack. |
| 169 | pub struct UsbDevice<'d, D: Driver<'d>> { | 188 | pub struct UsbDevice<'d, D: Driver<'d>> { |
| 170 | control_buf: &'d mut [u8], | 189 | control_buf: &'d mut [u8], |
| @@ -239,6 +258,24 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 239 | } | 258 | } |
| 240 | } | 259 | } |
| 241 | 260 | ||
| 261 | /// Returns a report of the consumed buffers | ||
| 262 | /// | ||
| 263 | /// Useful for tuning buffer sizes for actual usage | ||
| 264 | pub fn buffer_usage(&self) -> UsbBufferReport { | ||
| 265 | #[cfg(not(feature = "msos-descriptor"))] | ||
| 266 | let mdu = None; | ||
| 267 | #[cfg(feature = "msos-descriptor")] | ||
| 268 | let mdu = Some(self.inner.msos_descriptor.len()); | ||
| 269 | |||
| 270 | UsbBufferReport { | ||
| 271 | device_descriptor_used: self.inner.device_descriptor.len(), | ||
| 272 | config_descriptor_used: self.inner.config_descriptor.len(), | ||
| 273 | bos_descriptor_used: self.inner.bos_descriptor.len(), | ||
| 274 | msos_descriptor_used: mdu, | ||
| 275 | control_buffer_size: self.control_buf.len(), | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 242 | /// Runs the `UsbDevice` forever. | 279 | /// Runs the `UsbDevice` forever. |
| 243 | /// | 280 | /// |
| 244 | /// This future may leave the bus in an invalid state if it is dropped. | 281 | /// This future may leave the bus in an invalid state if it is dropped. |
diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index b1e0335ee..218d9931a 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs | |||
| @@ -32,6 +32,11 @@ impl<'d> MsOsDescriptorSet<'d> { | |||
| 32 | pub fn is_empty(&self) -> bool { | 32 | pub fn is_empty(&self) -> bool { |
| 33 | self.descriptor.is_empty() | 33 | self.descriptor.is_empty() |
| 34 | } | 34 | } |
| 35 | |||
| 36 | /// Returns the length of the descriptor field | ||
| 37 | pub fn len(&self) -> usize { | ||
| 38 | self.descriptor.len() | ||
| 39 | } | ||
| 35 | } | 40 | } |
| 36 | 41 | ||
| 37 | /// Writes a Microsoft OS 2.0 Descriptor set into a buffer. | 42 | /// Writes a Microsoft OS 2.0 Descriptor set into a buffer. |
diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs index 6c85ef60a..2e22c9de7 100644 --- a/tests/rp/src/bin/spi_async.rs +++ b/tests/rp/src/bin/spi_async.rs | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | //! Make sure to connect GPIO pins 3 (`PIN_3`) and 4 (`PIN_4`) together | ||
| 2 | //! to run this test. | ||
| 3 | //! | ||
| 1 | #![no_std] | 4 | #![no_std] |
| 2 | #![no_main] | 5 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 6 | #![feature(type_alias_impl_trait)] |
| @@ -18,10 +21,63 @@ async fn main(_spawner: Spawner) { | |||
| 18 | 21 | ||
| 19 | let mut spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | 22 | let mut spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); |
| 20 | 23 | ||
| 21 | let tx_buf = [1_u8, 2, 3, 4, 5, 6]; | 24 | // equal rx & tx buffers |
| 22 | let mut rx_buf = [0_u8; 6]; | 25 | { |
| 23 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | 26 | let tx_buf = [1_u8, 2, 3, 4, 5, 6]; |
| 24 | assert_eq!(rx_buf, tx_buf); | 27 | let mut rx_buf = [0_u8; 6]; |
| 28 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 29 | assert_eq!(rx_buf, tx_buf); | ||
| 30 | } | ||
| 31 | |||
| 32 | // tx > rx buffer | ||
| 33 | { | ||
| 34 | let tx_buf = [7_u8, 8, 9, 10, 11, 12]; | ||
| 35 | |||
| 36 | let mut rx_buf = [0_u8; 3]; | ||
| 37 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 38 | assert_eq!(rx_buf, tx_buf[..3]); | ||
| 39 | |||
| 40 | defmt::info!("tx > rx buffer - OK"); | ||
| 41 | } | ||
| 42 | |||
| 43 | // we make sure to that clearing FIFO works after the uneven buffers | ||
| 44 | |||
| 45 | // equal rx & tx buffers | ||
| 46 | { | ||
| 47 | let tx_buf = [13_u8, 14, 15, 16, 17, 18]; | ||
| 48 | let mut rx_buf = [0_u8; 6]; | ||
| 49 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 50 | assert_eq!(rx_buf, tx_buf); | ||
| 51 | |||
| 52 | defmt::info!("buffer rx length == tx length - OK"); | ||
| 53 | } | ||
| 54 | |||
| 55 | // rx > tx buffer | ||
| 56 | { | ||
| 57 | let tx_buf = [19_u8, 20, 21]; | ||
| 58 | let mut rx_buf = [0_u8; 6]; | ||
| 59 | |||
| 60 | // we should have written dummy data to tx buffer to sync clock. | ||
| 61 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 62 | |||
| 63 | assert_eq!( | ||
| 64 | rx_buf[..3], | ||
| 65 | tx_buf, | ||
| 66 | "only the first 3 TX bytes should have been received in the RX buffer" | ||
| 67 | ); | ||
| 68 | assert_eq!(rx_buf[3..], [0, 0, 0], "the rest of the RX bytes should be empty"); | ||
| 69 | defmt::info!("buffer rx length > tx length - OK"); | ||
| 70 | } | ||
| 71 | |||
| 72 | // equal rx & tx buffers | ||
| 73 | { | ||
| 74 | let tx_buf = [22_u8, 23, 24, 25, 26, 27]; | ||
| 75 | let mut rx_buf = [0_u8; 6]; | ||
| 76 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 77 | |||
| 78 | assert_eq!(rx_buf, tx_buf); | ||
| 79 | defmt::info!("buffer rx length = tx length - OK"); | ||
| 80 | } | ||
| 25 | 81 | ||
| 26 | info!("Test OK"); | 82 | info!("Test OK"); |
| 27 | cortex_m::asm::bkpt(); | 83 | cortex_m::asm::bkpt(); |
