Compare commits

...

35 Commits

Author SHA1 Message Date
630443a4d6 net-wiznet: report link up/down on cable plug/unplug. 2023-10-20 01:29:10 +02:00
035800bfbd Merge pull request #2091 from embassy-rs/comment-memory-x
docs: add linker script comments
2023-10-19 08:19:42 +00:00
c7803bb8f4 docs: add linker script comments
Existing comment were outdated. Provide an example configuration
for using the softdevice with the nRF52 examples.
2023-10-19 09:29:20 +02:00
d496a1213c Merge pull request #2090 from eZioPan/rcc-init-bypass-oden
bypass `ODEN` in `rcc::init()` if chip doesn't have it
2023-10-18 12:15:30 +00:00
241488ef1c bypass ODEN if chip doesn't have it 2023-10-18 19:42:31 +08:00
88b2cdd6a0 Merge pull request #2087 from riley-williams/rp2040-pwm-docs
Add docs to RP2040 PWM config
2023-10-18 06:30:08 +00:00
35ffdf2143 Merge pull request #2076 from embassy-rs/net-driver-simplify
net/driver: remove Medium, make HardwareAddress non_exhaustive.
2023-10-18 03:39:41 +00:00
3cbc687424 net/driver: remove Medium, make HardwareAddress non_exhaustive. 2023-10-18 05:28:16 +02:00
4f7b831676 Merge pull request #2088 from embassy-rs/rcc-no-spaghetti
stm32: rcc no spaghetti
2023-10-18 03:23:47 +00:00
f20f170b1f stm32/rcc: refactor and unify f4 into f7. 2023-10-18 05:11:31 +02:00
67010d123c stm32/rcc: refactor f7. 2023-10-18 05:01:11 +02:00
51708c8ed1 Merge pull request #2089 from artisdom/patch-1
Update basic_application.adoc
2023-10-18 02:59:48 +00:00
361fde35cf stm32/rcc: wait for mux switch. 2023-10-18 04:32:18 +02:00
7ce3b19389 stm32/rcc: remove unused enum. 2023-10-18 04:32:18 +02:00
10f08445e4 Update basic_application.adoc
typo: change "embassy::main" to "embassy_executor::main"
2023-10-18 14:53:49 +13:00
f24a1b62bb Merge pull request #2085 from xoviat/rcc
stm32: update metapac
2023-10-18 01:33:00 +00:00
bbd12c9372 stm32: update metapac 2023-10-17 20:31:44 -05:00
6906cc9c25 remove trailing spaces 2023-10-17 19:30:53 -04:00
cb211f88d3 Grammar and formatting 2023-10-17 19:17:29 -04:00
3f262a2603 Add docs to RP2040 PWM 2023-10-17 19:05:35 -04:00
d94b9fe6fb Merge pull request #2082 from embassy-rs/stm32wl-hil
stm32/tests: add stm32wl hil.
2023-10-17 14:58:53 +00:00
b478640463 fix clocks in stm32wl rng example. 2023-10-17 15:57:09 +02:00
846f2fc6e4 stm32/tests: add stm32wl hil. 2023-10-17 15:57:09 +02:00
683d5c3066 Merge pull request #2077 from xoviat/rcc
stm32: update metapac
2023-10-17 01:05:18 +00:00
a3574e519a stm32: update metapac 2023-10-16 20:04:10 -05:00
3e3317e8bd Merge pull request #2078 from GrantM11235/prefetch
stm32f1: Keep flash prefetch enabled
2023-10-17 00:29:30 +00:00
e7aeb9b29f stm32f1: Keep flash prefetch enabled 2023-10-16 19:23:01 -05:00
7fd868ade9 Merge pull request #2068 from barafael/const_usb_config_builder_new
Constify UsbDevice Config::new (and clippy fixes) in embassy-usb
2023-10-16 23:23:10 +00:00
6e6df22979 Merge pull request #2075 from CBJamo/rosc_example
Add example to show useage of rp2040 rosc
2023-10-16 23:22:06 +00:00
f7980885a5 Merge pull request #2066 from bugadani/net
Prepare embassy-net 0.2.0
2023-10-16 21:19:29 +00:00
5a1393aa0b Add example to show useage of rp2040 rosc 2023-10-16 16:17:07 -04:00
40e4ca4751 Prepare embassy-net(/-driver,/-driver-channel) 0.2.0 2023-10-16 20:59:06 +02:00
31d4516516 Apply Pedantic Clippy Lints 2023-10-15 23:52:44 +02:00
66e62e9994 Fix clippy 2023-10-15 22:25:35 +02:00
eeedaf2e76 Constify Config::new 2023-10-15 22:11:30 +02:00
95 changed files with 1096 additions and 1138 deletions

1
ci.sh
View File

@ -204,6 +204,7 @@ cargo batch \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f207zg --out-dir out/tests/stm32f207zg \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \

View File

