diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-04-06 18:53:51 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-04-06 18:59:37 +0200 |
| commit | 611d0238290c9f7b3b23d05ec07adf5b48ea3479 (patch) | |
| tree | 4307153f4ef858b6ed7fea703e9fbfe4eddb673a | |
| parent | 9f28d8097704f51fb7d7dcc8d459ce86aaf07eff (diff) | |
stm32: add H5 support.
| -rwxr-xr-x | ci.sh | 3 | ||||
| -rw-r--r-- | embassy-stm32/Cargo.toml | 73 | ||||
| -rw-r--r-- | embassy-stm32/build.rs | 7 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/v2/mod.rs | 25 | ||||
| -rw-r--r-- | embassy-stm32/src/exti.rs | 18 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/h5.rs | 606 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 13 | ||||
| -rw-r--r-- | embassy-stm32/src/time.rs | 46 | ||||
| -rw-r--r-- | embassy-stm32/src/usb/usb.rs | 5 | ||||
| -rw-r--r-- | examples/stm32h5/.cargo/config.toml | 8 | ||||
| -rw-r--r-- | examples/stm32h5/Cargo.toml | 71 | ||||
| -rw-r--r-- | examples/stm32h5/build.rs | 5 | ||||
| -rw-r--r-- | examples/stm32h5/memory.x | 5 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/blinky.rs | 27 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/button_exti.rs | 27 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/eth.rs | 133 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/i2c.rs | 44 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/rng.rs | 20 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/usart.rs | 43 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/usart_dma.rs | 46 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/usart_split.rs | 58 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/usb_serial.rs | 128 |
22 files changed, 1390 insertions, 21 deletions
| @@ -66,6 +66,8 @@ cargo batch \ | |||
| 66 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ | 66 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ |
| 67 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ | 67 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ |
| 68 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ | 68 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ |
| 69 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \ | ||
| 70 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \ | ||
| 69 | --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ | 71 | --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ |
| 70 | --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ | 72 | --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ |
| 71 | --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \ | 73 | --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \ |
| @@ -87,6 +89,7 @@ cargo batch \ | |||
| 87 | --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ | 89 | --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ |
| 88 | --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \ | 90 | --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \ |
| 89 | --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ | 91 | --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ |
| 92 | --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h5 \ | ||
| 90 | --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ | 93 | --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ |
| 91 | --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \ | 94 | --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \ |
| 92 | --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ | 95 | --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ |
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index da1dc24bf..3fd1e4b4e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -834,6 +834,37 @@ stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] | |||
| 834 | stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] | 834 | stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] |
| 835 | stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] | 835 | stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] |
| 836 | stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] | 836 | stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] |
| 837 | stm32h503cb = [ "stm32-metapac/stm32h503cb" ] | ||
| 838 | stm32h503eb = [ "stm32-metapac/stm32h503eb" ] | ||
| 839 | stm32h503kb = [ "stm32-metapac/stm32h503kb" ] | ||
| 840 | stm32h503rb = [ "stm32-metapac/stm32h503rb" ] | ||
| 841 | stm32h562ag = [ "stm32-metapac/stm32h562ag" ] | ||
| 842 | stm32h562ai = [ "stm32-metapac/stm32h562ai" ] | ||
| 843 | stm32h562ig = [ "stm32-metapac/stm32h562ig" ] | ||
| 844 | stm32h562ii = [ "stm32-metapac/stm32h562ii" ] | ||
| 845 | stm32h562rg = [ "stm32-metapac/stm32h562rg" ] | ||
| 846 | stm32h562ri = [ "stm32-metapac/stm32h562ri" ] | ||
| 847 | stm32h562vg = [ "stm32-metapac/stm32h562vg" ] | ||
| 848 | stm32h562vi = [ "stm32-metapac/stm32h562vi" ] | ||
| 849 | stm32h562zg = [ "stm32-metapac/stm32h562zg" ] | ||
| 850 | stm32h562zi = [ "stm32-metapac/stm32h562zi" ] | ||
| 851 | stm32h563ag = [ "stm32-metapac/stm32h563ag" ] | ||
| 852 | stm32h563ai = [ "stm32-metapac/stm32h563ai" ] | ||
| 853 | stm32h563ig = [ "stm32-metapac/stm32h563ig" ] | ||
| 854 | stm32h563ii = [ "stm32-metapac/stm32h563ii" ] | ||
| 855 | stm32h563mi = [ "stm32-metapac/stm32h563mi" ] | ||
| 856 | stm32h563rg = [ "stm32-metapac/stm32h563rg" ] | ||
| 857 | stm32h563ri = [ "stm32-metapac/stm32h563ri" ] | ||
| 858 | stm32h563vg = [ "stm32-metapac/stm32h563vg" ] | ||
| 859 | stm32h563vi = [ "stm32-metapac/stm32h563vi" ] | ||
| 860 | stm32h563zg = [ "stm32-metapac/stm32h563zg" ] | ||
| 861 | stm32h563zi = [ "stm32-metapac/stm32h563zi" ] | ||
| 862 | stm32h573ai = [ "stm32-metapac/stm32h573ai" ] | ||
| 863 | stm32h573ii = [ "stm32-metapac/stm32h573ii" ] | ||
| 864 | stm32h573mi = [ "stm32-metapac/stm32h573mi" ] | ||
| 865 | stm32h573ri = [ "stm32-metapac/stm32h573ri" ] | ||
| 866 | stm32h573vi = [ "stm32-metapac/stm32h573vi" ] | ||
| 867 | stm32h573zi = [ "stm32-metapac/stm32h573zi" ] | ||
| 837 | stm32h723ve = [ "stm32-metapac/stm32h723ve" ] | 868 | stm32h723ve = [ "stm32-metapac/stm32h723ve" ] |
| 838 | stm32h723vg = [ "stm32-metapac/stm32h723vg" ] | 869 | stm32h723vg = [ "stm32-metapac/stm32h723vg" ] |
| 839 | stm32h723ze = [ "stm32-metapac/stm32h723ze" ] | 870 | stm32h723ze = [ "stm32-metapac/stm32h723ze" ] |
| @@ -1316,6 +1347,22 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] | |||
| 1316 | stm32l562re = [ "stm32-metapac/stm32l562re" ] | 1347 | stm32l562re = [ "stm32-metapac/stm32l562re" ] |
| 1317 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] | 1348 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] |
| 1318 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] | 1349 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] |
| 1350 | stm32u535cb = [ "stm32-metapac/stm32u535cb" ] | ||
| 1351 | stm32u535cc = [ "stm32-metapac/stm32u535cc" ] | ||
| 1352 | stm32u535ce = [ "stm32-metapac/stm32u535ce" ] | ||
| 1353 | stm32u535je = [ "stm32-metapac/stm32u535je" ] | ||
| 1354 | stm32u535nc = [ "stm32-metapac/stm32u535nc" ] | ||
| 1355 | stm32u535ne = [ "stm32-metapac/stm32u535ne" ] | ||
| 1356 | stm32u535rb = [ "stm32-metapac/stm32u535rb" ] | ||
| 1357 | stm32u535rc = [ "stm32-metapac/stm32u535rc" ] | ||
| 1358 | stm32u535re = [ "stm32-metapac/stm32u535re" ] | ||
| 1359 | stm32u535vc = [ "stm32-metapac/stm32u535vc" ] | ||
| 1360 | stm32u535ve = [ "stm32-metapac/stm32u535ve" ] | ||
| 1361 | stm32u545ce = [ "stm32-metapac/stm32u545ce" ] | ||
| 1362 | stm32u545je = [ "stm32-metapac/stm32u545je" ] | ||
| 1363 | stm32u545ne = [ "stm32-metapac/stm32u545ne" ] | ||
| 1364 | stm32u545re = [ "stm32-metapac/stm32u545re" ] | ||
| 1365 | stm32u545ve = [ "stm32-metapac/stm32u545ve" ] | ||
| 1319 | stm32u575ag = [ "stm32-metapac/stm32u575ag" ] | 1366 | stm32u575ag = [ "stm32-metapac/stm32u575ag" ] |
| 1320 | stm32u575ai = [ "stm32-metapac/stm32u575ai" ] | 1367 | stm32u575ai = [ "stm32-metapac/stm32u575ai" ] |
| 1321 | stm32u575cg = [ "stm32-metapac/stm32u575cg" ] | 1368 | stm32u575cg = [ "stm32-metapac/stm32u575cg" ] |
| @@ -1337,6 +1384,32 @@ stm32u585qi = [ "stm32-metapac/stm32u585qi" ] | |||
| 1337 | stm32u585ri = [ "stm32-metapac/stm32u585ri" ] | 1384 | stm32u585ri = [ "stm32-metapac/stm32u585ri" ] |
| 1338 | stm32u585vi = [ "stm32-metapac/stm32u585vi" ] | 1385 | stm32u585vi = [ "stm32-metapac/stm32u585vi" ] |
| 1339 | stm32u585zi = [ "stm32-metapac/stm32u585zi" ] | 1386 | stm32u585zi = [ "stm32-metapac/stm32u585zi" ] |
| 1387 | stm32u595ai = [ "stm32-metapac/stm32u595ai" ] | ||
| 1388 | stm32u595aj = [ "stm32-metapac/stm32u595aj" ] | ||
| 1389 | stm32u595qi = [ "stm32-metapac/stm32u595qi" ] | ||
| 1390 | stm32u595qj = [ "stm32-metapac/stm32u595qj" ] | ||
| 1391 | stm32u595ri = [ "stm32-metapac/stm32u595ri" ] | ||
| 1392 | stm32u595rj = [ "stm32-metapac/stm32u595rj" ] | ||
| 1393 | stm32u595vi = [ "stm32-metapac/stm32u595vi" ] | ||
| 1394 | stm32u595vj = [ "stm32-metapac/stm32u595vj" ] | ||
| 1395 | stm32u595zi = [ "stm32-metapac/stm32u595zi" ] | ||
| 1396 | stm32u595zj = [ "stm32-metapac/stm32u595zj" ] | ||
| 1397 | stm32u599bj = [ "stm32-metapac/stm32u599bj" ] | ||
| 1398 | stm32u599ni = [ "stm32-metapac/stm32u599ni" ] | ||
| 1399 | stm32u599nj = [ "stm32-metapac/stm32u599nj" ] | ||
| 1400 | stm32u599vi = [ "stm32-metapac/stm32u599vi" ] | ||
| 1401 | stm32u599vj = [ "stm32-metapac/stm32u599vj" ] | ||
| 1402 | stm32u599zi = [ "stm32-metapac/stm32u599zi" ] | ||
| 1403 | stm32u599zj = [ "stm32-metapac/stm32u599zj" ] | ||
| 1404 | stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] | ||
| 1405 | stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] | ||
| 1406 | stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] | ||
| 1407 | stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] | ||
| 1408 | stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] | ||
| 1409 | stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] | ||
| 1410 | stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] | ||
| 1411 | stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] | ||
| 1412 | stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] | ||
| 1340 | stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] | 1413 | stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] |
| 1341 | stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] | 1414 | stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] |
| 1342 | stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] | 1415 | stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 61aceed93..b01e8ba45 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -50,7 +50,7 @@ fn main() { | |||
| 50 | // We *shouldn't* have singletons for these, but the HAL currently requires | 50 | // We *shouldn't* have singletons for these, but the HAL currently requires |
| 51 | // singletons, for using with RccPeripheral to enable/disable clocks to them. | 51 | // singletons, for using with RccPeripheral to enable/disable clocks to them. |
| 52 | "rcc" => { | 52 | "rcc" => { |
| 53 | if r.version.starts_with("h7") || r.version.starts_with("f4") { | 53 | if r.version.starts_with("h5") || r.version.starts_with("h7") || r.version.starts_with("f4") { |
| 54 | singletons.push("MCO1".to_string()); | 54 | singletons.push("MCO1".to_string()); |
| 55 | singletons.push("MCO2".to_string()); | 55 | singletons.push("MCO2".to_string()); |
| 56 | } | 56 | } |
| @@ -539,7 +539,10 @@ fn main() { | |||
| 539 | // MCO is special | 539 | // MCO is special |
| 540 | if pin.signal.starts_with("MCO_") { | 540 | if pin.signal.starts_with("MCO_") { |
| 541 | // Supported in H7 only for now | 541 | // Supported in H7 only for now |
| 542 | if regs.version.starts_with("h7") || regs.version.starts_with("f4") { | 542 | if regs.version.starts_with("h5") |
| 543 | || regs.version.starts_with("h7") | ||
| 544 | || regs.version.starts_with("f4") | ||
| 545 | { | ||
| 543 | peri = format_ident!("{}", pin.signal.replace("_", "")); | 546 | peri = format_ident!("{}", pin.signal.replace("_", "")); |
| 544 | } else { | 547 | } else { |
| 545 | continue; | 548 | continue; |
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index fcb4a296c..d49b1f767 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -9,7 +9,7 @@ pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; | |||
| 9 | use super::*; | 9 | use super::*; |
| 10 | use crate::gpio::sealed::{AFType, Pin as _}; | 10 | use crate::gpio::sealed::{AFType, Pin as _}; |
| 11 | use crate::gpio::{AnyPin, Speed}; | 11 | use crate::gpio::{AnyPin, Speed}; |
| 12 | use crate::pac::{ETH, RCC, SYSCFG}; | 12 | use crate::pac::ETH; |
| 13 | use crate::Peripheral; | 13 | use crate::Peripheral; |
| 14 | 14 | ||
| 15 | const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet | 15 | const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet |
| @@ -60,16 +60,33 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 60 | unsafe { | 60 | unsafe { |
| 61 | // Enable the necessary Clocks | 61 | // Enable the necessary Clocks |
| 62 | // NOTE(unsafe) We have exclusive access to the registers | 62 | // NOTE(unsafe) We have exclusive access to the registers |
| 63 | #[cfg(not(rcc_h5))] | ||
| 63 | critical_section::with(|_| { | 64 | critical_section::with(|_| { |
| 64 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | 65 | crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true)); |
| 65 | RCC.ahb1enr().modify(|w| { | 66 | crate::pac::RCC.ahb1enr().modify(|w| { |
| 66 | w.set_eth1macen(true); | 67 | w.set_eth1macen(true); |
| 67 | w.set_eth1txen(true); | 68 | w.set_eth1txen(true); |
| 68 | w.set_eth1rxen(true); | 69 | w.set_eth1rxen(true); |
| 69 | }); | 70 | }); |
| 70 | 71 | ||
| 71 | // RMII | 72 | // RMII |
| 72 | SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); | 73 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); |
| 74 | }); | ||
| 75 | |||
| 76 | #[cfg(rcc_h5)] | ||
| 77 | critical_section::with(|_| { | ||
| 78 | crate::pac::RCC.apb3enr().modify(|w| w.set_sbsen(true)); | ||
| 79 | |||
| 80 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 81 | w.set_ethen(true); | ||
| 82 | w.set_ethtxen(true); | ||
| 83 | w.set_ethrxen(true); | ||
| 84 | }); | ||
| 85 | |||
| 86 | // RMII | ||
| 87 | crate::pac::SBS | ||
| 88 | .pmcr() | ||
| 89 | .modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4)); | ||
| 73 | }); | 90 | }); |
| 74 | 91 | ||
| 75 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 92 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index e1ce09a49..10109e56a 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -25,11 +25,11 @@ fn cpu_regs() -> pac::exti::Exti { | |||
| 25 | EXTI | 25 | EXTI |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | #[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5)))] | 28 | #[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] |
| 29 | fn exticr_regs() -> pac::syscfg::Syscfg { | 29 | fn exticr_regs() -> pac::syscfg::Syscfg { |
| 30 | pac::SYSCFG | 30 | pac::SYSCFG |
| 31 | } | 31 | } |
| 32 | #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] | 32 | #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] |
| 33 | fn exticr_regs() -> pac::exti::Exti { | 33 | fn exticr_regs() -> pac::exti::Exti { |
| 34 | EXTI | 34 | EXTI |
| 35 | } | 35 | } |
| @@ -39,9 +39,9 @@ fn exticr_regs() -> pac::afio::Afio { | |||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | pub unsafe fn on_irq() { | 41 | pub unsafe fn on_irq() { |
| 42 | #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] | 42 | #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] |
| 43 | let bits = EXTI.pr(0).read().0; | 43 | let bits = EXTI.pr(0).read().0; |
| 44 | #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] | 44 | #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] |
| 45 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; | 45 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; |
| 46 | 46 | ||
| 47 | // Mask all the channels that fired. | 47 | // Mask all the channels that fired. |
| @@ -53,9 +53,9 @@ pub unsafe fn on_irq() { | |||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | // Clear pending | 55 | // Clear pending |
| 56 | #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] | 56 | #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] |
| 57 | EXTI.pr(0).write_value(Lines(bits)); | 57 | EXTI.pr(0).write_value(Lines(bits)); |
| 58 | #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] | 58 | #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] |
| 59 | { | 59 | { |
| 60 | EXTI.rpr(0).write_value(Lines(bits)); | 60 | EXTI.rpr(0).write_value(Lines(bits)); |
| 61 | EXTI.fpr(0).write_value(Lines(bits)); | 61 | EXTI.fpr(0).write_value(Lines(bits)); |
| @@ -213,9 +213,9 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 213 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); | 213 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); |
| 214 | 214 | ||
| 215 | // clear pending bit | 215 | // clear pending bit |
| 216 | #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] | 216 | #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] |
| 217 | EXTI.pr(0).write(|w| w.set_line(pin, true)); | 217 | EXTI.pr(0).write(|w| w.set_line(pin, true)); |
| 218 | #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] | 218 | #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] |
| 219 | { | 219 | { |
| 220 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); | 220 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); |
| 221 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); | 221 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); |
| @@ -364,7 +364,7 @@ pub(crate) unsafe fn init() { | |||
| 364 | 364 | ||
| 365 | foreach_exti_irq!(enable_irq); | 365 | foreach_exti_irq!(enable_irq); |
| 366 | 366 | ||
| 367 | #[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1)))] | 367 | #[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1, exti_h5, exti_h50)))] |
| 368 | <crate::peripherals::SYSCFG as crate::rcc::sealed::RccPeripheral>::enable(); | 368 | <crate::peripherals::SYSCFG as crate::rcc::sealed::RccPeripheral>::enable(); |
| 369 | #[cfg(stm32f1)] | 369 | #[cfg(stm32f1)] |
| 370 | <crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable(); | 370 | <crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable(); |
diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs new file mode 100644 index 000000000..17fbc6056 --- /dev/null +++ b/embassy-stm32/src/rcc/h5.rs | |||
| @@ -0,0 +1,606 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use stm32_metapac::rcc::vals::{Hpre, Ppre, Timpre}; | ||
| 4 | |||
| 5 | use crate::pac::pwr::vals::Vos; | ||
| 6 | use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw}; | ||
| 7 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 8 | use crate::rcc::{set_freqs, Clocks}; | ||
| 9 | use crate::time::Hertz; | ||
| 10 | use crate::{peripherals, Peripheral}; | ||
| 11 | |||
| 12 | /// HSI speed | ||
| 13 | pub const HSI_FREQ: Hertz = Hertz(64_000_000); | ||
| 14 | |||
| 15 | /// CSI speed | ||
| 16 | pub const CSI_FREQ: Hertz = Hertz(4_000_000); | ||
| 17 | |||
| 18 | /// HSI48 speed | ||
| 19 | pub const HSI48_FREQ: Hertz = Hertz(48_000_000); | ||
| 20 | |||
| 21 | /// LSI speed | ||
| 22 | pub const LSI_FREQ: Hertz = Hertz(32_000); | ||
| 23 | |||
| 24 | const VCO_MIN: u32 = 150_000_000; | ||
| 25 | const VCO_MAX: u32 = 420_000_000; | ||
| 26 | const VCO_WIDE_MIN: u32 = 128_000_000; | ||
| 27 | const VCO_WIDE_MAX: u32 = 560_000_000; | ||
| 28 | |||
| 29 | /// Voltage Scale | ||
| 30 | /// | ||
| 31 | /// Represents the voltage range feeding the CPU core. The maximum core | ||
| 32 | /// clock frequency depends on this value. | ||
| 33 | #[derive(Copy, Clone, PartialEq)] | ||
| 34 | pub enum VoltageScale { | ||
| 35 | /// VOS 0 range VCORE 1.30V - 1.40V | ||
| 36 | Scale0, | ||
| 37 | /// VOS 1 range VCORE 1.15V - 1.26V | ||
| 38 | Scale1, | ||
| 39 | /// VOS 2 range VCORE 1.05V - 1.15V | ||
| 40 | Scale2, | ||
| 41 | /// VOS 3 range VCORE 0.95V - 1.05V | ||
| 42 | Scale3, | ||
| 43 | } | ||
| 44 | |||
| 45 | pub enum HseMode { | ||
| 46 | /// crystal/ceramic oscillator (HSEBYP=0) | ||
| 47 | Oscillator, | ||
| 48 | /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) | ||
| 49 | BypassAnalog, | ||
| 50 | /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) | ||
| 51 | BypassDigital, | ||
| 52 | } | ||
| 53 | |||
| 54 | pub struct Hse { | ||
| 55 | /// HSE frequency. | ||
| 56 | pub freq: Hertz, | ||
| 57 | /// HSE mode. | ||
| 58 | pub mode: HseMode, | ||
| 59 | } | ||
| 60 | |||
| 61 | pub enum Hsi { | ||
| 62 | /// 64Mhz | ||
| 63 | Mhz64, | ||
| 64 | /// 32Mhz (divided by 2) | ||
| 65 | Mhz32, | ||
| 66 | /// 16Mhz (divided by 4) | ||
| 67 | Mhz16, | ||
| 68 | /// 8Mhz (divided by 8) | ||
| 69 | Mhz8, | ||
| 70 | } | ||
| 71 | |||
| 72 | pub enum Sysclk { | ||
| 73 | /// HSI selected as sysclk | ||
| 74 | HSI, | ||
| 75 | /// HSE selected as sysclk | ||
| 76 | HSE, | ||
| 77 | /// CSI selected as sysclk | ||
| 78 | CSI, | ||
| 79 | /// PLL1_P selected as sysclk | ||
| 80 | Pll1P, | ||
| 81 | } | ||
| 82 | |||
| 83 | pub enum PllSource { | ||
| 84 | Hsi, | ||
| 85 | Csi, | ||
| 86 | Hse, | ||
| 87 | } | ||
| 88 | |||
| 89 | pub struct Pll { | ||
| 90 | /// Source clock selection. | ||
| 91 | pub source: PllSource, | ||
| 92 | |||
| 93 | /// PLL pre-divider (DIVM). Must be between 1 and 63. | ||
| 94 | pub prediv: u8, | ||
| 95 | |||
| 96 | /// PLL multiplication factor. Must be between 4 and 512. | ||
| 97 | pub mul: u16, | ||
| 98 | |||
| 99 | /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128. | ||
| 100 | /// On PLL1, it must be even (in particular, it cannot be 1.) | ||
| 101 | pub divp: Option<u16>, | ||
| 102 | /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. | ||
| 103 | pub divq: Option<u16>, | ||
| 104 | /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. | ||
| 105 | pub divr: Option<u16>, | ||
| 106 | } | ||
| 107 | |||
| 108 | /// AHB prescaler | ||
| 109 | #[derive(Clone, Copy, PartialEq)] | ||
| 110 | pub enum AHBPrescaler { | ||
| 111 | NotDivided, | ||
| 112 | Div2, | ||
| 113 | Div4, | ||
| 114 | Div8, | ||
| 115 | Div16, | ||
| 116 | Div64, | ||
| 117 | Div128, | ||
| 118 | Div256, | ||
| 119 | Div512, | ||
| 120 | } | ||
| 121 | |||
| 122 | impl AHBPrescaler { | ||
| 123 | fn div(&self, clk: Hertz) -> Hertz { | ||
| 124 | match self { | ||
| 125 | Self::NotDivided => clk, | ||
| 126 | Self::Div2 => clk / 2u32, | ||
| 127 | Self::Div4 => clk / 4u32, | ||
| 128 | Self::Div8 => clk / 8u32, | ||
| 129 | Self::Div16 => clk / 16u32, | ||
| 130 | Self::Div64 => clk / 64u32, | ||
| 131 | Self::Div128 => clk / 128u32, | ||
| 132 | Self::Div256 => clk / 256u32, | ||
| 133 | Self::Div512 => clk / 512u32, | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | /// APB prescaler | ||
| 139 | #[derive(Clone, Copy)] | ||
| 140 | pub enum APBPrescaler { | ||
| 141 | NotDivided, | ||
| 142 | Div2, | ||
| 143 | Div4, | ||
| 144 | Div8, | ||
| 145 | Div16, | ||
| 146 | } | ||
| 147 | |||
| 148 | impl APBPrescaler { | ||
| 149 | fn div(&self, clk: Hertz) -> Hertz { | ||
| 150 | match self { | ||
| 151 | Self::NotDivided => clk, | ||
| 152 | Self::Div2 => clk / 2u32, | ||
| 153 | Self::Div4 => clk / 4u32, | ||
| 154 | Self::Div8 => clk / 8u32, | ||
| 155 | Self::Div16 => clk / 16u32, | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | fn div_tim(&self, clk: Hertz, tim: TimerPrescaler) -> Hertz { | ||
| 160 | match (tim, self) { | ||
| 161 | // The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a | ||
| 162 | // division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 | ||
| 163 | (TimerPrescaler::DefaultX2, Self::NotDivided) => clk, | ||
| 164 | (TimerPrescaler::DefaultX2, Self::Div2) => clk, | ||
| 165 | (TimerPrescaler::DefaultX2, Self::Div4) => clk / 2u32, | ||
| 166 | (TimerPrescaler::DefaultX2, Self::Div8) => clk / 4u32, | ||
| 167 | (TimerPrescaler::DefaultX2, Self::Div16) => clk / 8u32, | ||
| 168 | // The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2 | ||
| 169 | // corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2 | ||
| 170 | // this makes NO SENSE and is different than in the H7. Mistake in the RM?? | ||
| 171 | (TimerPrescaler::DefaultX4, Self::NotDivided) => clk * 2u32, | ||
| 172 | (TimerPrescaler::DefaultX4, Self::Div2) => clk, | ||
| 173 | (TimerPrescaler::DefaultX4, Self::Div4) => clk / 2u32, | ||
| 174 | (TimerPrescaler::DefaultX4, Self::Div8) => clk / 2u32, | ||
| 175 | (TimerPrescaler::DefaultX4, Self::Div16) => clk / 4u32, | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | /// APB prescaler | ||
| 181 | #[derive(Clone, Copy)] | ||
| 182 | pub enum TimerPrescaler { | ||
| 183 | DefaultX2, | ||
| 184 | DefaultX4, | ||
| 185 | } | ||
| 186 | |||
| 187 | impl From<TimerPrescaler> for Timpre { | ||
| 188 | fn from(value: TimerPrescaler) -> Self { | ||
| 189 | match value { | ||
| 190 | TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2, | ||
| 191 | TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4, | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | impl From<APBPrescaler> for Ppre { | ||
| 197 | fn from(val: APBPrescaler) -> Ppre { | ||
| 198 | match val { | ||
| 199 | APBPrescaler::NotDivided => Ppre::DIV1, | ||
| 200 | APBPrescaler::Div2 => Ppre::DIV2, | ||
| 201 | APBPrescaler::Div4 => Ppre::DIV4, | ||
| 202 | APBPrescaler::Div8 => Ppre::DIV8, | ||
| 203 | APBPrescaler::Div16 => Ppre::DIV16, | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | impl From<AHBPrescaler> for Hpre { | ||
| 209 | fn from(val: AHBPrescaler) -> Hpre { | ||
| 210 | match val { | ||
| 211 | AHBPrescaler::NotDivided => Hpre::DIV1, | ||
| 212 | AHBPrescaler::Div2 => Hpre::DIV2, | ||
| 213 | AHBPrescaler::Div4 => Hpre::DIV4, | ||
| 214 | AHBPrescaler::Div8 => Hpre::DIV8, | ||
| 215 | AHBPrescaler::Div16 => Hpre::DIV16, | ||
| 216 | AHBPrescaler::Div64 => Hpre::DIV64, | ||
| 217 | AHBPrescaler::Div128 => Hpre::DIV128, | ||
| 218 | AHBPrescaler::Div256 => Hpre::DIV256, | ||
| 219 | AHBPrescaler::Div512 => Hpre::DIV512, | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | /// Configuration of the core clocks | ||
| 225 | #[non_exhaustive] | ||
| 226 | pub struct Config { | ||
| 227 | pub hsi: Option<Hsi>, | ||
| 228 | pub hse: Option<Hse>, | ||
| 229 | pub csi: bool, | ||
| 230 | pub hsi48: bool, | ||
| 231 | pub sys: Sysclk, | ||
| 232 | |||
| 233 | pub pll1: Option<Pll>, | ||
| 234 | pub pll2: Option<Pll>, | ||
| 235 | #[cfg(rcc_h5)] | ||
| 236 | pub pll3: Option<Pll>, | ||
| 237 | |||
| 238 | pub ahb_pre: AHBPrescaler, | ||
| 239 | pub apb1_pre: APBPrescaler, | ||
| 240 | pub apb2_pre: APBPrescaler, | ||
| 241 | pub apb3_pre: APBPrescaler, | ||
| 242 | pub timer_prescaler: TimerPrescaler, | ||
| 243 | |||
| 244 | pub voltage_scale: VoltageScale, | ||
| 245 | } | ||
| 246 | |||
| 247 | impl Default for Config { | ||
| 248 | fn default() -> Self { | ||
| 249 | Self { | ||
| 250 | hsi: Some(Hsi::Mhz64), | ||
| 251 | hse: None, | ||
| 252 | csi: false, | ||
| 253 | hsi48: false, | ||
| 254 | sys: Sysclk::HSI, | ||
| 255 | pll1: None, | ||
| 256 | pll2: None, | ||
| 257 | #[cfg(rcc_h5)] | ||
| 258 | pll3: None, | ||
| 259 | |||
| 260 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 261 | apb1_pre: APBPrescaler::NotDivided, | ||
| 262 | apb2_pre: APBPrescaler::NotDivided, | ||
| 263 | apb3_pre: APBPrescaler::NotDivided, | ||
| 264 | timer_prescaler: TimerPrescaler::DefaultX2, | ||
| 265 | |||
| 266 | voltage_scale: VoltageScale::Scale3, | ||
| 267 | } | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | pub(crate) mod sealed { | ||
| 272 | pub trait McoInstance { | ||
| 273 | type Source; | ||
| 274 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 279 | |||
| 280 | pin_trait!(McoPin, McoInstance); | ||
| 281 | |||
| 282 | macro_rules! impl_peri { | ||
| 283 | ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { | ||
| 284 | impl sealed::McoInstance for peripherals::$peri { | ||
| 285 | type Source = $source; | ||
| 286 | |||
| 287 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { | ||
| 288 | RCC.cfgr().modify(|w| { | ||
| 289 | w.$set_source(source); | ||
| 290 | w.$set_prescaler(prescaler); | ||
| 291 | }); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | impl McoInstance for peripherals::$peri {} | ||
| 296 | }; | ||
| 297 | } | ||
| 298 | |||
| 299 | impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); | ||
| 300 | impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); | ||
| 301 | |||
| 302 | pub struct Mco<'d, T: McoInstance> { | ||
| 303 | phantom: PhantomData<&'d mut T>, | ||
| 304 | } | ||
| 305 | |||
| 306 | impl<'d, T: McoInstance> Mco<'d, T> { | ||
| 307 | pub fn new( | ||
| 308 | _peri: impl Peripheral<P = T> + 'd, | ||
| 309 | _pin: impl Peripheral<P = impl McoPin<T>> + 'd, | ||
| 310 | _source: T::Source, | ||
| 311 | ) -> Self { | ||
| 312 | todo!(); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | pub(crate) unsafe fn init(config: Config) { | ||
| 317 | let (vos, max_clk) = match config.voltage_scale { | ||
| 318 | VoltageScale::Scale0 => (Vos::SCALE0, Hertz(250_000_000)), | ||
| 319 | VoltageScale::Scale1 => (Vos::SCALE1, Hertz(200_000_000)), | ||
| 320 | VoltageScale::Scale2 => (Vos::SCALE2, Hertz(150_000_000)), | ||
| 321 | VoltageScale::Scale3 => (Vos::SCALE3, Hertz(100_000_000)), | ||
| 322 | }; | ||
| 323 | |||
| 324 | // Configure voltage scale. | ||
| 325 | PWR.voscr().modify(|w| w.set_vos(vos)); | ||
| 326 | while !PWR.vossr().read().vosrdy() {} | ||
| 327 | |||
| 328 | // Configure HSI | ||
| 329 | let hsi = match config.hsi { | ||
| 330 | None => { | ||
| 331 | RCC.cr().modify(|w| w.set_hsion(false)); | ||
| 332 | None | ||
| 333 | } | ||
| 334 | Some(hsi) => { | ||
| 335 | let (freq, hsidiv) = match hsi { | ||
| 336 | Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1), | ||
| 337 | Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2), | ||
| 338 | Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4), | ||
| 339 | Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8), | ||
| 340 | }; | ||
| 341 | RCC.cr().modify(|w| { | ||
| 342 | w.set_hsidiv(hsidiv); | ||
| 343 | w.set_hsion(true); | ||
| 344 | }); | ||
| 345 | while !RCC.cr().read().hsirdy() {} | ||
| 346 | Some(freq) | ||
| 347 | } | ||
| 348 | }; | ||
| 349 | |||
| 350 | // Configure HSE | ||
| 351 | let hse = match config.hse { | ||
| 352 | None => { | ||
| 353 | RCC.cr().modify(|w| w.set_hseon(false)); | ||
| 354 | None | ||
| 355 | } | ||
| 356 | Some(hse) => { | ||
| 357 | let (byp, ext) = match hse.mode { | ||
| 358 | HseMode::Oscillator => (false, Hseext::ANALOG), | ||
| 359 | HseMode::BypassAnalog => (true, Hseext::ANALOG), | ||
| 360 | HseMode::BypassDigital => (true, Hseext::DIGITAL), | ||
| 361 | }; | ||
| 362 | |||
| 363 | RCC.cr().modify(|w| { | ||
| 364 | w.set_hsebyp(byp); | ||
| 365 | w.set_hseext(ext); | ||
| 366 | }); | ||
| 367 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 368 | while !RCC.cr().read().hserdy() {} | ||
| 369 | Some(hse.freq) | ||
| 370 | } | ||
| 371 | }; | ||
| 372 | |||
| 373 | // Configure HSI48. | ||
| 374 | RCC.cr().modify(|w| w.set_hsi48on(config.hsi48)); | ||
| 375 | let _hsi48 = match config.hsi48 { | ||
| 376 | false => None, | ||
| 377 | true => { | ||
| 378 | while !RCC.cr().read().hsi48rdy() {} | ||
| 379 | Some(CSI_FREQ) | ||
| 380 | } | ||
| 381 | }; | ||
| 382 | |||
| 383 | // Configure CSI. | ||
| 384 | RCC.cr().modify(|w| w.set_csion(config.csi)); | ||
| 385 | let csi = match config.csi { | ||
| 386 | false => None, | ||
| 387 | true => { | ||
| 388 | while !RCC.cr().read().csirdy() {} | ||
| 389 | Some(CSI_FREQ) | ||
| 390 | } | ||
| 391 | }; | ||
| 392 | |||
| 393 | // Configure PLLs. | ||
| 394 | let pll_input = PllInput { csi, hse, hsi }; | ||
| 395 | let pll1 = init_pll(0, config.pll1, &pll_input); | ||
| 396 | let _pll2 = init_pll(1, config.pll2, &pll_input); | ||
| 397 | #[cfg(rcc_h5)] | ||
| 398 | let _pll3 = init_pll(2, config.pll3, &pll_input); | ||
| 399 | |||
| 400 | // Configure sysclk | ||
| 401 | let (sys, sw) = match config.sys { | ||
| 402 | Sysclk::HSI => (unwrap!(hsi), Sw::HSI), | ||
| 403 | Sysclk::HSE => (unwrap!(hse), Sw::HSE), | ||
| 404 | Sysclk::CSI => (unwrap!(csi), Sw::CSI), | ||
| 405 | Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1), | ||
| 406 | }; | ||
| 407 | assert!(sys <= max_clk); | ||
| 408 | |||
| 409 | let hclk = config.ahb_pre.div(sys); | ||
| 410 | |||
| 411 | let apb1 = config.apb1_pre.div(hclk); | ||
| 412 | let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler); | ||
| 413 | let apb2 = config.apb2_pre.div(hclk); | ||
| 414 | let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler); | ||
| 415 | let apb3 = config.apb3_pre.div(hclk); | ||
| 416 | |||
| 417 | flash_setup(hclk, config.voltage_scale); | ||
| 418 | |||
| 419 | // Set hpre | ||
| 420 | let hpre = config.ahb_pre.into(); | ||
| 421 | RCC.cfgr2().modify(|w| w.set_hpre(hpre)); | ||
| 422 | while RCC.cfgr2().read().hpre() != hpre {} | ||
| 423 | |||
| 424 | // set ppre | ||
| 425 | RCC.cfgr2().modify(|w| { | ||
| 426 | w.set_ppre1(config.apb1_pre.into()); | ||
| 427 | w.set_ppre2(config.apb2_pre.into()); | ||
| 428 | w.set_ppre3(config.apb3_pre.into()); | ||
| 429 | }); | ||
| 430 | |||
| 431 | RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into())); | ||
| 432 | |||
| 433 | RCC.cfgr().modify(|w| w.set_sw(sw)); | ||
| 434 | while RCC.cfgr().read().sws() != sw {} | ||
| 435 | |||
| 436 | set_freqs(Clocks { | ||
| 437 | sys, | ||
| 438 | ahb1: hclk, | ||
| 439 | ahb2: hclk, | ||
| 440 | ahb3: hclk, | ||
| 441 | ahb4: hclk, | ||
| 442 | apb1, | ||
| 443 | apb2, | ||
| 444 | apb3, | ||
| 445 | apb1_tim, | ||
| 446 | apb2_tim, | ||
| 447 | adc: None, | ||
| 448 | }); | ||
| 449 | } | ||
| 450 | |||
| 451 | struct PllInput { | ||
| 452 | hsi: Option<Hertz>, | ||
| 453 | hse: Option<Hertz>, | ||
| 454 | csi: Option<Hertz>, | ||
| 455 | } | ||
| 456 | |||
| 457 | struct PllOutput { | ||
| 458 | p: Option<Hertz>, | ||
| 459 | #[allow(dead_code)] | ||
| 460 | q: Option<Hertz>, | ||
| 461 | #[allow(dead_code)] | ||
| 462 | r: Option<Hertz>, | ||
| 463 | } | ||
| 464 | |||
| 465 | unsafe fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput { | ||
| 466 | let Some(config) = config else { | ||
| 467 | // Stop PLL | ||
| 468 | RCC.cr().modify(|w| w.set_pllon(num, false)); | ||
| 469 | while RCC.cr().read().pllrdy(num) {} | ||
| 470 | |||
| 471 | // "To save power when PLL1 is not used, the value of PLL1M must be set to 0."" | ||
| 472 | RCC.pllcfgr(num).write(|w| { | ||
| 473 | w.set_divm(0); | ||
| 474 | }); | ||
| 475 | |||
| 476 | return PllOutput{ | ||
| 477 | p: None, | ||
| 478 | q: None, | ||
| 479 | r: None, | ||
| 480 | } | ||
| 481 | }; | ||
| 482 | |||
| 483 | assert!(1 <= config.prediv && config.prediv <= 63); | ||
| 484 | assert!(4 <= config.mul && config.mul <= 512); | ||
| 485 | |||
| 486 | let (in_clk, src) = match config.source { | ||
| 487 | PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI), | ||
| 488 | PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE), | ||
| 489 | PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI), | ||
| 490 | }; | ||
| 491 | |||
| 492 | let ref_clk = in_clk / config.prediv as u32; | ||
| 493 | |||
| 494 | let ref_range = match ref_clk.0 { | ||
| 495 | ..=1_999_999 => Pllrge::RANGE1, | ||
| 496 | ..=3_999_999 => Pllrge::RANGE2, | ||
| 497 | ..=7_999_999 => Pllrge::RANGE4, | ||
| 498 | ..=16_000_000 => Pllrge::RANGE8, | ||
| 499 | x => panic!("pll ref_clk out of range: {} mhz", x), | ||
| 500 | }; | ||
| 501 | |||
| 502 | // The smaller range (150 to 420 MHz) must | ||
| 503 | // be chosen when the reference clock frequency is lower than 2 MHz. | ||
| 504 | let wide_allowed = ref_range != Pllrge::RANGE1; | ||
| 505 | |||
| 506 | let vco_clk = ref_clk * config.mul; | ||
| 507 | let vco_range = match vco_clk.0 { | ||
| 508 | VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO, | ||
| 509 | VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO, | ||
| 510 | x => panic!("pll vco_clk out of range: {} mhz", x), | ||
| 511 | }; | ||
| 512 | |||
| 513 | let p = config.divp.map(|div| { | ||
| 514 | assert!(1 <= div && div <= 128); | ||
| 515 | if num == 0 { | ||
| 516 | // on PLL1, DIVP must be even. | ||
| 517 | assert!(div % 2 == 0); | ||
| 518 | } | ||
| 519 | |||
| 520 | vco_clk / div | ||
| 521 | }); | ||
| 522 | let q = config.divq.map(|div| { | ||
| 523 | assert!(1 <= div && div <= 128); | ||
| 524 | vco_clk / div | ||
| 525 | }); | ||
| 526 | let r = config.divr.map(|div| { | ||
| 527 | assert!(1 <= div && div <= 128); | ||
| 528 | vco_clk / div | ||
| 529 | }); | ||
| 530 | |||
| 531 | RCC.pllcfgr(num).write(|w| { | ||
| 532 | w.set_pllsrc(src); | ||
| 533 | w.set_divm(config.prediv); | ||
| 534 | w.set_pllvcosel(vco_range); | ||
| 535 | w.set_pllrge(ref_range); | ||
| 536 | w.set_pllfracen(false); | ||
| 537 | w.set_pllpen(p.is_some()); | ||
| 538 | w.set_pllqen(q.is_some()); | ||
| 539 | w.set_pllren(r.is_some()); | ||
| 540 | }); | ||
| 541 | RCC.plldivr(num).write(|w| { | ||
| 542 | w.set_plln(config.mul - 1); | ||
| 543 | w.set_pllp((config.divp.unwrap_or(1) - 1) as u8); | ||
| 544 | w.set_pllq((config.divq.unwrap_or(1) - 1) as u8); | ||
| 545 | w.set_pllr((config.divr.unwrap_or(1) - 1) as u8); | ||
| 546 | }); | ||
| 547 | |||
| 548 | RCC.cr().modify(|w| w.set_pllon(num, true)); | ||
| 549 | while !RCC.cr().read().pllrdy(num) {} | ||
| 550 | |||
| 551 | PllOutput { p, q, r } | ||
| 552 | } | ||
| 553 | |||
| 554 | fn flash_setup(clk: Hertz, vos: VoltageScale) { | ||
| 555 | // RM0481 Rev 1, table 37 | ||
| 556 | // LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0 | ||
| 557 | // 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz | ||
| 558 | // 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz | ||
| 559 | // 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz | ||
| 560 | // 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz | ||
| 561 | // 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz | ||
| 562 | // 5 2 170 to 200 MHz 210 to 250 MHz | ||
| 563 | |||
| 564 | // See RM0433 Rev 7 Table 17. FLASH recommended number of wait | ||
| 565 | // states and programming delay | ||
| 566 | let (latency, wrhighfreq) = match (vos, clk.0) { | ||
| 567 | (VoltageScale::Scale0, ..=42_000_000) => (0, 0), | ||
| 568 | (VoltageScale::Scale0, ..=84_000_000) => (1, 0), | ||
| 569 | (VoltageScale::Scale0, ..=126_000_000) => (2, 1), | ||
| 570 | (VoltageScale::Scale0, ..=168_000_000) => (3, 1), | ||
| 571 | (VoltageScale::Scale0, ..=210_000_000) => (4, 2), | ||
| 572 | (VoltageScale::Scale0, ..=250_000_000) => (5, 2), | ||
| 573 | |||
| 574 | (VoltageScale::Scale1, ..=34_000_000) => (0, 0), | ||
| 575 | (VoltageScale::Scale1, ..=68_000_000) => (1, 0), | ||
| 576 | (VoltageScale::Scale1, ..=102_000_000) => (2, 1), | ||
| 577 | (VoltageScale::Scale1, ..=136_000_000) => (3, 1), | ||
| 578 | (VoltageScale::Scale1, ..=170_000_000) => (4, 2), | ||
| 579 | (VoltageScale::Scale1, ..=200_000_000) => (5, 2), | ||
| 580 | |||
| 581 | (VoltageScale::Scale2, ..=30_000_000) => (0, 0), | ||
| 582 | (VoltageScale::Scale2, ..=60_000_000) => (1, 0), | ||
| 583 | (VoltageScale::Scale2, ..=90_000_000) => (2, 1), | ||
| 584 | (VoltageScale::Scale2, ..=120_000_000) => (3, 1), | ||
| 585 | (VoltageScale::Scale2, ..=150_000_000) => (4, 2), | ||
| 586 | |||
| 587 | (VoltageScale::Scale3, ..=20_000_000) => (0, 0), | ||
| 588 | (VoltageScale::Scale3, ..=40_000_000) => (1, 0), | ||
| 589 | (VoltageScale::Scale3, ..=60_000_000) => (2, 1), | ||
| 590 | (VoltageScale::Scale3, ..=80_000_000) => (3, 1), | ||
| 591 | (VoltageScale::Scale3, ..=100_000_000) => (4, 2), | ||
| 592 | |||
| 593 | _ => unreachable!(), | ||
| 594 | }; | ||
| 595 | |||
| 596 | defmt::debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq); | ||
| 597 | |||
| 598 | // NOTE(unsafe) Atomic write | ||
| 599 | unsafe { | ||
| 600 | FLASH.acr().write(|w| { | ||
| 601 | w.set_wrhighfreq(wrhighfreq); | ||
| 602 | w.set_latency(latency); | ||
| 603 | }); | ||
| 604 | while FLASH.acr().read().latency() != latency {} | ||
| 605 | } | ||
| 606 | } | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d4bd3d6b8..d6a31f17b 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -21,6 +21,7 @@ use crate::time::Hertz; | |||
| 21 | #[cfg_attr(rcc_u5, path = "u5.rs")] | 21 | #[cfg_attr(rcc_u5, path = "u5.rs")] |
| 22 | #[cfg_attr(rcc_wb, path = "wb.rs")] | 22 | #[cfg_attr(rcc_wb, path = "wb.rs")] |
| 23 | #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] | 23 | #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] |
| 24 | #[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] | ||
| 24 | mod _version; | 25 | mod _version; |
| 25 | pub use _version::*; | 26 | pub use _version::*; |
| 26 | 27 | ||
| @@ -36,7 +37,7 @@ pub struct Clocks { | |||
| 36 | pub apb2: Hertz, | 37 | pub apb2: Hertz, |
| 37 | #[cfg(not(any(rcc_c0, rcc_g0)))] | 38 | #[cfg(not(any(rcc_c0, rcc_g0)))] |
| 38 | pub apb2_tim: Hertz, | 39 | pub apb2_tim: Hertz, |
| 39 | #[cfg(any(rcc_wl5, rcc_wle, rcc_u5))] | 40 | #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))] |
| 40 | pub apb3: Hertz, | 41 | pub apb3: Hertz, |
| 41 | #[cfg(any(rcc_h7, rcc_h7ab))] | 42 | #[cfg(any(rcc_h7, rcc_h7ab))] |
| 42 | pub apb4: Hertz, | 43 | pub apb4: Hertz, |
| @@ -44,14 +45,16 @@ pub struct Clocks { | |||
| 44 | // AHB | 45 | // AHB |
| 45 | pub ahb1: Hertz, | 46 | pub ahb1: Hertz, |
| 46 | #[cfg(any( | 47 | #[cfg(any( |
| 47 | rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5, rcc_wle | 48 | rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, |
| 49 | rcc_wl5, rcc_wle | ||
| 48 | ))] | 50 | ))] |
| 49 | pub ahb2: Hertz, | 51 | pub ahb2: Hertz, |
| 50 | #[cfg(any( | 52 | #[cfg(any( |
| 51 | rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5, rcc_wle | 53 | rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5, |
| 54 | rcc_wle | ||
| 52 | ))] | 55 | ))] |
| 53 | pub ahb3: Hertz, | 56 | pub ahb3: Hertz, |
| 54 | #[cfg(any(rcc_h7, rcc_h7ab))] | 57 | #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] |
| 55 | pub ahb4: Hertz, | 58 | pub ahb4: Hertz, |
| 56 | 59 | ||
| 57 | #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] | 60 | #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] |
| @@ -60,7 +63,7 @@ pub struct Clocks { | |||
| 60 | #[cfg(stm32f1)] | 63 | #[cfg(stm32f1)] |
| 61 | pub adc: Hertz, | 64 | pub adc: Hertz, |
| 62 | 65 | ||
| 63 | #[cfg(any(rcc_h7, rcc_h7ab))] | 66 | #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] |
| 64 | pub adc: Option<Hertz>, | 67 | pub adc: Option<Hertz>, |
| 65 | } | 68 | } |
| 66 | 69 | ||
diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 975517a48..f08abe331 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs | |||
| @@ -1,7 +1,9 @@ | |||
| 1 | //! Time units | 1 | //! Time units |
| 2 | 2 | ||
| 3 | use core::ops::{Div, Mul}; | ||
| 4 | |||
| 3 | /// Hertz | 5 | /// Hertz |
| 4 | #[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq)] | 6 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] |
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 6 | pub struct Hertz(pub u32); | 8 | pub struct Hertz(pub u32); |
| 7 | 9 | ||
| @@ -33,3 +35,45 @@ pub fn khz(kilohertz: u32) -> Hertz { | |||
| 33 | pub fn mhz(megahertz: u32) -> Hertz { | 35 | pub fn mhz(megahertz: u32) -> Hertz { |
| 34 | Hertz::mhz(megahertz) | 36 | Hertz::mhz(megahertz) |
| 35 | } | 37 | } |
| 38 | |||
| 39 | impl Mul<u32> for Hertz { | ||
| 40 | type Output = Hertz; | ||
| 41 | fn mul(self, rhs: u32) -> Self::Output { | ||
| 42 | Hertz(self.0 * rhs) | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | impl Div<u32> for Hertz { | ||
| 47 | type Output = Hertz; | ||
| 48 | fn div(self, rhs: u32) -> Self::Output { | ||
| 49 | Hertz(self.0 / rhs) | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | impl Mul<u16> for Hertz { | ||
| 54 | type Output = Hertz; | ||
| 55 | fn mul(self, rhs: u16) -> Self::Output { | ||
| 56 | self * (rhs as u32) | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | impl Div<u16> for Hertz { | ||
| 61 | type Output = Hertz; | ||
| 62 | fn div(self, rhs: u16) -> Self::Output { | ||
| 63 | self / (rhs as u32) | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Mul<u8> for Hertz { | ||
| 68 | type Output = Hertz; | ||
| 69 | fn mul(self, rhs: u8) -> Self::Output { | ||
| 70 | self * (rhs as u32) | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | impl Div<u8> for Hertz { | ||
| 75 | type Output = Hertz; | ||
| 76 | fn div(self, rhs: u8) -> Self::Output { | ||
| 77 | self / (rhs as u32) | ||
| 78 | } | ||
| 79 | } | ||
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index e6ee39549..ad68eaba2 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs | |||
| @@ -186,6 +186,11 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 186 | crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); | 186 | crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); |
| 187 | } | 187 | } |
| 188 | 188 | ||
| 189 | #[cfg(pwr_h5)] | ||
| 190 | unsafe { | ||
| 191 | crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)) | ||
| 192 | } | ||
| 193 | |||
| 189 | unsafe { | 194 | unsafe { |
| 190 | <T as RccPeripheral>::enable(); | 195 | <T as RccPeripheral>::enable(); |
| 191 | <T as RccPeripheral>::reset(); | 196 | <T as RccPeripheral>::reset(); |
diff --git a/examples/stm32h5/.cargo/config.toml b/examples/stm32h5/.cargo/config.toml new file mode 100644 index 000000000..c8b864b6c --- /dev/null +++ b/examples/stm32h5/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.thumbv8m.main-none-eabihf] | ||
| 2 | runner = 'probe-rs-cli run --chip STM32H563ZITx' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv8m.main-none-eabihf" | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml new file mode 100644 index 000000000..70702863f --- /dev/null +++ b/examples/stm32h5/Cargo.toml | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32h7-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | ||
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | ||
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } | ||
| 13 | embedded-io = { version = "0.4.0", features = ["async"] } | ||
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | |||
| 16 | defmt = "0.3" | ||
| 17 | defmt-rtt = "0.4" | ||
| 18 | |||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||
| 20 | cortex-m-rt = "0.7.0" | ||
| 21 | embedded-hal = "0.2.6" | ||
| 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | ||
| 23 | embedded-hal-async = { version = "=0.2.0-alpha.0" } | ||
| 24 | embedded-nal-async = "0.4.0" | ||
| 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||
| 27 | heapless = { version = "0.7.5", default-features = false } | ||
| 28 | rand_core = "0.6.3" | ||
| 29 | critical-section = "1.1" | ||
| 30 | micromath = "2.0.0" | ||
| 31 | stm32-fmc = "0.2.4" | ||
| 32 | embedded-storage = "0.3.0" | ||
| 33 | static_cell = "1.0" | ||
| 34 | |||
| 35 | # cargo build/run | ||
| 36 | [profile.dev] | ||
| 37 | codegen-units = 1 | ||
| 38 | debug = 2 | ||
| 39 | debug-assertions = true # <- | ||
| 40 | incremental = false | ||
| 41 | opt-level = 3 # <- | ||
| 42 | overflow-checks = true # <- | ||
| 43 | |||
| 44 | # cargo test | ||
| 45 | [profile.test] | ||
| 46 | codegen-units = 1 | ||
| 47 | debug = 2 | ||
| 48 | debug-assertions = true # <- | ||
| 49 | incremental = false | ||
| 50 | opt-level = 3 # <- | ||
| 51 | overflow-checks = true # <- | ||
| 52 | |||
| 53 | # cargo build/run --release | ||
| 54 | [profile.release] | ||
| 55 | codegen-units = 1 | ||
| 56 | debug = 2 | ||
| 57 | debug-assertions = false # <- | ||
| 58 | incremental = false | ||
| 59 | lto = 'fat' | ||
| 60 | opt-level = 3 # <- | ||
| 61 | overflow-checks = false # <- | ||
| 62 | |||
| 63 | # cargo test --release | ||
| 64 | [profile.bench] | ||
| 65 | codegen-units = 1 | ||
| 66 | debug = 2 | ||
| 67 | debug-assertions = false # <- | ||
| 68 | incremental = false | ||
| 69 | lto = 'fat' | ||
| 70 | opt-level = 3 # <- | ||
| 71 | overflow-checks = false # <- | ||
diff --git a/examples/stm32h5/build.rs b/examples/stm32h5/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32h5/build.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | fn main() { | ||
| 2 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 3 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 4 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 5 | } | ||
diff --git a/examples/stm32h5/memory.x b/examples/stm32h5/memory.x new file mode 100644 index 000000000..456061509 --- /dev/null +++ b/examples/stm32h5/memory.x | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x08000000, LENGTH = 0x200000 | ||
| 4 | RAM : ORIGIN = 0x20000000, LENGTH = 0x50000 | ||
| 5 | } | ||
diff --git a/examples/stm32h5/src/bin/blinky.rs b/examples/stm32h5/src/bin/blinky.rs new file mode 100644 index 000000000..f9bf90d2e --- /dev/null +++ b/examples/stm32h5/src/bin/blinky.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let mut led = Output::new(p.PB0, Level::High, Speed::Low); | ||
| 17 | |||
| 18 | loop { | ||
| 19 | info!("high"); | ||
| 20 | led.set_high(); | ||
| 21 | Timer::after(Duration::from_millis(500)).await; | ||
| 22 | |||
| 23 | info!("low"); | ||
| 24 | led.set_low(); | ||
| 25 | Timer::after(Duration::from_millis(500)).await; | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs new file mode 100644 index 000000000..dfe587d41 --- /dev/null +++ b/examples/stm32h5/src/bin/button_exti.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::exti::ExtiInput; | ||
| 8 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let button = Input::new(p.PC13, Pull::Down); | ||
| 17 | let mut button = ExtiInput::new(button, p.EXTI13); | ||
| 18 | |||
| 19 | info!("Press the USER button..."); | ||
| 20 | |||
| 21 | loop { | ||
| 22 | button.wait_for_rising_edge().await; | ||
| 23 | info!("Pressed!"); | ||
| 24 | button.wait_for_falling_edge().await; | ||
| 25 | info!("Released!"); | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs new file mode 100644 index 000000000..6d650da9e --- /dev/null +++ b/examples/stm32h5/src/bin/eth.rs | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_net::tcp::TcpSocket; | ||
| 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; | ||
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | ||
| 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 11 | use embassy_stm32::peripherals::ETH; | ||
| 12 | use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; | ||
| 13 | use embassy_stm32::rng::Rng; | ||
| 14 | use embassy_stm32::time::Hertz; | ||
| 15 | use embassy_stm32::{interrupt, Config}; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use embedded_io::asynch::Write; | ||
| 18 | use rand_core::RngCore; | ||
| 19 | use static_cell::StaticCell; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | macro_rules! singleton { | ||
| 23 | ($val:expr) => {{ | ||
| 24 | type T = impl Sized; | ||
| 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 26 | let (x,) = STATIC_CELL.init(($val,)); | ||
| 27 | x | ||
| 28 | }}; | ||
| 29 | } | ||
| 30 | |||
| 31 | type Device = Ethernet<'static, ETH, GenericSMI>; | ||
| 32 | |||
| 33 | #[embassy_executor::task] | ||
| 34 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 35 | stack.run().await | ||
| 36 | } | ||
| 37 | |||
| 38 | #[embassy_executor::main] | ||
| 39 | async fn main(spawner: Spawner) -> ! { | ||
| 40 | let mut config = Config::default(); | ||
| 41 | config.rcc.hsi = None; | ||
| 42 | config.rcc.hsi48 = true; // needed for rng | ||
| 43 | config.rcc.hse = Some(Hse { | ||
| 44 | freq: Hertz(8_000_000), | ||
| 45 | mode: HseMode::BypassDigital, | ||
| 46 | }); | ||
| 47 | config.rcc.pll1 = Some(Pll { | ||
| 48 | source: PllSource::Hse, | ||
| 49 | prediv: 2, | ||
| 50 | mul: 125, | ||
| 51 | divp: Some(2), | ||
| 52 | divq: Some(2), | ||
| 53 | divr: None, | ||
| 54 | }); | ||
| 55 | config.rcc.ahb_pre = AHBPrescaler::NotDivided; | ||
| 56 | config.rcc.apb1_pre = APBPrescaler::NotDivided; | ||
| 57 | config.rcc.apb2_pre = APBPrescaler::NotDivided; | ||
| 58 | config.rcc.apb3_pre = APBPrescaler::NotDivided; | ||
| 59 | config.rcc.sys = Sysclk::Pll1P; | ||
| 60 | config.rcc.voltage_scale = VoltageScale::Scale0; | ||
| 61 | let p = embassy_stm32::init(config); | ||
| 62 | info!("Hello World!"); | ||
| 63 | |||
| 64 | // Generate random seed. | ||
| 65 | let mut rng = Rng::new(p.RNG); | ||
| 66 | let mut seed = [0; 8]; | ||
| 67 | rng.fill_bytes(&mut seed); | ||
| 68 | let seed = u64::from_le_bytes(seed); | ||
| 69 | |||
| 70 | let eth_int = interrupt::take!(ETH); | ||
| 71 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | ||
| 72 | |||
| 73 | let device = Ethernet::new( | ||
| 74 | singleton!(PacketQueue::<4, 4>::new()), | ||
| 75 | p.ETH, | ||
| 76 | eth_int, | ||
| 77 | p.PA1, | ||
| 78 | p.PA2, | ||
| 79 | p.PC1, | ||
| 80 | p.PA7, | ||
| 81 | p.PC4, | ||
| 82 | p.PC5, | ||
| 83 | p.PG13, | ||
| 84 | p.PB15, | ||
| 85 | p.PG11, | ||
| 86 | GenericSMI, | ||
| 87 | mac_addr, | ||
| 88 | 0, | ||
| 89 | ); | ||
| 90 | |||
| 91 | let config = embassy_net::Config::Dhcp(Default::default()); | ||
| 92 | //let config = embassy_net::Config::Static(embassy_net::StaticConfig { | ||
| 93 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 94 | // dns_servers: Vec::new(), | ||
| 95 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 96 | //}); | ||
| 97 | |||
| 98 | // Init network stack | ||
| 99 | let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); | ||
| 100 | |||
| 101 | // Launch network task | ||
| 102 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 103 | |||
| 104 | info!("Network task initialized"); | ||
| 105 | |||
| 106 | // Then we can use it! | ||
| 107 | let mut rx_buffer = [0; 1024]; | ||
| 108 | let mut tx_buffer = [0; 1024]; | ||
| 109 | |||
| 110 | loop { | ||
| 111 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | ||
| 112 | |||
| 113 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 114 | |||
| 115 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); | ||
| 116 | info!("connecting..."); | ||
| 117 | let r = socket.connect(remote_endpoint).await; | ||
| 118 | if let Err(e) = r { | ||
| 119 | info!("connect error: {:?}", e); | ||
| 120 | Timer::after(Duration::from_secs(3)).await; | ||
| 121 | continue; | ||
| 122 | } | ||
| 123 | info!("connected!"); | ||
| 124 | loop { | ||
| 125 | let r = socket.write_all(b"Hello\n").await; | ||
| 126 | if let Err(e) = r { | ||
| 127 | info!("write error: {:?}", e); | ||
| 128 | continue; | ||
| 129 | } | ||
| 130 | Timer::after(Duration::from_secs(1)).await; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
diff --git a/examples/stm32h5/src/bin/i2c.rs b/examples/stm32h5/src/bin/i2c.rs new file mode 100644 index 000000000..6cbf58bbc --- /dev/null +++ b/examples/stm32h5/src/bin/i2c.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; | ||
| 8 | use embassy_stm32::interrupt; | ||
| 9 | use embassy_stm32::time::Hertz; | ||
| 10 | use embassy_time::Duration; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | const ADDRESS: u8 = 0x5F; | ||
| 14 | const WHOAMI: u8 = 0x0F; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | info!("Hello world!"); | ||
| 19 | let p = embassy_stm32::init(Default::default()); | ||
| 20 | |||
| 21 | let irq = interrupt::take!(I2C2_EV); | ||
| 22 | let mut i2c = I2c::new( | ||
| 23 | p.I2C2, | ||
| 24 | p.PB10, | ||
| 25 | p.PB11, | ||
| 26 | irq, | ||
| 27 | p.GPDMA1_CH4, | ||
| 28 | p.GPDMA1_CH5, | ||
| 29 | Hertz(100_000), | ||
| 30 | Default::default(), | ||
| 31 | ); | ||
| 32 | |||
| 33 | // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. | ||
| 34 | // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. | ||
| 35 | let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); | ||
| 36 | |||
| 37 | let mut data = [0u8; 1]; | ||
| 38 | |||
| 39 | match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { | ||
| 40 | Ok(()) => info!("Whoami: {}", data[0]), | ||
| 41 | Err(Error::Timeout) => error!("Operation timed out"), | ||
| 42 | Err(e) => error!("I2c Error: {:?}", e), | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/examples/stm32h5/src/bin/rng.rs b/examples/stm32h5/src/bin/rng.rs new file mode 100644 index 000000000..af9be0b62 --- /dev/null +++ b/examples/stm32h5/src/bin/rng.rs | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rng::Rng; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_stm32::init(Default::default()); | ||
| 13 | info!("Hello World!"); | ||
| 14 | |||
| 15 | let mut rng = Rng::new(p.RNG); | ||
| 16 | |||
| 17 | let mut buf = [0u8; 16]; | ||
| 18 | unwrap!(rng.async_fill_bytes(&mut buf).await); | ||
| 19 | info!("random bytes: {:02x}", buf); | ||
| 20 | } | ||
diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs new file mode 100644 index 000000000..405f18ec7 --- /dev/null +++ b/examples/stm32h5/src/bin/usart.rs | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use cortex_m_rt::entry; | ||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Executor; | ||
| 8 | use embassy_stm32::dma::NoDma; | ||
| 9 | use embassy_stm32::interrupt; | ||
| 10 | use embassy_stm32::usart::{Config, Uart}; | ||
| 11 | use static_cell::StaticCell; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::task] | ||
| 15 | async fn main_task() { | ||
| 16 | let p = embassy_stm32::init(Default::default()); | ||
| 17 | |||
| 18 | let config = Config::default(); | ||
| 19 | let irq = interrupt::take!(UART7); | ||
| 20 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); | ||
| 21 | |||
| 22 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 23 | info!("wrote Hello, starting echo"); | ||
| 24 | |||
| 25 | let mut buf = [0u8; 1]; | ||
| 26 | loop { | ||
| 27 | unwrap!(usart.blocking_read(&mut buf)); | ||
| 28 | unwrap!(usart.blocking_write(&buf)); | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 33 | |||
| 34 | #[entry] | ||
| 35 | fn main() -> ! { | ||
| 36 | info!("Hello World!"); | ||
| 37 | |||
| 38 | let executor = EXECUTOR.init(Executor::new()); | ||
| 39 | |||
| 40 | executor.run(|spawner| { | ||
| 41 | unwrap!(spawner.spawn(main_task())); | ||
| 42 | }) | ||
| 43 | } | ||
diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs new file mode 100644 index 000000000..43d791aae --- /dev/null +++ b/examples/stm32h5/src/bin/usart_dma.rs | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::fmt::Write; | ||
| 6 | |||
| 7 | use cortex_m_rt::entry; | ||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Executor; | ||
| 10 | use embassy_stm32::dma::NoDma; | ||
| 11 | use embassy_stm32::interrupt; | ||
| 12 | use embassy_stm32::usart::{Config, Uart}; | ||
| 13 | use heapless::String; | ||
| 14 | use static_cell::StaticCell; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[embassy_executor::task] | ||
| 18 | async fn main_task() { | ||
| 19 | let p = embassy_stm32::init(Default::default()); | ||
| 20 | |||
| 21 | let config = Config::default(); | ||
| 22 | let irq = interrupt::take!(UART7); | ||
| 23 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, NoDma, config); | ||
| 24 | |||
| 25 | for n in 0u32.. { | ||
| 26 | let mut s: String<128> = String::new(); | ||
| 27 | core::write!(&mut s, "Hello DMA World {}!\r\n", n).unwrap(); | ||
| 28 | |||
| 29 | usart.write(s.as_bytes()).await.ok(); | ||
| 30 | |||
| 31 | info!("wrote DMA"); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 36 | |||
| 37 | #[entry] | ||
| 38 | fn main() -> ! { | ||
| 39 | info!("Hello World!"); | ||
| 40 | |||
| 41 | let executor = EXECUTOR.init(Executor::new()); | ||
| 42 | |||
| 43 | executor.run(|spawner| { | ||
| 44 | unwrap!(spawner.spawn(main_task())); | ||
| 45 | }) | ||
| 46 | } | ||
diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs new file mode 100644 index 000000000..16a499582 --- /dev/null +++ b/examples/stm32h5/src/bin/usart_split.rs | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::interrupt; | ||
| 9 | use embassy_stm32::peripherals::{GPDMA1_CH1, UART7}; | ||
| 10 | use embassy_stm32::usart::{Config, Uart, UartRx}; | ||
| 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 12 | use embassy_sync::channel::Channel; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::task] | ||
| 16 | async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { | ||
| 17 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 18 | info!("wrote Hello, starting echo"); | ||
| 19 | |||
| 20 | let mut buf = [0u8; 1]; | ||
| 21 | loop { | ||
| 22 | unwrap!(usart.blocking_read(&mut buf)); | ||
| 23 | unwrap!(usart.blocking_write(&buf)); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async fn main(spawner: Spawner) -> ! { | ||
| 31 | let p = embassy_stm32::init(Default::default()); | ||
| 32 | info!("Hello World!"); | ||
| 33 | |||
| 34 | let config = Config::default(); | ||
| 35 | let irq = interrupt::take!(UART7); | ||
| 36 | let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, p.GPDMA1_CH1, config); | ||
| 37 | unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); | ||
| 38 | |||
| 39 | let (mut tx, rx) = usart.split(); | ||
| 40 | |||
| 41 | unwrap!(spawner.spawn(reader(rx))); | ||
| 42 | |||
| 43 | loop { | ||
| 44 | let buf = CHANNEL.recv().await; | ||
| 45 | info!("writing..."); | ||
| 46 | unwrap!(tx.write(&buf).await); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | #[embassy_executor::task] | ||
| 51 | async fn reader(mut rx: UartRx<'static, UART7, GPDMA1_CH1>) { | ||
| 52 | let mut buf = [0; 8]; | ||
| 53 | loop { | ||
| 54 | info!("reading..."); | ||
| 55 | unwrap!(rx.read(&mut buf).await); | ||
| 56 | CHANNEL.send(buf).await; | ||
| 57 | } | ||
| 58 | } | ||
diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs new file mode 100644 index 000000000..6af269c1d --- /dev/null +++ b/examples/stm32h5/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 10 | use embassy_stm32::{interrupt, pac, Config}; | ||
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | ||
| 13 | use embassy_usb::Builder; | ||
| 14 | use futures::future::join; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let mut config = Config::default(); | ||
| 20 | config.rcc.hsi = None; | ||
| 21 | config.rcc.hsi48 = true; // needed for usb | ||
| 22 | config.rcc.hse = Some(Hse { | ||
| 23 | freq: Hertz(8_000_000), | ||
| 24 | mode: HseMode::BypassDigital, | ||
| 25 | }); | ||
| 26 | config.rcc.pll1 = Some(Pll { | ||
| 27 | source: PllSource::Hse, | ||
| 28 | prediv: 2, | ||
| 29 | mul: 125, | ||
| 30 | divp: Some(2), // 250mhz | ||
| 31 | divq: None, | ||
| 32 | divr: None, | ||
| 33 | }); | ||
| 34 | config.rcc.ahb_pre = AHBPrescaler::Div2; | ||
| 35 | config.rcc.apb1_pre = APBPrescaler::Div4; | ||
| 36 | config.rcc.apb2_pre = APBPrescaler::Div2; | ||
| 37 | config.rcc.apb3_pre = APBPrescaler::Div4; | ||
| 38 | config.rcc.sys = Sysclk::Pll1P; | ||
| 39 | config.rcc.voltage_scale = VoltageScale::Scale0; | ||
| 40 | let p = embassy_stm32::init(config); | ||
| 41 | |||
| 42 | info!("Hello World!"); | ||
| 43 | |||
| 44 | unsafe { | ||
| 45 | pac::RCC.ccipr4().write(|w| { | ||
| 46 | w.set_usbsel(pac::rcc::vals::Usbsel::HSI48); | ||
| 47 | }); | ||
| 48 | } | ||
| 49 | |||
| 50 | // Create the driver, from the HAL. | ||
| 51 | let irq = interrupt::take!(USB_DRD_FS); | ||
| 52 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 53 | |||
| 54 | // Create embassy-usb Config | ||
| 55 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 56 | config.manufacturer = Some("Embassy"); | ||
| 57 | config.product = Some("USB-serial example"); | ||
| 58 | config.serial_number = Some("12345678"); | ||
| 59 | |||
| 60 | // Required for windows compatiblity. | ||
| 61 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 62 | config.device_class = 0xEF; | ||
| 63 | config.device_sub_class = 0x02; | ||
| 64 | config.device_protocol = 0x01; | ||
| 65 | config.composite_with_iads = true; | ||
| 66 | |||
| 67 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 68 | // It needs some buffers for building the descriptors. | ||
| 69 | let mut device_descriptor = [0; 256]; | ||
| 70 | let mut config_descriptor = [0; 256]; | ||
| 71 | let mut bos_descriptor = [0; 256]; | ||
| 72 | let mut control_buf = [0; 64]; | ||
| 73 | |||
| 74 | let mut state = State::new(); | ||
| 75 | |||
| 76 | let mut builder = Builder::new( | ||
| 77 | driver, | ||
| 78 | config, | ||
| 79 | &mut device_descriptor, | ||
| 80 | &mut config_descriptor, | ||
| 81 | &mut bos_descriptor, | ||
| 82 | &mut control_buf, | ||
| 83 | ); | ||
| 84 | |||
| 85 | // Create classes on the builder. | ||
| 86 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 87 | |||
| 88 | // Build the builder. | ||
| 89 | let mut usb = builder.build(); | ||
| 90 | |||
| 91 | // Run the USB device. | ||
| 92 | let usb_fut = usb.run(); | ||
| 93 | |||
| 94 | // Do stuff with the class! | ||
| 95 | let echo_fut = async { | ||
| 96 | loop { | ||
| 97 | class.wait_connection().await; | ||
| 98 | info!("Connected"); | ||
| 99 | let _ = echo(&mut class).await; | ||
| 100 | info!("Disconnected"); | ||
| 101 | } | ||
| 102 | }; | ||
| 103 | |||
| 104 | // Run everything concurrently. | ||
| 105 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 106 | join(usb_fut, echo_fut).await; | ||
| 107 | } | ||
| 108 | |||
| 109 | struct Disconnected {} | ||
| 110 | |||
| 111 | impl From<EndpointError> for Disconnected { | ||
| 112 | fn from(val: EndpointError) -> Self { | ||
| 113 | match val { | ||
| 114 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 115 | EndpointError::Disabled => Disconnected {}, | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 121 | let mut buf = [0; 64]; | ||
| 122 | loop { | ||
| 123 | let n = class.read_packet(&mut buf).await?; | ||
| 124 | let data = &buf[..n]; | ||
| 125 | info!("data: {:x}", data); | ||
| 126 | class.write_packet(data).await?; | ||
| 127 | } | ||
| 128 | } | ||
