Compare commits

...

21 Commits

Author SHA1 Message Date
630443a4d6 net-wiznet: report link up/down on cable plug/unplug. 2023-10-20 01:29:10 +02:00
035800bfbd Merge pull request #2091 from embassy-rs/comment-memory-x
docs: add linker script comments
2023-10-19 08:19:42 +00:00
c7803bb8f4 docs: add linker script comments
Existing comment were outdated. Provide an example configuration
for using the softdevice with the nRF52 examples.
2023-10-19 09:29:20 +02:00
d496a1213c Merge pull request #2090 from eZioPan/rcc-init-bypass-oden
bypass `ODEN` in `rcc::init()` if chip doesn't have it
2023-10-18 12:15:30 +00:00
241488ef1c bypass ODEN if chip doesn't have it 2023-10-18 19:42:31 +08:00
88b2cdd6a0 Merge pull request #2087 from riley-williams/rp2040-pwm-docs
Add docs to RP2040 PWM config
2023-10-18 06:30:08 +00:00
35ffdf2143 Merge pull request #2076 from embassy-rs/net-driver-simplify
net/driver: remove Medium, make HardwareAddress non_exhaustive.
2023-10-18 03:39:41 +00:00
3cbc687424 net/driver: remove Medium, make HardwareAddress non_exhaustive. 2023-10-18 05:28:16 +02:00
4f7b831676 Merge pull request #2088 from embassy-rs/rcc-no-spaghetti
stm32: rcc no spaghetti
2023-10-18 03:23:47 +00:00
f20f170b1f stm32/rcc: refactor and unify f4 into f7. 2023-10-18 05:11:31 +02:00
67010d123c stm32/rcc: refactor f7. 2023-10-18 05:01:11 +02:00
51708c8ed1 Merge pull request #2089 from artisdom/patch-1
Update basic_application.adoc
2023-10-18 02:59:48 +00:00
361fde35cf stm32/rcc: wait for mux switch. 2023-10-18 04:32:18 +02:00
7ce3b19389 stm32/rcc: remove unused enum. 2023-10-18 04:32:18 +02:00
10f08445e4 Update basic_application.adoc
typo: change "embassy::main" to "embassy_executor::main"
2023-10-18 14:53:49 +13:00
f24a1b62bb Merge pull request #2085 from xoviat/rcc
stm32: update metapac
2023-10-18 01:33:00 +00:00
bbd12c9372 stm32: update metapac 2023-10-17 20:31:44 -05:00
6906cc9c25 remove trailing spaces 2023-10-17 19:30:53 -04:00
cb211f88d3 Grammar and formatting 2023-10-17 19:17:29 -04:00
3f262a2603 Add docs to RP2040 PWM 2023-10-17 19:05:35 -04:00
d94b9fe6fb Merge pull request #2082 from embassy-rs/stm32wl-hil
stm32/tests: add stm32wl hil.
2023-10-17 14:58:53 +00:00
47 changed files with 751 additions and 931 deletions

View File

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

View File

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

View File

@ -5,14 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0 - 2023-10-15
## 0.2.0 - 2023-10-18
- Update embassy-net-driver
- `Runner::new` now takes an `embassy_net_driver::HardwareAddress` parameter
- Added `Runner::set_ieee802154_address`, `Runner::ieee802154_address`
- Update `embassy-net-driver` to v0.2
- `Runner::new` now takes an `embassy_net_driver::HardwareAddress` parameter.
- `Runner::set_ethernet_address` is now `set_hardware_address`.
## 0.1.0 - 2023-06-29
- First release

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ 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-15
## 0.2.0 - 2023-10-18
- Re-export `smoltcp::wire::IpEndpoint`
- Add poll functions on UdpSocket
@ -27,5 +27,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## 0.1.0 - 2023-06-29
- First release

View File

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

View File

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

View File

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

View File

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

View File