@ -14,7 +14,7 @@ firmware-logs = []
embassy-time = { version = "0.1.5", path = "../embassy-time"}
embassy-sync = { version = "0.3.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
defmt = { version = "0.3", optional = true }
log = { version = "0.4.17", optional = true }

View File

@ -1,7 +1,7 @@
use core::cmp::{max, min};
use ch::driver::LinkState;
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
use embassy_time::Timer;
pub use crate::bus::SpiBusCyw43;
@ -133,7 +133,7 @@ impl<'a> Control<'a> {
Timer::after_millis(100).await;
self.state_ch.set_ethernet_address(mac_addr);
self.state_ch.set_hardware_address(HardwareAddress::Ethernet(mac_addr));
debug!("INIT DONE");
}

View File

@ -48,7 +48,7 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera
include::example$basic/src/main.rs[lines="22..-1"]
----
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following:
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following:
. Creates an Embassy Executor
. Initializes the microcontroller HAL to get the `Peripherals`

View File

@ -16,7 +16,7 @@ log = { version = "0.4", default-features = false, optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.1.5", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
bitfield = "0.14.0"

View File

@ -0,0 +1,16 @@
# Changelog
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.2.0 - 2023-10-18
- Update `embassy-net-driver` to v0.2
- `Runner::new` now takes an `embassy_net_driver::HardwareAddress` parameter.
- `Runner::set_ethernet_address` is now `set_hardware_address`.
## 0.1.0 - 2023-06-29
- First release

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-net-driver-channel"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
@ -26,4 +26,4 @@ log = { version = "0.4.14", optional = true }
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }

View File

@ -7,7 +7,9 @@ The `embassy-net-driver` trait is polling-based. To implement it, you must write
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
knows when to poll your driver again to make more progress.
With `embassy-net-driver-channel`
With `embassy-net-driver-channel` you get a "channel-like" interface instead, where you can send/receive packets
to/from embassy-net. The intended usage is to spawn a "driver task" in the background that does this, passing
packets between the hardware and the channel.
## A note about deadlocks
@ -18,19 +20,19 @@ loop {
// Wait for either..
match select(
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
irq_pin.wait_for_low(),
irq_pin.wait_for_low(),
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
tx_chan.tx_buf(),
).await {
Either::First(_) => {
// a packet is ready to be received!
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
let n = receive_packet_over_spi(buf).await;
let n = receive_packet_over_spi(buf).await;
rx_chan.rx_done(n);
}
Either::Second(buf) => {
// a packet is ready to be sent!
send_packet_over_spi(buf).await;
send_packet_over_spi(buf).await;
tx_chan.tx_done();
}
}
@ -41,7 +43,7 @@ However, this code has a latent deadlock bug. The symptom is it can hang at `rx_
The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the RX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
```rust,ignore
loop {
@ -58,12 +60,12 @@ loop {
).await {
Either::First(buf) => {
// a packet is ready to be received!
let n = receive_packet_over_spi(buf).await;
let n = receive_packet_over_spi(buf).await;
rx_chan.rx_done(n);
}
Either::Second(buf) => {
// a packet is ready to be sent!
send_packet_over_spi(buf).await;
send_packet_over_spi(buf).await;
tx_chan.tx_done();
}
}
@ -79,12 +81,10 @@ These `embassy-net` drivers are implemented using this crate. You can look at th
- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips.
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
## Interoperability
This crate can run on any executor.
## License
This work is licensed under either of

View File

@ -8,9 +8,8 @@ use core::cell::RefCell;
use core::mem::MaybeUninit;
use core::task::{Context, Poll};
use driver::HardwareAddress;
pub use embassy_net_driver as driver;
use embassy_net_driver::{Capabilities, LinkState, Medium};
use embassy_net_driver::{Capabilities, LinkState};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::waitqueue::WakerRegistration;
@ -161,18 +160,10 @@ impl<'d> StateRunner<'d> {
});
}
pub fn set_ethernet_address(&self, address: [u8; 6]) {
pub fn set_hardware_address(&self, address: driver::HardwareAddress) {
self.shared.lock(|s| {
let s = &mut *s.borrow_mut();
s.hardware_address = driver::HardwareAddress::Ethernet(address);
s.waker.wake();
});
}
pub fn set_ieee802154_address(&self, address: [u8; 8]) {
self.shared.lock(|s| {
let s = &mut *s.borrow_mut();
s.hardware_address = driver::HardwareAddress::Ieee802154(address);
s.hardware_address = address;
s.waker.wake();
});
}
@ -232,11 +223,6 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
caps.medium = match &hardware_address {
HardwareAddress::Ethernet(_) => Medium::Ethernet,
HardwareAddress::Ieee802154(_) => Medium::Ieee802154,
HardwareAddress::Ip => Medium::Ip,
};
// safety: this is a self-referential struct, however:
// - it can't move while the `'d` borrow is active.

View File

@ -0,0 +1,17 @@
# Changelog
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.2.0 - 2023-10-18
- Added support for IEEE 802.15.4 mediums.
- Added `Driver::hardware_address()`, `HardwareAddress`.
- Removed `Medium` enum. The medium is deduced out of the hardware address.
- Removed `Driver::ethernet_address()`. Replacement is `hardware_address()`.
## 0.1.0 - 2023-06-29
- First release

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-net-driver"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Driver trait for the `embassy-net` async TCP/IP network stack."

View File

@ -7,12 +7,23 @@ use core::task::Context;
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum HardwareAddress {
/// A six-octet Ethernet address
/// Ethernet medium, with a A six-octet Ethernet address.
///
/// Devices of this type send and receive Ethernet frames,
/// and interfaces using it must do neighbor discovery via ARP or NDISC.
///
/// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
Ethernet([u8; 6]),
/// An eight-octet IEEE802.15.4 address
/// 6LoWPAN over IEEE802.15.4, with an eight-octet address.
Ieee802154([u8; 8]),
/// Indicates that a Driver is IP-native, and has no hardware address
/// Indicates that a Driver is IP-native, and has no hardware address.
///
/// Devices of this type send and receive IP frames, without an
/// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
///
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
Ip,
}
@ -64,6 +75,10 @@ pub trait Driver {
fn capabilities(&self) -> Capabilities;
/// Get the device's hardware address.
///
/// The returned hardware address also determines the "medium" of this driver. This indicates
/// what kind of packet the sent/received bytes are, and determines some behaviors of
/// the interface. For example, ARP/NDISC address resolution is only done for Ethernet mediums.
fn hardware_address(&self) -> HardwareAddress;
}
@ -124,13 +139,6 @@ pub trait TxToken {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Capabilities {
/// Medium of the device.
///
/// This indicates what kind of packet the sent/received bytes are, and determines
/// some behaviors of Interface. For example, ARP/NDISC address resolution is only done
/// for Ethernet mediums.
pub medium: Medium,
/// Maximum transmission unit.
///
/// The network device is unable to send or receive frames larger than the value returned
@ -161,32 +169,6 @@ pub struct Capabilities {
pub checksum: ChecksumCapabilities,
}
/// Type of medium of a device.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Medium {
/// Ethernet medium. Devices of this type send and receive Ethernet frames,
/// and interfaces using it must do neighbor discovery via ARP or NDISC.
///
/// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
Ethernet,
/// IP medium. Devices of this type send and receive IP frames, without an
/// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
///
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
Ip,
/// IEEE 802_15_4 medium
Ieee802154,
}
impl Default for Medium {
fn default() -> Medium {
Medium::Ethernet
}
}
/// A description of checksum behavior for every supported protocol.
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]

View File

@ -10,7 +10,7 @@ edition = "2021"
[dependencies]
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
embassy-time = { version = "0.1.5", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }

View File

@ -19,7 +19,7 @@ mod traits;
use core::cmp;
use core::convert::TryInto;
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
use embassy_time::Duration;
use embedded_hal::digital::OutputPin;
use embedded_hal::spi::{Operation, SpiDevice};
@ -671,7 +671,6 @@ where
fn capabilities(&self) -> Capabilities {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
caps.medium = Medium::Ethernet;
caps
}

View File

@ -10,7 +10,7 @@ log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.5", path = "../embassy-time" }
embassy-sync = { version = "0.3.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }

View File

@ -1,5 +1,5 @@
use ch::driver::LinkState;
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
use heapless::String;
use crate::ioctl::Shared;
@ -77,7 +77,7 @@ impl<'a> Control<'a> {
let mac_addr = self.get_mac_addr().await?;
debug!("mac addr: {:02x}", mac_addr);
self.state_ch.set_ethernet_address(mac_addr);
self.state_ch.set_hardware_address(HardwareAddress::Ethernet(mac_addr));
Ok(())
}

View File

@ -16,7 +16,7 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embedded-io-async = { version = "0.6.0" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
ppproto = { version = "0.1.2"}
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
async-io = "1.6.0"
log = "0.4.14"
libc = "0.2.101"

View File

@ -10,7 +10,7 @@ edition = "2021"
[dependencies]
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.1.5", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true }

View File

@ -1,14 +1,14 @@
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for WIZnet ethernet chips.
#![no_std]
#![feature(async_fn_in_trait)]
#![doc = include_str!("../README.md")]
pub mod chip;
mod device;
use embassy_futures::select::{select, Either};
use embassy_futures::select::{select3, Either3};
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::LinkState;
use embassy_time::Timer;
use embassy_time::{Duration, Ticker, Timer};
use embedded_hal::digital::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiDevice;
@ -49,32 +49,34 @@ pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
pub async fn run(mut self) -> ! {
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
let mut tick = Ticker::every(Duration::from_millis(500));
loop {
if self.mac.is_link_up().await {
state_chan.set_link_state(LinkState::Up);
loop {
match select(
async {
self.int.wait_for_low().await.ok();
rx_chan.rx_buf().await
},
tx_chan.tx_buf(),
)
.await
{
Either::First(p) => {
if let Ok(n) = self.mac.read_frame(p).await {
rx_chan.rx_done(n);
}
}
Either::Second(p) => {
self.mac.write_frame(p).await.ok();
tx_chan.tx_done();
}
match select3(
async {
self.int.wait_for_low().await.ok();
rx_chan.rx_buf().await
},
tx_chan.tx_buf(),
tick.next(),
)
.await
{
Either3::First(p) => {
if let Ok(n) = self.mac.read_frame(p).await {
rx_chan.rx_done(n);
}
}
Either3::Second(p) => {
self.mac.write_frame(p).await.ok();
tx_chan.tx_done();
}
Either3::Third(()) => {
if self.mac.is_link_up().await {
state_chan.set_link_state(LinkState::Up);
} else {
state_chan.set_link_state(LinkState::Down);
}
}
} else {
state_chan.set_link_state(LinkState::Down);
}
}
}

29
embassy-net/CHANGELOG.md Normal file
View File

@ -0,0 +1,29 @@
# Changelog
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.2.0 - 2023-10-18
- Re-export `smoltcp::wire::IpEndpoint`
- Add poll functions on UdpSocket
- Make dual-stack work in embassy-net
- Fix multicast support
- Allow ethernet and 802.15.4 to coexist
- Add IEEE802.15.4 address to embassy net Stack
- Use HardwareAddress in Driver
- Add async versions of smoltcp's `send` and `recv` closure based API
- add error translation to tcp errors
- Forward TCP/UDP socket capacity impls
- allow changing IP config at runtime
- allow non-'static drivers
- Remove impl_trait_projections
- update embedded-io, embedded-nal-async
- add support for dhcp hostname option
- Wake stack's task after queueing a DNS query
## 0.1.0 - 2023-06-29
- First release

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-net"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Async TCP/IP network stack for embedded systems"
@ -51,7 +51,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
"async",
] }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
embassy-time = { version = "0.1.5", path = "../embassy-time" }
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
embedded-io-async = { version = "0.6.0", optional = true }

View File

@ -1,7 +1,7 @@
use core::task::Context;
use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken};
use smoltcp::phy;
use embassy_net_driver::{Capabilities, Checksum, Driver, RxToken, TxToken};
use smoltcp::phy::{self, Medium};
use smoltcp::time::Instant;
pub(crate) struct DriverAdapter<'d, 'c, T>
@ -11,6 +11,7 @@ where
// must be Some when actually using this to rx/tx
pub cx: Option<&'d mut Context<'c>>,
pub inner: &'d mut T,
pub medium: Medium,
}
impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T>
@ -46,19 +47,7 @@ where
smolcaps.max_transmission_unit = caps.max_transmission_unit;
smolcaps.max_burst_size = caps.max_burst_size;
smolcaps.medium = match caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => phy::Medium::Ethernet,
#[cfg(feature = "medium-ip")]
Medium::Ip => phy::Medium::Ip,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => phy::Medium::Ieee802154,
#[allow(unreachable_patterns)]
_ => panic!(
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
caps.medium
),
};
smolcaps.medium = self.medium;
smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4);
smolcaps.checksum.tcp = convert(caps.checksum.tcp);
smolcaps.checksum.udp = convert(caps.checksum.udp);

View File

@ -33,6 +33,7 @@ use heapless::Vec;
pub use smoltcp::iface::MulticastError;
#[allow(unused_imports)]
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
use smoltcp::phy::Medium;
#[cfg(feature = "dhcpv4")]
use smoltcp::socket::dhcpv4::{self, RetryConfig};
#[cfg(feature = "medium-ethernet")]
@ -264,14 +265,17 @@ pub(crate) struct SocketStack {
next_local_port: u16,
}
fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress {
fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) {
match addr {
#[cfg(feature = "medium-ethernet")]
driver::HardwareAddress::Ethernet(eth) => HardwareAddress::Ethernet(EthernetAddress(eth)),
driver::HardwareAddress::Ethernet(eth) => (HardwareAddress::Ethernet(EthernetAddress(eth)), Medium::Ethernet),
#[cfg(feature = "medium-ieee802154")]
driver::HardwareAddress::Ieee802154(ieee) => HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),
driver::HardwareAddress::Ieee802154(ieee) => (
HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),
Medium::Ieee802154,
),
#[cfg(feature = "medium-ip")]
driver::HardwareAddress::Ip => HardwareAddress::Ip,
driver::HardwareAddress::Ip => (HardwareAddress::Ip, Medium::Ip),
#[allow(unreachable_patterns)]
_ => panic!(
@ -289,7 +293,8 @@ impl<D: Driver> Stack<D> {
resources: &'static mut StackResources<SOCK>,
random_seed: u64,
) -> Self {
let mut iface_cfg = smoltcp::iface::Config::new(to_smoltcp_hardware_address(device.hardware_address()));
let (hardware_addr, medium) = to_smoltcp_hardware_address(device.hardware_address());
let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr);
iface_cfg.random_seed = random_seed;
let iface = Interface::new(
@ -297,6 +302,7 @@ impl<D: Driver> Stack<D> {
&mut DriverAdapter {
inner: &mut device,
cx: None,
medium,
},
instant_to_smoltcp(Instant::now()),
);
@ -356,7 +362,7 @@ impl<D: Driver> Stack<D> {
/// Get the hardware address of the network interface.
pub fn hardware_address(&self) -> HardwareAddress {
self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address()))
self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address()).0)
}
/// Get whether the link is up.
@ -812,18 +818,28 @@ impl<D: Driver> Inner<D> {
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
s.waker.register(cx.waker());
let (_hardware_addr, medium) = to_smoltcp_hardware_address(self.device.hardware_address());
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
if self.device.capabilities().medium == embassy_net_driver::Medium::Ethernet
|| self.device.capabilities().medium == embassy_net_driver::Medium::Ieee802154
{
s.iface
.set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
let do_set = match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => true,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => true,
#[allow(unreachable_patterns)]
_ => false,
};
if do_set {
s.iface.set_hardware_addr(_hardware_addr);
}
}
let timestamp = instant_to_smoltcp(Instant::now());
let mut smoldev = DriverAdapter {
cx: Some(cx),
inner: &mut self.device,
medium,
};
s.iface.poll(timestamp, &mut smoldev, &mut s.sockets);

View File

@ -10,16 +10,39 @@ use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::{pac, peripherals, RegExt};
/// The configuration of a PWM slice.
/// Note the period in clock cycles of a slice can be computed as:
/// `(top + 1) * (phase_correct ? 1 : 2) * divider`
#[non_exhaustive]
#[derive(Clone)]
pub struct Config {
/// Inverts the PWM output signal on channel A.
pub invert_a: bool,
/// Inverts the PWM output signal on channel B.
pub invert_b: bool,
/// Enables phase-correct mode for PWM operation.
/// In phase-correct mode, the PWM signal is generated in such a way that
/// the pulse is always centered regardless of the duty cycle.
/// The output frequency is halved when phase-correct mode is enabled.
pub phase_correct: bool,
/// Enables the PWM slice, allowing it to generate an output.
pub enable: bool,
/// A fractional clock divider, represented as a fixed-point number with
/// 8 integer bits and 4 fractional bits. It allows precise control over
/// the PWM output frequency by gating the PWM counter increment.
/// A higher value will result in a slower output frequency.
pub divider: fixed::FixedU16<fixed::types::extra::U4>,
/// The output on channel A goes high when `compare_a` is higher than the
/// counter. A compare of 0 will produce an always low output, while a
/// compare of `top + 1` will produce an always high output.
pub compare_a: u16,
/// The output on channel B goes high when `compare_b` is higher than the
/// counter. A compare of 0 will produce an always low output, while a
/// compare of `top + 1` will produce an always high output.
pub compare_b: u16,
/// The point at which the counter wraps, representing the maximum possible
/// period. The counter will either wrap to 0 or reverse depending on the
/// setting of `phase_correct`.
pub top: u16,
}
@ -173,6 +196,9 @@ impl<'d, T: Channel> Pwm<'d, T> {
});
}
/// Advances a slices output phase by one count while it is running
/// by inserting a pulse into the clock enable. The counter
/// will not count faster than once per cycle.
#[inline]
pub fn phase_advance(&mut self) {
let p = self.inner.regs();
@ -180,6 +206,9 @@ impl<'d, T: Channel> Pwm<'d, T> {
while p.csr().read().ph_adv() {}
}
/// Retards a slices output phase by one count while it is running
/// by deleting a pulse from the clock enable. The counter will not
/// count backward when clock enable is permenantly low.
#[inline]
pub fn phase_retard(&mut self) {
let p = self.inner.regs();

View File

@ -17,7 +17,7 @@ embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" }
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true }
defmt = { version = "0.3", optional = true }
cortex-m = "0.7.6"

View File

@ -3,7 +3,7 @@
use core::task::Context;
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel;
@ -60,24 +60,15 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
// caps.max_burst_size = Some(self.tx.len());
caps.medium = Medium::Ieee802154;
caps
}
fn link_state(&mut self, _cx: &mut Context) -> LinkState {
// if self.phy.poll_link(&mut self.station_management, cx) {
// LinkState::Up
// } else {
// LinkState::Down
// }
LinkState::Down
}
fn hardware_address(&self) -> HardwareAddress {
// self.mac_addr
HardwareAddress::Ieee802154([0; 8])
}
}

View File

@ -37,7 +37,7 @@ embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
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.0", path = "../embassy-executor", 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-9330e31117668350a62572fdcd2598ec17d08042" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5b04234fbe61ea875f1a904cd5f68795daaeb526" }
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-9330e31117668350a62572fdcd2598ec17d08042", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5b04234fbe61ea875f1a904cd5f68795daaeb526", default-features = false, features = ["metadata"]}
[features]

