Merge remote-tracking branch 'origin/master' into feature-i2c-slave
# Conflicts: # examples/stm32g0/Cargo.toml
This commit is contained in:
commit
0fe0bc60fe
@ -62,9 +62,9 @@ async fn blink(pin: AnyPin) {
|
|||||||
loop {
|
loop {
|
||||||
// Timekeeping is globally available, no need to mess with hardware timers.
|
// Timekeeping is globally available, no need to mess with hardware timers.
|
||||||
led.set_high();
|
led.set_high();
|
||||||
Timer::after(Duration::from_millis(150)).await;
|
Timer::after_millis(150).await;
|
||||||
led.set_low();
|
led.set_low();
|
||||||
Timer::after(Duration::from_millis(150)).await;
|
Timer::after_millis(150).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
ci.sh
10
ci.sh
@ -192,9 +192,13 @@ cargo batch \
|
|||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/stm32h755zi \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/stm32h755zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --out-dir out/tests/stm32h753zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --out-dir out/tests/stm32h7a3zi \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u5a5zj --out-dir out/tests/stm32u5a5zj \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wba52cg --out-dir out/tests/stm32wba52cg \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --out-dir out/tests/stm32l073rz \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --out-dir out/tests/stm32l073rz \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --out-dir out/tests/stm32l152re \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --out-dir out/tests/stm32l152re \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --out-dir out/tests/stm32l4a6zg \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --out-dir out/tests/stm32l4a6zg \
|
||||||
@ -204,6 +208,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 \
|
||||||
@ -212,8 +217,13 @@ cargo batch \
|
|||||||
|
|
||||||
rm out/tests/stm32wb55rg/wpan_mac
|
rm out/tests/stm32wb55rg/wpan_mac
|
||||||
rm out/tests/stm32wb55rg/wpan_ble
|
rm out/tests/stm32wb55rg/wpan_ble
|
||||||
|
|
||||||
|
# unstable, I think it's running out of RAM?
|
||||||
rm out/tests/stm32f207zg/eth
|
rm out/tests/stm32f207zg/eth
|
||||||
|
|
||||||
|
# doesn't work, gives "noise error", no idea why. usart_dma does pass.
|
||||||
|
rm out/tests/stm32u5a5zj/usart
|
||||||
|
|
||||||
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
|
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
|
||||||
echo No teleprobe token found, skipping running HIL tests
|
echo No teleprobe token found, skipping running HIL tests
|
||||||
exit
|
exit
|
||||||
|
@ -11,10 +11,10 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.4", 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,5 +1,5 @@
|
|||||||
use embassy_futures::yield_now;
|
use embassy_futures::yield_now;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
use embedded_hal_1::digital::OutputPin;
|
use embedded_hal_1::digital::OutputPin;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
|
||||||
@ -51,9 +51,9 @@ where
|
|||||||
pub async fn init(&mut self) {
|
pub async fn init(&mut self) {
|
||||||
// Reset
|
// Reset
|
||||||
self.pwr.set_low().unwrap();
|
self.pwr.set_low().unwrap();
|
||||||
Timer::after(Duration::from_millis(20)).await;
|
Timer::after_millis(20).await;
|
||||||
self.pwr.set_high().unwrap();
|
self.pwr.set_high().unwrap();
|
||||||
Timer::after(Duration::from_millis(250)).await;
|
Timer::after_millis(250).await;
|
||||||
|
|
||||||
while self
|
while self
|
||||||
.read32_swapped(REG_BUS_TEST_RO)
|
.read32_swapped(REG_BUS_TEST_RO)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
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_time::{Duration, Timer};
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
|
||||||
pub use crate::bus::SpiBusCyw43;
|
pub use crate::bus::SpiBusCyw43;
|
||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
@ -87,22 +87,22 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar("country", &country_info.to_bytes()).await;
|
self.set_iovar("country", &country_info.to_bytes()).await;
|
||||||
|
|
||||||
// set country takes some time, next ioctls fail if we don't wait.
|
// set country takes some time, next ioctls fail if we don't wait.
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// Set antenna to chip antenna
|
// Set antenna to chip antenna
|
||||||
self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await;
|
self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await;
|
||||||
|
|
||||||
self.set_iovar_u32("bus:txglom", 0).await;
|
self.set_iovar_u32("bus:txglom", 0).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
//self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...??
|
//self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...??
|
||||||
//Timer::after(Duration::from_millis(100)).await;
|
//Timer::after_millis(100).await;
|
||||||
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
self.set_iovar_u32("ampdu_mpdu", 4).await;
|
self.set_iovar_u32("ampdu_mpdu", 4).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
//self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes
|
//self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes
|
||||||
|
|
||||||
//Timer::after(Duration::from_millis(100)).await;
|
//Timer::after_millis(100).await;
|
||||||
|
|
||||||
// evts
|
// evts
|
||||||
let mut evts = EventMask {
|
let mut evts = EventMask {
|
||||||
@ -121,19 +121,19 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await;
|
self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// set wifi up
|
// set wifi up
|
||||||
self.up().await;
|
self.up().await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto
|
self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto
|
||||||
self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any
|
self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any
|
||||||
|
|
||||||
Timer::after(Duration::from_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");
|
||||||
}
|
}
|
||||||
@ -185,7 +185,7 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
|
self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
|
||||||
self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
|
self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
let mut pfi = PassphraseInfo {
|
let mut pfi = PassphraseInfo {
|
||||||
len: passphrase.len() as _,
|
len: passphrase.len() as _,
|
||||||
@ -297,7 +297,7 @@ impl<'a> Control<'a> {
|
|||||||
if security != Security::OPEN {
|
if security != Security::OPEN {
|
||||||
self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK
|
self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// Set passphrase
|
// Set passphrase
|
||||||
let mut pfi = PassphraseInfo {
|
let mut pfi = PassphraseInfo {
|
||||||
|
@ -555,14 +555,14 @@ where
|
|||||||
|
|
||||||
self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
|
self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after_millis(1).await;
|
||||||
|
|
||||||
self.bus
|
self.bus
|
||||||
.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)
|
.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)
|
||||||
.await;
|
.await;
|
||||||
let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
|
let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after_millis(1).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn core_is_up(&mut self, core: Core) -> bool {
|
async fn core_is_up(&mut self, core: Core) -> bool {
|
||||||
|
@ -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`
|
||||||
|
@ -6,7 +6,7 @@ The Embassy executor is an async/await executor designed for embedded usage alon
|
|||||||
|
|
||||||
* No `alloc`, no heap needed. Task are statically allocated.
|
* No `alloc`, no heap needed. Task are statically allocated.
|
||||||
* No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
* No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
||||||
* Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`.
|
* Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
|
||||||
* No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
* No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
||||||
* Efficient polling: a wake will only poll the woken task, not all of them.
|
* Efficient polling: a wake will only poll the woken task, not all of them.
|
||||||
* Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
* Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
||||||
|
@ -21,7 +21,7 @@ default = ["time"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.4", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||||
"unproven",
|
"unproven",
|
||||||
] }
|
] }
|
||||||
|
@ -76,9 +76,7 @@ where
|
|||||||
#[cfg(not(feature = "time"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayUs(us) => {
|
Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await,
|
||||||
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -143,9 +141,7 @@ where
|
|||||||
#[cfg(not(feature = "time"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayUs(us) => {
|
Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await,
|
||||||
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -59,7 +59,7 @@ rtos-trace = { version = "0.1.2", optional = true }
|
|||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
||||||
embassy-time = { version = "0.1.4", path = "../embassy-time", optional = true}
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true}
|
||||||
atomic-polyfill = "1.0.1"
|
atomic-polyfill = "1.0.1"
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
static_cell = "1.1"
|
static_cell = "1.1"
|
||||||
|
@ -4,7 +4,7 @@ An async/await executor designed for embedded usage.
|
|||||||
|
|
||||||
- No `alloc`, no heap needed. Task futures are statically allocated.
|
- No `alloc`, no heap needed. Task futures are statically allocated.
|
||||||
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
||||||
- Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`.
|
- Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
|
||||||
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
||||||
- Efficient polling: a wake will only poll the woken task, not all of them.
|
- Efficient polling: a wake will only poll the woken task, not all of them.
|
||||||
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
||||||
|
@ -3,7 +3,7 @@ use core::task::{RawWaker, RawWakerVTable, Waker};
|
|||||||
|
|
||||||
use super::{wake_task, TaskHeader, TaskRef};
|
use super::{wake_task, TaskHeader, TaskRef};
|
||||||
|
|
||||||
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
|
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
|
||||||
|
|
||||||
unsafe fn clone(p: *const ()) -> RawWaker {
|
unsafe fn clone(p: *const ()) -> RawWaker {
|
||||||
RawWaker::new(p, &VTABLE)
|
RawWaker::new(p, &VTABLE)
|
||||||
|
@ -20,7 +20,7 @@ defmt = ["dep:defmt", "lorawan-device/defmt"]
|
|||||||
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 }
|
||||||
|
|
||||||
embassy-time = { version = "0.1.4", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
|
@ -34,6 +34,6 @@ impl lorawan_device::async_device::radio::Timer for LoraTimer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn delay_ms(&mut self, millis: u64) {
|
async fn delay_ms(&mut self, millis: u64) {
|
||||||
Timer::after(Duration::from_millis(millis)).await
|
Timer::after_millis(millis).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ 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.4", 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"
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ pub use crc32::ETH_FCS;
|
|||||||
use crc8::crc8;
|
use crc8::crc8;
|
||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select, Either};
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
use embedded_hal_1::digital::OutputPin;
|
use embedded_hal_1::digital::OutputPin;
|
||||||
use embedded_hal_async::digital::Wait;
|
use embedded_hal_async::digital::Wait;
|
||||||
use embedded_hal_async::spi::{Error, Operation, SpiDevice};
|
use embedded_hal_async::spi::{Error, Operation, SpiDevice};
|
||||||
@ -609,12 +609,12 @@ pub async fn new<const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait
|
|||||||
reset.set_low().unwrap();
|
reset.set_low().unwrap();
|
||||||
|
|
||||||
// Wait t1: 20-43mS
|
// Wait t1: 20-43mS
|
||||||
Timer::after(Duration::from_millis(30)).await;
|
Timer::after_millis(30).await;
|
||||||
|
|
||||||
reset.set_high().unwrap();
|
reset.set_high().unwrap();
|
||||||
|
|
||||||
// Wait t3: 50mS
|
// Wait t3: 50mS
|
||||||
Timer::after(Duration::from_millis(50)).await;
|
Timer::after_millis(50).await;
|
||||||
|
|
||||||
// Create device
|
// Create device
|
||||||
let mut mac = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx);
|
let mut mac = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx);
|
||||||
|
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
|
||||||
|
|
||||||
@ -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 {
|
||||||
@ -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,8 +10,8 @@ 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.4", 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 }
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@ edition = "2021"
|
|||||||
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 }
|
||||||
|
|
||||||
embassy-time = { version = "0.1.4", 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(())
|
||||||
}
|
}
|
||||||
|
@ -169,9 +169,9 @@ where
|
|||||||
pub async fn run(mut self) -> ! {
|
pub async fn run(mut self) -> ! {
|
||||||
debug!("resetting...");
|
debug!("resetting...");
|
||||||
self.reset.set_low().unwrap();
|
self.reset.set_low().unwrap();
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
self.reset.set_high().unwrap();
|
self.reset.set_high().unwrap();
|
||||||
Timer::after(Duration::from_millis(1000)).await;
|
Timer::after_millis(1000).await;
|
||||||
|
|
||||||
let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
||||||
let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
||||||
|
@ -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,8 +10,8 @@ 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.4", 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 }
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for WIZnet ethernet chips.
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
pub mod chip;
|
pub mod chip;
|
||||||
mod device;
|
mod device;
|
||||||
|
|
||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select3, Either3};
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_net_driver_channel::driver::LinkState;
|
use embassy_net_driver_channel::driver::LinkState;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Ticker, Timer};
|
||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
use embedded_hal_async::digital::Wait;
|
use embedded_hal_async::digital::Wait;
|
||||||
use embedded_hal_async::spi::SpiDevice;
|
use embedded_hal_async::spi::SpiDevice;
|
||||||
@ -49,35 +49,37 @@ pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
|
|||||||
impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
|
impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
|
||||||
pub async fn run(mut self) -> ! {
|
pub async fn run(mut self) -> ! {
|
||||||
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
|
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
|
||||||
|
let mut tick = Ticker::every(Duration::from_millis(500));
|
||||||
loop {
|
loop {
|
||||||
if self.mac.is_link_up().await {
|
match select3(
|
||||||
state_chan.set_link_state(LinkState::Up);
|
|
||||||
loop {
|
|
||||||
match select(
|
|
||||||
async {
|
async {
|
||||||
self.int.wait_for_low().await.ok();
|
self.int.wait_for_low().await.ok();
|
||||||
rx_chan.rx_buf().await
|
rx_chan.rx_buf().await
|
||||||
},
|
},
|
||||||
tx_chan.tx_buf(),
|
tx_chan.tx_buf(),
|
||||||
|
tick.next(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Either::First(p) => {
|
Either3::First(p) => {
|
||||||
if let Ok(n) = self.mac.read_frame(p).await {
|
if let Ok(n) = self.mac.read_frame(p).await {
|
||||||
rx_chan.rx_done(n);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Either::Second(p) => {
|
Either3::Second(p) => {
|
||||||
self.mac.write_frame(p).await.ok();
|
self.mac.write_frame(p).await.ok();
|
||||||
tx_chan.tx_done();
|
tx_chan.tx_done();
|
||||||
}
|
}
|
||||||
}
|
Either3::Third(()) => {
|
||||||
}
|
if self.mac.is_link_up().await {
|
||||||
|
state_chan.set_link_state(LinkState::Up);
|
||||||
} else {
|
} else {
|
||||||
state_chan.set_link_state(LinkState::Down);
|
state_chan.set_link_state(LinkState::Down);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net).
|
/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net).
|
||||||
@ -95,12 +97,12 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevi
|
|||||||
// Reset the chip.
|
// Reset the chip.
|
||||||
reset.set_low().ok();
|
reset.set_low().ok();
|
||||||
// Ensure the reset is registered.
|
// Ensure the reset is registered.
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after_millis(1).await;
|
||||||
reset.set_high().ok();
|
reset.set_high().ok();
|
||||||
|
|
||||||
// Wait for PLL lock. Some chips are slower than others.
|
// Wait for PLL lock. Some chips are slower than others.
|
||||||
// Slowest is w5100s which is 100ms, so let's just wait that.
|
// Slowest is w5100s which is 100ms, so let's just wait that.
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap();
|
let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap();
|
||||||
|
|
||||||
|
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,8 +51,8 @@ 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.4", 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.
|
||||||
@ -509,7 +515,10 @@ impl<D: Driver> Stack<D> {
|
|||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
|
let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
|
||||||
match socket.start_query(s.iface.context(), name, qtype) {
|
match socket.start_query(s.iface.context(), name, qtype) {
|
||||||
Ok(handle) => Poll::Ready(Ok(handle)),
|
Ok(handle) => {
|
||||||
|
s.waker.wake();
|
||||||
|
Poll::Ready(Ok(handle))
|
||||||
|
}
|
||||||
Err(dns::StartQueryError::NoFreeSlot) => {
|
Err(dns::StartQueryError::NoFreeSlot) => {
|
||||||
i.dns_waker.register(cx.waker());
|
i.dns_waker.register(cx.waker());
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
@ -809,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);
|
||||||
|
|
||||||
@ -841,6 +860,9 @@ impl<D: Driver> Inner<D> {
|
|||||||
let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
|
let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
|
||||||
|
|
||||||
if self.link_up {
|
if self.link_up {
|
||||||
|
if old_link_up != self.link_up {
|
||||||
|
socket.reset();
|
||||||
|
}
|
||||||
match socket.poll() {
|
match socket.poll() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(dhcpv4::Event::Deconfigured) => {
|
Some(dhcpv4::Event::Deconfigured) => {
|
||||||
|
@ -94,7 +94,7 @@ _gpio-p1 = []
|
|||||||
_nrf52832_anomaly_109 = []
|
_nrf52832_anomaly_109 = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.4", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
|
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
|
||||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
|
@ -7,9 +7,12 @@ pub mod pac {
|
|||||||
|
|
||||||
pub use nrf5340_app_pac::NVIC_PRIO_BITS;
|
pub use nrf5340_app_pac::NVIC_PRIO_BITS;
|
||||||
|
|
||||||
|
#[cfg(feature="rt")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use nrf5340_app_pac::interrupt;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use nrf5340_app_pac::{
|
pub use nrf5340_app_pac::{
|
||||||
interrupt,
|
|
||||||
Interrupt,
|
Interrupt,
|
||||||
Peripherals,
|
Peripherals,
|
||||||
|
|
||||||
@ -60,6 +63,8 @@ pub mod pac {
|
|||||||
wdt0_ns as wdt0,
|
wdt0_ns as wdt0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Non-Secure mode (NS) peripherals
|
||||||
|
pub mod ns {
|
||||||
#[cfg(feature = "nrf5340-app-ns")]
|
#[cfg(feature = "nrf5340-app-ns")]
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use nrf5340_app_pac::{
|
pub use nrf5340_app_pac::{
|
||||||
@ -130,7 +135,10 @@ pub mod pac {
|
|||||||
WDT0_NS as WDT0,
|
WDT0_NS as WDT0,
|
||||||
WDT1_NS as WDT1,
|
WDT1_NS as WDT1,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Secure mode (S) peripherals
|
||||||
|
pub mod s {
|
||||||
#[cfg(feature = "nrf5340-app-s")]
|
#[cfg(feature = "nrf5340-app-s")]
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use nrf5340_app_pac::{
|
pub use nrf5340_app_pac::{
|
||||||
@ -210,6 +218,12 @@ pub mod pac {
|
|||||||
WDT0_S as WDT0,
|
WDT0_S as WDT0,
|
||||||
WDT1_S as WDT1,
|
WDT1_S as WDT1,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "_ns")]
|
||||||
|
pub use ns::*;
|
||||||
|
#[cfg(feature = "_s")]
|
||||||
|
pub use s::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum buffer size that the EasyDMA can send/recv in one operation.
|
/// The maximum buffer size that the EasyDMA can send/recv in one operation.
|
||||||
|
@ -7,9 +7,12 @@ pub mod pac {
|
|||||||
|
|
||||||
pub use nrf5340_net_pac::NVIC_PRIO_BITS;
|
pub use nrf5340_net_pac::NVIC_PRIO_BITS;
|
||||||
|
|
||||||
|
#[cfg(feature="rt")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use nrf5340_net_pac::interrupt;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use nrf5340_net_pac::{
|
pub use nrf5340_net_pac::{
|
||||||
interrupt,
|
|
||||||
Interrupt,
|
Interrupt,
|
||||||
Peripherals,
|
Peripherals,
|
||||||
|
|
||||||
|
@ -7,9 +7,12 @@ pub mod pac {
|
|||||||
|
|
||||||
pub use nrf9160_pac::NVIC_PRIO_BITS;
|
pub use nrf9160_pac::NVIC_PRIO_BITS;
|
||||||
|
|
||||||
|
#[cfg(feature="rt")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use nrf9160_pac::interrupt;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use nrf9160_pac::{
|
pub use nrf9160_pac::{
|
||||||
interrupt,
|
|
||||||
Interrupt,
|
Interrupt,
|
||||||
|
|
||||||
cc_host_rgf_s as cc_host_rgf,
|
cc_host_rgf_s as cc_host_rgf,
|
||||||
@ -45,7 +48,8 @@ pub mod pac {
|
|||||||
wdt_ns as wdt,
|
wdt_ns as wdt,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "nrf9160-ns")]
|
/// Non-Secure mode (NS) peripherals
|
||||||
|
pub mod ns {
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use nrf9160_pac::{
|
pub use nrf9160_pac::{
|
||||||
CLOCK_NS as CLOCK,
|
CLOCK_NS as CLOCK,
|
||||||
@ -99,8 +103,10 @@ pub mod pac {
|
|||||||
VMC_NS as VMC,
|
VMC_NS as VMC,
|
||||||
WDT_NS as WDT,
|
WDT_NS as WDT,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "nrf9160-s")]
|
/// Secure mode (S) peripherals
|
||||||
|
pub mod s {
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use nrf9160_pac::{
|
pub use nrf9160_pac::{
|
||||||
CC_HOST_RGF_S as CC_HOST_RGF,
|
CC_HOST_RGF_S as CC_HOST_RGF,
|
||||||
@ -161,6 +167,12 @@ pub mod pac {
|
|||||||
VMC_S as VMC,
|
VMC_S as VMC,
|
||||||
WDT_S as WDT,
|
WDT_S as WDT,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "_ns")]
|
||||||
|
pub use ns::*;
|
||||||
|
#[cfg(feature = "_s")]
|
||||||
|
pub use s::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum buffer size that the EasyDMA can send/recv in one operation.
|
/// The maximum buffer size that the EasyDMA can send/recv in one operation.
|
||||||
|
@ -60,7 +60,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.4", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
|
embassy-time = { version = "0.1.5", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
|
||||||
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-2"] }
|
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
|
||||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
|
@ -213,6 +213,7 @@ impl<'d> Adc<'d, Async> {
|
|||||||
ch: &mut Channel<'_>,
|
ch: &mut Channel<'_>,
|
||||||
buf: &mut [W],
|
buf: &mut [W],
|
||||||
fcs_err: bool,
|
fcs_err: bool,
|
||||||
|
div: u16,
|
||||||
dma: impl Peripheral<P = impl dma::Channel>,
|
dma: impl Peripheral<P = impl dma::Channel>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let r = Self::regs();
|
let r = Self::regs();
|
||||||
@ -258,6 +259,7 @@ impl<'d> Adc<'d, Async> {
|
|||||||
// start conversions and wait for dma to finish. we can't report errors early
|
// start conversions and wait for dma to finish. we can't report errors early
|
||||||
// because there's no interrupt to signal them, and inspecting every element
|
// because there's no interrupt to signal them, and inspecting every element
|
||||||
// of the fifo is too costly to do here.
|
// of the fifo is too costly to do here.
|
||||||
|
r.div().write_set(|w| w.set_int(div));
|
||||||
r.cs().write_set(|w| w.set_start_many(true));
|
r.cs().write_set(|w| w.set_start_many(true));
|
||||||
dma.await;
|
dma.await;
|
||||||
mem::drop(auto_reset);
|
mem::drop(auto_reset);
|
||||||
@ -275,9 +277,10 @@ impl<'d> Adc<'d, Async> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
ch: &mut Channel<'_>,
|
ch: &mut Channel<'_>,
|
||||||
buf: &mut [S],
|
buf: &mut [S],
|
||||||
|
div: u16,
|
||||||
dma: impl Peripheral<P = impl dma::Channel>,
|
dma: impl Peripheral<P = impl dma::Channel>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.read_many_inner(ch, buf, false, dma).await
|
self.read_many_inner(ch, buf, false, div, dma).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -285,11 +288,12 @@ impl<'d> Adc<'d, Async> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
ch: &mut Channel<'_>,
|
ch: &mut Channel<'_>,
|
||||||
buf: &mut [Sample],
|
buf: &mut [Sample],
|
||||||
|
div: u16,
|
||||||
dma: impl Peripheral<P = impl dma::Channel>,
|
dma: impl Peripheral<P = impl dma::Channel>,
|
||||||
) {
|
) {
|
||||||
// errors are reported in individual samples
|
// errors are reported in individual samples
|
||||||
let _ = self
|
let _ = self
|
||||||
.read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, dma)
|
.read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, div, dma)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,16 +10,39 @@ use crate::gpio::sealed::Pin as _;
|
|||||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||||
use crate::{pac, peripherals, RegExt};
|
use crate::{pac, peripherals, RegExt};
|
||||||
|
|
||||||
|
/// The configuration of a PWM slice.
|
||||||
|
/// Note the period in clock cycles of a slice can be computed as:
|
||||||
|
/// `(top + 1) * (phase_correct ? 1 : 2) * divider`
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
/// Inverts the PWM output signal on channel A.
|
||||||
pub invert_a: bool,
|
pub invert_a: bool,
|
||||||
|
/// Inverts the PWM output signal on channel B.
|
||||||
pub invert_b: bool,
|
pub invert_b: bool,
|
||||||
|
/// Enables phase-correct mode for PWM operation.
|
||||||
|
/// In phase-correct mode, the PWM signal is generated in such a way that
|
||||||
|
/// the pulse is always centered regardless of the duty cycle.
|
||||||
|
/// The output frequency is halved when phase-correct mode is enabled.
|
||||||
pub phase_correct: bool,
|
pub phase_correct: bool,
|
||||||
|
/// Enables the PWM slice, allowing it to generate an output.
|
||||||
pub enable: bool,
|
pub enable: bool,
|
||||||
|
/// A fractional clock divider, represented as a fixed-point number with
|
||||||
|
/// 8 integer bits and 4 fractional bits. It allows precise control over
|
||||||
|
/// the PWM output frequency by gating the PWM counter increment.
|
||||||
|
/// A higher value will result in a slower output frequency.
|
||||||
pub divider: fixed::FixedU16<fixed::types::extra::U4>,
|
pub divider: fixed::FixedU16<fixed::types::extra::U4>,
|
||||||
|
/// The output on channel A goes high when `compare_a` is higher than the
|
||||||
|
/// counter. A compare of 0 will produce an always low output, while a
|
||||||
|
/// compare of `top + 1` will produce an always high output.
|
||||||
pub compare_a: u16,
|
pub compare_a: u16,
|
||||||
|
/// The output on channel B goes high when `compare_b` is higher than the
|
||||||
|
/// counter. A compare of 0 will produce an always low output, while a
|
||||||
|
/// compare of `top + 1` will produce an always high output.
|
||||||
pub compare_b: u16,
|
pub compare_b: u16,
|
||||||
|
/// The point at which the counter wraps, representing the maximum possible
|
||||||
|
/// period. The counter will either wrap to 0 or reverse depending on the
|
||||||
|
/// setting of `phase_correct`.
|
||||||
pub top: u16,
|
pub top: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +196,9 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advances a slice’s output phase by one count while it is running
|
||||||
|
/// by inserting a pulse into the clock enable. The counter
|
||||||
|
/// will not count faster than once per cycle.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn phase_advance(&mut self) {
|
pub fn phase_advance(&mut self) {
|
||||||
let p = self.inner.regs();
|
let p = self.inner.regs();
|
||||||
@ -180,6 +206,9 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
|||||||
while p.csr().read().ph_adv() {}
|
while p.csr().read().ph_adv() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retards a slice’s output phase by one count while it is running
|
||||||
|
/// by deleting a pulse from the clock enable. The counter will not
|
||||||
|
/// count backward when clock enable is permenantly low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn phase_retard(&mut self) {
|
pub fn phase_retard(&mut self) {
|
||||||
let p = self.inner.regs();
|
let p = self.inner.regs();
|
||||||
|
@ -5,7 +5,7 @@ use core::task::Poll;
|
|||||||
use atomic_polyfill::{AtomicU8, Ordering};
|
use atomic_polyfill::{AtomicU8, Ordering};
|
||||||
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::clocks::clk_peri_freq;
|
use crate::clocks::clk_peri_freq;
|
||||||
@ -435,7 +435,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
|||||||
Self::flush().await.unwrap();
|
Self::flush().await.unwrap();
|
||||||
while self.busy() {}
|
while self.busy() {}
|
||||||
regs.uartlcr_h().write_set(|w| w.set_brk(true));
|
regs.uartlcr_h().write_set(|w| w.set_brk(true));
|
||||||
Timer::after(Duration::from_micros(wait_usecs)).await;
|
Timer::after_micros(wait_usecs).await;
|
||||||
regs.uartlcr_h().write_clear(|w| w.set_brk(true));
|
regs.uartlcr_h().write_clear(|w| w.set_brk(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use atomic_polyfill::{AtomicU16, Ordering};
|
|||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select, Either};
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
use pac::uart::regs::Uartris;
|
use pac::uart::regs::Uartris;
|
||||||
|
|
||||||
use crate::clocks::clk_peri_freq;
|
use crate::clocks::clk_peri_freq;
|
||||||
@ -187,7 +187,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> {
|
|||||||
self.blocking_flush().unwrap();
|
self.blocking_flush().unwrap();
|
||||||
while self.busy() {}
|
while self.busy() {}
|
||||||
regs.uartlcr_h().write_set(|w| w.set_brk(true));
|
regs.uartlcr_h().write_set(|w| w.set_brk(true));
|
||||||
Timer::after(Duration::from_micros(wait_usecs)).await;
|
Timer::after_micros(wait_usecs).await;
|
||||||
regs.uartlcr_h().write_clear(|w| w.set_brk(true));
|
regs.uartlcr_h().write_clear(|w| w.set_brk(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,11 @@ features = ["stm32wb55rg"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
|
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.4", path = "../embassy-time", optional = true }
|
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])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,11 @@ flavors = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.4", path = "../embassy-time", optional = true }
|
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-e4a769aa67aa82603448377daa579d67a789983a" }
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-bcc9b6bf9fa195e91625849efc4ba473d9ace4e9" }
|
||||||
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-e4a769aa67aa82603448377daa579d67a789983a", default-features = false, features = ["metadata"]}
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-bcc9b6bf9fa195e91625849efc4ba473d9ace4e9", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
@ -352,7 +352,7 @@ fn main() {
|
|||||||
// ========
|
// ========
|
||||||
// Generate DMA IRQs.
|
// Generate DMA IRQs.
|
||||||
|
|
||||||
let mut dma_irqs: HashMap<&str, Vec<(&str, &str, &str)>> = HashMap::new();
|
let mut dma_irqs: BTreeMap<&str, Vec<(&str, &str, &str)>> = BTreeMap::new();
|
||||||
|
|
||||||
for p in METADATA.peripherals {
|
for p in METADATA.peripherals {
|
||||||
if let Some(r) = &p.registers {
|
if let Some(r) = &p.registers {
|
||||||
@ -371,13 +371,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (irq, channels) in dma_irqs {
|
let dma_irqs: TokenStream = dma_irqs
|
||||||
|
.iter()
|
||||||
|
.map(|(irq, channels)| {
|
||||||
let irq = format_ident!("{}", irq);
|
let irq = format_ident!("{}", irq);
|
||||||
|
|
||||||
let xdma = format_ident!("{}", channels[0].0);
|
let xdma = format_ident!("{}", channels[0].0);
|
||||||
let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch));
|
let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch));
|
||||||
|
|
||||||
g.extend(quote! {
|
quote! {
|
||||||
#[cfg(feature = "rt")]
|
#[cfg(feature = "rt")]
|
||||||
#[crate::interrupt]
|
#[crate::interrupt]
|
||||||
unsafe fn #irq () {
|
unsafe fn #irq () {
|
||||||
@ -385,23 +387,27 @@ fn main() {
|
|||||||
<crate::peripherals::#channels as crate::dma::#xdma::sealed::Channel>::on_irq();
|
<crate::peripherals::#channels as crate::dma::#xdma::sealed::Channel>::on_irq();
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
g.extend(dma_irqs);
|
||||||
|
|
||||||
// ========
|
// ========
|
||||||
// Generate rcc fieldset and enum maps
|
// Extract the rcc registers
|
||||||
let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
|
|
||||||
let rcc_registers = METADATA
|
let rcc_registers = METADATA
|
||||||
.peripherals
|
.peripherals
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|p| p.registers.as_ref())
|
.filter_map(|p| p.registers.as_ref())
|
||||||
.find(|r| r.kind == "rcc")
|
.find(|r| r.kind == "rcc")
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.ir;
|
|
||||||
|
|
||||||
let rcc_blocks = rcc_registers.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
|
// ========
|
||||||
let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.fieldsets.iter().map(|f| (f.name, f)).collect();
|
// Generate rcc fieldset and enum maps
|
||||||
let rcc_enums: HashMap<&str, &Enum> = rcc_registers.enums.iter().map(|e| (e.name, e)).collect();
|
let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
|
||||||
|
let rcc_blocks = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
|
||||||
|
let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.ir.fieldsets.iter().map(|f| (f.name, f)).collect();
|
||||||
|
let rcc_enums: HashMap<&str, &Enum> = rcc_registers.ir.enums.iter().map(|e| (e.name, e)).collect();
|
||||||
|
|
||||||
rcc_blocks
|
rcc_blocks
|
||||||
.iter()
|
.iter()
|
||||||
@ -432,7 +438,7 @@ fn main() {
|
|||||||
// Generate RccPeripheral impls
|
// Generate RccPeripheral impls
|
||||||
|
|
||||||
let refcounted_peripherals = HashSet::from(["usart", "adc"]);
|
let refcounted_peripherals = HashSet::from(["usart", "adc"]);
|
||||||
let mut refcount_statics = HashSet::new();
|
let mut refcount_statics = BTreeSet::new();
|
||||||
|
|
||||||
for p in METADATA.peripherals {
|
for p in METADATA.peripherals {
|
||||||
if !singletons.contains(&p.name.to_string()) {
|
if !singletons.contains(&p.name.to_string()) {
|
||||||
@ -465,9 +471,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!("{}", rcc.clock.to_ascii_lowercase());
|
let clk = format_ident!("{}", rcc.clock);
|
||||||
let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
|
let en_reg = format_ident!("{}", en.register);
|
||||||
let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase());
|
let set_en_field = format_ident!("set_{}", en.field);
|
||||||
|
|
||||||
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 =
|
||||||
@ -493,9 +499,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>| {
|
||||||
// temporary hack to restrict the scope of the implementation to h5
|
// restrict mux implementation to supported versions
|
||||||
if !&chip_name.starts_with("stm32h5") {
|
if !mux_supported {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,13 +526,17 @@ fn main() {
|
|||||||
.filter(|v| v.name != "DISABLE")
|
.filter(|v| v.name != "DISABLE")
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
let variant_name = format_ident!("{}", v.name);
|
let variant_name = format_ident!("{}", v.name);
|
||||||
|
let clock_name = format_ident!("{}", v.name.to_ascii_lowercase());
|
||||||
|
|
||||||
// temporary hack to restrict the scope of the implementation until clock names can be stabilized
|
if v.name.starts_with("HCLK") || v.name.starts_with("PCLK") || v.name == "SYS" {
|
||||||
let clock_name = format_ident!("mux_{}", v.name.to_ascii_lowercase());
|
quote! {
|
||||||
|
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name },
|
||||||
|
}
|
||||||
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name.unwrap() },
|
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name.unwrap() },
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -552,7 +564,7 @@ fn main() {
|
|||||||
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
||||||
#before_enable
|
#before_enable
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
crate::rcc::clock_refcount_add(_cs);
|
unsafe { crate::rcc::REFCOUNT_STOP2 += 1 };
|
||||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
||||||
#after_enable
|
#after_enable
|
||||||
#rst
|
#rst
|
||||||
@ -561,7 +573,7 @@ fn main() {
|
|||||||
#before_disable
|
#before_disable
|
||||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
|
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
crate::rcc::clock_refcount_sub(_cs);
|
unsafe { crate::rcc::REFCOUNT_STOP2 -= 1 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1007,15 +1019,7 @@ fn main() {
|
|||||||
|
|
||||||
// ========
|
// ========
|
||||||
// Generate Div/Mul impls for RCC prescalers/dividers/multipliers.
|
// Generate Div/Mul impls for RCC prescalers/dividers/multipliers.
|
||||||
let rcc_registers = METADATA
|
for e in rcc_registers.ir.enums {
|
||||||
.peripherals
|
|
||||||
.iter()
|
|
||||||
.filter_map(|p| p.registers.as_ref())
|
|
||||||
.find(|r| r.kind == "rcc")
|
|
||||||
.unwrap()
|
|
||||||
.ir;
|
|
||||||
|
|
||||||
for e in rcc_registers.enums {
|
|
||||||
fn is_rcc_name(e: &str) -> bool {
|
fn is_rcc_name(e: &str) -> bool {
|
||||||
match e {
|
match e {
|
||||||
"Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" => true,
|
"Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" => true,
|
||||||
|
@ -564,7 +564,7 @@ foreach_peripheral!(
|
|||||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||||
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
||||||
fn frequency() -> crate::time::Hertz {
|
fn frequency() -> crate::time::Hertz {
|
||||||
critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 })
|
critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
|
||||||
|
@ -191,7 +191,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
|||||||
// TODO MTU size setting not found for v1 ethernet, check if correct
|
// TODO MTU size setting not found for v1 ethernet, check if correct
|
||||||
|
|
||||||
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
||||||
let hclk = unsafe { crate::rcc::get_freqs() }.ahb1;
|
let hclk = unsafe { crate::rcc::get_freqs() }.hclk1;
|
||||||
let hclk_mhz = hclk.0 / 1_000_000;
|
let hclk_mhz = hclk.0 / 1_000_000;
|
||||||
|
|
||||||
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
||||||
|
@ -164,7 +164,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
||||||
let hclk = unsafe { crate::rcc::get_freqs() }.ahb1;
|
let hclk = unsafe { crate::rcc::get_freqs() }.hclk1;
|
||||||
let hclk_mhz = hclk.0 / 1_000_000;
|
let hclk_mhz = hclk.0 / 1_000_000;
|
||||||
|
|
||||||
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
||||||
|
@ -465,7 +465,7 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) {
|
|||||||
feature = "stm32f439vg",
|
feature = "stm32f439vg",
|
||||||
feature = "stm32f439zg",
|
feature = "stm32f439zg",
|
||||||
))]
|
))]
|
||||||
if second_bank_read && unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } {
|
if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() {
|
||||||
panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11");
|
panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -763,6 +763,13 @@ pub(crate) unsafe fn init(_cs: CriticalSection) {
|
|||||||
<crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable_and_reset_with_cs(_cs);
|
<crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable_and_reset_with_cs(_cs);
|
||||||
|
|
||||||
crate::_generated::init_gpio();
|
crate::_generated::init_gpio();
|
||||||
|
|
||||||
|
// Setting this bit is mandatory to use PG[15:2].
|
||||||
|
#[cfg(stm32u5)]
|
||||||
|
crate::pac::PWR.svmcr().modify(|w| {
|
||||||
|
w.set_io2sv(true);
|
||||||
|
w.set_io2vmen(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mod eh02 {
|
mod eh02 {
|
||||||
|
@ -170,7 +170,7 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
|
|||||||
let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg);
|
let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg);
|
||||||
|
|
||||||
#[cfg(all(rcc_f4, not(stm32f410)))]
|
#[cfg(all(rcc_f4, not(stm32f410)))]
|
||||||
let pclk = unsafe { get_freqs() }.plli2s.unwrap();
|
let pclk = unsafe { get_freqs() }.plli2s1_q.unwrap();
|
||||||
|
|
||||||
#[cfg(stm32f410)]
|
#[cfg(stm32f410)]
|
||||||
let pclk = T::frequency();
|
let pclk = T::frequency();
|
||||||
|
@ -5,6 +5,7 @@ use core::task::Poll;
|
|||||||
use self::sealed::Instance;
|
use self::sealed::Instance;
|
||||||
use crate::interrupt;
|
use crate::interrupt;
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
use crate::pac::rcc::vals::{Lptim1sel, Lptim2sel};
|
||||||
use crate::peripherals::IPCC;
|
use crate::peripherals::IPCC;
|
||||||
use crate::rcc::sealed::RccPeripheral;
|
use crate::rcc::sealed::RccPeripheral;
|
||||||
|
|
||||||
@ -273,7 +274,7 @@ fn _configure_pwr() {
|
|||||||
|
|
||||||
// set LPTIM1 & LPTIM2 clock source
|
// set LPTIM1 & LPTIM2 clock source
|
||||||
rcc.ccipr().modify(|w| {
|
rcc.ccipr().modify(|w| {
|
||||||
w.set_lptim1sel(0b00); // PCLK
|
w.set_lptim1sel(Lptim1sel::PCLK1);
|
||||||
w.set_lptim2sel(0b00); // PCLK
|
w.set_lptim2sel(Lptim2sel::PCLK1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -226,9 +226,9 @@ pub fn init(config: Config) -> Peripherals {
|
|||||||
time_driver::init(cs);
|
time_driver::init(cs);
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
while !crate::rcc::low_power_ready() {
|
{
|
||||||
crate::rcc::clock_refcount_sub(cs);
|
crate::rcc::REFCOUNT_STOP2 = 0
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
p
|
p
|
||||||
|
@ -6,7 +6,6 @@ use cortex_m::peripheral::SCB;
|
|||||||
use embassy_executor::*;
|
use embassy_executor::*;
|
||||||
|
|
||||||
use crate::interrupt;
|
use crate::interrupt;
|
||||||
use crate::rcc::low_power_ready;
|
|
||||||
use crate::time_driver::{get_driver, RtcDriver};
|
use crate::time_driver::{get_driver, RtcDriver};
|
||||||
|
|
||||||
const THREAD_PENDER: usize = usize::MAX;
|
const THREAD_PENDER: usize = usize::MAX;
|
||||||
@ -33,6 +32,15 @@ pub fn stop_with_rtc(rtc: &'static Rtc) {
|
|||||||
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
|
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stop_ready(stop_mode: StopMode) -> bool {
|
||||||
|
unsafe { EXECUTOR.as_mut().unwrap() }.stop_ready(stop_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum StopMode {
|
||||||
|
Stop2,
|
||||||
|
}
|
||||||
|
|
||||||
/// Thread mode executor, using WFE/SEV.
|
/// Thread mode executor, using WFE/SEV.
|
||||||
///
|
///
|
||||||
/// This is the simplest and most common kind of executor. It runs on
|
/// This is the simplest and most common kind of executor. It runs on
|
||||||
@ -80,12 +88,18 @@ impl Executor {
|
|||||||
trace!("low power: stop with rtc configured");
|
trace!("low power: stop with rtc configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stop_ready(&self, stop_mode: StopMode) -> bool {
|
||||||
|
match stop_mode {
|
||||||
|
StopMode::Stop2 => unsafe { crate::rcc::REFCOUNT_STOP2 == 0 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn configure_pwr(&mut self) {
|
fn configure_pwr(&mut self) {
|
||||||
self.scb.clear_sleepdeep();
|
self.scb.clear_sleepdeep();
|
||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
if !low_power_ready() {
|
if !self.stop_ready(StopMode::Stop2) {
|
||||||
trace!("low power: not ready to stop");
|
trace!("low power: not ready to stop");
|
||||||
} else if self.time_driver.pause_time().is_err() {
|
} else if self.time_driver.pause_time().is_err() {
|
||||||
trace!("low power: failed to pause time");
|
trace!("low power: failed to pause time");
|
||||||
|
@ -101,6 +101,8 @@ pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
|
|||||||
#[cfg(opamp_f3)]
|
#[cfg(opamp_f3)]
|
||||||
macro_rules! impl_opamp_output {
|
macro_rules! impl_opamp_output {
|
||||||
($inst:ident, $adc:ident, $ch:expr) => {
|
($inst:ident, $adc:ident, $ch:expr) => {
|
||||||
|
foreach_adc!(
|
||||||
|
($adc, $common_inst:ident, $adc_clock:ident) => {
|
||||||
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
|
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
|
||||||
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
||||||
{
|
{
|
||||||
@ -114,6 +116,8 @@ macro_rules! impl_opamp_output {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(opamp_f3)]
|
#[cfg(opamp_f3)]
|
||||||
|
@ -106,7 +106,7 @@ impl LsConfig {
|
|||||||
|
|
||||||
pub const fn off() -> Self {
|
pub const fn off() -> Self {
|
||||||
Self {
|
Self {
|
||||||
rtc: RtcClockSource::NOCLOCK,
|
rtc: RtcClockSource::DISABLE,
|
||||||
lsi: false,
|
lsi: false,
|
||||||
lse: None,
|
lse: None,
|
||||||
}
|
}
|
||||||
@ -133,7 +133,7 @@ impl LsConfig {
|
|||||||
Some(LSI_FREQ)
|
Some(LSI_FREQ)
|
||||||
}
|
}
|
||||||
RtcClockSource::LSE => Some(self.lse.as_ref().unwrap().frequency),
|
RtcClockSource::LSE => Some(self.lse.as_ref().unwrap().frequency),
|
||||||
RtcClockSource::NOCLOCK => None,
|
RtcClockSource::DISABLE => None,
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ impl LsConfig {
|
|||||||
ok &= reg.rtcsel() == self.rtc;
|
ok &= reg.rtcsel() == self.rtc;
|
||||||
#[cfg(not(rcc_wba))]
|
#[cfg(not(rcc_wba))]
|
||||||
{
|
{
|
||||||
ok &= reg.rtcen() == (self.rtc != RtcClockSource::NOCLOCK);
|
ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE);
|
||||||
}
|
}
|
||||||
ok &= reg.lseon() == lse_en;
|
ok &= reg.lseon() == lse_en;
|
||||||
ok &= reg.lsebyp() == lse_byp;
|
ok &= reg.lsebyp() == lse_byp;
|
||||||
@ -225,7 +225,7 @@ impl LsConfig {
|
|||||||
while !bdcr().read().lserdy() {}
|
while !bdcr().read().lserdy() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.rtc != RtcClockSource::NOCLOCK {
|
if self.rtc != RtcClockSource::DISABLE {
|
||||||
bdcr().modify(|w| {
|
bdcr().modify(|w| {
|
||||||
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||||
assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||||
|
@ -134,10 +134,12 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
|
hsi: None,
|
||||||
|
lse: None,
|
||||||
sys: sys_clk,
|
sys: sys_clk,
|
||||||
ahb1: ahb_freq,
|
hclk1: ahb_freq,
|
||||||
apb1: apb_freq,
|
pclk1: apb_freq,
|
||||||
apb1_tim: apb_tim_freq,
|
pclk1_tim: apb_tim_freq,
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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| {
|
||||||
@ -162,11 +162,11 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: Hertz(real_sysclk),
|
sys: Hertz(real_sysclk),
|
||||||
apb1: Hertz(pclk),
|
pclk1: Hertz(pclk),
|
||||||
apb2: Hertz(pclk),
|
pclk2: Hertz(pclk),
|
||||||
apb1_tim: Hertz(pclk * timer_mul),
|
pclk1_tim: Hertz(pclk * timer_mul),
|
||||||
apb2_tim: Hertz(pclk * timer_mul),
|
pclk2_tim: Hertz(pclk * timer_mul),
|
||||||
ahb1: Hertz(hclk),
|
hclk1: Hertz(hclk),
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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,7 @@ 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
|
Sw::PLL1_P
|
||||||
} else if config.hse.is_some() {
|
} else if config.hse.is_some() {
|
||||||
Sw::HSE
|
Sw::HSE
|
||||||
} else {
|
} else {
|
||||||
@ -180,11 +181,11 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: Hertz(real_sysclk),
|
sys: Hertz(real_sysclk),
|
||||||
apb1: Hertz(pclk1),
|
pclk1: Hertz(pclk1),
|
||||||
apb2: Hertz(pclk2),
|
pclk2: Hertz(pclk2),
|
||||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
pclk1_tim: Hertz(pclk1 * timer_mul1),
|
||||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
pclk2_tim: Hertz(pclk2 * timer_mul2),
|
||||||
ahb1: Hertz(hclk),
|
hclk1: Hertz(hclk),
|
||||||
adc: Some(Hertz(adcclk)),
|
adc: Some(Hertz(adcclk)),
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
|
@ -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
|
||||||
@ -307,14 +307,14 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: sys_clk,
|
sys: sys_clk,
|
||||||
ahb1: ahb_freq,
|
hclk1: ahb_freq,
|
||||||
ahb2: ahb_freq,
|
hclk2: ahb_freq,
|
||||||
ahb3: ahb_freq,
|
hclk3: ahb_freq,
|
||||||
apb1: apb1_freq,
|
pclk1: apb1_freq,
|
||||||
apb1_tim: apb1_tim_freq,
|
pclk1_tim: apb1_tim_freq,
|
||||||
apb2: apb2_freq,
|
pclk2: apb2_freq,
|
||||||
apb2_tim: apb2_tim_freq,
|
pclk2_tim: apb2_tim_freq,
|
||||||
pll48: Some(pll_clocks.pll48_freq),
|
pll1_q: Some(pll_clocks.pll48_freq),
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
@ -281,11 +281,11 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: sysclk,
|
sys: sysclk,
|
||||||
apb1: pclk1,
|
pclk1: pclk1,
|
||||||
apb2: pclk2,
|
pclk2: pclk2,
|
||||||
apb1_tim: pclk1 * timer_mul1,
|
pclk1_tim: pclk1 * timer_mul1,
|
||||||
apb2_tim: pclk2 * timer_mul2,
|
pclk2_tim: pclk2 * timer_mul2,
|
||||||
ahb1: hclk,
|
hclk1: hclk,
|
||||||
#[cfg(rcc_f3)]
|
#[cfg(rcc_f3)]
|
||||||
adc: adc,
|
adc: adc,
|
||||||
#[cfg(all(rcc_f3, adc3_common))]
|
#[cfg(all(rcc_f3, adc3_common))]
|
||||||
|
@ -1,396 +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),
|
|
||||||
apb1: Hertz(pclk1),
|
|
||||||
apb2: Hertz(pclk2),
|
|
||||||
|
|
||||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
|
||||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
|
||||||
|
|
||||||
ahb1: Hertz(hclk),
|
|
||||||
ahb2: Hertz(hclk),
|
|
||||||
ahb3: Hertz(hclk),
|
|
||||||
|
|
||||||
pll48: plls.pll48clk.map(Hertz),
|
|
||||||
|
|
||||||
#[cfg(not(stm32f410))]
|
|
||||||
plli2s: plls.plli2sclk.map(Hertz),
|
|
||||||
|
|
||||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
|
||||||
pllsai: plls.pllsaiclk.map(Hertz),
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
387
embassy-stm32/src/rcc/f4f7.rs
Normal file
387
embassy-stm32/src/rcc/f4f7.rs
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
pub use crate::pac::rcc::vals::{
|
||||||
|
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource,
|
||||||
|
Ppre as APBPrescaler, Sw as Sysclk,
|
||||||
|
};
|
||||||
|
use crate::pac::{FLASH, RCC};
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
// TODO: on some F4s, PLLM is shared between all PLLs. Enforce that.
|
||||||
|
// TODO: on some F4s, add support for plli2s_src
|
||||||
|
//
|
||||||
|
// plli2s plli2s_m plli2s_src pllsai pllsai_m
|
||||||
|
// f401 y shared
|
||||||
|
// f410
|
||||||
|
// f411 y individual
|
||||||
|
// f412 y individual y
|
||||||
|
// f4[12]3 y individual y
|
||||||
|
// f446 y individual y individual
|
||||||
|
// f4[67]9 y shared y shared
|
||||||
|
// f4[23][79] y shared y shared
|
||||||
|
// f4[01][57] y shared
|
||||||
|
|
||||||
|
/// HSI speed
|
||||||
|
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum HseMode {
|
||||||
|
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||||
|
Oscillator,
|
||||||
|
/// external analog clock (low swing) (HSEBYP=1)
|
||||||
|
Bypass,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct Hse {
|
||||||
|
/// HSE frequency.
|
||||||
|
pub freq: Hertz,
|
||||||
|
/// HSE mode.
|
||||||
|
pub mode: HseMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Pll {
|
||||||
|
/// PLL pre-divider (DIVM).
|
||||||
|
pub prediv: PllPreDiv,
|
||||||
|
|
||||||
|
/// PLL multiplication factor.
|
||||||
|
pub mul: PllMul,
|
||||||
|
|
||||||
|
/// PLL P division factor. If None, PLL P output is disabled.
|
||||||
|
pub divp: Option<Pllp>,
|
||||||
|
/// PLL Q division factor. If None, PLL Q output is disabled.
|
||||||
|
pub divq: Option<Pllq>,
|
||||||
|
/// PLL R division factor. If None, PLL R output is disabled.
|
||||||
|
pub divr: Option<Pllr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration of the core clocks
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Config {
|
||||||
|
pub hsi: bool,
|
||||||
|
pub hse: Option<Hse>,
|
||||||
|
pub sys: Sysclk,
|
||||||
|
|
||||||
|
pub pll_src: PllSource,
|
||||||
|
|
||||||
|
pub pll: Option<Pll>,
|
||||||
|
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
|
||||||
|
pub plli2s: Option<Pll>,
|
||||||
|
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
|
||||||
|
pub pllsai: Option<Pll>,
|
||||||
|
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb1_pre: APBPrescaler,
|
||||||
|
pub apb2_pre: APBPrescaler,
|
||||||
|
|
||||||
|
pub ls: super::LsConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hsi: true,
|
||||||
|
hse: None,
|
||||||
|
sys: Sysclk::HSI,
|
||||||
|
pll_src: PllSource::HSI,
|
||||||
|
pll: None,
|
||||||
|
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
|
||||||
|
plli2s: None,
|
||||||
|
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
|
||||||
|
pllsai: None,
|
||||||
|
|
||||||
|
ahb_pre: AHBPrescaler::DIV1,
|
||||||
|
apb1_pre: APBPrescaler::DIV1,
|
||||||
|
apb2_pre: APBPrescaler::DIV1,
|
||||||
|
|
||||||
|
ls: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
// always enable overdrive for now. Make it configurable in the future.
|
||||||
|
#[cfg(not(any(
|
||||||
|
stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417
|
||||||
|
)))]
|
||||||
|
{
|
||||||
|
use crate::pac::PWR;
|
||||||
|
PWR.cr1().modify(|w| w.set_oden(true));
|
||||||
|
while !PWR.csr1().read().odrdy() {}
|
||||||
|
|
||||||
|
PWR.cr1().modify(|w| w.set_odswen(true));
|
||||||
|
while !PWR.csr1().read().odswrdy() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure HSI
|
||||||
|
let hsi = match config.hsi {
|
||||||
|
false => {
|
||||||
|
RCC.cr().modify(|w| w.set_hsion(false));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
true => {
|
||||||
|
RCC.cr().modify(|w| w.set_hsion(true));
|
||||||
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
Some(HSI_FREQ)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure HSE
|
||||||
|
let hse = match config.hse {
|
||||||
|
None => {
|
||||||
|
RCC.cr().modify(|w| w.set_hseon(false));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Some(hse) => {
|
||||||
|
match hse.mode {
|
||||||
|
HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
|
||||||
|
HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
|
||||||
|
}
|
||||||
|
|
||||||
|
RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
|
||||||
|
RCC.cr().modify(|w| w.set_hseon(true));
|
||||||
|
while !RCC.cr().read().hserdy() {}
|
||||||
|
Some(hse.freq)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure PLLs.
|
||||||
|
let pll_input = PllInput {
|
||||||
|
hse,
|
||||||
|
hsi,
|
||||||
|
source: config.pll_src,
|
||||||
|
};
|
||||||
|
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
|
||||||
|
#[cfg(any(all(stm32f4, not(any(stm32f410, stm32f429))), stm32f7))]
|
||||||
|
let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input);
|
||||||
|
#[cfg(all(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7), not(stm32f429)))]
|
||||||
|
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) = super::util::calc_pclk(hclk, config.apb1_pre);
|
||||||
|
let (pclk2, pclk2_tim) = super::util::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(any(stm32f410, stm32f429))))]
|
||||||
|
plli2s1_q: _plli2s.q,
|
||||||
|
#[cfg(all(rcc_f4, not(any(stm32f410, stm32f429))))]
|
||||||
|
plli2s1_r: _plli2s.r,
|
||||||
|
|
||||||
|
#[cfg(stm32f429)]
|
||||||
|
plli2s1_q: None,
|
||||||
|
#[cfg(stm32f429)]
|
||||||
|
plli2s1_r: None,
|
||||||
|
|
||||||
|
#[cfg(any(stm32f427, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
|
pllsai1_q: _pllsai.q,
|
||||||
|
#[cfg(any(stm32f427, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
|
pllsai1_r: _pllsai.r,
|
||||||
|
|
||||||
|
#[cfg(stm32f429)]
|
||||||
|
pllsai1_q: None,
|
||||||
|
#[cfg(stm32f429)]
|
||||||
|
pllsai1_r: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[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 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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),
|
|
||||||
apb1: Hertz(pclk1),
|
|
||||||
apb2: Hertz(pclk2),
|
|
||||||
|
|
||||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
|
||||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
|
||||||
|
|
||||||
ahb1: Hertz(hclk),
|
|
||||||
ahb2: Hertz(hclk),
|
|
||||||
ahb3: Hertz(hclk),
|
|
||||||
|
|
||||||
pll48: 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;
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
use crate::pac::flash::vals::Latency;
|
use crate::pac::flash::vals::Latency;
|
||||||
use crate::pac::rcc::vals::{self, Sw};
|
use crate::pac::rcc::vals::{self, Sw};
|
||||||
pub use crate::pac::rcc::vals::{
|
pub use crate::pac::rcc::vals::{
|
||||||
Hpre as AHBPrescaler, Hsidiv as HSI16Prescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler,
|
Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler,
|
||||||
};
|
};
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
@ -14,7 +14,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
|||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum ClockSrc {
|
pub enum ClockSrc {
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
HSI16(HSI16Prescaler),
|
HSI(HSIPrescaler),
|
||||||
PLL(PllConfig),
|
PLL(PllConfig),
|
||||||
LSI,
|
LSI,
|
||||||
}
|
}
|
||||||
@ -46,9 +46,9 @@ pub struct PllConfig {
|
|||||||
impl Default for PllConfig {
|
impl Default for PllConfig {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> PllConfig {
|
fn default() -> PllConfig {
|
||||||
// HSI16 / 1 * 8 / 2 = 64 MHz
|
// HSI / 1 * 8 / 2 = 64 MHz
|
||||||
PllConfig {
|
PllConfig {
|
||||||
source: PllSrc::HSI16,
|
source: PllSrc::HSI,
|
||||||
m: Pllm::DIV1,
|
m: Pllm::DIV1,
|
||||||
n: Plln::MUL8,
|
n: Plln::MUL8,
|
||||||
r: Pllr::DIV2,
|
r: Pllr::DIV2,
|
||||||
@ -60,7 +60,7 @@ impl Default for PllConfig {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum PllSrc {
|
pub enum PllSrc {
|
||||||
HSI16,
|
HSI,
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ impl Default for Config {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Config {
|
fn default() -> Config {
|
||||||
Config {
|
Config {
|
||||||
mux: ClockSrc::HSI16(HSI16Prescaler::DIV1),
|
mux: ClockSrc::HSI(HSIPrescaler::DIV1),
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
ahb_pre: AHBPrescaler::DIV1,
|
||||||
apb_pre: APBPrescaler::DIV1,
|
apb_pre: APBPrescaler::DIV1,
|
||||||
low_power_run: false,
|
low_power_run: false,
|
||||||
@ -89,7 +89,7 @@ impl Default for Config {
|
|||||||
impl PllConfig {
|
impl PllConfig {
|
||||||
pub(crate) fn init(self) -> Hertz {
|
pub(crate) fn init(self) -> Hertz {
|
||||||
let (src, input_freq) = match self.source {
|
let (src, input_freq) = match self.source {
|
||||||
PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ),
|
PllSrc::HSI => (vals::Pllsrc::HSI, HSI_FREQ),
|
||||||
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq),
|
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ impl PllConfig {
|
|||||||
// > 3. Change the desired parameter.
|
// > 3. Change the desired parameter.
|
||||||
// Enable whichever clock source we're using, and wait for it to become ready
|
// Enable whichever clock source we're using, and wait for it to become ready
|
||||||
match self.source {
|
match self.source {
|
||||||
PllSrc::HSI16 => {
|
PllSrc::HSI => {
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
RCC.cr().write(|w| w.set_hsion(true));
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
}
|
}
|
||||||
@ -167,8 +167,8 @@ impl PllConfig {
|
|||||||
|
|
||||||
pub(crate) unsafe fn init(config: Config) {
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
let (sys_clk, sw) = match config.mux {
|
let (sys_clk, sw) = match config.mux {
|
||||||
ClockSrc::HSI16(div) => {
|
ClockSrc::HSI(div) => {
|
||||||
// Enable HSI16
|
// Enable HSI
|
||||||
RCC.cr().write(|w| {
|
RCC.cr().write(|w| {
|
||||||
w.set_hsidiv(div);
|
w.set_hsidiv(div);
|
||||||
w.set_hsion(true)
|
w.set_hsion(true)
|
||||||
@ -186,7 +186,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
ClockSrc::PLL(pll) => {
|
ClockSrc::PLL(pll) => {
|
||||||
let freq = pll.init();
|
let freq = pll.init();
|
||||||
(freq, Sw::PLLRCLK)
|
(freq, Sw::PLL1_R)
|
||||||
}
|
}
|
||||||
ClockSrc::LSI => {
|
ClockSrc::LSI => {
|
||||||
// Enable LSI
|
// Enable LSI
|
||||||
@ -275,9 +275,9 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: sys_clk,
|
sys: sys_clk,
|
||||||
ahb1: ahb_freq,
|
hclk1: ahb_freq,
|
||||||
apb1: apb_freq,
|
pclk1: apb_freq,
|
||||||
apb1_tim: apb_tim_freq,
|
pclk1_tim: apb_tim_freq,
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,14 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
|||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum ClockSrc {
|
pub enum ClockSrc {
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
HSI16,
|
HSI,
|
||||||
PLL,
|
PLL,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PLL clock input source
|
/// PLL clock input source
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum PllSrc {
|
pub enum PllSrc {
|
||||||
HSI16,
|
HSI,
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ impl Into<Pllsrc> for PllSrc {
|
|||||||
fn into(self) -> Pllsrc {
|
fn into(self) -> Pllsrc {
|
||||||
match self {
|
match self {
|
||||||
PllSrc::HSE(..) => Pllsrc::HSE,
|
PllSrc::HSE(..) => Pllsrc::HSE,
|
||||||
PllSrc::HSI16 => Pllsrc::HSI16,
|
PllSrc::HSI => Pllsrc::HSI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,15 +112,15 @@ impl Default for Config {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Config {
|
fn default() -> Config {
|
||||||
Config {
|
Config {
|
||||||
mux: ClockSrc::HSI16,
|
mux: ClockSrc::HSI,
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
ahb_pre: AHBPrescaler::DIV1,
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
apb1_pre: APBPrescaler::DIV1,
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
apb2_pre: APBPrescaler::DIV1,
|
||||||
low_power_run: false,
|
low_power_run: false,
|
||||||
pll: None,
|
pll: None,
|
||||||
clock_48mhz_src: None,
|
clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(None)),
|
||||||
adc12_clock_source: Adcsel::NOCLK,
|
adc12_clock_source: Adcsel::DISABLE,
|
||||||
adc345_clock_source: Adcsel::NOCLK,
|
adc345_clock_source: Adcsel::DISABLE,
|
||||||
ls: Default::default(),
|
ls: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ pub struct PllFreq {
|
|||||||
pub(crate) unsafe fn init(config: Config) {
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
let pll_freq = config.pll.map(|pll_config| {
|
let pll_freq = config.pll.map(|pll_config| {
|
||||||
let src_freq = match pll_config.source {
|
let src_freq = match pll_config.source {
|
||||||
PllSrc::HSI16 => {
|
PllSrc::HSI => {
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
RCC.cr().write(|w| w.set_hsion(true));
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
@ -196,12 +196,12 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let (sys_clk, sw) = match config.mux {
|
let (sys_clk, sw) = match config.mux {
|
||||||
ClockSrc::HSI16 => {
|
ClockSrc::HSI => {
|
||||||
// Enable HSI16
|
// Enable HSI
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
RCC.cr().write(|w| w.set_hsion(true));
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
(HSI_FREQ, Sw::HSI16)
|
(HSI_FREQ, Sw::HSI)
|
||||||
}
|
}
|
||||||
ClockSrc::HSE(freq) => {
|
ClockSrc::HSE(freq) => {
|
||||||
// Enable HSE
|
// Enable HSE
|
||||||
@ -249,7 +249,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(Hertz(freq), Sw::PLLRCLK)
|
(Hertz(freq), Sw::PLL1_R)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
|
let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
|
||||||
assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
|
assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
|
||||||
|
|
||||||
crate::pac::rcc::vals::Clk48sel::PLLQCLK
|
crate::pac::rcc::vals::Clk48sel::PLL1_Q
|
||||||
}
|
}
|
||||||
Clock48MhzSrc::Hsi48(crs_config) => {
|
Clock48MhzSrc::Hsi48(crs_config) => {
|
||||||
// Enable HSI48
|
// Enable HSI48
|
||||||
@ -326,16 +326,16 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source));
|
RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source));
|
||||||
|
|
||||||
let adc12_ck = match config.adc12_clock_source {
|
let adc12_ck = match config.adc12_clock_source {
|
||||||
AdcClockSource::NOCLK => None,
|
AdcClockSource::DISABLE => None,
|
||||||
AdcClockSource::PLLP => pll_freq.as_ref().unwrap().pll_p,
|
AdcClockSource::PLL1_P => pll_freq.as_ref().unwrap().pll_p,
|
||||||
AdcClockSource::SYSCLK => Some(sys_clk),
|
AdcClockSource::SYS => Some(sys_clk),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let adc345_ck = match config.adc345_clock_source {
|
let adc345_ck = match config.adc345_clock_source {
|
||||||
AdcClockSource::NOCLK => None,
|
AdcClockSource::DISABLE => None,
|
||||||
AdcClockSource::PLLP => pll_freq.as_ref().unwrap().pll_p,
|
AdcClockSource::PLL1_P => pll_freq.as_ref().unwrap().pll_p,
|
||||||
AdcClockSource::SYSCLK => Some(sys_clk),
|
AdcClockSource::SYS => Some(sys_clk),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -348,14 +348,15 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: sys_clk,
|
sys: sys_clk,
|
||||||
ahb1: ahb_freq,
|
hclk1: ahb_freq,
|
||||||
ahb2: ahb_freq,
|
hclk2: ahb_freq,
|
||||||
apb1: apb1_freq,
|
pclk1: apb1_freq,
|
||||||
apb1_tim: apb1_tim_freq,
|
pclk1_tim: apb1_tim_freq,
|
||||||
apb2: apb2_freq,
|
pclk2: apb2_freq,
|
||||||
apb2_tim: apb2_tim_freq,
|
pclk2_tim: apb2_tim_freq,
|
||||||
adc: adc12_ck,
|
adc: adc12_ck,
|
||||||
adc34: adc345_ck,
|
adc34: adc345_ck,
|
||||||
|
pll1_p: None,
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,11 @@ use crate::pac::pwr::vals::Vos;
|
|||||||
pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource;
|
pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource;
|
||||||
#[cfg(stm32h7)]
|
#[cfg(stm32h7)]
|
||||||
pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
|
pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
|
||||||
use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre};
|
pub use crate::pac::rcc::vals::{
|
||||||
pub use crate::pac::rcc::vals::{Ckpersel as PerClockSource, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul};
|
Ckpersel as PerClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
|
||||||
|
Pllsrc as PllSource, Sw as Sysclk,
|
||||||
|
};
|
||||||
|
use crate::pac::rcc::vals::{Ckpersel, Pllrge, Pllvcosel, Timpre};
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
@ -58,50 +61,9 @@ 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)]
|
|
||||||
pub enum Hsi {
|
|
||||||
/// 64Mhz
|
|
||||||
Mhz64,
|
|
||||||
/// 32Mhz (divided by 2)
|
|
||||||
Mhz32,
|
|
||||||
/// 16Mhz (divided by 4)
|
|
||||||
Mhz16,
|
|
||||||
/// 8Mhz (divided by 8)
|
|
||||||
Mhz8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum Sysclk {
|
|
||||||
/// HSI selected as sysclk
|
|
||||||
HSI,
|
|
||||||
/// HSE selected as sysclk
|
|
||||||
HSE,
|
|
||||||
/// CSI selected as sysclk
|
|
||||||
CSI,
|
|
||||||
/// PLL1_P selected as sysclk
|
|
||||||
Pll1P,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum PllSource {
|
|
||||||
Hsi,
|
|
||||||
Csi,
|
|
||||||
Hse,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Pll {
|
pub struct Pll {
|
||||||
/// Source clock selection.
|
/// Source clock selection.
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub source: PllSource,
|
pub source: PllSource,
|
||||||
|
|
||||||
/// PLL pre-divider (DIVM).
|
/// PLL pre-divider (DIVM).
|
||||||
@ -161,19 +123,12 @@ impl From<TimerPrescaler> for Timpre {
|
|||||||
/// Configuration of the core clocks
|
/// Configuration of the core clocks
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub hsi: Option<Hsi>,
|
pub hsi: Option<HSIPrescaler>,
|
||||||
pub hse: Option<Hse>,
|
pub hse: Option<Hse>,
|
||||||
#[cfg(stm32h7)]
|
|
||||||
pub lse: Option<Lse>,
|
|
||||||
#[cfg(stm32h7)]
|
|
||||||
pub lsi: bool,
|
|
||||||
pub csi: bool,
|
pub csi: bool,
|
||||||
pub hsi48: bool,
|
pub hsi48: bool,
|
||||||
pub sys: Sysclk,
|
pub sys: Sysclk,
|
||||||
|
|
||||||
#[cfg(stm32h7)]
|
|
||||||
pub pll_src: PllSource,
|
|
||||||
|
|
||||||
pub pll1: Option<Pll>,
|
pub pll1: Option<Pll>,
|
||||||
pub pll2: Option<Pll>,
|
pub pll2: Option<Pll>,
|
||||||
#[cfg(any(rcc_h5, stm32h7))]
|
#[cfg(any(rcc_h5, stm32h7))]
|
||||||
@ -197,17 +152,11 @@ pub struct Config {
|
|||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
hsi: Some(Hsi::Mhz64),
|
hsi: Some(HSIPrescaler::DIV1),
|
||||||
hse: None,
|
hse: None,
|
||||||
#[cfg(stm32h7)]
|
|
||||||
lse: None,
|
|
||||||
#[cfg(stm32h7)]
|
|
||||||
lsi: false,
|
|
||||||
csi: false,
|
csi: false,
|
||||||
hsi48: false,
|
hsi48: false,
|
||||||
sys: Sysclk::HSI,
|
sys: Sysclk::HSI,
|
||||||
#[cfg(stm32h7)]
|
|
||||||
pll_src: PllSource::Hsi,
|
|
||||||
pll1: None,
|
pll1: None,
|
||||||
pll2: None,
|
pll2: None,
|
||||||
#[cfg(any(rcc_h5, stm32h7))]
|
#[cfg(any(rcc_h5, stm32h7))]
|
||||||
@ -320,19 +269,13 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
RCC.cr().modify(|w| w.set_hsion(false));
|
RCC.cr().modify(|w| w.set_hsion(false));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Some(hsi) => {
|
Some(hsidiv) => {
|
||||||
let (freq, hsidiv) = match hsi {
|
|
||||||
Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
|
|
||||||
Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
|
|
||||||
Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
|
|
||||||
Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
|
|
||||||
};
|
|
||||||
RCC.cr().modify(|w| {
|
RCC.cr().modify(|w| {
|
||||||
w.set_hsidiv(hsidiv);
|
w.set_hsidiv(hsidiv);
|
||||||
w.set_hsion(true);
|
w.set_hsion(true);
|
||||||
});
|
});
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
Some(freq)
|
Some(HSI_FREQ / hsidiv)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -377,25 +320,29 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configure PLLs.
|
// H7 has shared PLLSRC, check it's equal in all PLLs.
|
||||||
let pll_input = PllInput {
|
|
||||||
csi,
|
|
||||||
hse,
|
|
||||||
hsi,
|
|
||||||
#[cfg(stm32h7)]
|
#[cfg(stm32h7)]
|
||||||
source: config.pll_src,
|
{
|
||||||
|
let plls = [&config.pll1, &config.pll2, &config.pll3];
|
||||||
|
if !super::util::all_equal(plls.into_iter().flatten().map(|p| p.source)) {
|
||||||
|
panic!("Source must be equal across all enabled PLLs.")
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure PLLs.
|
||||||
|
let pll_input = PllInput { csi, hse, hsi };
|
||||||
let pll1 = init_pll(0, config.pll1, &pll_input);
|
let pll1 = init_pll(0, config.pll1, &pll_input);
|
||||||
let pll2 = init_pll(1, config.pll2, &pll_input);
|
let pll2 = init_pll(1, config.pll2, &pll_input);
|
||||||
#[cfg(any(rcc_h5, stm32h7))]
|
#[cfg(any(rcc_h5, stm32h7))]
|
||||||
let pll3 = init_pll(2, config.pll3, &pll_input);
|
let pll3 = init_pll(2, config.pll3, &pll_input);
|
||||||
|
|
||||||
// Configure sysclk
|
// Configure sysclk
|
||||||
let (sys, sw) = match config.sys {
|
let sys = match config.sys {
|
||||||
Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
|
Sysclk::HSI => unwrap!(hsi),
|
||||||
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
|
Sysclk::HSE => unwrap!(hse),
|
||||||
Sysclk::CSI => (unwrap!(csi), Sw::CSI),
|
Sysclk::CSI => unwrap!(csi),
|
||||||
Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
|
Sysclk::PLL1_P => unwrap!(pll1.p),
|
||||||
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check limits.
|
// Check limits.
|
||||||
@ -406,7 +353,14 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)),
|
VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)),
|
||||||
VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)),
|
VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)),
|
||||||
};
|
};
|
||||||
#[cfg(stm32h7)]
|
#[cfg(pwr_h7rm0455)]
|
||||||
|
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
|
||||||
|
VoltageScale::Scale0 => (Hertz(280_000_000), Hertz(280_000_000), Hertz(140_000_000)),
|
||||||
|
VoltageScale::Scale1 => (Hertz(225_000_000), Hertz(225_000_000), Hertz(112_500_000)),
|
||||||
|
VoltageScale::Scale2 => (Hertz(160_000_000), Hertz(160_000_000), Hertz(80_000_000)),
|
||||||
|
VoltageScale::Scale3 => (Hertz(88_000_000), Hertz(88_000_000), Hertz(44_000_000)),
|
||||||
|
};
|
||||||
|
#[cfg(all(stm32h7, not(pwr_h7rm0455)))]
|
||||||
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
|
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
|
||||||
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
|
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
|
||||||
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
|
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
|
||||||
@ -453,8 +407,8 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
};
|
};
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
let adc = match config.adc_clock_source {
|
let adc = match config.adc_clock_source {
|
||||||
AdcClockSource::HCLK => Some(hclk),
|
AdcClockSource::HCLK1 => Some(hclk),
|
||||||
AdcClockSource::SYSCLK => Some(sys),
|
AdcClockSource::SYS => Some(sys),
|
||||||
AdcClockSource::PLL2_R => pll2.r,
|
AdcClockSource::PLL2_R => pll2.r,
|
||||||
AdcClockSource::HSE => hse,
|
AdcClockSource::HSE => hse,
|
||||||
AdcClockSource::HSI => hsi,
|
AdcClockSource::HSI => hsi,
|
||||||
@ -512,8 +466,8 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
|
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
RCC.cfgr().modify(|w| w.set_sw(config.sys));
|
||||||
while RCC.cfgr().read().sws() != sw {}
|
while RCC.cfgr().read().sws() != config.sys {}
|
||||||
|
|
||||||
// IO compensation cell - Requires CSI clock and SYSCFG
|
// IO compensation cell - Requires CSI clock and SYSCFG
|
||||||
#[cfg(stm32h7)] // TODO h5
|
#[cfg(stm32h7)] // TODO h5
|
||||||
@ -532,70 +486,65 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys,
|
sys,
|
||||||
ahb1: hclk,
|
hclk1: hclk,
|
||||||
ahb2: hclk,
|
hclk2: hclk,
|
||||||
ahb3: hclk,
|
hclk3: hclk,
|
||||||
ahb4: hclk,
|
hclk4: hclk,
|
||||||
apb1,
|
pclk1: apb1,
|
||||||
apb2,
|
pclk2: apb2,
|
||||||
apb3,
|
pclk3: apb3,
|
||||||
#[cfg(stm32h7)]
|
#[cfg(stm32h7)]
|
||||||
apb4,
|
pclk4: apb4,
|
||||||
apb1_tim,
|
#[cfg(stm32h5)]
|
||||||
apb2_tim,
|
pclk4: Hertz(1),
|
||||||
|
pclk1_tim: apb1_tim,
|
||||||
|
pclk2_tim: apb2_tim,
|
||||||
adc,
|
adc,
|
||||||
rtc,
|
rtc,
|
||||||
|
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
hsi: None,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
mux_apb1: Some(apb1),
|
hsi48: None,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
mux_apb2: Some(apb2),
|
lsi: None,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
mux_apb3: Some(apb3),
|
csi: None,
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_apb4: None,
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
lse: None,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
hse: None,
|
||||||
|
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pll1_q: pll1.q,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pll2_p: pll2.p,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pll2_q: pll2.q,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pll2_r: pll2.r,
|
||||||
|
#[cfg(any(rcc_h5, stm32h7))]
|
||||||
|
pll3_p: pll3.p,
|
||||||
|
#[cfg(any(rcc_h5, stm32h7))]
|
||||||
|
pll3_q: pll3.q,
|
||||||
|
#[cfg(any(rcc_h5, stm32h7))]
|
||||||
|
pll3_r: pll3.r,
|
||||||
|
|
||||||
|
#[cfg(rcc_h50)]
|
||||||
|
pll3_p: None,
|
||||||
|
#[cfg(rcc_h50)]
|
||||||
|
pll3_q: None,
|
||||||
|
#[cfg(rcc_h50)]
|
||||||
|
pll3_r: None,
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
mux_rcc_hclk4: None,
|
audioclk: None,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
per: None,
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h7)]
|
||||||
mux_pll2_q: None,
|
rcc_pclk_d3: None,
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_pll3_q: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_hsi: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_csi: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_lse: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_pll1_q: pll1.q,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_pll2_p: pll2.p,
|
|
||||||
#[cfg(rcc_h5)]
|
|
||||||
mux_pll3_p: pll3.p,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_audioclk: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_per: None,
|
|
||||||
|
|
||||||
#[cfg(rcc_h5)]
|
|
||||||
mux_pll3_r: pll3.r,
|
|
||||||
#[cfg(all(not(rcc_h5), stm32h5))]
|
|
||||||
mux_pll3_r: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_pll3_1: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_hsi48_ker: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_lsi: None,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_pll2_r: pll2.r,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_hse: hse,
|
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
mux_hsi48: None,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,8 +552,6 @@ struct PllInput {
|
|||||||
hsi: Option<Hertz>,
|
hsi: Option<Hertz>,
|
||||||
hse: Option<Hertz>,
|
hse: Option<Hertz>,
|
||||||
csi: Option<Hertz>,
|
csi: Option<Hertz>,
|
||||||
#[cfg(stm32h7)]
|
|
||||||
source: PllSource,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PllOutput {
|
struct PllOutput {
|
||||||
@ -634,15 +581,11 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
let in_clk = match config.source {
|
||||||
let source = config.source;
|
PllSource::DISABLE => panic!("must not set PllSource::Disable"),
|
||||||
#[cfg(stm32h7)]
|
PllSource::HSI => unwrap!(input.hsi),
|
||||||
let source = input.source;
|
PllSource::HSE => unwrap!(input.hse),
|
||||||
|
PllSource::CSI => unwrap!(input.csi),
|
||||||
let (in_clk, src) = match source {
|
|
||||||
PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
|
|
||||||
PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
|
|
||||||
PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let ref_clk = in_clk / config.prediv as u32;
|
let ref_clk = in_clk / config.prediv as u32;
|
||||||
@ -682,7 +625,7 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
|
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
RCC.pllcfgr(num).write(|w| {
|
RCC.pllcfgr(num).write(|w| {
|
||||||
w.set_pllsrc(src);
|
w.set_pllsrc(config.source);
|
||||||
w.set_divm(config.prediv);
|
w.set_divm(config.prediv);
|
||||||
w.set_pllvcosel(vco_range);
|
w.set_pllvcosel(vco_range);
|
||||||
w.set_pllrge(ref_range);
|
w.set_pllrge(ref_range);
|
||||||
@ -696,7 +639,7 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
{
|
{
|
||||||
RCC.pllckselr().modify(|w| {
|
RCC.pllckselr().modify(|w| {
|
||||||
w.set_divm(num, config.prediv);
|
w.set_divm(num, config.prediv);
|
||||||
w.set_pllsrc(src);
|
w.set_pllsrc(config.source);
|
||||||
});
|
});
|
||||||
RCC.pllcfgr().modify(|w| {
|
RCC.pllcfgr().modify(|w| {
|
||||||
w.set_pllvcosel(num, vco_range);
|
w.set_pllvcosel(num, vco_range);
|
||||||
|
@ -18,20 +18,20 @@ pub enum ClockSrc {
|
|||||||
MSI(MSIRange),
|
MSI(MSIRange),
|
||||||
PLL(PLLSource, PLLMul, PLLDiv),
|
PLL(PLLSource, PLLMul, PLLDiv),
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
HSI16,
|
HSI,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PLL clock input source
|
/// PLL clock input source
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum PLLSource {
|
pub enum PLLSource {
|
||||||
HSI16,
|
HSI,
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
}
|
}
|
||||||
|
|
||||||
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::HSI => Pllsrc::HSI,
|
||||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
PLLSource::HSE(_) => Pllsrc::HSE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,12 +83,12 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
let freq = 32_768 * (1 << (range as u8 + 1));
|
let freq = 32_768 * (1 << (range as u8 + 1));
|
||||||
(Hertz(freq), Sw::MSI)
|
(Hertz(freq), Sw::MSI)
|
||||||
}
|
}
|
||||||
ClockSrc::HSI16 => {
|
ClockSrc::HSI => {
|
||||||
// Enable HSI16
|
// Enable HSI
|
||||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
RCC.cr().write(|w| w.set_hsion(true));
|
||||||
while !RCC.cr().read().hsi16rdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
(HSI_FREQ, Sw::HSI16)
|
(HSI_FREQ, Sw::HSI)
|
||||||
}
|
}
|
||||||
ClockSrc::HSE(freq) => {
|
ClockSrc::HSE(freq) => {
|
||||||
// Enable HSE
|
// Enable HSE
|
||||||
@ -105,10 +105,10 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
while !RCC.cr().read().hserdy() {}
|
while !RCC.cr().read().hserdy() {}
|
||||||
freq
|
freq
|
||||||
}
|
}
|
||||||
PLLSource::HSI16 => {
|
PLLSource::HSI => {
|
||||||
// Enable HSI
|
// Enable HSI
|
||||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
RCC.cr().write(|w| w.set_hsion(true));
|
||||||
while !RCC.cr().read().hsi16rdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
HSI_FREQ
|
HSI_FREQ
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -156,23 +156,9 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
w.set_ppre2(config.apb2_pre);
|
w.set_ppre2(config.apb2_pre);
|
||||||
});
|
});
|
||||||
|
|
||||||
let ahb_freq = sys_clk / config.ahb_pre;
|
let hclk1 = sys_clk / config.ahb_pre;
|
||||||
|
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
|
||||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(crs)]
|
#[cfg(crs)]
|
||||||
if config.enable_hsi48 {
|
if config.enable_hsi48 {
|
||||||
@ -209,11 +195,11 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: sys_clk,
|
sys: sys_clk,
|
||||||
ahb1: ahb_freq,
|
hclk1,
|
||||||
apb1: apb1_freq,
|
pclk1,
|
||||||
apb2: apb2_freq,
|
pclk2,
|
||||||
apb1_tim: apb1_tim_freq,
|
pclk1_tim,
|
||||||
apb2_tim: apb2_tim_freq,
|
pclk2_tim,
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,296 +0,0 @@
|
|||||||
use crate::pac::rcc::regs::Cfgr;
|
|
||||||
pub use crate::pac::rcc::vals::{
|
|
||||||
Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv,
|
|
||||||
Pllr as PllRDiv, Ppre as APBPrescaler,
|
|
||||||
};
|
|
||||||
use crate::pac::rcc::vals::{Msirange, Pllsrc, Sw};
|
|
||||||
use crate::pac::{FLASH, RCC};
|
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
|
||||||
use crate::time::Hertz;
|
|
||||||
|
|
||||||
/// HSI speed
|
|
||||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
MSI(MSIRange),
|
|
||||||
PLL(PLLSource, PllRDiv, PllPreDiv, PllMul, Option<PllQDiv>),
|
|
||||||
HSE(Hertz),
|
|
||||||
HSI16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PLL clock input source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PLLSource {
|
|
||||||
HSI16,
|
|
||||||
HSE(Hertz),
|
|
||||||
MSI(MSIRange),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PLLSource> for Pllsrc {
|
|
||||||
fn from(val: PLLSource) -> Pllsrc {
|
|
||||||
match val {
|
|
||||||
PLLSource::HSI16 => Pllsrc::HSI16,
|
|
||||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
|
||||||
PLLSource::MSI(_) => Pllsrc::MSI,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
pub pllsai1: Option<(PllMul, PllPreDiv, Option<PllRDiv>, Option<PllQDiv>, Option<PllPDiv>)>,
|
|
||||||
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
|
|
||||||
pub hsi48: bool,
|
|
||||||
pub ls: super::LsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::MSI(MSIRange::RANGE4M),
|
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
|
||||||
pllsai1: None,
|
|
||||||
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
|
|
||||||
hsi48: false,
|
|
||||||
ls: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) unsafe fn init(config: Config) {
|
|
||||||
// Switch to MSI to prevent problems with PLL configuration.
|
|
||||||
if !RCC.cr().read().msion() {
|
|
||||||
// Turn on MSI and configure it to 4MHz.
|
|
||||||
RCC.cr().modify(|w| {
|
|
||||||
w.set_msirgsel(true); // MSI Range is provided by MSIRANGE[3:0].
|
|
||||||
w.set_msirange(MSIRange::RANGE4M);
|
|
||||||
w.set_msipllen(false);
|
|
||||||
w.set_msion(true)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait until MSI is running
|
|
||||||
while !RCC.cr().read().msirdy() {}
|
|
||||||
}
|
|
||||||
if RCC.cfgr().read().sws() != Sw::MSI {
|
|
||||||
// Set MSI as a clock source, reset prescalers.
|
|
||||||
RCC.cfgr().write_value(Cfgr::default());
|
|
||||||
// Wait for clock switch status bits to change.
|
|
||||||
while RCC.cfgr().read().sws() != Sw::MSI {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rtc = config.ls.init();
|
|
||||||
|
|
||||||
let (sys_clk, sw) = match config.mux {
|
|
||||||
ClockSrc::MSI(range) => {
|
|
||||||
// Enable MSI
|
|
||||||
RCC.cr().write(|w| {
|
|
||||||
w.set_msirange(range);
|
|
||||||
w.set_msirgsel(true);
|
|
||||||
w.set_msion(true);
|
|
||||||
|
|
||||||
// If LSE is enabled, enable calibration of MSI
|
|
||||||
w.set_msipllen(config.ls.lse.is_some());
|
|
||||||
});
|
|
||||||
while !RCC.cr().read().msirdy() {}
|
|
||||||
|
|
||||||
// Enable as clock source for USB, RNG if running at 48 MHz
|
|
||||||
if range == MSIRange::RANGE48M {
|
|
||||||
RCC.ccipr().modify(|w| {
|
|
||||||
w.set_clk48sel(0b11);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(msirange_to_hertz(range), Sw::MSI)
|
|
||||||
}
|
|
||||||
ClockSrc::HSI16 => {
|
|
||||||
// Enable HSI16
|
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
|
||||||
|
|
||||||
(HSI_FREQ, Sw::HSI16)
|
|
||||||
}
|
|
||||||
ClockSrc::HSE(freq) => {
|
|
||||||
// Enable HSE
|
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
|
|
||||||
(freq, Sw::HSE)
|
|
||||||
}
|
|
||||||
ClockSrc::PLL(src, divr, prediv, mul, divq) => {
|
|
||||||
let src_freq = match src {
|
|
||||||
PLLSource::HSE(freq) => {
|
|
||||||
// Enable HSE
|
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
freq
|
|
||||||
}
|
|
||||||
PLLSource::HSI16 => {
|
|
||||||
// Enable HSI
|
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
|
||||||
HSI_FREQ
|
|
||||||
}
|
|
||||||
PLLSource::MSI(range) => {
|
|
||||||
// Enable MSI
|
|
||||||
RCC.cr().write(|w| {
|
|
||||||
w.set_msirange(range);
|
|
||||||
w.set_msipllen(false); // should be turned on if LSE is started
|
|
||||||
w.set_msirgsel(true);
|
|
||||||
w.set_msion(true);
|
|
||||||
});
|
|
||||||
while !RCC.cr().read().msirdy() {}
|
|
||||||
|
|
||||||
msirange_to_hertz(range)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Disable PLL
|
|
||||||
RCC.cr().modify(|w| w.set_pllon(false));
|
|
||||||
while RCC.cr().read().pllrdy() {}
|
|
||||||
|
|
||||||
let freq = src_freq / prediv * mul / divr;
|
|
||||||
|
|
||||||
#[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))]
|
|
||||||
assert!(freq.0 <= 120_000_000);
|
|
||||||
#[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))]
|
|
||||||
assert!(freq.0 <= 80_000_000);
|
|
||||||
|
|
||||||
RCC.pllcfgr().write(move |w| {
|
|
||||||
w.set_plln(mul);
|
|
||||||
w.set_pllm(prediv);
|
|
||||||
w.set_pllr(divr);
|
|
||||||
if let Some(divq) = divq {
|
|
||||||
w.set_pllq(divq);
|
|
||||||
w.set_pllqen(true);
|
|
||||||
}
|
|
||||||
w.set_pllsrc(src.into());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enable as clock source for USB, RNG if PLL48 divisor is provided
|
|
||||||
if let Some(divq) = divq {
|
|
||||||
let freq = src_freq / prediv * mul / divq;
|
|
||||||
assert!(freq.0 == 48_000_000);
|
|
||||||
RCC.ccipr().modify(|w| {
|
|
||||||
w.set_clk48sel(0b10);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 {
|
|
||||||
RCC.pllsai1cfgr().write(move |w| {
|
|
||||||
w.set_plln(mul);
|
|
||||||
w.set_pllm(prediv);
|
|
||||||
if let Some(r_div) = r_div {
|
|
||||||
w.set_pllr(r_div);
|
|
||||||
w.set_pllren(true);
|
|
||||||
}
|
|
||||||
if let Some(q_div) = q_div {
|
|
||||||
w.set_pllq(q_div);
|
|
||||||
w.set_pllqen(true);
|
|
||||||
let freq = src_freq / prediv * mul / q_div;
|
|
||||||
if freq.0 == 48_000_000 {
|
|
||||||
RCC.ccipr().modify(|w| {
|
|
||||||
w.set_clk48sel(0b1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(p_div) = p_div {
|
|
||||||
w.set_pllp(p_div);
|
|
||||||
w.set_pllpen(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
RCC.cr().modify(|w| w.set_pllsai1on(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable PLL
|
|
||||||
RCC.cr().modify(|w| w.set_pllon(true));
|
|
||||||
while !RCC.cr().read().pllrdy() {}
|
|
||||||
RCC.pllcfgr().modify(|w| w.set_pllren(true));
|
|
||||||
|
|
||||||
(freq, Sw::PLL)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
|
|
||||||
if config.hsi48 {
|
|
||||||
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
|
||||||
while !RCC.crrcr().read().hsi48rdy() {}
|
|
||||||
|
|
||||||
// Enable as clock source for USB, RNG and SDMMC
|
|
||||||
RCC.ccipr().modify(|w| w.set_clk48sel(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set flash wait states
|
|
||||||
FLASH.acr().modify(|w| {
|
|
||||||
w.set_latency(match sys_clk.0 {
|
|
||||||
0..=16_000_000 => 0,
|
|
||||||
0..=32_000_000 => 1,
|
|
||||||
0..=48_000_000 => 2,
|
|
||||||
0..=64_000_000 => 3,
|
|
||||||
_ => 4,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
|
||||||
w.set_sw(sw);
|
|
||||||
w.set_hpre(config.ahb_pre);
|
|
||||||
w.set_ppre1(config.apb1_pre);
|
|
||||||
w.set_ppre2(config.apb2_pre);
|
|
||||||
});
|
|
||||||
|
|
||||||
let ahb_freq = sys_clk / config.ahb_pre;
|
|
||||||
|
|
||||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
set_freqs(Clocks {
|
|
||||||
sys: sys_clk,
|
|
||||||
ahb1: ahb_freq,
|
|
||||||
ahb2: ahb_freq,
|
|
||||||
ahb3: ahb_freq,
|
|
||||||
apb1: apb1_freq,
|
|
||||||
apb2: apb2_freq,
|
|
||||||
apb1_tim: apb1_tim_freq,
|
|
||||||
apb2_tim: apb2_tim_freq,
|
|
||||||
rtc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn msirange_to_hertz(range: Msirange) -> Hertz {
|
|
||||||
match range {
|
|
||||||
MSIRange::RANGE100K => Hertz(100_000),
|
|
||||||
MSIRange::RANGE200K => Hertz(200_000),
|
|
||||||
MSIRange::RANGE400K => Hertz(400_000),
|
|
||||||
MSIRange::RANGE800K => Hertz(800_000),
|
|
||||||
MSIRange::RANGE1M => Hertz(1_000_000),
|
|
||||||
MSIRange::RANGE2M => Hertz(2_000_000),
|
|
||||||
MSIRange::RANGE4M => Hertz(4_000_000),
|
|
||||||
MSIRange::RANGE8M => Hertz(8_000_000),
|
|
||||||
MSIRange::RANGE16M => Hertz(16_000_000),
|
|
||||||
MSIRange::RANGE24M => Hertz(24_000_000),
|
|
||||||
MSIRange::RANGE32M => Hertz(32_000_000),
|
|
||||||
MSIRange::RANGE48M => Hertz(48_000_000),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
523
embassy-stm32/src/rcc/l4l5.rs
Normal file
523
embassy-stm32/src/rcc/l4l5.rs
Normal file
@ -0,0 +1,523 @@
|
|||||||
|
use crate::pac::rcc::regs::Cfgr;
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
pub use crate::pac::rcc::vals::Clk48sel as Clk48Src;
|
||||||
|
#[cfg(any(stm32wb, stm32wl))]
|
||||||
|
pub use crate::pac::rcc::vals::Hsepre as HsePrescaler;
|
||||||
|
pub use crate::pac::rcc::vals::{
|
||||||
|
Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv,
|
||||||
|
Pllr as PllRDiv, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc,
|
||||||
|
};
|
||||||
|
use crate::pac::{FLASH, RCC};
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
/// HSI speed
|
||||||
|
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, 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,
|
||||||
|
/// HSE prescaler
|
||||||
|
#[cfg(any(stm32wb, stm32wl))]
|
||||||
|
pub prescaler: HsePrescaler,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Pll {
|
||||||
|
/// PLL source
|
||||||
|
pub source: PLLSource,
|
||||||
|
|
||||||
|
/// PLL pre-divider (DIVM).
|
||||||
|
pub prediv: PllPreDiv,
|
||||||
|
|
||||||
|
/// PLL multiplication factor.
|
||||||
|
pub mul: PllMul,
|
||||||
|
|
||||||
|
/// PLL P division factor. If None, PLL P output is disabled.
|
||||||
|
pub divp: Option<PllPDiv>,
|
||||||
|
/// PLL Q division factor. If None, PLL Q output is disabled.
|
||||||
|
pub divq: Option<PllQDiv>,
|
||||||
|
/// PLL R division factor. If None, PLL R output is disabled.
|
||||||
|
pub divr: Option<PllRDiv>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
pub struct Config {
|
||||||
|
// base clock sources
|
||||||
|
pub msi: Option<MSIRange>,
|
||||||
|
pub hsi: bool,
|
||||||
|
pub hse: Option<Hse>,
|
||||||
|
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
|
||||||
|
pub hsi48: bool,
|
||||||
|
|
||||||
|
// pll
|
||||||
|
pub pll: Option<Pll>,
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
pub pllsai1: Option<Pll>,
|
||||||
|
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
|
||||||
|
pub pllsai2: Option<Pll>,
|
||||||
|
|
||||||
|
// sysclk, buses.
|
||||||
|
pub mux: ClockSrc,
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb1_pre: APBPrescaler,
|
||||||
|
pub apb2_pre: APBPrescaler,
|
||||||
|
#[cfg(any(stm32wl5x, stm32wb))]
|
||||||
|
pub core2_ahb_pre: AHBPrescaler,
|
||||||
|
#[cfg(any(stm32wl, stm32wb))]
|
||||||
|
pub shared_ahb_pre: AHBPrescaler,
|
||||||
|
|
||||||
|
// muxes
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
pub clk48_src: Clk48Src,
|
||||||
|
|
||||||
|
// low speed LSI/LSE/RTC
|
||||||
|
pub ls: super::LsConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
hse: None,
|
||||||
|
hsi: false,
|
||||||
|
msi: Some(MSIRange::RANGE4M),
|
||||||
|
mux: ClockSrc::MSI,
|
||||||
|
ahb_pre: AHBPrescaler::DIV1,
|
||||||
|
apb1_pre: APBPrescaler::DIV1,
|
||||||
|
apb2_pre: APBPrescaler::DIV1,
|
||||||
|
#[cfg(any(stm32wl5x, stm32wb))]
|
||||||
|
core2_ahb_pre: AHBPrescaler::DIV1,
|
||||||
|
#[cfg(any(stm32wl, stm32wb))]
|
||||||
|
shared_ahb_pre: AHBPrescaler::DIV1,
|
||||||
|
pll: None,
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
pllsai1: None,
|
||||||
|
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
|
||||||
|
pllsai2: None,
|
||||||
|
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
|
||||||
|
hsi48: true,
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
clk48_src: Clk48Src::HSI48,
|
||||||
|
ls: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(stm32wb)]
|
||||||
|
pub const WPAN_DEFAULT: Config = Config {
|
||||||
|
hse: Some(Hse {
|
||||||
|
freq: Hertz(32_000_000),
|
||||||
|
mode: HseMode::Oscillator,
|
||||||
|
prescaler: HsePrescaler::DIV1,
|
||||||
|
}),
|
||||||
|
mux: ClockSrc::PLL1_R,
|
||||||
|
hsi48: true,
|
||||||
|
msi: None,
|
||||||
|
hsi: false,
|
||||||
|
clk48_src: Clk48Src::PLL1_Q,
|
||||||
|
|
||||||
|
ls: super::LsConfig::default_lse(),
|
||||||
|
|
||||||
|
pll: Some(Pll {
|
||||||
|
source: PLLSource::HSE,
|
||||||
|
prediv: PllPreDiv::DIV2,
|
||||||
|
mul: PllMul::MUL12,
|
||||||
|
divp: Some(PllPDiv::DIV3), // 32 / 2 * 12 / 3 = 64Mhz
|
||||||
|
divq: Some(PllQDiv::DIV4), // 32 / 2 * 12 / 4 = 48Mhz
|
||||||
|
divr: Some(PllRDiv::DIV3), // 32 / 2 * 12 / 3 = 64Mhz
|
||||||
|
}),
|
||||||
|
pllsai1: None,
|
||||||
|
|
||||||
|
ahb_pre: AHBPrescaler::DIV1,
|
||||||
|
core2_ahb_pre: AHBPrescaler::DIV2,
|
||||||
|
shared_ahb_pre: AHBPrescaler::DIV1,
|
||||||
|
apb1_pre: APBPrescaler::DIV1,
|
||||||
|
apb2_pre: APBPrescaler::DIV1,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
// Switch to MSI to prevent problems with PLL configuration.
|
||||||
|
if !RCC.cr().read().msion() {
|
||||||
|
// Turn on MSI and configure it to 4MHz.
|
||||||
|
RCC.cr().modify(|w| {
|
||||||
|
#[cfg(not(stm32wb))]
|
||||||
|
w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
|
||||||
|
w.set_msirange(MSIRange::RANGE4M);
|
||||||
|
w.set_msipllen(false);
|
||||||
|
w.set_msion(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait until MSI is running
|
||||||
|
while !RCC.cr().read().msirdy() {}
|
||||||
|
}
|
||||||
|
if RCC.cfgr().read().sws() != ClockSrc::MSI {
|
||||||
|
// Set MSI as a clock source, reset prescalers.
|
||||||
|
RCC.cfgr().write_value(Cfgr::default());
|
||||||
|
// Wait for clock switch status bits to change.
|
||||||
|
while RCC.cfgr().read().sws() != ClockSrc::MSI {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(stm32l5)]
|
||||||
|
crate::pac::PWR.cr1().modify(|w| {
|
||||||
|
w.set_vos(crate::pac::pwr::vals::Vos::RANGE0);
|
||||||
|
});
|
||||||
|
|
||||||
|
let rtc = config.ls.init();
|
||||||
|
|
||||||
|
let msi = config.msi.map(|range| {
|
||||||
|
// Enable MSI
|
||||||
|
RCC.cr().modify(|w| {
|
||||||
|
#[cfg(not(stm32wb))]
|
||||||
|
w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
|
||||||
|
w.set_msirange(range);
|
||||||
|
w.set_msion(true);
|
||||||
|
|
||||||
|
// If LSE is enabled, enable calibration of MSI
|
||||||
|
w.set_msipllen(config.ls.lse.is_some());
|
||||||
|
});
|
||||||
|
while !RCC.cr().read().msirdy() {}
|
||||||
|
|
||||||
|
// Enable as clock source for USB, RNG if running at 48 MHz
|
||||||
|
if range == MSIRange::RANGE48M {}
|
||||||
|
|
||||||
|
msirange_to_hertz(range)
|
||||||
|
});
|
||||||
|
|
||||||
|
let hsi = config.hsi.then(|| {
|
||||||
|
RCC.cr().modify(|w| w.set_hsion(true));
|
||||||
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
|
HSI_FREQ
|
||||||
|
});
|
||||||
|
|
||||||
|
let hse = config.hse.map(|hse| {
|
||||||
|
RCC.cr().modify(|w| {
|
||||||
|
#[cfg(stm32wl)]
|
||||||
|
w.set_hsebyppwr(hse.mode == HseMode::Bypass);
|
||||||
|
#[cfg(not(stm32wl))]
|
||||||
|
w.set_hsebyp(hse.mode == HseMode::Bypass);
|
||||||
|
w.set_hseon(true);
|
||||||
|
});
|
||||||
|
while !RCC.cr().read().hserdy() {}
|
||||||
|
|
||||||
|
hse.freq
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
|
||||||
|
let hsi48 = config.hsi48.then(|| {
|
||||||
|
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
||||||
|
while !RCC.crrcr().read().hsi48rdy() {}
|
||||||
|
|
||||||
|
Hertz(48_000_000)
|
||||||
|
});
|
||||||
|
#[cfg(any(stm32l47x, stm32l48x))]
|
||||||
|
let hsi48 = None;
|
||||||
|
|
||||||
|
let _plls = [
|
||||||
|
&config.pll,
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
&config.pllsai1,
|
||||||
|
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
|
||||||
|
&config.pllsai2,
|
||||||
|
];
|
||||||
|
|
||||||
|
// L4 has shared PLLSRC, PLLM, check it's equal in all PLLs.
|
||||||
|
#[cfg(all(stm32l4, not(rcc_l4plus)))]
|
||||||
|
match super::util::get_equal(_plls.into_iter().flatten().map(|p| (p.source, p.prediv))) {
|
||||||
|
Err(()) => panic!("Source must be equal across all enabled PLLs."),
|
||||||
|
Ok(None) => {}
|
||||||
|
Ok(Some((source, prediv))) => RCC.pllcfgr().write(|w| {
|
||||||
|
w.set_pllm(prediv);
|
||||||
|
w.set_pllsrc(source);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// L4+, WL has shared PLLSRC, check it's equal in all PLLs.
|
||||||
|
#[cfg(any(rcc_l4plus, stm32wl))]
|
||||||
|
match super::util::get_equal(_plls.into_iter().flatten().map(|p| p.source)) {
|
||||||
|
Err(()) => panic!("Source must be equal across all enabled PLLs."),
|
||||||
|
Ok(None) => {}
|
||||||
|
Ok(Some(source)) => RCC.pllcfgr().write(|w| {
|
||||||
|
w.set_pllsrc(source);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let pll_input = PllInput { hse, hsi, msi };
|
||||||
|
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
let pllsai1 = init_pll(PllInstance::Pllsai1, config.pllsai1, &pll_input);
|
||||||
|
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
|
||||||
|
let _pllsai2 = init_pll(PllInstance::Pllsai2, config.pllsai2, &pll_input);
|
||||||
|
|
||||||
|
let sys_clk = match config.mux {
|
||||||
|
ClockSrc::HSE => hse.unwrap(),
|
||||||
|
ClockSrc::HSI => hsi.unwrap(),
|
||||||
|
ClockSrc::MSI => msi.unwrap(),
|
||||||
|
ClockSrc::PLL1_R => pll.r.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(stm32l4)]
|
||||||
|
RCC.ccipr().modify(|w| w.set_clk48sel(config.clk48_src));
|
||||||
|
#[cfg(stm32l5)]
|
||||||
|
RCC.ccipr1().modify(|w| w.set_clk48sel(config.clk48_src));
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
let _clk48 = match config.clk48_src {
|
||||||
|
Clk48Src::HSI48 => hsi48,
|
||||||
|
Clk48Src::MSI => msi,
|
||||||
|
Clk48Src::PLLSAI1_Q => pllsai1.q,
|
||||||
|
Clk48Src::PLL1_Q => pll.q,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(rcc_l4plus)]
|
||||||
|
assert!(sys_clk.0 <= 120_000_000);
|
||||||
|
#[cfg(all(stm32l4, not(rcc_l4plus)))]
|
||||||
|
assert!(sys_clk.0 <= 80_000_000);
|
||||||
|
|
||||||
|
let hclk1 = sys_clk / config.ahb_pre;
|
||||||
|
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
|
||||||
|
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
|
||||||
|
#[cfg(not(any(stm32wl5x, stm32wb)))]
|
||||||
|
let hclk2 = hclk1;
|
||||||
|
#[cfg(any(stm32wl5x, stm32wb))]
|
||||||
|
let hclk2 = sys_clk / config.core2_ahb_pre;
|
||||||
|
#[cfg(not(any(stm32wl, stm32wb)))]
|
||||||
|
let hclk3 = hclk1;
|
||||||
|
#[cfg(any(stm32wl, stm32wb))]
|
||||||
|
let hclk3 = sys_clk / config.shared_ahb_pre;
|
||||||
|
|
||||||
|
// Set flash wait states
|
||||||
|
#[cfg(stm32l4)]
|
||||||
|
let latency = match hclk1.0 {
|
||||||
|
0..=16_000_000 => 0,
|
||||||
|
0..=32_000_000 => 1,
|
||||||
|
0..=48_000_000 => 2,
|
||||||
|
0..=64_000_000 => 3,
|
||||||
|
_ => 4,
|
||||||
|
};
|
||||||
|
#[cfg(stm32l5)]
|
||||||
|
let latency = match hclk1.0 {
|
||||||
|
// VCORE Range 0 (performance), others TODO
|
||||||
|
0..=20_000_000 => 0,
|
||||||
|
0..=40_000_000 => 1,
|
||||||
|
0..=60_000_000 => 2,
|
||||||
|
0..=80_000_000 => 3,
|
||||||
|
0..=100_000_000 => 4,
|
||||||
|
_ => 5,
|
||||||
|
};
|
||||||
|
#[cfg(stm32wl)]
|
||||||
|
let latency = match hclk3.0 {
|
||||||
|
// VOS RANGE1, others TODO.
|
||||||
|
..=18_000_000 => 0,
|
||||||
|
..=36_000_000 => 1,
|
||||||
|
_ => 2,
|
||||||
|
};
|
||||||
|
#[cfg(stm32wb)]
|
||||||
|
let latency = match hclk3.0 {
|
||||||
|
// VOS RANGE1, others TODO.
|
||||||
|
..=18_000_000 => 0,
|
||||||
|
..=36_000_000 => 1,
|
||||||
|
..=54_000_000 => 2,
|
||||||
|
..=64_000_000 => 3,
|
||||||
|
_ => 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
FLASH.acr().modify(|w| w.set_latency(latency));
|
||||||
|
while FLASH.acr().read().latency() != latency {}
|
||||||
|
|
||||||
|
RCC.cfgr().modify(|w| {
|
||||||
|
w.set_sw(config.mux);
|
||||||
|
w.set_hpre(config.ahb_pre);
|
||||||
|
w.set_ppre1(config.apb1_pre);
|
||||||
|
w.set_ppre2(config.apb2_pre);
|
||||||
|
});
|
||||||
|
while RCC.cfgr().read().sws() != config.mux {}
|
||||||
|
|
||||||
|
#[cfg(any(stm32wl, stm32wb))]
|
||||||
|
{
|
||||||
|
RCC.extcfgr().modify(|w| {
|
||||||
|
w.set_shdhpre(config.shared_ahb_pre);
|
||||||
|
#[cfg(any(stm32wl5x, stm32wb))]
|
||||||
|
w.set_c2hpre(config.core2_ahb_pre);
|
||||||
|
});
|
||||||
|
while !RCC.extcfgr().read().shdhpref() {}
|
||||||
|
#[cfg(any(stm32wl5x, stm32wb))]
|
||||||
|
while !RCC.extcfgr().read().c2hpref() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_freqs(Clocks {
|
||||||
|
sys: sys_clk,
|
||||||
|
hclk1,
|
||||||
|
hclk2,
|
||||||
|
hclk3,
|
||||||
|
pclk1,
|
||||||
|
pclk2,
|
||||||
|
pclk1_tim,
|
||||||
|
pclk2_tim,
|
||||||
|
#[cfg(stm32wl)]
|
||||||
|
pclk3: hclk3,
|
||||||
|
#[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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msirange_to_hertz(range: MSIRange) -> Hertz {
|
||||||
|
match range {
|
||||||
|
MSIRange::RANGE100K => Hertz(100_000),
|
||||||
|
MSIRange::RANGE200K => Hertz(200_000),
|
||||||
|
MSIRange::RANGE400K => Hertz(400_000),
|
||||||
|
MSIRange::RANGE800K => Hertz(800_000),
|
||||||
|
MSIRange::RANGE1M => Hertz(1_000_000),
|
||||||
|
MSIRange::RANGE2M => Hertz(2_000_000),
|
||||||
|
MSIRange::RANGE4M => Hertz(4_000_000),
|
||||||
|
MSIRange::RANGE8M => Hertz(8_000_000),
|
||||||
|
MSIRange::RANGE16M => Hertz(16_000_000),
|
||||||
|
MSIRange::RANGE24M => Hertz(24_000_000),
|
||||||
|
MSIRange::RANGE32M => Hertz(32_000_000),
|
||||||
|
MSIRange::RANGE48M => Hertz(48_000_000),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PllInput {
|
||||||
|
hsi: Option<Hertz>,
|
||||||
|
hse: Option<Hertz>,
|
||||||
|
msi: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Default)]
|
||||||
|
struct PllOutput {
|
||||||
|
p: Option<Hertz>,
|
||||||
|
q: Option<Hertz>,
|
||||||
|
r: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
enum PllInstance {
|
||||||
|
Pll,
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
Pllsai1,
|
||||||
|
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
|
||||||
|
Pllsai2,
|
||||||
|
}
|
||||||
|
|
||||||
|
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(stm32l4, stm32l5, stm32wb))]
|
||||||
|
PllInstance::Pllsai1 => {
|
||||||
|
RCC.cr().modify(|w| w.set_pllsai1on(enabled));
|
||||||
|
while RCC.cr().read().pllsai1rdy() != enabled {}
|
||||||
|
}
|
||||||
|
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
|
||||||
|
PllInstance::Pllsai2 => {
|
||||||
|
RCC.cr().modify(|w| w.set_pllsai2on(enabled));
|
||||||
|
while RCC.cr().read().pllsai2rdy() != 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 pll.source {
|
||||||
|
PLLSource::DISABLE => panic!("must not select PLL source as DISABLE"),
|
||||||
|
PLLSource::HSE => input.hse,
|
||||||
|
PLLSource::HSI => input.hsi,
|
||||||
|
PLLSource::MSI => input.msi,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pll_src = pll_src.unwrap();
|
||||||
|
|
||||||
|
let vco_freq = pll_src / pll.prediv * pll.mul;
|
||||||
|
|
||||||
|
let p = pll.divp.map(|div| vco_freq / div);
|
||||||
|
let q = pll.divq.map(|div| vco_freq / div);
|
||||||
|
let r = pll.divr.map(|div| vco_freq / div);
|
||||||
|
|
||||||
|
#[cfg(stm32l5)]
|
||||||
|
if instance == PllInstance::Pllsai2 {
|
||||||
|
assert!(q.is_none(), "PLLSAI2_Q is not available on L5");
|
||||||
|
assert!(r.is_none(), "PLLSAI2_R is not available on L5");
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! write_fields {
|
||||||
|
($w:ident) => {
|
||||||
|
$w.set_plln(pll.mul);
|
||||||
|
if let Some(divp) = pll.divp {
|
||||||
|
$w.set_pllp(divp);
|
||||||
|
$w.set_pllpen(true);
|
||||||
|
}
|
||||||
|
if let Some(divq) = pll.divq {
|
||||||
|
$w.set_pllq(divq);
|
||||||
|
$w.set_pllqen(true);
|
||||||
|
}
|
||||||
|
if let Some(divr) = pll.divr {
|
||||||
|
$w.set_pllr(divr);
|
||||||
|
$w.set_pllren(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
match instance {
|
||||||
|
PllInstance::Pll => RCC.pllcfgr().write(|w| {
|
||||||
|
w.set_pllm(pll.prediv);
|
||||||
|
w.set_pllsrc(pll.source);
|
||||||
|
write_fields!(w);
|
||||||
|
}),
|
||||||
|
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||||
|
PllInstance::Pllsai1 => RCC.pllsai1cfgr().write(|w| {
|
||||||
|
#[cfg(any(rcc_l4plus, stm32l5))]
|
||||||
|
w.set_pllm(pll.prediv);
|
||||||
|
#[cfg(stm32l5)]
|
||||||
|
w.set_pllsrc(pll.source);
|
||||||
|
write_fields!(w);
|
||||||
|
}),
|
||||||
|
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
|
||||||
|
PllInstance::Pllsai2 => RCC.pllsai2cfgr().write(|w| {
|
||||||
|
#[cfg(any(rcc_l4plus, stm32l5))]
|
||||||
|
w.set_pllm(pll.prediv);
|
||||||
|
#[cfg(stm32l5)]
|
||||||
|
w.set_pllsrc(pll.source);
|
||||||
|
write_fields!(w);
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable PLL
|
||||||
|
pll_enable(instance, true);
|
||||||
|
|
||||||
|
PllOutput { p, q, r }
|
||||||
|
}
|
@ -1,291 +0,0 @@
|
|||||||
use crate::pac::rcc::regs::Cfgr;
|
|
||||||
pub use crate::pac::rcc::vals::{
|
|
||||||
Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv,
|
|
||||||
Pllr as PllRDiv, Ppre as APBPrescaler,
|
|
||||||
};
|
|
||||||
use crate::pac::rcc::vals::{Msirange, Pllsrc, 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);
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
MSI(MSIRange),
|
|
||||||
PLL(PLLSource, PllRDiv, PllPreDiv, PllMul, Option<PllQDiv>),
|
|
||||||
HSE(Hertz),
|
|
||||||
HSI16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PLL clock input source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PLLSource {
|
|
||||||
HSI16,
|
|
||||||
HSE(Hertz),
|
|
||||||
MSI(MSIRange),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PLLSource> for Pllsrc {
|
|
||||||
fn from(val: PLLSource) -> Pllsrc {
|
|
||||||
match val {
|
|
||||||
PLLSource::HSI16 => Pllsrc::HSI16,
|
|
||||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
|
||||||
PLLSource::MSI(_) => Pllsrc::MSI,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
pub pllsai1: Option<(PllMul, PllPreDiv, Option<PllRDiv>, Option<PllQDiv>, Option<PllPDiv>)>,
|
|
||||||
pub hsi48: bool,
|
|
||||||
pub ls: super::LsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::MSI(MSIRange::RANGE4M),
|
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
|
||||||
pllsai1: None,
|
|
||||||
hsi48: false,
|
|
||||||
ls: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) unsafe fn init(config: Config) {
|
|
||||||
// Switch to MSI to prevent problems with PLL configuration.
|
|
||||||
if !RCC.cr().read().msion() {
|
|
||||||
// Turn on MSI and configure it to 4MHz.
|
|
||||||
RCC.cr().modify(|w| {
|
|
||||||
w.set_msirgsel(true); // MSI Range is provided by MSIRANGE[3:0].
|
|
||||||
w.set_msirange(MSIRange::RANGE4M);
|
|
||||||
w.set_msipllen(false);
|
|
||||||
w.set_msion(true)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait until MSI is running
|
|
||||||
while !RCC.cr().read().msirdy() {}
|
|
||||||
}
|
|
||||||
if RCC.cfgr().read().sws() != Sw::MSI {
|
|
||||||
// Set MSI as a clock source, reset prescalers.
|
|
||||||
RCC.cfgr().write_value(Cfgr::default());
|
|
||||||
// Wait for clock switch status bits to change.
|
|
||||||
while RCC.cfgr().read().sws() != Sw::MSI {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rtc = config.ls.init();
|
|
||||||
|
|
||||||
PWR.cr1().modify(|w| w.set_vos(stm32_metapac::pwr::vals::Vos::RANGE0));
|
|
||||||
let (sys_clk, sw) = match config.mux {
|
|
||||||
ClockSrc::MSI(range) => {
|
|
||||||
// Enable MSI
|
|
||||||
RCC.cr().write(|w| {
|
|
||||||
w.set_msirange(range);
|
|
||||||
w.set_msirgsel(true);
|
|
||||||
w.set_msion(true);
|
|
||||||
|
|
||||||
// If LSE is enabled, enable calibration of MSI
|
|
||||||
w.set_msipllen(config.ls.lse.is_some());
|
|
||||||
});
|
|
||||||
while !RCC.cr().read().msirdy() {}
|
|
||||||
|
|
||||||
// Enable as clock source for USB, RNG if running at 48 MHz
|
|
||||||
if range == MSIRange::RANGE48M {
|
|
||||||
RCC.ccipr1().modify(|w| {
|
|
||||||
w.set_clk48msel(0b11);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(msirange_to_hertz(range), Sw::MSI)
|
|
||||||
}
|
|
||||||
ClockSrc::HSI16 => {
|
|
||||||
// Enable HSI16
|
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
|
||||||
|
|
||||||
(HSI_FREQ, Sw::HSI16)
|
|
||||||
}
|
|
||||||
ClockSrc::HSE(freq) => {
|
|
||||||
// Enable HSE
|
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
|
|
||||||
(freq, Sw::HSE)
|
|
||||||
}
|
|
||||||
ClockSrc::PLL(src, divr, prediv, mul, divq) => {
|
|
||||||
let src_freq = match src {
|
|
||||||
PLLSource::HSE(freq) => {
|
|
||||||
// Enable HSE
|
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
freq
|
|
||||||
}
|
|
||||||
PLLSource::HSI16 => {
|
|
||||||
// Enable HSI
|
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
|
||||||
HSI_FREQ
|
|
||||||
}
|
|
||||||
PLLSource::MSI(range) => {
|
|
||||||
// Enable MSI
|
|
||||||
RCC.cr().write(|w| {
|
|
||||||
w.set_msirange(range);
|
|
||||||
w.set_msipllen(false); // should be turned on if LSE is started
|
|
||||||
w.set_msirgsel(true);
|
|
||||||
w.set_msion(true);
|
|
||||||
});
|
|
||||||
while !RCC.cr().read().msirdy() {}
|
|
||||||
|
|
||||||
msirange_to_hertz(range)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Disable PLL
|
|
||||||
RCC.cr().modify(|w| w.set_pllon(false));
|
|
||||||
while RCC.cr().read().pllrdy() {}
|
|
||||||
|
|
||||||
let freq = src_freq / prediv * mul / divr;
|
|
||||||
|
|
||||||
RCC.pllcfgr().write(move |w| {
|
|
||||||
w.set_plln(mul);
|
|
||||||
w.set_pllm(prediv);
|
|
||||||
w.set_pllr(divr);
|
|
||||||
if let Some(divq) = divq {
|
|
||||||
w.set_pllq(divq);
|
|
||||||
w.set_pllqen(true);
|
|
||||||
}
|
|
||||||
w.set_pllsrc(src.into());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enable as clock source for USB, RNG if PLL48 divisor is provided
|
|
||||||
if let Some(divq) = divq {
|
|
||||||
let freq = src_freq / prediv * mul / divq;
|
|
||||||
assert!(freq.0 == 48_000_000);
|
|
||||||
RCC.ccipr1().modify(|w| {
|
|
||||||
w.set_clk48msel(0b10);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 {
|
|
||||||
RCC.pllsai1cfgr().write(move |w| {
|
|
||||||
w.set_plln(mul);
|
|
||||||
w.set_pllm(prediv);
|
|
||||||
if let Some(r_div) = r_div {
|
|
||||||
w.set_pllr(r_div);
|
|
||||||
w.set_pllren(true);
|
|
||||||
}
|
|
||||||
if let Some(q_div) = q_div {
|
|
||||||
w.set_pllq(q_div);
|
|
||||||
w.set_pllqen(true);
|
|
||||||
let freq = src_freq / prediv * mul / q_div;
|
|
||||||
if freq.0 == 48_000_000 {
|
|
||||||
RCC.ccipr1().modify(|w| {
|
|
||||||
w.set_clk48msel(0b1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(p_div) = p_div {
|
|
||||||
w.set_pllp(p_div);
|
|
||||||
w.set_pllpen(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
RCC.cr().modify(|w| w.set_pllsai1on(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable PLL
|
|
||||||
RCC.cr().modify(|w| w.set_pllon(true));
|
|
||||||
while !RCC.cr().read().pllrdy() {}
|
|
||||||
RCC.pllcfgr().modify(|w| w.set_pllren(true));
|
|
||||||
|
|
||||||
(freq, Sw::PLL)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if config.hsi48 {
|
|
||||||
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
|
||||||
while !RCC.crrcr().read().hsi48rdy() {}
|
|
||||||
|
|
||||||
// Enable as clock source for USB, RNG and SDMMC
|
|
||||||
RCC.ccipr1().modify(|w| w.set_clk48msel(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set flash wait states
|
|
||||||
// VCORE Range 0 (performance), others TODO
|
|
||||||
FLASH.acr().modify(|w| {
|
|
||||||
w.set_latency(match sys_clk.0 {
|
|
||||||
0..=20_000_000 => 0,
|
|
||||||
0..=40_000_000 => 1,
|
|
||||||
0..=60_000_000 => 2,
|
|
||||||
0..=80_000_000 => 3,
|
|
||||||
0..=100_000_000 => 4,
|
|
||||||
_ => 5,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
|
||||||
w.set_sw(sw);
|
|
||||||
w.set_hpre(config.ahb_pre);
|
|
||||||
w.set_ppre1(config.apb1_pre);
|
|
||||||
w.set_ppre2(config.apb2_pre);
|
|
||||||
});
|
|
||||||
|
|
||||||
let ahb_freq = sys_clk / config.ahb_pre;
|
|
||||||
|
|
||||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
set_freqs(Clocks {
|
|
||||||
sys: sys_clk,
|
|
||||||
ahb1: ahb_freq,
|
|
||||||
ahb2: ahb_freq,
|
|
||||||
ahb3: ahb_freq,
|
|
||||||
apb1: apb1_freq,
|
|
||||||
apb2: apb2_freq,
|
|
||||||
apb1_tim: apb1_tim_freq,
|
|
||||||
apb2_tim: apb2_tim_freq,
|
|
||||||
rtc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn msirange_to_hertz(range: Msirange) -> Hertz {
|
|
||||||
match range {
|
|
||||||
MSIRange::RANGE100K => Hertz(100_000),
|
|
||||||
MSIRange::RANGE200K => Hertz(200_000),
|
|
||||||
MSIRange::RANGE400K => Hertz(400_000),
|
|
||||||
MSIRange::RANGE800K => Hertz(800_000),
|
|
||||||
MSIRange::RANGE1M => Hertz(1_000_000),
|
|
||||||
MSIRange::RANGE2M => Hertz(2_000_000),
|
|
||||||
MSIRange::RANGE4M => Hertz(4_000_000),
|
|
||||||
MSIRange::RANGE8M => Hertz(8_000_000),
|
|
||||||
MSIRange::RANGE16M => Hertz(16_000_000),
|
|
||||||
MSIRange::RANGE24M => Hertz(24_000_000),
|
|
||||||
MSIRange::RANGE32M => Hertz(32_000_000),
|
|
||||||
MSIRange::RANGE48M => Hertz(48_000_000),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,22 +13,16 @@ 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")]
|
||||||
#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab), path = "h.rs")]
|
#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab), path = "h.rs")]
|
||||||
#[cfg_attr(any(rcc_l0, rcc_l0_v2, rcc_l1), path = "l0l1.rs")]
|
#[cfg_attr(any(rcc_l0, rcc_l0_v2, rcc_l1), path = "l0l1.rs")]
|
||||||
#[cfg_attr(rcc_l4, path = "l4.rs")]
|
#[cfg_attr(any(rcc_l4, rcc_l4plus, rcc_l5, rcc_wl5, rcc_wle, rcc_wb), path = "l4l5.rs")]
|
||||||
#[cfg_attr(rcc_l5, path = "l5.rs")]
|
|
||||||
#[cfg_attr(rcc_u5, path = "u5.rs")]
|
#[cfg_attr(rcc_u5, path = "u5.rs")]
|
||||||
#[cfg_attr(rcc_wb, path = "wb.rs")]
|
|
||||||
#[cfg_attr(rcc_wba, path = "wba.rs")]
|
#[cfg_attr(rcc_wba, path = "wba.rs")]
|
||||||
#[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")]
|
|
||||||
mod _version;
|
mod _version;
|
||||||
#[cfg(feature = "low-power")]
|
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
|
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
|
|
||||||
@ -48,23 +42,24 @@ pub struct Clocks {
|
|||||||
pub sys: Hertz,
|
pub sys: Hertz,
|
||||||
|
|
||||||
// APB
|
// APB
|
||||||
pub apb1: Hertz,
|
pub pclk1: Hertz,
|
||||||
pub apb1_tim: Hertz,
|
pub pclk1_tim: Hertz,
|
||||||
#[cfg(not(any(rcc_c0, rcc_g0)))]
|
#[cfg(not(any(rcc_c0, rcc_g0)))]
|
||||||
pub apb2: Hertz,
|
pub pclk2: Hertz,
|
||||||
#[cfg(not(any(rcc_c0, rcc_g0)))]
|
#[cfg(not(any(rcc_c0, rcc_g0)))]
|
||||||
pub apb2_tim: Hertz,
|
pub pclk2_tim: Hertz,
|
||||||
#[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab, rcc_u5))]
|
#[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab, rcc_u5))]
|
||||||
pub apb3: Hertz,
|
pub pclk3: Hertz,
|
||||||
#[cfg(any(rcc_h7, rcc_h7rm0433, rcc_h7ab))]
|
#[cfg(any(rcc_h7, rcc_h7rm0433, rcc_h7ab, stm32h5))]
|
||||||
pub apb4: Hertz,
|
pub pclk4: Hertz,
|
||||||
#[cfg(any(rcc_wba))]
|
#[cfg(any(rcc_wba))]
|
||||||
pub apb7: Hertz,
|
pub pclk7: Hertz,
|
||||||
|
|
||||||
// AHB
|
// AHB
|
||||||
pub ahb1: Hertz,
|
pub hclk1: Hertz,
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
rcc_l4,
|
rcc_l4,
|
||||||
|
rcc_l4plus,
|
||||||
rcc_l5,
|
rcc_l5,
|
||||||
rcc_f2,
|
rcc_f2,
|
||||||
rcc_f4,
|
rcc_f4,
|
||||||
@ -82,9 +77,10 @@ pub struct Clocks {
|
|||||||
rcc_wl5,
|
rcc_wl5,
|
||||||
rcc_wle
|
rcc_wle
|
||||||
))]
|
))]
|
||||||
pub ahb2: Hertz,
|
pub hclk2: Hertz,
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
rcc_l4,
|
rcc_l4,
|
||||||
|
rcc_l4plus,
|
||||||
rcc_l5,
|
rcc_l5,
|
||||||
rcc_f2,
|
rcc_f2,
|
||||||
rcc_f4,
|
rcc_f4,
|
||||||
@ -100,18 +96,40 @@ pub struct Clocks {
|
|||||||
rcc_wl5,
|
rcc_wl5,
|
||||||
rcc_wle
|
rcc_wle
|
||||||
))]
|
))]
|
||||||
pub ahb3: Hertz,
|
pub hclk3: Hertz,
|
||||||
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab, rcc_wba))]
|
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab, rcc_wba))]
|
||||||
pub ahb4: Hertz,
|
pub hclk4: Hertz,
|
||||||
|
|
||||||
#[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
|
|
||||||
pub pll48: Option<Hertz>,
|
|
||||||
|
|
||||||
#[cfg(all(rcc_f4, not(stm32f410)))]
|
#[cfg(all(rcc_f4, not(stm32f410)))]
|
||||||
pub plli2s: Option<Hertz>,
|
pub plli2s1_q: Option<Hertz>,
|
||||||
|
#[cfg(all(rcc_f4, not(stm32f410)))]
|
||||||
|
pub plli2s1_r: Option<Hertz>,
|
||||||
|
|
||||||
|
#[cfg(rcc_l4)]
|
||||||
|
pub pllsai1_p: Option<Hertz>,
|
||||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
pub pllsai: Option<Hertz>,
|
pub pllsai1_q: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
|
pub pllsai1_r: Option<Hertz>,
|
||||||
|
#[cfg(rcc_l4)]
|
||||||
|
pub pllsai2_p: Option<Hertz>,
|
||||||
|
|
||||||
|
#[cfg(any(stm32g4, rcc_l4))]
|
||||||
|
pub pll1_p: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_l4))]
|
||||||
|
pub pll1_q: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pub pll2_p: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pub pll2_q: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pub pll2_r: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pub pll3_p: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pub pll3_q: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pub pll3_r: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
rcc_f1,
|
rcc_f1,
|
||||||
@ -135,79 +153,35 @@ pub struct Clocks {
|
|||||||
|
|
||||||
pub rtc: Option<Hertz>,
|
pub rtc: Option<Hertz>,
|
||||||
|
|
||||||
|
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
|
||||||
|
pub hsi: Option<Hertz>,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
pub mux_apb1: Option<Hertz>,
|
pub hsi48: Option<Hertz>,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
pub mux_apb2: Option<Hertz>,
|
pub lsi: Option<Hertz>,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
pub mux_apb3: Option<Hertz>,
|
pub csi: Option<Hertz>,
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_apb4: Option<Hertz>,
|
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
|
||||||
|
pub lse: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pub hse: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
pub mux_rcc_hclk4: Option<Hertz>,
|
pub audioclk: Option<Hertz>,
|
||||||
|
#[cfg(any(stm32h5, stm32h7))]
|
||||||
|
pub per: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h7)]
|
||||||
pub mux_pll2_q: Option<Hertz>,
|
pub rcc_pclk_d3: Option<Hertz>,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(rcc_l4)]
|
||||||
pub mux_pll3_q: Option<Hertz>,
|
pub sai1_extclk: Option<Hertz>,
|
||||||
#[cfg(stm32h5)]
|
#[cfg(rcc_l4)]
|
||||||
pub mux_hsi: Option<Hertz>,
|
pub sai2_extclk: Option<Hertz>,
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_csi: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_lse: Option<Hertz>,
|
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_pll1_q: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_pll2_p: Option<Hertz>,
|
|
||||||
#[cfg(rcc_h5)]
|
|
||||||
pub mux_pll3_p: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_audioclk: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_per: Option<Hertz>,
|
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_pll3_r: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_pll3_1: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_hsi48_ker: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_lsi: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_pll2_r: Option<Hertz>,
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_hse: Option<Hertz>,
|
|
||||||
|
|
||||||
#[cfg(stm32h5)]
|
|
||||||
pub mux_hsi48: Option<Hertz>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0);
|
pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
|
||||||
pub fn low_power_ready() -> bool {
|
|
||||||
// trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst));
|
|
||||||
CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
|
||||||
pub(crate) fn clock_refcount_add(_cs: critical_section::CriticalSection) {
|
|
||||||
// We don't check for overflow because constructing more than u32 peripherals is unlikely
|
|
||||||
let n = CLOCK_REFCOUNT.load(Ordering::Relaxed);
|
|
||||||
CLOCK_REFCOUNT.store(n + 1, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
|
||||||
pub(crate) fn clock_refcount_sub(_cs: critical_section::CriticalSection) {
|
|
||||||
let n = CLOCK_REFCOUNT.load(Ordering::Relaxed);
|
|
||||||
assert!(n != 0);
|
|
||||||
CLOCK_REFCOUNT.store(n - 1, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Frozen clock frequencies
|
/// Frozen clock frequencies
|
||||||
///
|
///
|
||||||
@ -250,3 +224,33 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait RccPeripheral: sealed::RccPeripheral + 'static {}
|
pub trait RccPeripheral: sealed::RccPeripheral + 'static {}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
mod util {
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
pub 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> bool {
|
||||||
|
let Some(x) = iter.next() else { return true };
|
||||||
|
if !iter.all(|y| y == x) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> Result<Option<T>, ()> {
|
||||||
|
let Some(x) = iter.next() else { return Ok(None) };
|
||||||
|
if !iter.all(|y| y == x) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(Some(x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
|||||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
pub enum ClockSrc {
|
pub enum ClockSrc {
|
||||||
/// Use an internal medium speed oscillator (MSIS) as the system clock.
|
/// Use an internal medium speed oscillator (MSIS) as the system clock.
|
||||||
MSI(Msirange),
|
MSI(Msirange),
|
||||||
@ -19,9 +20,9 @@ pub enum ClockSrc {
|
|||||||
/// never exceed 50 MHz.
|
/// never exceed 50 MHz.
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
/// Use the 16 MHz internal high speed oscillator as the system clock.
|
/// Use the 16 MHz internal high speed oscillator as the system clock.
|
||||||
HSI16,
|
HSI,
|
||||||
/// Use PLL1 as the system clock.
|
/// Use PLL1 as the system clock.
|
||||||
PLL1R(PllConfig),
|
PLL1_R(PllConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ClockSrc {
|
impl Default for ClockSrc {
|
||||||
@ -53,10 +54,10 @@ pub struct PllConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PllConfig {
|
impl PllConfig {
|
||||||
/// A configuration for HSI16 / 1 * 10 / 1 = 160 MHz
|
/// A configuration for HSI / 1 * 10 / 1 = 160 MHz
|
||||||
pub const fn hsi16_160mhz() -> Self {
|
pub const fn hsi_160mhz() -> Self {
|
||||||
PllConfig {
|
PllConfig {
|
||||||
source: PllSrc::HSI16,
|
source: PllSrc::HSI,
|
||||||
m: Pllm::DIV1,
|
m: Pllm::DIV1,
|
||||||
n: Plln::MUL10,
|
n: Plln::MUL10,
|
||||||
r: Plldiv::DIV1,
|
r: Plldiv::DIV1,
|
||||||
@ -84,7 +85,7 @@ pub enum PllSrc {
|
|||||||
/// never exceed 50 MHz.
|
/// never exceed 50 MHz.
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
/// Use the 16 MHz internal high speed oscillator as the PLL source.
|
/// Use the 16 MHz internal high speed oscillator as the PLL source.
|
||||||
HSI16,
|
HSI,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Pllsrc> for PllSrc {
|
impl Into<Pllsrc> for PllSrc {
|
||||||
@ -92,7 +93,7 @@ impl Into<Pllsrc> for PllSrc {
|
|||||||
match self {
|
match self {
|
||||||
PllSrc::MSIS(..) => Pllsrc::MSIS,
|
PllSrc::MSIS(..) => Pllsrc::MSIS,
|
||||||
PllSrc::HSE(..) => Pllsrc::HSE,
|
PllSrc::HSE(..) => Pllsrc::HSE,
|
||||||
PllSrc::HSI16 => Pllsrc::HSI16,
|
PllSrc::HSI => Pllsrc::HSI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,8 +103,8 @@ impl Into<Sw> for ClockSrc {
|
|||||||
match self {
|
match self {
|
||||||
ClockSrc::MSI(..) => Sw::MSIS,
|
ClockSrc::MSI(..) => Sw::MSIS,
|
||||||
ClockSrc::HSE(..) => Sw::HSE,
|
ClockSrc::HSE(..) => Sw::HSE,
|
||||||
ClockSrc::HSI16 => Sw::HSI16,
|
ClockSrc::HSI => Sw::HSI,
|
||||||
ClockSrc::PLL1R(..) => Sw::PLL1_R,
|
ClockSrc::PLL1_R(..) => Sw::PLL1_R,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,7 +126,7 @@ pub struct Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
unsafe fn init_hsi16(&self) -> Hertz {
|
unsafe fn init_hsi(&self) -> Hertz {
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
RCC.cr().write(|w| w.set_hsion(true));
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
@ -169,7 +170,7 @@ impl Config {
|
|||||||
|
|
||||||
RCC.icscr1().modify(|w| {
|
RCC.icscr1().modify(|w| {
|
||||||
w.set_msisrange(range);
|
w.set_msisrange(range);
|
||||||
w.set_msirgsel(Msirgsel::RCC_ICSCR1);
|
w.set_msirgsel(Msirgsel::ICSCR1);
|
||||||
});
|
});
|
||||||
RCC.cr().write(|w| {
|
RCC.cr().write(|w| {
|
||||||
w.set_msipllen(false);
|
w.set_msipllen(false);
|
||||||
@ -188,7 +189,7 @@ impl Default for Config {
|
|||||||
apb1_pre: APBPrescaler::DIV1,
|
apb1_pre: APBPrescaler::DIV1,
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
apb2_pre: APBPrescaler::DIV1,
|
||||||
apb3_pre: APBPrescaler::DIV1,
|
apb3_pre: APBPrescaler::DIV1,
|
||||||
hsi48: false,
|
hsi48: true,
|
||||||
voltage_range: VoltageScale::RANGE3,
|
voltage_range: VoltageScale::RANGE3,
|
||||||
ls: Default::default(),
|
ls: Default::default(),
|
||||||
}
|
}
|
||||||
@ -211,13 +212,13 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
let sys_clk = match config.mux {
|
let sys_clk = match config.mux {
|
||||||
ClockSrc::MSI(range) => config.init_msis(range),
|
ClockSrc::MSI(range) => config.init_msis(range),
|
||||||
ClockSrc::HSE(freq) => config.init_hse(freq),
|
ClockSrc::HSE(freq) => config.init_hse(freq),
|
||||||
ClockSrc::HSI16 => config.init_hsi16(),
|
ClockSrc::HSI => config.init_hsi(),
|
||||||
ClockSrc::PLL1R(pll) => {
|
ClockSrc::PLL1_R(pll) => {
|
||||||
// Configure the PLL source
|
// Configure the PLL source
|
||||||
let source_clk = match pll.source {
|
let source_clk = match pll.source {
|
||||||
PllSrc::MSIS(range) => config.init_msis(range),
|
PllSrc::MSIS(range) => config.init_msis(range),
|
||||||
PllSrc::HSE(hertz) => config.init_hse(hertz),
|
PllSrc::HSE(hertz) => config.init_hse(hertz),
|
||||||
PllSrc::HSI16 => config.init_hsi16(),
|
PllSrc::HSI => config.init_hsi(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate the reference clock, which is the source divided by m
|
// Calculate the reference clock, which is the source divided by m
|
||||||
@ -292,7 +293,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
// Set the prescaler for PWR EPOD
|
// Set the prescaler for PWR EPOD
|
||||||
w.set_pllmboost(mboost);
|
w.set_pllmboost(mboost);
|
||||||
|
|
||||||
// Enable PLL1R output
|
// Enable PLL1_R output
|
||||||
w.set_pllren(true);
|
w.set_pllren(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -436,14 +437,14 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: sys_clk,
|
sys: sys_clk,
|
||||||
ahb1: ahb_freq,
|
hclk1: ahb_freq,
|
||||||
ahb2: ahb_freq,
|
hclk2: ahb_freq,
|
||||||
ahb3: ahb_freq,
|
hclk3: ahb_freq,
|
||||||
apb1: apb1_freq,
|
pclk1: apb1_freq,
|
||||||
apb2: apb2_freq,
|
pclk2: apb2_freq,
|
||||||
apb3: apb3_freq,
|
pclk3: apb3_freq,
|
||||||
apb1_tim: apb1_tim_freq,
|
pclk1_tim: apb1_tim_freq,
|
||||||
apb2_tim: apb2_tim_freq,
|
pclk2_tim: apb2_tim_freq,
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,248 +0,0 @@
|
|||||||
pub use crate::pac::rcc::vals::{
|
|
||||||
Hpre as AHBPrescaler, Hsepre as HsePrescaler, Pllm, Plln, Pllp, Pllq, Pllr, Pllsrc as PllSource,
|
|
||||||
Ppre as APBPrescaler, Sw as Sysclk,
|
|
||||||
};
|
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
|
||||||
use crate::time::{mhz, Hertz};
|
|
||||||
|
|
||||||
/// HSI speed
|
|
||||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
|
||||||
|
|
||||||
pub struct Hse {
|
|
||||||
pub prediv: HsePrescaler,
|
|
||||||
|
|
||||||
pub frequency: Hertz,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PllMux {
|
|
||||||
/// Source clock selection.
|
|
||||||
pub source: PllSource,
|
|
||||||
|
|
||||||
/// PLL pre-divider (DIVM). Must be between 1 and 63.
|
|
||||||
pub prediv: Pllm,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pll {
|
|
||||||
/// PLL multiplication factor. Must be between 4 and 512.
|
|
||||||
pub mul: Plln,
|
|
||||||
|
|
||||||
/// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
|
|
||||||
/// On PLL1, it must be even (in particular, it cannot be 1.)
|
|
||||||
pub divp: Option<Pllp>,
|
|
||||||
/// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
|
|
||||||
pub divq: Option<Pllq>,
|
|
||||||
/// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
|
|
||||||
pub divr: Option<Pllr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub hse: Option<Hse>,
|
|
||||||
pub sys: Sysclk,
|
|
||||||
pub mux: Option<PllMux>,
|
|
||||||
|
|
||||||
pub pll: Option<Pll>,
|
|
||||||
pub pllsai: Option<Pll>,
|
|
||||||
|
|
||||||
pub ahb1_pre: AHBPrescaler,
|
|
||||||
pub ahb2_pre: AHBPrescaler,
|
|
||||||
pub ahb3_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
|
|
||||||
pub ls: super::LsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const WPAN_DEFAULT: Config = Config {
|
|
||||||
hse: Some(Hse {
|
|
||||||
frequency: mhz(32),
|
|
||||||
prediv: HsePrescaler::DIV1,
|
|
||||||
}),
|
|
||||||
sys: Sysclk::PLL,
|
|
||||||
mux: Some(PllMux {
|
|
||||||
source: PllSource::HSE,
|
|
||||||
prediv: Pllm::DIV2,
|
|
||||||
}),
|
|
||||||
|
|
||||||
ls: super::LsConfig::default_lse(),
|
|
||||||
|
|
||||||
pll: Some(Pll {
|
|
||||||
mul: Plln::MUL12,
|
|
||||||
divp: Some(Pllp::DIV3),
|
|
||||||
divq: Some(Pllq::DIV4),
|
|
||||||
divr: Some(Pllr::DIV3),
|
|
||||||
}),
|
|
||||||
pllsai: None,
|
|
||||||
|
|
||||||
ahb1_pre: AHBPrescaler::DIV1,
|
|
||||||
ahb2_pre: AHBPrescaler::DIV2,
|
|
||||||
ahb3_pre: AHBPrescaler::DIV1,
|
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
hse: None,
|
|
||||||
sys: Sysclk::HSI16,
|
|
||||||
mux: None,
|
|
||||||
pll: None,
|
|
||||||
pllsai: None,
|
|
||||||
|
|
||||||
ls: Default::default(),
|
|
||||||
|
|
||||||
ahb1_pre: AHBPrescaler::DIV1,
|
|
||||||
ahb2_pre: AHBPrescaler::DIV1,
|
|
||||||
ahb3_pre: AHBPrescaler::DIV1,
|
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(stm32wb)]
|
|
||||||
/// RCC initialization function
|
|
||||||
pub(crate) unsafe fn init(config: Config) {
|
|
||||||
let hse_clk = config.hse.as_ref().map(|hse| hse.frequency / hse.prediv);
|
|
||||||
|
|
||||||
let mux_clk = config.mux.as_ref().map(|pll_mux| {
|
|
||||||
(match pll_mux.source {
|
|
||||||
PllSource::HSE => hse_clk.unwrap(),
|
|
||||||
PllSource::HSI16 => HSI_FREQ,
|
|
||||||
_ => unreachable!(),
|
|
||||||
} / pll_mux.prediv)
|
|
||||||
});
|
|
||||||
|
|
||||||
let (pll_r, _pll_q, _pll_p) = match &config.pll {
|
|
||||||
Some(pll) => {
|
|
||||||
let pll_vco = mux_clk.unwrap() * pll.mul as u32;
|
|
||||||
|
|
||||||
(
|
|
||||||
pll.divr.map(|divr| pll_vco / divr),
|
|
||||||
pll.divq.map(|divq| pll_vco / divq),
|
|
||||||
pll.divp.map(|divp| pll_vco / divp),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
None => (None, None, None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let sys_clk = match config.sys {
|
|
||||||
Sysclk::HSE => hse_clk.unwrap(),
|
|
||||||
Sysclk::HSI16 => HSI_FREQ,
|
|
||||||
Sysclk::PLL => pll_r.unwrap(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let ahb1_clk = sys_clk / config.ahb1_pre;
|
|
||||||
let ahb2_clk = sys_clk / config.ahb2_pre;
|
|
||||||
let ahb3_clk = sys_clk / config.ahb3_pre;
|
|
||||||
|
|
||||||
let (apb1_clk, apb1_tim_clk) = match config.apb1_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb1_clk, ahb1_clk),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb1_clk / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (apb2_clk, apb2_tim_clk) = match config.apb2_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb1_clk, ahb1_clk),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb1_clk / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let rcc = crate::pac::RCC;
|
|
||||||
|
|
||||||
let needs_hsi = if let Some(pll_mux) = &config.mux {
|
|
||||||
pll_mux.source == PllSource::HSI16
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if needs_hsi || config.sys == Sysclk::HSI16 {
|
|
||||||
rcc.cr().modify(|w| {
|
|
||||||
w.set_hsion(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
while !rcc.cr().read().hsirdy() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
rcc.cfgr().modify(|w| w.set_stopwuck(true));
|
|
||||||
|
|
||||||
let rtc = config.ls.init();
|
|
||||||
|
|
||||||
match &config.hse {
|
|
||||||
Some(hse) => {
|
|
||||||
rcc.cr().modify(|w| {
|
|
||||||
w.set_hsepre(hse.prediv);
|
|
||||||
w.set_hseon(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
while !rcc.cr().read().hserdy() {}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match &config.mux {
|
|
||||||
Some(pll_mux) => {
|
|
||||||
rcc.pllcfgr().modify(|w| {
|
|
||||||
w.set_pllm(pll_mux.prediv);
|
|
||||||
w.set_pllsrc(pll_mux.source.into());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
match &config.pll {
|
|
||||||
Some(pll) => {
|
|
||||||
rcc.pllcfgr().modify(|w| {
|
|
||||||
w.set_plln(pll.mul);
|
|
||||||
pll.divp.map(|divp| {
|
|
||||||
w.set_pllpen(true);
|
|
||||||
w.set_pllp(divp)
|
|
||||||
});
|
|
||||||
pll.divq.map(|divq| {
|
|
||||||
w.set_pllqen(true);
|
|
||||||
w.set_pllq(divq)
|
|
||||||
});
|
|
||||||
pll.divr.map(|divr| {
|
|
||||||
w.set_pllren(true);
|
|
||||||
w.set_pllr(divr);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
rcc.cr().modify(|w| w.set_pllon(true));
|
|
||||||
|
|
||||||
while !rcc.cr().read().pllrdy() {}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
rcc.cfgr().modify(|w| {
|
|
||||||
w.set_sw(config.sys.into());
|
|
||||||
w.set_hpre(config.ahb1_pre);
|
|
||||||
w.set_ppre1(config.apb1_pre);
|
|
||||||
w.set_ppre2(config.apb2_pre);
|
|
||||||
});
|
|
||||||
|
|
||||||
rcc.extcfgr().modify(|w| {
|
|
||||||
w.set_c2hpre(config.ahb2_pre);
|
|
||||||
w.set_shdhpre(config.ahb3_pre);
|
|
||||||
});
|
|
||||||
|
|
||||||
set_freqs(Clocks {
|
|
||||||
sys: sys_clk,
|
|
||||||
ahb1: ahb1_clk,
|
|
||||||
ahb2: ahb2_clk,
|
|
||||||
ahb3: ahb3_clk,
|
|
||||||
apb1: apb1_clk,
|
|
||||||
apb2: apb2_clk,
|
|
||||||
apb1_tim: apb1_tim_clk,
|
|
||||||
apb2_tim: apb2_tim_clk,
|
|
||||||
rtc,
|
|
||||||
})
|
|
||||||
}
|
|
@ -13,20 +13,20 @@ pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum ClockSrc {
|
pub enum ClockSrc {
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
HSI16,
|
HSI,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum PllSrc {
|
pub enum PllSrc {
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
HSI16,
|
HSI,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Pllsrc> for PllSrc {
|
impl Into<Pllsrc> for PllSrc {
|
||||||
fn into(self) -> Pllsrc {
|
fn into(self) -> Pllsrc {
|
||||||
match self {
|
match self {
|
||||||
PllSrc::HSE(..) => Pllsrc::HSE,
|
PllSrc::HSE(..) => Pllsrc::HSE,
|
||||||
PllSrc::HSI16 => Pllsrc::HSI16,
|
PllSrc::HSI => Pllsrc::HSI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@ impl Into<Sw> for ClockSrc {
|
|||||||
fn into(self) -> Sw {
|
fn into(self) -> Sw {
|
||||||
match self {
|
match self {
|
||||||
ClockSrc::HSE(..) => Sw::HSE,
|
ClockSrc::HSE(..) => Sw::HSE,
|
||||||
ClockSrc::HSI16 => Sw::HSI16,
|
ClockSrc::HSI => Sw::HSI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ pub struct Config {
|
|||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
mux: ClockSrc::HSI16,
|
mux: ClockSrc::HSI,
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
ahb_pre: AHBPrescaler::DIV1,
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
apb1_pre: APBPrescaler::DIV1,
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
apb2_pre: APBPrescaler::DIV1,
|
||||||
@ -70,7 +70,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
freq
|
freq
|
||||||
}
|
}
|
||||||
ClockSrc::HSI16 => {
|
ClockSrc::HSI => {
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
RCC.cr().write(|w| w.set_hsion(true));
|
||||||
while !RCC.cr().read().hsirdy() {}
|
while !RCC.cr().read().hsirdy() {}
|
||||||
|
|
||||||
@ -142,14 +142,14 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: sys_clk,
|
sys: sys_clk,
|
||||||
ahb1: ahb_freq,
|
hclk1: ahb_freq,
|
||||||
ahb2: ahb_freq,
|
hclk2: ahb_freq,
|
||||||
ahb4: ahb_freq,
|
hclk4: ahb_freq,
|
||||||
apb1: apb1_freq,
|
pclk1: apb1_freq,
|
||||||
apb2: apb2_freq,
|
pclk2: apb2_freq,
|
||||||
apb7: apb7_freq,
|
pclk7: apb7_freq,
|
||||||
apb1_tim: apb1_tim_freq,
|
pclk1_tim: apb1_tim_freq,
|
||||||
apb2_tim: apb2_tim_freq,
|
pclk2_tim: apb2_tim_freq,
|
||||||
rtc,
|
rtc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
|
||||||
use crate::pac::rcc::vals::Sw;
|
|
||||||
pub use crate::pac::rcc::vals::{
|
|
||||||
Adcsel as AdcClockSource, Hpre as AHBPrescaler, Msirange as MSIRange, Pllm, Plln, Pllp, Pllq, Pllr,
|
|
||||||
Pllsrc as PllSource, Ppre as APBPrescaler,
|
|
||||||
};
|
|
||||||
use crate::pac::{FLASH, RCC};
|
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
|
||||||
use crate::time::Hertz;
|
|
||||||
|
|
||||||
/// HSI speed
|
|
||||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
|
||||||
|
|
||||||
/// HSE speed
|
|
||||||
pub const HSE_FREQ: Hertz = Hertz(32_000_000);
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
MSI(MSIRange),
|
|
||||||
HSE,
|
|
||||||
HSI16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub shd_ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
pub adc_clock_source: AdcClockSource,
|
|
||||||
pub ls: super::LsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::MSI(MSIRange::RANGE4M),
|
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
|
||||||
shd_ahb_pre: AHBPrescaler::DIV1,
|
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
|
||||||
adc_clock_source: AdcClockSource::HSI16,
|
|
||||||
ls: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) unsafe fn init(config: Config) {
|
|
||||||
let (sys_clk, sw, vos) = match config.mux {
|
|
||||||
ClockSrc::HSI16 => (HSI_FREQ, Sw::HSI16, VoltageScale::RANGE2),
|
|
||||||
ClockSrc::HSE => (HSE_FREQ, Sw::HSE, VoltageScale::RANGE1),
|
|
||||||
ClockSrc::MSI(range) => (msirange_to_hertz(range), Sw::MSI, msirange_to_vos(range)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let ahb_freq = sys_clk / config.ahb_pre;
|
|
||||||
let shd_ahb_freq = sys_clk / config.shd_ahb_pre;
|
|
||||||
|
|
||||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, freq * 2u32)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Adjust flash latency
|
|
||||||
let flash_clk_src_freq = shd_ahb_freq;
|
|
||||||
let ws = match vos {
|
|
||||||
VoltageScale::RANGE1 => match flash_clk_src_freq.0 {
|
|
||||||
0..=18_000_000 => 0b000,
|
|
||||||
18_000_001..=36_000_000 => 0b001,
|
|
||||||
_ => 0b010,
|
|
||||||
},
|
|
||||||
VoltageScale::RANGE2 => match flash_clk_src_freq.0 {
|
|
||||||
0..=6_000_000 => 0b000,
|
|
||||||
6_000_001..=12_000_000 => 0b001,
|
|
||||||
_ => 0b010,
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
FLASH.acr().modify(|w| {
|
|
||||||
w.set_latency(ws);
|
|
||||||
});
|
|
||||||
|
|
||||||
while FLASH.acr().read().latency() != ws {}
|
|
||||||
|
|
||||||
match config.mux {
|
|
||||||
ClockSrc::HSI16 => {
|
|
||||||
// Enable HSI16
|
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
|
||||||
}
|
|
||||||
ClockSrc::HSE => {
|
|
||||||
// Enable HSE
|
|
||||||
RCC.cr().write(|w| {
|
|
||||||
w.set_hsebyppwr(true);
|
|
||||||
w.set_hseon(true);
|
|
||||||
});
|
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
}
|
|
||||||
ClockSrc::MSI(range) => {
|
|
||||||
let cr = RCC.cr().read();
|
|
||||||
assert!(!cr.msion() || cr.msirdy());
|
|
||||||
RCC.cr().write(|w| {
|
|
||||||
w.set_msirgsel(true);
|
|
||||||
w.set_msirange(range);
|
|
||||||
w.set_msion(true);
|
|
||||||
|
|
||||||
// If LSE is enabled, enable calibration of MSI
|
|
||||||
w.set_msipllen(config.ls.lse.is_some());
|
|
||||||
});
|
|
||||||
while !RCC.cr().read().msirdy() {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RCC.extcfgr().modify(|w| {
|
|
||||||
w.set_shdhpre(config.shd_ahb_pre);
|
|
||||||
});
|
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
|
||||||
w.set_sw(sw.into());
|
|
||||||
w.set_hpre(config.ahb_pre);
|
|
||||||
w.set_ppre1(config.apb1_pre);
|
|
||||||
w.set_ppre2(config.apb2_pre);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ADC clock MUX
|
|
||||||
RCC.ccipr().modify(|w| w.set_adcsel(config.adc_clock_source));
|
|
||||||
|
|
||||||
// TODO: switch voltage range
|
|
||||||
|
|
||||||
let rtc = config.ls.init();
|
|
||||||
|
|
||||||
set_freqs(Clocks {
|
|
||||||
sys: sys_clk,
|
|
||||||
ahb1: ahb_freq,
|
|
||||||
ahb2: ahb_freq,
|
|
||||||
ahb3: shd_ahb_freq,
|
|
||||||
apb1: apb1_freq,
|
|
||||||
apb2: apb2_freq,
|
|
||||||
apb3: shd_ahb_freq,
|
|
||||||
apb1_tim: apb1_tim_freq,
|
|
||||||
apb2_tim: apb2_tim_freq,
|
|
||||||
rtc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn msirange_to_hertz(range: MSIRange) -> Hertz {
|
|
||||||
match range {
|
|
||||||
MSIRange::RANGE100K => Hertz(100_000),
|
|
||||||
MSIRange::RANGE200K => Hertz(200_000),
|
|
||||||
MSIRange::RANGE400K => Hertz(400_000),
|
|
||||||
MSIRange::RANGE800K => Hertz(800_000),
|
|
||||||
MSIRange::RANGE1M => Hertz(1_000_000),
|
|
||||||
MSIRange::RANGE2M => Hertz(2_000_000),
|
|
||||||
MSIRange::RANGE4M => Hertz(4_000_000),
|
|
||||||
MSIRange::RANGE8M => Hertz(8_000_000),
|
|
||||||
MSIRange::RANGE16M => Hertz(16_000_000),
|
|
||||||
MSIRange::RANGE24M => Hertz(24_000_000),
|
|
||||||
MSIRange::RANGE32M => Hertz(32_000_000),
|
|
||||||
MSIRange::RANGE48M => Hertz(48_000_000),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn msirange_to_vos(range: MSIRange) -> VoltageScale {
|
|
||||||
if range.to_bits() > MSIRange::RANGE16M.to_bits() {
|
|
||||||
VoltageScale::RANGE1
|
|
||||||
} else {
|
|
||||||
VoltageScale::RANGE2
|
|
||||||
}
|
|
||||||
}
|
|
@ -85,7 +85,7 @@ impl<'d, T: Instance> Rng<'d, T> {
|
|||||||
reg.set_ie(false);
|
reg.set_ie(false);
|
||||||
reg.set_rngen(true);
|
reg.set_rngen(true);
|
||||||
});
|
});
|
||||||
T::regs().cr().write(|reg| {
|
T::regs().cr().modify(|reg| {
|
||||||
reg.set_ced(false);
|
reg.set_ced(false);
|
||||||
});
|
});
|
||||||
// wait for CONDRST to be set
|
// wait for CONDRST to be set
|
||||||
|
@ -4,8 +4,60 @@ use core::convert::From;
|
|||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::{self, Datelike, NaiveDate, Timelike, Weekday};
|
use chrono::{self, Datelike, NaiveDate, Timelike, Weekday};
|
||||||
|
|
||||||
use super::byte_to_bcd2;
|
#[cfg(any(feature = "defmt", feature = "time"))]
|
||||||
use crate::pac::rtc::Rtc;
|
use crate::peripherals::RTC;
|
||||||
|
#[cfg(any(feature = "defmt", feature = "time"))]
|
||||||
|
use crate::rtc::sealed::Instance;
|
||||||
|
|
||||||
|
/// Represents an instant in time that can be substracted to compute a duration
|
||||||
|
pub struct RtcInstant {
|
||||||
|
/// 0..59
|
||||||
|
pub second: u8,
|
||||||
|
/// 0..256
|
||||||
|
pub subsecond: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RtcInstant {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn from(second: u8, subsecond: u16) -> Result<Self, super::RtcError> {
|
||||||
|
Ok(Self { second, subsecond })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl defmt::Format for RtcInstant {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(
|
||||||
|
fmt,
|
||||||
|
"{}:{}",
|
||||||
|
self.second,
|
||||||
|
RTC::regs().prer().read().prediv_s() - self.subsecond,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
impl core::ops::Sub for RtcInstant {
|
||||||
|
type Output = embassy_time::Duration;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
use embassy_time::{Duration, TICK_HZ};
|
||||||
|
|
||||||
|
let second = if self.second < rhs.second {
|
||||||
|
self.second + 60
|
||||||
|
} else {
|
||||||
|
self.second
|
||||||
|
};
|
||||||
|
|
||||||
|
let psc = RTC::regs().prer().read().prediv_s() as u32;
|
||||||
|
|
||||||
|
let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32);
|
||||||
|
let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32);
|
||||||
|
let rtc_ticks = self_ticks - other_ticks;
|
||||||
|
|
||||||
|
Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Errors regarding the [`DateTime`] struct.
|
/// Errors regarding the [`DateTime`] struct.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@ -32,19 +84,85 @@ pub enum Error {
|
|||||||
/// Structure containing date and time information
|
/// Structure containing date and time information
|
||||||
pub struct DateTime {
|
pub struct DateTime {
|
||||||
/// 0..4095
|
/// 0..4095
|
||||||
pub year: u16,
|
year: u16,
|
||||||
/// 1..12, 1 is January
|
/// 1..12, 1 is January
|
||||||
pub month: u8,
|
month: u8,
|
||||||
/// 1..28,29,30,31 depending on month
|
/// 1..28,29,30,31 depending on month
|
||||||
pub day: u8,
|
day: u8,
|
||||||
///
|
///
|
||||||
pub day_of_week: DayOfWeek,
|
day_of_week: DayOfWeek,
|
||||||
/// 0..23
|
/// 0..23
|
||||||
pub hour: u8,
|
hour: u8,
|
||||||
/// 0..59
|
/// 0..59
|
||||||
pub minute: u8,
|
minute: u8,
|
||||||
/// 0..59
|
/// 0..59
|
||||||
pub second: u8,
|
second: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DateTime {
|
||||||
|
pub const fn year(&self) -> u16 {
|
||||||
|
self.year
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn month(&self) -> u8 {
|
||||||
|
self.month
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn day(&self) -> u8 {
|
||||||
|
self.day
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn day_of_week(&self) -> DayOfWeek {
|
||||||
|
self.day_of_week
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn hour(&self) -> u8 {
|
||||||
|
self.hour
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn minute(&self) -> u8 {
|
||||||
|
self.minute
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn second(&self) -> u8 {
|
||||||
|
self.second
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from(
|
||||||
|
year: u16,
|
||||||
|
month: u8,
|
||||||
|
day: u8,
|
||||||
|
day_of_week: u8,
|
||||||
|
hour: u8,
|
||||||
|
minute: u8,
|
||||||
|
second: u8,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let day_of_week = day_of_week_from_u8(day_of_week)?;
|
||||||
|
|
||||||
|
if year > 4095 {
|
||||||
|
Err(Error::InvalidYear)
|
||||||
|
} else if month < 1 || month > 12 {
|
||||||
|
Err(Error::InvalidMonth)
|
||||||
|
} else if day < 1 || day > 31 {
|
||||||
|
Err(Error::InvalidDay)
|
||||||
|
} else if hour > 23 {
|
||||||
|
Err(Error::InvalidHour)
|
||||||
|
} else if minute > 59 {
|
||||||
|
Err(Error::InvalidMinute)
|
||||||
|
} else if second > 59 {
|
||||||
|
Err(Error::InvalidSecond)
|
||||||
|
} else {
|
||||||
|
Ok(Self {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
day_of_week,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
@ -142,58 +260,3 @@ pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
|
|
||||||
let (ht, hu) = byte_to_bcd2(t.hour as u8);
|
|
||||||
let (mnt, mnu) = byte_to_bcd2(t.minute as u8);
|
|
||||||
let (st, su) = byte_to_bcd2(t.second as u8);
|
|
||||||
|
|
||||||
let (dt, du) = byte_to_bcd2(t.day as u8);
|
|
||||||
let (mt, mu) = byte_to_bcd2(t.month as u8);
|
|
||||||
let yr = t.year as u16;
|
|
||||||
let yr_offset = (yr - 1970_u16) as u8;
|
|
||||||
let (yt, yu) = byte_to_bcd2(yr_offset);
|
|
||||||
|
|
||||||
use crate::pac::rtc::vals::Ampm;
|
|
||||||
|
|
||||||
rtc.tr().write(|w| {
|
|
||||||
w.set_ht(ht);
|
|
||||||
w.set_hu(hu);
|
|
||||||
w.set_mnt(mnt);
|
|
||||||
w.set_mnu(mnu);
|
|
||||||
w.set_st(st);
|
|
||||||
w.set_su(su);
|
|
||||||
w.set_pm(Ampm::AM);
|
|
||||||
});
|
|
||||||
|
|
||||||
rtc.dr().write(|w| {
|
|
||||||
w.set_dt(dt);
|
|
||||||
w.set_du(du);
|
|
||||||
w.set_mt(mt > 0);
|
|
||||||
w.set_mu(mu);
|
|
||||||
w.set_yt(yt);
|
|
||||||
w.set_yu(yu);
|
|
||||||
w.set_wdu(day_of_week_to_u8(t.day_of_week));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn datetime(
|
|
||||||
year: u16,
|
|
||||||
month: u8,
|
|
||||||
day: u8,
|
|
||||||
day_of_week: u8,
|
|
||||||
hour: u8,
|
|
||||||
minute: u8,
|
|
||||||
second: u8,
|
|
||||||
) -> Result<DateTime, Error> {
|
|
||||||
let day_of_week = day_of_week_from_u8(day_of_week)?;
|
|
||||||
Ok(DateTime {
|
|
||||||
year,
|
|
||||||
month,
|
|
||||||
day,
|
|
||||||
day_of_week,
|
|
||||||
hour,
|
|
||||||
minute,
|
|
||||||
second,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -9,7 +9,8 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
|
||||||
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError, RtcInstant};
|
||||||
|
use crate::rtc::datetime::day_of_week_to_u8;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
/// refer to AN4759 to compare features of RTC2 and RTC3
|
/// refer to AN4759 to compare features of RTC2 and RTC3
|
||||||
@ -39,48 +40,6 @@ pub enum RtcError {
|
|||||||
NotRunning,
|
NotRunning,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
|
||||||
/// Represents an instant in time that can be substracted to compute a duration
|
|
||||||
struct RtcInstant {
|
|
||||||
second: u8,
|
|
||||||
subsecond: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "low-power", feature = "defmt"))]
|
|
||||||
impl defmt::Format for RtcInstant {
|
|
||||||
fn format(&self, fmt: defmt::Formatter) {
|
|
||||||
defmt::write!(
|
|
||||||
fmt,
|
|
||||||
"{}:{}",
|
|
||||||
self.second,
|
|
||||||
RTC::regs().prer().read().prediv_s() - self.subsecond,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
|
||||||
impl core::ops::Sub for RtcInstant {
|
|
||||||
type Output = embassy_time::Duration;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
|
||||||
use embassy_time::{Duration, TICK_HZ};
|
|
||||||
|
|
||||||
let second = if self.second < rhs.second {
|
|
||||||
self.second + 60
|
|
||||||
} else {
|
|
||||||
self.second
|
|
||||||
};
|
|
||||||
|
|
||||||
let psc = RTC::regs().prer().read().prediv_s() as u32;
|
|
||||||
|
|
||||||
let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32);
|
|
||||||
let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32);
|
|
||||||
let rtc_ticks = self_ticks - other_ticks;
|
|
||||||
|
|
||||||
Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RtcTimeProvider {
|
pub struct RtcTimeProvider {
|
||||||
_private: (),
|
_private: (),
|
||||||
}
|
}
|
||||||
@ -113,7 +72,7 @@ impl RtcTimeProvider {
|
|||||||
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
|
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
|
||||||
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
|
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
|
||||||
|
|
||||||
return self::datetime::datetime(year, month, day, weekday, hour, minute, second)
|
return DateTime::from(year, month, day, weekday, hour, minute, second)
|
||||||
.map_err(RtcError::InvalidDateTime);
|
.map_err(RtcError::InvalidDateTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +93,7 @@ impl RtcTimeProvider {
|
|||||||
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
|
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
|
||||||
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
|
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
|
||||||
|
|
||||||
self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
|
DateTime::from(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,7 +143,13 @@ impl Default for RtcCalibrationCyclePeriod {
|
|||||||
impl Rtc {
|
impl Rtc {
|
||||||
pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
|
pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
|
||||||
#[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))]
|
#[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))]
|
||||||
<RTC as crate::rcc::sealed::RccPeripheral>::enable_and_reset();
|
critical_section::with(|cs| {
|
||||||
|
<RTC as crate::rcc::sealed::RccPeripheral>::enable_and_reset_with_cs(cs);
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
unsafe {
|
||||||
|
crate::rcc::REFCOUNT_STOP2 -= 1
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
@ -219,14 +184,46 @@ impl Rtc {
|
|||||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||||
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
|
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
|
||||||
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
|
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
|
||||||
self.write(true, |rtc| self::datetime::write_date_time(rtc, t));
|
self.write(true, |rtc| {
|
||||||
|
let (ht, hu) = byte_to_bcd2(t.hour() as u8);
|
||||||
|
let (mnt, mnu) = byte_to_bcd2(t.minute() as u8);
|
||||||
|
let (st, su) = byte_to_bcd2(t.second() as u8);
|
||||||
|
|
||||||
|
let (dt, du) = byte_to_bcd2(t.day() as u8);
|
||||||
|
let (mt, mu) = byte_to_bcd2(t.month() as u8);
|
||||||
|
let yr = t.year() as u16;
|
||||||
|
let yr_offset = (yr - 1970_u16) as u8;
|
||||||
|
let (yt, yu) = byte_to_bcd2(yr_offset);
|
||||||
|
|
||||||
|
use crate::pac::rtc::vals::Ampm;
|
||||||
|
|
||||||
|
rtc.tr().write(|w| {
|
||||||
|
w.set_ht(ht);
|
||||||
|
w.set_hu(hu);
|
||||||
|
w.set_mnt(mnt);
|
||||||
|
w.set_mnu(mnu);
|
||||||
|
w.set_st(st);
|
||||||
|
w.set_su(su);
|
||||||
|
w.set_pm(Ampm::AM);
|
||||||
|
});
|
||||||
|
|
||||||
|
rtc.dr().write(|w| {
|
||||||
|
w.set_dt(dt);
|
||||||
|
w.set_du(du);
|
||||||
|
w.set_mt(mt > 0);
|
||||||
|
w.set_mu(mu);
|
||||||
|
w.set_yt(yt);
|
||||||
|
w.set_yu(yu);
|
||||||
|
w.set_wdu(day_of_week_to_u8(t.day_of_week()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(not(rtc_v2f2))]
|
||||||
/// Return the current instant.
|
/// Return the current instant.
|
||||||
fn instant(&self) -> RtcInstant {
|
pub fn instant(&self) -> Result<RtcInstant, RtcError> {
|
||||||
let r = RTC::regs();
|
let r = RTC::regs();
|
||||||
let tr = r.tr().read();
|
let tr = r.tr().read();
|
||||||
let subsecond = r.ssr().read().ss();
|
let subsecond = r.ssr().read().ss();
|
||||||
@ -235,7 +232,7 @@ impl Rtc {
|
|||||||
// Unlock the registers
|
// Unlock the registers
|
||||||
r.dr().read();
|
r.dr().read();
|
||||||
|
|
||||||
RtcInstant { second, subsecond }
|
RtcInstant::from(second, subsecond.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the current datetime.
|
/// Return the current datetime.
|
||||||
|
@ -95,15 +95,16 @@ impl super::Rtc {
|
|||||||
regs.cr().modify(|w| w.set_wutie(true));
|
regs.cr().modify(|w| w.set_wutie(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let instant = self.instant().unwrap();
|
||||||
trace!(
|
trace!(
|
||||||
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
|
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
|
||||||
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
|
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
|
||||||
prescaler as u32,
|
prescaler as u32,
|
||||||
rtc_ticks,
|
rtc_ticks,
|
||||||
self.instant(),
|
instant,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none())
|
assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
@ -112,8 +113,9 @@ impl super::Rtc {
|
|||||||
pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
|
pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
|
||||||
|
let instant = self.instant().unwrap();
|
||||||
if RTC::regs().cr().read().wute() {
|
if RTC::regs().cr().read().wute() {
|
||||||
trace!("rtc: stop wakeup alarm at {}", self.instant());
|
trace!("rtc: stop wakeup alarm at {}", instant);
|
||||||
|
|
||||||
self.write(false, |regs| {
|
self.write(false, |regs| {
|
||||||
regs.cr().modify(|w| w.set_wutie(false));
|
regs.cr().modify(|w| w.set_wutie(false));
|
||||||
@ -128,10 +130,7 @@ impl super::Rtc {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stop_time
|
self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time)
|
||||||
.borrow(cs)
|
|
||||||
.take()
|
|
||||||
.map(|stop_time| self.instant() - stop_time)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
|
@ -1457,7 +1457,7 @@ cfg_if::cfg_if! {
|
|||||||
macro_rules! kernel_clk {
|
macro_rules! kernel_clk {
|
||||||
($inst:ident) => {
|
($inst:ident) => {
|
||||||
critical_section::with(|_| unsafe {
|
critical_section::with(|_| unsafe {
|
||||||
crate::rcc::get_freqs().pll48
|
crate::rcc::get_freqs().pll1_q
|
||||||
}).expect("PLL48 is required for SDIO")
|
}).expect("PLL48 is required for SDIO")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1466,20 +1466,20 @@ cfg_if::cfg_if! {
|
|||||||
(SDMMC1) => {
|
(SDMMC1) => {
|
||||||
critical_section::with(|_| unsafe {
|
critical_section::with(|_| unsafe {
|
||||||
let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc1sel();
|
let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc1sel();
|
||||||
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK {
|
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
|
||||||
crate::rcc::get_freqs().sys
|
crate::rcc::get_freqs().sys
|
||||||
} else {
|
} else {
|
||||||
crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC")
|
crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
(SDMMC2) => {
|
(SDMMC2) => {
|
||||||
critical_section::with(|_| unsafe {
|
critical_section::with(|_| unsafe {
|
||||||
let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc2sel();
|
let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc2sel();
|
||||||
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK {
|
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
|
||||||
crate::rcc::get_freqs().sys
|
crate::rcc::get_freqs().sys
|
||||||
} else {
|
} else {
|
||||||
crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC")
|
crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -57,18 +57,20 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
|||||||
_ch4: Option<PwmPin<'d, T, Ch4>>,
|
_ch4: Option<PwmPin<'d, T, Ch4>>,
|
||||||
_ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>,
|
_ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>,
|
||||||
freq: Hertz,
|
freq: Hertz,
|
||||||
|
counting_mode: CountingMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_inner(tim, freq)
|
Self::new_inner(tim, freq, counting_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
|
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
|
||||||
into_ref!(tim);
|
into_ref!(tim);
|
||||||
|
|
||||||
T::enable_and_reset();
|
T::enable_and_reset();
|
||||||
|
|
||||||
let mut this = Self { inner: tim };
|
let mut this = Self { inner: tim };
|
||||||
|
|
||||||
this.inner.set_frequency(freq);
|
this.inner.set_counting_mode(counting_mode);
|
||||||
|
this.set_freq(freq);
|
||||||
this.inner.start();
|
this.inner.start();
|
||||||
|
|
||||||
this.inner.enable_outputs();
|
this.inner.enable_outputs();
|
||||||
@ -95,7 +97,12 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_freq(&mut self, freq: Hertz) {
|
pub fn set_freq(&mut self, freq: Hertz) {
|
||||||
self.inner.set_frequency(freq);
|
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
|
||||||
|
2u8
|
||||||
|
} else {
|
||||||
|
1u8
|
||||||
|
};
|
||||||
|
self.inner.set_frequency(freq * multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_duty(&self) -> u16 {
|
pub fn get_max_duty(&self) -> u16 {
|
||||||
|
@ -29,10 +29,17 @@ pub(crate) mod sealed {
|
|||||||
Self::regs().cr1().modify(|r| r.set_cen(false));
|
Self::regs().cr1().modify(|r| r.set_cen(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset the counter value to 0
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
Self::regs().cnt().write(|r| r.set_cnt(0));
|
Self::regs().cnt().write(|r| r.set_cnt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
|
||||||
|
///
|
||||||
|
/// This means that in the default edge-aligned mode,
|
||||||
|
/// the timer counter will wrap around at the same frequency as is being set.
|
||||||
|
/// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved
|
||||||
|
/// because it needs to count up and down.
|
||||||
fn set_frequency(&mut self, frequency: Hertz) {
|
fn set_frequency(&mut self, frequency: Hertz) {
|
||||||
let f = frequency.0;
|
let f = frequency.0;
|
||||||
let timer_f = Self::frequency().0;
|
let timer_f = Self::frequency().0;
|
||||||
@ -85,8 +92,21 @@ pub(crate) mod sealed {
|
|||||||
pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
|
pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
|
||||||
fn regs_gp16() -> crate::pac::timer::TimGp16;
|
fn regs_gp16() -> crate::pac::timer::TimGp16;
|
||||||
|
|
||||||
fn set_count_direction(&mut self, direction: vals::Dir) {
|
fn set_counting_mode(&mut self, mode: CountingMode) {
|
||||||
Self::regs_gp16().cr1().modify(|r| r.set_dir(direction));
|
let (cms, dir) = mode.into();
|
||||||
|
|
||||||
|
let timer_enabled = Self::regs().cr1().read().cen();
|
||||||
|
// Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running.
|
||||||
|
// Changing direction is discouraged while the timer is running.
|
||||||
|
assert!(!timer_enabled);
|
||||||
|
|
||||||
|
Self::regs_gp16().cr1().modify(|r| r.set_dir(dir));
|
||||||
|
Self::regs_gp16().cr1().modify(|r| r.set_cms(cms))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_counting_mode(&self) -> CountingMode {
|
||||||
|
let cr1 = Self::regs_gp16().cr1().read();
|
||||||
|
(cr1.cms(), cr1.dir()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_clock_division(&mut self, ckd: vals::Ckd) {
|
fn set_clock_division(&mut self, ckd: vals::Ckd) {
|
||||||
@ -293,6 +313,73 @@ impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub enum CountingMode {
|
||||||
|
#[default]
|
||||||
|
/// The timer counts up to the reload value and then resets back to 0.
|
||||||
|
EdgeAlignedUp,
|
||||||
|
/// The timer counts down to 0 and then resets back to the reload value.
|
||||||
|
EdgeAlignedDown,
|
||||||
|
/// The timer counts up to the reload value and then counts back to 0.
|
||||||
|
///
|
||||||
|
/// The output compare interrupt flags of channels configured in output are
|
||||||
|
/// set when the counter is counting down.
|
||||||
|
CenterAlignedDownInterrupts,
|
||||||
|
/// The timer counts up to the reload value and then counts back to 0.
|
||||||
|
///
|
||||||
|
/// The output compare interrupt flags of channels configured in output are
|
||||||
|
/// set when the counter is counting up.
|
||||||
|
CenterAlignedUpInterrupts,
|
||||||
|
/// The timer counts up to the reload value and then counts back to 0.
|
||||||
|
///
|
||||||
|
/// The output compare interrupt flags of channels configured in output are
|
||||||
|
/// set when the counter is counting both up or down.
|
||||||
|
CenterAlignedBothInterrupts,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CountingMode {
|
||||||
|
pub fn is_edge_aligned(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_center_aligned(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
CountingMode::CenterAlignedDownInterrupts
|
||||||
|
| CountingMode::CenterAlignedUpInterrupts
|
||||||
|
| CountingMode::CenterAlignedBothInterrupts => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CountingMode> for (vals::Cms, vals::Dir) {
|
||||||
|
fn from(value: CountingMode) -> Self {
|
||||||
|
match value {
|
||||||
|
CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP),
|
||||||
|
CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN),
|
||||||
|
CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP),
|
||||||
|
CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP),
|
||||||
|
CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(vals::Cms, vals::Dir)> for CountingMode {
|
||||||
|
fn from(value: (vals::Cms, vals::Dir)) -> Self {
|
||||||
|
match value {
|
||||||
|
(vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp,
|
||||||
|
(vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown,
|
||||||
|
(vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts,
|
||||||
|
(vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts,
|
||||||
|
(vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum OutputCompareMode {
|
pub enum OutputCompareMode {
|
||||||
Frozen,
|
Frozen,
|
||||||
@ -471,9 +558,5 @@ foreach_interrupt! {
|
|||||||
crate::pac::$inst
|
crate::pac::$inst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -56,18 +56,20 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
|||||||
_ch3: Option<PwmPin<'d, T, Ch3>>,
|
_ch3: Option<PwmPin<'d, T, Ch3>>,
|
||||||
_ch4: Option<PwmPin<'d, T, Ch4>>,
|
_ch4: Option<PwmPin<'d, T, Ch4>>,
|
||||||
freq: Hertz,
|
freq: Hertz,
|
||||||
|
counting_mode: CountingMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_inner(tim, freq)
|
Self::new_inner(tim, freq, counting_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
|
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
|
||||||
into_ref!(tim);
|
into_ref!(tim);
|
||||||
|
|
||||||
T::enable_and_reset();
|
T::enable_and_reset();
|
||||||
|
|
||||||
let mut this = Self { inner: tim };
|
let mut this = Self { inner: tim };
|
||||||
|
|
||||||
this.inner.set_frequency(freq);
|
this.inner.set_counting_mode(counting_mode);
|
||||||
|
this.set_freq(freq);
|
||||||
this.inner.start();
|
this.inner.start();
|
||||||
|
|
||||||
this.inner.enable_outputs();
|
this.inner.enable_outputs();
|
||||||
@ -92,7 +94,12 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_freq(&mut self, freq: Hertz) {
|
pub fn set_freq(&mut self, freq: Hertz) {
|
||||||
self.inner.set_frequency(freq);
|
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
|
||||||
|
2u8
|
||||||
|
} else {
|
||||||
|
1u8
|
||||||
|
};
|
||||||
|
self.inner.set_frequency(freq * multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_duty(&self) -> u16 {
|
pub fn get_max_duty(&self) -> u16 {
|
||||||
|
@ -116,28 +116,28 @@ pub struct BufferedUartRx<'d, T: BasicInstance> {
|
|||||||
|
|
||||||
impl<'d, T: BasicInstance> SetConfig for BufferedUart<'d, T> {
|
impl<'d, T: BasicInstance> SetConfig for BufferedUart<'d, T> {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
type ConfigError = ();
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
self.set_config(config).map_err(|_| ())
|
self.set_config(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: BasicInstance> SetConfig for BufferedUartRx<'d, T> {
|
impl<'d, T: BasicInstance> SetConfig for BufferedUartRx<'d, T> {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
type ConfigError = ();
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
self.set_config(config).map_err(|_| ())
|
self.set_config(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: BasicInstance> SetConfig for BufferedUartTx<'d, T> {
|
impl<'d, T: BasicInstance> SetConfig for BufferedUartTx<'d, T> {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
type ConfigError = ();
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
self.set_config(config).map_err(|_| ())
|
self.set_config(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,9 +233,6 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
|||||||
configure(r, &config, T::frequency(), T::KIND, true, true)?;
|
configure(r, &config, T::frequency(), T::KIND, true, true)?;
|
||||||
|
|
||||||
r.cr1().modify(|w| {
|
r.cr1().modify(|w| {
|
||||||
#[cfg(lpuart_v2)]
|
|
||||||
w.set_fifoen(true);
|
|
||||||
|
|
||||||
w.set_rxneie(true);
|
w.set_rxneie(true);
|
||||||
w.set_idleie(true);
|
w.set_idleie(true);
|
||||||
});
|
});
|
||||||
@ -254,7 +251,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
reconfigure::<T>(config)
|
reconfigure::<T>(config)?;
|
||||||
|
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_rxneie(true);
|
||||||
|
w.set_idleie(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +338,14 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
reconfigure::<T>(config)
|
reconfigure::<T>(config)?;
|
||||||
|
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_rxneie(true);
|
||||||
|
w.set_idleie(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +419,14 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
reconfigure::<T>(config)
|
reconfigure::<T>(config)?;
|
||||||
|
|
||||||
|
T::regs().cr1().modify(|w| {
|
||||||
|
w.set_rxneie(true);
|
||||||
|
w.set_idleie(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +108,7 @@ pub enum StopBits {
|
|||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
BaudrateTooLow,
|
BaudrateTooLow,
|
||||||
BaudrateTooHigh,
|
BaudrateTooHigh,
|
||||||
|
RxOrTxNotEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -181,11 +182,11 @@ pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
|
|||||||
|
|
||||||
impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> {
|
impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
type ConfigError = ();
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
self.tx.set_config(config).map_err(|_| ())?;
|
self.tx.set_config(config)?;
|
||||||
self.rx.set_config(config).map_err(|_| ())
|
self.rx.set_config(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,10 +197,10 @@ pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> {
|
|||||||
|
|
||||||
impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> {
|
impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
type ConfigError = ();
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
self.set_config(config).map_err(|_| ())
|
self.set_config(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,10 +214,10 @@ pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> {
|
|||||||
|
|
||||||
impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> {
|
impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
type ConfigError = ();
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
self.set_config(config).map_err(|_| ())
|
self.set_config(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,7 +867,7 @@ fn configure(
|
|||||||
enable_tx: bool,
|
enable_tx: bool,
|
||||||
) -> Result<(), ConfigError> {
|
) -> Result<(), ConfigError> {
|
||||||
if !enable_rx && !enable_tx {
|
if !enable_rx && !enable_tx {
|
||||||
panic!("USART: At least one of RX or TX should be enabled");
|
return Err(ConfigError::RxOrTxNotEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(usart_v4))]
|
#[cfg(not(usart_v4))]
|
||||||
@ -909,6 +910,11 @@ fn configure(
|
|||||||
brr + rounding
|
brr + rounding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UART must be disabled during configuration.
|
||||||
|
r.cr1().modify(|w| {
|
||||||
|
w.set_ue(false);
|
||||||
|
});
|
||||||
|
|
||||||
#[cfg(not(usart_v1))]
|
#[cfg(not(usart_v1))]
|
||||||
let mut over8 = false;
|
let mut over8 = false;
|
||||||
let mut found_brr = None;
|
let mut found_brr = None;
|
||||||
@ -968,6 +974,12 @@ fn configure(
|
|||||||
#[cfg(any(usart_v3, usart_v4))]
|
#[cfg(any(usart_v3, usart_v4))]
|
||||||
w.set_swap(config.swap_rx_tx);
|
w.set_swap(config.swap_rx_tx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[cfg(not(usart_v1))]
|
||||||
|
r.cr3().modify(|w| {
|
||||||
|
w.set_onebit(config.assume_noise_free);
|
||||||
|
});
|
||||||
|
|
||||||
r.cr1().write(|w| {
|
r.cr1().write(|w| {
|
||||||
// enable uart
|
// enable uart
|
||||||
w.set_ue(true);
|
w.set_ue(true);
|
||||||
@ -976,6 +988,7 @@ fn configure(
|
|||||||
// enable receiver
|
// enable receiver
|
||||||
w.set_re(enable_rx);
|
w.set_re(enable_rx);
|
||||||
// configure word size
|
// configure word size
|
||||||
|
// if using odd or even parity it must be configured to 9bits
|
||||||
w.set_m0(if config.parity != Parity::ParityNone {
|
w.set_m0(if config.parity != Parity::ParityNone {
|
||||||
vals::M0::BIT9
|
vals::M0::BIT9
|
||||||
} else {
|
} else {
|
||||||
@ -994,11 +1007,6 @@ fn configure(
|
|||||||
w.set_fifoen(true);
|
w.set_fifoen(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(usart_v1))]
|
|
||||||
r.cr3().modify(|w| {
|
|
||||||
w.set_onebit(config.assume_noise_free);
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,10 +18,10 @@ pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma<T>> {
|
|||||||
|
|
||||||
impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> SetConfig for RingBufferedUartRx<'d, T, RxDma> {
|
impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> SetConfig for RingBufferedUartRx<'d, T, RxDma> {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
type ConfigError = ();
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
self.set_config(config).map_err(|_| ())
|
self.set_config(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,17 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 0.1.6 - ???
|
||||||
|
|
||||||
|
- Added tick rates in multiples of 10 kHz
|
||||||
|
|
||||||
|
## 0.1.5 - 2023-10-16
|
||||||
|
|
||||||
|
- Added `links` key to Cargo.toml, to prevent multiple copies of this crate in the same binary.
|
||||||
|
Needed because different copies might get different tick rates, causing
|
||||||
|
wrong delays if the time driver is using one copy and user code is using another.
|
||||||
|
This is especially common when mixing crates from crates.io and git.
|
||||||
|
|
||||||
## 0.1.4 - 2023-10-12
|
## 0.1.4 - 2023-10-12
|
||||||
|
|
||||||
- Added more tick rates
|
- Added more tick rates
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-time"
|
name = "embassy-time"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Instant and Duration for embedded no-std systems, with async timer support"
|
description = "Instant and Duration for embedded no-std systems, with async timer support"
|
||||||
repository = "https://github.com/embassy-rs/embassy"
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
@ -13,6 +13,12 @@ categories = [
|
|||||||
"asynchronous",
|
"asynchronous",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Prevent multiple copies of this crate in the same binary.
|
||||||
|
# Needed because different copies might get different tick rates, causing
|
||||||
|
# wrong delays if the time driver is using one copy and user code is using another.
|
||||||
|
# This is especially common when mixing crates from crates.io and git.
|
||||||
|
links = "embassy-time"
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-v$VERSION/embassy-time/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-v$VERSION/embassy-time/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time/src/"
|
||||||
@ -120,6 +126,25 @@ tick-hz-65_536_000 = []
|
|||||||
tick-hz-131_072_000 = []
|
tick-hz-131_072_000 = []
|
||||||
tick-hz-262_144_000 = []
|
tick-hz-262_144_000 = []
|
||||||
tick-hz-524_288_000 = []
|
tick-hz-524_288_000 = []
|
||||||
|
tick-hz-20_000 = []
|
||||||
|
tick-hz-40_000 = []
|
||||||
|
tick-hz-80_000 = []
|
||||||
|
tick-hz-160_000 = []
|
||||||
|
tick-hz-320_000 = []
|
||||||
|
tick-hz-640_000 = []
|
||||||
|
tick-hz-1_280_000 = []
|
||||||
|
tick-hz-2_560_000 = []
|
||||||
|
tick-hz-5_120_000 = []
|
||||||
|
tick-hz-10_240_000 = []
|
||||||
|
tick-hz-20_480_000 = []
|
||||||
|
tick-hz-40_960_000 = []
|
||||||
|
tick-hz-81_920_000 = []
|
||||||
|
tick-hz-163_840_000 = []
|
||||||
|
tick-hz-327_680_000 = []
|
||||||
|
tick-hz-655_360_000 = []
|
||||||
|
tick-hz-1_310_720_000 = []
|
||||||
|
tick-hz-2_621_440_000 = []
|
||||||
|
tick-hz-5_242_880_000 = []
|
||||||
tick-hz-2_000_000 = []
|
tick-hz-2_000_000 = []
|
||||||
tick-hz-3_000_000 = []
|
tick-hz-3_000_000 = []
|
||||||
tick-hz-4_000_000 = []
|
tick-hz-4_000_000 = []
|
||||||
|
3
embassy-time/build.rs
Normal file
3
embassy-time/build.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// empty, needed to be able to use `links` in Cargo.toml.
|
||||||
|
|
||||||
|
fn main() {}
|
@ -13,6 +13,8 @@ for i in range(1, 25):
|
|||||||
ticks.append(2**i)
|
ticks.append(2**i)
|
||||||
for i in range(1, 20):
|
for i in range(1, 20):
|
||||||
ticks.append(2**i * 1000)
|
ticks.append(2**i * 1000)
|
||||||
|
for i in range(1, 20):
|
||||||
|
ticks.append(2**i * 10000)
|
||||||
for i in range(1, 10):
|
for i in range(1, 10):
|
||||||
ticks.append(2**i * 1000000)
|
ticks.append(2**i * 1000000)
|
||||||
ticks.append(2**i * 9 // 8 * 1000000)
|
ticks.append(2**i * 9 // 8 * 1000000)
|
||||||
|
@ -36,11 +36,11 @@ mod eha {
|
|||||||
|
|
||||||
impl embedded_hal_async::delay::DelayUs for Delay {
|
impl embedded_hal_async::delay::DelayUs for Delay {
|
||||||
async fn delay_us(&mut self, micros: u32) {
|
async fn delay_us(&mut self, micros: u32) {
|
||||||
Timer::after(Duration::from_micros(micros as _)).await
|
Timer::after_micros(micros as _).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delay_ms(&mut self, millis: u32) {
|
async fn delay_ms(&mut self, millis: u32) {
|
||||||
Timer::after(Duration::from_millis(millis as _)).await
|
Timer::after_millis(millis as _).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,44 @@ pub const TICK_HZ: u64 = 131_072_000;
|
|||||||
pub const TICK_HZ: u64 = 262_144_000;
|
pub const TICK_HZ: u64 = 262_144_000;
|
||||||
#[cfg(feature = "tick-hz-524_288_000")]
|
#[cfg(feature = "tick-hz-524_288_000")]
|
||||||
pub const TICK_HZ: u64 = 524_288_000;
|
pub const TICK_HZ: u64 = 524_288_000;
|
||||||
|
#[cfg(feature = "tick-hz-20_000")]
|
||||||
|
pub const TICK_HZ: u64 = 20_000;
|
||||||
|
#[cfg(feature = "tick-hz-40_000")]
|
||||||
|
pub const TICK_HZ: u64 = 40_000;
|
||||||
|
#[cfg(feature = "tick-hz-80_000")]
|
||||||
|
pub const TICK_HZ: u64 = 80_000;
|
||||||
|
#[cfg(feature = "tick-hz-160_000")]
|
||||||
|
pub const TICK_HZ: u64 = 160_000;
|
||||||
|
#[cfg(feature = "tick-hz-320_000")]
|
||||||
|
pub const TICK_HZ: u64 = 320_000;
|
||||||
|
#[cfg(feature = "tick-hz-640_000")]
|
||||||
|
pub const TICK_HZ: u64 = 640_000;
|
||||||
|
#[cfg(feature = "tick-hz-1_280_000")]
|
||||||
|
pub const TICK_HZ: u64 = 1_280_000;
|
||||||
|
#[cfg(feature = "tick-hz-2_560_000")]
|
||||||
|
pub const TICK_HZ: u64 = 2_560_000;
|
||||||
|
#[cfg(feature = "tick-hz-5_120_000")]
|
||||||
|
pub const TICK_HZ: u64 = 5_120_000;
|
||||||
|
#[cfg(feature = "tick-hz-10_240_000")]
|
||||||
|
pub const TICK_HZ: u64 = 10_240_000;
|
||||||
|
#[cfg(feature = "tick-hz-20_480_000")]
|
||||||
|
pub const TICK_HZ: u64 = 20_480_000;
|
||||||
|
#[cfg(feature = "tick-hz-40_960_000")]
|
||||||
|
pub const TICK_HZ: u64 = 40_960_000;
|
||||||
|
#[cfg(feature = "tick-hz-81_920_000")]
|
||||||
|
pub const TICK_HZ: u64 = 81_920_000;
|
||||||
|
#[cfg(feature = "tick-hz-163_840_000")]
|
||||||
|
pub const TICK_HZ: u64 = 163_840_000;
|
||||||
|
#[cfg(feature = "tick-hz-327_680_000")]
|
||||||
|
pub const TICK_HZ: u64 = 327_680_000;
|
||||||
|
#[cfg(feature = "tick-hz-655_360_000")]
|
||||||
|
pub const TICK_HZ: u64 = 655_360_000;
|
||||||
|
#[cfg(feature = "tick-hz-1_310_720_000")]
|
||||||
|
pub const TICK_HZ: u64 = 1_310_720_000;
|
||||||
|
#[cfg(feature = "tick-hz-2_621_440_000")]
|
||||||
|
pub const TICK_HZ: u64 = 2_621_440_000;
|
||||||
|
#[cfg(feature = "tick-hz-5_242_880_000")]
|
||||||
|
pub const TICK_HZ: u64 = 5_242_880_000;
|
||||||
#[cfg(feature = "tick-hz-2_000_000")]
|
#[cfg(feature = "tick-hz-2_000_000")]
|
||||||
pub const TICK_HZ: u64 = 2_000_000;
|
pub const TICK_HZ: u64 = 2_000_000;
|
||||||
#[cfg(feature = "tick-hz-3_000_000")]
|
#[cfg(feature = "tick-hz-3_000_000")]
|
||||||
@ -334,6 +372,25 @@ pub const TICK_HZ: u64 = 980_000_000;
|
|||||||
feature = "tick-hz-131_072_000",
|
feature = "tick-hz-131_072_000",
|
||||||
feature = "tick-hz-262_144_000",
|
feature = "tick-hz-262_144_000",
|
||||||
feature = "tick-hz-524_288_000",
|
feature = "tick-hz-524_288_000",
|
||||||
|
feature = "tick-hz-20_000",
|
||||||
|
feature = "tick-hz-40_000",
|
||||||
|
feature = "tick-hz-80_000",
|
||||||
|
feature = "tick-hz-160_000",
|
||||||
|
feature = "tick-hz-320_000",
|
||||||
|
feature = "tick-hz-640_000",
|
||||||
|
feature = "tick-hz-1_280_000",
|
||||||
|
feature = "tick-hz-2_560_000",
|
||||||
|
feature = "tick-hz-5_120_000",
|
||||||
|
feature = "tick-hz-10_240_000",
|
||||||
|
feature = "tick-hz-20_480_000",
|
||||||
|
feature = "tick-hz-40_960_000",
|
||||||
|
feature = "tick-hz-81_920_000",
|
||||||
|
feature = "tick-hz-163_840_000",
|
||||||
|
feature = "tick-hz-327_680_000",
|
||||||
|
feature = "tick-hz-655_360_000",
|
||||||
|
feature = "tick-hz-1_310_720_000",
|
||||||
|
feature = "tick-hz-2_621_440_000",
|
||||||
|
feature = "tick-hz-5_242_880_000",
|
||||||
feature = "tick-hz-2_000_000",
|
feature = "tick-hz-2_000_000",
|
||||||
feature = "tick-hz-3_000_000",
|
feature = "tick-hz-3_000_000",
|
||||||
feature = "tick-hz-4_000_000",
|
feature = "tick-hz-4_000_000",
|
||||||
|
@ -64,6 +64,42 @@ impl Timer {
|
|||||||
yielded_once: false,
|
yielded_once: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expire after the specified number of ticks.
|
||||||
|
///
|
||||||
|
/// This method is a convenience wrapper for calling `Timer::after(Duration::from_ticks())`.
|
||||||
|
/// For more details, refer to [`Timer::after()`] and [`Duration::from_ticks()`].
|
||||||
|
#[inline]
|
||||||
|
pub fn after_ticks(ticks: u64) -> Self {
|
||||||
|
Self::after(Duration::from_ticks(ticks))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expire after the specified number of microseconds.
|
||||||
|
///
|
||||||
|
/// This method is a convenience wrapper for calling `Timer::after(Duration::from_micros())`.
|
||||||
|
/// For more details, refer to [`Timer::after()`] and [`Duration::from_micros()`].
|
||||||
|
#[inline]
|
||||||
|
pub fn after_micros(micros: u64) -> Self {
|
||||||
|
Self::after(Duration::from_micros(micros))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expire after the specified number of milliseconds.
|
||||||
|
///
|
||||||
|
/// This method is a convenience wrapper for calling `Timer::after(Duration::from_millis())`.
|
||||||
|
/// For more details, refer to [`Timer::after`] and [`Duration::from_millis()`].
|
||||||
|
#[inline]
|
||||||
|
pub fn after_millis(millis: u64) -> Self {
|
||||||
|
Self::after(Duration::from_millis(millis))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expire after the specified number of seconds.
|
||||||
|
///
|
||||||
|
/// This method is a convenience wrapper for calling `Timer::after(Duration::from_secs())`.
|
||||||
|
/// For more details, refer to [`Timer::after`] and [`Duration::from_secs()`].
|
||||||
|
#[inline]
|
||||||
|
pub fn after_secs(secs: u64) -> Self {
|
||||||
|
Self::after(Duration::from_secs(secs))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Unpin for Timer {}
|
impl Unpin for Timer {}
|
||||||
|
@ -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 }
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user