Compare commits

...

77 Commits

Author SHA1 Message Date
27e6634c9d executor: add faster ARM-specific impl.
Does a wake+poll in 79 cycles in nrf52840.
2023-11-15 18:47:11 +01:00
1f9b649f80 executor: release v0.3.3 2023-11-15 18:44:01 +01:00
bef9b7a853 executor: remove atomic-polyfill. 2023-11-15 18:43:27 +01:00
50a983fd9b executor: add missing main macro reexport on xtensa. 2023-11-14 18:45:23 +01:00
7e5deae589 Merge pull request #2170 from korken89/fix/embassy-net-dns
Fix for embassy net dns entries not being extensible
2023-11-14 07:56:08 +00:00
a2c440ef8c Update changelog 2023-11-14 08:50:51 +01:00
fd670a9ae5 Use smoltcp constant in results from DNS 2023-11-14 08:50:51 +01:00
ef69f386ab Update smoltcp and fix errors from that 2023-11-14 08:50:51 +01:00
872f1ec4c2 Merge pull request #2183 from embassy-rs/buffereduarte-fix
nrf/buffered_uarte: fix missing hwfc enable.
2023-11-13 23:26:19 +00:00
6ccd8c051e nrf/buffered_uarte: add test for recovering from buffer full. 2023-11-14 00:22:30 +01:00
c46418f123 nrf/buffered_uarte: fix hang when buffer full due to PPI missing the endrx event.
Fixes #2181
2023-11-14 00:22:17 +01:00
19ff043acd nrf/buffered_uarte: fix missing hwfc enable. 2023-11-13 22:37:13 +01:00
ea99671729 Merge pull request #2180 from MaxiluxSystems/gpdma-drop-fix
stm32/gpdma: fix drop() to use documented method for aborting transfer
2023-11-13 16:53:50 +00:00
8eff749823 stm32/gpdma: fix drop() to use RM's method for aborting transfer
see e.g. STM32H503 RM section 15.4.4...

1. Write 1 into GPDMA_CxCR.SUSP
2. Poll GPDMA_CxSR.SUSPF until it is 1
3. Write 1 into GPDMA_CxCR.RESET (occurs upon next init, in new_inner())
2023-11-13 16:41:09 +00:00
cdcd3e26dd Merge pull request #2179 from t-moe/features/embassy-futures-bump
Bump embassy-futures 0.1.0 -> 0.1.1
2023-11-13 11:40:09 +00:00
18c1f9dd56 Bump embassy-futures 0.1.0 -> 0.1.1 2023-11-13 11:25:11 +01:00
ac7134ed0d Merge pull request #2178 from embassy-rs/rcc-no-spaghetti
stm32/rcc: unify f2 into f4/f7.
2023-11-13 01:08:27 +00:00
ace5221080 stm32/rcc: unify f2 into f4/f7. 2023-11-13 01:59:33 +01:00
2376b3bdfa stm32/rcc: fix pll enum naming on f4, f7. 2023-11-13 01:56:50 +01:00
f00e97a5f1 Merge pull request #2177 from embassy-rs/rcc-no-spaghetti
stm32/rcc: unify l0l1 and l4l5.
2023-11-13 00:46:43 +00:00
066dc297ed stm32/rcc: unify l0l1 and l4l5. 2023-11-13 01:05:07 +01:00
4fe344ebc0 stm32/rcc: consistent casing and naming for PLL enums. 2023-11-13 00:52:01 +01:00
39c7371621 Merge pull request #2163 from embassy-rs/update-heapless
Update heapless to v0.8
2023-11-10 15:51:33 +00:00
4647df14b1 Update heapless to v0.8, embedded-nal-async to v0.7 2023-11-10 16:43:53 +01:00
b3367be9c8 Merge pull request #2161 from xoviat/low-power
add low power for g4
2023-11-09 01:16:02 +00:00
da4feb3693 Merge branch 'main' of github.com:embassy-rs/embassy into low-power 2023-11-08 19:13:12 -06:00
05a4bb3a4a rustfmt 2023-11-08 19:06:29 -06:00
4b4c28d875 stm32: add low power for g4 2023-11-08 19:04:20 -06:00
1977acdb11 Merge pull request #2164 from embassy-rs/msos-descriptor
usb: remove msos-descriptor feature.
2023-11-08 22:23:37 +00:00
0b015bd727 usb: remove msos-descriptor feature. 2023-11-08 23:21:52 +01:00
79acb560ec Pin noproto git rev. 2023-11-08 23:05:00 +01:00
94586576a0 Merge pull request #2160 from Nigecat/main
Fix typo in embassy-net docs
2023-11-07 23:56:03 +00:00
a3e200d011 Fix typo in embassy-net docs 2023-11-08 09:28:33 +11:00
3bccb67231 Merge pull request #2155 from xoviat/eth
stm32: resolve eth/v2 security bug
2023-11-07 21:42:55 +00:00
553f0158c0 stm32: resolve eth/v2 security bug
fixes #2129
2023-11-07 15:39:06 -06:00
b44a5fcaf6 Merge pull request #2159 from kalkyl/usb-bulk-cleanup
rp: Remove control handler from USB raw bulk example
2023-11-07 19:52:11 +00:00
8effff3383 rp: Remove control handler from USB raw bulk example 2023-11-07 20:45:01 +01:00
05f983c607 Merge pull request #2158 from kalkyl/usb-bulk
rp: Add USB raw bulk example
2023-11-07 19:22:55 +00:00
d44383e9a7 fmt 2023-11-07 20:19:56 +01:00
37a773c037 Use driver reexport 2023-11-07 20:17:19 +01:00
d1adc93614 rp: Add USB raw bulk example 2023-11-07 19:57:05 +01:00
78a7ee7ec4 Merge pull request #2157 from kalkyl/usb-raw
rp: Add USB raw example + msos-descriptor to examples and usb-logger
2023-11-07 10:30:52 +00:00
e3fe13e905 Add docs 2023-11-07 10:58:35 +01:00
38bfa6916f Update pio-uart example 2023-11-07 09:15:21 +01:00
50139752bc Add comments 2023-11-07 09:10:18 +01:00
db4cd73894 rp: Add USB raw example + msos descriptor to examples and usb-logger 2023-11-07 09:05:10 +01:00
8369f7614a Merge pull request #2156 from adamgreig/usb-raw-example
Update stm32 usb_raw example to use MSOS descriptors for WinUSB
2023-11-07 02:35:59 +00:00
326bc98bd2 Update stm32 usb_raw example to use MSOS descriptors for WinUSB 2023-11-07 02:34:27 +00:00
584fc358fd Merge pull request #2154 from bugadani/executor
Prepare embassy-executor 0.3.2
2023-11-06 20:53:19 +00:00
b8f9341edc Prepare embassy-executor 0.3.2 2023-11-06 21:50:40 +01:00
7ca557b917 Merge pull request #2152 from bugadani/atomic
Executor: Yeet core::sync::atomic from risc-v
2023-11-06 18:46:50 +00:00
8d8d50cc7a Yeet core::sync::atomic, remove futures-util dep 2023-11-06 17:35:02 +01:00
5948d934d3 Merge pull request #2150 from bugadani/block
Ensure TcpIo not blocking when reading into empty slice
2023-11-06 12:44:15 +00:00
746936f8cd Merge pull request #2151 from eZioPan/rcc-init-set-vos
check PLL settings before set VOS
2023-11-06 12:43:29 +00:00
8f543062aa check PLL settings before set VOS 2023-11-06 18:30:59 +08:00
15660cfc68 Ensure TcpIo not blocking when reading into empty slice 2023-11-06 09:12:16 +01:00
74f70dc7b4 Merge pull request #2149 from embassy-rs/usb-fixes3
stm32/otg: fix enumeration on non-f4 chips.
2023-11-06 03:19:50 +00:00
7084570478 stm32/otg: fix enumeration on non-f4 chips.
Fixes regression from #2148
2023-11-06 04:13:06 +01:00
58719dcb58 Merge pull request #2148 from embassy-rs/usb-fixes3
stm32/otg: fix CONTROL OUT transfers on F4.
2023-11-06 02:46:38 +00:00
b4eef6b1ee stm32/otg: fix CONTROL OUT transfers on F4. 2023-11-06 03:38:42 +01:00
70a700e430 stm32/otg: log TRDT 2023-11-06 03:38:13 +01:00
b8679c0cc8 stm32/rcc: set highest VOS on some F4s with no overdrive. 2023-11-06 03:37:39 +01:00
ad861179cc Merge pull request #2147 from xoviat/low-power
stm32: update metapac and use stop data
2023-11-06 02:35:57 +00:00
d9b00c01e0 usb: reject instead of panic on CONTROL OUT longer than the buf. 2023-11-06 03:26:00 +01:00
239ad5ebea stm32: update metapac and use stop data 2023-11-05 20:09:33 -06:00
46cffcc8d4 Merge pull request #2119 from JuliDi/fmc-sram-adc
STM32: Add raw access to FMC peripheral and fix typo in build.rs
2023-11-06 01:31:56 +00:00
e6462f1e88 Merge pull request #2146 from adamgreig/stm32g4-opamp
Remove accidentally leftover println
2023-11-06 01:15:55 +00:00
d464d1a841 Remove accidentally leftover println 2023-11-06 01:11:57 +00:00
980c3cf42b Merge pull request #2142 from adamgreig/stm32g4-opamp
stm32: support internal output on g4 opamps
2023-11-06 00:01:34 +00:00
28eb4cd817 stm32: support internal output on g4 opamps 2023-11-05 23:57:15 +00:00
3de01bc223 Merge pull request #2145 from embassy-rs/rcc-no-spaghetti
stm32/rcc: add shared code for hsi48 with crs support.
2023-11-05 22:58:18 +00:00
0272deb158 stm32/rcc: add shared code for hsi48 with crs support. 2023-11-05 23:52:54 +01:00
c4a8b79dbc Merge pull request #2143 from adamgreig/raw-usb-example
Add raw USB example using control transfers
2023-11-05 17:34:58 +00:00
fa45dcd034 Add raw USB example using control transfers 2023-11-05 16:46:45 +00:00
6ff91851b1 Merge pull request #2141 from embassy-rs/l0l1-modernize
stm32/rcc: switch to modern api for l0, l1.
2023-11-05 02:09:48 +00:00
b1e5b6ffe1 Add raw fmc access implementation 2023-10-28 13:50:02 +02:00
2aaf4bf96b fix typo in build.rs that caused fmc ClkPin to not be implemented 2023-10-15 19:14:34 +02:00
142 changed files with 2338 additions and 1398 deletions

View File

@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.3.3 - 2023-11-15
- Add `main` macro reexport for Xtensa arch.
- Remove use of `atomic-polyfill`. The executor now has multiple implementations of its internal data structures for cases where the target supports atomics or doesn't.
## 0.3.2 - 2023-11-06
- Use `atomic-polyfill` for `riscv32`
- Removed unused dependencies (static_cell, futures-util)
## 0.3.1 - 2023-11-01
- Fix spurious "Found waker not created by the Embassy executor" error in recent nightlies.

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-executor"
version = "0.3.1"
version = "0.3.3"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage"
@ -34,7 +34,7 @@ _arch = [] # some arch was picked
arch-std = ["_arch", "critical-section/std"]
arch-cortex-m = ["_arch", "dep:cortex-m"]
arch-xtensa = ["_arch"]
arch-riscv32 = ["_arch"]
arch-riscv32 = ["_arch", "dep:portable-atomic"]
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
@ -57,12 +57,14 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
rtos-trace = { version = "0.1.2", optional = true }
futures-util = { version = "0.3.17", default-features = false }
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true}
atomic-polyfill = "1.0.1"
critical-section = "1.1"
# needed for riscv
# remove when https://github.com/rust-lang/rust/pull/114499 is merged
portable-atomic = { version = "1.5", optional = true }
# arch-cortex-m dependencies
cortex-m = { version = "0.7.6", optional = true }

View File