View File

@ -466,15 +466,9 @@ fn main() {
let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" };
let pname = format_ident!("{}", p.name);
let clk = format_ident!(
"{}",
rcc.clock
.to_ascii_lowercase()
.replace("ahb", "hclk")
.replace("apb", "pclk")
);
let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase());
let clk = format_ident!("{}", rcc.clock);
let en_reg = format_ident!("{}", en.register);
let set_en_field = format_ident!("set_{}", en.field);
let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) {
let refcount_static =
@ -500,11 +494,11 @@ fn main() {
(TokenStream::new(), TokenStream::new())
};
let mux_supported = HashSet::from(["c0", "h5", "h50", "h7", "h7ab", "h7rm0433", "g4", "l4"])
.contains(rcc_registers.version);
let mux_for = |mux: Option<&'static PeripheralRccRegister>| {
let checked_rccs = HashSet::from(["h5", "h50", "h7", "h7ab", "h7rm0433", "g4"]);
// restrict mux implementation to supported versions
if !checked_rccs.contains(rcc_registers.version) {
if !mux_supported {
return None;
}

View File

@ -134,6 +134,8 @@ pub(crate) unsafe fn init(config: Config) {
};
set_freqs(Clocks {
hsi: None,
lse: None,
sys: sys_clk,
hclk1: ahb_freq,
pclk1: apb_freq,

View File

@ -127,7 +127,7 @@ pub(crate) unsafe fn init(config: Config) {
}
if config.usb_pll {
RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLLCLK));
RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLL1_P));
}
// TODO: Option to use CRS (Clock Recovery)
@ -140,7 +140,7 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cfgr().modify(|w| {
w.set_ppre(Ppre::from_bits(ppre_bits));
w.set_hpre(Hpre::from_bits(hpre_bits));
w.set_sw(Sw::PLL)
w.set_sw(Sw::PLL1_P)
});
} else {
RCC.cfgr().modify(|w| {

View File

@ -102,7 +102,6 @@ pub(crate) unsafe fn init(config: Config) {
assert!(pclk2 <= 72_000_000);
// Only needed for stm32f103?
FLASH.acr().write(|w| {
w.set_latency(if real_sysclk <= 24_000_000 {
Latency::WS0
@ -111,6 +110,8 @@ pub(crate) unsafe fn init(config: Config) {
} else {
Latency::WS2
});
// the prefetch buffer is enabled by default, let's keep it enabled
w.set_prftbe(true);
});
// the USB clock is only valid if an external crystal is used, the PLL is enabled, and the
@ -168,7 +169,14 @@ pub(crate) unsafe fn init(config: Config) {
#[cfg(not(rcc_f100))]
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
w.set_sw(if pllmul_bits.is_some() {
Sw::PLL
#[cfg(not(rcc_f1cl))]
{
Sw::PLL1_P
}
#[cfg(rcc_f1cl)]
{
Sw::PLL
}
} else if config.hse.is_some() {
Sw::HSE
} else {

View File

@ -256,7 +256,7 @@ pub(crate) unsafe fn init(config: Config) {
ClockSrc::PLL => {
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
(pll_clocks.main_freq, Sw::PLL)
(pll_clocks.main_freq, Sw::PLL1_P)
}
};
// RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL

View File

@ -214,7 +214,7 @@ pub(crate) unsafe fn init(config: Config) {
// CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings
RCC.cfgr().modify(|w| {
w.set_sw(match (pll_config, config.hse) {
(Some(_), _) => Sw::PLL,
(Some(_), _) => Sw::PLL1_P,
(None, Some(_)) => Sw::HSE,
(None, None) => Sw::HSI,
})
@ -271,7 +271,7 @@ pub(crate) unsafe fn init(config: Config) {
pll_config.unwrap();
assert!((pclk2 == sysclk) || (pclk2 * 2u32 == sysclk));
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL));
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL1_P));
Some(sysclk * 2u32)
}

View File

@ -1,400 +0,0 @@
use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllq, Pllr, Ppre, Sw};
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);
/// Clocks configuration
#[non_exhaustive]
#[derive(Default)]
pub struct Config {
pub hse: Option<Hertz>,
pub bypass_hse: bool,
pub hclk: Option<Hertz>,
pub sys_ck: Option<Hertz>,
pub pclk1: Option<Hertz>,
pub pclk2: Option<Hertz>,
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
pub plli2s: Option<Hertz>,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pub pllsai: Option<Hertz>,
pub pll48: bool,
pub ls: super::LsConfig,
}
#[cfg(stm32f410)]
fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
None
}
// Not currently implemented, but will be in the future
#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
None
}
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423)))]
fn calculate_sai_i2s_pll_values(vco_in: u32, max_div: u32, target: Option<u32>) -> Option<(u32, u32, u32)> {
let min_div = 2;
let target = match target {
Some(target) => target,
None => return None,
};
// We loop through the possible divider values to find the best configuration. Looping
// through all possible "N" values would result in more iterations.
let (n, outdiv, output, _error) = (min_div..=max_div)
.filter_map(|outdiv| {
let target_vco_out = match target.checked_mul(outdiv) {
Some(x) => x,
None => return None,
};
let n = (target_vco_out + (vco_in >> 1)) / vco_in;
let vco_out = vco_in * n;
if !(100_000_000..=432_000_000).contains(&vco_out) {
return None;
}
let output = vco_out / outdiv;
let error = (output as i32 - target as i32).unsigned_abs();
Some((n, outdiv, output, error))
})
.min_by_key(|(_, _, _, error)| *error)?;
Some((n, outdiv, output))
}
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 7, plli2s)?;
RCC.plli2scfgr().modify(|w| {
w.set_plli2sn(n as u16);
w.set_plli2sr(outdiv as u8);
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
w.set_plli2sq(outdiv as u8); //set sai divider same as i2s
});
Some(output)
}
#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))]
fn setup_sai_pll(_vco_in: u32, _pllsai: Option<u32>) -> Option<u32> {
None
}
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
fn setup_sai_pll(vco_in: u32, pllsai: Option<u32>) -> Option<u32> {
let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 15, pllsai)?;
RCC.pllsaicfgr().modify(|w| {
w.set_pllsain(n as u16);
w.set_pllsaiq(outdiv as u8);
});
Some(output)
}
fn setup_pll(
pllsrcclk: u32,
use_hse: bool,
pllsysclk: Option<u32>,
plli2s: Option<u32>,
pllsai: Option<u32>,
pll48clk: bool,
) -> PllResults {
use crate::pac::rcc::vals::{Pllp, Pllsrc};
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
if pllsysclk.is_none() && !pll48clk {
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
return PllResults {
use_pll: false,
pllsysclk: None,
pll48clk: None,
plli2sclk: None,
pllsaiclk: None,
};
}
// Input divisor from PLL source clock, must result to frequency in
// the range from 1 to 2 MHz
let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
let pllm_max = pllsrcclk / 1_000_000;
// Sysclk output divisor must be one of 2, 4, 6 or 8
let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
let target_freq = if pll48clk { 48_000_000 } else { sysclk * sysclk_div };
// Find the lowest pllm value that minimize the difference between
// target frequency and the real vco_out frequency.
let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| {
let vco_in = pllsrcclk / pllm;
let plln = target_freq / vco_in;
target_freq - vco_in * plln
}));
let vco_in = pllsrcclk / pllm;
assert!((1_000_000..=2_000_000).contains(&vco_in));
// Main scaler, must result in >= 100MHz (>= 192MHz for F401)
// and <= 432MHz, min 50, max 432
let plln = if pll48clk {
// try the different valid pllq according to the valid
// main scaller values, and take the best
let pllq = unwrap!((4..=9).min_by_key(|pllq| {
let plln = 48_000_000 * pllq / vco_in;
let pll48_diff = 48_000_000 - vco_in * plln / pllq;
let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
(pll48_diff, sysclk_diff)
}));
48_000_000 * pllq / vco_in
} else {
sysclk * sysclk_div / vco_in
};
let pllp = (sysclk_div / 2) - 1;
let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
let real_pll48clk = vco_in * plln / pllq;
RCC.pllcfgr().modify(|w| {
w.set_pllm(Pllm::from_bits(pllm as u8));
w.set_plln(Plln::from_bits(plln as u16));
w.set_pllp(Pllp::from_bits(pllp as u8));
w.set_pllq(Pllq::from_bits(pllq as u8));
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
w.set_pllr(Pllr::from_bits(0));
});
let real_pllsysclk = vco_in * plln / sysclk_div;
PllResults {
use_pll: true,
pllsysclk: Some(real_pllsysclk),
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
plli2sclk: setup_i2s_pll(vco_in, plli2s),
pllsaiclk: setup_sai_pll(vco_in, pllsai),
}
}
fn flash_setup(sysclk: u32) {
use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
critical_section::with(|_| {
FLASH
.acr()
.modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
});
}
pub(crate) unsafe fn init(config: Config) {
let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0);
let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
let sysclk_on_pll = sysclk != pllsrcclk;
let plls = setup_pll(
pllsrcclk,
config.hse.is_some(),
if sysclk_on_pll { Some(sysclk) } else { None },
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
config.plli2s.map(|i2s| i2s.0),
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
None,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
config.pllsai.map(|sai| sai.0),
#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))]
None,
config.pll48,
);
if config.pll48 {
let freq = unwrap!(plls.pll48clk);
assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32);
}
let sysclk = if sysclk_on_pll { unwrap!(plls.pllsysclk) } else { sysclk };
// AHB prescaler
let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk);
let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
0 => unreachable!(),
1 => (Hpre::DIV1, 1),
2 => (Hpre::DIV2, 2),
3..=5 => (Hpre::DIV4, 4),
6..=11 => (Hpre::DIV8, 8),
12..=39 => (Hpre::DIV16, 16),
40..=95 => (Hpre::DIV64, 64),
96..=191 => (Hpre::DIV128, 128),
192..=383 => (Hpre::DIV256, 256),
_ => (Hpre::DIV512, 512),
};
// Calculate real AHB clock
let hclk = sysclk / hpre_div;
let pclk1 = config
.pclk1
.map(|p| p.0)
.unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk));
let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
// Calculate real APB1 clock
let pclk1 = hclk / ppre1;
assert!(pclk1 <= max::PCLK1_MAX);
let pclk2 = config
.pclk2
.map(|p| p.0)
.unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk));
let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
// Calculate real APB2 clock
let pclk2 = hclk / ppre2;
assert!(pclk2 <= max::PCLK2_MAX);
flash_setup(sysclk);
if config.hse.is_some() {
RCC.cr().modify(|w| {
w.set_hsebyp(config.bypass_hse);
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
}
if plls.use_pll {
RCC.cr().modify(|w| w.set_pllon(true));
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {}
PWR.cr1().modify(|w| w.set_odswen(true));
while !PWR.csr1().read().odswrdy() {}
}
while !RCC.cr().read().pllrdy() {}
}
#[cfg(not(stm32f410))]
if plls.plli2sclk.is_some() {
RCC.cr().modify(|w| w.set_plli2son(true));
while !RCC.cr().read().plli2srdy() {}
}
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
if plls.pllsaiclk.is_some() {
RCC.cr().modify(|w| w.set_pllsaion(true));
while !RCC.cr().read().pllsairdy() {}
}
RCC.cfgr().modify(|w| {
w.set_ppre2(Ppre::from_bits(ppre2_bits));
w.set_ppre1(Ppre::from_bits(ppre1_bits));
w.set_hpre(hpre_bits);
});
// Wait for the new prescalers to kick in
// "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
cortex_m::asm::delay(16);
RCC.cfgr().modify(|w| {
w.set_sw(if sysclk_on_pll {
Sw::PLL
} else if config.hse.is_some() {
Sw::HSE
} else {
Sw::HSI
})
});
let rtc = config.ls.init();
set_freqs(Clocks {
sys: Hertz(sysclk),
pclk1: Hertz(pclk1),
pclk2: Hertz(pclk2),
pclk1_tim: Hertz(pclk1 * timer_mul1),
pclk2_tim: Hertz(pclk2 * timer_mul2),
hclk1: Hertz(hclk),
hclk2: Hertz(hclk),
hclk3: Hertz(hclk),
pll1_q: plls.pll48clk.map(Hertz),
#[cfg(not(stm32f410))]
plli2s1_q: plls.plli2sclk.map(Hertz),
#[cfg(not(stm32f410))]
plli2s1_r: None,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pllsai1_q: plls.pllsaiclk.map(Hertz),
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pllsai1_r: None,
rtc,
});
}
struct PllResults {
use_pll: bool,
pllsysclk: Option<u32>,
pll48clk: Option<u32>,
#[allow(dead_code)]
plli2sclk: Option<u32>,
#[allow(dead_code)]
pllsaiclk: Option<u32>,
}
mod max {
#[cfg(stm32f401)]
pub(crate) const SYSCLK_MAX: u32 = 84_000_000;
#[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))]
pub(crate) const SYSCLK_MAX: u32 = 168_000_000;
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
pub(crate) const SYSCLK_MAX: u32 = 100_000_000;
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))]
pub(crate) const SYSCLK_MAX: u32 = 180_000_000;
pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 168_000_000;
pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2;
#[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX;
#[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))]
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
pub(crate) const PLL_48_CLK: u32 = 48_000_000;
pub(crate) const PLL_48_TOLERANCE: u32 = 120_000;
}

