Compare commits
26 Commits
embassy-ti
...
embassy-ne
Author | SHA1 | Date | |
---|---|---|---|
35ffdf2143 | |||
3cbc687424 | |||
4f7b831676 | |||
f20f170b1f | |||
67010d123c | |||
51708c8ed1 | |||
361fde35cf | |||
7ce3b19389 | |||
10f08445e4 | |||
f24a1b62bb | |||
bbd12c9372 | |||
d94b9fe6fb | |||
b478640463 | |||
846f2fc6e4 | |||
683d5c3066 | |||
a3574e519a | |||
3e3317e8bd | |||
e7aeb9b29f | |||
7fd868ade9 | |||
6e6df22979 | |||
f7980885a5 | |||
5a1393aa0b | |||
40e4ca4751 | |||
31d4516516 | |||
66e62e9994 | |||
eeedaf2e76 |
1
ci.sh
1
ci.sh
@ -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 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 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 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/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/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 \
|
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
|
||||||
|
@ -14,7 +14,7 @@ firmware-logs = []
|
|||||||
embassy-time = { version = "0.1.5", path = "../embassy-time"}
|
embassy-time = { version = "0.1.5", path = "../embassy-time"}
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
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 }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.17", optional = true }
|
log = { version = "0.4.17", optional = true }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min};
|
||||||
|
|
||||||
use ch::driver::LinkState;
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
|
|
||||||
pub use crate::bus::SpiBusCyw43;
|
pub use crate::bus::SpiBusCyw43;
|
||||||
@ -133,7 +133,7 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
Timer::after_millis(100).await;
|
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");
|
debug!("INIT DONE");
|
||||||
}
|
}
|
||||||
|
@ -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"]
|
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
|
. Creates an Embassy Executor
|
||||||
. Initializes the microcontroller HAL to get the `Peripherals`
|
. Initializes the microcontroller HAL to get the `Peripherals`
|
||||||
|
@ -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-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||||
embedded-hal-async = { 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"] }
|
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-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
bitfield = "0.14.0"
|
bitfield = "0.14.0"
|
||||||
|
16
embassy-net-driver-channel/CHANGELOG.md
Normal file
16
embassy-net-driver-channel/CHANGELOG.md
Normal 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
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-net-driver-channel"
|
name = "embassy-net-driver-channel"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
|
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-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
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" }
|
||||||
|
@ -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`
|
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.
|
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
|
## A note about deadlocks
|
||||||
|
|
||||||
@ -18,19 +20,19 @@ loop {
|
|||||||
// Wait for either..
|
// Wait for either..
|
||||||
match select(
|
match select(
|
||||||
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
// ... 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
|
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
||||||
tx_chan.tx_buf(),
|
tx_chan.tx_buf(),
|
||||||
).await {
|
).await {
|
||||||
Either::First(_) => {
|
Either::First(_) => {
|
||||||
// a packet is ready to be received!
|
// a packet is ready to be received!
|
||||||
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
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);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
Either::Second(buf) => {
|
Either::Second(buf) => {
|
||||||
// a packet is ready to be sent!
|
// a packet is ready to be sent!
|
||||||
send_packet_over_spi(buf).await;
|
send_packet_over_spi(buf).await;
|
||||||
tx_chan.tx_done();
|
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 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
|
```rust,ignore
|
||||||
loop {
|
loop {
|
||||||
@ -58,12 +60,12 @@ loop {
|
|||||||
).await {
|
).await {
|
||||||
Either::First(buf) => {
|
Either::First(buf) => {
|
||||||
// a packet is ready to be received!
|
// 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);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
Either::Second(buf) => {
|
Either::Second(buf) => {
|
||||||
// a packet is ready to be sent!
|
// a packet is ready to be sent!
|
||||||
send_packet_over_spi(buf).await;
|
send_packet_over_spi(buf).await;
|
||||||
tx_chan.tx_done();
|
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-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.
|
- [`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
|
## Interoperability
|
||||||
|
|
||||||
This crate can run on any executor.
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This work is licensed under either of
|
This work is licensed under either of
|
||||||
|
@ -8,9 +8,8 @@ use core::cell::RefCell;
|
|||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
use driver::HardwareAddress;
|
|
||||||
pub use embassy_net_driver as driver;
|
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::raw::NoopRawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embassy_sync::waitqueue::WakerRegistration;
|
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| {
|
self.shared.lock(|s| {
|
||||||
let s = &mut *s.borrow_mut();
|
let s = &mut *s.borrow_mut();
|
||||||
s.hardware_address = driver::HardwareAddress::Ethernet(address);
|
s.hardware_address = 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.waker.wake();
|
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>) {
|
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
||||||
let mut caps = Capabilities::default();
|
let mut caps = Capabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
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:
|
// safety: this is a self-referential struct, however:
|
||||||
// - it can't move while the `'d` borrow is active.
|
// - it can't move while the `'d` borrow is active.
|
||||||
|
17
embassy-net-driver/CHANGELOG.md
Normal file
17
embassy-net-driver/CHANGELOG.md
Normal 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
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-net-driver"
|
name = "embassy-net-driver"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
|
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
|
||||||
|
@ -7,12 +7,23 @@ use core::task::Context;
|
|||||||
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
|
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum HardwareAddress {
|
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]),
|
Ethernet([u8; 6]),
|
||||||
/// An eight-octet IEEE802.15.4 address
|
/// 6LoWPAN over IEEE802.15.4, with an eight-octet address.
|
||||||
Ieee802154([u8; 8]),
|
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,
|
Ip,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +75,10 @@ pub trait Driver {
|
|||||||
fn capabilities(&self) -> Capabilities;
|
fn capabilities(&self) -> Capabilities;
|
||||||
|
|
||||||
/// Get the device's hardware address.
|
/// 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;
|
fn hardware_address(&self) -> HardwareAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,13 +139,6 @@ pub trait TxToken {
|
|||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Capabilities {
|
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.
|
/// Maximum transmission unit.
|
||||||
///
|
///
|
||||||
/// The network device is unable to send or receive frames larger than the value returned
|
/// 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,
|
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.
|
/// A description of checksum behavior for every supported protocol.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
@ -10,7 +10,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "1.0.0-rc.1" }
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { 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-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ mod traits;
|
|||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
|
|
||||||
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
|
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
|
||||||
use embassy_time::Duration;
|
use embassy_time::Duration;
|
||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
use embedded_hal::spi::{Operation, SpiDevice};
|
use embedded_hal::spi::{Operation, SpiDevice};
|
||||||
@ -671,7 +671,6 @@ where
|
|||||||
fn capabilities(&self) -> Capabilities {
|
fn capabilities(&self) -> Capabilities {
|
||||||
let mut caps = Capabilities::default();
|
let mut caps = Capabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
caps.medium = Medium::Ethernet;
|
|
||||||
caps
|
caps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ log = { version = "0.4.14", optional = true }
|
|||||||
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
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 = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use ch::driver::LinkState;
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
use heapless::String;
|
use heapless::String;
|
||||||
|
|
||||||
use crate::ioctl::Shared;
|
use crate::ioctl::Shared;
|
||||||
@ -77,7 +77,7 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
let mac_addr = self.get_mac_addr().await?;
|
let mac_addr = self.get_mac_addr().await?;
|
||||||
debug!("mac addr: {:02x}", mac_addr);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ defmt = { version = "0.3", optional = true }
|
|||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embedded-io-async = { version = "0.6.0" }
|
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" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
ppproto = { version = "0.1.2"}
|
ppproto = { version = "0.1.2"}
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[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"
|
async-io = "1.6.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
libc = "0.2.101"
|
libc = "0.2.101"
|
||||||
|
@ -10,7 +10,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "1.0.0-rc.1" }
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { 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-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
29
embassy-net/CHANGELOG.md
Normal file
29
embassy-net/CHANGELOG.md
Normal 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
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-net"
|
name = "embassy-net"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "Async TCP/IP network stack for embedded systems"
|
description = "Async TCP/IP network stack for embedded systems"
|
||||||
@ -51,7 +51,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
|
|||||||
"async",
|
"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-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embedded-io-async = { version = "0.6.0", optional = true }
|
embedded-io-async = { version = "0.6.0", optional = true }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use core::task::Context;
|
use core::task::Context;
|
||||||
|
|
||||||
use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken};
|
use embassy_net_driver::{Capabilities, Checksum, Driver, RxToken, TxToken};
|
||||||
use smoltcp::phy;
|
use smoltcp::phy::{self, Medium};
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
|
|
||||||
pub(crate) struct DriverAdapter<'d, 'c, T>
|
pub(crate) struct DriverAdapter<'d, 'c, T>
|
||||||
@ -11,6 +11,7 @@ where
|
|||||||
// must be Some when actually using this to rx/tx
|
// must be Some when actually using this to rx/tx
|
||||||
pub cx: Option<&'d mut Context<'c>>,
|
pub cx: Option<&'d mut Context<'c>>,
|
||||||
pub inner: &'d mut T,
|
pub inner: &'d mut T,
|
||||||
|
pub medium: Medium,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T>
|
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_transmission_unit = caps.max_transmission_unit;
|
||||||
smolcaps.max_burst_size = caps.max_burst_size;
|
smolcaps.max_burst_size = caps.max_burst_size;
|
||||||
smolcaps.medium = match caps.medium {
|
smolcaps.medium = self.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.checksum.ipv4 = convert(caps.checksum.ipv4);
|
smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4);
|
||||||
smolcaps.checksum.tcp = convert(caps.checksum.tcp);
|
smolcaps.checksum.tcp = convert(caps.checksum.tcp);
|
||||||
smolcaps.checksum.udp = convert(caps.checksum.udp);
|
smolcaps.checksum.udp = convert(caps.checksum.udp);
|
||||||
|
@ -33,6 +33,7 @@ use heapless::Vec;
|
|||||||
pub use smoltcp::iface::MulticastError;
|
pub use smoltcp::iface::MulticastError;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
||||||
|
use smoltcp::phy::Medium;
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
@ -264,14 +265,17 @@ pub(crate) struct SocketStack {
|
|||||||
next_local_port: u16,
|
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 {
|
match addr {
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[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")]
|
#[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")]
|
#[cfg(feature = "medium-ip")]
|
||||||
driver::HardwareAddress::Ip => HardwareAddress::Ip,
|
driver::HardwareAddress::Ip => (HardwareAddress::Ip, Medium::Ip),
|
||||||
|
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
@ -289,7 +293,8 @@ impl<D: Driver> Stack<D> {
|
|||||||
resources: &'static mut StackResources<SOCK>,
|
resources: &'static mut StackResources<SOCK>,
|
||||||
random_seed: u64,
|
random_seed: u64,
|
||||||
) -> Self {
|
) -> 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;
|
iface_cfg.random_seed = random_seed;
|
||||||
|
|
||||||
let iface = Interface::new(
|
let iface = Interface::new(
|
||||||
@ -297,6 +302,7 @@ impl<D: Driver> Stack<D> {
|
|||||||
&mut DriverAdapter {
|
&mut DriverAdapter {
|
||||||
inner: &mut device,
|
inner: &mut device,
|
||||||
cx: None,
|
cx: None,
|
||||||
|
medium,
|
||||||
},
|
},
|
||||||
instant_to_smoltcp(Instant::now()),
|
instant_to_smoltcp(Instant::now()),
|
||||||
);
|
);
|
||||||
@ -356,7 +362,7 @@ impl<D: Driver> Stack<D> {
|
|||||||
|
|
||||||
/// Get the hardware address of the network interface.
|
/// Get the hardware address of the network interface.
|
||||||
pub fn hardware_address(&self) -> HardwareAddress {
|
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.
|
/// 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) {
|
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
|
||||||
s.waker.register(cx.waker());
|
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"))]
|
#[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
|
let do_set = match medium {
|
||||||
.set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
|
#[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 timestamp = instant_to_smoltcp(Instant::now());
|
||||||
let mut smoldev = DriverAdapter {
|
let mut smoldev = DriverAdapter {
|
||||||
cx: Some(cx),
|
cx: Some(cx),
|
||||||
inner: &mut self.device,
|
inner: &mut self.device,
|
||||||
|
medium,
|
||||||
};
|
};
|
||||||
s.iface.poll(timestamp, &mut smoldev, &mut s.sockets);
|
s.iface.poll(timestamp, &mut smoldev, &mut s.sockets);
|
||||||
|
|
||||||
|
@ -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-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" }
|
embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
|
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 }
|
defmt = { version = "0.3", optional = true }
|
||||||
cortex-m = "0.7.6"
|
cortex-m = "0.7.6"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use core::task::Context;
|
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::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::channel::Channel;
|
use embassy_sync::channel::Channel;
|
||||||
|
|
||||||
@ -60,24 +60,15 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
|
|||||||
let mut caps = Capabilities::default();
|
let mut caps = Capabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
// caps.max_burst_size = Some(self.tx.len());
|
// caps.max_burst_size = Some(self.tx.len());
|
||||||
|
|
||||||
caps.medium = Medium::Ieee802154;
|
|
||||||
caps
|
caps
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_state(&mut self, _cx: &mut Context) -> LinkState {
|
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
|
LinkState::Down
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hardware_address(&self) -> HardwareAddress {
|
fn hardware_address(&self) -> HardwareAddress {
|
||||||
// self.mac_addr
|
// self.mac_addr
|
||||||
|
|
||||||
HardwareAddress::Ieee802154([0; 8])
|
HardwareAddress::Ieee802154([0; 8])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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-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-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-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-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
|
||||||
embassy-executor = { version = "0.3.0", path = "../embassy-executor", 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"
|
sdio-host = "0.5.0"
|
||||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||||
critical-section = "1.1"
|
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"
|
vcell = "0.1.3"
|
||||||
bxcan = "0.7.0"
|
bxcan = "0.7.0"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
quote = "1.0.15"
|
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]
|
[features]
|
||||||
|
@ -466,15 +466,9 @@ fn main() {
|
|||||||
|
|
||||||
let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" };
|
let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" };
|
||||||
let pname = format_ident!("{}", p.name);
|
let pname = format_ident!("{}", p.name);
|
||||||
let clk = format_ident!(
|
let clk = format_ident!("{}", rcc.clock);
|
||||||
"{}",
|
let en_reg = format_ident!("{}", en.register);
|
||||||
rcc.clock
|
let set_en_field = format_ident!("set_{}", en.field);
|
||||||
.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 (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) {
|
let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) {
|
||||||
let refcount_static =
|
let refcount_static =
|
||||||
@ -500,11 +494,11 @@ fn main() {
|
|||||||
(TokenStream::new(), TokenStream::new())
|
(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 mux_for = |mux: Option<&'static PeripheralRccRegister>| {
|
||||||
let checked_rccs = HashSet::from(["h5", "h50", "h7", "h7ab", "h7rm0433", "g4"]);
|
|
||||||
|
|
||||||
// restrict mux implementation to supported versions
|
// restrict mux implementation to supported versions
|
||||||
if !checked_rccs.contains(rcc_registers.version) {
|
if !mux_supported {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +134,8 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
|
hsi: None,
|
||||||
|
lse: None,
|
||||||
sys: sys_clk,
|
sys: sys_clk,
|
||||||
hclk1: ahb_freq,
|
hclk1: ahb_freq,
|
||||||
pclk1: apb_freq,
|
pclk1: apb_freq,
|
||||||
|
@ -127,7 +127,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.usb_pll {
|
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)
|
// TODO: Option to use CRS (Clock Recovery)
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_ppre(Ppre::from_bits(ppre_bits));
|
w.set_ppre(Ppre::from_bits(ppre_bits));
|
||||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||||
w.set_sw(Sw::PLL)
|
w.set_sw(Sw::PLL1_P)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
|
@ -102,7 +102,6 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
assert!(pclk2 <= 72_000_000);
|
assert!(pclk2 <= 72_000_000);
|
||||||
|
|
||||||
// Only needed for stm32f103?
|
|
||||||
FLASH.acr().write(|w| {
|
FLASH.acr().write(|w| {
|
||||||
w.set_latency(if real_sysclk <= 24_000_000 {
|
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||||
Latency::WS0
|
Latency::WS0
|
||||||
@ -111,6 +110,8 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
} else {
|
} else {
|
||||||
Latency::WS2
|
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
|
// 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))]
|
#[cfg(not(rcc_f100))]
|
||||||
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
|
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
|
||||||
w.set_sw(if pllmul_bits.is_some() {
|
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() {
|
} else if config.hse.is_some() {
|
||||||
Sw::HSE
|
Sw::HSE
|
||||||
} else {
|
} else {
|
||||||
|
@ -256,7 +256,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
ClockSrc::PLL => {
|
ClockSrc::PLL => {
|
||||||
RCC.cr().modify(|w| w.set_pllon(true));
|
RCC.cr().modify(|w| w.set_pllon(true));
|
||||||
while !RCC.cr().read().pllrdy() {}
|
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
|
// RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL
|
||||||
|
@ -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
|
// CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_sw(match (pll_config, config.hse) {
|
w.set_sw(match (pll_config, config.hse) {
|
||||||
(Some(_), _) => Sw::PLL,
|
(Some(_), _) => Sw::PLL1_P,
|
||||||
(None, Some(_)) => Sw::HSE,
|
(None, Some(_)) => Sw::HSE,
|
||||||
(None, None) => Sw::HSI,
|
(None, None) => Sw::HSI,
|
||||||
})
|
})
|
||||||
@ -271,7 +271,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
pll_config.unwrap();
|
pll_config.unwrap();
|
||||||
assert!((pclk2 == sysclk) || (pclk2 * 2u32 == sysclk));
|
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)
|
Some(sysclk * 2u32)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
379
embassy-stm32/src/rcc/f4f7.rs
Normal file
379
embassy-stm32/src/rcc/f4f7.rs
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
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, PWR, 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.
|
||||||
|
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);
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
@ -58,15 +58,6 @@ pub struct Hse {
|
|||||||
pub mode: HseMode,
|
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)]
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum Hsi {
|
pub enum Hsi {
|
||||||
/// 64Mhz
|
/// 64Mhz
|
||||||
|
@ -31,7 +31,7 @@ pub enum PLLSource {
|
|||||||
impl From<PLLSource> for Pllsrc {
|
impl From<PLLSource> for Pllsrc {
|
||||||
fn from(val: PLLSource) -> Pllsrc {
|
fn from(val: PLLSource) -> Pllsrc {
|
||||||
match val {
|
match val {
|
||||||
PLLSource::HSI16 => Pllsrc::HSI16,
|
PLLSource::HSI16 => Pllsrc::HSI,
|
||||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
PLLSource::HSE(_) => Pllsrc::HSE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
RCC.cr().write(|w| w.set_hsi16on(true));
|
||||||
while !RCC.cr().read().hsi16rdy() {}
|
while !RCC.cr().read().hsi16rdy() {}
|
||||||
|
|
||||||
(HSI_FREQ, Sw::HSI16)
|
(HSI_FREQ, Sw::HSI)
|
||||||
}
|
}
|
||||||
ClockSrc::HSE(freq) => {
|
ClockSrc::HSE(freq) => {
|
||||||
// Enable HSE
|
// Enable HSE
|
||||||
@ -131,7 +131,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
RCC.cr().modify(|w| w.set_pllon(true));
|
RCC.cr().modify(|w| w.set_pllon(true));
|
||||||
while !RCC.cr().read().pllrdy() {}
|
while !RCC.cr().read().pllrdy() {}
|
||||||
|
|
||||||
(freq, Sw::PLL)
|
(freq, Sw::PLL1_P)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,9 +187,12 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
let sys_clk = match config.mux {
|
let sys_clk = match config.mux {
|
||||||
ClockSrc::HSE => hse.unwrap(),
|
ClockSrc::HSE => hse.unwrap(),
|
||||||
ClockSrc::HSI16 => hsi16.unwrap(),
|
ClockSrc::HSI => hsi16.unwrap(),
|
||||||
ClockSrc::MSI => msi.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)]
|
#[cfg(stm32l4)]
|
||||||
@ -200,7 +203,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
Clk48Src::HSI48 => hsi48,
|
Clk48Src::HSI48 => hsi48,
|
||||||
Clk48Src::MSI => msi,
|
Clk48Src::MSI => msi,
|
||||||
Clk48Src::PLLSAI1_Q => pllsai1._q,
|
Clk48Src::PLLSAI1_Q => pllsai1._q,
|
||||||
Clk48Src::PLL_Q => pll._q,
|
Clk48Src::PLL1_Q => pll._q,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(rcc_l4plus)]
|
#[cfg(rcc_l4plus)]
|
||||||
@ -238,6 +241,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
w.set_ppre1(config.apb1_pre);
|
w.set_ppre1(config.apb1_pre);
|
||||||
w.set_ppre2(config.apb2_pre);
|
w.set_ppre2(config.apb2_pre);
|
||||||
});
|
});
|
||||||
|
while RCC.cfgr().read().sws() != config.mux {}
|
||||||
|
|
||||||
let ahb_freq = sys_clk / config.ahb_pre;
|
let ahb_freq = sys_clk / config.ahb_pre;
|
||||||
|
|
||||||
@ -266,6 +270,22 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
pclk2: apb2_freq,
|
pclk2: apb2_freq,
|
||||||
pclk1_tim: apb1_tim_freq,
|
pclk1_tim: apb1_tim_freq,
|
||||||
pclk2_tim: apb2_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,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -341,7 +361,7 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
|
|||||||
let pll_src = match pll.source {
|
let pll_src = match pll.source {
|
||||||
PLLSource::NONE => panic!("must not select PLL source as NONE"),
|
PLLSource::NONE => panic!("must not select PLL source as NONE"),
|
||||||
PLLSource::HSE => input.hse,
|
PLLSource::HSE => input.hse,
|
||||||
PLLSource::HSI16 => input.hsi16,
|
PLLSource::HSI => input.hsi16,
|
||||||
PLLSource::MSI => input.msi,
|
PLLSource::MSI => input.msi,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,8 +13,7 @@ pub use mco::*;
|
|||||||
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
|
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
|
||||||
#[cfg_attr(rcc_f2, path = "f2.rs")]
|
#[cfg_attr(rcc_f2, path = "f2.rs")]
|
||||||
#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")]
|
#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")]
|
||||||
#[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")]
|
#[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")]
|
||||||
#[cfg_attr(rcc_f7, path = "f7.rs")]
|
|
||||||
#[cfg_attr(rcc_c0, path = "c0.rs")]
|
#[cfg_attr(rcc_c0, path = "c0.rs")]
|
||||||
#[cfg_attr(rcc_g0, path = "g0.rs")]
|
#[cfg_attr(rcc_g0, path = "g0.rs")]
|
||||||
#[cfg_attr(rcc_g4, path = "g4.rs")]
|
#[cfg_attr(rcc_g4, path = "g4.rs")]
|
||||||
@ -110,14 +109,18 @@ pub struct Clocks {
|
|||||||
#[cfg(all(rcc_f4, not(stm32f410)))]
|
#[cfg(all(rcc_f4, not(stm32f410)))]
|
||||||
pub plli2s1_r: Option<Hertz>,
|
pub plli2s1_r: Option<Hertz>,
|
||||||
|
|
||||||
|
#[cfg(rcc_l4)]
|
||||||
|
pub pllsai1_p: Option<Hertz>,
|
||||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
pub pllsai1_q: Option<Hertz>,
|
pub pllsai1_q: Option<Hertz>,
|
||||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
pub pllsai1_r: Option<Hertz>,
|
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>,
|
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>,
|
pub pll1_q: Option<Hertz>,
|
||||||
#[cfg(any(stm32h5, stm32h7))]
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
pub pll2_p: Option<Hertz>,
|
pub pll2_p: Option<Hertz>,
|
||||||
@ -154,7 +157,7 @@ pub struct Clocks {
|
|||||||
|
|
||||||
pub rtc: Option<Hertz>,
|
pub rtc: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(any(stm32h5, stm32h7))]
|
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
|
||||||
pub hsi: Option<Hertz>,
|
pub hsi: Option<Hertz>,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
pub hsi48: Option<Hertz>,
|
pub hsi48: Option<Hertz>,
|
||||||
@ -163,7 +166,7 @@ pub struct Clocks {
|
|||||||
#[cfg(any(stm32h5, stm32h7))]
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
pub csi: Option<Hertz>,
|
pub csi: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(any(stm32h5, stm32h7))]
|
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
|
||||||
pub lse: Option<Hertz>,
|
pub lse: Option<Hertz>,
|
||||||
#[cfg(any(stm32h5, stm32h7))]
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
pub hse: Option<Hertz>,
|
pub hse: Option<Hertz>,
|
||||||
@ -175,6 +178,10 @@ pub struct Clocks {
|
|||||||
|
|
||||||
#[cfg(stm32h7)]
|
#[cfg(stm32h7)]
|
||||||
pub rcc_pclk_d3: Option<Hertz>,
|
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")]
|
#[cfg(feature = "low-power")]
|
||||||
|
@ -42,7 +42,7 @@ max-handler-count-8 = []
|
|||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
|
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
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 }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
@ -70,9 +70,11 @@ fn main() {
|
|||||||
|
|
||||||
// envvars take priority.
|
// envvars take priority.
|
||||||
if !cfg.seen_env {
|
if !cfg.seen_env {
|
||||||
if cfg.seen_feature {
|
assert!(
|
||||||
panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value);
|
!cfg.seen_feature,
|
||||||
}
|
"multiple values set for feature {}: {} and {}",
|
||||||
|
name, cfg.value, value
|
||||||
|
);
|
||||||
|
|
||||||
cfg.value = value;
|
cfg.value = value;
|
||||||
cfg.seen_feature = true;
|
cfg.seen_feature = true;
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
use crate::config::*;
|
use crate::config::MAX_HANDLER_COUNT;
|
||||||
use crate::descriptor::{BosWriter, DescriptorWriter};
|
use crate::descriptor::{BosWriter, DescriptorWriter};
|
||||||
use crate::driver::{Driver, Endpoint, EndpointType};
|
use crate::driver::{Driver, Endpoint, EndpointType};
|
||||||
#[cfg(feature = "msos-descriptor")]
|
#[cfg(feature = "msos-descriptor")]
|
||||||
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
|
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};
|
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
/// Configuration used when creating [UsbDevice].
|
/// Configuration used when creating [`UsbDevice`].
|
||||||
pub struct Config<'a> {
|
pub struct Config<'a> {
|
||||||
pub(crate) vendor_id: u16,
|
pub(crate) vendor_id: u16,
|
||||||
pub(crate) product_id: u16,
|
pub(crate) product_id: u16,
|
||||||
@ -99,7 +99,7 @@ pub struct Config<'a> {
|
|||||||
|
|
||||||
impl<'a> Config<'a> {
|
impl<'a> Config<'a> {
|
||||||
/// Create default configuration with the provided vid and pid values.
|
/// 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 {
|
Self {
|
||||||
device_class: 0x00,
|
device_class: 0x00,
|
||||||
device_sub_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");
|
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 {
|
assert!(
|
||||||
panic!("The maximum allowed value for `max_power` is 500mA");
|
config.max_power <= 500,
|
||||||
}
|
"The maximum allowed value for `max_power` is 500mA"
|
||||||
|
);
|
||||||
|
|
||||||
match config.max_packet_size_0 {
|
match config.max_packet_size_0 {
|
||||||
8 | 16 | 32 | 64 => {}
|
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
|
/// The Handler is called on some USB bus events, and to handle all control requests not already
|
||||||
/// handled by the USB stack.
|
/// handled by the USB stack.
|
||||||
pub fn handler(&mut self, handler: &'d mut dyn Handler) {
|
pub fn handler(&mut self, handler: &'d mut dyn Handler) {
|
||||||
if self.handlers.push(handler).is_err() {
|
assert!(
|
||||||
panic!(
|
self.handlers.push(handler).is_ok(),
|
||||||
"embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}",
|
"embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}",
|
||||||
MAX_HANDLER_COUNT
|
MAX_HANDLER_COUNT
|
||||||
)
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a new string index.
|
/// Allocates a new string index.
|
||||||
@ -332,12 +332,10 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
|
|||||||
num_alt_settings: 0,
|
num_alt_settings: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.builder.interfaces.push(iface).is_err() {
|
assert!(self.builder.interfaces.push(iface).is_ok(),
|
||||||
panic!(
|
"embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
|
||||||
"embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
|
MAX_INTERFACE_COUNT
|
||||||
MAX_INTERFACE_COUNT
|
);
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
InterfaceBuilder {
|
InterfaceBuilder {
|
||||||
builder: self.builder,
|
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> {
|
impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
|
||||||
/// Get the interface number.
|
/// Get the interface number.
|
||||||
pub fn interface_number(&self) -> InterfaceNumber {
|
pub const fn interface_number(&self) -> InterfaceNumber {
|
||||||
self.interface_number
|
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> {
|
impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||||
/// Get the interface number.
|
/// Get the interface number.
|
||||||
pub fn interface_number(&self) -> InterfaceNumber {
|
pub const fn interface_number(&self) -> InterfaceNumber {
|
||||||
self.interface_number
|
self.interface_number
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the alternate setting 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
|
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
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
/// classes care about the order.
|
/// classes care about the order.
|
||||||
pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {
|
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 {
|
fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
||||||
|
@ -11,7 +11,7 @@ use embassy_sync::waitqueue::WakerRegistration;
|
|||||||
|
|
||||||
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
|
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
|
||||||
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||||
use crate::types::*;
|
use crate::types::InterfaceNumber;
|
||||||
use crate::{Builder, Handler};
|
use crate::{Builder, Handler};
|
||||||
|
|
||||||
/// This should be used as `device_class` when building the `UsbDevice`.
|
/// This should be used as `device_class` when building the `UsbDevice`.
|
||||||
@ -39,12 +39,18 @@ pub struct State<'a> {
|
|||||||
shared: ControlShared,
|
shared: ControlShared,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Default for State<'a> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> State<'a> {
|
impl<'a> State<'a> {
|
||||||
/// Create a new `State`.
|
/// Create a new `State`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
control: MaybeUninit::uninit(),
|
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
|
/// 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:
|
/// 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.
|
/// - `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.
|
/// - `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
|
/// - 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)
|
/// 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
|
/// 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.
|
/// 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 {
|
impl ControlShared {
|
||||||
async fn changed(&self) {
|
async fn changed(&self) {
|
||||||
poll_fn(|cx| match self.changed.load(Ordering::Relaxed) {
|
poll_fn(|cx| {
|
||||||
true => {
|
if self.changed.load(Ordering::Relaxed) {
|
||||||
self.changed.store(false, Ordering::Relaxed);
|
self.changed.store(false, Ordering::Relaxed);
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
}
|
} else {
|
||||||
false => {
|
|
||||||
self.waker.borrow_mut().register(cx.waker());
|
self.waker.borrow_mut().register(cx.waker());
|
||||||
Poll::Pending
|
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_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
|
||||||
REQ_GET_LINE_CODING if req.length == 7 => {
|
REQ_GET_LINE_CODING if req.length == 7 => {
|
||||||
debug!("Sending line coding");
|
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);
|
assert!(buf.len() >= 7);
|
||||||
buf[0..4].copy_from_slice(&coding.data_rate.to_le_bytes());
|
buf[0..4].copy_from_slice(&coding.data_rate.to_le_bytes());
|
||||||
buf[4] = coding.stop_bits as u8;
|
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> {
|
impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
||||||
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
|
/// 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.
|
/// 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 {
|
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self {
|
||||||
assert!(builder.control_buf_len() >= 7);
|
assert!(builder.control_buf_len() >= 7);
|
||||||
|
|
||||||
@ -242,7 +247,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
|||||||
&[
|
&[
|
||||||
CDC_TYPE_UNION, // bDescriptorSubtype
|
CDC_TYPE_UNION, // bDescriptorSubtype
|
||||||
comm_if.into(), // bControlInterface
|
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
|
/// 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.
|
/// for USB to UART serial port emulators, and can be ignored if not relevant.
|
||||||
pub fn line_coding(&self) -> LineCoding {
|
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
|
/// 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
|
/// Waits for the USB host to enable this interface
|
||||||
pub async fn wait_connection(&mut self) {
|
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.
|
/// Split the class into a sender and receiver.
|
||||||
@ -356,7 +361,7 @@ pub struct ControlChanged<'d> {
|
|||||||
impl<'d> ControlChanged<'d> {
|
impl<'d> ControlChanged<'d> {
|
||||||
/// Return a future for when the control settings change
|
/// Return a future for when the control settings change
|
||||||
pub async fn control_changed(&self) {
|
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
|
/// 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.
|
/// for USB to UART serial port emulators, and can be ignored if not relevant.
|
||||||
pub fn line_coding(&self) -> LineCoding {
|
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
|
/// 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
|
/// Waits for the USB host to enable this interface
|
||||||
pub async fn wait_connection(&mut self) {
|
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
|
/// 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.
|
/// for USB to UART serial port emulators, and can be ignored if not relevant.
|
||||||
pub fn line_coding(&self) -> LineCoding {
|
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
|
/// 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
|
/// Waits for the USB host to enable this interface
|
||||||
pub async fn wait_connection(&mut self) {
|
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.
|
/// 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
|
self.data_bits
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the parity type for UART communication.
|
/// Gets the parity type for UART communication.
|
||||||
pub fn parity_type(&self) -> ParityType {
|
pub const fn parity_type(&self) -> ParityType {
|
||||||
self.parity_type
|
self.parity_type
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the data rate in bits per second for UART communication.
|
/// 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
|
self.data_rate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,11 @@
|
|||||||
|
|
||||||
use core::intrinsics::copy_nonoverlapping;
|
use core::intrinsics::copy_nonoverlapping;
|
||||||
use core::mem::{size_of, MaybeUninit};
|
use core::mem::{size_of, MaybeUninit};
|
||||||
|
use core::ptr::addr_of;
|
||||||
|
|
||||||
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
|
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
|
||||||
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||||
use crate::types::*;
|
use crate::types::{InterfaceNumber, StringIndex};
|
||||||
use crate::{Builder, Handler};
|
use crate::{Builder, Handler};
|
||||||
|
|
||||||
pub mod embassy_net;
|
pub mod embassy_net;
|
||||||
@ -62,9 +63,9 @@ const REQ_SET_NTB_INPUT_SIZE: u8 = 0x86;
|
|||||||
//const NOTIF_POLL_INTERVAL: u8 = 20;
|
//const NOTIF_POLL_INTERVAL: u8 = 20;
|
||||||
|
|
||||||
const NTB_MAX_SIZE: usize = 2048;
|
const NTB_MAX_SIZE: usize = 2048;
|
||||||
const SIG_NTH: u32 = 0x484d434e;
|
const SIG_NTH: u32 = 0x484d_434e;
|
||||||
const SIG_NDP_NO_FCS: u32 = 0x304d434e;
|
const SIG_NDP_NO_FCS: u32 = 0x304d_434e;
|
||||||
const SIG_NDP_WITH_FCS: u32 = 0x314d434e;
|
const SIG_NDP_WITH_FCS: u32 = 0x314d_434e;
|
||||||
|
|
||||||
const ALTERNATE_SETTING_DISABLED: u8 = 0x00;
|
const ALTERNATE_SETTING_DISABLED: u8 = 0x00;
|
||||||
const ALTERNATE_SETTING_ENABLED: u8 = 0x01;
|
const ALTERNATE_SETTING_ENABLED: u8 = 0x01;
|
||||||
@ -111,7 +112,7 @@ struct NtbParametersDir {
|
|||||||
|
|
||||||
fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
|
fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
|
||||||
let len = size_of::<T>();
|
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]
|
&buf[..len]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,27 +122,28 @@ pub struct State<'a> {
|
|||||||
shared: ControlShared,
|
shared: ControlShared,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Default for State<'a> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> State<'a> {
|
impl<'a> State<'a> {
|
||||||
/// Create a new `State`.
|
/// Create a new `State`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
control: MaybeUninit::uninit(),
|
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 {
|
struct ControlShared {
|
||||||
mac_addr: [u8; 6],
|
mac_addr: [u8; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ControlShared {
|
|
||||||
fn default() -> Self {
|
|
||||||
ControlShared { mac_addr: [0; 6] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Control<'a> {
|
struct Control<'a> {
|
||||||
mac_addr_string: StringIndex,
|
mac_addr_string: StringIndex,
|
||||||
shared: &'a ControlShared,
|
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.
|
/// 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> {
|
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 OUT_HEADER_LEN: usize = 28;
|
||||||
const ABS_MAX_PACKET_SIZE: usize = 512;
|
const ABS_MAX_PACKET_SIZE: usize = 512;
|
||||||
|
|
||||||
|
let seq = self.seq;
|
||||||
|
self.seq = self.seq.wrapping_add(1);
|
||||||
|
|
||||||
let header = NtbOutHeader {
|
let header = NtbOutHeader {
|
||||||
nth_sig: SIG_NTH,
|
nth_sig: SIG_NTH,
|
||||||
nth_len: 0x0c,
|
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?;
|
self.write_ep.write(&buf[..self.max_packet_size]).await?;
|
||||||
|
|
||||||
for chunk in d2.chunks(self.max_packet_size) {
|
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.
|
// Send ZLP if needed.
|
||||||
@ -459,12 +461,9 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> {
|
|||||||
let ntb = &ntb[..pos];
|
let ntb = &ntb[..pos];
|
||||||
|
|
||||||
// Process NTB header (NTH)
|
// Process NTB header (NTH)
|
||||||
let nth = match ntb.get(..12) {
|
let Some(nth) = ntb.get(..12) else {
|
||||||
Some(x) => x,
|
warn!("Received too short NTB");
|
||||||
None => {
|
continue;
|
||||||
warn!("Received too short NTB");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let sig = u32::from_le_bytes(nth[0..4].try_into().unwrap());
|
let sig = u32::from_le_bytes(nth[0..4].try_into().unwrap());
|
||||||
if sig != SIG_NTH {
|
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;
|
let ndp_idx = u16::from_le_bytes(nth[10..12].try_into().unwrap()) as usize;
|
||||||
|
|
||||||
// Process NTB Datagram Pointer (NDP)
|
// Process NTB Datagram Pointer (NDP)
|
||||||
let ndp = match ntb.get(ndp_idx..ndp_idx + 12) {
|
let Some(ndp) = ntb.get(ndp_idx..ndp_idx + 12) else {
|
||||||
Some(x) => x,
|
warn!("NTH has an NDP pointer out of range.");
|
||||||
None => {
|
continue;
|
||||||
warn!("NTH has an NDP pointer out of range.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let sig = u32::from_le_bytes(ndp[0..4].try_into().unwrap());
|
let sig = u32::from_le_bytes(ndp[0..4].try_into().unwrap());
|
||||||
if sig != SIG_NDP_NO_FCS && sig != SIG_NDP_WITH_FCS {
|
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.
|
// Process actual datagram, finally.
|
||||||
let datagram = match ntb.get(datagram_index..datagram_index + datagram_len) {
|
let Some(datagram) = ntb.get(datagram_index..datagram_index + datagram_len) else {
|
||||||
Some(x) => x,
|
warn!("NDP has a datagram pointer out of range.");
|
||||||
None => {
|
continue;
|
||||||
warn!("NDP has a datagram pointer out of range.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
buf[..datagram_len].copy_from_slice(datagram);
|
buf[..datagram_len].copy_from_slice(datagram);
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ pub enum ReportId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ReportId {
|
impl ReportId {
|
||||||
fn try_from(value: u16) -> Result<Self, ()> {
|
const fn try_from(value: u16) -> Result<Self, ()> {
|
||||||
match value >> 8 {
|
match value >> 8 {
|
||||||
1 => Ok(ReportId::In(value as u8)),
|
1 => Ok(ReportId::In(value as u8)),
|
||||||
2 => Ok(ReportId::Out(value as u8)),
|
2 => Ok(ReportId::Out(value as u8)),
|
||||||
@ -79,9 +79,15 @@ pub struct State<'d> {
|
|||||||
out_report_offset: AtomicUsize,
|
out_report_offset: AtomicUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'d> Default for State<'d> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d> State<'d> {
|
impl<'d> State<'d> {
|
||||||
/// Create a new `State`.
|
/// Create a new `State`.
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
State {
|
State {
|
||||||
control: MaybeUninit::uninit(),
|
control: MaybeUninit::uninit(),
|
||||||
out_report_offset: AtomicUsize::new(0),
|
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> {
|
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)
|
/// 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.
|
/// 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.
|
/// 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.reader.ready().await;
|
||||||
self.writer.ready().await;
|
self.writer.ready().await;
|
||||||
}
|
}
|
||||||
@ -224,7 +230,7 @@ pub enum ReadError {
|
|||||||
|
|
||||||
impl From<EndpointError> for ReadError {
|
impl From<EndpointError> for ReadError {
|
||||||
fn from(val: EndpointError) -> Self {
|
fn from(val: EndpointError) -> Self {
|
||||||
use EndpointError::*;
|
use EndpointError::{BufferOverflow, Disabled};
|
||||||
match val {
|
match val {
|
||||||
BufferOverflow => ReadError::BufferOverflow,
|
BufferOverflow => ReadError::BufferOverflow,
|
||||||
Disabled => ReadError::Disabled,
|
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.
|
/// Waits for the interrupt in endpoint to be enabled.
|
||||||
pub async fn ready(&mut self) -> () {
|
pub async fn ready(&mut self) {
|
||||||
self.ep_in.wait_enabled().await
|
self.ep_in.wait_enabled().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes an input report by serializing the given report structure.
|
/// Writes an input report by serializing the given report structure.
|
||||||
#[cfg(feature = "usbd-hid")]
|
#[cfg(feature = "usbd-hid")]
|
||||||
pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {
|
pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {
|
||||||
let mut buf: [u8; N] = [0; N];
|
let mut buf: [u8; N] = [0; N];
|
||||||
let size = match serialize(&mut buf, r) {
|
let Ok(size) = serialize(&mut buf, r) else {
|
||||||
Ok(size) => size,
|
return Err(EndpointError::BufferOverflow);
|
||||||
Err(_) => return Err(EndpointError::BufferOverflow),
|
|
||||||
};
|
};
|
||||||
self.write(&buf[0..size]).await
|
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> {
|
impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
|
||||||
/// Waits for the interrupt out endpoint to be enabled.
|
/// Waits for the interrupt out endpoint to be enabled.
|
||||||
pub async fn ready(&mut self) -> () {
|
pub async fn ready(&mut self) {
|
||||||
self.ep_out.wait_enabled().await
|
self.ep_out.wait_enabled().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delivers output reports from the Interrupt Out pipe to `handler`.
|
/// 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 {
|
if size < max_packet_size || total == N {
|
||||||
self.offset.store(0, Ordering::Release);
|
self.offset.store(0, Ordering::Release);
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
self.offset.store(total, Ordering::Release);
|
|
||||||
}
|
}
|
||||||
|
self.offset.store(total, Ordering::Release);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.offset.store(0, Ordering::Release);
|
self.offset.store(0, Ordering::Release);
|
||||||
@ -466,7 +470,7 @@ impl<'d> Handler for Control<'d> {
|
|||||||
HID_REQ_SET_IDLE => {
|
HID_REQ_SET_IDLE => {
|
||||||
if let Some(handler) = self.request_handler {
|
if let Some(handler) = self.request_handler {
|
||||||
let id = req.value as u8;
|
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 = u32::from(req.value >> 8);
|
||||||
let dur = if dur == 0 { u32::MAX } else { 4 * dur };
|
let dur = if dur == 0 { u32::MAX } else { 4 * dur };
|
||||||
handler.set_idle_ms(id, dur);
|
handler.set_idle_ms(id, dur);
|
||||||
@ -522,7 +526,7 @@ impl<'d> Handler for Control<'d> {
|
|||||||
HID_REQ_GET_IDLE => {
|
HID_REQ_GET_IDLE => {
|
||||||
if let Some(handler) = self.request_handler {
|
if let Some(handler) = self.request_handler {
|
||||||
let id = req.value as u8;
|
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) {
|
if let Some(dur) = handler.get_idle_ms(id) {
|
||||||
let dur = u8::try_from(dur / 4).unwrap_or(0);
|
let dur = u8::try_from(dur / 4).unwrap_or(0);
|
||||||
buf[0] = dur;
|
buf[0] = dur;
|
||||||
|
@ -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.
|
/// 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:
|
/// 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.
|
/// - `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.
|
/// - `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
|
/// - 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)
|
/// 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
|
/// 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.
|
/// 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> {
|
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.
|
/// 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.
|
/// 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 {
|
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);
|
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
|
/// Waits for the USB host to enable this interface
|
||||||
pub async fn wait_connection(&mut self) {
|
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.
|
/// 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
|
/// Waits for the USB host to enable this interface
|
||||||
pub async fn wait_connection(&mut self) {
|
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
|
/// Waits for the USB host to enable this interface
|
||||||
pub async fn wait_connection(&mut self) {
|
pub async fn wait_connection(&mut self) {
|
||||||
self.read_ep.wait_enabled().await
|
self.read_ep.wait_enabled().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ impl Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR 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)
|
((self.value >> 8) as u8, self.value as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::builder::Config;
|
use crate::builder::Config;
|
||||||
use crate::driver::EndpointInfo;
|
use crate::driver::EndpointInfo;
|
||||||
use crate::types::*;
|
use crate::types::{InterfaceNumber, StringIndex};
|
||||||
use crate::CONFIGURATION_VALUE;
|
use crate::CONFIGURATION_VALUE;
|
||||||
|
|
||||||
/// Standard descriptor types
|
/// 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.
|
/// 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
|
self.position
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,9 +67,10 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) {
|
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) {
|
||||||
let length = descriptor.len();
|
let length = descriptor.len();
|
||||||
|
|
||||||
if (self.position + 2 + length) > self.buf.len() || (length + 2) > 255 {
|
assert!(
|
||||||
panic!("Descriptor buffer full");
|
(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] = (length + 2) as u8;
|
||||||
self.buf[self.position + 1] = descriptor_type;
|
self.buf[self.position + 1] = descriptor_type;
|
||||||
@ -102,7 +103,7 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
config.serial_number.map_or(0, |_| 3), // iSerialNumber
|
config.serial_number.map_or(0, |_| 3), // iSerialNumber
|
||||||
1, // bNumConfigurations
|
1, // bNumConfigurations
|
||||||
],
|
],
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn configuration(&mut self, config: &Config) {
|
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
|
| if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes
|
||||||
(config.max_power / 2) as u8, // bMaxPower
|
(config.max_power / 2) as u8, // bMaxPower
|
||||||
],
|
],
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@ -248,9 +249,7 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
pub(crate) fn string(&mut self, string: &str) {
|
pub(crate) fn string(&mut self, string: &str) {
|
||||||
let mut pos = self.position;
|
let mut pos = self.position;
|
||||||
|
|
||||||
if pos + 2 > self.buf.len() {
|
assert!(pos + 2 <= self.buf.len(), "Descriptor buffer full");
|
||||||
panic!("Descriptor buffer full");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.buf[pos] = 0; // length placeholder
|
self.buf[pos] = 0; // length placeholder
|
||||||
self.buf[pos + 1] = descriptor_type::STRING;
|
self.buf[pos + 1] = descriptor_type::STRING;
|
||||||
@ -258,9 +257,7 @@ impl<'a> DescriptorWriter<'a> {
|
|||||||
pos += 2;
|
pos += 2;
|
||||||
|
|
||||||
for c in string.encode_utf16() {
|
for c in string.encode_utf16() {
|
||||||
if pos >= self.buf.len() {
|
assert!(pos < self.buf.len(), "Descriptor buffer full");
|
||||||
panic!("Descriptor buffer full");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
|
self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
|
||||||
pos += 2;
|
pos += 2;
|
||||||
@ -279,9 +276,9 @@ pub struct BosWriter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BosWriter<'a> {
|
impl<'a> BosWriter<'a> {
|
||||||
pub(crate) fn new(writer: DescriptorWriter<'a>) -> Self {
|
pub(crate) const fn new(writer: DescriptorWriter<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
writer: writer,
|
writer,
|
||||||
num_caps_mark: None,
|
num_caps_mark: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,9 +311,10 @@ impl<'a> BosWriter<'a> {
|
|||||||
let mut start = self.writer.position;
|
let mut start = self.writer.position;
|
||||||
let blen = data.len();
|
let blen = data.len();
|
||||||
|
|
||||||
if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 {
|
assert!(
|
||||||
panic!("Descriptor buffer full");
|
(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] = (blen + 3) as u8;
|
||||||
self.writer.buf[start + 1] = descriptor_type::CAPABILITY;
|
self.writer.buf[start + 1] = descriptor_type::CAPABILITY;
|
||||||
|
@ -11,11 +11,11 @@ pub struct Reader<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Reader<'a> {
|
impl<'a> Reader<'a> {
|
||||||
pub fn new(data: &'a [u8]) -> Self {
|
pub const fn new(data: &'a [u8]) -> Self {
|
||||||
Self { data }
|
Self { data }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eof(&self) -> bool {
|
pub const fn eof(&self) -> bool {
|
||||||
self.data.is_empty()
|
self.data.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<
|
|||||||
}
|
}
|
||||||
descriptor_type::ENDPOINT => {
|
descriptor_type::ENDPOINT => {
|
||||||
ep.ep_address = EndpointAddress::from(r.read_u8()?);
|
ep.ep_address = EndpointAddress::from(r.read_u8()?);
|
||||||
f(ep)
|
f(ep);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,12 @@ use embassy_futures::select::{select, Either};
|
|||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder};
|
pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder};
|
||||||
use crate::config::*;
|
use crate::config::{MAX_HANDLER_COUNT, MAX_INTERFACE_COUNT};
|
||||||
use crate::control::*;
|
use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType};
|
||||||
use crate::descriptor::*;
|
use crate::descriptor::{descriptor_type, lang_id};
|
||||||
use crate::descriptor_reader::foreach_endpoint;
|
use crate::descriptor_reader::foreach_endpoint;
|
||||||
use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event};
|
use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event};
|
||||||
use crate::types::*;
|
use crate::types::{InterfaceNumber, StringIndex};
|
||||||
|
|
||||||
/// The global state of the USB device.
|
/// 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
|
/// After dropping the future, [`UsbDevice::disable()`] should be called
|
||||||
/// before calling any other `UsbDevice` methods to fully reset the
|
/// before calling any other `UsbDevice` methods to fully reset the
|
||||||
/// peripheral.
|
/// peripheral.
|
||||||
pub async fn run_until_suspend(&mut self) -> () {
|
pub async fn run_until_suspend(&mut self) {
|
||||||
while !self.inner.suspended {
|
while !self.inner.suspended {
|
||||||
let control_fut = self.control.setup();
|
let control_fut = self.control.setup();
|
||||||
let bus_fut = self.inner.bus.poll();
|
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) {
|
async fn handle_control_in(&mut self, req: Request) {
|
||||||
|
const DEVICE_DESCRIPTOR_LEN: usize = 18;
|
||||||
|
|
||||||
let mut resp_length = req.length as usize;
|
let mut resp_length = req.length as usize;
|
||||||
let max_packet_size = self.control.max_packet_size();
|
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
|
// 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.
|
// a full-length packet is a short packet, thinking we're done sending data.
|
||||||
// See https://github.com/hathach/tinyusb/issues/184
|
// 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 < resp_length {
|
||||||
if self.inner.address == 0
|
|
||||||
&& max_packet_size < DEVICE_DESCRIPTOR_LEN
|
|
||||||
&& (max_packet_size as usize) < resp_length
|
|
||||||
{
|
|
||||||
trace!("received control req while not addressed: capping response to 1 packet.");
|
trace!("received control req while not addressed: capping response to 1 packet.");
|
||||||
resp_length = max_packet_size;
|
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) => {
|
InResponse::Accepted(data) => {
|
||||||
let len = data.len().min(resp_length);
|
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]
|
let chunks = data[0..len]
|
||||||
.chunks(max_packet_size)
|
.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.control.accept_set_address(self.inner.address).await;
|
||||||
self.inner.set_address_pending = false;
|
self.inner.set_address_pending = false;
|
||||||
} else {
|
} else {
|
||||||
self.control.accept().await
|
self.control.accept().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutResponse::Rejected => self.control.reject().await,
|
OutResponse::Rejected => self.control.reject().await,
|
||||||
@ -548,9 +546,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
|
|||||||
|
|
||||||
OutResponse::Accepted
|
OutResponse::Accepted
|
||||||
}
|
}
|
||||||
(Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => match self.device_state {
|
(Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => {
|
||||||
UsbDeviceState::Default => OutResponse::Accepted,
|
if self.device_state != UsbDeviceState::Default {
|
||||||
_ => {
|
|
||||||
debug!("SET_CONFIGURATION: unconfigured");
|
debug!("SET_CONFIGURATION: unconfigured");
|
||||||
self.device_state = UsbDeviceState::Addressed;
|
self.device_state = UsbDeviceState::Addressed;
|
||||||
|
|
||||||
@ -564,17 +561,15 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
|
|||||||
for h in &mut self.handlers {
|
for h in &mut self.handlers {
|
||||||
h.configured(false);
|
h.configured(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutResponse::Accepted
|
|
||||||
}
|
}
|
||||||
},
|
OutResponse::Accepted
|
||||||
|
}
|
||||||
_ => OutResponse::Rejected,
|
_ => OutResponse::Rejected,
|
||||||
},
|
},
|
||||||
(RequestType::Standard, Recipient::Interface) => {
|
(RequestType::Standard, Recipient::Interface) => {
|
||||||
let iface_num = InterfaceNumber::new(req.index as _);
|
let iface_num = InterfaceNumber::new(req.index as _);
|
||||||
let iface = match self.interfaces.get_mut(iface_num.0 as usize) {
|
let Some(iface) = self.interfaces.get_mut(iface_num.0 as usize) else {
|
||||||
Some(iface) => iface,
|
return OutResponse::Rejected;
|
||||||
None => return OutResponse::Rejected,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match req.request {
|
match req.request {
|
||||||
@ -650,9 +645,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
|
|||||||
_ => InResponse::Rejected,
|
_ => InResponse::Rejected,
|
||||||
},
|
},
|
||||||
(RequestType::Standard, Recipient::Interface) => {
|
(RequestType::Standard, Recipient::Interface) => {
|
||||||
let iface = match self.interfaces.get_mut(req.index as usize) {
|
let Some(iface) = self.interfaces.get_mut(req.index as usize) else {
|
||||||
Some(iface) => iface,
|
return InResponse::Rejected;
|
||||||
None => return InResponse::Rejected,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match req.request {
|
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> {
|
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)
|
core::mem::transmute(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -756,16 +750,12 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(s) = s {
|
if let Some(s) = s {
|
||||||
if buf.len() < 2 {
|
assert!(buf.len() >= 2, "control buffer too small");
|
||||||
panic!("control buffer too small");
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[1] = descriptor_type::STRING;
|
buf[1] = descriptor_type::STRING;
|
||||||
let mut pos = 2;
|
let mut pos = 2;
|
||||||
for c in s.encode_utf16() {
|
for c in s.encode_utf16() {
|
||||||
if pos + 2 >= buf.len() {
|
assert!(pos + 2 < buf.len(), "control buffer too small");
|
||||||
panic!("control buffer too small");
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
|
buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
|
||||||
pos += 2;
|
pos += 2;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
|
|
||||||
use super::{capability_type, BosWriter};
|
use crate::descriptor::{capability_type, BosWriter};
|
||||||
use crate::types::InterfaceNumber;
|
use crate::types::InterfaceNumber;
|
||||||
|
|
||||||
/// A serialized Microsoft OS 2.0 Descriptor set.
|
/// A serialized Microsoft OS 2.0 Descriptor set.
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
pub struct InterfaceNumber(pub u8);
|
pub struct InterfaceNumber(pub u8);
|
||||||
|
|
||||||
impl InterfaceNumber {
|
impl InterfaceNumber {
|
||||||
pub(crate) fn new(index: u8) -> InterfaceNumber {
|
pub(crate) const fn new(index: u8) -> InterfaceNumber {
|
||||||
InterfaceNumber(index)
|
InterfaceNumber(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ impl From<InterfaceNumber> for u8 {
|
|||||||
pub struct StringIndex(pub u8);
|
pub struct StringIndex(pub u8);
|
||||||
|
|
||||||
impl StringIndex {
|
impl StringIndex {
|
||||||
pub(crate) fn new(index: u8) -> StringIndex {
|
pub(crate) const fn new(index: u8) -> StringIndex {
|
||||||
StringIndex(index)
|
StringIndex(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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-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-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-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 }
|
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 = { version = "0.6.0", features = ["defmt-03"] }
|
||||||
embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] }
|
embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] }
|
||||||
|
@ -27,7 +27,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [
|
|||||||
"gpiote",
|
"gpiote",
|
||||||
"unstable-pac",
|
"unstable-pac",
|
||||||
] }
|
] }
|
||||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [
|
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = [
|
||||||
"nightly",
|
"nightly",
|
||||||
"defmt",
|
"defmt",
|
||||||
"tcp",
|
"tcp",
|
||||||
|
@ -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-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-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-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-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
|
||||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||||
embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
|
embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
|
||||||
|
32
examples/rp/src/bin/rosc.rs
Normal file
32
examples/rp/src/bin/rosc.rs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["log"] }
|
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-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-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-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" }
|
||||||
embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]}
|
embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]}
|
||||||
embedded-io-async = { version = "0.6.0" }
|
embedded-io-async = { version = "0.6.0" }
|
||||||
|
@ -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-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-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
|
||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||||
embassy-net = { version = "0.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 = "0.3"
|
||||||
defmt-rtt = "0.4"
|
defmt-rtt = "0.4"
|
||||||
|
@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI;
|
|||||||
use embassy_stm32::eth::{Ethernet, PacketQueue};
|
use embassy_stm32::eth::{Ethernet, PacketQueue};
|
||||||
use embassy_stm32::peripherals::ETH;
|
use embassy_stm32::peripherals::ETH;
|
||||||
use embassy_stm32::rng::Rng;
|
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_stm32::{bind_interrupts, eth, peripherals, rng, Config};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use embedded_io_async::Write;
|
use embedded_io_async::Write;
|
||||||
@ -32,7 +32,25 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) -> ! {
|
async fn main(spawner: Spawner) -> ! {
|
||||||
let mut config = Config::default();
|
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);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
@ -4,15 +4,13 @@
|
|||||||
|
|
||||||
use defmt::info;
|
use defmt::info;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::time::Hertz;
|
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::Config;
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) -> ! {
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
let mut config = Config::default();
|
let config = Config::default();
|
||||||
config.rcc.sys_ck = Some(Hertz(84_000_000));
|
|
||||||
let _p = embassy_stm32::init(config);
|
let _p = embassy_stm32::init(config);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::sdmmc::{DataBlock, Sdmmc};
|
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 embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -20,8 +20,25 @@ bind_interrupts!(struct Irqs {
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
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);
|
let p = embassy_stm32::init(config);
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_net::tcp::TcpSocket;
|
use embassy_net::tcp::TcpSocket;
|
||||||
use embassy_net::{Stack, StackResources};
|
use embassy_net::{Stack, StackResources};
|
||||||
use embassy_stm32::rng::{self, Rng};
|
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::usb_otg::Driver;
|
||||||
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
|
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
|
||||||
use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};
|
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!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let mut config = Config::default();
|
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);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use defmt::{panic, *};
|
use defmt::{panic, *};
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::time::mhz;
|
use embassy_stm32::time::Hertz;
|
||||||
use embassy_stm32::usb_otg::{Driver, Instance};
|
use embassy_stm32::usb_otg::{Driver, Instance};
|
||||||
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
|
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
|
||||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||||
@ -22,9 +22,25 @@ async fn main(_spawner: Spawner) {
|
|||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let mut config = Config::default();
|
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);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
|
@ -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-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-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-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" }
|
embedded-io-async = { version = "0.6.0" }
|
||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI;
|
|||||||
use embassy_stm32::eth::{Ethernet, PacketQueue};
|
use embassy_stm32::eth::{Ethernet, PacketQueue};
|
||||||
use embassy_stm32::peripherals::ETH;
|
use embassy_stm32::peripherals::ETH;
|
||||||
use embassy_stm32::rng::Rng;
|
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_stm32::{bind_interrupts, eth, peripherals, rng, Config};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use embedded_io_async::Write;
|
use embedded_io_async::Write;
|
||||||
@ -33,7 +33,25 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) -> ! {
|
async fn main(spawner: Spawner) -> ! {
|
||||||
let mut config = Config::default();
|
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);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
@ -4,15 +4,13 @@
|
|||||||
|
|
||||||
use defmt::info;
|
use defmt::info;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::time::Hertz;
|
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::Config;
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) -> ! {
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
let mut config = Config::default();
|
let config = Config::default();
|
||||||
config.rcc.sys_ck = Some(Hertz(84_000_000));
|
|
||||||
let _p = embassy_stm32::init(config);
|
let _p = embassy_stm32::init(config);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::sdmmc::Sdmmc;
|
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 embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -16,8 +16,25 @@ bind_interrupts!(struct Irqs {
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
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);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use defmt::{panic, *};
|
use defmt::{panic, *};
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::time::mhz;
|
use embassy_stm32::time::Hertz;
|
||||||
use embassy_stm32::usb_otg::{Driver, Instance};
|
use embassy_stm32::usb_otg::{Driver, Instance};
|
||||||
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
|
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
|
||||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||||
@ -22,10 +22,25 @@ async fn main(_spawner: Spawner) {
|
|||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.hse = Some(mhz(8));
|
{
|
||||||
config.rcc.pll48 = true;
|
use embassy_stm32::rcc::*;
|
||||||
config.rcc.sys_ck = Some(mhz(200));
|
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);
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
|
@ -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-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-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-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" }
|
embedded-io-async = { version = "0.6.0" }
|
||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||||
|
|
||||||
|
@ -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-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-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-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" }
|
embedded-io-async = { version = "0.6.0" }
|
||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||||
|
|
||||||
|
@ -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-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||||
embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" }
|
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" }
|
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||||
embedded-io-async = { version = "0.6.0", features = ["defmt-03"] }
|
embedded-io-async = { version = "0.6.0", features = ["defmt-03"] }
|
||||||
embedded-io = { version = "0.6.0", features = ["defmt-03"] }
|
embedded-io = { version = "0.6.0", features = ["defmt-03"] }
|
||||||
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
|||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
pac::RCC.ccipr().modify(|w| {
|
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));
|
pac::RCC.ahb2enr().modify(|w| w.set_adcen(true));
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
info!("Hello World!");
|
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);
|
let mut led = Output::new(p.PB14, Level::High, Speed::Low);
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@ bind_interrupts!(struct Irqs {
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL1_R;
|
||||||
config.rcc.hsi16 = true;
|
config.rcc.hsi16 = true;
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
source: PLLSource::HSI16,
|
source: PLLSource::HSI,
|
||||||
prediv: PllPreDiv::DIV1,
|
prediv: PllPreDiv::DIV1,
|
||||||
mul: PllMul::MUL18,
|
mul: PllMul::MUL18,
|
||||||
divp: None,
|
divp: None,
|
||||||
|
@ -15,7 +15,7 @@ use {defmt_rtt as _, panic_probe as _};
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
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.hse = Some(Hertz::mhz(8));
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
source: PLLSource::HSE,
|
source: PLLSource::HSE,
|
||||||
|
@ -77,7 +77,7 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// 80Mhz clock (Source: 8 / SrcDiv: 1 * PLLMul 20 / ClkDiv 2)
|
// 80Mhz clock (Source: 8 / SrcDiv: 1 * PLLMul 20 / ClkDiv 2)
|
||||||
// 80MHz highest frequency for flash 0 wait.
|
// 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.hse = Some(Hertz::mhz(8));
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
source: PLLSource::HSE,
|
source: PLLSource::HSE,
|
||||||
|
@ -24,10 +24,10 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.hsi48 = true;
|
config.rcc.hsi48 = true;
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL1_R;
|
||||||
config.rcc.hsi16 = true;
|
config.rcc.hsi16 = true;
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
source: PLLSource::HSI16,
|
source: PLLSource::HSI,
|
||||||
prediv: PllPreDiv::DIV1,
|
prediv: PllPreDiv::DIV1,
|
||||||
mul: PllMul::MUL10,
|
mul: PllMul::MUL10,
|
||||||
divp: None,
|
divp: None,
|
||||||
|
@ -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-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-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-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" }
|
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||||
usbd-hid = "0.6.0"
|
usbd-hid = "0.6.0"
|
||||||
|
|
||||||
|
@ -17,10 +17,10 @@ bind_interrupts!(struct Irqs {
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.hsi16 = true;
|
config.rcc.hsi16 = true;
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL1_R;
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
// 64Mhz clock (16 / 1 * 8 / 2)
|
// 64Mhz clock (16 / 1 * 8 / 2)
|
||||||
source: PLLSource::HSI16,
|
source: PLLSource::HSI,
|
||||||
prediv: PllPreDiv::DIV1,
|
prediv: PllPreDiv::DIV1,
|
||||||
mul: PllMul::MUL8,
|
mul: PllMul::MUL8,
|
||||||
divp: None,
|
divp: None,
|
||||||
|
@ -46,10 +46,10 @@ async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! {
|
|||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.hsi16 = true;
|
config.rcc.hsi16 = true;
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL1_R;
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
// 80Mhz clock (16 / 1 * 10 / 2)
|
// 80Mhz clock (16 / 1 * 10 / 2)
|
||||||
source: PLLSource::HSI16,
|
source: PLLSource::HSI,
|
||||||
prediv: PllPreDiv::DIV1,
|
prediv: PllPreDiv::DIV1,
|
||||||
mul: PllMul::MUL10,
|
mul: PllMul::MUL10,
|
||||||
divp: None,
|
divp: None,
|
||||||
|
@ -23,10 +23,10 @@ bind_interrupts!(struct Irqs {
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.hsi16 = true;
|
config.rcc.hsi16 = true;
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL1_R;
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
// 80Mhz clock (16 / 1 * 10 / 2)
|
// 80Mhz clock (16 / 1 * 10 / 2)
|
||||||
source: PLLSource::HSI16,
|
source: PLLSource::HSI,
|
||||||
prediv: PllPreDiv::DIV1,
|
prediv: PllPreDiv::DIV1,
|
||||||
mul: PllMul::MUL10,
|
mul: PllMul::MUL10,
|
||||||
divp: None,
|
divp: None,
|
||||||
|
@ -21,10 +21,10 @@ bind_interrupts!(struct Irqs {
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.hsi16 = true;
|
config.rcc.hsi16 = true;
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL1_R;
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
// 80Mhz clock (16 / 1 * 10 / 2)
|
// 80Mhz clock (16 / 1 * 10 / 2)
|
||||||
source: PLLSource::HSI16,
|
source: PLLSource::HSI,
|
||||||
prediv: PllPreDiv::DIV1,
|
prediv: PllPreDiv::DIV1,
|
||||||
mul: PllMul::MUL10,
|
mul: PllMul::MUL10,
|
||||||
divp: None,
|
divp: None,
|
||||||
|
@ -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-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-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-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 = "0.3"
|
||||||
defmt-rtt = "0.4"
|
defmt-rtt = "0.4"
|
||||||
|
@ -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-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-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-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 = "0.3"
|
||||||
defmt-rtt = "0.4"
|
defmt-rtt = "0.4"
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::rcc::{ClockSrc, MSIRange};
|
||||||
use embassy_stm32::rng::{self, Rng};
|
use embassy_stm32::rng::{self, Rng};
|
||||||
use embassy_stm32::{bind_interrupts, pac, peripherals};
|
use embassy_stm32::{bind_interrupts, pac, peripherals};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
@ -15,12 +16,10 @@ bind_interrupts!(struct Irqs{
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = embassy_stm32::Config::default();
|
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);
|
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!");
|
info!("Hello World!");
|
||||||
|
|
||||||
|
@ -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-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"] }
|
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" }
|
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-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"] }
|
embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] }
|
||||||
embedded-hal-async = { version = "1.0.0-rc.1" }
|
embedded-hal-async = { version = "1.0.0-rc.1" }
|
||||||
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] }
|
||||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||||
defmt = "0.3.0"
|
defmt = "0.3.0"
|
||||||
|
@ -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-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-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-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"] }
|
embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
|
||||||
cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] }
|
cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] }
|
||||||
cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] }
|
cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] }
|
||||||
|
@ -24,6 +24,7 @@ stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
|
|||||||
stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"]
|
stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"]
|
||||||
stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
|
stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
|
||||||
stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
|
stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
|
||||||
|
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
|
||||||
|
|
||||||
eth = []
|
eth = []
|
||||||
rng = []
|
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-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-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-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" }
|
perf-client = { path = "../perf-client" }
|
||||||
|
|
||||||
defmt = "0.3.0"
|
defmt = "0.3.0"
|
||||||
|
@ -8,12 +8,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
println!("cargo:rustc-link-search={}", out.display());
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
println!("cargo:rustc-link-arg-bins=--nmagic");
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
|
||||||
// too little RAM to run from RAM.
|
|
||||||
if cfg!(any(
|
if cfg!(any(
|
||||||
|
// too little RAM to run from RAM.
|
||||||
feature = "stm32f103c8",
|
feature = "stm32f103c8",
|
||||||
feature = "stm32c031c6",
|
feature = "stm32c031c6",
|
||||||
feature = "stm32wb55rg",
|
feature = "stm32wb55rg",
|
||||||
feature = "stm32l073rz",
|
feature = "stm32l073rz",
|
||||||
|
// wrong ram size in stm32-data
|
||||||
|
feature = "stm32wl55jc",
|
||||||
)) {
|
)) {
|
||||||
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
println!("cargo:rerun-if-changed=link.x");
|
println!("cargo:rerun-if-changed=link.x");
|
||||||
|
@ -42,6 +42,8 @@ teleprobe_meta::target!(b"nucleo-stm32f207zg");
|
|||||||
teleprobe_meta::target!(b"nucleo-stm32f303ze");
|
teleprobe_meta::target!(b"nucleo-stm32f303ze");
|
||||||
#[cfg(feature = "stm32l496zg")]
|
#[cfg(feature = "stm32l496zg")]
|
||||||
teleprobe_meta::target!(b"nucleo-stm32l496zg");
|
teleprobe_meta::target!(b"nucleo-stm32l496zg");
|
||||||
|
#[cfg(feature = "stm32wl55jc")]
|
||||||
|
teleprobe_meta::target!(b"nucleo-stm32wl55jc");
|
||||||
|
|
||||||
macro_rules! define_peris {
|
macro_rules! define_peris {
|
||||||
($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => {
|
($($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,
|
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>;},
|
@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 {
|
pub fn config() -> Config {
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
@ -216,16 +224,44 @@ pub fn config() -> Config {
|
|||||||
|
|
||||||
#[cfg(feature = "stm32f429zi")]
|
#[cfg(feature = "stm32f429zi")]
|
||||||
{
|
{
|
||||||
// TODO: stm32f429zi can do up to 180mhz, but that makes tests fail.
|
use embassy_stm32::rcc::*;
|
||||||
// perhaps we have some bug w.r.t overdrive.
|
config.rcc.hse = Some(Hse {
|
||||||
config.rcc.sys_ck = Some(Hertz(168_000_000));
|
freq: Hertz(8_000_000),
|
||||||
config.rcc.pclk1 = Some(Hertz(42_000_000));
|
mode: HseMode::Bypass,
|
||||||
config.rcc.pclk2 = Some(Hertz(84_000_000));
|
});
|
||||||
|
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")]
|
#[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")]
|
#[cfg(feature = "stm32h563zi")]
|
||||||
@ -287,10 +323,17 @@ pub fn config() -> Config {
|
|||||||
#[cfg(any(feature = "stm32l496zg", feature = "stm32l4a6zg", feature = "stm32l4r5zi"))]
|
#[cfg(any(feature = "stm32l496zg", feature = "stm32l4a6zg", feature = "stm32l4r5zi"))]
|
||||||
{
|
{
|
||||||
use embassy_stm32::rcc::*;
|
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.hsi16 = true;
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
source: PLLSource::HSI16,
|
source: PLLSource::HSI,
|
||||||
prediv: PllPreDiv::DIV1,
|
prediv: PllPreDiv::DIV1,
|
||||||
mul: PllMul::MUL18,
|
mul: PllMul::MUL18,
|
||||||
divp: None,
|
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"))]
|
#[cfg(any(feature = "stm32l552ze"))]
|
||||||
{
|
{
|
||||||
use embassy_stm32::rcc::*;
|
use embassy_stm32::rcc::*;
|
||||||
config.rcc.hsi16 = true;
|
config.rcc.hsi16 = true;
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL1_R;
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
// 110Mhz clock (16 / 4 * 55 / 2)
|
// 110Mhz clock (16 / 4 * 55 / 2)
|
||||||
source: PLLSource::HSI16,
|
source: PLLSource::HSI,
|
||||||
prediv: PllPreDiv::DIV4,
|
prediv: PllPreDiv::DIV4,
|
||||||
mul: PllMul::MUL55,
|
mul: PllMul::MUL55,
|
||||||
divp: None,
|
divp: None,
|
||||||
|
Reference in New Issue
Block a user