@ -115,12 +115,12 @@ mod thread {
pub use interrupt::*;
#[cfg(feature = "executor-interrupt")]
mod interrupt {
use core::cell::UnsafeCell;
use core::cell::{Cell, UnsafeCell};
use core::mem::MaybeUninit;
use atomic_polyfill::{AtomicBool, Ordering};
use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC;
use critical_section::Mutex;
use crate::raw;
@ -146,7 +146,7 @@ mod interrupt {
/// It is somewhat more complex to use, it's recommended to use the thread-mode
/// [`Executor`] instead, if it works for your use case.
pub struct InterruptExecutor {
started: AtomicBool,
started: Mutex<Cell<bool>>,
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
}
@ -158,7 +158,7 @@ mod interrupt {
#[inline]
pub const fn new() -> Self {
Self {
started: AtomicBool::new(false),
started: Mutex::new(Cell::new(false)),
executor: UnsafeCell::new(MaybeUninit::uninit()),
}
}
@ -167,7 +167,8 @@ mod interrupt {
///
/// # Safety
///
/// You MUST call this from the interrupt handler, and from nowhere else.
/// - You MUST call this from the interrupt handler, and from nowhere else.
/// - You must not call this before calling `start()`.
pub unsafe fn on_interrupt(&'static self) {
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
executor.poll();
@ -196,11 +197,7 @@ mod interrupt {
/// do it after.
///
pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
if self
.started
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
if critical_section::with(|cs| self.started.borrow(cs).replace(true)) {
panic!("InterruptExecutor::start() called multiple times on the same executor.");
}
@ -222,10 +219,10 @@ mod interrupt {
/// This returns a [`SendSpawner`] you can use to spawn tasks on this
/// executor.
///
/// This MUST only be called on an executor that has already been spawned.
/// This MUST only be called on an executor that has already been started.
/// The function will panic otherwise.
pub fn spawner(&'static self) -> crate::SendSpawner {
if !self.started.load(Ordering::Acquire) {
if !critical_section::with(|cs| self.started.borrow(cs).get()) {
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
}
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };

View File

@ -6,10 +6,10 @@ pub use thread::*;
#[cfg(feature = "executor-thread")]
mod thread {
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main;
use portable_atomic::{AtomicBool, Ordering};
use crate::{raw, Spawner};

View File

@ -8,6 +8,9 @@ mod thread {
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main;
use crate::{raw, Spawner};
/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa

View File

@ -7,7 +7,15 @@
//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_macros::task) macro, which are fully safe.
#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")]
#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
mod run_queue;
#[cfg_attr(all(cortex_m, target_has_atomic = "8"), path = "state_atomics_arm.rs")]
#[cfg_attr(all(not(cortex_m), target_has_atomic = "8"), path = "state_atomics.rs")]
#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
mod state;
#[cfg(feature = "integrated-timers")]
mod timer_queue;
pub(crate) mod util;
@ -21,7 +29,6 @@ use core::pin::Pin;
use core::ptr::NonNull;
use core::task::{Context, Poll};
use atomic_polyfill::{AtomicU32, Ordering};
#[cfg(feature = "integrated-timers")]
use embassy_time::driver::{self, AlarmHandle};
#[cfg(feature = "integrated-timers")]
@ -30,21 +37,14 @@ use embassy_time::Instant;
use rtos_trace::trace;
use self::run_queue::{RunQueue, RunQueueItem};
use self::state::State;
use self::util::{SyncUnsafeCell, UninitCell};
pub use self::waker::task_from_waker;
use super::SpawnToken;
/// Task is spawned (has a future)
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
/// Task is in the executor run queue
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
/// Task is in the executor timer queue
#[cfg(feature = "integrated-timers")]
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
/// Raw task header for use in task pointers.
pub(crate) struct TaskHeader {
pub(crate) state: AtomicU32,
pub(crate) state: State,
pub(crate) run_queue_item: RunQueueItem,
pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>,
poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
@ -116,7 +116,7 @@ impl<F: Future + 'static> TaskStorage<F> {
pub const fn new() -> Self {
Self {
raw: TaskHeader {
state: AtomicU32::new(0),
state: State::new(),
run_queue_item: RunQueueItem::new(),
executor: SyncUnsafeCell::new(None),
// Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
@ -161,7 +161,7 @@ impl<F: Future + 'static> TaskStorage<F> {
match future.poll(&mut cx) {
Poll::Ready(_) => {
this.future.drop_in_place();
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
this.raw.state.despawn();
#[cfg(feature = "integrated-timers")]
this.raw.expires_at.set(Instant::MAX);
@ -193,11 +193,7 @@ impl<F: Future + 'static> AvailableTask<F> {
///
/// This function returns `None` if a task has already been spawned and has not finished running.
pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
task.raw
.state
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
.ok()
.map(|_| Self { task })
task.raw.state.spawn().then(|| Self { task })
}
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
@ -394,8 +390,7 @@ impl SyncExecutor {
#[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.state.run_dequeue() {
// 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.
@ -546,18 +541,7 @@ impl Executor {
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
pub fn wake_task(task: TaskRef) {
let header = task.header();
let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
// If already scheduled, or if not started,
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
None
} else {
// Mark it as scheduled
Some(state | STATE_RUN_QUEUED)
}
});
if res.is_ok() {
if header.state.run_enqueue() {
// We have just marked the task as scheduled, so enqueue it.
unsafe {
let executor = header.executor.get().unwrap_unchecked();
@ -571,18 +555,7 @@ pub fn wake_task(task: TaskRef) {
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
pub fn wake_task_no_pend(task: TaskRef) {
let header = task.header();
let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
// If already scheduled, or if not started,
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
None
} else {
// Mark it as scheduled
Some(state | STATE_RUN_QUEUED)
}
});
if res.is_ok() {
if header.state.run_enqueue() {
// We have just marked the task as scheduled, so enqueue it.
unsafe {
let executor = header.executor.get().unwrap_unchecked();

View File

@ -1,7 +1,6 @@
use core::ptr;
use core::ptr::NonNull;
use atomic_polyfill::{AtomicPtr, Ordering};
use core::sync::atomic::{AtomicPtr, Ordering};
use super::{TaskHeader, TaskRef};
use crate::raw::util::SyncUnsafeCell;

View File

@ -0,0 +1,75 @@
use core::cell::Cell;
use critical_section::{CriticalSection, Mutex};
use super::TaskRef;
pub(crate) struct RunQueueItem {
next: Mutex<Cell<Option<TaskRef>>>,
}
impl RunQueueItem {
pub const fn new() -> Self {
Self {
next: Mutex::new(Cell::new(None)),
}
}
}
/// Atomic task queue using a very, very simple lock-free linked-list queue:
///
/// To enqueue a task, task.next is set to the old head, and head is atomically set to task.
///
/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with
/// null. Then the batch is iterated following the next pointers until null is reached.
///
/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK
/// for our purposes: it can't create fairness problems since the next batch won't run until the
/// current batch is completely processed, so even if a task enqueues itself instantly (for example
/// by waking its own waker) can't prevent other tasks from running.
pub(crate) struct RunQueue {
head: Mutex<Cell<Option<TaskRef>>>,
}
impl RunQueue {
pub const fn new() -> Self {
Self {
head: Mutex::new(Cell::new(None)),
}
}
/// Enqueues an item. Returns true if the queue was empty.
///
/// # Safety
///
/// `item` must NOT be already enqueued in any queue.
#[inline(always)]
pub(crate) unsafe fn enqueue(&self, task: TaskRef) -> bool {
critical_section::with(|cs| {
let prev = self.head.borrow(cs).replace(Some(task));
task.header().run_queue_item.next.borrow(cs).set(prev);
prev.is_none()
})
}
/// Empty the queue, then call `on_task` for each task that was in the queue.
/// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
/// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
// Atomically empty the queue.
let mut next = critical_section::with(|cs| self.head.borrow(cs).take());
// Iterate the linked list of tasks that were previously in the queue.
while let Some(task) = next {
// If the task re-enqueues itself, the `next` pointer will get overwritten.
// Therefore, first read the next pointer, and only then process the task.
// safety: we know if the task is enqueued, no one else will touch the `next` pointer.
let cs = unsafe { CriticalSection::new() };
next = task.header().run_queue_item.next.borrow(cs).get();
on_task(task);
}
}
}

View File

@ -0,0 +1,73 @@
use core::sync::atomic::{AtomicU32, Ordering};
/// Task is spawned (has a future)
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
/// Task is in the executor run queue
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
/// Task is in the executor timer queue
#[cfg(feature = "integrated-timers")]
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
pub(crate) struct State {
state: AtomicU32,
}
impl State {
pub const fn new() -> State {
Self {
state: AtomicU32::new(0),
}
}
/// If task is idle, mark it as spawned + run_queued and return true.
#[inline(always)]
pub fn spawn(&self) -> bool {
self.state
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
}
/// Unmark the task as spawned.
#[inline(always)]
pub fn despawn(&self) {
self.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
}
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
#[inline(always)]
pub fn run_enqueue(&self) -> bool {
self.state
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
// If already scheduled, or if not started,
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
None
} else {
// Mark it as scheduled
Some(state | STATE_RUN_QUEUED)
}
})
.is_ok()
}
/// Unmark the task as run-queued. Return whether the task is spawned.
#[inline(always)]
pub fn run_dequeue(&self) -> bool {
let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
state & STATE_SPAWNED != 0
}
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
#[cfg(feature = "integrated-timers")]
#[inline(always)]
pub fn timer_enqueue(&self) -> bool {
let old_state = self.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
old_state & STATE_TIMER_QUEUED == 0
}
/// Unmark the task as timer-queued.
#[cfg(feature = "integrated-timers")]
#[inline(always)]
pub fn timer_dequeue(&self) {
self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel);
}
}

View File

@ -0,0 +1,103 @@
use core::arch::asm;
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
// Must be kept in sync with the layout of `State`!
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8;
#[repr(C, align(4))]
pub(crate) struct State {
/// Task is spawned (has a future)
spawned: AtomicBool,
/// Task is in the executor run queue
run_queued: AtomicBool,
/// Task is in the executor timer queue
timer_queued: AtomicBool,
pad: AtomicBool,
}
impl State {
pub const fn new() -> State {
Self {
spawned: AtomicBool::new(false),
run_queued: AtomicBool::new(false),
timer_queued: AtomicBool::new(false),
pad: AtomicBool::new(false),
}
}
fn as_u32(&self) -> &AtomicU32 {
unsafe { &*(self as *const _ as *const AtomicU32) }
}
/// If task is idle, mark it as spawned + run_queued and return true.
#[inline(always)]
pub fn spawn(&self) -> bool {
compiler_fence(Ordering::Release);
let r = self
.as_u32()
.compare_exchange(
0,
STATE_SPAWNED | STATE_RUN_QUEUED,
Ordering::Relaxed,
Ordering::Relaxed,
)
.is_ok();
compiler_fence(Ordering::Acquire);
r
}
/// Unmark the task as spawned.
#[inline(always)]
pub fn despawn(&self) {
compiler_fence(Ordering::Release);
self.spawned.store(false, Ordering::Relaxed);
}
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
#[inline(always)]
pub fn run_enqueue(&self) -> bool {
unsafe {
loop {
let state: u32;
asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack));
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
asm!("clrex", options(nomem, nostack));
return false;
}
let outcome: usize;
let new_state = state | STATE_RUN_QUEUED;
asm!("strex {}, {}, [{}]", out(reg) outcome, in(reg) new_state, in(reg) self, options(nostack));
if outcome == 0 {
return true;
}
}
}
}
/// Unmark the task as run-queued. Return whether the task is spawned.
#[inline(always)]
pub fn run_dequeue(&self) -> bool {
compiler_fence(Ordering::Release);
let r = self.spawned.load(Ordering::Relaxed);
self.run_queued.store(false, Ordering::Relaxed);
r
}
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
#[cfg(feature = "integrated-timers")]
#[inline(always)]
pub fn timer_enqueue(&self) -> bool {
!self.timer_queued.swap(true, Ordering::Relaxed)
}
/// Unmark the task as timer-queued.
#[cfg(feature = "integrated-timers")]
#[inline(always)]
pub fn timer_dequeue(&self) {
self.timer_queued.store(false, Ordering::Relaxed);
}
}

View File

@ -0,0 +1,93 @@
use core::cell::Cell;
use critical_section::Mutex;
/// Task is spawned (has a future)
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
/// Task is in the executor run queue
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
/// Task is in the executor timer queue
#[cfg(feature = "integrated-timers")]
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
pub(crate) struct State {
state: Mutex<Cell<u32>>,
}
impl State {
pub const fn new() -> State {
Self {
state: Mutex::new(Cell::new(0)),
}
}
fn update<R>(&self, f: impl FnOnce(&mut u32) -> R) -> R {
critical_section::with(|cs| {
let s = self.state.borrow(cs);
let mut val = s.get();
let r = f(&mut val);
s.set(val);
r
})
}
/// If task is idle, mark it as spawned + run_queued and return true.
#[inline(always)]
pub fn spawn(&self) -> bool {
self.update(|s| {
if *s == 0 {
*s = STATE_SPAWNED | STATE_RUN_QUEUED;
true
} else {
false
}
})
}
/// Unmark the task as spawned.
#[inline(always)]
pub fn despawn(&self) {
self.update(|s| *s &= !STATE_SPAWNED);
}
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
#[inline(always)]
pub fn run_enqueue(&self) -> bool {
self.update(|s| {
if (*s & STATE_RUN_QUEUED != 0) || (*s & STATE_SPAWNED == 0) {
false
} else {
*s |= STATE_RUN_QUEUED;
true
}
})
}
/// Unmark the task as run-queued. Return whether the task is spawned.
#[inline(always)]
pub fn run_dequeue(&self) -> bool {
self.update(|s| {
let ok = *s & STATE_SPAWNED != 0;
*s &= !STATE_RUN_QUEUED;
ok
})
}
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
#[cfg(feature = "integrated-timers")]
#[inline(always)]
pub fn timer_enqueue(&self) -> bool {
self.update(|s| {
let ok = *s & STATE_TIMER_QUEUED == 0;
*s |= STATE_TIMER_QUEUED;
ok
})
}
/// Unmark the task as timer-queued.
#[cfg(feature = "integrated-timers")]
#[inline(always)]
pub fn timer_dequeue(&self) {
self.update(|s| *s &= !STATE_TIMER_QUEUED);
}
}

View File

@ -1,9 +1,8 @@
use core::cmp::min;
use atomic_polyfill::Ordering;
use embassy_time::Instant;
use super::{TaskRef, STATE_TIMER_QUEUED};
use super::TaskRef;
use crate::raw::util::SyncUnsafeCell;
pub(crate) struct TimerQueueItem {
@ -32,10 +31,7 @@ impl TimerQueue {
pub(crate) unsafe fn update(&self, p: TaskRef) {
let task = p.header();
if task.expires_at.get() != Instant::MAX {
let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
let is_new = old_state & STATE_TIMER_QUEUED == 0;
if is_new {
if task.state.timer_enqueue() {
task.timer_queue_item.next.set(self.head.get());
self.head.set(Some(p));
}
@ -75,7 +71,7 @@ impl TimerQueue {
} else {
// Remove it
prev.set(task.timer_queue_item.next.get());
task.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel);
task.state.timer_dequeue();
}
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-futures"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
description = "no-std, no-alloc utilities for working with futures"
repository = "https://github.com/embassy-rs/embassy"

View File

@ -10,7 +10,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
heapless = "0.7.16"
heapless = "0.8"
defmt = { version = "0.3", optional = true }
log = { version = "0.4", default-features = false, optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }

View File

@ -15,9 +15,9 @@ embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
noproto = { git="https://github.com/embassy-rs/noproto", rev = "f5e6d1f325b6ad4e344f60452b09576e24671f62", default-features = false, features = ["derive"] }
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
heapless = "0.7.16"
heapless = "0.8"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"

View File

@ -97,8 +97,8 @@ impl<'a> Control<'a> {
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
let req = proto::CtrlMsgReqConnectAp {
ssid: String::from(ssid),
pwd: String::from(password),
ssid: unwrap!(String::try_from(ssid)),
pwd: unwrap!(String::try_from(password)),
bssid: String::new(),
listen_interval: 3,
is_wpa3_supported: false,

View File

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
- Avoid never resolving `TcpIo::read` when the output buffer is empty.
- Update to `smoltcp` git.
- Forward constants from `smoltcp` in DNS query results so changing DNS result size in `smoltcp` properly propagates.
## 0.2.1 - 2023-10-31
- Re-add impl_trait_projections

View File

@ -25,7 +25,7 @@ features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "me
default = []
std = []
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"]
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "heapless/defmt-03"]
nightly = ["dep:embedded-io-async", "dep:embedded-nal-async"]
@ -46,7 +46,7 @@ igmp = ["smoltcp/proto-igmp"]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
smoltcp = { version = "0.10.0", default-features = false, features = [
smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp.git", rev = "b57e2f9e70e82a13f31d5ea17e55232c11cc2b2d", default-features = false, features = [
"socket",
"async",
] }
@ -57,10 +57,10 @@ embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
embedded-io-async = { version = "0.6.0", optional = true }
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
heapless = { version = "0.7.5", default-features = false }
heapless = { version = "0.8", default-features = false }
as-slice = "0.2.1"
generic-array = { version = "0.14.4", default-features = false }
stable_deref_trait = { version = "1.2.0", default-features = false }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
atomic-pool = "1.0"
embedded-nal-async = { version = "0.6.0", optional = true }
embedded-nal-async = { version = "0.7", optional = true }

View File

@ -4,7 +4,7 @@
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience.
memory management designed to work well for embedded systems, aiming for a more "Just Works" experience.
## Features

View File

@ -63,7 +63,11 @@ where
}
/// Make a query for a given name and return the corresponding IP addresses.
pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result<Vec<IpAddress, 1>, Error> {
pub async fn query(
&self,
name: &str,
qtype: DnsQueryType,
) -> Result<Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, Error> {
self.stack.dns_query(name, qtype).await
}
}
@ -101,7 +105,8 @@ where
async fn get_host_by_address(
&self,
_addr: embedded_nal_async::IpAddr,
) -> Result<heapless::String<256>, Self::Error> {
_result: &mut [u8],
) -> Result<usize, Self::Error> {
todo!()
}
}

View File

@ -494,7 +494,11 @@ impl<D: Driver> Stack<D> {
/// Make a query for a given name and return the corresponding IP addresses.
#[cfg(feature = "dns")]
pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result<Vec<IpAddress, 1>, dns::Error> {
pub async fn dns_query(
&self,
name: &str,
qtype: dns::DnsQueryType,
) -> Result<Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, dns::Error> {
// For A and AAAA queries we try detect whether `name` is just an IP address
match qtype {
#[cfg(feature = "proto-ipv4")]

View File

@ -390,6 +390,13 @@ impl<'d> TcpIo<'d> {
// CAUTION: smoltcp semantics around EOF are different to what you'd expect
// from posix-like IO, so we have to tweak things here.
self.with_mut(|s, _| match s.recv_slice(buf) {
// Reading into empty buffer
Ok(0) if buf.is_empty() => {
// embedded_io_async::Read's contract is to not block if buf is empty. While
// this function is not a direct implementor of the trait method, we still don't
// want our future to never resolve.
Poll::Ready(Ok(0))
}
// No data ready
Ok(0) => {
s.register_recv_waker(cx.waker());
@ -611,10 +618,7 @@ pub mod client {
async fn connect<'a>(
&'a self,
remote: embedded_nal_async::SocketAddr,
) -> Result<Self::Connection<'a>, Self::Error>
where
Self: 'a,
{
) -> Result<Self::Connection<'a>, Self::Error> {
let addr: crate::IpAddress = match remote.ip() {
#[cfg(feature = "proto-ipv4")]
IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())),

View File

@ -12,7 +12,7 @@ use core::cmp::min;
use core::future::poll_fn;
use core::marker::PhantomData;
use core::slice;
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, AtomicUsize, Ordering};
use core::task::Poll;
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
@ -41,7 +41,9 @@ mod sealed {
pub rx_waker: AtomicWaker,
pub rx_buf: RingBuffer,
pub rx_bufs: AtomicU8,
pub rx_started: AtomicBool,
pub rx_started_count: AtomicU8,
pub rx_ended_count: AtomicU8,
pub rx_ppi_ch: AtomicU8,
}
}
@ -65,7 +67,9 @@ impl State {
rx_waker: AtomicWaker::new(),
rx_buf: RingBuffer::new(),
rx_bufs: AtomicU8::new(0),
rx_started: AtomicBool::new(false),
rx_started_count: AtomicU8::new(0),
rx_ended_count: AtomicU8::new(0),
rx_ppi_ch: AtomicU8::new(0),
}
}
@ -104,28 +108,20 @@ impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for Interrupt
s.rx_waker.wake();
}
// If not RXing, start.
if s.rx_bufs.load(Ordering::Relaxed) == 0 {
let (ptr, len) = rx.push_buf();
if len >= half_len {
//trace!(" irq_rx: starting {:?}", half_len);
s.rx_bufs.store(1, Ordering::Relaxed);
if r.events_endrx.read().bits() != 0 {
//trace!(" irq_rx: endrx");
r.events_endrx.reset();
// Set up the DMA read
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
// Start UARTE Receive transaction
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
rx.push_done(half_len);
r.intenset.write(|w| w.rxstarted().set());
}
let val = s.rx_ended_count.load(Ordering::Relaxed);
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
}
if r.events_rxstarted.read().bits() != 0 {
if r.events_rxstarted.read().bits() != 0 || !s.rx_started.load(Ordering::Relaxed) {
//trace!(" irq_rx: rxstarted");
let (ptr, len) = rx.push_buf();
if len >= half_len {
r.events_rxstarted.reset();
//trace!(" irq_rx: starting second {:?}", half_len);
// Set up the DMA read
@ -134,11 +130,50 @@ impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for Interrupt
let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
// Enable endrx -> startrx PPI channel.
// From this point on, if endrx happens, startrx is automatically fired.
ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
// It is possible that endrx happened BEFORE enabling the PPI. In this case
// the PPI channel doesn't trigger, and we'd hang. We have to detect this
// and manually start.
// check again in case endrx has happened between the last check and now.
if r.events_endrx.read().bits() != 0 {
//trace!(" irq_rx: endrx");
r.events_endrx.reset();
let val = s.rx_ended_count.load(Ordering::Relaxed);
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
}
let rx_ended = s.rx_ended_count.load(Ordering::Relaxed);
let rx_started = s.rx_started_count.load(Ordering::Relaxed);
// If we started the same amount of transfers as ended, the last rxend has
// already occured.
let rxend_happened = rx_started == rx_ended;
// Check if the PPI channel is still enabled. The PPI channel disables itself
// when it fires, so if it's still enabled it hasn't fired.
let ppi_ch_enabled = ppi::regs().chen.read().bits() & (1 << chn) != 0;
// if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed.
// this condition also naturally matches if `!started`, needed to kickstart the DMA.
if rxend_happened && ppi_ch_enabled {
//trace!("manually starting.");
// disable the ppi ch, it's of no use anymore.
ppi::regs().chenclr.write(|w| unsafe { w.bits(1 << chn) });
// manually start
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
}
rx.push_done(half_len);
r.events_rxstarted.reset();
s.rx_started_count.store(rx_started.wrapping_add(1), Ordering::Relaxed);
s.rx_started.store(true, Ordering::Relaxed);
} else {
//trace!(" irq_rx: rxstarted no buf");
r.intenclr.write(|w| w.rxstarted().clear());
@ -282,6 +317,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
let r = U::regs();
let hwfc = cts.is_some();
rxd.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
@ -303,7 +340,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
// Initialize state
let s = U::buffered_state();
s.tx_count.store(0, Ordering::Relaxed);
s.rx_bufs.store(0, Ordering::Relaxed);
s.rx_started_count.store(0, Ordering::Relaxed);
s.rx_ended_count.store(0, Ordering::Relaxed);
let len = tx_buffer.len();
unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
let len = rx_buffer.len();
@ -311,7 +349,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
// Configure
r.config.write(|w| {
w.hwfc().bit(false);
w.hwfc().bit(hwfc);
w.parity().variant(config.parity);
w
});
@ -333,6 +371,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
w.endtx().set();
w.rxstarted().set();
w.error().set();
w.endrx().set();
w
});

View File

@ -94,5 +94,5 @@ pio = {version= "0.2.1" }
rp2040-boot2 = "0.3"
[dev-dependencies]
embassy-executor = { version = "0.3.1", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] }
embassy-executor = { version = "0.3.3", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] }
static_cell = { version = "2" }

View File

@ -21,7 +21,7 @@ embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", option
defmt = { version = "0.3", optional = true }
cortex-m = "0.7.6"
heapless = "0.7.16"
heapless = "0.8"
aligned = "0.4.1"
bit_field = "0.10.2"

View File

@ -18,7 +18,7 @@ flavors = [
{ regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" },
{ regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
{ regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" },
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] },
{ regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" },
{ regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
{ regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] },
@ -39,7 +39,7 @@ embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", fea
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
embassy-executor = { version = "0.3.1", path = "../embassy-executor", optional = true }
embassy-executor = { version = "0.3.3", path = "../embassy-executor", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
@ -58,7 +58,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-73b8c37ae74fc28b247188c989fd99400611bd6b" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
vcell = "0.1.3"
bxcan = "0.7.0"
nb = "1.0.0"
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies]
proc-macro2 = "1.0.36"
quote = "1.0.15"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-73b8c37ae74fc28b247188c989fd99400611bd6b", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]}
[features]

View File

@ -6,7 +6,7 @@ use std::{env, fs};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet};
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, METADATA};
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, StopMode, METADATA};
fn main() {
let target = env::var("TARGET").unwrap();
@ -557,18 +557,18 @@ fn main() {
};
/*
If LP and non-LP peripherals share the same RCC enable bit, then a refcount leak will result.
A refcount leak can result if the same field is shared by peripherals with different stop modes
This should be checked in stm32-data-gen.
This condition should be checked in stm32-data
*/
let stop_refcount = if p.name.starts_with("LP") {
quote! { REFCOUNT_STOP2 }
} else {
quote! { REFCOUNT_STOP1 }
let stop_refcount = match rcc.stop_mode {
StopMode::Standby => None,
StopMode::Stop2 => Some(quote! { REFCOUNT_STOP2 }),
StopMode::Stop1 => Some(quote! { REFCOUNT_STOP1 }),
};
let (incr_stop_refcount, decr_stop_refcount) = if p.name != "RTC" {
(
let (incr_stop_refcount, decr_stop_refcount) = match stop_refcount {
Some(stop_refcount) => (
quote! {
#[cfg(feature = "low-power")]
unsafe { crate::rcc::#stop_refcount += 1 };
@ -577,9 +577,8 @@ fn main() {
#[cfg(feature = "low-power")]
unsafe { crate::rcc::#stop_refcount -= 1 };
},
)
} else {
(quote! {}, quote! {})
),
None => (TokenStream::new(), TokenStream::new()),
};
g.extend(quote! {
@ -828,7 +827,7 @@ fn main() {
(("fmc", "NCE"), quote!(crate::fmc::NCEPin)),
(("fmc", "NOE"), quote!(crate::fmc::NOEPin)),
(("fmc", "NWE"), quote!(crate::fmc::NWEPin)),
(("fmc", "Clk"), quote!(crate::fmc::ClkPin)),
(("fmc", "CLK"), quote!(crate::fmc::ClkPin)),
(("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
(("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
(("timer", "CH1"), quote!(crate::timer::Channel1Pin)),
@ -944,17 +943,23 @@ fn main() {
}
if regs.kind == "opamp" {
if !pin.signal.starts_with("VP") {
continue;
if pin.signal.starts_with("VP") {
// Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc)
let peri = format_ident!("{}", p.name);
let pin_name = format_ident!("{}", pin.pin);
let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap();
g.extend(quote! {
impl_opamp_vp_pin!( #peri, #pin_name, #ch);
})
} else if pin.signal == "VOUT" {
// Impl OutputPin for the VOUT pin
let peri = format_ident!("{}", p.name);
let pin_name = format_ident!("{}", pin.pin);
g.extend(quote! {
impl_opamp_vout_pin!( #peri, #pin_name );
})
}
let peri = format_ident!("{}", p.name);
let pin_name = format_ident!("{}", pin.pin);
let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap();
g.extend(quote! {
impl_opamp_pin!( #peri, #pin_name, #ch);
})
}
// DAC is special

View File

@ -299,19 +299,15 @@ impl<'a, C: Channel> Transfer<'a, C> {
pub fn request_stop(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
// Disable the channel. Keep the IEs enabled so the irqs still fire.
ch.cr().write(|w| {
w.set_tcie(true);
w.set_useie(true);
w.set_dteie(true);
w.set_suspie(true);
ch.cr().modify(|w| {
w.set_susp(true);
})
}
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().ch(self.channel.num());
!ch.sr().read().tcf()
let sr = ch.sr().read();
!sr.tcf() && !sr.suspf()
}
/// Gets the total remaining transfers for the channel

View File

@ -119,13 +119,11 @@ impl<'a> TDesRing<'a> {
// "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::Release);
self.index = self.index + 1;
if self.index == self.descriptors.len() {
self.index = 0;
}
// signal DMA it can try again.
ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0)
// See issue #2129
ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = &td as *const _ as u32);
self.index = (self.index + 1) % self.descriptors.len();
}
}
@ -237,21 +235,19 @@ impl<'a> RDesRing<'a> {
/// Pop the packet previously returned by `available`.
pub(crate) fn pop_packet(&mut self) {
let descriptor = &mut self.descriptors[self.index];
assert!(descriptor.available());
let rd = &mut self.descriptors[self.index];
assert!(rd.available());
self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr());
rd.set_ready(self.buffers[self.index].0.as_mut_ptr());
// "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::Release);
// signal DMA it can try again.
ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0);
// See issue #2129
ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = &rd as *const _ as u32);
// Increment index.
self.index += 1;
if self.index == self.descriptors.len() {
self.index = 0
}
self.index = (self.index + 1) % self.descriptors.len();
}
}

View File

@ -12,6 +12,37 @@ pub struct Fmc<'d, T: Instance> {
unsafe impl<'d, T> Send for Fmc<'d, T> where T: Instance {}
impl<'d, T> Fmc<'d, T>
where
T: Instance,
{
/// Create a raw FMC instance.
///
/// **Note:** This is currently used to provide access to some basic FMC functions
/// for manual configuration for memory types that stm32-fmc does not support.
pub fn new_raw(_instance: impl Peripheral<P = T> + 'd) -> Self {
Self { peri: PhantomData }
}
/// Enable the FMC peripheral and reset it.
pub fn enable(&mut self) {
T::enable_and_reset();
}
/// Enable the memory controller on applicable chips.
pub fn memory_controller_enable(&mut self) {
// fmc v1 and v2 does not have the fmcen bit
// fsmc v1, v2 and v3 does not have the fmcen bit
// This is a "not" because it is expected that all future versions have this bit
#[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))]
T::REGS.bcr1().modify(|r| r.set_fmcen(true));
}
pub fn source_clock_hz(&self) -> u32 {
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
}
}
unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T>
where
T: Instance,

View File

@ -13,21 +13,50 @@ pub enum OpAmpGain {
Mul16,
}
pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin<T>> {
_inner: &'d OpAmp<'d, T>,
_input: &'p mut P,
#[derive(Clone, Copy)]
pub enum OpAmpSpeed {
Normal,
HighSpeed,
}
#[cfg(opamp_g4)]
impl From<OpAmpSpeed> for crate::pac::opamp::vals::OpampCsrOpahsm {
fn from(v: OpAmpSpeed) -> Self {
match v {
OpAmpSpeed::Normal => crate::pac::opamp::vals::OpampCsrOpahsm::NORMAL,
OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::OpampCsrOpahsm::HIGHSPEED,
}
}
}
/// OpAmp external outputs, wired to a GPIO pad.
///
/// The GPIO output pad is held by this struct to ensure it cannot be used elsewhere.
///
/// This struct can also be used as an ADC input.
pub struct OpAmpOutput<'d, 'p, T: Instance, P: OutputPin<T>> {
_inner: &'d OpAmp<'d, T>,
_output: &'p mut P,
}
/// OpAmp internal outputs, wired directly to ADC inputs.
///
/// This struct can be used as an ADC input.
pub struct OpAmpInternalOutput<'d, T: Instance> {
_inner: &'d OpAmp<'d, T>,
}
/// OpAmp driver.
pub struct OpAmp<'d, T: Instance> {
_inner: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> OpAmp<'d, T> {
pub fn new(opamp: impl Peripheral<P = T> + 'd) -> Self {
Self::new_inner(opamp)
}
fn new_inner(opamp: impl Peripheral<P = T> + 'd) -> Self {
/// Create a new driver instance.
///
/// Enables the OpAmp and configures the speed, but
/// does not set any other configuration.
pub fn new(opamp: impl Peripheral<P = T> + 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self {
into_ref!(opamp);
#[cfg(opamp_f3)]
@ -38,15 +67,34 @@ impl<'d, T: Instance> OpAmp<'d, T> {
#[cfg(opamp_g4)]
T::regs().opamp_csr().modify(|w| {
w.set_opaen(true);
w.set_opahsm(speed.into());
});
Self { _inner: opamp }
}
pub fn buffer_for<'a, 'b, P>(&'a mut self, pin: &'b mut P, gain: OpAmpGain) -> OpAmpOutput<'a, 'b, T, P>
/// Configure the OpAmp as a buffer for the provided input pin,
/// outputting to the provided output pin.
///
/// The input pin is configured for analogue mode but not consumed,
/// so it may subsequently be used for ADC or comparator inputs.
///
/// The output pin is held within the returned [`OpAmpOutput`] struct,
/// preventing it being used elsewhere. The `OpAmpOutput` can then be
/// directly used as an ADC input.
pub fn buffer_ext<'a, 'b, IP, OP>(
&'a mut self,
in_pin: &IP,
out_pin: &'b mut OP,
gain: OpAmpGain,
) -> OpAmpOutput<'a, 'b, T, OP>
where
P: NonInvertingPin<T>,
IP: NonInvertingPin<T> + crate::gpio::sealed::Pin,
OP: OutputPin<T> + crate::gpio::sealed::Pin,
{
in_pin.set_as_analog();
out_pin.set_as_analog();
let (vm_sel, pga_gain) = match gain {
OpAmpGain::Mul1 => (0b11, 0b00),
OpAmpGain::Mul2 => (0b10, 0b00),
@ -57,25 +105,76 @@ impl<'d, T: Instance> OpAmp<'d, T> {
#[cfg(opamp_f3)]
T::regs().opampcsr().modify(|w| {
w.set_vp_sel(pin.channel());
w.set_vp_sel(in_pin.channel());
w.set_vm_sel(vm_sel);
w.set_pga_gain(pga_gain);
w.set_opampen(true);
});
#[cfg(opamp_g4)]
T::regs().opamp_csr().modify(|w| {
use crate::pac::opamp::vals::*;
w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel()));
w.set_vp_sel(OpampCsrVpSel::from_bits(in_pin.channel()));
w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel));
w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain));
w.set_opaintoen(OpampCsrOpaintoen::OUTPUTPIN);
w.set_opaen(true);
});
OpAmpOutput {
_inner: self,
_input: pin,
_output: out_pin,
}
}
/// Configure the OpAmp as a buffer for the provided input pin,
/// with the output only used internally.
///
/// The input pin is configured for analogue mode but not consumed,
/// so it may be subsequently used for ADC or comparator inputs.
///
/// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
#[cfg(opamp_g4)]
pub fn buffer_int<'a, P>(&'a mut self, pin: &P, gain: OpAmpGain) -> OpAmpInternalOutput<'a, T>
where
P: NonInvertingPin<T> + crate::gpio::sealed::Pin,
{
pin.set_as_analog();
let (vm_sel, pga_gain) = match gain {
OpAmpGain::Mul1 => (0b11, 0b00),
OpAmpGain::Mul2 => (0b10, 0b00),
OpAmpGain::Mul4 => (0b10, 0b01),
OpAmpGain::Mul8 => (0b10, 0b10),
OpAmpGain::Mul16 => (0b10, 0b11),
};
T::regs().opamp_csr().modify(|w| {
use crate::pac::opamp::vals::*;
w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel()));
w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel));
w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain));
w.set_opaintoen(OpampCsrOpaintoen::ADCCHANNEL);
w.set_opaen(true);
});
OpAmpInternalOutput { _inner: self }
}
}
impl<'d, T: Instance> Drop for OpAmp<'d, T> {
fn drop(&mut self) {
#[cfg(opamp_f3)]
T::regs().opampcsr().modify(|w| {
w.set_opampen(false);
});
#[cfg(opamp_g4)]
T::regs().opamp_csr().modify(|w| {
w.set_opaen(false);
});
}
}
pub trait Instance: sealed::Instance + 'static {}
@ -92,18 +191,19 @@ pub(crate) mod sealed {
pub trait InvertingPin<T: Instance> {
fn channel(&self) -> u8;
}
pub trait OutputPin<T: Instance> {}
}
pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {}
pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
pub trait OutputPin<T: Instance>: sealed::OutputPin<T> {}
#[cfg(opamp_f3)]
macro_rules! impl_opamp_output {
macro_rules! impl_opamp_external_output {
($inst:ident, $adc:ident, $ch:expr) => {
foreach_adc!(
($adc, $common_inst:ident, $adc_clock:ident) => {
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
{
fn channel(&self) -> u8 {
@ -111,7 +211,7 @@ macro_rules! impl_opamp_output {
}
}
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
{
}
@ -120,19 +220,79 @@ macro_rules! impl_opamp_output {
};
}
#[cfg(opamp_f3)]
foreach_peripheral!(
(opamp, OPAMP1) => {
impl_opamp_output!(OPAMP1, ADC1, 3);
impl_opamp_external_output!(OPAMP1, ADC1, 3);
};
(opamp, OPAMP2) => {
impl_opamp_output!(OPAMP2, ADC2, 3);
impl_opamp_external_output!(OPAMP2, ADC2, 3);
};
(opamp, OPAMP3) => {
impl_opamp_output!(OPAMP3, ADC3, 1);
impl_opamp_external_output!(OPAMP3, ADC3, 1);
};
// OPAMP4 only in STM32G4 Cat 3 devices
(opamp, OPAMP4) => {
impl_opamp_output!(OPAMP4, ADC4, 3);
impl_opamp_external_output!(OPAMP4, ADC4, 3);
};
// OPAMP5 only in STM32G4 Cat 3 devices
(opamp, OPAMP5) => {
impl_opamp_external_output!(OPAMP5, ADC5, 1);
};
// OPAMP6 only in STM32G4 Cat 3/4 devices
(opamp, OPAMP6) => {
impl_opamp_external_output!(OPAMP6, ADC1, 14);
};
);
#[cfg(opamp_g4)]
macro_rules! impl_opamp_internal_output {
($inst:ident, $adc:ident, $ch:expr) => {
foreach_adc!(
($adc, $common_inst:ident, $adc_clock:ident) => {
impl<'d> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
for OpAmpInternalOutput<'d, crate::peripherals::$inst>
{
fn channel(&self) -> u8 {
$ch
}
}
impl<'d> crate::adc::AdcPin<crate::peripherals::$adc>
for OpAmpInternalOutput<'d, crate::peripherals::$inst>
{
}
};
);
};
}
#[cfg(opamp_g4)]
foreach_peripheral!(
(opamp, OPAMP1) => {
impl_opamp_internal_output!(OPAMP1, ADC1, 13);
};
(opamp, OPAMP2) => {
impl_opamp_internal_output!(OPAMP2, ADC2, 16);
};
(opamp, OPAMP3) => {
impl_opamp_internal_output!(OPAMP3, ADC2, 18);
// Only in Cat 3/4 devices
impl_opamp_internal_output!(OPAMP3, ADC3, 13);
};
// OPAMP4 only in Cat 3 devices
(opamp, OPAMP4) => {
impl_opamp_internal_output!(OPAMP4, ADC5, 5);
};
// OPAMP5 only in Cat 3 devices
(opamp, OPAMP5) => {
impl_opamp_internal_output!(OPAMP5, ADC5, 3);
};
// OPAMP6 only in Cat 3/4 devices
(opamp, OPAMP6) => {
// Only in Cat 3 devices
impl_opamp_internal_output!(OPAMP6, ADC4, 17);
// Only in Cat 4 devices
impl_opamp_internal_output!(OPAMP6, ADC3, 17);
};
);
@ -145,13 +305,12 @@ foreach_peripheral! {
}
impl Instance for crate::peripherals::$inst {
}
};
}
#[allow(unused_macros)]
macro_rules! impl_opamp_pin {
macro_rules! impl_opamp_vp_pin {
($inst:ident, $pin:ident, $ch:expr) => {
impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {}
impl crate::opamp::sealed::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {
@ -161,3 +320,11 @@ macro_rules! impl_opamp_pin {
}
};
}
#[allow(unused_macros)]
macro_rules! impl_opamp_vout_pin {
($inst:ident, $pin:ident) => {
impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}
impl crate::opamp::sealed::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}
};
}

View File

@ -1,7 +1,11 @@
use stm32_metapac::flash::vals::Latency;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource,
Ppre as APBPrescaler, Sw as Sysclk,
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,
Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
};
#[cfg(any(stm32f4, stm32f7))]
use crate::pac::PWR;
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
@ -48,11 +52,27 @@ pub struct Pll {
pub mul: PllMul,
/// PLL P division factor. If None, PLL P output is disabled.
pub divp: Option<Pllp>,
pub divp: Option<PllPDiv>,
/// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<Pllq>,
pub divq: Option<PllQDiv>,
/// PLL R division factor. If None, PLL R output is disabled.
pub divr: Option<Pllr>,
pub divr: Option<PllRDiv>,
}
/// Voltage range of the power supply used.
///
/// Used to calculate flash waitstates. See
/// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency
#[cfg(stm32f2)]
pub enum VoltageScale {
/// 2.7 to 3.6 V
Range0,
/// 2.4 to 2.7 V
Range1,
/// 2.1 to 2.4 V
Range2,
/// 1.8 to 2.1 V
Range3,
}
/// Configuration of the core clocks
@ -65,7 +85,7 @@ pub struct Config {
pub pll_src: PllSource,
pub pll: Option<Pll>,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
pub plli2s: Option<Pll>,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
pub pllsai: Option<Pll>,
@ -75,6 +95,9 @@ pub struct Config {
pub apb2_pre: APBPrescaler,
pub ls: super::LsConfig,
#[cfg(stm32f2)]
pub voltage: VoltageScale,
}
impl Default for Config {
@ -85,7 +108,7 @@ impl Default for Config {
sys: Sysclk::HSI,
pll_src: PllSource::HSI,
pll: None,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
plli2s: None,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
pllsai: None,
@ -95,17 +118,24 @@ impl Default for Config {
apb2_pre: APBPrescaler::DIV1,
ls: Default::default(),
#[cfg(stm32f2)]
voltage: VoltageScale::Range3,
}
}
}
pub(crate) unsafe fn init(config: Config) {
// set VOS to SCALE1, if use PLL
// TODO: check real clock speed before set VOS
#[cfg(any(stm32f4, stm32f7))]
if config.pll.is_some() {
PWR.cr1().modify(|w| w.set_vos(crate::pac::pwr::vals::Vos::SCALE1));
}
// always enable overdrive for now. Make it configurable in the future.
#[cfg(not(any(
stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417
)))]
#[cfg(any(stm32f446, stm32f4x9, stm32f427, stm32f437, stm32f7))]
{
use crate::pac::PWR;
PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {}
@ -152,7 +182,7 @@ pub(crate) unsafe fn init(config: Config) {
source: config.pll_src,
};
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input);
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input);
@ -176,7 +206,48 @@ pub(crate) unsafe fn init(config: Config) {
let rtc = config.ls.init();
flash_setup(hclk);
#[cfg(stm32f2)]
let latency = match (config.voltage, hclk.0) {
(VoltageScale::Range3, ..=16_000_000) => Latency::WS0,
(VoltageScale::Range3, ..=32_000_000) => Latency::WS1,
(VoltageScale::Range3, ..=48_000_000) => Latency::WS2,
(VoltageScale::Range3, ..=64_000_000) => Latency::WS3,
(VoltageScale::Range3, ..=80_000_000) => Latency::WS4,
(VoltageScale::Range3, ..=96_000_000) => Latency::WS5,
(VoltageScale::Range3, ..=112_000_000) => Latency::WS6,
(VoltageScale::Range3, ..=120_000_000) => Latency::WS7,
(VoltageScale::Range2, ..=18_000_000) => Latency::WS0,
(VoltageScale::Range2, ..=36_000_000) => Latency::WS1,
(VoltageScale::Range2, ..=54_000_000) => Latency::WS2,
(VoltageScale::Range2, ..=72_000_000) => Latency::WS3,
(VoltageScale::Range2, ..=90_000_000) => Latency::WS4,
(VoltageScale::Range2, ..=108_000_000) => Latency::WS5,
(VoltageScale::Range2, ..=120_000_000) => Latency::WS6,
(VoltageScale::Range1, ..=24_000_000) => Latency::WS0,
(VoltageScale::Range1, ..=48_000_000) => Latency::WS1,
(VoltageScale::Range1, ..=72_000_000) => Latency::WS2,
(VoltageScale::Range1, ..=96_000_000) => Latency::WS3,
(VoltageScale::Range1, ..=120_000_000) => Latency::WS4,
(VoltageScale::Range0, ..=30_000_000) => Latency::WS0,
(VoltageScale::Range0, ..=60_000_000) => Latency::WS1,
(VoltageScale::Range0, ..=90_000_000) => Latency::WS2,
(VoltageScale::Range0, ..=120_000_000) => Latency::WS3,
_ => unreachable!(),
};
#[cfg(any(stm32f4, stm32f7))]
let latency = {
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
let latency = (hclk.0 - 1) / FLASH_LATENCY_STEP;
debug!("flash: latency={}", latency);
Latency::from_bits(latency as u8)
};
FLASH.acr().write(|w| w.set_latency(latency));
while FLASH.acr().read().latency() != latency {}
RCC.cfgr().modify(|w| {
w.set_sw(config.sys);
@ -226,7 +297,7 @@ struct PllOutput {
#[derive(PartialEq, Eq, Clone, Copy)]
enum PllInstance {
Pll,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
Plli2s,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
Pllsai,
@ -238,7 +309,7 @@ fn pll_enable(instance: PllInstance, enabled: bool) {
RCC.cr().modify(|w| w.set_pllon(enabled));
while RCC.cr().read().pllrdy() != enabled {}
}
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
PllInstance::Plli2s => {
RCC.cr().modify(|w| w.set_plli2son(enabled));
while RCC.cr().read().plli2srdy() != enabled {}
@ -269,6 +340,18 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
let vco_freq = in_freq * pll.mul;
assert!(max::PLL_VCO.contains(&vco_freq));
// stm32f2 plls are like swiss cheese
#[cfg(stm32f2)]
match instance {
PllInstance::Pll => {
assert!(pll.divr.is_none());
}
PllInstance::Plli2s => {
assert!(pll.divp.is_none());
assert!(pll.divq.is_none());
}
}
let p = pll.divp.map(|div| vco_freq / div);
let q = pll.divq.map(|div| vco_freq / div);
let r = pll.divr.map(|div| vco_freq / div);
@ -282,6 +365,7 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
if let Some(divq) = pll.divq {
$w.set_pllq(divq);
}
#[cfg(any(stm32f4, stm32f7))]
if let Some(divr) = pll.divr {
$w.set_pllr(divr);
}
@ -298,6 +382,12 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {
write_fields!(w);
}),
#[cfg(stm32f2)]
PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {
if let Some(divr) = pll.divr {
w.set_pllr(divr);
}
}),
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| {
write_fields!(w);
@ -310,22 +400,6 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
PllOutput { p, q, r }
}
fn flash_setup(clk: Hertz) {
use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
let latency = (clk.0 - 1) / FLASH_LATENCY_STEP;
debug!("flash: latency={}", latency);
let latency = Latency::from_bits(latency as u8);
FLASH.acr().write(|w| {
w.set_latency(latency);
});
while FLASH.acr().read().latency() != latency {}
}
#[cfg(stm32f7)]
mod max {
use core::ops::RangeInclusive;
@ -374,3 +448,22 @@ mod max {
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000);
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000);
}
#[cfg(stm32f2)]
mod max {
use core::ops::RangeInclusive;
use crate::time::Hertz;
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000);
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(26_000_000);
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(120_000_000);
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0);
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0 / 4);
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0 / 2);
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(0_950_000)..=Hertz(2_100_000);
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(192_000_000)..=Hertz(432_000_000);
}

View File

@ -1,320 +0,0 @@
use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::Sw;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PLLPreDiv, Plln as PLLMul, Pllp as PLLPDiv, Pllq as PLLQDiv, Pllsrc as PLLSrc,
Ppre as APBPrescaler,
};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
#[derive(Clone, Copy)]
pub struct HSEConfig {
pub frequency: Hertz,
pub source: HSESrc,
}
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
HSE,
HSI,
PLL,
}
/// HSE clock source
#[derive(Clone, Copy)]
pub enum HSESrc {
/// Crystal/ceramic resonator
Crystal,
/// External clock source, HSE bypassed
Bypass,
}
#[derive(Clone, Copy)]
pub struct PLLConfig {
pub pre_div: PLLPreDiv,
pub mul: PLLMul,
pub p_div: PLLPDiv,
pub q_div: PLLQDiv,
}
impl Default for PLLConfig {
fn default() -> Self {
PLLConfig {
pre_div: PLLPreDiv::DIV16,
mul: PLLMul::MUL192,
p_div: PLLPDiv::DIV2,
q_div: PLLQDiv::DIV4,
}
}
}
impl PLLConfig {
pub fn clocks(&self, src_freq: Hertz) -> PLLClocks {
let in_freq = src_freq / self.pre_div;
let vco_freq = src_freq / self.pre_div * self.mul;
let main_freq = vco_freq / self.p_div;
let pll48_freq = vco_freq / self.q_div;
PLLClocks {
in_freq,
vco_freq,
main_freq,
pll48_freq,
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub struct PLLClocks {
pub in_freq: Hertz,
pub vco_freq: Hertz,
pub main_freq: Hertz,
pub pll48_freq: Hertz,
}
/// Voltage range of the power supply used.
///
/// Used to calculate flash waitstates. See
/// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency
pub enum VoltageScale {
/// 2.7 to 3.6 V
Range0,
/// 2.4 to 2.7 V
Range1,
/// 2.1 to 2.4 V
Range2,
/// 1.8 to 2.1 V
Range3,
}
impl VoltageScale {
const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> {
let ahb_freq = ahb_freq.0;
// Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock
// frequency
match self {
VoltageScale::Range3 => {
if ahb_freq <= 16_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 32_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 48_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 64_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 80_000_000 {
Some(Latency::WS4)
} else if ahb_freq <= 96_000_000 {
Some(Latency::WS5)
} else if ahb_freq <= 112_000_000 {
Some(Latency::WS6)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS7)
} else {
None
}
}
VoltageScale::Range2 => {
if ahb_freq <= 18_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 36_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 54_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 72_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 90_000_000 {
Some(Latency::WS4)
} else if ahb_freq <= 108_000_000 {
Some(Latency::WS5)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS6)
} else {
None
}
}
VoltageScale::Range1 => {
if ahb_freq <= 24_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 48_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 72_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 96_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS4)
} else {
None
}
}
VoltageScale::Range0 => {
if ahb_freq <= 30_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 60_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 90_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS3)
} else {
None
}
}
}
}
}
/// Clocks configuration
pub struct Config {
pub hse: Option<HSEConfig>,
pub hsi: bool,
pub pll_mux: PLLSrc,
pub pll: PLLConfig,
pub mux: ClockSrc,
pub voltage: VoltageScale,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub ls: super::LsConfig,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
hse: None,
hsi: true,
pll_mux: PLLSrc::HSI,
pll: PLLConfig::default(),
voltage: VoltageScale::Range3,
mux: ClockSrc::HSI,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
// Make sure HSI is enabled
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
if let Some(hse_config) = config.hse {
RCC.cr().modify(|w| {
w.set_hsebyp(match hse_config.source {
HSESrc::Bypass => true,
HSESrc::Crystal => false,
});
w.set_hseon(true)
});
while !RCC.cr().read().hserdy() {}
}
let pll_src_freq = match config.pll_mux {
PLLSrc::HSE => {
let hse_config = config
.hse
.unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input"));
hse_config.frequency
}
PLLSrc::HSI => HSI_FREQ,
};
// Reference: STM32F215xx/217xx datasheet Table 33. Main PLL characteristics
let pll_clocks = config.pll.clocks(pll_src_freq);
assert!(Hertz(950_000) <= pll_clocks.in_freq && pll_clocks.in_freq <= Hertz(2_100_000));
assert!(Hertz(192_000_000) <= pll_clocks.vco_freq && pll_clocks.vco_freq <= Hertz(432_000_000));
assert!(Hertz(24_000_000) <= pll_clocks.main_freq && pll_clocks.main_freq <= Hertz(120_000_000));
// USB actually requires == 48 MHz, but other PLL48 peripherals are fine with <= 48MHz
assert!(pll_clocks.pll48_freq <= Hertz(48_000_000));
RCC.pllcfgr().write(|w| {
w.set_pllsrc(config.pll_mux);
w.set_pllm(config.pll.pre_div);
w.set_plln(config.pll.mul);
w.set_pllp(config.pll.p_div);
w.set_pllq(config.pll.q_div);
});
let (sys_clk, sw) = match config.mux {
ClockSrc::HSI => {
assert!(config.hsi, "HSI must be enabled to be used as system clock");
(HSI_FREQ, Sw::HSI)
}
ClockSrc::HSE => {
let hse_config = config
.hse
.unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input"));
(hse_config.frequency, Sw::HSE)
}
ClockSrc::PLL => {
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
(pll_clocks.main_freq, Sw::PLL1_P)
}
};
// RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL
// max output to be 120 MHz, so there's no way to get higher frequencies
assert!(sys_clk <= Hertz(120_000_000));
let ahb_freq = sys_clk / config.ahb_pre;
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(ahb_freq <= Hertz(120_000_000));
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, Hertz(freq.0 * 2))
}
};
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(apb1_freq <= Hertz(30_000_000));
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, Hertz(freq.0 * 2))
}
};
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(apb2_freq <= Hertz(60_000_000));
let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq));
FLASH.acr().modify(|w| w.set_latency(flash_ws));
RCC.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
// Turn off HSI to save power if we don't need it
if !config.hsi {
RCC.cr().modify(|w| w.set_hsion(false));
}
let rtc = config.ls.init();
set_freqs(Clocks {
sys: sys_clk,
hclk1: ahb_freq,
hclk2: ahb_freq,
hclk3: ahb_freq,
pclk1: apb1_freq,
pclk1_tim: apb1_tim_freq,
pclk2: apb2_freq,
pclk2_tim: apb2_tim_freq,
pll1_q: Some(pll_clocks.pll48_freq),
rtc,
});
}

View File

@ -28,7 +28,7 @@ pub enum ClockSrc {
#[derive(Clone, Copy)]
pub struct PllConfig {
/// The source from which the PLL receives a clock signal
pub source: PllSrc,
pub source: PllSource,
/// The initial divisor of that clock signal
pub m: Pllm,
/// The PLL VCO multiplier, which must be in the range `8..=86`.
@ -48,7 +48,7 @@ impl Default for PllConfig {
fn default() -> PllConfig {
// HSI / 1 * 8 / 2 = 64 MHz
PllConfig {
source: PllSrc::HSI,
source: PllSource::HSI,
m: Pllm::DIV1,
n: Plln::MUL8,
r: Pllr::DIV2,
@ -59,7 +59,7 @@ impl Default for PllConfig {
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSrc {
pub enum PllSource {
HSI,
HSE(Hertz),
}
@ -89,8 +89,8 @@ impl Default for Config {
impl PllConfig {
pub(crate) fn init(self) -> Hertz {
let (src, input_freq) = match self.source {
PllSrc::HSI => (vals::Pllsrc::HSI, HSI_FREQ),
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq),
PllSource::HSI => (vals::Pllsrc::HSI, HSI_FREQ),
PllSource::HSE(freq) => (vals::Pllsrc::HSE, freq),
};
let m_freq = input_freq / self.m;
@ -121,11 +121,11 @@ impl PllConfig {
// > 3. Change the desired parameter.
// Enable whichever clock source we're using, and wait for it to become ready
match self.source {
PllSrc::HSI => {
PllSource::HSI => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
}
PllSrc::HSE(_) => {
PllSource::HSE(_) => {
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
}

View File

@ -7,7 +7,6 @@ pub use crate::pac::rcc::vals::{
Pllr as PllR, Ppre as APBPrescaler,
};
use crate::pac::{PWR, RCC};
use crate::rcc::sealed::RccPeripheral;
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
@ -24,16 +23,16 @@ pub enum ClockSrc {
/// PLL clock input source
#[derive(Clone, Copy, Debug)]
pub enum PllSrc {
pub enum PllSource {
HSI,
HSE(Hertz),
}
impl Into<Pllsrc> for PllSrc {
impl Into<Pllsrc> for PllSource {
fn into(self) -> Pllsrc {
match self {
PllSrc::HSE(..) => Pllsrc::HSE,
PllSrc::HSI => Pllsrc::HSI,
PllSource::HSE(..) => Pllsrc::HSE,
PllSource::HSI => Pllsrc::HSI,
}
}
}
@ -45,7 +44,7 @@ impl Into<Pllsrc> for PllSrc {
/// frequency ranges for each of these settings.
pub struct Pll {
/// PLL Source clock selection.
pub source: PllSrc,
pub source: PllSource,
/// PLL pre-divider
pub prediv_m: PllM,
@ -67,23 +66,13 @@ pub struct Pll {
pub enum Clock48MhzSrc {
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
/// oscillator to comply with the USB specification for oscillator tolerance.
Hsi48(Option<CrsConfig>),
Hsi48(super::Hsi48Config),
/// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
/// PLL needs to be using the HSE source to comply with the USB specification for oscillator
/// tolerance.
PllQ,
}
/// Sets the sync source for the Clock Recovery System (CRS).
pub enum CrsSyncSource {
/// Use an external GPIO to sync the CRS.
Gpio,
/// Use the Low Speed External oscillator to sync the CRS.
Lse,
/// Use the USB SOF to sync the CRS.
Usb,
}
/// Clocks configutation
pub struct Config {
pub mux: ClockSrc,
@ -102,12 +91,6 @@ pub struct Config {
pub ls: super::LsConfig,
}
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
pub struct CrsConfig {
/// Sync source for the CRS.
pub sync_src: CrsSyncSource,
}
impl Default for Config {
#[inline]
fn default() -> Config {
@ -118,7 +101,7 @@ impl Default for Config {
apb2_pre: APBPrescaler::DIV1,
low_power_run: false,
pll: None,
clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(None)),
clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())),
adc12_clock_source: Adcsel::DISABLE,
adc345_clock_source: Adcsel::DISABLE,
ls: Default::default(),
@ -135,13 +118,13 @@ pub struct PllFreq {
pub(crate) unsafe fn init(config: Config) {
let pll_freq = config.pll.map(|pll_config| {
let src_freq = match pll_config.source {
PllSrc::HSI => {
PllSource::HSI => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
}
PllSrc::HSE(freq) => {
PllSource::HSE(freq) => {
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
freq
@ -288,33 +271,8 @@ pub(crate) unsafe fn init(config: Config) {
crate::pac::rcc::vals::Clk48sel::PLL1_Q
}
Clock48MhzSrc::Hsi48(crs_config) => {
// Enable HSI48
RCC.crrcr().modify(|w| w.set_hsi48on(true));
// Wait for HSI48 to turn on
while RCC.crrcr().read().hsi48rdy() == false {}
// Enable and setup CRS if needed
if let Some(crs_config) = crs_config {
crate::peripherals::CRS::enable_and_reset();
let sync_src = match crs_config.sync_src {
CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO,
CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE,
CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB,
};
crate::pac::CRS.cfgr().modify(|w| {
w.set_syncsrc(sync_src);
});
// These are the correct settings for standard USB operation. If other settings
// are needed there will need to be additional config options for the CRS.
crate::pac::CRS.cr().modify(|w| {
w.set_autotrimen(true);
w.set_cen(true);
});
}
Clock48MhzSrc::Hsi48(config) => {
super::init_hsi48(config);
crate::pac::rcc::vals::Clk48sel::HSI48
}
};

View File

@ -21,9 +21,6 @@ pub const HSI_FREQ: Hertz = Hertz(64_000_000);
/// CSI speed
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
/// HSI48 speed
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
const VCO_RANGE: RangeInclusive<Hertz> = Hertz(150_000_000)..=Hertz(420_000_000);
#[cfg(any(stm32h5, pwr_h7rm0455))]
const VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(128_000_000)..=Hertz(560_000_000);
@ -126,7 +123,7 @@ pub struct Config {
pub hsi: Option<HSIPrescaler>,
pub hse: Option<Hse>,
pub csi: bool,
pub hsi48: bool,
pub hsi48: Option<super::Hsi48Config>,
pub sys: Sysclk,
pub pll1: Option<Pll>,
@ -155,7 +152,7 @@ impl Default for Config {
hsi: Some(HSIPrescaler::DIV1),
hse: None,
csi: false,
hsi48: false,
hsi48: Some(Default::default()),
sys: Sysclk::HSI,
pll1: None,
pll2: None,
@ -301,14 +298,7 @@ pub(crate) unsafe fn init(config: Config) {
};
// Configure HSI48.
RCC.cr().modify(|w| w.set_hsi48on(config.hsi48));
let _hsi48 = match config.hsi48 {
false => None,
true => {
while !RCC.cr().read().hsi48rdy() {}
Some(CSI_FREQ)
}
};
let _hsi48 = config.hsi48.map(super::init_hsi48);
// Configure CSI.
RCC.cr().modify(|w| w.set_csion(config.csi));

View File

@ -0,0 +1,62 @@
#![allow(unused)]
use crate::pac::crs::vals::Syncsrc;
use crate::pac::{CRS, RCC};
use crate::rcc::sealed::RccPeripheral;
use crate::time::Hertz;
/// HSI48 speed
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
/// Configuration for the HSI48 clock
#[derive(Clone, Copy, Debug)]
pub struct Hsi48Config {
/// Enable CRS Sync from USB Start Of Frame (SOF) events.
/// Required if HSI48 is going to be used as USB clock.
///
/// Other use cases of CRS are not supported yet.
pub sync_from_usb: bool,
}
impl Default for Hsi48Config {
fn default() -> Self {
Self { sync_from_usb: false }
}
}
pub(crate) fn init_hsi48(config: Hsi48Config) -> Hertz {
// Enable VREFINT reference for HSI48 oscillator
#[cfg(stm32l0)]
crate::pac::SYSCFG.cfgr3().modify(|w| {
w.set_enref_hsi48(true);
w.set_en_vrefint(true);
});
// Enable HSI48
#[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32u5, stm32wba, stm32f0)))]
let r = RCC.crrcr();
#[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32u5, stm32wba))]
let r = RCC.cr();
#[cfg(any(stm32f0))]
let r = RCC.cr2();
r.modify(|w| w.set_hsi48on(true));
while r.read().hsi48rdy() == false {}
if config.sync_from_usb {
crate::peripherals::CRS::enable_and_reset();
CRS.cfgr().modify(|w| {
w.set_syncsrc(Syncsrc::USB);
});
// These are the correct settings for standard USB operation. If other settings
// are needed there will need to be additional config options for the CRS.
crate::pac::CRS.cr().modify(|w| {
w.set_autotrimen(true);
w.set_cen(true);
});
}
HSI48_FREQ
}

View File

@ -1,12 +1,13 @@
#[cfg(any(stm32l0, stm32l1))]
pub use crate::pac::pwr::vals::Vos as VoltageScale;
use crate::pac::rcc::regs::Cfgr;
#[cfg(any(stm32l4, stm32l5, stm32wb))]
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
#[cfg(any(rcc_l0_v2, stm32l4, stm32l5, stm32wb))]
pub use crate::pac::rcc::vals::Clk48sel as Clk48Src;
#[cfg(any(stm32wb, stm32wl))]
pub use crate::pac::rcc::vals::Hsepre as HsePrescaler;
pub use crate::pac::rcc::vals::{
Adcsel as AdcClockSource, Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul,
Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc,
};
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange as MSIRange, Ppre as APBPrescaler, Sw as ClockSrc};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
@ -33,33 +34,14 @@ pub struct Hse {
pub prescaler: HsePrescaler,
}
#[derive(Clone, Copy)]
pub struct Pll {
/// PLL source
pub source: PLLSource,
/// PLL pre-divider (DIVM).
pub prediv: PllPreDiv,
/// PLL multiplication factor.
pub mul: PllMul,
/// PLL P division factor. If None, PLL P output is disabled.
pub divp: Option<PllPDiv>,
/// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<PllQDiv>,
/// PLL R division factor. If None, PLL R output is disabled.
pub divr: Option<PllRDiv>,
}
/// Clocks configuration
pub struct Config {
// base clock sources
pub msi: Option<MSIRange>,
pub hsi: bool,
pub hse: Option<Hse>,
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
pub hsi48: bool,
#[cfg(crs)]
pub hsi48: Option<super::Hsi48Config>,
// pll
pub pll: Option<Pll>,
@ -79,13 +61,17 @@ pub struct Config {
pub shared_ahb_pre: AHBPrescaler,
// muxes
#[cfg(any(stm32l4, stm32l5, stm32wb))]
#[cfg(any(rcc_l0_v2, stm32l4, stm32l5, stm32wb))]
pub clk48_src: Clk48Src,
// low speed LSI/LSE/RTC
pub ls: super::LsConfig,
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
pub adc_clock_source: AdcClockSource,
#[cfg(any(stm32l0, stm32l1))]
pub voltage_scale: VoltageScale,
}
impl Default for Config {
@ -108,12 +94,15 @@ impl Default for Config {
pllsai1: None,
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
pllsai2: None,
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
hsi48: true,
#[cfg(any(stm32l4, stm32l5, stm32wb))]
#[cfg(crs)]
hsi48: Some(Default::default()),
#[cfg(any(rcc_l0_v2, stm32l4, stm32l5, stm32wb))]
clk48_src: Clk48Src::HSI48,
ls: Default::default(),
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
adc_clock_source: AdcClockSource::SYS,
#[cfg(any(stm32l0, stm32l1))]
voltage_scale: VoltageScale::RANGE1,
}
}
}
@ -126,7 +115,8 @@ pub const WPAN_DEFAULT: Config = Config {
prescaler: HsePrescaler::DIV1,
}),
mux: ClockSrc::PLL1_R,
hsi48: true,
#[cfg(crs)]
hsi48: Some(super::Hsi48Config { sync_from_usb: false }),
msi: None,
hsi: false,
clk48_src: Clk48Src::PLL1_Q,
@ -134,7 +124,7 @@ pub const WPAN_DEFAULT: Config = Config {
ls: super::LsConfig::default_lse(),
pll: Some(Pll {
source: PLLSource::HSE,
source: PllSource::HSE,
prediv: PllPreDiv::DIV2,
mul: PllMul::MUL12,
divp: Some(PllPDiv::DIV3), // 32 / 2 * 12 / 3 = 64Mhz
@ -151,20 +141,26 @@ pub const WPAN_DEFAULT: Config = Config {
adc_clock_source: AdcClockSource::SYS,
};
fn msi_enable(range: MSIRange) {
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
RCC.cr().modify(|w| {
#[cfg(not(stm32wb))]
w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
w.set_msirange(range);
w.set_msipllen(false);
});
#[cfg(any(stm32l0, stm32l1))]
RCC.icscr().modify(|w| w.set_msirange(range));
RCC.cr().modify(|w| w.set_msion(true));
while !RCC.cr().read().msirdy() {}
}
pub(crate) unsafe fn init(config: Config) {
// Switch to MSI to prevent problems with PLL configuration.
if !RCC.cr().read().msion() {
// Turn on MSI and configure it to 4MHz.
RCC.cr().modify(|w| {
#[cfg(not(stm32wb))]
w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
w.set_msirange(MSIRange::RANGE4M);
w.set_msipllen(false);
w.set_msion(true)
});
// Wait until MSI is running
while !RCC.cr().read().msirdy() {}
msi_enable(MSIRange::RANGE4M)
}
if RCC.cfgr().read().sws() != ClockSrc::MSI {
// Set MSI as a clock source, reset prescalers.
@ -173,6 +169,14 @@ pub(crate) unsafe fn init(config: Config) {
while RCC.cfgr().read().sws() != ClockSrc::MSI {}
}
// Set voltage scale
#[cfg(any(stm32l0, stm32l1))]
{
while crate::pac::PWR.csr().read().vosf() {}
crate::pac::PWR.cr().write(|w| w.set_vos(config.voltage_scale));
while crate::pac::PWR.csr().read().vosf() {}
}
#[cfg(stm32l5)]
crate::pac::PWR.cr1().modify(|w| {
w.set_vos(crate::pac::pwr::vals::Vos::RANGE0);
@ -181,21 +185,16 @@ pub(crate) unsafe fn init(config: Config) {
let rtc = config.ls.init();
let msi = config.msi.map(|range| {
// Enable MSI
RCC.cr().modify(|w| {
#[cfg(not(stm32wb))]
w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
w.set_msirange(range);
w.set_msion(true);
// If LSE is enabled, enable calibration of MSI
w.set_msipllen(config.ls.lse.is_some());
});
while !RCC.cr().read().msirdy() {}
msi_enable(range);
msirange_to_hertz(range)
});
// If LSE is enabled and the right freq, enable calibration of MSI
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
if config.ls.lse.map(|x| x.frequency) == Some(Hertz(32_768)) {
RCC.cr().modify(|w| w.set_msipllen(true));
}
let hsi = config.hsi.then(|| {
RCC.cr().modify(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
@ -216,15 +215,13 @@ pub(crate) unsafe fn init(config: Config) {
hse.freq
});
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
let hsi48 = config.hsi48.then(|| {
RCC.crrcr().modify(|w| w.set_hsi48on(true));
while !RCC.crrcr().read().hsi48rdy() {}
Hertz(48_000_000)
#[cfg(crs)]
let _hsi48 = config.hsi48.map(|config| {
//
super::init_hsi48(config)
});
#[cfg(any(stm32l47x, stm32l48x))]
let hsi48 = None;
#[cfg(not(crs))]
let _hsi48: Option<Hertz> = None;
let _plls = [
&config.pll,
@ -255,7 +252,12 @@ pub(crate) unsafe fn init(config: Config) {
}),
};
let pll_input = PllInput { hse, hsi, msi };
let pll_input = PllInput {
hse,
hsi,
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
msi,
};
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
#[cfg(any(stm32l4, stm32l5, stm32wb))]
let pllsai1 = init_pll(PllInstance::Pllsai1, config.pllsai1, &pll_input);
@ -269,13 +271,16 @@ pub(crate) unsafe fn init(config: Config) {
ClockSrc::PLL1_R => pll.r.unwrap(),
};
#[cfg(stm32l4)]
#[cfg(any(rcc_l0_v2, stm32l4, stm32l5, stm32wb))]
RCC.ccipr().modify(|w| w.set_clk48sel(config.clk48_src));
#[cfg(stm32l5)]
RCC.ccipr1().modify(|w| w.set_clk48sel(config.clk48_src));
#[cfg(any(rcc_l0_v2))]
let _clk48 = match config.clk48_src {
Clk48Src::HSI48 => _hsi48,
Clk48Src::PLL1_VCO_DIV_2 => pll.clk48,
};
#[cfg(any(stm32l4, stm32l5, stm32wb))]
let _clk48 = match config.clk48_src {
Clk48Src::HSI48 => hsi48,
Clk48Src::HSI48 => _hsi48,
Clk48Src::MSI => msi,
Clk48Src::PLLSAI1_Q => pllsai1.q,
Clk48Src::PLL1_Q => pll.q,
@ -289,16 +294,23 @@ pub(crate) unsafe fn init(config: Config) {
let hclk1 = sys_clk / config.ahb_pre;
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
#[cfg(not(any(stm32wl5x, stm32wb)))]
#[cfg(any(stm32l4, stm32l5, stm32wlex))]
let hclk2 = hclk1;
#[cfg(any(stm32wl5x, stm32wb))]
let hclk2 = sys_clk / config.core2_ahb_pre;
#[cfg(not(any(stm32wl, stm32wb)))]
#[cfg(any(stm32l4, stm32l5, stm32wlex))]
let hclk3 = hclk1;
#[cfg(any(stm32wl, stm32wb))]
#[cfg(any(stm32wl5x, stm32wb))]
let hclk3 = sys_clk / config.shared_ahb_pre;
// Set flash wait states
#[cfg(any(stm32l0, stm32l1))]
let latency = match (config.voltage_scale, sys_clk.0) {
(VoltageScale::RANGE1, ..=16_000_000) => false,
(VoltageScale::RANGE2, ..=8_000_000) => false,
(VoltageScale::RANGE3, ..=4_200_000) => false,
_ => true,
};
#[cfg(stm32l4)]
let latency = match hclk1.0 {
0..=16_000_000 => 0,
@ -334,6 +346,10 @@ pub(crate) unsafe fn init(config: Config) {
_ => 4,
};
#[cfg(stm32l1)]
FLASH.acr().write(|w| w.set_acc64(true));
#[cfg(not(stm32l5))]
FLASH.acr().modify(|w| w.set_prften(true));
FLASH.acr().modify(|w| w.set_latency(latency));
while FLASH.acr().read().latency() != latency {}
@ -345,9 +361,7 @@ pub(crate) unsafe fn init(config: Config) {
});
while RCC.cfgr().read().sws() != config.mux {}
#[cfg(stm32l5)]
RCC.ccipr1().modify(|w| w.set_adcsel(config.adc_clock_source));
#[cfg(not(stm32l5))]
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
RCC.ccipr().modify(|w| w.set_adcsel(config.adc_clock_source));
#[cfg(any(stm32wl, stm32wb))]
@ -365,7 +379,9 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: sys_clk,
hclk1,
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
hclk2,
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
hclk3,
pclk1,
pclk2,
@ -393,6 +409,12 @@ pub(crate) unsafe fn init(config: Config) {
});
}
#[cfg(any(stm32l0, stm32l1))]
fn msirange_to_hertz(range: MSIRange) -> Hertz {
Hertz(32_768 * (1 << (range as u8 + 1)))
}
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
fn msirange_to_hertz(range: MSIRange) -> Hertz {
match range {
MSIRange::RANGE100K => Hertz(100_000),
@ -411,20 +433,6 @@ fn msirange_to_hertz(range: MSIRange) -> Hertz {
}
}
struct PllInput {
hsi: Option<Hertz>,
hse: Option<Hertz>,
msi: Option<Hertz>,
}
#[allow(unused)]
#[derive(Default)]
struct PllOutput {
p: Option<Hertz>,
q: Option<Hertz>,
r: Option<Hertz>,
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum PllInstance {
Pll,
@ -453,77 +461,182 @@ fn pll_enable(instance: PllInstance, enabled: bool) {
}
}
fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput {
// Disable PLL
pll_enable(instance, false);
pub use pll::*;
let Some(pll) = config else { return PllOutput::default() };
#[cfg(any(stm32l0, stm32l1))]
mod pll {
use super::{pll_enable, PllInstance};
pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource};
use crate::pac::RCC;
use crate::time::Hertz;
let pll_src = match pll.source {
PLLSource::DISABLE => panic!("must not select PLL source as DISABLE"),
PLLSource::HSE => input.hse,
PLLSource::HSI => input.hsi,
PLLSource::MSI => input.msi,
};
#[derive(Clone, Copy)]
pub struct Pll {
/// PLL source
pub source: PllSource,
let pll_src = pll_src.unwrap();
/// PLL multiplication factor.
pub mul: PllMul,
let vco_freq = pll_src / pll.prediv * pll.mul;
let p = pll.divp.map(|div| vco_freq / div);
let q = pll.divq.map(|div| vco_freq / div);
let r = pll.divr.map(|div| vco_freq / div);
#[cfg(stm32l5)]
if instance == PllInstance::Pllsai2 {
assert!(q.is_none(), "PLLSAI2_Q is not available on L5");
assert!(r.is_none(), "PLLSAI2_R is not available on L5");
/// PLL main output division factor.
pub div: PllDiv,
}
macro_rules! write_fields {
($w:ident) => {
$w.set_plln(pll.mul);
if let Some(divp) = pll.divp {
$w.set_pllp(divp);
$w.set_pllpen(true);
}
if let Some(divq) = pll.divq {
$w.set_pllq(divq);
$w.set_pllqen(true);
}
if let Some(divr) = pll.divr {
$w.set_pllr(divr);
$w.set_pllren(true);
}
pub(super) struct PllInput {
pub hsi: Option<Hertz>,
pub hse: Option<Hertz>,
}
#[allow(unused)]
#[derive(Default)]
pub(super) struct PllOutput {
pub r: Option<Hertz>,
pub clk48: Option<Hertz>,
}
pub(super) fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput {
// Disable PLL
pll_enable(instance, false);
let Some(pll) = config else { return PllOutput::default() };
let pll_src = match pll.source {
PllSource::HSE => unwrap!(input.hse),
PllSource::HSI => unwrap!(input.hsi),
};
let vco_freq = pll_src * pll.mul;
let r = vco_freq / pll.div;
let clk48 = (vco_freq == Hertz(96_000_000)).then_some(Hertz(48_000_000));
assert!(r <= Hertz(32_000_000));
RCC.cfgr().write(move |w| {
w.set_pllmul(pll.mul);
w.set_plldiv(pll.div);
w.set_pllsrc(pll.source);
});
// Enable PLL
pll_enable(instance, true);
PllOutput { r: Some(r), clk48 }
}
}
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
mod pll {
use super::{pll_enable, PllInstance};
pub use crate::pac::rcc::vals::{
Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource,
};
use crate::pac::RCC;
use crate::time::Hertz;
#[derive(Clone, Copy)]
pub struct Pll {
/// PLL source
pub source: PllSource,
/// PLL pre-divider (DIVM).
pub prediv: PllPreDiv,
/// PLL multiplication factor.
pub mul: PllMul,
/// PLL P division factor. If None, PLL P output is disabled.
pub divp: Option<PllPDiv>,
/// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<PllQDiv>,
/// PLL R division factor. If None, PLL R output is disabled.
pub divr: Option<PllRDiv>,
}
pub(super) struct PllInput {
pub hsi: Option<Hertz>,
pub hse: Option<Hertz>,
pub msi: Option<Hertz>,
}
#[allow(unused)]
#[derive(Default)]
pub(super) struct PllOutput {
pub p: Option<Hertz>,
pub q: Option<Hertz>,
pub r: Option<Hertz>,
}
pub(super) fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput {
// Disable PLL
pll_enable(instance, false);
let Some(pll) = config else { return PllOutput::default() };
let pll_src = match pll.source {
PllSource::DISABLE => panic!("must not select PLL source as DISABLE"),
PllSource::HSE => unwrap!(input.hse),
PllSource::HSI => unwrap!(input.hsi),
PllSource::MSI => unwrap!(input.msi),
};
let vco_freq = pll_src / pll.prediv * pll.mul;
let p = pll.divp.map(|div| vco_freq / div);
let q = pll.divq.map(|div| vco_freq / div);
let r = pll.divr.map(|div| vco_freq / div);
#[cfg(stm32l5)]
if instance == PllInstance::Pllsai2 {
assert!(q.is_none(), "PLLSAI2_Q is not available on L5");
assert!(r.is_none(), "PLLSAI2_R is not available on L5");
}
macro_rules! write_fields {
($w:ident) => {
$w.set_plln(pll.mul);
if let Some(divp) = pll.divp {
$w.set_pllp(divp);
$w.set_pllpen(true);
}
if let Some(divq) = pll.divq {
$w.set_pllq(divq);
$w.set_pllqen(true);
}
if let Some(divr) = pll.divr {
$w.set_pllr(divr);
$w.set_pllren(true);
}
};
}
match instance {
PllInstance::Pll => RCC.pllcfgr().write(|w| {
w.set_pllm(pll.prediv);
w.set_pllsrc(pll.source);
write_fields!(w);
}),
#[cfg(any(stm32l4, stm32l5, stm32wb))]
PllInstance::Pllsai1 => RCC.pllsai1cfgr().write(|w| {
#[cfg(any(rcc_l4plus, stm32l5))]
w.set_pllm(pll.prediv);
#[cfg(stm32l5)]
w.set_pllsrc(pll.source);
write_fields!(w);
}),
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
PllInstance::Pllsai2 => RCC.pllsai2cfgr().write(|w| {
#[cfg(any(rcc_l4plus, stm32l5))]
w.set_pllm(pll.prediv);
#[cfg(stm32l5)]
w.set_pllsrc(pll.source);
write_fields!(w);
}),
}
// Enable PLL
pll_enable(instance, true);
PllOutput { p, q, r }
}
match instance {
PllInstance::Pll => RCC.pllcfgr().write(|w| {
w.set_pllm(pll.prediv);
w.set_pllsrc(pll.source);
write_fields!(w);
}),
#[cfg(any(stm32l4, stm32l5, stm32wb))]
PllInstance::Pllsai1 => RCC.pllsai1cfgr().write(|w| {
#[cfg(any(rcc_l4plus, stm32l5))]
w.set_pllm(pll.prediv);
#[cfg(stm32l5)]
w.set_pllsrc(pll.source);
write_fields!(w);
}),
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
PllInstance::Pllsai2 => RCC.pllsai2cfgr().write(|w| {
#[cfg(any(rcc_l4plus, stm32l5))]
w.set_pllm(pll.prediv);
#[cfg(stm32l5)]
w.set_pllsrc(pll.source);
write_fields!(w);
}),
}
// Enable PLL
pll_enable(instance, true);
PllOutput { p, q, r }
}

View File

@ -1,218 +0,0 @@
pub use crate::pac::pwr::vals::Vos as VoltageScale;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Msirange as MSIRange, Plldiv as PLLDiv, Plldiv as PllDiv, Pllmul as PLLMul, Pllmul as PllMul,
Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc,
};
#[cfg(crs)]
use crate::pac::{crs, CRS, SYSCFG};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode {
/// crystal/ceramic oscillator (HSEBYP=0)
Oscillator,
/// external analog clock (low swing) (HSEBYP=1)
Bypass,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse {
/// HSE frequency.
pub freq: Hertz,
/// HSE mode.
pub mode: HseMode,
}
#[derive(Clone, Copy)]
pub struct Pll {
/// PLL source
pub source: PLLSource,
/// PLL multiplication factor.
pub mul: PllMul,
/// PLL main output division factor.
pub div: PllDiv,
}
/// Clocks configutation
pub struct Config {
// base clock sources
pub msi: Option<MSIRange>,
pub hsi: bool,
pub hse: Option<Hse>,
#[cfg(crs)]
pub hsi48: bool,
pub pll: Option<Pll>,
pub mux: ClockSrc,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub ls: super::LsConfig,
pub voltage_scale: VoltageScale,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
msi: Some(MSIRange::RANGE5),
hse: None,
hsi: false,
#[cfg(crs)]
hsi48: false,
pll: None,
mux: ClockSrc::MSI,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
voltage_scale: VoltageScale::RANGE1,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
// Set voltage scale
while PWR.csr().read().vosf() {}
PWR.cr().write(|w| w.set_vos(config.voltage_scale));
while PWR.csr().read().vosf() {}
let rtc = config.ls.init();
let msi = config.msi.map(|range| {
RCC.icscr().modify(|w| w.set_msirange(range));
RCC.cr().modify(|w| w.set_msion(true));
while !RCC.cr().read().msirdy() {}
Hertz(32_768 * (1 << (range as u8 + 1)))
});
let hsi = config.hsi.then(|| {
RCC.cr().modify(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
});
let hse = config.hse.map(|hse| {
RCC.cr().modify(|w| {
w.set_hsebyp(hse.mode == HseMode::Bypass);
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
hse.freq
});
let pll = config.pll.map(|pll| {
let freq = match pll.source {
PLLSource::HSE => hse.unwrap(),
PLLSource::HSI => hsi.unwrap(),
};
// Disable PLL
RCC.cr().modify(|w| w.set_pllon(false));
while RCC.cr().read().pllrdy() {}
let freq = freq * pll.mul / pll.div;
assert!(freq <= Hertz(32_000_000));
RCC.cfgr().write(move |w| {
w.set_pllmul(pll.mul);
w.set_plldiv(pll.div);
w.set_pllsrc(pll.source);
});
// Enable PLL
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
freq
});
let sys_clk = match config.mux {
ClockSrc::HSE => hse.unwrap(),
ClockSrc::HSI => hsi.unwrap(),
ClockSrc::MSI => msi.unwrap(),
ClockSrc::PLL1_P => pll.unwrap(),
};
let wait_states = match (config.voltage_scale, sys_clk.0) {
(VoltageScale::RANGE1, ..=16_000_000) => 0,
(VoltageScale::RANGE2, ..=8_000_000) => 0,
(VoltageScale::RANGE3, ..=4_200_000) => 0,
_ => 1,
};
#[cfg(stm32l1)]
FLASH.acr().write(|w| w.set_acc64(true));
FLASH.acr().modify(|w| w.set_prften(true));
FLASH.acr().modify(|w| w.set_latency(wait_states != 0));
RCC.cfgr().modify(|w| {
w.set_sw(config.mux);
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
let hclk1 = sys_clk / config.ahb_pre;
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
#[cfg(crs)]
if config.hsi48 {
// Reset CRS peripheral
RCC.apb1rstr().modify(|w| w.set_crsrst(true));
RCC.apb1rstr().modify(|w| w.set_crsrst(false));
// Enable CRS peripheral
RCC.apb1enr().modify(|w| w.set_crsen(true));
// Initialize CRS
CRS.cfgr().write(|w|
// Select LSE as synchronization source
w.set_syncsrc(crs::vals::Syncsrc::LSE));
CRS.cr().modify(|w| {
w.set_autotrimen(true);
w.set_cen(true);
});
// Enable VREFINT reference for HSI48 oscillator
SYSCFG.cfgr3().modify(|w| {
w.set_enref_hsi48(true);
w.set_en_vrefint(true);
});
// Select HSI48 as USB clock
RCC.ccipr().modify(|w| w.set_hsi48msel(true));
// Enable dedicated USB clock
RCC.crrcr().modify(|w| w.set_hsi48on(true));
while !RCC.crrcr().read().hsi48rdy() {}
}
set_freqs(Clocks {
sys: sys_clk,
hclk1,
pclk1,
pclk2,
pclk1_tim,
pclk2_tim,
rtc,
});
}

View File

@ -9,17 +9,20 @@ mod mco;
pub use bd::*;
pub use mco::*;
#[cfg(crs)]
mod hsi48;
#[cfg(crs)]
pub use hsi48::*;
#[cfg_attr(rcc_f0, path = "f0.rs")]
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
#[cfg_attr(rcc_f2, path = "f2.rs")]
#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")]
#[cfg_attr(any(stm32f1), path = "f1.rs")]
#[cfg_attr(any(stm32f3), path = "f3.rs")]
#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f.rs")]
#[cfg_attr(rcc_c0, path = "c0.rs")]
#[cfg_attr(rcc_g0, path = "g0.rs")]
#[cfg_attr(rcc_g4, path = "g4.rs")]
#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab), path = "h.rs")]
#[cfg_attr(any(rcc_l0, rcc_l0_v2, rcc_l1), path = "l0l1.rs")]
#[cfg_attr(any(rcc_l4, rcc_l4plus, rcc_l5, rcc_wl5, rcc_wle, rcc_wb), path = "l4l5.rs")]
#[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")]
#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")]
#[cfg_attr(rcc_u5, path = "u5.rs")]
#[cfg_attr(rcc_wba, path = "wba.rs")]
mod _version;

View File

@ -35,7 +35,7 @@ impl Default for ClockSrc {
#[derive(Clone, Copy)]
pub struct PllConfig {
/// The clock source for the PLL.
pub source: PllSrc,
pub source: PllSource,
/// The PLL prescaler.
///
/// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
@ -57,7 +57,7 @@ impl PllConfig {
/// A configuration for HSI / 1 * 10 / 1 = 160 MHz
pub const fn hsi_160mhz() -> Self {
PllConfig {
source: PllSrc::HSI,
source: PllSource::HSI,
m: Pllm::DIV1,
n: Plln::MUL10,
r: Plldiv::DIV1,
@ -67,7 +67,7 @@ impl PllConfig {
/// A configuration for MSIS @ 48 MHz / 3 * 10 / 1 = 160 MHz
pub const fn msis_160mhz() -> Self {
PllConfig {
source: PllSrc::MSIS(Msirange::RANGE_48MHZ),
source: PllSource::MSIS(Msirange::RANGE_48MHZ),
m: Pllm::DIV3,
n: Plln::MUL10,
r: Plldiv::DIV1,
@ -76,7 +76,7 @@ impl PllConfig {
}
#[derive(Clone, Copy)]
pub enum PllSrc {
pub enum PllSource {
/// Use an internal medium speed oscillator as the PLL source.
MSIS(Msirange),
/// Use the external high speed clock as the system PLL source.
@ -88,12 +88,12 @@ pub enum PllSrc {
HSI,
}
impl Into<Pllsrc> for PllSrc {
impl Into<Pllsrc> for PllSource {
fn into(self) -> Pllsrc {
match self {
PllSrc::MSIS(..) => Pllsrc::MSIS,
PllSrc::HSE(..) => Pllsrc::HSE,
PllSrc::HSI => Pllsrc::HSI,
PllSource::MSIS(..) => Pllsrc::MSIS,
PllSource::HSE(..) => Pllsrc::HSE,
PllSource::HSI => Pllsrc::HSI,
}
}
}
@ -115,7 +115,7 @@ pub struct Config {
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub apb3_pre: APBPrescaler,
pub hsi48: bool,
pub hsi48: Option<super::Hsi48Config>,
/// The voltage range influences the maximum clock frequencies for different parts of the
/// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks
/// exceeding 55 MHz require at least `RANGE2`.
@ -189,7 +189,7 @@ impl Default for Config {
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
apb3_pre: APBPrescaler::DIV1,
hsi48: true,
hsi48: Some(Default::default()),
voltage_range: VoltageScale::RANGE3,
ls: Default::default(),
}
@ -216,9 +216,9 @@ pub(crate) unsafe fn init(config: Config) {
ClockSrc::PLL1_R(pll) => {
// Configure the PLL source
let source_clk = match pll.source {
PllSrc::MSIS(range) => config.init_msis(range),
PllSrc::HSE(hertz) => config.init_hse(hertz),
PllSrc::HSI => config.init_hsi(),
PllSource::MSIS(range) => config.init_msis(range),
PllSource::HSE(hertz) => config.init_hse(hertz),
PllSource::HSI => config.init_hsi(),
};
// Calculate the reference clock, which is the source divided by m
@ -322,10 +322,7 @@ pub(crate) unsafe fn init(config: Config) {
}
};
if config.hsi48 {
RCC.cr().modify(|w| w.set_hsi48on(true));
while !RCC.cr().read().hsi48rdy() {}
}
let _hsi48 = config.hsi48.map(super::init_hsi48);
// The clock source is ready
// Calculate and set the flash wait states

View File

@ -17,16 +17,16 @@ pub enum ClockSrc {
}
#[derive(Clone, Copy, Debug)]
pub enum PllSrc {
pub enum PllSource {
HSE(Hertz),
HSI,
}
impl Into<Pllsrc> for PllSrc {
impl Into<Pllsrc> for PllSource {
fn into(self) -> Pllsrc {
match self {
PllSrc::HSE(..) => Pllsrc::HSE,
PllSrc::HSI => Pllsrc::HSI,
PllSource::HSE(..) => Pllsrc::HSE,
PllSource::HSI => Pllsrc::HSI,
}
}
}

View File

@ -33,6 +33,61 @@ use embassy_hal_internal::Peripheral;
use crate::peripherals::RTC;
use crate::rtc::sealed::Instance;
#[allow(dead_code)]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub(crate) enum WakeupPrescaler {
Div2 = 2,
Div4 = 4,
Div8 = 8,
Div16 = 16,
}
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))]
impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
fn from(val: WakeupPrescaler) -> Self {
use crate::pac::rtc::vals::Wucksel;
match val {
WakeupPrescaler::Div2 => Wucksel::DIV2,
WakeupPrescaler::Div4 => Wucksel::DIV4,
WakeupPrescaler::Div8 => Wucksel::DIV8,
WakeupPrescaler::Div16 => Wucksel::DIV16,
}
}
}
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))]
impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
use crate::pac::rtc::vals::Wucksel;
match val {
Wucksel::DIV2 => WakeupPrescaler::Div2,
Wucksel::DIV4 => WakeupPrescaler::Div4,
Wucksel::DIV8 => WakeupPrescaler::Div8,
Wucksel::DIV16 => WakeupPrescaler::Div16,
_ => unreachable!(),
}
}
}
#[cfg(feature = "low-power")]
impl WakeupPrescaler {
pub fn compute_min(val: u32) -> Self {
*[
WakeupPrescaler::Div2,
WakeupPrescaler::Div4,
WakeupPrescaler::Div8,
WakeupPrescaler::Div16,
]
.iter()
.skip_while(|psc| **psc as u32 <= val)
.next()
.unwrap_or(&WakeupPrescaler::Div16)
}
}
/// Errors that can occur on methods on [RtcClock]
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)]
@ -277,6 +332,114 @@ impl Rtc {
pub fn write_backup_register(&self, register: usize, value: u32) {
RTC::write_backup_register(&RTC::regs(), register, value)
}
#[cfg(feature = "low-power")]
/// start the wakeup alarm and wtih a duration that is as close to but less than
/// the requested duration, and record the instant the wakeup alarm was started
pub(crate) fn start_wakeup_alarm(
&self,
requested_duration: embassy_time::Duration,
cs: critical_section::CriticalSection,
) {
use embassy_time::{Duration, TICK_HZ};
#[cfg(any(rtc_v3, rtc_v3u5))]
use crate::pac::rtc::vals::Calrf;
// Panic if the rcc mod knows we're not using low-power rtc
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
unsafe { crate::rcc::get_freqs() }.rtc.unwrap();
let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);
let rtc_hz = Self::frequency().0 as u64;
let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;
let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
// adjust the rtc ticks to the prescaler and subtract one rtc tick
let rtc_ticks = rtc_ticks / prescaler as u64;
let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16;
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wute(false));
#[cfg(any(
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
))]
{
regs.isr().modify(|w| w.set_wutf(false));
while !regs.isr().read().wutwf() {}
}
#[cfg(any(rtc_v3, rtc_v3u5))]
{
regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
while !regs.icsr().read().wutwf() {}
}
regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
regs.wutr().write(|w| w.set_wut(rtc_ticks));
regs.cr().modify(|w| w.set_wute(true));
regs.cr().modify(|w| w.set_wutie(true));
});
let instant = self.instant().unwrap();
trace!(
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
prescaler as u32,
rtc_ticks,
instant,
);
assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
}
#[cfg(feature = "low-power")]
/// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
/// was called, otherwise none
pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
use crate::interrupt::typelevel::Interrupt;
#[cfg(any(rtc_v3, rtc_v3u5))]
use crate::pac::rtc::vals::Calrf;
let instant = self.instant().unwrap();
if RTC::regs().cr().read().wute() {
trace!("rtc: stop wakeup alarm at {}", instant);
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wutie(false));
regs.cr().modify(|w| w.set_wute(false));
#[cfg(any(
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
))]
regs.isr().modify(|w| w.set_wutf(false));
#[cfg(any(rtc_v3, rtc_v3u5))]
regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
crate::pac::EXTI
.pr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
});
}
self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time)
}
#[cfg(feature = "low-power")]
pub(crate) fn enable_wakeup_line(&self) {
use crate::interrupt::typelevel::Interrupt;
use crate::pac::EXTI;
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
unsafe { <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::enable() };
EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
}
}
pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {

View File

@ -6,145 +6,7 @@ use crate::peripherals::RTC;
use crate::rtc::sealed::Instance;
#[allow(dead_code)]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub(crate) enum WakeupPrescaler {
Div2 = 2,
Div4 = 4,
Div8 = 8,
Div16 = 16,
}
#[cfg(any(stm32wb, stm32f4, stm32l0))]
impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
fn from(val: WakeupPrescaler) -> Self {
use crate::pac::rtc::vals::Wucksel;
match val {
WakeupPrescaler::Div2 => Wucksel::DIV2,
WakeupPrescaler::Div4 => Wucksel::DIV4,
WakeupPrescaler::Div8 => Wucksel::DIV8,
WakeupPrescaler::Div16 => Wucksel::DIV16,
}
}
}
#[cfg(any(stm32wb, stm32f4, stm32l0))]
impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
use crate::pac::rtc::vals::Wucksel;
match val {
Wucksel::DIV2 => WakeupPrescaler::Div2,
Wucksel::DIV4 => WakeupPrescaler::Div4,
Wucksel::DIV8 => WakeupPrescaler::Div8,
Wucksel::DIV16 => WakeupPrescaler::Div16,
_ => unreachable!(),
}
}
}
#[allow(dead_code)]
impl WakeupPrescaler {
pub fn compute_min(val: u32) -> Self {
*[
WakeupPrescaler::Div2,
WakeupPrescaler::Div4,
WakeupPrescaler::Div8,
WakeupPrescaler::Div16,
]
.iter()
.skip_while(|psc| **psc as u32 <= val)
.next()
.unwrap_or(&WakeupPrescaler::Div16)
}
}
impl super::Rtc {
#[cfg(feature = "low-power")]
/// start the wakeup alarm and wtih a duration that is as close to but less than
/// the requested duration, and record the instant the wakeup alarm was started
pub(crate) fn start_wakeup_alarm(
&self,
requested_duration: embassy_time::Duration,
cs: critical_section::CriticalSection,
) {
use embassy_time::{Duration, TICK_HZ};
// Panic if the rcc mod knows we're not using low-power rtc
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
unsafe { crate::rcc::get_freqs() }.rtc.unwrap();
let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);
let rtc_hz = Self::frequency().0 as u64;
let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;
let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
// adjust the rtc ticks to the prescaler and subtract one rtc tick
let rtc_ticks = rtc_ticks / prescaler as u64;
let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16;
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wute(false));
regs.isr().modify(|w| w.set_wutf(false));
while !regs.isr().read().wutwf() {}
regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
regs.wutr().write(|w| w.set_wut(rtc_ticks));
regs.cr().modify(|w| w.set_wute(true));
regs.cr().modify(|w| w.set_wutie(true));
});
let instant = self.instant().unwrap();
trace!(
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
prescaler as u32,
rtc_ticks,
instant,
);
assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
}
#[cfg(feature = "low-power")]
/// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
/// was called, otherwise none
pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
use crate::interrupt::typelevel::Interrupt;
let instant = self.instant().unwrap();
if RTC::regs().cr().read().wute() {
trace!("rtc: stop wakeup alarm at {}", instant);
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wutie(false));
regs.cr().modify(|w| w.set_wute(false));
regs.isr().modify(|w| w.set_wutf(false));
crate::pac::EXTI
.pr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
});
}
self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time)
}
#[cfg(feature = "low-power")]
pub(crate) fn enable_wakeup_line(&self) {
use crate::interrupt::typelevel::Interrupt;
use crate::pac::EXTI;
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
unsafe { <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::enable() };
EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
}
/// Applies the RTC config
/// It this changes the RTC clock source the time will be reset
pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) {

View File

@ -95,7 +95,7 @@ impl super::Rtc {
})
}
pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R
where
F: FnOnce(&crate::pac::rtc::Rtc) -> R,
{
@ -129,6 +129,12 @@ impl super::Rtc {
impl sealed::Instance for crate::peripherals::RTC {
const BACKUP_REGISTER_COUNT: usize = 32;
#[cfg(all(feature = "low-power", stm32g4))]
const EXTI_WAKEUP_LINE: usize = 20;
#[cfg(all(feature = "low-power", stm32g4))]
type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
#[allow(clippy::if_same_then_else)]
if register < Self::BACKUP_REGISTER_COUNT {

View File

@ -40,6 +40,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
// Handle RX
while r.gintsts().read().rxflvl() {
let status = r.grxstsp().read();
trace!("=== status {:08x}", status.0);
let ep_num = status.epnum() as usize;
let len = status.bcnt() as usize;
@ -51,6 +52,15 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
assert!(len == 8, "invalid SETUP packet length={}", len);
assert!(ep_num == 0, "invalid SETUP packet endpoint={}", ep_num);
// flushing TX if something stuck in control endpoint
if r.dieptsiz(ep_num).read().pktcnt() != 0 {
r.grstctl().modify(|w| {
w.set_txfnum(ep_num as _);
w.set_txfflsh(true);
});
while r.grstctl().read().txfflsh() {}
}
if state.ep0_setup_ready.load(Ordering::Relaxed) == false {
// SAFETY: exclusive access ensured by atomic bool
let data = unsafe { &mut *state.ep0_setup_data.get() };
@ -96,6 +106,11 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
}
vals::Pktstsd::SETUP_DATA_DONE => {
trace!("SETUP_DATA_DONE ep={}", ep_num);
if quirk_setup_late_cnak(r) {
// Clear NAK to indicate we are ready to receive more data
r.doepctl(ep_num).modify(|w| w.set_cnak(true));
}
}
x => trace!("unknown PKTSTS: {}", x.to_bits()),
}
@ -911,11 +926,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
trace!("enumdne");
let speed = r.dsts().read().enumspd();
trace!(" speed={}", speed.to_bits());
r.gusbcfg().modify(|w| {
w.set_trdt(calculate_trdt(speed, T::frequency()));
});
let trdt = calculate_trdt(speed, T::frequency());
trace!(" speed={} trdt={}", speed.to_bits(), trdt);
r.gusbcfg().modify(|w| w.set_trdt(trdt));
r.gintsts().write(|w| w.set_enumdne(true)); // clear
Self::restore_irqs();
@ -1304,20 +1317,22 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> {
state.ep_out_wakers[0].register(cx.waker());
let r = T::regs();
if state.ep0_setup_ready.load(Ordering::Relaxed) {
let data = unsafe { *state.ep0_setup_data.get() };
state.ep0_setup_ready.store(false, Ordering::Release);
// EP0 should not be controlled by `Bus` so this RMW does not need a critical section
// Receive 1 SETUP packet
T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| {
r.doeptsiz(self.ep_out.info.addr.index()).modify(|w| {
w.set_rxdpid_stupcnt(1);
});
// Clear NAK to indicate we are ready to receive more data
T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| {
w.set_cnak(true);
});
if !quirk_setup_late_cnak(r) {
r.doepctl(self.ep_out.info.addr.index()).modify(|w| w.set_cnak(true));
}
trace!("SETUP received: {:?}", data);
Poll::Ready(data)
@ -1453,3 +1468,7 @@ fn calculate_trdt(speed: vals::Dspd, ahb_freq: Hertz) -> u8 {
_ => unimplemented!(),
}
}
fn quirk_setup_late_cnak(r: crate::pac::otg::Otg) -> bool {
r.cid().read().0 & 0xf000 == 0x1000
}

View File

@ -33,7 +33,7 @@ log = { version = "0.4.14", optional = true }
futures-util = { version = "0.3.17", default-features = false }
critical-section = "1.1"
heapless = "0.7.5"
heapless = "0.8"
cfg-if = "1.0.0"
embedded-io-async = { version = "0.6.0", optional = true }

View File

@ -248,7 +248,7 @@ embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
futures-util = { version = "0.3.17", default-features = false }
critical-section = "1.1"
cfg-if = "1.0.0"
heapless = "0.7"
heapless = "0.8"
# WASM dependencies
wasm-bindgen = { version = "0.2.81", optional = true }
@ -258,4 +258,4 @@ wasm-timer = { version = "0.2.5", optional = true }
[dev-dependencies]
serial_test = "0.9"
critical-section = { version = "1.1", features = ["std"] }
embassy-executor = { version = "0.3.1", path = "../embassy-executor", features = ["nightly"] }
embassy-executor = { version = "0.3.3", path = "../embassy-executor", features = ["nightly"] }

View File

@ -19,6 +19,7 @@ pub struct LoggerState<'d> {
device_descriptor: [u8; 32],
config_descriptor: [u8; 128],
bos_descriptor: [u8; 16],
msos_descriptor: [u8; 256],
control_buf: [u8; 64],
}
@ -30,6 +31,7 @@ impl<'d> LoggerState<'d> {
device_descriptor: [0; 32],
config_descriptor: [0; 128],
bos_descriptor: [0; 16],
msos_descriptor: [0; 256],
control_buf: [0; 64],
}
}
@ -73,6 +75,7 @@ impl<const N: usize> UsbLogger<N> {
&mut state.device_descriptor,
&mut state.config_descriptor,
&mut state.bos_descriptor,
&mut state.msos_descriptor,
&mut state.control_buf,
);

View File

@ -13,7 +13,6 @@ target = "thumbv7em-none-eabi"
[features]
defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
msos-descriptor = []
default = ["usbd-hid"]
# BEGIN AUTOGENERATED CONFIG FEATURES
@ -46,7 +45,7 @@ embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
heapless = "0.7.10"
heapless = "0.8"
# for HID
usbd-hid = { version = "0.6.0", optional = true }

View File

@ -3,7 +3,6 @@ use heapless::Vec;
use crate::config::MAX_HANDLER_COUNT;
use crate::descriptor::{BosWriter, DescriptorWriter};
use crate::driver::{Driver, Endpoint, EndpointType};
#[cfg(feature = "msos-descriptor")]
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
use crate::types::{InterfaceNumber, StringIndex};
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
@ -133,7 +132,6 @@ pub struct Builder<'d, D: Driver<'d>> {
config_descriptor: DescriptorWriter<'d>,
bos_descriptor: BosWriter<'d>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter<'d>,
}
@ -149,7 +147,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
device_descriptor_buf: &'d mut [u8],
config_descriptor_buf: &'d mut [u8],
bos_descriptor_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8],
msos_descriptor_buf: &'d mut [u8],
control_buf: &'d mut [u8],
) -> Self {
// Magic values specified in USB-IF ECN on IADs.
@ -189,14 +187,12 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
config_descriptor,
bos_descriptor,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),
}
}
/// Creates the [`UsbDevice`] instance with the configuration in this builder.
pub fn build(mut self) -> UsbDevice<'d, D> {
#[cfg(feature = "msos-descriptor")]
let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
self.config_descriptor.end_configuration();
@ -206,7 +202,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
info!("USB: device_descriptor used: {}", self.device_descriptor.position());
info!("USB: config_descriptor used: {}", self.config_descriptor.position());
info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
#[cfg(feature = "msos-descriptor")]
info!("USB: msos_descriptor used: {}", msos_descriptor.len());
info!("USB: control_buf size: {}", self.control_buf.len());
@ -217,10 +212,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
self.device_descriptor.into_buf(),
self.config_descriptor.into_buf(),
self.bos_descriptor.writer.into_buf(),
msos_descriptor,
self.interfaces,
self.control_buf,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
)
}
@ -251,7 +245,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
builder: self,
iface_count_index,
#[cfg(feature = "msos-descriptor")]
first_interface,
}
}
@ -275,7 +268,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
StringIndex::new(index)
}
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Descriptor Set.
///
/// Panics if called more than once.
@ -283,13 +275,11 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
self.msos_descriptor.header(windows_version, vendor_code);
}
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Device Level Feature Descriptor.
pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
self.msos_descriptor.device_feature(desc);
}
#[cfg(feature = "msos-descriptor")]
/// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that
/// do not add their own.
pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {
@ -306,13 +296,11 @@ pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
builder: &'a mut Builder<'d, D>,
iface_count_index: Option<usize>,
#[cfg(feature = "msos-descriptor")]
first_interface: InterfaceNumber,
}
impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
fn drop(&mut self) {
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.end_function();
}
}
@ -344,7 +332,6 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
}
}
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Function Level Feature Descriptor.
pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
if !self.builder.msos_descriptor.is_in_config_subset() {
@ -355,7 +342,6 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
self.builder.msos_descriptor.function(self.first_interface);
}
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.function_feature(desc);
}
}

View File

@ -175,10 +175,7 @@ pub struct UsbBufferReport {
/// Number of bos descriptor bytes used
pub bos_descriptor_used: usize,
/// Number of msos descriptor bytes used
///
/// Will be `None` if the "msos-descriptor" feature is not active.
/// Otherwise will return Some(bytes).
pub msos_descriptor_used: Option<usize>,
pub msos_descriptor_used: usize,
/// Size of the control buffer
pub control_buffer_size: usize,
}
@ -197,6 +194,7 @@ struct Inner<'d, D: Driver<'d>> {
device_descriptor: &'d [u8],
config_descriptor: &'d [u8],
bos_descriptor: &'d [u8],
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
device_state: UsbDeviceState,
suspended: bool,
@ -212,9 +210,6 @@ struct Inner<'d, D: Driver<'d>> {
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
}
impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
@ -225,9 +220,9 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
device_descriptor: &'d [u8],
config_descriptor: &'d [u8],
bos_descriptor: &'d [u8],
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
) -> UsbDevice<'d, D> {
// Start the USB bus.
// This prevent further allocation by consuming the driver.
@ -242,6 +237,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
device_descriptor,
config_descriptor,
bos_descriptor,
msos_descriptor,
device_state: UsbDeviceState::Unpowered,
suspended: false,
@ -251,8 +247,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
set_address_pending: false,
interfaces,
handlers,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
},
}
}
@ -261,16 +255,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
///
/// Useful for tuning buffer sizes for actual usage
pub fn buffer_usage(&self) -> UsbBufferReport {
#[cfg(not(feature = "msos-descriptor"))]
let mdu = None;
#[cfg(feature = "msos-descriptor")]
let mdu = Some(self.inner.msos_descriptor.len());
UsbBufferReport {
device_descriptor_used: self.inner.device_descriptor.len(),
config_descriptor_used: self.inner.config_descriptor.len(),
bos_descriptor_used: self.inner.bos_descriptor.len(),
msos_descriptor_used: mdu,
msos_descriptor_used: self.inner.msos_descriptor.len(),
control_buffer_size: self.control_buf.len(),
}
}
@ -406,6 +395,16 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
let max_packet_size = self.control.max_packet_size();
let mut total = 0;
if req_length > self.control_buf.len() {
warn!(
"got CONTROL OUT with length {} higher than the control_buf len {}, rejecting.",
req_length,
self.control_buf.len()
);
self.control.reject().await;
return;
}
let chunks = self.control_buf[..req_length].chunks_mut(max_packet_size);
for (first, last, chunk) in first_last(chunks) {
let size = match self.control.data_out(chunk, first, last).await {
@ -674,7 +673,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
}
_ => InResponse::Rejected,
},
#[cfg(feature = "msos-descriptor")]
(RequestType::Vendor, Recipient::Device) => {
if !self.msos_descriptor.is_empty()
&& req.request == self.msos_descriptor.vendor_code()

View File

@ -1,5 +1,3 @@
#![cfg(feature = "msos-descriptor")]
//! Microsoft OS Descriptors
//!
//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly"] }
embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] }

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly"] }
embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] }
embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp", features = ["nightly"] }

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] }

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] }

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] }

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] }
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] }

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] }

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] }

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] }