@ -58,7 +58,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c20cbde88fdfaef4645361d09df0cb63a4dc6462" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5b04234fbe61ea875f1a904cd5f68795daaeb526" }
vcell = "0.1.3"
bxcan = "0.7.0"
nb = "1.0.0"
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies]
proc-macro2 = "1.0.36"
quote = "1.0.15"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c20cbde88fdfaef4645361d09df0cb63a4dc6462", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5b04234fbe61ea875f1a904cd5f68795daaeb526", default-features = false, features = ["metadata"]}
[features]

View File

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

View File

@ -169,7 +169,14 @@ pub(crate) unsafe fn init(config: Config) {
#[cfg(not(rcc_f100))]
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
w.set_sw(if pllmul_bits.is_some() {
Sw::PLL
#[cfg(not(rcc_f1cl))]
{
Sw::PLL1_P
}
#[cfg(rcc_f1cl)]
{
Sw::PLL
}
} else if config.hse.is_some() {
Sw::HSE
} else {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -131,7 +131,7 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
(freq, Sw::PLL)
(freq, Sw::PLL1_P)
}
};

View File

@ -187,12 +187,12 @@ pub(crate) unsafe fn init(config: Config) {
let sys_clk = match config.mux {
ClockSrc::HSE => hse.unwrap(),
#[cfg(rcc_l5)]
ClockSrc::HSI16 => hsi16.unwrap(),
#[cfg(not(rcc_l5))]
ClockSrc::HSI => hsi16.unwrap(),
ClockSrc::MSI => msi.unwrap(),
ClockSrc::PLL => pll._r.unwrap(),
#[cfg(rcc_l4)]
ClockSrc::PLL1_P => pll._r.unwrap(),
#[cfg(not(rcc_l4))]
ClockSrc::PLL1_R => pll._r.unwrap(),
};
#[cfg(stm32l4)]
@ -203,9 +203,6 @@ pub(crate) unsafe fn init(config: Config) {
Clk48Src::HSI48 => hsi48,
Clk48Src::MSI => msi,
Clk48Src::PLLSAI1_Q => pllsai1._q,
#[cfg(rcc_l5)]
Clk48Src::PLL_Q => pll._q,
#[cfg(not(rcc_l5))]
Clk48Src::PLL1_Q => pll._q,
};
@ -244,6 +241,7 @@ pub(crate) unsafe fn init(config: Config) {
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
while RCC.cfgr().read().sws() != config.mux {}
let ahb_freq = sys_clk / config.ahb_pre;
@ -363,9 +361,6 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
let pll_src = match pll.source {
PLLSource::NONE => panic!("must not select PLL source as NONE"),
PLLSource::HSE => input.hse,
#[cfg(rcc_l5)]
PLLSource::HSI16 => input.hsi16,
#[cfg(not(rcc_l5))]
PLLSource::HSI => input.hsi16,
PLLSource::MSI => input.msi,
};

View File

@ -13,8 +13,7 @@ pub use mco::*;
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
#[cfg_attr(rcc_f2, path = "f2.rs")]
#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")]
#[cfg_attr(rcc_f7, path = "f7.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")]
#[cfg_attr(rcc_c0, path = "c0.rs")]
#[cfg_attr(rcc_g0, path = "g0.rs")]
#[cfg_attr(rcc_g4, path = "g4.rs")]

View File

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

View File

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

View File

@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI;
use embassy_stm32::eth::{Ethernet, PacketQueue};
use embassy_stm32::peripherals::ETH;
use embassy_stm32::rng::Rng;
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
use embassy_time::Timer;
use embedded_io_async::Write;
@ -32,7 +32,25 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(200));
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL180,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
divq: None,
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
let p = embassy_stm32::init(config);
info!("Hello World!");

View File

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

View File

@ -5,7 +5,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::sdmmc::{DataBlock, Sdmmc};
use embassy_stm32::time::mhz;
use embassy_stm32::time::{mhz, Hertz};
use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config};
use {defmt_rtt as _, panic_probe as _};
@ -20,8 +20,25 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(48));
config.rcc.pll48 = true;
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
let p = embassy_stm32::init(config);
info!("Hello World!");

View File