View File

@ -0,0 +1,385 @@
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,
};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
// TODO: on some F4s, PLLM is shared between all PLLs. Enforce that.
// TODO: on some F4s, add support for plli2s_src
//
// plli2s plli2s_m plli2s_src pllsai pllsai_m
// f401 y shared
// f410
// f411 y individual
// f412 y individual y
// f4[12]3 y individual y
// f446 y individual y individual
// f4[67]9 y shared y shared
// f4[23][79] y shared y shared
// f4[01][57] y shared
/// 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 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<Pllp>,
/// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<Pllq>,
/// PLL R division factor. If None, PLL R output is disabled.
pub divr: Option<Pllr>,
}
/// Configuration of the core clocks
#[non_exhaustive]
pub struct Config {
pub hsi: bool,
pub hse: Option<Hse>,
pub sys: Sysclk,
pub pll_src: PllSource,
pub pll: Option<Pll>,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
pub plli2s: Option<Pll>,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
pub pllsai: Option<Pll>,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub ls: super::LsConfig,
}
impl Default for Config {
fn default() -> Self {
Self {
hsi: true,
hse: None,
sys: Sysclk::HSI,
pll_src: PllSource::HSI,
pll: None,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
plli2s: None,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
pllsai: None,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
// always enable overdrive for now. Make it configurable in the future.
#[cfg(not(any(
stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417
)))]
{
use crate::pac::PWR;
PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {}
PWR.cr1().modify(|w| w.set_odswen(true));
while !PWR.csr1().read().odswrdy() {}
}
// Configure HSI
let hsi = match config.hsi {
false => {
RCC.cr().modify(|w| w.set_hsion(false));
None
}
true => {
RCC.cr().modify(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
Some(HSI_FREQ)
}
};
// Configure HSE
let hse = match config.hse {
None => {
RCC.cr().modify(|w| w.set_hseon(false));
None
}
Some(hse) => {
match hse.mode {
HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
}
RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
RCC.cr().modify(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
Some(hse.freq)
}
};
// Configure PLLs.
let pll_input = PllInput {
hse,
hsi,
source: config.pll_src,
};
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
#[cfg(any(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);
// Configure sysclk
let sys = match config.sys {
Sysclk::HSI => unwrap!(hsi),
Sysclk::HSE => unwrap!(hse),
Sysclk::PLL1_P => unwrap!(pll.p),
_ => unreachable!(),
};
let hclk = sys / config.ahb_pre;
let (pclk1, pclk1_tim) = calc_pclk(hclk, config.apb1_pre);
let (pclk2, pclk2_tim) = calc_pclk(hclk, config.apb2_pre);
assert!(max::SYSCLK.contains(&sys));
assert!(max::HCLK.contains(&hclk));
assert!(max::PCLK1.contains(&pclk1));
assert!(max::PCLK2.contains(&pclk2));
let rtc = config.ls.init();
flash_setup(hclk);
RCC.cfgr().modify(|w| {
w.set_sw(config.sys);
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
while RCC.cfgr().read().sws() != config.sys {}
set_freqs(Clocks {
sys,
hclk1: hclk,
hclk2: hclk,
hclk3: hclk,
pclk1,
pclk2,
pclk1_tim,
pclk2_tim,
rtc,
pll1_q: pll.q,
#[cfg(all(rcc_f4, not(stm32f410)))]
plli2s1_q: _plli2s.q,
#[cfg(all(rcc_f4, not(stm32f410)))]
plli2s1_r: _plli2s.r,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pllsai1_q: _pllsai.q,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pllsai1_r: _pllsai.r,
});
}
struct PllInput {
source: PllSource,
hsi: Option<Hertz>,
hse: Option<Hertz>,
}
#[derive(Default)]
#[allow(unused)]
struct PllOutput {
p: Option<Hertz>,
q: Option<Hertz>,
r: Option<Hertz>,
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum PllInstance {
Pll,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
Plli2s,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
Pllsai,
}
fn pll_enable(instance: PllInstance, enabled: bool) {
match instance {
PllInstance::Pll => {
RCC.cr().modify(|w| w.set_pllon(enabled));
while RCC.cr().read().pllrdy() != enabled {}
}
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
PllInstance::Plli2s => {
RCC.cr().modify(|w| w.set_plli2son(enabled));
while RCC.cr().read().plli2srdy() != enabled {}
}
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
PllInstance::Pllsai => {
RCC.cr().modify(|w| w.set_pllsaion(enabled));
while RCC.cr().read().pllsairdy() != enabled {}
}
}
}
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 input.source {
PllSource::HSE => input.hse,
PllSource::HSI => input.hsi,
};
let pll_src = pll_src.unwrap();
let in_freq = pll_src / pll.prediv;
assert!(max::PLL_IN.contains(&in_freq));
let vco_freq = in_freq * pll.mul;
assert!(max::PLL_VCO.contains(&vco_freq));
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);
macro_rules! write_fields {
($w:ident) => {
$w.set_plln(pll.mul);
if let Some(divp) = pll.divp {
$w.set_pllp(divp);
}
if let Some(divq) = pll.divq {
$w.set_pllq(divq);
}
if let Some(divr) = pll.divr {
$w.set_pllr(divr);
}
};
}
match instance {
PllInstance::Pll => RCC.pllcfgr().write(|w| {
w.set_pllm(pll.prediv);
w.set_pllsrc(input.source);
write_fields!(w);
}),
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {
write_fields!(w);
}),
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| {
write_fields!(w);
}),
}
// Enable PLL
pll_enable(instance, true);
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 {}
}
fn calc_pclk<D>(hclk: Hertz, ppre: D) -> (Hertz, Hertz)
where
Hertz: core::ops::Div<D, Output = Hertz>,
{
let pclk = hclk / ppre;
let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 };
(pclk, pclk_tim)
}
#[cfg(stm32f7)]
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(50_000_000);
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000);
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000);
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 4);
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 2);
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(stm32f4)]
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(50_000_000);
#[cfg(stm32f401)]
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(84_000_000);
#[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))]
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(168_000_000);
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(100_000_000);
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))]
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(180_000_000);
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0);
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(PCLK2.end().0 / 2);
#[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(HCLK.end().0);
#[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))]
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(HCLK.end().0 / 2);
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);
}

View File

@ -1,305 +0,0 @@
use crate::pac::pwr::vals::Vos;
use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllp, Pllq, Pllsrc, Ppre, Sw};
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);
/// Clocks configuration
#[non_exhaustive]
#[derive(Default)]
pub struct Config {
pub hse: Option<Hertz>,
pub bypass_hse: bool,
pub hclk: Option<Hertz>,
pub sys_ck: Option<Hertz>,
pub pclk1: Option<Hertz>,
pub pclk2: Option<Hertz>,
pub pll48: bool,
pub ls: super::LsConfig,
}
fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults {
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
if pllsysclk.is_none() && !pll48clk {
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
return PllResults {
use_pll: false,
pllsysclk: None,
pll48clk: None,
};
}
// Input divisor from PLL source clock, must result to frequency in
// the range from 1 to 2 MHz
let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
let pllm_max = pllsrcclk / 1_000_000;
// Sysclk output divisor must be one of 2, 4, 6 or 8
let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
let target_freq = if pll48clk { 48_000_000 } else { sysclk * sysclk_div };
// Find the lowest pllm value that minimize the difference between
// target frequency and the real vco_out frequency.
let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| {
let vco_in = pllsrcclk / pllm;
let plln = target_freq / vco_in;
target_freq - vco_in * plln
}));
let vco_in = pllsrcclk / pllm;
assert!((1_000_000..=2_000_000).contains(&vco_in));
// Main scaler, must result in >= 100MHz (>= 192MHz for F401)
// and <= 432MHz, min 50, max 432
let plln = if pll48clk {
// try the different valid pllq according to the valid
// main scaller values, and take the best
let pllq = unwrap!((4..=9).min_by_key(|pllq| {
let plln = 48_000_000 * pllq / vco_in;
let pll48_diff = 48_000_000 - vco_in * plln / pllq;
let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
(pll48_diff, sysclk_diff)
}));
48_000_000 * pllq / vco_in
} else {
sysclk * sysclk_div / vco_in
};
let pllp = (sysclk_div / 2) - 1;
let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
let real_pll48clk = vco_in * plln / pllq;
RCC.pllcfgr().modify(|w| {
w.set_pllm(Pllm::from_bits(pllm as u8));
w.set_plln(Plln::from_bits(plln as u16));
w.set_pllp(Pllp::from_bits(pllp as u8));
w.set_pllq(Pllq::from_bits(pllq as u8));
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
});
let real_pllsysclk = vco_in * plln / sysclk_div;
PllResults {
use_pll: true,
pllsysclk: Some(real_pllsysclk),
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
}
}
fn flash_setup(sysclk: u32) {
use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
critical_section::with(|_| {
FLASH
.acr()
.modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
});
}
pub(crate) unsafe fn init(config: Config) {
if let Some(hse) = config.hse {
if config.bypass_hse {
assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0));
} else {
assert!((max::HSE_OSC_MIN..=max::HSE_OSC_MAX).contains(&hse.0));
}
}
let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0);
let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
let sysclk_on_pll = sysclk != pllsrcclk;
assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk));
let plls = setup_pll(
pllsrcclk,
config.hse.is_some(),
if sysclk_on_pll { Some(sysclk) } else { None },
config.pll48,
);
if config.pll48 {
let freq = unwrap!(plls.pll48clk);
assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32);
}
let sysclk = if sysclk_on_pll { unwrap!(plls.pllsysclk) } else { sysclk };
// AHB prescaler
let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk);
let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
0 => unreachable!(),
1 => (Hpre::DIV1, 1),
2 => (Hpre::DIV2, 2),
3..=5 => (Hpre::DIV4, 4),
6..=11 => (Hpre::DIV8, 8),
12..=39 => (Hpre::DIV16, 16),
40..=95 => (Hpre::DIV64, 64),
96..=191 => (Hpre::DIV128, 128),
192..=383 => (Hpre::DIV256, 256),
_ => (Hpre::DIV512, 512),
};
// Calculate real AHB clock
let hclk = sysclk / hpre_div;
assert!(hclk <= max::HCLK_MAX);
let pclk1 = config
.pclk1
.map(|p| p.0)
.unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk));
let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
// Calculate real APB1 clock
let pclk1 = hclk / ppre1;
assert!((max::PCLK1_MIN..=max::PCLK1_MAX).contains(&pclk1));
let pclk2 = config
.pclk2
.map(|p| p.0)
.unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk));
let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
// Calculate real APB2 clock
let pclk2 = hclk / ppre2;
assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2));
flash_setup(sysclk);
if config.hse.is_some() {
RCC.cr().modify(|w| {
w.set_hsebyp(config.bypass_hse);
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
}
if plls.use_pll {
RCC.cr().modify(|w| w.set_pllon(false));
// setup VOSScale
let vos_scale = if sysclk <= 144_000_000 {
3
} else if sysclk <= 168_000_000 {
2
} else {
1
};
PWR.cr1().modify(|w| {
w.set_vos(match vos_scale {
3 => Vos::SCALE3,
2 => Vos::SCALE2,
1 => Vos::SCALE1,
_ => panic!("Invalid VOS Scale."),
})
});
RCC.cr().modify(|w| w.set_pllon(true));
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {}
PWR.cr1().modify(|w| w.set_odswen(true));
while !PWR.csr1().read().odswrdy() {}
}
while !RCC.cr().read().pllrdy() {}
}
RCC.cfgr().modify(|w| {
w.set_ppre2(Ppre::from_bits(ppre2_bits));
w.set_ppre1(Ppre::from_bits(ppre1_bits));
w.set_hpre(hpre_bits);
});
// Wait for the new prescalers to kick in
// "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
cortex_m::asm::delay(16);
RCC.cfgr().modify(|w| {
w.set_sw(if sysclk_on_pll {
Sw::PLL
} else if config.hse.is_some() {
Sw::HSE
} else {
Sw::HSI
})
});
let rtc = config.ls.init();
set_freqs(Clocks {
sys: Hertz(sysclk),
pclk1: Hertz(pclk1),
pclk2: Hertz(pclk2),
pclk1_tim: Hertz(pclk1 * timer_mul1),
pclk2_tim: Hertz(pclk2 * timer_mul2),
hclk1: Hertz(hclk),
hclk2: Hertz(hclk),
hclk3: Hertz(hclk),
pll1_q: plls.pll48clk.map(Hertz),
rtc,
});
}
struct PllResults {
use_pll: bool,
pllsysclk: Option<u32>,
pll48clk: Option<u32>,
}
mod max {
pub(crate) const HSE_OSC_MIN: u32 = 4_000_000;
pub(crate) const HSE_OSC_MAX: u32 = 26_000_000;
pub(crate) const HSE_BYPASS_MIN: u32 = 1_000_000;
pub(crate) const HSE_BYPASS_MAX: u32 = 50_000_000;
pub(crate) const HCLK_MAX: u32 = 216_000_000;
pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 180_000_000;
pub(crate) const SYSCLK_MIN: u32 = 12_500_000;
pub(crate) const SYSCLK_MAX: u32 = 216_000_000;
pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN;
pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4;
pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN;
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
// USB specification allows +-0.25%
pub(crate) const PLL_48_CLK: u32 = 48_000_000;
pub(crate) const PLL_48_TOLERANCE: u32 = 120_000;
}