View File

@ -17,7 +17,7 @@ log = [
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time" }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }

View File

@ -30,11 +30,11 @@ nightly = [
[dependencies]
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
embedded-io = { version = "0.6.0", features = ["defmt-03"] }
embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] }
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true }

View File

@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = [
"defmt",
] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread",
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread",
"nightly",
"defmt",
"integrated-timers",

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] }
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
@ -39,7 +39,7 @@ st7789 = "0.6.1"
display-interface = "0.4.1"
byte-slice-cast = { version = "1.2.0", default-features = false }
smart-leds = "0.3.0"
heapless = "0.7.15"
heapless = "0.8"
usbd-hid = "0.6.1"
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }

View File

@ -75,6 +75,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);

View File

@ -71,6 +71,7 @@ async fn main(spawner: Spawner) {
&mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..],
&mut [], // no msos descriptors
&mut make_static!([0; 128])[..],
);

View File

@ -41,7 +41,7 @@ async fn main(_spawner: Spawner) {
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
// You can also add a Microsoft OS descriptor.
// let mut msos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let request_handler = MyRequestHandler {};
let mut device_handler = MyDeviceHandler::new();
@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
// &mut msos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);

View File

@ -58,6 +58,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);

View File

@ -0,0 +1,199 @@
//! Example of using USB without a pre-defined class, but instead responding to
//! raw USB control requests.
//!
//! The host computer can either:
//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer
//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive
//!
//! For higher throughput data, you can add some bulk endpoints after creating the alternate,
//! but for low rate command/response, plain control transfers can be very simple and effective.
//!
//! Example code to send/receive data using `nusb`:
//!
//! ```ignore
//! use futures_lite::future::block_on;
//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
//!
//! fn main() {
//! let di = nusb::list_devices()
//! .unwrap()
//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)
//! .expect("no device found");
//! let device = di.open().expect("error opening device");
//! let interface = device.claim_interface(0).expect("error claiming interface");
//!
//! // Send "hello world" to device
//! let result = block_on(interface.control_out(ControlOut {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 100,
//! value: 200,
//! index: 0,
//! data: b"hello world",
//! }));
//! println!("{result:?}");
//!
//! // Receive "hello" from device
//! let result = block_on(interface.control_in(ControlIn {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 101,
//! value: 201,
//! index: 0,
//! length: 5,
//! }));
//! println!("{result:?}");
//! }
//! ```
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_rp::bind_interrupts;
use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler};
use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::types::InterfaceNumber;
use embassy_usb::{Builder, Config, Handler};
use {defmt_rtt as _, panic_probe as _};
// This is a randomly generated GUID to allow clients on Windows to find our device
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
bind_interrupts!(struct Irqs {
USBCTRL_IRQ => InterruptHandler<USB>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello there!");
let p = embassy_rp::init(Default::default());
// Create the driver, from the HAL.
let driver = Driver::new(p.USB, Irqs);
// Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB raw example");
config.serial_number = Some("12345678");
config.max_power = 100;
config.max_packet_size_0 = 64;
// // Required for windows compatibility.
// // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
config.device_class = 0xEF;
config.device_sub_class = 0x02;
config.device_protocol = 0x01;
config.composite_with_iads = true;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut handler = ControlHandler {
if_num: InterfaceNumber(0),
};
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let _alt = interface.alt_setting(0xFF, 0, 0, None);
handler.if_num = interface.interface_number();
drop(function);
builder.handler(&mut handler);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
usb.run().await;
}
/// Handle CONTROL endpoint requests and responses. For many simple requests and responses
/// you can get away with only using the control endpoint.
struct ControlHandler {
if_num: InterfaceNumber,
}
impl Handler for ControlHandler {
/// Respond to HostToDevice control messages, where the host sends us a command and
/// optionally some data, and we can only acknowledge or reject it.
fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option<OutResponse> {
// Log the request before filtering to help with debugging.
info!("Got control_out, request={}, buf={:a}", req, buf);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Accept request 100, value 200, reject others.
if req.request == 100 && req.value == 200 {
Some(OutResponse::Accepted)
} else {
Some(OutResponse::Rejected)
}
}
/// Respond to DeviceToHost control messages, where the host requests some data from us.
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
info!("Got control_in, request={}", req);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Respond "hello" to request 101, value 201, when asked for 5 bytes, otherwise reject.
if req.request == 101 && req.value == 201 && req.length == 5 {
buf[..5].copy_from_slice(b"hello");
Some(InResponse::Accepted(&buf[..5]))
} else {
Some(InResponse::Rejected)
}
}
}