@ -7,7 +7,7 @@ use embassy_executor::Spawner;
use embassy_net::tcp::TcpSocket;
use embassy_net::{Stack, StackResources};
use embassy_stm32::rng::{self, Rng};
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::Driver;
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};
@ -46,9 +46,25 @@ async fn main(spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
config.rcc.pll48 = true;
config.rcc.sys_ck = Some(mhz(48));
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
let p = embassy_stm32::init(config);
// Create the driver, from the HAL.

View File

@ -4,7 +4,7 @@
use defmt::{panic, *};
use embassy_executor::Spawner;
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
@ -22,9 +22,25 @@ async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
config.rcc.pll48 = true;
config.rcc.sys_ck = Some(mhz(48));
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
let p = embassy_stm32::init(config);
// Create the driver, from the HAL.

View File

@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI;
use embassy_stm32::eth::{Ethernet, PacketQueue};
use embassy_stm32::peripherals::ETH;
use embassy_stm32::rng::Rng;
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
use embassy_time::Timer;
use embedded_io_async::Write;
@ -33,7 +33,25 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(200));
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: None,
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
let p = embassy_stm32::init(config);
info!("Hello World!");

View File

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

View File

@ -5,7 +5,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::sdmmc::Sdmmc;
use embassy_stm32::time::mhz;
use embassy_stm32::time::{mhz, Hertz};
use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config};
use {defmt_rtt as _, panic_probe as _};
@ -16,8 +16,25 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(200));
config.rcc.pll48 = true;
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
let p = embassy_stm32::init(config);
info!("Hello World!");

View File

@ -4,7 +4,7 @@
use defmt::{panic, *};
use embassy_executor::Spawner;
use embassy_stm32::time::mhz;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
@ -22,10 +22,25 @@ async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
config.rcc.hse = Some(mhz(8));
config.rcc.pll48 = true;
config.rcc.sys_ck = Some(mhz(200));
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
let p = embassy_stm32::init(config);
// Create the driver, from the HAL.

View File

@ -16,7 +16,7 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.hsi16 = true;
config.rcc.pll = Some(Pll {
source: PLLSource::HSI,

View File

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

View File

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

View File

@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) {
let mut config = Config::default();
config.rcc.hsi48 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.hsi16 = true;
config.rcc.pll = Some(Pll {
source: PLLSource::HSI,

View File

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

View File

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

View File

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

View File

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

View File

@ -224,16 +224,44 @@ pub fn config() -> Config {
#[cfg(feature = "stm32f429zi")]
{
// TODO: stm32f429zi can do up to 180mhz, but that makes tests fail.
// perhaps we have some bug w.r.t overdrive.
config.rcc.sys_ck = Some(Hertz(168_000_000));
config.rcc.pclk1 = Some(Hertz(42_000_000));
config.rcc.pclk2 = Some(Hertz(84_000_000));
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL180,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
divq: None,
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
#[cfg(feature = "stm32f767zi")]
{
config.rcc.sys_ck = Some(Hertz(200_000_000));
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz.
divq: None,
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
#[cfg(feature = "stm32h563zi")]
@ -295,7 +323,14 @@ pub fn config() -> Config {
#[cfg(any(feature = "stm32l496zg", feature = "stm32l4a6zg", feature = "stm32l4r5zi"))]
{
use embassy_stm32::rcc::*;
config.rcc.mux = ClockSrc::PLL;
#[cfg(feature = "stm32l4r5zi")]
{
config.rcc.mux = ClockSrc::PLL1_R;
}
#[cfg(not(feature = "stm32l4r5zi"))]
{
config.rcc.mux = ClockSrc::PLL1_P;
}
config.rcc.hsi16 = true;
config.rcc.pll = Some(Pll {
source: PLLSource::HSI,
@ -320,10 +355,10 @@ pub fn config() -> Config {
{
use embassy_stm32::rcc::*;
config.rcc.hsi16 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.pll = Some(Pll {
// 110Mhz clock (16 / 4 * 55 / 2)
source: PLLSource::HSI16,
source: PLLSource::HSI,
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL55,
divp: None,