Compare commits
242 Commits
stm32-cs-s
...
bufferedua
Author | SHA1 | Date | |
---|---|---|---|
6ccd8c051e | |||
c46418f123 | |||
19ff043acd | |||
ea99671729 | |||
8eff749823 | |||
cdcd3e26dd | |||
18c1f9dd56 | |||
ac7134ed0d | |||
ace5221080 | |||
2376b3bdfa | |||
f00e97a5f1 | |||
066dc297ed | |||
4fe344ebc0 | |||
39c7371621 | |||
4647df14b1 | |||
b3367be9c8 | |||
da4feb3693 | |||
05a4bb3a4a | |||
4b4c28d875 | |||
1977acdb11 | |||
0b015bd727 | |||
79acb560ec | |||
94586576a0 | |||
a3e200d011 | |||
3bccb67231 | |||
553f0158c0 | |||
b44a5fcaf6 | |||
8effff3383 | |||
05f983c607 | |||
d44383e9a7 | |||
37a773c037 | |||
d1adc93614 | |||
78a7ee7ec4 | |||
e3fe13e905 | |||
38bfa6916f | |||
50139752bc | |||
db4cd73894 | |||
8369f7614a | |||
326bc98bd2 | |||
584fc358fd | |||
b8f9341edc | |||
7ca557b917 | |||
8d8d50cc7a | |||
5948d934d3 | |||
746936f8cd | |||
8f543062aa | |||
15660cfc68 | |||
74f70dc7b4 | |||
7084570478 | |||
58719dcb58 | |||
b4eef6b1ee | |||
70a700e430 | |||
b8679c0cc8 | |||
ad861179cc | |||
d9b00c01e0 | |||
239ad5ebea | |||
46cffcc8d4 | |||
e6462f1e88 | |||
d464d1a841 | |||
980c3cf42b | |||
28eb4cd817 | |||
3de01bc223 | |||
0272deb158 | |||
c4a8b79dbc | |||
fa45dcd034 | |||
6ff91851b1 | |||
8911a4d855 | |||
056c409443 | |||
3f2abd4fd5 | |||
dc467e89a0 | |||
655ed3aa88 | |||
14ec0d27bf | |||
649f1a122a | |||
413b394d31 | |||
7ea2c3508a | |||
ec744558b2 | |||
1b9292dbcd | |||
44486c5b39 | |||
1f51367eb9 | |||
d6f42eafad | |||
dcce8945af | |||
d98c064bfe | |||
a904538555 | |||
4ef3dc5b90 | |||
bab61f9665 | |||
2765f0978f | |||
bc07539133 | |||
b4a82b7ed4 | |||
e2688dda22 | |||
d0d8585e4c | |||
729d69246a | |||
e78a6db151 | |||
f8721c3786 | |||
e519e00265 | |||
35bb20abe7 | |||
dd6a29adb2 | |||
5f9602d28b | |||
74683c706b | |||
2795e1350d | |||
aed3e5674f | |||
d941882066 | |||
3f74ff7235 | |||
9cead47212 | |||
78739d4aa9 | |||
e07e790613 | |||
ca283eed0c | |||
57edf289ea | |||
3912f5d67b | |||
c9b50e46a5 | |||
aa97fe7cbd | |||
ad07ea0290 | |||
573734008a | |||
f4a78e00a7 | |||
0d6094c8b1 | |||
b1e5b6ffe1 | |||
b6fc682117 | |||
0beb84768e | |||
b98a279367 | |||
e8a3cfaed6 | |||
0cc3e18db6 | |||
6b19c0abd1 | |||
f956d19e6e | |||
ceb0d0bf08 | |||
b3879ec223 | |||
bda99e59ec | |||
25c2a9baaa | |||
1e362c750b | |||
1a51a84313 | |||
7f72dbdaf2 | |||
e8c162ac03 | |||
1aaa19748a | |||
9e230b64a4 | |||
17b4cf8ce7 | |||
df4aa0fe25 | |||
188ee59ba6 | |||
591612db7e | |||
d673f8a865 | |||
82593bd404 | |||
a39ae12edc | |||
0ef1cb29f7 | |||
64ab23d17d | |||
18c9bcd44a | |||
e895ea2d8b | |||
b9e13cb5d1 | |||
46ff2c82aa | |||
a84ad741a4 | |||
412bcad2d1 | |||
e70c531d3d | |||
7c5f963d1f | |||
62e1e1637c | |||
3d03c18d4f | |||
2157c5a4e3 | |||
0fb677aad7 | |||
b1d0947a18 | |||
5b3f75dc72 | |||
6f2995cd4c | |||
88ada52146 | |||
d622181205 | |||
630443a4d6 | |||
035800bfbd | |||
c7803bb8f4 | |||
d496a1213c | |||
241488ef1c | |||
88b2cdd6a0 | |||
35ffdf2143 | |||
3cbc687424 | |||
4f7b831676 | |||
f20f170b1f | |||
67010d123c | |||
51708c8ed1 | |||
361fde35cf | |||
7ce3b19389 | |||
10f08445e4 | |||
f24a1b62bb | |||
bbd12c9372 | |||
6906cc9c25 | |||
cb211f88d3 | |||
3f262a2603 | |||
d94b9fe6fb | |||
b478640463 | |||
846f2fc6e4 | |||
683d5c3066 | |||
a3574e519a | |||
3e3317e8bd | |||
e7aeb9b29f | |||
7fd868ade9 | |||
6e6df22979 | |||
f7980885a5 | |||
5a1393aa0b | |||
40e4ca4751 | |||
1d810baa18 | |||
213b4c9dca | |||
889d482d2d | |||
ea0e83a7f9 | |||
f0497039ed | |||
aff77d2b65 | |||
a7c6999670 | |||
18e96898ea | |||
870dcc5970 | |||
5c5e681819 | |||
f54753beaa | |||
b24520579a | |||
31d4516516 | |||
66e62e9994 | |||
eeedaf2e76 | |||
1fc35c753b | |||
2aaf4bf96b | |||
cd92bc3145 | |||
4a156df7a1 | |||
c46e758e2c | |||
8a10948ce9 | |||
7045c53170 | |||
0621e957a0 | |||
a818f33658 | |||
3bfbf2697f | |||
7559f9e583 | |||
c8fdbe19f9 | |||
2e50bf667a | |||
ba62037642 | |||
824556c9c8 | |||
f7c0ec66d2 | |||
69bb455c60 | |||
575db17264 | |||
0aa99e66f7 | |||
3264941c1b | |||
a57d383b1d | |||
9a7fda87b0 | |||
adc810d24b | |||
75baf186f3 | |||
a6bbb130c5 | |||
d1ac6d2824 | |||
636958ff70 | |||
7f6fe93f55 | |||
e9b1904db8 | |||
02d2c06b23 | |||
debda2f52c | |||
0c97ce2fcc | |||
62d6bb6c8a | |||
a9dc887060 | |||
137e47f98d | |||
05a9b11316 | |||
561126b0d6 |
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
ci.sh
11
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,14 @@ 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
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(incomplete_features)]
|
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
|
|
||||||
use core::slice;
|
use core::slice;
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.3", 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.4.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,10 +1,9 @@
|
|||||||
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;
|
|
||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
use crate::events::{Event, EventSubscriber, Events};
|
use crate::events::{Event, EventSubscriber, Events};
|
||||||
use crate::fmt::Bytes;
|
use crate::fmt::Bytes;
|
||||||
@ -87,22 +86,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 +120,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 +184,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 +296,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 {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![allow(incomplete_features)]
|
|
||||||
#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)]
|
#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
|
@ -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 {
|
||||||
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||||
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
embassy-time = { version = "0.1.4", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
||||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
|
@ -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.
|
||||||
|
@ -28,7 +28,7 @@ digest = "0.10"
|
|||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
||||||
embassy-sync = { version = "0.3.0", path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
@ -16,7 +16,7 @@ target = "thumbv7em-none-eabi"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
embassy-sync = { path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-nrf = { path = "../../embassy-nrf" }
|
embassy-nrf = { path = "../../embassy-nrf" }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
cortex-m = { version = "0.7.6" }
|
cortex-m = { version = "0.7.6" }
|
||||||
|
@ -17,7 +17,7 @@ defmt = { version = "0.3", optional = true }
|
|||||||
defmt-rtt = { version = "0.4", optional = true }
|
defmt-rtt = { version = "0.4", optional = true }
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
|
|
||||||
embassy-sync = { path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-rp = { path = "../../embassy-rp", default-features = false }
|
embassy-rp = { path = "../../embassy-rp", default-features = false }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
embassy-time = { path = "../../embassy-time" }
|
embassy-time = { path = "../../embassy-time" }
|
||||||
|
@ -18,7 +18,7 @@ defmt = { version = "0.3", optional = true }
|
|||||||
defmt-rtt = { version = "0.4", optional = true }
|
defmt-rtt = { version = "0.4", optional = true }
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
|
|
||||||
embassy-sync = { path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-stm32 = { path = "../../embassy-stm32", default-features = false }
|
embassy-stm32 = { path = "../../embassy-stm32", default-features = false }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
cortex-m = { version = "0.7.6" }
|
cortex-m = { version = "0.7.6" }
|
||||||
|
@ -20,8 +20,8 @@ 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.4.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.3", 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",
|
||||||
] }
|
] }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, try_blocks))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! Utilities to use `embedded-hal` traits with Embassy.
|
//! Utilities to use `embedded-hal` traits with Embassy.
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,15 @@ 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.3.2 - 2023-11-06
|
||||||
|
|
||||||
|
- Use `atomic-polyfill` for `riscv32`
|
||||||
|
- Removed unused dependencies (static_cell, futures-util)
|
||||||
|
|
||||||
|
## 0.3.1 - 2023-11-01
|
||||||
|
|
||||||
|
- Fix spurious "Found waker not created by the Embassy executor" error in recent nightlies.
|
||||||
|
|
||||||
## 0.3.0 - 2023-08-25
|
## 0.3.0 - 2023-08-25
|
||||||
|
|
||||||
- Replaced Pender. Implementations now must define an extern function called `__pender`.
|
- Replaced Pender. Implementations now must define an extern function called `__pender`.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-executor"
|
name = "embassy-executor"
|
||||||
version = "0.3.0"
|
version = "0.3.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "async/await executor designed for embedded usage"
|
description = "async/await executor designed for embedded usage"
|
||||||
@ -57,12 +57,10 @@ defmt = { version = "0.3", optional = true }
|
|||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
rtos-trace = { version = "0.1.2", optional = true }
|
rtos-trace = { version = "0.1.2", optional = true }
|
||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
|
||||||
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
||||||
embassy-time = { version = "0.1.3", 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"
|
|
||||||
|
|
||||||
# arch-cortex-m dependencies
|
# arch-cortex-m dependencies
|
||||||
cortex-m = { version = "0.7.6", optional = true }
|
cortex-m = { version = "0.7.6", optional = true }
|
||||||
|
@ -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.
|
||||||
|
@ -6,8 +6,8 @@ pub use thread::*;
|
|||||||
#[cfg(feature = "executor-thread")]
|
#[cfg(feature = "executor-thread")]
|
||||||
mod thread {
|
mod thread {
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
|
use atomic_polyfill::{AtomicBool, Ordering};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_macros::main_riscv as main;
|
pub use embassy_macros::main_riscv as main;
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-xtensa", "arch-std", "
|
|||||||
mod arch;
|
mod arch;
|
||||||
|
|
||||||
#[cfg(feature = "_arch")]
|
#[cfg(feature = "_arch")]
|
||||||
|
#[allow(unused_imports)] // don't warn if the module is empty.
|
||||||
pub use arch::*;
|
pub use arch::*;
|
||||||
|
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
@ -46,7 +47,6 @@ pub use spawner::*;
|
|||||||
pub mod _export {
|
pub mod _export {
|
||||||
#[cfg(feature = "rtos-trace")]
|
#[cfg(feature = "rtos-trace")]
|
||||||
pub use rtos_trace::trace;
|
pub use rtos_trace::trace;
|
||||||
pub use static_cell::StaticCell;
|
|
||||||
|
|
||||||
/// Expands the given block of code when `embassy-executor` is compiled with
|
/// Expands the given block of code when `embassy-executor` is compiled with
|
||||||
/// the `rtos-trace-interrupt` feature.
|
/// the `rtos-trace-interrupt` feature.
|
||||||
|
@ -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)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-futures"
|
name = "embassy-futures"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "no-std, no-alloc utilities for working with futures"
|
description = "no-std, no-alloc utilities for working with futures"
|
||||||
repository = "https://github.com/embassy-rs/embassy"
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# embassy-macros
|
# embassy-hal-internal
|
||||||
|
|
||||||
An [Embassy](https://embassy.dev) project.
|
An [Embassy](https://embassy.dev) project.
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ pub mod drop;
|
|||||||
mod macros;
|
mod macros;
|
||||||
mod peripheral;
|
mod peripheral;
|
||||||
pub mod ratio;
|
pub mod ratio;
|
||||||
pub mod ring_buffer;
|
|
||||||
pub use peripheral::{Peripheral, PeripheralRef};
|
pub use peripheral::{Peripheral, PeripheralRef};
|
||||||
|
|
||||||
#[cfg(feature = "cortex-m")]
|
#[cfg(feature = "cortex-m")]
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
pub struct RingBuffer<'a> {
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
empty: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RingBuffer<'a> {
|
|
||||||
pub fn new(buf: &'a mut [u8]) -> Self {
|
|
||||||
Self {
|
|
||||||
buf,
|
|
||||||
start: 0,
|
|
||||||
end: 0,
|
|
||||||
empty: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_buf(&mut self) -> &mut [u8] {
|
|
||||||
if self.start == self.end && !self.empty {
|
|
||||||
trace!(" ringbuf: push_buf empty");
|
|
||||||
return &mut self.buf[..0];
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = if self.start <= self.end {
|
|
||||||
self.buf.len() - self.end
|
|
||||||
} else {
|
|
||||||
self.start - self.end
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n);
|
|
||||||
&mut self.buf[self.end..self.end + n]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(&mut self, n: usize) {
|
|
||||||
trace!(" ringbuf: push {:?}", n);
|
|
||||||
if n == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.end = self.wrap(self.end + n);
|
|
||||||
self.empty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_buf(&mut self) -> &mut [u8] {
|
|
||||||
if self.empty {
|
|
||||||
trace!(" ringbuf: pop_buf empty");
|
|
||||||
return &mut self.buf[..0];
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = if self.end <= self.start {
|
|
||||||
self.buf.len() - self.start
|
|
||||||
} else {
|
|
||||||
self.end - self.start
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n);
|
|
||||||
&mut self.buf[self.start..self.start + n]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(&mut self, n: usize) {
|
|
||||||
trace!(" ringbuf: pop {:?}", n);
|
|
||||||
if n == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.start = self.wrap(self.start + n);
|
|
||||||
self.empty = self.start == self.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_full(&self) -> bool {
|
|
||||||
self.start == self.end && !self.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.start = 0;
|
|
||||||
self.end = 0;
|
|
||||||
self.empty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap(&self, n: usize) -> usize {
|
|
||||||
assert!(n <= self.buf.len());
|
|
||||||
if n == self.buf.len() {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_pop() {
|
|
||||||
let mut b = [0; 4];
|
|
||||||
let mut rb = RingBuffer::new(&mut b);
|
|
||||||
let buf = rb.push_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
buf[0] = 1;
|
|
||||||
buf[1] = 2;
|
|
||||||
buf[2] = 3;
|
|
||||||
buf[3] = 4;
|
|
||||||
rb.push(4);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
assert_eq!(1, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(3, buf.len());
|
|
||||||
assert_eq!(2, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(2, buf.len());
|
|
||||||
assert_eq!(3, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(1, buf.len());
|
|
||||||
assert_eq!(4, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(0, buf.len());
|
|
||||||
|
|
||||||
let buf = rb.push_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,8 +20,8 @@ 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.3", 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.4.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" }
|
||||||
embedded-hal = { version = "0.2", features = ["unproven"] }
|
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait, impl_trait_projections)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
//! embassy-lora holds LoRa-specific functionality.
|
//! embassy-lora holds LoRa-specific functionality.
|
||||||
|
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
@ -34,6 +35,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,7 @@ pub fn wasm() -> TokenStream {
|
|||||||
quote! {
|
quote! {
|
||||||
#[wasm_bindgen::prelude::wasm_bindgen(start)]
|
#[wasm_bindgen::prelude::wasm_bindgen(start)]
|
||||||
pub fn main() -> Result<(), wasm_bindgen::JsValue> {
|
pub fn main() -> Result<(), wasm_bindgen::JsValue> {
|
||||||
static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new();
|
let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(::embassy_executor::Executor::new()));
|
||||||
let executor = EXECUTOR.init(::embassy_executor::Executor::new());
|
|
||||||
|
|
||||||
executor.start(|spawner| {
|
executor.start(|spawner| {
|
||||||
spawner.spawn(__embassy_main(spawner)).unwrap();
|
spawner.spawn(__embassy_main(spawner)).unwrap();
|
||||||
|
@ -10,21 +10,19 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
heapless = "0.7.16"
|
heapless = "0.8"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4", default-features = false, optional = true }
|
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.3" }
|
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"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged.
|
embedded-hal-mock = { version = "=0.10.0-rc.1", features = ["embedded-hal-async", "eh1"] }
|
||||||
#embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", branch = "1-alpha", features = ["embedded-hal-async", "eh1"] }] }
|
|
||||||
embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] }
|
|
||||||
crc = "3.0.1"
|
crc = "3.0.1"
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
critical-section = { version = "1.1.2", features = ["std"] }
|
critical-section = { version = "1.1.2", features = ["std"] }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![deny(clippy::pedantic)]
|
#![deny(clippy::pedantic)]
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
#![cfg_attr(not(any(test, feature = "std")), no_std)]
|
#![cfg_attr(not(any(test, feature = "std")), no_std)]
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
#![allow(clippy::missing_errors_doc)]
|
#![allow(clippy::missing_errors_doc)]
|
||||||
@ -20,7 +21,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 +610,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."
|
||||||
@ -24,6 +24,6 @@ features = ["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-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||||
|
@ -7,7 +7,9 @@ The `embassy-net-driver` trait is polling-based. To implement it, you must write
|
|||||||
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
|
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
|
||||||
knows when to poll your driver again to make more progress.
|
knows when to poll your driver again to make more progress.
|
||||||
|
|
||||||
With `embassy-net-driver-channel`
|
With `embassy-net-driver-channel` you get a "channel-like" interface instead, where you can send/receive packets
|
||||||
|
to/from embassy-net. The intended usage is to spawn a "driver task" in the background that does this, passing
|
||||||
|
packets between the hardware and the channel.
|
||||||
|
|
||||||
## A note about deadlocks
|
## A note about deadlocks
|
||||||
|
|
||||||
@ -18,19 +20,19 @@ loop {
|
|||||||
// Wait for either..
|
// Wait for either..
|
||||||
match select(
|
match select(
|
||||||
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
||||||
irq_pin.wait_for_low(),
|
irq_pin.wait_for_low(),
|
||||||
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
||||||
tx_chan.tx_buf(),
|
tx_chan.tx_buf(),
|
||||||
).await {
|
).await {
|
||||||
Either::First(_) => {
|
Either::First(_) => {
|
||||||
// a packet is ready to be received!
|
// a packet is ready to be received!
|
||||||
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
||||||
let n = receive_packet_over_spi(buf).await;
|
let n = receive_packet_over_spi(buf).await;
|
||||||
rx_chan.rx_done(n);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
Either::Second(buf) => {
|
Either::Second(buf) => {
|
||||||
// a packet is ready to be sent!
|
// a packet is ready to be sent!
|
||||||
send_packet_over_spi(buf).await;
|
send_packet_over_spi(buf).await;
|
||||||
tx_chan.tx_done();
|
tx_chan.tx_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +43,7 @@ However, this code has a latent deadlock bug. The symptom is it can hang at `rx_
|
|||||||
|
|
||||||
The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
|
The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
|
||||||
|
|
||||||
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
|
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the RX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
loop {
|
loop {
|
||||||
@ -58,12 +60,12 @@ loop {
|
|||||||
).await {
|
).await {
|
||||||
Either::First(buf) => {
|
Either::First(buf) => {
|
||||||
// a packet is ready to be received!
|
// a packet is ready to be received!
|
||||||
let n = receive_packet_over_spi(buf).await;
|
let n = receive_packet_over_spi(buf).await;
|
||||||
rx_chan.rx_done(n);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
Either::Second(buf) => {
|
Either::Second(buf) => {
|
||||||
// a packet is ready to be sent!
|
// a packet is ready to be sent!
|
||||||
send_packet_over_spi(buf).await;
|
send_packet_over_spi(buf).await;
|
||||||
tx_chan.tx_done();
|
tx_chan.tx_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,12 +81,10 @@ These `embassy-net` drivers are implemented using this crate. You can look at th
|
|||||||
- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips.
|
- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips.
|
||||||
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
|
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
|
||||||
|
|
||||||
|
|
||||||
## Interoperability
|
## Interoperability
|
||||||
|
|
||||||
This crate can run on any executor.
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This work is licensed under either of
|
This work is licensed under either of
|
||||||
|
@ -8,9 +8,8 @@ use core::cell::RefCell;
|
|||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
use driver::HardwareAddress;
|
|
||||||
pub use embassy_net_driver as driver;
|
pub use embassy_net_driver as driver;
|
||||||
use embassy_net_driver::{Capabilities, LinkState, Medium};
|
use embassy_net_driver::{Capabilities, LinkState};
|
||||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embassy_sync::waitqueue::WakerRegistration;
|
use embassy_sync::waitqueue::WakerRegistration;
|
||||||
@ -161,18 +160,10 @@ impl<'d> StateRunner<'d> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ethernet_address(&self, address: [u8; 6]) {
|
pub fn set_hardware_address(&self, address: driver::HardwareAddress) {
|
||||||
self.shared.lock(|s| {
|
self.shared.lock(|s| {
|
||||||
let s = &mut *s.borrow_mut();
|
let s = &mut *s.borrow_mut();
|
||||||
s.hardware_address = driver::HardwareAddress::Ethernet(address);
|
s.hardware_address = address;
|
||||||
s.waker.wake();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_ieee802154_address(&self, address: [u8; 8]) {
|
|
||||||
self.shared.lock(|s| {
|
|
||||||
let s = &mut *s.borrow_mut();
|
|
||||||
s.hardware_address = driver::HardwareAddress::Ieee802154(address);
|
|
||||||
s.waker.wake();
|
s.waker.wake();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -232,11 +223,6 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
|||||||
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
||||||
let mut caps = Capabilities::default();
|
let mut caps = Capabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
caps.medium = match &hardware_address {
|
|
||||||
HardwareAddress::Ethernet(_) => Medium::Ethernet,
|
|
||||||
HardwareAddress::Ieee802154(_) => Medium::Ieee802154,
|
|
||||||
HardwareAddress::Ip => Medium::Ip,
|
|
||||||
};
|
|
||||||
|
|
||||||
// safety: this is a self-referential struct, however:
|
// safety: this is a self-referential struct, however:
|
||||||
// - it can't move while the `'d` borrow is active.
|
// - it can't move while the `'d` borrow is active.
|
||||||
|
17
embassy-net-driver/CHANGELOG.md
Normal file
17
embassy-net-driver/CHANGELOG.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 0.2.0 - 2023-10-18
|
||||||
|
|
||||||
|
- Added support for IEEE 802.15.4 mediums.
|
||||||
|
- Added `Driver::hardware_address()`, `HardwareAddress`.
|
||||||
|
- Removed `Medium` enum. The medium is deduced out of the hardware address.
|
||||||
|
- Removed `Driver::ethernet_address()`. Replacement is `hardware_address()`.
|
||||||
|
|
||||||
|
## 0.1.0 - 2023-06-29
|
||||||
|
|
||||||
|
- First release
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-net-driver"
|
name = "embassy-net-driver"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
|
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
|
||||||
|
@ -7,12 +7,23 @@ use core::task::Context;
|
|||||||
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
|
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum HardwareAddress {
|
pub enum HardwareAddress {
|
||||||
/// A six-octet Ethernet address
|
/// Ethernet medium, with a A six-octet Ethernet address.
|
||||||
|
///
|
||||||
|
/// Devices of this type send and receive Ethernet frames,
|
||||||
|
/// and interfaces using it must do neighbor discovery via ARP or NDISC.
|
||||||
|
///
|
||||||
|
/// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
|
||||||
Ethernet([u8; 6]),
|
Ethernet([u8; 6]),
|
||||||
/// An eight-octet IEEE802.15.4 address
|
/// 6LoWPAN over IEEE802.15.4, with an eight-octet address.
|
||||||
Ieee802154([u8; 8]),
|
Ieee802154([u8; 8]),
|
||||||
/// Indicates that a Driver is IP-native, and has no hardware address
|
/// Indicates that a Driver is IP-native, and has no hardware address.
|
||||||
|
///
|
||||||
|
/// Devices of this type send and receive IP frames, without an
|
||||||
|
/// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
|
||||||
|
///
|
||||||
|
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
||||||
Ip,
|
Ip,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +75,10 @@ pub trait Driver {
|
|||||||
fn capabilities(&self) -> Capabilities;
|
fn capabilities(&self) -> Capabilities;
|
||||||
|
|
||||||
/// Get the device's hardware address.
|
/// Get the device's hardware address.
|
||||||
|
///
|
||||||
|
/// The returned hardware address also determines the "medium" of this driver. This indicates
|
||||||
|
/// what kind of packet the sent/received bytes are, and determines some behaviors of
|
||||||
|
/// the interface. For example, ARP/NDISC address resolution is only done for Ethernet mediums.
|
||||||
fn hardware_address(&self) -> HardwareAddress;
|
fn hardware_address(&self) -> HardwareAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,13 +139,6 @@ pub trait TxToken {
|
|||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Capabilities {
|
pub struct Capabilities {
|
||||||
/// Medium of the device.
|
|
||||||
///
|
|
||||||
/// This indicates what kind of packet the sent/received bytes are, and determines
|
|
||||||
/// some behaviors of Interface. For example, ARP/NDISC address resolution is only done
|
|
||||||
/// for Ethernet mediums.
|
|
||||||
pub medium: Medium,
|
|
||||||
|
|
||||||
/// Maximum transmission unit.
|
/// Maximum transmission unit.
|
||||||
///
|
///
|
||||||
/// The network device is unable to send or receive frames larger than the value returned
|
/// The network device is unable to send or receive frames larger than the value returned
|
||||||
@ -161,32 +169,6 @@ pub struct Capabilities {
|
|||||||
pub checksum: ChecksumCapabilities,
|
pub checksum: ChecksumCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type of medium of a device.
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum Medium {
|
|
||||||
/// Ethernet medium. Devices of this type send and receive Ethernet frames,
|
|
||||||
/// and interfaces using it must do neighbor discovery via ARP or NDISC.
|
|
||||||
///
|
|
||||||
/// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
|
|
||||||
Ethernet,
|
|
||||||
|
|
||||||
/// IP medium. Devices of this type send and receive IP frames, without an
|
|
||||||
/// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
|
|
||||||
///
|
|
||||||
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
|
||||||
Ip,
|
|
||||||
|
|
||||||
/// IEEE 802_15_4 medium
|
|
||||||
Ieee802154,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Medium {
|
|
||||||
fn default() -> Medium {
|
|
||||||
Medium::Ethernet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A description of checksum behavior for every supported protocol.
|
/// A description of checksum behavior for every supported protocol.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
@ -10,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.3", 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,17 +7,17 @@ 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.3", 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.4.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" }
|
||||||
|
|
||||||
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
|
noproto = { git="https://github.com/embassy-rs/noproto", rev = "f5e6d1f325b6ad4e344f60452b09576e24671f62", default-features = false, features = ["derive"] }
|
||||||
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
||||||
heapless = "0.7.16"
|
heapless = "0.8"
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
@ -97,8 +97,8 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
|
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
|
||||||
let req = proto::CtrlMsgReqConnectAp {
|
let req = proto::CtrlMsgReqConnectAp {
|
||||||
ssid: String::from(ssid),
|
ssid: unwrap!(String::try_from(ssid)),
|
||||||
pwd: String::from(password),
|
pwd: unwrap!(String::try_from(password)),
|
||||||
bssid: String::new(),
|
bssid: String::new(),
|
||||||
listen_interval: 3,
|
listen_interval: 3,
|
||||||
is_wpa3_supported: false,
|
is_wpa3_supported: false,
|
||||||
|
@ -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,10 +16,10 @@ 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.4.0", path = "../embassy-sync" }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/"
|
||||||
|
@ -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.3", 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,15 @@
|
|||||||
//! [`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)]
|
||||||
|
#![allow(stable_features, unknown_lints, 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,32 +50,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> {
|
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);
|
async {
|
||||||
loop {
|
self.int.wait_for_low().await.ok();
|
||||||
match select(
|
rx_chan.rx_buf().await
|
||||||
async {
|
},
|
||||||
self.int.wait_for_low().await.ok();
|
tx_chan.tx_buf(),
|
||||||
rx_chan.rx_buf().await
|
tick.next(),
|
||||||
},
|
)
|
||||||
tx_chan.tx_buf(),
|
.await
|
||||||
)
|
{
|
||||||
.await
|
Either3::First(p) => {
|
||||||
{
|
if let Ok(n) = self.mac.read_frame(p).await {
|
||||||
Either::First(p) => {
|
rx_chan.rx_done(n);
|
||||||
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();
|
||||||
Either::Second(p) => {
|
tx_chan.tx_done();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,12 +98,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();
|
||||||
|
|
||||||
|
38
embassy-net/CHANGELOG.md
Normal file
38
embassy-net/CHANGELOG.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 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).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- Avoid never resolving `TcpIo::read` when the output buffer is empty.
|
||||||
|
|
||||||
|
## 0.2.1 - 2023-10-31
|
||||||
|
|
||||||
|
- Re-add impl_trait_projections
|
||||||
|
- Fix: Reset DHCP socket when the link up is detected
|
||||||
|
|
||||||
|
## 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.1"
|
||||||
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"
|
||||||
@ -46,21 +46,21 @@ igmp = ["smoltcp/proto-igmp"]
|
|||||||
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 }
|
||||||
|
|
||||||
smoltcp = { version = "0.10.0", default-features = false, features = [
|
smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev = "9b791ae3057e10f7afcb70c67deb5daf714293a9", default-features = false, features = [
|
||||||
"socket",
|
"socket",
|
||||||
"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.3", 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.4.0", path = "../embassy-sync" }
|
||||||
embedded-io-async = { version = "0.6.0", optional = true }
|
embedded-io-async = { version = "0.6.0", optional = true }
|
||||||
|
|
||||||
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
||||||
heapless = { version = "0.7.5", default-features = false }
|
heapless = { version = "0.8", default-features = false }
|
||||||
as-slice = "0.2.1"
|
as-slice = "0.2.1"
|
||||||
generic-array = { version = "0.14.4", default-features = false }
|
generic-array = { version = "0.14.4", default-features = false }
|
||||||
stable_deref_trait = { version = "1.2.0", default-features = false }
|
stable_deref_trait = { version = "1.2.0", default-features = false }
|
||||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||||
atomic-pool = "1.0"
|
atomic-pool = "1.0"
|
||||||
embedded-nal-async = { version = "0.6.0", optional = true }
|
embedded-nal-async = { version = "0.7", optional = true }
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
|
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
|
||||||
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
|
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
|
||||||
memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience.
|
memory management designed to work well for embedded systems, aiming for a more "Just Works" experience.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -101,7 +101,8 @@ where
|
|||||||
async fn get_host_by_address(
|
async fn get_host_by_address(
|
||||||
&self,
|
&self,
|
||||||
_addr: embedded_nal_async::IpAddr,
|
_addr: embedded_nal_async::IpAddr,
|
||||||
) -> Result<heapless::String<256>, Self::Error> {
|
_result: &mut [u8],
|
||||||
|
) -> Result<usize, Self::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
@ -33,6 +34,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 +266,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 +294,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 +303,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 +363,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 +516,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 +819,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 +861,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) => {
|
||||||
|
@ -390,6 +390,13 @@ impl<'d> TcpIo<'d> {
|
|||||||
// CAUTION: smoltcp semantics around EOF are different to what you'd expect
|
// CAUTION: smoltcp semantics around EOF are different to what you'd expect
|
||||||
// from posix-like IO, so we have to tweak things here.
|
// from posix-like IO, so we have to tweak things here.
|
||||||
self.with_mut(|s, _| match s.recv_slice(buf) {
|
self.with_mut(|s, _| match s.recv_slice(buf) {
|
||||||
|
// Reading into empty buffer
|
||||||
|
Ok(0) if buf.is_empty() => {
|
||||||
|
// embedded_io_async::Read's contract is to not block if buf is empty. While
|
||||||
|
// this function is not a direct implementor of the trait method, we still don't
|
||||||
|
// want our future to never resolve.
|
||||||
|
Poll::Ready(Ok(0))
|
||||||
|
}
|
||||||
// No data ready
|
// No data ready
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
s.register_recv_waker(cx.waker());
|
s.register_recv_waker(cx.waker());
|
||||||
@ -611,10 +618,7 @@ pub mod client {
|
|||||||
async fn connect<'a>(
|
async fn connect<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
remote: embedded_nal_async::SocketAddr,
|
remote: embedded_nal_async::SocketAddr,
|
||||||
) -> Result<Self::Connection<'a>, Self::Error>
|
) -> Result<Self::Connection<'a>, Self::Error> {
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
{
|
|
||||||
let addr: crate::IpAddress = match remote.ip() {
|
let addr: crate::IpAddress = match remote.ip() {
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())),
|
IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())),
|
||||||
|
@ -94,8 +94,8 @@ _gpio-p1 = []
|
|||||||
_nrf52832_anomaly_109 = []
|
_nrf52832_anomaly_109 = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.3", 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.4.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" }
|
||||||
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 }
|
||||||
@ -110,7 +110,6 @@ defmt = { version = "0.3", optional = true }
|
|||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
cortex-m-rt = ">=0.6.15,<0.8"
|
cortex-m-rt = ">=0.6.15,<0.8"
|
||||||
cortex-m = "0.7.6"
|
cortex-m = "0.7.6"
|
||||||
futures = { version = "0.3.17", default-features = false }
|
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
fixed = "1.10.0"
|
fixed = "1.10.0"
|
||||||
|
@ -12,7 +12,7 @@ use core::cmp::min;
|
|||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
|
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, AtomicUsize, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
||||||
@ -41,7 +41,9 @@ mod sealed {
|
|||||||
|
|
||||||
pub rx_waker: AtomicWaker,
|
pub rx_waker: AtomicWaker,
|
||||||
pub rx_buf: RingBuffer,
|
pub rx_buf: RingBuffer,
|
||||||
pub rx_bufs: AtomicU8,
|
pub rx_started: AtomicBool,
|
||||||
|
pub rx_started_count: AtomicU8,
|
||||||
|
pub rx_ended_count: AtomicU8,
|
||||||
pub rx_ppi_ch: AtomicU8,
|
pub rx_ppi_ch: AtomicU8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +67,9 @@ impl State {
|
|||||||
|
|
||||||
rx_waker: AtomicWaker::new(),
|
rx_waker: AtomicWaker::new(),
|
||||||
rx_buf: RingBuffer::new(),
|
rx_buf: RingBuffer::new(),
|
||||||
rx_bufs: AtomicU8::new(0),
|
rx_started: AtomicBool::new(false),
|
||||||
|
rx_started_count: AtomicU8::new(0),
|
||||||
|
rx_ended_count: AtomicU8::new(0),
|
||||||
rx_ppi_ch: AtomicU8::new(0),
|
rx_ppi_ch: AtomicU8::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,28 +108,20 @@ impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for Interrupt
|
|||||||
s.rx_waker.wake();
|
s.rx_waker.wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not RXing, start.
|
if r.events_endrx.read().bits() != 0 {
|
||||||
if s.rx_bufs.load(Ordering::Relaxed) == 0 {
|
//trace!(" irq_rx: endrx");
|
||||||
let (ptr, len) = rx.push_buf();
|
r.events_endrx.reset();
|
||||||
if len >= half_len {
|
|
||||||
//trace!(" irq_rx: starting {:?}", half_len);
|
|
||||||
s.rx_bufs.store(1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
// Set up the DMA read
|
let val = s.rx_ended_count.load(Ordering::Relaxed);
|
||||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
|
||||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
|
|
||||||
|
|
||||||
// Start UARTE Receive transaction
|
|
||||||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
|
||||||
rx.push_done(half_len);
|
|
||||||
r.intenset.write(|w| w.rxstarted().set());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.events_rxstarted.read().bits() != 0 {
|
if r.events_rxstarted.read().bits() != 0 || !s.rx_started.load(Ordering::Relaxed) {
|
||||||
//trace!(" irq_rx: rxstarted");
|
//trace!(" irq_rx: rxstarted");
|
||||||
let (ptr, len) = rx.push_buf();
|
let (ptr, len) = rx.push_buf();
|
||||||
if len >= half_len {
|
if len >= half_len {
|
||||||
|
r.events_rxstarted.reset();
|
||||||
|
|
||||||
//trace!(" irq_rx: starting second {:?}", half_len);
|
//trace!(" irq_rx: starting second {:?}", half_len);
|
||||||
|
|
||||||
// Set up the DMA read
|
// Set up the DMA read
|
||||||
@ -134,11 +130,50 @@ impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for Interrupt
|
|||||||
|
|
||||||
let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
|
let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Enable endrx -> startrx PPI channel.
|
||||||
|
// From this point on, if endrx happens, startrx is automatically fired.
|
||||||
ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
|
ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
|
||||||
|
|
||||||
|
// It is possible that endrx happened BEFORE enabling the PPI. In this case
|
||||||
|
// the PPI channel doesn't trigger, and we'd hang. We have to detect this
|
||||||
|
// and manually start.
|
||||||
|
|
||||||
|
// check again in case endrx has happened between the last check and now.
|
||||||
|
if r.events_endrx.read().bits() != 0 {
|
||||||
|
//trace!(" irq_rx: endrx");
|
||||||
|
r.events_endrx.reset();
|
||||||
|
|
||||||
|
let val = s.rx_ended_count.load(Ordering::Relaxed);
|
||||||
|
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rx_ended = s.rx_ended_count.load(Ordering::Relaxed);
|
||||||
|
let rx_started = s.rx_started_count.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// If we started the same amount of transfers as ended, the last rxend has
|
||||||
|
// already occured.
|
||||||
|
let rxend_happened = rx_started == rx_ended;
|
||||||
|
|
||||||
|
// Check if the PPI channel is still enabled. The PPI channel disables itself
|
||||||
|
// when it fires, so if it's still enabled it hasn't fired.
|
||||||
|
let ppi_ch_enabled = ppi::regs().chen.read().bits() & (1 << chn) != 0;
|
||||||
|
|
||||||
|
// if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed.
|
||||||
|
// this condition also naturally matches if `!started`, needed to kickstart the DMA.
|
||||||
|
if rxend_happened && ppi_ch_enabled {
|
||||||
|
//trace!("manually starting.");
|
||||||
|
|
||||||
|
// disable the ppi ch, it's of no use anymore.
|
||||||
|
ppi::regs().chenclr.write(|w| unsafe { w.bits(1 << chn) });
|
||||||
|
|
||||||
|
// manually start
|
||||||
|
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
|
||||||
rx.push_done(half_len);
|
rx.push_done(half_len);
|
||||||
|
|
||||||
r.events_rxstarted.reset();
|
s.rx_started_count.store(rx_started.wrapping_add(1), Ordering::Relaxed);
|
||||||
|
s.rx_started.store(true, Ordering::Relaxed);
|
||||||
} else {
|
} else {
|
||||||
//trace!(" irq_rx: rxstarted no buf");
|
//trace!(" irq_rx: rxstarted no buf");
|
||||||
r.intenclr.write(|w| w.rxstarted().clear());
|
r.intenclr.write(|w| w.rxstarted().clear());
|
||||||
@ -282,6 +317,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
|
|
||||||
let r = U::regs();
|
let r = U::regs();
|
||||||
|
|
||||||
|
let hwfc = cts.is_some();
|
||||||
|
|
||||||
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
||||||
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
||||||
|
|
||||||
@ -303,7 +340,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
// Initialize state
|
// Initialize state
|
||||||
let s = U::buffered_state();
|
let s = U::buffered_state();
|
||||||
s.tx_count.store(0, Ordering::Relaxed);
|
s.tx_count.store(0, Ordering::Relaxed);
|
||||||
s.rx_bufs.store(0, Ordering::Relaxed);
|
s.rx_started_count.store(0, Ordering::Relaxed);
|
||||||
|
s.rx_ended_count.store(0, Ordering::Relaxed);
|
||||||
let len = tx_buffer.len();
|
let len = tx_buffer.len();
|
||||||
unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
|
unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
|
||||||
let len = rx_buffer.len();
|
let len = rx_buffer.len();
|
||||||
@ -311,7 +349,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
|
|
||||||
// Configure
|
// Configure
|
||||||
r.config.write(|w| {
|
r.config.write(|w| {
|
||||||
w.hwfc().bit(false);
|
w.hwfc().bit(hwfc);
|
||||||
w.parity().variant(config.parity);
|
w.parity().variant(config.parity);
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
@ -333,6 +371,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
w.endtx().set();
|
w.endtx().set();
|
||||||
w.rxstarted().set();
|
w.rxstarted().set();
|
||||||
w.error().set();
|
w.error().set();
|
||||||
|
w.endrx().set();
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,10 +6,13 @@ pub mod pac {
|
|||||||
// To avoid cfg spam, we remove _ns or _s suffixes here.
|
// To avoid cfg spam, we remove _ns or _s suffixes here.
|
||||||
|
|
||||||
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,156 +63,167 @@ pub mod pac {
|
|||||||
wdt0_ns as wdt0,
|
wdt0_ns as wdt0,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "nrf5340-app-ns")]
|
/// Non-Secure mode (NS) peripherals
|
||||||
#[doc(no_inline)]
|
pub mod ns {
|
||||||
pub use nrf5340_app_pac::{
|
#[cfg(feature = "nrf5340-app-ns")]
|
||||||
CLOCK_NS as CLOCK,
|
#[doc(no_inline)]
|
||||||
COMP_NS as COMP,
|
pub use nrf5340_app_pac::{
|
||||||
CTRLAP_NS as CTRLAP,
|
CLOCK_NS as CLOCK,
|
||||||
DCNF_NS as DCNF,
|
COMP_NS as COMP,
|
||||||
DPPIC_NS as DPPIC,
|
CTRLAP_NS as CTRLAP,
|
||||||
EGU0_NS as EGU0,
|
DCNF_NS as DCNF,
|
||||||
EGU1_NS as EGU1,
|
DPPIC_NS as DPPIC,
|
||||||
EGU2_NS as EGU2,
|
EGU0_NS as EGU0,
|
||||||
EGU3_NS as EGU3,
|
EGU1_NS as EGU1,
|
||||||
EGU4_NS as EGU4,
|
EGU2_NS as EGU2,
|
||||||
EGU5_NS as EGU5,
|
EGU3_NS as EGU3,
|
||||||
FPU_NS as FPU,
|
EGU4_NS as EGU4,
|
||||||
GPIOTE1_NS as GPIOTE1,
|
EGU5_NS as EGU5,
|
||||||
I2S0_NS as I2S0,
|
FPU_NS as FPU,
|
||||||
IPC_NS as IPC,
|
GPIOTE1_NS as GPIOTE1,
|
||||||
KMU_NS as KMU,
|
I2S0_NS as I2S0,
|
||||||
LPCOMP_NS as LPCOMP,
|
IPC_NS as IPC,
|
||||||
MUTEX_NS as MUTEX,
|
KMU_NS as KMU,
|
||||||
NFCT_NS as NFCT,
|
LPCOMP_NS as LPCOMP,
|
||||||
NVMC_NS as NVMC,
|
MUTEX_NS as MUTEX,
|
||||||
OSCILLATORS_NS as OSCILLATORS,
|
NFCT_NS as NFCT,
|
||||||
P0_NS as P0,
|
NVMC_NS as NVMC,
|
||||||
P1_NS as P1,
|
OSCILLATORS_NS as OSCILLATORS,
|
||||||
PDM0_NS as PDM0,
|
P0_NS as P0,
|
||||||
POWER_NS as POWER,
|
P1_NS as P1,
|
||||||
PWM0_NS as PWM0,
|
PDM0_NS as PDM0,
|
||||||
PWM1_NS as PWM1,
|
POWER_NS as POWER,
|
||||||
PWM2_NS as PWM2,
|
PWM0_NS as PWM0,
|
||||||
PWM3_NS as PWM3,
|
PWM1_NS as PWM1,
|
||||||
QDEC0_NS as QDEC0,
|
PWM2_NS as PWM2,
|
||||||
QDEC1_NS as QDEC1,
|
PWM3_NS as PWM3,
|
||||||
QSPI_NS as QSPI,
|
QDEC0_NS as QDEC0,
|
||||||
REGULATORS_NS as REGULATORS,
|
QDEC1_NS as QDEC1,
|
||||||
RESET_NS as RESET,
|
QSPI_NS as QSPI,
|
||||||
RTC0_NS as RTC0,
|
REGULATORS_NS as REGULATORS,
|
||||||
RTC1_NS as RTC1,
|
RESET_NS as RESET,
|
||||||
SAADC_NS as SAADC,
|
RTC0_NS as RTC0,
|
||||||
SPIM0_NS as SPIM0,
|
RTC1_NS as RTC1,
|
||||||
SPIM1_NS as SPIM1,
|
SAADC_NS as SAADC,
|
||||||
SPIM2_NS as SPIM2,
|
SPIM0_NS as SPIM0,
|
||||||
SPIM3_NS as SPIM3,
|
SPIM1_NS as SPIM1,
|
||||||
SPIM4_NS as SPIM4,
|
SPIM2_NS as SPIM2,
|
||||||
SPIS0_NS as SPIS0,
|
SPIM3_NS as SPIM3,
|
||||||
SPIS1_NS as SPIS1,
|
SPIM4_NS as SPIM4,
|
||||||
SPIS2_NS as SPIS2,
|
SPIS0_NS as SPIS0,
|
||||||
SPIS3_NS as SPIS3,
|
SPIS1_NS as SPIS1,
|
||||||
TIMER0_NS as TIMER0,
|
SPIS2_NS as SPIS2,
|
||||||
TIMER1_NS as TIMER1,
|
SPIS3_NS as SPIS3,
|
||||||
TIMER2_NS as TIMER2,
|
TIMER0_NS as TIMER0,
|
||||||
TWIM0_NS as TWIM0,
|
TIMER1_NS as TIMER1,
|
||||||
TWIM1_NS as TWIM1,
|
TIMER2_NS as TIMER2,
|
||||||
TWIM2_NS as TWIM2,
|
TWIM0_NS as TWIM0,
|
||||||
TWIM3_NS as TWIM3,
|
TWIM1_NS as TWIM1,
|
||||||
TWIS0_NS as TWIS0,
|
TWIM2_NS as TWIM2,
|
||||||
TWIS1_NS as TWIS1,
|
TWIM3_NS as TWIM3,
|
||||||
TWIS2_NS as TWIS2,
|
TWIS0_NS as TWIS0,
|
||||||
TWIS3_NS as TWIS3,
|
TWIS1_NS as TWIS1,
|
||||||
UARTE0_NS as UARTE0,
|
TWIS2_NS as TWIS2,
|
||||||
UARTE1_NS as UARTE1,
|
TWIS3_NS as TWIS3,
|
||||||
UARTE2_NS as UARTE2,
|
UARTE0_NS as UARTE0,
|
||||||
UARTE3_NS as UARTE3,
|
UARTE1_NS as UARTE1,
|
||||||
USBD_NS as USBD,
|
UARTE2_NS as UARTE2,
|
||||||
USBREGULATOR_NS as USBREGULATOR,
|
UARTE3_NS as UARTE3,
|
||||||
VMC_NS as VMC,
|
USBD_NS as USBD,
|
||||||
WDT0_NS as WDT0,
|
USBREGULATOR_NS as USBREGULATOR,
|
||||||
WDT1_NS as WDT1,
|
VMC_NS as VMC,
|
||||||
};
|
WDT0_NS as WDT0,
|
||||||
|
WDT1_NS as WDT1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "nrf5340-app-s")]
|
/// Secure mode (S) peripherals
|
||||||
#[doc(no_inline)]
|
pub mod s {
|
||||||
pub use nrf5340_app_pac::{
|
#[cfg(feature = "nrf5340-app-s")]
|
||||||
CACHEDATA_S as CACHEDATA,
|
#[doc(no_inline)]
|
||||||
CACHEINFO_S as CACHEINFO,
|
pub use nrf5340_app_pac::{
|
||||||
CACHE_S as CACHE,
|
CACHEDATA_S as CACHEDATA,
|
||||||
CLOCK_S as CLOCK,
|
CACHEINFO_S as CACHEINFO,
|
||||||
COMP_S as COMP,
|
CACHE_S as CACHE,
|
||||||
CRYPTOCELL_S as CRYPTOCELL,
|
CLOCK_S as CLOCK,
|
||||||
CTI_S as CTI,
|
COMP_S as COMP,
|
||||||
CTRLAP_S as CTRLAP,
|
CRYPTOCELL_S as CRYPTOCELL,
|
||||||
DCNF_S as DCNF,
|
CTI_S as CTI,
|
||||||
DPPIC_S as DPPIC,
|
CTRLAP_S as CTRLAP,
|
||||||
EGU0_S as EGU0,
|
DCNF_S as DCNF,
|
||||||
EGU1_S as EGU1,
|
DPPIC_S as DPPIC,
|
||||||
EGU2_S as EGU2,
|
EGU0_S as EGU0,
|
||||||
EGU3_S as EGU3,
|
EGU1_S as EGU1,
|
||||||
EGU4_S as EGU4,
|
EGU2_S as EGU2,
|
||||||
EGU5_S as EGU5,
|
EGU3_S as EGU3,
|
||||||
FICR_S as FICR,
|
EGU4_S as EGU4,
|
||||||
FPU_S as FPU,
|
EGU5_S as EGU5,
|
||||||
GPIOTE0_S as GPIOTE0,
|
FICR_S as FICR,
|
||||||
I2S0_S as I2S0,
|
FPU_S as FPU,
|
||||||
IPC_S as IPC,
|
GPIOTE0_S as GPIOTE0,
|
||||||
KMU_S as KMU,
|
I2S0_S as I2S0,
|
||||||
LPCOMP_S as LPCOMP,
|
IPC_S as IPC,
|
||||||
MUTEX_S as MUTEX,
|
KMU_S as KMU,
|
||||||
NFCT_S as NFCT,
|
LPCOMP_S as LPCOMP,
|
||||||
NVMC_S as NVMC,
|
MUTEX_S as MUTEX,
|
||||||
OSCILLATORS_S as OSCILLATORS,
|
NFCT_S as NFCT,
|
||||||
P0_S as P0,
|
NVMC_S as NVMC,
|
||||||
P1_S as P1,
|
OSCILLATORS_S as OSCILLATORS,
|
||||||
PDM0_S as PDM0,
|
P0_S as P0,
|
||||||
POWER_S as POWER,
|
P1_S as P1,
|
||||||
PWM0_S as PWM0,
|
PDM0_S as PDM0,
|
||||||
PWM1_S as PWM1,
|
POWER_S as POWER,
|
||||||
PWM2_S as PWM2,
|
PWM0_S as PWM0,
|
||||||
PWM3_S as PWM3,
|
PWM1_S as PWM1,
|
||||||
QDEC0_S as QDEC0,
|
PWM2_S as PWM2,
|
||||||
QDEC1_S as QDEC1,
|
PWM3_S as PWM3,
|
||||||
QSPI_S as QSPI,
|
QDEC0_S as QDEC0,
|
||||||
REGULATORS_S as REGULATORS,
|
QDEC1_S as QDEC1,
|
||||||
RESET_S as RESET,
|
QSPI_S as QSPI,
|
||||||
RTC0_S as RTC0,
|
REGULATORS_S as REGULATORS,
|
||||||
RTC1_S as RTC1,
|
RESET_S as RESET,
|
||||||
SAADC_S as SAADC,
|
RTC0_S as RTC0,
|
||||||
SPIM0_S as SPIM0,
|
RTC1_S as RTC1,
|
||||||
SPIM1_S as SPIM1,
|
SAADC_S as SAADC,
|
||||||
SPIM2_S as SPIM2,
|
SPIM0_S as SPIM0,
|
||||||
SPIM3_S as SPIM3,
|
SPIM1_S as SPIM1,
|
||||||
SPIM4_S as SPIM4,
|
SPIM2_S as SPIM2,
|
||||||
SPIS0_S as SPIS0,
|
SPIM3_S as SPIM3,
|
||||||
SPIS1_S as SPIS1,
|
SPIM4_S as SPIM4,
|
||||||
SPIS2_S as SPIS2,
|
SPIS0_S as SPIS0,
|
||||||
SPIS3_S as SPIS3,
|
SPIS1_S as SPIS1,
|
||||||
SPU_S as SPU,
|
SPIS2_S as SPIS2,
|
||||||
TAD_S as TAD,
|
SPIS3_S as SPIS3,
|
||||||
TIMER0_S as TIMER0,
|
SPU_S as SPU,
|
||||||
TIMER1_S as TIMER1,
|
TAD_S as TAD,
|
||||||
TIMER2_S as TIMER2,
|
TIMER0_S as TIMER0,
|
||||||
TWIM0_S as TWIM0,
|
TIMER1_S as TIMER1,
|
||||||
TWIM1_S as TWIM1,
|
TIMER2_S as TIMER2,
|
||||||
TWIM2_S as TWIM2,
|
TWIM0_S as TWIM0,
|
||||||
TWIM3_S as TWIM3,
|
TWIM1_S as TWIM1,
|
||||||
TWIS0_S as TWIS0,
|
TWIM2_S as TWIM2,
|
||||||
TWIS1_S as TWIS1,
|
TWIM3_S as TWIM3,
|
||||||
TWIS2_S as TWIS2,
|
TWIS0_S as TWIS0,
|
||||||
TWIS3_S as TWIS3,
|
TWIS1_S as TWIS1,
|
||||||
UARTE0_S as UARTE0,
|
TWIS2_S as TWIS2,
|
||||||
UARTE1_S as UARTE1,
|
TWIS3_S as TWIS3,
|
||||||
UARTE2_S as UARTE2,
|
UARTE0_S as UARTE0,
|
||||||
UARTE3_S as UARTE3,
|
UARTE1_S as UARTE1,
|
||||||
UICR_S as UICR,
|
UARTE2_S as UARTE2,
|
||||||
USBD_S as USBD,
|
UARTE3_S as UARTE3,
|
||||||
USBREGULATOR_S as USBREGULATOR,
|
UICR_S as UICR,
|
||||||
VMC_S as VMC,
|
USBD_S as USBD,
|
||||||
WDT0_S as WDT0,
|
USBREGULATOR_S as USBREGULATOR,
|
||||||
WDT1_S as WDT1,
|
VMC_S as VMC,
|
||||||
};
|
WDT0_S as WDT0,
|
||||||
|
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,122 +48,131 @@ pub mod pac {
|
|||||||
wdt_ns as wdt,
|
wdt_ns as wdt,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "nrf9160-ns")]
|
/// Non-Secure mode (NS) peripherals
|
||||||
#[doc(no_inline)]
|
pub mod ns {
|
||||||
pub use nrf9160_pac::{
|
#[doc(no_inline)]
|
||||||
CLOCK_NS as CLOCK,
|
pub use nrf9160_pac::{
|
||||||
DPPIC_NS as DPPIC,
|
CLOCK_NS as CLOCK,
|
||||||
EGU0_NS as EGU0,
|
DPPIC_NS as DPPIC,
|
||||||
EGU1_NS as EGU1,
|
EGU0_NS as EGU0,
|
||||||
EGU2_NS as EGU2,
|
EGU1_NS as EGU1,
|
||||||
EGU3_NS as EGU3,
|
EGU2_NS as EGU2,
|
||||||
EGU4_NS as EGU4,
|
EGU3_NS as EGU3,
|
||||||
EGU5_NS as EGU5,
|
EGU4_NS as EGU4,
|
||||||
FPU_NS as FPU,
|
EGU5_NS as EGU5,
|
||||||
GPIOTE1_NS as GPIOTE1,
|
FPU_NS as FPU,
|
||||||
I2S_NS as I2S,
|
GPIOTE1_NS as GPIOTE1,
|
||||||
IPC_NS as IPC,
|
I2S_NS as I2S,
|
||||||
KMU_NS as KMU,
|
IPC_NS as IPC,
|
||||||
NVMC_NS as NVMC,
|
KMU_NS as KMU,
|
||||||
P0_NS as P0,
|
NVMC_NS as NVMC,
|
||||||
PDM_NS as PDM,
|
P0_NS as P0,
|
||||||
POWER_NS as POWER,
|
PDM_NS as PDM,
|
||||||
PWM0_NS as PWM0,
|
POWER_NS as POWER,
|
||||||
PWM1_NS as PWM1,
|
PWM0_NS as PWM0,
|
||||||
PWM2_NS as PWM2,
|
PWM1_NS as PWM1,
|
||||||
PWM3_NS as PWM3,
|
PWM2_NS as PWM2,
|
||||||
REGULATORS_NS as REGULATORS,
|
PWM3_NS as PWM3,
|
||||||
RTC0_NS as RTC0,
|
REGULATORS_NS as REGULATORS,
|
||||||
RTC1_NS as RTC1,
|
RTC0_NS as RTC0,
|
||||||
SAADC_NS as SAADC,
|
RTC1_NS as RTC1,
|
||||||
SPIM0_NS as SPIM0,
|
SAADC_NS as SAADC,
|
||||||
SPIM1_NS as SPIM1,
|
SPIM0_NS as SPIM0,
|
||||||
SPIM2_NS as SPIM2,
|
SPIM1_NS as SPIM1,
|
||||||
SPIM3_NS as SPIM3,
|
SPIM2_NS as SPIM2,
|
||||||
SPIS0_NS as SPIS0,
|
SPIM3_NS as SPIM3,
|
||||||
SPIS1_NS as SPIS1,
|
SPIS0_NS as SPIS0,
|
||||||
SPIS2_NS as SPIS2,
|
SPIS1_NS as SPIS1,
|
||||||
SPIS3_NS as SPIS3,
|
SPIS2_NS as SPIS2,
|
||||||
TIMER0_NS as TIMER0,
|
SPIS3_NS as SPIS3,
|
||||||
TIMER1_NS as TIMER1,
|
TIMER0_NS as TIMER0,
|
||||||
TIMER2_NS as TIMER2,
|
TIMER1_NS as TIMER1,
|
||||||
TWIM0_NS as TWIM0,
|
TIMER2_NS as TIMER2,
|
||||||
TWIM1_NS as TWIM1,
|
TWIM0_NS as TWIM0,
|
||||||
TWIM2_NS as TWIM2,
|
TWIM1_NS as TWIM1,
|
||||||
TWIM3_NS as TWIM3,
|
TWIM2_NS as TWIM2,
|
||||||
TWIS0_NS as TWIS0,
|
TWIM3_NS as TWIM3,
|
||||||
TWIS1_NS as TWIS1,
|
TWIS0_NS as TWIS0,
|
||||||
TWIS2_NS as TWIS2,
|
TWIS1_NS as TWIS1,
|
||||||
TWIS3_NS as TWIS3,
|
TWIS2_NS as TWIS2,
|
||||||
UARTE0_NS as UARTE0,
|
TWIS3_NS as TWIS3,
|
||||||
UARTE1_NS as UARTE1,
|
UARTE0_NS as UARTE0,
|
||||||
UARTE2_NS as UARTE2,
|
UARTE1_NS as UARTE1,
|
||||||
UARTE3_NS as UARTE3,
|
UARTE2_NS as UARTE2,
|
||||||
VMC_NS as VMC,
|
UARTE3_NS as UARTE3,
|
||||||
WDT_NS as WDT,
|
VMC_NS as VMC,
|
||||||
};
|
WDT_NS as WDT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "nrf9160-s")]
|
/// Secure mode (S) peripherals
|
||||||
#[doc(no_inline)]
|
pub mod s {
|
||||||
pub use nrf9160_pac::{
|
#[doc(no_inline)]
|
||||||
CC_HOST_RGF_S as CC_HOST_RGF,
|
pub use nrf9160_pac::{
|
||||||
CLOCK_S as CLOCK,
|
CC_HOST_RGF_S as CC_HOST_RGF,
|
||||||
CRYPTOCELL_S as CRYPTOCELL,
|
CLOCK_S as CLOCK,
|
||||||
CTRL_AP_PERI_S as CTRL_AP_PERI,
|
CRYPTOCELL_S as CRYPTOCELL,
|
||||||
DPPIC_S as DPPIC,
|
CTRL_AP_PERI_S as CTRL_AP_PERI,
|
||||||
EGU0_S as EGU0,
|
DPPIC_S as DPPIC,
|
||||||
EGU1_S as EGU1,
|
EGU0_S as EGU0,
|
||||||
EGU2_S as EGU2,
|
EGU1_S as EGU1,
|
||||||
EGU3_S as EGU3,
|
EGU2_S as EGU2,
|
||||||
EGU4_S as EGU4,
|
EGU3_S as EGU3,
|
||||||
EGU5_S as EGU5,
|
EGU4_S as EGU4,
|
||||||
FICR_S as FICR,
|
EGU5_S as EGU5,
|
||||||
FPU_S as FPU,
|
FICR_S as FICR,
|
||||||
GPIOTE0_S as GPIOTE0,
|
FPU_S as FPU,
|
||||||
I2S_S as I2S,
|
GPIOTE0_S as GPIOTE0,
|
||||||
IPC_S as IPC,
|
I2S_S as I2S,
|
||||||
KMU_S as KMU,
|
IPC_S as IPC,
|
||||||
NVMC_S as NVMC,
|
KMU_S as KMU,
|
||||||
P0_S as P0,
|
NVMC_S as NVMC,
|
||||||
PDM_S as PDM,
|
P0_S as P0,
|
||||||
POWER_S as POWER,
|
PDM_S as PDM,
|
||||||
PWM0_S as PWM0,
|
POWER_S as POWER,
|
||||||
PWM1_S as PWM1,
|
PWM0_S as PWM0,
|
||||||
PWM2_S as PWM2,
|
PWM1_S as PWM1,
|
||||||
PWM3_S as PWM3,
|
PWM2_S as PWM2,
|
||||||
REGULATORS_S as REGULATORS,
|
PWM3_S as PWM3,
|
||||||
RTC0_S as RTC0,
|
REGULATORS_S as REGULATORS,
|
||||||
RTC1_S as RTC1,
|
RTC0_S as RTC0,
|
||||||
SAADC_S as SAADC,
|
RTC1_S as RTC1,
|
||||||
SPIM0_S as SPIM0,
|
SAADC_S as SAADC,
|
||||||
SPIM1_S as SPIM1,
|
SPIM0_S as SPIM0,
|
||||||
SPIM2_S as SPIM2,
|
SPIM1_S as SPIM1,
|
||||||
SPIM3_S as SPIM3,
|
SPIM2_S as SPIM2,
|
||||||
SPIS0_S as SPIS0,
|
SPIM3_S as SPIM3,
|
||||||
SPIS1_S as SPIS1,
|
SPIS0_S as SPIS0,
|
||||||
SPIS2_S as SPIS2,
|
SPIS1_S as SPIS1,
|
||||||
SPIS3_S as SPIS3,
|
SPIS2_S as SPIS2,
|
||||||
SPU_S as SPU,
|
SPIS3_S as SPIS3,
|
||||||
TAD_S as TAD,
|
SPU_S as SPU,
|
||||||
TIMER0_S as TIMER0,
|
TAD_S as TAD,
|
||||||
TIMER1_S as TIMER1,
|
TIMER0_S as TIMER0,
|
||||||
TIMER2_S as TIMER2,
|
TIMER1_S as TIMER1,
|
||||||
TWIM0_S as TWIM0,
|
TIMER2_S as TIMER2,
|
||||||
TWIM1_S as TWIM1,
|
TWIM0_S as TWIM0,
|
||||||
TWIM2_S as TWIM2,
|
TWIM1_S as TWIM1,
|
||||||
TWIM3_S as TWIM3,
|
TWIM2_S as TWIM2,
|
||||||
TWIS0_S as TWIS0,
|
TWIM3_S as TWIM3,
|
||||||
TWIS1_S as TWIS1,
|
TWIS0_S as TWIS0,
|
||||||
TWIS2_S as TWIS2,
|
TWIS1_S as TWIS1,
|
||||||
TWIS3_S as TWIS3,
|
TWIS2_S as TWIS2,
|
||||||
UARTE0_S as UARTE0,
|
TWIS3_S as TWIS3,
|
||||||
UARTE1_S as UARTE1,
|
UARTE0_S as UARTE0,
|
||||||
UARTE2_S as UARTE2,
|
UARTE1_S as UARTE1,
|
||||||
UARTE3_S as UARTE3,
|
UARTE2_S as UARTE2,
|
||||||
UICR_S as UICR,
|
UARTE3_S as UARTE3,
|
||||||
VMC_S as VMC,
|
UICR_S as UICR,
|
||||||
WDT_S as WDT,
|
VMC_S as VMC,
|
||||||
};
|
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.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
@ -9,7 +10,6 @@ use core::task::Poll;
|
|||||||
use embassy_hal_internal::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use fixed::types::I7F1;
|
use fixed::types::I7F1;
|
||||||
use futures::future::poll_fn;
|
|
||||||
|
|
||||||
use crate::chip::EASY_DMA_SIZE;
|
use crate::chip::EASY_DMA_SIZE;
|
||||||
use crate::gpio::sealed::Pin;
|
use crate::gpio::sealed::Pin;
|
||||||
|
@ -59,8 +59,8 @@ nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "em
|
|||||||
unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
|
unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.3", 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" }
|
||||||
@ -94,5 +94,5 @@ pio = {version= "0.2.1" }
|
|||||||
rp2040-boot2 = "0.3"
|
rp2040-boot2 = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
embassy-executor = { version = "0.3.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] }
|
embassy-executor = { version = "0.3.1", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] }
|
||||||
static_cell = "1.1"
|
static_cell = { version = "2" }
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,16 @@ 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.4.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.3", 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"
|
||||||
heapless = "0.7.16"
|
heapless = "0.8"
|
||||||
aligned = "0.4.1"
|
aligned = "0.4.1"
|
||||||
|
|
||||||
bit_field = "0.10.2"
|
bit_field = "0.10.2"
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(any(feature = "ble", feature = "mac"), feature(async_fn_in_trait))]
|
#![cfg_attr(any(feature = "ble", feature = "mac"), feature(async_fn_in_trait))]
|
||||||
|
#![cfg_attr(
|
||||||
|
any(feature = "ble", feature = "mac"),
|
||||||
|
allow(stable_features, unknown_lints, async_fn_in_trait)
|
||||||
|
)]
|
||||||
#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))]
|
#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))]
|
||||||
|
|
||||||
// This must go FIRST so that all the other modules see its macros.
|
// This must go FIRST so that all the other modules see its macros.
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
#![allow(incomplete_features)]
|
|
||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
|
|
||||||
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 +59,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])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ flavors = [
|
|||||||
{ regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" },
|
{ regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" },
|
||||||
{ regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
|
{ regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
|
||||||
{ regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
|
{ regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
|
||||||
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" },
|
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] },
|
||||||
{ regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" },
|
{ regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" },
|
||||||
{ regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
|
{ regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
|
||||||
{ regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] },
|
{ regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] },
|
||||||
@ -32,14 +32,14 @@ flavors = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.3", 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.1", path = "../embassy-executor", optional = true }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
||||||
@ -58,7 +58,7 @@ rand_core = "0.6.3"
|
|||||||
sdio-host = "0.5.0"
|
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-6bfa5a0dcec6a9bd42cea94ba11eeae1a17a7f2c" }
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
|
||||||
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-6bfa5a0dcec6a9bd42cea94ba11eeae1a17a7f2c", default-features = false, features = ["metadata"]}
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@ -90,6 +90,7 @@ defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-emb
|
|||||||
|
|
||||||
exti = []
|
exti = []
|
||||||
low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ]
|
low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ]
|
||||||
|
low-power-debug-with-sleep = []
|
||||||
embassy-executor = []
|
embassy-executor = []
|
||||||
|
|
||||||
## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/)
|
## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
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};
|
||||||
|
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use stm32_metapac::metadata::ir::{BlockItemInner, Enum};
|
use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet};
|
||||||
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, METADATA};
|
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, StopMode, METADATA};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let target = env::var("TARGET").unwrap();
|
let target = env::var("TARGET").unwrap();
|
||||||
@ -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,58 +371,59 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (irq, channels) in dma_irqs {
|
let dma_irqs: TokenStream = dma_irqs
|
||||||
let irq = format_ident!("{}", irq);
|
.iter()
|
||||||
|
.map(|(irq, channels)| {
|
||||||
|
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 () {
|
||||||
#(
|
#(
|
||||||
<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);
|
||||||
|
|
||||||
|
// ========
|
||||||
|
// Extract the rcc registers
|
||||||
|
let rcc_registers = METADATA
|
||||||
|
.peripherals
|
||||||
|
.iter()
|
||||||
|
.filter_map(|p| p.registers.as_ref())
|
||||||
|
.find(|r| r.kind == "rcc")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// ========
|
// ========
|
||||||
// Generate rcc fieldset and enum maps
|
// Generate rcc fieldset and enum maps
|
||||||
let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
|
let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
|
||||||
let rcc_registers = METADATA
|
let rcc_blocks = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
|
||||||
.peripherals
|
let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.ir.fieldsets.iter().map(|f| (f.name, f)).collect();
|
||||||
.iter()
|
let rcc_enums: HashMap<&str, &Enum> = rcc_registers.ir.enums.iter().map(|e| (e.name, e)).collect();
|
||||||
.filter_map(|p| p.registers.as_ref())
|
|
||||||
.find(|r| r.kind == "rcc")
|
|
||||||
.unwrap()
|
|
||||||
.ir;
|
|
||||||
|
|
||||||
let rcc_blocks = rcc_registers.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
|
rcc_blocks
|
||||||
|
|
||||||
let rcc_block_item_map: HashMap<&str, &str> = rcc_blocks
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|b| match &b.inner {
|
.filter_map(|b| match &b.inner {
|
||||||
BlockItemInner::Register(register) => register.fieldset.map(|f| (f, b.name)),
|
BlockItemInner::Register(register) => register.fieldset.map(|f| (b.name, f)),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect();
|
.filter_map(|(b, f)| {
|
||||||
|
rcc_fieldsets.get(f).map(|f| {
|
||||||
let rcc_enum_map: HashMap<&str, &Enum> = rcc_registers.enums.iter().map(|e| (e.name, e)).collect();
|
|
||||||
|
|
||||||
rcc_registers
|
|
||||||
.fieldsets
|
|
||||||
.iter()
|
|
||||||
.filter_map(|f| {
|
|
||||||
rcc_block_item_map.get(f.name).map(|b| {
|
|
||||||
(
|
(
|
||||||
*b,
|
b,
|
||||||
f.fields
|
f.fields
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|f| {
|
.filter_map(|f| {
|
||||||
let enumm = f.enumm?;
|
let enumm = f.enumm?;
|
||||||
let enumm = rcc_enum_map.get(enumm)?;
|
let enumm = rcc_enums.get(enumm)?;
|
||||||
|
|
||||||
Some((f.name, *enumm))
|
Some((f.name, *enumm))
|
||||||
})
|
})
|
||||||
@ -437,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()) {
|
||||||
@ -470,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 =
|
||||||
@ -498,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,12 +526,16 @@ 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 },
|
||||||
quote! {
|
}
|
||||||
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name.unwrap() },
|
} else {
|
||||||
|
quote! {
|
||||||
|
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name.unwrap() },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -549,6 +556,31 @@ fn main() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
A refcount leak can result if the same field is shared by peripherals with different stop modes
|
||||||
|
|
||||||
|
This condition should be checked in stm32-data
|
||||||
|
*/
|
||||||
|
let stop_refcount = match rcc.stop_mode {
|
||||||
|
StopMode::Standby => None,
|
||||||
|
StopMode::Stop2 => Some(quote! { REFCOUNT_STOP2 }),
|
||||||
|
StopMode::Stop1 => Some(quote! { REFCOUNT_STOP1 }),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (incr_stop_refcount, decr_stop_refcount) = match stop_refcount {
|
||||||
|
Some(stop_refcount) => (
|
||||||
|
quote! {
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
unsafe { crate::rcc::#stop_refcount += 1 };
|
||||||
|
},
|
||||||
|
quote! {
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
unsafe { crate::rcc::#stop_refcount -= 1 };
|
||||||
|
},
|
||||||
|
),
|
||||||
|
None => (TokenStream::new(), TokenStream::new()),
|
||||||
|
};
|
||||||
|
|
||||||
g.extend(quote! {
|
g.extend(quote! {
|
||||||
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
||||||
fn frequency() -> crate::time::Hertz {
|
fn frequency() -> crate::time::Hertz {
|
||||||
@ -556,8 +588,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")]
|
#incr_stop_refcount
|
||||||
crate::rcc::clock_refcount_add(_cs);
|
|
||||||
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
|
||||||
@ -565,8 +596,7 @@ fn main() {
|
|||||||
fn disable_with_cs(_cs: critical_section::CriticalSection) {
|
fn disable_with_cs(_cs: critical_section::CriticalSection) {
|
||||||
#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")]
|
#decr_stop_refcount
|
||||||
crate::rcc::clock_refcount_sub(_cs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,7 +827,7 @@ fn main() {
|
|||||||
(("fmc", "NCE"), quote!(crate::fmc::NCEPin)),
|
(("fmc", "NCE"), quote!(crate::fmc::NCEPin)),
|
||||||
(("fmc", "NOE"), quote!(crate::fmc::NOEPin)),
|
(("fmc", "NOE"), quote!(crate::fmc::NOEPin)),
|
||||||
(("fmc", "NWE"), quote!(crate::fmc::NWEPin)),
|
(("fmc", "NWE"), quote!(crate::fmc::NWEPin)),
|
||||||
(("fmc", "Clk"), quote!(crate::fmc::ClkPin)),
|
(("fmc", "CLK"), quote!(crate::fmc::ClkPin)),
|
||||||
(("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
|
(("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
|
||||||
(("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
|
(("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
|
||||||
(("timer", "CH1"), quote!(crate::timer::Channel1Pin)),
|
(("timer", "CH1"), quote!(crate::timer::Channel1Pin)),
|
||||||
@ -913,17 +943,23 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if regs.kind == "opamp" {
|
if regs.kind == "opamp" {
|
||||||
if !pin.signal.starts_with("VP") {
|
if pin.signal.starts_with("VP") {
|
||||||
continue;
|
// Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc)
|
||||||
|
let peri = format_ident!("{}", p.name);
|
||||||
|
let pin_name = format_ident!("{}", pin.pin);
|
||||||
|
let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap();
|
||||||
|
|
||||||
|
g.extend(quote! {
|
||||||
|
impl_opamp_vp_pin!( #peri, #pin_name, #ch);
|
||||||
|
})
|
||||||
|
} else if pin.signal == "VOUT" {
|
||||||
|
// Impl OutputPin for the VOUT pin
|
||||||
|
let peri = format_ident!("{}", p.name);
|
||||||
|
let pin_name = format_ident!("{}", pin.pin);
|
||||||
|
g.extend(quote! {
|
||||||
|
impl_opamp_vout_pin!( #peri, #pin_name );
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let peri = format_ident!("{}", p.name);
|
|
||||||
let pin_name = format_ident!("{}", pin.pin);
|
|
||||||
let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap();
|
|
||||||
|
|
||||||
g.extend(quote! {
|
|
||||||
impl_opamp_pin!( #peri, #pin_name, #ch);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DAC is special
|
// DAC is special
|
||||||
@ -1012,15 +1048,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) {
|
||||||
|
@ -299,19 +299,15 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
|
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
|
ch.cr().modify(|w| {
|
||||||
// Disable the channel. Keep the IEs enabled so the irqs still fire.
|
w.set_susp(true);
|
||||||
ch.cr().write(|w| {
|
|
||||||
w.set_tcie(true);
|
|
||||||
w.set_useie(true);
|
|
||||||
w.set_dteie(true);
|
|
||||||
w.set_suspie(true);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
!ch.sr().read().tcf()
|
let sr = ch.sr().read();
|
||||||
|
!sr.tcf() && !sr.suspf()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the total remaining transfers for the channel
|
/// Gets the total remaining transfers for the channel
|
||||||
|
@ -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
|
||||||
|
@ -119,13 +119,11 @@ impl<'a> TDesRing<'a> {
|
|||||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||||
fence(Ordering::Release);
|
fence(Ordering::Release);
|
||||||
|
|
||||||
self.index = self.index + 1;
|
|
||||||
if self.index == self.descriptors.len() {
|
|
||||||
self.index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// signal DMA it can try again.
|
// signal DMA it can try again.
|
||||||
ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0)
|
// See issue #2129
|
||||||
|
ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = &td as *const _ as u32);
|
||||||
|
|
||||||
|
self.index = (self.index + 1) % self.descriptors.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,21 +235,19 @@ impl<'a> RDesRing<'a> {
|
|||||||
|
|
||||||
/// Pop the packet previously returned by `available`.
|
/// Pop the packet previously returned by `available`.
|
||||||
pub(crate) fn pop_packet(&mut self) {
|
pub(crate) fn pop_packet(&mut self) {
|
||||||
let descriptor = &mut self.descriptors[self.index];
|
let rd = &mut self.descriptors[self.index];
|
||||||
assert!(descriptor.available());
|
assert!(rd.available());
|
||||||
|
|
||||||
self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr());
|
rd.set_ready(self.buffers[self.index].0.as_mut_ptr());
|
||||||
|
|
||||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||||
fence(Ordering::Release);
|
fence(Ordering::Release);
|
||||||
|
|
||||||
// signal DMA it can try again.
|
// signal DMA it can try again.
|
||||||
ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0);
|
// See issue #2129
|
||||||
|
ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = &rd as *const _ as u32);
|
||||||
|
|
||||||
// Increment index.
|
// Increment index.
|
||||||
self.index += 1;
|
self.index = (self.index + 1) % self.descriptors.len();
|
||||||
if self.index == self.descriptors.len() {
|
|
||||||
self.index = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -47,6 +47,9 @@ pub unsafe fn on_irq() {
|
|||||||
#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
|
#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
|
||||||
let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0;
|
let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0;
|
||||||
|
|
||||||
|
// We don't handle or change any EXTI lines above 16.
|
||||||
|
let bits = bits & 0x0000FFFF;
|
||||||
|
|
||||||
// Mask all the channels that fired.
|
// Mask all the channels that fired.
|
||||||
cpu_regs().imr(0).modify(|w| w.0 &= !bits);
|
cpu_regs().imr(0).modify(|w| w.0 &= !bits);
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,37 @@ pub struct Fmc<'d, T: Instance> {
|
|||||||
|
|
||||||
unsafe impl<'d, T> Send for Fmc<'d, T> where T: Instance {}
|
unsafe impl<'d, T> Send for Fmc<'d, T> where T: Instance {}
|
||||||
|
|
||||||
|
impl<'d, T> Fmc<'d, T>
|
||||||
|
where
|
||||||
|
T: Instance,
|
||||||
|
{
|
||||||
|
/// Create a raw FMC instance.
|
||||||
|
///
|
||||||
|
/// **Note:** This is currently used to provide access to some basic FMC functions
|
||||||
|
/// for manual configuration for memory types that stm32-fmc does not support.
|
||||||
|
pub fn new_raw(_instance: impl Peripheral<P = T> + 'd) -> Self {
|
||||||
|
Self { peri: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the FMC peripheral and reset it.
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
T::enable_and_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the memory controller on applicable chips.
|
||||||
|
pub fn memory_controller_enable(&mut self) {
|
||||||
|
// fmc v1 and v2 does not have the fmcen bit
|
||||||
|
// fsmc v1, v2 and v3 does not have the fmcen bit
|
||||||
|
// This is a "not" because it is expected that all future versions have this bit
|
||||||
|
#[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))]
|
||||||
|
T::REGS.bcr1().modify(|r| r.set_fmcen(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_clock_hz(&self) -> u32 {
|
||||||
|
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T>
|
unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T>
|
||||||
where
|
where
|
||||||
T: Instance,
|
T: Instance,
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![cfg_attr(not(test), no_std)]
|
#![cfg_attr(not(test), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
|
|
||||||
//! ## Feature flags
|
//! ## Feature flags
|
||||||
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
|
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
|
||||||
@ -226,8 +227,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;
|
||||||
|
crate::rcc::REFCOUNT_STOP1 = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,50 @@
|
|||||||
|
/// The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating
|
||||||
|
/// to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which
|
||||||
|
/// can use knowledge of which peripherals are currently blocked upon to transparently and safely
|
||||||
|
/// enter such low-power modes (currently, only `STOP2`) when idle.
|
||||||
|
///
|
||||||
|
/// The executor determines which peripherals are active by their RCC state; consequently,
|
||||||
|
/// low-power states can only be entered if all peripherals have been `drop`'d. There are a few
|
||||||
|
/// exceptions to this rule:
|
||||||
|
///
|
||||||
|
/// * `GPIO`
|
||||||
|
/// * `RCC`
|
||||||
|
///
|
||||||
|
/// Since entering and leaving low-power modes typically incurs a significant latency, the
|
||||||
|
/// low-power executor will only attempt to enter when the next timer event is at least
|
||||||
|
/// [`time_driver::MIN_STOP_PAUSE`] in the future.
|
||||||
|
///
|
||||||
|
/// Currently there is no macro analogous to `embassy_executor::main` for this executor;
|
||||||
|
/// consequently one must define their entrypoint manually. Moveover, you must relinquish control
|
||||||
|
/// of the `RTC` peripheral to the executor. This will typically look like
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use embassy_executor::Spawner;
|
||||||
|
/// use embassy_stm32::low_power::Executor;
|
||||||
|
/// use embassy_stm32::rtc::{Rtc, RtcConfig};
|
||||||
|
/// use static_cell::make_static;
|
||||||
|
///
|
||||||
|
/// #[cortex_m_rt::entry]
|
||||||
|
/// fn main() -> ! {
|
||||||
|
/// Executor::take().run(|spawner| {
|
||||||
|
/// unwrap!(spawner.spawn(async_main(spawner)));
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[embassy_executor::task]
|
||||||
|
/// async fn async_main(spawner: Spawner) {
|
||||||
|
/// // initialize the platform...
|
||||||
|
/// let mut config = embassy_stm32::Config::default();
|
||||||
|
/// let p = embassy_stm32::init(config);
|
||||||
|
///
|
||||||
|
/// // give the RTC to the executor...
|
||||||
|
/// let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
|
||||||
|
/// let rtc = make_static!(rtc);
|
||||||
|
/// embassy_stm32::low_power::stop_with_rtc(rtc);
|
||||||
|
///
|
||||||
|
/// // your application here...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
@ -6,7 +53,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 +79,21 @@ 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 {
|
||||||
|
match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() {
|
||||||
|
Some(StopMode::Stop2) => true,
|
||||||
|
Some(StopMode::Stop1) => stop_mode == StopMode::Stop1,
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum StopMode {
|
||||||
|
Stop1,
|
||||||
|
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
|
||||||
@ -53,7 +114,7 @@ pub struct Executor {
|
|||||||
impl Executor {
|
impl Executor {
|
||||||
/// Create a new Executor.
|
/// Create a new Executor.
|
||||||
pub fn take() -> &'static mut Self {
|
pub fn take() -> &'static mut Self {
|
||||||
unsafe {
|
critical_section::with(|_| unsafe {
|
||||||
assert!(EXECUTOR.is_none());
|
assert!(EXECUTOR.is_none());
|
||||||
|
|
||||||
EXECUTOR = Some(Self {
|
EXECUTOR = Some(Self {
|
||||||
@ -64,7 +125,7 @@ impl Executor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
EXECUTOR.as_mut().unwrap()
|
EXECUTOR.as_mut().unwrap()
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn on_wakeup_irq(&mut self) {
|
unsafe fn on_wakeup_irq(&mut self) {
|
||||||
@ -80,17 +141,39 @@ impl Executor {
|
|||||||
trace!("low power: stop with rtc configured");
|
trace!("low power: stop with rtc configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stop_mode(&self) -> Option<StopMode> {
|
||||||
|
if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } && unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } {
|
||||||
|
Some(StopMode::Stop2)
|
||||||
|
} else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } {
|
||||||
|
Some(StopMode::Stop1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_stop(&mut self, _stop_mode: StopMode) {
|
||||||
|
// TODO: configure chip-specific settings for stop
|
||||||
|
}
|
||||||
|
|
||||||
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() {
|
let stop_mode = self.stop_mode();
|
||||||
|
if stop_mode.is_none() {
|
||||||
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");
|
||||||
} else {
|
} else {
|
||||||
trace!("low power: stop");
|
let stop_mode = stop_mode.unwrap();
|
||||||
|
match stop_mode {
|
||||||
|
StopMode::Stop1 => trace!("low power: stop 1"),
|
||||||
|
StopMode::Stop2 => trace!("low power: stop 2"),
|
||||||
|
}
|
||||||
|
self.configure_stop(stop_mode);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "low-power-debug-with-sleep"))]
|
||||||
self.scb.set_sleepdeep();
|
self.scb.set_sleepdeep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,21 +13,50 @@ pub enum OpAmpGain {
|
|||||||
Mul16,
|
Mul16,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin<T>> {
|
#[derive(Clone, Copy)]
|
||||||
_inner: &'d OpAmp<'d, T>,
|
pub enum OpAmpSpeed {
|
||||||
_input: &'p mut P,
|
Normal,
|
||||||
|
HighSpeed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(opamp_g4)]
|
||||||
|
impl From<OpAmpSpeed> for crate::pac::opamp::vals::OpampCsrOpahsm {
|
||||||
|
fn from(v: OpAmpSpeed) -> Self {
|
||||||
|
match v {
|
||||||
|
OpAmpSpeed::Normal => crate::pac::opamp::vals::OpampCsrOpahsm::NORMAL,
|
||||||
|
OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::OpampCsrOpahsm::HIGHSPEED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// OpAmp external outputs, wired to a GPIO pad.
|
||||||
|
///
|
||||||
|
/// The GPIO output pad is held by this struct to ensure it cannot be used elsewhere.
|
||||||
|
///
|
||||||
|
/// This struct can also be used as an ADC input.
|
||||||
|
pub struct OpAmpOutput<'d, 'p, T: Instance, P: OutputPin<T>> {
|
||||||
|
_inner: &'d OpAmp<'d, T>,
|
||||||
|
_output: &'p mut P,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// OpAmp internal outputs, wired directly to ADC inputs.
|
||||||
|
///
|
||||||
|
/// This struct can be used as an ADC input.
|
||||||
|
pub struct OpAmpInternalOutput<'d, T: Instance> {
|
||||||
|
_inner: &'d OpAmp<'d, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// OpAmp driver.
|
||||||
pub struct OpAmp<'d, T: Instance> {
|
pub struct OpAmp<'d, T: Instance> {
|
||||||
_inner: PeripheralRef<'d, T>,
|
_inner: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> OpAmp<'d, T> {
|
impl<'d, T: Instance> OpAmp<'d, T> {
|
||||||
pub fn new(opamp: impl Peripheral<P = T> + 'd) -> Self {
|
/// Create a new driver instance.
|
||||||
Self::new_inner(opamp)
|
///
|
||||||
}
|
/// Enables the OpAmp and configures the speed, but
|
||||||
|
/// does not set any other configuration.
|
||||||
fn new_inner(opamp: impl Peripheral<P = T> + 'd) -> Self {
|
pub fn new(opamp: impl Peripheral<P = T> + 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self {
|
||||||
into_ref!(opamp);
|
into_ref!(opamp);
|
||||||
|
|
||||||
#[cfg(opamp_f3)]
|
#[cfg(opamp_f3)]
|
||||||
@ -38,15 +67,34 @@ impl<'d, T: Instance> OpAmp<'d, T> {
|
|||||||
#[cfg(opamp_g4)]
|
#[cfg(opamp_g4)]
|
||||||
T::regs().opamp_csr().modify(|w| {
|
T::regs().opamp_csr().modify(|w| {
|
||||||
w.set_opaen(true);
|
w.set_opaen(true);
|
||||||
|
w.set_opahsm(speed.into());
|
||||||
});
|
});
|
||||||
|
|
||||||
Self { _inner: opamp }
|
Self { _inner: opamp }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_for<'a, 'b, P>(&'a mut self, pin: &'b mut P, gain: OpAmpGain) -> OpAmpOutput<'a, 'b, T, P>
|
/// Configure the OpAmp as a buffer for the provided input pin,
|
||||||
|
/// outputting to the provided output pin.
|
||||||
|
///
|
||||||
|
/// The input pin is configured for analogue mode but not consumed,
|
||||||
|
/// so it may subsequently be used for ADC or comparator inputs.
|
||||||
|
///
|
||||||
|
/// The output pin is held within the returned [`OpAmpOutput`] struct,
|
||||||
|
/// preventing it being used elsewhere. The `OpAmpOutput` can then be
|
||||||
|
/// directly used as an ADC input.
|
||||||
|
pub fn buffer_ext<'a, 'b, IP, OP>(
|
||||||
|
&'a mut self,
|
||||||
|
in_pin: &IP,
|
||||||
|
out_pin: &'b mut OP,
|
||||||
|
gain: OpAmpGain,
|
||||||
|
) -> OpAmpOutput<'a, 'b, T, OP>
|
||||||
where
|
where
|
||||||
P: NonInvertingPin<T>,
|
IP: NonInvertingPin<T> + crate::gpio::sealed::Pin,
|
||||||
|
OP: OutputPin<T> + crate::gpio::sealed::Pin,
|
||||||
{
|
{
|
||||||
|
in_pin.set_as_analog();
|
||||||
|
out_pin.set_as_analog();
|
||||||
|
|
||||||
let (vm_sel, pga_gain) = match gain {
|
let (vm_sel, pga_gain) = match gain {
|
||||||
OpAmpGain::Mul1 => (0b11, 0b00),
|
OpAmpGain::Mul1 => (0b11, 0b00),
|
||||||
OpAmpGain::Mul2 => (0b10, 0b00),
|
OpAmpGain::Mul2 => (0b10, 0b00),
|
||||||
@ -57,25 +105,76 @@ impl<'d, T: Instance> OpAmp<'d, T> {
|
|||||||
|
|
||||||
#[cfg(opamp_f3)]
|
#[cfg(opamp_f3)]
|
||||||
T::regs().opampcsr().modify(|w| {
|
T::regs().opampcsr().modify(|w| {
|
||||||
w.set_vp_sel(pin.channel());
|
w.set_vp_sel(in_pin.channel());
|
||||||
w.set_vm_sel(vm_sel);
|
w.set_vm_sel(vm_sel);
|
||||||
w.set_pga_gain(pga_gain);
|
w.set_pga_gain(pga_gain);
|
||||||
|
w.set_opampen(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(opamp_g4)]
|
#[cfg(opamp_g4)]
|
||||||
T::regs().opamp_csr().modify(|w| {
|
T::regs().opamp_csr().modify(|w| {
|
||||||
use crate::pac::opamp::vals::*;
|
use crate::pac::opamp::vals::*;
|
||||||
|
|
||||||
w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel()));
|
w.set_vp_sel(OpampCsrVpSel::from_bits(in_pin.channel()));
|
||||||
w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel));
|
w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel));
|
||||||
w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain));
|
w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain));
|
||||||
|
w.set_opaintoen(OpampCsrOpaintoen::OUTPUTPIN);
|
||||||
|
w.set_opaen(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
OpAmpOutput {
|
OpAmpOutput {
|
||||||
_inner: self,
|
_inner: self,
|
||||||
_input: pin,
|
_output: out_pin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configure the OpAmp as a buffer for the provided input pin,
|
||||||
|
/// with the output only used internally.
|
||||||
|
///
|
||||||
|
/// The input pin is configured for analogue mode but not consumed,
|
||||||
|
/// so it may be subsequently used for ADC or comparator inputs.
|
||||||
|
///
|
||||||
|
/// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
|
||||||
|
#[cfg(opamp_g4)]
|
||||||
|
pub fn buffer_int<'a, P>(&'a mut self, pin: &P, gain: OpAmpGain) -> OpAmpInternalOutput<'a, T>
|
||||||
|
where
|
||||||
|
P: NonInvertingPin<T> + crate::gpio::sealed::Pin,
|
||||||
|
{
|
||||||
|
pin.set_as_analog();
|
||||||
|
|
||||||
|
let (vm_sel, pga_gain) = match gain {
|
||||||
|
OpAmpGain::Mul1 => (0b11, 0b00),
|
||||||
|
OpAmpGain::Mul2 => (0b10, 0b00),
|
||||||
|
OpAmpGain::Mul4 => (0b10, 0b01),
|
||||||
|
OpAmpGain::Mul8 => (0b10, 0b10),
|
||||||
|
OpAmpGain::Mul16 => (0b10, 0b11),
|
||||||
|
};
|
||||||
|
|
||||||
|
T::regs().opamp_csr().modify(|w| {
|
||||||
|
use crate::pac::opamp::vals::*;
|
||||||
|
w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel()));
|
||||||
|
w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel));
|
||||||
|
w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain));
|
||||||
|
w.set_opaintoen(OpampCsrOpaintoen::ADCCHANNEL);
|
||||||
|
w.set_opaen(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
OpAmpInternalOutput { _inner: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Drop for OpAmp<'d, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
#[cfg(opamp_f3)]
|
||||||
|
T::regs().opampcsr().modify(|w| {
|
||||||
|
w.set_opampen(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(opamp_g4)]
|
||||||
|
T::regs().opamp_csr().modify(|w| {
|
||||||
|
w.set_opaen(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance: sealed::Instance + 'static {}
|
pub trait Instance: sealed::Instance + 'static {}
|
||||||
@ -92,43 +191,108 @@ pub(crate) mod sealed {
|
|||||||
pub trait InvertingPin<T: Instance> {
|
pub trait InvertingPin<T: Instance> {
|
||||||
fn channel(&self) -> u8;
|
fn channel(&self) -> u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait OutputPin<T: Instance> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {}
|
pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {}
|
||||||
|
|
||||||
pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
|
pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
|
||||||
|
pub trait OutputPin<T: Instance>: sealed::OutputPin<T> {}
|
||||||
|
|
||||||
#[cfg(opamp_f3)]
|
macro_rules! impl_opamp_external_output {
|
||||||
macro_rules! impl_opamp_output {
|
|
||||||
($inst:ident, $adc:ident, $ch:expr) => {
|
($inst:ident, $adc:ident, $ch:expr) => {
|
||||||
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
|
foreach_adc!(
|
||||||
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
($adc, $common_inst:ident, $adc_clock:ident) => {
|
||||||
{
|
impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
|
||||||
fn channel(&self) -> u8 {
|
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
||||||
$ch
|
{
|
||||||
}
|
fn channel(&self) -> u8 {
|
||||||
}
|
$ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
|
impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
|
||||||
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(opamp_f3)]
|
|
||||||
foreach_peripheral!(
|
foreach_peripheral!(
|
||||||
(opamp, OPAMP1) => {
|
(opamp, OPAMP1) => {
|
||||||
impl_opamp_output!(OPAMP1, ADC1, 3);
|
impl_opamp_external_output!(OPAMP1, ADC1, 3);
|
||||||
};
|
};
|
||||||
(opamp, OPAMP2) => {
|
(opamp, OPAMP2) => {
|
||||||
impl_opamp_output!(OPAMP2, ADC2, 3);
|
impl_opamp_external_output!(OPAMP2, ADC2, 3);
|
||||||
};
|
};
|
||||||
(opamp, OPAMP3) => {
|
(opamp, OPAMP3) => {
|
||||||
impl_opamp_output!(OPAMP3, ADC3, 1);
|
impl_opamp_external_output!(OPAMP3, ADC3, 1);
|
||||||
};
|
};
|
||||||
|
// OPAMP4 only in STM32G4 Cat 3 devices
|
||||||
(opamp, OPAMP4) => {
|
(opamp, OPAMP4) => {
|
||||||
impl_opamp_output!(OPAMP4, ADC4, 3);
|
impl_opamp_external_output!(OPAMP4, ADC4, 3);
|
||||||
|
};
|
||||||
|
// OPAMP5 only in STM32G4 Cat 3 devices
|
||||||
|
(opamp, OPAMP5) => {
|
||||||
|
impl_opamp_external_output!(OPAMP5, ADC5, 1);
|
||||||
|
};
|
||||||
|
// OPAMP6 only in STM32G4 Cat 3/4 devices
|
||||||
|
(opamp, OPAMP6) => {
|
||||||
|
impl_opamp_external_output!(OPAMP6, ADC1, 14);
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(opamp_g4)]
|
||||||
|
macro_rules! impl_opamp_internal_output {
|
||||||
|
($inst:ident, $adc:ident, $ch:expr) => {
|
||||||
|
foreach_adc!(
|
||||||
|
($adc, $common_inst:ident, $adc_clock:ident) => {
|
||||||
|
impl<'d> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
|
||||||
|
for OpAmpInternalOutput<'d, crate::peripherals::$inst>
|
||||||
|
{
|
||||||
|
fn channel(&self) -> u8 {
|
||||||
|
$ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> crate::adc::AdcPin<crate::peripherals::$adc>
|
||||||
|
for OpAmpInternalOutput<'d, crate::peripherals::$inst>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(opamp_g4)]
|
||||||
|
foreach_peripheral!(
|
||||||
|
(opamp, OPAMP1) => {
|
||||||
|
impl_opamp_internal_output!(OPAMP1, ADC1, 13);
|
||||||
|
};
|
||||||
|
(opamp, OPAMP2) => {
|
||||||
|
impl_opamp_internal_output!(OPAMP2, ADC2, 16);
|
||||||
|
};
|
||||||
|
(opamp, OPAMP3) => {
|
||||||
|
impl_opamp_internal_output!(OPAMP3, ADC2, 18);
|
||||||
|
// Only in Cat 3/4 devices
|
||||||
|
impl_opamp_internal_output!(OPAMP3, ADC3, 13);
|
||||||
|
};
|
||||||
|
// OPAMP4 only in Cat 3 devices
|
||||||
|
(opamp, OPAMP4) => {
|
||||||
|
impl_opamp_internal_output!(OPAMP4, ADC5, 5);
|
||||||
|
};
|
||||||
|
// OPAMP5 only in Cat 3 devices
|
||||||
|
(opamp, OPAMP5) => {
|
||||||
|
impl_opamp_internal_output!(OPAMP5, ADC5, 3);
|
||||||
|
};
|
||||||
|
// OPAMP6 only in Cat 3/4 devices
|
||||||
|
(opamp, OPAMP6) => {
|
||||||
|
// Only in Cat 3 devices
|
||||||
|
impl_opamp_internal_output!(OPAMP6, ADC4, 17);
|
||||||
|
// Only in Cat 4 devices
|
||||||
|
impl_opamp_internal_output!(OPAMP6, ADC3, 17);
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -141,13 +305,12 @@ foreach_peripheral! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for crate::peripherals::$inst {
|
impl Instance for crate::peripherals::$inst {
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
macro_rules! impl_opamp_pin {
|
macro_rules! impl_opamp_vp_pin {
|
||||||
($inst:ident, $pin:ident, $ch:expr) => {
|
($inst:ident, $pin:ident, $ch:expr) => {
|
||||||
impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {}
|
impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||||
impl crate::opamp::sealed::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {
|
impl crate::opamp::sealed::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {
|
||||||
@ -157,3 +320,11 @@ macro_rules! impl_opamp_pin {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_macros)]
|
||||||
|
macro_rules! impl_opamp_vout_pin {
|
||||||
|
($inst:ident, $pin:ident) => {
|
||||||
|
impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||||
|
impl crate::opamp::sealed::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
469
embassy-stm32/src/rcc/f.rs
Normal file
469
embassy-stm32/src/rcc/f.rs
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
use stm32_metapac::flash::vals::Latency;
|
||||||
|
|
||||||
|
pub use crate::pac::rcc::vals::{
|
||||||
|
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,
|
||||||
|
Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
|
||||||
|
};
|
||||||
|
#[cfg(any(stm32f4, stm32f7))]
|
||||||
|
use crate::pac::PWR;
|
||||||
|
use crate::pac::{FLASH, RCC};
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
// 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<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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Voltage range of the power supply used.
|
||||||
|
///
|
||||||
|
/// Used to calculate flash waitstates. See
|
||||||
|
/// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency
|
||||||
|
#[cfg(stm32f2)]
|
||||||
|
pub enum VoltageScale {
|
||||||
|
/// 2.7 to 3.6 V
|
||||||
|
Range0,
|
||||||
|
/// 2.4 to 2.7 V
|
||||||
|
Range1,
|
||||||
|
/// 2.1 to 2.4 V
|
||||||
|
Range2,
|
||||||
|
/// 1.8 to 2.1 V
|
||||||
|
Range3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration of the core clocks
|
||||||
|
#[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(stm32f2, 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,
|
||||||
|
|
||||||
|
#[cfg(stm32f2)]
|
||||||
|
pub voltage: VoltageScale,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hsi: true,
|
||||||
|
hse: None,
|
||||||
|
sys: Sysclk::HSI,
|
||||||
|
pll_src: PllSource::HSI,
|
||||||
|
pll: None,
|
||||||
|
#[cfg(any(stm32f2, 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(),
|
||||||
|
|
||||||
|
#[cfg(stm32f2)]
|
||||||
|
voltage: VoltageScale::Range3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
// set VOS to SCALE1, if use PLL
|
||||||
|
// TODO: check real clock speed before set VOS
|
||||||
|
#[cfg(any(stm32f4, stm32f7))]
|
||||||
|
if config.pll.is_some() {
|
||||||
|
PWR.cr1().modify(|w| w.set_vos(crate::pac::pwr::vals::Vos::SCALE1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// always enable overdrive for now. Make it configurable in the future.
|
||||||
|
#[cfg(any(stm32f446, stm32f4x9, stm32f427, stm32f437, stm32f7))]
|
||||||
|
{
|
||||||
|
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(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
|
||||||
|
let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input);
|
||||||
|
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
|
||||||
|
let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
#[cfg(stm32f2)]
|
||||||
|
let latency = match (config.voltage, hclk.0) {
|
||||||
|
(VoltageScale::Range3, ..=16_000_000) => Latency::WS0,
|
||||||
|
(VoltageScale::Range3, ..=32_000_000) => Latency::WS1,
|
||||||
|
(VoltageScale::Range3, ..=48_000_000) => Latency::WS2,
|
||||||
|
(VoltageScale::Range3, ..=64_000_000) => Latency::WS3,
|
||||||
|
(VoltageScale::Range3, ..=80_000_000) => Latency::WS4,
|
||||||
|
(VoltageScale::Range3, ..=96_000_000) => Latency::WS5,
|
||||||
|
(VoltageScale::Range3, ..=112_000_000) => Latency::WS6,
|
||||||
|
(VoltageScale::Range3, ..=120_000_000) => Latency::WS7,
|
||||||
|
(VoltageScale::Range2, ..=18_000_000) => Latency::WS0,
|
||||||
|
(VoltageScale::Range2, ..=36_000_000) => Latency::WS1,
|
||||||
|
(VoltageScale::Range2, ..=54_000_000) => Latency::WS2,
|
||||||
|
(VoltageScale::Range2, ..=72_000_000) => Latency::WS3,
|
||||||
|
(VoltageScale::Range2, ..=90_000_000) => Latency::WS4,
|
||||||
|
(VoltageScale::Range2, ..=108_000_000) => Latency::WS5,
|
||||||
|
(VoltageScale::Range2, ..=120_000_000) => Latency::WS6,
|
||||||
|
(VoltageScale::Range1, ..=24_000_000) => Latency::WS0,
|
||||||
|
(VoltageScale::Range1, ..=48_000_000) => Latency::WS1,
|
||||||
|
(VoltageScale::Range1, ..=72_000_000) => Latency::WS2,
|
||||||
|
(VoltageScale::Range1, ..=96_000_000) => Latency::WS3,
|
||||||
|
(VoltageScale::Range1, ..=120_000_000) => Latency::WS4,
|
||||||
|
(VoltageScale::Range0, ..=30_000_000) => Latency::WS0,
|
||||||
|
(VoltageScale::Range0, ..=60_000_000) => Latency::WS1,
|
||||||
|
(VoltageScale::Range0, ..=90_000_000) => Latency::WS2,
|
||||||
|
(VoltageScale::Range0, ..=120_000_000) => Latency::WS3,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(any(stm32f4, stm32f7))]
|
||||||
|
let latency = {
|
||||||
|
// Be conservative with voltage ranges
|
||||||
|
const FLASH_LATENCY_STEP: u32 = 30_000_000;
|
||||||
|
|
||||||
|
let latency = (hclk.0 - 1) / FLASH_LATENCY_STEP;
|
||||||
|
debug!("flash: latency={}", latency);
|
||||||
|
|
||||||
|
Latency::from_bits(latency as u8)
|
||||||
|
};
|
||||||
|
|
||||||
|
FLASH.acr().write(|w| w.set_latency(latency));
|
||||||
|
while FLASH.acr().read().latency() != latency {}
|
||||||
|
|
||||||
|
RCC.cfgr().modify(|w| {
|
||||||
|
w.set_sw(config.sys);
|
||||||
|
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(stm32f2, 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(stm32f2, 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));
|
||||||
|
|
||||||
|
// stm32f2 plls are like swiss cheese
|
||||||
|
#[cfg(stm32f2)]
|
||||||
|
match instance {
|
||||||
|
PllInstance::Pll => {
|
||||||
|
assert!(pll.divr.is_none());
|
||||||
|
}
|
||||||
|
PllInstance::Plli2s => {
|
||||||
|
assert!(pll.divp.is_none());
|
||||||
|
assert!(pll.divq.is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let p = pll.divp.map(|div| vco_freq / div);
|
||||||
|
let q = pll.divq.map(|div| vco_freq / div);
|
||||||
|
let r = pll.divr.map(|div| vco_freq / div);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#[cfg(any(stm32f4, stm32f7))]
|
||||||
|
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(stm32f2)]
|
||||||
|
PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {
|
||||||
|
if let Some(divr) = pll.divr {
|
||||||
|
w.set_pllr(divr);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
|
||||||
|
PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| {
|
||||||
|
write_fields!(w);
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable PLL
|
||||||
|
pll_enable(instance, true);
|
||||||
|
|
||||||
|
PllOutput { p, q, r }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(stm32f2)]
|
||||||
|
mod max {
|
||||||
|
use core::ops::RangeInclusive;
|
||||||
|
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000);
|
||||||
|
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(26_000_000);
|
||||||
|
|
||||||
|
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(120_000_000);
|
||||||
|
|
||||||
|
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0);
|
||||||
|
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0 / 4);
|
||||||
|
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0 / 2);
|
||||||
|
|
||||||
|
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(0_950_000)..=Hertz(2_100_000);
|
||||||
|
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(192_000_000)..=Hertz(432_000_000);
|
||||||
|
}
|
@ -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,
|
||||||
});
|
});
|
||||||
|
@ -1,320 +0,0 @@
|
|||||||
use crate::pac::flash::vals::Latency;
|
|
||||||
use crate::pac::rcc::vals::Sw;
|
|
||||||
pub use crate::pac::rcc::vals::{
|
|
||||||
Hpre as AHBPrescaler, Pllm as PLLPreDiv, Plln as PLLMul, Pllp as PLLPDiv, Pllq as PLLQDiv, Pllsrc as PLLSrc,
|
|
||||||
Ppre as APBPrescaler,
|
|
||||||
};
|
|
||||||
use crate::pac::{FLASH, RCC};
|
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
|
||||||
use crate::time::Hertz;
|
|
||||||
|
|
||||||
/// HSI speed
|
|
||||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct HSEConfig {
|
|
||||||
pub frequency: Hertz,
|
|
||||||
pub source: HSESrc,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
HSE,
|
|
||||||
HSI,
|
|
||||||
PLL,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HSE clock source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum HSESrc {
|
|
||||||
/// Crystal/ceramic resonator
|
|
||||||
Crystal,
|
|
||||||
/// External clock source, HSE bypassed
|
|
||||||
Bypass,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct PLLConfig {
|
|
||||||
pub pre_div: PLLPreDiv,
|
|
||||||
pub mul: PLLMul,
|
|
||||||
pub p_div: PLLPDiv,
|
|
||||||
pub q_div: PLLQDiv,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PLLConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
PLLConfig {
|
|
||||||
pre_div: PLLPreDiv::DIV16,
|
|
||||||
mul: PLLMul::MUL192,
|
|
||||||
p_div: PLLPDiv::DIV2,
|
|
||||||
q_div: PLLQDiv::DIV4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PLLConfig {
|
|
||||||
pub fn clocks(&self, src_freq: Hertz) -> PLLClocks {
|
|
||||||
let in_freq = src_freq / self.pre_div;
|
|
||||||
let vco_freq = src_freq / self.pre_div * self.mul;
|
|
||||||
let main_freq = vco_freq / self.p_div;
|
|
||||||
let pll48_freq = vco_freq / self.q_div;
|
|
||||||
PLLClocks {
|
|
||||||
in_freq,
|
|
||||||
vco_freq,
|
|
||||||
main_freq,
|
|
||||||
pll48_freq,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub struct PLLClocks {
|
|
||||||
pub in_freq: Hertz,
|
|
||||||
pub vco_freq: Hertz,
|
|
||||||
pub main_freq: Hertz,
|
|
||||||
pub pll48_freq: Hertz,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Voltage range of the power supply used.
|
|
||||||
///
|
|
||||||
/// Used to calculate flash waitstates. See
|
|
||||||
/// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency
|
|
||||||
pub enum VoltageScale {
|
|
||||||
/// 2.7 to 3.6 V
|
|
||||||
Range0,
|
|
||||||
/// 2.4 to 2.7 V
|
|
||||||
Range1,
|
|
||||||
/// 2.1 to 2.4 V
|
|
||||||
Range2,
|
|
||||||
/// 1.8 to 2.1 V
|
|
||||||
Range3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VoltageScale {
|
|
||||||
const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> {
|
|
||||||
let ahb_freq = ahb_freq.0;
|
|
||||||
// Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock
|
|
||||||
// frequency
|
|
||||||
match self {
|
|
||||||
VoltageScale::Range3 => {
|
|
||||||
if ahb_freq <= 16_000_000 {
|
|
||||||
Some(Latency::WS0)
|
|
||||||
} else if ahb_freq <= 32_000_000 {
|
|
||||||
Some(Latency::WS1)
|
|
||||||
} else if ahb_freq <= 48_000_000 {
|
|
||||||
Some(Latency::WS2)
|
|
||||||
} else if ahb_freq <= 64_000_000 {
|
|
||||||
Some(Latency::WS3)
|
|
||||||
} else if ahb_freq <= 80_000_000 {
|
|
||||||
Some(Latency::WS4)
|
|
||||||
} else if ahb_freq <= 96_000_000 {
|
|
||||||
Some(Latency::WS5)
|
|
||||||
} else if ahb_freq <= 112_000_000 {
|
|
||||||
Some(Latency::WS6)
|
|
||||||
} else if ahb_freq <= 120_000_000 {
|
|
||||||
Some(Latency::WS7)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VoltageScale::Range2 => {
|
|
||||||
if ahb_freq <= 18_000_000 {
|
|
||||||
Some(Latency::WS0)
|
|
||||||
} else if ahb_freq <= 36_000_000 {
|
|
||||||
Some(Latency::WS1)
|
|
||||||
} else if ahb_freq <= 54_000_000 {
|
|
||||||
Some(Latency::WS2)
|
|
||||||
} else if ahb_freq <= 72_000_000 {
|
|
||||||
Some(Latency::WS3)
|
|
||||||
} else if ahb_freq <= 90_000_000 {
|
|
||||||
Some(Latency::WS4)
|
|
||||||
} else if ahb_freq <= 108_000_000 {
|
|
||||||
Some(Latency::WS5)
|
|
||||||
} else if ahb_freq <= 120_000_000 {
|
|
||||||
Some(Latency::WS6)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VoltageScale::Range1 => {
|
|
||||||
if ahb_freq <= 24_000_000 {
|
|
||||||
Some(Latency::WS0)
|
|
||||||
} else if ahb_freq <= 48_000_000 {
|
|
||||||
Some(Latency::WS1)
|
|
||||||
} else if ahb_freq <= 72_000_000 {
|
|
||||||
Some(Latency::WS2)
|
|
||||||
} else if ahb_freq <= 96_000_000 {
|
|
||||||
Some(Latency::WS3)
|
|
||||||
} else if ahb_freq <= 120_000_000 {
|
|
||||||
Some(Latency::WS4)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VoltageScale::Range0 => {
|
|
||||||
if ahb_freq <= 30_000_000 {
|
|
||||||
Some(Latency::WS0)
|
|
||||||
} else if ahb_freq <= 60_000_000 {
|
|
||||||
Some(Latency::WS1)
|
|
||||||
} else if ahb_freq <= 90_000_000 {
|
|
||||||
Some(Latency::WS2)
|
|
||||||
} else if ahb_freq <= 120_000_000 {
|
|
||||||
Some(Latency::WS3)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configuration
|
|
||||||
pub struct Config {
|
|
||||||
pub hse: Option<HSEConfig>,
|
|
||||||
pub hsi: bool,
|
|
||||||
pub pll_mux: PLLSrc,
|
|
||||||
pub pll: PLLConfig,
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub voltage: VoltageScale,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
pub ls: super::LsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
hse: None,
|
|
||||||
hsi: true,
|
|
||||||
pll_mux: PLLSrc::HSI,
|
|
||||||
pll: PLLConfig::default(),
|
|
||||||
voltage: VoltageScale::Range3,
|
|
||||||
mux: ClockSrc::HSI,
|
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
|
||||||
ls: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) unsafe fn init(config: Config) {
|
|
||||||
// Make sure HSI is enabled
|
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
|
||||||
|
|
||||||
if let Some(hse_config) = config.hse {
|
|
||||||
RCC.cr().modify(|w| {
|
|
||||||
w.set_hsebyp(match hse_config.source {
|
|
||||||
HSESrc::Bypass => true,
|
|
||||||
HSESrc::Crystal => false,
|
|
||||||
});
|
|
||||||
w.set_hseon(true)
|
|
||||||
});
|
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pll_src_freq = match config.pll_mux {
|
|
||||||
PLLSrc::HSE => {
|
|
||||||
let hse_config = config
|
|
||||||
.hse
|
|
||||||
.unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input"));
|
|
||||||
hse_config.frequency
|
|
||||||
}
|
|
||||||
PLLSrc::HSI => HSI_FREQ,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reference: STM32F215xx/217xx datasheet Table 33. Main PLL characteristics
|
|
||||||
let pll_clocks = config.pll.clocks(pll_src_freq);
|
|
||||||
assert!(Hertz(950_000) <= pll_clocks.in_freq && pll_clocks.in_freq <= Hertz(2_100_000));
|
|
||||||
assert!(Hertz(192_000_000) <= pll_clocks.vco_freq && pll_clocks.vco_freq <= Hertz(432_000_000));
|
|
||||||
assert!(Hertz(24_000_000) <= pll_clocks.main_freq && pll_clocks.main_freq <= Hertz(120_000_000));
|
|
||||||
// USB actually requires == 48 MHz, but other PLL48 peripherals are fine with <= 48MHz
|
|
||||||
assert!(pll_clocks.pll48_freq <= Hertz(48_000_000));
|
|
||||||
|
|
||||||
RCC.pllcfgr().write(|w| {
|
|
||||||
w.set_pllsrc(config.pll_mux);
|
|
||||||
w.set_pllm(config.pll.pre_div);
|
|
||||||
w.set_plln(config.pll.mul);
|
|
||||||
w.set_pllp(config.pll.p_div);
|
|
||||||
w.set_pllq(config.pll.q_div);
|
|
||||||
});
|
|
||||||
|
|
||||||
let (sys_clk, sw) = match config.mux {
|
|
||||||
ClockSrc::HSI => {
|
|
||||||
assert!(config.hsi, "HSI must be enabled to be used as system clock");
|
|
||||||
(HSI_FREQ, Sw::HSI)
|
|
||||||
}
|
|
||||||
ClockSrc::HSE => {
|
|
||||||
let hse_config = config
|
|
||||||
.hse
|
|
||||||
.unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input"));
|
|
||||||
(hse_config.frequency, Sw::HSE)
|
|
||||||
}
|
|
||||||
ClockSrc::PLL => {
|
|
||||||
RCC.cr().modify(|w| w.set_pllon(true));
|
|
||||||
while !RCC.cr().read().pllrdy() {}
|
|
||||||
(pll_clocks.main_freq, Sw::PLL)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL
|
|
||||||
// max output to be 120 MHz, so there's no way to get higher frequencies
|
|
||||||
assert!(sys_clk <= Hertz(120_000_000));
|
|
||||||
|
|
||||||
let ahb_freq = sys_clk / config.ahb_pre;
|
|
||||||
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
|
|
||||||
assert!(ahb_freq <= Hertz(120_000_000));
|
|
||||||
|
|
||||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, Hertz(freq.0 * 2))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
|
|
||||||
assert!(apb1_freq <= Hertz(30_000_000));
|
|
||||||
|
|
||||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
|
||||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
|
||||||
pre => {
|
|
||||||
let freq = ahb_freq / pre;
|
|
||||||
(freq, Hertz(freq.0 * 2))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
|
|
||||||
assert!(apb2_freq <= Hertz(60_000_000));
|
|
||||||
|
|
||||||
let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq));
|
|
||||||
FLASH.acr().modify(|w| w.set_latency(flash_ws));
|
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
|
||||||
w.set_sw(sw.into());
|
|
||||||
w.set_hpre(config.ahb_pre);
|
|
||||||
w.set_ppre1(config.apb1_pre);
|
|
||||||
w.set_ppre2(config.apb2_pre);
|
|
||||||
});
|
|
||||||
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
|
|
||||||
|
|
||||||
// Turn off HSI to save power if we don't need it
|
|
||||||
if !config.hsi {
|
|
||||||
RCC.cr().modify(|w| w.set_hsion(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
let rtc = config.ls.init();
|
|
||||||
|
|
||||||
set_freqs(Clocks {
|
|
||||||
sys: sys_clk,
|
|
||||||
ahb1: ahb_freq,
|
|
||||||
ahb2: ahb_freq,
|
|
||||||
ahb3: ahb_freq,
|
|
||||||
apb1: apb1_freq,
|
|
||||||
apb1_tim: apb1_tim_freq,
|
|
||||||
apb2: apb2_freq,
|
|
||||||
apb2_tim: apb2_tim_freq,
|
|
||||||
pll48: Some(pll_clocks.pll48_freq),
|
|
||||||
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;
|
|
||||||
}
|
|
@ -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,
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ pub enum ClockSrc {
|
|||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct PllConfig {
|
pub struct PllConfig {
|
||||||
/// The source from which the PLL receives a clock signal
|
/// The source from which the PLL receives a clock signal
|
||||||
pub source: PllSrc,
|
pub source: PllSource,
|
||||||
/// The initial divisor of that clock signal
|
/// The initial divisor of that clock signal
|
||||||
pub m: Pllm,
|
pub m: Pllm,
|
||||||
/// The PLL VCO multiplier, which must be in the range `8..=86`.
|
/// The PLL VCO multiplier, which must be in the range `8..=86`.
|
||||||
@ -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: PllSource::HSI,
|
||||||
m: Pllm::DIV1,
|
m: Pllm::DIV1,
|
||||||
n: Plln::MUL8,
|
n: Plln::MUL8,
|
||||||
r: Pllr::DIV2,
|
r: Pllr::DIV2,
|
||||||
@ -59,8 +59,8 @@ impl Default for PllConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum PllSrc {
|
pub enum PllSource {
|
||||||
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,8 +89,8 @@ 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),
|
PllSource::HSI => (vals::Pllsrc::HSI, HSI_FREQ),
|
||||||
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq),
|
PllSource::HSE(freq) => (vals::Pllsrc::HSE, freq),
|
||||||
};
|
};
|
||||||
|
|
||||||
let m_freq = input_freq / self.m;
|
let m_freq = input_freq / self.m;
|
||||||
@ -121,11 +121,11 @@ 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 => {
|
PllSource::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() {}
|
||||||
}
|
}
|
||||||
PllSrc::HSE(_) => {
|
PllSource::HSE(_) => {
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
RCC.cr().write(|w| w.set_hseon(true));
|
||||||
while !RCC.cr().read().hserdy() {}
|
while !RCC.cr().read().hserdy() {}
|
||||||
}
|
}
|
||||||
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ pub use crate::pac::rcc::vals::{
|
|||||||
Pllr as PllR, Ppre as APBPrescaler,
|
Pllr as PllR, Ppre as APBPrescaler,
|
||||||
};
|
};
|
||||||
use crate::pac::{PWR, RCC};
|
use crate::pac::{PWR, RCC};
|
||||||
use crate::rcc::sealed::RccPeripheral;
|
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
@ -18,22 +17,22 @@ 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 PllSource {
|
||||||
HSI16,
|
HSI,
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Pllsrc> for PllSrc {
|
impl Into<Pllsrc> for PllSource {
|
||||||
fn into(self) -> Pllsrc {
|
fn into(self) -> Pllsrc {
|
||||||
match self {
|
match self {
|
||||||
PllSrc::HSE(..) => Pllsrc::HSE,
|
PllSource::HSE(..) => Pllsrc::HSE,
|
||||||
PllSrc::HSI16 => Pllsrc::HSI16,
|
PllSource::HSI => Pllsrc::HSI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +44,7 @@ impl Into<Pllsrc> for PllSrc {
|
|||||||
/// frequency ranges for each of these settings.
|
/// frequency ranges for each of these settings.
|
||||||
pub struct Pll {
|
pub struct Pll {
|
||||||
/// PLL Source clock selection.
|
/// PLL Source clock selection.
|
||||||
pub source: PllSrc,
|
pub source: PllSource,
|
||||||
|
|
||||||
/// PLL pre-divider
|
/// PLL pre-divider
|
||||||
pub prediv_m: PllM,
|
pub prediv_m: PllM,
|
||||||
@ -67,23 +66,13 @@ pub struct Pll {
|
|||||||
pub enum Clock48MhzSrc {
|
pub enum Clock48MhzSrc {
|
||||||
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
|
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
|
||||||
/// oscillator to comply with the USB specification for oscillator tolerance.
|
/// oscillator to comply with the USB specification for oscillator tolerance.
|
||||||
Hsi48(Option<CrsConfig>),
|
Hsi48(super::Hsi48Config),
|
||||||
/// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
|
/// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
|
||||||
/// PLL needs to be using the HSE source to comply with the USB specification for oscillator
|
/// PLL needs to be using the HSE source to comply with the USB specification for oscillator
|
||||||
/// tolerance.
|
/// tolerance.
|
||||||
PllQ,
|
PllQ,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the sync source for the Clock Recovery System (CRS).
|
|
||||||
pub enum CrsSyncSource {
|
|
||||||
/// Use an external GPIO to sync the CRS.
|
|
||||||
Gpio,
|
|
||||||
/// Use the Low Speed External oscillator to sync the CRS.
|
|
||||||
Lse,
|
|
||||||
/// Use the USB SOF to sync the CRS.
|
|
||||||
Usb,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
/// Clocks configutation
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub mux: ClockSrc,
|
pub mux: ClockSrc,
|
||||||
@ -102,25 +91,19 @@ pub struct Config {
|
|||||||
pub ls: super::LsConfig,
|
pub ls: super::LsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
|
|
||||||
pub struct CrsConfig {
|
|
||||||
/// Sync source for the CRS.
|
|
||||||
pub sync_src: CrsSyncSource,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
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(Default::default())),
|
||||||
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,13 +118,13 @@ 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 => {
|
PllSource::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
|
HSI_FREQ
|
||||||
}
|
}
|
||||||
PllSrc::HSE(freq) => {
|
PllSource::HSE(freq) => {
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
RCC.cr().write(|w| w.set_hseon(true));
|
||||||
while !RCC.cr().read().hserdy() {}
|
while !RCC.cr().read().hserdy() {}
|
||||||
freq
|
freq
|
||||||
@ -196,12 +179,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 +232,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(Hertz(freq), Sw::PLLRCLK)
|
(Hertz(freq), Sw::PLL1_R)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -286,35 +269,10 @@ 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(config) => {
|
||||||
// Enable HSI48
|
super::init_hsi48(config);
|
||||||
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
|
||||||
// Wait for HSI48 to turn on
|
|
||||||
while RCC.crrcr().read().hsi48rdy() == false {}
|
|
||||||
|
|
||||||
// Enable and setup CRS if needed
|
|
||||||
if let Some(crs_config) = crs_config {
|
|
||||||
crate::peripherals::CRS::enable_and_reset();
|
|
||||||
|
|
||||||
let sync_src = match crs_config.sync_src {
|
|
||||||
CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO,
|
|
||||||
CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE,
|
|
||||||
CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB,
|
|
||||||
};
|
|
||||||
|
|
||||||
crate::pac::CRS.cfgr().modify(|w| {
|
|
||||||
w.set_syncsrc(sync_src);
|
|
||||||
});
|
|
||||||
|
|
||||||
// These are the correct settings for standard USB operation. If other settings
|
|
||||||
// are needed there will need to be additional config options for the CRS.
|
|
||||||
crate::pac::CRS.cr().modify(|w| {
|
|
||||||
w.set_autotrimen(true);
|
|
||||||
w.set_cen(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
crate::pac::rcc::vals::Clk48sel::HSI48
|
crate::pac::rcc::vals::Clk48sel::HSI48
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -326,16 +284,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 +306,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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user