View File

@ -0,0 +1,142 @@
//! Example of using USB without a pre-defined class, but instead using raw USB bulk transfers.
//!
//! Example code to send/receive data using `nusb`:
//!
//! ```ignore
//! use futures_lite::future::block_on;
//! use nusb::transfer::RequestBuffer;
//!
//! const BULK_OUT_EP: u8 = 0x01;
//! const BULK_IN_EP: u8 = 0x81;
//!
//! fn main() {
//! let di = nusb::list_devices()
//! .unwrap()
//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)
//! .expect("no device found");
//! let device = di.open().expect("error opening device");
//! let interface = device.claim_interface(0).expect("error claiming interface");
//!
//! let result = block_on(interface.bulk_out(BULK_OUT_EP, b"hello world".into()));
//! println!("{result:?}");
//! let result = block_on(interface.bulk_in(BULK_IN_EP, RequestBuffer::new(64)));
//! println!("{result:?}");
//! }
//! ```
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_rp::bind_interrupts;
use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler};
use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::{Builder, Config};
use {defmt_rtt as _, panic_probe as _};
// This is a randomly generated GUID to allow clients on Windows to find our device
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
bind_interrupts!(struct Irqs {
USBCTRL_IRQ => InterruptHandler<USB>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello there!");
let p = embassy_rp::init(Default::default());
// Create the driver, from the HAL.
let driver = Driver::new(p.USB, Irqs);
// Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB raw example");
config.serial_number = Some("12345678");
config.max_power = 100;
config.max_packet_size_0 = 64;
// // Required for windows compatibility.
// // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
config.device_class = 0xEF;
config.device_sub_class = 0x02;
config.device_protocol = 0x01;
config.composite_with_iads = true;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let mut alt = interface.alt_setting(0xFF, 0, 0, None);
let mut read_ep = alt.endpoint_bulk_out(64);
let mut write_ep = alt.endpoint_bulk_in(64);
drop(function);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
let usb_fut = usb.run();
// Do stuff with the class!
let echo_fut = async {
loop {
read_ep.wait_enabled().await;
info!("Connected");
loop {
let mut data = [0; 64];
match read_ep.read(&mut data).await {
Ok(n) => {
info!("Got bulk: {:a}", data[..n]);
// Echo back to the host:
write_ep.write(&data[..n]).await.ok();
}
Err(_) => break,
}
}
info!("Disconnected");
}
};
// Run everything concurrently.
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
join(usb_fut, echo_fut).await;
}

View File

@ -60,6 +60,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["log"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["log", "std", "nightly"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] }
embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" }
@ -14,7 +14,6 @@ embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features
embedded-io-async = { version = "0.6.0" }
embedded-io-adapters = { version = "0.6.0", features = ["futures-03"] }
critical-section = { version = "1.1", features = ["std"] }
smoltcp = { version = "0.10.0", features = ["dns-max-server-count-4"] }
async-io = "1.6.0"
env_logger = "0.9.0"
@ -23,7 +22,7 @@ log = "0.4.14"
nix = "0.26.2"
clap = { version = "3.0.0-beta.5", features = ["derive"] }
rand_core = { version = "0.6.3", features = ["std"] }
heapless = { version = "0.7.5", default-features = false }
heapless = { version = "0.8", default-features = false }
static_cell = { version = "2", features = ["nightly"]}
[profile.release]

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
# Change stm32c031c6 to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
defmt = "0.3"
@ -19,7 +19,7 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
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 }
heapless = { version = "0.8", default-features = false }
[profile.release]
debug = 2

