Compare commits
279 Commits
embassy-sy
...
rcc-mux
Author | SHA1 | Date | |
---|---|---|---|
c8aa9c3b98 | |||
7810ef1c5e | |||
c1dc1741f6 | |||
6deec1b853 | |||
ba5b5e98fe | |||
158aeac438 | |||
f27c929cb8 | |||
0d0fbe957e | |||
e1a1c51d00 | |||
ac84631a2a | |||
70a91945fc | |||
32b89eeba1 | |||
c283e2d1b9 | |||
4a43cd3982 | |||
0dda888c95 | |||
b91d1eaca0 | |||
5a19d18b9c | |||
ed7b6056a6 | |||
322f9cb153 | |||
3ec4e6320a | |||
233aa1b53a | |||
21915a9a3f | |||
d0d0ceec6a | |||
9be61a2967 | |||
0cfa8d1bb5 | |||
eff73d6dfa | |||
3616d68aaa | |||
904f1cfc7a | |||
e25c7c23d0 | |||
4ee39f6dce | |||
e62bf6a9fd | |||
7526b8edba | |||
c3ef98a73d | |||
6186fe0807 | |||
c4cff0b79b | |||
2e5ab7981e | |||
a4b6bc31f8 | |||
2ae9d28d47 | |||
1457b53e48 | |||
9d311121f2 | |||
25c3754cd2 | |||
85c6f23dcb | |||
9c6a2d9cbd | |||
b67b179933 | |||
d4ed8e5779 | |||
3e054a6f0d | |||
c6d53e7bce | |||
3bf8e4de5f | |||
9090a78df4 | |||
022d870d61 | |||
534eb960e9 | |||
7a2b8481f7 | |||
710874021a | |||
3a8e0d4a27 | |||
b217d147de | |||
68c4820dde | |||
f30fc949ff | |||
75c1a9a3aa | |||
4075ac84e1 | |||
f01609036f | |||
930b8f3495 | |||
38e7709a24 | |||
65ed19aae2 | |||
be9c05fbc9 | |||
42176b1a3a | |||
e1a0635ca3 | |||
b887b7516e | |||
c6513f93fe | |||
59f706ee2f | |||
ba7d74ac16 | |||
0324cee0ca | |||
8ac5c1a963 | |||
e7ab044935 | |||
0c4cddfafb | |||
e561e673c2 | |||
09dfcd6b66 | |||
bb8a1b7f1f | |||
b012a67df7 | |||
40f30fa4cd | |||
f197aa9419 | |||
80a740bc72 | |||
ad524373ed | |||
9badf740d8 | |||
6e901846df | |||
c0a6c78a14 | |||
58280048e3 | |||
00824af82b | |||
adf9ffb109 | |||
bc203ebe4b | |||
e042b3056d | |||
9dc927250c | |||
b856d760f4 | |||
a742a80171 | |||
90ade1b311 | |||
a931dfd9ad | |||
eff61648fe | |||
9228a6334b | |||
3c9dfbbc9d | |||
ce0add61f8 | |||
bf6e06060b | |||
eb12114345 | |||
4b252586e5 | |||
5f6a915a32 | |||
923f1851ee | |||
bd267a6479 | |||
f3aa0cfe5a | |||
6ea5aa347d | |||
8c13126cff | |||
81da9ca621 | |||
729338875c | |||
f98c8886b2 | |||
a1036e111e | |||
2af97e7595 | |||
2008030a8b | |||
480328d07e | |||
05218a52e6 | |||
d2a2734752 | |||
5ad34404af | |||
a7b1e51650 | |||
70005c3956 | |||
93adbb9922 | |||
e80df91e02 | |||
5cb58754d4 | |||
5c83179071 | |||
49608714aa | |||
f116ca83e0 | |||
6e5f3f9515 | |||
5924cc8b49 | |||
0d49aaf8ca | |||
5dd9e9b3b7 | |||
d42cfda2db | |||
d1f4511cd1 | |||
fa8d5da4a5 | |||
7bc57ca3f7 | |||
7ad102695d | |||
4999de6f82 | |||
9b8bdad403 | |||
3c3fb0a529 | |||
dc4c27aead | |||
74eb519810 | |||
61dc36b1f2 | |||
55a0a15be2 | |||
fc8f96fea5 | |||
08175f191a | |||
8da987cfbb | |||
1be5f193c3 | |||
4a632e08b7 | |||
6cdee93934 | |||
5c8c1b2146 | |||
2addfc4b86 | |||
92df87781d | |||
a35d149cb1 | |||
c52320f467 | |||
2f9b59c5cf | |||
ce91fb2bfc | |||
95b3d9eb3b | |||
f1e7205055 | |||
6627824c02 | |||
d62533879c | |||
e1a8caffa4 | |||
dffdb9268b | |||
22e820b743 | |||
aa14f18d3f | |||
39f1b26a39 | |||
e70143ef8f | |||
d6654bf8b5 | |||
322a4a8401 | |||
ee5591017b | |||
b1b32f079b | |||
e8a462768e | |||
ce0d787781 | |||
fa2e63f74b | |||
23f3889167 | |||
a72a91ab06 | |||
b9ef831ff7 | |||
e538aeacc8 | |||
6b3745fc47 | |||
6200d22438 | |||
901d31e8f8 | |||
79146c4bd5 | |||
d9eae79f3e | |||
3d1b4116ec | |||
901f0257bd | |||
fec4194ae6 | |||
f866735802 | |||
20ea76c19c | |||
e1951f3ddf | |||
8b5fb254c0 | |||
624f786203 | |||
a57e48459e | |||
c604d8a8f1 | |||
be0c52bf5e | |||
44bb405683 | |||
e8587e2fba | |||
5d8817d109 | |||
c79a84a98a | |||
96edbd84fb | |||
dc400a0539 | |||
04b09a2acb | |||
481d2998ef | |||
4b695120fc | |||
bd9021ca1d | |||
6e63c8d4bf | |||
70acc093dd | |||
f7c2db245f | |||
2543bcafaf | |||
9f2fc04caa | |||
f32bfafa61 | |||
e03239e88d | |||
7d5e62d4a7 | |||
65d36f9f99 | |||
347ff18644 | |||
1e80fd81f7 | |||
52e269edbb | |||
c849620cd6 | |||
6f83acc010 | |||
2b7b7a917d | |||
f1488864eb | |||
7cf327130e | |||
02b0523199 | |||
47175198c0 | |||
4b20e6c258 | |||
83b4c01273 | |||
e313ca4ae8 | |||
ad64d7b20b | |||
5626a28af2 | |||
766ae724cb | |||
00b9f9acef | |||
d46920dce6 | |||
e1ed492577 | |||
2405aff11f | |||
a402aed3d1 | |||
c573959a95 | |||
561696dfad | |||
b1abaa380f | |||
e640933e2f | |||
183824fbdd | |||
5d0c40f61d | |||
fdfe12fe1c | |||
4bfbcd6c72 | |||
0da793e5de | |||
0dcb34fc7d | |||
feaeee1e83 | |||
a6ef314be1 | |||
daeb497045 | |||
88eb5cca71 | |||
cd128c20fa | |||
087ef918bf | |||
75cae09e79 | |||
bbe1d96045 | |||
3ddc9cd110 | |||
de2773afdd | |||
2930be5e17 | |||
09f087e53e | |||
044b837caa | |||
ad0a306ea5 | |||
b5fffbf66f | |||
537e2f3b44 | |||
80d7193b5b | |||
8315cf064e | |||
a2a26f489b | |||
f27620cc0b | |||
2911774849 | |||
6da75ea285 | |||
aa2fa29b89 | |||
c28a6bdd0b | |||
5a158b94bd | |||
c091ed2f63 | |||
45e9e51bdc | |||
7c77d2bd94 | |||
f76d94098d | |||
07233ca5b4 | |||
9fb14379c3 | |||
309c3d6b47 | |||
b9d4b18f14 | |||
3a1ed823f8 | |||
f033089625 | |||
6d8a5c6c20 | |||
3aedbce5c2 |
4
.github/ci/build-stable.sh
vendored
4
.github/ci/build-stable.sh
vendored
@ -8,6 +8,10 @@ export RUSTUP_HOME=/ci/cache/rustup
|
||||
export CARGO_HOME=/ci/cache/cargo
|
||||
export CARGO_TARGET_DIR=/ci/cache/target
|
||||
|
||||
# needed for "dumb HTTP" transport support
|
||||
# used when pointing stm32-metapac to a CI-built one.
|
||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||
|
||||
hashtime restore /ci/cache/filetime.json || true
|
||||
hashtime save /ci/cache/filetime.json
|
||||
|
||||
|
4
.github/ci/build.sh
vendored
4
.github/ci/build.sh
vendored
@ -14,6 +14,10 @@ if [ -f /ci/secrets/teleprobe-token.txt ]; then
|
||||
export TELEPROBE_CACHE=/ci/cache/teleprobe_cache.json
|
||||
fi
|
||||
|
||||
# needed for "dumb HTTP" transport support
|
||||
# used when pointing stm32-metapac to a CI-built one.
|
||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||
|
||||
hashtime restore /ci/cache/filetime.json || true
|
||||
hashtime save /ci/cache/filetime.json
|
||||
|
||||
|
9
.github/ci/doc.sh
vendored
9
.github/ci/doc.sh
vendored
@ -49,8 +49,7 @@ kubectl cp webroot/static $POD:/data
|
||||
# build and upload stm32 last
|
||||
# so that it doesn't prevent other crates from getting docs updates when it breaks.
|
||||
|
||||
# temporarily disabled because it's bringing CI down.
|
||||
#rm -rf webroot
|
||||
#docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
|
||||
#POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
||||
#kubectl cp webroot/crates $POD:/data
|
||||
rm -rf webroot
|
||||
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
|
||||
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
||||
kubectl cp webroot/crates $POD:/data
|
||||
|
4
.github/ci/test.sh
vendored
4
.github/ci/test.sh
vendored
@ -8,6 +8,10 @@ export RUSTUP_HOME=/ci/cache/rustup
|
||||
export CARGO_HOME=/ci/cache/cargo
|
||||
export CARGO_TARGET_DIR=/ci/cache/target
|
||||
|
||||
# needed for "dumb HTTP" transport support
|
||||
# used when pointing stm32-metapac to a CI-built one.
|
||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||
|
||||
hashtime restore /ci/cache/filetime.json || true
|
||||
hashtime save /ci/cache/filetime.json
|
||||
|
||||
|
39
ci.sh
39
ci.sh
@ -1,9 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
set -eo pipefail
|
||||
|
||||
export RUSTFLAGS=-Dwarnings
|
||||
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
||||
if [[ -z "${CARGO_TARGET_DIR}" ]]; then
|
||||
export CARGO_TARGET_DIR=target_ci
|
||||
fi
|
||||
|
||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||
|
||||
@ -36,7 +39,7 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly,dhcpv4-hostname \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
|
||||
@ -80,11 +83,26 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f401ve,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f405zg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f407zg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f412zg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f415zg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f417zg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f423zh,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f427zi,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f437zi,log,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f439zi,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f446ze,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f469zi,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f479zi,defmt,exti,time-driver-any,unstable-traits,embedded-sdmmc \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h753zi,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h735zg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
|
||||
@ -92,6 +110,7 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l073cz,defmt,exti,time-driver-any,unstable-traits,low-power \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits \
|
||||
@ -144,6 +163,7 @@ cargo batch \
|
||||
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
|
||||
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
|
||||
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
|
||||
--- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \
|
||||
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
|
||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \
|
||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf9160 \
|
||||
@ -169,17 +189,28 @@ cargo batch \
|
||||
--- 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 stm32u585ai --out-dir out/tests/stm32u585ai \
|
||||
--- 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 thumbv7em-none-eabi --features stm32l4a6zg --out-dir out/tests/stm32l4a6zg \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r5zi --out-dir out/tests/stm32l4r5zi \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze --out-dir out/tests/stm32l552ze \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f767zi --out-dir out/tests/stm32f767zi \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f207zg --out-dir out/tests/stm32f207zg \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \
|
||||
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
|
||||
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
||||
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
|
||||
$BUILD_EXTRA
|
||||
|
||||
|
||||
rm out/tests/stm32wb55rg/wpan_mac
|
||||
rm out/tests/stm32wb55rg/wpan_ble
|
||||
rm out/tests/stm32f207zg/eth
|
||||
|
||||
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
|
||||
echo No teleprobe token found, skipping running HIL tests
|
||||
exit
|
||||
fi
|
||||
|
||||
rm out/tests/stm32wb55rg/wpan_ble
|
||||
|
||||
teleprobe client run -r out/tests
|
||||
|
@ -15,7 +15,6 @@ embassy-time = { version = "0.1.3", path = "../embassy-time"}
|
||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync"}
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
||||
atomic-polyfill = "0.1.5"
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.17", optional = true }
|
||||
|
@ -1,6 +1,6 @@
|
||||
* xref:getting_started.adoc[Getting started]
|
||||
** xref:basic_application.adoc[Basic application]
|
||||
** xref:layer_by_layer.adoc[Layer by Layer]
|
||||
* xref:layer_by_layer.adoc[Bare metal to async]
|
||||
* xref:runtime.adoc[Executor]
|
||||
* xref:hal.adoc[HAL]
|
||||
** xref:nrf.adoc[nRF]
|
||||
@ -8,3 +8,5 @@
|
||||
* xref:bootloader.adoc[Bootloader]
|
||||
|
||||
* xref:examples.adoc[Examples]
|
||||
* xref:developer.adoc[Developer]
|
||||
** xref:developer_stm32.adoc[Developer: STM32]
|
||||
|
1
docs/modules/ROOT/pages/developer.adoc
Normal file
1
docs/modules/ROOT/pages/developer.adoc
Normal file
@ -0,0 +1 @@
|
||||
= Developer Documentation
|
79
docs/modules/ROOT/pages/developer_stm32.adoc
Normal file
79
docs/modules/ROOT/pages/developer_stm32.adoc
Normal file
@ -0,0 +1,79 @@
|
||||
= Developer Documentation: STM32
|
||||
|
||||
== Understanding metapac
|
||||
|
||||
When a project that imports `embassy-stm32` is compiled, that project selects the feature corresponding to the chip that project is using. Based on that feature, `embassy-stm32` selects supported link:https://anysilicon.com/ip-intellectual-property-core-semiconductors/[IP] for the chip, and enables the corresponding HAL implementations. But how does `embassy-stm32` know what IP the chip contains, out of the hundreds of chips that we support? It's a long story that starts with `stm32-data-sources`.
|
||||
|
||||
== `stm32-data-sources`
|
||||
|
||||
link:https://github.com/embassy-rs/stm32-data-sources[`stm32-data-sources`] is as mostly barren repository. It has no README, no documentation, and few watchers. But it's the core of what makes `embassy-stm32` possible. The data for every chip that we support is taken in part from a corresponding XML file like link:https://github.com/embassy-rs/stm32-data-sources/blob/b8b85202e22a954d6c59d4a43d9795d34cff05cf/cubedb/mcu/STM32F051K4Ux.xml[`STM32F051K4Ux.xml`]. In that file, you'll see lines like the following:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<IP InstanceName="I2C1" Name="I2C" Version="i2c2_v1_1_Cube"/>
|
||||
<!-- snip -->
|
||||
<IP ConfigFile="TIM-STM32F0xx" InstanceName="TIM1" Name="TIM1_8F0" Version="gptimer2_v2_x_Cube"/>
|
||||
----
|
||||
|
||||
These lines indicate that this chip has an i2c, and that it's version is "v1_1". It also indicates that it has a general purpose timer that with a version of "v2_x". From this data, it's possible to determine which implementations should be included in `embassy-stm32`. But actually doing that is another matter.
|
||||
|
||||
|
||||
== `stm32-data`
|
||||
|
||||
While all users of this project are familiar with `embassy-stm32`, fewer are familiar with the project that powers it: `stm32-data`. This project doesn't just aim to generate data for `embassy-stm32`, but for machine consumption in general. To acheive this, information from multiple files from the `stm32-data-sources` project are combined and parsed to assign register block implementations for each supported IP. The core of this matching resides in `chips.rs`:
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
(".*:I2C:i2c2_v1_1", ("i2c", "v2", "I2C")),
|
||||
// snip
|
||||
(r".*TIM\d.*:gptimer.*", ("timer", "v1", "TIM_GP16")),
|
||||
----
|
||||
|
||||
In this case, the i2c version corresponds to our "v2" and the general purpose timer version corresponds to our "v1". Therefore, the `i2c_v2.yaml` and `timer_v1.yaml` register block implementations are assigned to those IP, respectively. The result is that these lines arr generated in `STM32F051K4.json`:
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"name": "I2C1",
|
||||
"address": 1073763328,
|
||||
"registers": {
|
||||
"kind": "i2c",
|
||||
"version": "v2",
|
||||
"block": "I2C"
|
||||
},
|
||||
// snip
|
||||
}
|
||||
// snip
|
||||
{
|
||||
"name": "TIM1",
|
||||
"address": 1073818624,
|
||||
"registers": {
|
||||
"kind": "timer",
|
||||
"version": "v1",
|
||||
"block": "TIM_ADV"
|
||||
},
|
||||
// snip
|
||||
}
|
||||
----
|
||||
|
||||
In addition to register blocks, data for pin and RCC mapping is also generated and consumed by `embassy-stm32`. `stm32-metapac-gen` is used to package and publish the data as a crate.
|
||||
|
||||
|
||||
== `embassy-stm32`
|
||||
|
||||
In the `lib.rs` file located in the root of `embassy-stm32`, you'll see this line:
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
#[cfg(i2c)]
|
||||
pub mod i2c;
|
||||
----
|
||||
|
||||
And in the `mod.rs` of the i2c mod, you'll see this:
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
||||
----
|
||||
|
||||
Because i2c is supported for STM32F051K4 and its version corresponds to our "v2", the `i2c` and `i2c_v2`, configuration directives will be present, and `embassy-stm32` will include these files, respectively. This and other configuration directives and tables are generated from the data for chip, allowing `embassy-stm32` to expressively and clearly adapt logic and implementations to what is required for each chip. Compared to other projects across the embedded ecosystem, `embassy-stm32` is the only project that can re-use code across the entire stm32 lineup and remove difficult-to-implement unsafe logic to the HAL.
|
@ -3,7 +3,7 @@
|
||||
So you want to try Embassy, great! To get started, there are a few tools you need to install:
|
||||
|
||||
* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.
|
||||
* link:https://crates.io/crates/probe-run[probe-run] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
|
||||
* link:https://crates.io/crates/probe-rs[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
|
||||
|
||||
If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.
|
||||
|
||||
@ -30,6 +30,10 @@ Embassy supports many microcontroller families, but the easiest ways to get star
|
||||
|
||||
* link:https://www.raspberrypi.com/products/raspberry-pi-pico/[Raspberry Pi Pico]
|
||||
|
||||
=== ESP32
|
||||
|
||||
* link:https://github.com/esp-rs/esp-rust-board[ESP32C3]
|
||||
|
||||
== Running an example
|
||||
|
||||
First you need to clone the [github repository];
|
||||
@ -38,7 +42,6 @@ First you need to clone the [github repository];
|
||||
----
|
||||
git clone https://github.com/embassy-rs/embassy.git
|
||||
cd embassy
|
||||
git submodule update --init
|
||||
----
|
||||
|
||||
You can run an example by opening a terminal and entering the following commands:
|
||||
|
@ -7,4 +7,6 @@ Embassy provides HALs for several microcontroller families:
|
||||
* `embassy-rp` for the Raspberry Pi RP2040 microcontrollers
|
||||
|
||||
These HALs implement async/await functionality for most peripherals while also implementing the
|
||||
async traits in `embedded-hal-async`. You can also use these HALs with another executor.
|
||||
async traits in `embedded-hal` and `embedded-hal-async`. You can also use these HALs with another executor.
|
||||
|
||||
For the ESP32 series, there is an link:https://github.com/esp-rs/esp-hal[esp-hal] which you can use.
|
||||
|
@ -4,14 +4,9 @@ Embassy is a project to make async/await a first-class option for embedded devel
|
||||
|
||||
== What is async?
|
||||
|
||||
Software written without async may block on I/O operations. In an std environment, such as a PC, software can handle this either by using threads or non-blocking operations.
|
||||
When handling I/O, software must call functions that block program execution until the I/O operation completes. When running inside of an OS such as Linux, such functions generally transfer control to the kernel so that another task, known as a thread, can be executed if available, or the CPU can be put to sleep until another such task is ready to perform more work. Because an OS cannot presume that threads will behave cooperatively, threads are relatively resource-intensive, and may be forcibly interrupted they do not transfer control back to the kernel within an allotted time. But if tasks could be presumed to behave cooperatively, or at least not maliciously, it would be possible to create tasks that appear to be almost free when compared to a traditional OS thread. In Rust, these lightweight tasks, known as 'coroutines' or 'goroutines' in other languages, are implemented with async.
|
||||
|
||||
With threads, one thread blocks on an I/O operation, another is able to take its place. However, even on a PC, threads are relatively heavy, and therefore some programming languages, such as Go, have implemented a concept called coroutines or 'goroutines' that are much lighter and less-intensive than threads.
|
||||
|
||||
The other way to handle blocking I/O operations is to support polling the state of the underlying peripherals to check whether it is available to perform the requested operation. In programming languages without builtin async support,
|
||||
this requires building a complex loop checking for events.
|
||||
|
||||
In Rust, non-blocking operations can be implemented using async-await. Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute. Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
|
||||
Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute. Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
|
||||
|
||||
== What is Embassy?
|
||||
|
||||
@ -22,13 +17,26 @@ The Embassy project consists of several crates that you can use together or inde
|
||||
* **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
||||
** link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
|
||||
** link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
|
||||
** link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 microcontroller.
|
||||
** link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips.
|
||||
+
|
||||
NOTE: A common question is if one can use the Embassy HALs standalone. Yes, it is possible! There are no dependency on the executor within the HALs. You can even use them without async,
|
||||
as they implement both the link:https://github.com/rust-embedded/embedded-hal[Embedded HAL] blocking and async traits.
|
||||
|
||||
* **Networking** - The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently.
|
||||
* **Networking** - The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found.
|
||||
|
||||
* **Bluetooth** - The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||
|
||||
* **LoRa** - link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers.
|
||||
* **LoRa** - link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation.
|
||||
|
||||
* **USB** - link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||
|
||||
* **Bootloader and DFU** - link:https://github.com/embassy-rs/embassy/tree/master/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
||||
|
||||
== Resources
|
||||
|
||||
For more reading material on async Rust and Embassy:
|
||||
|
||||
* link:https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown[Comparsion of FreeRTOS and Embassy]
|
||||
* link:https://dev.to/apollolabsbin/series/20707[Tutorials]
|
||||
* link:https://blog.drogue.io/firmware-updates-part-1/[Firmware Updates with Embassy]
|
||||
|
@ -1,4 +1,4 @@
|
||||
= Embassy layer by layer
|
||||
= From bare metal to async Rust
|
||||
|
||||
If you're new to Embassy, it can be overwhelming to grasp all the terminology and concepts. This guide aims to clarify the different layers in Embassy, which problem each layer solves for the application writer.
|
||||
|
||||
@ -8,8 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh
|
||||
|
||||
== PAC version
|
||||
|
||||
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types
|
||||
to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
|
||||
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
|
||||
|
||||
Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
|
||||
|
||||
|
@ -8,7 +8,7 @@ The nRF timer driver operates at 32768 Hz by default.
|
||||
|
||||
== Peripherals
|
||||
|
||||
The following peripherals have a HAL implementation at present:
|
||||
The following peripherals have a HAL implementation at present
|
||||
|
||||
* PWM
|
||||
* SPIM
|
||||
@ -23,3 +23,7 @@ The following peripherals have a HAL implementation at present:
|
||||
* UARTE
|
||||
* TWIM
|
||||
* SAADC
|
||||
|
||||
== Bluetooth
|
||||
|
||||
For bluetooth, you can use the link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate.
|
||||
|
@ -10,7 +10,7 @@ The Embassy executor is an async/await executor designed for embedded usage alon
|
||||
* 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.
|
||||
* 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.
|
||||
* Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
||||
* Creating multiple executor instances is supported, to run tasks at different priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
||||
|
||||
== Executor
|
||||
|
||||
|
@ -11,7 +11,7 @@ pub use asynch::Partition;
|
||||
pub use blocking::BlockingPartition;
|
||||
|
||||
/// Partition error
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error<T> {
|
||||
/// The requested flash area is outside the partition
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, try_blocks))]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Utilities to use `embedded-hal` traits with Embassy.
|
||||
@ -26,6 +26,18 @@ pub trait SetConfig {
|
||||
/// The configuration type used by this driver.
|
||||
type Config;
|
||||
|
||||
/// The error type that can occur if `set_config` fails.
|
||||
type ConfigError;
|
||||
|
||||
/// Set the configuration of the driver.
|
||||
fn set_config(&mut self, config: &Self::Config);
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError>;
|
||||
}
|
||||
|
||||
/// Get the configuration of a peripheral driver.
|
||||
pub trait GetConfig {
|
||||
/// The configuration type used by this driver.
|
||||
type Config;
|
||||
|
||||
/// Get the configuration of the driver.
|
||||
fn get_config(&self) -> Self::Config;
|
||||
}
|
||||
|
@ -125,14 +125,14 @@ where
|
||||
{
|
||||
async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||
bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||
bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -144,7 +144,7 @@ where
|
||||
rd_buffer: &mut [u8],
|
||||
) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||
bus.write_read(address, wr_buffer, rd_buffer)
|
||||
.await
|
||||
.map_err(I2cDeviceError::I2c)?;
|
||||
@ -153,7 +153,7 @@ where
|
||||
|
||||
async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||
bus.transaction(address, operations)
|
||||
.await
|
||||
.map_err(I2cDeviceError::I2c)?;
|
||||
|
@ -130,7 +130,7 @@ where
|
||||
{
|
||||
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res: Result<(), BUS::Error> = try {
|
||||
|
@ -148,7 +148,7 @@ where
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||
bus.read(address, buffer).map_err(I2cDeviceError::I2c)
|
||||
})
|
||||
}
|
||||
@ -156,7 +156,7 @@ where
|
||||
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||
bus.write(address, bytes).map_err(I2cDeviceError::I2c)
|
||||
})
|
||||
}
|
||||
@ -164,7 +164,7 @@ where
|
||||
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||
bus.write_read(address, wr_buffer, rd_buffer)
|
||||
.map_err(I2cDeviceError::I2c)
|
||||
})
|
||||
|
@ -163,7 +163,7 @@ where
|
||||
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
bus.set_config(&self.config);
|
||||
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res = operations.iter_mut().try_for_each(|op| match op {
|
||||
|
@ -14,6 +14,8 @@ pub mod blocking;
|
||||
pub enum I2cDeviceError<BUS> {
|
||||
/// An operation on the inner I2C bus failed.
|
||||
I2c(BUS),
|
||||
/// Configuration of the inner I2C bus failed.
|
||||
Config,
|
||||
}
|
||||
|
||||
impl<BUS> i2c::Error for I2cDeviceError<BUS>
|
||||
@ -23,6 +25,7 @@ where
|
||||
fn kind(&self) -> i2c::ErrorKind {
|
||||
match self {
|
||||
Self::I2c(e) => e.kind(),
|
||||
Self::Config => i2c::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -38,6 +41,8 @@ pub enum SpiDeviceError<BUS, CS> {
|
||||
Cs(CS),
|
||||
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
|
||||
DelayUsNotSupported,
|
||||
/// The SPI bus could not be configured.
|
||||
Config,
|
||||
}
|
||||
|
||||
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
||||
@ -50,6 +55,7 @@ where
|
||||
Self::Spi(e) => e.kind(),
|
||||
Self::Cs(_) => spi::ErrorKind::Other,
|
||||
Self::DelayUsNotSupported => spi::ErrorKind::Other,
|
||||
Self::Config => spi::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,9 +73,10 @@ mod thread {
|
||||
pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
|
||||
unsafe {
|
||||
let executor = &self.inner;
|
||||
self.ctx.closure.write(Closure::new(move |_| {
|
||||
let future = Closure::new(move |_| {
|
||||
executor.poll();
|
||||
}));
|
||||
});
|
||||
self.ctx.closure.write_in_place(|| future);
|
||||
init(self.inner.spawner());
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ impl<F: Future + 'static> AvailableTask<F> {
|
||||
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
||||
unsafe {
|
||||
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
||||
self.task.future.write(future());
|
||||
self.task.future.write_in_place(future);
|
||||
|
||||
let task = TaskRef::new(self.task);
|
||||
|
||||
|
@ -17,8 +17,9 @@ impl<T> UninitCell<T> {
|
||||
&mut *self.as_mut_ptr()
|
||||
}
|
||||
|
||||
pub unsafe fn write(&self, val: T) {
|
||||
ptr::write(self.as_mut_ptr(), val)
|
||||
#[inline(never)]
|
||||
pub unsafe fn write_in_place(&self, func: impl FnOnce() -> T) {
|
||||
ptr::write(self.as_mut_ptr(), func())
|
||||
}
|
||||
|
||||
pub unsafe fn drop_in_place(&self) {
|
||||
|
@ -27,8 +27,5 @@ embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||
|
||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||
lora-phy = { version = "1" }
|
||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true }
|
||||
|
||||
[patch.crates-io]
|
||||
lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"}
|
||||
lora-phy = { version = "2" }
|
||||
lorawan-device = { version = "0.11.0", default-features = false, features = ["async"], optional = true }
|
||||
|
@ -67,24 +67,20 @@ where
|
||||
self.board_type = board_type;
|
||||
}
|
||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||
let pwr = pac::PWR;
|
||||
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
|
||||
pac::PWR.subghzspicr().modify(|w| w.set_nss(false));
|
||||
Ok(())
|
||||
}
|
||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||
let pwr = pac::PWR;
|
||||
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
|
||||
pac::PWR.subghzspicr().modify(|w| w.set_nss(true));
|
||||
Ok(())
|
||||
}
|
||||
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||
let rcc = pac::RCC;
|
||||
rcc.csr().modify(|w| w.set_rfrst(true));
|
||||
rcc.csr().modify(|w| w.set_rfrst(false));
|
||||
pac::RCC.csr().modify(|w| w.set_rfrst(true));
|
||||
pac::RCC.csr().modify(|w| w.set_rfrst(false));
|
||||
Ok(())
|
||||
}
|
||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||
let pwr = pac::PWR;
|
||||
while pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY {}
|
||||
while pac::PWR.sr2().read().rfbusys() {}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![feature(async_fn_in_trait, impl_trait_projections)]
|
||||
#![feature(async_fn_in_trait)]
|
||||
//! embassy-lora holds LoRa-specific functionality.
|
||||
|
||||
pub(crate) mod fmt;
|
||||
|
@ -194,10 +194,15 @@ where
|
||||
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
|
||||
}
|
||||
|
||||
/// Returns the device's MAC address
|
||||
pub fn address(&self) -> [u8; 6] {
|
||||
self.mac_addr
|
||||
}
|
||||
|
||||
/// Flushes the transmit buffer, ensuring all pending transmissions have completed
|
||||
/// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
|
||||
/// return `None` on subsequent invocations
|
||||
pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
||||
pub fn receive(&mut self, buf: &mut [u8]) -> Option<usize> {
|
||||
if self.pending_packets() == 0 {
|
||||
// Errata #6: we can't rely on PKTIF so we check PKTCNT
|
||||
return None;
|
||||
@ -241,7 +246,7 @@ where
|
||||
|
||||
self.next_packet = next_packet;
|
||||
|
||||
Some(&mut buf[..len as usize])
|
||||
Some(len as usize)
|
||||
}
|
||||
|
||||
fn wait_tx_ready(&mut self) {
|
||||
@ -642,9 +647,8 @@ where
|
||||
fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
let rx_buf = unsafe { &mut RX_BUF };
|
||||
let tx_buf = unsafe { &mut TX_BUF };
|
||||
if let Some(pkt) = self.receive(rx_buf) {
|
||||
let n = pkt.len();
|
||||
Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self }))
|
||||
if let Some(n) = self.receive(rx_buf) {
|
||||
Some((RxToken { buf: &mut rx_buf[..n] }, TxToken { buf: tx_buf, eth: self }))
|
||||
} else {
|
||||
cx.waker().wake_by_ref();
|
||||
None
|
||||
|
@ -15,7 +15,7 @@ log = ["dep:log", "ppproto/log"]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embedded-io-async = { version = "0.5.0" }
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
ppproto = { version = "0.1.2"}
|
||||
|
@ -11,7 +11,7 @@ use core::mem::MaybeUninit;
|
||||
use embassy_futures::select::{select, Either};
|
||||
use embassy_net_driver_channel as ch;
|
||||
use embassy_net_driver_channel::driver::LinkState;
|
||||
use embedded_io_async::{BufRead, Write, WriteAllError};
|
||||
use embedded_io_async::{BufRead, Write};
|
||||
use ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction};
|
||||
pub use ppproto::{Config, Ipv4Status};
|
||||
|
||||
@ -49,23 +49,12 @@ pub enum RunError<E> {
|
||||
Read(E),
|
||||
/// Writing to the serial port failed.
|
||||
Write(E),
|
||||
/// Writing to the serial port wrote zero bytes, indicating it can't accept more data.
|
||||
WriteZero,
|
||||
/// Writing to the serial got EOF.
|
||||
Eof,
|
||||
/// PPP protocol was terminated by the peer
|
||||
Terminated,
|
||||
}
|
||||
|
||||
impl<E> From<WriteAllError<E>> for RunError<E> {
|
||||
fn from(value: WriteAllError<E>) -> Self {
|
||||
match value {
|
||||
WriteAllError::Other(e) => Self::Write(e),
|
||||
WriteAllError::WriteZero => Self::WriteZero,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Runner<'d> {
|
||||
/// You must call this in a background task for the driver to operate.
|
||||
///
|
||||
@ -125,7 +114,7 @@ impl<'d> Runner<'d> {
|
||||
buf[..pkt.len()].copy_from_slice(pkt);
|
||||
rx_chan.rx_done(pkt.len());
|
||||
}
|
||||
PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await?,
|
||||
PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await.map_err(RunError::Write)?,
|
||||
}
|
||||
|
||||
let status = ppp.status();
|
||||
@ -148,7 +137,7 @@ impl<'d> Runner<'d> {
|
||||
}
|
||||
Either::Second(pkt) => {
|
||||
match ppp.send(pkt, &mut tx_buf) {
|
||||
Ok(n) => rw.write_all(&tx_buf[..n]).await?,
|
||||
Ok(n) => rw.write_all(&tx_buf[..n]).await.map_err(RunError::Write)?,
|
||||
Err(BufferFullError) => unreachable!(),
|
||||
}
|
||||
tx_chan.tx_done();
|
||||
|
@ -33,6 +33,7 @@ udp = ["smoltcp/socket-udp"]
|
||||
tcp = ["smoltcp/socket-tcp"]
|
||||
dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"]
|
||||
dhcpv4 = ["proto-ipv4", "medium-ethernet", "smoltcp/socket-dhcpv4"]
|
||||
dhcpv4-hostname = ["dhcpv4"]
|
||||
proto-ipv4 = ["smoltcp/proto-ipv4"]
|
||||
proto-ipv6 = ["smoltcp/proto-ipv6"]
|
||||
medium-ethernet = ["smoltcp/medium-ethernet"]
|
||||
@ -53,7 +54,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
|
||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||
embedded-io-async = { version = "0.5.0", optional = true }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
|
||||
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
||||
heapless = { version = "0.7.5", default-features = false }
|
||||
@ -62,5 +63,4 @@ generic-array = { version = "0.14.4", default-features = false }
|
||||
stable_deref_trait = { version = "1.2.0", default-features = false }
|
||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||
atomic-pool = "1.0"
|
||||
embedded-nal-async = { version = "0.5.0", optional = true }
|
||||
atomic-polyfill = { version = "1.0" }
|
||||
embedded-nal-async = { version = "0.6.0", optional = true }
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
@ -39,7 +39,7 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
||||
pub use smoltcp::wire::EthernetAddress;
|
||||
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))]
|
||||
pub use smoltcp::wire::HardwareAddress;
|
||||
#[cfg(feature = "udp")]
|
||||
#[cfg(any(feature = "udp", feature = "tcp"))]
|
||||
pub use smoltcp::wire::IpListenEndpoint;
|
||||
#[cfg(feature = "medium-ieee802154")]
|
||||
pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame};
|
||||
@ -56,12 +56,22 @@ const LOCAL_PORT_MIN: u16 = 1025;
|
||||
const LOCAL_PORT_MAX: u16 = 65535;
|
||||
#[cfg(feature = "dns")]
|
||||
const MAX_QUERIES: usize = 4;
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
const MAX_HOSTNAME_LEN: usize = 32;
|
||||
|
||||
/// Memory resources needed for a network stack.
|
||||
pub struct StackResources<const SOCK: usize> {
|
||||
sockets: [SocketStorage<'static>; SOCK],
|
||||
#[cfg(feature = "dns")]
|
||||
queries: [Option<dns::DnsQuery>; MAX_QUERIES],
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: core::cell::UnsafeCell<HostnameResources>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
struct HostnameResources {
|
||||
option: smoltcp::wire::DhcpOption<'static>,
|
||||
data: [u8; MAX_HOSTNAME_LEN],
|
||||
}
|
||||
|
||||
impl<const SOCK: usize> StackResources<SOCK> {
|
||||
@ -73,6 +83,11 @@ impl<const SOCK: usize> StackResources<SOCK> {
|
||||
sockets: [SocketStorage::EMPTY; SOCK],
|
||||
#[cfg(feature = "dns")]
|
||||
queries: [INIT; MAX_QUERIES],
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: core::cell::UnsafeCell::new(HostnameResources {
|
||||
option: smoltcp::wire::DhcpOption { kind: 0, data: &[] },
|
||||
data: [0; MAX_HOSTNAME_LEN],
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,6 +119,7 @@ pub struct StaticConfigV6 {
|
||||
/// DHCP configuration.
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct DhcpConfig {
|
||||
/// Maximum lease duration.
|
||||
///
|
||||
@ -120,6 +136,9 @@ pub struct DhcpConfig {
|
||||
pub server_port: u16,
|
||||
/// Client port. This is almost always 68. Do not change unless you know what you're doing.
|
||||
pub client_port: u16,
|
||||
/// Our hostname. This will be sent to the DHCP server as Option 12.
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
pub hostname: Option<heapless::String<MAX_HOSTNAME_LEN>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
@ -131,6 +150,8 @@ impl Default for DhcpConfig {
|
||||
ignore_naks: Default::default(),
|
||||
server_port: smoltcp::wire::DHCP_SERVER_PORT,
|
||||
client_port: smoltcp::wire::DHCP_CLIENT_PORT,
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -232,6 +253,8 @@ struct Inner<D: Driver> {
|
||||
dns_socket: SocketHandle,
|
||||
#[cfg(feature = "dns")]
|
||||
dns_waker: WakerRegistration,
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: &'static mut core::cell::UnsafeCell<HostnameResources>,
|
||||
}
|
||||
|
||||
pub(crate) struct SocketStack {
|
||||
@ -258,7 +281,7 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Driver + 'static> Stack<D> {
|
||||
impl<D: Driver> Stack<D> {
|
||||
/// Create a new network stack.
|
||||
pub fn new<const SOCK: usize>(
|
||||
mut device: D,
|
||||
@ -307,6 +330,8 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
)),
|
||||
#[cfg(feature = "dns")]
|
||||
dns_waker: WakerRegistration::new(),
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: &mut resources.hostname,
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
@ -555,7 +580,7 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "igmp")]
|
||||
impl<D: Driver + 'static> Stack<D> {
|
||||
impl<D: Driver> Stack<D> {
|
||||
/// Join a multicast group.
|
||||
pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
where
|
||||
@ -645,7 +670,7 @@ impl SocketStack {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Driver + 'static> Inner<D> {
|
||||
impl<D: Driver> Inner<D> {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) {
|
||||
// Handle static config.
|
||||
@ -673,6 +698,25 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
|
||||
socket.set_ports(c.server_port, c.client_port);
|
||||
socket.set_retry_config(c.retry_config);
|
||||
|
||||
socket.set_outgoing_options(&[]);
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
if let Some(h) = c.hostname {
|
||||
// safety: we just did set_outgoing_options([]) so we know the socket is no longer holding a reference.
|
||||
let hostname = unsafe { &mut *self.hostname.get() };
|
||||
|
||||
// create data
|
||||
// safety: we know the buffer lives forever, new borrows the StackResources for 'static.
|
||||
// also we won't modify it until next call to this function.
|
||||
hostname.data[..h.len()].copy_from_slice(h.as_bytes());
|
||||
let data: &[u8] = &hostname.data[..h.len()];
|
||||
let data: &'static [u8] = unsafe { core::mem::transmute(data) };
|
||||
|
||||
// set the option.
|
||||
hostname.option = smoltcp::wire::DhcpOption { data, kind: 12 };
|
||||
socket.set_outgoing_options(core::slice::from_ref(&hostname.option));
|
||||
}
|
||||
|
||||
socket.reset();
|
||||
}
|
||||
_ => {
|
||||
|
@ -579,11 +579,10 @@ mod embedded_io_impls {
|
||||
/// TCP client compatible with `embedded-nal-async` traits.
|
||||
#[cfg(feature = "nightly")]
|
||||
pub mod client {
|
||||
use core::cell::UnsafeCell;
|
||||
use core::cell::{Cell, UnsafeCell};
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use atomic_polyfill::{AtomicBool, Ordering};
|
||||
use embedded_nal_async::IpAddr;
|
||||
|
||||
use super::*;
|
||||
@ -702,15 +701,13 @@ pub mod client {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<const N: usize, const TX_SZ: usize, const RX_SZ: usize> Sync for TcpClientState<N, TX_SZ, RX_SZ> {}
|
||||
|
||||
struct Pool<T, const N: usize> {
|
||||
used: [AtomicBool; N],
|
||||
used: [Cell<bool>; N],
|
||||
data: [UnsafeCell<MaybeUninit<T>>; N],
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Pool<T, N> {
|
||||
const VALUE: AtomicBool = AtomicBool::new(false);
|
||||
const VALUE: Cell<bool> = Cell::new(false);
|
||||
const UNINIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());
|
||||
|
||||
const fn new() -> Self {
|
||||
@ -724,7 +721,9 @@ pub mod client {
|
||||
impl<T, const N: usize> Pool<T, N> {
|
||||
fn alloc(&self) -> Option<NonNull<T>> {
|
||||
for n in 0..N {
|
||||
if self.used[n].swap(true, Ordering::SeqCst) == false {
|
||||
// this can't race because Pool is not Sync.
|
||||
if !self.used[n].get() {
|
||||
self.used[n].set(true);
|
||||
let p = self.data[n].get() as *mut T;
|
||||
return Some(unsafe { NonNull::new_unchecked(p) });
|
||||
}
|
||||
@ -738,7 +737,7 @@ pub mod client {
|
||||
let n = p.as_ptr().offset_from(origin);
|
||||
assert!(n >= 0);
|
||||
assert!((n as usize) < N);
|
||||
self.used[n as usize].store(false, Ordering::SeqCst);
|
||||
self.used[n as usize].set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ nrf52805 = ["nrf52805-pac", "_nrf52"]
|
||||
nrf52810 = ["nrf52810-pac", "_nrf52"]
|
||||
nrf52811 = ["nrf52811-pac", "_nrf52"]
|
||||
nrf52820 = ["nrf52820-pac", "_nrf52"]
|
||||
nrf52832 = ["nrf52832-pac", "_nrf52"]
|
||||
nrf52832 = ["nrf52832-pac", "_nrf52", "_nrf52832_anomaly_109"]
|
||||
nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"]
|
||||
nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"]
|
||||
nrf5340-app-s = ["_nrf5340-app", "_s"]
|
||||
@ -90,6 +90,9 @@ _ppi = []
|
||||
_dppi = []
|
||||
_gpio-p1 = []
|
||||
|
||||
# Errata workarounds
|
||||
_nrf52832_anomaly_109 = []
|
||||
|
||||
[dependencies]
|
||||
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
|
||||
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||
@ -100,8 +103,8 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona
|
||||
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-async = { version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-io = { version = "0.5.0" }
|
||||
embedded-io-async = { version = "0.5.0", optional = true }
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
@ -102,6 +102,7 @@ mod chip;
|
||||
#[macro_export]
|
||||
macro_rules! bind_interrupts {
|
||||
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
|
||||
#[derive(Copy, Clone)]
|
||||
$vis struct $name;
|
||||
|
||||
$(
|
||||
|
@ -68,30 +68,14 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
let r = T::regs();
|
||||
let s = T::state();
|
||||
|
||||
#[cfg(feature = "nrf52832")]
|
||||
// NRF32 Anomaly 109 workaround... NRF52832
|
||||
if r.intenset.read().started().is_enabled() && r.events_started.read().bits() != 0 {
|
||||
// Handle the first "fake" transmission
|
||||
r.events_started.reset();
|
||||
r.events_end.reset();
|
||||
|
||||
// Update DMA registers with correct rx/tx buffer sizes
|
||||
r.rxd
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.maxcnt().bits(s.rx.load(Ordering::Relaxed)) });
|
||||
r.txd
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.maxcnt().bits(s.tx.load(Ordering::Relaxed)) });
|
||||
|
||||
// Disable interrupt for STARTED event...
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
if r.events_started.read().bits() != 0 {
|
||||
s.waker.wake();
|
||||
r.intenclr.write(|w| w.started().clear());
|
||||
// ... and start actual, hopefully glitch-free transmission
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
return;
|
||||
}
|
||||
|
||||
if r.events_end.read().bits() != 0 {
|
||||
s.end_waker.wake();
|
||||
s.waker.wake();
|
||||
r.intenclr.write(|w| w.end().clear());
|
||||
}
|
||||
}
|
||||
@ -192,7 +176,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
let mut spim = Self { _p: spim };
|
||||
|
||||
// Apply runtime peripheral configuration
|
||||
Self::set_config(&mut spim, &config);
|
||||
Self::set_config(&mut spim, &config).unwrap();
|
||||
|
||||
// Disable all events interrupts
|
||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
@ -222,8 +206,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
|
||||
|
||||
// ANOMALY 109 workaround
|
||||
#[cfg(feature = "nrf52832")]
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
{
|
||||
let s = T::state();
|
||||
|
||||
@ -254,6 +237,9 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
self.prepare(rx, tx)?;
|
||||
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
while let Poll::Pending = self.nrf52832_dma_workaround_status() {}
|
||||
|
||||
// Wait for 'end' event.
|
||||
while T::regs().events_end.read().bits() == 0 {}
|
||||
|
||||
@ -278,9 +264,19 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
self.prepare(rx, tx)?;
|
||||
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
poll_fn(|cx| {
|
||||
let s = T::state();
|
||||
|
||||
s.waker.register(cx.waker());
|
||||
|
||||
self.nrf52832_dma_workaround_status()
|
||||
})
|
||||
.await;
|
||||
|
||||
// Wait for 'end' event.
|
||||
poll_fn(|cx| {
|
||||
T::state().end_waker.register(cx.waker());
|
||||
T::state().waker.register(cx.waker());
|
||||
if T::regs().events_end.read().bits() != 0 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
@ -371,6 +367,32 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
self.async_inner_from_ram(&mut [], data).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
fn nrf52832_dma_workaround_status(&mut self) -> Poll<()> {
|
||||
let r = T::regs();
|
||||
if r.events_started.read().bits() != 0 {
|
||||
let s = T::state();
|
||||
|
||||
// Handle the first "fake" transmission
|
||||
r.events_started.reset();
|
||||
r.events_end.reset();
|
||||
|
||||
// Update DMA registers with correct rx/tx buffer sizes
|
||||
r.rxd
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.maxcnt().bits(s.rx.load(Ordering::Relaxed)) });
|
||||
r.txd
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.maxcnt().bits(s.tx.load(Ordering::Relaxed)) });
|
||||
|
||||
r.intenset.write(|w| w.end().set());
|
||||
// ... and start actual, hopefully glitch-free transmission
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
return Poll::Ready(());
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Spim<'d, T> {
|
||||
@ -395,7 +417,7 @@ impl<'d, T: Instance> Drop for Spim<'d, T> {
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
#[cfg(feature = "nrf52832")]
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
use core::sync::atomic::AtomicU8;
|
||||
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
@ -403,20 +425,20 @@ pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub struct State {
|
||||
pub end_waker: AtomicWaker,
|
||||
#[cfg(feature = "nrf52832")]
|
||||
pub waker: AtomicWaker,
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
pub rx: AtomicU8,
|
||||
#[cfg(feature = "nrf52832")]
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
pub tx: AtomicU8,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
end_waker: AtomicWaker::new(),
|
||||
#[cfg(feature = "nrf52832")]
|
||||
waker: AtomicWaker::new(),
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
rx: AtomicU8::new(0),
|
||||
#[cfg(feature = "nrf52832")]
|
||||
#[cfg(feature = "_nrf52832_anomaly_109")]
|
||||
tx: AtomicU8::new(0),
|
||||
}
|
||||
}
|
||||
@ -544,7 +566,8 @@ mod eha {
|
||||
|
||||
impl<'d, T: Instance> SetConfig for Spim<'d, T> {
|
||||
type Config = Config;
|
||||
fn set_config(&mut self, config: &Self::Config) {
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||
let r = T::regs();
|
||||
// Configure mode.
|
||||
let mode = config.mode;
|
||||
@ -582,5 +605,7 @@ impl<'d, T: Instance> SetConfig for Spim<'d, T> {
|
||||
// Set over-read character
|
||||
let orc = config.orc;
|
||||
r.orc.write(|w| unsafe { w.orc().bits(orc) });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
let mut spis = Self { _p: spis };
|
||||
|
||||
// Apply runtime peripheral configuration
|
||||
Self::set_config(&mut spis, &config);
|
||||
Self::set_config(&mut spis, &config).unwrap();
|
||||
|
||||
// Disable all events interrupts.
|
||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
@ -467,7 +467,8 @@ macro_rules! impl_spis {
|
||||
|
||||
impl<'d, T: Instance> SetConfig for Spis<'d, T> {
|
||||
type Config = Config;
|
||||
fn set_config(&mut self, config: &Self::Config) {
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||
let r = T::regs();
|
||||
// Configure mode.
|
||||
let mode = config.mode;
|
||||
@ -509,5 +510,7 @@ impl<'d, T: Instance> SetConfig for Spis<'d, T> {
|
||||
// Configure auto-acquire on 'transfer end' event.
|
||||
let auto_acquire = config.auto_acquire;
|
||||
r.shorts.write(|w| w.end_acquire().bit(auto_acquire));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
let mut twim = Self { _p: twim };
|
||||
|
||||
// Apply runtime peripheral configuration
|
||||
Self::set_config(&mut twim, &config);
|
||||
Self::set_config(&mut twim, &config).unwrap();
|
||||
|
||||
// Disable all events interrupts
|
||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
@ -890,9 +890,12 @@ mod eha {
|
||||
|
||||
impl<'d, T: Instance> SetConfig for Twim<'d, T> {
|
||||
type Config = Config;
|
||||
fn set_config(&mut self, config: &Self::Config) {
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||
let r = T::regs();
|
||||
r.frequency
|
||||
.write(|w| unsafe { w.frequency().bits(config.frequency as u32) });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -75,8 +75,8 @@ cortex-m = "0.7.6"
|
||||
critical-section = "1.1"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
chrono = { version = "0.4", default-features = false, optional = true }
|
||||
embedded-io = { version = "0.5.0" }
|
||||
embedded-io-async = { version = "0.5.0", optional = true }
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
embedded-storage = { version = "0.3" }
|
||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||
rand_core = "0.6.4"
|
||||
@ -89,7 +89,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional =
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true}
|
||||
|
||||
paste = "1.0"
|
||||
pio-proc = {version= "0.2" }
|
||||
pio = {version= "0.2.1" }
|
||||
rp2040-boot2 = "0.3"
|
||||
|
83
embassy-rp/src/bootsel.rs
Normal file
83
embassy-rp/src/bootsel.rs
Normal file
@ -0,0 +1,83 @@
|
||||
//! Boot Select button
|
||||
//!
|
||||
//! The RP2040 rom supports a BOOTSEL button that is used to enter the USB bootloader
|
||||
//! if held during reset. To avoid wasting GPIO pins, the button is multiplexed onto
|
||||
//! the CS pin of the QSPI flash, but that makes it somewhat expensive and complicated
|
||||
//! to utilize outside of the rom's bootloader.
|
||||
//!
|
||||
//! This module provides functionality to poll BOOTSEL from an embassy application.
|
||||
|
||||
use crate::flash::in_ram;
|
||||
|
||||
impl crate::peripherals::BOOTSEL {
|
||||
/// Polls the BOOTSEL button. Returns true if the button is pressed.
|
||||
///
|
||||
/// Polling isn't cheap, as this function waits for core 1 to finish it's current
|
||||
/// task and for any DMAs from flash to complete
|
||||
pub fn is_pressed(&mut self) -> bool {
|
||||
let mut cs_status = Default::default();
|
||||
|
||||
unsafe { in_ram(|| cs_status = ram_helpers::read_cs_status()) }.expect("Must be called from Core 0");
|
||||
|
||||
// bootsel is active low, so invert
|
||||
!cs_status.infrompad()
|
||||
}
|
||||
}
|
||||
|
||||
mod ram_helpers {
|
||||
use rp_pac::io::regs::GpioStatus;
|
||||
|
||||
/// Temporally reconfigures the CS gpio and returns the GpioStatus.
|
||||
|
||||
/// This function runs from RAM so it can disable flash XIP.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure flash is idle and will remain idle.
|
||||
/// This function must live in ram. It uses inline asm to avoid any
|
||||
/// potential calls to ABI functions that might be in flash.
|
||||
#[inline(never)]
|
||||
#[link_section = ".data.ram_func"]
|
||||
#[cfg(target_arch = "arm")]
|
||||
pub unsafe fn read_cs_status() -> GpioStatus {
|
||||
let result: u32;
|
||||
|
||||
// Magic value, used as both OEOVER::DISABLE and delay loop counter
|
||||
let magic = 0x2000;
|
||||
|
||||
core::arch::asm!(
|
||||
".equiv GPIO_STATUS, 0x0",
|
||||
".equiv GPIO_CTRL, 0x4",
|
||||
|
||||
"ldr {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]",
|
||||
|
||||
// The BOOTSEL pulls the flash's CS line low though a 1K resistor.
|
||||
// this is weak enough to avoid disrupting normal operation.
|
||||
// But, if we disable CS's output drive and allow it to float...
|
||||
"str {val}, [{cs_gpio}, $GPIO_CTRL]",
|
||||
|
||||
// ...then wait for the state to settle...
|
||||
"1:", // ~4000 cycle delay loop
|
||||
"subs {val}, #8",
|
||||
"bne 1b",
|
||||
|
||||
// ...we can read the current state of bootsel
|
||||
"ldr {val}, [{cs_gpio}, $GPIO_STATUS]",
|
||||
|
||||
// Finally, restore CS to normal operation so XIP can continue
|
||||
"str {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]",
|
||||
|
||||
cs_gpio = in(reg) rp_pac::IO_QSPI.gpio(1).as_ptr(),
|
||||
orig_ctrl = out(reg) _,
|
||||
val = inout(reg) magic => result,
|
||||
options(nostack),
|
||||
);
|
||||
|
||||
core::mem::transmute(result)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "arm"))]
|
||||
pub unsafe fn read_cs_status() -> GpioStatus {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
@ -131,7 +131,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
|
||||
let len = to - from;
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len))? };
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_erase(from, len))? };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -156,7 +156,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
|
||||
let unaligned_offset = offset as usize - start;
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
|
||||
}
|
||||
|
||||
let remaining_len = bytes.len() - start_padding;
|
||||
@ -174,12 +174,12 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
if bytes.as_ptr() as usize >= 0x2000_0000 {
|
||||
let aligned_data = &bytes[start_padding..end_padding];
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data))? }
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data))? }
|
||||
} else {
|
||||
for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {
|
||||
let mut ram_buf = [0xFF_u8; PAGE_SIZE];
|
||||
ram_buf.copy_from_slice(chunk);
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf))? }
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf))? }
|
||||
aligned_offset += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
@ -194,47 +194,15 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
|
||||
let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Make sure to uphold the contract points with rp2040-flash.
|
||||
/// - interrupts must be disabled
|
||||
/// - DMA must not access flash memory
|
||||
unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> {
|
||||
// Make sure we're running on CORE0
|
||||
let core_id: u32 = pac::SIO.cpuid().read();
|
||||
if core_id != 0 {
|
||||
return Err(Error::InvalidCore);
|
||||
}
|
||||
|
||||
// Make sure CORE1 is paused during the entire duration of the RAM function
|
||||
crate::multicore::pause_core1();
|
||||
|
||||
critical_section::with(|_| {
|
||||
// Wait for all DMA channels in flash to finish before ram operation
|
||||
const SRAM_LOWER: u32 = 0x2000_0000;
|
||||
for n in 0..crate::dma::CHANNEL_COUNT {
|
||||
let ch = crate::pac::DMA.ch(n);
|
||||
while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {}
|
||||
}
|
||||
// Wait for completion of any background reads
|
||||
while pac::XIP_CTRL.stream_ctr().read().0 > 0 {}
|
||||
|
||||
// Run our flash operation in RAM
|
||||
operation();
|
||||
});
|
||||
|
||||
// Resume CORE1 execution
|
||||
crate::multicore::resume_core1();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read SPI flash unique ID
|
||||
pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> {
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? };
|
||||
unsafe { in_ram(|| ram_helpers::flash_unique_id(uid))? };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -242,7 +210,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> {
|
||||
let mut jedec = None;
|
||||
unsafe {
|
||||
self.in_ram(|| {
|
||||
in_ram(|| {
|
||||
jedec.replace(ram_helpers::flash_jedec_id());
|
||||
})?;
|
||||
};
|
||||
@ -871,6 +839,38 @@ mod ram_helpers {
|
||||
}
|
||||
}
|
||||
|
||||
/// Make sure to uphold the contract points with rp2040-flash.
|
||||
/// - interrupts must be disabled
|
||||
/// - DMA must not access flash memory
|
||||
pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
|
||||
// Make sure we're running on CORE0
|
||||
let core_id: u32 = pac::SIO.cpuid().read();
|
||||
if core_id != 0 {
|
||||
return Err(Error::InvalidCore);
|
||||
}
|
||||
|
||||
// Make sure CORE1 is paused during the entire duration of the RAM function
|
||||
crate::multicore::pause_core1();
|
||||
|
||||
critical_section::with(|_| {
|
||||
// Wait for all DMA channels in flash to finish before ram operation
|
||||
const SRAM_LOWER: u32 = 0x2000_0000;
|
||||
for n in 0..crate::dma::CHANNEL_COUNT {
|
||||
let ch = crate::pac::DMA.ch(n);
|
||||
while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {}
|
||||
}
|
||||
// Wait for completion of any background reads
|
||||
while pac::XIP_CTRL.stream_ctr().read().0 > 0 {}
|
||||
|
||||
// Run our flash operation in RAM
|
||||
operation();
|
||||
});
|
||||
|
||||
// Resume CORE1 execution
|
||||
crate::multicore::resume_core1();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Instance {}
|
||||
pub trait Mode {}
|
||||
|
@ -97,6 +97,12 @@ impl<'d, T: Pin> Input<'d, T> {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
/// Set the pin's Schmitt trigger.
|
||||
#[inline]
|
||||
pub fn set_schmitt(&mut self, enable: bool) {
|
||||
self.pin.set_schmitt(enable)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high(&self) -> bool {
|
||||
self.pin.is_high()
|
||||
@ -216,7 +222,6 @@ fn IO_IRQ_QSPI() {
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
struct InputFuture<'a, T: Pin> {
|
||||
pin: PeripheralRef<'a, T>,
|
||||
level: InterruptTrigger,
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> InputFuture<'d, T> {
|
||||
@ -243,7 +248,6 @@ impl<'d, T: Pin> InputFuture<'d, T> {
|
||||
.inte((pin.pin() / 8) as usize)
|
||||
.write_set(|w| match level {
|
||||
InterruptTrigger::LevelHigh => {
|
||||
trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin());
|
||||
w.set_level_high(pin_group, true);
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
@ -261,7 +265,7 @@ impl<'d, T: Pin> InputFuture<'d, T> {
|
||||
}
|
||||
});
|
||||
|
||||
Self { pin, level }
|
||||
Self { pin }
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,14 +301,8 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> {
|
||||
&& !inte.level_high(pin_group)
|
||||
&& !inte.level_low(pin_group)
|
||||
{
|
||||
trace!(
|
||||
"{:?} for pin {} was cleared, return Poll::Ready",
|
||||
self.level,
|
||||
self.pin.pin()
|
||||
);
|
||||
return Poll::Ready(());
|
||||
}
|
||||
trace!("InputFuture::poll return Poll::Pending");
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
@ -326,6 +324,18 @@ impl<'d, T: Pin> Output<'d, T> {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
/// Set the pin's drive strength.
|
||||
#[inline]
|
||||
pub fn set_drive_strength(&mut self, strength: Drive) {
|
||||
self.pin.set_drive_strength(strength)
|
||||
}
|
||||
|
||||
// Set the pin's slew rate.
|
||||
#[inline]
|
||||
pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
|
||||
self.pin.set_slew_rate(slew_rate)
|
||||
}
|
||||
|
||||
/// Set the output as high.
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
@ -386,6 +396,18 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
/// Set the pin's drive strength.
|
||||
#[inline]
|
||||
pub fn set_drive_strength(&mut self, strength: Drive) {
|
||||
self.pin.set_drive_strength(strength)
|
||||
}
|
||||
|
||||
// Set the pin's slew rate.
|
||||
#[inline]
|
||||
pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
|
||||
self.pin.set_slew_rate(slew_rate)
|
||||
}
|
||||
|
||||
/// Set the output as high.
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
@ -541,6 +563,14 @@ impl<'d, T: Pin> Flex<'d, T> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the pin's Schmitt trigger.
|
||||
#[inline]
|
||||
pub fn set_schmitt(&mut self, enable: bool) {
|
||||
self.pin.pad_ctrl().modify(|w| {
|
||||
w.set_schmitt(enable);
|
||||
});
|
||||
}
|
||||
|
||||
/// Put the pin into input mode.
|
||||
///
|
||||
/// The pull setting is left unchanged.
|
||||
|
@ -6,13 +6,12 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use pac::i2c;
|
||||
|
||||
use crate::gpio::sealed::Pin;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::{interrupt, pac, peripherals, Peripheral};
|
||||
|
||||
/// I2C error abort reason
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum AbortReason {
|
||||
/// A bus operation was not acknowledged, e.g. due to the addressed device
|
||||
@ -27,7 +26,7 @@ pub enum AbortReason {
|
||||
}
|
||||
|
||||
/// I2C error
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// I2C abort with error
|
||||
@ -295,13 +294,24 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
|
||||
|
||||
pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
Self::setup(addr)?;
|
||||
self.read_async_internal(buffer, false, true).await
|
||||
self.read_async_internal(buffer, true, true).await
|
||||
}
|
||||
|
||||
pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator<Item = u8>) -> Result<(), Error> {
|
||||
Self::setup(addr)?;
|
||||
self.write_async_internal(bytes, true).await
|
||||
}
|
||||
|
||||
pub async fn write_read_async(
|
||||
&mut self,
|
||||
addr: u16,
|
||||
bytes: impl IntoIterator<Item = u8>,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
Self::setup(addr)?;
|
||||
self.write_async_internal(bytes, false).await?;
|
||||
self.read_async_internal(buffer, true, true).await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
@ -318,6 +328,22 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_up_i2c_pin<'d, P, T>(pin: &P)
|
||||
where
|
||||
P: core::ops::Deref<Target = T>,
|
||||
T: crate::gpio::Pin,
|
||||
{
|
||||
pin.gpio().ctrl().write(|w| w.set_funcsel(3));
|
||||
pin.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_slewfast(false);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
|
||||
fn new_inner(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
@ -355,23 +381,8 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
// Configure SCL & SDA pins
|
||||
scl.gpio().ctrl().write(|w| w.set_funcsel(3));
|
||||
sda.gpio().ctrl().write(|w| w.set_funcsel(3));
|
||||
|
||||
scl.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
sda.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
set_up_i2c_pin(&scl);
|
||||
set_up_i2c_pin(&sda);
|
||||
|
||||
// Configure baudrate
|
||||
|
||||
@ -713,7 +724,7 @@ mod nightly {
|
||||
|
||||
Self::setup(addr)?;
|
||||
self.write_async_internal(write.iter().cloned(), false).await?;
|
||||
self.read_async_internal(read, false, true).await
|
||||
self.read_async_internal(read, true, true).await
|
||||
}
|
||||
|
||||
async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
|
||||
|
@ -5,12 +5,14 @@ use core::task::Poll;
|
||||
use embassy_hal_internal::into_ref;
|
||||
use pac::i2c;
|
||||
|
||||
use crate::i2c::{i2c_reserved_addr, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE};
|
||||
use crate::i2c::{
|
||||
i2c_reserved_addr, set_up_i2c_pin, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE,
|
||||
};
|
||||
use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
/// I2C error
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
@ -100,23 +102,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
// Configure SCL & SDA pins
|
||||
scl.gpio().ctrl().write(|w| w.set_funcsel(3));
|
||||
sda.gpio().ctrl().write(|w| w.set_funcsel(3));
|
||||
|
||||
scl.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
sda.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
set_up_i2c_pin(&scl);
|
||||
set_up_i2c_pin(&sda);
|
||||
|
||||
// Clear interrupts
|
||||
p.ic_clr_intr().read();
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
@ -10,6 +10,7 @@ mod critical_section_impl;
|
||||
mod intrinsics;
|
||||
|
||||
pub mod adc;
|
||||
pub mod bootsel;
|
||||
pub mod clocks;
|
||||
pub mod dma;
|
||||
pub mod flash;
|
||||
@ -90,7 +91,8 @@ embassy_hal_internal::interrupt_mod!(
|
||||
#[macro_export]
|
||||
macro_rules! bind_interrupts {
|
||||
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
|
||||
$vis struct $name;
|
||||
#[derive(Copy, Clone)]
|
||||
$vis struct $name;
|
||||
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
@ -192,6 +194,7 @@ embassy_hal_internal::peripherals! {
|
||||
PIO1,
|
||||
|
||||
WATCHDOG,
|
||||
BOOTSEL,
|
||||
}
|
||||
|
||||
macro_rules! select_bootloader {
|
||||
|
@ -1079,18 +1079,43 @@ impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
|
||||
pub trait PioPin: sealed::PioPin + gpio::Pin {}
|
||||
|
||||
macro_rules! impl_pio_pin {
|
||||
($( $num:tt )*) => {
|
||||
($( $pin:ident, )*) => {
|
||||
$(
|
||||
paste::paste!{
|
||||
impl sealed::PioPin for peripherals::[< PIN_ $num >] {}
|
||||
impl PioPin for peripherals::[< PIN_ $num >] {}
|
||||
}
|
||||
impl sealed::PioPin for peripherals::$pin {}
|
||||
impl PioPin for peripherals::$pin {}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_pio_pin! {
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
10 11 12 13 14 15 16 17 18 19
|
||||
20 21 22 23 24 25 26 27 28 29
|
||||
PIN_0,
|
||||
PIN_1,
|
||||
PIN_2,
|
||||
PIN_3,
|
||||
PIN_4,
|
||||
PIN_5,
|
||||
PIN_6,
|
||||
PIN_7,
|
||||
PIN_8,
|
||||
PIN_9,
|
||||
PIN_10,
|
||||
PIN_11,
|
||||
PIN_12,
|
||||
PIN_13,
|
||||
PIN_14,
|
||||
PIN_15,
|
||||
PIN_16,
|
||||
PIN_17,
|
||||
PIN_18,
|
||||
PIN_19,
|
||||
PIN_20,
|
||||
PIN_21,
|
||||
PIN_22,
|
||||
PIN_23,
|
||||
PIN_24,
|
||||
PIN_25,
|
||||
PIN_26,
|
||||
PIN_27,
|
||||
PIN_28,
|
||||
PIN_29,
|
||||
}
|
||||
|
@ -597,7 +597,8 @@ mod eha {
|
||||
|
||||
impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> {
|
||||
type Config = Config;
|
||||
fn set_config(&mut self, config: &Self::Config) {
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
||||
let p = self.inner.regs();
|
||||
let (presc, postdiv) = calc_prescs(config.frequency);
|
||||
p.cpsr().write(|w| w.set_cpsdvsr(presc));
|
||||
@ -607,5 +608,7 @@ impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> {
|
||||
w.set_sph(config.phase == Phase::CaptureOnSecondTransition);
|
||||
w.set_scr(postdiv);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -490,8 +490,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for BufferedInterr
|
||||
w.set_oeic(ris.oeris());
|
||||
});
|
||||
|
||||
trace!("on_interrupt ris={:#X}", ris.0);
|
||||
|
||||
// Errors
|
||||
if ris.feris() {
|
||||
warn!("Framing error");
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr;
|
||||
|
||||
use embassy_stm32::ipcc::Ipcc;
|
||||
@ -13,7 +12,7 @@ use crate::unsafe_linked_list::LinkedListNode;
|
||||
use crate::{channels, evt};
|
||||
|
||||
pub struct Ble {
|
||||
phantom: PhantomData<Ble>,
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl Ble {
|
||||
@ -29,7 +28,7 @@ impl Ble {
|
||||
});
|
||||
}
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self { _private: () }
|
||||
}
|
||||
/// `HW_IPCC_BLE_EvtNot`
|
||||
pub async fn tl_read(&self) -> EvtBox<Self> {
|
||||
|
@ -1,5 +1,4 @@
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::task::Poll;
|
||||
@ -21,12 +20,12 @@ static MAC_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub struct Mac {
|
||||
phantom: PhantomData<Mac>,
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl Mac {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { phantom: PhantomData }
|
||||
Self { _private: () }
|
||||
}
|
||||
|
||||
/// `HW_IPCC_MAC_802_15_4_EvtNot`
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! Memory manager routines
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::task::Poll;
|
||||
|
||||
@ -21,7 +20,7 @@ static MM_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
pub struct MemoryManager {
|
||||
phantom: PhantomData<MemoryManager>,
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl MemoryManager {
|
||||
@ -44,7 +43,7 @@ impl MemoryManager {
|
||||
});
|
||||
}
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self { _private: () }
|
||||
}
|
||||
|
||||
pub async fn run_queue(&self) {
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr;
|
||||
|
||||
use crate::cmd::CmdPacket;
|
||||
@ -12,7 +11,7 @@ use crate::unsafe_linked_list::LinkedListNode;
|
||||
use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};
|
||||
|
||||
pub struct Sys {
|
||||
phantom: PhantomData<Sys>,
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl Sys {
|
||||
@ -27,7 +26,7 @@ impl Sys {
|
||||
});
|
||||
}
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self { _private: () }
|
||||
}
|
||||
|
||||
/// Returns CPU2 wireless firmware information (if present).
|
||||
|
@ -21,12 +21,13 @@ flavors = [
|
||||
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" },
|
||||
{ regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] },
|
||||
{ regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" },
|
||||
{ regex_feature = "stm32l4.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32l5.*", target = "thumbv8m.main-none-eabihf" },
|
||||
{ regex_feature = "stm32u5.*", target = "thumbv8m.main-none-eabihf" },
|
||||
{ regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" },
|
||||
{ regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" },
|
||||
]
|
||||
|
||||
@ -57,16 +58,14 @@ rand_core = "0.6.3"
|
||||
sdio-host = "0.5.0"
|
||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||
critical-section = "1.1"
|
||||
atomic-polyfill = "1.0.1"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4e6a74f69c4bc5d2d4872ba50d805e75bfe55cad" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6bfa5a0dcec6a9bd42cea94ba11eeae1a17a7f2c" }
|
||||
vcell = "0.1.3"
|
||||
bxcan = "0.7.0"
|
||||
nb = "1.0.0"
|
||||
stm32-fmc = "0.3.0"
|
||||
seq-macro = "0.3.0"
|
||||
cfg-if = "1.0.0"
|
||||
embedded-io = { version = "0.5.0" }
|
||||
embedded-io-async = { version = "0.5.0", optional = true }
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
chrono = { version = "^0.4", default-features = false, optional = true}
|
||||
bit_field = "0.10.2"
|
||||
document-features = "0.2.7"
|
||||
@ -77,7 +76,8 @@ critical-section = { version = "1.1", features = ["std"] }
|
||||
[build-dependencies]
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4e6a74f69c4bc5d2d4872ba50d805e75bfe55cad", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6bfa5a0dcec6a9bd42cea94ba11eeae1a17a7f2c", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
@ -133,6 +133,22 @@ time-driver-tim12 = ["_time-driver"]
|
||||
time-driver-tim15 = ["_time-driver"]
|
||||
|
||||
|
||||
#! ## Analog Switch Pins (Pxy_C) on STM32H7 series
|
||||
#! Get `PXY` and `PXY_C` singletons. Digital impls are on `PXY`, Analog impls are on `PXY_C`
|
||||
#! If disabled, you get only the `PXY` singleton. It has both digital and analog impls.
|
||||
|
||||
## Split PA0
|
||||
split-pa0 = ["_split-pins-enabled"]
|
||||
## Split PA1
|
||||
split-pa1 = ["_split-pins-enabled"]
|
||||
## Split PC2
|
||||
split-pc2 = ["_split-pins-enabled"]
|
||||
## Split PC3
|
||||
split-pc3 = ["_split-pins-enabled"]
|
||||
|
||||
## internal use only
|
||||
_split-pins-enabled = []
|
||||
|
||||
#! ## Chip-selection features
|
||||
#! Select your chip by specifying the model as a feature, e.g. `stm32c011d6`.
|
||||
#! Check the `Cargo.toml` for the latest list of supported chips.
|
||||
@ -1454,6 +1470,10 @@ stm32wb55vc = [ "stm32-metapac/stm32wb55vc" ]
|
||||
stm32wb55ve = [ "stm32-metapac/stm32wb55ve" ]
|
||||
stm32wb55vg = [ "stm32-metapac/stm32wb55vg" ]
|
||||
stm32wb55vy = [ "stm32-metapac/stm32wb55vy" ]
|
||||
stm32wba52ce = [ "stm32-metapac/stm32wba52ce" ]
|
||||
stm32wba52cg = [ "stm32-metapac/stm32wba52cg" ]
|
||||
stm32wba52ke = [ "stm32-metapac/stm32wba52ke" ]
|
||||
stm32wba52kg = [ "stm32-metapac/stm32wba52kg" ]
|
||||
stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4" ]
|
||||
stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p" ]
|
||||
stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4" ]
|
||||
|
@ -5,9 +5,36 @@ use std::{env, fs};
|
||||
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use stm32_metapac::metadata::{MemoryRegionKind, METADATA};
|
||||
use stm32_metapac::metadata::ir::{BlockItemInner, Enum};
|
||||
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, METADATA};
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
|
||||
if target.starts_with("thumbv6m-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv6m");
|
||||
} else if target.starts_with("thumbv7m-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv7m");
|
||||
} else if target.starts_with("thumbv7em-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv7m");
|
||||
println!("cargo:rustc-cfg=armv7em"); // (not currently used)
|
||||
} else if target.starts_with("thumbv8m.base") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv8m");
|
||||
println!("cargo:rustc-cfg=armv8m_base");
|
||||
} else if target.starts_with("thumbv8m.main") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv8m");
|
||||
println!("cargo:rustc-cfg=armv8m_main");
|
||||
}
|
||||
|
||||
if target.ends_with("-eabihf") {
|
||||
println!("cargo:rustc-cfg=has_fpu");
|
||||
}
|
||||
|
||||
let chip_name = match env::vars()
|
||||
.map(|(a, _)| a)
|
||||
.filter(|x| x.starts_with("CARGO_FEATURE_STM32"))
|
||||
@ -50,12 +77,14 @@ fn main() {
|
||||
// We *shouldn't* have singletons for these, but the HAL currently requires
|
||||
// singletons, for using with RccPeripheral to enable/disable clocks to them.
|
||||
"rcc" => {
|
||||
if r.version.starts_with("h5") || r.version.starts_with("h7") || r.version.starts_with("f4") {
|
||||
singletons.push("MCO1".to_string());
|
||||
singletons.push("MCO2".to_string());
|
||||
}
|
||||
if r.version.starts_with("l4") {
|
||||
singletons.push("MCO".to_string());
|
||||
for pin in p.pins {
|
||||
if pin.signal.starts_with("MCO") {
|
||||
let name = pin.signal.replace('_', "").to_string();
|
||||
if !singletons.contains(&name) {
|
||||
println!("cargo:rustc-cfg={}", name.to_ascii_lowercase());
|
||||
singletons.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
singletons.push(p.name.to_string());
|
||||
}
|
||||
@ -81,6 +110,60 @@ fn main() {
|
||||
singletons.push(c.name.to_string());
|
||||
}
|
||||
|
||||
let mut pin_set = std::collections::HashSet::new();
|
||||
for p in METADATA.peripherals {
|
||||
for pin in p.pins {
|
||||
pin_set.insert(pin.pin);
|
||||
}
|
||||
}
|
||||
|
||||
struct SplitFeature {
|
||||
feature_name: String,
|
||||
pin_name_with_c: String,
|
||||
#[cfg(feature = "_split-pins-enabled")]
|
||||
pin_name_without_c: String,
|
||||
}
|
||||
|
||||
// Extra analog switch pins available on most H7 chips
|
||||
let split_features: Vec<SplitFeature> = vec![
|
||||
#[cfg(feature = "split-pa0")]
|
||||
SplitFeature {
|
||||
feature_name: "split-pa0".to_string(),
|
||||
pin_name_with_c: "PA0_C".to_string(),
|
||||
pin_name_without_c: "PA0".to_string(),
|
||||
},
|
||||
#[cfg(feature = "split-pa1")]
|
||||
SplitFeature {
|
||||
feature_name: "split-pa1".to_string(),
|
||||
pin_name_with_c: "PA1_C".to_string(),
|
||||
pin_name_without_c: "PA1".to_string(),
|
||||
},
|
||||
#[cfg(feature = "split-pc2")]
|
||||
SplitFeature {
|
||||
feature_name: "split-pc2".to_string(),
|
||||
pin_name_with_c: "PC2_C".to_string(),
|
||||
pin_name_without_c: "PC2".to_string(),
|
||||
},
|
||||
#[cfg(feature = "split-pc3")]
|
||||
SplitFeature {
|
||||
feature_name: "split-pc3".to_string(),
|
||||
pin_name_with_c: "PC3_C".to_string(),
|
||||
pin_name_without_c: "PC3".to_string(),
|
||||
},
|
||||
];
|
||||
|
||||
for split_feature in &split_features {
|
||||
if pin_set.contains(split_feature.pin_name_with_c.as_str()) {
|
||||
singletons.push(split_feature.pin_name_with_c.clone());
|
||||
} else {
|
||||
panic!(
|
||||
"'{}' feature invalid for this chip! No pin '{}' found.\n
|
||||
Found pins: {:#?}",
|
||||
split_feature.feature_name, split_feature.pin_name_with_c, pin_set
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ========
|
||||
// Handle time-driver-XXXX features.
|
||||
|
||||
@ -305,6 +388,51 @@ fn main() {
|
||||
});
|
||||
}
|
||||
|
||||
// ========
|
||||
// Generate rcc fieldset and enum maps
|
||||
let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
|
||||
let rcc_registers = METADATA
|
||||
.peripherals
|
||||
.iter()
|
||||
.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;
|
||||
|
||||
let rcc_block_item_map: HashMap<&str, &str> = rcc_blocks
|
||||
.iter()
|
||||
.filter_map(|b| match &b.inner {
|
||||
BlockItemInner::Register(register) => register.fieldset.map(|f| (f, b.name)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
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,
|
||||
f.fields
|
||||
.iter()
|
||||
.filter_map(|f| {
|
||||
let enumm = f.enumm?;
|
||||
let enumm = rcc_enum_map.get(enumm)?;
|
||||
|
||||
Some((f.name, *enumm))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
// ========
|
||||
// Generate RccPeripheral impls
|
||||
|
||||
@ -372,26 +500,77 @@ fn main() {
|
||||
(TokenStream::new(), TokenStream::new())
|
||||
};
|
||||
|
||||
let mux_for = |mux: Option<&'static PeripheralRccRegister>| {
|
||||
// temporary hack to restrict the scope of the implementation to h5
|
||||
if !&chip_name.starts_with("stm32h5") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mux = mux?;
|
||||
let fieldset = rcc_enum_map.get(mux.register)?;
|
||||
let enumm = fieldset.get(mux.field)?;
|
||||
|
||||
Some((mux, *enumm))
|
||||
};
|
||||
|
||||
let clock_frequency = match mux_for(rcc.mux.as_ref()) {
|
||||
Some((mux, rcc_enumm)) => {
|
||||
let fieldset_name = format_ident!("{}", mux.register);
|
||||
let field_name = format_ident!("{}", mux.field);
|
||||
let enum_name = format_ident!("{}", rcc_enumm.name);
|
||||
|
||||
let match_arms: TokenStream = rcc_enumm
|
||||
.variants
|
||||
.iter()
|
||||
.filter(|v| v.name != "DISABLE")
|
||||
.map(|v| {
|
||||
let variant_name = format_ident!("{}", v.name);
|
||||
|
||||
// temporary hack to restrict the scope of the implementation until clock names can be stabilized
|
||||
let clock_name = format_ident!("mux_{}", v.name.to_ascii_lowercase());
|
||||
|
||||
quote! {
|
||||
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name.unwrap() },
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
quote! {
|
||||
use crate::pac::rcc::vals::#enum_name;
|
||||
|
||||
#[allow(unreachable_patterns)]
|
||||
match crate::pac::RCC.#fieldset_name().read().#field_name() {
|
||||
#match_arms
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
None => quote! {
|
||||
unsafe { crate::rcc::get_freqs().#clk }
|
||||
},
|
||||
};
|
||||
|
||||
g.extend(quote! {
|
||||
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
unsafe { crate::rcc::get_freqs().#clk }
|
||||
#clock_frequency
|
||||
}
|
||||
fn enable() {
|
||||
critical_section::with(|_| {
|
||||
critical_section::with(|_cs| {
|
||||
#before_enable
|
||||
#[cfg(feature = "low-power")]
|
||||
crate::rcc::clock_refcount_add();
|
||||
crate::rcc::clock_refcount_add(_cs);
|
||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
||||
#after_enable
|
||||
})
|
||||
}
|
||||
fn disable() {
|
||||
critical_section::with(|_| {
|
||||
critical_section::with(|_cs| {
|
||||
#before_disable
|
||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
|
||||
#[cfg(feature = "low-power")]
|
||||
crate::rcc::clock_refcount_sub();
|
||||
crate::rcc::clock_refcount_sub(_cs);
|
||||
})
|
||||
}
|
||||
fn reset() {
|
||||
@ -404,12 +583,14 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let mut refcount_mod = TokenStream::new();
|
||||
for refcount_static in refcount_statics {
|
||||
refcount_mod.extend(quote! {
|
||||
pub(crate) static mut #refcount_static: u8 = 0;
|
||||
});
|
||||
}
|
||||
let refcount_mod: TokenStream = refcount_statics
|
||||
.iter()
|
||||
.map(|refcount_static| {
|
||||
quote! {
|
||||
pub(crate) static mut #refcount_static: u8 = 0;
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
g.extend(quote! {
|
||||
mod refcount_statics {
|
||||
@ -463,6 +644,15 @@ fn main() {
|
||||
(("lpuart", "RTS"), quote!(crate::usart::RtsPin)),
|
||||
(("lpuart", "CK"), quote!(crate::usart::CkPin)),
|
||||
(("lpuart", "DE"), quote!(crate::usart::DePin)),
|
||||
(("sai", "SCK_A"), quote!(crate::sai::SckAPin)),
|
||||
(("sai", "SCK_B"), quote!(crate::sai::SckBPin)),
|
||||
(("sai", "FS_A"), quote!(crate::sai::FsAPin)),
|
||||
(("sai", "FS_B"), quote!(crate::sai::FsBPin)),
|
||||
(("sai", "SD_A"), quote!(crate::sai::SdAPin)),
|
||||
(("sai", "SD_B"), quote!(crate::sai::SdBPin)),
|
||||
(("sai", "MCLK_A"), quote!(crate::sai::MclkAPin)),
|
||||
(("sai", "MCLK_B"), quote!(crate::sai::MclkBPin)),
|
||||
(("sai", "WS"), quote!(crate::sai::WsPin)),
|
||||
(("spi", "SCK"), quote!(crate::spi::SckPin)),
|
||||
(("spi", "MOSI"), quote!(crate::spi::MosiPin)),
|
||||
(("spi", "MISO"), quote!(crate::spi::MisoPin)),
|
||||
@ -656,12 +846,17 @@ fn main() {
|
||||
(("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)),
|
||||
(("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)),
|
||||
(("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)),
|
||||
(("quadspi", "BK1_IO0"), quote!(crate::qspi::D0Pin)),
|
||||
(("quadspi", "BK1_IO1"), quote!(crate::qspi::D1Pin)),
|
||||
(("quadspi", "BK1_IO2"), quote!(crate::qspi::D2Pin)),
|
||||
(("quadspi", "BK1_IO3"), quote!(crate::qspi::D3Pin)),
|
||||
(("quadspi", "BK1_IO0"), quote!(crate::qspi::BK1D0Pin)),
|
||||
(("quadspi", "BK1_IO1"), quote!(crate::qspi::BK1D1Pin)),
|
||||
(("quadspi", "BK1_IO2"), quote!(crate::qspi::BK1D2Pin)),
|
||||
(("quadspi", "BK1_IO3"), quote!(crate::qspi::BK1D3Pin)),
|
||||
(("quadspi", "BK1_NCS"), quote!(crate::qspi::BK1NSSPin)),
|
||||
(("quadspi", "BK2_IO0"), quote!(crate::qspi::BK2D0Pin)),
|
||||
(("quadspi", "BK2_IO1"), quote!(crate::qspi::BK2D1Pin)),
|
||||
(("quadspi", "BK2_IO2"), quote!(crate::qspi::BK2D2Pin)),
|
||||
(("quadspi", "BK2_IO3"), quote!(crate::qspi::BK2D3Pin)),
|
||||
(("quadspi", "BK2_NCS"), quote!(crate::qspi::BK2NSSPin)),
|
||||
(("quadspi", "CLK"), quote!(crate::qspi::SckPin)),
|
||||
(("quadspi", "BK1_NCS"), quote!(crate::qspi::NSSPin)),
|
||||
].into();
|
||||
|
||||
for p in METADATA.peripherals {
|
||||
@ -670,29 +865,21 @@ fn main() {
|
||||
let key = (regs.kind, pin.signal);
|
||||
if let Some(tr) = signals.get(&key) {
|
||||
let mut peri = format_ident!("{}", p.name);
|
||||
let pin_name = format_ident!("{}", pin.pin);
|
||||
|
||||
let pin_name = {
|
||||
// If we encounter a _C pin but the split_feature for this pin is not enabled, skip it
|
||||
if pin.pin.ends_with("_C") && !split_features.iter().any(|x| x.pin_name_with_c == pin.pin) {
|
||||
continue;
|
||||
}
|
||||
|
||||
format_ident!("{}", pin.pin)
|
||||
};
|
||||
|
||||
let af = pin.af.unwrap_or(0);
|
||||
|
||||
// MCO is special
|
||||
if pin.signal.starts_with("MCO_") {
|
||||
// Supported in H7 only for now
|
||||
if regs.version.starts_with("h5")
|
||||
|| regs.version.starts_with("h7")
|
||||
|| regs.version.starts_with("f4")
|
||||
{
|
||||
peri = format_ident!("{}", pin.signal.replace('_', ""));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if pin.signal == "MCO" {
|
||||
// Supported in H7 only for now
|
||||
if regs.version.starts_with("l4") {
|
||||
peri = format_ident!("MCO");
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if pin.signal.starts_with("MCO") {
|
||||
peri = format_ident!("{}", pin.signal.replace('_', ""));
|
||||
}
|
||||
|
||||
g.extend(quote! {
|
||||
@ -707,7 +894,13 @@ fn main() {
|
||||
}
|
||||
|
||||
let peri = format_ident!("{}", p.name);
|
||||
let pin_name = format_ident!("{}", pin.pin);
|
||||
let pin_name = {
|
||||
// If we encounter a _C pin but the split_feature for this pin is not enabled, skip it
|
||||
if pin.pin.ends_with("_C") && !split_features.iter().any(|x| x.pin_name_with_c == pin.pin) {
|
||||
continue;
|
||||
}
|
||||
format_ident!("{}", pin.pin)
|
||||
};
|
||||
|
||||
// H7 has differential voltage measurements
|
||||
let ch: Option<u8> = if pin.signal.starts_with("INP") {
|
||||
@ -727,6 +920,20 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
if regs.kind == "opamp" {
|
||||
if !pin.signal.starts_with("VP") {
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
if regs.kind == "dac" {
|
||||
let peri = format_ident!("{}", p.name);
|
||||
@ -750,6 +957,8 @@ fn main() {
|
||||
(("usart", "TX"), quote!(crate::usart::TxDma)),
|
||||
(("lpuart", "RX"), quote!(crate::usart::RxDma)),
|
||||
(("lpuart", "TX"), quote!(crate::usart::TxDma)),
|
||||
(("sai", "A"), quote!(crate::sai::DmaA)),
|
||||
(("sai", "B"), quote!(crate::sai::DmaB)),
|
||||
(("spi", "RX"), quote!(crate::spi::RxDma)),
|
||||
(("spi", "TX"), quote!(crate::spi::TxDma)),
|
||||
(("i2c", "RX"), quote!(crate::i2c::RxDma)),
|
||||
@ -809,6 +1018,105 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// ========
|
||||
// Generate Div/Mul impls for RCC prescalers/dividers/multipliers.
|
||||
let rcc_registers = METADATA
|
||||
.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 {
|
||||
match e {
|
||||
"Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" => true,
|
||||
"Timpre" | "Pllrclkpre" => false,
|
||||
e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Frac {
|
||||
num: u32,
|
||||
denom: u32,
|
||||
}
|
||||
|
||||
impl Frac {
|
||||
fn simplify(self) -> Self {
|
||||
let d = gcd(self.num, self.denom);
|
||||
Self {
|
||||
num: self.num / d,
|
||||
denom: self.denom / d,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gcd(a: u32, b: u32) -> u32 {
|
||||
if b == 0 {
|
||||
return a;
|
||||
}
|
||||
gcd(b, a % b)
|
||||
}
|
||||
|
||||
fn parse_num(n: &str) -> Result<Frac, ()> {
|
||||
for prefix in ["DIV", "MUL"] {
|
||||
if let Some(n) = n.strip_prefix(prefix) {
|
||||
let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32;
|
||||
let mantissa = n.replace('_', "").parse().map_err(|_| ())?;
|
||||
let f = Frac {
|
||||
num: mantissa,
|
||||
denom: 10u32.pow(exponent),
|
||||
};
|
||||
return Ok(f.simplify());
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
if is_rcc_name(e.name) {
|
||||
let enum_name = format_ident!("{}", e.name);
|
||||
let mut muls = Vec::new();
|
||||
let mut divs = Vec::new();
|
||||
for v in e.variants {
|
||||
let Ok(val) = parse_num(v.name) else {
|
||||
panic!("could not parse mul/div. enum={} variant={}", e.name, v.name)
|
||||
};
|
||||
let variant_name = format_ident!("{}", v.name);
|
||||
let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name);
|
||||
let num = val.num;
|
||||
let denom = val.denom;
|
||||
muls.push(quote!(#variant => self * #num / #denom,));
|
||||
divs.push(quote!(#variant => self * #denom / #num,));
|
||||
}
|
||||
|
||||
g.extend(quote! {
|
||||
impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
|
||||
type Output = crate::time::Hertz;
|
||||
fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
|
||||
match rhs {
|
||||
#(#divs)*
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl core::ops::Mul<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
|
||||
type Output = crate::time::Hertz;
|
||||
fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
|
||||
match rhs {
|
||||
#(#muls)*
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ========
|
||||
// Write foreach_foo! macrotables
|
||||
|
||||
@ -817,6 +1125,17 @@ fn main() {
|
||||
let mut peripherals_table: Vec<Vec<String>> = Vec::new();
|
||||
let mut pins_table: Vec<Vec<String>> = Vec::new();
|
||||
let mut dma_channels_table: Vec<Vec<String>> = Vec::new();
|
||||
let mut adc_common_table: Vec<Vec<String>> = Vec::new();
|
||||
|
||||
/*
|
||||
If ADC3_COMMON exists, ADC3 and higher are assigned to it
|
||||
All other ADCs are assigned to ADC_COMMON
|
||||
|
||||
ADC3 and higher are assigned to the adc34 clock in the table
|
||||
The adc3_common cfg directive is added if ADC3_COMMON exists
|
||||
*/
|
||||
let has_adc3 = METADATA.peripherals.iter().find(|p| p.name == "ADC3_COMMON").is_some();
|
||||
let set_adc345 = HashSet::from(["ADC3", "ADC4", "ADC5"]);
|
||||
|
||||
for m in METADATA
|
||||
.memory
|
||||
@ -844,16 +1163,45 @@ fn main() {
|
||||
|
||||
for pin_num in 0u32..16 {
|
||||
let pin_name = format!("P{}{}", port_letter, pin_num);
|
||||
|
||||
pins_table.push(vec![
|
||||
pin_name,
|
||||
pin_name.clone(),
|
||||
p.name.to_string(),
|
||||
port_num.to_string(),
|
||||
pin_num.to_string(),
|
||||
format!("EXTI{}", pin_num),
|
||||
]);
|
||||
|
||||
// If we have the split pins, we need to do a little extra work:
|
||||
// Add the "_C" variant to the table. The solution is not optimal, though.
|
||||
// Adding them only when the corresponding GPIOx also appears.
|
||||
// This should avoid unintended side-effects as much as possible.
|
||||
#[cfg(feature = "_split-pins-enabled")]
|
||||
for split_feature in &split_features {
|
||||
if split_feature.pin_name_without_c == pin_name {
|
||||
pins_table.push(vec![
|
||||
split_feature.pin_name_with_c.to_string(),
|
||||
p.name.to_string(),
|
||||
port_num.to_string(),
|
||||
pin_num.to_string(),
|
||||
format!("EXTI{}", pin_num),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if regs.kind == "adc" {
|
||||
let (adc_common, adc_clock) = if set_adc345.contains(p.name) && has_adc3 {
|
||||
("ADC3_COMMON", "adc34")
|
||||
} else {
|
||||
("ADC_COMMON", "adc")
|
||||
};
|
||||
|
||||
let row = vec![p.name.to_string(), adc_common.to_string(), adc_clock.to_string()];
|
||||
adc_common_table.push(row);
|
||||
}
|
||||
|
||||
for irq in p.interrupts {
|
||||
let row = vec![
|
||||
p.name.to_string(),
|
||||
@ -932,6 +1280,7 @@ fn main() {
|
||||
make_table(&mut m, "foreach_peripheral", &peripherals_table);
|
||||
make_table(&mut m, "foreach_pin", &pins_table);
|
||||
make_table(&mut m, "foreach_dma_channel", &dma_channels_table);
|
||||
make_table(&mut m, "foreach_adc", &adc_common_table);
|
||||
|
||||
let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
let out_file = out_dir.join("_macros.rs").to_string_lossy().to_string();
|
||||
@ -973,13 +1322,24 @@ fn main() {
|
||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]);
|
||||
}
|
||||
|
||||
// =======
|
||||
// ADC3_COMMON is present
|
||||
if has_adc3 {
|
||||
println!("cargo:rustc-cfg={}", "adc3_common");
|
||||
}
|
||||
|
||||
// =======
|
||||
// Features for targeting groups of chips
|
||||
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429
|
||||
println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
|
||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
|
||||
if &chip_name[..8] == "stm32wba" {
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..8]); // stm32wba
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..10]); // stm32wba52
|
||||
} else {
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429
|
||||
println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
|
||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
|
||||
}
|
||||
|
||||
// Handle time-driver-XXXX features.
|
||||
if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {}
|
||||
|
@ -1,16 +1,37 @@
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
|
||||
use crate::rcc::get_freqs;
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
pub const VDDA_CALIB_MV: u32 = 3300;
|
||||
pub const ADC_MAX: u32 = (1 << 12) - 1;
|
||||
// No calibration data for F103, voltage should be 1.2v
|
||||
pub const VREF_INT: u32 = 1200;
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
if T::regs().sr().read().eoc() {
|
||||
T::regs().cr1().modify(|w| w.set_eocie(false));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
T::state().waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vref;
|
||||
impl<T: Instance> AdcPin<T> for Vref {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
||||
@ -95,18 +116,28 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
fn convert(&mut self) -> u16 {
|
||||
async fn convert(&mut self) -> u16 {
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(true);
|
||||
reg.set_swstart(true);
|
||||
});
|
||||
while T::regs().cr2().read().swstart() {}
|
||||
while !T::regs().sr().read().eoc() {}
|
||||
T::regs().cr1().modify(|w| w.set_eocie(true));
|
||||
|
||||
poll_fn(|cx| {
|
||||
T::state().waker.register(cx.waker());
|
||||
|
||||
if !T::regs().cr2().read().swstart() && T::regs().sr().read().eoc() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
T::regs().dr().read().0 as u16
|
||||
}
|
||||
|
||||
pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_scan(false);
|
||||
@ -123,7 +154,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
|
||||
// Configure the channel to sample
|
||||
T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel()));
|
||||
self.convert()
|
||||
self.convert().await
|
||||
}
|
||||
|
||||
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
@ -135,3 +166,11 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Adc<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
T::regs().cr2().modify(|reg| reg.set_adon(false));
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,36 @@
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
pub const VDDA_CALIB_MV: u32 = 3300;
|
||||
pub const ADC_MAX: u32 = (1 << 12) - 1;
|
||||
pub const VREF_INT: u32 = 1230;
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
if T::regs().isr().read().eoc() {
|
||||
T::regs().ier().modify(|w| w.set_eocie(false));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
T::state().waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vref;
|
||||
impl<T: Instance> AdcPin<T> for Vref {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
||||
@ -17,6 +39,13 @@ impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
||||
}
|
||||
}
|
||||
|
||||
impl Vref {
|
||||
/// The value that vref would be if vdda was at 3300mv
|
||||
pub fn value(&self) -> u16 {
|
||||
crate::pac::VREFINTCAL.data().read().value()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
@ -26,7 +55,11 @@ impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
pub fn new(
|
||||
adc: impl Peripheral<P = T> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
delay: &mut impl DelayUs<u32>,
|
||||
) -> Self {
|
||||
use crate::pac::adc::vals;
|
||||
|
||||
into_ref!(adc);
|
||||
@ -50,7 +83,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
while T::regs().cr().read().adcal() {}
|
||||
|
||||
// Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223)
|
||||
delay.delay_us(6 * 1_000_000 / Self::freq().0);
|
||||
delay.delay_us(1 + (6 * 1_000_000 / Self::freq().0));
|
||||
|
||||
// Enable the adc
|
||||
T::regs().cr().modify(|w| w.set_aden(true));
|
||||
@ -58,6 +91,11 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
// Wait until the adc is ready
|
||||
while !T::regs().isr().read().adrdy() {}
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe {
|
||||
T::Interrupt::enable();
|
||||
}
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
@ -97,30 +135,61 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
fn convert(&mut self) -> u16 {
|
||||
async fn convert(&mut self) -> u16 {
|
||||
T::regs().isr().write(|_| {});
|
||||
T::regs().ier().modify(|w| w.set_eocie(true));
|
||||
T::regs().cr().modify(|w| w.set_adstart(true));
|
||||
|
||||
while !T::regs().isr().read().eoc() && !T::regs().isr().read().eos() {}
|
||||
poll_fn(|cx| {
|
||||
T::state().waker.register(cx.waker());
|
||||
|
||||
if T::regs().isr().read().eoc() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
T::regs().isr().write(|_| {});
|
||||
|
||||
T::regs().dr().read().rdata()
|
||||
}
|
||||
|
||||
pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
|
||||
// Configure the channel to sample
|
||||
T::regs().sqr1().write(|w| w.set_sq(0, pin.channel()));
|
||||
self.convert()
|
||||
self.convert().await
|
||||
}
|
||||
|
||||
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Adc<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
use crate::pac::adc::vals;
|
||||
|
||||
T::regs().cr().modify(|w| w.set_adstp(true));
|
||||
|
||||
while T::regs().cr().read().adstp() {}
|
||||
|
||||
T::regs().cr().modify(|w| w.set_addis(true));
|
||||
|
||||
while T::regs().cr().read().aden() {}
|
||||
|
||||
// Disable the adc regulator
|
||||
T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE));
|
||||
T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::DISABLED));
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
@ -31,15 +31,41 @@ pub struct Adc<'d, T: Instance> {
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
pub struct State {
|
||||
pub waker: AtomicWaker,
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InterruptableInstance {
|
||||
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
pub trait Instance: InterruptableInstance {
|
||||
fn regs() -> crate::pac::adc::Adc;
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz;
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
|
||||
pub trait AdcPin<T: Instance> {
|
||||
#[cfg(any(adc_v1, adc_v2))]
|
||||
fn set_as_analog(&mut self) {}
|
||||
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
|
||||
@ -56,129 +82,37 @@ pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc:
|
||||
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
||||
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
|
||||
#[cfg(not(any(stm32h7, adc_f3, adc_v4)))]
|
||||
foreach_peripheral!(
|
||||
(adc, $inst:ident) => {
|
||||
foreach_adc!(
|
||||
($inst:ident, $common_inst:ident, $clock:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, $common_inst:ident) => {
|
||||
return crate::pac::$common_inst
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::adc::Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
||||
#[cfg(any(stm32h7, adc_f3, adc_v4))]
|
||||
foreach_peripheral!(
|
||||
(adc, ADC3) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::ADC3 {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::ADC3
|
||||
}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
#[allow(unreachable_code)]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC3_COMMON) => {
|
||||
return crate::pac::ADC3_COMMON
|
||||
};
|
||||
// Fall back to ADC_COMMON if ADC3_COMMON does not exist
|
||||
(adccommon, ADC_COMMON) => {
|
||||
return crate::pac::ADC_COMMON
|
||||
};
|
||||
}
|
||||
return crate::pac::$common_inst
|
||||
}
|
||||
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
unsafe { crate::rcc::get_freqs() }.adc34.unwrap()
|
||||
unsafe { crate::rcc::get_freqs() }.$clock.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1))]
|
||||
fn state() -> &'static sealed::State {
|
||||
static STATE: sealed::State = sealed::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::adc::Instance for peripherals::ADC3 {}
|
||||
};
|
||||
(adc, ADC4) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::ADC4 {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::ADC4
|
||||
}
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
#[allow(unreachable_code)]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC3_COMMON) => {
|
||||
return crate::pac::ADC3_COMMON
|
||||
};
|
||||
// Fall back to ADC_COMMON if ADC3_COMMON does not exist
|
||||
(adccommon, ADC_COMMON) => {
|
||||
return crate::pac::ADC_COMMON
|
||||
};
|
||||
foreach_interrupt!(
|
||||
($inst,adc,ADC,GLOBAL,$irq:ident) => {
|
||||
impl sealed::InterruptableInstance for peripherals::$inst {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
unsafe { crate::rcc::get_freqs() }.adc34.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::adc::Instance for peripherals::ADC4 {}
|
||||
};
|
||||
(adc, ADC5) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::ADC5 {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::ADC5
|
||||
}
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
#[allow(unreachable_code)]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC3_COMMON) => {
|
||||
return crate::pac::ADC3_COMMON
|
||||
};
|
||||
// Fall back to ADC_COMMON if ADC3_COMMON does not exist
|
||||
(adccommon, ADC_COMMON) => {
|
||||
return crate::pac::ADC_COMMON
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
unsafe { crate::rcc::get_freqs() }.adc34.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::adc::Instance for peripherals::ADC5 {}
|
||||
};
|
||||
(adc, $inst:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC_COMMON) => {
|
||||
return crate::pac::ADC_COMMON
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
unsafe { crate::rcc::get_freqs() }.adc.unwrap()
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
impl crate::adc::Instance for peripherals::$inst {}
|
||||
};
|
||||
@ -189,6 +123,11 @@ macro_rules! impl_adc_pin {
|
||||
impl crate::adc::AdcPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||
|
||||
impl crate::adc::sealed::AdcPin<peripherals::$inst> for crate::peripherals::$pin {
|
||||
#[cfg(any(adc_v1, adc_v2))]
|
||||
fn set_as_analog(&mut self) {
|
||||
<Self as crate::gpio::sealed::Pin>::set_as_analog(self);
|
||||
}
|
||||
|
||||
fn channel(&self) -> u8 {
|
||||
$ch
|
||||
}
|
||||
|
@ -1,39 +1,65 @@
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
|
||||
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::peripherals::ADC;
|
||||
use crate::Peripheral;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
pub const VDDA_CALIB_MV: u32 = 3300;
|
||||
pub const VREF_INT: u32 = 1230;
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
if T::regs().isr().read().eoc() {
|
||||
T::regs().ier().modify(|w| w.set_eocie(false));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
T::state().waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vbat;
|
||||
impl InternalChannel<ADC> for Vbat {}
|
||||
impl super::sealed::InternalChannel<ADC> for Vbat {
|
||||
impl AdcPin<ADC> for Vbat {}
|
||||
impl super::sealed::AdcPin<ADC> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vref;
|
||||
impl InternalChannel<ADC> for Vref {}
|
||||
impl super::sealed::InternalChannel<ADC> for Vref {
|
||||
impl AdcPin<ADC> for Vref {}
|
||||
impl super::sealed::AdcPin<ADC> for Vref {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl InternalChannel<ADC> for Temperature {}
|
||||
impl super::sealed::InternalChannel<ADC> for Temperature {
|
||||
impl AdcPin<ADC> for Temperature {}
|
||||
impl super::sealed::AdcPin<ADC> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
16
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
pub fn new(
|
||||
adc: impl Peripheral<P = T> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
delay: &mut impl DelayUs<u32>,
|
||||
) -> Self {
|
||||
into_ref!(adc);
|
||||
T::enable();
|
||||
T::reset();
|
||||
@ -44,12 +70,32 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
// tstab = 14 * 1/fadc
|
||||
delay.delay_us(1);
|
||||
|
||||
let s = Self {
|
||||
// A.7.1 ADC calibration code example
|
||||
T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));
|
||||
T::regs().cr().modify(|reg| reg.set_adcal(true));
|
||||
while T::regs().cr().read().adcal() {}
|
||||
|
||||
// A.7.2 ADC enable sequence code example
|
||||
if T::regs().isr().read().adrdy() {
|
||||
T::regs().isr().modify(|reg| reg.set_adrdy(true));
|
||||
}
|
||||
T::regs().cr().modify(|reg| reg.set_aden(true));
|
||||
while !T::regs().isr().read().adrdy() {
|
||||
// ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration
|
||||
// Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the
|
||||
// ADEN bit until the ADRDY flag goes high.
|
||||
T::regs().cr().modify(|reg| reg.set_aden(true));
|
||||
}
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe {
|
||||
T::Interrupt::enable();
|
||||
}
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
};
|
||||
s.calibrate();
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_vbat(&self, _delay: &mut impl DelayUs<u32>) -> Vbat {
|
||||
@ -80,21 +126,6 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
Temperature
|
||||
}
|
||||
|
||||
fn calibrate(&self) {
|
||||
// A.7.1 ADC calibration code example
|
||||
if T::regs().cr().read().aden() {
|
||||
T::regs().cr().modify(|reg| reg.set_addis(true));
|
||||
}
|
||||
while T::regs().cr().read().aden() {
|
||||
// spin
|
||||
}
|
||||
T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));
|
||||
T::regs().cr().modify(|reg| reg.set_adcal(true));
|
||||
while T::regs().cr().read().adcal() {
|
||||
// spin
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
||||
self.sample_time = sample_time;
|
||||
}
|
||||
@ -103,57 +134,50 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
|
||||
pub fn read<P>(&mut self, pin: &mut P) -> u16
|
||||
where
|
||||
P: AdcPin<T> + crate::gpio::sealed::Pin,
|
||||
{
|
||||
pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
let channel = pin.channel();
|
||||
pin.set_as_analog();
|
||||
self.read_channel(channel)
|
||||
|
||||
// A.7.5 Single conversion sequence code example - Software trigger
|
||||
T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true));
|
||||
|
||||
self.convert().await
|
||||
}
|
||||
|
||||
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
|
||||
let channel = channel.channel();
|
||||
self.read_channel(channel)
|
||||
}
|
||||
|
||||
fn read_channel(&mut self, channel: u8) -> u16 {
|
||||
// A.7.2 ADC enable sequence code example
|
||||
if T::regs().isr().read().adrdy() {
|
||||
T::regs().isr().modify(|reg| reg.set_adrdy(true));
|
||||
}
|
||||
T::regs().cr().modify(|reg| reg.set_aden(true));
|
||||
while !T::regs().isr().read().adrdy() {
|
||||
// ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration
|
||||
// Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the
|
||||
// ADEN bit until the ADRDY flag goes high.
|
||||
T::regs().cr().modify(|reg| reg.set_aden(true));
|
||||
}
|
||||
|
||||
async fn convert(&mut self) -> u16 {
|
||||
T::regs().isr().modify(|reg| {
|
||||
reg.set_eoc(true);
|
||||
reg.set_eosmp(true);
|
||||
});
|
||||
|
||||
// A.7.5 Single conversion sequence code example - Software trigger
|
||||
T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true));
|
||||
T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into()));
|
||||
T::regs().ier().modify(|w| w.set_eocie(true));
|
||||
T::regs().cr().modify(|reg| reg.set_adstart(true));
|
||||
while !T::regs().isr().read().eoc() {
|
||||
// spin
|
||||
}
|
||||
let value = T::regs().dr().read().0 as u16;
|
||||
|
||||
// A.7.3 ADC disable code example
|
||||
T::regs().cr().modify(|reg| reg.set_adstp(true));
|
||||
while T::regs().cr().read().adstp() {
|
||||
// spin
|
||||
}
|
||||
T::regs().cr().modify(|reg| reg.set_addis(true));
|
||||
while T::regs().cr().read().aden() {
|
||||
// spin
|
||||
}
|
||||
poll_fn(|cx| {
|
||||
T::state().waker.register(cx.waker());
|
||||
|
||||
value
|
||||
if T::regs().isr().read().eoc() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
T::regs().dr().read().data()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Adc<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
// A.7.3 ADC disable code example
|
||||
T::regs().cr().modify(|reg| reg.set_adstp(true));
|
||||
while T::regs().cr().read().adstp() {}
|
||||
|
||||
T::regs().cr().modify(|reg| reg.set_addis(true));
|
||||
while T::regs().cr().read().aden() {}
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use embassy_hal_internal::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use super::InternalChannel;
|
||||
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
|
||||
use crate::peripherals::ADC1;
|
||||
use crate::time::Hertz;
|
||||
@ -16,8 +15,8 @@ pub const VREF_CALIB_MV: u32 = 3300;
|
||||
pub const ADC_POWERUP_TIME_US: u32 = 3;
|
||||
|
||||
pub struct VrefInt;
|
||||
impl InternalChannel<ADC1> for VrefInt {}
|
||||
impl super::sealed::InternalChannel<ADC1> for VrefInt {
|
||||
impl AdcPin<ADC1> for VrefInt {}
|
||||
impl super::sealed::AdcPin<ADC1> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
@ -31,8 +30,8 @@ impl VrefInt {
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl InternalChannel<ADC1> for Temperature {}
|
||||
impl super::sealed::InternalChannel<ADC1> for Temperature {
|
||||
impl AdcPin<ADC1> for Temperature {}
|
||||
impl super::sealed::AdcPin<ADC1> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(stm32f40, stm32f41))] {
|
||||
@ -52,8 +51,8 @@ impl Temperature {
|
||||
}
|
||||
|
||||
pub struct Vbat;
|
||||
impl InternalChannel<ADC1> for Vbat {}
|
||||
impl super::sealed::InternalChannel<ADC1> for Vbat {
|
||||
impl AdcPin<ADC1> for Vbat {}
|
||||
impl super::sealed::AdcPin<ADC1> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
@ -125,7 +124,7 @@ where
|
||||
/// [Adc::read_internal()] to perform conversion.
|
||||
pub fn enable_vrefint(&self) -> VrefInt {
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
|
||||
reg.set_tsvrefe(true);
|
||||
});
|
||||
|
||||
VrefInt {}
|
||||
@ -138,7 +137,7 @@ where
|
||||
/// temperature sensor will return vbat value.
|
||||
pub fn enable_temperature(&self) -> Temperature {
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
|
||||
reg.set_tsvrefe(true);
|
||||
});
|
||||
|
||||
Temperature {}
|
||||
@ -148,7 +147,7 @@ where
|
||||
/// [Adc::read_internal()] to perform conversion.
|
||||
pub fn enable_vbat(&self) -> Vbat {
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED);
|
||||
reg.set_vbate(true);
|
||||
});
|
||||
|
||||
Vbat {}
|
||||
@ -176,22 +175,11 @@ where
|
||||
T::regs().dr().read().0 as u16
|
||||
}
|
||||
|
||||
pub fn read<P>(&mut self, pin: &mut P) -> u16
|
||||
where
|
||||
P: AdcPin<T>,
|
||||
P: crate::gpio::sealed::Pin,
|
||||
{
|
||||
pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
pin.set_as_analog();
|
||||
|
||||
self.read_channel(pin.channel())
|
||||
}
|
||||
|
||||
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
|
||||
self.read_channel(channel.channel())
|
||||
}
|
||||
|
||||
fn read_channel(&mut self, channel: u8) -> u16 {
|
||||
// Configure ADC
|
||||
let channel = pin.channel();
|
||||
|
||||
// Select channel
|
||||
T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
|
||||
@ -199,9 +187,7 @@ where
|
||||
// Configure channel
|
||||
Self::set_channel_sample_time(channel, self.sample_time);
|
||||
|
||||
let val = self.convert();
|
||||
|
||||
val
|
||||
self.convert()
|
||||
}
|
||||
|
||||
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
@ -216,6 +202,10 @@ where
|
||||
|
||||
impl<'d, T: Instance> Drop for Adc<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(false);
|
||||
});
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use crate::{peripherals, Peripheral};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Curstom Errors
|
||||
/// Custom Errors
|
||||
pub enum Error {
|
||||
UnconfiguredChannel,
|
||||
InvalidValue,
|
||||
@ -564,7 +564,7 @@ pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
||||
foreach_peripheral!(
|
||||
(dac, $inst:ident) => {
|
||||
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
|
||||
#[cfg(rcc_h7)]
|
||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 })
|
||||
@ -590,7 +590,7 @@ foreach_peripheral!(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rcc_h7)]
|
||||
#[cfg(any(rcc_h7, rcc_h7rm0433))]
|
||||
impl crate::rcc::RccPeripheral for peripherals::$inst {}
|
||||
|
||||
impl crate::dac::sealed::Instance for peripherals::$inst {
|
||||
|
@ -2,10 +2,9 @@
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::sync::atomic::{fence, AtomicUsize, Ordering};
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
use atomic_polyfill::AtomicUsize;
|
||||
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
@ -127,7 +126,13 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index
|
||||
} else if isr.tcif(channel_num) && cr.read().tcie() {
|
||||
// Acknowledge transfer complete interrupt
|
||||
dma.ifcr().write(|w| w.set_tcif(channel_num, true));
|
||||
#[cfg(not(armv6m))]
|
||||
STATE.complete_count[index].fetch_add(1, Ordering::Release);
|
||||
#[cfg(armv6m)]
|
||||
critical_section::with(|_| {
|
||||
let x = STATE.complete_count[index].load(Ordering::Relaxed);
|
||||
STATE.complete_count[index].store(x + 1, Ordering::Release);
|
||||
})
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -391,7 +396,14 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
|
||||
}
|
||||
|
||||
fn reset_complete_count(&mut self) -> usize {
|
||||
STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel)
|
||||
#[cfg(not(armv6m))]
|
||||
return STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel);
|
||||
#[cfg(armv6m)]
|
||||
return critical_section::with(|_| {
|
||||
let x = STATE.complete_count[self.0.index()].load(Ordering::Acquire);
|
||||
STATE.complete_count[self.0.index()].store(0, Ordering::Release);
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
fn set_waker(&mut self, waker: &Waker) {
|
||||
|
@ -41,39 +41,40 @@ mod phy_consts {
|
||||
}
|
||||
use self::phy_consts::*;
|
||||
|
||||
/// Generic SMI Ethernet PHY
|
||||
/// Generic SMI Ethernet PHY implementation
|
||||
pub struct GenericSMI {
|
||||
phy_addr: u8,
|
||||
#[cfg(feature = "time")]
|
||||
poll_interval: Duration,
|
||||
#[cfg(not(feature = "time"))]
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl GenericSMI {
|
||||
pub fn new() -> Self {
|
||||
/// Construct the PHY. It assumes the address `phy_addr` in the SMI communication
|
||||
pub fn new(phy_addr: u8) -> Self {
|
||||
Self {
|
||||
phy_addr,
|
||||
#[cfg(feature = "time")]
|
||||
poll_interval: Duration::from_millis(500),
|
||||
#[cfg(not(feature = "time"))]
|
||||
_private: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl PHY for GenericSMI {
|
||||
/// Reset PHY and wait for it to come out of reset.
|
||||
fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) {
|
||||
sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_RESET);
|
||||
while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
|
||||
sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET);
|
||||
while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
|
||||
}
|
||||
|
||||
/// PHY initialisation.
|
||||
fn phy_init<S: StationManagement>(&mut self, sm: &mut S) {
|
||||
// Clear WU CSR
|
||||
self.smi_write_ext(sm, PHY_REG_WUCSR, 0);
|
||||
|
||||
// Enable auto-negotiation
|
||||
sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M);
|
||||
sm.smi_write(
|
||||
self.phy_addr,
|
||||
PHY_REG_BCR,
|
||||
PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M,
|
||||
);
|
||||
}
|
||||
|
||||
fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool {
|
||||
@ -83,7 +84,7 @@ unsafe impl PHY for GenericSMI {
|
||||
#[cfg(feature = "time")]
|
||||
let _ = Timer::after(self.poll_interval).poll_unpin(cx);
|
||||
|
||||
let bsr = sm.smi_read(PHY_REG_BSR);
|
||||
let bsr = sm.smi_read(self.phy_addr, PHY_REG_BSR);
|
||||
|
||||
// No link without autonegotiate
|
||||
if bsr & PHY_REG_BSR_ANDONE == 0 {
|
||||
@ -108,9 +109,9 @@ impl GenericSMI {
|
||||
|
||||
// Writes a value to an extended PHY register in MMD address space
|
||||
fn smi_write_ext<S: StationManagement>(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) {
|
||||
sm.smi_write(PHY_REG_CTL, 0x0003); // set address
|
||||
sm.smi_write(PHY_REG_ADDAR, reg_addr);
|
||||
sm.smi_write(PHY_REG_CTL, 0x4003); // set data
|
||||
sm.smi_write(PHY_REG_ADDAR, reg_data);
|
||||
sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address
|
||||
sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr);
|
||||
sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data
|
||||
sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data);
|
||||
}
|
||||
}
|
||||
|
@ -134,9 +134,9 @@ impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> {
|
||||
/// The methods cannot move out of self
|
||||
pub unsafe trait StationManagement {
|
||||
/// Read a register over SMI.
|
||||
fn smi_read(&mut self, reg: u8) -> u16;
|
||||
fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16;
|
||||
/// Write a register over SMI.
|
||||
fn smi_write(&mut self, reg: u8, val: u16);
|
||||
fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16);
|
||||
}
|
||||
|
||||
/// Traits for an Ethernet PHY
|
||||
|
@ -107,7 +107,6 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
tx_en: impl Peripheral<P = impl TXEnPin<T>> + 'd,
|
||||
phy: P,
|
||||
mac_addr: [u8; 6],
|
||||
phy_addr: u8,
|
||||
) -> Self {
|
||||
into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
|
||||
@ -129,7 +128,6 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
|
||||
#[cfg(any(eth_v1b, eth_v1c))]
|
||||
critical_section::with(|_| {
|
||||
RCC.apb2enr().modify(|w| w.set_syscfgen(true));
|
||||
RCC.ahb1enr().modify(|w| {
|
||||
w.set_ethen(true);
|
||||
w.set_ethtxen(true);
|
||||
@ -228,7 +226,6 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
station_management: EthernetStationManagement {
|
||||
peri: PhantomData,
|
||||
clock_range: clock_range,
|
||||
phy_addr: phy_addr,
|
||||
},
|
||||
mac_addr,
|
||||
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
|
||||
@ -272,15 +269,14 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
pub struct EthernetStationManagement<T: Instance> {
|
||||
peri: PhantomData<T>,
|
||||
clock_range: Cr,
|
||||
phy_addr: u8,
|
||||
}
|
||||
|
||||
unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
fn smi_read(&mut self, reg: u8) -> u16 {
|
||||
fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
|
||||
let mac = ETH.ethernet_mac();
|
||||
|
||||
mac.macmiiar().modify(|w| {
|
||||
w.set_pa(self.phy_addr);
|
||||
w.set_pa(phy_addr);
|
||||
w.set_mr(reg);
|
||||
w.set_mw(Mw::READ); // read operation
|
||||
w.set_cr(self.clock_range);
|
||||
@ -290,12 +286,12 @@ unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
mac.macmiidr().read().md()
|
||||
}
|
||||
|
||||
fn smi_write(&mut self, reg: u8, val: u16) {
|
||||
fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
|
||||
let mac = ETH.ethernet_mac();
|
||||
|
||||
mac.macmiidr().write(|w| w.set_md(val));
|
||||
mac.macmiiar().modify(|w| {
|
||||
w.set_pa(self.phy_addr);
|
||||
w.set_pa(phy_addr);
|
||||
w.set_mr(reg);
|
||||
w.set_mw(Mw::WRITE); // write
|
||||
w.set_cr(self.clock_range);
|
||||
|
@ -34,8 +34,6 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandl
|
||||
}
|
||||
}
|
||||
|
||||
const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet
|
||||
|
||||
pub struct Ethernet<'d, T: Instance, P: PHY> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
pub(crate) tx: TDesRing<'d>,
|
||||
@ -73,14 +71,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
tx_en: impl Peripheral<P = impl TXEnPin<T>> + 'd,
|
||||
phy: P,
|
||||
mac_addr: [u8; 6],
|
||||
phy_addr: u8,
|
||||
) -> Self {
|
||||
into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
|
||||
// Enable the necessary Clocks
|
||||
#[cfg(not(rcc_h5))]
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true));
|
||||
crate::pac::RCC.ahb1enr().modify(|w| {
|
||||
w.set_eth1macen(true);
|
||||
w.set_eth1txen(true);
|
||||
@ -102,9 +98,9 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
});
|
||||
|
||||
// RMII
|
||||
crate::pac::SBS
|
||||
crate::pac::SYSCFG
|
||||
.pmcr()
|
||||
.modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4));
|
||||
.modify(|w| w.set_eth_sel_phy(crate::pac::syscfg::vals::EthSelPhy::B_0X4));
|
||||
});
|
||||
|
||||
config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
@ -164,7 +160,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ?
|
||||
dma.dmacrx_cr().modify(|w| {
|
||||
w.set_rxpbl(1); // 32 ?
|
||||
w.set_rbsz(MTU as u16);
|
||||
w.set_rbsz(RX_BUFFER_SIZE as u16);
|
||||
});
|
||||
|
||||
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
|
||||
@ -205,7 +201,6 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
station_management: EthernetStationManagement {
|
||||
peri: PhantomData,
|
||||
clock_range: clock_range,
|
||||
phy_addr: phy_addr,
|
||||
},
|
||||
mac_addr,
|
||||
};
|
||||
@ -245,15 +240,14 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
pub struct EthernetStationManagement<T: Instance> {
|
||||
peri: PhantomData<T>,
|
||||
clock_range: u8,
|
||||
phy_addr: u8,
|
||||
}
|
||||
|
||||
unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
fn smi_read(&mut self, reg: u8) -> u16 {
|
||||
fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
|
||||
let mac = ETH.ethernet_mac();
|
||||
|
||||
mac.macmdioar().modify(|w| {
|
||||
w.set_pa(self.phy_addr);
|
||||
w.set_pa(phy_addr);
|
||||
w.set_rda(reg);
|
||||
w.set_goc(0b11); // read
|
||||
w.set_cr(self.clock_range);
|
||||
@ -263,12 +257,12 @@ unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
mac.macmdiodr().read().md()
|
||||
}
|
||||
|
||||
fn smi_write(&mut self, reg: u8, val: u16) {
|
||||
fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
|
||||
let mac = ETH.ethernet_mac();
|
||||
|
||||
mac.macmdiodr().write(|w| w.set_md(val));
|
||||
mac.macmdioar().modify(|w| {
|
||||
w.set_pa(self.phy_addr);
|
||||
w.set_pa(phy_addr);
|
||||
w.set_rda(reg);
|
||||
w.set_goc(0b01); // write
|
||||
w.set_cr(self.clock_range);
|
||||
|
@ -6,7 +6,7 @@ use core::task::{Context, Poll};
|
||||
use embassy_hal_internal::impl_peripheral;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::gpio::{AnyPin, Input, Pin as GpioPin};
|
||||
use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin};
|
||||
use crate::pac::exti::regs::Lines;
|
||||
use crate::pac::EXTI;
|
||||
use crate::{interrupt, pac, peripherals, Peripheral};
|
||||
@ -39,6 +39,9 @@ fn exticr_regs() -> pac::afio::Afio {
|
||||
}
|
||||
|
||||
pub unsafe fn on_irq() {
|
||||
#[cfg(feature = "low-power")]
|
||||
crate::low_power::on_wakeup_irq();
|
||||
|
||||
#[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
|
||||
let bits = EXTI.pr(0).read().0;
|
||||
#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
|
||||
@ -98,6 +101,10 @@ impl<'d, T: GpioPin> ExtiInput<'d, T> {
|
||||
self.pin.is_low()
|
||||
}
|
||||
|
||||
pub fn get_level(&self) -> Level {
|
||||
self.pin.get_level()
|
||||
}
|
||||
|
||||
pub async fn wait_for_high<'a>(&'a mut self) {
|
||||
let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false);
|
||||
if self.is_high() {
|
||||
@ -364,9 +371,4 @@ pub(crate) unsafe fn init() {
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
|
||||
foreach_exti_irq!(enable_irq);
|
||||
|
||||
#[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1, exti_h5, exti_h50)))]
|
||||
<crate::peripherals::SYSCFG as crate::rcc::sealed::RccPeripheral>::enable();
|
||||
#[cfg(stm32f1)]
|
||||
<crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable();
|
||||
}
|
||||
|
@ -19,8 +19,10 @@ pub(crate) unsafe fn lock() {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unlock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_fkeyr(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
|
||||
if pac::FLASH.cr().read().lock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_fkeyr(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_blocking_write() {
|
||||
|
@ -19,8 +19,10 @@ pub(crate) unsafe fn lock() {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unlock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_fkeyr(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
|
||||
if pac::FLASH.cr().read().lock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_fkeyr(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_blocking_write() {
|
||||
|
@ -228,8 +228,10 @@ pub(crate) unsafe fn lock() {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unlock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_key(0x45670123));
|
||||
pac::FLASH.keyr().write(|w| w.set_key(0xCDEF89AB));
|
||||
if pac::FLASH.cr().read().lock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_key(0x45670123));
|
||||
pac::FLASH.keyr().write(|w| w.set_key(0xCDEF89AB));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_write() {
|
||||
|
@ -19,8 +19,10 @@ pub(crate) unsafe fn lock() {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unlock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
|
||||
if pac::FLASH.cr().read().lock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_blocking_write() {
|
||||
|
@ -24,8 +24,10 @@ pub(crate) unsafe fn unlock() {
|
||||
while pac::FLASH.sr().read().bsy() {}
|
||||
|
||||
// Unlock flash
|
||||
pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||
if pac::FLASH.cr().read().lock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_blocking_write() {
|
||||
|
@ -26,11 +26,15 @@ pub(crate) unsafe fn lock() {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unlock() {
|
||||
pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||
pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||
if pac::FLASH.bank(0).cr().read().lock() {
|
||||
pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||
pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||
}
|
||||
if is_dual_bank() {
|
||||
pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||
pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||
if pac::FLASH.bank(1).cr().read().lock() {
|
||||
pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||
pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,17 +28,23 @@ pub(crate) unsafe fn lock() {
|
||||
pub(crate) unsafe fn unlock() {
|
||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||
{
|
||||
pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||
if pac::FLASH.cr().read().lock() {
|
||||
pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||
pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(flash_l0, flash_l1))]
|
||||
{
|
||||
pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
|
||||
pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
|
||||
if pac::FLASH.pecr().read().pelock() {
|
||||
pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
|
||||
pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
|
||||
}
|
||||
|
||||
pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
|
||||
pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
|
||||
if pac::FLASH.pecr().read().prglock() {
|
||||
pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
|
||||
pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -758,6 +758,9 @@ foreach_pin!(
|
||||
);
|
||||
|
||||
pub(crate) unsafe fn init() {
|
||||
#[cfg(afio)]
|
||||
<crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable();
|
||||
|
||||
crate::_generated::init_gpio();
|
||||
}
|
||||
|
||||
@ -971,6 +974,18 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> InputPin for OutputOpenDrain<'d, T> {
|
||||
#[inline]
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_low())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> OutputPin for OutputOpenDrain<'d, T> {
|
||||
#[inline]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
|
@ -78,12 +78,76 @@ pub(crate) mod sealed {
|
||||
pub trait Instance: RccPeripheral {
|
||||
fn regs() -> crate::pac::hrtim::Hrtim;
|
||||
|
||||
fn set_master_frequency(frequency: Hertz);
|
||||
fn set_master_frequency(frequency: Hertz) {
|
||||
let f = frequency.0;
|
||||
#[cfg(not(stm32f334))]
|
||||
let timer_f = Self::frequency().0;
|
||||
#[cfg(stm32f334)]
|
||||
let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(Self::frequency()).0;
|
||||
|
||||
fn set_channel_frequency(channnel: usize, frequency: Hertz);
|
||||
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
} else {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let timer_f = 32 * (timer_f / psc as u32);
|
||||
let per: u16 = (timer_f / f) as u16;
|
||||
|
||||
let regs = Self::regs();
|
||||
|
||||
regs.mcr().modify(|w| w.set_ckpsc(psc.into()));
|
||||
regs.mper().modify(|w| w.set_mper(per));
|
||||
}
|
||||
|
||||
fn set_channel_frequency(channel: usize, frequency: Hertz) {
|
||||
let f = frequency.0;
|
||||
#[cfg(not(stm32f334))]
|
||||
let timer_f = Self::frequency().0;
|
||||
#[cfg(stm32f334)]
|
||||
let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(Self::frequency()).0;
|
||||
|
||||
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
} else {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let timer_f = 32 * (timer_f / psc as u32);
|
||||
let per: u16 = (timer_f / f) as u16;
|
||||
|
||||
let regs = Self::regs();
|
||||
|
||||
regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into()));
|
||||
regs.tim(channel).per().modify(|w| w.set_per(per));
|
||||
}
|
||||
|
||||
/// Set the dead time as a proportion of max_duty
|
||||
fn set_channel_dead_time(channnel: usize, dead_time: u16);
|
||||
|
||||
fn set_channel_dead_time(channel: usize, dead_time: u16) {
|
||||
let regs = Self::regs();
|
||||
|
||||
let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into();
|
||||
|
||||
// The dead-time base clock runs 4 times slower than the hrtim base clock
|
||||
// u9::MAX = 511
|
||||
let psc_min = (channel_psc as u32 * dead_time as u32) / (4 * 511);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
} else {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let dt_val = (psc as u32 * dead_time as u32) / (4 * channel_psc as u32);
|
||||
|
||||
regs.tim(channel).dt().modify(|w| {
|
||||
w.set_dtprsc(psc.into());
|
||||
w.set_dtf(dt_val as u16);
|
||||
w.set_dtr(dt_val as u16);
|
||||
});
|
||||
}
|
||||
|
||||
// fn enable_outputs(enable: bool);
|
||||
//
|
||||
@ -99,84 +163,6 @@ foreach_interrupt! {
|
||||
fn regs() -> crate::pac::hrtim::Hrtim {
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
||||
fn set_master_frequency(frequency: Hertz) {
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
|
||||
let f = frequency.0;
|
||||
#[cfg(not(stm32f334))]
|
||||
let timer_f = Self::frequency().0;
|
||||
#[cfg(stm32f334)]
|
||||
let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(
|
||||
Self::frequency()
|
||||
).0;
|
||||
|
||||
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
} else {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let timer_f = 32 * (timer_f / psc as u32);
|
||||
let per: u16 = (timer_f / f) as u16;
|
||||
|
||||
let regs = Self::regs();
|
||||
|
||||
regs.mcr().modify(|w| w.set_ckpsc(psc.into()));
|
||||
regs.mper().modify(|w| w.set_mper(per));
|
||||
}
|
||||
|
||||
fn set_channel_frequency(channel: usize, frequency: Hertz) {
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
|
||||
let f = frequency.0;
|
||||
#[cfg(not(stm32f334))]
|
||||
let timer_f = Self::frequency().0;
|
||||
#[cfg(stm32f334)]
|
||||
let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(
|
||||
Self::frequency()
|
||||
).0;
|
||||
|
||||
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
} else {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let timer_f = 32 * (timer_f / psc as u32);
|
||||
let per: u16 = (timer_f / f) as u16;
|
||||
|
||||
let regs = Self::regs();
|
||||
|
||||
regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into()));
|
||||
regs.tim(channel).per().modify(|w| w.set_per(per));
|
||||
}
|
||||
|
||||
fn set_channel_dead_time(channel: usize, dead_time: u16) {
|
||||
|
||||
let regs = Self::regs();
|
||||
|
||||
let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into();
|
||||
|
||||
// The dead-time base clock runs 4 times slower than the hrtim base clock
|
||||
// u9::MAX = 511
|
||||
let psc_min = (channel_psc as u32 * dead_time as u32) / (4 * 511);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
} else {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let dt_val = (psc as u32 * dead_time as u32) / (4 * channel_psc as u32);
|
||||
|
||||
regs.tim(channel).dt().modify(|w| {
|
||||
w.set_dtprsc(psc.into());
|
||||
w.set_dtf(dt_val as u16);
|
||||
w.set_dtr(dt_val as u16);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for crate::peripherals::$inst {
|
||||
|
@ -14,7 +14,7 @@ pub use timeout::*;
|
||||
|
||||
use crate::peripherals;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
Bus,
|
||||
|
@ -518,7 +518,8 @@ impl Timings {
|
||||
|
||||
impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
||||
type Config = Hertz;
|
||||
fn set_config(&mut self, config: &Self::Config) {
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
||||
let timings = Timings::new(T::frequency(), *config);
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_freq(timings.freq);
|
||||
@ -531,5 +532,7 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
||||
T::regs().trise().modify(|reg| {
|
||||
reg.set_trise(timings.trise);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1075,7 +1075,8 @@ mod eha {
|
||||
|
||||
impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
||||
type Config = Hertz;
|
||||
fn set_config(&mut self, config: &Self::Config) {
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
||||
let timings = Timings::new(T::frequency(), *config);
|
||||
T::regs().timingr().write(|reg| {
|
||||
reg.set_presc(timings.prescale);
|
||||
@ -1084,5 +1085,7 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
||||
reg.set_sdadel(timings.sdadel);
|
||||
reg.set_scldel(timings.scldel);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
||||
|
||||
//! ## Feature flags
|
||||
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
|
||||
@ -49,12 +49,16 @@ pub mod i2s;
|
||||
pub mod ipcc;
|
||||
#[cfg(feature = "low-power")]
|
||||
pub mod low_power;
|
||||
#[cfg(opamp)]
|
||||
pub mod opamp;
|
||||
#[cfg(quadspi)]
|
||||
pub mod qspi;
|
||||
#[cfg(rng)]
|
||||
pub mod rng;
|
||||
#[cfg(all(rtc, not(rtc_v1)))]
|
||||
pub mod rtc;
|
||||
#[cfg(sai)]
|
||||
pub mod sai;
|
||||
#[cfg(sdmmc)]
|
||||
pub mod sdmmc;
|
||||
#[cfg(spi)]
|
||||
@ -88,6 +92,7 @@ pub use crate::_generated::interrupt;
|
||||
#[macro_export]
|
||||
macro_rules! bind_interrupts {
|
||||
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
|
||||
#[derive(Copy, Clone)]
|
||||
$vis struct $name;
|
||||
|
||||
$(
|
||||
@ -117,6 +122,7 @@ pub(crate) use stm32_metapac as pac;
|
||||
use crate::interrupt::Priority;
|
||||
#[cfg(feature = "rt")]
|
||||
pub use crate::pac::NVIC_PRIO_BITS;
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
@ -154,7 +160,7 @@ pub fn init(config: Config) -> Peripherals {
|
||||
#[cfg(dbgmcu)]
|
||||
if config.enable_debug_during_sleep {
|
||||
crate::pac::DBGMCU.cr().modify(|cr| {
|
||||
#[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5))]
|
||||
#[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5, dbgmcu_wba))]
|
||||
{
|
||||
cr.set_dbg_stop(true);
|
||||
cr.set_dbg_standby(true);
|
||||
@ -179,7 +185,26 @@ pub fn init(config: Config) -> Peripherals {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(any(stm32f1, stm32wb, stm32wl)))]
|
||||
peripherals::SYSCFG::enable();
|
||||
#[cfg(not(any(stm32h5, stm32h7, stm32wb, stm32wl)))]
|
||||
peripherals::PWR::enable();
|
||||
#[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7)))]
|
||||
peripherals::FLASH::enable();
|
||||
|
||||
unsafe {
|
||||
#[cfg(feature = "_split-pins-enabled")]
|
||||
crate::pac::SYSCFG.pmcr().modify(|pmcr| {
|
||||
#[cfg(feature = "split-pa0")]
|
||||
pmcr.set_pa0so(true);
|
||||
#[cfg(feature = "split-pa1")]
|
||||
pmcr.set_pa1so(true);
|
||||
#[cfg(feature = "split-pc2")]
|
||||
pmcr.set_pc2so(true);
|
||||
#[cfg(feature = "split-pc3")]
|
||||
pmcr.set_pc3so(true);
|
||||
});
|
||||
|
||||
gpio::init();
|
||||
dma::init(
|
||||
#[cfg(bdma)]
|
||||
@ -200,7 +225,9 @@ pub fn init(config: Config) -> Peripherals {
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
while !crate::rcc::low_power_ready() {
|
||||
crate::rcc::clock_refcount_sub();
|
||||
critical_section::with(|cs| {
|
||||
crate::rcc::clock_refcount_sub(cs);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
use core::arch::asm;
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
use cortex_m::peripheral::SCB;
|
||||
use embassy_executor::*;
|
||||
|
||||
use crate::interrupt;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::rcc::low_power_ready;
|
||||
use crate::time_driver::{get_driver, RtcDriver};
|
||||
|
||||
@ -19,36 +19,20 @@ foreach_interrupt! {
|
||||
(RTC, rtc, $block:ident, WKUP, $irq:ident) => {
|
||||
#[interrupt]
|
||||
unsafe fn $irq() {
|
||||
unsafe { EXECUTOR.as_mut().unwrap() }.on_wakeup_irq();
|
||||
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// pub fn timer_driver_pause_time() {
|
||||
// pause_time();
|
||||
// }
|
||||
#[allow(dead_code)]
|
||||
pub(crate) unsafe fn on_wakeup_irq() {
|
||||
EXECUTOR.as_mut().unwrap().on_wakeup_irq();
|
||||
}
|
||||
|
||||
pub fn stop_with_rtc(rtc: &'static Rtc) {
|
||||
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
|
||||
}
|
||||
|
||||
// pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) {
|
||||
// let rtc_instant = unsafe { EXECUTOR.as_mut().unwrap() }
|
||||
// .rtc
|
||||
// .unwrap()
|
||||
// .start_wakeup_alarm(requested_duration);
|
||||
//
|
||||
// unsafe { EXECUTOR.as_mut().unwrap() }.last_stop = Some(rtc_instant);
|
||||
// }
|
||||
//
|
||||
// pub fn set_sleepdeep() {
|
||||
// unsafe { EXECUTOR.as_mut().unwrap() }.scb.set_sleepdeep();
|
||||
// }
|
||||
//
|
||||
// pub fn stop_wakeup_alarm() -> RtcInstant {
|
||||
// unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm()
|
||||
// }
|
||||
|
||||
/// Thread mode executor, using WFE/SEV.
|
||||
///
|
||||
/// This is the simplest and most common kind of executor. It runs on
|
||||
@ -84,39 +68,31 @@ impl Executor {
|
||||
}
|
||||
|
||||
unsafe fn on_wakeup_irq(&mut self) {
|
||||
trace!("low power: on wakeup irq");
|
||||
|
||||
self.time_driver.resume_time();
|
||||
trace!("low power: resume time");
|
||||
trace!("low power: resume");
|
||||
}
|
||||
|
||||
pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) {
|
||||
trace!("low power: stop with rtc configured");
|
||||
|
||||
self.time_driver.set_rtc(rtc);
|
||||
|
||||
crate::interrupt::typelevel::RTC_WKUP::unpend();
|
||||
unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
|
||||
|
||||
rtc.enable_wakeup_line();
|
||||
|
||||
trace!("low power: stop with rtc configured");
|
||||
}
|
||||
|
||||
fn configure_pwr(&mut self) {
|
||||
trace!("low power: configure_pwr");
|
||||
|
||||
self.scb.clear_sleepdeep();
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
if !low_power_ready() {
|
||||
trace!("low power: configure_pwr: low power not ready");
|
||||
return;
|
||||
trace!("low power: not ready to stop");
|
||||
} else if self.time_driver.pause_time().is_err() {
|
||||
trace!("low power: failed to pause time");
|
||||
} else {
|
||||
trace!("low power: stop");
|
||||
self.scb.set_sleepdeep();
|
||||
}
|
||||
|
||||
if self.time_driver.pause_time().is_err() {
|
||||
trace!("low power: configure_pwr: time driver failed to pause");
|
||||
return;
|
||||
}
|
||||
|
||||
trace!("low power: enter stop...");
|
||||
self.scb.set_sleepdeep();
|
||||
}
|
||||
|
||||
/// Run the executor.
|
||||
|
159
embassy-stm32/src/opamp.rs
Normal file
159
embassy-stm32/src/opamp.rs
Normal file
@ -0,0 +1,159 @@
|
||||
#![macro_use]
|
||||
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::Peripheral;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum OpAmpGain {
|
||||
Mul1,
|
||||
Mul2,
|
||||
Mul4,
|
||||
Mul8,
|
||||
Mul16,
|
||||
}
|
||||
|
||||
pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin<T>> {
|
||||
_inner: &'d OpAmp<'d, T>,
|
||||
_input: &'p mut P,
|
||||
}
|
||||
|
||||
pub struct OpAmp<'d, T: Instance> {
|
||||
_inner: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> OpAmp<'d, T> {
|
||||
pub fn new(opamp: impl Peripheral<P = T> + 'd) -> Self {
|
||||
Self::new_inner(opamp)
|
||||
}
|
||||
|
||||
fn new_inner(opamp: impl Peripheral<P = T> + 'd) -> Self {
|
||||
into_ref!(opamp);
|
||||
|
||||
#[cfg(opamp_f3)]
|
||||
T::regs().opampcsr().modify(|w| {
|
||||
w.set_opampen(true);
|
||||
});
|
||||
|
||||
#[cfg(opamp_g4)]
|
||||
T::regs().opamp_csr().modify(|w| {
|
||||
w.set_opaen(true);
|
||||
});
|
||||
|
||||
Self { _inner: opamp }
|
||||
}
|
||||
|
||||
pub fn buffer_for<'a, 'b, P>(&'a mut self, pin: &'b mut P, gain: OpAmpGain) -> OpAmpOutput<'a, 'b, T, P>
|
||||
where
|
||||
P: NonInvertingPin<T>,
|
||||
{
|
||||
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),
|
||||
};
|
||||
|
||||
#[cfg(opamp_f3)]
|
||||
T::regs().opampcsr().modify(|w| {
|
||||
w.set_vp_sel(pin.channel());
|
||||
w.set_vm_sel(vm_sel);
|
||||
w.set_pga_gain(pga_gain);
|
||||
});
|
||||
|
||||
#[cfg(opamp_g4)]
|
||||
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));
|
||||
});
|
||||
|
||||
OpAmpOutput {
|
||||
_inner: self,
|
||||
_input: pin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + 'static {}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> crate::pac::opamp::Opamp;
|
||||
}
|
||||
|
||||
pub trait NonInvertingPin<T: Instance> {
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
|
||||
pub trait InvertingPin<T: Instance> {
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {}
|
||||
|
||||
pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
|
||||
|
||||
#[cfg(opamp_f3)]
|
||||
macro_rules! impl_opamp_output {
|
||||
($inst:ident, $adc:ident, $ch:expr) => {
|
||||
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
|
||||
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
||||
{
|
||||
fn channel(&self) -> u8 {
|
||||
$ch
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
|
||||
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(opamp_f3)]
|
||||
foreach_peripheral!(
|
||||
(opamp, OPAMP1) => {
|
||||
impl_opamp_output!(OPAMP1, ADC1, 3);
|
||||
};
|
||||
(opamp, OPAMP2) => {
|
||||
impl_opamp_output!(OPAMP2, ADC2, 3);
|
||||
};
|
||||
(opamp, OPAMP3) => {
|
||||
impl_opamp_output!(OPAMP3, ADC3, 1);
|
||||
};
|
||||
(opamp, OPAMP4) => {
|
||||
impl_opamp_output!(OPAMP4, ADC4, 3);
|
||||
};
|
||||
);
|
||||
|
||||
foreach_peripheral! {
|
||||
(opamp, $inst:ident) => {
|
||||
impl sealed::Instance for crate::peripherals::$inst {
|
||||
fn regs() -> crate::pac::opamp::Opamp {
|
||||
crate::pac::$inst
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for crate::peripherals::$inst {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_opamp_pin {
|
||||
($inst:ident, $pin:ident, $ch:expr) => {
|
||||
impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||
impl crate::opamp::sealed::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {
|
||||
fn channel(&self) -> u8 {
|
||||
$ch
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -38,6 +38,22 @@ impl Into<u8> for QspiWidth {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum FlashSelection {
|
||||
Flash1,
|
||||
Flash2,
|
||||
}
|
||||
|
||||
impl Into<bool> for FlashSelection {
|
||||
fn into(self) -> bool {
|
||||
match self {
|
||||
FlashSelection::Flash1 => false,
|
||||
FlashSelection::Flash2 => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum MemorySize {
|
||||
_1KiB,
|
||||
|
@ -7,7 +7,7 @@ use enums::*;
|
||||
|
||||
use crate::dma::Transfer;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::gpio::{AnyPin, Pull};
|
||||
use crate::pac::quadspi::Quadspi as Regs;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
@ -83,30 +83,30 @@ pub struct Qspi<'d, T: Instance, Dma> {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
pub fn new(
|
||||
pub fn new_bk1(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl BK1D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl BK1D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl BK1D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl BK1D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl BK1NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, d0, d1, d2, d3, sck, nss);
|
||||
|
||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||
sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
|
||||
nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up);
|
||||
nss.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
|
||||
d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
d0.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
|
||||
d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
d1.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
|
||||
d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
d2.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
|
||||
d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
d3.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
|
||||
Self::new_inner(
|
||||
@ -119,6 +119,47 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
Some(nss.map_into()),
|
||||
dma,
|
||||
config,
|
||||
FlashSelection::Flash2,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_bk2(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl BK2D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl BK2D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl BK2D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl BK2D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl BK2NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, d0, d1, d2, d3, sck, nss);
|
||||
|
||||
sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up);
|
||||
nss.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
d0.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
d1.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
d2.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None);
|
||||
d3.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
|
||||
Self::new_inner(
|
||||
peri,
|
||||
Some(d0.map_into()),
|
||||
Some(d1.map_into()),
|
||||
Some(d2.map_into()),
|
||||
Some(d3.map_into()),
|
||||
Some(sck.map_into()),
|
||||
Some(nss.map_into()),
|
||||
dma,
|
||||
config,
|
||||
FlashSelection::Flash2,
|
||||
)
|
||||
}
|
||||
|
||||
@ -132,22 +173,40 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
fsel: FlashSelection,
|
||||
) -> Self {
|
||||
into_ref!(peri, dma);
|
||||
|
||||
T::enable();
|
||||
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
|
||||
T::reset();
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
T::REGS.cr().write(|w| {
|
||||
w.set_prescaler(config.prescaler);
|
||||
#[cfg(stm32h7)]
|
||||
{
|
||||
use stm32_metapac::quadspi::regs::Cr;
|
||||
// Apply precautionary steps according to the errata...
|
||||
T::REGS.cr().write_value(Cr(0));
|
||||
while T::REGS.sr().read().busy() {}
|
||||
T::REGS.cr().write_value(Cr(0xFF000001));
|
||||
T::REGS.ccr().write(|w| w.set_frcm(true));
|
||||
T::REGS.ccr().write(|w| w.set_frcm(true));
|
||||
T::REGS.cr().write_value(Cr(0));
|
||||
while T::REGS.sr().read().busy() {}
|
||||
}
|
||||
|
||||
T::REGS.cr().modify(|w| {
|
||||
w.set_en(true);
|
||||
//w.set_tcen(false);
|
||||
w.set_sshift(false);
|
||||
w.set_fthres(config.fifo_threshold.into());
|
||||
w.set_prescaler(config.prescaler);
|
||||
w.set_fsel(fsel.into());
|
||||
});
|
||||
T::REGS.dcr().write(|w| {
|
||||
T::REGS.dcr().modify(|w| {
|
||||
w.set_fsize(config.memory_size.into());
|
||||
w.set_csht(config.cs_high_time.into());
|
||||
w.set_ckmode(false);
|
||||
w.set_ckmode(true);
|
||||
});
|
||||
|
||||
Self {
|
||||
@ -164,6 +223,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
}
|
||||
|
||||
pub fn command(&mut self, transaction: TransferConfig) {
|
||||
#[cfg(not(stm32h7))]
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
@ -172,6 +232,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
||||
#[cfg(not(stm32h7))]
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
@ -195,7 +256,10 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
||||
// STM32H7 does not have dmaen
|
||||
#[cfg(not(stm32h7))]
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
@ -238,6 +302,8 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
)
|
||||
};
|
||||
|
||||
// STM32H7 does not have dmaen
|
||||
#[cfg(not(stm32h7))]
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
@ -264,6 +330,8 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
)
|
||||
};
|
||||
|
||||
// STM32H7 does not have dmaen
|
||||
#[cfg(not(stm32h7))]
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
@ -313,11 +381,17 @@ pub(crate) mod sealed {
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||
|
||||
pin_trait!(SckPin, Instance);
|
||||
pin_trait!(D0Pin, Instance);
|
||||
pin_trait!(D1Pin, Instance);
|
||||
pin_trait!(D2Pin, Instance);
|
||||
pin_trait!(D3Pin, Instance);
|
||||
pin_trait!(NSSPin, Instance);
|
||||
pin_trait!(BK1D0Pin, Instance);
|
||||
pin_trait!(BK1D1Pin, Instance);
|
||||
pin_trait!(BK1D2Pin, Instance);
|
||||
pin_trait!(BK1D3Pin, Instance);
|
||||
pin_trait!(BK1NSSPin, Instance);
|
||||
|
||||
pin_trait!(BK2D0Pin, Instance);
|
||||
pin_trait!(BK2D1Pin, Instance);
|
||||
pin_trait!(BK2D2Pin, Instance);
|
||||
pin_trait!(BK2D3Pin, Instance);
|
||||
pin_trait!(BK2NSSPin, Instance);
|
||||
|
||||
dma_trait!(QuadDma, Instance);
|
||||
|
||||
|
@ -1,199 +1,245 @@
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
use crate::pac::common::{Reg, RW};
|
||||
pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource;
|
||||
use crate::time::Hertz;
|
||||
|
||||
#[cfg(any(stm32f0, stm32f1, stm32f3))]
|
||||
pub const LSI_FREQ: Hertz = Hertz(40_000);
|
||||
#[cfg(not(any(stm32f0, stm32f1, stm32f3)))]
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum LseMode {
|
||||
Oscillator(LseDrive),
|
||||
Bypass,
|
||||
}
|
||||
|
||||
pub struct LseConfig {
|
||||
pub frequency: Hertz,
|
||||
pub mode: LseMode,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub enum LseDrive {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
Low = 0,
|
||||
MediumLow = 0x01,
|
||||
#[default]
|
||||
MediumHigh = 0x02,
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
High = 0x03,
|
||||
}
|
||||
|
||||
#[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
|
||||
// All families but these have the LSEDRV register
|
||||
#[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))]
|
||||
impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv {
|
||||
fn from(value: LseDrive) -> Self {
|
||||
use crate::pac::rcc::vals::Lsedrv;
|
||||
|
||||
match value {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
LseDrive::Low => Lsedrv::LOW,
|
||||
LseDrive::MediumLow => Lsedrv::MEDIUMLOW,
|
||||
LseDrive::MediumHigh => Lsedrv::MEDIUMHIGH,
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
LseDrive::High => Lsedrv::HIGH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum RtcClockSource {
|
||||
/// 00: No clock
|
||||
NoClock = 0b00,
|
||||
/// 01: LSE oscillator clock used as RTC clock
|
||||
LSE = 0b01,
|
||||
/// 10: LSI oscillator clock used as RTC clock
|
||||
LSI = 0b10,
|
||||
/// 11: HSE oscillator clock divided by 32 used as RTC clock
|
||||
HSE = 0b11,
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))]
|
||||
type Bdcr = crate::pac::rcc::regs::Bdcr;
|
||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||
type Bdcr = crate::pac::rcc::regs::Csr;
|
||||
#[cfg(any(stm32c0))]
|
||||
type Bdcr = crate::pac::rcc::regs::Csr1;
|
||||
|
||||
#[cfg(any(stm32c0))]
|
||||
fn unlock() {}
|
||||
|
||||
#[cfg(not(any(stm32c0)))]
|
||||
fn unlock() {
|
||||
#[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))]
|
||||
let cr = crate::pac::PWR.cr();
|
||||
#[cfg(not(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1, stm32u5, stm32h5, stm32wba)))]
|
||||
let cr = crate::pac::PWR.cr1();
|
||||
#[cfg(any(stm32u5, stm32h5, stm32wba))]
|
||||
let cr = crate::pac::PWR.dbpcr();
|
||||
|
||||
cr.modify(|w| w.set_dbp(true));
|
||||
while !cr.read().dbp() {}
|
||||
}
|
||||
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))]
|
||||
#[allow(dead_code)]
|
||||
type Bdcr = crate::pac::rcc::regs::Bdcr;
|
||||
fn bdcr() -> Reg<Bdcr, RW> {
|
||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||
return crate::pac::RCC.csr();
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))]
|
||||
return crate::pac::RCC.bdcr();
|
||||
#[cfg(any(stm32c0))]
|
||||
return crate::pac::RCC.csr1();
|
||||
}
|
||||
|
||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||
#[allow(dead_code)]
|
||||
type Bdcr = crate::pac::rcc::regs::Csr;
|
||||
pub struct LsConfig {
|
||||
pub rtc: RtcClockSource,
|
||||
pub lsi: bool,
|
||||
pub lse: Option<LseConfig>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct BackupDomain {}
|
||||
|
||||
impl BackupDomain {
|
||||
#[cfg(any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code, unused_variables)]
|
||||
fn modify<R>(f: impl FnOnce(&mut Bdcr) -> R) -> R {
|
||||
#[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
|
||||
let cr = crate::pac::PWR.cr();
|
||||
#[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
let cr = crate::pac::PWR.cr1();
|
||||
|
||||
// TODO: Missing from PAC for l0 and f0?
|
||||
#[cfg(not(any(rtc_v2f0, rtc_v2l0, rtc_v3u5)))]
|
||||
{
|
||||
cr.modify(|w| w.set_dbp(true));
|
||||
while !cr.read().dbp() {}
|
||||
impl LsConfig {
|
||||
pub const fn default_lse() -> Self {
|
||||
Self {
|
||||
rtc: RtcClockSource::LSE,
|
||||
lse: Some(LseConfig {
|
||||
frequency: Hertz(32_768),
|
||||
mode: LseMode::Oscillator(LseDrive::MediumHigh),
|
||||
}),
|
||||
lsi: false,
|
||||
}
|
||||
|
||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||
let cr = crate::pac::RCC.csr();
|
||||
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||
let cr = crate::pac::RCC.bdcr();
|
||||
|
||||
cr.modify(|w| f(w))
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code)]
|
||||
fn read() -> Bdcr {
|
||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||
let r = crate::pac::RCC.csr().read();
|
||||
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||
let r = crate::pac::RCC.bdcr().read();
|
||||
|
||||
r
|
||||
pub const fn default_lsi() -> Self {
|
||||
Self {
|
||||
rtc: RtcClockSource::LSI,
|
||||
lsi: true,
|
||||
lse: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code, unused_variables)]
|
||||
pub fn configure_ls(clock_source: RtcClockSource, lse_drive: Option<LseDrive>) {
|
||||
match clock_source {
|
||||
pub const fn off() -> Self {
|
||||
Self {
|
||||
rtc: RtcClockSource::NOCLOCK,
|
||||
lsi: false,
|
||||
lse: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LsConfig {
|
||||
fn default() -> Self {
|
||||
// on L5, just the fact that LSI is enabled makes things crash.
|
||||
// TODO: investigate.
|
||||
|
||||
#[cfg(not(stm32l5))]
|
||||
return Self::default_lsi();
|
||||
#[cfg(stm32l5)]
|
||||
return Self::off();
|
||||
}
|
||||
}
|
||||
|
||||
impl LsConfig {
|
||||
pub(crate) fn init(&self) -> Option<Hertz> {
|
||||
let rtc_clk = match self.rtc {
|
||||
RtcClockSource::LSI => {
|
||||
#[cfg(rtc_v3u5)]
|
||||
let csr = crate::pac::RCC.bdcr();
|
||||
|
||||
#[cfg(not(rtc_v3u5))]
|
||||
let csr = crate::pac::RCC.csr();
|
||||
|
||||
Self::modify(|_| {
|
||||
#[cfg(not(rtc_v2wb))]
|
||||
csr.modify(|w| w.set_lsion(true));
|
||||
|
||||
#[cfg(rtc_v2wb)]
|
||||
csr.modify(|w| w.set_lsi1on(true));
|
||||
});
|
||||
|
||||
#[cfg(not(rtc_v2wb))]
|
||||
while !csr.read().lsirdy() {}
|
||||
|
||||
#[cfg(rtc_v2wb)]
|
||||
while !csr.read().lsi1rdy() {}
|
||||
assert!(self.lsi);
|
||||
Some(LSI_FREQ)
|
||||
}
|
||||
RtcClockSource::LSE => {
|
||||
let lse_drive = lse_drive.unwrap_or_default();
|
||||
|
||||
Self::modify(|w| {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
|
||||
w.set_lsedrv(lse_drive.into());
|
||||
w.set_lseon(true);
|
||||
});
|
||||
|
||||
while !Self::read().lserdy() {}
|
||||
}
|
||||
_ => {}
|
||||
RtcClockSource::LSE => Some(self.lse.as_ref().unwrap().frequency),
|
||||
RtcClockSource::NOCLOCK => None,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
Self::configure_rtc(clock_source);
|
||||
}
|
||||
let (lse_en, lse_byp, lse_drv) = match &self.lse {
|
||||
Some(c) => match c.mode {
|
||||
LseMode::Oscillator(lse_drv) => (true, false, Some(lse_drv)),
|
||||
LseMode::Bypass => (true, true, None),
|
||||
},
|
||||
None => (false, false, None),
|
||||
};
|
||||
_ = lse_drv; // not all chips have it.
|
||||
|
||||
#[cfg(any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code, unused_variables)]
|
||||
pub fn configure_rtc(clock_source: RtcClockSource) {
|
||||
let clock_source = clock_source as u8;
|
||||
#[cfg(any(
|
||||
not(any(rtc_v3, rtc_v3u5, rtc_v2wb)),
|
||||
all(any(rtc_v3, rtc_v3u5), not(any(rcc_wl5, rcc_wle)))
|
||||
))]
|
||||
let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source);
|
||||
// Disable backup domain write protection
|
||||
unlock();
|
||||
|
||||
#[cfg(not(rtc_v2wb))]
|
||||
Self::modify(|w| {
|
||||
// Select RTC source
|
||||
w.set_rtcsel(clock_source);
|
||||
});
|
||||
}
|
||||
if self.lsi {
|
||||
#[cfg(any(stm32u5, stm32h5, stm32wba))]
|
||||
let csr = crate::pac::RCC.bdcr();
|
||||
#[cfg(not(any(stm32u5, stm32h5, stm32wba, stm32c0)))]
|
||||
let csr = crate::pac::RCC.csr();
|
||||
#[cfg(any(stm32c0))]
|
||||
let csr = crate::pac::RCC.csr2();
|
||||
|
||||
#[cfg(any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code)]
|
||||
pub fn enable_rtc() {
|
||||
let reg = Self::read();
|
||||
#[cfg(not(any(rcc_wb, rcc_wba)))]
|
||||
csr.modify(|w| w.set_lsion(true));
|
||||
|
||||
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
#[cfg(any(rcc_wb, rcc_wba))]
|
||||
csr.modify(|w| w.set_lsi1on(true));
|
||||
|
||||
if !reg.rtcen() {
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))]
|
||||
Self::modify(|w| w.set_bdrst(true));
|
||||
#[cfg(not(any(rcc_wb, rcc_wba)))]
|
||||
while !csr.read().lsirdy() {}
|
||||
|
||||
Self::modify(|w| {
|
||||
// Reset
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))]
|
||||
w.set_bdrst(false);
|
||||
#[cfg(any(rcc_wb, rcc_wba))]
|
||||
while !csr.read().lsi1rdy() {}
|
||||
}
|
||||
|
||||
// backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets.
|
||||
// once set, changing it requires a backup domain reset.
|
||||
// first check if the configuration matches what we want.
|
||||
|
||||
// check if it's already enabled and in the source we want.
|
||||
let reg = bdcr().read();
|
||||
let mut ok = true;
|
||||
ok &= reg.rtcsel() == self.rtc;
|
||||
#[cfg(not(rcc_wba))]
|
||||
{
|
||||
ok &= reg.rtcen() == (self.rtc != RtcClockSource::NOCLOCK);
|
||||
}
|
||||
ok &= reg.lseon() == lse_en;
|
||||
ok &= reg.lsebyp() == lse_byp;
|
||||
#[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))]
|
||||
if let Some(lse_drv) = lse_drv {
|
||||
ok &= reg.lsedrv() == lse_drv.into();
|
||||
}
|
||||
|
||||
// if configuration is OK, we're done.
|
||||
if ok {
|
||||
trace!("BDCR ok: {:08x}", bdcr().read().0);
|
||||
return rtc_clk;
|
||||
}
|
||||
|
||||
// If not OK, reset backup domain and configure it.
|
||||
#[cfg(not(any(rcc_l0, rcc_l0_v2, rcc_l1, stm32h5, stm32c0)))]
|
||||
{
|
||||
bdcr().modify(|w| w.set_bdrst(true));
|
||||
bdcr().modify(|w| w.set_bdrst(false));
|
||||
}
|
||||
#[cfg(any(stm32h5))]
|
||||
{
|
||||
bdcr().modify(|w| w.set_vswrst(true));
|
||||
bdcr().modify(|w| w.set_vswrst(false));
|
||||
}
|
||||
#[cfg(any(stm32c0))]
|
||||
{
|
||||
bdcr().modify(|w| w.set_rtcrst(true));
|
||||
bdcr().modify(|w| w.set_rtcrst(false));
|
||||
}
|
||||
|
||||
if lse_en {
|
||||
bdcr().modify(|w| {
|
||||
#[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))]
|
||||
if let Some(lse_drv) = lse_drv {
|
||||
w.set_lsedrv(lse_drv.into());
|
||||
}
|
||||
w.set_lsebyp(lse_byp);
|
||||
w.set_lseon(true);
|
||||
});
|
||||
|
||||
while !bdcr().read().lserdy() {}
|
||||
}
|
||||
|
||||
if self.rtc != RtcClockSource::NOCLOCK {
|
||||
bdcr().modify(|w| {
|
||||
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
#[cfg(not(rcc_wba))]
|
||||
w.set_rtcen(true);
|
||||
w.set_rtcsel(reg.rtcsel());
|
||||
|
||||
// Restore bcdr
|
||||
#[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
w.set_lscosel(reg.lscosel());
|
||||
#[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
w.set_lscoen(reg.lscoen());
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
|
||||
#[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
w.set_rtcsel(self.rtc);
|
||||
});
|
||||
}
|
||||
|
||||
trace!("BDCR configured: {:08x}", bdcr().read().0);
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
rtc_clk
|
||||
}
|
||||
}
|
||||
|
@ -1,174 +0,0 @@
|
||||
use core::ops::Div;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::pac::rcc;
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// Voltage Scale
|
||||
///
|
||||
/// Represents the voltage range feeding the CPU core. The maximum core
|
||||
/// clock frequency depends on this value.
|
||||
///
|
||||
/// Scale0 represents the highest voltage range
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
Scale0,
|
||||
Scale1,
|
||||
#[cfg(not(any(rcc_wl5, rcc_wle)))]
|
||||
Scale2,
|
||||
#[cfg(not(any(rcc_wl5, rcc_wle)))]
|
||||
Scale3,
|
||||
}
|
||||
|
||||
/// AHB prescaler
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum AHBPrescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div3,
|
||||
Div4,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div5,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div6,
|
||||
Div8,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div10,
|
||||
Div16,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div32,
|
||||
Div64,
|
||||
Div128,
|
||||
Div256,
|
||||
Div512,
|
||||
}
|
||||
|
||||
impl Div<AHBPrescaler> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: AHBPrescaler) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::Div2 => 2,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div3 => 3,
|
||||
AHBPrescaler::Div4 => 4,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div5 => 5,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div6 => 6,
|
||||
AHBPrescaler::Div8 => 8,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div10 => 10,
|
||||
AHBPrescaler::Div16 => 16,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div32 => 32,
|
||||
AHBPrescaler::Div64 => 64,
|
||||
AHBPrescaler::Div128 => 128,
|
||||
AHBPrescaler::Div256 => 256,
|
||||
AHBPrescaler::Div512 => 512,
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(rcc_g4, rcc_wb, rcc_wl5, rcc_wle)))]
|
||||
impl From<AHBPrescaler> for rcc::vals::Hpre {
|
||||
fn from(val: AHBPrescaler) -> rcc::vals::Hpre {
|
||||
use rcc::vals::Hpre;
|
||||
|
||||
match val {
|
||||
#[cfg(not(rcc_u5))]
|
||||
AHBPrescaler::NotDivided => Hpre::DIV1,
|
||||
#[cfg(rcc_u5)]
|
||||
AHBPrescaler::NotDivided => Hpre::NONE,
|
||||
AHBPrescaler::Div2 => Hpre::DIV2,
|
||||
AHBPrescaler::Div4 => Hpre::DIV4,
|
||||
AHBPrescaler::Div8 => Hpre::DIV8,
|
||||
AHBPrescaler::Div16 => Hpre::DIV16,
|
||||
AHBPrescaler::Div64 => Hpre::DIV64,
|
||||
AHBPrescaler::Div128 => Hpre::DIV128,
|
||||
AHBPrescaler::Div256 => Hpre::DIV256,
|
||||
AHBPrescaler::Div512 => Hpre::DIV512,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
impl From<AHBPrescaler> for u8 {
|
||||
fn from(val: AHBPrescaler) -> u8 {
|
||||
match val {
|
||||
AHBPrescaler::NotDivided => 0x0,
|
||||
AHBPrescaler::Div2 => 0x08,
|
||||
AHBPrescaler::Div3 => 0x01,
|
||||
AHBPrescaler::Div4 => 0x09,
|
||||
AHBPrescaler::Div5 => 0x02,
|
||||
AHBPrescaler::Div6 => 0x05,
|
||||
AHBPrescaler::Div8 => 0x0a,
|
||||
AHBPrescaler::Div10 => 0x06,
|
||||
AHBPrescaler::Div16 => 0x0b,
|
||||
AHBPrescaler::Div32 => 0x07,
|
||||
AHBPrescaler::Div64 => 0x0c,
|
||||
AHBPrescaler::Div128 => 0x0d,
|
||||
AHBPrescaler::Div256 => 0x0e,
|
||||
AHBPrescaler::Div512 => 0x0f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// APB prescaler
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum APBPrescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
}
|
||||
|
||||
impl Div<APBPrescaler> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: APBPrescaler) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
APBPrescaler::NotDivided => 1,
|
||||
APBPrescaler::Div2 => 2,
|
||||
APBPrescaler::Div4 => 4,
|
||||
APBPrescaler::Div8 => 8,
|
||||
APBPrescaler::Div16 => 16,
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_g4, rcc_h7, rcc_h7ab, rcc_wb, rcc_wl5, rcc_wle)))]
|
||||
impl From<APBPrescaler> for rcc::vals::Ppre {
|
||||
fn from(val: APBPrescaler) -> rcc::vals::Ppre {
|
||||
use rcc::vals::Ppre;
|
||||
|
||||
match val {
|
||||
#[cfg(not(rcc_u5))]
|
||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
||||
#[cfg(rcc_u5)]
|
||||
APBPrescaler::NotDivided => Ppre::NONE,
|
||||
APBPrescaler::Div2 => Ppre::DIV2,
|
||||
APBPrescaler::Div4 => Ppre::DIV4,
|
||||
APBPrescaler::Div8 => Ppre::DIV8,
|
||||
APBPrescaler::Div16 => Ppre::DIV16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
impl From<APBPrescaler> for u8 {
|
||||
fn from(val: APBPrescaler) -> u8 {
|
||||
match val {
|
||||
APBPrescaler::NotDivided => 1,
|
||||
APBPrescaler::Div2 => 0x04,
|
||||
APBPrescaler::Div4 => 0x05,
|
||||
APBPrescaler::Div8 => 0x06,
|
||||
APBPrescaler::Div16 => 0x07,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw};
|
||||
use crate::pac::rcc::vals::Sw;
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Ppre as APBPrescaler};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -8,9 +8,6 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(48_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
@ -19,47 +16,22 @@ pub enum ClockSrc {
|
||||
LSI,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HSIPrescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
Div32,
|
||||
Div64,
|
||||
Div128,
|
||||
}
|
||||
|
||||
impl Into<Hsidiv> for HSIPrescaler {
|
||||
fn into(self) -> Hsidiv {
|
||||
match self {
|
||||
HSIPrescaler::NotDivided => Hsidiv::DIV1,
|
||||
HSIPrescaler::Div2 => Hsidiv::DIV2,
|
||||
HSIPrescaler::Div4 => Hsidiv::DIV4,
|
||||
HSIPrescaler::Div8 => Hsidiv::DIV8,
|
||||
HSIPrescaler::Div16 => Hsidiv::DIV16,
|
||||
HSIPrescaler::Div32 => Hsidiv::DIV32,
|
||||
HSIPrescaler::Div64 => Hsidiv::DIV64,
|
||||
HSIPrescaler::Div128 => Hsidiv::DIV128,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb_pre: APBPrescaler,
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
#[inline]
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::HSI(HSIPrescaler::NotDivided),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb_pre: APBPrescaler::NotDivided,
|
||||
mux: ClockSrc::HSI(HSIPrescaler::DIV1),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb_pre: APBPrescaler::DIV1,
|
||||
ls: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,33 +40,34 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::HSI(div) => {
|
||||
// Enable HSI
|
||||
let div: Hsidiv = div.into();
|
||||
RCC.cr().write(|w| {
|
||||
w.set_hsidiv(div);
|
||||
w.set_hsion(true)
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
|
||||
(HSI_FREQ / div, Sw::HSI)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
(freq, Sw::HSE)
|
||||
}
|
||||
ClockSrc::LSI => {
|
||||
// Enable LSI
|
||||
RCC.csr2().write(|w| w.set_lsion(true));
|
||||
while !RCC.csr2().read().lsirdy() {}
|
||||
(LSI_FREQ.0, Sw::LSI)
|
||||
(super::LSI_FREQ, Sw::LSI)
|
||||
}
|
||||
};
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
// Determine the flash latency implied by the target clock speed
|
||||
// RM0454 § 3.3.4:
|
||||
let target_flash_latency = if sys_clk <= 24_000_000 {
|
||||
let target_flash_latency = if sys_clk <= Hertz(24_000_000) {
|
||||
Latency::WS0
|
||||
} else {
|
||||
Latency::WS1
|
||||
@ -129,7 +102,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
|
||||
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
||||
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre.into(), config.apb_pre.into());
|
||||
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre, config.apb_pre);
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(hpre);
|
||||
@ -150,33 +123,21 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
FLASH.acr().modify(|w| w.set_latency(target_flash_latency));
|
||||
}
|
||||
|
||||
let ahb_div = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::Div2 => 2,
|
||||
AHBPrescaler::Div4 => 4,
|
||||
AHBPrescaler::Div8 => 8,
|
||||
AHBPrescaler::Div16 => 16,
|
||||
AHBPrescaler::Div64 => 64,
|
||||
AHBPrescaler::Div128 => 128,
|
||||
AHBPrescaler::Div256 => 256,
|
||||
AHBPrescaler::Div512 => 512,
|
||||
};
|
||||
let ahb_freq = sys_clk / ahb_div;
|
||||
let ahb_freq = sys_clk / config.ahb_pre;
|
||||
|
||||
let (apb_freq, apb_tim_freq) = match config.apb_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
ahb1: Hertz(ahb_freq),
|
||||
apb1: Hertz(apb_freq),
|
||||
apb1_tim: Hertz(apb_tim_freq),
|
||||
sys: sys_clk,
|
||||
ahb1: ahb_freq,
|
||||
apb1: apb_freq,
|
||||
apb1_tim: apb_tim_freq,
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
@ -8,9 +8,6 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(40_000);
|
||||
|
||||
/// Configuration of the clocks
|
||||
///
|
||||
/// hse takes precedence over hsi48 if both are enabled
|
||||
@ -27,6 +24,8 @@ pub struct Config {
|
||||
pub sys_ck: Option<Hertz>,
|
||||
pub hclk: Option<Hertz>,
|
||||
pub pclk: Option<Hertz>,
|
||||
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
@ -159,6 +158,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
})
|
||||
}
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(real_sysclk),
|
||||
apb1: Hertz(pclk),
|
||||
@ -166,5 +167,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb1_tim: Hertz(pclk * timer_mul),
|
||||
apb2_tim: Hertz(pclk * timer_mul),
|
||||
ahb1: Hertz(hclk),
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
@ -9,9 +9,6 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(40_000);
|
||||
|
||||
/// Configuration of the clocks
|
||||
///
|
||||
#[non_exhaustive]
|
||||
@ -25,6 +22,8 @@ pub struct Config {
|
||||
pub pclk2: Option<Hertz>,
|
||||
pub adcclk: Option<Hertz>,
|
||||
pub pllxtpre: bool,
|
||||
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
@ -163,8 +162,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Only needed for stm32f103?
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_adcpre(Adcpre::from_bits(apre_bits));
|
||||
w.set_ppre2(Ppre1::from_bits(ppre2_bits));
|
||||
w.set_ppre1(Ppre1::from_bits(ppre1_bits));
|
||||
w.set_ppre2(Ppre::from_bits(ppre2_bits));
|
||||
w.set_ppre1(Ppre::from_bits(ppre1_bits));
|
||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||
#[cfg(not(rcc_f100))]
|
||||
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
|
||||
@ -177,6 +176,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
});
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(real_sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
@ -185,5 +186,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb1: Hertz(hclk),
|
||||
adc: Some(Hertz(adcclk)),
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
@ -1,21 +1,16 @@
|
||||
use core::convert::TryFrom;
|
||||
use core::ops::{Div, Mul};
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::bd::BackupDomain;
|
||||
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::rtc::RtcClockSource;
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct HSEConfig {
|
||||
pub frequency: Hertz,
|
||||
@ -43,17 +38,17 @@ pub enum HSESrc {
|
||||
pub struct PLLConfig {
|
||||
pub pre_div: PLLPreDiv,
|
||||
pub mul: PLLMul,
|
||||
pub main_div: PLLMainDiv,
|
||||
pub pll48_div: PLL48Div,
|
||||
pub p_div: PLLPDiv,
|
||||
pub q_div: PLLQDiv,
|
||||
}
|
||||
|
||||
impl Default for PLLConfig {
|
||||
fn default() -> Self {
|
||||
PLLConfig {
|
||||
pre_div: PLLPreDiv(16),
|
||||
mul: PLLMul(192),
|
||||
main_div: PLLMainDiv::Div2,
|
||||
pll48_div: PLL48Div(4),
|
||||
pre_div: PLLPreDiv::DIV16,
|
||||
mul: PLLMul::MUL192,
|
||||
p_div: PLLPDiv::DIV2,
|
||||
q_div: PLLQDiv::DIV4,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,9 +56,9 @@ impl Default for PLLConfig {
|
||||
impl PLLConfig {
|
||||
pub fn clocks(&self, src_freq: Hertz) -> PLLClocks {
|
||||
let in_freq = src_freq / self.pre_div;
|
||||
let vco_freq = Hertz((src_freq.0 as u64 * self.mul.0 as u64 / self.pre_div.0 as u64) as u32);
|
||||
let main_freq = vco_freq / self.main_div;
|
||||
let pll48_freq = vco_freq / self.pll48_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,
|
||||
@ -72,129 +67,6 @@ impl PLLConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clock source for both main PLL and PLLI2S
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum PLLSrc {
|
||||
HSE,
|
||||
HSI,
|
||||
}
|
||||
|
||||
impl Into<Pllsrc> for PLLSrc {
|
||||
fn into(self) -> Pllsrc {
|
||||
match self {
|
||||
PLLSrc::HSE => Pllsrc::HSE,
|
||||
PLLSrc::HSI => Pllsrc::HSI,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Division factor for both main PLL and PLLI2S
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct PLLPreDiv(u8);
|
||||
|
||||
impl TryFrom<u8> for PLLPreDiv {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
2..=63 => Ok(PLLPreDiv(value)),
|
||||
_ => Err("PLLPreDiv must be within range 2..=63"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<PLLPreDiv> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: PLLPreDiv) -> Self::Output {
|
||||
Hertz(self.0 / u32::from(rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplication factor for main PLL
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct PLLMul(u16);
|
||||
|
||||
impl Mul<PLLMul> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn mul(self, rhs: PLLMul) -> Self::Output {
|
||||
Hertz(self.0 * u32::from(rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for PLLMul {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
192..=432 => Ok(PLLMul(value)),
|
||||
_ => Err("PLLMul must be within range 192..=432"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PLL division factor for the main system clock
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum PLLMainDiv {
|
||||
Div2,
|
||||
Div4,
|
||||
Div6,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl Into<Pllp> for PLLMainDiv {
|
||||
fn into(self) -> Pllp {
|
||||
match self {
|
||||
PLLMainDiv::Div2 => Pllp::DIV2,
|
||||
PLLMainDiv::Div4 => Pllp::DIV4,
|
||||
PLLMainDiv::Div6 => Pllp::DIV6,
|
||||
PLLMainDiv::Div8 => Pllp::DIV8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<PLLMainDiv> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: PLLMainDiv) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
PLLMainDiv::Div2 => 2,
|
||||
PLLMainDiv::Div4 => 4,
|
||||
PLLMainDiv::Div6 => 6,
|
||||
PLLMainDiv::Div8 => 8,
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
||||
|
||||
/// PLL division factor for USB OTG FS / SDIO / RNG
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct PLL48Div(u8);
|
||||
|
||||
impl Div<PLL48Div> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: PLL48Div) -> Self::Output {
|
||||
Hertz(self.0 / u32::from(rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for PLL48Div {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
2..=15 => Ok(PLL48Div(value)),
|
||||
_ => Err("PLL48Div must be within range 2..=15"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct PLLClocks {
|
||||
pub in_freq: Hertz,
|
||||
@ -203,7 +75,20 @@ pub struct PLLClocks {
|
||||
pub pll48_freq: Hertz,
|
||||
}
|
||||
|
||||
pub use super::bus::VoltageScale;
|
||||
/// 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> {
|
||||
@ -211,7 +96,7 @@ impl VoltageScale {
|
||||
// Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock
|
||||
// frequency
|
||||
match self {
|
||||
VoltageScale::Scale3 => {
|
||||
VoltageScale::Range3 => {
|
||||
if ahb_freq <= 16_000_000 {
|
||||
Some(Latency::WS0)
|
||||
} else if ahb_freq <= 32_000_000 {
|
||||
@ -232,7 +117,7 @@ impl VoltageScale {
|
||||
None
|
||||
}
|
||||
}
|
||||
VoltageScale::Scale2 => {
|
||||
VoltageScale::Range2 => {
|
||||
if ahb_freq <= 18_000_000 {
|
||||
Some(Latency::WS0)
|
||||
} else if ahb_freq <= 36_000_000 {
|
||||
@ -251,7 +136,7 @@ impl VoltageScale {
|
||||
None
|
||||
}
|
||||
}
|
||||
VoltageScale::Scale1 => {
|
||||
VoltageScale::Range1 => {
|
||||
if ahb_freq <= 24_000_000 {
|
||||
Some(Latency::WS0)
|
||||
} else if ahb_freq <= 48_000_000 {
|
||||
@ -266,7 +151,7 @@ impl VoltageScale {
|
||||
None
|
||||
}
|
||||
}
|
||||
VoltageScale::Scale0 => {
|
||||
VoltageScale::Range0 => {
|
||||
if ahb_freq <= 30_000_000 {
|
||||
Some(Latency::WS0)
|
||||
} else if ahb_freq <= 60_000_000 {
|
||||
@ -290,11 +175,11 @@ pub struct Config {
|
||||
pub pll_mux: PLLSrc,
|
||||
pub pll: PLLConfig,
|
||||
pub mux: ClockSrc,
|
||||
pub rtc: Option<RtcClockSource>,
|
||||
pub voltage: VoltageScale,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -305,12 +190,12 @@ impl Default for Config {
|
||||
hsi: true,
|
||||
pll_mux: PLLSrc::HSI,
|
||||
pll: PLLConfig::default(),
|
||||
voltage: VoltageScale::Scale3,
|
||||
voltage: VoltageScale::Range3,
|
||||
mux: ClockSrc::HSI,
|
||||
rtc: None,
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
ls: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,11 +235,11 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
assert!(pll_clocks.pll48_freq <= Hertz(48_000_000));
|
||||
|
||||
RCC.pllcfgr().write(|w| {
|
||||
w.set_pllsrc(config.pll_mux.into());
|
||||
w.set_pllm(config.pll.pre_div.0);
|
||||
w.set_plln(config.pll.mul.0);
|
||||
w.set_pllp(config.pll.main_div.into());
|
||||
w.set_pllq(config.pll.pll48_div.0);
|
||||
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 {
|
||||
@ -383,7 +268,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
assert!(ahb_freq <= Hertz(120_000_000));
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, Hertz(freq.0 * 2))
|
||||
@ -393,7 +278,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
assert!(apb1_freq <= Hertz(30_000_000));
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, Hertz(freq.0 * 2))
|
||||
@ -407,9 +292,9 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.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() {}
|
||||
|
||||
@ -418,12 +303,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
}
|
||||
|
||||
RCC.apb1enr().modify(|w| w.set_pwren(true));
|
||||
PWR.cr().read();
|
||||
|
||||
config
|
||||
.rtc
|
||||
.map(|clock_source| BackupDomain::configure_ls(clock_source, None));
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk,
|
||||
@ -435,5 +315,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb2: apb2_freq,
|
||||
apb2_tim: apb2_tim_freq,
|
||||
pll48: Some(pll_clocks.pll48_freq),
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
#[cfg(rcc_f3)]
|
||||
use crate::pac::adccommon::vals::Ckmode;
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Adcpres, Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
|
||||
pub use crate::pac::rcc::vals::Adcpres;
|
||||
use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -9,28 +10,6 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(40_000);
|
||||
|
||||
impl From<AdcClockSource> for Adcpres {
|
||||
fn from(value: AdcClockSource) -> Self {
|
||||
match value {
|
||||
AdcClockSource::PllDiv1 => Adcpres::DIV1,
|
||||
AdcClockSource::PllDiv2 => Adcpres::DIV2,
|
||||
AdcClockSource::PllDiv4 => Adcpres::DIV4,
|
||||
AdcClockSource::PllDiv6 => Adcpres::DIV6,
|
||||
AdcClockSource::PllDiv8 => Adcpres::DIV8,
|
||||
AdcClockSource::PllDiv12 => Adcpres::DIV12,
|
||||
AdcClockSource::PllDiv16 => Adcpres::DIV16,
|
||||
AdcClockSource::PllDiv32 => Adcpres::DIV32,
|
||||
AdcClockSource::PllDiv64 => Adcpres::DIV64,
|
||||
AdcClockSource::PllDiv128 => Adcpres::DIV128,
|
||||
AdcClockSource::PllDiv256 => Adcpres::DIV256,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
impl From<AdcClockSource> for Ckmode {
|
||||
fn from(value: AdcClockSource) -> Self {
|
||||
@ -45,32 +24,13 @@ impl From<AdcClockSource> for Ckmode {
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
PllDiv1 = 1,
|
||||
PllDiv2 = 2,
|
||||
PllDiv4 = 4,
|
||||
PllDiv6 = 6,
|
||||
PllDiv8 = 8,
|
||||
PllDiv12 = 12,
|
||||
PllDiv16 = 16,
|
||||
PllDiv32 = 32,
|
||||
PllDiv64 = 64,
|
||||
PllDiv128 = 128,
|
||||
PllDiv256 = 256,
|
||||
Pll(Adcpres),
|
||||
BusDiv1,
|
||||
BusDiv2,
|
||||
BusDiv4,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn is_bus(&self) -> bool {
|
||||
match self {
|
||||
Self::BusDiv1 => true,
|
||||
Self::BusDiv2 => true,
|
||||
Self::BusDiv4 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bus_div(&self) -> u32 {
|
||||
match self {
|
||||
Self::BusDiv1 => 1,
|
||||
@ -124,6 +84,7 @@ pub struct Config {
|
||||
pub adc34: Option<AdcClockSource>,
|
||||
#[cfg(stm32f334)]
|
||||
pub hrtim: HrtimClockSource,
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
// Information required to setup the PLL clock
|
||||
@ -137,67 +98,67 @@ struct PllConfig {
|
||||
/// Initialize and Set the clock frequencies
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// Calculate the real System clock, and PLL configuration if applicable
|
||||
let (Hertz(sysclk), pll_config) = get_sysclk(&config);
|
||||
assert!(sysclk <= 72_000_000);
|
||||
let (sysclk, pll_config) = get_sysclk(&config);
|
||||
assert!(sysclk.0 <= 72_000_000);
|
||||
|
||||
// Calculate real AHB clock
|
||||
let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk);
|
||||
let (hpre_bits, hpre_div) = match sysclk / hclk {
|
||||
let hclk = config.hclk.map(|h| h).unwrap_or(sysclk);
|
||||
let hpre = match sysclk.0 / hclk.0 {
|
||||
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),
|
||||
1 => Hpre::DIV1,
|
||||
2 => Hpre::DIV2,
|
||||
3..=5 => Hpre::DIV4,
|
||||
6..=11 => Hpre::DIV8,
|
||||
12..=39 => Hpre::DIV16,
|
||||
40..=95 => Hpre::DIV64,
|
||||
96..=191 => Hpre::DIV128,
|
||||
192..=383 => Hpre::DIV256,
|
||||
_ => Hpre::DIV512,
|
||||
};
|
||||
let hclk = sysclk / hpre_div;
|
||||
assert!(hclk <= 72_000_000);
|
||||
let hclk = sysclk / hpre;
|
||||
assert!(hclk <= Hertz(72_000_000));
|
||||
|
||||
// Calculate real APB1 clock
|
||||
let pclk1 = config.pclk1.map(|p| p.0).unwrap_or(hclk);
|
||||
let (ppre1_bits, ppre1) = match hclk / pclk1 {
|
||||
let pclk1 = config.pclk1.unwrap_or(hclk);
|
||||
let ppre1 = match hclk / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => (Ppre::DIV1, 1),
|
||||
2 => (Ppre::DIV2, 2),
|
||||
3..=5 => (Ppre::DIV4, 4),
|
||||
6..=11 => (Ppre::DIV8, 8),
|
||||
_ => (Ppre::DIV16, 16),
|
||||
1 => Ppre::DIV1,
|
||||
2 => Ppre::DIV2,
|
||||
3..=5 => Ppre::DIV4,
|
||||
6..=11 => Ppre::DIV8,
|
||||
_ => Ppre::DIV16,
|
||||
};
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
let timer_mul1 = if ppre1 == Ppre::DIV1 { 1u32 } else { 2 };
|
||||
let pclk1 = hclk / ppre1;
|
||||
assert!(pclk1 <= 36_000_000);
|
||||
assert!(pclk1 <= Hertz(36_000_000));
|
||||
|
||||
// Calculate real APB2 clock
|
||||
let pclk2 = config.pclk2.map(|p| p.0).unwrap_or(hclk);
|
||||
let (ppre2_bits, ppre2) = match hclk / pclk2 {
|
||||
let pclk2 = config.pclk2.unwrap_or(hclk);
|
||||
let ppre2 = match hclk / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => (Ppre::DIV1, 1),
|
||||
2 => (Ppre::DIV2, 2),
|
||||
3..=5 => (Ppre::DIV4, 4),
|
||||
6..=11 => (Ppre::DIV8, 8),
|
||||
_ => (Ppre::DIV16, 16),
|
||||
1 => Ppre::DIV1,
|
||||
2 => Ppre::DIV2,
|
||||
3..=5 => Ppre::DIV4,
|
||||
6..=11 => Ppre::DIV8,
|
||||
_ => Ppre::DIV16,
|
||||
};
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
let timer_mul2 = if ppre2 == Ppre::DIV1 { 1u32 } else { 2 };
|
||||
let pclk2 = hclk / ppre2;
|
||||
assert!(pclk2 <= 72_000_000);
|
||||
assert!(pclk2 <= Hertz(72_000_000));
|
||||
|
||||
// Set latency based on HCLK frquency
|
||||
// RM0316: "The prefetch buffer must be kept on when using a prescaler
|
||||
// different from 1 on the AHB clock.", "Half-cycle access cannot be
|
||||
// used when there is a prescaler different from 1 on the AHB clock"
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(if hclk <= 24_000_000 {
|
||||
w.set_latency(if hclk <= Hertz(24_000_000) {
|
||||
Latency::WS0
|
||||
} else if hclk <= 48_000_000 {
|
||||
} else if hclk <= Hertz(48_000_000) {
|
||||
Latency::WS1
|
||||
} else {
|
||||
Latency::WS2
|
||||
});
|
||||
if hpre_div != 1 {
|
||||
if hpre != Hpre::DIV1 {
|
||||
w.set_hlfcya(false);
|
||||
w.set_prftbe(true);
|
||||
}
|
||||
@ -240,9 +201,9 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Set prescalers
|
||||
// CFGR has been written before (PLL, PLL48) don't overwrite these settings
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(ppre2_bits);
|
||||
w.set_ppre1(ppre1_bits);
|
||||
w.set_hpre(hpre_bits);
|
||||
w.set_ppre2(ppre2);
|
||||
w.set_ppre1(ppre1);
|
||||
w.set_hpre(hpre);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
@ -260,41 +221,43 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
let adc = config.adc.map(|adc| {
|
||||
if !adc.is_bus() {
|
||||
let adc = config.adc.map(|adc| match adc {
|
||||
AdcClockSource::Pll(adcpres) => {
|
||||
RCC.cfgr2().modify(|w| {
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
w.set_adc12pres(adc.into());
|
||||
w.set_adc12pres(adcpres);
|
||||
|
||||
Hertz(sysclk / adc as u32)
|
||||
})
|
||||
} else {
|
||||
crate::pac::ADC_COMMON.ccr().modify(|w| {
|
||||
assert!(!(adc.bus_div() == 1 && hpre_bits != Hpre::DIV1));
|
||||
|
||||
w.set_ckmode(adc.into());
|
||||
|
||||
Hertz(sysclk / adc.bus_div() as u32)
|
||||
sysclk / adcpres
|
||||
})
|
||||
}
|
||||
_ => crate::pac::ADC_COMMON.ccr().modify(|w| {
|
||||
assert!(!(adc.bus_div() == 1 && hpre != Hpre::DIV1));
|
||||
|
||||
w.set_ckmode(adc.into());
|
||||
|
||||
sysclk / adc.bus_div()
|
||||
}),
|
||||
});
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
let adc34 = config.adc.map(|adc| {
|
||||
if !adc.is_bus() {
|
||||
#[cfg(all(rcc_f3, adc3_common))]
|
||||
let adc34 = config.adc34.map(|adc| match adc {
|
||||
AdcClockSource::Pll(adcpres) => {
|
||||
RCC.cfgr2().modify(|w| {
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
w.set_adc12pres(adc.into());
|
||||
w.set_adc34pres(adcpres);
|
||||
|
||||
Hertz(sysclk / adc as u32)
|
||||
sysclk / adcpres
|
||||
})
|
||||
} else {
|
||||
// TODO: need to use only if adc32_common is present
|
||||
|
||||
todo!()
|
||||
}
|
||||
_ => crate::pac::ADC_COMMON.ccr().modify(|w| {
|
||||
assert!(!(adc.bus_div() == 1 && hpre != Hpre::DIV1));
|
||||
|
||||
w.set_ckmode(adc.into());
|
||||
|
||||
sysclk / adc.bus_div()
|
||||
}),
|
||||
});
|
||||
|
||||
#[cfg(stm32f334)]
|
||||
@ -306,27 +269,32 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
assert!((pclk2 == sysclk) || (pclk2 * 2 == sysclk));
|
||||
assert!((pclk2 == sysclk) || (pclk2 * 2u32 == sysclk));
|
||||
|
||||
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL));
|
||||
|
||||
Some(Hertz(sysclk * 2))
|
||||
Some(sysclk * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
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),
|
||||
sys: sysclk,
|
||||
apb1: pclk1,
|
||||
apb2: pclk2,
|
||||
apb1_tim: pclk1 * timer_mul1,
|
||||
apb2_tim: pclk2 * timer_mul2,
|
||||
ahb1: hclk,
|
||||
#[cfg(rcc_f3)]
|
||||
adc: adc,
|
||||
#[cfg(rcc_f3)]
|
||||
#[cfg(all(rcc_f3, adc3_common))]
|
||||
adc34: adc34,
|
||||
#[cfg(all(rcc_f3, not(adc3_common)))]
|
||||
adc34: None,
|
||||
#[cfg(stm32f334)]
|
||||
hrtim: hrtim,
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
||||
@ -415,16 +383,16 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
|
||||
|
||||
#[inline]
|
||||
#[allow(unused_variables)]
|
||||
fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre {
|
||||
fn get_usb_pre(config: &Config, sysclk: Hertz, pclk1: Hertz, pll_config: &Option<PllConfig>) -> Usbpre {
|
||||
cfg_if::cfg_if! {
|
||||
// Some chips do not have USB
|
||||
if #[cfg(any(stm32f301, stm32f318, stm32f334))] {
|
||||
panic!("USB clock not supported by the chip");
|
||||
} else {
|
||||
let usb_ok = config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000);
|
||||
let usb_ok = config.hse.is_some() && pll_config.is_some() && (pclk1 >= Hertz(10_000_000));
|
||||
match (usb_ok, sysclk) {
|
||||
(true, 72_000_000) => Usbpre::DIV1_5,
|
||||
(true, 48_000_000) => Usbpre::DIV1,
|
||||
(true, Hertz(72_000_000)) => Usbpre::DIV1_5,
|
||||
(true, Hertz(48_000_000)) => Usbpre::DIV1,
|
||||
_ => panic!(
|
||||
"USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz"
|
||||
),
|
||||
|
@ -1,24 +1,11 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre};
|
||||
|
||||
use super::sealed::RccPeripheral;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
|
||||
use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllq, Pllr, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
/// Clocks configuration
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
@ -33,8 +20,11 @@ pub struct Config {
|
||||
#[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 rtc: Option<RtcClockSource>,
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
#[cfg(stm32f410)]
|
||||
@ -48,11 +38,10 @@ fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
|
||||
fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
|
||||
#[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 max_div = 7;
|
||||
let target = match plli2s {
|
||||
let target = match target {
|
||||
Some(target) => target,
|
||||
None => return None,
|
||||
};
|
||||
@ -76,15 +65,48 @@ fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
|
||||
})
|
||||
.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)
|
||||
}
|
||||
|
||||
fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Option<u32>, pll48clk: bool) -> PllResults {
|
||||
#[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);
|
||||
@ -96,6 +118,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
|
||||
pllsysclk: None,
|
||||
pll48clk: None,
|
||||
plli2sclk: None,
|
||||
pllsaiclk: None,
|
||||
};
|
||||
}
|
||||
// Input divisor from PLL source clock, must result to frequency in
|
||||
@ -141,11 +164,12 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
|
||||
let real_pll48clk = vco_in * plln / pllq;
|
||||
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
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 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;
|
||||
@ -155,164 +179,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
|
||||
pllsysclk: Some(real_pllsysclk),
|
||||
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
|
||||
plli2sclk: setup_i2s_pll(vco_in, plli2s),
|
||||
}
|
||||
}
|
||||
|
||||
pub enum McoClock {
|
||||
DIV1,
|
||||
DIV2,
|
||||
DIV3,
|
||||
DIV4,
|
||||
DIV5,
|
||||
}
|
||||
|
||||
impl McoClock {
|
||||
fn into_raw(&self) -> Mcopre {
|
||||
match self {
|
||||
McoClock::DIV1 => Mcopre::DIV1,
|
||||
McoClock::DIV2 => Mcopre::DIV2,
|
||||
McoClock::DIV3 => Mcopre::DIV3,
|
||||
McoClock::DIV4 => Mcopre::DIV4,
|
||||
McoClock::DIV5 => Mcopre::DIV5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Mco1Source {
|
||||
Hsi,
|
||||
Lse,
|
||||
Hse,
|
||||
Pll,
|
||||
}
|
||||
|
||||
impl Default for Mco1Source {
|
||||
fn default() -> Self {
|
||||
Self::Hsi
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoSource {
|
||||
type Raw;
|
||||
|
||||
fn into_raw(&self) -> Self::Raw;
|
||||
}
|
||||
|
||||
impl McoSource for Mco1Source {
|
||||
type Raw = Mco1;
|
||||
fn into_raw(&self) -> Self::Raw {
|
||||
match self {
|
||||
Mco1Source::Hsi => Mco1::HSI,
|
||||
Mco1Source::Lse => Mco1::LSE,
|
||||
Mco1Source::Hse => Mco1::HSE,
|
||||
Mco1Source::Pll => Mco1::PLL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Mco2Source {
|
||||
SysClk,
|
||||
Plli2s,
|
||||
Hse,
|
||||
Pll,
|
||||
}
|
||||
|
||||
impl Default for Mco2Source {
|
||||
fn default() -> Self {
|
||||
Self::SysClk
|
||||
}
|
||||
}
|
||||
|
||||
impl McoSource for Mco2Source {
|
||||
type Raw = Mco2;
|
||||
fn into_raw(&self) -> Self::Raw {
|
||||
match self {
|
||||
Mco2Source::SysClk => Mco2::SYSCLK,
|
||||
Mco2Source::Plli2s => Mco2::PLLI2S,
|
||||
Mco2Source::Hse => Mco2::HSE,
|
||||
Mco2Source::Pll => Mco2::PLL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use stm32_metapac::rcc::vals::Mcopre;
|
||||
pub trait McoInstance {
|
||||
type Source;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoInstance: sealed::McoInstance + 'static {}
|
||||
|
||||
pin_trait!(McoPin, McoInstance);
|
||||
|
||||
impl sealed::McoInstance for peripherals::MCO1 {
|
||||
type Source = Mco1;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_mco1(source);
|
||||
w.set_mco1pre(prescaler);
|
||||
});
|
||||
match source {
|
||||
Mco1::PLL => {
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
Mco1::HSI => {
|
||||
RCC.cr().modify(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl McoInstance for peripherals::MCO1 {}
|
||||
|
||||
impl sealed::McoInstance for peripherals::MCO2 {
|
||||
type Source = Mco2;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_mco2(source);
|
||||
w.set_mco2pre(prescaler);
|
||||
});
|
||||
match source {
|
||||
Mco2::PLL => {
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
#[cfg(not(stm32f410))]
|
||||
Mco2::PLLI2S => {
|
||||
RCC.cr().modify(|w| w.set_plli2son(true));
|
||||
while !RCC.cr().read().plli2srdy() {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl McoInstance for peripherals::MCO2 {}
|
||||
|
||||
pub struct Mco<'d, T: McoInstance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: McoInstance> Mco<'d, T> {
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
pin: impl Peripheral<P = impl McoPin<T>> + 'd,
|
||||
source: impl McoSource<Raw = T::Source>,
|
||||
prescaler: McoClock,
|
||||
) -> Self {
|
||||
into_ref!(pin);
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
|
||||
pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
|
||||
pin.set_speed(Speed::VeryHigh);
|
||||
});
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
pllsaiclk: setup_sai_pll(vco_in, pllsai),
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,8 +197,6 @@ fn flash_setup(sysclk: u32) {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
crate::peripherals::PWR::enable();
|
||||
|
||||
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;
|
||||
@ -344,6 +209,10 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
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,
|
||||
);
|
||||
|
||||
@ -441,6 +310,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
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));
|
||||
@ -461,14 +336,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
})
|
||||
});
|
||||
|
||||
config
|
||||
.rtc
|
||||
.map(|clock_source| BackupDomain::configure_ls(clock_source, None));
|
||||
|
||||
let rtc = match config.rtc {
|
||||
Some(RtcClockSource::LSI) => Some(LSI_FREQ),
|
||||
_ => None,
|
||||
};
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
@ -488,10 +356,9 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
plli2s: plls.plli2sclk.map(Hertz),
|
||||
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||
pllsai: None,
|
||||
pllsai: plls.pllsaiclk.map(Hertz),
|
||||
|
||||
rtc: rtc,
|
||||
rtc_hse: None,
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
||||
@ -501,6 +368,8 @@ struct PllResults {
|
||||
pll48clk: Option<u32>,
|
||||
#[allow(dead_code)]
|
||||
plli2sclk: Option<u32>,
|
||||
#[allow(dead_code)]
|
||||
pllsaiclk: Option<u32>,
|
||||
}
|
||||
|
||||
mod max {
|
||||
|
@ -1,6 +1,5 @@
|
||||
use super::sealed::RccPeripheral;
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
|
||||
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;
|
||||
@ -8,9 +7,6 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
/// Clocks configuration
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
@ -23,11 +19,10 @@ pub struct Config {
|
||||
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 {
|
||||
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)));
|
||||
@ -81,10 +76,10 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bo
|
||||
let real_pll48clk = vco_in * plln / pllq;
|
||||
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
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 as u8);
|
||||
w.set_pllq(Pllq::from_bits(pllq as u8));
|
||||
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
|
||||
});
|
||||
|
||||
@ -111,8 +106,6 @@ fn flash_setup(sysclk: u32) {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
crate::peripherals::PWR::enable();
|
||||
|
||||
if let Some(hse) = config.hse {
|
||||
if config.bypass_hse {
|
||||
assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0));
|
||||
@ -212,10 +205,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
if plls.use_pll {
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
|
||||
// enable PWR and setup VOSScale
|
||||
|
||||
RCC.apb1enr().modify(|w| w.set_pwren(true));
|
||||
|
||||
// setup VOSScale
|
||||
let vos_scale = if sysclk <= 144_000_000 {
|
||||
3
|
||||
} else if sysclk <= 168_000_000 {
|
||||
@ -265,6 +255,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
})
|
||||
});
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
@ -278,6 +270,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
ahb3: Hertz(hclk),
|
||||
|
||||
pll48: plls.pll48clk.map(Hertz),
|
||||
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw};
|
||||
use crate::pac::rcc::vals::{self, Sw};
|
||||
pub use crate::pac::rcc::vals::{
|
||||
Hpre as AHBPrescaler, Hsidiv as HSI16Prescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler,
|
||||
};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -8,9 +10,6 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
@ -20,33 +19,6 @@ pub enum ClockSrc {
|
||||
LSI,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HSI16Prescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
Div32,
|
||||
Div64,
|
||||
Div128,
|
||||
}
|
||||
|
||||
impl Into<Hsidiv> for HSI16Prescaler {
|
||||
fn into(self) -> Hsidiv {
|
||||
match self {
|
||||
HSI16Prescaler::NotDivided => Hsidiv::DIV1,
|
||||
HSI16Prescaler::Div2 => Hsidiv::DIV2,
|
||||
HSI16Prescaler::Div4 => Hsidiv::DIV4,
|
||||
HSI16Prescaler::Div8 => Hsidiv::DIV8,
|
||||
HSI16Prescaler::Div16 => Hsidiv::DIV16,
|
||||
HSI16Prescaler::Div32 => Hsidiv::DIV32,
|
||||
HSI16Prescaler::Div64 => Hsidiv::DIV64,
|
||||
HSI16Prescaler::Div128 => Hsidiv::DIV128,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The PLL configuration.
|
||||
///
|
||||
/// * `VCOCLK = source / m * n`
|
||||
@ -60,15 +32,15 @@ pub struct PllConfig {
|
||||
/// The initial divisor of that clock signal
|
||||
pub m: Pllm,
|
||||
/// The PLL VCO multiplier, which must be in the range `8..=86`.
|
||||
pub n: u8,
|
||||
pub n: Plln,
|
||||
/// The final divisor for `PLLRCLK` output which drives the system clock
|
||||
pub r: Pllr,
|
||||
|
||||
/// The divisor for the `PLLQCLK` output, if desired
|
||||
pub q: Option<Pllr>,
|
||||
pub q: Option<Pllq>,
|
||||
|
||||
/// The divisor for the `PLLPCLK` output, if desired
|
||||
pub p: Option<Pllr>,
|
||||
pub p: Option<Pllp>,
|
||||
}
|
||||
|
||||
impl Default for PllConfig {
|
||||
@ -77,9 +49,9 @@ impl Default for PllConfig {
|
||||
// HSI16 / 1 * 8 / 2 = 64 MHz
|
||||
PllConfig {
|
||||
source: PllSrc::HSI16,
|
||||
m: Pllm::Div1,
|
||||
n: 8,
|
||||
r: Pllr::Div2,
|
||||
m: Pllm::DIV1,
|
||||
n: Plln::MUL8,
|
||||
r: Pllr::DIV2,
|
||||
q: None,
|
||||
p: None,
|
||||
}
|
||||
@ -92,131 +64,51 @@ pub enum PllSrc {
|
||||
HSE(Hertz),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Pllm {
|
||||
Div1,
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
Div5,
|
||||
Div6,
|
||||
Div7,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl From<Pllm> for u8 {
|
||||
fn from(v: Pllm) -> Self {
|
||||
match v {
|
||||
Pllm::Div1 => 0b000,
|
||||
Pllm::Div2 => 0b001,
|
||||
Pllm::Div3 => 0b010,
|
||||
Pllm::Div4 => 0b011,
|
||||
Pllm::Div5 => 0b100,
|
||||
Pllm::Div6 => 0b101,
|
||||
Pllm::Div7 => 0b110,
|
||||
Pllm::Div8 => 0b111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pllm> for u32 {
|
||||
fn from(v: Pllm) -> Self {
|
||||
match v {
|
||||
Pllm::Div1 => 1,
|
||||
Pllm::Div2 => 2,
|
||||
Pllm::Div3 => 3,
|
||||
Pllm::Div4 => 4,
|
||||
Pllm::Div5 => 5,
|
||||
Pllm::Div6 => 6,
|
||||
Pllm::Div7 => 7,
|
||||
Pllm::Div8 => 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Pllr {
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
Div5,
|
||||
Div6,
|
||||
Div7,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl From<Pllr> for u8 {
|
||||
fn from(v: Pllr) -> Self {
|
||||
match v {
|
||||
Pllr::Div2 => 0b000,
|
||||
Pllr::Div3 => 0b001,
|
||||
Pllr::Div4 => 0b010,
|
||||
Pllr::Div5 => 0b011,
|
||||
Pllr::Div6 => 0b101,
|
||||
Pllr::Div7 => 0b110,
|
||||
Pllr::Div8 => 0b111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pllr> for u32 {
|
||||
fn from(v: Pllr) -> Self {
|
||||
match v {
|
||||
Pllr::Div2 => 2,
|
||||
Pllr::Div3 => 3,
|
||||
Pllr::Div4 => 4,
|
||||
Pllr::Div5 => 5,
|
||||
Pllr::Div6 => 6,
|
||||
Pllr::Div7 => 7,
|
||||
Pllr::Div8 => 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb_pre: APBPrescaler,
|
||||
pub low_power_run: bool,
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
#[inline]
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb_pre: APBPrescaler::NotDivided,
|
||||
mux: ClockSrc::HSI16(HSI16Prescaler::DIV1),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb_pre: APBPrescaler::DIV1,
|
||||
low_power_run: false,
|
||||
ls: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PllConfig {
|
||||
pub(crate) fn init(self) -> u32 {
|
||||
assert!(self.n >= 8 && self.n <= 86);
|
||||
pub(crate) fn init(self) -> Hertz {
|
||||
let (src, input_freq) = match self.source {
|
||||
PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ.0),
|
||||
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq.0),
|
||||
PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ),
|
||||
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq),
|
||||
};
|
||||
|
||||
let m_freq = input_freq / u32::from(self.m);
|
||||
let m_freq = input_freq / self.m;
|
||||
// RM0454 § 5.4.4:
|
||||
// > Caution: The software must set these bits so that the PLL input frequency after the
|
||||
// > /M divider is between 2.66 and 16 MHz.
|
||||
debug_assert!(m_freq >= 2_660_000 && m_freq <= 16_000_000);
|
||||
debug_assert!(m_freq.0 >= 2_660_000 && m_freq.0 <= 16_000_000);
|
||||
|
||||
let n_freq = m_freq * self.n as u32;
|
||||
// RM0454 § 5.4.4:
|
||||
// > Caution: The software must set these bits so that the VCO output frequency is between
|
||||
// > 64 and 344 MHz.
|
||||
debug_assert!(n_freq >= 64_000_000 && n_freq <= 344_000_000);
|
||||
debug_assert!(n_freq.0 >= 64_000_000 && n_freq.0 <= 344_000_000);
|
||||
|
||||
let r_freq = n_freq / u32::from(self.r);
|
||||
let r_freq = n_freq / self.r;
|
||||
// RM0454 § 5.4.4:
|
||||
// > Caution: The software must set this bitfield so as not to exceed 64 MHz on this clock.
|
||||
debug_assert!(r_freq <= 64_000_000);
|
||||
debug_assert!(r_freq.0 <= 64_000_000);
|
||||
|
||||
// RM0454 § 5.2.3:
|
||||
// > To modify the PLL configuration, proceed as follows:
|
||||
@ -239,25 +131,16 @@ impl PllConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure PLLSYSCFGR
|
||||
RCC.pllsyscfgr().modify(|w| {
|
||||
w.set_pllr(u8::from(self.r));
|
||||
// Configure PLLCFGR
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllr(self.r);
|
||||
w.set_pllren(false);
|
||||
|
||||
if let Some(q) = self.q {
|
||||
w.set_pllq(u8::from(q));
|
||||
}
|
||||
w.set_pllq(self.q.unwrap_or(Pllq::DIV2));
|
||||
w.set_pllqen(false);
|
||||
|
||||
if let Some(p) = self.p {
|
||||
w.set_pllp(u8::from(p));
|
||||
}
|
||||
w.set_pllp(self.p.unwrap_or(Pllp::DIV2));
|
||||
w.set_pllpen(false);
|
||||
|
||||
w.set_plln(self.n);
|
||||
|
||||
w.set_pllm(self.m as u8);
|
||||
|
||||
w.set_pllm(self.m);
|
||||
w.set_pllsrc(src)
|
||||
});
|
||||
|
||||
@ -269,7 +152,7 @@ impl PllConfig {
|
||||
|
||||
// > 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, and PLLREN in PLL
|
||||
// > configuration register (RCC_PLLCFGR).
|
||||
RCC.pllsyscfgr().modify(|w| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
// We'll use R for system clock, so enable that unconditionally
|
||||
w.set_pllren(true);
|
||||
|
||||
@ -286,21 +169,20 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::HSI16(div) => {
|
||||
// Enable HSI16
|
||||
let div: Hsidiv = div.into();
|
||||
RCC.cr().write(|w| {
|
||||
w.set_hsidiv(div);
|
||||
w.set_hsion(true)
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
|
||||
(HSI_FREQ / div, Sw::HSI)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
(freq, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(pll) => {
|
||||
let freq = pll.init();
|
||||
@ -310,15 +192,15 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Enable LSI
|
||||
RCC.csr().write(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
(LSI_FREQ.0, Sw::LSI)
|
||||
(super::LSI_FREQ, Sw::LSI)
|
||||
}
|
||||
};
|
||||
|
||||
// Determine the flash latency implied by the target clock speed
|
||||
// RM0454 § 3.3.4:
|
||||
let target_flash_latency = if sys_clk <= 24_000_000 {
|
||||
let target_flash_latency = if sys_clk.0 <= 24_000_000 {
|
||||
Latency::WS0
|
||||
} else if sys_clk <= 48_000_000 {
|
||||
} else if sys_clk.0 <= 48_000_000 {
|
||||
Latency::WS1
|
||||
} else {
|
||||
Latency::WS2
|
||||
@ -353,7 +235,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
|
||||
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
|
||||
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre.into(), config.apb_pre.into());
|
||||
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre, config.apb_pre);
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(hpre);
|
||||
@ -374,27 +256,28 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
FLASH.acr().modify(|w| w.set_latency(target_flash_latency));
|
||||
}
|
||||
|
||||
let ahb_freq = Hertz(sys_clk) / config.ahb_pre;
|
||||
let ahb_freq = sys_clk / config.ahb_pre;
|
||||
|
||||
let (apb_freq, apb_tim_freq) = match config.apb_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq.0, ahb_freq.0),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq.0 / pre as u32;
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
if config.low_power_run {
|
||||
assert!(sys_clk <= 2_000_000);
|
||||
assert!(sys_clk.0 <= 2_000_000);
|
||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||
}
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
sys: sys_clk,
|
||||
ahb1: ahb_freq,
|
||||
apb1: Hertz(apb_freq),
|
||||
apb1_tim: Hertz(apb_tim_freq),
|
||||
apb1: apb_freq,
|
||||
apb1_tim: apb_tim_freq,
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
use stm32_metapac::flash::vals::Latency;
|
||||
use stm32_metapac::rcc::vals::{Adcsel, Hpre, Pllsrc, Ppre, Sw};
|
||||
use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw};
|
||||
use stm32_metapac::FLASH;
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
pub use crate::pac::rcc::vals::{
|
||||
Adcsel as AdcClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, Pllp as PllP, Pllq as PllQ,
|
||||
Pllr as PllR, Ppre as APBPrescaler,
|
||||
};
|
||||
use crate::pac::{PWR, RCC};
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
@ -11,32 +14,6 @@ use crate::time::Hertz;
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
NoClk,
|
||||
SysClk,
|
||||
PllP,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn adcsel(&self) -> Adcsel {
|
||||
match self {
|
||||
AdcClockSource::NoClk => Adcsel::NOCLK,
|
||||
AdcClockSource::SysClk => Adcsel::SYSCLK,
|
||||
AdcClockSource::PllP => Adcsel::PLLP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AdcClockSource {
|
||||
fn default() -> Self {
|
||||
Self::NoClk
|
||||
}
|
||||
}
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
@ -61,181 +38,6 @@ impl Into<Pllsrc> for PllSrc {
|
||||
}
|
||||
}
|
||||
|
||||
seq_macro::seq!(P in 2..=31 {
|
||||
/// Output divider for the PLL P output.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllP {
|
||||
// Note: If PLL P is set to 0 the PLLP bit controls the output division. There does not seem to
|
||||
// a good reason to do this so the API does not support it.
|
||||
// Div1 is invalid
|
||||
#(
|
||||
Div~P,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<PllP> for u8 {
|
||||
/// Returns the register value for the P output divider.
|
||||
fn from(val: PllP) -> u8 {
|
||||
match val {
|
||||
#(
|
||||
PllP::Div~P => P,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
impl PllP {
|
||||
/// Returns the numeric value of the P output divider.
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
val as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Output divider for the PLL Q output.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllQ {
|
||||
Div2,
|
||||
Div4,
|
||||
Div6,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl PllQ {
|
||||
/// Returns the numeric value of the Q output divider.
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
(val as u32 + 1) * 2
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PllQ> for u8 {
|
||||
/// Returns the register value for the Q output divider.
|
||||
fn from(val: PllQ) -> u8 {
|
||||
match val {
|
||||
PllQ::Div2 => 0b00,
|
||||
PllQ::Div4 => 0b01,
|
||||
PllQ::Div6 => 0b10,
|
||||
PllQ::Div8 => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Output divider for the PLL R output.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllR {
|
||||
Div2,
|
||||
Div4,
|
||||
Div6,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl PllR {
|
||||
/// Returns the numeric value of the R output divider.
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
(val as u32 + 1) * 2
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PllR> for u8 {
|
||||
/// Returns the register value for the R output divider.
|
||||
fn from(val: PllR) -> u8 {
|
||||
match val {
|
||||
PllR::Div2 => 0b00,
|
||||
PllR::Div4 => 0b01,
|
||||
PllR::Div6 => 0b10,
|
||||
PllR::Div8 => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seq_macro::seq!(N in 8..=127 {
|
||||
/// Multiplication factor for the PLL VCO input clock.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllN {
|
||||
#(
|
||||
Mul~N,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<PllN> for u8 {
|
||||
/// Returns the register value for the N multiplication factor.
|
||||
fn from(val: PllN) -> u8 {
|
||||
match val {
|
||||
#(
|
||||
PllN::Mul~N => N,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PllN {
|
||||
/// Returns the numeric value of the N multiplication factor.
|
||||
pub fn to_mul(self) -> u32 {
|
||||
match self {
|
||||
#(
|
||||
PllN::Mul~N => N,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/// PLL Pre-division. This must be set such that the PLL input is between 2.66 MHz and 16 MHz.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum PllM {
|
||||
Div1,
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
Div5,
|
||||
Div6,
|
||||
Div7,
|
||||
Div8,
|
||||
Div9,
|
||||
Div10,
|
||||
Div11,
|
||||
Div12,
|
||||
Div13,
|
||||
Div14,
|
||||
Div15,
|
||||
Div16,
|
||||
}
|
||||
|
||||
impl PllM {
|
||||
/// Returns the numeric value of the M pre-division.
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
val as u32 + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PllM> for u8 {
|
||||
/// Returns the register value for the M pre-division.
|
||||
fn from(val: PllM) -> u8 {
|
||||
match val {
|
||||
PllM::Div1 => 0b0000,
|
||||
PllM::Div2 => 0b0001,
|
||||
PllM::Div3 => 0b0010,
|
||||
PllM::Div4 => 0b0011,
|
||||
PllM::Div5 => 0b0100,
|
||||
PllM::Div6 => 0b0101,
|
||||
PllM::Div7 => 0b0110,
|
||||
PllM::Div8 => 0b0111,
|
||||
PllM::Div9 => 0b1000,
|
||||
PllM::Div10 => 0b1001,
|
||||
PllM::Div11 => 0b1010,
|
||||
PllM::Div12 => 0b1011,
|
||||
PllM::Div13 => 0b1100,
|
||||
PllM::Div14 => 0b1101,
|
||||
PllM::Div15 => 0b1110,
|
||||
PllM::Div16 => 0b1111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PLL Configuration
|
||||
///
|
||||
/// Use this struct to configure the PLL source, input frequency, multiplication factor, and output
|
||||
@ -261,62 +63,6 @@ pub struct Pll {
|
||||
pub div_r: Option<PllR>,
|
||||
}
|
||||
|
||||
impl AHBPrescaler {
|
||||
const fn div(self) -> u32 {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::Div2 => 2,
|
||||
AHBPrescaler::Div4 => 4,
|
||||
AHBPrescaler::Div8 => 8,
|
||||
AHBPrescaler::Div16 => 16,
|
||||
AHBPrescaler::Div64 => 64,
|
||||
AHBPrescaler::Div128 => 128,
|
||||
AHBPrescaler::Div256 => 256,
|
||||
AHBPrescaler::Div512 => 512,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl APBPrescaler {
|
||||
const fn div(self) -> u32 {
|
||||
match self {
|
||||
APBPrescaler::NotDivided => 1,
|
||||
APBPrescaler::Div2 => 2,
|
||||
APBPrescaler::Div4 => 4,
|
||||
APBPrescaler::Div8 => 8,
|
||||
APBPrescaler::Div16 => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Ppre> for APBPrescaler {
|
||||
fn into(self) -> Ppre {
|
||||
match self {
|
||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
||||
APBPrescaler::Div2 => Ppre::DIV2,
|
||||
APBPrescaler::Div4 => Ppre::DIV4,
|
||||
APBPrescaler::Div8 => Ppre::DIV8,
|
||||
APBPrescaler::Div16 => Ppre::DIV16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Hpre> for AHBPrescaler {
|
||||
fn into(self) -> Hpre {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => Hpre::DIV1,
|
||||
AHBPrescaler::Div2 => Hpre::DIV2,
|
||||
AHBPrescaler::Div4 => Hpre::DIV4,
|
||||
AHBPrescaler::Div8 => Hpre::DIV8,
|
||||
AHBPrescaler::Div16 => Hpre::DIV16,
|
||||
AHBPrescaler::Div64 => Hpre::DIV64,
|
||||
AHBPrescaler::Div128 => Hpre::DIV128,
|
||||
AHBPrescaler::Div256 => Hpre::DIV256,
|
||||
AHBPrescaler::Div512 => Hpre::DIV512,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the source for the 48MHz clock to the USB and RNG peripherals.
|
||||
pub enum Clock48MhzSrc {
|
||||
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
|
||||
@ -352,6 +98,8 @@ pub struct Config {
|
||||
pub clock_48mhz_src: Option<Clock48MhzSrc>,
|
||||
pub adc12_clock_source: AdcClockSource,
|
||||
pub adc345_clock_source: AdcClockSource,
|
||||
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
|
||||
@ -365,14 +113,15 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::HSI16,
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
low_power_run: false,
|
||||
pll: None,
|
||||
clock_48mhz_src: None,
|
||||
adc12_clock_source: Default::default(),
|
||||
adc345_clock_source: Default::default(),
|
||||
adc12_clock_source: Adcsel::NOCLK,
|
||||
adc345_clock_source: Adcsel::NOCLK,
|
||||
ls: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -390,12 +139,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI_FREQ.0
|
||||
HSI_FREQ
|
||||
}
|
||||
PllSrc::HSE(freq) => {
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
freq.0
|
||||
freq
|
||||
}
|
||||
};
|
||||
|
||||
@ -403,36 +152,36 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let internal_freq = src_freq / pll_config.prediv_m.to_div() * pll_config.mul_n.to_mul();
|
||||
let internal_freq = src_freq / pll_config.prediv_m * pll_config.mul_n;
|
||||
|
||||
RCC.pllcfgr().write(|w| {
|
||||
w.set_plln(pll_config.mul_n.into());
|
||||
w.set_pllm(pll_config.prediv_m.into());
|
||||
w.set_plln(pll_config.mul_n);
|
||||
w.set_pllm(pll_config.prediv_m);
|
||||
w.set_pllsrc(pll_config.source.into());
|
||||
});
|
||||
|
||||
let pll_p_freq = pll_config.div_p.map(|div_p| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllpdiv(div_p.into());
|
||||
w.set_pllp(div_p);
|
||||
w.set_pllpen(true);
|
||||
});
|
||||
Hertz(internal_freq / div_p.to_div())
|
||||
internal_freq / div_p
|
||||
});
|
||||
|
||||
let pll_q_freq = pll_config.div_q.map(|div_q| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllq(div_q.into());
|
||||
w.set_pllq(div_q);
|
||||
w.set_pllqen(true);
|
||||
});
|
||||
Hertz(internal_freq / div_q.to_div())
|
||||
internal_freq / div_q
|
||||
});
|
||||
|
||||
let pll_r_freq = pll_config.div_r.map(|div_r| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllr(div_r.into());
|
||||
w.set_pllr(div_r);
|
||||
w.set_pllren(true);
|
||||
});
|
||||
Hertz(internal_freq / div_r.to_div())
|
||||
internal_freq / div_r
|
||||
});
|
||||
|
||||
// Enable the PLL
|
||||
@ -452,14 +201,14 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ.0, Sw::HSI16)
|
||||
(HSI_FREQ, Sw::HSI16)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
(freq, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL => {
|
||||
assert!(pll_freq.is_some());
|
||||
@ -500,35 +249,32 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
}
|
||||
|
||||
(freq, Sw::PLLRCLK)
|
||||
(Hertz(freq), Sw::PLLRCLK)
|
||||
}
|
||||
};
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
w.set_hpre(config.ahb_pre);
|
||||
w.set_ppre1(config.apb1_pre);
|
||||
w.set_ppre2(config.apb2_pre);
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => sys_clk / pre.div(),
|
||||
};
|
||||
let ahb_freq = sys_clk / config.ahb_pre;
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre.div();
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre.div();
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
@ -576,43 +322,40 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.ccipr().modify(|w| w.set_clk48sel(source));
|
||||
}
|
||||
|
||||
RCC.ccipr()
|
||||
.modify(|w| w.set_adc12sel(config.adc12_clock_source.adcsel()));
|
||||
RCC.ccipr()
|
||||
.modify(|w| w.set_adc345sel(config.adc345_clock_source.adcsel()));
|
||||
RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source));
|
||||
RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source));
|
||||
|
||||
let adc12_ck = match config.adc12_clock_source {
|
||||
AdcClockSource::NoClk => None,
|
||||
AdcClockSource::PllP => match &pll_freq {
|
||||
Some(pll) => pll.pll_p,
|
||||
None => None,
|
||||
},
|
||||
AdcClockSource::SysClk => Some(Hertz(sys_clk)),
|
||||
AdcClockSource::NOCLK => None,
|
||||
AdcClockSource::PLLP => pll_freq.as_ref().unwrap().pll_p,
|
||||
AdcClockSource::SYSCLK => Some(sys_clk),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let adc345_ck = match config.adc345_clock_source {
|
||||
AdcClockSource::NoClk => None,
|
||||
AdcClockSource::PllP => match &pll_freq {
|
||||
Some(pll) => pll.pll_p,
|
||||
None => None,
|
||||
},
|
||||
AdcClockSource::SysClk => Some(Hertz(sys_clk)),
|
||||
AdcClockSource::NOCLK => None,
|
||||
AdcClockSource::PLLP => pll_freq.as_ref().unwrap().pll_p,
|
||||
AdcClockSource::SYSCLK => Some(sys_clk),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if config.low_power_run {
|
||||
assert!(sys_clk <= 2_000_000);
|
||||
assert!(sys_clk <= Hertz(2_000_000));
|
||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||
}
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
ahb1: Hertz(ahb_freq),
|
||||
ahb2: Hertz(ahb_freq),
|
||||
apb1: Hertz(apb1_freq),
|
||||
apb1_tim: Hertz(apb1_tim_freq),
|
||||
apb2: Hertz(apb2_freq),
|
||||
apb2_tim: Hertz(apb2_tim_freq),
|
||||
sys: sys_clk,
|
||||
ahb1: ahb_freq,
|
||||
ahb2: ahb_freq,
|
||||
apb1: apb1_freq,
|
||||
apb1_tim: apb1_tim_freq,
|
||||
apb2: apb2_freq,
|
||||
apb2_tim: apb2_tim_freq,
|
||||
adc: adc12_ck,
|
||||
adc34: adc345_ck,
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
829
embassy-stm32/src/rcc/h.rs
Normal file
829
embassy-stm32/src/rcc/h.rs
Normal file
@ -0,0 +1,829 @@
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
use crate::pac;
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
#[cfg(stm32h5)]
|
||||
pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource;
|
||||
#[cfg(stm32h7)]
|
||||
pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
|
||||
use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre};
|
||||
pub use crate::pac::rcc::vals::{Ckpersel as PerClockSource, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
|
||||
|
||||
/// CSI speed
|
||||
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
|
||||
|
||||
/// HSI48 speed
|
||||
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
|
||||
|
||||
const VCO_RANGE: RangeInclusive<Hertz> = Hertz(150_000_000)..=Hertz(420_000_000);
|
||||
#[cfg(any(stm32h5, pwr_h7rm0455))]
|
||||
const VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(128_000_000)..=Hertz(560_000_000);
|
||||
#[cfg(pwr_h7rm0468)]
|
||||
const VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(192_000_000)..=Hertz(836_000_000);
|
||||
#[cfg(any(pwr_h7rm0399, pwr_h7rm0433))]
|
||||
const VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(192_000_000)..=Hertz(960_000_000);
|
||||
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
Scale0,
|
||||
Scale1,
|
||||
Scale2,
|
||||
Scale3,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum HseMode {
|
||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||
Oscillator,
|
||||
/// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
|
||||
Bypass,
|
||||
/// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
|
||||
#[cfg(any(rcc_h5, rcc_h50))]
|
||||
BypassDigital,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Hse {
|
||||
/// HSE frequency.
|
||||
pub freq: Hertz,
|
||||
/// HSE mode.
|
||||
pub mode: HseMode,
|
||||
}
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Lse {
|
||||
/// 32.768 kHz crystal/ceramic oscillator (LSEBYP=0)
|
||||
Oscillator,
|
||||
/// external clock input up to 1MHz (LSEBYP=1)
|
||||
Bypass(Hertz),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Hsi {
|
||||
/// 64Mhz
|
||||
Mhz64,
|
||||
/// 32Mhz (divided by 2)
|
||||
Mhz32,
|
||||
/// 16Mhz (divided by 4)
|
||||
Mhz16,
|
||||
/// 8Mhz (divided by 8)
|
||||
Mhz8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Sysclk {
|
||||
/// HSI selected as sysclk
|
||||
HSI,
|
||||
/// HSE selected as sysclk
|
||||
HSE,
|
||||
/// CSI selected as sysclk
|
||||
CSI,
|
||||
/// PLL1_P selected as sysclk
|
||||
Pll1P,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum PllSource {
|
||||
Hsi,
|
||||
Csi,
|
||||
Hse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Pll {
|
||||
/// Source clock selection.
|
||||
#[cfg(stm32h5)]
|
||||
pub source: PllSource,
|
||||
|
||||
/// PLL pre-divider (DIVM).
|
||||
pub prediv: PllPreDiv,
|
||||
|
||||
/// PLL multiplication factor.
|
||||
pub mul: PllMul,
|
||||
|
||||
/// PLL P division factor. If None, PLL P output is disabled.
|
||||
/// On PLL1, it must be even (in particular, it cannot be 1.)
|
||||
pub divp: Option<PllDiv>,
|
||||
/// PLL Q division factor. If None, PLL Q output is disabled.
|
||||
pub divq: Option<PllDiv>,
|
||||
/// PLL R division factor. If None, PLL R output is disabled.
|
||||
pub divr: Option<PllDiv>,
|
||||
}
|
||||
|
||||
fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz {
|
||||
match (tim, apb) {
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk,
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk,
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32,
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32,
|
||||
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk,
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk,
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk,
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32,
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Timer prescaler
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum TimerPrescaler {
|
||||
/// The timers kernel clock is equal to hclk if PPREx corresponds to a
|
||||
/// division by 1 or 2, else it is equal to 2*pclk
|
||||
DefaultX2,
|
||||
|
||||
/// The timers kernel clock is equal to hclk if PPREx corresponds to a
|
||||
/// division by 1, 2 or 4, else it is equal to 4*pclk
|
||||
DefaultX4,
|
||||
}
|
||||
|
||||
impl From<TimerPrescaler> for Timpre {
|
||||
fn from(value: TimerPrescaler) -> Self {
|
||||
match value {
|
||||
TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2,
|
||||
TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration of the core clocks
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
pub hsi: Option<Hsi>,
|
||||
pub hse: Option<Hse>,
|
||||
#[cfg(stm32h7)]
|
||||
pub lse: Option<Lse>,
|
||||
#[cfg(stm32h7)]
|
||||
pub lsi: bool,
|
||||
pub csi: bool,
|
||||
pub hsi48: bool,
|
||||
pub sys: Sysclk,
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
pub pll_src: PllSource,
|
||||
|
||||
pub pll1: Option<Pll>,
|
||||
pub pll2: Option<Pll>,
|
||||
#[cfg(any(rcc_h5, stm32h7))]
|
||||
pub pll3: Option<Pll>,
|
||||
|
||||
pub d1c_pre: AHBPrescaler,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub apb3_pre: APBPrescaler,
|
||||
#[cfg(stm32h7)]
|
||||
pub apb4_pre: APBPrescaler,
|
||||
|
||||
pub per_clock_source: PerClockSource,
|
||||
pub adc_clock_source: AdcClockSource,
|
||||
pub timer_prescaler: TimerPrescaler,
|
||||
pub voltage_scale: VoltageScale,
|
||||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hsi: Some(Hsi::Mhz64),
|
||||
hse: None,
|
||||
#[cfg(stm32h7)]
|
||||
lse: None,
|
||||
#[cfg(stm32h7)]
|
||||
lsi: false,
|
||||
csi: false,
|
||||
hsi48: false,
|
||||
sys: Sysclk::HSI,
|
||||
#[cfg(stm32h7)]
|
||||
pll_src: PllSource::Hsi,
|
||||
pll1: None,
|
||||
pll2: None,
|
||||
#[cfg(any(rcc_h5, stm32h7))]
|
||||
pll3: None,
|
||||
|
||||
d1c_pre: AHBPrescaler::DIV1,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb3_pre: APBPrescaler::DIV1,
|
||||
#[cfg(stm32h7)]
|
||||
apb4_pre: APBPrescaler::DIV1,
|
||||
|
||||
per_clock_source: PerClockSource::HSI,
|
||||
adc_clock_source: AdcClockSource::from_bits(0), // PLL2_P on H7, HCLK on H5
|
||||
timer_prescaler: TimerPrescaler::DefaultX2,
|
||||
voltage_scale: VoltageScale::Scale0,
|
||||
ls: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// NB. The lower bytes of CR3 can only be written once after
|
||||
// POR, and must be written with a valid combination. Refer to
|
||||
// RM0433 Rev 7 6.8.4. This is partially enforced by dropping
|
||||
// `self` at the end of this method, but of course we cannot
|
||||
// know what happened between the previous POR and here.
|
||||
#[cfg(pwr_h7rm0433)]
|
||||
PWR.cr3().modify(|w| {
|
||||
w.set_scuen(true);
|
||||
w.set_ldoen(true);
|
||||
w.set_bypass(false);
|
||||
});
|
||||
|
||||
#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
|
||||
PWR.cr3().modify(|w| {
|
||||
// hardcode "Direct SPMS" for now, this is what works on nucleos with the
|
||||
// default solderbridge configuration.
|
||||
w.set_sden(true);
|
||||
w.set_ldoen(false);
|
||||
});
|
||||
|
||||
// Validate the supply configuration. If you are stuck here, it is
|
||||
// because the voltages on your board do not match those specified
|
||||
// in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset
|
||||
// VOS = Scale 3, so check that the voltage on the VCAP pins =
|
||||
// 1.0V.
|
||||
#[cfg(any(pwr_h7rm0433, pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
|
||||
while !PWR.csr1().read().actvosrdy() {}
|
||||
|
||||
// Configure voltage scale.
|
||||
#[cfg(any(pwr_h5, pwr_h50))]
|
||||
{
|
||||
PWR.voscr().modify(|w| {
|
||||
w.set_vos(match config.voltage_scale {
|
||||
VoltageScale::Scale0 => Vos::SCALE0,
|
||||
VoltageScale::Scale1 => Vos::SCALE1,
|
||||
VoltageScale::Scale2 => Vos::SCALE2,
|
||||
VoltageScale::Scale3 => Vos::SCALE3,
|
||||
})
|
||||
});
|
||||
while !PWR.vossr().read().vosrdy() {}
|
||||
}
|
||||
|
||||
#[cfg(syscfg_h7)]
|
||||
{
|
||||
// in chips without the overdrive bit, we can go from any scale to any scale directly.
|
||||
PWR.d3cr().modify(|w| {
|
||||
w.set_vos(match config.voltage_scale {
|
||||
VoltageScale::Scale0 => Vos::SCALE0,
|
||||
VoltageScale::Scale1 => Vos::SCALE1,
|
||||
VoltageScale::Scale2 => Vos::SCALE2,
|
||||
VoltageScale::Scale3 => Vos::SCALE3,
|
||||
})
|
||||
});
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
}
|
||||
|
||||
#[cfg(syscfg_h7od)]
|
||||
{
|
||||
match config.voltage_scale {
|
||||
VoltageScale::Scale0 => {
|
||||
// to go to scale0, we must go to Scale1 first...
|
||||
PWR.d3cr().modify(|w| w.set_vos(Vos::SCALE1));
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
|
||||
// Then enable overdrive.
|
||||
critical_section::with(|_| pac::SYSCFG.pwrcr().modify(|w| w.set_oden(1)));
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
}
|
||||
_ => {
|
||||
// for all other scales, we can go directly.
|
||||
PWR.d3cr().modify(|w| {
|
||||
w.set_vos(match config.voltage_scale {
|
||||
VoltageScale::Scale0 => unreachable!(),
|
||||
VoltageScale::Scale1 => Vos::SCALE1,
|
||||
VoltageScale::Scale2 => Vos::SCALE2,
|
||||
VoltageScale::Scale3 => Vos::SCALE3,
|
||||
})
|
||||
});
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure HSI
|
||||
let hsi = match config.hsi {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
None
|
||||
}
|
||||
Some(hsi) => {
|
||||
let (freq, hsidiv) = match hsi {
|
||||
Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
|
||||
Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
|
||||
Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
|
||||
Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
|
||||
};
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsidiv(hsidiv);
|
||||
w.set_hsion(true);
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
Some(freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSE
|
||||
let hse = match config.hse {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hseon(false));
|
||||
None
|
||||
}
|
||||
Some(hse) => {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(hse.mode != HseMode::Oscillator);
|
||||
#[cfg(any(rcc_h5, rcc_h50))]
|
||||
w.set_hseext(match hse.mode {
|
||||
HseMode::Oscillator | HseMode::Bypass => pac::rcc::vals::Hseext::ANALOG,
|
||||
HseMode::BypassDigital => pac::rcc::vals::Hseext::DIGITAL,
|
||||
});
|
||||
});
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse.freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSI48.
|
||||
RCC.cr().modify(|w| w.set_hsi48on(config.hsi48));
|
||||
let _hsi48 = match config.hsi48 {
|
||||
false => None,
|
||||
true => {
|
||||
while !RCC.cr().read().hsi48rdy() {}
|
||||
Some(CSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure CSI.
|
||||
RCC.cr().modify(|w| w.set_csion(config.csi));
|
||||
let csi = match config.csi {
|
||||
false => None,
|
||||
true => {
|
||||
while !RCC.cr().read().csirdy() {}
|
||||
Some(CSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure PLLs.
|
||||
let pll_input = PllInput {
|
||||
csi,
|
||||
hse,
|
||||
hsi,
|
||||
#[cfg(stm32h7)]
|
||||
source: config.pll_src,
|
||||
};
|
||||
let pll1 = init_pll(0, config.pll1, &pll_input);
|
||||
let pll2 = init_pll(1, config.pll2, &pll_input);
|
||||
#[cfg(any(rcc_h5, stm32h7))]
|
||||
let pll3 = init_pll(2, config.pll3, &pll_input);
|
||||
|
||||
// Configure sysclk
|
||||
let (sys, sw) = match config.sys {
|
||||
Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
|
||||
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
|
||||
Sysclk::CSI => (unwrap!(csi), Sw::CSI),
|
||||
Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
|
||||
};
|
||||
|
||||
// Check limits.
|
||||
#[cfg(stm32h5)]
|
||||
let (hclk_max, pclk_max) = match config.voltage_scale {
|
||||
VoltageScale::Scale0 => (Hertz(250_000_000), Hertz(250_000_000)),
|
||||
VoltageScale::Scale1 => (Hertz(200_000_000), Hertz(200_000_000)),
|
||||
VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)),
|
||||
VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)),
|
||||
};
|
||||
#[cfg(stm32h7)]
|
||||
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
|
||||
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
|
||||
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
|
||||
VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)),
|
||||
VoltageScale::Scale3 => (Hertz(200_000_000), Hertz(100_000_000), Hertz(50_000_000)),
|
||||
};
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
let hclk = {
|
||||
let d1cpre_clk = sys / config.d1c_pre;
|
||||
assert!(d1cpre_clk <= d1cpre_clk_max);
|
||||
sys / config.ahb_pre
|
||||
};
|
||||
#[cfg(stm32h5)]
|
||||
let hclk = sys / config.ahb_pre;
|
||||
assert!(hclk <= hclk_max);
|
||||
|
||||
let apb1 = hclk / config.apb1_pre;
|
||||
let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler);
|
||||
assert!(apb1 <= pclk_max);
|
||||
let apb2 = hclk / config.apb2_pre;
|
||||
let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler);
|
||||
assert!(apb2 <= pclk_max);
|
||||
let apb3 = hclk / config.apb3_pre;
|
||||
assert!(apb3 <= pclk_max);
|
||||
#[cfg(stm32h7)]
|
||||
let apb4 = hclk / config.apb4_pre;
|
||||
#[cfg(stm32h7)]
|
||||
assert!(apb4 <= pclk_max);
|
||||
|
||||
let _per_ck = match config.per_clock_source {
|
||||
Ckpersel::HSI => hsi,
|
||||
Ckpersel::CSI => csi,
|
||||
Ckpersel::HSE => hse,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
let adc = match config.adc_clock_source {
|
||||
AdcClockSource::PLL2_P => pll2.p,
|
||||
AdcClockSource::PLL3_R => pll3.r,
|
||||
AdcClockSource::PER => _per_ck,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
#[cfg(stm32h5)]
|
||||
let adc = match config.adc_clock_source {
|
||||
AdcClockSource::HCLK => Some(hclk),
|
||||
AdcClockSource::SYSCLK => Some(sys),
|
||||
AdcClockSource::PLL2_R => pll2.r,
|
||||
AdcClockSource::HSE => hse,
|
||||
AdcClockSource::HSI_KER => hsi,
|
||||
AdcClockSource::CSI_KER => csi,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
flash_setup(hclk, config.voltage_scale);
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
{
|
||||
RCC.d1cfgr().modify(|w| {
|
||||
w.set_d1cpre(config.d1c_pre);
|
||||
w.set_d1ppre(config.apb3_pre);
|
||||
w.set_hpre(config.ahb_pre);
|
||||
});
|
||||
// Ensure core prescaler value is valid before future lower core voltage
|
||||
while RCC.d1cfgr().read().d1cpre() != config.d1c_pre {}
|
||||
|
||||
RCC.d2cfgr().modify(|w| {
|
||||
w.set_d2ppre1(config.apb1_pre);
|
||||
w.set_d2ppre2(config.apb2_pre);
|
||||
});
|
||||
RCC.d3cfgr().modify(|w| {
|
||||
w.set_d3ppre(config.apb4_pre);
|
||||
});
|
||||
|
||||
RCC.d1ccipr().modify(|w| {
|
||||
w.set_ckpersel(config.per_clock_source);
|
||||
});
|
||||
RCC.d3ccipr().modify(|w| {
|
||||
w.set_adcsel(config.adc_clock_source);
|
||||
});
|
||||
}
|
||||
#[cfg(stm32h5)]
|
||||
{
|
||||
// Set hpre
|
||||
RCC.cfgr2().modify(|w| w.set_hpre(config.ahb_pre));
|
||||
while RCC.cfgr2().read().hpre() != config.ahb_pre {}
|
||||
|
||||
// set ppre
|
||||
RCC.cfgr2().modify(|w| {
|
||||
w.set_ppre1(config.apb1_pre);
|
||||
w.set_ppre2(config.apb2_pre);
|
||||
w.set_ppre3(config.apb3_pre);
|
||||
});
|
||||
|
||||
RCC.ccipr5().modify(|w| {
|
||||
w.set_ckpersel(config.per_clock_source);
|
||||
w.set_adcdacsel(config.adc_clock_source)
|
||||
});
|
||||
}
|
||||
|
||||
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
|
||||
|
||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
||||
while RCC.cfgr().read().sws() != sw {}
|
||||
|
||||
// IO compensation cell - Requires CSI clock and SYSCFG
|
||||
#[cfg(stm32h7)] // TODO h5
|
||||
if csi.is_some() {
|
||||
// Enable the compensation cell, using back-bias voltage code
|
||||
// provide by the cell.
|
||||
critical_section::with(|_| {
|
||||
pac::SYSCFG.cccsr().modify(|w| {
|
||||
w.set_en(true);
|
||||
w.set_cs(false);
|
||||
w.set_hslv(false);
|
||||
})
|
||||
});
|
||||
while !pac::SYSCFG.cccsr().read().ready() {}
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys,
|
||||
ahb1: hclk,
|
||||
ahb2: hclk,
|
||||
ahb3: hclk,
|
||||
ahb4: hclk,
|
||||
apb1,
|
||||
apb2,
|
||||
apb3,
|
||||
#[cfg(stm32h7)]
|
||||
apb4,
|
||||
apb1_tim,
|
||||
apb2_tim,
|
||||
adc,
|
||||
rtc,
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
mux_rcc_pclk1: Some(apb1),
|
||||
#[cfg(stm32h5)]
|
||||
mux_pll2_q: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_pll3_q: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_hsi_ker: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_csi_ker: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_lse: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_pll1_q: pll1.q,
|
||||
#[cfg(stm32h5)]
|
||||
mux_pll2_p: pll2.p,
|
||||
#[cfg(rcc_h5)]
|
||||
mux_pll3_p: pll3.p,
|
||||
#[cfg(stm32h5)]
|
||||
mux_audioclk: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_per: None,
|
||||
|
||||
#[cfg(rcc_h5)]
|
||||
mux_pll3_r: pll3.r,
|
||||
#[cfg(all(not(rcc_h5), stm32h5))]
|
||||
mux_pll3_r: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_rcc_pclk3: Some(apb3),
|
||||
#[cfg(stm32h5)]
|
||||
mux_pll3_1: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_hsi48_ker: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_lsi_ker: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_pll2_r: pll2.r,
|
||||
#[cfg(stm32h5)]
|
||||
mux_rcc_pclk2: Some(apb2),
|
||||
#[cfg(stm32h5)]
|
||||
mux_rcc_pclk4: None,
|
||||
#[cfg(stm32h5)]
|
||||
mux_hse: hse,
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
mux_hsi48: None,
|
||||
});
|
||||
}
|
||||
|
||||
struct PllInput {
|
||||
hsi: Option<Hertz>,
|
||||
hse: Option<Hertz>,
|
||||
csi: Option<Hertz>,
|
||||
#[cfg(stm32h7)]
|
||||
source: PllSource,
|
||||
}
|
||||
|
||||
struct PllOutput {
|
||||
p: Option<Hertz>,
|
||||
#[allow(dead_code)]
|
||||
q: Option<Hertz>,
|
||||
#[allow(dead_code)]
|
||||
r: Option<Hertz>,
|
||||
}
|
||||
|
||||
fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
let Some(config) = config else {
|
||||
// Stop PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(num, false));
|
||||
while RCC.cr().read().pllrdy(num) {}
|
||||
|
||||
// "To save power when PLL1 is not used, the value of PLL1M must be set to 0.""
|
||||
#[cfg(stm32h7)]
|
||||
RCC.pllckselr().write(|w| w.set_divm(num, PllPreDiv::from_bits(0)));
|
||||
#[cfg(stm32h5)]
|
||||
RCC.pllcfgr(num).write(|w| w.set_divm(PllPreDiv::from_bits(0)));
|
||||
|
||||
return PllOutput {
|
||||
p: None,
|
||||
q: None,
|
||||
r: None,
|
||||
};
|
||||
};
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
let source = config.source;
|
||||
#[cfg(stm32h7)]
|
||||
let source = input.source;
|
||||
|
||||
let (in_clk, src) = match source {
|
||||
PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
|
||||
PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
|
||||
PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
|
||||
};
|
||||
|
||||
let ref_clk = in_clk / config.prediv as u32;
|
||||
|
||||
let ref_range = match ref_clk.0 {
|
||||
..=1_999_999 => Pllrge::RANGE1,
|
||||
..=3_999_999 => Pllrge::RANGE2,
|
||||
..=7_999_999 => Pllrge::RANGE4,
|
||||
..=16_000_000 => Pllrge::RANGE8,
|
||||
x => panic!("pll ref_clk out of range: {} mhz", x),
|
||||
};
|
||||
|
||||
// The smaller range (150 to 420 MHz) must
|
||||
// be chosen when the reference clock frequency is lower than 2 MHz.
|
||||
let wide_allowed = ref_range != Pllrge::RANGE1;
|
||||
|
||||
let vco_clk = ref_clk * config.mul;
|
||||
let vco_range = if VCO_RANGE.contains(&vco_clk) {
|
||||
Pllvcosel::MEDIUMVCO
|
||||
} else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk) {
|
||||
Pllvcosel::WIDEVCO
|
||||
} else {
|
||||
panic!("pll vco_clk out of range: {} mhz", vco_clk.0)
|
||||
};
|
||||
|
||||
let p = config.divp.map(|div| {
|
||||
if num == 0 {
|
||||
// on PLL1, DIVP must be even.
|
||||
// The enum value is 1 less than the divider, so check it's odd.
|
||||
assert!(div.to_bits() % 2 == 1);
|
||||
}
|
||||
|
||||
vco_clk / div
|
||||
});
|
||||
let q = config.divq.map(|div| vco_clk / div);
|
||||
let r = config.divr.map(|div| vco_clk / div);
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
RCC.pllcfgr(num).write(|w| {
|
||||
w.set_pllsrc(src);
|
||||
w.set_divm(config.prediv);
|
||||
w.set_pllvcosel(vco_range);
|
||||
w.set_pllrge(ref_range);
|
||||
w.set_pllfracen(false);
|
||||
w.set_pllpen(p.is_some());
|
||||
w.set_pllqen(q.is_some());
|
||||
w.set_pllren(r.is_some());
|
||||
});
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
{
|
||||
RCC.pllckselr().modify(|w| {
|
||||
w.set_divm(num, config.prediv);
|
||||
w.set_pllsrc(src);
|
||||
});
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllvcosel(num, vco_range);
|
||||
w.set_pllrge(num, ref_range);
|
||||
w.set_pllfracen(num, false);
|
||||
w.set_divpen(num, p.is_some());
|
||||
w.set_divqen(num, q.is_some());
|
||||
w.set_divren(num, r.is_some());
|
||||
});
|
||||
}
|
||||
|
||||
RCC.plldivr(num).write(|w| {
|
||||
w.set_plln(config.mul);
|
||||
w.set_pllp(config.divp.unwrap_or(PllDiv::DIV2));
|
||||
w.set_pllq(config.divq.unwrap_or(PllDiv::DIV2));
|
||||
w.set_pllr(config.divr.unwrap_or(PllDiv::DIV2));
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(num, true));
|
||||
while !RCC.cr().read().pllrdy(num) {}
|
||||
|
||||
PllOutput { p, q, r }
|
||||
}
|
||||
|
||||
fn flash_setup(clk: Hertz, vos: VoltageScale) {
|
||||
// RM0481 Rev 1, table 37
|
||||
// LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0
|
||||
// 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz
|
||||
// 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz
|
||||
// 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz
|
||||
// 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz
|
||||
// 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz
|
||||
// 5 2 170 to 200 MHz 210 to 250 MHz
|
||||
#[cfg(stm32h5)]
|
||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||
(VoltageScale::Scale0, ..=42_000_000) => (0, 0),
|
||||
(VoltageScale::Scale0, ..=84_000_000) => (1, 0),
|
||||
(VoltageScale::Scale0, ..=126_000_000) => (2, 1),
|
||||
(VoltageScale::Scale0, ..=168_000_000) => (3, 1),
|
||||
(VoltageScale::Scale0, ..=210_000_000) => (4, 2),
|
||||
(VoltageScale::Scale0, ..=250_000_000) => (5, 2),
|
||||
|
||||
(VoltageScale::Scale1, ..=34_000_000) => (0, 0),
|
||||
(VoltageScale::Scale1, ..=68_000_000) => (1, 0),
|
||||
(VoltageScale::Scale1, ..=102_000_000) => (2, 1),
|
||||
(VoltageScale::Scale1, ..=136_000_000) => (3, 1),
|
||||
(VoltageScale::Scale1, ..=170_000_000) => (4, 2),
|
||||
(VoltageScale::Scale1, ..=200_000_000) => (5, 2),
|
||||
|
||||
(VoltageScale::Scale2, ..=30_000_000) => (0, 0),
|
||||
(VoltageScale::Scale2, ..=60_000_000) => (1, 0),
|
||||
(VoltageScale::Scale2, ..=90_000_000) => (2, 1),
|
||||
(VoltageScale::Scale2, ..=120_000_000) => (3, 1),
|
||||
(VoltageScale::Scale2, ..=150_000_000) => (4, 2),
|
||||
|
||||
(VoltageScale::Scale3, ..=20_000_000) => (0, 0),
|
||||
(VoltageScale::Scale3, ..=40_000_000) => (1, 0),
|
||||
(VoltageScale::Scale3, ..=60_000_000) => (2, 1),
|
||||
(VoltageScale::Scale3, ..=80_000_000) => (3, 1),
|
||||
(VoltageScale::Scale3, ..=100_000_000) => (4, 2),
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
#[cfg(flash_h7)]
|
||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||
// VOS 0 range VCORE 1.26V - 1.40V
|
||||
(VoltageScale::Scale0, ..=70_000_000) => (0, 0),
|
||||
(VoltageScale::Scale0, ..=140_000_000) => (1, 1),
|
||||
(VoltageScale::Scale0, ..=185_000_000) => (2, 1),
|
||||
(VoltageScale::Scale0, ..=210_000_000) => (2, 2),
|
||||
(VoltageScale::Scale0, ..=225_000_000) => (3, 2),
|
||||
(VoltageScale::Scale0, ..=240_000_000) => (4, 2),
|
||||
// VOS 1 range VCORE 1.15V - 1.26V
|
||||
(VoltageScale::Scale1, ..=70_000_000) => (0, 0),
|
||||
(VoltageScale::Scale1, ..=140_000_000) => (1, 1),
|
||||
(VoltageScale::Scale1, ..=185_000_000) => (2, 1),
|
||||
(VoltageScale::Scale1, ..=210_000_000) => (2, 2),
|
||||
(VoltageScale::Scale1, ..=225_000_000) => (3, 2),
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
(VoltageScale::Scale2, ..=55_000_000) => (0, 0),
|
||||
(VoltageScale::Scale2, ..=110_000_000) => (1, 1),
|
||||
(VoltageScale::Scale2, ..=165_000_000) => (2, 1),
|
||||
(VoltageScale::Scale2, ..=224_000_000) => (3, 2),
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
(VoltageScale::Scale3, ..=45_000_000) => (0, 0),
|
||||
(VoltageScale::Scale3, ..=90_000_000) => (1, 1),
|
||||
(VoltageScale::Scale3, ..=135_000_000) => (2, 1),
|
||||
(VoltageScale::Scale3, ..=180_000_000) => (3, 2),
|
||||
(VoltageScale::Scale3, ..=224_000_000) => (4, 2),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
|
||||
// states and programming delay
|
||||
#[cfg(flash_h7ab)]
|
||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||
// VOS 0 range VCORE 1.25V - 1.35V
|
||||
(VoltageScale::Scale0, ..=42_000_000) => (0, 0),
|
||||
(VoltageScale::Scale0, ..=84_000_000) => (1, 0),
|
||||
(VoltageScale::Scale0, ..=126_000_000) => (2, 1),
|
||||
(VoltageScale::Scale0, ..=168_000_000) => (3, 1),
|
||||
(VoltageScale::Scale0, ..=210_000_000) => (4, 2),
|
||||
(VoltageScale::Scale0, ..=252_000_000) => (5, 2),
|
||||
(VoltageScale::Scale0, ..=280_000_000) => (6, 3),
|
||||
// VOS 1 range VCORE 1.15V - 1.25V
|
||||
(VoltageScale::Scale1, ..=38_000_000) => (0, 0),
|
||||
(VoltageScale::Scale1, ..=76_000_000) => (1, 0),
|
||||
(VoltageScale::Scale1, ..=114_000_000) => (2, 1),
|
||||
(VoltageScale::Scale1, ..=152_000_000) => (3, 1),
|
||||
(VoltageScale::Scale1, ..=190_000_000) => (4, 2),
|
||||
(VoltageScale::Scale1, ..=225_000_000) => (5, 2),
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
(VoltageScale::Scale2, ..=34) => (0, 0),
|
||||
(VoltageScale::Scale2, ..=68) => (1, 0),
|
||||
(VoltageScale::Scale2, ..=102) => (2, 1),
|
||||
(VoltageScale::Scale2, ..=136) => (3, 1),
|
||||
(VoltageScale::Scale2, ..=160) => (4, 2),
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
(VoltageScale::Scale3, ..=22) => (0, 0),
|
||||
(VoltageScale::Scale3, ..=44) => (1, 0),
|
||||
(VoltageScale::Scale3, ..=66) => (2, 1),
|
||||
(VoltageScale::Scale3, ..=88) => (3, 1),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq);
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_wrhighfreq(wrhighfreq);
|
||||
w.set_latency(latency);
|
||||
});
|
||||
while FLASH.acr().read().latency() != latency {}
|
||||
}
|
@ -1,511 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use stm32_metapac::rcc::vals::Timpre;
|
||||
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
|
||||
|
||||
/// CSI speed
|
||||
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
|
||||
|
||||
/// HSI48 speed
|
||||
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
const VCO_MIN: u32 = 150_000_000;
|
||||
const VCO_MAX: u32 = 420_000_000;
|
||||
const VCO_WIDE_MIN: u32 = 128_000_000;
|
||||
const VCO_WIDE_MAX: u32 = 560_000_000;
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler, VoltageScale};
|
||||
|
||||
pub enum HseMode {
|
||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||
Oscillator,
|
||||
/// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
|
||||
BypassAnalog,
|
||||
/// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
|
||||
BypassDigital,
|
||||
}
|
||||
|
||||
pub struct Hse {
|
||||
/// HSE frequency.
|
||||
pub freq: Hertz,
|
||||
/// HSE mode.
|
||||
pub mode: HseMode,
|
||||
}
|
||||
|
||||
pub enum Hsi {
|
||||
/// 64Mhz
|
||||
Mhz64,
|
||||
/// 32Mhz (divided by 2)
|
||||
Mhz32,
|
||||
/// 16Mhz (divided by 4)
|
||||
Mhz16,
|
||||
/// 8Mhz (divided by 8)
|
||||
Mhz8,
|
||||
}
|
||||
|
||||
pub enum Sysclk {
|
||||
/// HSI selected as sysclk
|
||||
HSI,
|
||||
/// HSE selected as sysclk
|
||||
HSE,
|
||||
/// CSI selected as sysclk
|
||||
CSI,
|
||||
/// PLL1_P selected as sysclk
|
||||
Pll1P,
|
||||
}
|
||||
|
||||
pub enum PllSource {
|
||||
Hsi,
|
||||
Csi,
|
||||
Hse,
|
||||
}
|
||||
|
||||
pub struct Pll {
|
||||
/// Source clock selection.
|
||||
pub source: PllSource,
|
||||
|
||||
/// PLL pre-divider (DIVM). Must be between 1 and 63.
|
||||
pub prediv: u8,
|
||||
|
||||
/// PLL multiplication factor. Must be between 4 and 512.
|
||||
pub mul: u16,
|
||||
|
||||
/// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
|
||||
/// On PLL1, it must be even (in particular, it cannot be 1.)
|
||||
pub divp: Option<u16>,
|
||||
/// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
|
||||
pub divq: Option<u16>,
|
||||
/// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
|
||||
pub divr: Option<u16>,
|
||||
}
|
||||
|
||||
impl APBPrescaler {
|
||||
fn div_tim(&self, clk: Hertz, tim: TimerPrescaler) -> Hertz {
|
||||
match (tim, self) {
|
||||
// The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a
|
||||
// division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2
|
||||
(TimerPrescaler::DefaultX2, Self::NotDivided) => clk,
|
||||
(TimerPrescaler::DefaultX2, Self::Div2) => clk,
|
||||
(TimerPrescaler::DefaultX2, Self::Div4) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX2, Self::Div8) => clk / 4u32,
|
||||
(TimerPrescaler::DefaultX2, Self::Div16) => clk / 8u32,
|
||||
// The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2
|
||||
// corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2
|
||||
// this makes NO SENSE and is different than in the H7. Mistake in the RM??
|
||||
(TimerPrescaler::DefaultX4, Self::NotDivided) => clk * 2u32,
|
||||
(TimerPrescaler::DefaultX4, Self::Div2) => clk,
|
||||
(TimerPrescaler::DefaultX4, Self::Div4) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX4, Self::Div8) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX4, Self::Div16) => clk / 4u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// APB prescaler
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum TimerPrescaler {
|
||||
DefaultX2,
|
||||
DefaultX4,
|
||||
}
|
||||
|
||||
impl From<TimerPrescaler> for Timpre {
|
||||
fn from(value: TimerPrescaler) -> Self {
|
||||
match value {
|
||||
TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2,
|
||||
TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration of the core clocks
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
pub hsi: Option<Hsi>,
|
||||
pub hse: Option<Hse>,
|
||||
pub csi: bool,
|
||||
pub hsi48: bool,
|
||||
pub sys: Sysclk,
|
||||
|
||||
pub pll1: Option<Pll>,
|
||||
pub pll2: Option<Pll>,
|
||||
#[cfg(rcc_h5)]
|
||||
pub pll3: Option<Pll>,
|
||||
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub apb3_pre: APBPrescaler,
|
||||
pub timer_prescaler: TimerPrescaler,
|
||||
|
||||
pub voltage_scale: VoltageScale,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hsi: Some(Hsi::Mhz64),
|
||||
hse: None,
|
||||
csi: false,
|
||||
hsi48: false,
|
||||
sys: Sysclk::HSI,
|
||||
pll1: None,
|
||||
pll2: None,
|
||||
#[cfg(rcc_h5)]
|
||||
pll3: None,
|
||||
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
apb3_pre: APBPrescaler::NotDivided,
|
||||
timer_prescaler: TimerPrescaler::DefaultX2,
|
||||
|
||||
voltage_scale: VoltageScale::Scale3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait McoInstance {
|
||||
type Source;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoInstance: sealed::McoInstance + 'static {}
|
||||
|
||||
pin_trait!(McoPin, McoInstance);
|
||||
|
||||
macro_rules! impl_peri {
|
||||
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
|
||||
impl sealed::McoInstance for peripherals::$peri {
|
||||
type Source = $source;
|
||||
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.$set_source(source);
|
||||
w.$set_prescaler(prescaler);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl McoInstance for peripherals::$peri {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
|
||||
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
|
||||
|
||||
pub struct Mco<'d, T: McoInstance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: McoInstance> Mco<'d, T> {
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
_pin: impl Peripheral<P = impl McoPin<T>> + 'd,
|
||||
_source: T::Source,
|
||||
) -> Self {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let (vos, max_clk) = match config.voltage_scale {
|
||||
VoltageScale::Scale0 => (Vos::SCALE0, Hertz(250_000_000)),
|
||||
VoltageScale::Scale1 => (Vos::SCALE1, Hertz(200_000_000)),
|
||||
VoltageScale::Scale2 => (Vos::SCALE2, Hertz(150_000_000)),
|
||||
VoltageScale::Scale3 => (Vos::SCALE3, Hertz(100_000_000)),
|
||||
};
|
||||
|
||||
// Configure voltage scale.
|
||||
PWR.voscr().modify(|w| w.set_vos(vos));
|
||||
while !PWR.vossr().read().vosrdy() {}
|
||||
|
||||
// Configure HSI
|
||||
let hsi = match config.hsi {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
None
|
||||
}
|
||||
Some(hsi) => {
|
||||
let (freq, hsidiv) = match hsi {
|
||||
Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
|
||||
Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
|
||||
Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
|
||||
Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
|
||||
};
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsidiv(hsidiv);
|
||||
w.set_hsion(true);
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
Some(freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSE
|
||||
let hse = match config.hse {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hseon(false));
|
||||
None
|
||||
}
|
||||
Some(hse) => {
|
||||
let (byp, ext) = match hse.mode {
|
||||
HseMode::Oscillator => (false, Hseext::ANALOG),
|
||||
HseMode::BypassAnalog => (true, Hseext::ANALOG),
|
||||
HseMode::BypassDigital => (true, Hseext::DIGITAL),
|
||||
};
|
||||
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(byp);
|
||||
w.set_hseext(ext);
|
||||
});
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse.freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSI48.
|
||||
RCC.cr().modify(|w| w.set_hsi48on(config.hsi48));
|
||||
let _hsi48 = match config.hsi48 {
|
||||
false => None,
|
||||
true => {
|
||||
while !RCC.cr().read().hsi48rdy() {}
|
||||
Some(CSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure CSI.
|
||||
RCC.cr().modify(|w| w.set_csion(config.csi));
|
||||
let csi = match config.csi {
|
||||
false => None,
|
||||
true => {
|
||||
while !RCC.cr().read().csirdy() {}
|
||||
Some(CSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure PLLs.
|
||||
let pll_input = PllInput { csi, hse, hsi };
|
||||
let pll1 = init_pll(0, config.pll1, &pll_input);
|
||||
let _pll2 = init_pll(1, config.pll2, &pll_input);
|
||||
#[cfg(rcc_h5)]
|
||||
let _pll3 = init_pll(2, config.pll3, &pll_input);
|
||||
|
||||
// Configure sysclk
|
||||
let (sys, sw) = match config.sys {
|
||||
Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
|
||||
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
|
||||
Sysclk::CSI => (unwrap!(csi), Sw::CSI),
|
||||
Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
|
||||
};
|
||||
assert!(sys <= max_clk);
|
||||
|
||||
let hclk = sys / config.ahb_pre;
|
||||
|
||||
let apb1 = hclk / config.apb1_pre;
|
||||
let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler);
|
||||
let apb2 = hclk / config.apb2_pre;
|
||||
let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler);
|
||||
let apb3 = hclk / config.apb3_pre;
|
||||
|
||||
flash_setup(hclk, config.voltage_scale);
|
||||
|
||||
// Set hpre
|
||||
let hpre = config.ahb_pre.into();
|
||||
RCC.cfgr2().modify(|w| w.set_hpre(hpre));
|
||||
while RCC.cfgr2().read().hpre() != hpre {}
|
||||
|
||||
// set ppre
|
||||
RCC.cfgr2().modify(|w| {
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
w.set_ppre3(config.apb3_pre.into());
|
||||
});
|
||||
|
||||
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
|
||||
|
||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
||||
while RCC.cfgr().read().sws() != sw {}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys,
|
||||
ahb1: hclk,
|
||||
ahb2: hclk,
|
||||
ahb3: hclk,
|
||||
ahb4: hclk,
|
||||
apb1,
|
||||
apb2,
|
||||
apb3,
|
||||
apb1_tim,
|
||||
apb2_tim,
|
||||
adc: None,
|
||||
});
|
||||
}
|
||||
|
||||
struct PllInput {
|
||||
hsi: Option<Hertz>,
|
||||
hse: Option<Hertz>,
|
||||
csi: Option<Hertz>,
|
||||
}
|
||||
|
||||
struct PllOutput {
|
||||
p: Option<Hertz>,
|
||||
#[allow(dead_code)]
|
||||
q: Option<Hertz>,
|
||||
#[allow(dead_code)]
|
||||
r: Option<Hertz>,
|
||||
}
|
||||
|
||||
fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
let Some(config) = config else {
|
||||
// Stop PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(num, false));
|
||||
while RCC.cr().read().pllrdy(num) {}
|
||||
|
||||
// "To save power when PLL1 is not used, the value of PLL1M must be set to 0.""
|
||||
RCC.pllcfgr(num).write(|w| {
|
||||
w.set_divm(0);
|
||||
});
|
||||
|
||||
return PllOutput {
|
||||
p: None,
|
||||
q: None,
|
||||
r: None,
|
||||
};
|
||||
};
|
||||
|
||||
assert!(1 <= config.prediv && config.prediv <= 63);
|
||||
assert!(4 <= config.mul && config.mul <= 512);
|
||||
|
||||
let (in_clk, src) = match config.source {
|
||||
PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
|
||||
PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
|
||||
PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
|
||||
};
|
||||
|
||||
let ref_clk = in_clk / config.prediv as u32;
|
||||
|
||||
let ref_range = match ref_clk.0 {
|
||||
..=1_999_999 => Pllrge::RANGE1,
|
||||
..=3_999_999 => Pllrge::RANGE2,
|
||||
..=7_999_999 => Pllrge::RANGE4,
|
||||
..=16_000_000 => Pllrge::RANGE8,
|
||||
x => panic!("pll ref_clk out of range: {} mhz", x),
|
||||
};
|
||||
|
||||
// The smaller range (150 to 420 MHz) must
|
||||
// be chosen when the reference clock frequency is lower than 2 MHz.
|
||||
let wide_allowed = ref_range != Pllrge::RANGE1;
|
||||
|
||||
let vco_clk = ref_clk * config.mul;
|
||||
let vco_range = match vco_clk.0 {
|
||||
VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO,
|
||||
VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO,
|
||||
x => panic!("pll vco_clk out of range: {} mhz", x),
|
||||
};
|
||||
|
||||
let p = config.divp.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
if num == 0 {
|
||||
// on PLL1, DIVP must be even.
|
||||
assert!(div % 2 == 0);
|
||||
}
|
||||
|
||||
vco_clk / div
|
||||
});
|
||||
let q = config.divq.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
vco_clk / div
|
||||
});
|
||||
let r = config.divr.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
vco_clk / div
|
||||
});
|
||||
|
||||
RCC.pllcfgr(num).write(|w| {
|
||||
w.set_pllsrc(src);
|
||||
w.set_divm(config.prediv);
|
||||
w.set_pllvcosel(vco_range);
|
||||
w.set_pllrge(ref_range);
|
||||
w.set_pllfracen(false);
|
||||
w.set_pllpen(p.is_some());
|
||||
w.set_pllqen(q.is_some());
|
||||
w.set_pllren(r.is_some());
|
||||
});
|
||||
RCC.plldivr(num).write(|w| {
|
||||
w.set_plln(config.mul - 1);
|
||||
w.set_pllp((config.divp.unwrap_or(1) - 1) as u8);
|
||||
w.set_pllq((config.divq.unwrap_or(1) - 1) as u8);
|
||||
w.set_pllr((config.divr.unwrap_or(1) - 1) as u8);
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(num, true));
|
||||
while !RCC.cr().read().pllrdy(num) {}
|
||||
|
||||
PllOutput { p, q, r }
|
||||
}
|
||||
|
||||
fn flash_setup(clk: Hertz, vos: VoltageScale) {
|
||||
// RM0481 Rev 1, table 37
|
||||
// LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0
|
||||
// 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz
|
||||
// 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz
|
||||
// 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz
|
||||
// 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz
|
||||
// 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz
|
||||
// 5 2 170 to 200 MHz 210 to 250 MHz
|
||||
|
||||
// See RM0433 Rev 7 Table 17. FLASH recommended number of wait
|
||||
// states and programming delay
|
||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||
(VoltageScale::Scale0, ..=42_000_000) => (0, 0),
|
||||
(VoltageScale::Scale0, ..=84_000_000) => (1, 0),
|
||||
(VoltageScale::Scale0, ..=126_000_000) => (2, 1),
|
||||
(VoltageScale::Scale0, ..=168_000_000) => (3, 1),
|
||||
(VoltageScale::Scale0, ..=210_000_000) => (4, 2),
|
||||
(VoltageScale::Scale0, ..=250_000_000) => (5, 2),
|
||||
|
||||
(VoltageScale::Scale1, ..=34_000_000) => (0, 0),
|
||||
(VoltageScale::Scale1, ..=68_000_000) => (1, 0),
|
||||
(VoltageScale::Scale1, ..=102_000_000) => (2, 1),
|
||||
(VoltageScale::Scale1, ..=136_000_000) => (3, 1),
|
||||
(VoltageScale::Scale1, ..=170_000_000) => (4, 2),
|
||||
(VoltageScale::Scale1, ..=200_000_000) => (5, 2),
|
||||
|
||||
(VoltageScale::Scale2, ..=30_000_000) => (0, 0),
|
||||
(VoltageScale::Scale2, ..=60_000_000) => (1, 0),
|
||||
(VoltageScale::Scale2, ..=90_000_000) => (2, 1),
|
||||
(VoltageScale::Scale2, ..=120_000_000) => (3, 1),
|
||||
(VoltageScale::Scale2, ..=150_000_000) => (4, 2),
|
||||
|
||||
(VoltageScale::Scale3, ..=20_000_000) => (0, 0),
|
||||
(VoltageScale::Scale3, ..=40_000_000) => (1, 0),
|
||||
(VoltageScale::Scale3, ..=60_000_000) => (2, 1),
|
||||
(VoltageScale::Scale3, ..=80_000_000) => (3, 1),
|
||||
(VoltageScale::Scale3, ..=100_000_000) => (4, 2),
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
defmt::debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq);
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_wrhighfreq(wrhighfreq);
|
||||
w.set_latency(latency);
|
||||
});
|
||||
while FLASH.acr().read().latency() != latency {}
|
||||
}
|
@ -1,879 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
pub use pll::PllConfig;
|
||||
use stm32_metapac::rcc::vals::{Mco1, Mco2};
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
use crate::pac::rcc::vals::{Adcsel, Ckpersel, Dppre, Hpre, Hsidiv, Pllsrc, Sw, Timpre};
|
||||
use crate::pac::{PWR, RCC, SYSCFG};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
|
||||
|
||||
/// CSI speed
|
||||
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
|
||||
|
||||
/// HSI48 speed
|
||||
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
pub use super::bus::VoltageScale;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
Pll2PCk,
|
||||
Pll3RCk,
|
||||
PerCk,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn adcsel(&self) -> Adcsel {
|
||||
match self {
|
||||
AdcClockSource::Pll2PCk => Adcsel::PLL2_P,
|
||||
AdcClockSource::Pll3RCk => Adcsel::PLL3_R,
|
||||
AdcClockSource::PerCk => Adcsel::PER,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AdcClockSource {
|
||||
fn default() -> Self {
|
||||
Self::Pll2PCk
|
||||
}
|
||||
}
|
||||
|
||||
/// Core clock frequencies
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CoreClocks {
|
||||
pub hclk: Hertz,
|
||||
pub pclk1: Hertz,
|
||||
pub pclk2: Hertz,
|
||||
pub pclk3: Hertz,
|
||||
pub pclk4: Hertz,
|
||||
pub ppre1: u8,
|
||||
pub ppre2: u8,
|
||||
pub ppre3: u8,
|
||||
pub ppre4: u8,
|
||||
pub csi_ck: Option<Hertz>,
|
||||
pub hsi_ck: Option<Hertz>,
|
||||
pub hsi48_ck: Option<Hertz>,
|
||||
pub lsi_ck: Option<Hertz>,
|
||||
pub per_ck: Option<Hertz>,
|
||||
pub hse_ck: Option<Hertz>,
|
||||
pub pll1_p_ck: Option<Hertz>,
|
||||
pub pll1_q_ck: Option<Hertz>,
|
||||
pub pll1_r_ck: Option<Hertz>,
|
||||
pub pll2_p_ck: Option<Hertz>,
|
||||
pub pll2_q_ck: Option<Hertz>,
|
||||
pub pll2_r_ck: Option<Hertz>,
|
||||
pub pll3_p_ck: Option<Hertz>,
|
||||
pub pll3_q_ck: Option<Hertz>,
|
||||
pub pll3_r_ck: Option<Hertz>,
|
||||
pub timx_ker_ck: Option<Hertz>,
|
||||
pub timy_ker_ck: Option<Hertz>,
|
||||
pub adc_ker_ck: Option<Hertz>,
|
||||
pub sys_ck: Hertz,
|
||||
pub c_ck: Hertz,
|
||||
}
|
||||
|
||||
/// Configuration of the core clocks
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
pub hse: Option<Hertz>,
|
||||
pub bypass_hse: bool,
|
||||
pub sys_ck: Option<Hertz>,
|
||||
pub per_ck: Option<Hertz>,
|
||||
pub hclk: Option<Hertz>,
|
||||
pub pclk1: Option<Hertz>,
|
||||
pub pclk2: Option<Hertz>,
|
||||
pub pclk3: Option<Hertz>,
|
||||
pub pclk4: Option<Hertz>,
|
||||
pub pll1: PllConfig,
|
||||
pub pll2: PllConfig,
|
||||
pub pll3: PllConfig,
|
||||
pub adc_clock_source: AdcClockSource,
|
||||
}
|
||||
|
||||
/// Setup traceclk
|
||||
/// Returns a pll1_r_ck
|
||||
fn traceclk_setup(config: &mut Config, sys_use_pll1_p: bool) {
|
||||
let pll1_r_ck = match (sys_use_pll1_p, config.pll1.r_ck) {
|
||||
// pll1_p_ck selected as system clock but pll1_r_ck not
|
||||
// set. The traceclk mux is synchronous with the system
|
||||
// clock mux, but has pll1_r_ck as an input. In order to
|
||||
// keep traceclk running, we force a pll1_r_ck.
|
||||
(true, None) => Some(Hertz(unwrap!(config.pll1.p_ck).0 / 2)),
|
||||
|
||||
// Either pll1 not selected as system clock, free choice
|
||||
// of pll1_r_ck. Or pll1 is selected, assume user has set
|
||||
// a suitable pll1_r_ck frequency.
|
||||
_ => config.pll1.r_ck,
|
||||
};
|
||||
config.pll1.r_ck = pll1_r_ck;
|
||||
}
|
||||
|
||||
/// Divider calculator for pclk 1 - 4
|
||||
///
|
||||
/// Returns real pclk, bits, ppre and the timer kernel clock
|
||||
fn ppre_calculate(
|
||||
requested_pclk: u32,
|
||||
hclk: u32,
|
||||
max_pclk: u32,
|
||||
tim_pre: Option<Timpre>,
|
||||
) -> (u32, u8, u8, Option<u32>) {
|
||||
let (bits, ppre) = match (hclk + requested_pclk - 1) / requested_pclk {
|
||||
0 => panic!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let real_pclk = hclk / u32::from(ppre);
|
||||
assert!(real_pclk <= max_pclk);
|
||||
|
||||
let tim_ker_clk = if let Some(tim_pre) = tim_pre {
|
||||
let clk = match (bits, tim_pre) {
|
||||
(0b101, Timpre::DEFAULTX2) => hclk / 2,
|
||||
(0b110, Timpre::DEFAULTX4) => hclk / 2,
|
||||
(0b110, Timpre::DEFAULTX2) => hclk / 4,
|
||||
(0b111, Timpre::DEFAULTX4) => hclk / 4,
|
||||
(0b111, Timpre::DEFAULTX2) => hclk / 8,
|
||||
_ => hclk,
|
||||
};
|
||||
Some(clk)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(real_pclk, bits, ppre, tim_ker_clk)
|
||||
}
|
||||
|
||||
/// Setup sys_ck
|
||||
/// Returns sys_ck frequency, and a pll1_p_ck
|
||||
fn sys_ck_setup(config: &mut Config, srcclk: Hertz) -> (Hertz, bool) {
|
||||
// Compare available with wanted clocks
|
||||
let sys_ck = config.sys_ck.unwrap_or(srcclk);
|
||||
|
||||
if sys_ck != srcclk {
|
||||
// The requested system clock is not the immediately available
|
||||
// HSE/HSI clock. Perhaps there are other ways of obtaining
|
||||
// the requested system clock (such as `HSIDIV`) but we will
|
||||
// ignore those for now.
|
||||
//
|
||||
// Therefore we must use pll1_p_ck
|
||||
let pll1_p_ck = match config.pll1.p_ck {
|
||||
Some(p_ck) => {
|
||||
assert!(
|
||||
p_ck == sys_ck,
|
||||
"Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck"
|
||||
);
|
||||
Some(p_ck)
|
||||
}
|
||||
None => Some(sys_ck),
|
||||
};
|
||||
config.pll1.p_ck = pll1_p_ck;
|
||||
|
||||
(sys_ck, true)
|
||||
} else {
|
||||
// sys_ck is derived directly from a source clock
|
||||
// (HSE/HSI). pll1_p_ck can be as requested
|
||||
(sys_ck, false)
|
||||
}
|
||||
}
|
||||
|
||||
fn flash_setup(rcc_aclk: u32, vos: VoltageScale) {
|
||||
use crate::pac::FLASH;
|
||||
|
||||
// ACLK in MHz, round down and subtract 1 from integers. eg.
|
||||
// 61_999_999 -> 61MHz
|
||||
// 62_000_000 -> 61MHz
|
||||
// 62_000_001 -> 62MHz
|
||||
let rcc_aclk_mhz = (rcc_aclk - 1) / 1_000_000;
|
||||
|
||||
// See RM0433 Rev 7 Table 17. FLASH recommended number of wait
|
||||
// states and programming delay
|
||||
#[cfg(flash_h7)]
|
||||
let (wait_states, progr_delay) = match vos {
|
||||
// VOS 0 range VCORE 1.26V - 1.40V
|
||||
VoltageScale::Scale0 => match rcc_aclk_mhz {
|
||||
0..=69 => (0, 0),
|
||||
70..=139 => (1, 1),
|
||||
140..=184 => (2, 1),
|
||||
185..=209 => (2, 2),
|
||||
210..=224 => (3, 2),
|
||||
225..=239 => (4, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 1 range VCORE 1.15V - 1.26V
|
||||
VoltageScale::Scale1 => match rcc_aclk_mhz {
|
||||
0..=69 => (0, 0),
|
||||
70..=139 => (1, 1),
|
||||
140..=184 => (2, 1),
|
||||
185..=209 => (2, 2),
|
||||
210..=224 => (3, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::Scale2 => match rcc_aclk_mhz {
|
||||
0..=54 => (0, 0),
|
||||
55..=109 => (1, 1),
|
||||
110..=164 => (2, 1),
|
||||
165..=224 => (3, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::Scale3 => match rcc_aclk_mhz {
|
||||
0..=44 => (0, 0),
|
||||
45..=89 => (1, 1),
|
||||
90..=134 => (2, 1),
|
||||
135..=179 => (3, 2),
|
||||
180..=224 => (4, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
};
|
||||
|
||||
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
|
||||
// states and programming delay
|
||||
#[cfg(flash_h7ab)]
|
||||
let (wait_states, progr_delay) = match vos {
|
||||
// VOS 0 range VCORE 1.25V - 1.35V
|
||||
VoltageScale::Scale0 => match rcc_aclk_mhz {
|
||||
0..=42 => (0, 0),
|
||||
43..=84 => (1, 0),
|
||||
85..=126 => (2, 1),
|
||||
127..=168 => (3, 1),
|
||||
169..=210 => (4, 2),
|
||||
211..=252 => (5, 2),
|
||||
253..=280 => (6, 3),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 1 range VCORE 1.15V - 1.25V
|
||||
VoltageScale::Scale1 => match rcc_aclk_mhz {
|
||||
0..=38 => (0, 0),
|
||||
39..=76 => (1, 0),
|
||||
77..=114 => (2, 1),
|
||||
115..=152 => (3, 1),
|
||||
153..=190 => (4, 2),
|
||||
191..=225 => (5, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::Scale2 => match rcc_aclk_mhz {
|
||||
0..=34 => (0, 0),
|
||||
35..=68 => (1, 0),
|
||||
69..=102 => (2, 1),
|
||||
103..=136 => (3, 1),
|
||||
137..=160 => (4, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::Scale3 => match rcc_aclk_mhz {
|
||||
0..=22 => (0, 0),
|
||||
23..=44 => (1, 0),
|
||||
45..=66 => (2, 1),
|
||||
67..=88 => (3, 1),
|
||||
_ => (7, 3),
|
||||
},
|
||||
};
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_wrhighfreq(progr_delay);
|
||||
w.set_latency(wait_states)
|
||||
});
|
||||
while FLASH.acr().read().latency() != wait_states {}
|
||||
}
|
||||
|
||||
pub enum McoClock {
|
||||
Disabled,
|
||||
Bypassed,
|
||||
Divided(u8),
|
||||
}
|
||||
|
||||
impl McoClock {
|
||||
fn into_raw(&self) -> u8 {
|
||||
match self {
|
||||
McoClock::Disabled => 0,
|
||||
McoClock::Bypassed => 1,
|
||||
McoClock::Divided(divisor) => {
|
||||
if *divisor > 15 {
|
||||
panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.")
|
||||
}
|
||||
*divisor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Mco1Source {
|
||||
Hsi,
|
||||
Lse,
|
||||
Hse,
|
||||
Pll1Q,
|
||||
Hsi48,
|
||||
}
|
||||
|
||||
impl Default for Mco1Source {
|
||||
fn default() -> Self {
|
||||
Self::Hsi
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoSource {
|
||||
type Raw;
|
||||
|
||||
fn into_raw(&self) -> Self::Raw;
|
||||
}
|
||||
|
||||
impl McoSource for Mco1Source {
|
||||
type Raw = Mco1;
|
||||
fn into_raw(&self) -> Self::Raw {
|
||||
match self {
|
||||
Mco1Source::Hsi => Mco1::HSI,
|
||||
Mco1Source::Lse => Mco1::LSE,
|
||||
Mco1Source::Hse => Mco1::HSE,
|
||||
Mco1Source::Pll1Q => Mco1::PLL1_Q,
|
||||
Mco1Source::Hsi48 => Mco1::HSI48,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Mco2Source {
|
||||
SysClk,
|
||||
Pll2Q,
|
||||
Hse,
|
||||
Pll1Q,
|
||||
Csi,
|
||||
Lsi,
|
||||
}
|
||||
|
||||
impl Default for Mco2Source {
|
||||
fn default() -> Self {
|
||||
Self::SysClk
|
||||
}
|
||||
}
|
||||
|
||||
impl McoSource for Mco2Source {
|
||||
type Raw = Mco2;
|
||||
fn into_raw(&self) -> Self::Raw {
|
||||
match self {
|
||||
Mco2Source::SysClk => Mco2::SYSCLK,
|
||||
Mco2Source::Pll2Q => Mco2::PLL2_P,
|
||||
Mco2Source::Hse => Mco2::HSE,
|
||||
Mco2Source::Pll1Q => Mco2::PLL1_P,
|
||||
Mco2Source::Csi => Mco2::CSI,
|
||||
Mco2Source::Lsi => Mco2::LSI,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait McoInstance {
|
||||
type Source;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoInstance: sealed::McoInstance + 'static {}
|
||||
|
||||
pin_trait!(McoPin, McoInstance);
|
||||
|
||||
macro_rules! impl_peri {
|
||||
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
|
||||
impl sealed::McoInstance for peripherals::$peri {
|
||||
type Source = $source;
|
||||
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.$set_source(source);
|
||||
w.$set_prescaler(prescaler);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl McoInstance for peripherals::$peri {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
|
||||
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
|
||||
|
||||
pub struct Mco<'d, T: McoInstance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: McoInstance> Mco<'d, T> {
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
pin: impl Peripheral<P = impl McoPin<T>> + 'd,
|
||||
source: impl McoSource<Raw = T::Source>,
|
||||
prescaler: McoClock,
|
||||
) -> Self {
|
||||
into_ref!(pin);
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
|
||||
pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
|
||||
pin.set_speed(Speed::VeryHigh);
|
||||
});
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(mut config: Config) {
|
||||
// TODO make configurable?
|
||||
let enable_overdrive = false;
|
||||
|
||||
// NB. The lower bytes of CR3 can only be written once after
|
||||
// POR, and must be written with a valid combination. Refer to
|
||||
// RM0433 Rev 7 6.8.4. This is partially enforced by dropping
|
||||
// `self` at the end of this method, but of course we cannot
|
||||
// know what happened between the previous POR and here.
|
||||
#[cfg(pwr_h7)]
|
||||
PWR.cr3().modify(|w| {
|
||||
w.set_scuen(true);
|
||||
w.set_ldoen(true);
|
||||
w.set_bypass(false);
|
||||
});
|
||||
|
||||
#[cfg(pwr_h7smps)]
|
||||
PWR.cr3().modify(|w| {
|
||||
// hardcode "Direct SPMS" for now, this is what works on nucleos with the
|
||||
// default solderbridge configuration.
|
||||
w.set_sden(true);
|
||||
w.set_ldoen(false);
|
||||
});
|
||||
|
||||
// Validate the supply configuration. If you are stuck here, it is
|
||||
// because the voltages on your board do not match those specified
|
||||
// in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset
|
||||
// VOS = Scale 3, so check that the voltage on the VCAP pins =
|
||||
// 1.0V.
|
||||
while !PWR.csr1().read().actvosrdy() {}
|
||||
|
||||
// Go to Scale 1
|
||||
PWR.d3cr().modify(|w| w.set_vos(0b11));
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
|
||||
let pwr_vos = if !enable_overdrive {
|
||||
VoltageScale::Scale1
|
||||
} else {
|
||||
critical_section::with(|_| {
|
||||
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
|
||||
|
||||
SYSCFG.pwrcr().modify(|w| w.set_oden(1));
|
||||
});
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
VoltageScale::Scale0
|
||||
};
|
||||
|
||||
// Freeze the core clocks, returning a Core Clocks Distribution
|
||||
// and Reset (CCDR) structure. The actual frequency of the clocks
|
||||
// configured is returned in the `clocks` member of the CCDR
|
||||
// structure.
|
||||
//
|
||||
// Note that `freeze` will never result in a clock _faster_ than
|
||||
// that specified. It may result in a clock that is a factor of [1,
|
||||
// 2) slower.
|
||||
//
|
||||
// `syscfg` is required to enable the I/O compensation cell.
|
||||
//
|
||||
// # Panics
|
||||
//
|
||||
// If a clock specification cannot be achieved within the
|
||||
// hardware specification then this function will panic. This
|
||||
// function may also panic if a clock specification can be
|
||||
// achieved, but the mechanism for doing so is not yet
|
||||
// implemented here.
|
||||
|
||||
let srcclk = config.hse.unwrap_or(HSI_FREQ); // Available clocks
|
||||
let (sys_ck, sys_use_pll1_p) = sys_ck_setup(&mut config, srcclk);
|
||||
|
||||
// Configure traceclk from PLL if needed
|
||||
traceclk_setup(&mut config, sys_use_pll1_p);
|
||||
|
||||
let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = pll::pll_setup(srcclk.0, &config.pll1, 0);
|
||||
let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = pll::pll_setup(srcclk.0, &config.pll2, 1);
|
||||
let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = pll::pll_setup(srcclk.0, &config.pll3, 2);
|
||||
|
||||
let sys_ck = if sys_use_pll1_p {
|
||||
Hertz(unwrap!(pll1_p_ck)) // Must have been set by sys_ck_setup
|
||||
} else {
|
||||
sys_ck
|
||||
};
|
||||
|
||||
// This routine does not support HSIDIV != 1. To
|
||||
// do so it would need to ensure all PLLxON bits are clear
|
||||
// before changing the value of HSIDIV
|
||||
let cr = RCC.cr().read();
|
||||
assert!(cr.hsion());
|
||||
assert!(cr.hsidiv() == Hsidiv::DIV1);
|
||||
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
|
||||
// per_ck from HSI by default
|
||||
let (per_ck, ckpersel) = match (config.per_ck == config.hse, config.per_ck) {
|
||||
(true, Some(hse)) => (hse, Ckpersel::HSE), // HSE
|
||||
(_, Some(CSI_FREQ)) => (CSI_FREQ, Ckpersel::CSI), // CSI
|
||||
_ => (HSI_FREQ, Ckpersel::HSI), // HSI
|
||||
};
|
||||
|
||||
// D1 Core Prescaler
|
||||
// Set to 1
|
||||
let d1cpre_bits = 0;
|
||||
let d1cpre_div = 1;
|
||||
let sys_d1cpre_ck = sys_ck.0 / d1cpre_div;
|
||||
|
||||
// Refer to part datasheet "General operating conditions"
|
||||
// table for (rev V). We do not assert checks for earlier
|
||||
// revisions which may have lower limits.
|
||||
let (sys_d1cpre_ck_max, rcc_hclk_max, pclk_max) = match pwr_vos {
|
||||
VoltageScale::Scale0 => (480_000_000, 240_000_000, 120_000_000),
|
||||
VoltageScale::Scale1 => (400_000_000, 200_000_000, 100_000_000),
|
||||
VoltageScale::Scale2 => (300_000_000, 150_000_000, 75_000_000),
|
||||
_ => (200_000_000, 100_000_000, 50_000_000),
|
||||
};
|
||||
assert!(sys_d1cpre_ck <= sys_d1cpre_ck_max);
|
||||
|
||||
let rcc_hclk = config.hclk.map(|v| v.0).unwrap_or(sys_d1cpre_ck / 2);
|
||||
assert!(rcc_hclk <= rcc_hclk_max);
|
||||
|
||||
// Estimate divisor
|
||||
let (hpre_bits, hpre_div) = match (sys_d1cpre_ck + rcc_hclk - 1) / rcc_hclk {
|
||||
0 => panic!(),
|
||||
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 AXI and AHB clock
|
||||
let rcc_hclk = sys_d1cpre_ck / hpre_div;
|
||||
assert!(rcc_hclk <= rcc_hclk_max);
|
||||
let rcc_aclk = rcc_hclk; // AXI clock is always equal to AHB clock on H7
|
||||
// Timer prescaler selection
|
||||
let timpre = Timpre::DEFAULTX2;
|
||||
|
||||
let requested_pclk1 = config.pclk1.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk1, ppre1_bits, ppre1, rcc_timerx_ker_ck) =
|
||||
ppre_calculate(requested_pclk1, rcc_hclk, pclk_max, Some(timpre));
|
||||
|
||||
let requested_pclk2 = config.pclk2.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk2, ppre2_bits, ppre2, rcc_timery_ker_ck) =
|
||||
ppre_calculate(requested_pclk2, rcc_hclk, pclk_max, Some(timpre));
|
||||
|
||||
let requested_pclk3 = config.pclk3.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk3, ppre3_bits, ppre3, _) = ppre_calculate(requested_pclk3, rcc_hclk, pclk_max, None);
|
||||
|
||||
let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None);
|
||||
|
||||
// Start switching clocks -------------------
|
||||
|
||||
// Ensure CSI is on and stable
|
||||
RCC.cr().modify(|w| w.set_csion(true));
|
||||
while !RCC.cr().read().csirdy() {}
|
||||
|
||||
// Ensure HSI48 is on and stable
|
||||
RCC.cr().modify(|w| w.set_hsi48on(true));
|
||||
while !RCC.cr().read().hsi48on() {}
|
||||
|
||||
// XXX: support MCO ?
|
||||
|
||||
let hse_ck = match config.hse {
|
||||
Some(hse) => {
|
||||
// Ensure HSE is on and stable
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hseon(true);
|
||||
w.set_hsebyp(config.bypass_hse);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let pllsrc = if config.hse.is_some() { Pllsrc::HSE } else { Pllsrc::HSI };
|
||||
RCC.pllckselr().modify(|w| w.set_pllsrc(pllsrc));
|
||||
|
||||
let enable_pll = |pll| {
|
||||
RCC.cr().modify(|w| w.set_pllon(pll, true));
|
||||
while !RCC.cr().read().pllrdy(pll) {}
|
||||
};
|
||||
|
||||
if pll1_p_ck.is_some() {
|
||||
enable_pll(0);
|
||||
}
|
||||
|
||||
if pll2_p_ck.is_some() {
|
||||
enable_pll(1);
|
||||
}
|
||||
|
||||
if pll3_p_ck.is_some() {
|
||||
enable_pll(2);
|
||||
}
|
||||
|
||||
// Core Prescaler / AHB Prescaler / APB3 Prescaler
|
||||
RCC.d1cfgr().modify(|w| {
|
||||
w.set_d1cpre(Hpre::from_bits(d1cpre_bits));
|
||||
w.set_d1ppre(Dppre::from_bits(ppre3_bits));
|
||||
w.set_hpre(hpre_bits)
|
||||
});
|
||||
// Ensure core prescaler value is valid before future lower
|
||||
// core voltage
|
||||
while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {}
|
||||
|
||||
flash_setup(rcc_aclk, pwr_vos);
|
||||
|
||||
// APB1 / APB2 Prescaler
|
||||
RCC.d2cfgr().modify(|w| {
|
||||
w.set_d2ppre1(Dppre::from_bits(ppre1_bits));
|
||||
w.set_d2ppre2(Dppre::from_bits(ppre2_bits));
|
||||
});
|
||||
|
||||
// APB4 Prescaler
|
||||
RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre::from_bits(ppre4_bits)));
|
||||
|
||||
// Peripheral Clock (per_ck)
|
||||
RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
|
||||
|
||||
// ADC clock MUX
|
||||
RCC.d3ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel()));
|
||||
|
||||
let adc_ker_ck = match config.adc_clock_source {
|
||||
AdcClockSource::Pll2PCk => pll2_p_ck.map(Hertz),
|
||||
AdcClockSource::Pll3RCk => pll3_r_ck.map(Hertz),
|
||||
AdcClockSource::PerCk => Some(per_ck),
|
||||
};
|
||||
|
||||
// Set timer clocks prescaler setting
|
||||
RCC.cfgr().modify(|w| w.set_timpre(timpre));
|
||||
|
||||
// Select system clock source
|
||||
let sw = match (sys_use_pll1_p, config.hse.is_some()) {
|
||||
(true, _) => Sw::PLL1,
|
||||
(false, true) => Sw::HSE,
|
||||
_ => Sw::HSI,
|
||||
};
|
||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
||||
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
|
||||
|
||||
// IO compensation cell - Requires CSI clock and SYSCFG
|
||||
assert!(RCC.cr().read().csirdy());
|
||||
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
|
||||
|
||||
// Enable the compensation cell, using back-bias voltage code
|
||||
// provide by the cell.
|
||||
critical_section::with(|_| {
|
||||
SYSCFG.cccsr().modify(|w| {
|
||||
w.set_en(true);
|
||||
w.set_cs(false);
|
||||
w.set_hslv(false);
|
||||
})
|
||||
});
|
||||
while !SYSCFG.cccsr().read().ready() {}
|
||||
|
||||
let core_clocks = CoreClocks {
|
||||
hclk: Hertz(rcc_hclk),
|
||||
pclk1: Hertz(rcc_pclk1),
|
||||
pclk2: Hertz(rcc_pclk2),
|
||||
pclk3: Hertz(rcc_pclk3),
|
||||
pclk4: Hertz(rcc_pclk4),
|
||||
ppre1,
|
||||
ppre2,
|
||||
ppre3,
|
||||
ppre4,
|
||||
csi_ck: Some(CSI_FREQ),
|
||||
hsi_ck: Some(HSI_FREQ),
|
||||
hsi48_ck: Some(HSI48_FREQ),
|
||||
lsi_ck: Some(LSI_FREQ),
|
||||
per_ck: Some(per_ck),
|
||||
hse_ck,
|
||||
pll1_p_ck: pll1_p_ck.map(Hertz),
|
||||
pll1_q_ck: pll1_q_ck.map(Hertz),
|
||||
pll1_r_ck: pll1_r_ck.map(Hertz),
|
||||
pll2_p_ck: pll2_p_ck.map(Hertz),
|
||||
pll2_q_ck: pll2_q_ck.map(Hertz),
|
||||
pll2_r_ck: pll2_r_ck.map(Hertz),
|
||||
pll3_p_ck: pll3_p_ck.map(Hertz),
|
||||
pll3_q_ck: pll3_q_ck.map(Hertz),
|
||||
pll3_r_ck: pll3_r_ck.map(Hertz),
|
||||
timx_ker_ck: rcc_timerx_ker_ck.map(Hertz),
|
||||
timy_ker_ck: rcc_timery_ker_ck.map(Hertz),
|
||||
adc_ker_ck,
|
||||
sys_ck,
|
||||
c_ck: Hertz(sys_d1cpre_ck),
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: core_clocks.c_ck,
|
||||
ahb1: core_clocks.hclk,
|
||||
ahb2: core_clocks.hclk,
|
||||
ahb3: core_clocks.hclk,
|
||||
ahb4: core_clocks.hclk,
|
||||
apb1: core_clocks.pclk1,
|
||||
apb2: core_clocks.pclk2,
|
||||
apb4: core_clocks.pclk4,
|
||||
apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1),
|
||||
apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2),
|
||||
adc: core_clocks.adc_ker_ck,
|
||||
});
|
||||
}
|
||||
|
||||
mod pll {
|
||||
use super::{Hertz, RCC};
|
||||
|
||||
const VCO_MIN: u32 = 150_000_000;
|
||||
const VCO_MAX: u32 = 420_000_000;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PllConfig {
|
||||
pub p_ck: Option<Hertz>,
|
||||
pub q_ck: Option<Hertz>,
|
||||
pub r_ck: Option<Hertz>,
|
||||
}
|
||||
|
||||
pub(super) struct PllConfigResults {
|
||||
pub ref_x_ck: u32,
|
||||
pub pll_x_m: u32,
|
||||
pub pll_x_p: u32,
|
||||
pub vco_ck_target: u32,
|
||||
}
|
||||
|
||||
fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) {
|
||||
let pll_x_p = if plln == 0 {
|
||||
if output > VCO_MAX / 2 {
|
||||
1
|
||||
} else {
|
||||
((VCO_MAX / output) | 1) - 1 // Must be even or unity
|
||||
}
|
||||
} else {
|
||||
// Specific to PLL2/3, will subtract 1 later
|
||||
if output > VCO_MAX / 2 {
|
||||
1
|
||||
} else {
|
||||
VCO_MAX / output
|
||||
}
|
||||
};
|
||||
|
||||
let vco_ck = output * pll_x_p;
|
||||
|
||||
assert!(pll_x_p < 128);
|
||||
assert!(vco_ck >= VCO_MIN);
|
||||
assert!(vco_ck <= VCO_MAX);
|
||||
|
||||
(vco_ck, pll_x_p)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Must have exclusive access to the RCC register block
|
||||
fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults {
|
||||
use crate::pac::rcc::vals::{Pllrge, Pllvcosel};
|
||||
|
||||
let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln);
|
||||
|
||||
// Input divisor, resulting in a reference clock in the range
|
||||
// 1 to 2 MHz. Choose the highest reference clock (lowest m)
|
||||
let pll_x_m = (pll_src + 1_999_999) / 2_000_000;
|
||||
assert!(pll_x_m < 64);
|
||||
|
||||
// Calculate resulting reference clock
|
||||
let ref_x_ck = pll_src / pll_x_m;
|
||||
assert!((1_000_000..=2_000_000).contains(&ref_x_ck));
|
||||
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO);
|
||||
w.set_pllrge(plln, Pllrge::RANGE1);
|
||||
});
|
||||
PllConfigResults {
|
||||
ref_x_ck,
|
||||
pll_x_m,
|
||||
pll_x_p,
|
||||
vco_ck_target,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Must have exclusive access to the RCC register block
|
||||
pub(super) fn pll_setup(pll_src: u32, config: &PllConfig, plln: usize) -> (Option<u32>, Option<u32>, Option<u32>) {
|
||||
use crate::pac::rcc::vals::Divp;
|
||||
|
||||
match config.p_ck {
|
||||
Some(requested_output) => {
|
||||
let config_results = vco_setup(pll_src, requested_output.0, plln);
|
||||
let PllConfigResults {
|
||||
ref_x_ck,
|
||||
pll_x_m,
|
||||
pll_x_p,
|
||||
vco_ck_target,
|
||||
} = config_results;
|
||||
|
||||
RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8));
|
||||
|
||||
// Feedback divider. Integer only
|
||||
let pll_x_n = vco_ck_target / ref_x_ck;
|
||||
assert!(pll_x_n >= 4);
|
||||
assert!(pll_x_n <= 512);
|
||||
RCC.plldivr(plln).modify(|w| w.set_divn1((pll_x_n - 1) as u16));
|
||||
|
||||
// No FRACN
|
||||
RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
|
||||
let vco_ck = ref_x_ck * pll_x_n;
|
||||
|
||||
RCC.plldivr(plln)
|
||||
.modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8)));
|
||||
RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
|
||||
|
||||
// Calulate additional output dividers
|
||||
let q_ck = match config.q_ck {
|
||||
Some(Hertz(ck)) if ck > 0 => {
|
||||
let div = (vco_ck + ck - 1) / ck;
|
||||
RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8));
|
||||
RCC.pllcfgr().modify(|w| w.set_divqen(plln, true));
|
||||
Some(vco_ck / div)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let r_ck = match config.r_ck {
|
||||
Some(Hertz(ck)) if ck > 0 => {
|
||||
let div = (vco_ck + ck - 1) / ck;
|
||||
RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8));
|
||||
RCC.pllcfgr().modify(|w| w.set_divren(plln, true));
|
||||
Some(vco_ck / div)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
(Some(vco_ck / pll_x_p), q_ck, r_ck)
|
||||
}
|
||||
None => {
|
||||
assert!(
|
||||
config.q_ck.is_none(),
|
||||
"Must set PLL P clock for Q clock to take effect!"
|
||||
);
|
||||
assert!(
|
||||
config.r_ck.is_none(),
|
||||
"Must set PLL P clock for R clock to take effect!"
|
||||
);
|
||||
(None, None, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,318 +0,0 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::RCC;
|
||||
#[cfg(crs)]
|
||||
use crate::pac::{crs, CRS, SYSCFG};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
MSI(MSIRange),
|
||||
PLL(PLLSource, PLLMul, PLLDiv),
|
||||
HSE(Hertz),
|
||||
HSI16,
|
||||
}
|
||||
|
||||
/// MSI Clock Range
|
||||
///
|
||||
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
||||
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum MSIRange {
|
||||
/// Around 65.536 kHz
|
||||
Range0,
|
||||
/// Around 131.072 kHz
|
||||
Range1,
|
||||
/// Around 262.144 kHz
|
||||
Range2,
|
||||
/// Around 524.288 kHz
|
||||
Range3,
|
||||
/// Around 1.048 MHz
|
||||
Range4,
|
||||
/// Around 2.097 MHz (reset value)
|
||||
Range5,
|
||||
/// Around 4.194 MHz
|
||||
Range6,
|
||||
}
|
||||
|
||||
impl Default for MSIRange {
|
||||
fn default() -> MSIRange {
|
||||
MSIRange::Range5
|
||||
}
|
||||
}
|
||||
|
||||
/// PLL divider
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLDiv {
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
}
|
||||
|
||||
/// PLL multiplier
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLMul {
|
||||
Mul3,
|
||||
Mul4,
|
||||
Mul6,
|
||||
Mul8,
|
||||
Mul12,
|
||||
Mul16,
|
||||
Mul24,
|
||||
Mul32,
|
||||
Mul48,
|
||||
}
|
||||
|
||||
/// PLL clock input source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLSource {
|
||||
HSI16,
|
||||
HSE(Hertz),
|
||||
}
|
||||
|
||||
impl From<PLLMul> for Pllmul {
|
||||
fn from(val: PLLMul) -> Pllmul {
|
||||
match val {
|
||||
PLLMul::Mul3 => Pllmul::MUL3,
|
||||
PLLMul::Mul4 => Pllmul::MUL4,
|
||||
PLLMul::Mul6 => Pllmul::MUL6,
|
||||
PLLMul::Mul8 => Pllmul::MUL8,
|
||||
PLLMul::Mul12 => Pllmul::MUL12,
|
||||
PLLMul::Mul16 => Pllmul::MUL16,
|
||||
PLLMul::Mul24 => Pllmul::MUL24,
|
||||
PLLMul::Mul32 => Pllmul::MUL32,
|
||||
PLLMul::Mul48 => Pllmul::MUL48,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PLLDiv> for Plldiv {
|
||||
fn from(val: PLLDiv) -> Plldiv {
|
||||
match val {
|
||||
PLLDiv::Div2 => Plldiv::DIV2,
|
||||
PLLDiv::Div3 => Plldiv::DIV3,
|
||||
PLLDiv::Div4 => Plldiv::DIV4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PLLSource> for Pllsrc {
|
||||
fn from(val: PLLSource) -> Pllsrc {
|
||||
match val {
|
||||
PLLSource::HSI16 => Pllsrc::HSI16,
|
||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MSIRange> for Msirange {
|
||||
fn from(val: MSIRange) -> Msirange {
|
||||
match val {
|
||||
MSIRange::Range0 => Msirange::RANGE0,
|
||||
MSIRange::Range1 => Msirange::RANGE1,
|
||||
MSIRange::Range2 => Msirange::RANGE2,
|
||||
MSIRange::Range3 => Msirange::RANGE3,
|
||||
MSIRange::Range4 => Msirange::RANGE4,
|
||||
MSIRange::Range5 => Msirange::RANGE5,
|
||||
MSIRange::Range6 => Msirange::RANGE6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
#[cfg(crs)]
|
||||
pub enable_hsi48: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
#[inline]
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::default()),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
#[cfg(crs)]
|
||||
enable_hsi48: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
// Set MSI range
|
||||
RCC.icscr().write(|w| w.set_msirange(range.into()));
|
||||
|
||||
// Enable MSI
|
||||
RCC.cr().write(|w| w.set_msion(true));
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
|
||||
let freq = 32_768 * (1 << (range as u8 + 1));
|
||||
(freq, Sw::MSI)
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
||||
while !RCC.cr().read().hsi16rdyf() {}
|
||||
|
||||
(HSI_FREQ.0, Sw::HSI16)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(src, mul, div) => {
|
||||
let freq = match src {
|
||||
PLLSource::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
freq.0
|
||||
}
|
||||
PLLSource::HSI16 => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
||||
while !RCC.cr().read().hsi16rdyf() {}
|
||||
HSI_FREQ.0
|
||||
}
|
||||
};
|
||||
|
||||
// Disable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let freq = match mul {
|
||||
PLLMul::Mul3 => freq * 3,
|
||||
PLLMul::Mul4 => freq * 4,
|
||||
PLLMul::Mul6 => freq * 6,
|
||||
PLLMul::Mul8 => freq * 8,
|
||||
PLLMul::Mul12 => freq * 12,
|
||||
PLLMul::Mul16 => freq * 16,
|
||||
PLLMul::Mul24 => freq * 24,
|
||||
PLLMul::Mul32 => freq * 32,
|
||||
PLLMul::Mul48 => freq * 48,
|
||||
};
|
||||
|
||||
let freq = match div {
|
||||
PLLDiv::Div2 => freq / 2,
|
||||
PLLDiv::Div3 => freq / 3,
|
||||
PLLDiv::Div4 => freq / 4,
|
||||
};
|
||||
assert!(freq <= 32_000_000);
|
||||
|
||||
RCC.cfgr().write(move |w| {
|
||||
w.set_pllmul(mul.into());
|
||||
w.set_plldiv(div.into());
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
// Enable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
(freq, Sw::PLL)
|
||||
}
|
||||
};
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(crs)]
|
||||
if config.enable_hsi48 {
|
||||
// Reset SYSCFG peripheral
|
||||
RCC.apb2rstr().modify(|w| w.set_syscfgrst(true));
|
||||
RCC.apb2rstr().modify(|w| w.set_syscfgrst(false));
|
||||
|
||||
// Enable SYSCFG peripheral
|
||||
RCC.apb2enr().modify(|w| w.set_syscfgen(true));
|
||||
|
||||
// Reset CRS peripheral
|
||||
RCC.apb1rstr().modify(|w| w.set_crsrst(true));
|
||||
RCC.apb1rstr().modify(|w| w.set_crsrst(false));
|
||||
|
||||
// Enable CRS peripheral
|
||||
RCC.apb1enr().modify(|w| w.set_crsen(true));
|
||||
|
||||
// Initialize CRS
|
||||
CRS.cfgr().write(|w|
|
||||
|
||||
// Select LSE as synchronization source
|
||||
w.set_syncsrc(crs::vals::Syncsrc::LSE));
|
||||
CRS.cr().modify(|w| {
|
||||
w.set_autotrimen(true);
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
// Enable VREFINT reference for HSI48 oscillator
|
||||
SYSCFG.cfgr3().modify(|w| {
|
||||
w.set_enref_hsi48(true);
|
||||
w.set_en_vrefint(true);
|
||||
});
|
||||
|
||||
// Select HSI48 as USB clock
|
||||
RCC.ccipr().modify(|w| w.set_hsi48msel(true));
|
||||
|
||||
// Enable dedicated USB clock
|
||||
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
||||
while !RCC.crrcr().read().hsi48rdy() {}
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
ahb1: Hertz(ahb_freq),
|
||||
apb1: Hertz(apb1_freq),
|
||||
apb2: Hertz(apb2_freq),
|
||||
apb1_tim: Hertz(apb1_tim_freq),
|
||||
apb2_tim: Hertz(apb2_tim_freq),
|
||||
});
|
||||
}
|
219
embassy-stm32/src/rcc/l0l1.rs
Normal file
219
embassy-stm32/src/rcc/l0l1.rs
Normal file
@ -0,0 +1,219 @@
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
pub use crate::pac::rcc::vals::{
|
||||
Hpre as AHBPrescaler, Msirange as MSIRange, Plldiv as PLLDiv, Pllmul as PLLMul, Ppre as APBPrescaler,
|
||||
};
|
||||
use crate::pac::rcc::vals::{Pllsrc, Sw};
|
||||
#[cfg(crs)]
|
||||
use crate::pac::{crs, CRS, SYSCFG};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
MSI(MSIRange),
|
||||
PLL(PLLSource, PLLMul, PLLDiv),
|
||||
HSE(Hertz),
|
||||
HSI16,
|
||||
}
|
||||
|
||||
/// PLL clock input source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLSource {
|
||||
HSI16,
|
||||
HSE(Hertz),
|
||||
}
|
||||
|
||||
impl From<PLLSource> for Pllsrc {
|
||||
fn from(val: PLLSource) -> Pllsrc {
|
||||
match val {
|
||||
PLLSource::HSI16 => Pllsrc::HSI16,
|
||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
#[cfg(crs)]
|
||||
pub enable_hsi48: bool,
|
||||
pub ls: super::LsConfig,
|
||||
pub voltage_scale: VoltageScale,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
#[inline]
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::RANGE5),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
#[cfg(crs)]
|
||||
enable_hsi48: false,
|
||||
voltage_scale: VoltageScale::RANGE1,
|
||||
ls: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// Set voltage scale
|
||||
while PWR.csr().read().vosf() {}
|
||||
PWR.cr().write(|w| w.set_vos(config.voltage_scale));
|
||||
while PWR.csr().read().vosf() {}
|
||||
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
// Set MSI range
|
||||
RCC.icscr().write(|w| w.set_msirange(range));
|
||||
|
||||
// Enable MSI
|
||||
RCC.cr().write(|w| w.set_msion(true));
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
|
||||
let freq = 32_768 * (1 << (range as u8 + 1));
|
||||
(Hertz(freq), Sw::MSI)
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
||||
while !RCC.cr().read().hsi16rdy() {}
|
||||
|
||||
(HSI_FREQ, Sw::HSI16)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(src, mul, div) => {
|
||||
let freq = match src {
|
||||
PLLSource::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
freq
|
||||
}
|
||||
PLLSource::HSI16 => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
||||
while !RCC.cr().read().hsi16rdy() {}
|
||||
HSI_FREQ
|
||||
}
|
||||
};
|
||||
|
||||
// Disable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let freq = freq * mul / div;
|
||||
|
||||
assert!(freq <= Hertz(32_000_000));
|
||||
|
||||
RCC.cfgr().write(move |w| {
|
||||
w.set_pllmul(mul);
|
||||
w.set_plldiv(div);
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
// Enable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
(freq, Sw::PLL)
|
||||
}
|
||||
};
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
let wait_states = match (config.voltage_scale, sys_clk.0) {
|
||||
(VoltageScale::RANGE1, ..=16_000_000) => 0,
|
||||
(VoltageScale::RANGE2, ..=8_000_000) => 0,
|
||||
(VoltageScale::RANGE3, ..=4_200_000) => 0,
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
#[cfg(stm32l1)]
|
||||
FLASH.acr().write(|w| w.set_acc64(true));
|
||||
FLASH.acr().modify(|w| w.set_prften(true));
|
||||
FLASH.acr().modify(|w| w.set_latency(wait_states != 0));
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(config.ahb_pre);
|
||||
w.set_ppre1(config.apb1_pre);
|
||||
w.set_ppre2(config.apb2_pre);
|
||||
});
|
||||
|
||||
let ahb_freq = sys_clk / config.ahb_pre;
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(crs)]
|
||||
if config.enable_hsi48 {
|
||||
// Reset CRS peripheral
|
||||
RCC.apb1rstr().modify(|w| w.set_crsrst(true));
|
||||
RCC.apb1rstr().modify(|w| w.set_crsrst(false));
|
||||
|
||||
// Enable CRS peripheral
|
||||
RCC.apb1enr().modify(|w| w.set_crsen(true));
|
||||
|
||||
// Initialize CRS
|
||||
CRS.cfgr().write(|w|
|
||||
|
||||
// Select LSE as synchronization source
|
||||
w.set_syncsrc(crs::vals::Syncsrc::LSE));
|
||||
CRS.cr().modify(|w| {
|
||||
w.set_autotrimen(true);
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
// Enable VREFINT reference for HSI48 oscillator
|
||||
SYSCFG.cfgr3().modify(|w| {
|
||||
w.set_enref_hsi48(true);
|
||||
w.set_en_vrefint(true);
|
||||
});
|
||||
|
||||
// Select HSI48 as USB clock
|
||||
RCC.ccipr().modify(|w| w.set_hsi48msel(true));
|
||||
|
||||
// Enable dedicated USB clock
|
||||
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
||||
while !RCC.crrcr().read().hsi48rdy() {}
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk,
|
||||
ahb1: ahb_freq,
|
||||
apb1: apb1_freq,
|
||||
apb2: apb2_freq,
|
||||
apb1_tim: apb1_tim_freq,
|
||||
apb2_tim: apb2_tim_freq,
|
||||
rtc,
|
||||
});
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user