View File

@ -58,15 +58,6 @@ pub struct Hse {
pub mode: HseMode,
}
#[cfg(stm32h7)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Lse {
/// 32.768 kHz crystal/ceramic oscillator (LSEBYP=0)
Oscillator,
/// external clock input up to 1MHz (LSEBYP=1)
Bypass(Hertz),
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Hsi {
/// 64Mhz

View File

@ -31,7 +31,7 @@ pub enum PLLSource {
impl From<PLLSource> for Pllsrc {
fn from(val: PLLSource) -> Pllsrc {
match val {
PLLSource::HSI16 => Pllsrc::HSI16,
PLLSource::HSI16 => Pllsrc::HSI,
PLLSource::HSE(_) => Pllsrc::HSE,
}
}
@ -88,7 +88,7 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cr().write(|w| w.set_hsi16on(true));
while !RCC.cr().read().hsi16rdy() {}
(HSI_FREQ, Sw::HSI16)
(HSI_FREQ, Sw::HSI)
}
ClockSrc::HSE(freq) => {
// Enable HSE
@ -131,7 +131,7 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
(freq, Sw::PLL)
(freq, Sw::PLL1_P)
}
};

View File

@ -187,9 +187,12 @@ pub(crate) unsafe fn init(config: Config) {
let sys_clk = match config.mux {
ClockSrc::HSE => hse.unwrap(),
ClockSrc::HSI16 => hsi16.unwrap(),
ClockSrc::HSI => hsi16.unwrap(),
ClockSrc::MSI => msi.unwrap(),
ClockSrc::PLL => pll._r.unwrap(),
#[cfg(rcc_l4)]
ClockSrc::PLL1_P => pll._r.unwrap(),
#[cfg(not(rcc_l4))]
ClockSrc::PLL1_R => pll._r.unwrap(),
};
#[cfg(stm32l4)]
@ -200,7 +203,7 @@ pub(crate) unsafe fn init(config: Config) {
Clk48Src::HSI48 => hsi48,
Clk48Src::MSI => msi,
Clk48Src::PLLSAI1_Q => pllsai1._q,
Clk48Src::PLL_Q => pll._q,
Clk48Src::PLL1_Q => pll._q,
};
#[cfg(rcc_l4plus)]
@ -238,6 +241,7 @@ pub(crate) unsafe fn init(config: Config) {
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
while RCC.cfgr().read().sws() != config.mux {}
let ahb_freq = sys_clk / config.ahb_pre;
@ -266,6 +270,22 @@ pub(crate) unsafe fn init(config: Config) {
pclk2: apb2_freq,
pclk1_tim: apb1_tim_freq,
pclk2_tim: apb2_tim_freq,
#[cfg(rcc_l4)]
hsi: None,
#[cfg(rcc_l4)]
lse: None,
#[cfg(rcc_l4)]
pllsai1_p: None,
#[cfg(rcc_l4)]
pllsai2_p: None,
#[cfg(rcc_l4)]
pll1_p: None,
#[cfg(rcc_l4)]
pll1_q: None,
#[cfg(rcc_l4)]
sai1_extclk: None,
#[cfg(rcc_l4)]
sai2_extclk: None,
rtc,
});
}
@ -341,7 +361,7 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
let pll_src = match pll.source {
PLLSource::NONE => panic!("must not select PLL source as NONE"),
PLLSource::HSE => input.hse,
PLLSource::HSI16 => input.hsi16,
PLLSource::HSI => input.hsi16,
PLLSource::MSI => input.msi,
};

View File

@ -13,8 +13,7 @@ pub use mco::*;
#[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), path = "f4.rs")]
#[cfg_attr(rcc_f7, path = "f7.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")]
#[cfg_attr(rcc_c0, path = "c0.rs")]
#[cfg_attr(rcc_g0, path = "g0.rs")]
#[cfg_attr(rcc_g4, path = "g4.rs")]
@ -110,14 +109,18 @@ pub struct Clocks {
#[cfg(all(rcc_f4, not(stm32f410)))]
pub plli2s1_r: Option<Hertz>,
#[cfg(rcc_l4)]
pub pllsai1_p: Option<Hertz>,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pub pllsai1_q: Option<Hertz>,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pub pllsai1_r: Option<Hertz>,
#[cfg(rcc_l4)]
pub pllsai2_p: Option<Hertz>,
#[cfg(stm32g4)]
#[cfg(any(stm32g4, rcc_l4))]
pub pll1_p: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7, rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
#[cfg(any(stm32h5, stm32h7, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_l4))]
pub pll1_q: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub pll2_p: Option<Hertz>,
@ -154,7 +157,7 @@ pub struct Clocks {
pub rtc: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
pub hsi: Option<Hertz>,
#[cfg(stm32h5)]
pub hsi48: Option<Hertz>,
@ -163,7 +166,7 @@ pub struct Clocks {
#[cfg(any(stm32h5, stm32h7))]
pub csi: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
pub lse: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub hse: Option<Hertz>,
@ -175,6 +178,10 @@ pub struct Clocks {
#[cfg(stm32h7)]
pub rcc_pclk_d3: Option<Hertz>,
#[cfg(rcc_l4)]
pub sai1_extclk: Option<Hertz>,
#[cfg(rcc_l4)]
pub sai2_extclk: Option<Hertz>,
}
#[cfg(feature = "low-power")]

View File

@ -42,7 +42,7 @@ max-handler-count-8 = []
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }

View File

@ -70,9 +70,11 @@ fn main() {
// envvars take priority.
if !cfg.seen_env {
if cfg.seen_feature {
panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value);
}
assert!(
!cfg.seen_feature,
"multiple values set for feature {}: {} and {}",
name, cfg.value, value
);
cfg.value = value;
cfg.seen_feature = true;

View File

@ -1,17 +1,17 @@
use heapless::Vec;
use crate::config::*;
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::*;
use crate::types::{InterfaceNumber, StringIndex};
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
/// Configuration used when creating [UsbDevice].
/// Configuration used when creating [`UsbDevice`].
pub struct Config<'a> {
pub(crate) vendor_id: u16,
pub(crate) product_id: u16,
@ -99,7 +99,7 @@ pub struct Config<'a> {
impl<'a> Config<'a> {
/// Create default configuration with the provided vid and pid values.
pub fn new(vid: u16, pid: u16) -> Self {
pub const fn new(vid: u16, pid: u16) -> Self {
Self {
device_class: 0x00,
device_sub_class: 0x00,
@ -159,9 +159,10 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
panic!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01");
}
if config.max_power > 500 {
panic!("The maximum allowed value for `max_power` is 500mA");
}
assert!(
config.max_power <= 500,
"The maximum allowed value for `max_power` is 500mA"
);
match config.max_packet_size_0 {
8 | 16 | 32 | 64 => {}
@ -260,12 +261,11 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
/// The Handler is called on some USB bus events, and to handle all control requests not already
/// handled by the USB stack.
pub fn handler(&mut self, handler: &'d mut dyn Handler) {
if self.handlers.push(handler).is_err() {
panic!(
"embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}",
MAX_HANDLER_COUNT
)
}
assert!(
self.handlers.push(handler).is_ok(),
"embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}",
MAX_HANDLER_COUNT
);
}
/// Allocates a new string index.
@ -332,12 +332,10 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
num_alt_settings: 0,
};
if self.builder.interfaces.push(iface).is_err() {
panic!(
"embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
MAX_INTERFACE_COUNT
)
}
assert!(self.builder.interfaces.push(iface).is_ok(),
"embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
MAX_INTERFACE_COUNT
);
InterfaceBuilder {
builder: self.builder,
@ -371,7 +369,7 @@ pub struct InterfaceBuilder<'a, 'd, D: Driver<'d>> {
impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
/// Get the interface number.
pub fn interface_number(&self) -> InterfaceNumber {
pub const fn interface_number(&self) -> InterfaceNumber {
self.interface_number
}
@ -422,12 +420,12 @@ pub struct InterfaceAltBuilder<'a, 'd, D: Driver<'d>> {
impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
/// Get the interface number.
pub fn interface_number(&self) -> InterfaceNumber {
pub const fn interface_number(&self) -> InterfaceNumber {
self.interface_number
}
/// Get the alternate setting number.
pub fn alt_setting_number(&self) -> u8 {
pub const fn alt_setting_number(&self) -> u8 {
self.alt_setting_number
}
@ -436,7 +434,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
/// Descriptors are written in the order builder functions are called. Note that some
/// classes care about the order.
pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {
self.builder.config_descriptor.write(descriptor_type, descriptor)
self.builder.config_descriptor.write(descriptor_type, descriptor);
}
fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {

View File

@ -11,7 +11,7 @@ use embassy_sync::waitqueue::WakerRegistration;
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::*;
use crate::types::InterfaceNumber;
use crate::{Builder, Handler};
/// This should be used as `device_class` when building the `UsbDevice`.
@ -39,12 +39,18 @@ pub struct State<'a> {
shared: ControlShared,
}
impl<'a> Default for State<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> State<'a> {
/// Create a new `State`.
pub fn new() -> Self {
Self {
control: MaybeUninit::uninit(),
shared: Default::default(),
shared: ControlShared::default(),
}
}
}
@ -55,9 +61,9 @@ impl<'a> State<'a> {
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
/// port. The following constraints must be followed if you use this class directly:
///
/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes.
/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes.
/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
/// - `read_packet` must be called with a buffer large enough to hold `max_packet_size` bytes.
/// - `write_packet` must not be called with a buffer larger than `max_packet_size` bytes.
/// - If you write a packet that is exactly `max_packet_size` bytes long, it won't be processed by the
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
@ -103,17 +109,16 @@ impl Default for ControlShared {
impl ControlShared {
async fn changed(&self) {
poll_fn(|cx| match self.changed.load(Ordering::Relaxed) {
true => {
poll_fn(|cx| {
if self.changed.load(Ordering::Relaxed) {
self.changed.store(false, Ordering::Relaxed);
Poll::Ready(())
}
false => {
} else {
self.waker.borrow_mut().register(cx.waker());
Poll::Pending
}
})
.await
.await;
}
}
@ -192,7 +197,7 @@ impl<'d> Handler for Control<'d> {
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
REQ_GET_LINE_CODING if req.length == 7 => {
debug!("Sending line coding");
let coding = self.shared().line_coding.lock(|x| x.get());
let coding = self.shared().line_coding.lock(Cell::get);
assert!(buf.len() >= 7);
buf[0..4].copy_from_slice(&coding.data_rate.to_le_bytes());
buf[4] = coding.stop_bits as u8;
@ -206,8 +211,8 @@ impl<'d> Handler for Control<'d> {
}
impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
/// Creates a new CdcAcmClass with the provided UsbBus and `max_packet_size` in bytes. For
/// full-speed devices, `max_packet_size` has to be one of 8, 16, 32 or 64.
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self {
assert!(builder.control_buf_len() >= 7);
@ -242,7 +247,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
&[
CDC_TYPE_UNION, // bDescriptorSubtype
comm_if.into(), // bControlInterface
data_if.into(), // bSubordinateInterface
data_if, // bSubordinateInterface
],
);
@ -283,7 +288,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
/// Gets the current line coding. The line coding contains information that's mainly relevant
/// for USB to UART serial port emulators, and can be ignored if not relevant.
pub fn line_coding(&self) -> LineCoding {
self.control.line_coding.lock(|x| x.get())
self.control.line_coding.lock(Cell::get)
}
/// Gets the DTR (data terminal ready) state
@ -308,7 +313,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.read_ep.wait_enabled().await
self.read_ep.wait_enabled().await;
}
/// Split the class into a sender and receiver.
@ -356,7 +361,7 @@ pub struct ControlChanged<'d> {
impl<'d> ControlChanged<'d> {
/// Return a future for when the control settings change
pub async fn control_changed(&self) {
self.control.changed().await
self.control.changed().await;
}
}
@ -378,7 +383,7 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
/// Gets the current line coding. The line coding contains information that's mainly relevant
/// for USB to UART serial port emulators, and can be ignored if not relevant.
pub fn line_coding(&self) -> LineCoding {
self.control.line_coding.lock(|x| x.get())
self.control.line_coding.lock(Cell::get)
}
/// Gets the DTR (data terminal ready) state
@ -398,7 +403,7 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.write_ep.wait_enabled().await
self.write_ep.wait_enabled().await;
}
}
@ -420,7 +425,7 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> {
/// Gets the current line coding. The line coding contains information that's mainly relevant
/// for USB to UART serial port emulators, and can be ignored if not relevant.
pub fn line_coding(&self) -> LineCoding {
self.control.line_coding.lock(|x| x.get())
self.control.line_coding.lock(Cell::get)
}
/// Gets the DTR (data terminal ready) state
@ -440,7 +445,7 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> {
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.read_ep.wait_enabled().await
self.read_ep.wait_enabled().await;
}
}
@ -514,17 +519,17 @@ impl LineCoding {
}
/// Gets the number of data bits for UART communication.
pub fn data_bits(&self) -> u8 {
pub const fn data_bits(&self) -> u8 {
self.data_bits
}
/// Gets the parity type for UART communication.
pub fn parity_type(&self) -> ParityType {
pub const fn parity_type(&self) -> ParityType {
self.parity_type
}
/// Gets the data rate in bits per second for UART communication.
pub fn data_rate(&self) -> u32 {
pub const fn data_rate(&self) -> u32 {
self.data_rate
}
}

View File

@ -16,10 +16,11 @@
use core::intrinsics::copy_nonoverlapping;
use core::mem::{size_of, MaybeUninit};
use core::ptr::addr_of;
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::*;
use crate::types::{InterfaceNumber, StringIndex};
use crate::{Builder, Handler};
pub mod embassy_net;
@ -62,9 +63,9 @@ const REQ_SET_NTB_INPUT_SIZE: u8 = 0x86;
//const NOTIF_POLL_INTERVAL: u8 = 20;
const NTB_MAX_SIZE: usize = 2048;
const SIG_NTH: u32 = 0x484d434e;
const SIG_NDP_NO_FCS: u32 = 0x304d434e;
const SIG_NDP_WITH_FCS: u32 = 0x314d434e;
const SIG_NTH: u32 = 0x484d_434e;
const SIG_NDP_NO_FCS: u32 = 0x304d_434e;
const SIG_NDP_WITH_FCS: u32 = 0x314d_434e;
const ALTERNATE_SETTING_DISABLED: u8 = 0x00;
const ALTERNATE_SETTING_ENABLED: u8 = 0x01;
@ -111,7 +112,7 @@ struct NtbParametersDir {
fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
let len = size_of::<T>();
unsafe { copy_nonoverlapping(&data as *const _ as *const u8, buf.as_mut_ptr(), len) }
unsafe { copy_nonoverlapping(addr_of!(data).cast(), buf.as_mut_ptr(), len) }
&buf[..len]
}
@ -121,27 +122,28 @@ pub struct State<'a> {
shared: ControlShared,
}
impl<'a> Default for State<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> State<'a> {
/// Create a new `State`.
pub fn new() -> Self {
Self {
control: MaybeUninit::uninit(),
shared: Default::default(),
shared: ControlShared::default(),
}
}
}
/// Shared data between Control and CdcAcmClass
/// Shared data between Control and `CdcAcmClass`
#[derive(Default)]
struct ControlShared {
mac_addr: [u8; 6],
}
impl Default for ControlShared {
fn default() -> Self {
ControlShared { mac_addr: [0; 6] }
}
}
struct Control<'a> {
mac_addr_string: StringIndex,
shared: &'a ControlShared,
@ -377,12 +379,12 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
///
/// This waits until the packet is successfully stored in the CDC-NCM endpoint buffers.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
let seq = self.seq;
self.seq = self.seq.wrapping_add(1);
const OUT_HEADER_LEN: usize = 28;
const ABS_MAX_PACKET_SIZE: usize = 512;
let seq = self.seq;
self.seq = self.seq.wrapping_add(1);
let header = NtbOutHeader {
nth_sig: SIG_NTH,
nth_len: 0x0c,
@ -416,7 +418,7 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
self.write_ep.write(&buf[..self.max_packet_size]).await?;
for chunk in d2.chunks(self.max_packet_size) {
self.write_ep.write(&chunk).await?;
self.write_ep.write(chunk).await?;
}
// Send ZLP if needed.
@ -459,12 +461,9 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> {
let ntb = &ntb[..pos];
// Process NTB header (NTH)
let nth = match ntb.get(..12) {
Some(x) => x,
None => {
warn!("Received too short NTB");
continue;
}
let Some(nth) = ntb.get(..12) else {
warn!("Received too short NTB");
continue;
};
let sig = u32::from_le_bytes(nth[0..4].try_into().unwrap());
if sig != SIG_NTH {
@ -474,12 +473,9 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> {
let ndp_idx = u16::from_le_bytes(nth[10..12].try_into().unwrap()) as usize;
// Process NTB Datagram Pointer (NDP)
let ndp = match ntb.get(ndp_idx..ndp_idx + 12) {
Some(x) => x,
None => {
warn!("NTH has an NDP pointer out of range.");
continue;
}
let Some(ndp) = ntb.get(ndp_idx..ndp_idx + 12) else {
warn!("NTH has an NDP pointer out of range.");
continue;
};
let sig = u32::from_le_bytes(ndp[0..4].try_into().unwrap());
if sig != SIG_NDP_NO_FCS && sig != SIG_NDP_WITH_FCS {
@ -495,12 +491,9 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> {
}
// Process actual datagram, finally.
let datagram = match ntb.get(datagram_index..datagram_index + datagram_len) {
Some(x) => x,
None => {
warn!("NDP has a datagram pointer out of range.");
continue;
}
let Some(datagram) = ntb.get(datagram_index..datagram_index + datagram_len) else {
warn!("NDP has a datagram pointer out of range.");
continue;
};
buf[..datagram_len].copy_from_slice(datagram);

View File

@ -63,7 +63,7 @@ pub enum ReportId {
}
impl ReportId {
fn try_from(value: u16) -> Result<Self, ()> {
const fn try_from(value: u16) -> Result<Self, ()> {
match value >> 8 {
1 => Ok(ReportId::In(value as u8)),
2 => Ok(ReportId::Out(value as u8)),
@ -79,9 +79,15 @@ pub struct State<'d> {
out_report_offset: AtomicUsize,
}
impl<'d> Default for State<'d> {
fn default() -> Self {
Self::new()
}
}
impl<'d> State<'d> {
/// Create a new `State`.
pub fn new() -> Self {
pub const fn new() -> Self {
State {
control: MaybeUninit::uninit(),
out_report_offset: AtomicUsize::new(0),
@ -148,7 +154,7 @@ fn build<'d, D: Driver<'d>>(
}
impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWriter<'d, D, READ_N, WRITE_N> {
/// Creates a new HidReaderWriter.
/// Creates a new `HidReaderWriter`.
///
/// This will allocate one IN and one OUT endpoints. If you only need writing (sending)
/// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only.
@ -171,7 +177,7 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit
}
/// Waits for both IN and OUT endpoints to be enabled.
pub async fn ready(&mut self) -> () {
pub async fn ready(&mut self) {
self.reader.ready().await;
self.writer.ready().await;
}
@ -224,7 +230,7 @@ pub enum ReadError {
impl From<EndpointError> for ReadError {
fn from(val: EndpointError) -> Self {
use EndpointError::*;
use EndpointError::{BufferOverflow, Disabled};
match val {
BufferOverflow => ReadError::BufferOverflow,
Disabled => ReadError::Disabled,
@ -251,17 +257,16 @@ impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> {
}
/// Waits for the interrupt in endpoint to be enabled.
pub async fn ready(&mut self) -> () {
self.ep_in.wait_enabled().await
pub async fn ready(&mut self) {
self.ep_in.wait_enabled().await;
}
/// Writes an input report by serializing the given report structure.
#[cfg(feature = "usbd-hid")]
pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {
let mut buf: [u8; N] = [0; N];
let size = match serialize(&mut buf, r) {
Ok(size) => size,
Err(_) => return Err(EndpointError::BufferOverflow),
let Ok(size) = serialize(&mut buf, r) else {
return Err(EndpointError::BufferOverflow);
};
self.write(&buf[0..size]).await
}
@ -286,8 +291,8 @@ impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> {
impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
/// Waits for the interrupt out endpoint to be enabled.
pub async fn ready(&mut self) -> () {
self.ep_out.wait_enabled().await
pub async fn ready(&mut self) {
self.ep_out.wait_enabled().await;
}
/// Delivers output reports from the Interrupt Out pipe to `handler`.
@ -344,9 +349,8 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
if size < max_packet_size || total == N {
self.offset.store(0, Ordering::Release);
break;
} else {
self.offset.store(total, Ordering::Release);
}
self.offset.store(total, Ordering::Release);
}
Err(err) => {
self.offset.store(0, Ordering::Release);
@ -466,7 +470,7 @@ impl<'d> Handler for Control<'d> {
HID_REQ_SET_IDLE => {
if let Some(handler) = self.request_handler {
let id = req.value as u8;
let id = (id != 0).then(|| ReportId::In(id));
let id = (id != 0).then_some(ReportId::In(id));
let dur = u32::from(req.value >> 8);
let dur = if dur == 0 { u32::MAX } else { 4 * dur };
handler.set_idle_ms(id, dur);
@ -522,7 +526,7 @@ impl<'d> Handler for Control<'d> {
HID_REQ_GET_IDLE => {
if let Some(handler) = self.request_handler {
let id = req.value as u8;
let id = (id != 0).then(|| ReportId::In(id));
let id = (id != 0).then_some(ReportId::In(id));
if let Some(dur) = handler.get_idle_ms(id) {
let dur = u8::try_from(dur / 4).unwrap_or(0);
buf[0] = dur;

View File

@ -27,9 +27,9 @@ const MIDI_OUT_SIZE: u8 = 0x09;
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like port.
/// The following constraints must be followed if you use this class directly:
///
/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes.
/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes.
/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
/// - `read_packet` must be called with a buffer large enough to hold `max_packet_size` bytes.
/// - `write_packet` must not be called with a buffer larger than `max_packet_size` bytes.
/// - If you write a packet that is exactly `max_packet_size` bytes long, it won't be processed by the
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
@ -39,8 +39,8 @@ pub struct MidiClass<'d, D: Driver<'d>> {
}
impl<'d, D: Driver<'d>> MidiClass<'d, D> {
/// Creates a new MidiClass with the provided UsbBus, number of input and output jacks and max_packet_size in bytes.
/// For full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
/// Creates a new `MidiClass` with the provided UsbBus, number of input and output jacks and `max_packet_size` in bytes.
/// For full-speed devices, `max_packet_size` has to be one of 8, 16, 32 or 64.
pub fn new(builder: &mut Builder<'d, D>, n_in_jacks: u8, n_out_jacks: u8, max_packet_size: u16) -> Self {
let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE);
@ -160,7 +160,7 @@ impl<'d, D: Driver<'d>> MidiClass<'d, D> {
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.read_ep.wait_enabled().await
self.read_ep.wait_enabled().await;
}
/// Split the class into a sender and receiver.
@ -197,7 +197,7 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.write_ep.wait_enabled().await
self.write_ep.wait_enabled().await;
}
}
@ -222,6 +222,6 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> {
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.read_ep.wait_enabled().await
self.read_ep.wait_enabled().await;
}
}

View File

@ -120,7 +120,7 @@ impl Request {
}
/// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request.
pub fn descriptor_type_index(&self) -> (u8, u8) {
pub const fn descriptor_type_index(&self) -> (u8, u8) {
((self.value >> 8) as u8, self.value as u8)
}
}

View File

@ -2,7 +2,7 @@
use crate::builder::Config;
use crate::driver::EndpointInfo;
use crate::types::*;
use crate::types::{InterfaceNumber, StringIndex};
use crate::CONFIGURATION_VALUE;
/// Standard descriptor types
@ -59,7 +59,7 @@ impl<'a> DescriptorWriter<'a> {
}
/// Gets the current position in the buffer, i.e. the number of bytes written so far.
pub fn position(&self) -> usize {
pub const fn position(&self) -> usize {
self.position
}
@ -67,9 +67,10 @@ impl<'a> DescriptorWriter<'a> {
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) {
let length = descriptor.len();
if (self.position + 2 + length) > self.buf.len() || (length + 2) > 255 {
panic!("Descriptor buffer full");
}
assert!(
(self.position + 2 + length) <= self.buf.len() && (length + 2) <= 255,
"Descriptor buffer full"
);
self.buf[self.position] = (length + 2) as u8;
self.buf[self.position + 1] = descriptor_type;
@ -102,7 +103,7 @@ impl<'a> DescriptorWriter<'a> {
config.serial_number.map_or(0, |_| 3), // iSerialNumber
1, // bNumConfigurations
],
)
);
}
pub(crate) fn configuration(&mut self, config: &Config) {
@ -120,7 +121,7 @@ impl<'a> DescriptorWriter<'a> {
| if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes
(config.max_power / 2) as u8, // bMaxPower
],
)
);
}
#[allow(unused)]
@ -248,9 +249,7 @@ impl<'a> DescriptorWriter<'a> {
pub(crate) fn string(&mut self, string: &str) {
let mut pos = self.position;
if pos + 2 > self.buf.len() {
panic!("Descriptor buffer full");
}
assert!(pos + 2 <= self.buf.len(), "Descriptor buffer full");
self.buf[pos] = 0; // length placeholder
self.buf[pos + 1] = descriptor_type::STRING;
@ -258,9 +257,7 @@ impl<'a> DescriptorWriter<'a> {
pos += 2;
for c in string.encode_utf16() {
if pos >= self.buf.len() {
panic!("Descriptor buffer full");
}
assert!(pos < self.buf.len(), "Descriptor buffer full");
self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
pos += 2;
@ -279,9 +276,9 @@ pub struct BosWriter<'a> {
}
impl<'a> BosWriter<'a> {
pub(crate) fn new(writer: DescriptorWriter<'a>) -> Self {
pub(crate) const fn new(writer: DescriptorWriter<'a>) -> Self {
Self {
writer: writer,
writer,
num_caps_mark: None,
}
}
@ -314,9 +311,10 @@ impl<'a> BosWriter<'a> {
let mut start = self.writer.position;
let blen = data.len();
if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 {
panic!("Descriptor buffer full");
}
assert!(
(start + blen + 3) <= self.writer.buf.len() && (blen + 3) <= 255,
"Descriptor buffer full"
);
self.writer.buf[start] = (blen + 3) as u8;
self.writer.buf[start + 1] = descriptor_type::CAPABILITY;

View File

@ -11,11 +11,11 @@ pub struct Reader<'a> {
}
impl<'a> Reader<'a> {
pub fn new(data: &'a [u8]) -> Self {
pub const fn new(data: &'a [u8]) -> Self {
Self { data }
}
pub fn eof(&self) -> bool {
pub const fn eof(&self) -> bool {
self.data.is_empty()
}
@ -102,7 +102,7 @@ pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<
}
descriptor_type::ENDPOINT => {
ep.ep_address = EndpointAddress::from(r.read_u8()?);
f(ep)
f(ep);
}
_ => {}
}

View File

@ -24,12 +24,12 @@ use embassy_futures::select::{select, Either};
use heapless::Vec;
pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder};
use crate::config::*;
use crate::control::*;
use crate::descriptor::*;
use crate::config::{MAX_HANDLER_COUNT, MAX_INTERFACE_COUNT};
use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use crate::descriptor::{descriptor_type, lang_id};
use crate::descriptor_reader::foreach_endpoint;
use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event};
use crate::types::*;
use crate::types::{InterfaceNumber, StringIndex};
/// The global state of the USB device.
///
@ -294,7 +294,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
/// After dropping the future, [`UsbDevice::disable()`] should be called
/// before calling any other `UsbDevice` methods to fully reset the
/// peripheral.
pub async fn run_until_suspend(&mut self) -> () {
pub async fn run_until_suspend(&mut self) {
while !self.inner.suspended {
let control_fut = self.control.setup();
let bus_fut = self.inner.bus.poll();
@ -364,6 +364,8 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
}
async fn handle_control_in(&mut self, req: Request) {
const DEVICE_DESCRIPTOR_LEN: usize = 18;
let mut resp_length = req.length as usize;
let max_packet_size = self.control.max_packet_size();
@ -371,19 +373,15 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
// The host doesn't know our EP0 max packet size yet, and might assume
// a full-length packet is a short packet, thinking we're done sending data.
// See https://github.com/hathach/tinyusb/issues/184
const DEVICE_DESCRIPTOR_LEN: usize = 18;
if self.inner.address == 0
&& max_packet_size < DEVICE_DESCRIPTOR_LEN
&& (max_packet_size as usize) < resp_length
{
if self.inner.address == 0 && max_packet_size < DEVICE_DESCRIPTOR_LEN && max_packet_size < resp_length {
trace!("received control req while not addressed: capping response to 1 packet.");
resp_length = max_packet_size;
}
match self.inner.handle_control_in(req, &mut self.control_buf) {
match self.inner.handle_control_in(req, self.control_buf) {
InResponse::Accepted(data) => {
let len = data.len().min(resp_length);
let need_zlp = len != resp_length && (len % usize::from(max_packet_size)) == 0;
let need_zlp = len != resp_length && (len % max_packet_size) == 0;
let chunks = data[0..len]
.chunks(max_packet_size)
@ -435,7 +433,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
self.control.accept_set_address(self.inner.address).await;
self.inner.set_address_pending = false;
} else {
self.control.accept().await
self.control.accept().await;
}
}
OutResponse::Rejected => self.control.reject().await,
@ -548,9 +546,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
OutResponse::Accepted
}
(Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => match self.device_state {
UsbDeviceState::Default => OutResponse::Accepted,
_ => {
(Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => {
if self.device_state != UsbDeviceState::Default {
debug!("SET_CONFIGURATION: unconfigured");
self.device_state = UsbDeviceState::Addressed;
@ -564,17 +561,15 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
for h in &mut self.handlers {
h.configured(false);
}
OutResponse::Accepted
}
},
OutResponse::Accepted
}
_ => OutResponse::Rejected,
},
(RequestType::Standard, Recipient::Interface) => {
let iface_num = InterfaceNumber::new(req.index as _);
let iface = match self.interfaces.get_mut(iface_num.0 as usize) {
Some(iface) => iface,
None => return OutResponse::Rejected,
let Some(iface) = self.interfaces.get_mut(iface_num.0 as usize) else {
return OutResponse::Rejected;
};
match req.request {
@ -650,9 +645,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
_ => InResponse::Rejected,
},
(RequestType::Standard, Recipient::Interface) => {
let iface = match self.interfaces.get_mut(req.index as usize) {
Some(iface) => iface,
None => return InResponse::Rejected,
let Some(iface) = self.interfaces.get_mut(req.index as usize) else {
return InResponse::Rejected;
};
match req.request {
@ -706,7 +700,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
}
fn handle_control_in_delegated<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
unsafe fn extend_lifetime<'x, 'y>(r: InResponse<'x>) -> InResponse<'y> {
unsafe fn extend_lifetime<'y>(r: InResponse<'_>) -> InResponse<'y> {
core::mem::transmute(r)
}
@ -756,16 +750,12 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
};
if let Some(s) = s {
if buf.len() < 2 {
panic!("control buffer too small");
}
assert!(buf.len() >= 2, "control buffer too small");
buf[1] = descriptor_type::STRING;
let mut pos = 2;
for c in s.encode_utf16() {
if pos + 2 >= buf.len() {
panic!("control buffer too small");
}
assert!(pos + 2 < buf.len(), "control buffer too small");
buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
pos += 2;

View File

@ -6,7 +6,7 @@
use core::mem::size_of;
use super::{capability_type, BosWriter};
use crate::descriptor::{capability_type, BosWriter};
use crate::types::InterfaceNumber;
/// A serialized Microsoft OS 2.0 Descriptor set.

View File

@ -7,7 +7,7 @@
pub struct InterfaceNumber(pub u8);
impl InterfaceNumber {
pub(crate) fn new(index: u8) -> InterfaceNumber {
pub(crate) const fn new(index: u8) -> InterfaceNumber {
InterfaceNumber(index)
}
}
@ -25,7 +25,7 @@ impl From<InterfaceNumber> for u8 {
pub struct StringIndex(pub u8);
impl StringIndex {
pub(crate) fn new(index: u8) -> StringIndex {
pub(crate) const fn new(index: u8) -> StringIndex {
StringIndex(index)
}
}

View File

@ -1,7 +1,12 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
/* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
RAM : ORIGIN = 0x20000000, LENGTH = 256K
/* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */
/*
FLASH : ORIGIN = 0x00027000, LENGTH = 868K
RAM : ORIGIN = 0x20020000, LENGTH = 128K
*/
}

View File

@ -33,7 +33,7 @@ embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["de
embassy-executor = { version = "0.3.0", 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.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
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 }
embedded-io = { version = "0.6.0", features = ["defmt-03"] }
embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] }

View File

@ -1,7 +1,12 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
/* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
RAM : ORIGIN = 0x20000000, LENGTH = 256K
/* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */
/*
FLASH : ORIGIN = 0x00027000, LENGTH = 868K
RAM : ORIGIN = 0x20020000, LENGTH = 128K
*/
}

View File

@ -27,7 +27,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [
"gpiote",
"unstable-pac",
] }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = [
"nightly",
"defmt",
"tcp",

View File

@ -12,7 +12,7 @@ embassy-executor = { version = "0.3.0", path = "../../embassy-executor", feature
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"] }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }

View File

@ -0,0 +1,32 @@
//! This example test the RP Pico on board LED.
//!
//! It does not work with the RP Pico W board. See wifi_blinky.rs.
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::{clocks, gpio};
use embassy_time::Timer;
use gpio::{Level, Output};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = embassy_rp::config::Config::default();
config.clocks = clocks::ClockConfig::rosc();
let p = embassy_rp::init(config);
let mut led = Output::new(p.PIN_25, Level::Low);
loop {
info!("led on!");
led.set_high();
Timer::after_secs(1).await;
info!("led off!");
led.set_low();
Timer::after_secs(1).await;
}
}

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["log"] }
embassy-executor = { version = "0.3.0", 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.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] }
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" }
embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]}
embedded-io-async = { version = "0.6.0" }

View File

@ -11,7 +11,7 @@ embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["de
embassy-executor = { version = "0.3.0", 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-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
defmt = "0.3"
defmt-rtt = "0.4"

View File

@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI;
use embassy_stm32::eth::{Ethernet, PacketQueue};
use embassy_stm32::peripherals::ETH;
use embassy_stm32::rng::Rng;
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
use embassy_time::Timer;
use embedded_io_async::Write;
@ -32,7 +32,25 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(200));
{
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::MUL180,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
divq: None,
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);
info!("Hello World!");

View File

@ -4,15 +4,13 @@
use defmt::info;
use embassy_executor::Spawner;
use embassy_stm32::time::Hertz;
use embassy_stm32::Config;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let mut config = Config::default();
config.rcc.sys_ck = Some(Hertz(84_000_000));
let config = Config::default();
let _p = embassy_stm32::init(config);
loop {

View File

@ -5,7 +5,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::sdmmc::{DataBlock, Sdmmc};
use embassy_stm32::time::mhz;
use embassy_stm32::time::{mhz, Hertz};
use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config};
use {defmt_rtt as _, panic_probe as _};
@ -20,8 +20,25 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(48));
config.rcc.pll48 = true;
{
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(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::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);
info!("Hello World!");

View File

@ -7,7 +7,7 @@ use embassy_executor::Spawner;
use embassy_net::tcp::TcpSocket;
use embassy_net::{Stack, StackResources};
use embassy_stm32::rng::{self, Rng};
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::Driver;
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};
@ -46,9 +46,25 @@ async fn main(spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
config.rcc.pll48 = true;
config.rcc.sys_ck = Some(mhz(48));
{
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(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::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.

View File

@ -4,7 +4,7 @@
use defmt::{panic, *};
use embassy_executor::Spawner;
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
@ -22,9 +22,25 @@ async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
config.rcc.pll48 = true;
config.rcc.sys_ck = Some(mhz(48));
{
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(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::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.

View File

@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.0", 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 = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
embedded-io-async = { version = "0.6.0" }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }

View File

@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI;
use embassy_stm32::eth::{Ethernet, PacketQueue};
use embassy_stm32::peripherals::ETH;
use embassy_stm32::rng::Rng;
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
use embassy_time::Timer;
use embedded_io_async::Write;
@ -33,7 +33,25 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(200));
{
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::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: None,
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);
info!("Hello World!");

View File

@ -4,15 +4,13 @@
use defmt::info;
use embassy_executor::Spawner;
use embassy_stm32::time::Hertz;
use embassy_stm32::Config;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let mut config = Config::default();
config.rcc.sys_ck = Some(Hertz(84_000_000));
let config = Config::default();
let _p = embassy_stm32::init(config);
loop {

View File

@ -5,7 +5,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::sdmmc::Sdmmc;
use embassy_stm32::time::mhz;
use embassy_stm32::time::{mhz, Hertz};
use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config};
use {defmt_rtt as _, panic_probe as _};
@ -16,8 +16,25 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(200));
config.rcc.pll48 = true;
{
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::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 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);
info!("Hello World!");

View File

@ -4,7 +4,7 @@
use defmt::{panic, *};
use embassy_executor::Spawner;
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
@ -22,10 +22,25 @@ async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
config.rcc.hse = Some(mhz(8));
config.rcc.pll48 = true;
config.rcc.sys_ck = Some(mhz(200));
{
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::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 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.

View File

@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.0", 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 = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] }
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" }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }

View File

@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.0", 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 = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] }
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" }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }

View File

@ -13,7 +13,7 @@ embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["de
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "udp", "tcp", "dhcpv4", "medium-ethernet"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "udp", "tcp", "dhcpv4", "medium-ethernet"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
embedded-io-async = { version = "0.6.0", features = ["defmt-03"] }
embedded-io = { version = "0.6.0", features = ["defmt-03"] }

View File

@ -13,7 +13,7 @@ fn main() -> ! {
info!("Hello World!");
pac::RCC.ccipr().modify(|w| {
w.set_adcsel(pac::rcc::vals::Adcsel::SYSCLK);
w.set_adcsel(pac::rcc::vals::Adcsel::SYS);
});
pac::RCC.ahb2enr().modify(|w| w.set_adcen(true));

View File

@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let _mco = Mco::new(p.MCO, p.PA8, McoSource::HSI16, McoPrescaler::DIV1);
let _mco = Mco::new(p.MCO, p.PA8, McoSource::HSI, McoPrescaler::DIV1);
let mut led = Output::new(p.PB14, Level::High, Speed::Low);

View File

@ -16,10 +16,10 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.hsi16 = true;
config.rcc.pll = Some(Pll {
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL18,
divp: None,

View File

@ -15,7 +15,7 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.hse = Some(Hertz::mhz(8));
config.rcc.pll = Some(Pll {
source: PLLSource::HSE,

View File

@ -77,7 +77,7 @@ async fn main(spawner: Spawner) {
// 80Mhz clock (Source: 8 / SrcDiv: 1 * PLLMul 20 / ClkDiv 2)
// 80MHz highest frequency for flash 0 wait.
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.hse = Some(Hertz::mhz(8));
config.rcc.pll = Some(Pll {
source: PLLSource::HSE,

View File

@ -24,10 +24,10 @@ async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.hsi48 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.hsi16 = true;
config.rcc.pll = Some(Pll {
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL10,
divp: None,

View File

@ -11,7 +11,7 @@ embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["de
embassy-executor = { version = "0.3.0", 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-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
usbd-hid = "0.6.0"

View File

@ -17,10 +17,10 @@ bind_interrupts!(struct Irqs {
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.hsi16 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.pll = Some(Pll {
// 64Mhz clock (16 / 1 * 8 / 2)
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL8,
divp: None,

View File

@ -46,10 +46,10 @@ async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! {
async fn main(spawner: Spawner) {
let mut config = Config::default();
config.rcc.hsi16 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.pll = Some(Pll {
// 80Mhz clock (16 / 1 * 10 / 2)
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL10,
divp: None,

View File

@ -23,10 +23,10 @@ bind_interrupts!(struct Irqs {
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.hsi16 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.pll = Some(Pll {
// 80Mhz clock (16 / 1 * 10 / 2)
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL10,
divp: None,

View File

@ -21,10 +21,10 @@ bind_interrupts!(struct Irqs {
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.hsi16 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.pll = Some(Pll {
// 80Mhz clock (16 / 1 * 10 / 2)
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL10,
divp: None,

View File

@ -11,7 +11,7 @@ embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", fea
embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.0", 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.1.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", "nightly"], optional=true }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", "nightly"], optional=true }
defmt = "0.3"
defmt-rtt = "0.4"

View File

@ -9,7 +9,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.0", 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.1.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", "nightly"], optional=true }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", "nightly"], optional=true }
defmt = "0.3"
defmt-rtt = "0.4"

View File

@ -4,6 +4,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::rcc::{ClockSrc, MSIRange};
use embassy_stm32::rng::{self, Rng};
use embassy_stm32::{bind_interrupts, pac, peripherals};
use {defmt_rtt as _, panic_probe as _};
@ -15,12 +16,10 @@ bind_interrupts!(struct Irqs{
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = embassy_stm32::Config::default();
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE;
config.rcc.mux = ClockSrc::MSI(MSIRange::RANGE32M);
let p = embassy_stm32::init(config);
pac::RCC.ccipr().modify(|w| {
w.set_rngsel(0b01);
});
pac::RCC.ccipr().modify(|w| w.set_rngsel(0b11)); // msi
info!("Hello World!");

View File

@ -13,7 +13,7 @@ embassy-executor = { version = "0.3.0", path = "../../embassy-executor", feature
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
embedded-io-async = { version = "0.6.0" }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] }
embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] }
embedded-hal-async = { version = "1.0.0-rc.1" }

View File

@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
defmt = "0.3.0"

View File

@ -12,7 +12,7 @@ embassy-executor = { version = "0.3.0", path = "../../embassy-executor", feature
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] }
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] }
cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] }

View File

@ -24,6 +24,7 @@ stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"]
stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
eth = []
rng = []
@ -46,7 +47,7 @@ embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["de
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
perf-client = { path = "../perf-client" }
defmt = "0.3.0"

View File

@ -8,12 +8,14 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rustc-link-arg-bins=--nmagic");
// too little RAM to run from RAM.
if cfg!(any(
// too little RAM to run from RAM.
feature = "stm32f103c8",
feature = "stm32c031c6",
feature = "stm32wb55rg",
feature = "stm32l073rz",
// wrong ram size in stm32-data
feature = "stm32wl55jc",
)) {
println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rerun-if-changed=link.x");

View File

@ -42,6 +42,8 @@ teleprobe_meta::target!(b"nucleo-stm32f207zg");
teleprobe_meta::target!(b"nucleo-stm32f303ze");
#[cfg(feature = "stm32l496zg")]
teleprobe_meta::target!(b"nucleo-stm32l496zg");
#[cfg(feature = "stm32wl55jc")]
teleprobe_meta::target!(b"nucleo-stm32wl55jc");
macro_rules! define_peris {
($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => {
@ -181,6 +183,12 @@ define_peris!(
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
);
#[cfg(feature = "stm32wl55jc")]
define_peris!(
UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,
SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,
@irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
);
pub fn config() -> Config {
#[allow(unused_mut)]
@ -216,16 +224,44 @@ pub fn config() -> Config {
#[cfg(feature = "stm32f429zi")]
{
// TODO: stm32f429zi can do up to 180mhz, but that makes tests fail.
// perhaps we have some bug w.r.t overdrive.
config.rcc.sys_ck = Some(Hertz(168_000_000));
config.rcc.pclk1 = Some(Hertz(42_000_000));
config.rcc.pclk2 = Some(Hertz(84_000_000));
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::MUL180,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
divq: None,
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;
}
#[cfg(feature = "stm32f767zi")]
{
config.rcc.sys_ck = Some(Hertz(200_000_000));
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::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz.
divq: None,
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;
}
#[cfg(feature = "stm32h563zi")]
@ -287,10 +323,17 @@ pub fn config() -> Config {
#[cfg(any(feature = "stm32l496zg", feature = "stm32l4a6zg", feature = "stm32l4r5zi"))]
{
use embassy_stm32::rcc::*;
config.rcc.mux = ClockSrc::PLL;
#[cfg(feature = "stm32l4r5zi")]
{
config.rcc.mux = ClockSrc::PLL1_R;
}
#[cfg(not(feature = "stm32l4r5zi"))]
{
config.rcc.mux = ClockSrc::PLL1_P;
}
config.rcc.hsi16 = true;
config.rcc.pll = Some(Pll {
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL18,
divp: None,
@ -299,14 +342,23 @@ pub fn config() -> Config {
});
}
#[cfg(feature = "stm32wl55jc")]
{
use embassy_stm32::rcc::*;
config.rcc.mux = ClockSrc::MSI(MSIRange::RANGE32M);
embassy_stm32::pac::RCC.ccipr().modify(|w| {
w.set_rngsel(0b11); // msi
});
}
#[cfg(any(feature = "stm32l552ze"))]
{
use embassy_stm32::rcc::*;
config.rcc.hsi16 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.pll = Some(Pll {
// 110Mhz clock (16 / 4 * 55 / 2)
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL55,
divp: None,