View File

@ -15,7 +15,7 @@ defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = "0.3"
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
static_cell = { version = "2", features = ["nightly"]}
portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] }

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
# Change stm32f103c8 to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@ -21,7 +21,7 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
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 }
heapless = { version = "0.8", default-features = false }
nb = "1.0.0"
[profile.dev]

View File

@ -60,6 +60,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
# Change stm32f207zg to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
defmt = "0.3"
@ -19,7 +19,7 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
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 }
heapless = { version = "0.8", default-features = false }
nb = "1.0.0"
[profile.release]

View File

@ -6,9 +6,6 @@ use core::convert::TryFrom;
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::rcc::{
APBPrescaler, ClockSrc, HSEConfig, HSESrc, PLLConfig, PLLMul, PLLPDiv, PLLPreDiv, PLLQDiv, PLLSrc,
};
use embassy_stm32::time::Hertz;
use embassy_stm32::Config;
use embassy_time::Timer;
@ -19,29 +16,35 @@ async fn main(_spawner: Spawner) {
// Example config for maximum performance on a NUCLEO-F207ZG board
let mut config = Config::default();
// By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)
config.rcc.hse = Some(HSEConfig {
frequency: Hertz(8_000_000),
source: HSESrc::Bypass,
});
// PLL uses HSE as the clock source
config.rcc.pll_mux = PLLSrc::HSE;
config.rcc.pll = PLLConfig {
// 8 MHz clock source / 8 = 1 MHz PLL input
pre_div: unwrap!(PLLPreDiv::try_from(8)),
// 1 MHz PLL input * 240 = 240 MHz PLL VCO
mul: unwrap!(PLLMul::try_from(240)),
// 240 MHz PLL VCO / 2 = 120 MHz main PLL output
p_div: PLLPDiv::DIV2,
// 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
q_div: PLLQDiv::DIV5,
};
// System clock comes from PLL (= the 120 MHz main PLL output)
config.rcc.mux = ClockSrc::PLL;
// 120 MHz / 4 = 30 MHz APB1 frequency
config.rcc.apb1_pre = APBPrescaler::DIV4;
// 120 MHz / 2 = 60 MHz APB2 frequency
config.rcc.apb2_pre = APBPrescaler::DIV2;
{
use embassy_stm32::rcc::*;
// By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
// PLL uses HSE as the clock source
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
// 8 MHz clock source / 8 = 1 MHz PLL input
prediv: unwrap!(PllPreDiv::try_from(8)),
// 1 MHz PLL input * 240 = 240 MHz PLL VCO
mul: unwrap!(PllMul::try_from(240)),
// 240 MHz PLL VCO / 2 = 120 MHz main PLL output
divp: Some(PllPDiv::DIV2),
// 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
divq: Some(PllQDiv::DIV5),
divr: None,
});
// System clock comes from PLL (= the 120 MHz main PLL output)
config.rcc.sys = Sysclk::PLL1_P;
// 120 MHz / 4 = 30 MHz APB1 frequency
config.rcc.apb1_pre = APBPrescaler::DIV4;
// 120 MHz / 2 = 60 MHz APB2 frequency
config.rcc.apb2_pre = APBPrescaler::DIV2;
}
let _p = embassy_stm32::init(config);

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
# Change stm32f303ze to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@ -21,7 +21,7 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
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 }
heapless = { version = "0.8", default-features = false }
nb = "1.0.0"
embedded-storage = "0.3.0"
static_cell = { version = "2", features = ["nightly"]}

View File

@ -57,6 +57,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);

View File

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
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", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
@ -20,7 +20,7 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
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 }
heapless = { version = "0.8", default-features = false }
nb = "1.0.0"
embedded-storage = "0.3.0"
static_cell = { version = "2", features = ["nightly"]}

View File

@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) -> ! {
let mut vrefint = adc.enable_vref(&mut Delay);
let mut temperature = adc.enable_temperature();
let mut buffer = opamp.buffer_for(&mut p.PA7, OpAmpGain::Mul1);
let mut buffer = opamp.buffer_ext(&p.PA7, &mut p.PA6, OpAmpGain::Mul1);
loop {
let vref = adc.read(&mut vrefint).await;

View File

@ -8,9 +8,9 @@ license = "MIT OR Apache-2.0"
# Change stm32f429zi to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt" ] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
defmt = "0.3"
@ -23,7 +23,7 @@ embedded-io = { version = "0.6.0" }
embedded-io-async = { version = "0.6.0" }
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 }
heapless = { version = "0.8", default-features = false }
nb = "1.0.0"
embedded-storage = "0.3.0"
micromath = "2.0.0"

View File

@ -42,7 +42,7 @@ async fn main(spawner: Spawner) -> ! {
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL180,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
divq: None,
divr: None,
});

View File

@ -30,8 +30,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;

View File

@ -56,8 +56,8 @@ async fn main(spawner: Spawner) {
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
@ -94,6 +94,7 @@ async fn main(spawner: Spawner) {
&mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..],
&mut [], // no msos descriptors
&mut make_static!([0; 128])[..],
);

View File

@ -0,0 +1,222 @@
//! Example of using USB without a pre-defined class, but instead responding to
//! raw USB control requests.
//!
//! The host computer can either:
//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer
//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive
//!
//! For higher throughput data, you can add some bulk endpoints after creating the alternate,
//! but for low rate command/response, plain control transfers can be very simple and effective.
//!
//! Example code to send/receive data using `nusb`:
//!
//! ```ignore
//! use futures_lite::future::block_on;
//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
//!
//! fn main() {
//! let di = nusb::list_devices()
//! .unwrap()
//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)
//! .expect("no device found");
//! let device = di.open().expect("error opening device");
//! let interface = device.claim_interface(0).expect("error claiming interface");
//!
//! // Send "hello world" to device
//! let result = block_on(interface.control_out(ControlOut {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 100,
//! value: 200,
//! index: 0,
//! data: b"hello world",
//! }));
//! println!("{result:?}");
//!
//! // Receive "hello" from device
//! let result = block_on(interface.control_in(ControlIn {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 101,
//! value: 201,
//! index: 0,
//! length: 5,
//! }));
//! println!("{result:?}");
//! }
//! ```
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::Driver;
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::types::InterfaceNumber;
use embassy_usb::{Builder, Handler};
use {defmt_rtt as _, panic_probe as _};
// Randomly generated UUID because Windows requires you provide one to use WinUSB.
// In principle WinUSB-using software could find this device (or a specific interface
// on it) by its GUID instead of using the VID/PID, but in practice that seems unhelpful.
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{DAC2087C-63FA-458D-A55D-827C0762DEC7}"];
bind_interrupts!(struct Irqs {
OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168,
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
let p = embassy_stm32::init(config);
// Create the driver, from the HAL.
let mut ep_out_buffer = [0u8; 256];
let mut config = embassy_stm32::usb_otg::Config::default();
config.vbus_detection = true;
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
// Create embassy-usb Config
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB-raw example");
config.serial_number = Some("12345678");
// Required for windows compatibility.
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
config.device_class = 0xEF;
config.device_sub_class = 0x02;
config.device_protocol = 0x01;
config.composite_with_iads = true;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut handler = ControlHandler {
if_num: InterfaceNumber(0),
};
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let _alternate = interface.alt_setting(0xFF, 0, 0, None);
handler.if_num = interface.interface_number();
drop(function);
builder.handler(&mut handler);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
usb.run().await;
}
/// Handle CONTROL endpoint requests and responses. For many simple requests and responses
/// you can get away with only using the control endpoint.
struct ControlHandler {
if_num: InterfaceNumber,
}
impl Handler for ControlHandler {
/// Respond to HostToDevice control messages, where the host sends us a command and
/// optionally some data, and we can only acknowledge or reject it.
fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option<OutResponse> {
// Log the request before filtering to help with debugging.
info!("Got control_out, request={}, buf={:a}", req, buf);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Accept request 100, value 200, reject others.
if req.request == 100 && req.value == 200 {
Some(OutResponse::Accepted)
} else {
Some(OutResponse::Rejected)
}
}
/// Respond to DeviceToHost control messages, where the host requests some data from us.
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
info!("Got control_in, request={}", req);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Respond "hello" to request 101, value 201, when asked for 5 bytes, otherwise reject.
if req.request == 101 && req.value == 201 && req.length == 5 {
buf[..5].copy_from_slice(b"hello");
Some(InResponse::Accepted(&buf[..5]))
} else {
Some(InResponse::Rejected)
}
}
}

View File

@ -32,8 +32,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
@ -77,6 +77,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
# Change stm32f767zi to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
embedded-io-async = { version = "0.6.0" }
@ -22,7 +22,7 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
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 }
heapless = { version = "0.8", default-features = false }
nb = "1.0.0"
rand_core = "0.6.3"
critical-section = "1.1"

View File

@ -43,7 +43,7 @@ async fn main(spawner: Spawner) -> ! {
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: None,
divr: None,
});

View File

@ -26,8 +26,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(PllQDiv::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;

View File

@ -32,8 +32,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz
divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(PllQDiv::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
@ -77,6 +77,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
# Change stm32g071rb to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
defmt = "0.3"
@ -19,7 +19,7 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
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 }
heapless = { version = "0.8", default-features = false }
portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] }
[profile.release]

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
# Change stm32g491re to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@ -22,7 +22,7 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
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 }
heapless = { version = "0.8", default-features = false }
[profile.release]
debug = 2

View File

@ -5,7 +5,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::adc::{Adc, SampleTime};
use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSrc};
use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSource};
use embassy_stm32::Config;
use embassy_time::{Delay, Timer};
use {defmt_rtt as _, panic_probe as _};
@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.pll = Some(Pll {
source: PllSrc::HSI,
source: PllSource::HSI,
prediv_m: PllM::DIV4,
mul_n: PllN::MUL85,
div_p: None,

View File

@ -4,7 +4,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllR, PllSrc};
use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllR, PllSource};
use embassy_stm32::Config;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.pll = Some(Pll {
source: PllSrc::HSI,
source: PllSource::HSI,
prediv_m: PllM::DIV4,
mul_n: PllN::MUL85,
div_p: None,

View File

@ -4,7 +4,7 @@
use defmt::{panic, *};
use embassy_executor::Spawner;
use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc};
use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, Hsi48Config, Pll, PllM, PllN, PllQ, PllR, PllSource};
use embassy_stm32::time::Hertz;
use embassy_stm32::usb::{self, Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, Config};
@ -25,14 +25,14 @@ async fn main(_spawner: Spawner) {
// Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE.
const USE_HSI48: bool = true;
let pllq_div = if USE_HSI48 { None } else { Some(PllQ::DIV6) };
let plldivq = if USE_HSI48 { None } else { Some(PllQ::DIV6) };
config.rcc.pll = Some(Pll {
source: PllSrc::HSE(Hertz(8_000_000)),
source: PllSource::HSE(Hertz(8_000_000)),
prediv_m: PllM::DIV2,
mul_n: PllN::MUL72,
div_p: None,
div_q: pllq_div,
div_q: plldivq,
// Main system clock at 144 MHz
div_r: Some(PllR::DIV2),
});
@ -41,9 +41,7 @@ async fn main(_spawner: Spawner) {
if USE_HSI48 {
// Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig {
sync_src: CrsSyncSource::Usb,
})));
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true }));
} else {
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ);
}
@ -77,6 +75,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
# Change stm32h563zi to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] }
embedded-io-async = { version = "0.6.0" }
@ -22,10 +22,10 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embedded-nal-async = { version = "0.6.0" }
embedded-nal-async = { version = "0.7" }
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 }
heapless = { version = "0.8", default-features = false }
rand_core = "0.6.3"
critical-section = "1.1"
micromath = "2.0.0"

Some files were not shown because too many files have changed in this diff Show More