diff --git a/.github/bors.toml b/.github/bors.toml deleted file mode 100644 index 1ecc9d8d..00000000 --- a/.github/bors.toml +++ /dev/null @@ -1,4 +0,0 @@ -status = [ - "all", -] -delete_merged_branches = true diff --git a/.github/ci/build-stable.sh b/.github/ci/build-stable.sh index 0dadd610..8012f692 100755 --- a/.github/ci/build-stable.sh +++ b/.github/ci/build-stable.sh @@ -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 diff --git a/.github/ci/build.sh b/.github/ci/build.sh index 30ca1e6f..78ab976d 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh @@ -11,8 +11,13 @@ if [ -f /ci/secrets/teleprobe-token.txt ]; then echo Got teleprobe token! export TELEPROBE_HOST=https://teleprobe.embassy.dev export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt) + 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 diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 57184dc1..66caa915 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -39,6 +39,7 @@ docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/g docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static +docserver-builder -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) @@ -47,8 +48,8 @@ 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. + 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 diff --git a/.github/ci/test.sh b/.github/ci/test.sh index 2892bcf8..af0f21c2 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -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 @@ -28,3 +32,5 @@ cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --featu cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti + +cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml diff --git a/ci.sh b/ci.sh index e83b3b84..af98d6ed 100755 --- a/ci.sh +++ b/ci.sh @@ -19,6 +19,19 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- 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 \ @@ -72,6 +85,8 @@ cargo batch \ --- 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,stm32f429zi,log,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 \ @@ -79,6 +94,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 \ @@ -131,9 +147,10 @@ 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/nrf \ - --- 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/nrf \ + --- 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 \ --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \ --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \ --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f7 \ @@ -156,11 +173,17 @@ 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/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/nrf52840-dk/wifi_esp_hosted_perf if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 855d54b1..dae7419c 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -11,8 +11,8 @@ log = ["dep:log"] firmware-logs = [] [dependencies] -embassy-time = { version = "0.1.2", path = "../embassy-time"} -embassy-sync = { version = "0.2.0", path = "../embassy-sync"} +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" @@ -31,4 +31,4 @@ num_enum = { version = "0.5.7", default-features = false } src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/" target = "thumbv6m-none-eabi" -features = ["defmt", "firmware-logs"] \ No newline at end of file +features = ["defmt", "firmware-logs"] diff --git a/cyw43/src/consts.rs b/cyw43/src/consts.rs index 1f655158..4e2836f3 100644 --- a/cyw43/src/consts.rs +++ b/cyw43/src/consts.rs @@ -96,6 +96,7 @@ pub(crate) const IOCTL_CMD_UP: u32 = 2; pub(crate) const IOCTL_CMD_DOWN: u32 = 3; pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30; +pub(crate) const IOCTL_CMD_DISASSOC: u32 = 52; pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64; pub(crate) const IOCTL_CMD_SET_AP: u32 = 118; pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index c67614dd..a6d1f0bf 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -124,7 +124,7 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; // set wifi up - self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; + self.up().await; Timer::after(Duration::from_millis(100)).await; @@ -138,6 +138,16 @@ impl<'a> Control<'a> { debug!("INIT DONE"); } + /// Set the WiFi interface up. + async fn up(&mut self) { + self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; + } + + /// Set the interface down. + async fn down(&mut self) { + self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; + } + pub async fn set_power_management(&mut self, mode: PowerManagementMode) { // power save mode let mode_num = mode.mode(); @@ -256,13 +266,13 @@ impl<'a> Control<'a> { } // Temporarily set wifi down - self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; + self.down().await; // Turn off APSTA mode self.set_iovar_u32("apsta", 0).await; // Set wifi up again - self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; + self.up().await; // Turn on AP mode self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; @@ -423,6 +433,11 @@ impl<'a> Control<'a> { events: &self.events, } } + /// Leave the wifi, with which we are currently associated. + pub async fn leave(&mut self) { + self.ioctl(IoctlType::Set, IOCTL_CMD_DISASSOC, 0, &mut []).await; + info!("Disassociated") + } } pub struct Scanner<'a> { diff --git a/cyw43/src/fmt.rs b/cyw43/src/fmt.rs index 9534c101..78e583c1 100644 --- a/cyw43/src/fmt.rs +++ b/cyw43/src/fmt.rs @@ -83,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -226,7 +229,8 @@ impl Try for Result { } } -pub struct Bytes<'a>(pub &'a [u8]); +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index 30a3d5f2..6b124cf7 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs @@ -27,7 +27,7 @@ use ioctl::IoctlState; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; -pub use crate::control::{Control, Error as ControlError}; +pub use crate::control::{Control, Error as ControlError, Scanner}; pub use crate::runner::Runner; pub use crate::structs::BssInfo; diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 86f2996f..261a3c19 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -8,3 +8,5 @@ * xref:bootloader.adoc[Bootloader] * xref:examples.adoc[Examples] +* xref:developer.adoc[Developer] +** xref:developer_stm32.adoc[Developer: STM32] \ No newline at end of file diff --git a/docs/modules/ROOT/pages/developer.adoc b/docs/modules/ROOT/pages/developer.adoc new file mode 100644 index 00000000..e03ee51a --- /dev/null +++ b/docs/modules/ROOT/pages/developer.adoc @@ -0,0 +1 @@ += Developer Documentation \ No newline at end of file diff --git a/docs/modules/ROOT/pages/developer_stm32.adoc b/docs/modules/ROOT/pages/developer_stm32.adoc new file mode 100644 index 00000000..7c04ab1a --- /dev/null +++ b/docs/modules/ROOT/pages/developer_stm32.adoc @@ -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] +---- + + + +---- + +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. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 0a17c673..805a1e70 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -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? diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 415d7960..6a334f01 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -28,7 +28,7 @@ digest = "0.10" log = { version = "0.4", optional = true } ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync" } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } diff --git a/embassy-boot/boot/src/fmt.rs b/embassy-boot/boot/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-boot/boot/src/fmt.rs +++ b/embassy-boot/boot/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-boot/nrf/src/fmt.rs b/embassy-boot/nrf/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-boot/nrf/src/fmt.rs +++ b/embassy-boot/nrf/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-boot/rp/src/fmt.rs b/embassy-boot/rp/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-boot/rp/src/fmt.rs +++ b/embassy-boot/rp/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-boot/stm32/src/fmt.rs b/embassy-boot/stm32/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-boot/stm32/src/fmt.rs +++ b/embassy-boot/stm32/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index fd921d27..62a95ed9 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -20,8 +20,8 @@ default = ["time"] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", ] } diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 4853ba29..ccbca1eb 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,11 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 0.3.0 - TBD +## 0.3.0 - 2023-08-25 - Replaced Pender. Implementations now must define an extern function called `__pender`. - Made `raw::AvailableTask` public - Made `SpawnToken::new_failed` public +- You can now use arbitrary expressions to specify `#[task(pool_size = X)]` ## 0.2.1 - 2023-08-10 diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 2a67f70d..35944625 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -58,8 +58,8 @@ log = { version = "0.4.14", optional = true } rtos-trace = { version = "0.1.2", optional = true } futures-util = { version = "0.3.17", default-features = false } -embassy-macros = { version = "0.2.0", path = "../embassy-macros" } -embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true} +embassy-macros = { version = "0.2.1", path = "../embassy-macros" } +embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" critical-section = "1.1" static_cell = "1.1" diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 0806a22a..fde862f3 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,5 +1,3 @@ -const THREAD_PENDER: usize = usize::MAX; - #[export_name = "__pender"] #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] fn __pender(context: *mut ()) { @@ -48,13 +46,14 @@ fn __pender(context: *mut ()) { pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { + pub(super) const THREAD_PENDER: usize = usize::MAX; + use core::arch::asm; use core::marker::PhantomData; #[cfg(feature = "nightly")] pub use embassy_macros::main_cortex_m as main; - use crate::arch::THREAD_PENDER; use crate::{raw, Spawner}; /// Thread mode executor, using WFE/SEV. diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 934fd69e..15aed867 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -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()); } } diff --git a/embassy-executor/src/fmt.rs b/embassy-executor/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-executor/src/fmt.rs +++ b/embassy-executor/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index c1d82e18..6d2c1c18 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -203,7 +203,7 @@ impl AvailableTask { fn initialize_impl(self, future: impl FnOnce() -> F) -> SpawnToken { unsafe { self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); - self.task.future.write(future()); + self.task.future.write_in_place(future); let task = TaskRef::new(self.task); diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index e2e8f4df..c46085e4 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs @@ -17,8 +17,9 @@ impl UninitCell { &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) { diff --git a/embassy-futures/src/fmt.rs b/embassy-futures/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-futures/src/fmt.rs +++ b/embassy-futures/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-hal-internal/src/fmt.rs b/embassy-hal-internal/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-hal-internal/src/fmt.rs +++ b/embassy-hal-internal/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index feea0658..88f815cd 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -20,15 +20,12 @@ defmt = ["dep:defmt", "lorawan-device/defmt"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-hal = { 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"} \ No newline at end of file +lora-phy = { version = "2" } +lorawan-device = { version = "0.11.0", default-features = false, features = ["async"], optional = true } diff --git a/embassy-lora/src/fmt.rs b/embassy-lora/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-lora/src/fmt.rs +++ b/embassy-lora/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs index 136973fe..d22beb33 100644 --- a/embassy-lora/src/iv.rs +++ b/embassy-lora/src/iv.rs @@ -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(()) } @@ -284,11 +280,7 @@ where self.busy.wait_for_low().await.map_err(|_| Busy) } async fn await_irq(&mut self) -> Result<(), RadioError> { - if self.board_type != BoardType::RpPicoWaveshareSx1262 { - self.dio1.wait_for_high().await.map_err(|_| DIO1)?; - } else { - self.dio1.wait_for_rising_edge().await.map_err(|_| DIO1)?; - } + self.dio1.wait_for_high().await.map_err(|_| DIO1)?; Ok(()) } diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 3b8fe8b4..a893cd30 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-macros" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "MIT OR Apache-2.0" description = "macros for creating the entry point and tasks for embassy-executor" diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml new file mode 100644 index 00000000..8de8eade --- /dev/null +++ b/embassy-net-adin1110/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "embassy-net-adin1110" +version = "0.2.0" +description = "embassy-net driver for the ADIN1110 ethernet chip" +keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +heapless = "0.7.16" +defmt = { version = "0.3", optional = true } +log = { version = "0.4", default-features = false, optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } +embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] } +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } +embassy-time = { version = "0.1.3" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +bitfield = "0.14.0" + +[dev-dependencies] +# reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged. +#embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", branch = "1-alpha", features = ["embedded-hal-async", "eh1"] }] } +embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] } +crc = "3.0.1" +env_logger = "0.10" +critical-section = { version = "1.1.2", features = ["std"] } +futures-test = "0.3.28" + +[features] +default = [ ] +defmt = [ "dep:defmt", "embedded-hal-1/defmt-03" ] +log = ["dep:log"] + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-adin1110-v$VERSION/embassy-net-adin1110/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-adin1110/src/" +target = "thumbv7em-none-eabi" +features = ["defmt"] diff --git a/embassy-net-adin1110/README.md b/embassy-net-adin1110/README.md new file mode 100644 index 00000000..8ea10b71 --- /dev/null +++ b/embassy-net-adin1110/README.md @@ -0,0 +1,88 @@ +# SPE ADIN1110 `embassy-net` integration + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the `Analog ADIN1110` SPI SPE ethernet chips. + +## What is SPE or Single Pair Ethernet / 10 BASE-T1L + +SPE stands for Single Pair Ethernet. As the names implies, SPE uses differential signalling with 2 wires (a twisted-pair) in a cable as the physical medium. +SPE is full-duplex - it can transmit and receive ethernet packets at the same time. SPE is still ethernet, only the physical layer is different. + +SPE also supports [`PoDL (Power over Data Line)`](https://www.ti.com/lit/an/snla395/snla395.pdf), power delivery from 0.5 up to 50 Watts, similar to [`PoE`](https://en.wikipedia.org/wiki/Power_over_Ethernet), but an additional hardware and handshake protocol are needed. + +SPE has many link speeds but only `10 BASE-T1L` is able to reach cable lengths up to 1000 meters in `2.4 Vpp` transmit amplitude. +Currently in 2023, none of the standards are compatible with each other. +Thus `10 BASE-T1L` won't work with a `10 BASE-T1S`, `100 BASE-T1` or any standard `x BASE-T`. + +In the industry SPE is also called [`APL (Advanced Physical Layer)`](https://www.ethernet-apl.org), and is based on the `10 BASE-T1L` standard. + +APL can be used in [`intrinsic safety applications/explosion hazardous areas`](https://en.wikipedia.org/wiki/Electrical_equipment_in_hazardous_areas) which has its own name and standard called [`2-WISE (2-wire intrinsically safe ethernet) IEC TS 60079-47:2021`](https://webstore.iec.ch/publication/64292). + +`10 BASE-T1L` and `ADIN1110` are designed to support intrinsic safety applications. The power supply energy is fixed and PDoL is not supported. + +## Supported SPI modes + +`ADIN1110` supports two SPI modes. `Generic` and [`OPEN Alliance 10BASE-T1x MAC-PHY serial interface`](https://opensig.org/download/document/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf) + +Both modes support with and without additional CRC. +Currently only `Generic` SPI with or without CRC is supported. + +*NOTE:* SPI Mode is selected by the hardware pins `SPI_CFG0` and `SPI_CFG1`. Software can't detect nor change the mode. + +## Hardware + +- Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) for an example. +- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit (End Of Life)`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!** + +## Other SPE chips + +* [`Analog ADIN2111`](https://www.analog.com/en/products/adin2111.html) 2 Port SPI version. Can work with this driver. +* [`Analog ADIN1100`](https://www.analog.com/en/products/adin1100.html) RGMII version. + +## Testing + +ADIN1110 library can tested on the host with a mock SPI driver. + +$ `cargo test --target x86_64-unknown-linux-gnu` + +## Benchmark + +- Benchmarked on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html), with [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) example. + +Basic `ping` benchmark +```rust,ignore +# ping -c 60 + +60 packets transmitted, 60 received, 0% packet loss, time 59066ms +rtt min/avg/max/mdev = 1.089/1.161/1.237/0.018 ms + +# ping -s 1472 -M do -c 60 + +60 packets transmitted, 60 received, 0% packet loss, time 59066ms +rtt min/avg/max/mdev = 5.122/5.162/6.177/0.133 ms +``` + +HTTP load generator benchmark with [`oha`](https://github.com/hatoo/oha) +```rust,ignore +# oha -c 1 http:// -z 60s +Summary: + Success rate: 50.00% + Total: 60.0005 secs + Slowest: 0.0055 secs + Fastest: 0.0033 secs + Average: 0.0034 secs + Requests/sec: 362.1971 + + Total data: 2.99 MiB + Size/request: 289 B + Size/sec: 51.11 KiB +``` + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-adin1110/src/crc32.rs b/embassy-net-adin1110/src/crc32.rs new file mode 100644 index 00000000..ec020b70 --- /dev/null +++ b/embassy-net-adin1110/src/crc32.rs @@ -0,0 +1,359 @@ +pub const CRC32R_LOOKUP_TABLE: [u32; 256] = [ + 0x0000_0000, + 0x7707_3096, + 0xEE0E_612C, + 0x9909_51BA, + 0x076D_C419, + 0x706A_F48F, + 0xE963_A535, + 0x9E64_95A3, + 0x0EDB_8832, + 0x79DC_B8A4, + 0xE0D5_E91E, + 0x97D2_D988, + 0x09B6_4C2B, + 0x7EB1_7CBD, + 0xE7B8_2D07, + 0x90BF_1D91, + 0x1DB7_1064, + 0x6AB0_20F2, + 0xF3B9_7148, + 0x84BE_41DE, + 0x1ADA_D47D, + 0x6DDD_E4EB, + 0xF4D4_B551, + 0x83D3_85C7, + 0x136C_9856, + 0x646B_A8C0, + 0xFD62_F97A, + 0x8A65_C9EC, + 0x1401_5C4F, + 0x6306_6CD9, + 0xFA0F_3D63, + 0x8D08_0DF5, + 0x3B6E_20C8, + 0x4C69_105E, + 0xD560_41E4, + 0xA267_7172, + 0x3C03_E4D1, + 0x4B04_D447, + 0xD20D_85FD, + 0xA50A_B56B, + 0x35B5_A8FA, + 0x42B2_986C, + 0xDBBB_C9D6, + 0xACBC_F940, + 0x32D8_6CE3, + 0x45DF_5C75, + 0xDCD6_0DCF, + 0xABD1_3D59, + 0x26D9_30AC, + 0x51DE_003A, + 0xC8D7_5180, + 0xBFD0_6116, + 0x21B4_F4B5, + 0x56B3_C423, + 0xCFBA_9599, + 0xB8BD_A50F, + 0x2802_B89E, + 0x5F05_8808, + 0xC60C_D9B2, + 0xB10B_E924, + 0x2F6F_7C87, + 0x5868_4C11, + 0xC161_1DAB, + 0xB666_2D3D, + 0x76DC_4190, + 0x01DB_7106, + 0x98D2_20BC, + 0xEFD5_102A, + 0x71B1_8589, + 0x06B6_B51F, + 0x9FBF_E4A5, + 0xE8B8_D433, + 0x7807_C9A2, + 0x0F00_F934, + 0x9609_A88E, + 0xE10E_9818, + 0x7F6A_0DBB, + 0x086D_3D2D, + 0x9164_6C97, + 0xE663_5C01, + 0x6B6B_51F4, + 0x1C6C_6162, + 0x8565_30D8, + 0xF262_004E, + 0x6C06_95ED, + 0x1B01_A57B, + 0x8208_F4C1, + 0xF50F_C457, + 0x65B0_D9C6, + 0x12B7_E950, + 0x8BBE_B8EA, + 0xFCB9_887C, + 0x62DD_1DDF, + 0x15DA_2D49, + 0x8CD3_7CF3, + 0xFBD4_4C65, + 0x4DB2_6158, + 0x3AB5_51CE, + 0xA3BC_0074, + 0xD4BB_30E2, + 0x4ADF_A541, + 0x3DD8_95D7, + 0xA4D1_C46D, + 0xD3D6_F4FB, + 0x4369_E96A, + 0x346E_D9FC, + 0xAD67_8846, + 0xDA60_B8D0, + 0x4404_2D73, + 0x3303_1DE5, + 0xAA0A_4C5F, + 0xDD0D_7CC9, + 0x5005_713C, + 0x2702_41AA, + 0xBE0B_1010, + 0xC90C_2086, + 0x5768_B525, + 0x206F_85B3, + 0xB966_D409, + 0xCE61_E49F, + 0x5EDE_F90E, + 0x29D9_C998, + 0xB0D0_9822, + 0xC7D7_A8B4, + 0x59B3_3D17, + 0x2EB4_0D81, + 0xB7BD_5C3B, + 0xC0BA_6CAD, + 0xEDB8_8320, + 0x9ABF_B3B6, + 0x03B6_E20C, + 0x74B1_D29A, + 0xEAD5_4739, + 0x9DD2_77AF, + 0x04DB_2615, + 0x73DC_1683, + 0xE363_0B12, + 0x9464_3B84, + 0x0D6D_6A3E, + 0x7A6A_5AA8, + 0xE40E_CF0B, + 0x9309_FF9D, + 0x0A00_AE27, + 0x7D07_9EB1, + 0xF00F_9344, + 0x8708_A3D2, + 0x1E01_F268, + 0x6906_C2FE, + 0xF762_575D, + 0x8065_67CB, + 0x196C_3671, + 0x6E6B_06E7, + 0xFED4_1B76, + 0x89D3_2BE0, + 0x10DA_7A5A, + 0x67DD_4ACC, + 0xF9B9_DF6F, + 0x8EBE_EFF9, + 0x17B7_BE43, + 0x60B0_8ED5, + 0xD6D6_A3E8, + 0xA1D1_937E, + 0x38D8_C2C4, + 0x4FDF_F252, + 0xD1BB_67F1, + 0xA6BC_5767, + 0x3FB5_06DD, + 0x48B2_364B, + 0xD80D_2BDA, + 0xAF0A_1B4C, + 0x3603_4AF6, + 0x4104_7A60, + 0xDF60_EFC3, + 0xA867_DF55, + 0x316E_8EEF, + 0x4669_BE79, + 0xCB61_B38C, + 0xBC66_831A, + 0x256F_D2A0, + 0x5268_E236, + 0xCC0C_7795, + 0xBB0B_4703, + 0x2202_16B9, + 0x5505_262F, + 0xC5BA_3BBE, + 0xB2BD_0B28, + 0x2BB4_5A92, + 0x5CB3_6A04, + 0xC2D7_FFA7, + 0xB5D0_CF31, + 0x2CD9_9E8B, + 0x5BDE_AE1D, + 0x9B64_C2B0, + 0xEC63_F226, + 0x756A_A39C, + 0x026D_930A, + 0x9C09_06A9, + 0xEB0E_363F, + 0x7207_6785, + 0x0500_5713, + 0x95BF_4A82, + 0xE2B8_7A14, + 0x7BB1_2BAE, + 0x0CB6_1B38, + 0x92D2_8E9B, + 0xE5D5_BE0D, + 0x7CDC_EFB7, + 0x0BDB_DF21, + 0x86D3_D2D4, + 0xF1D4_E242, + 0x68DD_B3F8, + 0x1FDA_836E, + 0x81BE_16CD, + 0xF6B9_265B, + 0x6FB0_77E1, + 0x18B7_4777, + 0x8808_5AE6, + 0xFF0F_6A70, + 0x6606_3BCA, + 0x1101_0B5C, + 0x8F65_9EFF, + 0xF862_AE69, + 0x616B_FFD3, + 0x166C_CF45, + 0xA00A_E278, + 0xD70D_D2EE, + 0x4E04_8354, + 0x3903_B3C2, + 0xA767_2661, + 0xD060_16F7, + 0x4969_474D, + 0x3E6E_77DB, + 0xAED1_6A4A, + 0xD9D6_5ADC, + 0x40DF_0B66, + 0x37D8_3BF0, + 0xA9BC_AE53, + 0xDEBB_9EC5, + 0x47B2_CF7F, + 0x30B5_FFE9, + 0xBDBD_F21C, + 0xCABA_C28A, + 0x53B3_9330, + 0x24B4_A3A6, + 0xBAD0_3605, + 0xCDD7_0693, + 0x54DE_5729, + 0x23D9_67BF, + 0xB366_7A2E, + 0xC461_4AB8, + 0x5D68_1B02, + 0x2A6F_2B94, + 0xB40B_BE37, + 0xC30C_8EA1, + 0x5A05_DF1B, + 0x2D02_EF8D, +]; + +/// Generate Ethernet Frame Check Sequence +#[allow(non_camel_case_types)] +#[derive(Debug)] +pub struct ETH_FCS(pub u32); + +impl ETH_FCS { + pub const CRC32_OK: u32 = 0x2144_df1c; + + #[must_use] + pub fn new(data: &[u8]) -> Self { + let fcs = data.iter().fold(u32::MAX, |crc, byte| { + let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte; + CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) + }) ^ u32::MAX; + Self(fcs) + } + + #[must_use] + pub fn update(self, data: &[u8]) -> Self { + let fcs = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| { + let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte; + CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) + }) ^ u32::MAX; + Self(fcs) + } + + #[must_use] + pub fn crc_ok(&self) -> bool { + self.0 == Self::CRC32_OK + } + + #[must_use] + pub fn hton_bytes(&self) -> [u8; 4] { + self.0.to_le_bytes() + } + + #[must_use] + pub fn hton(&self) -> u32 { + self.0.to_le() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn crc32_ethernet_frame() { + let packet_a = &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xff, 0x06, 0x00, 0x01, 0x08, 0x00, + 0x06, 0x04, 0x00, 0x01, 0x00, 0xe0, 0x4c, 0x68, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xa8, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x65, 0x90, 0x3d, + ]; + + let packet_b = &[ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00, + 0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78, + 0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c, + ]; + + // Packet A + let own_crc = ETH_FCS::new(&packet_a[0..60]); + let crc_bytes = own_crc.hton_bytes(); + println!("{:08x} {:02x?}", own_crc.0, crc_bytes); + assert_eq!(&crc_bytes, &packet_a[60..64]); + + let own_crc = ETH_FCS::new(packet_a); + println!("{:08x}", own_crc.0); + assert_eq!(own_crc.0, ETH_FCS::CRC32_OK); + + // Packet B + let own_crc = ETH_FCS::new(&packet_b[0..60]); + let crc_bytes = own_crc.hton_bytes(); + println!("{:08x} {:02x?}", own_crc.0, crc_bytes); + assert_eq!(&crc_bytes, &packet_b[60..64]); + + let own_crc = ETH_FCS::new(packet_b); + println!("{:08x}", own_crc.0); + assert_eq!(own_crc.0, ETH_FCS::CRC32_OK); + } + + #[test] + fn crc32_update() { + let full_data = &[ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00, + 0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78, + 0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c, + ]; + + let (part_a, part_b) = full_data.split_at(16); + let crc_partially = ETH_FCS::new(part_a).update(part_b); + + let crc_full = ETH_FCS::new(full_data); + + assert_eq!(crc_full.0, crc_partially.0); + } +} diff --git a/embassy-net-adin1110/src/crc8.rs b/embassy-net-adin1110/src/crc8.rs new file mode 100644 index 00000000..7d20a740 --- /dev/null +++ b/embassy-net-adin1110/src/crc8.rs @@ -0,0 +1,53 @@ +/// CRC-8/ITU +const CRC8X_TABLE: [u8; 256] = [ + 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, + 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, + 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, + 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, + 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, + 0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, + 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80, 0x95, + 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, + 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, + 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, + 0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e, + 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7, + 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, + 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3, +]; + +/// Calculate the crc of a pease of data. +pub fn crc8(data: &[u8]) -> u8 { + data.iter().fold(0, |crc, &byte| CRC8X_TABLE[usize::from(byte ^ crc)]) +} + +#[cfg(test)] +mod tests { + use ::crc::{Crc, CRC_8_SMBUS}; + + use super::crc8; + + #[test] + fn spi_header_crc8() { + let data = &[0x80, 0x00]; + + let c = Crc::::new(&CRC_8_SMBUS); + let mut dig = c.digest(); + dig.update(data); + let sw_crc = dig.finalize(); + + let own_crc = crc8(data); + + assert_eq!(own_crc, sw_crc); + assert_eq!(own_crc, 182); + + let data = &[0x80, 0x01]; + let mut dig = c.digest(); + dig.update(data); + let sw_crc = dig.finalize(); + let own_crc = crc8(data); + + assert_eq!(own_crc, sw_crc); + assert_eq!(own_crc, 177); + } +} diff --git a/embassy-net-adin1110/src/fmt.rs b/embassy-net-adin1110/src/fmt.rs new file mode 100644 index 00000000..12737c69 --- /dev/null +++ b/embassy-net-adin1110/src/fmt.rs @@ -0,0 +1,254 @@ +#![macro_use] +#![allow(unused_macros)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} + +pub struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs new file mode 100644 index 00000000..53f36128 --- /dev/null +++ b/embassy-net-adin1110/src/lib.rs @@ -0,0 +1,1323 @@ +#![deny(clippy::pedantic)] +#![feature(async_fn_in_trait)] +#![cfg_attr(not(any(test, feature = "std")), no_std)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::missing_panics_doc)] +#![doc = include_str!("../README.md")] + +// must go first! +mod fmt; + +mod crc32; +mod crc8; +mod mdio; +mod phy; +mod regs; + +use ch::driver::LinkState; +pub use crc32::ETH_FCS; +use crc8::crc8; +use embassy_futures::select::{select, Either}; +use embassy_net_driver_channel as ch; +use embassy_time::{Duration, Timer}; +use embedded_hal_1::digital::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::{Error, Operation, SpiDevice}; +use heapless::Vec; +pub use mdio::MdioBus; +pub use phy::{Phy10BaseT1x, RegsC22, RegsC45}; +pub use regs::{Config0, Config2, SpiRegisters as sr, Status0, Status1}; + +use crate::fmt::Bytes; +use crate::regs::{LedCntrl, LedFunc, LedPol, LedPolarity, SpiHeader}; + +/// ADIN1110 intern PHY ID +pub const PHYID: u32 = 0x0283_BC91; + +/// Error values ADIN1110 +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(non_camel_case_types)] +pub enum AdinError { + /// SPI-BUS Error + Spi(E), + /// Ethernet FCS error + FCS, + /// SPI Header CRC error + SPI_CRC, + /// Received or sended ethernet packet is too big + PACKET_TOO_BIG, + /// Received or sended ethernet packet is too small + PACKET_TOO_SMALL, + /// MDIO transaction timeout + MDIO_ACC_TIMEOUT, +} + +/// Type alias `Result` type with `AdinError` as error type. +pub type AEResult = core::result::Result>; + +/// Internet PHY address +pub const MDIO_PHY_ADDR: u8 = 0x01; + +/// Maximum Transmission Unit +pub const MTU: usize = 1514; + +/// Max SPI/Frame buffer size +pub const MAX_BUFF: usize = 2048; + +const DONT_CARE_BYTE: u8 = 0x00; +const TURN_AROUND_BYTE: u8 = 0x00; + +/// Packet minimal frame/packet length +const ETH_MIN_LEN: usize = 64; +/// Ethernet `Frame Check Sequence` length +const FCS_LEN: usize = 4; +/// Packet minimal frame/packet length without `Frame Check Sequence` length +const ETH_MIN_WITHOUT_FCS_LEN: usize = ETH_MIN_LEN - FCS_LEN; + +/// SPI Header, contains SPI action and register id. +const SPI_HEADER_LEN: usize = 2; +/// SPI Header CRC length +const SPI_HEADER_CRC_LEN: usize = 1; +/// SPI Header Turn Around length +const SPI_HEADER_TA_LEN: usize = 1; +/// Frame Header length +const FRAME_HEADER_LEN: usize = 2; +/// Space for last bytes to create multipule 4 bytes on the end of a FIFO read/write. +const SPI_SPACE_MULTIPULE: usize = 3; + +/// P1 = 0x00, P2 = 0x01 +const PORT_ID_BYTE: u8 = 0x00; + +/// Type alias for the embassy-net driver for ADIN1110 +pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; + +/// Internal state for the embassy-net integration. +pub struct State { + ch_state: ch::State, +} +impl State { + /// Create a new `State`. + #[must_use] + pub const fn new() -> Self { + Self { + ch_state: ch::State::new(), + } + } +} + +/// ADIN1110 embassy-net driver +#[derive(Debug)] +pub struct ADIN1110 { + /// SPI bus + spi: SPI, + /// Enable CRC on SPI transfer. + /// This must match with the hardware pin `SPI_CFG0` were low = CRC enable, high = CRC disabled. + spi_crc: bool, + /// Append FCS by the application of transmit packet, false = FCS is appended by the MAC, true = FCS appended by the application. + append_fcs_on_tx: bool, +} + +impl ADIN1110 { + /// Create a new ADIN1110 instance. + pub fn new(spi: SPI, spi_crc: bool, append_fcs_on_tx: bool) -> Self { + Self { + spi, + spi_crc, + append_fcs_on_tx, + } + } + + /// Read a SPI register + pub async fn read_reg(&mut self, reg: sr) -> AEResult { + let mut tx_buf = Vec::::new(); + + let mut spi_hdr = SpiHeader(0); + spi_hdr.set_control(true); + spi_hdr.set_addr(reg); + let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); + + if self.spi_crc { + // Add CRC for header data + let _ = tx_buf.push(crc8(&tx_buf)); + } + + // Turn around byte, give the chip the time to access/setup the answer data. + let _ = tx_buf.push(TURN_AROUND_BYTE); + + let mut rx_buf = [0; 5]; + + let spi_read_len = if self.spi_crc { rx_buf.len() } else { rx_buf.len() - 1 }; + + let mut spi_op = [Operation::Write(&tx_buf), Operation::Read(&mut rx_buf[0..spi_read_len])]; + + self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?; + + if self.spi_crc { + let crc = crc8(&rx_buf[0..4]); + if crc != rx_buf[4] { + return Err(AdinError::SPI_CRC); + } + } + + let value = u32::from_be_bytes(rx_buf[0..4].try_into().unwrap()); + + trace!("REG Read {} = {:08x} SPI {}", reg, value, Bytes(&tx_buf)); + + Ok(value) + } + + /// Write a SPI register + pub async fn write_reg(&mut self, reg: sr, value: u32) -> AEResult<(), SPI::Error> { + let mut tx_buf = Vec::::new(); + + let mut spi_hdr = SpiHeader(0); + spi_hdr.set_control(true); + spi_hdr.set_write(true); + spi_hdr.set_addr(reg); + let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); + + if self.spi_crc { + // Add CRC for header data + let _ = tx_buf.push(crc8(&tx_buf)); + } + + let val = value.to_be_bytes(); + let _ = tx_buf.extend_from_slice(val.as_slice()); + + if self.spi_crc { + // Add CRC for header data + let _ = tx_buf.push(crc8(val.as_slice())); + } + + trace!("REG Write {} = {:08x} SPI {}", reg, value, Bytes(&tx_buf)); + + self.spi.write(&tx_buf).await.map_err(AdinError::Spi) + } + + /// helper function for write to `MDIO_ACC` register and wait for ready! + async fn write_mdio_acc_reg(&mut self, mdio_acc_val: u32) -> AEResult { + self.write_reg(sr::MDIO_ACC, mdio_acc_val).await?; + + // TODO: Add proper timeout! + for _ in 0..100_000 { + let val = self.read_reg(sr::MDIO_ACC).await?; + if val & 0x8000_0000 != 0 { + return Ok(val); + } + } + + Err(AdinError::MDIO_ACC_TIMEOUT) + } + + /// Read out fifo ethernet packet memory received via the wire. + pub async fn read_fifo(&mut self, frame: &mut [u8]) -> AEResult { + const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + SPI_HEADER_TA_LEN; + const TAIL_LEN: usize = FCS_LEN + SPI_SPACE_MULTIPULE; + + let mut tx_buf = Vec::::new(); + + // Size of the frame, also includes the `frame header` and `FCS`. + let fifo_frame_size = self.read_reg(sr::RX_FSIZE).await? as usize; + + if fifo_frame_size < ETH_MIN_LEN + FRAME_HEADER_LEN { + return Err(AdinError::PACKET_TOO_SMALL); + } + + let packet_size = fifo_frame_size - FRAME_HEADER_LEN - FCS_LEN; + + if packet_size > frame.len() { + trace!("MAX: {} WANT: {}", frame.len(), packet_size); + return Err(AdinError::PACKET_TOO_BIG); + } + + let mut spi_hdr = SpiHeader(0); + spi_hdr.set_control(true); + spi_hdr.set_addr(sr::RX); + let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); + + if self.spi_crc { + // Add CRC for header data + let _ = tx_buf.push(crc8(&tx_buf)); + } + + // Turn around byte, TODO: Unknown that this is. + let _ = tx_buf.push(TURN_AROUND_BYTE); + + let mut frame_header = [0, 0]; + let mut fcs_and_extra = [0; TAIL_LEN]; + + // Packet read of write to the MAC packet buffer must be a multipul of 4! + let tail_size = (fifo_frame_size & 0x03) + FCS_LEN; + + let mut spi_op = [ + Operation::Write(&tx_buf), + Operation::Read(&mut frame_header), + Operation::Read(&mut frame[0..packet_size]), + Operation::Read(&mut fcs_and_extra[0..tail_size]), + ]; + + self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?; + + // According to register `CONFIG2`, bit 5 `CRC_APPEND` discription: + // "Similarly, on receive, the CRC32 is forwarded with the frame to the host where the host must verify it is correct." + // The application must allways check the FCS. It seems that the MAC/PHY has no option to handle this. + let fcs_calc = ETH_FCS::new(&frame[0..packet_size]); + + if fcs_calc.hton_bytes() == fcs_and_extra[0..4] { + Ok(packet_size) + } else { + Err(AdinError::FCS) + } + } + + /// Write to fifo ethernet packet memory send over the wire. + pub async fn write_fifo(&mut self, frame: &[u8]) -> AEResult<(), SPI::Error> { + const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + FRAME_HEADER_LEN; + const TAIL_LEN: usize = ETH_MIN_LEN - FCS_LEN + FCS_LEN + SPI_SPACE_MULTIPULE; + + if frame.len() < (6 + 6 + 2) { + return Err(AdinError::PACKET_TOO_SMALL); + } + if frame.len() > (MAX_BUFF - FRAME_HEADER_LEN) { + return Err(AdinError::PACKET_TOO_BIG); + } + + // SPI HEADER + [OPTIONAL SPI CRC] + FRAME HEADER + let mut head_data = Vec::::new(); + // [OPTIONAL PAD DATA] + FCS + [OPTINAL BYTES MAKE SPI FRAME EVEN] + let mut tail_data = Vec::::new(); + + let mut spi_hdr = SpiHeader(0); + spi_hdr.set_control(true); + spi_hdr.set_write(true); + spi_hdr.set_addr(sr::TX); + + head_data + .extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()) + .map_err(|_e| AdinError::PACKET_TOO_BIG)?; + + if self.spi_crc { + // Add CRC for header data + head_data + .push(crc8(&head_data[0..2])) + .map_err(|_| AdinError::PACKET_TOO_BIG)?; + } + + // Add port number, ADIN1110 its fixed to zero/P1, but for ADIN2111 has two ports. + head_data + .extend_from_slice(u16::from(PORT_ID_BYTE).to_be_bytes().as_slice()) + .map_err(|_e| AdinError::PACKET_TOO_BIG)?; + + // ADIN1110 MAC and PHY don´t accept ethernet packet smaller than 64 bytes. + // So padded the data minus the FCS, FCS is automatilly added to by the MAC. + if frame.len() < ETH_MIN_WITHOUT_FCS_LEN { + let _ = tail_data.resize(ETH_MIN_WITHOUT_FCS_LEN - frame.len(), 0x00); + } + + // Append FCS by the application + if self.append_fcs_on_tx { + let mut frame_fcs = ETH_FCS::new(frame); + + if !tail_data.is_empty() { + frame_fcs = frame_fcs.update(&tail_data); + } + + let _ = tail_data.extend_from_slice(frame_fcs.hton_bytes().as_slice()); + } + + // len = frame_size + optional padding + 2 bytes Frame header + let send_len_orig = frame.len() + tail_data.len() + FRAME_HEADER_LEN; + + let send_len = u32::try_from(send_len_orig).map_err(|_| AdinError::PACKET_TOO_BIG)?; + + // Packet read of write to the MAC packet buffer must be a multipul of 4 bytes! + let pad_len = send_len_orig & 0x03; + if pad_len != 0 { + let spi_pad_len = 4 - pad_len + tail_data.len(); + let _ = tail_data.resize(spi_pad_len, DONT_CARE_BYTE); + } + + self.write_reg(sr::TX_FSIZE, send_len).await?; + + trace!( + "TX: hdr {} [{}] {}-{}-{} SIZE: {}", + head_data.len(), + frame.len(), + Bytes(head_data.as_slice()), + Bytes(frame), + Bytes(tail_data.as_slice()), + send_len, + ); + + let mut transaction = [ + Operation::Write(head_data.as_slice()), + Operation::Write(frame), + Operation::Write(tail_data.as_slice()), + ]; + + self.spi.transaction(&mut transaction).await.map_err(AdinError::Spi) + } + + /// Programs the mac address in the mac filters. + /// Also set the boardcast address. + /// The chip supports 2 priority queues but current code doesn't support this mode. + pub async fn set_mac_addr(&mut self, mac: &[u8; 6]) -> AEResult<(), SPI::Error> { + let mac_high_part = u16::from_be_bytes(mac[0..2].try_into().unwrap()); + let mac_low_part = u32::from_be_bytes(mac[2..6].try_into().unwrap()); + + // program our mac address in the mac address filter + self.write_reg(sr::ADDR_FILT_UPR0, (1 << 16) | (1 << 30) | u32::from(mac_high_part)) + .await?; + self.write_reg(sr::ADDR_FILT_LWR0, mac_low_part).await?; + + self.write_reg(sr::ADDR_MSK_UPR0, u32::from(mac_high_part)).await?; + self.write_reg(sr::ADDR_MSK_LWR0, mac_low_part).await?; + + // Also program broadcast address in the mac address filter + self.write_reg(sr::ADDR_FILT_UPR1, (1 << 16) | (1 << 30) | 0xFFFF) + .await?; + self.write_reg(sr::ADDR_FILT_LWR1, 0xFFFF_FFFF).await?; + self.write_reg(sr::ADDR_MSK_UPR1, 0xFFFF).await?; + self.write_reg(sr::ADDR_MSK_LWR1, 0xFFFF_FFFF).await?; + + Ok(()) + } +} + +impl mdio::MdioBus for ADIN1110 { + type Error = AdinError; + + /// Read from the PHY Registers as Clause 22. + async fn read_cl22(&mut self, phy_id: u8, reg: u8) -> Result { + let mdio_acc_val: u32 = + (0x1 << 28) | u32::from(phy_id & 0x1F) << 21 | u32::from(reg & 0x1F) << 16 | (0x3 << 26); + + // Result is in the lower half of the answer. + #[allow(clippy::cast_possible_truncation)] + self.write_mdio_acc_reg(mdio_acc_val).await.map(|val| val as u16) + } + + /// Read from the PHY Registers as Clause 45. + async fn read_cl45(&mut self, phy_id: u8, regc45: (u8, u16)) -> Result { + let mdio_acc_val = u32::from(phy_id & 0x1F) << 21 | u32::from(regc45.0 & 0x1F) << 16 | u32::from(regc45.1); + + self.write_mdio_acc_reg(mdio_acc_val).await?; + + let mdio_acc_val = u32::from(phy_id & 0x1F) << 21 | u32::from(regc45.0 & 0x1F) << 16 | (0x03 << 26); + + // Result is in the lower half of the answer. + #[allow(clippy::cast_possible_truncation)] + self.write_mdio_acc_reg(mdio_acc_val).await.map(|val| val as u16) + } + + /// Write to the PHY Registers as Clause 22. + async fn write_cl22(&mut self, phy_id: u8, reg: u8, val: u16) -> Result<(), Self::Error> { + let mdio_acc_val: u32 = + (0x1 << 28) | u32::from(phy_id & 0x1F) << 21 | u32::from(reg & 0x1F) << 16 | (0x1 << 26) | u32::from(val); + + self.write_mdio_acc_reg(mdio_acc_val).await.map(|_| ()) + } + + /// Write to the PHY Registers as Clause 45. + async fn write_cl45(&mut self, phy_id: u8, regc45: (u8, u16), value: u16) -> AEResult<(), SPI::Error> { + let phy_id = u32::from(phy_id & 0x1F) << 21; + let dev_addr = u32::from(regc45.0 & 0x1F) << 16; + let reg = u32::from(regc45.1); + + let mdio_acc_val: u32 = phy_id | dev_addr | reg; + self.write_mdio_acc_reg(mdio_acc_val).await?; + + let mdio_acc_val: u32 = phy_id | dev_addr | (0x01 << 26) | u32::from(value); + self.write_mdio_acc_reg(mdio_acc_val).await.map(|_| ()) + } +} + +/// Background runner for the ADIN1110. +/// +/// You must call `.run()` in a background task for the ADIN1110 to operate. +pub struct Runner<'d, SPI, INT, RST> { + mac: ADIN1110, + ch: ch::Runner<'d, MTU>, + int: INT, + is_link_up: bool, + _reset: RST, +} + +impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { + #[allow(clippy::too_many_lines)] + pub async fn run(mut self) -> ! { + loop { + let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); + + loop { + debug!("Waiting for interrupts"); + match select(self.int.wait_for_low(), tx_chan.tx_buf()).await { + Either::First(_) => { + let mut status1_clr = Status1(0); + let mut status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap()); + + while status1.p1_rx_rdy() { + debug!("alloc RX packet buffer"); + match select(rx_chan.rx_buf(), tx_chan.tx_buf()).await { + // Handle frames that needs to transmit from the wire. + // Note: rx_chan.rx_buf() channel don´t accept new request + // when the tx_chan is full. So these will be handled + // automaticly. + Either::First(frame) => match self.mac.read_fifo(frame).await { + Ok(n) => { + rx_chan.rx_done(n); + } + Err(e) => match e { + AdinError::PACKET_TOO_BIG => { + error!("RX Packet too big, DROP"); + self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap(); + } + AdinError::PACKET_TOO_SMALL => { + error!("RX Packet too small, DROP"); + self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap(); + } + AdinError::Spi(e) => { + error!("RX Spi error {}", e.kind()); + } + e => { + error!("RX Error {:?}", e); + } + }, + }, + Either::Second(frame) => { + // Handle frames that needs to transmit to the wire. + self.mac.write_fifo(frame).await.unwrap(); + tx_chan.tx_done(); + } + } + status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap()); + } + + let status0 = Status0(self.mac.read_reg(sr::STATUS0).await.unwrap()); + if status1.0 & !0x1b != 0 { + error!("SPE CHIP STATUS 0:{:08x} 1:{:08x}", status0.0, status1.0); + } + + if status1.tx_rdy() { + status1_clr.set_tx_rdy(true); + trace!("TX_DONE"); + } + + if status1.link_change() { + let link = status1.p1_link_status(); + self.is_link_up = link; + + if link { + let link_status = self + .mac + .read_cl45(MDIO_PHY_ADDR, RegsC45::DA7::AN_STATUS_EXTRA.into()) + .await + .unwrap(); + + let volt = if link_status & (0b11 << 5) == (0b11 << 5) { + "2.4" + } else { + "1.0" + }; + + let mse = self + .mac + .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1::MSE_VAL.into()) + .await + .unwrap(); + + info!("LINK Changed: Link Up, Volt: {} V p-p, MSE: {:0004}", volt, mse); + } else { + info!("LINK Changed: Link Down"); + } + + state_chan.set_link_state(if link { LinkState::Up } else { LinkState::Down }); + status1_clr.set_link_change(true); + } + + if status1.tx_ecc_err() { + error!("SPI TX_ECC_ERR error, CLEAR TX FIFO"); + self.mac.write_reg(sr::FIFO_CLR, 2).await.unwrap(); + status1_clr.set_tx_ecc_err(true); + } + + if status1.rx_ecc_err() { + error!("SPI RX_ECC_ERR error"); + status1_clr.set_rx_ecc_err(true); + } + + if status1.spi_err() { + error!("SPI SPI_ERR CRC error"); + status1_clr.set_spi_err(true); + } + + if status0.phyint() { + let crsm_irq_st = self + .mac + .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::CRSM_IRQ_STATUS.into()) + .await + .unwrap(); + + let phy_irq_st = self + .mac + .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1F::PHY_SYBSYS_IRQ_STATUS.into()) + .await + .unwrap(); + + warn!( + "SPE CHIP PHY CRSM_IRQ_STATUS {:04x} PHY_SUBSYS_IRQ_STATUS {:04x}", + crsm_irq_st, phy_irq_st + ); + } + + if status0.txfcse() { + error!("Ethernet Frame FCS and calc FCS don't match!"); + } + + // Clear status0 + self.mac.write_reg(sr::STATUS0, 0xFFF).await.unwrap(); + self.mac.write_reg(sr::STATUS1, status1_clr.0).await.unwrap(); + } + Either::Second(packet) => { + // Handle frames that needs to transmit to the wire. + self.mac.write_fifo(packet).await.unwrap(); + tx_chan.tx_done(); + } + } + } + } + } +} + +/// Obtain a driver for using the ADIN1110 with [`embassy-net`](crates.io/crates/embassy-net). +pub async fn new( + mac_addr: [u8; 6], + state: &'_ mut State, + spi_dev: SPI, + int: INT, + mut reset: RST, + spi_crc: bool, + append_fcs_on_tx: bool, +) -> (Device<'_>, Runner<'_, SPI, INT, RST>) { + use crate::regs::{IMask0, IMask1}; + + info!("INIT ADIN1110"); + + // Reset sequence + reset.set_low().unwrap(); + + // Wait t1: 20-43mS + Timer::after(Duration::from_millis(30)).await; + + reset.set_high().unwrap(); + + // Wait t3: 50mS + Timer::after(Duration::from_millis(50)).await; + + // Create device + let mut mac = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx); + + // Check PHYID + let id = mac.read_reg(sr::PHYID).await.unwrap(); + assert_eq!(id, PHYID); + + debug!("SPE: CHIP MAC/ID: {:08x}", id); + + #[cfg(any(feature = "defmt", feature = "log"))] + { + let adin_phy = Phy10BaseT1x::default(); + let phy_id = adin_phy.get_id(&mut mac).await.unwrap(); + debug!("SPE: CHIP: PHY ID: {:08x}", phy_id); + } + + let mi_control = mac.read_cl22(MDIO_PHY_ADDR, RegsC22::CONTROL as u8).await.unwrap(); + debug!("SPE CHIP PHY MI_CONTROL {:04x}", mi_control); + if mi_control & 0x0800 != 0 { + let val = mi_control & !0x0800; + debug!("SPE CHIP PHY MI_CONTROL Disable PowerDown"); + mac.write_cl22(MDIO_PHY_ADDR, RegsC22::CONTROL as u8, val) + .await + .unwrap(); + } + + // Config0 + let mut config0 = Config0(0x0000_0006); + config0.set_txfcsve(mac.append_fcs_on_tx); + mac.write_reg(sr::CONFIG0, config0.0).await.unwrap(); + + // Config2 + let mut config2 = Config2(0x0000_0800); + // crc_append must be disable if tx_fcs_validation_enable is true! + config2.set_crc_append(!mac.append_fcs_on_tx); + mac.write_reg(sr::CONFIG2, config2.0).await.unwrap(); + + // Pin Mux Config 1 + let led_val = (0b11 << 6) | (0b11 << 4); // | (0b00 << 1); + mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::DIGIO_PINMUX.into(), led_val) + .await + .unwrap(); + + let mut led_pol = LedPolarity(0); + led_pol.set_led1_polarity(LedPol::ActiveLow); + led_pol.set_led0_polarity(LedPol::ActiveLow); + + // Led Polarity Regisgere Active Low + mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::LED_POLARITY.into(), led_pol.0) + .await + .unwrap(); + + // Led Both On + let mut led_cntr = LedCntrl(0x0); + + // LED1: Yellow + led_cntr.set_led1_en(true); + led_cntr.set_led1_function(LedFunc::TxLevel2P4); + // LED0: Green + led_cntr.set_led0_en(true); + led_cntr.set_led0_function(LedFunc::LinkupTxRxActicity); + + mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::LED_CNTRL.into(), led_cntr.0) + .await + .unwrap(); + + // Set ADIN1110 Interrupts, RX_READY and LINK_CHANGE + // Enable interrupts LINK_CHANGE, TX_RDY, RX_RDY(P1), SPI_ERR + // Have to clear the mask the enable it. + let mut imask0_val = IMask0(0x0000_1FBF); + imask0_val.set_txfcsem(false); + imask0_val.set_phyintm(false); + imask0_val.set_txboem(false); + imask0_val.set_rxboem(false); + imask0_val.set_txpem(false); + + mac.write_reg(sr::IMASK0, imask0_val.0).await.unwrap(); + + // Set ADIN1110 Interrupts, RX_READY and LINK_CHANGE + // Enable interrupts LINK_CHANGE, TX_RDY, RX_RDY(P1), SPI_ERR + // Have to clear the mask the enable it. + let mut imask1_val = IMask1(0x43FA_1F1A); + imask1_val.set_link_change_mask(false); + imask1_val.set_p1_rx_rdy_mask(false); + imask1_val.set_spi_err_mask(false); + imask1_val.set_tx_ecc_err_mask(false); + imask1_val.set_rx_ecc_err_mask(false); + + mac.write_reg(sr::IMASK1, imask1_val.0).await.unwrap(); + + // Program mac address but also sets mac filters. + mac.set_mac_addr(&mac_addr).await.unwrap(); + + let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); + ( + device, + Runner { + ch: runner, + mac, + int, + is_link_up: false, + _reset: reset, + }, + ) +} + +#[allow(clippy::similar_names)] +#[cfg(test)] +mod tests { + use core::convert::Infallible; + + use embedded_hal_1::digital::{ErrorType, OutputPin}; + use embedded_hal_async::delay::DelayUs; + use embedded_hal_bus::spi::ExclusiveDevice; + use embedded_hal_mock::common::Generic; + use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction}; + + #[derive(Debug, Default)] + struct CsPinMock { + pub high: u32, + pub low: u32, + } + impl OutputPin for CsPinMock { + fn set_low(&mut self) -> Result<(), Self::Error> { + self.low += 1; + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.high += 1; + Ok(()) + } + } + impl ErrorType for CsPinMock { + type Error = Infallible; + } + + use super::*; + + // TODO: This is currently a workaround unit `ExclusiveDevice` is moved to `embedded-hal-bus` + // see https://github.com/rust-embedded/embedded-hal/pull/462#issuecomment-1560014426 + struct MockDelay {} + + impl DelayUs for MockDelay { + async fn delay_us(&mut self, _us: u32) { + todo!() + } + + async fn delay_ms(&mut self, _ms: u32) { + todo!() + } + } + + struct TestHarnass { + spe: ADIN1110, CsPinMock, MockDelay>>, + spi: Generic, + } + + impl TestHarnass { + pub fn new(expectations: &[SpiTransaction], spi_crc: bool, append_fcs_on_tx: bool) -> Self { + let cs = CsPinMock::default(); + let delay = MockDelay {}; + let spi = SpiMock::new(expectations); + let spi_dev: ExclusiveDevice, CsPinMock, MockDelay> = + ExclusiveDevice::new(spi.clone(), cs, delay); + let spe: ADIN1110< + ExclusiveDevice, CsPinMock, MockDelay>, + > = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx); + + Self { spe, spi } + } + + pub fn done(&mut self) { + self.spi.done(); + } + } + + #[futures_test::test] + async fn mac_read_registers_without_crc() { + // Configure expectations + let expectations = [ + // 1st + SpiTransaction::write_vec(vec![0x80, 0x01, TURN_AROUND_BYTE]), + SpiTransaction::read_vec(vec![0x02, 0x83, 0xBC, 0x91]), + SpiTransaction::flush(), + // 2nd + SpiTransaction::write_vec(vec![0x80, 0x02, TURN_AROUND_BYTE]), + SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3]), + SpiTransaction::flush(), + ]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, false, true); + + // Read PHIID + let val = th.spe.read_reg(sr::PHYID).await.expect("Error"); + assert_eq!(val, 0x0283_BC91); + + // Read CAPAVILITY + let val = th.spe.read_reg(sr::CAPABILITY).await.expect("Error"); + assert_eq!(val, 0x0000_06C3); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn mac_read_registers_with_crc() { + // Configure expectations + let expectations = [ + // 1st + SpiTransaction::write_vec(vec![0x80, 0x01, 177, TURN_AROUND_BYTE]), + SpiTransaction::read_vec(vec![0x02, 0x83, 0xBC, 0x91, 215]), + SpiTransaction::flush(), + // 2nd + SpiTransaction::write_vec(vec![0x80, 0x02, 184, TURN_AROUND_BYTE]), + SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3, 57]), + SpiTransaction::flush(), + ]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + assert_eq!(crc8(0x0283_BC91_u32.to_be_bytes().as_slice()), 215); + assert_eq!(crc8(0x0000_06C3_u32.to_be_bytes().as_slice()), 57); + + // Read PHIID + let val = th.spe.read_reg(sr::PHYID).await.expect("Error"); + assert_eq!(val, 0x0283_BC91); + + // Read CAPAVILITY + let val = th.spe.read_reg(sr::CAPABILITY).await.expect("Error"); + assert_eq!(val, 0x0000_06C3); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn mac_write_registers_without_crc() { + // Configure expectations + let expectations = [ + SpiTransaction::write_vec(vec![0xA0, 0x09, 0x12, 0x34, 0x56, 0x78]), + SpiTransaction::flush(), + ]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, false, true); + + // Write reg: 0x1FFF + assert!(th.spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok()); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn mac_write_registers_with_crc() { + // Configure expectations + let expectations = [ + SpiTransaction::write_vec(vec![0xA0, 0x09, 39, 0x12, 0x34, 0x56, 0x78, 28]), + SpiTransaction::flush(), + ]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + // Write reg: 0x1FFF + assert!(th.spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok()); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn write_packet_to_fifo_minimal_with_crc() { + // Configure expectations + let mut expectations = vec![]; + + // Write TX_SIZE reg + expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 66, 201])); + expectations.push(SpiTransaction::flush()); + + // Write TX reg. + // SPI Header + optional CRC + Frame Header + expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); + // Packet data + let packet = [0xFF_u8; 60]; + expectations.push(SpiTransaction::write_vec(packet.to_vec())); + + let mut tail = std::vec::Vec::::with_capacity(100); + // Padding + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { + tail.resize(padding_len, 0x00); + } + // Packet FCS + optinal padding + tail.extend_from_slice(&[77, 241, 140, 244, DONT_CARE_BYTE, DONT_CARE_BYTE]); + + expectations.push(SpiTransaction::write_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + assert!(th.spe.write_fifo(&packet).await.is_ok()); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn write_packet_to_fifo_minimal_with_crc_without_fcs() { + // Configure expectations + let mut expectations = vec![]; + + // Write TX_SIZE reg + expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 62, 186])); + expectations.push(SpiTransaction::flush()); + + // Write TX reg. + // SPI Header + optional CRC + Frame Header + expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); + // Packet data + let packet = [0xFF_u8; 60]; + expectations.push(SpiTransaction::write_vec(packet.to_vec())); + + let mut tail = std::vec::Vec::::with_capacity(100); + // Padding + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { + tail.resize(padding_len, 0x00); + } + // Packet FCS + optinal padding + tail.extend_from_slice(&[DONT_CARE_BYTE, DONT_CARE_BYTE]); + + expectations.push(SpiTransaction::write_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, false); + + assert!(th.spe.write_fifo(&packet).await.is_ok()); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn write_packet_to_fifo_max_mtu_with_crc() { + assert_eq!(MTU, 1514); + // Configure expectations + let mut expectations = vec![]; + + // Write TX_SIZE reg + expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 5, 240, 159])); + expectations.push(SpiTransaction::flush()); + + // Write TX reg. + // SPI Header + optional CRC + Frame Header + expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); + // Packet data + let packet = [0xAA_u8; MTU]; + expectations.push(SpiTransaction::write_vec(packet.to_vec())); + + let mut tail = std::vec::Vec::::with_capacity(100); + // Padding + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { + tail.resize(padding_len, 0x00); + } + // Packet FCS + optinal padding + tail.extend_from_slice(&[49, 196, 205, 160]); + + expectations.push(SpiTransaction::write_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + assert!(th.spe.write_fifo(&packet).await.is_ok()); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn write_packet_to_fifo_invalid_lengths() { + assert_eq!(MTU, 1514); + + // Configure expectations + let expectations = vec![]; + + // Max packet size = MAX_BUFF - FRAME_HEADER_LEN + let packet = [0xAA_u8; MAX_BUFF - FRAME_HEADER_LEN + 1]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + // minimal + assert!(matches!( + th.spe.write_fifo(&packet[0..(6 + 6 + 2 - 1)]).await, + Err(AdinError::PACKET_TOO_SMALL) + )); + + // max + 1 + assert!(matches!( + th.spe.write_fifo(&packet).await, + Err(AdinError::PACKET_TOO_BIG) + )); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn write_packet_to_fifo_arp_46bytes_with_crc() { + // Configure expectations + let mut expectations = vec![]; + + // Write TX_SIZE reg + expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 66, 201])); + expectations.push(SpiTransaction::flush()); + + // Write TX reg. + // Header + expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); + // Packet data + let packet = [ + 34, 51, 68, 85, 102, 119, 18, 52, 86, 120, 154, 188, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 18, 52, 86, 120, 154, + 188, 192, 168, 16, 4, 34, 51, 68, 85, 102, 119, 192, 168, 16, 1, + ]; + expectations.push(SpiTransaction::write_vec(packet.to_vec())); + + let mut tail = std::vec::Vec::::with_capacity(100); + // Padding + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { + tail.resize(padding_len, 0x00); + } + // Packet FCS + optinal padding + tail.extend_from_slice(&[147, 149, 213, 68, DONT_CARE_BYTE, DONT_CARE_BYTE]); + + expectations.push(SpiTransaction::write_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + assert!(th.spe.write_fifo(&packet).await.is_ok()); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn write_packet_to_fifo_arp_46bytes_without_crc() { + // Configure expectations + let mut expectations = vec![]; + + // Write TX_SIZE reg + expectations.push(SpiTransaction::write_vec(vec![160, 48, 0, 0, 0, 66])); + expectations.push(SpiTransaction::flush()); + + // Write TX reg. + // SPI Header + Frame Header + expectations.push(SpiTransaction::write_vec(vec![160, 49, 0, 0])); + // Packet data + let packet = [ + 34, 51, 68, 85, 102, 119, 18, 52, 86, 120, 154, 188, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 18, 52, 86, 120, 154, + 188, 192, 168, 16, 4, 34, 51, 68, 85, 102, 119, 192, 168, 16, 1, + ]; + expectations.push(SpiTransaction::write_vec(packet.to_vec())); + + let mut tail = std::vec::Vec::::with_capacity(100); + // Padding + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { + tail.resize(padding_len, 0x00); + } + // Packet FCS + optinal padding + tail.extend_from_slice(&[147, 149, 213, 68, DONT_CARE_BYTE, DONT_CARE_BYTE]); + + expectations.push(SpiTransaction::write_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, false, true); + + assert!(th.spe.write_fifo(&packet).await.is_ok()); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn read_packet_from_fifo_packet_too_big_for_frame_buffer() { + // Configure expectations + let mut expectations = vec![]; + + // Read RX_SIZE reg + let rx_size: u32 = u32::try_from(ETH_MIN_LEN + FRAME_HEADER_LEN + FCS_LEN).unwrap(); + let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); + rx_size_vec.push(crc8(&rx_size_vec)); + + expectations.push(SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE])); + expectations.push(SpiTransaction::read_vec(rx_size_vec)); + expectations.push(SpiTransaction::flush()); + + let mut frame = [0; MTU]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + let ret = th.spe.read_fifo(&mut frame[0..ETH_MIN_LEN - 1]).await; + assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_BIG))); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn read_packet_from_fifo_packet_too_small() { + // Configure expectations + let mut expectations = vec![]; + + // This value is importen for this test! + assert_eq!(ETH_MIN_LEN, 64); + + // Packet data, size = `ETH_MIN_LEN` - `FCS_LEN` - 1 + let packet = [0; 64 - FCS_LEN - 1]; + + // Read RX_SIZE reg + let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); + let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); + rx_size_vec.push(crc8(&rx_size_vec)); + + expectations.push(SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE])); + expectations.push(SpiTransaction::read_vec(rx_size_vec)); + expectations.push(SpiTransaction::flush()); + + let mut frame = [0; MTU]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + let ret = th.spe.read_fifo(&mut frame).await; + assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_SMALL))); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn read_packet_from_fifo_packet_corrupted_fcs() { + let mut frame = [0; MTU]; + // Configure expectations + let mut expectations = vec![]; + + let packet = [0xDE; 60]; + let crc_en = true; + + // Read RX_SIZE reg + let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); + let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); + if crc_en { + rx_size_vec.push(crc8(&rx_size_vec)); + } + + // SPI Header with CRC + let mut rx_fsize = vec![128, 144, 79, TURN_AROUND_BYTE]; + if !crc_en { + // remove the CRC on idx 2 + rx_fsize.swap_remove(2); + } + expectations.push(SpiTransaction::write_vec(rx_fsize)); + expectations.push(SpiTransaction::read_vec(rx_size_vec)); + expectations.push(SpiTransaction::flush()); + + // Read RX reg, SPI Header with CRC + let mut rx_reg = vec![128, 145, 72, TURN_AROUND_BYTE]; + if !crc_en { + // remove the CRC on idx 2 + rx_reg.swap_remove(2); + } + expectations.push(SpiTransaction::write_vec(rx_reg)); + // Frame Header + expectations.push(SpiTransaction::read_vec(vec![0, 0])); + // Packet data + expectations.push(SpiTransaction::read_vec(packet.to_vec())); + + let packet_crc = ETH_FCS::new(&packet); + + let mut tail = std::vec::Vec::::with_capacity(100); + + tail.extend_from_slice(&packet_crc.hton_bytes()); + // increase last byte with 1. + if let Some(crc) = tail.last_mut() { + *crc = crc.wrapping_add(1); + } + + // Need extra bytes? + let pad = (packet.len() + FCS_LEN + FRAME_HEADER_LEN) & 0x03; + if pad != 0 { + // Packet FCS + optinal padding + tail.resize(tail.len() + pad, DONT_CARE_BYTE); + } + + expectations.push(SpiTransaction::read_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, crc_en, false); + + let ret = th.spe.read_fifo(&mut frame).await.expect_err("Error!"); + assert!(matches!(ret, AdinError::FCS)); + + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn read_packet_to_fifo_check_spi_read_multipule_of_u32_valid_lengths() { + let packet_buffer = [0; MTU]; + let mut frame = [0; MTU]; + let mut expectations = std::vec::Vec::with_capacity(16); + + // Packet data, size = `ETH_MIN_LEN` - `FCS_LEN` + for packet_size in [60, 61, 62, 63, 64, MTU - 4, MTU - 3, MTU - 2, MTU - 1, MTU] { + for crc_en in [false, true] { + expectations.clear(); + + let packet = &packet_buffer[0..packet_size]; + + // Read RX_SIZE reg + let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); + let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); + if crc_en { + rx_size_vec.push(crc8(&rx_size_vec)); + } + + // SPI Header with CRC + let mut rx_fsize = vec![128, 144, 79, TURN_AROUND_BYTE]; + if !crc_en { + // remove the CRC on idx 2 + rx_fsize.swap_remove(2); + } + expectations.push(SpiTransaction::write_vec(rx_fsize)); + expectations.push(SpiTransaction::read_vec(rx_size_vec)); + expectations.push(SpiTransaction::flush()); + + // Read RX reg, SPI Header with CRC + let mut rx_reg = vec![128, 145, 72, TURN_AROUND_BYTE]; + if !crc_en { + // remove the CRC on idx 2 + rx_reg.swap_remove(2); + } + expectations.push(SpiTransaction::write_vec(rx_reg)); + // Frame Header + expectations.push(SpiTransaction::read_vec(vec![0, 0])); + // Packet data + expectations.push(SpiTransaction::read_vec(packet.to_vec())); + + let packet_crc = ETH_FCS::new(packet); + + let mut tail = std::vec::Vec::::with_capacity(100); + + tail.extend_from_slice(&packet_crc.hton_bytes()); + + // Need extra bytes? + let pad = (packet_size + FCS_LEN + FRAME_HEADER_LEN) & 0x03; + if pad != 0 { + // Packet FCS + optinal padding + tail.resize(tail.len() + pad, DONT_CARE_BYTE); + } + + expectations.push(SpiTransaction::read_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, crc_en, false); + + let ret = th.spe.read_fifo(&mut frame).await.expect("Error!"); + assert_eq!(ret, packet_size); + + // Mark end of the SPI test. + th.done(); + } + } + } + + #[futures_test::test] + async fn spi_crc_error() { + // Configure expectations + let expectations = vec![ + SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE]), + SpiTransaction::read_vec(vec![0x00, 0x00, 0x00, 0x00, 0xDD]), + SpiTransaction::flush(), + ]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, false); + + let ret = th.spe.read_reg(sr::RX_FSIZE).await; + assert!(matches!(dbg!(ret), Err(AdinError::SPI_CRC))); + + // Mark end of the SPI test. + th.done(); + } +} diff --git a/embassy-net-adin1110/src/mdio.rs b/embassy-net-adin1110/src/mdio.rs new file mode 100644 index 00000000..1ae5f004 --- /dev/null +++ b/embassy-net-adin1110/src/mdio.rs @@ -0,0 +1,176 @@ +/// PHY Address: (0..=0x1F), 5-bits long. +#[allow(dead_code)] +type PhyAddr = u8; + +/// PHY Register: (0..=0x1F), 5-bits long. +#[allow(dead_code)] +type RegC22 = u8; + +/// PHY Register Clause 45. +#[allow(dead_code)] +type RegC45 = u16; + +/// PHY Register Value +#[allow(dead_code)] +type RegVal = u16; + +#[allow(dead_code)] +const REG13: RegC22 = 13; +#[allow(dead_code)] +const REG14: RegC22 = 14; + +#[allow(dead_code)] +const PHYADDR_MASK: u8 = 0x1f; +#[allow(dead_code)] +const DEV_MASK: u8 = 0x1f; + +#[allow(dead_code)] +#[repr(u16)] +enum Reg13Op { + Addr = 0b00 << 14, + Write = 0b01 << 14, + PostReadIncAddr = 0b10 << 14, + Read = 0b11 << 14, +} + +/// `MdioBus` trait +/// Driver needs to implement the Clause 22 +/// Optional Clause 45 is the device supports this. +/// +/// Clause 45 methodes are bases on +pub trait MdioBus { + type Error; + + /// Read, Clause 22 + async fn read_cl22(&mut self, phy_id: PhyAddr, reg: RegC22) -> Result; + + /// Write, Clause 22 + async fn write_cl22(&mut self, phy_id: PhyAddr, reg: RegC22, reg_val: RegVal) -> Result<(), Self::Error>; + + /// Read, Clause 45 + /// This is the default implementation. + /// Many hardware these days support direct Clause 45 operations. + /// Implement this function when your hardware supports it. + async fn read_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45)) -> Result { + // Write FN + let val = (Reg13Op::Addr as RegVal) | RegVal::from(regc45.0 & DEV_MASK); + + self.write_cl22(phy_id, REG13, val).await?; + // Write Addr + self.write_cl22(phy_id, REG14, regc45.1).await?; + + // Write FN + let val = (Reg13Op::Read as RegVal) | RegVal::from(regc45.0 & DEV_MASK); + self.write_cl22(phy_id, REG13, val).await?; + // Write Addr + self.read_cl22(phy_id, REG14).await + } + + /// Write, Clause 45 + /// This is the default implementation. + /// Many hardware these days support direct Clause 45 operations. + /// Implement this function when your hardware supports it. + async fn write_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45), reg_val: RegVal) -> Result<(), Self::Error> { + let dev_addr = RegVal::from(regc45.0 & DEV_MASK); + let reg = regc45.1; + + // Write FN + let val = (Reg13Op::Addr as RegVal) | dev_addr; + self.write_cl22(phy_id, REG13, val).await?; + // Write Addr + self.write_cl22(phy_id, REG14, reg).await?; + + // Write FN + let val = (Reg13Op::Write as RegVal) | dev_addr; + self.write_cl22(phy_id, REG13, val).await?; + // Write Addr + self.write_cl22(phy_id, REG14, reg_val).await + } +} + +#[cfg(test)] +mod tests { + use core::convert::Infallible; + + use super::{MdioBus, PhyAddr, RegC22, RegVal}; + + #[derive(Debug, PartialEq, Eq)] + enum A { + Read(PhyAddr, RegC22), + Write(PhyAddr, RegC22, RegVal), + } + + struct MockMdioBus(Vec); + + impl MockMdioBus { + pub fn clear(&mut self) { + self.0.clear(); + } + } + + impl MdioBus for MockMdioBus { + type Error = Infallible; + + async fn write_cl22( + &mut self, + phy_id: super::PhyAddr, + reg: super::RegC22, + reg_val: super::RegVal, + ) -> Result<(), Self::Error> { + self.0.push(A::Write(phy_id, reg, reg_val)); + Ok(()) + } + + async fn read_cl22( + &mut self, + phy_id: super::PhyAddr, + reg: super::RegC22, + ) -> Result { + self.0.push(A::Read(phy_id, reg)); + Ok(0) + } + } + + #[futures_test::test] + async fn read_test() { + let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); + + mdiobus.clear(); + mdiobus.read_cl22(0x01, 0x00).await.unwrap(); + assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]); + + mdiobus.clear(); + mdiobus.read_cl45(0x01, (0xBB, 0x1234)).await.unwrap(); + assert_eq!( + mdiobus.0, + vec![ + #[allow(clippy::identity_op)] + A::Write(0x01, 13, (0b00 << 14) | 27), + A::Write(0x01, 14, 0x1234), + A::Write(0x01, 13, (0b11 << 14) | 27), + A::Read(0x01, 14) + ] + ); + } + + #[futures_test::test] + async fn write_test() { + let mut mdiobus = MockMdioBus(Vec::with_capacity(20)); + + mdiobus.clear(); + mdiobus.write_cl22(0x01, 0x00, 0xABCD).await.unwrap(); + assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]); + + mdiobus.clear(); + mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).await.unwrap(); + assert_eq!( + mdiobus.0, + vec![ + A::Write(0x01, 13, 27), + A::Write(0x01, 14, 0x1234), + A::Write(0x01, 13, (0b01 << 14) | 27), + A::Write(0x01, 14, 0xABCD) + ] + ); + } +} diff --git a/embassy-net-adin1110/src/phy.rs b/embassy-net-adin1110/src/phy.rs new file mode 100644 index 00000000..d54d843d --- /dev/null +++ b/embassy-net-adin1110/src/phy.rs @@ -0,0 +1,143 @@ +use crate::mdio::MdioBus; + +#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)] +#[repr(u8)] +/// Clause 22 Registers +pub enum RegsC22 { + /// MII Control Register + CONTROL = 0x00, + /// MII Status Register + STATUS = 0x01, + /// PHY Identifier 1 Register + PHY_ID1 = 0x02, + /// PHY Identifier 2 Register. + PHY_ID2 = 0x03, +} + +/// Clause 45 Registers +#[allow(non_snake_case, dead_code)] +pub mod RegsC45 { + /// Device Address: 0x01 + #[allow(non_camel_case_types, clippy::upper_case_acronyms)] + #[repr(u16)] + pub enum DA1 { + /// PMA/PMD Control 1 Register + PMA_PMD_CNTRL1 = 0x0000, + /// PMA/PMD Status 1 Register + PMA_PMD_STAT1 = 0x0001, + /// MSE Value Register + MSE_VAL = 0x830B, + } + + impl DA1 { + #[must_use] + pub fn into(self) -> (u8, u16) { + (0x01, self as u16) + } + } + + /// Device Address: 0x03 + #[allow(non_camel_case_types, clippy::upper_case_acronyms)] + #[repr(u16)] + pub enum DA3 { + /// PCS Control 1 Register + PCS_CNTRL1 = 0x0000, + /// PCS Status 1 Register + PCS_STAT1 = 0x0001, + /// PCS Status 2 Register + PCS_STAT2 = 0x0008, + } + + impl DA3 { + #[must_use] + pub fn into(self) -> (u8, u16) { + (0x03, self as u16) + } + } + + /// Device Address: 0x07 + #[allow(non_camel_case_types, clippy::upper_case_acronyms)] + #[repr(u16)] + pub enum DA7 { + /// Extra Autonegotiation Status Register + AN_STATUS_EXTRA = 0x8001, + } + + impl DA7 { + #[must_use] + pub fn into(self) -> (u8, u16) { + (0x07, self as u16) + } + } + + /// Device Address: 0x1E + #[allow(non_camel_case_types, clippy::upper_case_acronyms)] + #[repr(u16)] + pub enum DA1E { + /// System Interrupt Status Register + CRSM_IRQ_STATUS = 0x0010, + /// System Interrupt Mask Register + CRSM_IRQ_MASK = 0x0020, + /// Pin Mux Configuration 1 Register + DIGIO_PINMUX = 0x8c56, + /// LED Control Register. + LED_CNTRL = 0x8C82, + /// LED Polarity Register + LED_POLARITY = 0x8C83, + } + + impl DA1E { + #[must_use] + pub fn into(self) -> (u8, u16) { + (0x1e, self as u16) + } + } + + /// Device Address: 0x1F + #[allow(non_camel_case_types, clippy::upper_case_acronyms)] + #[repr(u16)] + pub enum DA1F { + /// PHY Subsystem Interrupt Status Register + PHY_SYBSYS_IRQ_STATUS = 0x0011, + /// PHY Subsystem Interrupt Mask Register + PHY_SYBSYS_IRQ_MASK = 0x0021, + } + + impl DA1F { + #[must_use] + pub fn into(self) -> (u8, u16) { + (0x1f, self as u16) + } + } +} + +/// 10-BASE-T1x PHY functions. +pub struct Phy10BaseT1x(u8); + +impl Default for Phy10BaseT1x { + fn default() -> Self { + Self(0x01) + } +} + +impl Phy10BaseT1x { + /// Get the both parts of the PHYID. + pub async fn get_id(&self, mdiobus: &mut MDIOBUS) -> Result + where + MDIOBUS: MdioBus, + MDE: core::fmt::Debug, + { + let mut phyid = u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID1 as u8).await?) << 16; + phyid |= u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID2 as u8).await?); + Ok(phyid) + } + + /// Get the Mean Squared Error Value. + pub async fn get_sqi(&self, mdiobus: &mut MDIOBUS) -> Result + where + MDIOBUS: MdioBus, + MDE: core::fmt::Debug, + { + mdiobus.read_cl45(self.0, RegsC45::DA1::MSE_VAL.into()).await + } +} diff --git a/embassy-net-adin1110/src/regs.rs b/embassy-net-adin1110/src/regs.rs new file mode 100644 index 00000000..beaf9466 --- /dev/null +++ b/embassy-net-adin1110/src/regs.rs @@ -0,0 +1,416 @@ +use core::fmt::{Debug, Display}; + +use bitfield::{bitfield, bitfield_bitrange, bitfield_fields}; + +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u16)] +/// SPI REGISTER DETAILS +/// Table 38. +pub enum SpiRegisters { + IDVER = 0x00, + PHYID = 0x01, + CAPABILITY = 0x02, + RESET = 0x03, + CONFIG0 = 0x04, + CONFIG2 = 0x06, + STATUS0 = 0x08, + STATUS1 = 0x09, + IMASK0 = 0x0C, + IMASK1 = 0x0D, + MDIO_ACC = 0x20, + TX_FSIZE = 0x30, + TX = 0x31, + TX_SPACE = 0x32, + FIFO_CLR = 0x36, + ADDR_FILT_UPR0 = 0x50, + ADDR_FILT_LWR0 = 0x51, + ADDR_FILT_UPR1 = 0x52, + ADDR_FILT_LWR1 = 0x53, + ADDR_MSK_LWR0 = 0x70, + ADDR_MSK_UPR0 = 0x71, + ADDR_MSK_LWR1 = 0x72, + ADDR_MSK_UPR1 = 0x73, + RX_FSIZE = 0x90, + RX = 0x91, +} + +impl Display for SpiRegisters { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{self:?}") + } +} + +impl From for u16 { + fn from(val: SpiRegisters) -> Self { + val as u16 + } +} + +impl From for SpiRegisters { + fn from(value: u16) -> Self { + match value { + 0x00 => Self::IDVER, + 0x01 => Self::PHYID, + 0x02 => Self::CAPABILITY, + 0x03 => Self::RESET, + 0x04 => Self::CONFIG0, + 0x06 => Self::CONFIG2, + 0x08 => Self::STATUS0, + 0x09 => Self::STATUS1, + 0x0C => Self::IMASK0, + 0x0D => Self::IMASK1, + 0x20 => Self::MDIO_ACC, + 0x30 => Self::TX_FSIZE, + 0x31 => Self::TX, + 0x32 => Self::TX_SPACE, + 0x36 => Self::FIFO_CLR, + 0x50 => Self::ADDR_FILT_UPR0, + 0x51 => Self::ADDR_FILT_LWR0, + 0x52 => Self::ADDR_FILT_UPR1, + 0x53 => Self::ADDR_FILT_LWR1, + 0x70 => Self::ADDR_MSK_LWR0, + 0x71 => Self::ADDR_MSK_UPR0, + 0x72 => Self::ADDR_MSK_LWR1, + 0x73 => Self::ADDR_MSK_UPR1, + 0x90 => Self::RX_FSIZE, + 0x91 => Self::RX, + e => panic!("Unknown value {}", e), + } + } +} + +// Register definitions +bitfield! { + /// Status0 Register bits + pub struct Status0(u32); + impl Debug; + u32; + /// Control Data Protection Error + pub cdpe, _ : 12; + /// Transmit Frame Check Squence Error + pub txfcse, _: 11; + /// Transmit Time Stamp Capture Available C + pub ttscac, _ : 10; + /// Transmit Time Stamp Capture Available B + pub ttscab, _ : 9; + /// Transmit Time Stamp Capture Available A + pub ttscaa, _ : 8; + /// PHY Interrupt for Port 1 + pub phyint, _ : 7; + /// Reset Complete + pub resetc, _ : 6; + /// Header error + pub hdre, _ : 5; + /// Loss of Frame Error + pub lofe, _ : 4; + /// Receiver Buffer Overflow Error + pub rxboe, _ : 3; + /// Host Tx FIFO Under Run Error + pub txbue, _ : 2; + /// Host Tx FIFO Overflow + pub txboe, _ : 1; + /// Transmit Protocol Error + pub txpe, _ : 0; +} + +bitfield! { + /// Status1 Register bits + pub struct Status1(u32); + impl Debug; + u32; + /// ECC Error on Reading the Frame Size from a Tx FIFO + pub tx_ecc_err, set_tx_ecc_err: 12; + /// ECC Error on Reading the Frame Size from an Rx FIFO + pub rx_ecc_err, set_rx_ecc_err : 11; + /// Detected an Error on an SPI Transaction + pub spi_err, set_spi_err: 10; + /// Rx MAC Interframe Gap Error + pub p1_rx_ifg_err, set_p1_rx_ifg_err : 8; + /// Port1 Rx Ready High Priority + pub p1_rx_rdy_hi, set_p1_rx_rdy_hi : 5; + /// Port 1 Rx FIFO Contains Data + pub p1_rx_rdy, set_p1_rx_rdy : 4; + /// Tx Ready + pub tx_rdy, set_tx_rdy : 3; + /// Link Status Changed + pub link_change, set_link_change : 1; + /// Port 1 Link Status + pub p1_link_status, _ : 0; +} + +bitfield! { + /// Config0 Register bits + pub struct Config0(u32); + impl Debug; + u32; + /// Configuration Synchronization + pub sync, set_sync : 15; + /// Transmit Frame Check Sequence Validation Enable + pub txfcsve, set_txfcsve : 14; + /// !CS Align Receive Frame Enable + pub csarfe, set_csarfe : 13; + /// Zero Align Receive Frame Enable + pub zarfe, set_zarfe : 12; + /// Transmit Credit Threshold + pub tcxthresh, set_tcxthresh : 11, 10; + /// Transmit Cut Through Enable + pub txcte, set_txcte : 9; + /// Receive Cut Through Enable + pub rxcte, set_rxcte : 8; + /// Frame Time Stamp Enable + pub ftse, set_ftse : 7; + /// Receive Frame Time Stamp Select + pub ftss, set_ftss : 6; + /// Enable Control Data Read Write Protection + pub prote, set_prote : 5; + /// Enable TX Data Chunk Sequence and Retry + pub seqe, set_seqe : 4; + /// Chunk Payload Selector (N). + pub cps, set_cps : 2, 0; +} + +bitfield! { + /// Config2 Register bits + pub struct Config2(u32); + impl Debug; + u32; + /// Assert TX_RDY When the Tx FIFO is Empty + pub tx_rdy_on_empty, set_tx_rdy_on_empty : 8; + /// Determines If the SFD is Detected in the PHY or MAC + pub sdf_detect_src, set_sdf_detect_src : 7; + /// Statistics Clear on Reading + pub stats_clr_on_rd, set_stats_clr_on_rd : 6; + /// Enable SPI CRC + pub crc_append, set_crc_append : 5; + /// Admit Frames with IFG Errors on Port 1 (P1) + pub p1_rcv_ifg_err_frm, set_p1_rcv_ifg_err_frm : 4; + /// Forward Frames Not Matching Any MAC Address to the Host + pub p1_fwd_unk2host, set_p1_fwd_unk2host : 2; + /// SPI to MDIO Bridge MDC Clock Speed + pub mspeed, set_mspeed : 0; +} + +bitfield! { + /// IMASK0 Register bits + pub struct IMask0(u32); + impl Debug; + u32; + /// Control Data Protection Error Mask + pub cppem, set_cppem : 12; + /// Transmit Frame Check Sequence Error Mask + pub txfcsem, set_txfcsem : 11; + /// Transmit Time Stamp Capture Available C Mask + pub ttscacm, set_ttscacm : 10; + /// Transmit Time Stamp Capture Available B Mask + pub ttscabm, set_ttscabm : 9; + /// Transmit Time Stamp Capture Available A Mask + pub ttscaam, set_ttscaam : 8; + /// Physical Layer Interrupt Mask + pub phyintm, set_phyintm : 7; + /// RESET Complete Mask + pub resetcm, set_resetcm : 6; + /// Header Error Mask + pub hdrem, set_hdrem : 5; + /// Loss of Frame Error Mask + pub lofem, set_lofem : 4; + /// Receive Buffer Overflow Error Mask + pub rxboem, set_rxboem : 3; + /// Transmit Buffer Underflow Error Mask + pub txbuem, set_txbuem : 2; + /// Transmit Buffer Overflow Error Mask + pub txboem, set_txboem : 1; + /// Transmit Protocol Error Mask + pub txpem, set_txpem : 0; +} + +bitfield! { + /// IMASK1 Register bits + pub struct IMask1(u32); + impl Debug; + u32; + /// Mask Bit for TXF_ECC_ERR + pub tx_ecc_err_mask, set_tx_ecc_err_mask : 12; + /// Mask Bit for RXF_ECC_ERR + pub rx_ecc_err_mask, set_rx_ecc_err_mask : 11; + /// Mask Bit for SPI_ERR + /// This field is only used with the generic SPI protocol + pub spi_err_mask, set_spi_err_mask : 10; + /// Mask Bit for RX_IFG_ERR + pub p1_rx_ifg_err_mask, set_p1_rx_ifg_err_mask : 8; + /// Mask Bit for P1_RX_RDY + /// This field is only used with the generic SPI protocol + pub p1_rx_rdy_mask, set_p1_rx_rdy_mask : 4; + /// Mask Bit for TX_FRM_DONE + /// This field is only used with the generic SPI protocol + pub tx_rdy_mask, set_tx_rdy_mask : 3; + /// Mask Bit for LINK_CHANGE + pub link_change_mask, set_link_change_mask : 1; +} + +/// LED Functions +#[repr(u8)] +pub enum LedFunc { + LinkupTxRxActicity = 0, + LinkupTxActicity, + LinkupRxActicity, + LinkupOnly, + TxRxActivity, + TxActivity, + RxActivity, + LinkupRxEr, + LinkupRxTxEr, + RxEr, + RxTxEr, + TxSop, + RxSop, + On, + Off, + Blink, + TxLevel2P4, + TxLevel1P0, + Master, + Slave, + IncompatiableLinkCfg, + AnLinkGood, + AnComplete, + TsTimer, + LocRcvrStatus, + RemRcvrStatus, + Clk25Ref, + TxTCLK, + Clk120MHz, +} + +impl From for u8 { + fn from(val: LedFunc) -> Self { + val as u8 + } +} + +impl From for LedFunc { + fn from(value: u8) -> Self { + match value { + 0 => LedFunc::LinkupTxRxActicity, + 1 => LedFunc::LinkupTxActicity, + 2 => LedFunc::LinkupRxActicity, + 3 => LedFunc::LinkupOnly, + 4 => LedFunc::TxRxActivity, + 5 => LedFunc::TxActivity, + 6 => LedFunc::RxActivity, + 7 => LedFunc::LinkupRxEr, + 8 => LedFunc::LinkupRxTxEr, + 9 => LedFunc::RxEr, + 10 => LedFunc::RxTxEr, + 11 => LedFunc::TxSop, + 12 => LedFunc::RxSop, + 13 => LedFunc::On, + 14 => LedFunc::Off, + 15 => LedFunc::Blink, + 16 => LedFunc::TxLevel2P4, + 17 => LedFunc::TxLevel1P0, + 18 => LedFunc::Master, + 19 => LedFunc::Slave, + 20 => LedFunc::IncompatiableLinkCfg, + 21 => LedFunc::AnLinkGood, + 22 => LedFunc::AnComplete, + 23 => LedFunc::TsTimer, + 24 => LedFunc::LocRcvrStatus, + 25 => LedFunc::RemRcvrStatus, + 26 => LedFunc::Clk25Ref, + 27 => LedFunc::TxTCLK, + 28 => LedFunc::Clk120MHz, + e => panic!("Invalid value {}", e), + } + } +} + +/// LED Control Register +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct LedCntrl(pub u16); +bitfield_bitrange! {struct LedCntrl(u16)} + +impl LedCntrl { + bitfield_fields! { + u8; + /// LED 0 Pin Function + pub from into LedFunc, led0_function, set_led0_function: 4, 0; + /// LED 0 Mode Selection + pub led0_mode, set_led0_mode: 5; + /// Qualify Certain LED 0 Options with Link Status. + pub led0_link_st_qualify, set_led0_link_st_qualify: 6; + /// LED 0 Enable + pub led0_en, set_led0_en: 7; + /// LED 1 Pin Function + pub from into LedFunc, led1_function, set_led1_function: 12, 8; + /// /// LED 1 Mode Selection + pub led1_mode, set_led1_mode: 13; + /// Qualify Certain LED 1 Options with Link Status. + pub led1_link_st_qualify, set_led1_link_st_qualify: 14; + /// LED 1 Enable + pub led1_en, set_led1_en: 15; + } + + pub fn new() -> Self { + LedCntrl(0) + } +} + +// LED Polarity +#[repr(u8)] +pub enum LedPol { + AutoSense = 0, + ActiveHigh, + ActiveLow, +} + +impl From for u8 { + fn from(val: LedPol) -> Self { + val as u8 + } +} + +impl From for LedPol { + fn from(value: u8) -> Self { + match value { + 0 => LedPol::AutoSense, + 1 => LedPol::ActiveHigh, + 2 => LedPol::ActiveLow, + e => panic!("Invalid value {}", e), + } + } +} + +/// LED Control Register +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct LedPolarity(pub u16); +bitfield_bitrange! {struct LedPolarity(u16)} + +impl LedPolarity { + bitfield_fields! { + u8; + /// LED 1 Polarity + pub from into LedPol, led1_polarity, set_led1_polarity: 3, 2; + /// LED 0 Polarity + pub from into LedPol, led0_polarity, set_led0_polarity: 1, 0; + } +} + +/// SPI Header +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct SpiHeader(pub u16); +bitfield_bitrange! {struct SpiHeader(u16)} + +impl SpiHeader { + bitfield_fields! { + u16; + /// Mask Bit for TXF_ECC_ERR + pub control, set_control : 15; + pub full_duplex, set_full_duplex : 14; + /// Read or Write to register + pub write, set_write : 13; + /// Registers ID/addr + pub from into SpiRegisters, addr, set_addr: 11, 0; + } +} diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index bee2e302..4588af02 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -24,6 +24,6 @@ features = ["defmt"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } diff --git a/embassy-net-driver-channel/src/fmt.rs b/embassy-net-driver-channel/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-net-driver-channel/src/fmt.rs +++ b/embassy-net-driver-channel/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 076238ba..bf7ae521 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -8,11 +8,13 @@ use core::cell::RefCell; use core::mem::MaybeUninit; use core::task::{Context, Poll}; +use driver::HardwareAddress; pub use embassy_net_driver as driver; use embassy_net_driver::{Capabilities, LinkState, Medium}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_sync::waitqueue::WakerRegistration; +use embassy_sync::zerocopy_channel; pub struct State { rx: [PacketBuf; N_RX], @@ -73,6 +75,18 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { ) } + pub fn borrow_split(&mut self) -> (StateRunner<'_>, RxRunner<'_, MTU>, TxRunner<'_, MTU>) { + ( + StateRunner { shared: self.shared }, + RxRunner { + rx_chan: self.rx_chan.borrow(), + }, + TxRunner { + tx_chan: self.tx_chan.borrow(), + }, + ) + } + pub fn state_runner(&self) -> StateRunner<'d> { StateRunner { shared: self.shared } } @@ -117,24 +131,24 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { } pub async fn tx_buf(&mut self) -> &mut [u8] { - let p = self.tx_chan.recv().await; + let p = self.tx_chan.receive().await; &mut p.buf[..p.len] } pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { - let p = self.tx_chan.try_recv()?; + let p = self.tx_chan.try_receive()?; Some(&mut p.buf[..p.len]) } pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { - match self.tx_chan.poll_recv(cx) { + match self.tx_chan.poll_receive(cx) { Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), Poll::Pending => Poll::Pending, } } pub fn tx_done(&mut self) { - self.tx_chan.recv_done(); + self.tx_chan.receive_done(); } } @@ -191,24 +205,24 @@ impl<'d, const MTU: usize> RxRunner<'d, MTU> { impl<'d, const MTU: usize> TxRunner<'d, MTU> { pub async fn tx_buf(&mut self) -> &mut [u8] { - let p = self.tx_chan.recv().await; + let p = self.tx_chan.receive().await; &mut p.buf[..p.len] } pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { - let p = self.tx_chan.try_recv()?; + let p = self.tx_chan.try_receive()?; Some(&mut p.buf[..p.len]) } pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { - match self.tx_chan.poll_recv(cx) { + match self.tx_chan.poll_receive(cx) { Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), Poll::Pending => Poll::Pending, } } pub fn tx_done(&mut self) { - self.tx_chan.recv_done(); + self.tx_chan.receive_done(); } } @@ -218,7 +232,11 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( ) -> (Runner<'d, MTU>, Device<'d, MTU>) { let mut caps = Capabilities::default(); caps.max_transmission_unit = MTU; - caps.medium = Medium::Ethernet; + caps.medium = match &hardware_address { + HardwareAddress::Ethernet(_) => Medium::Ethernet, + HardwareAddress::Ieee802154(_) => Medium::Ieee802154, + HardwareAddress::Ip => Medium::Ip, + }; // safety: this is a self-referential struct, however: // - it can't move while the `'d` borrow is active. @@ -276,7 +294,7 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { + if self.rx.poll_receive(cx).is_ready() && self.tx.poll_send(cx).is_ready() { Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) } else { None @@ -320,9 +338,9 @@ impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { F: FnOnce(&mut [u8]) -> R, { // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.rx.try_recv()); + let pkt = unwrap!(self.rx.try_receive()); let r = f(&mut pkt.buf[..pkt.len]); - self.rx.recv_done(); + self.rx.receive_done(); r } } @@ -344,215 +362,3 @@ impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { r } } - -mod zerocopy_channel { - use core::cell::RefCell; - use core::future::poll_fn; - use core::marker::PhantomData; - use core::task::{Context, Poll}; - - use embassy_sync::blocking_mutex::raw::RawMutex; - use embassy_sync::blocking_mutex::Mutex; - use embassy_sync::waitqueue::WakerRegistration; - - pub struct Channel<'a, M: RawMutex, T> { - buf: *mut T, - phantom: PhantomData<&'a mut T>, - state: Mutex>, - } - - impl<'a, M: RawMutex, T> Channel<'a, M, T> { - pub fn new(buf: &'a mut [T]) -> Self { - let len = buf.len(); - assert!(len != 0); - - Self { - buf: buf.as_mut_ptr(), - phantom: PhantomData, - state: Mutex::new(RefCell::new(State { - len, - front: 0, - back: 0, - full: false, - send_waker: WakerRegistration::new(), - recv_waker: WakerRegistration::new(), - })), - } - } - - pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { - (Sender { channel: self }, Receiver { channel: self }) - } - } - - pub struct Sender<'a, M: RawMutex, T> { - channel: &'a Channel<'a, M, T>, - } - - impl<'a, M: RawMutex, T> Sender<'a, M, T> { - pub fn borrow(&mut self) -> Sender<'_, M, T> { - Sender { channel: self.channel } - } - - pub fn try_send(&mut self) -> Option<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), - None => None, - } - }) - } - - pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), - None => { - s.recv_waker.register(cx.waker()); - Poll::Pending - } - } - }) - } - - pub async fn send(&mut self) -> &mut T { - let i = poll_fn(|cx| { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Poll::Ready(i), - None => { - s.recv_waker.register(cx.waker()); - Poll::Pending - } - } - }) - }) - .await; - unsafe { &mut *self.channel.buf.add(i) } - } - - pub fn send_done(&mut self) { - self.channel.state.lock(|s| s.borrow_mut().push_done()) - } - } - pub struct Receiver<'a, M: RawMutex, T> { - channel: &'a Channel<'a, M, T>, - } - - impl<'a, M: RawMutex, T> Receiver<'a, M, T> { - pub fn borrow(&mut self) -> Receiver<'_, M, T> { - Receiver { channel: self.channel } - } - - pub fn try_recv(&mut self) -> Option<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), - None => None, - } - }) - } - - pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), - None => { - s.send_waker.register(cx.waker()); - Poll::Pending - } - } - }) - } - - pub async fn recv(&mut self) -> &mut T { - let i = poll_fn(|cx| { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Poll::Ready(i), - None => { - s.send_waker.register(cx.waker()); - Poll::Pending - } - } - }) - }) - .await; - unsafe { &mut *self.channel.buf.add(i) } - } - - pub fn recv_done(&mut self) { - self.channel.state.lock(|s| s.borrow_mut().pop_done()) - } - } - - struct State { - len: usize, - - /// Front index. Always 0..=(N-1) - front: usize, - /// Back index. Always 0..=(N-1). - back: usize, - - /// Used to distinguish "empty" and "full" cases when `front == back`. - /// May only be `true` if `front == back`, always `false` otherwise. - full: bool, - - send_waker: WakerRegistration, - recv_waker: WakerRegistration, - } - - impl State { - fn increment(&self, i: usize) -> usize { - if i + 1 == self.len { - 0 - } else { - i + 1 - } - } - - fn is_full(&self) -> bool { - self.full - } - - fn is_empty(&self) -> bool { - self.front == self.back && !self.full - } - - fn push_index(&mut self) -> Option { - match self.is_full() { - true => None, - false => Some(self.back), - } - } - - fn push_done(&mut self) { - assert!(!self.is_full()); - self.back = self.increment(self.back); - if self.back == self.front { - self.full = true; - } - self.send_waker.wake(); - } - - fn pop_index(&mut self) -> Option { - match self.is_empty() { - true => None, - false => Some(self.front), - } - } - - fn pop_done(&mut self) { - assert!(!self.is_empty()); - self.front = self.increment(self.front); - self.full = false; - self.recv_waker.wake(); - } - } -} diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml index e02c984e..161d055c 100644 --- a/embassy-net-enc28j60/Cargo.toml +++ b/embassy-net-enc28j60/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" embedded-hal = { version = "1.0.0-rc.1" } embedded-hal-async = { version = "=1.0.0-rc.1" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } -embassy-time = { version = "0.1.2", path = "../embassy-time" } +embassy-time = { version = "0.1.3", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } defmt = { version = "0.3", optional = true } @@ -20,4 +20,4 @@ log = { version = "0.4.14", optional = true } [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/" -target = "thumbv7em-none-eabi" \ No newline at end of file +target = "thumbv7em-none-eabi" diff --git a/embassy-net-enc28j60/src/fmt.rs b/embassy-net-enc28j60/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-net-enc28j60/src/fmt.rs +++ b/embassy-net-enc28j60/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index b44cefaf..f96a6ff1 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -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 { 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 diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index d334cf3f..54cd8859 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-time = { version = "0.1.2", path = "../embassy-time" } -embassy-sync = { version = "0.2.0", path = "../embassy-sync"} +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"} @@ -23,4 +23,4 @@ heapless = "0.7.16" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/" target = "thumbv7em-none-eabi" -features = ["defmt"] \ No newline at end of file +features = ["defmt"] diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index ce6636a8..a4996b58 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -19,6 +19,8 @@ pub struct Control<'a> { } #[allow(unused)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] enum WifiMode { None = 0, Sta = 1, @@ -26,6 +28,18 @@ enum WifiMode { ApSta = 3, } +pub use proto::CtrlWifiSecProt as Security; + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Status { + pub ssid: String<32>, + pub bssid: [u8; 6], + pub rssi: i32, + pub channel: u32, + pub security: Security, +} + macro_rules! ioctl { ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => { let mut msg = proto::CtrlMsg { @@ -34,7 +48,9 @@ macro_rules! ioctl { payload: Some(proto::CtrlMsgPayload::$req_variant($req)), }; $self.ioctl(&mut msg).await?; - let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else { + #[allow(unused_mut)] + let Some(proto::CtrlMsgPayload::$resp_variant(mut $resp)) = msg.payload + else { warn!("unexpected response variant"); return Err(Error::Internal); }; @@ -66,6 +82,19 @@ impl<'a> Control<'a> { Ok(()) } + pub async fn get_status(&mut self) -> Result { + let req = proto::CtrlMsgReqGetApConfig {}; + ioctl!(self, ReqGetApConfig, RespGetApConfig, req, resp); + trim_nulls(&mut resp.ssid); + Ok(Status { + ssid: resp.ssid, + bssid: parse_mac(&resp.bssid)?, + rssi: resp.rssi as _, + channel: resp.chnl, + security: resp.sec_prot, + }) + } + pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> { let req = proto::CtrlMsgReqConnectAp { ssid: String::from(ssid), @@ -98,27 +127,7 @@ impl<'a> Control<'a> { mode: WifiMode::Sta as _, }; ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp); - - // WHY IS THIS A STRING? WHYYYY - fn nibble_from_hex(b: u8) -> u8 { - match b { - b'0'..=b'9' => b - b'0', - b'a'..=b'f' => b + 0xa - b'a', - b'A'..=b'F' => b + 0xa - b'A', - _ => panic!("invalid hex digit {}", b), - } - } - - let mac = resp.mac.as_bytes(); - let mut res = [0; 6]; - if mac.len() != 17 { - warn!("unexpected MAC respnse length"); - return Err(Error::Internal); - } - for (i, b) in res.iter_mut().enumerate() { - *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) - } - Ok(res) + parse_mac(&resp.mac) } async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> { @@ -167,3 +176,35 @@ impl<'a> Control<'a> { Ok(()) } } + +// WHY IS THIS A STRING? WHYYYY +fn parse_mac(mac: &str) -> Result<[u8; 6], Error> { + fn nibble_from_hex(b: u8) -> Result { + match b { + b'0'..=b'9' => Ok(b - b'0'), + b'a'..=b'f' => Ok(b + 0xa - b'a'), + b'A'..=b'F' => Ok(b + 0xa - b'A'), + _ => { + warn!("invalid hex digit {}", b); + Err(Error::Internal) + } + } + } + + let mac = mac.as_bytes(); + let mut res = [0; 6]; + if mac.len() != 17 { + warn!("unexpected MAC length"); + return Err(Error::Internal); + } + for (i, b) in res.iter_mut().enumerate() { + *b = (nibble_from_hex(mac[i * 3])? << 4) | nibble_from_hex(mac[i * 3 + 1])? + } + Ok(res) +} + +fn trim_nulls(s: &mut String) { + while s.chars().rev().next() == Some(0 as char) { + s.pop(); + } +} diff --git a/embassy-net-esp-hosted/src/fmt.rs b/embassy-net-esp-hosted/src/fmt.rs index 91984bde..78e583c1 100644 --- a/embassy-net-esp-hosted/src/fmt.rs +++ b/embassy-net-esp-hosted/src/fmt.rs @@ -93,7 +93,7 @@ macro_rules! unreachable { #[cfg(feature = "defmt")] macro_rules! unreachable { ($($x:tt)*) => { - ::defmt::unreachable!($($x)*); + ::defmt::unreachable!($($x)*) }; } @@ -229,7 +229,8 @@ impl Try for Result { } } -pub struct Bytes<'a>(pub &'a [u8]); +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embassy-net-ppp/Cargo.toml b/embassy-net-ppp/Cargo.toml new file mode 100644 index 00000000..da09f780 --- /dev/null +++ b/embassy-net-ppp/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "embassy-net-ppp" +version = "0.1.0" +description = "embassy-net driver for PPP over Serial" +keywords = ["embedded", "ppp", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[features] +defmt = ["dep:defmt", "ppproto/defmt"] +log = ["dep:log", "ppproto/log"] + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +embedded-io-async = { version = "0.5.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"} +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-ppp/src/" +target = "thumbv7em-none-eabi" +features = ["defmt"] diff --git a/embassy-net-ppp/README.md b/embassy-net-ppp/README.md new file mode 100644 index 00000000..58d67395 --- /dev/null +++ b/embassy-net-ppp/README.md @@ -0,0 +1,19 @@ +# `embassy-net-ppp` + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for PPP over Serial. + +## Interoperability + +This crate can run on any executor. + +It supports any serial port implementing [`embedded-io-async`](https://crates.io/crates/embedded-io-async). + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-ppp/src/fmt.rs b/embassy-net-ppp/src/fmt.rs new file mode 100644 index 00000000..78e583c1 --- /dev/null +++ b/embassy-net-ppp/src/fmt.rs @@ -0,0 +1,258 @@ +#![macro_use] +#![allow(unused_macros)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unreachable { + ($($x:tt)*) => { + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-net-ppp/src/lib.rs b/embassy-net-ppp/src/lib.rs new file mode 100644 index 00000000..66496ee0 --- /dev/null +++ b/embassy-net-ppp/src/lib.rs @@ -0,0 +1,185 @@ +#![no_std] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] + +// must be first +mod fmt; + +use core::convert::Infallible; +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 ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction}; +pub use ppproto::{Config, Ipv4Status}; + +const MTU: usize = 1500; + +/// Type alias for the embassy-net driver. +pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; + +/// Internal state for the embassy-net integration. +pub struct State { + ch_state: ch::State, +} + +impl State { + /// Create a new `State`. + pub const fn new() -> Self { + Self { + ch_state: ch::State::new(), + } + } +} + +/// Background runner for the driver. +/// +/// You must call `.run()` in a background task for the driver to operate. +pub struct Runner<'d> { + ch: ch::Runner<'d, MTU>, +} + +/// Error returned by [`Runner::run`]. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RunError { + /// Reading from the serial port failed. + 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 From> for RunError { + fn from(value: WriteAllError) -> 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. + /// + /// If reading/writing to the underlying serial port fails, the link state + /// is set to Down and the error is returned. + /// + /// It is allowed to cancel this function's future (i.e. drop it). This will terminate + /// the PPP connection and set the link state to Down. + /// + /// After this function returns or is canceled, you can call it again to establish + /// a new PPP connection. + pub async fn run( + &mut self, + mut rw: RW, + config: ppproto::Config<'_>, + mut on_ipv4_up: impl FnMut(Ipv4Status), + ) -> Result> { + let mut ppp = PPPoS::new(config); + ppp.open().unwrap(); + + let (state_chan, mut rx_chan, mut tx_chan) = self.ch.borrow_split(); + state_chan.set_link_state(LinkState::Down); + let _ondrop = OnDrop::new(|| state_chan.set_link_state(LinkState::Down)); + + let mut rx_buf = [0; 2048]; + let mut tx_buf = [0; 2048]; + + let mut needs_poll = true; + let mut was_up = false; + + loop { + let rx_fut = async { + let buf = rx_chan.rx_buf().await; + let rx_data = match needs_poll { + true => &[][..], + false => match rw.fill_buf().await { + Ok(rx_data) if rx_data.len() == 0 => return Err(RunError::Eof), + Ok(rx_data) => rx_data, + Err(e) => return Err(RunError::Read(e)), + }, + }; + Ok((buf, rx_data)) + }; + let tx_fut = tx_chan.tx_buf(); + match select(rx_fut, tx_fut).await { + Either::First(r) => { + needs_poll = false; + + let (buf, rx_data) = r?; + let n = ppp.consume(rx_data, &mut rx_buf); + rw.consume(n); + + match ppp.poll(&mut tx_buf, &mut rx_buf) { + PPPoSAction::None => {} + PPPoSAction::Received(rg) => { + let pkt = &rx_buf[rg]; + buf[..pkt.len()].copy_from_slice(pkt); + rx_chan.rx_done(pkt.len()); + } + PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await?, + } + + let status = ppp.status(); + match status.phase { + ppproto::Phase::Dead => { + return Err(RunError::Terminated); + } + ppproto::Phase::Open => { + if !was_up { + on_ipv4_up(status.ipv4.unwrap()); + } + was_up = true; + state_chan.set_link_state(LinkState::Up); + } + _ => { + was_up = false; + state_chan.set_link_state(LinkState::Down); + } + } + } + Either::Second(pkt) => { + match ppp.send(pkt, &mut tx_buf) { + Ok(n) => rw.write_all(&tx_buf[..n]).await?, + Err(BufferFullError) => unreachable!(), + } + tx_chan.tx_done(); + } + } + } + } +} + +/// Create a PPP embassy-net driver instance. +/// +/// This returns two structs: +/// - a `Device` that you must pass to the `embassy-net` stack. +/// - a `Runner`. You must call `.run()` on it in a background task. +pub fn new<'a, const N_RX: usize, const N_TX: usize>(state: &'a mut State) -> (Device<'a>, Runner<'a>) { + let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ip); + (device, Runner { ch: runner }) +} + +struct OnDrop { + f: MaybeUninit, +} + +impl OnDrop { + fn new(f: F) -> Self { + Self { f: MaybeUninit::new(f) } + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} diff --git a/embassy-net-wiznet/Cargo.toml b/embassy-net-wiznet/Cargo.toml index adf0b45f..afa0d5cd 100644 --- a/embassy-net-wiznet/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" embedded-hal = { version = "1.0.0-rc.1" } embedded-hal-async = { version = "=1.0.0-rc.1" } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } -embassy-time = { version = "0.1.2", path = "../embassy-time" } +embassy-time = { version = "0.1.3", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } defmt = { version = "0.3", optional = true } @@ -19,4 +19,4 @@ defmt = { version = "0.3", optional = true } src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/" target = "thumbv7em-none-eabi" -features = ["defmt"] \ No newline at end of file +features = ["defmt"] diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 0c551f20..8aca92a6 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -9,6 +9,7 @@ categories = [ "embedded", "no-std", "asynchronous", + "network-programming", ] [package.metadata.embassy_docs] @@ -50,8 +51,8 @@ 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.2", path = "../embassy-time" } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +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 } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index d29ab897..8c2b7d31 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -22,13 +22,13 @@ where fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { self.inner - .receive(self.cx.as_deref_mut().unwrap()) + .receive(unwrap!(self.cx.as_deref_mut())) .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) } /// Construct a transmit token. fn transmit(&mut self, _timestamp: Instant) -> Option> { - self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter) + self.inner.transmit(unwrap!(self.cx.as_deref_mut())).map(TxTokenAdapter) } /// Get a description of device capabilities. diff --git a/embassy-net/src/fmt.rs b/embassy-net/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-net/src/fmt.rs +++ b/embassy-net/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 2fb34f43..0d7ac47a 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -3,6 +3,9 @@ #![warn(missing_docs)] #![doc = include_str!("../README.md")] +#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))] +compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6"); + // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; @@ -20,7 +23,7 @@ use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; pub use embassy_net_driver as driver; -use embassy_net_driver::{Driver, LinkState, Medium}; +use embassy_net_driver::{Driver, LinkState}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; @@ -133,6 +136,8 @@ impl Default for DhcpConfig { } /// Network stack configuration. +#[derive(Debug, Clone, Default)] +#[non_exhaustive] pub struct Config { /// IPv4 configuration #[cfg(feature = "proto-ipv4")] @@ -163,10 +168,11 @@ impl Config { } } - /// IPv6 configuration with dynamic addressing. + /// IPv4 configuration with dynamic addressing. /// /// # Example /// ```rust + /// # use embassy_net::Config; /// let _cfg = Config::dhcpv4(Default::default()); /// ``` #[cfg(feature = "dhcpv4")] @@ -181,23 +187,27 @@ impl Config { /// Network stack IPv4 configuration. #[cfg(feature = "proto-ipv4")] +#[derive(Debug, Clone, Default)] pub enum ConfigV4 { + /// Do not configure IPv4. + #[default] + None, /// Use a static IPv4 address configuration. Static(StaticConfigV4), /// Use DHCP to obtain an IP address configuration. #[cfg(feature = "dhcpv4")] Dhcp(DhcpConfig), - /// Do not configure IPv6. - None, } /// Network stack IPv6 configuration. #[cfg(feature = "proto-ipv6")] +#[derive(Debug, Clone, Default)] pub enum ConfigV6 { + /// Do not configure IPv6. + #[default] + None, /// Use a static IPv6 address configuration. Static(StaticConfigV6), - /// Do not configure IPv6. - None, } /// A network stack. @@ -217,6 +227,7 @@ struct Inner { static_v6: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, + config_waker: WakerRegistration, #[cfg(feature = "dns")] dns_socket: SocketHandle, #[cfg(feature = "dns")] @@ -240,11 +251,14 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress driver::HardwareAddress::Ip => HardwareAddress::Ip, #[allow(unreachable_patterns)] - _ => panic!("Unsupported address {:?}. Make sure to enable medium-ethernet or medium-ieee802154 in embassy-net's Cargo features.", addr), + _ => panic!( + "Unsupported medium {:?}. Make sure to enable the right medium feature in embassy-net's Cargo features.", + addr + ), } } -impl Stack { +impl Stack { /// Create a new network stack. pub fn new( mut device: D, @@ -276,7 +290,6 @@ impl Stack { next_local_port, }; - #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] let mut inner = Inner { device, link_up: false, @@ -286,6 +299,7 @@ impl Stack { static_v6: None, #[cfg(feature = "dhcpv4")] dhcp_socket: None, + config_waker: WakerRegistration::new(), #[cfg(feature = "dns")] dns_socket: socket.sockets.add(dns::Socket::new( &[], @@ -295,30 +309,11 @@ impl Stack { dns_waker: WakerRegistration::new(), }; - #[cfg(feature = "medium-ieee802154")] - let _ = config; - #[cfg(feature = "proto-ipv4")] - match config.ipv4 { - ConfigV4::Static(config) => { - inner.apply_config_v4(&mut socket, config); - } - #[cfg(feature = "dhcpv4")] - ConfigV4::Dhcp(config) => { - let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); - inner.apply_dhcp_config(&mut dhcp_socket, config); - let handle = socket.sockets.add(dhcp_socket); - inner.dhcp_socket = Some(handle); - } - ConfigV4::None => {} - } + inner.set_config_v4(&mut socket, config.ipv4); #[cfg(feature = "proto-ipv6")] - match config.ipv6 { - ConfigV6::Static(config) => { - inner.apply_config_v6(&mut socket, config); - } - ConfigV6::None => {} - } + inner.set_config_v6(&mut socket, config.ipv6); + inner.apply_static_config(&mut socket); Self { socket: RefCell::new(socket), @@ -371,16 +366,86 @@ impl Stack { v4_up || v6_up } + /// Wait for the network stack to obtain a valid IP configuration. + /// + /// ## Notes: + /// - Ensure [`Stack::run`] has been called before using this function. + /// + /// - This function may never return (e.g. if no configuration is obtained through DHCP). + /// The caller is supposed to handle a timeout for this case. + /// + /// ## Example + /// ```ignore + /// let config = embassy_net::Config::dhcpv4(Default::default()); + ///// Init network stack + /// let stack = &*make_static!(embassy_net::Stack::new( + /// device, + /// config, + /// make_static!(embassy_net::StackResources::<2>::new()), + /// seed + /// )); + /// // Launch network task that runs `stack.run().await` + /// spawner.spawn(net_task(stack)).unwrap(); + /// // Wait for DHCP config + /// stack.wait_config_up().await; + /// // use the network stack + /// // ... + /// ``` + pub async fn wait_config_up(&self) { + // If the config is up already, we can return immediately. + if self.is_config_up() { + return; + } + + poll_fn(|cx| { + if self.is_config_up() { + Poll::Ready(()) + } else { + // If the config is not up, we register a waker that is woken up + // when a config is applied (static or DHCP). + trace!("Waiting for config up"); + + self.with_mut(|_, i| { + i.config_waker.register(cx.waker()); + }); + + Poll::Pending + } + }) + .await; + } + /// Get the current IPv4 configuration. + /// + /// If using DHCP, this will be None if DHCP hasn't been able to + /// acquire an IP address, or Some if it has. #[cfg(feature = "proto-ipv4")] pub fn config_v4(&self) -> Option { - self.with(|_s, i| i.static_v4.clone()) + self.with(|_, i| i.static_v4.clone()) } /// Get the current IPv6 configuration. #[cfg(feature = "proto-ipv6")] pub fn config_v6(&self) -> Option { - self.with(|_s, i| i.static_v6.clone()) + self.with(|_, i| i.static_v6.clone()) + } + + /// Set the IPv4 configuration. + #[cfg(feature = "proto-ipv4")] + pub fn set_config_v4(&self, config: ConfigV4) { + self.with_mut(|s, i| { + i.set_config_v4(s, config); + i.apply_static_config(s); + }) + } + + /// Set the IPv6 configuration. + #[cfg(feature = "proto-ipv6")] + pub fn set_config_v6(&self, config: ConfigV6) { + self.with_mut(|s, i| { + i.set_config_v6(s, config); + i.apply_static_config(s); + }) } /// Run the network stack. @@ -490,7 +555,7 @@ impl Stack { } #[cfg(feature = "igmp")] -impl Stack { +impl Stack { /// Join a multicast group. pub async fn join_multicast_group(&self, addr: T) -> Result where @@ -580,168 +645,129 @@ impl SocketStack { } } -impl Inner { +impl Inner { #[cfg(feature = "proto-ipv4")] - fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) { - debug!("Acquired IP configuration:"); - - debug!(" IP address: {}", config.address); - s.iface.update_ip_addrs(|addrs| { - if let Some((index, _)) = addrs - .iter() - .enumerate() - .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) - { - addrs.remove(index); - } - addrs.push(IpCidr::Ipv4(config.address)).unwrap(); - }); - - #[cfg(feature = "medium-ip")] - let skip_gateway = self.device.capabilities().medium != Medium::Ip; - #[cfg(not(feature = "medium-ip"))] - let skip_gateway = false; - - if !skip_gateway { - if let Some(gateway) = config.gateway { - debug!(" Default gateway: {}", gateway); - s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); - } else { - debug!(" Default gateway: None"); - s.iface.routes_mut().remove_default_ipv4_route(); - } - } - for (i, s) in config.dns_servers.iter().enumerate() { - debug!(" DNS server {}: {}", i, s); - } - - self.static_v4 = Some(config); - - #[cfg(feature = "dns")] - { - self.update_dns_servers(s) - } - } - - /// Replaces the current IPv6 static configuration with a newly supplied config. - #[cfg(feature = "proto-ipv6")] - fn apply_config_v6(&mut self, s: &mut SocketStack, config: StaticConfigV6) { - #[cfg(feature = "medium-ethernet")] - let medium = self.device.capabilities().medium; - - debug!("Acquired IPv6 configuration:"); - - debug!(" IP address: {}", config.address); - s.iface.update_ip_addrs(|addrs| { - if let Some((index, _)) = addrs - .iter() - .enumerate() - .find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_))) - { - addrs.remove(index); - } - addrs.push(IpCidr::Ipv6(config.address)).unwrap(); - }); - - #[cfg(feature = "medium-ethernet")] - if Medium::Ethernet == medium { - if let Some(gateway) = config.gateway { - debug!(" Default gateway: {}", gateway); - s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap(); - } else { - debug!(" Default gateway: None"); - s.iface.routes_mut().remove_default_ipv6_route(); - } - } - for (i, s) in config.dns_servers.iter().enumerate() { - debug!(" DNS server {}: {}", i, s); - } - - self.static_v6 = Some(config); - - #[cfg(feature = "dns")] - { - self.update_dns_servers(s) - } - } - - #[cfg(feature = "dns")] - fn update_dns_servers(&mut self, s: &mut SocketStack) { - let socket = s.sockets.get_mut::(self.dns_socket); - - let servers_v4; - #[cfg(feature = "proto-ipv4")] - { - servers_v4 = self - .static_v4 - .iter() - .flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv4(*c))); + pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) { + // Handle static config. + self.static_v4 = match config.clone() { + ConfigV4::None => None, + #[cfg(feature = "dhcpv4")] + ConfigV4::Dhcp(_) => None, + ConfigV4::Static(c) => Some(c), }; - #[cfg(not(feature = "proto-ipv4"))] - { - servers_v4 = core::iter::empty(); - } - let servers_v6; - #[cfg(feature = "proto-ipv6")] - { - servers_v6 = self - .static_v6 - .iter() - .flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv6(*c))); - } - #[cfg(not(feature = "proto-ipv6"))] - { - servers_v6 = core::iter::empty(); - } + // Handle DHCP config. + #[cfg(feature = "dhcpv4")] + match config { + ConfigV4::Dhcp(c) => { + // Create the socket if it doesn't exist. + if self.dhcp_socket.is_none() { + let socket = smoltcp::socket::dhcpv4::Socket::new(); + let handle = _s.sockets.add(socket); + self.dhcp_socket = Some(handle); + } - // Prefer the v6 DNS servers over the v4 servers - let servers: Vec = servers_v6.chain(servers_v4).collect(); - socket.update_servers(&servers[..]); - } - - #[cfg(feature = "dhcpv4")] - fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { - socket.set_ignore_naks(config.ignore_naks); - socket.set_max_lease_duration(config.max_lease_duration.map(crate::time::duration_to_smoltcp)); - socket.set_ports(config.server_port, config.client_port); - socket.set_retry_config(config.retry_config); - } - - #[cfg(feature = "dhcpv4")] - fn unapply_config_v4(&mut self, s: &mut SocketStack) { - #[cfg(feature = "medium-ethernet")] - let medium = self.device.capabilities().medium; - debug!("Lost IP configuration"); - s.iface.update_ip_addrs(|ip_addrs| { - #[cfg(feature = "proto-ipv4")] - if let Some((index, _)) = ip_addrs - .iter() - .enumerate() - .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) - { - ip_addrs.remove(index); + // Configure it + let socket = _s.sockets.get_mut::(unwrap!(self.dhcp_socket)); + socket.set_ignore_naks(c.ignore_naks); + 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.reset(); } - }); - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { - #[cfg(feature = "proto-ipv4")] - { - s.iface.routes_mut().remove_default_ipv4_route(); + _ => { + // Remove DHCP socket if any. + if let Some(socket) = self.dhcp_socket { + _s.sockets.remove(socket); + self.dhcp_socket = None; + } } } + } + + #[cfg(feature = "proto-ipv6")] + pub fn set_config_v6(&mut self, _s: &mut SocketStack, config: ConfigV6) { + self.static_v6 = match config { + ConfigV6::None => None, + ConfigV6::Static(c) => Some(c), + }; + } + + fn apply_static_config(&mut self, s: &mut SocketStack) { + let mut addrs = Vec::new(); + #[cfg(feature = "dns")] + let mut dns_servers: Vec<_, 6> = Vec::new(); #[cfg(feature = "proto-ipv4")] - { - self.static_v4 = None + let mut gateway_v4 = None; + #[cfg(feature = "proto-ipv6")] + let mut gateway_v6 = None; + + #[cfg(feature = "proto-ipv4")] + if let Some(config) = &self.static_v4 { + debug!("IPv4: UP"); + debug!(" IP address: {:?}", config.address); + debug!(" Default gateway: {:?}", config.gateway); + + unwrap!(addrs.push(IpCidr::Ipv4(config.address)).ok()); + gateway_v4 = config.gateway.into(); + #[cfg(feature = "dns")] + for s in &config.dns_servers { + debug!(" DNS server: {:?}", s); + unwrap!(dns_servers.push(s.clone().into()).ok()); + } + } else { + info!("IPv4: DOWN"); } + + #[cfg(feature = "proto-ipv6")] + if let Some(config) = &self.static_v6 { + debug!("IPv6: UP"); + debug!(" IP address: {:?}", config.address); + debug!(" Default gateway: {:?}", config.gateway); + + unwrap!(addrs.push(IpCidr::Ipv6(config.address)).ok()); + gateway_v6 = config.gateway.into(); + #[cfg(feature = "dns")] + for s in &config.dns_servers { + debug!(" DNS server: {:?}", s); + unwrap!(dns_servers.push(s.clone().into()).ok()); + } + } else { + info!("IPv6: DOWN"); + } + + // Apply addresses + s.iface.update_ip_addrs(|a| *a = addrs); + + // Apply gateways + #[cfg(feature = "proto-ipv4")] + if let Some(gateway) = gateway_v4 { + unwrap!(s.iface.routes_mut().add_default_ipv4_route(gateway)); + } else { + s.iface.routes_mut().remove_default_ipv4_route(); + } + #[cfg(feature = "proto-ipv6")] + if let Some(gateway) = gateway_v6 { + unwrap!(s.iface.routes_mut().add_default_ipv6_route(gateway)); + } else { + s.iface.routes_mut().remove_default_ipv6_route(); + } + + // Apply DNS servers + #[cfg(feature = "dns")] + s.sockets + .get_mut::(self.dns_socket) + .update_servers(&dns_servers[..]); + + self.config_waker.wake(); } fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { s.waker.register(cx.waker()); #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - if self.device.capabilities().medium == Medium::Ethernet - || self.device.capabilities().medium == Medium::Ieee802154 + if self.device.capabilities().medium == embassy_net_driver::Medium::Ethernet + || self.device.capabilities().medium == embassy_net_driver::Medium::Ieee802154 { s.iface .set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address())); @@ -763,6 +789,9 @@ impl Inner { info!("link_up = {:?}", self.link_up); } + #[allow(unused_mut)] + let mut apply_config = false; + #[cfg(feature = "dhcpv4")] if let Some(dhcp_handle) = self.dhcp_socket { let socket = s.sockets.get_mut::(dhcp_handle); @@ -770,25 +799,29 @@ impl Inner { if self.link_up { match socket.poll() { None => {} - Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s), + Some(dhcpv4::Event::Deconfigured) => { + self.static_v4 = None; + apply_config = true; + } Some(dhcpv4::Event::Configured(config)) => { - let config = StaticConfigV4 { + self.static_v4 = Some(StaticConfigV4 { address: config.address, gateway: config.router, dns_servers: config.dns_servers, - }; - self.apply_config_v4(s, config) + }); + apply_config = true; } } } else if old_link_up { socket.reset(); - self.unapply_config_v4(s); + self.static_v4 = None; + apply_config = true; } } - //if old_link_up || self.link_up { - // self.poll_configurator(timestamp) - //} - // + + if apply_config { + self.apply_static_config(s); + } if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { let t = Timer::at(instant_from_smoltcp(poll_at)); diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c92ad2d2..a12fd382 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -440,7 +440,7 @@ impl<'d> TcpIo<'d> { Poll::Ready(Err(Error::ConnectionReset)) } } else { - Poll::Ready(match s.send(f.take().unwrap()) { + Poll::Ready(match s.send(unwrap!(f.take())) { // Connection reset. TODO: this can also be timeouts etc, investigate. Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset), Ok(r) => Ok(r), @@ -468,7 +468,7 @@ impl<'d> TcpIo<'d> { Poll::Ready(Err(Error::ConnectionReset)) } } else { - Poll::Ready(match s.recv(f.take().unwrap()) { + Poll::Ready(match s.recv(unwrap!(f.take())) { // Connection reset. TODO: this can also be timeouts etc, investigate. Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => { Err(Error::ConnectionReset) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 0a5a7b8f..61058c1b 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -29,6 +29,8 @@ pub enum BindError { pub enum Error { /// No route to host. NoRoute, + /// Socket not bound to an outgoing port. + SocketNotBound, } /// An UDP socket. @@ -155,7 +157,14 @@ impl<'a> UdpSocket<'a> { s.register_send_waker(cx.waker()); Poll::Pending } - Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)), + Err(udp::SendError::Unaddressable) => { + // If no sender/outgoing port is specified, there is not really "no route" + if s.endpoint().port == 0 { + Poll::Ready(Err(Error::SocketNotBound)) + } else { + Poll::Ready(Err(Error::NoRoute)) + } + } }) } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 67ec4eb9..3c706b47 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -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,9 +90,12 @@ _ppi = [] _dppi = [] _gpio-p1 = [] +# Errata workarounds +_nrf52832_anomaly_109 = [] + [dependencies] -embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } diff --git a/embassy-nrf/src/fmt.rs b/embassy-nrf/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-nrf/src/fmt.rs +++ b/embassy-nrf/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 7488bc08..d16b4a43 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -86,7 +86,6 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { unsafe { irq.enable() }; let g = regs(); - g.events_port.write(|w| w); g.intenset.write(|w| w.port().set()); } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 355a0049..9c4b6569 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -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; $( diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index d131a43d..4828af43 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -68,8 +68,14 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); + #[cfg(feature = "_nrf52832_anomaly_109")] + if r.events_started.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.started().clear()); + } + if r.events_end.read().bits() != 0 { - s.end_waker.wake(); + s.waker.wake(); r.intenclr.write(|w| w.end().clear()); } } @@ -167,42 +173,10 @@ impl<'d, T: Instance> Spim<'d, T> { // Enable SPIM instance. r.enable.write(|w| w.enable().enabled()); - // Configure mode. - let mode = config.mode; - r.config.write(|w| { - match mode { - MODE_0 => { - w.order().msb_first(); - w.cpol().active_high(); - w.cpha().leading(); - } - MODE_1 => { - w.order().msb_first(); - w.cpol().active_high(); - w.cpha().trailing(); - } - MODE_2 => { - w.order().msb_first(); - w.cpol().active_low(); - w.cpha().leading(); - } - MODE_3 => { - w.order().msb_first(); - w.cpol().active_low(); - w.cpha().trailing(); - } - } + let mut spim = Self { _p: spim }; - w - }); - - // Configure frequency. - let frequency = config.frequency; - r.frequency.write(|w| w.frequency().variant(frequency)); - - // Set over-read character - let orc = config.orc; - r.orc.write(|w| unsafe { w.orc().bits(orc) }); + // Apply runtime peripheral configuration + Self::set_config(&mut spim, &config); // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); @@ -210,7 +184,7 @@ impl<'d, T: Instance> Spim<'d, T> { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - Self { _p: spim } + spim } fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { @@ -223,14 +197,32 @@ impl<'d, T: Instance> Spim<'d, T> { let r = T::regs(); // Set up the DMA write. - let (ptr, len) = slice_ptr_parts(tx); + let (ptr, tx_len) = slice_ptr_parts(tx); r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); - r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) }); // Set up the DMA read. - let (ptr, len) = slice_ptr_parts_mut(rx); + let (ptr, rx_len) = slice_ptr_parts_mut(rx); r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); + + #[cfg(feature = "_nrf52832_anomaly_109")] + { + let s = T::state(); + + r.events_started.reset(); + + // Set rx/tx buffer lengths to 0... + r.txd.maxcnt.reset(); + r.rxd.maxcnt.reset(); + + // ...and keep track of original buffer lengths... + s.tx.store(tx_len as _, Ordering::Relaxed); + s.rx.store(rx_len as _, Ordering::Relaxed); + + // ...signalling the start of the fake transfer. + r.intenset.write(|w| w.started().bit(true)); + } // Reset and enable the event r.events_end.reset(); @@ -245,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 {} @@ -269,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(()); } @@ -362,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> { @@ -386,18 +417,29 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { } pub(crate) mod sealed { + #[cfg(feature = "_nrf52832_anomaly_109")] + use core::sync::atomic::AtomicU8; + use embassy_sync::waitqueue::AtomicWaker; use super::*; pub struct State { - pub end_waker: AtomicWaker, + pub waker: AtomicWaker, + #[cfg(feature = "_nrf52832_anomaly_109")] + pub rx: AtomicU8, + #[cfg(feature = "_nrf52832_anomaly_109")] + pub tx: AtomicU8, } impl State { pub const fn new() -> Self { Self { - end_waker: AtomicWaker::new(), + waker: AtomicWaker::new(), + #[cfg(feature = "_nrf52832_anomaly_109")] + rx: AtomicU8::new(0), + #[cfg(feature = "_nrf52832_anomaly_109")] + tx: AtomicU8::new(0), } } } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 21282512..e695ba6b 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -169,47 +169,10 @@ impl<'d, T: Instance> Spis<'d, T> { // Enable SPIS instance. r.enable.write(|w| w.enable().enabled()); - // Configure mode. - let mode = config.mode; - r.config.write(|w| { - match mode { - MODE_0 => { - w.order().msb_first(); - w.cpol().active_high(); - w.cpha().leading(); - } - MODE_1 => { - w.order().msb_first(); - w.cpol().active_high(); - w.cpha().trailing(); - } - MODE_2 => { - w.order().msb_first(); - w.cpol().active_low(); - w.cpha().leading(); - } - MODE_3 => { - w.order().msb_first(); - w.cpol().active_low(); - w.cpha().trailing(); - } - } + let mut spis = Self { _p: spis }; - w - }); - - // Set over-read character. - let orc = config.orc; - r.orc.write(|w| unsafe { w.orc().bits(orc) }); - - // Set default character. - let def = config.def; - r.def.write(|w| unsafe { w.def().bits(def) }); - - // Configure auto-acquire on 'transfer end' event. - if config.auto_acquire { - r.shorts.write(|w| w.end_acquire().bit(true)); - } + // Apply runtime peripheral configuration + Self::set_config(&mut spis, &config); // Disable all events interrupts. r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); @@ -217,7 +180,7 @@ impl<'d, T: Instance> Spis<'d, T> { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - Self { _p: spis } + spis } fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index cec46d8d..5e2998b1 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -57,7 +57,6 @@ impl<'d> Temp<'d> { /// ```no_run /// use embassy_nrf::{bind_interrupts, temp}; /// use embassy_nrf::temp::Temp; - /// use embassy_time::{Duration, Timer}; /// /// bind_interrupts!(struct Irqs { /// TEMP => temp::InterruptHandler; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index fdea480e..fe38fb10 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -167,9 +167,10 @@ impl<'d, T: Instance> Twim<'d, T> { // Enable TWIM instance. r.enable.write(|w| w.enable().enabled()); - // Configure frequency. - r.frequency - .write(|w| unsafe { w.frequency().bits(config.frequency as u32) }); + let mut twim = Self { _p: twim }; + + // Apply runtime peripheral configuration + Self::set_config(&mut twim, &config); // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); @@ -177,7 +178,7 @@ impl<'d, T: Instance> Twim<'d, T> { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - Self { _p: twim } + twim } /// Set TX buffer, checking that it is in RAM and has suitable length. diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 60143c2b..1147286f 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -59,8 +59,8 @@ nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "em unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] [dependencies] -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.2", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.3", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } @@ -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" diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 7b25ecff..22066546 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -94,6 +94,7 @@ impl ClockConfig { post_div1: 6, post_div2: 5, }), + delay_multiplier: 128, }), ref_clk: RefClkConfig { src: RefClkSrc::Xosc, @@ -203,6 +204,7 @@ pub struct XoscConfig { pub hz: u32, pub sys_pll: Option, pub usb_pll: Option, + pub delay_multiplier: u32, } pub struct PllConfig { @@ -363,7 +365,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // start XOSC // datasheet mentions support for clock inputs into XIN, but doesn't go into // how this is achieved. pico-sdk doesn't support this at all. - start_xosc(config.hz); + start_xosc(config.hz, config.delay_multiplier); let pll_sys_freq = match config.sys_pll { Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config), @@ -624,12 +626,12 @@ pub fn clk_rtc_freq() -> u16 { CLOCKS.rtc.load(Ordering::Relaxed) } -fn start_xosc(crystal_hz: u32) { +fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { pac::XOSC .ctrl() .write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ)); - let startup_delay = ((crystal_hz / 1000) + 128) / 256; + let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256; pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16)); pac::XOSC.ctrl().write(|w| { w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ); diff --git a/embassy-rp/src/fmt.rs b/embassy-rp/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-rp/src/fmt.rs +++ b/embassy-rp/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 7a5ddd32..c358682c 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -21,6 +21,8 @@ pub enum AbortReason { NoAcknowledge, /// The arbitration was lost, e.g. electrical problems with the clock signal ArbitrationLoss, + /// Transmit ended with data still in fifo + TxNotEmpty(u16), Other(u32), } @@ -52,7 +54,7 @@ impl Default for Config { } } -const FIFO_SIZE: u8 = 16; +pub const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, @@ -636,6 +638,7 @@ mod eh1 { Self::Abort(AbortReason::NoAcknowledge) => { embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) } + Self::Abort(AbortReason::TxNotEmpty(_)) => embedded_hal_1::i2c::ErrorKind::Other, Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other, @@ -738,8 +741,8 @@ mod nightly { } } -fn i2c_reserved_addr(addr: u16) -> bool { - (addr & 0x78) == 0 || (addr & 0x78) == 0x78 +pub fn i2c_reserved_addr(addr: u16) -> bool { + ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 } mod sealed { diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs new file mode 100644 index 00000000..6136d69c --- /dev/null +++ b/embassy-rp/src/i2c_slave.rs @@ -0,0 +1,338 @@ +use core::future; +use core::marker::PhantomData; +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::interrupt::typelevel::{Binding, Interrupt}; +use crate::{pac, Peripheral}; + +/// I2C error +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// I2C abort with error + Abort(AbortReason), + /// User passed in a response buffer that was 0 length + InvalidResponseBufferLength, +} + +/// Received command +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Command { + /// General Call + GeneralCall(usize), + /// Read + Read, + /// Write+read + WriteRead(usize), + /// Write + Write(usize), +} + +/// Possible responses to responding to a read +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ReadStatus { + /// Transaction Complete, controller naked our last byte + Done, + /// Transaction Incomplete, controller trying to read more bytes than were provided + NeedMoreBytes, + /// Transaction Complere, but controller stopped reading bytes before we ran out + LeftoverBytes(u16), +} + +/// Slave Configuration +#[non_exhaustive] +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + /// Target Address + pub addr: u16, +} + +impl Default for Config { + fn default() -> Self { + Self { addr: 0x55 } + } +} + +pub struct I2cSlave<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> I2cSlave<'d, T> { + pub fn new( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + _irq: impl Binding>, + config: Config, + ) -> Self { + into_ref!(_peri, scl, sda); + + assert!(!i2c_reserved_addr(config.addr)); + assert!(config.addr != 0); + + let p = T::regs(); + + let reset = T::reset(); + crate::reset::reset(reset); + crate::reset::unreset_wait(reset); + + p.ic_enable().write(|w| w.set_enable(false)); + + p.ic_sar().write(|w| w.set_ic_sar(config.addr)); + p.ic_con().modify(|w| { + w.set_master_mode(false); + w.set_ic_slave_disable(false); + w.set_tx_empty_ctrl(true); + }); + + // Set FIFO watermarks to 1 to make things simpler. This is encoded + // by a register value of 0. Rx watermark should never change, but Tx watermark will be + // adjusted in operation. + p.ic_tx_tl().write(|w| w.set_tx_tl(0)); + 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); + }); + + // Clear interrupts + p.ic_clr_intr().read(); + + // Enable I2C block + p.ic_enable().write(|w| w.set_enable(true)); + + // mask everything initially + p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self { phantom: PhantomData } + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once the waker is set (to eg enable the required interrupts). + #[inline(always)] + async fn wait_on(&mut self, mut f: F, mut g: G) -> U + where + F: FnMut(&mut Self) -> Poll, + G: FnMut(&mut Self), + { + future::poll_fn(|cx| { + let r = f(self); + + trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0); + + if r.is_pending() { + T::waker().register(cx.waker()); + g(self); + } + + r + }) + .await + } + + #[inline(always)] + fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize { + let p = T::regs(); + let len = p.ic_rxflr().read().rxflr() as usize; + let end = offset + len; + for i in offset..end { + buffer[i] = p.ic_data_cmd().read().dat(); + } + end + } + + #[inline(always)] + fn write_to_fifo(&mut self, buffer: &[u8]) { + let p = T::regs(); + for byte in buffer { + p.ic_data_cmd().write(|w| w.set_dat(*byte)); + } + } + + /// Wait asynchronously for commands from an I2C master. + /// `buffer` is provided in case master does a 'write' and is unused for 'read'. + pub async fn listen(&mut self, buffer: &mut [u8]) -> Result { + let p = T::regs(); + + p.ic_clr_intr().read(); + // set rx fifo watermark to 1 byte + p.ic_rx_tl().write(|w| w.set_rx_tl(0)); + + let mut len = 0; + let ret = self + .wait_on( + |me| { + let stat = p.ic_raw_intr_stat().read(); + if p.ic_rxflr().read().rxflr() > 0 { + len = me.drain_fifo(buffer, len); + // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise + p.ic_rx_tl().write(|w| w.set_rx_tl(11)); + } + + if stat.restart_det() && stat.rd_req() { + Poll::Ready(Ok(Command::WriteRead(len))) + } else if stat.gen_call() && stat.stop_det() && len > 0 { + Poll::Ready(Ok(Command::GeneralCall(len))) + } else if stat.stop_det() { + Poll::Ready(Ok(Command::Write(len))) + } else if stat.rd_req() { + Poll::Ready(Ok(Command::Read)) + } else { + Poll::Pending + } + }, + |_me| { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_restart_det(true); + w.set_m_gen_call(true); + w.set_m_rd_req(true); + w.set_m_rx_full(true); + }); + }, + ) + .await; + + p.ic_clr_intr().read(); + + ret + } + + /// Respond to an I2C master READ command, asynchronously. + pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result { + let p = T::regs(); + + if buffer.len() == 0 { + return Err(Error::InvalidResponseBufferLength); + } + + let mut chunks = buffer.chunks(FIFO_SIZE as usize); + + let ret = self + .wait_on( + |me| { + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { + return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); + } else { + return Poll::Ready(Err(abort_reason)); + } + } + + if let Some(chunk) = chunks.next() { + me.write_to_fifo(chunk); + + Poll::Pending + } else { + let stat = p.ic_raw_intr_stat().read(); + + if stat.rx_done() && stat.stop_det() { + Poll::Ready(Ok(ReadStatus::Done)) + } else if stat.rd_req() { + Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) + } else { + Poll::Pending + } + } + }, + |_me| { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_rx_done(true); + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await; + + p.ic_clr_intr().read(); + + ret + } + + /// Respond to reads with the fill byte until the controller stops asking + pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> { + loop { + match self.respond_to_read(&[fill]).await { + Ok(ReadStatus::NeedMoreBytes) => (), + Ok(_) => break Ok(()), + Err(e) => break Err(e), + } + } + } + + /// Respond to a master read, then fill any remaining read bytes with `fill` + pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result { + let resp_stat = self.respond_to_read(buffer).await?; + + if resp_stat == ReadStatus::NeedMoreBytes { + self.respond_till_stop(fill).await?; + Ok(ReadStatus::Done) + } else { + Ok(resp_stat) + } + } + + #[inline(always)] + fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { + let p = T::regs(); + let mut abort_reason = p.ic_tx_abrt_source().read(); + + // Mask off fifo flush count + let tx_flush_cnt = abort_reason.tx_flush_cnt(); + abort_reason.set_tx_flush_cnt(0); + + // Mask off master_dis + abort_reason.set_abrt_master_dis(false); + + if abort_reason.0 != 0 { + // Note clearing the abort flag also clears the reason, and this + // instance of flag is clear-on-read! Note also the + // IC_CLR_TX_ABRT register always reads as 0. + p.ic_clr_tx_abrt().read(); + + let reason = if abort_reason.abrt_7b_addr_noack() + | abort_reason.abrt_10addr1_noack() + | abort_reason.abrt_10addr2_noack() + { + AbortReason::NoAcknowledge + } else if abort_reason.arb_lost() { + AbortReason::ArbitrationLoss + } else if abort_reason.abrt_slvflush_txfifo() { + AbortReason::TxNotEmpty(tx_flush_cnt) + } else { + AbortReason::Other(abort_reason.0) + }; + + Err(Error::Abort(reason)) + } else { + Ok(()) + } + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 49bd3533..c3561bbe 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -16,6 +16,7 @@ pub mod flash; mod float; pub mod gpio; pub mod i2c; +pub mod i2c_slave; pub mod multicore; pub mod pwm; mod reset; @@ -89,7 +90,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)] diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index c09d0914..97dfce2e 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -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, } diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 96c47484..3b10b7c5 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -12,8 +12,8 @@ features = ["stm32wb55rg"] [dependencies] embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" } embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-stm32-wpan/src/fmt.rs b/embassy-stm32-wpan/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-stm32-wpan/src/fmt.rs +++ b/embassy-stm32-wpan/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs index cd32692e..c5f2334f 100644 --- a/embassy-stm32-wpan/src/sub/ble.rs +++ b/embassy-stm32-wpan/src/sub/ble.rs @@ -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, + _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 { diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index b0cf0248..baf4da97 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -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, + _private: (), } impl Mac { pub(crate) fn new() -> Self { - Self { phantom: PhantomData } + Self { _private: () } } /// `HW_IPCC_MAC_802_15_4_EvtNot` diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs index da05ad1d..4e4d2f85 100644 --- a/embassy-stm32-wpan/src/sub/mm.rs +++ b/embassy-stm32-wpan/src/sub/mm.rs @@ -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> = Aligned(MaybeUninit::uninit()); pub struct MemoryManager { - phantom: PhantomData, + _private: (), } impl MemoryManager { @@ -44,7 +43,7 @@ impl MemoryManager { }); } - Self { phantom: PhantomData } + Self { _private: () } } pub async fn run_queue(&self) { diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index c17fd531..bd2ea3f7 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -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, + _private: (), } impl Sys { @@ -27,7 +26,7 @@ impl Sys { }); } - Self { phantom: PhantomData } + Self { _private: () } } /// Returns CPU2 wireless firmware information (if present). diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 612a54f2..1ef92430 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -8,31 +8,32 @@ license = "MIT OR Apache-2.0" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time", "low-power"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi", features = ["low-power"] }, { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, { 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" }, ] [dependencies] -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } @@ -58,11 +59,11 @@ 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 = "12" +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-735cab337aad6161f3d6bcf3e49cd1f034bc3130" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" -stm32-fmc = "0.2.4" +stm32-fmc = "0.3.0" seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.5.0" } @@ -77,7 +78,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { path = "../../stm32-data-generated/stm32-metapac", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-735cab337aad6161f3d6bcf3e49cd1f034bc3130", default-features = false, features = ["metadata"]} [features] default = ["rt"] @@ -1454,6 +1455,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" ] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 1b86dad7..ccc9210d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -308,13 +308,11 @@ fn main() { // ======== // Generate RccPeripheral impls + let refcounted_peripherals = HashSet::from(["usart", "adc"]); + let mut refcount_statics = HashSet::new(); + for p in METADATA.peripherals { - // generating RccPeripheral impl for H7 ADC3 would result in bad frequency - if !singletons.contains(&p.name.to_string()) - || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) - || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "f3")) - || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "v4")) - { + if !singletons.contains(&p.name.to_string()) { continue; } @@ -344,11 +342,36 @@ fn main() { TokenStream::new() }; + let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" }; let pname = format_ident!("{}", p.name); let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase()); let en_reg = format_ident!("{}", en.register.to_ascii_lowercase()); let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase()); + let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) { + let refcount_static = + format_ident!("{}_{}", en.register.to_ascii_uppercase(), en.field.to_ascii_uppercase()); + + refcount_statics.insert(refcount_static.clone()); + + ( + quote! { + unsafe { refcount_statics::#refcount_static += 1 }; + if unsafe { refcount_statics::#refcount_static } > 1 { + return; + } + }, + quote! { + unsafe { refcount_statics::#refcount_static -= 1 }; + if unsafe { refcount_statics::#refcount_static } > 0 { + return; + } + }, + ) + } else { + (TokenStream::new(), TokenStream::new()) + }; + g.extend(quote! { impl crate::rcc::sealed::RccPeripheral for peripherals::#pname { fn frequency() -> crate::time::Hertz { @@ -356,6 +379,7 @@ fn main() { } fn enable() { critical_section::with(|_| { + #before_enable #[cfg(feature = "low-power")] crate::rcc::clock_refcount_add(); crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); @@ -364,6 +388,7 @@ fn main() { } fn disable() { critical_section::with(|_| { + #before_disable crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); #[cfg(feature = "low-power")] crate::rcc::clock_refcount_sub(); @@ -379,6 +404,19 @@ 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; + }); + } + + g.extend(quote! { + mod refcount_statics { + #refcount_mod + } + }); + // ======== // Generate fns to enable GPIO, DMA in RCC @@ -673,6 +711,10 @@ fn main() { // ADC is special if regs.kind == "adc" { + if p.rcc.is_none() { + continue; + } + let peri = format_ident!("{}", p.name); let pin_name = format_ident!("{}", pin.pin); @@ -786,6 +828,17 @@ fn main() { let mut peripherals_table: Vec> = Vec::new(); let mut pins_table: Vec> = Vec::new(); let mut dma_channels_table: Vec> = Vec::new(); + let mut adc_common_table: Vec> = 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 @@ -823,6 +876,17 @@ fn main() { } } + 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(), @@ -901,6 +965,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(); @@ -942,13 +1007,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() {} diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index e577ec28..c1326481 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -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 { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + 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 AdcPin for Vref {} impl super::sealed::AdcPin for Vref { @@ -60,7 +81,7 @@ impl<'d, T: Instance> Adc<'d, T> { } fn freq() -> Hertz { - unsafe { get_freqs() }.adc + unsafe { get_freqs() }.adc.unwrap() } pub fn sample_time_for_us(&self, us: u32) -> SampleTime { @@ -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) -> u16 { + pub async fn read(&mut self, pin: &mut impl AdcPin) -> 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(); + } +} diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs new file mode 100644 index 00000000..7c13f810 --- /dev/null +++ b/embassy-stm32/src/adc/f3.rs @@ -0,0 +1,195 @@ +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::{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 { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + 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 AdcPin for Vref {} +impl super::sealed::AdcPin for Vref { + fn channel(&self) -> u8 { + 18 + } +} + +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 AdcPin for Temperature {} +impl super::sealed::AdcPin for Temperature { + fn channel(&self) -> u8 { + 16 + } +} + +impl<'d, T: Instance> Adc<'d, T> { + pub fn new( + adc: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + delay: &mut impl DelayUs, + ) -> Self { + use crate::pac::adc::vals; + + into_ref!(adc); + + T::enable(); + T::reset(); + + // Enable the adc regulator + T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE)); + T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::ENABLED)); + + // Wait for the regulator to stabilize + delay.delay_us(10); + + assert!(!T::regs().cr().read().aden()); + + // Begin calibration + T::regs().cr().modify(|w| w.set_adcaldif(false)); + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + + // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223) + delay.delay_us(1 + (6 * 1_000_000 / Self::freq().0)); + + // Enable the adc + T::regs().cr().modify(|w| w.set_aden(true)); + + // 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(), + } + } + + fn freq() -> Hertz { + ::frequency() + } + + pub fn sample_time_for_us(&self, us: u32) -> SampleTime { + match us * Self::freq().0 / 1_000_000 { + 0..=1 => SampleTime::Cycles1_5, + 2..=4 => SampleTime::Cycles4_5, + 5..=7 => SampleTime::Cycles7_5, + 8..=19 => SampleTime::Cycles19_5, + 20..=61 => SampleTime::Cycles61_5, + 62..=181 => SampleTime::Cycles181_5, + _ => SampleTime::Cycles601_5, + } + } + + pub fn enable_vref(&self, _delay: &mut impl DelayUs) -> Vref { + T::common_regs().ccr().modify(|w| w.set_vrefen(true)); + + Vref {} + } + + pub fn enable_temperature(&self) -> Temperature { + T::common_regs().ccr().modify(|w| w.set_tsen(true)); + + Temperature {} + } + + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + /// Perform a single conversion. + 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)); + + 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 async fn read(&mut self, pin: &mut impl AdcPin) -> 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().await + } + + fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); + if ch <= 9 { + T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time)); + } else { + 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(); + } +} diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index e57889aa..365738a3 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,23 +1,24 @@ #![macro_use] -#[cfg(not(any(adc_f3, adc_f3_v2)))] +#[cfg(not(adc_f3_v2))] #[cfg_attr(adc_f1, path = "f1.rs")] +#[cfg_attr(adc_f3, path = "f3.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] #[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] +#[cfg(not(any(adc_f1, adc_f3_v2)))] mod resolution; mod sample_time; -#[cfg(not(any(adc_f3, adc_f3_v2)))] #[allow(unused)] +#[cfg(not(adc_f3_v2))] pub use _version::*; #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] pub use resolution::Resolution; -#[cfg(not(any(adc_f3, adc_f3_v2)))] +#[cfg(not(adc_f3_v2))] pub use sample_time::SampleTime; use crate::peripherals; @@ -25,18 +26,46 @@ use crate::peripherals; pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, - #[cfg(not(any(adc_f3, adc_f3_v2)))] + #[cfg(not(adc_f3_v2))] sample_time: SampleTime, } 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, adc_f3_v2)))] + #[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 { + #[cfg(any(adc_v1, adc_v2))] + fn set_as_analog(&mut self) {} + fn channel(&self) -> u8; } @@ -45,68 +74,45 @@ pub(crate) mod sealed { } } -#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))] +#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4, adc_f3)))] pub trait Instance: sealed::Instance + crate::Peripheral

{} -#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4))] +#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4, adc_f3))] pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} pub trait AdcPin: sealed::AdcPin {} pub trait InternalChannel: sealed::InternalChannel {} -#[cfg(not(stm32h7))] -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, adc_f3_v2)))] + + #[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 - }; - } + return crate::pac::$common_inst + } + + #[cfg(adc_f3)] + fn frequency() -> crate::time::Hertz { + 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::$inst {} - }; -); - -#[cfg(stm32h7)] -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)))] - fn common_regs() -> crate::pac::adccommon::AdcCommon { - foreach_peripheral!{ - (adccommon, ADC3_COMMON) => { - return crate::pac::ADC3_COMMON - }; + foreach_interrupt!( + ($inst,adc,ADC,GLOBAL,$irq:ident) => { + impl sealed::InterruptableInstance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; } - } - } - - impl crate::adc::Instance for peripherals::ADC3 {} - }; - (adc, $inst:ident) => { - impl crate::adc::sealed::Instance for peripherals::$inst { - fn regs() -> crate::pac::adc::Adc { - crate::pac::$inst - } - #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> crate::pac::adccommon::AdcCommon { - foreach_peripheral!{ - (adccommon, ADC_COMMON) => { - return crate::pac::ADC_COMMON - }; - } - } - } + }; + ); impl crate::adc::Instance for peripherals::$inst {} }; @@ -117,6 +123,11 @@ macro_rules! impl_adc_pin { impl crate::adc::AdcPin for crate::peripherals::$pin {} impl crate::adc::sealed::AdcPin for crate::peripherals::$pin { + #[cfg(any(adc_v1, adc_v2))] + fn set_as_analog(&mut self) { + ::set_as_analog(self); + } + fn channel(&self) -> u8 { $ch } diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 67fb9b8c..5668137b 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,4 +1,4 @@ -#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] +#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { TwelveBit, @@ -19,7 +19,7 @@ pub enum Resolution { impl Default for Resolution { fn default() -> Self { - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] { Self::TwelveBit } @@ -40,7 +40,7 @@ impl From for crate::pac::adc::vals::Res { Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } @@ -56,7 +56,7 @@ impl Resolution { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, Resolution::EightBit => (1 << 8) - 1, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] Resolution::SixBit => (1 << 6) - 1, } } diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 5480e7a7..6a661929 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -1,4 +1,4 @@ -#[cfg(not(any(adc_f3, adc_f3_v2)))] +#[cfg(not(adc_f3_v2))] macro_rules! impl_sample_time { ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] @@ -105,3 +105,19 @@ impl_sample_time!( ("810.5", Cycles810_5, CYCLES810_5) ) ); + +#[cfg(adc_f3)] +impl_sample_time!( + "1.5", + Cycles1_5, + ( + ("1.5", Cycles1_5, CYCLES1_5), + ("2.5", Cycles2_5, CYCLES2_5), + ("4.5", Cycles4_5, CYCLES4_5), + ("7.5", Cycles7_5, CYCLES7_5), + ("19.5", Cycles19_5, CYCLES19_5), + ("61.5", Cycles61_5, CYCLES61_5), + ("181.5", Cycles181_5, CYCLES181_5), + ("601.5", Cycles601_5, CYCLES601_5) + ) +); diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index e8245884..fded26e4 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -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 { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + 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 for Vbat {} -impl super::sealed::InternalChannel for Vbat { +impl AdcPin for Vbat {} +impl super::sealed::AdcPin for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl InternalChannel for Vref {} -impl super::sealed::InternalChannel for Vref { +impl AdcPin for Vref {} +impl super::sealed::AdcPin for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl InternalChannel for Temperature {} -impl super::sealed::InternalChannel for Temperature { +impl AdcPin for Temperature {} +impl super::sealed::AdcPin for Temperature { fn channel(&self) -> u8 { 16 } } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + pub fn new( + adc: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + delay: &mut impl DelayUs, + ) -> 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) -> 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

(&mut self, pin: &mut P) -> u16 - where - P: AdcPin + crate::gpio::sealed::Pin, - { + pub async fn read(&mut self, pin: &mut impl AdcPin) -> 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) -> 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(); } } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 9a7acea5..a669013c 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -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 for VrefInt {} -impl super::sealed::InternalChannel for VrefInt { +impl AdcPin for VrefInt {} +impl super::sealed::AdcPin for VrefInt { fn channel(&self) -> u8 { 17 } @@ -31,8 +30,8 @@ impl VrefInt { } pub struct Temperature; -impl InternalChannel for Temperature {} -impl super::sealed::InternalChannel for Temperature { +impl AdcPin for Temperature {} +impl super::sealed::AdcPin 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 for Vbat {} -impl super::sealed::InternalChannel for Vbat { +impl AdcPin for Vbat {} +impl super::sealed::AdcPin for Vbat { fn channel(&self) -> u8 { 18 } @@ -102,7 +101,7 @@ where let presc = Prescaler::from_pclk2(T::frequency()); T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); + reg.set_adon(true); }); delay.delay_us(ADC_POWERUP_TIME_US); @@ -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

(&mut self, pin: &mut P) -> u16 - where - P: AdcPin, - P: crate::gpio::sealed::Pin, - { + pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { pin.set_as_analog(); - self.read_channel(pin.channel()) - } - - pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> 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(); } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 821cc7f6..011ecc28 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -13,7 +13,7 @@ pub const VREF_CALIB_MV: u32 = 3000; /// configuration. fn enable() { critical_section::with(|_| { - #[cfg(stm32h7)] + #[cfg(any(stm32h7, stm32wl))] crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true)); #[cfg(stm32g0)] crate::pac::RCC.apbenr2().modify(|w| w.set_adcen(true)); @@ -26,9 +26,9 @@ pub struct VrefInt; impl AdcPin for VrefInt {} impl super::sealed::AdcPin for VrefInt { fn channel(&self) -> u8 { - #[cfg(not(stm32g0))] + #[cfg(not(adc_g0))] let val = 0; - #[cfg(stm32g0)] + #[cfg(adc_g0)] let val = 13; val } @@ -38,9 +38,9 @@ pub struct Temperature; impl AdcPin for Temperature {} impl super::sealed::AdcPin for Temperature { fn channel(&self) -> u8 { - #[cfg(not(stm32g0))] + #[cfg(not(adc_g0))] let val = 17; - #[cfg(stm32g0)] + #[cfg(adc_g0)] let val = 12; val } @@ -50,9 +50,9 @@ pub struct Vbat; impl AdcPin for Vbat {} impl super::sealed::AdcPin for Vbat { fn channel(&self) -> u8 { - #[cfg(not(stm32g0))] + #[cfg(not(adc_g0))] let val = 18; - #[cfg(stm32g0)] + #[cfg(adc_g0)] let val = 14; val } @@ -92,9 +92,14 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn enable_vrefint(&self, delay: &mut impl DelayUs) -> VrefInt { + #[cfg(not(adc_g0))] T::common_regs().ccr().modify(|reg| { reg.set_vrefen(true); }); + #[cfg(adc_g0)] + T::regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us // to stabilize the internal voltage reference, we wait a little more. @@ -106,17 +111,27 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn enable_temperature(&self) -> Temperature { + #[cfg(not(adc_g0))] T::common_regs().ccr().modify(|reg| { reg.set_ch17sel(true); }); + #[cfg(adc_g0)] + T::regs().ccr().modify(|reg| { + reg.set_tsen(true); + }); Temperature {} } pub fn enable_vbat(&self) -> Vbat { + #[cfg(not(adc_g0))] T::common_regs().ccr().modify(|reg| { reg.set_ch18sel(true); }); + #[cfg(adc_g0)] + T::regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); Vbat {} } @@ -126,9 +141,9 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - #[cfg(not(stm32g0))] + #[cfg(not(adc_g0))] T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - #[cfg(stm32g0)] + #[cfg(adc_g0)] T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); } @@ -182,9 +197,9 @@ impl<'d, T: Instance> Adc<'d, T> { Self::set_channel_sample_time(pin.channel(), self.sample_time); // Select channel - #[cfg(not(stm32g0))] + #[cfg(not(adc_g0))] T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); - #[cfg(stm32g0)] + #[cfg(adc_g0)] T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel())); // Some models are affected by an erratum: @@ -203,12 +218,12 @@ impl<'d, T: Instance> Adc<'d, T> { val } - #[cfg(stm32g0)] + #[cfg(adc_g0)] fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); } - #[cfg(not(stm32g0))] + #[cfg(not(adc_g0))] fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { let sample_time = sample_time.into(); T::regs() diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 64d0f0c7..655c0cb6 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,6 +1,5 @@ -use core::sync::atomic::{AtomicU8, Ordering}; - use embedded_hal_02::blocking::delay::DelayUs; +#[allow(unused)] use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; @@ -13,12 +12,31 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs +/// Max single ADC operation clock frequency +#[cfg(stm32g4)] +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); +#[cfg(stm32h7)] +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); + +#[cfg(stm32g4)] +const VREF_CHANNEL: u8 = 18; +#[cfg(stm32g4)] +const TEMP_CHANNEL: u8 = 16; + +#[cfg(stm32h7)] +const VREF_CHANNEL: u8 = 19; +#[cfg(stm32h7)] +const TEMP_CHANNEL: u8 = 18; + +// TODO this should be 14 for H7a/b/35 +const VBAT_CHANNEL: u8 = 17; + +// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl InternalChannel for VrefInt {} impl super::sealed::InternalChannel for VrefInt { fn channel(&self) -> u8 { - 19 + VREF_CHANNEL } } @@ -26,7 +44,7 @@ pub struct Temperature; impl InternalChannel for Temperature {} impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { - 18 + TEMP_CHANNEL } } @@ -34,128 +52,10 @@ pub struct Vbat; impl InternalChannel for Vbat {} impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { - // TODO this should be 14 for H7a/b/35 - 17 + VBAT_CHANNEL } } -static ADC12_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0); - -#[cfg(stm32h7)] -foreach_peripheral!( - (adc, ADC1) => { - impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC1 { - fn frequency() -> crate::time::Hertz { - critical_section::with(|_| { - match unsafe { crate::rcc::get_freqs() }.adc { - Some(ck) => ck, - None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") - } - }) - } - - fn enable() { - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) - }); - ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); - } - - fn disable() { - if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); - }) - } - ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst); - } - - fn reset() { - if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { - critical_section::with(|_| { - crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); - crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); - }); - } - } - } - - impl crate::rcc::RccPeripheral for crate::peripherals::ADC1 {} - }; - (adc, ADC2) => { - impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC2 { - fn frequency() -> crate::time::Hertz { - critical_section::with(|_| { - match unsafe { crate::rcc::get_freqs() }.adc { - Some(ck) => ck, - None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") - } - }) - } - - fn enable() { - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) - }); - ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); - } - - fn disable() { - if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); - }) - } - ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst); - } - - fn reset() { - if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { - critical_section::with(|_| { - crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); - crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); - }); - } - } - } - - impl crate::rcc::RccPeripheral for crate::peripherals::ADC2 {} - }; - (adc, ADC3) => { - impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC3 { - fn frequency() -> crate::time::Hertz { - critical_section::with(|_| { - match unsafe { crate::rcc::get_freqs() }.adc { - Some(ck) => ck, - None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") - } - }) - } - - fn enable() { - critical_section::with(|_| { - crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true)) - }); - } - - fn disable() { - critical_section::with(|_| { - crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false)); - }) - } - - fn reset() { - critical_section::with(|_| { - crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true)); - crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false)); - }); - } - } - - impl crate::rcc::RccPeripheral for crate::peripherals::ADC3 {} - }; -); - // NOTE (unused): The prescaler enum closely copies the hardware capabilities, // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. #[allow(unused)] @@ -176,7 +76,7 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / 50_000_000; + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, @@ -237,20 +137,23 @@ impl<'d, T: Instance> Adc<'d, T> { let frequency = Hertz(T::frequency().0 / prescaler.divisor()); info!("ADC frequency set to {} Hz", frequency.0); - if frequency > Hertz::mhz(50) { - panic!("Maximal allowed frequency for the ADC is 50 MHz and it varies with different packages, refer to ST docs for more information."); + if frequency > MAX_ADC_CLK_FREQ { + panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); } - let boost = if frequency < Hertz::khz(6_250) { - Boost::LT6_25 - } else if frequency < Hertz::khz(12_500) { - Boost::LT12_5 - } else if frequency < Hertz::mhz(25) { - Boost::LT25 - } else { - Boost::LT50 - }; - T::regs().cr().modify(|w| w.set_boost(boost)); + #[cfg(stm32h7)] + { + let boost = if frequency < Hertz::khz(6_250) { + Boost::LT6_25 + } else if frequency < Hertz::khz(12_500) { + Boost::LT12_5 + } else if frequency < Hertz::mhz(25) { + Boost::LT25 + } else { + Boost::LT50 + }; + T::regs().cr().modify(|w| w.set_boost(boost)); + } let mut s = Self { adc, sample_time: Default::default(), @@ -379,10 +282,14 @@ impl<'d, T: Instance> Adc<'d, T> { // Configure channel Self::set_channel_sample_time(channel, self.sample_time); - T::regs().cfgr2().modify(|w| w.set_lshift(0)); - T::regs() - .pcsel() - .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + #[cfg(stm32h7)] + { + T::regs().cfgr2().modify(|w| w.set_lshift(0)); + T::regs() + .pcsel() + .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + } + T::regs().sqr1().write(|reg| { reg.set_sq(0, channel); reg.set_l(0); diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index a1e0240c..4d19103d 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -129,7 +129,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); diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index ada495fd..6efd40e3 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -34,8 +34,6 @@ impl interrupt::typelevel::Handler 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>, @@ -80,7 +78,6 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { // 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 +99,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 +161,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 diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 925cf39b..62f32170 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -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)))] - ::enable(); - #[cfg(stm32f1)] - ::enable(); } diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index a382cb74..c709d46d 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -758,6 +758,9 @@ foreach_pin!( ); pub(crate) unsafe fn init() { + #[cfg(afio)] + ::enable(); + crate::_generated::init_gpio(); } diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 31c48814..c47b0c09 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -8,6 +8,8 @@ pub use traits::Instance; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; +#[cfg(stm32f334)] +use crate::rcc::get_freqs; use crate::time::Hertz; use crate::Peripheral; @@ -158,17 +160,29 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> { T::enable(); ::reset(); - // // Enable and and stabilize the DLL - // T::regs().dllcr().modify(|w| { - // // w.set_calen(true); - // // w.set_calrte(11); - // w.set_cal(true); - // }); - // - // debug!("wait for dll calibration"); - // while !T::regs().isr().read().dllrdy() {} - // - // debug!("dll calibration complete"); + #[cfg(stm32f334)] + if unsafe { get_freqs() }.hrtim.is_some() { + // Enable and and stabilize the DLL + T::regs().dllcr().modify(|w| { + w.set_cal(true); + }); + + trace!("hrtim: wait for dll calibration"); + while !T::regs().isr().read().dllrdy() {} + + trace!("hrtim: dll calibration complete"); + + // Enable periodic calibration + // Cal must be disabled before we can enable it + T::regs().dllcr().modify(|w| { + w.set_cal(false); + }); + + T::regs().dllcr().modify(|w| { + w.set_calen(true); + w.set_calrte(11); + }); + } Self { _inner: tim, diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs index 158a6886..34a363a1 100644 --- a/embassy-stm32/src/hrtim/traits.rs +++ b/embassy-stm32/src/hrtim/traits.rs @@ -1,31 +1,17 @@ use crate::rcc::sealed::RccPeripheral; use crate::time::Hertz; +#[repr(u8)] #[derive(Clone, Copy)] pub(crate) enum Prescaler { - Div1, - Div2, - Div4, - Div8, - Div16, - Div32, - Div64, - Div128, -} - -impl From for u32 { - fn from(val: Prescaler) -> Self { - match val { - Prescaler::Div1 => 1, - Prescaler::Div2 => 2, - Prescaler::Div4 => 4, - Prescaler::Div8 => 8, - Prescaler::Div16 => 16, - Prescaler::Div32 => 32, - Prescaler::Div64 => 64, - Prescaler::Div128 => 128, - } - } + Div1 = 1, + Div2 = 2, + Div4 = 4, + Div8 = 8, + Div16 = 16, + Div32 = 32, + Div64 = 64, + Div128 = 128, } impl From for u8 { @@ -72,7 +58,7 @@ impl Prescaler { Prescaler::Div128, ] .iter() - .skip_while(|psc| >::into(**psc) <= val) + .skip_while(|psc| **psc as u32 <= val) .next() .unwrap() } @@ -80,7 +66,7 @@ impl Prescaler { pub fn compute_min_low_res(val: u32) -> Self { *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] .iter() - .skip_while(|psc| >::into(**psc) <= val) + .skip_while(|psc| **psc as u32 <= val) .next() .unwrap() } @@ -92,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); // @@ -113,77 +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; - let timer_f = 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 psc_val: u32 = psc.into(); - let timer_f = 32 * (timer_f / psc_val); - 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; - let timer_f = 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 psc_val: u32 = psc.into(); - let timer_f = 32 * (timer_f / psc_val); - 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(); - let psc_val: u32 = channel_psc.into(); - - - // The dead-time base clock runs 4 times slower than the hrtim base clock - // u9::MAX = 511 - let psc_min = (psc_val * 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_psc_val: u32 = psc.into(); - let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); - - 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 { diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 618d85af..f32dd0f0 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -339,6 +339,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } +impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { + fn drop(&mut self) { + T::disable(); + } +} + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { type Error = Error; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 4327899b..36f70e32 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -838,6 +838,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } +impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { + fn drop(&mut self) { + T::disable(); + } +} + mod eh02 { use super::*; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index b5a12859..2718c96d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -90,6 +90,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; $( @@ -119,6 +120,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 { @@ -156,7 +158,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); @@ -181,6 +183,13 @@ 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 { gpio::init(); dma::init( @@ -199,6 +208,11 @@ pub fn init(config: Config) -> Peripherals { // must be after rcc init #[cfg(feature = "_time-driver")] time_driver::init(); + + #[cfg(feature = "low-power")] + while !crate::rcc::low_power_ready() { + crate::rcc::clock_refcount_sub(); + } } p diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 7814fa38..ce8afb57 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -3,45 +3,33 @@ use core::marker::PhantomData; use cortex_m::peripheral::SCB; use embassy_executor::*; -use embassy_time::Duration; use crate::interrupt; -use crate::interrupt::typelevel::Interrupt; -use crate::pac::EXTI; use crate::rcc::low_power_ready; +use crate::time_driver::{get_driver, RtcDriver}; const THREAD_PENDER: usize = usize::MAX; -const THRESHOLD: Duration = Duration::from_millis(500); -use crate::rtc::{Rtc, RtcInstant}; +use crate::rtc::Rtc; -static mut RTC: Option<&'static Rtc> = None; +static mut EXECUTOR: Option = None; foreach_interrupt! { (RTC, rtc, $block:ident, WKUP, $irq:ident) => { #[interrupt] unsafe fn $irq() { - Executor::on_wakeup_irq(); + EXECUTOR.as_mut().unwrap().on_wakeup_irq(); } }; } +#[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) { - crate::interrupt::typelevel::RTC_WKUP::unpend(); - unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; - - EXTI.rtsr(0).modify(|w| w.set_line(22, true)); - EXTI.imr(0).modify(|w| w.set_line(22, true)); - - unsafe { RTC = Some(rtc) }; -} - -pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { - unsafe { RTC }.unwrap().start_wakeup_alarm(requested_duration) -} - -pub fn stop_wakeup_alarm() -> RtcInstant { - unsafe { RTC }.unwrap().stop_wakeup_alarm() + unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) } /// Thread mode executor, using WFE/SEV. @@ -57,54 +45,58 @@ pub fn stop_wakeup_alarm() -> RtcInstant { pub struct Executor { inner: raw::Executor, not_send: PhantomData<*mut ()>, + scb: SCB, + time_driver: &'static RtcDriver, } impl Executor { /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(THREAD_PENDER as *mut ()), - not_send: PhantomData, + pub fn take() -> &'static mut Self { + unsafe { + assert!(EXECUTOR.is_none()); + + EXECUTOR = Some(Self { + inner: raw::Executor::new(THREAD_PENDER as *mut ()), + not_send: PhantomData, + scb: cortex_m::Peripherals::steal().SCB, + time_driver: get_driver(), + }); + + EXECUTOR.as_mut().unwrap() } } - unsafe fn on_wakeup_irq() { - info!("on wakeup irq"); + unsafe fn on_wakeup_irq(&mut self) { + trace!("low power: on wakeup irq"); - cortex_m::asm::bkpt(); + self.time_driver.resume_time(); + trace!("low power: resume time"); } - fn time_until_next_alarm(&self) -> Duration { - Duration::from_secs(3) + pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { + self.time_driver.set_rtc(rtc); + + rtc.enable_wakeup_line(); + + trace!("low power: stop with rtc configured"); } - fn get_scb() -> SCB { - unsafe { cortex_m::Peripherals::steal() }.SCB - } - - fn configure_pwr(&self) { - trace!("configure_pwr"); + fn configure_pwr(&mut self) { + trace!("low power: configure_pwr"); + self.scb.clear_sleepdeep(); if !low_power_ready() { + trace!("low power: configure_pwr: low power not ready"); return; } - let time_until_next_alarm = self.time_until_next_alarm(); - if time_until_next_alarm < THRESHOLD { + if self.time_driver.pause_time().is_err() { + trace!("low power: configure_pwr: time driver failed to pause"); return; } - trace!("low power stop required"); - - critical_section::with(|_| { - trace!("executor: set wakeup alarm..."); - - start_wakeup_alarm(time_until_next_alarm); - - trace!("low power wait for rtc ready..."); - - Self::get_scb().set_sleepdeep(); - }); + trace!("low power: enter stop..."); + self.scb.set_sleepdeep(); } /// Run the executor. @@ -126,11 +118,11 @@ impl Executor { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner()); loop { unsafe { - self.inner.poll(); + EXECUTOR.as_mut().unwrap().inner.poll(); self.configure_pwr(); asm!("wfe"); }; diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs new file mode 100644 index 00000000..de27130f --- /dev/null +++ b/embassy-stm32/src/rcc/bd.rs @@ -0,0 +1,176 @@ +#[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))] +impl From 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, + } + } +} + +pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; + +#[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))] +#[allow(dead_code)] +type Bdcr = crate::pac::rcc::regs::Bdcr; + +#[cfg(any(rtc_v2l0, rtc_v2l1))] +#[allow(dead_code)] +type Bdcr = crate::pac::rcc::regs::Csr; + +#[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(f: impl FnOnce(&mut Bdcr) -> R) -> R { + #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1, rtc_v2l0))] + 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_v3u5)))] + { + cr.modify(|w| w.set_dbp(true)); + while !cr.read().dbp() {} + } + + #[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 + } + + #[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, lsi: bool, lse: Option) { + if lsi { + #[cfg(rtc_v3u5)] + let csr = crate::pac::RCC.bdcr(); + + #[cfg(not(rtc_v3u5))] + let csr = crate::pac::RCC.csr(); + + // Disable backup domain write protection + Self::modify(|_| {}); + + #[cfg(not(any(rcc_wb, rcc_wba)))] + csr.modify(|w| w.set_lsion(true)); + + #[cfg(any(rcc_wb, rcc_wba))] + csr.modify(|w| w.set_lsi1on(true)); + + #[cfg(not(any(rcc_wb, rcc_wba)))] + while !csr.read().lsirdy() {} + + #[cfg(any(rcc_wb, rcc_wba))] + while !csr.read().lsi1rdy() {} + } + + if let Some(lse_drive) = lse { + 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() {} + } + + match clock_source { + RtcClockSource::LSI => assert!(lsi), + RtcClockSource::LSE => assert!(&lse.is_some()), + _ => {} + }; + + if clock_source == RtcClockSource::NOCLOCK { + // disable it + Self::modify(|w| { + #[cfg(not(rcc_wba))] + w.set_rtcen(false); + w.set_rtcsel(clock_source); + }); + } else { + // check if it's already enabled and in the source we want. + let reg = Self::read(); + let ok = reg.rtcsel() == clock_source; + #[cfg(not(rcc_wba))] + let ok = ok & reg.rtcen(); + + // if not, configure it. + if !ok { + #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + #[cfg(not(any(rcc_l0, rcc_l1)))] + Self::modify(|w| w.set_bdrst(true)); + + Self::modify(|w| { + // Reset + #[cfg(not(any(rcc_l0, rcc_l1)))] + w.set_bdrst(false); + + #[cfg(not(rcc_wba))] + w.set_rtcen(true); + w.set_rtcsel(clock_source); + + // 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()); + }); + } + } + } +} diff --git a/embassy-stm32/src/rcc/bus.rs b/embassy-stm32/src/rcc/bus.rs new file mode 100644 index 00000000..495cf7fe --- /dev/null +++ b/embassy-stm32/src/rcc/bus.rs @@ -0,0 +1,56 @@ +use core::ops::Div; + +#[allow(unused_imports)] +use crate::pac::rcc; +pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler}; +use crate::time::Hertz; + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: AHBPrescaler) -> Self::Output { + let divisor = match rhs { + AHBPrescaler::DIV1 => 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, + #[cfg(not(rcc_wba))] + AHBPrescaler::DIV64 => 64, + #[cfg(not(rcc_wba))] + AHBPrescaler::DIV128 => 128, + #[cfg(not(rcc_wba))] + AHBPrescaler::DIV256 => 256, + #[cfg(not(rcc_wba))] + AHBPrescaler::DIV512 => 512, + _ => unreachable!(), + }; + Hertz(self.0 / divisor) + } +} + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: APBPrescaler) -> Self::Output { + let divisor = match rhs { + APBPrescaler::DIV1 => 1, + APBPrescaler::DIV2 => 2, + APBPrescaler::DIV4 => 4, + APBPrescaler::DIV8 => 8, + APBPrescaler::DIV16 => 16, + _ => unreachable!(), + }; + Hertz(self.0 / divisor) + } +} diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 6a932634..8f45e7c0 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -1,4 +1,4 @@ -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw}; use crate::pac::{FLASH, RCC}; @@ -58,8 +58,8 @@ impl Default for Config { fn default() -> Config { Config { mux: ClockSrc::HSI(HSIPrescaler::NotDivided), - ahb_pre: AHBPrescaler::NotDivided, - apb_pre: APBPrescaler::NotDivided, + ahb_pre: AHBPrescaler::DIV1, + apb_pre: APBPrescaler::DIV1, } } } @@ -151,20 +151,21 @@ pub(crate) unsafe fn init(config: Config) { } 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, + AHBPrescaler::DIV1 => 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, + _ => unreachable!(), }; let ahb_freq = sys_clk / ahb_div; 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); diff --git a/embassy-stm32/src/rcc/common.rs b/embassy-stm32/src/rcc/common.rs deleted file mode 100644 index 62736a43..00000000 --- a/embassy-stm32/src/rcc/common.rs +++ /dev/null @@ -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 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 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 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 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 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 for u8 { - fn from(val: APBPrescaler) -> u8 { - match val { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 0x04, - APBPrescaler::Div4 => 0x05, - APBPrescaler::Div8 => 0x06, - APBPrescaler::Div16 => 0x07, - } - } -} diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs index b6200231..081c0c76 100644 --- a/embassy-stm32/src/rcc/f1.rs +++ b/embassy-stm32/src/rcc/f1.rs @@ -163,8 +163,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)); @@ -184,6 +184,6 @@ pub(crate) unsafe fn init(config: Config) { apb1_tim: Hertz(pclk1 * timer_mul1), apb2_tim: Hertz(pclk2 * timer_mul2), ahb1: Hertz(hclk), - adc: Hertz(adcclk), + adc: Some(Hertz(adcclk)), }); } diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index ec4ed99b..44de5bf1 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -1,11 +1,13 @@ use core::convert::TryFrom; use core::ops::{Div, Mul}; -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw}; use crate::pac::{FLASH, RCC}; +use crate::rcc::bd::BackupDomain; use crate::rcc::{set_freqs, Clocks}; +use crate::rtc::RtcClockSource; use crate::time::Hertz; /// HSI speed @@ -201,7 +203,20 @@ pub struct PLLClocks { pub pll48_freq: Hertz, } -pub use super::common::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 { @@ -209,7 +224,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 { @@ -230,7 +245,7 @@ impl VoltageScale { None } } - VoltageScale::Scale2 => { + VoltageScale::Range2 => { if ahb_freq <= 18_000_000 { Some(Latency::WS0) } else if ahb_freq <= 36_000_000 { @@ -249,7 +264,7 @@ impl VoltageScale { None } } - VoltageScale::Scale1 => { + VoltageScale::Range1 => { if ahb_freq <= 24_000_000 { Some(Latency::WS0) } else if ahb_freq <= 48_000_000 { @@ -264,7 +279,7 @@ impl VoltageScale { None } } - VoltageScale::Scale0 => { + VoltageScale::Range0 => { if ahb_freq <= 30_000_000 { Some(Latency::WS0) } else if ahb_freq <= 60_000_000 { @@ -288,6 +303,9 @@ pub struct Config { pub pll_mux: PLLSrc, pub pll: PLLConfig, pub mux: ClockSrc, + pub rtc: Option, + pub lsi: bool, + pub lse: Option, pub voltage: VoltageScale, pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, @@ -302,11 +320,14 @@ impl Default for Config { hsi: true, pll_mux: PLLSrc::HSI, pll: PLLConfig::default(), - voltage: VoltageScale::Scale3, + voltage: VoltageScale::Range3, mux: ClockSrc::HSI, - ahb_pre: AHBPrescaler::NotDivided, - apb1_pre: APBPrescaler::NotDivided, - apb2_pre: APBPrescaler::NotDivided, + rtc: None, + lsi: false, + lse: None, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, } } } @@ -379,7 +400,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)) @@ -389,7 +410,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)) @@ -414,6 +435,12 @@ pub(crate) unsafe fn init(config: Config) { RCC.cr().modify(|w| w.set_hsion(false)); } + BackupDomain::configure_ls( + config.rtc.unwrap_or(RtcClockSource::NOCLOCK), + config.lsi, + config.lse.map(|_| Default::default()), + ); + set_freqs(Clocks { sys: sys_clk, ahb1: ahb_freq, diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs index 321270a7..630dbd4f 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -1,5 +1,7 @@ +#[cfg(rcc_f3)] +use crate::pac::adccommon::vals::Ckmode; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}; +use crate::pac::rcc::vals::{Adcpres, Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -10,6 +12,82 @@ pub const HSI_FREQ: Hertz = Hertz(8_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(40_000); +impl From 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 for Ckmode { + fn from(value: AdcClockSource) -> Self { + match value { + AdcClockSource::BusDiv1 => Ckmode::SYNCDIV1, + AdcClockSource::BusDiv2 => Ckmode::SYNCDIV2, + AdcClockSource::BusDiv4 => Ckmode::SYNCDIV4, + _ => unreachable!(), + } + } +} + +#[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, + 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, + Self::BusDiv2 => 2, + Self::BusDiv4 => 4, + _ => unreachable!(), + } + } +} + +#[derive(Default)] +pub enum HrtimClockSource { + #[default] + BusClk, + PllClk, +} + /// Clocks configutation #[non_exhaustive] #[derive(Default)] @@ -36,9 +114,20 @@ pub struct Config { /// - The System clock frequency is either 48MHz or 72MHz /// - APB1 clock has a minimum frequency of 10MHz pub pll48: bool, + #[cfg(rcc_f3)] + /// ADC clock setup + /// - For AHB, a psc of 4 or less must be used + pub adc: Option, + #[cfg(rcc_f3)] + /// ADC clock setup + /// - For AHB, a psc of 4 or less must be used + pub adc34: Option, + #[cfg(stm32f334)] + pub hrtim: HrtimClockSource, } // Information required to setup the PLL clock +#[derive(Clone, Copy)] struct PllConfig { pll_src: Pllsrc, pll_mul: Pllmul, @@ -170,6 +259,65 @@ pub(crate) unsafe fn init(config: Config) { }) }); + #[cfg(rcc_f3)] + let adc = config.adc.map(|adc| { + if !adc.is_bus() { + RCC.cfgr2().modify(|w| { + // Make sure that we're using the PLL + pll_config.unwrap(); + w.set_adc12pres(adc.into()); + + 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) + }) + } + }); + + #[cfg(all(rcc_f3, adc3_common))] + let adc34 = config.adc.map(|adc| { + if !adc.is_bus() { + RCC.cfgr2().modify(|w| { + // Make sure that we're using the PLL + pll_config.unwrap(); + w.set_adc12pres(adc.into()); + + Hertz(sysclk / adc as u32) + }) + } else { + crate::pac::ADC3_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) + }) + } + }); + + #[cfg(stm32f334)] + let hrtim = match config.hrtim { + // Must be configured after the bus is ready, otherwise it won't work + HrtimClockSource::BusClk => None, + HrtimClockSource::PllClk => { + use crate::pac::rcc::vals::Timsw; + + // Make sure that we're using the PLL + pll_config.unwrap(); + assert!((pclk2 == sysclk) || (pclk2 * 2 == sysclk)); + + RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL)); + + Some(Hertz(sysclk * 2)) + } + }; + set_freqs(Clocks { sys: Hertz(sysclk), apb1: Hertz(pclk1), @@ -177,6 +325,14 @@ pub(crate) unsafe fn init(config: Config) { apb1_tim: Hertz(pclk1 * timer_mul1), apb2_tim: Hertz(pclk2 * timer_mul2), ahb1: Hertz(hclk), + #[cfg(rcc_f3)] + adc: adc, + #[cfg(all(rcc_f3, adc3_common))] + adc34: adc34, + #[cfg(all(rcc_f3, not(adc3_common)))] + adc34: None, + #[cfg(stm32f334)] + hrtim: hrtim, }); } @@ -201,9 +357,9 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { // Calculates the Multiplier and the Divisor to arrive at // the required System clock from PLL source frequency let get_mul_div = |sysclk, pllsrcclk| { - let common_div = gcd(sysclk, pllsrcclk); - let mut multiplier = sysclk / common_div; - let mut divisor = pllsrcclk / common_div; + let bus_div = gcd(sysclk, pllsrcclk); + let mut multiplier = sysclk / bus_div; + let mut divisor = pllsrcclk / bus_div; // Minimum PLL multiplier is two if multiplier == 1 { multiplier *= 2; diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 72bdbd5d..2c027ebe 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -3,13 +3,12 @@ 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::{FLASH, PWR, RCC}; +use crate::rcc::bd::{BackupDomain, RtcClockSource}; use crate::rcc::{set_freqs, Clocks}; -use crate::rtc::{Rtc, RtcClockSource}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -38,6 +37,8 @@ pub struct Config { pub pll48: bool, pub rtc: Option, + pub lsi: bool, + pub lse: Option, } #[cfg(stm32f410)] @@ -360,8 +361,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; @@ -501,20 +500,15 @@ pub(crate) unsafe fn init(config: Config) { }) }); - match config.rtc { - Some(RtcClockSource::LSI) => { - RCC.csr().modify(|w| w.set_lsion(true)); - while !RCC.csr().read().lsirdy() {} - } - _ => {} - } - - config.rtc.map(|clock_source| { - Rtc::set_clock_source(clock_source); - }); + BackupDomain::configure_ls( + config.rtc.unwrap_or(RtcClockSource::NOCLOCK), + config.lsi, + config.lse.map(|_| Default::default()), + ); let rtc = match config.rtc { Some(RtcClockSource::LSI) => Some(LSI_FREQ), + Some(RtcClockSource::LSE) => Some(config.lse.unwrap()), _ => None, }; @@ -539,6 +533,7 @@ pub(crate) unsafe fn init(config: Config) { pllsai: plls.pllsaiclk.map(Hertz), rtc: rtc, + rtc_hse: None, }); } diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs index 85cb9c66..f32559e2 100644 --- a/embassy-stm32/src/rcc/f7.rs +++ b/embassy-stm32/src/rcc/f7.rs @@ -1,7 +1,7 @@ -use super::sealed::RccPeripheral; use crate::pac::pwr::vals::Vos; use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; +use crate::rcc::bd::{BackupDomain, RtcClockSource}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -23,6 +23,9 @@ pub struct Config { pub pclk2: Option, pub pll48: bool, + pub rtc: Option, + pub lsi: bool, + pub lse: Option, } fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48clk: bool) -> PllResults { @@ -111,8 +114,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 +213,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 +263,18 @@ pub(crate) unsafe fn init(config: Config) { }) }); + BackupDomain::configure_ls( + config.rtc.unwrap_or(RtcClockSource::NOCLOCK), + config.lsi, + config.lse.map(|_| Default::default()), + ); + + let rtc = match config.rtc { + Some(RtcClockSource::LSI) => Some(LSI_FREQ), + Some(RtcClockSource::LSE) => Some(config.lse.unwrap()), + _ => None, + }; + set_freqs(Clocks { sys: Hertz(sysclk), apb1: Hertz(pclk1), @@ -278,6 +288,8 @@ pub(crate) unsafe fn init(config: Config) { ahb3: Hertz(hclk), pll48: plls.pll48clk.map(Hertz), + + rtc, }); } diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index bf2d5199..7f0a2c7f 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -1,4 +1,4 @@ -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; @@ -186,8 +186,8 @@ impl Default for Config { fn default() -> Config { Config { mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided), - ahb_pre: AHBPrescaler::NotDivided, - apb_pre: APBPrescaler::NotDivided, + ahb_pre: AHBPrescaler::DIV1, + apb_pre: APBPrescaler::DIV1, low_power_run: false, } } @@ -377,7 +377,7 @@ pub(crate) unsafe fn init(config: Config) { let ahb_freq = Hertz(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.0, ahb_freq.0), pre => { let pre: Ppre = pre.into(); let pre: u8 = 1 << (pre.to_bits() - 3); diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index dff04023..41bebc91 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,8 +1,8 @@ use stm32_metapac::flash::vals::Latency; -use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; +use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw}; use stm32_metapac::FLASH; -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; use crate::pac::{PWR, RCC}; use crate::rcc::sealed::RccPeripheral; use crate::rcc::{set_freqs, Clocks}; @@ -14,6 +14,29 @@ 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 { @@ -238,59 +261,29 @@ pub struct Pll { pub div_r: Option, } -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, - } +fn ahb_div(ahb: AHBPrescaler) -> u32 { + match ahb { + AHBPrescaler::DIV1 => 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, + _ => unreachable!(), } } -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 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 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, - } +fn apb_div(apb: APBPrescaler) -> u32 { + match apb { + APBPrescaler::DIV1 => 1, + APBPrescaler::DIV2 => 2, + APBPrescaler::DIV4 => 4, + APBPrescaler::DIV8 => 8, + APBPrescaler::DIV16 => 16, + _ => unreachable!(), } } @@ -327,6 +320,8 @@ pub struct Config { pub pll: Option, /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. pub clock_48mhz_src: Option, + pub adc12_clock_source: AdcClockSource, + pub adc345_clock_source: AdcClockSource, } /// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator. @@ -340,12 +335,14 @@ 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(), } } } @@ -485,22 +482,22 @@ pub(crate) unsafe fn init(config: Config) { }); let ahb_freq: u32 = match config.ahb_pre { - AHBPrescaler::NotDivided => sys_clk, - pre => sys_clk / pre.div(), + AHBPrescaler::DIV1 => sys_clk, + pre => sys_clk / ahb_div(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(); + let freq = ahb_freq / apb_div(pre); (freq, freq * 2) } }; 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(); + let freq = ahb_freq / apb_div(pre); (freq, freq * 2) } }; @@ -549,6 +546,29 @@ 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())); + + 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)), + }; + + 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)), + }; + if config.low_power_run { assert!(sys_clk <= 2_000_000); PWR.cr1().modify(|w| w.set_lpr(true)); @@ -562,5 +582,7 @@ pub(crate) unsafe fn init(config: Config) { apb1_tim: Hertz(apb1_tim_freq), apb2: Hertz(apb2_freq), apb2_tim: Hertz(apb2_tim_freq), + adc: adc12_ck, + adc34: adc345_ck, }); } diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs new file mode 100644 index 00000000..43e8db22 --- /dev/null +++ b/embassy-stm32/src/rcc/h.rs @@ -0,0 +1,772 @@ +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; +pub use crate::pac::rcc::vals::Ckpersel as PerClockSource; +use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre}; +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); + +/// LSI speed +pub const LSI_FREQ: Hertz = Hertz(32_000); + +const VCO_RANGE: RangeInclusive = 150_000_000..=420_000_000; +#[cfg(any(stm32h5, pwr_h7rm0455))] +const VCO_WIDE_RANGE: RangeInclusive = 128_000_000..=560_000_000; +#[cfg(pwr_h7rm0468)] +const VCO_WIDE_RANGE: RangeInclusive = 192_000_000..=836_000_000; +#[cfg(any(pwr_h7rm0399, pwr_h7rm0433))] +const VCO_WIDE_RANGE: RangeInclusive = 192_000_000..=960_000_000; + +pub use super::bus::{AHBPrescaler, 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, +} + +#[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). 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, + /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. + pub divq: Option, + /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. + pub divr: Option, +} + +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 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, + pub hse: Option, + pub csi: bool, + pub hsi48: bool, + pub sys: Sysclk, + + #[cfg(stm32h7)] + pub pll_src: PllSource, + + pub pll1: Option, + pub pll2: Option, + #[cfg(any(rcc_h5, stm32h7))] + pub pll3: Option, + + 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, +} + +impl Default for Config { + fn default() -> Self { + Self { + hsi: Some(Hsi::Mhz64), + hse: None, + 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, + } + } +} + +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); + + #[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: adc, + }); +} + +struct PllInput { + hsi: Option, + hse: Option, + csi: Option, + #[cfg(stm32h7)] + source: PllSource, +} + +struct PllOutput { + p: Option, + #[allow(dead_code)] + q: Option, + #[allow(dead_code)] + r: Option, +} + +fn init_pll(num: usize, config: Option, 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, 0)); + #[cfg(stm32h5)] + 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); + + #[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.0) { + Pllvcosel::MEDIUMVCO + } else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk.0) { + Pllvcosel::WIDEVCO + } else { + panic!("pll vco_clk out of range: {} mhz", vco_clk.0) + }; + + 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 + }); + + #[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 - 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 + #[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 {} +} diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs deleted file mode 100644 index 2e72b193..00000000 --- a/embassy-stm32/src/rcc/h5.rs +++ /dev/null @@ -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::common::{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, - /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. - pub divq: Option, - /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. - pub divr: Option, -} - -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 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, - pub hse: Option, - pub csi: bool, - pub hsi48: bool, - pub sys: Sysclk, - - pub pll1: Option, - pub pll2: Option, - #[cfg(rcc_h5)] - pub pll3: Option, - - 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

+ 'd, - _pin: impl Peripheral

> + '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, - hse: Option, - csi: Option, -} - -struct PllOutput { - p: Option, - #[allow(dead_code)] - q: Option, - #[allow(dead_code)] - r: Option, -} - -fn init_pll(num: usize, config: Option, 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 {} -} diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs deleted file mode 100644 index 7fb4fb95..00000000 --- a/embassy-stm32/src/rcc/h7.rs +++ /dev/null @@ -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::common::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, - pub hsi_ck: Option, - pub hsi48_ck: Option, - pub lsi_ck: Option, - pub per_ck: Option, - pub hse_ck: Option, - pub pll1_p_ck: Option, - pub pll1_q_ck: Option, - pub pll1_r_ck: Option, - pub pll2_p_ck: Option, - pub pll2_q_ck: Option, - pub pll2_r_ck: Option, - pub pll3_p_ck: Option, - pub pll3_q_ck: Option, - pub pll3_r_ck: Option, - pub timx_ker_ck: Option, - pub timy_ker_ck: Option, - pub adc_ker_ck: Option, - pub sys_ck: Hertz, - pub c_ck: Hertz, -} - -/// Configuration of the core clocks -#[non_exhaustive] -#[derive(Default)] -pub struct Config { - pub hse: Option, - pub bypass_hse: bool, - pub sys_ck: Option, - pub per_ck: Option, - pub hclk: Option, - pub pclk1: Option, - pub pclk2: Option, - pub pclk3: Option, - pub pclk4: Option, - 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, -) -> (u32, u8, u8, Option) { - 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

+ 'd, - pin: impl Peripheral

> + 'd, - source: impl McoSource, - 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, - pub q_ck: Option, - pub r_ck: Option, - } - - 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, Option, Option) { - 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) - } - } - } -} diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs index 46b58ca7..7358be31 100644 --- a/embassy-stm32/src/rcc/l0.rs +++ b/embassy-stm32/src/rcc/l0.rs @@ -1,8 +1,11 @@ -pub use super::common::{AHBPrescaler, APBPrescaler}; +use super::bd::BackupDomain; +pub use super::bus::{AHBPrescaler, APBPrescaler}; +use super::RtcClockSource; +pub use crate::pac::pwr::vals::Vos as VoltageScale; 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::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -135,6 +138,10 @@ pub struct Config { pub apb2_pre: APBPrescaler, #[cfg(crs)] pub enable_hsi48: bool, + pub rtc: Option, + pub lse: Option, + pub lsi: bool, + pub voltage_scale: VoltageScale, } impl Default for Config { @@ -142,16 +149,25 @@ impl Default for Config { fn default() -> Config { Config { mux: ClockSrc::MSI(MSIRange::default()), - ahb_pre: AHBPrescaler::NotDivided, - apb1_pre: APBPrescaler::NotDivided, - apb2_pre: APBPrescaler::NotDivided, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, #[cfg(crs)] enable_hsi48: false, + rtc: None, + lse: None, + lsi: false, + voltage_scale: VoltageScale::RANGE1, } } } 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 @@ -231,6 +247,28 @@ pub(crate) unsafe fn init(config: Config) { } }; + BackupDomain::configure_ls( + config.rtc.unwrap_or(RtcClockSource::NOCLOCK), + config.lsi, + config.lse.map(|_| Default::default()), + ); + + let wait_states = match config.voltage_scale { + VoltageScale::RANGE1 => match sys_clk { + ..=16_000_000 => 0, + _ => 1, + }, + VoltageScale::RANGE2 => match sys_clk { + ..=8_000_000 => 0, + _ => 1, + }, + VoltageScale::RANGE3 => 0, + _ => unreachable!(), + }; + FLASH.acr().modify(|w| { + w.set_latency(wait_states != 0); + }); + RCC.cfgr().modify(|w| { w.set_sw(sw); w.set_hpre(config.ahb_pre.into()); @@ -239,7 +277,7 @@ pub(crate) unsafe fn init(config: Config) { }); let ahb_freq: u32 = match config.ahb_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: Hpre = pre.into(); let pre = 1 << (pre.to_bits() as u32 - 7); @@ -248,7 +286,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb1_freq, apb1_tim_freq) = match config.apb1_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); @@ -258,7 +296,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb2_freq, apb2_tim_freq) = match config.apb2_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); @@ -269,13 +307,6 @@ pub(crate) unsafe fn init(config: Config) { #[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)); diff --git a/embassy-stm32/src/rcc/l1.rs b/embassy-stm32/src/rcc/l1.rs index bdfc5b87..90524fb3 100644 --- a/embassy-stm32/src/rcc/l1.rs +++ b/embassy-stm32/src/rcc/l1.rs @@ -1,4 +1,4 @@ -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -138,9 +138,9 @@ impl Default for Config { fn default() -> Config { Config { mux: ClockSrc::MSI(MSIRange::default()), - ahb_pre: AHBPrescaler::NotDivided, - apb1_pre: APBPrescaler::NotDivided, - apb2_pre: APBPrescaler::NotDivided, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, } } } @@ -240,7 +240,7 @@ pub(crate) unsafe fn init(config: Config) { }); let ahb_freq: u32 = match config.ahb_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: Hpre = pre.into(); let pre = 1 << (pre.to_bits() as u32 - 7); @@ -249,7 +249,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb1_freq, apb1_tim_freq) = match config.apb1_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); @@ -259,7 +259,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb2_freq, apb2_tim_freq) = match config.apb2_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); diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index b34b8caa..6f1f7458 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -2,15 +2,15 @@ use core::marker::PhantomData; use embassy_hal_internal::into_ref; use stm32_metapac::rcc::regs::Cfgr; -use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; +use stm32_metapac::rcc::vals::{Mcopre, Mcosel}; -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; -use crate::pac::{FLASH, PWR, RCC}; +use crate::pac::{FLASH, RCC}; +use crate::rcc::bd::{BackupDomain, RtcClockSource}; use crate::rcc::{set_freqs, Clocks}; -use crate::rtc::{Rtc, RtcClockSource as RCS}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -241,6 +241,8 @@ pub struct Config { #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] pub hsi48: bool, pub rtc_mux: RtcClockSource, + pub lse: Option, + pub lsi: bool, } impl Default for Config { @@ -248,22 +250,19 @@ impl Default for Config { fn default() -> Config { Config { mux: ClockSrc::MSI(MSIRange::Range6), - ahb_pre: AHBPrescaler::NotDivided, - apb1_pre: APBPrescaler::NotDivided, - apb2_pre: APBPrescaler::NotDivided, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, pllsai1: None, #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] hsi48: false, - rtc_mux: RtcClockSource::LSI32, + rtc_mux: RtcClockSource::LSI, + lsi: true, + lse: None, } } } -pub enum RtcClockSource { - LSE32, - LSI32, -} - pub enum McoClock { DIV1, DIV2, @@ -410,37 +409,7 @@ pub(crate) unsafe fn init(config: Config) { while RCC.cfgr().read().sws() != Sw::MSI {} } - RCC.apb1enr1().modify(|w| w.set_pwren(true)); - - match config.rtc_mux { - RtcClockSource::LSE32 => { - // 1. Unlock the backup domain - PWR.cr1().modify(|w| w.set_dbp(true)); - - // 2. Setup the LSE - RCC.bdcr().modify(|w| { - // Enable LSE - w.set_lseon(true); - // Max drive strength - // TODO: should probably be settable - w.set_lsedrv(Lsedrv::HIGH); - }); - - // Wait until LSE is running - while !RCC.bdcr().read().lserdy() {} - - Rtc::set_clock_source(RCS::LSE); - } - RtcClockSource::LSI32 => { - // Turn on the internal 32 kHz LSI oscillator - RCC.csr().modify(|w| w.set_lsion(true)); - - // Wait until LSI is running - while !RCC.csr().read().lsirdy() {} - - Rtc::set_clock_source(RCS::LSI); - } - } + BackupDomain::configure_ls(config.rtc_mux, config.lsi, config.lse.map(|_| Default::default())); let (sys_clk, sw) = match config.mux { ClockSrc::MSI(range) => { @@ -451,7 +420,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_msirgsel(true); w.set_msion(true); - if let RtcClockSource::LSE32 = config.rtc_mux { + if let RtcClockSource::LSE = config.rtc_mux { // If LSE is enabled, enable calibration of MSI w.set_msipllen(true); } else { @@ -609,7 +578,7 @@ pub(crate) unsafe fn init(config: Config) { }); let ahb_freq: u32 = match config.ahb_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: Hpre = pre.into(); let pre = 1 << (pre.to_bits() as u32 - 7); @@ -618,7 +587,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb1_freq, apb1_tim_freq) = match config.apb1_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); @@ -628,7 +597,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb2_freq, apb2_tim_freq) = match config.apb2_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); @@ -637,8 +606,6 @@ pub(crate) unsafe fn init(config: Config) { } }; - RCC.apb1enr1().modify(|w| w.set_pwren(true)); - set_freqs(Clocks { sys: Hertz(sys_clk), ahb1: Hertz(ahb_freq), diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs index a85e1488..652bdcb7 100644 --- a/embassy-stm32/src/rcc/l5.rs +++ b/embassy-stm32/src/rcc/l5.rs @@ -1,6 +1,6 @@ use stm32_metapac::PWR; -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -238,9 +238,9 @@ impl Default for Config { fn default() -> Config { Config { mux: ClockSrc::MSI(MSIRange::Range6), - ahb_pre: AHBPrescaler::NotDivided, - apb1_pre: APBPrescaler::NotDivided, - apb2_pre: APBPrescaler::NotDivided, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, pllsai1: None, hsi48: false, } @@ -317,11 +317,6 @@ pub(crate) unsafe fn init(config: Config) { let freq = (src_freq / prediv.to_div() * mul.to_mul()) / div.to_div(); - #[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))] - assert!(freq <= 120_000_000); - #[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))] - assert!(freq <= 80_000_000); - RCC.pllcfgr().write(move |w| { w.set_plln(mul.into()); w.set_pllm(prediv.into()); @@ -407,7 +402,7 @@ pub(crate) unsafe fn init(config: Config) { }); let ahb_freq: u32 = match config.ahb_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: Hpre = pre.into(); let pre = 1 << (pre.to_bits() as u32 - 7); @@ -416,7 +411,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb1_freq, apb1_tim_freq) = match config.apb1_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); @@ -426,7 +421,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb2_freq, apb2_tim_freq) = match config.apb2_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); diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs new file mode 100644 index 00000000..2453ed82 --- /dev/null +++ b/embassy-stm32/src/rcc/mco.rs @@ -0,0 +1,71 @@ +use core::marker::PhantomData; + +use embassy_hal_internal::into_ref; + +use crate::gpio::sealed::AFType; +use crate::gpio::Speed; +pub use crate::pac::rcc::vals::{Mco1 as Mco1Source, Mco2 as Mco2Source}; +use crate::pac::RCC; +use crate::{peripherals, Peripheral}; + +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, Mco1Source, set_mco1, set_mco1pre); +impl_peri!(MCO2, Mco2Source, set_mco2, set_mco2pre); + +pub struct Mco<'d, T: McoInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: McoInstance> Mco<'d, T> { + /// Create a new MCO instance. + /// + /// `prescaler` must be between 1 and 15. + pub fn new( + _peri: impl Peripheral

+ 'd, + pin: impl Peripheral

> + 'd, + source: T::Source, + prescaler: u8, + ) -> Self { + into_ref!(pin); + + assert!( + 1 <= prescaler && prescaler <= 15, + "Mco prescaler must be between 1 and 15. Refer to the reference manual for more information." + ); + + critical_section::with(|_| unsafe { + T::apply_clock_settings(source, prescaler); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_speed(Speed::VeryHigh); + }); + + Self { phantom: PhantomData } + } +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 3c75923e..9ccf2ac4 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -1,11 +1,17 @@ #![macro_use] -pub mod common; - use core::mem::MaybeUninit; +pub use crate::rcc::bd::RtcClockSource; use crate::time::Hertz; +pub(crate) mod bd; +mod bus; +#[cfg(any(stm32h5, stm32h7))] +mod mco; +#[cfg(any(stm32h5, stm32h7))] +pub use mco::*; + #[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")] @@ -15,20 +21,30 @@ use crate::time::Hertz; #[cfg_attr(rcc_c0, path = "c0.rs")] #[cfg_attr(rcc_g0, path = "g0.rs")] #[cfg_attr(rcc_g4, path = "g4.rs")] -#[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")] +#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab), path = "h.rs")] #[cfg_attr(rcc_l0, path = "l0.rs")] #[cfg_attr(rcc_l1, path = "l1.rs")] #[cfg_attr(rcc_l4, path = "l4.rs")] #[cfg_attr(rcc_l5, path = "l5.rs")] #[cfg_attr(rcc_u5, path = "u5.rs")] #[cfg_attr(rcc_wb, path = "wb.rs")] +#[cfg_attr(rcc_wba, path = "wba.rs")] #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] -#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] mod _version; pub use _version::*; #[cfg(feature = "low-power")] use atomic_polyfill::{AtomicU32, Ordering}; +// Model Clock Configuration +// +// pub struct Clocks { +// hse: Option, +// hsi: bool, +// lse: Option, +// lsi: bool, +// rtc: RtcSource, +// } + #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Clocks { @@ -41,16 +57,18 @@ pub struct Clocks { pub apb2: Hertz, #[cfg(not(any(rcc_c0, rcc_g0)))] pub apb2_tim: Hertz, - #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))] + #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5))] pub apb3: Hertz, #[cfg(any(rcc_h7, rcc_h7ab))] pub apb4: Hertz, + #[cfg(any(rcc_wba))] + pub apb7: Hertz, // AHB pub ahb1: Hertz, #[cfg(any( 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, - rcc_wl5, rcc_wle + rcc_wba, rcc_wl5, rcc_wle ))] pub ahb2: Hertz, #[cfg(any( @@ -58,7 +76,7 @@ pub struct Clocks { rcc_wle ))] pub ahb3: Hertz, - #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] + #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_wba))] pub ahb4: Hertz, #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] @@ -70,15 +88,22 @@ pub struct Clocks { #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] pub pllsai: Option, - #[cfg(stm32f1)] - pub adc: Hertz, - - #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] + #[cfg(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3, rcc_g4))] pub adc: Option, - #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] - /// Set only if the lsi or lse is configured + #[cfg(any(rcc_f3, rcc_g4))] + pub adc34: Option, + + #[cfg(stm32f334)] + pub hrtim: Option, + + #[cfg(any(rcc_wb, rcc_f4, rcc_f410, rcc_f7))] + /// Set only if the lsi or lse is configured, indicates stop is supported pub rtc: Option, + + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] + /// Set if the hse is configured, indicates stop is not supported + pub rtc_hse: Option, } #[cfg(feature = "low-power")] @@ -86,6 +111,8 @@ static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0); #[cfg(feature = "low-power")] pub fn low_power_ready() -> bool { + trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst)); + CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 } diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index b5feeb0c..d9a53128 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -1,6 +1,6 @@ use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw}; -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -11,7 +11,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); -pub use super::common::VoltageScale; +pub use crate::pac::pwr::vals::Vos as VoltageScale; #[derive(Copy, Clone)] pub enum ClockSrc { @@ -119,53 +119,13 @@ impl Into for PllM { } } -impl Into for AHBPrescaler { - fn into(self) -> u8 { - match self { - AHBPrescaler::NotDivided => 1, - AHBPrescaler::Div2 => 0x08, - AHBPrescaler::Div4 => 0x09, - AHBPrescaler::Div8 => 0x0a, - AHBPrescaler::Div16 => 0x0b, - AHBPrescaler::Div64 => 0x0c, - AHBPrescaler::Div128 => 0x0d, - AHBPrescaler::Div256 => 0x0e, - AHBPrescaler::Div512 => 0x0f, - } - } -} - -impl Default for AHBPrescaler { - fn default() -> Self { - AHBPrescaler::NotDivided - } -} - -impl Default for APBPrescaler { - fn default() -> Self { - APBPrescaler::NotDivided - } -} - -impl Into for APBPrescaler { - fn into(self) -> u8 { - match self { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 0x04, - APBPrescaler::Div4 => 0x05, - APBPrescaler::Div8 => 0x06, - APBPrescaler::Div16 => 0x07, - } - } -} - impl Into for ClockSrc { fn into(self) -> Sw { match self { ClockSrc::MSI(..) => Sw::MSIS, ClockSrc::HSE(..) => Sw::HSE, ClockSrc::HSI16 => Sw::HSI16, - ClockSrc::PLL1R(..) => Sw::PLL1R, + ClockSrc::PLL1R(..) => Sw::PLL1_R, } } } @@ -239,10 +199,10 @@ impl Default for Config { fn default() -> Self { Self { mux: ClockSrc::MSI(MSIRange::default()), - ahb_pre: Default::default(), - apb1_pre: Default::default(), - apb2_pre: Default::default(), - apb3_pre: Default::default(), + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, + apb3_pre: APBPrescaler::DIV1, hsi48: false, } } @@ -326,12 +286,12 @@ pub(crate) unsafe fn init(config: Config) { } // TODO make configurable - let power_vos = VoltageScale::Scale3; + let power_vos = VoltageScale::RANGE3; // states and programming delay let wait_states = match power_vos { - // VOS 0 range VCORE 1.26V - 1.40V - VoltageScale::Scale0 => { + // VOS 1 range VCORE 1.26V - 1.40V + VoltageScale::RANGE1 => { if sys_clk < 32_000_000 { 0 } else if sys_clk < 64_000_000 { @@ -344,8 +304,8 @@ pub(crate) unsafe fn init(config: Config) { 4 } } - // VOS 1 range VCORE 1.15V - 1.26V - VoltageScale::Scale1 => { + // VOS 2 range VCORE 1.15V - 1.26V + VoltageScale::RANGE2 => { if sys_clk < 30_000_000 { 0 } else if sys_clk < 60_000_000 { @@ -356,8 +316,8 @@ pub(crate) unsafe fn init(config: Config) { 3 } } - // VOS 2 range VCORE 1.05V - 1.15V - VoltageScale::Scale2 => { + // VOS 3 range VCORE 1.05V - 1.15V + VoltageScale::RANGE3 => { if sys_clk < 24_000_000 { 0 } else if sys_clk < 48_000_000 { @@ -366,8 +326,8 @@ pub(crate) unsafe fn init(config: Config) { 2 } } - // VOS 3 range VCORE 0.95V - 1.05V - VoltageScale::Scale3 => { + // VOS 4 range VCORE 0.95V - 1.05V + VoltageScale::RANGE4 => { if sys_clk < 12_000_000 { 0 } else { @@ -395,7 +355,7 @@ pub(crate) unsafe fn init(config: Config) { }); let ahb_freq: u32 = match config.ahb_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: u8 = pre.into(); let pre = 1 << (pre as u32 - 7); @@ -404,7 +364,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + APBPrescaler::DIV1 => (ahb_freq, ahb_freq), pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); @@ -414,7 +374,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + APBPrescaler::DIV1 => (ahb_freq, ahb_freq), pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); @@ -424,7 +384,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + APBPrescaler::DIV1 => (ahb_freq, ahb_freq), pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index ae708b37..ee45a342 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,6 +1,6 @@ -pub use super::common::{AHBPrescaler, APBPrescaler}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; +use crate::rcc::bd::{BackupDomain, RtcClockSource}; use crate::rcc::Clocks; -use crate::rtc::{Rtc, RtcClockSource}; use crate::time::{khz, mhz, Hertz}; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, @@ -108,6 +108,7 @@ pub struct Pll { pub struct Config { pub hse: Option, pub lse: Option, + pub lsi: bool, pub sys: Sysclk, pub mux: Option, pub pll48: Option, @@ -135,7 +136,8 @@ pub const WPAN_DEFAULT: Config = Config { prediv: 2, }), pll48: None, - rtc: None, + rtc: Some(RtcClockSource::LSE), + lsi: false, pll: Some(Pll { mul: 12, @@ -145,11 +147,11 @@ pub const WPAN_DEFAULT: Config = Config { }), pllsai: None, - ahb1_pre: AHBPrescaler::NotDivided, - ahb2_pre: AHBPrescaler::Div2, - ahb3_pre: AHBPrescaler::NotDivided, - apb1_pre: APBPrescaler::NotDivided, - apb2_pre: APBPrescaler::NotDivided, + ahb1_pre: AHBPrescaler::DIV1, + ahb2_pre: AHBPrescaler::DIV2, + ahb3_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, }; impl Default for Config { @@ -164,12 +166,13 @@ impl Default for Config { pll: None, pllsai: None, rtc: None, + lsi: false, - ahb1_pre: AHBPrescaler::NotDivided, - ahb2_pre: AHBPrescaler::NotDivided, - ahb3_pre: AHBPrescaler::NotDivided, - apb1_pre: APBPrescaler::NotDivided, - apb2_pre: APBPrescaler::NotDivided, + ahb1_pre: AHBPrescaler::DIV1, + ahb2_pre: AHBPrescaler::DIV1, + ahb3_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, } } } @@ -209,7 +212,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { }; let ahb1_clk = match config.ahb1_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: u8 = pre.into(); let pre = 1u32 << (pre as u32 - 7); @@ -218,7 +221,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { }; let ahb2_clk = match config.ahb2_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: u8 = pre.into(); let pre = 1u32 << (pre as u32 - 7); @@ -227,7 +230,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { }; let ahb3_clk = match config.ahb3_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: u8 = pre.into(); let pre = 1u32 << (pre as u32 - 7); @@ -236,7 +239,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { }; let (apb1_clk, apb1_tim_clk) = match config.apb1_pre { - APBPrescaler::NotDivided => (ahb1_clk, ahb1_clk), + APBPrescaler::DIV1 => (ahb1_clk, ahb1_clk), pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); @@ -246,7 +249,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { }; let (apb2_clk, apb2_tim_clk) = match config.apb2_pre { - APBPrescaler::NotDivided => (ahb1_clk, ahb1_clk), + APBPrescaler::DIV1 => (ahb1_clk, ahb1_clk), pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); @@ -271,11 +274,11 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { apb1_tim: apb1_tim_clk, apb2_tim: apb2_tim_clk, rtc: rtc_clk, + rtc_hse: None, } } pub(crate) fn configure_clocks(config: &Config) { - let pwr = crate::pac::PWR; let rcc = crate::pac::RCC; let needs_hsi = if let Some(pll_mux) = &config.mux { @@ -292,29 +295,13 @@ pub(crate) fn configure_clocks(config: &Config) { while !rcc.cr().read().hsirdy() {} } - let needs_lsi = if let Some(rtc_mux) = &config.rtc { - *rtc_mux == RtcClockSource::LSI - } else { - false - }; + rcc.cfgr().modify(|w| w.set_stopwuck(true)); - if needs_lsi { - rcc.csr().modify(|w| w.set_lsi1on(true)); - - while !rcc.csr().read().lsi1rdy() {} - } - - match &config.lse { - Some(_) => { - rcc.cfgr().modify(|w| w.set_stopwuck(true)); - - pwr.cr1().modify(|w| w.set_dbp(true)); - pwr.cr1().modify(|w| w.set_dbp(true)); - - rcc.bdcr().modify(|w| w.set_lseon(true)); - } - _ => {} - } + BackupDomain::configure_ls( + config.rtc.unwrap_or(RtcClockSource::NOCLOCK), + config.lsi, + config.lse.map(|_| Default::default()), + ); match &config.hse { Some(hse) => { @@ -374,6 +361,4 @@ pub(crate) fn configure_clocks(config: &Config) { w.set_c2hpre(config.ahb2_pre.into()); w.set_shdhpre(config.ahb3_pre.into()); }); - - config.rtc.map(|clock_source| Rtc::set_clock_source(clock_source)); } diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs new file mode 100644 index 00000000..c5d7ab62 --- /dev/null +++ b/embassy-stm32/src/rcc/wba.rs @@ -0,0 +1,154 @@ +use stm32_metapac::rcc::vals::{Pllsrc, Sw}; + +use crate::pac::{FLASH, RCC}; +use crate::rcc::{set_freqs, Clocks}; +use crate::time::Hertz; + +/// HSI speed +pub const HSI_FREQ: Hertz = Hertz(16_000_000); + +/// LSI speed +pub const LSI_FREQ: Hertz = Hertz(32_000); + +pub use crate::pac::pwr::vals::Vos as VoltageScale; +pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler}; + +#[derive(Copy, Clone)] +pub enum ClockSrc { + HSE(Hertz), + HSI16, +} + +#[derive(Clone, Copy, Debug)] +pub enum PllSrc { + HSE(Hertz), + HSI16, +} + +impl Into for PllSrc { + fn into(self) -> Pllsrc { + match self { + PllSrc::HSE(..) => Pllsrc::HSE32, + PllSrc::HSI16 => Pllsrc::HSI16, + } + } +} + +impl Into for ClockSrc { + fn into(self) -> Sw { + match self { + ClockSrc::HSE(..) => Sw::HSE32, + ClockSrc::HSI16 => Sw::HSI16, + } + } +} + +#[derive(Copy, Clone)] +pub struct Config { + pub mux: ClockSrc, + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, + pub apb7_pre: APBPrescaler, +} + +impl Default for Config { + fn default() -> Self { + Self { + mux: ClockSrc::HSI16, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, + apb7_pre: APBPrescaler::DIV1, + } + } +} + +pub(crate) unsafe fn init(config: Config) { + let sys_clk = match config.mux { + ClockSrc::HSE(freq) => { + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + + freq + } + ClockSrc::HSI16 => { + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + HSI_FREQ + } + }; + + // TODO make configurable + let power_vos = VoltageScale::RANGE1; + + // states and programming delay + let wait_states = match power_vos { + VoltageScale::RANGE1 => match sys_clk.0 { + ..=32_000_000 => 0, + ..=64_000_000 => 1, + ..=96_000_000 => 2, + ..=100_000_000 => 3, + _ => 4, + }, + VoltageScale::RANGE2 => match sys_clk.0 { + ..=8_000_000 => 0, + ..=16_000_000 => 1, + _ => 2, + }, + }; + + FLASH.acr().modify(|w| { + w.set_latency(wait_states); + }); + + RCC.cfgr1().modify(|w| { + w.set_sw(config.mux.into()); + }); + + RCC.cfgr2().modify(|w| { + w.set_hpre(config.ahb_pre.into()); + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + + RCC.cfgr3().modify(|w| { + w.set_ppre7(config.apb7_pre.into()); + }); + + 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) + } + }; + let (apb7_freq, _apb7_tim_freq) = match config.apb7_pre { + APBPrescaler::DIV1 => (ahb_freq, ahb_freq), + pre => { + let freq = ahb_freq / pre; + (freq, freq * 2u32) + } + }; + + set_freqs(Clocks { + sys: sys_clk, + ahb1: ahb_freq, + ahb2: ahb_freq, + ahb4: ahb_freq, + apb1: apb1_freq, + apb2: apb2_freq, + apb7: apb7_freq, + apb1_tim: apb1_tim_freq, + apb2_tim: apb2_tim_freq, + }); +} diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index f1dd2bd7..6643d278 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -1,8 +1,9 @@ -pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; -use crate::pac::pwr::vals::Dbp; -use crate::pac::{FLASH, PWR, RCC}; +pub use super::bus::{AHBPrescaler, APBPrescaler}; +pub use crate::pac::pwr::vals::Vos as VoltageScale; +use crate::pac::rcc::vals::Adcsel; +use crate::pac::{FLASH, RCC}; +use crate::rcc::bd::{BackupDomain, RtcClockSource}; use crate::rcc::{set_freqs, Clocks}; -use crate::rtc::{Rtc, RtcClockSource as RCS}; use crate::time::Hertz; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, @@ -75,9 +76,9 @@ impl MSIRange { fn vos(&self) -> VoltageScale { if self > &MSIRange::Range8 { - VoltageScale::Scale0 + VoltageScale::RANGE1 } else { - VoltageScale::Scale1 + VoltageScale::RANGE2 } } } @@ -107,6 +108,29 @@ impl Into for MSIRange { } } +#[derive(Clone, Copy)] +pub enum AdcClockSource { + HSI16, + PLLPCLK, + SYSCLK, +} + +impl AdcClockSource { + pub fn adcsel(&self) -> Adcsel { + match self { + AdcClockSource::HSI16 => Adcsel::HSI16, + AdcClockSource::PLLPCLK => Adcsel::PLLPCLK, + AdcClockSource::SYSCLK => Adcsel::SYSCLK, + } + } +} + +impl Default for AdcClockSource { + fn default() -> Self { + Self::HSI16 + } +} + /// Clocks configutation pub struct Config { pub mux: ClockSrc, @@ -114,9 +138,10 @@ pub struct Config { pub shd_ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, - pub enable_lsi: bool, - pub enable_rtc_apb: bool, pub rtc_mux: RtcClockSource, + pub lse: Option, + pub lsi: bool, + pub adc_clock_source: AdcClockSource, } impl Default for Config { @@ -124,22 +149,18 @@ impl Default for Config { fn default() -> Config { Config { mux: ClockSrc::MSI(MSIRange::default()), - ahb_pre: AHBPrescaler::NotDivided, - shd_ahb_pre: AHBPrescaler::NotDivided, - apb1_pre: APBPrescaler::NotDivided, - apb2_pre: APBPrescaler::NotDivided, - enable_lsi: false, - enable_rtc_apb: false, - rtc_mux: RtcClockSource::LSI32, + ahb_pre: AHBPrescaler::DIV1, + shd_ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, + rtc_mux: RtcClockSource::LSI, + lsi: true, + lse: None, + adc_clock_source: AdcClockSource::default(), } } } -pub enum RtcClockSource { - LSE32, - LSI32, -} - #[repr(u8)] pub enum Lsedrv { Low = 0, @@ -150,13 +171,13 @@ pub enum Lsedrv { pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw, vos) = match config.mux { - ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Scale1), - ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Scale0), + ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::RANGE2), + ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::RANGE1), ClockSrc::MSI(range) => (range.freq(), 0x00, range.vos()), }; let ahb_freq: u32 = match config.ahb_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: u8 = pre.into(); let pre = 1 << (pre as u32 - 7); @@ -165,7 +186,7 @@ pub(crate) unsafe fn init(config: Config) { }; let shd_ahb_freq: u32 = match config.shd_ahb_pre { - AHBPrescaler::NotDivided => sys_clk, + AHBPrescaler::DIV1 => sys_clk, pre => { let pre: u8 = pre.into(); let pre = 1 << (pre as u32 - 7); @@ -174,7 +195,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + APBPrescaler::DIV1 => (ahb_freq, ahb_freq), pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); @@ -184,7 +205,7 @@ pub(crate) unsafe fn init(config: Config) { }; let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + APBPrescaler::DIV1 => (ahb_freq, ahb_freq), pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); @@ -196,16 +217,17 @@ pub(crate) unsafe fn init(config: Config) { // Adjust flash latency let flash_clk_src_freq: u32 = shd_ahb_freq; let ws = match vos { - VoltageScale::Scale0 => match flash_clk_src_freq { + VoltageScale::RANGE1 => match flash_clk_src_freq { 0..=18_000_000 => 0b000, 18_000_001..=36_000_000 => 0b001, _ => 0b010, }, - VoltageScale::Scale1 => match flash_clk_src_freq { + VoltageScale::RANGE2 => match flash_clk_src_freq { 0..=6_000_000 => 0b000, 6_000_001..=12_000_000 => 0b001, _ => 0b010, }, + _ => unreachable!(), }; FLASH.acr().modify(|w| { @@ -214,35 +236,8 @@ pub(crate) unsafe fn init(config: Config) { while FLASH.acr().read().latency() != ws {} - match config.rtc_mux { - RtcClockSource::LSE32 => { - // 1. Unlock the backup domain - PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); - - // 2. Setup the LSE - RCC.bdcr().modify(|w| { - // Enable LSE - w.set_lseon(true); - // Max drive strength - // TODO: should probably be settable - w.set_lsedrv(Lsedrv::High as u8); //---// PAM - should not be commented - }); - - // Wait until LSE is running - while !RCC.bdcr().read().lserdy() {} - - Rtc::set_clock_source(RCS::LSE); - } - RtcClockSource::LSI32 => { - // Turn on the internal 32 kHz LSI oscillator - RCC.csr().modify(|w| w.set_lsion(true)); - - // Wait until LSI is running - while !RCC.csr().read().lsirdy() {} - - Rtc::set_clock_source(RCS::LSI); - } - } + // Enables the LSI if configured + BackupDomain::configure_ls(config.rtc_mux, config.lsi, config.lse.map(|_| Default::default())); match config.mux { ClockSrc::HSI16 => { @@ -266,7 +261,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_msirange(range.into()); w.set_msion(true); - if let RtcClockSource::LSE32 = config.rtc_mux { + if let RtcClockSource::LSE = config.rtc_mux { // If LSE is enabled, enable calibration of MSI w.set_msipllen(true); } else { @@ -277,16 +272,8 @@ pub(crate) unsafe fn init(config: Config) { } } - if config.enable_rtc_apb { - // enable peripheral clock for communication - crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // read to allow the pwr clock to enable - crate::pac::PWR.cr1().read(); - } - RCC.extcfgr().modify(|w| { - if config.shd_ahb_pre == AHBPrescaler::NotDivided { + if config.shd_ahb_pre == AHBPrescaler::DIV1 { w.set_shdhpre(0); } else { w.set_shdhpre(config.shd_ahb_pre.into()); @@ -295,24 +282,15 @@ pub(crate) unsafe fn init(config: Config) { RCC.cfgr().modify(|w| { w.set_sw(sw.into()); - if config.ahb_pre == AHBPrescaler::NotDivided { - w.set_hpre(0); - } else { - w.set_hpre(config.ahb_pre.into()); - } + w.set_hpre(config.ahb_pre); w.set_ppre1(config.apb1_pre.into()); w.set_ppre2(config.apb2_pre.into()); }); - // TODO: switch voltage range + // ADC clock MUX + RCC.ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel())); - if config.enable_lsi { - let csr = RCC.csr().read(); - if !csr.lsion() { - RCC.csr().modify(|w| w.set_lsion(true)); - while !RCC.csr().read().lsirdy() {} - } - } + // TODO: switch voltage range set_freqs(Clocks { sys: Hertz(sys_clk), diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 30816e43..0979dce8 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -119,7 +119,31 @@ impl<'d, T: Instance> Rng<'d, T> { pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { for chunk in dest.chunks_mut(4) { - let bits = T::regs().sr().read(); + let mut bits = T::regs().sr().read(); + if !bits.seis() && !bits.ceis() && !bits.drdy() { + // wait for interrupt + poll_fn(|cx| { + // quick check to avoid registration if already done. + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + return Poll::Ready(()); + } + RNG_WAKER.register(cx.waker()); + T::regs().cr().modify(|reg| reg.set_ie(true)); + // Need to check condition **after** `register` to avoid a race + // condition that would result in lost notifications. + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + // Re-read the status register after wait. + bits = T::regs().sr().read() + } if bits.seis() { // in case of noise-source or seed error we try to recover here // but we must not use the data in DR and we return an error @@ -143,26 +167,6 @@ impl<'d, T: Instance> Rng<'d, T> { for (dest, src) in chunk.iter_mut().zip(random_word.to_be_bytes().iter()) { *dest = *src } - } else { - // wait for interrupt - poll_fn(|cx| { - // quick check to avoid registration if already done. - let bits = T::regs().sr().read(); - if bits.drdy() || bits.seis() || bits.ceis() { - return Poll::Ready(()); - } - RNG_WAKER.register(cx.waker()); - T::regs().cr().modify(|reg| reg.set_ie(true)); - // Need to check condition **after** `register` to avoid a race - // condition that would result in lost notifications. - let bits = T::regs().sr().read(); - if bits.drdy() || bits.seis() || bits.ceis() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; } } diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index a9c48d88..3efe9be5 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -89,7 +89,7 @@ pub enum DayOfWeek { #[cfg(feature = "chrono")] impl From for DayOfWeek { fn from(weekday: Weekday) -> Self { - day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap() + day_of_week_from_u8(weekday.num_days_from_monday() as u8).unwrap() } } diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index a6102077..73b78f25 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -1,7 +1,17 @@ //! RTC peripheral abstraction mod datetime; +#[cfg(feature = "low-power")] +use core::cell::Cell; + +#[cfg(feature = "low-power")] +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +#[cfg(feature = "low-power")] +use embassy_sync::blocking_mutex::Mutex; + pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; +pub use crate::rcc::RtcClockSource; +use crate::time::Hertz; /// refer to AN4759 to compare features of RTC2 and RTC3 #[cfg_attr(any(rtc_v1), path = "v1.rs")] @@ -30,106 +40,53 @@ pub enum RtcError { NotRunning, } -/// RTC Abstraction -pub struct Rtc { - rtc_config: RtcConfig, +#[cfg(feature = "low-power")] +/// Represents an instant in time that can be substracted to compute a duration +struct RtcInstant { + second: u8, + subsecond: u16, } -#[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, -} - -#[derive(Copy, Clone, PartialEq)] -pub struct RtcConfig { - /// Asynchronous prescaler factor - /// This is the asynchronous division factor: - /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) - /// ck_apre drives the subsecond register - async_prescaler: u8, - /// Synchronous prescaler factor - /// This is the synchronous division factor: - /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1) - /// ck_spre must be 1Hz - sync_prescaler: u16, -} - -impl Default for RtcConfig { - /// LSI with prescalers assuming 32.768 kHz. - /// Raw sub-seconds in 1/256. - fn default() -> Self { - RtcConfig { - async_prescaler: 127, - sync_prescaler: 255, - } +#[cfg(all(feature = "low-power", feature = "defmt"))] +impl defmt::Format for RtcInstant { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!( + fmt, + "{}:{}", + self.second, + RTC::regs().prer().read().prediv_s() - self.subsecond, + ) } } -impl RtcConfig { - /// Set the asynchronous prescaler of RTC config - pub fn async_prescaler(mut self, prescaler: u8) -> Self { - self.async_prescaler = prescaler; - self - } +#[cfg(feature = "low-power")] +impl core::ops::Sub for RtcInstant { + type Output = embassy_time::Duration; - /// Set the synchronous prescaler of RTC config - pub fn sync_prescaler(mut self, prescaler: u16) -> Self { - self.sync_prescaler = prescaler; - self + fn sub(self, rhs: Self) -> Self::Output { + use embassy_time::{Duration, TICK_HZ}; + + let second = if self.second < rhs.second { + self.second + 60 + } else { + self.second + }; + + let psc = RTC::regs().prer().read().prediv_s() as u32; + + let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32); + let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32); + let rtc_ticks = self_ticks - other_ticks; + + Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64) } } -#[derive(Copy, Clone, Debug, PartialEq)] -#[repr(u8)] -pub enum RtcCalibrationCyclePeriod { - /// 8-second calibration period - Seconds8, - /// 16-second calibration period - Seconds16, - /// 32-second calibration period - Seconds32, +pub struct RtcTimeProvider { + _private: (), } -impl Default for RtcCalibrationCyclePeriod { - fn default() -> Self { - RtcCalibrationCyclePeriod::Seconds32 - } -} - -impl Rtc { - pub fn new(_rtc: impl Peripheral

, rtc_config: RtcConfig) -> Self { - RTC::enable_peripheral_clk(); - - let mut rtc_struct = Self { rtc_config }; - - Self::enable(); - - rtc_struct.configure(rtc_config); - rtc_struct.rtc_config = rtc_config; - - rtc_struct - } - - /// Set the datetime to a new value. - /// - /// # Errors - /// - /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. - pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> { - self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; - self.write(true, |rtc| self::datetime::write_date_time(rtc, t)); - - Ok(()) - } - +impl RtcTimeProvider { /// Return the current datetime. /// /// # Errors @@ -152,6 +109,126 @@ impl Rtc { self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) } +} + +/// RTC Abstraction +pub struct Rtc { + #[cfg(feature = "low-power")] + stop_time: Mutex>>, + #[cfg(not(feature = "low-power"))] + _private: (), +} + +#[non_exhaustive] +#[derive(Copy, Clone, PartialEq)] +pub struct RtcConfig { + /// The subsecond counter frequency; default is 256 + /// + /// A high counter frequency may impact stop power consumption + pub frequency: Hertz, +} + +impl Default for RtcConfig { + /// LSI with prescalers assuming 32.768 kHz. + /// Raw sub-seconds in 1/256. + fn default() -> Self { + RtcConfig { frequency: Hertz(256) } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum RtcCalibrationCyclePeriod { + /// 8-second calibration period + Seconds8, + /// 16-second calibration period + Seconds16, + /// 32-second calibration period + Seconds32, +} + +impl Default for RtcCalibrationCyclePeriod { + fn default() -> Self { + RtcCalibrationCyclePeriod::Seconds32 + } +} + +impl Rtc { + pub fn new(_rtc: impl Peripheral

, rtc_config: RtcConfig) -> Self { + #[cfg(any(rcc_wle, rcc_wl5, rcc_g4, rcc_g0, rtc_v2l4, rtc_v2wb))] + ::enable(); + + let mut this = Self { + #[cfg(feature = "low-power")] + stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + #[cfg(not(feature = "low-power"))] + _private: (), + }; + + let frequency = Self::frequency(); + let async_psc = ((frequency.0 / rtc_config.frequency.0) - 1) as u8; + let sync_psc = (rtc_config.frequency.0 - 1) as u16; + + this.configure(async_psc, sync_psc); + + this + } + + fn frequency() -> Hertz { + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] + let freqs = unsafe { crate::rcc::get_freqs() }; + + // Load the clock frequency from the rcc mod, if supported + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] + match freqs.rtc { + Some(hertz) => hertz, + None => freqs.rtc_hse.unwrap(), + } + + // Assume the default value, if not supported + #[cfg(not(any(rcc_wb, rcc_f4, rcc_f410)))] + Hertz(32_768) + } + + /// Acquire a [`RtcTimeProvider`] instance. + pub const fn time_provider(&self) -> RtcTimeProvider { + RtcTimeProvider { _private: () } + } + + /// Set the datetime to a new value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> { + self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; + self.write(true, |rtc| self::datetime::write_date_time(rtc, t)); + + Ok(()) + } + + #[cfg(feature = "low-power")] + /// Return the current instant. + fn instant(&self) -> RtcInstant { + let r = RTC::regs(); + let tr = r.tr().read(); + let subsecond = r.ssr().read().ss(); + let second = bcd2_to_byte((tr.st(), tr.su())); + + // Unlock the registers + r.dr().read(); + + RtcInstant { second, subsecond } + } + + /// Return the current datetime. + /// + /// # Errors + /// + /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. + pub fn now(&self) -> Result { + self.time_provider().now() + } /// Check if daylight savings time is active. pub fn get_daylight_savings(&self) -> bool { @@ -166,10 +243,6 @@ impl Rtc { }) } - pub fn get_config(&self) -> RtcConfig { - self.rtc_config - } - pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT; /// Read content of the backup register. @@ -215,12 +288,16 @@ pub(crate) mod sealed { pub trait Instance { const BACKUP_REGISTER_COUNT: usize; + #[cfg(feature = "low-power")] + const EXTI_WAKEUP_LINE: usize; + + #[cfg(feature = "low-power")] + type WakeupInterrupt: crate::interrupt::typelevel::Interrupt; + fn regs() -> Rtc { crate::pac::RTC } - fn enable_peripheral_clk() {} - /// Read content of the backup register. /// /// The registers retain their values during wakes from standby mode or system resets. They also diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index bcb127ec..4608d311 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,77 +1,21 @@ use stm32_metapac::rtc::vals::{Init, Osel, Pol}; -use super::{sealed, RtcClockSource, RtcConfig}; +use super::sealed; use crate::pac::rtc::Rtc; use crate::peripherals::RTC; use crate::rtc::sealed::Instance; -#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] -pub struct RtcInstant { - ssr: u16, - st: u8, -} - -#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] -impl RtcInstant { - pub fn now() -> Self { - // TODO: read value twice - use crate::rtc::bcd2_to_byte; - - let tr = RTC::regs().tr().read(); - let tr2 = RTC::regs().tr().read(); - let ssr = RTC::regs().ssr().read().ss(); - let ssr2 = RTC::regs().ssr().read().ss(); - - let st = bcd2_to_byte((tr.st(), tr.su())); - let st2 = bcd2_to_byte((tr2.st(), tr2.su())); - - assert!(st == st2); - assert!(ssr == ssr2); - - let _ = RTC::regs().dr().read(); - - trace!("ssr: {}", ssr); - trace!("st: {}", st); - - Self { ssr, st } - } -} - -#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] -impl core::ops::Sub for RtcInstant { - type Output = embassy_time::Duration; - - fn sub(self, rhs: Self) -> Self::Output { - use embassy_time::{Duration, TICK_HZ}; - - let st = if self.st < rhs.st { self.st + 60 } else { self.st }; - - // TODO: read prescaler - - let self_ticks = st as u32 * 256 + (255 - self.ssr as u32); - let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); - let rtc_ticks = self_ticks - other_ticks; - - trace!("self, other, rtc ticks: {}, {}, {}", self_ticks, other_ticks, rtc_ticks); - - Duration::from_ticks( - ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) - * TICK_HZ as u32) as u32 - / 256u32) as u64, - ) - } -} - #[allow(dead_code)] +#[repr(u8)] #[derive(Clone, Copy, Debug)] pub(crate) enum WakeupPrescaler { - Div2, - Div4, - Div8, - Div16, + Div2 = 2, + Div4 = 4, + Div8 = 8, + Div16 = 16, } -#[cfg(any(stm32wb, stm32f4))] +#[cfg(any(stm32wb, stm32f4, stm32l0))] impl From for crate::pac::rtc::vals::Wucksel { fn from(val: WakeupPrescaler) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -85,7 +29,7 @@ impl From for crate::pac::rtc::vals::Wucksel { } } -#[cfg(any(stm32wb, stm32f4))] +#[cfg(any(stm32wb, stm32f4, stm32l0))] impl From for WakeupPrescaler { fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -100,17 +44,6 @@ impl From for WakeupPrescaler { } } -impl From for u32 { - fn from(val: WakeupPrescaler) -> Self { - match val { - WakeupPrescaler::Div2 => 2, - WakeupPrescaler::Div4 => 4, - WakeupPrescaler::Div8 => 8, - WakeupPrescaler::Div16 => 16, - } - } -} - #[allow(dead_code)] impl WakeupPrescaler { pub fn compute_min(val: u32) -> Self { @@ -121,158 +54,100 @@ impl WakeupPrescaler { WakeupPrescaler::Div16, ] .iter() - .skip_while(|psc| >::into(**psc) <= val) + .skip_while(|psc| **psc as u32 <= val) .next() .unwrap_or(&WakeupPrescaler::Div16) } } impl super::Rtc { - fn unlock_registers() { - #[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))] - let cr = crate::pac::PWR.cr1(); - - // TODO: Missing from PAC for l0 and f0? - #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] - { - if !cr.read().dbp() { - cr.modify(|w| w.set_dbp(true)); - while !cr.read().dbp() {} - } - } - } - - #[allow(dead_code)] - #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] - /// start the wakeup alarm and return the actual duration of the alarm - /// the actual duration will be the closest value possible that is less - /// than the requested duration. - /// - /// note: this api is exposed for testing purposes until low power is implemented. - /// it is not intended to be public - pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant { + #[cfg(feature = "low-power")] + /// start the wakeup alarm and wtih a duration that is as close to but less than + /// the requested duration, and record the instant the wakeup alarm was started + pub(crate) fn start_wakeup_alarm( + &self, + requested_duration: embassy_time::Duration, + cs: critical_section::CriticalSection, + ) { use embassy_time::{Duration, TICK_HZ}; - use crate::rcc::get_freqs; + // Panic if the rcc mod knows we're not using low-power rtc + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] + unsafe { crate::rcc::get_freqs() }.rtc.unwrap(); - let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; - - let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ; + let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); + let rtc_hz = Self::frequency().0 as u64; + let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); - // adjust the rtc ticks to the prescaler - let rtc_ticks = rtc_ticks / (>::into(prescaler) as u64); - let rtc_ticks = if rtc_ticks >= u16::MAX as u64 { - u16::MAX - 1 - } else { - rtc_ticks as u16 - }; - - let duration = Duration::from_ticks( - rtc_ticks as u64 * TICK_HZ * (>::into(prescaler) as u64) / rtc_hz, - ); - - trace!("set wakeup timer for {} ms", duration.as_millis()); + // adjust the rtc ticks to the prescaler and subtract one rtc tick + let rtc_ticks = rtc_ticks / prescaler as u64; + let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16; self.write(false, |regs| { - regs.cr().modify(|w| w.set_wutie(true)); - regs.cr().modify(|w| w.set_wute(false)); regs.isr().modify(|w| w.set_wutf(false)); while !regs.isr().read().wutwf() {} regs.cr().modify(|w| w.set_wucksel(prescaler.into())); + regs.wutr().write(|w| w.set_wut(rtc_ticks)); regs.cr().modify(|w| w.set_wute(true)); + regs.cr().modify(|w| w.set_wutie(true)); }); - RtcInstant::now() + trace!( + "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", + Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), + prescaler as u32, + rtc_ticks, + self.instant(), + ); + + assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none()) } - #[allow(dead_code)] - #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] - /// stop the wakeup alarm and return the time remaining - /// - /// note: this api is exposed for testing purposes until low power is implemented. - /// it is not intended to be public - pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { - trace!("disable wakeup timer..."); + #[cfg(feature = "low-power")] + /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` + /// was called, otherwise none + pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { + use crate::interrupt::typelevel::Interrupt; + + trace!("rtc: stop wakeup alarm at {}", self.instant()); self.write(false, |regs| { + regs.cr().modify(|w| w.set_wutie(false)); regs.cr().modify(|w| w.set_wute(false)); regs.isr().modify(|w| w.set_wutf(false)); + + crate::pac::EXTI + .pr(0) + .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + + ::WakeupInterrupt::unpend(); }); - RtcInstant::now() - } - - #[allow(dead_code)] - pub(crate) fn set_clock_source(clock_source: RtcClockSource) { - #[cfg(not(rtc_v2wb))] - use stm32_metapac::rcc::vals::Rtcsel; - - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - let cr = crate::pac::RCC.bdcr(); - #[cfg(any(rtc_v2l0, rtc_v2l1))] - let cr = crate::pac::RCC.csr(); - - Self::unlock_registers(); - - cr.modify(|w| { - // Select RTC source - #[cfg(not(rtc_v2wb))] - w.set_rtcsel(Rtcsel::from_bits(clock_source as u8)); - #[cfg(rtc_v2wb)] - w.set_rtcsel(clock_source as u8); - }); - } - - pub(super) fn enable() { - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - let reg = crate::pac::RCC.bdcr().read(); - #[cfg(any(rtc_v2l0, rtc_v2l1))] - let reg = crate::pac::RCC.csr().read(); - - #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - if !reg.rtcen() { - Self::unlock_registers(); - - #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - let cr = crate::pac::RCC.bdcr(); - #[cfg(any(rtc_v2l0, rtc_v2l1))] - let cr = crate::pac::RCC.csr(); - - cr.modify(|w| { - // Reset - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - w.set_bdrst(false); - - w.set_rtcen(true); - w.set_rtcsel(reg.rtcsel()); - - // Restore bcdr - #[cfg(any(rtc_v2l4, rtc_v2wb))] - w.set_lscosel(reg.lscosel()); - #[cfg(any(rtc_v2l4, rtc_v2wb))] - w.set_lscoen(reg.lscoen()); - - w.set_lseon(reg.lseon()); - - #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); + if let Some(stop_time) = self.stop_time.borrow(cs).take() { + Some(self.instant() - stop_time) + } else { + None } } + #[cfg(feature = "low-power")] + pub(crate) fn enable_wakeup_line(&self) { + use crate::interrupt::typelevel::Interrupt; + use crate::pac::EXTI; + + ::WakeupInterrupt::unpend(); + unsafe { ::WakeupInterrupt::enable() }; + + EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + } + /// Applies the RTC config /// It this changes the RTC clock source the time will be reset - pub(super) fn configure(&mut self, rtc_config: RtcConfig) { + pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { self.write(true, |rtc| { rtc.cr().modify(|w| { #[cfg(rtc_v2f2)] @@ -284,8 +159,8 @@ impl super::Rtc { }); rtc.prer().modify(|w| { - w.set_prediv_s(rtc_config.sync_prescaler); - w.set_prediv_a(rtc_config.async_prescaler); + w.set_prediv_s(sync_psc); + w.set_prediv_a(async_psc); }); }); } @@ -390,21 +265,17 @@ impl super::Rtc { impl sealed::Instance for crate::peripherals::RTC { const BACKUP_REGISTER_COUNT: usize = 20; - fn enable_peripheral_clk() { - #[cfg(any(rtc_v2l4, rtc_v2wb))] - { - // enable peripheral clock for communication - crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + #[cfg(all(feature = "low-power", stm32f4))] + const EXTI_WAKEUP_LINE: usize = 22; - // read to allow the pwr clock to enable - crate::pac::PWR.cr1().read(); - } - #[cfg(any(rtc_v2f2))] - { - crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true)); - crate::pac::PWR.cr().read(); - } - } + #[cfg(all(feature = "low-power", stm32l0))] + const EXTI_WAKEUP_LINE: usize = 20; + + #[cfg(all(feature = "low-power", stm32f4))] + type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; + + #[cfg(all(feature = "low-power", stm32l0))] + type WakeupInterrupt = crate::interrupt::typelevel::RTC; fn read_backup_register(rtc: &Rtc, register: usize) -> Option { if register < Self::BACKUP_REGISTER_COUNT { diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 3297303e..a6b2655d 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -1,77 +1,14 @@ use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; -use super::{sealed, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; +use super::{sealed, RtcCalibrationCyclePeriod}; use crate::pac::rtc::Rtc; use crate::peripherals::RTC; use crate::rtc::sealed::Instance; impl super::Rtc { - fn unlock_registers() { - // Unlock the backup domain - #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] - { - if !crate::pac::PWR.cr1().read().dbp() { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} - } - } - #[cfg(any(rcc_wl5, rcc_wle))] - { - use crate::pac::pwr::vals::Dbp; - - if crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); - while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} - } - } - } - - #[allow(dead_code)] - pub(crate) fn set_clock_source(clock_source: RtcClockSource) { - let clock_source = clock_source as u8; - #[cfg(not(any(rcc_wl5, rcc_wle)))] - let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source); - - Self::unlock_registers(); - - crate::pac::RCC.bdcr().modify(|w| { - // Select RTC source - w.set_rtcsel(clock_source); - }); - } - - pub(super) fn enable() { - let bdcr = crate::pac::RCC.bdcr(); - - let reg = bdcr.read(); - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - if !reg.rtcen() { - Self::unlock_registers(); - - bdcr.modify(|w| w.set_bdrst(true)); - - bdcr.modify(|w| { - // Reset - w.set_bdrst(false); - - w.set_rtcen(true); - w.set_rtcsel(reg.rtcsel()); - - // Restore bcdr - w.set_lscosel(reg.lscosel()); - w.set_lscoen(reg.lscoen()); - - w.set_lseon(reg.lseon()); - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } - } - /// Applies the RTC config /// It this changes the RTC clock source the time will be reset - pub(super) fn configure(&mut self, rtc_config: RtcConfig) { + pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { self.write(true, |rtc| { rtc.cr().modify(|w| { w.set_fmt(Fmt::TWENTYFOURHOUR); @@ -80,8 +17,8 @@ impl super::Rtc { }); rtc.prer().modify(|w| { - w.set_prediv_s(rtc_config.sync_prescaler); - w.set_prediv_a(rtc_config.async_prescaler); + w.set_prediv_s(sync_psc); + w.set_prediv_a(async_psc); }); // TODO: configuration for output pins diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 6b532363..9fb380fd 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -33,6 +33,8 @@ impl InterruptHandler { w.set_dtimeoutie(enable); w.set_dataendie(enable); + #[cfg(sdmmc_v1)] + w.set_stbiterre(enable); #[cfg(sdmmc_v2)] w.set_dabortie(enable); }); @@ -102,6 +104,8 @@ pub enum Error { BadClock, SignalingSwitchFailed, PeripheralBusy, + #[cfg(sdmmc_v1)] + StBitErr, } /// A SD command @@ -707,9 +711,15 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { + } + if status.dtimeout() { return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { return Poll::Ready(Ok(())); } Poll::Pending @@ -782,9 +792,15 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { + } + if status.dtimeout() { return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { return Poll::Ready(Ok(())); } Poll::Pending @@ -836,6 +852,8 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { w.set_dataendc(true); w.set_dbckendc(true); w.set_sdioitc(true); + #[cfg(sdmmc_v1)] + w.set_stbiterrc(true); #[cfg(sdmmc_v2)] { @@ -873,9 +891,15 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { + } + if status.dtimeout() { return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { return Poll::Ready(Ok(())); } Poll::Pending @@ -1156,9 +1180,15 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { + } + if status.dtimeout() { return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { return Poll::Ready(Ok(())); } Poll::Pending @@ -1207,9 +1237,15 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { + } + if status.dtimeout() { return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { return Poll::Ready(Ok(())); } Poll::Pending diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index e2bc8d7f..f40bce78 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -323,7 +323,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } /// Reconfigures it with the supplied config. - pub fn reconfigure(&mut self, config: Config) { + pub fn set_config(&mut self, config: Config) { let cpha = config.raw_phase(); let cpol = config.raw_polarity(); @@ -646,6 +646,8 @@ impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> { self.sck.as_ref().map(|x| x.set_as_disconnected()); self.mosi.as_ref().map(|x| x.set_as_disconnected()); self.miso.as_ref().map(|x| x.set_as_disconnected()); + + T::disable(); } } @@ -1060,6 +1062,6 @@ foreach_peripheral!( impl<'d, T: Instance, Tx, Rx> SetConfig for Spi<'d, T, Tx, Rx> { type Config = Config; fn set_config(&mut self, config: &Self::Config) { - self.reconfigure(*config); + self.set_config(*config); } } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 2622442f..5b01937f 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -14,6 +14,8 @@ use stm32_metapac::timer::regs; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; use crate::rcc::sealed::RccPeripheral; +#[cfg(feature = "low-power")] +use crate::rtc::Rtc; use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance}; use crate::{interrupt, peripherals}; @@ -130,12 +132,14 @@ impl AlarmState { } } -struct RtcDriver { +pub(crate) struct RtcDriver { /// Number of 2^15 periods elapsed since boot. period: AtomicU32, alarm_count: AtomicU8, /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. alarms: Mutex, + #[cfg(feature = "low-power")] + rtc: Mutex>>, } const ALARM_STATE_NEW: AlarmState = AlarmState::new(); @@ -144,6 +148,8 @@ embassy_time::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), alarm_count: AtomicU8::new(0), alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), + #[cfg(feature = "low-power")] + rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), }); impl RtcDriver { @@ -259,6 +265,127 @@ impl RtcDriver { let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; f(alarm.ctx.get()); } + + /* + Low-power private functions: all operate within a critical seciton + */ + + #[cfg(feature = "low-power")] + /// Compute the approximate amount of time until the next alarm + fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { + let now = self.now() + 32; + + embassy_time::Duration::from_ticks( + self.alarms + .borrow(cs) + .iter() + .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) + .min() + .unwrap_or(u64::MAX), + ) + } + + #[cfg(feature = "low-power")] + /// Add the given offset to the current time + fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { + let offset = offset.as_ticks(); + let cnt = T::regs_gp16().cnt().read().cnt() as u32; + let period = self.period.load(Ordering::SeqCst); + + // Correct the race, if it exists + let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { + period + 1 + } else { + period + }; + + // Normalize to the full overflow + let period = (period / 2) * 2; + + // Add the offset + let period = period + 2 * (offset / u16::MAX as u64) as u32; + let cnt = cnt + (offset % u16::MAX as u64) as u32; + + let (cnt, period) = if cnt > u16::MAX as u32 { + (cnt - u16::MAX as u32, period + 2) + } else { + (cnt, period) + }; + + let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; + + self.period.store(period, Ordering::SeqCst); + T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); + + // Now, recompute all alarms + for i in 0..ALARM_COUNT { + let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; + let alarm = self.get_alarm(cs, alarm_handle); + + self.set_alarm(alarm_handle, alarm.timestamp.get()); + } + } + + #[cfg(feature = "low-power")] + /// Stop the wakeup alarm, if enabled, and add the appropriate offset + fn stop_wakeup_alarm(&self, cs: CriticalSection) { + if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { + self.add_time(offset, cs); + } + } + + /* + Low-power public functions: all create a critical section + */ + #[cfg(feature = "low-power")] + /// Set the rtc but panic if it's already been set + pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { + critical_section::with(|cs| assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none())); + } + + #[cfg(feature = "low-power")] + /// Pause the timer if ready; return err if not + pub(crate) fn pause_time(&self) -> Result<(), ()> { + critical_section::with(|cs| { + /* + If the wakeup timer is currently running, then we need to stop it and + add the elapsed time to the current time, as this will impact the result + of `time_until_next_alarm`. + */ + self.stop_wakeup_alarm(cs); + + let time_until_next_alarm = self.time_until_next_alarm(cs); + if time_until_next_alarm < embassy_time::Duration::from_millis(250) { + Err(()) + } else { + self.rtc + .borrow(cs) + .get() + .unwrap() + .start_wakeup_alarm(time_until_next_alarm, cs); + + T::regs_gp16().cr1().modify(|w| w.set_cen(false)); + + Ok(()) + } + }) + } + + #[cfg(feature = "low-power")] + /// Resume the timer with the given offset + pub(crate) fn resume_time(&self) { + if T::regs_gp16().cr1().read().cen() { + // Time isn't currently stopped + + return; + } + + critical_section::with(|cs| { + self.stop_wakeup_alarm(cs); + + T::regs_gp16().cr1().modify(|w| w.set_cen(true)); + }) + } } impl Driver for RtcDriver { @@ -329,6 +456,11 @@ impl Driver for RtcDriver { } } +#[cfg(feature = "low-power")] +pub(crate) fn get_driver() -> &'static RtcDriver { + &DRIVER +} + pub(crate) fn init() { DRIVER.init() } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 4ffb2a28..1d642ed3 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,4 +1,5 @@ pub mod complementary_pwm; +pub mod qei; pub mod simple_pwm; use stm32_metapac::timer::vals; @@ -14,6 +15,7 @@ pub mod low_level { } pub(crate) mod sealed { + use super::*; pub trait Basic16bitInstance: RccPeripheral { type Interrupt: interrupt::typelevel::Interrupt; @@ -31,10 +33,16 @@ pub(crate) mod sealed { fn clear_update_interrupt(&mut self) -> bool; fn enable_update_interrupt(&mut self, enable: bool); + + fn set_autoreload_preload(&mut self, enable: vals::Arpe); } pub trait GeneralPurpose16bitInstance: Basic16bitInstance { fn regs_gp16() -> crate::pac::timer::TimGp16; + + fn set_count_direction(&mut self, direction: vals::Dir); + + fn set_clock_division(&mut self, ckd: vals::Ckd); } pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { @@ -48,6 +56,18 @@ pub(crate) mod sealed { } pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { + fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf); + + fn clear_input_interrupt(&mut self, channel: Channel); + + fn enable_input_interrupt(&mut self, channel: Channel, enable: bool); + + fn set_input_capture_prescaler(&mut self, channel: Channel, val: u8); + + fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection); + + fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode); + /// Global output enable. Does not do anything on non-advanced timers. fn enable_outputs(&mut self, enable: bool); @@ -59,6 +79,8 @@ pub(crate) mod sealed { fn set_compare_value(&mut self, channel: Channel, value: u16); + fn get_capture_value(&mut self, channel: Channel) -> u16; + fn get_max_compare_value(&self) -> u16; } @@ -73,6 +95,18 @@ pub(crate) mod sealed { } pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance { + fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf); + + fn clear_input_interrupt(&mut self, channel: Channel); + + fn enable_input_interrupt(&mut self, channel: Channel, enable: bool); + + fn set_input_capture_prescaler(&mut self, channel: Channel, val: u8); + + fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection); + + fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode); + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); @@ -81,6 +115,8 @@ pub(crate) mod sealed { fn set_compare_value(&mut self, channel: Channel, value: u32); + fn get_capture_value(&mut self, channel: Channel) -> u32; + fn get_max_compare_value(&self) -> u32; } } @@ -104,6 +140,30 @@ impl Channel { } } +#[derive(Clone, Copy)] +pub enum InputCaptureMode { + Rising, + Falling, + BothEdges, +} + +#[derive(Clone, Copy)] +pub enum InputTISelection { + Normal, + Alternate, + TRC, +} + +impl From for stm32_metapac::timer::vals::CcmrInputCcs { + fn from(tisel: InputTISelection) -> Self { + match tisel { + InputTISelection::Normal => stm32_metapac::timer::vals::CcmrInputCcs::TI4, + InputTISelection::Alternate => stm32_metapac::timer::vals::CcmrInputCcs::TI3, + InputTISelection::TRC => stm32_metapac::timer::vals::CcmrInputCcs::TRC, + } + } +} + #[derive(Clone, Copy)] pub enum OutputCompareMode { Frozen, @@ -211,6 +271,7 @@ macro_rules! impl_basic_16bit_timer { use core::convert::TryInto; let f = frequency.0; let timer_f = Self::frequency().0; + assert!(f > 0); let pclk_ticks_per_timer_period = timer_f / f; let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into()); let arr: u16 = unwrap!((pclk_ticks_per_timer_period / (u32::from(psc) + 1)).try_into()); @@ -240,6 +301,10 @@ macro_rules! impl_basic_16bit_timer { fn enable_update_interrupt(&mut self, enable: bool) { Self::regs().dier().write(|r| r.set_uie(enable)); } + + fn set_autoreload_preload(&mut self, enable: vals::Arpe) { + Self::regs().cr1().modify(|r| r.set_arpe(enable)); + } } }; } @@ -255,6 +320,7 @@ macro_rules! impl_32bit_timer { fn set_frequency(&mut self, frequency: Hertz) { use core::convert::TryInto; let f = frequency.0; + assert!(f > 0); let timer_f = Self::frequency().0; let pclk_ticks_per_timer_period = (timer_f / f) as u64; let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); @@ -276,6 +342,59 @@ macro_rules! impl_32bit_timer { macro_rules! impl_compare_capable_16bit { ($inst:ident) => { impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) { + use sealed::GeneralPurpose16bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp16() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_icf(raw_channel % 2, icf)); + } + + fn clear_input_interrupt(&mut self, channel: Channel) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16() + .sr() + .modify(|r| r.set_ccif(channel.raw(), false)); + } + + fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16() + .dier() + .modify(|r| r.set_ccie(channel.raw(), enable)); + } + fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) { + use sealed::GeneralPurpose16bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp16() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_icpsc(raw_channel % 2, factor)); + } + + fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { + use sealed::GeneralPurpose16bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp16() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); + } + fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16().ccer().modify(|r| match mode { + InputCaptureMode::Rising => { + r.set_ccnp(channel.raw(), false); + r.set_ccp(channel.raw(), false); + } + InputCaptureMode::Falling => { + r.set_ccnp(channel.raw(), false); + r.set_ccp(channel.raw(), true); + } + InputCaptureMode::BothEdges => { + r.set_ccnp(channel.raw(), true); + r.set_ccp(channel.raw(), true); + } + }); + } fn enable_outputs(&mut self, _enable: bool) {} fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { @@ -305,6 +424,11 @@ macro_rules! impl_compare_capable_16bit { Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); } + fn get_capture_value(&mut self, channel: Channel) -> u16 { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16().ccr(channel.raw()).read().ccr() + } + fn get_max_compare_value(&self) -> u16 { use sealed::GeneralPurpose16bitInstance; Self::regs_gp16().arr().read().arr() @@ -329,6 +453,14 @@ foreach_interrupt! { fn regs_gp16() -> crate::pac::timer::TimGp16 { crate::pac::$inst } + + fn set_count_direction(&mut self, direction: vals::Dir) { + Self::regs_gp16().cr1().modify(|r| r.set_dir(direction)); + } + + fn set_clock_division(&mut self, ckd: vals::Ckd) { + Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); + } } }; @@ -343,6 +475,59 @@ foreach_interrupt! { impl GeneralPurpose32bitInstance for crate::peripherals::$inst {} impl sealed::CaptureCompare32bitInstance for crate::peripherals::$inst { + fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) { + use sealed::GeneralPurpose32bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp32() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_icf(raw_channel % 2, icf)); + } + + fn clear_input_interrupt(&mut self, channel: Channel) { + use sealed::GeneralPurpose32bitInstance; + Self::regs_gp32() + .sr() + .modify(|r| r.set_ccif(channel.raw(), false)); + } + fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) { + use sealed::GeneralPurpose32bitInstance; + Self::regs_gp32() + .dier() + .modify(|r| r.set_ccie(channel.raw(), enable)); + } + fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp32() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_icpsc(raw_channel % 2, factor)); + } + + fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp32() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); + } + + fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().ccer().modify(|r| match mode { + InputCaptureMode::Rising => { + r.set_ccnp(channel.raw(), false); + r.set_ccp(channel.raw(), false); + } + InputCaptureMode::Falling => { + r.set_ccnp(channel.raw(), false); + r.set_ccp(channel.raw(), true); + } + InputCaptureMode::BothEdges => { + r.set_ccnp(channel.raw(), true); + r.set_ccp(channel.raw(), true); + } + }); + } fn set_output_compare_mode( &mut self, channel: Channel, @@ -370,6 +555,11 @@ foreach_interrupt! { Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); } + fn get_capture_value(&mut self, channel: Channel) -> u32 { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().ccr(channel.raw()).read().ccr() + } + fn get_max_compare_value(&self) -> u32 { use crate::timer::sealed::GeneralPurpose32bitInstance; Self::regs_gp32().arr().read().arr() as u32 @@ -380,6 +570,14 @@ foreach_interrupt! { fn regs_gp16() -> crate::pac::timer::TimGp16 { unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } } + + fn set_count_direction(&mut self, direction: vals::Dir) { + Self::regs_gp16().cr1().modify(|r| r.set_dir(direction)); + } + + fn set_clock_division(&mut self, ckd: vals::Ckd) { + Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); + } } }; @@ -396,6 +594,14 @@ foreach_interrupt! { fn regs_gp16() -> crate::pac::timer::TimGp16 { unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } } + + fn set_count_direction(&mut self, direction: vals::Dir) { + Self::regs_gp16().cr1().modify(|r| r.set_dir(direction)); + } + + fn set_clock_division(&mut self, ckd: vals::Ckd) { + Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); + } } impl sealed::AdvancedControlInstance for crate::peripherals::$inst { @@ -405,6 +611,57 @@ foreach_interrupt! { } impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) { + use crate::timer::sealed::AdvancedControlInstance; + let raw_channel = channel.raw(); + Self::regs_advanced() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_icf(raw_channel % 2, icf)); + } + + fn clear_input_interrupt(&mut self, channel: Channel) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .sr() + .modify(|r| r.set_ccif(channel.raw(), false)); + } + fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .dier() + .modify(|r| r.set_ccie(channel.raw(), enable)); + } + fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) { + use crate::timer::sealed::AdvancedControlInstance; + let raw_channel = channel.raw(); + Self::regs_advanced() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_icpsc(raw_channel % 2, factor)); + } + fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { + use crate::timer::sealed::AdvancedControlInstance; + let raw_channel = channel.raw(); + Self::regs_advanced() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); + } + fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().ccer().modify(|r| match mode { + InputCaptureMode::Rising => { + r.set_ccnp(channel.raw(), false); + r.set_ccp(channel.raw(), false); + } + InputCaptureMode::Falling => { + r.set_ccnp(channel.raw(), false); + r.set_ccp(channel.raw(), true); + } + InputCaptureMode::BothEdges => { + r.set_ccnp(channel.raw(), true); + r.set_ccp(channel.raw(), true); + } + }); + } fn enable_outputs(&mut self, enable: bool) { use crate::timer::sealed::AdvancedControlInstance; let r = Self::regs_advanced(); @@ -437,6 +694,11 @@ foreach_interrupt! { .modify(|w| w.set_cce(channel.raw(), enable)); } + fn get_capture_value(&mut self, channel: Channel) -> u16 { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().ccr(channel.raw()).read().ccr() + } + fn set_compare_value(&mut self, channel: Channel, value: u16) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced() diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs new file mode 100644 index 00000000..15f2c3a7 --- /dev/null +++ b/embassy-stm32/src/timer/qei.rs @@ -0,0 +1,96 @@ +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use super::*; +use crate::gpio::sealed::AFType; +use crate::gpio::AnyPin; +use crate::Peripheral; + +pub enum Direction { + Upcounting, + Downcounting, +} + +pub struct Ch1; +pub struct Ch2; + +pub struct QeiPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +macro_rules! channel_impl { + ($new_chx:ident, $channel:ident, $pin_trait:ident) => { + impl<'d, Perip: CaptureCompare16bitInstance> QeiPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::Input); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + QeiPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + }; +} + +channel_impl!(new_ch1, Ch1, Channel1Pin); +channel_impl!(new_ch2, Ch2, Channel2Pin); + +pub struct Qei<'d, T> { + _inner: PeripheralRef<'d, T>, +} + +impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { + pub fn new(tim: impl Peripheral

+ 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { + Self::new_inner(tim) + } + + fn new_inner(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + + T::enable(); + ::reset(); + + // Configure TxC1 and TxC2 as captures + T::regs_gp16().ccmr_input(0).modify(|w| { + w.set_ccs(0, vals::CcmrInputCcs::TI4); + w.set_ccs(1, vals::CcmrInputCcs::TI4); + }); + + // enable and configure to capture on rising edge + T::regs_gp16().ccer().modify(|w| { + w.set_cce(0, true); + w.set_cce(1, true); + + w.set_ccp(0, false); + w.set_ccp(1, false); + }); + + T::regs_gp16().smcr().modify(|w| { + w.set_sms(vals::Sms::ENCODER_MODE_3); + }); + + T::regs_gp16().arr().modify(|w| w.set_arr(u16::MAX)); + T::regs_gp16().cr1().modify(|w| w.set_cen(true)); + + Self { _inner: tim } + } + + pub fn read_direction(&self) -> Direction { + match T::regs_gp16().cr1().read().dir() { + vals::Dir::DOWN => Direction::Downcounting, + vals::Dir::UP => Direction::Upcounting, + } + } + + pub fn count(&self) -> u16 { + T::regs_gp16().cnt().read().cnt() + } +} diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 596d40bf..e2d6e42a 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -114,6 +114,30 @@ pub struct BufferedUartRx<'d, T: BasicInstance> { phantom: PhantomData<&'d mut T>, } +impl<'d, T: BasicInstance> SetConfig for BufferedUart<'d, T> { + type Config = Config; + + fn set_config(&mut self, config: &Self::Config) { + unwrap!(self.set_config(config)) + } +} + +impl<'d, T: BasicInstance> SetConfig for BufferedUartRx<'d, T> { + type Config = Config; + + fn set_config(&mut self, config: &Self::Config) { + unwrap!(self.set_config(config)) + } +} + +impl<'d, T: BasicInstance> SetConfig for BufferedUartTx<'d, T> { + type Config = Config; + + fn set_config(&mut self, config: &Self::Config) { + unwrap!(self.set_config(config)) + } +} + impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( peri: impl Peripheral

+ 'd, @@ -123,7 +147,9 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, - ) -> BufferedUart<'d, T> { + ) -> Result { + // UartRx and UartTx have one refcount ea. + T::enable(); T::enable(); T::reset(); @@ -140,9 +166,11 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, - ) -> BufferedUart<'d, T> { + ) -> Result { into_ref!(cts, rts); + // UartRx and UartTx have one refcount ea. + T::enable(); T::enable(); T::reset(); @@ -166,9 +194,11 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, - ) -> BufferedUart<'d, T> { + ) -> Result { into_ref!(de); + // UartRx and UartTx have one refcount ea. + T::enable(); T::enable(); T::reset(); @@ -187,7 +217,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, - ) -> BufferedUart<'d, T> { + ) -> Result { into_ref!(_peri, rx, tx); let state = T::buffered_state(); @@ -200,7 +230,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - configure(r, &config, T::frequency(), T::KIND, true, true); + configure(r, &config, T::frequency(), T::KIND, true, true)?; r.cr1().modify(|w| { #[cfg(lpuart_v2)] @@ -213,15 +243,19 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - Self { + Ok(Self { rx: BufferedUartRx { phantom: PhantomData }, tx: BufferedUartTx { phantom: PhantomData }, - } + }) } pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) { (self.tx, self.rx) } + + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure::(config) + } } impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { @@ -298,6 +332,10 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { T::Interrupt::pend(); } } + + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure::(config) + } } impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { @@ -368,6 +406,10 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { } } } + + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure::(config) + } } impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> { @@ -382,6 +424,8 @@ impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> { T::Interrupt::disable(); } } + + T::disable(); } } @@ -397,12 +441,8 @@ impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> { T::Interrupt::disable(); } } - } -} -impl embedded_io_async::Error for Error { - fn kind(&self) -> embedded_io_async::ErrorKind { - embedded_io_async::ErrorKind::Other + T::disable(); } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 255ddfd4..9835f1ac 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -5,6 +5,7 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_embedded_hal::SetConfig; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; use futures::future::{select, Either}; @@ -12,11 +13,10 @@ use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; use crate::interrupt::typelevel::Interrupt; -#[cfg(not(any(usart_v1, usart_v2)))] #[allow(unused_imports)] +#[cfg(not(any(usart_v1, usart_v2)))] use crate::pac::usart::regs::Isr as Sr; #[cfg(any(usart_v1, usart_v2))] -#[allow(unused_imports)] use crate::pac::usart::regs::Sr; #[cfg(not(any(usart_v1, usart_v2)))] use crate::pac::usart::Lpuart as Regs; @@ -75,12 +75,14 @@ impl interrupt::typelevel::Handler for Interrupt } #[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DataBits { DataBits8, DataBits9, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Parity { ParityNone, ParityEven, @@ -88,6 +90,7 @@ pub enum Parity { } #[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum StopBits { #[doc = "1 stop bit"] STOP1, @@ -99,6 +102,14 @@ pub enum StopBits { STOP1P5, } +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ConfigError { + BaudrateTooLow, + BaudrateTooHigh, +} + #[non_exhaustive] #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Config { @@ -168,11 +179,28 @@ pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { rx: UartRx<'d, T, RxDma>, } +impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> { + type Config = Config; + + fn set_config(&mut self, config: &Self::Config) { + unwrap!(self.tx.set_config(config)); + unwrap!(self.rx.set_config(config)); + } +} + pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> { phantom: PhantomData<&'d mut T>, tx_dma: PeripheralRef<'d, TxDma>, } +impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> { + type Config = Config; + + fn set_config(&mut self, config: &Self::Config) { + unwrap!(self.set_config(config)); + } +} + pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { _peri: PeripheralRef<'d, T>, rx_dma: PeripheralRef<'d, RxDma>, @@ -181,6 +209,14 @@ pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { buffered_sr: stm32_metapac::usart::regs::Sr, } +impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> { + type Config = Config; + + fn set_config(&mut self, config: &Self::Config) { + unwrap!(self.set_config(config)); + } +} + impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. pub fn new( @@ -188,7 +224,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { tx: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { T::enable(); T::reset(); @@ -201,7 +237,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { cts: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { into_ref!(cts); T::enable(); @@ -219,22 +255,26 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { tx: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { into_ref!(_peri, tx, tx_dma); let r = T::regs(); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - configure(r, &config, T::frequency(), T::KIND, false, true); + configure(r, &config, T::frequency(), T::KIND, false, true)?; // create state once! let _s = T::state(); - Self { + Ok(Self { tx_dma, phantom: PhantomData, - } + }) + } + + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure::(config) } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> @@ -277,7 +317,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rx: impl Peripheral

> + 'd, rx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { T::enable(); T::reset(); @@ -291,7 +331,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rts: impl Peripheral

> + 'd, rx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { into_ref!(rts); T::enable(); @@ -310,14 +350,14 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rx: impl Peripheral

> + 'd, rx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { into_ref!(peri, rx, rx_dma); let r = T::regs(); rx.set_as_af(rx.af_num(), AFType::Input); - configure(r, &config, T::frequency(), T::KIND, true, false); + configure(r, &config, T::frequency(), T::KIND, true, false)?; T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -325,13 +365,17 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // create state once! let _s = T::state(); - Self { + Ok(Self { _peri: peri, rx_dma, detect_previous_overrun: config.detect_previous_overrun, #[cfg(any(usart_v1, usart_v2))] buffered_sr: stm32_metapac::usart::regs::Sr(0), - } + }) + } + + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure::(config) } #[cfg(any(usart_v1, usart_v2))] @@ -545,6 +589,13 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { unsafe { rdr(r).read_volatile() }; clear_interrupt_flags(r, sr); + if enable_idle_line_detection { + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); + } + compiler_fence(Ordering::SeqCst); let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); @@ -618,6 +669,18 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } +impl<'d, T: BasicInstance, TxDma> Drop for UartTx<'d, T, TxDma> { + fn drop(&mut self) { + T::disable(); + } +} + +impl<'d, T: BasicInstance, TxDma> Drop for UartRx<'d, T, TxDma> { + fn drop(&mut self) { + T::disable(); + } +} + impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { pub fn new( peri: impl Peripheral

+ 'd, @@ -627,7 +690,9 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { + // UartRx and UartTx have one refcount ea. + T::enable(); T::enable(); T::reset(); @@ -644,9 +709,11 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { into_ref!(cts, rts); + // UartRx and UartTx have one refcount ea. + T::enable(); T::enable(); T::reset(); @@ -669,9 +736,11 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { into_ref!(de); + // UartRx and UartTx have one refcount ea. + T::enable(); T::enable(); T::reset(); @@ -689,7 +758,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, - ) -> Self { + ) -> Result { into_ref!(peri, rx, tx, tx_dma, rx_dma); let r = T::regs(); @@ -711,7 +780,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } - configure(r, &config, T::frequency(), T::KIND, true, true); + configure(r, &config, T::frequency(), T::KIND, true, true)?; T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -719,7 +788,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { // create state once! let _s = T::state(); - Self { + Ok(Self { tx: UartTx { tx_dma, phantom: PhantomData, @@ -731,7 +800,11 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { #[cfg(any(usart_v1, usart_v2))] buffered_sr: stm32_metapac::usart::regs::Sr(0), }, - } + }) + } + + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure::(config) } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> @@ -779,7 +852,27 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } -fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: bool, enable_tx: bool) { +fn reconfigure(config: &Config) -> Result<(), ConfigError> { + T::Interrupt::disable(); + let r = T::regs(); + + let cr = r.cr1().read(); + configure(r, config, T::frequency(), T::KIND, cr.re(), cr.te())?; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Ok(()) +} + +fn configure( + r: Regs, + config: &Config, + pclk_freq: Hertz, + kind: Kind, + enable_rx: bool, + enable_tx: bool, +) -> Result<(), ConfigError> { if !enable_rx && !enable_tx { panic!("USART: At least one of RX or TX should be enabled"); } @@ -847,7 +940,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: found_brr = Some(brr); break; } - panic!("USART: baudrate too high"); + return Err(ConfigError::BaudrateTooHigh); } if brr < brr_max { @@ -859,7 +952,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: } } - let brr = found_brr.expect("USART: baudrate too low"); + let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; #[cfg(not(usart_v1))] let oversampling = if over8 { "8 bit" } else { "16 bit" }; @@ -905,12 +998,16 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: }); #[cfg(not(usart_v1))] w.set_over8(vals::Over8::from_bits(over8 as _)); + #[cfg(usart_v4)] + w.set_fifoen(true); }); #[cfg(not(usart_v1))] r.cr3().modify(|w| { w.set_onebit(config.assume_noise_free); }); + + Ok(()) } mod eh02 { @@ -1012,20 +1109,61 @@ mod eh1 { } } -#[cfg(all(feature = "unstable-traits", feature = "nightly"))] -mod eio { - use embedded_io_async::{ErrorType, Write}; +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} - use super::*; +impl embedded_io::ErrorType for Uart<'_, T, TxDma, RxDma> +where + T: BasicInstance, +{ + type Error = Error; +} - impl ErrorType for Uart<'_, T, TxDma, RxDma> - where - T: BasicInstance, - { - type Error = Error; +impl embedded_io::ErrorType for UartTx<'_, T, TxDma> +where + T: BasicInstance, +{ + type Error = Error; +} + +impl embedded_io::Write for Uart<'_, T, TxDma, RxDma> +where + T: BasicInstance, + TxDma: crate::usart::TxDma, +{ + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write(buf)?; + Ok(buf.len()) } - impl Write for Uart<'_, T, TxDma, RxDma> + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + +impl embedded_io::Write for UartTx<'_, T, TxDma> +where + T: BasicInstance, + TxDma: crate::usart::TxDma, +{ + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eio { + use super::*; + + impl embedded_io_async::Write for Uart<'_, T, TxDma, RxDma> where T: BasicInstance, TxDma: super::TxDma, @@ -1040,14 +1178,7 @@ mod eio { } } - impl ErrorType for UartTx<'_, T, TxDma> - where - T: BasicInstance, - { - type Error = Error; - } - - impl Write for UartTx<'_, T, TxDma> + impl embedded_io_async::Write for UartTx<'_, T, TxDma> where T: BasicInstance, TxDma: super::TxDma, diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index b3f57062..347aae7c 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -1,11 +1,13 @@ use core::future::poll_fn; +use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_embedded_hal::SetConfig; use embassy_hal_internal::PeripheralRef; use futures::future::{select, Either}; -use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; +use super::{clear_interrupt_flags, rdr, reconfigure, sr, BasicInstance, Config, ConfigError, Error, UartRx}; use crate::dma::ReadableRingBuffer; use crate::usart::{Regs, Sr}; @@ -14,6 +16,14 @@ pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma> { ring_buf: ReadableRingBuffer<'d, RxDma, u8>, } +impl<'d, T: BasicInstance, RxDma: super::RxDma> SetConfig for RingBufferedUartRx<'d, T, RxDma> { + type Config = Config; + + fn set_config(&mut self, config: &Self::Config) { + unwrap!(self.set_config(config)); + } +} + impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { /// Turn the `UartRx` into a buffered uart which can continously receive in the background /// without the possibility of loosing bytes. The `dma_buf` is a buffer registered to the @@ -24,12 +34,16 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { let request = self.rx_dma.request(); let opts = Default::default(); - let ring_buf = unsafe { ReadableRingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; + // Safety: we forget the struct before this function returns. + let rx_dma = unsafe { self.rx_dma.clone_unchecked() }; + let _peri = unsafe { self._peri.clone_unchecked() }; - RingBufferedUartRx { - _peri: self._peri, - ring_buf, - } + let ring_buf = unsafe { ReadableRingBuffer::new_read(rx_dma, request, rdr(T::regs()), dma_buf, opts) }; + + // Don't disable the clock + mem::forget(self); + + RingBufferedUartRx { _peri, ring_buf } } } @@ -49,6 +63,11 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD Err(err) } + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.teardown_uart(); + reconfigure::(config) + } + /// Start uart background receive fn setup_uart(&mut self) { // fence before starting DMA. @@ -186,6 +205,8 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD impl> Drop for RingBufferedUartRx<'_, T, RxDma> { fn drop(&mut self) { self.teardown_uart(); + + T::disable(); } } /// Return an error result if the Sr register has errors diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index cef19635..b24fc74e 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -264,10 +264,7 @@ impl<'d, T: Instance> Driver<'d, T> { let regs = T::regs(); #[cfg(stm32l5)] - { - crate::peripherals::PWR::enable(); - crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); - } + crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); #[cfg(pwr_h5)] crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)); diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 348f0f79..1fe010bb 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -540,10 +540,7 @@ impl<'d, T: Instance> Bus<'d, T> { impl<'d, T: Instance> Bus<'d, T> { fn init(&mut self) { #[cfg(stm32l4)] - { - crate::peripherals::PWR::enable(); - critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); - } + critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); #[cfg(stm32f7)] { @@ -618,15 +615,10 @@ impl<'d, T: Instance> Bus<'d, T> { { // Enable USB power critical_section::with(|_| { - crate::pac::RCC.ahb3enr().modify(|w| { - w.set_pwren(true); - }); - cortex_m::asm::delay(2); - crate::pac::PWR.svmcr().modify(|w| { w.set_usv(true); w.set_uvmen(true); - }); + }) }); // Wait for USB power to stabilize diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index a60f3f7c..2c53dd0f 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.3.0 - 2023-09-14 + +- switch to embedded-io 0.5 +- add api for polling channels with context +- standardise fn names on channels +- add zero-copy channel + ## 0.2.0 - 2023-04-13 - pubsub: Fix messages not getting popped when the last subscriber that needed them gets dropped. @@ -19,4 +26,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.1.0 - 2022-08-26 -- First release \ No newline at end of file +- First release diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 94d6799e..f7739f30 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-sync" -version = "0.2.0" +version = "0.3.0" edition = "2021" description = "no-std, no-alloc synchronization primitives with async support" repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 62ea1307..a512e0c4 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -471,7 +471,7 @@ where } fn lock(&self, f: impl FnOnce(&mut ChannelState) -> R) -> R { - self.inner.lock(|rc| f(&mut *rc.borrow_mut())) + self.inner.lock(|rc| f(&mut *unwrap!(rc.try_borrow_mut()))) } fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { diff --git a/embassy-sync/src/fmt.rs b/embassy-sync/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-sync/src/fmt.rs +++ b/embassy-sync/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 53d95d08..8a9f841e 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -17,3 +17,4 @@ pub mod pipe; pub mod pubsub; pub mod signal; pub mod waitqueue; +pub mod zerocopy_channel; diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index fcf056d3..72459d66 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -149,7 +149,7 @@ where { fn drop(&mut self) { self.mutex.state.lock(|s| { - let mut s = s.borrow_mut(); + let mut s = unwrap!(s.try_borrow_mut()); s.locked = false; s.waker.wake(); }) diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 21d451ea..ec0cbbf2 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -1,7 +1,8 @@ //! Async byte stream pipe. -use core::cell::RefCell; +use core::cell::{RefCell, UnsafeCell}; use core::future::Future; +use core::ops::Range; use core::pin::Pin; use core::task::{Context, Poll}; @@ -82,17 +83,6 @@ where pipe: &'p Pipe, } -impl<'p, M, const N: usize> Clone for Reader<'p, M, N> -where - M: RawMutex, -{ - fn clone(&self) -> Self { - Reader { pipe: self.pipe } - } -} - -impl<'p, M, const N: usize> Copy for Reader<'p, M, N> where M: RawMutex {} - impl<'p, M, const N: usize> Reader<'p, M, N> where M: RawMutex, @@ -110,6 +100,29 @@ where pub fn try_read(&self, buf: &mut [u8]) -> Result { self.pipe.try_read(buf) } + + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + /// + /// If no bytes are currently available to read, this function waits until at least one byte is available. + /// + /// If the reader is at end-of-file (EOF), an empty slice is returned. + pub fn fill_buf(&mut self) -> FillBufFuture<'_, M, N> { + FillBufFuture { pipe: Some(self.pipe) } + } + + /// Try returning contents of the internal buffer. + /// + /// If no bytes are currently available to read, this function returns `Err(TryReadError::Empty)`. + /// + /// If the reader is at end-of-file (EOF), an empty slice is returned. + pub fn try_fill_buf(&mut self) -> Result<&[u8], TryReadError> { + unsafe { self.pipe.try_fill_buf_with_context(None) } + } + + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + pub fn consume(&mut self, amt: usize) { + self.pipe.consume(amt) + } } /// Future returned by [`Pipe::read`] and [`Reader::read`]. @@ -138,6 +151,35 @@ where impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {} +/// Future returned by [`Pipe::fill_buf`] and [`Reader::fill_buf`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct FillBufFuture<'p, M, const N: usize> +where + M: RawMutex, +{ + pipe: Option<&'p Pipe>, +} + +impl<'p, M, const N: usize> Future for FillBufFuture<'p, M, N> +where + M: RawMutex, +{ + type Output = &'p [u8]; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let pipe = self.pipe.take().unwrap(); + match unsafe { pipe.try_fill_buf_with_context(Some(cx)) } { + Ok(buf) => Poll::Ready(buf), + Err(TryReadError::Empty) => { + self.pipe = Some(pipe); + Poll::Pending + } + } + } +} + +impl<'p, M, const N: usize> Unpin for FillBufFuture<'p, M, N> where M: RawMutex {} + /// Error returned by [`try_read`](Pipe::try_read). #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -162,67 +204,24 @@ struct PipeState { write_waker: WakerRegistration, } -impl PipeState { - const fn new() -> Self { - PipeState { - buffer: RingBuffer::new(), - read_waker: WakerRegistration::new(), - write_waker: WakerRegistration::new(), - } +#[repr(transparent)] +struct Buffer(UnsafeCell<[u8; N]>); + +impl Buffer { + unsafe fn get<'a>(&self, r: Range) -> &'a [u8] { + let p = self.0.get() as *const u8; + core::slice::from_raw_parts(p.add(r.start), r.end - r.start) } - fn clear(&mut self) { - self.buffer.clear(); - self.write_waker.wake(); - } - - fn try_read(&mut self, buf: &mut [u8]) -> Result { - self.try_read_with_context(None, buf) - } - - fn try_read_with_context(&mut self, cx: Option<&mut Context<'_>>, buf: &mut [u8]) -> Result { - if self.buffer.is_full() { - self.write_waker.wake(); - } - - let available = self.buffer.pop_buf(); - if available.is_empty() { - if let Some(cx) = cx { - self.read_waker.register(cx.waker()); - } - return Err(TryReadError::Empty); - } - - let n = available.len().min(buf.len()); - buf[..n].copy_from_slice(&available[..n]); - self.buffer.pop(n); - Ok(n) - } - - fn try_write(&mut self, buf: &[u8]) -> Result { - self.try_write_with_context(None, buf) - } - - fn try_write_with_context(&mut self, cx: Option<&mut Context<'_>>, buf: &[u8]) -> Result { - if self.buffer.is_empty() { - self.read_waker.wake(); - } - - let available = self.buffer.push_buf(); - if available.is_empty() { - if let Some(cx) = cx { - self.write_waker.register(cx.waker()); - } - return Err(TryWriteError::Full); - } - - let n = available.len().min(buf.len()); - available[..n].copy_from_slice(&buf[..n]); - self.buffer.push(n); - Ok(n) + unsafe fn get_mut<'a>(&self, r: Range) -> &'a mut [u8] { + let p = self.0.get() as *mut u8; + core::slice::from_raw_parts_mut(p.add(r.start), r.end - r.start) } } +unsafe impl Send for Buffer {} +unsafe impl Sync for Buffer {} + /// A bounded byte-oriented pipe for communicating between asynchronous tasks /// with backpressure. /// @@ -234,6 +233,7 @@ pub struct Pipe where M: RawMutex, { + buf: Buffer, inner: Mutex>>, } @@ -252,7 +252,12 @@ where /// ``` pub const fn new() -> Self { Self { - inner: Mutex::new(RefCell::new(PipeState::new())), + buf: Buffer(UnsafeCell::new([0; N])), + inner: Mutex::new(RefCell::new(PipeState { + buffer: RingBuffer::new(), + read_waker: WakerRegistration::new(), + write_waker: WakerRegistration::new(), + })), } } @@ -261,21 +266,91 @@ where } fn try_read_with_context(&self, cx: Option<&mut Context<'_>>, buf: &mut [u8]) -> Result { - self.lock(|c| c.try_read_with_context(cx, buf)) + self.inner.lock(|rc: &RefCell>| { + let s = &mut *rc.borrow_mut(); + + if s.buffer.is_full() { + s.write_waker.wake(); + } + + let available = unsafe { self.buf.get(s.buffer.pop_buf()) }; + if available.is_empty() { + if let Some(cx) = cx { + s.read_waker.register(cx.waker()); + } + return Err(TryReadError::Empty); + } + + let n = available.len().min(buf.len()); + buf[..n].copy_from_slice(&available[..n]); + s.buffer.pop(n); + Ok(n) + }) + } + + // safety: While the returned slice is alive, + // no `read` or `consume` methods in the pipe must be called. + unsafe fn try_fill_buf_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<&[u8], TryReadError> { + self.inner.lock(|rc: &RefCell>| { + let s = &mut *rc.borrow_mut(); + + if s.buffer.is_full() { + s.write_waker.wake(); + } + + let available = unsafe { self.buf.get(s.buffer.pop_buf()) }; + if available.is_empty() { + if let Some(cx) = cx { + s.read_waker.register(cx.waker()); + } + return Err(TryReadError::Empty); + } + + Ok(available) + }) + } + + fn consume(&self, amt: usize) { + self.inner.lock(|rc: &RefCell>| { + let s = &mut *rc.borrow_mut(); + let available = s.buffer.pop_buf(); + assert!(amt <= available.len()); + s.buffer.pop(amt); + }) } fn try_write_with_context(&self, cx: Option<&mut Context<'_>>, buf: &[u8]) -> Result { - self.lock(|c| c.try_write_with_context(cx, buf)) + self.inner.lock(|rc: &RefCell>| { + let s = &mut *rc.borrow_mut(); + + if s.buffer.is_empty() { + s.read_waker.wake(); + } + + let available = unsafe { self.buf.get_mut(s.buffer.push_buf()) }; + if available.is_empty() { + if let Some(cx) = cx { + s.write_waker.register(cx.waker()); + } + return Err(TryWriteError::Full); + } + + let n = available.len().min(buf.len()); + available[..n].copy_from_slice(&buf[..n]); + s.buffer.push(n); + Ok(n) + }) } - /// Get a writer for this pipe. - pub fn writer(&self) -> Writer<'_, M, N> { - Writer { pipe: self } - } - - /// Get a reader for this pipe. - pub fn reader(&self) -> Reader<'_, M, N> { - Reader { pipe: self } + /// Split this pipe into a BufRead-capable reader and a writer. + /// + /// The reader and writer borrow the current pipe mutably, so it is not + /// possible to use it directly while they exist. This is needed because + /// implementing `BufRead` requires there is a single reader. + /// + /// The writer is cloneable, the reader is not. + pub fn split(&mut self) -> (Reader<'_, M, N>, Writer<'_, M, N>) { + (Reader { pipe: self }, Writer { pipe: self }) } /// Write some bytes to the pipe. @@ -312,7 +387,7 @@ where /// or return an error if the pipe is empty. See [`write`](Self::write) for a variant /// that waits instead of returning an error. pub fn try_write(&self, buf: &[u8]) -> Result { - self.lock(|c| c.try_write(buf)) + self.try_write_with_context(None, buf) } /// Read some bytes from the pipe. @@ -339,12 +414,17 @@ where /// or return an error if the pipe is empty. See [`read`](Self::read) for a variant /// that waits instead of returning an error. pub fn try_read(&self, buf: &mut [u8]) -> Result { - self.lock(|c| c.try_read(buf)) + self.try_read_with_context(None, buf) } /// Clear the data in the pipe's buffer. pub fn clear(&self) { - self.lock(|c| c.clear()) + self.inner.lock(|rc: &RefCell>| { + let s = &mut *rc.borrow_mut(); + + s.buffer.clear(); + s.write_waker.wake(); + }) } /// Return whether the pipe is full (no free space in the buffer) @@ -433,6 +513,16 @@ mod io_impls { } } + impl embedded_io_async::BufRead for Reader<'_, M, N> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + Ok(Reader::fill_buf(self).await) + } + + fn consume(&mut self, amt: usize) { + Reader::consume(self, amt) + } + } + impl embedded_io_async::ErrorType for Writer<'_, M, N> { type Error = Infallible; } @@ -457,43 +547,39 @@ mod tests { use super::*; use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; - fn capacity(c: &PipeState) -> usize { - N - c.buffer.len() - } - #[test] fn writing_once() { - let mut c = PipeState::<3>::new(); + let c = Pipe::::new(); assert!(c.try_write(&[1]).is_ok()); - assert_eq!(capacity(&c), 2); + assert_eq!(c.free_capacity(), 2); } #[test] fn writing_when_full() { - let mut c = PipeState::<3>::new(); + let c = Pipe::::new(); assert_eq!(c.try_write(&[42]), Ok(1)); assert_eq!(c.try_write(&[43]), Ok(1)); assert_eq!(c.try_write(&[44]), Ok(1)); assert_eq!(c.try_write(&[45]), Err(TryWriteError::Full)); - assert_eq!(capacity(&c), 0); + assert_eq!(c.free_capacity(), 0); } #[test] fn receiving_once_with_one_send() { - let mut c = PipeState::<3>::new(); + let c = Pipe::::new(); assert!(c.try_write(&[42]).is_ok()); let mut buf = [0; 16]; assert_eq!(c.try_read(&mut buf), Ok(1)); assert_eq!(buf[0], 42); - assert_eq!(capacity(&c), 3); + assert_eq!(c.free_capacity(), 3); } #[test] fn receiving_when_empty() { - let mut c = PipeState::<3>::new(); + let c = Pipe::::new(); let mut buf = [0; 16]; assert_eq!(c.try_read(&mut buf), Err(TryReadError::Empty)); - assert_eq!(capacity(&c), 3); + assert_eq!(c.free_capacity(), 3); } #[test] @@ -506,13 +592,37 @@ mod tests { } #[test] - fn cloning() { - let c = Pipe::::new(); - let r1 = c.reader(); - let w1 = c.writer(); + fn read_buf() { + let mut c = Pipe::::new(); + let (mut r, w) = c.split(); + assert!(w.try_write(&[42, 43]).is_ok()); + let buf = r.try_fill_buf().unwrap(); + assert_eq!(buf, &[42, 43]); + let buf = r.try_fill_buf().unwrap(); + assert_eq!(buf, &[42, 43]); + r.consume(1); + let buf = r.try_fill_buf().unwrap(); + assert_eq!(buf, &[43]); + r.consume(1); + assert_eq!(r.try_fill_buf(), Err(TryReadError::Empty)); + assert_eq!(w.try_write(&[44, 45, 46]), Ok(1)); + assert_eq!(w.try_write(&[45, 46]), Ok(2)); + let buf = r.try_fill_buf().unwrap(); + assert_eq!(buf, &[44]); // only one byte due to wraparound. + r.consume(1); + let buf = r.try_fill_buf().unwrap(); + assert_eq!(buf, &[45, 46]); + assert!(w.try_write(&[47]).is_ok()); + let buf = r.try_fill_buf().unwrap(); + assert_eq!(buf, &[45, 46, 47]); + r.consume(3); + } - let _ = r1.clone(); - let _ = w1.clone(); + #[test] + fn writer_is_cloneable() { + let mut c = Pipe::::new(); + let (_r, w) = c.split(); + let _ = w.clone(); } #[futures_test::test] diff --git a/embassy-sync/src/ring_buffer.rs b/embassy-sync/src/ring_buffer.rs index 52108402..d95ffa7c 100644 --- a/embassy-sync/src/ring_buffer.rs +++ b/embassy-sync/src/ring_buffer.rs @@ -1,5 +1,6 @@ +use core::ops::Range; + pub struct RingBuffer { - buf: [u8; N], start: usize, end: usize, empty: bool, @@ -8,27 +9,26 @@ pub struct RingBuffer { impl RingBuffer { pub const fn new() -> Self { Self { - buf: [0; N], start: 0, end: 0, empty: true, } } - pub fn push_buf(&mut self) -> &mut [u8] { + pub fn push_buf(&mut self) -> Range { if self.start == self.end && !self.empty { trace!(" ringbuf: push_buf empty"); - return &mut self.buf[..0]; + return 0..0; } let n = if self.start <= self.end { - self.buf.len() - self.end + N - self.end } else { self.start - self.end }; trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); - &mut self.buf[self.end..self.end + n] + self.end..self.end + n } pub fn push(&mut self, n: usize) { @@ -41,20 +41,20 @@ impl RingBuffer { self.empty = false; } - pub fn pop_buf(&mut self) -> &mut [u8] { + pub fn pop_buf(&mut self) -> Range { if self.empty { trace!(" ringbuf: pop_buf empty"); - return &mut self.buf[..0]; + return 0..0; } let n = if self.end <= self.start { - self.buf.len() - self.start + N - self.start } else { self.end - self.start }; trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); - &mut self.buf[self.start..self.start + n] + self.start..self.start + n } pub fn pop(&mut self, n: usize) { @@ -93,8 +93,8 @@ impl RingBuffer { } fn wrap(&self, n: usize) -> usize { - assert!(n <= self.buf.len()); - if n == self.buf.len() { + assert!(n <= N); + if n == N { 0 } else { n @@ -110,37 +110,29 @@ mod tests { fn push_pop() { let mut rb: RingBuffer<4> = RingBuffer::new(); let buf = rb.push_buf(); - assert_eq!(4, buf.len()); - buf[0] = 1; - buf[1] = 2; - buf[2] = 3; - buf[3] = 4; + assert_eq!(0..4, buf); rb.push(4); let buf = rb.pop_buf(); - assert_eq!(4, buf.len()); - assert_eq!(1, buf[0]); + assert_eq!(0..4, buf); rb.pop(1); let buf = rb.pop_buf(); - assert_eq!(3, buf.len()); - assert_eq!(2, buf[0]); + assert_eq!(1..4, buf); rb.pop(1); let buf = rb.pop_buf(); - assert_eq!(2, buf.len()); - assert_eq!(3, buf[0]); + assert_eq!(2..4, buf); rb.pop(1); let buf = rb.pop_buf(); - assert_eq!(1, buf.len()); - assert_eq!(4, buf[0]); + assert_eq!(3..4, buf); rb.pop(1); let buf = rb.pop_buf(); - assert_eq!(0, buf.len()); + assert_eq!(0..0, buf); let buf = rb.push_buf(); - assert_eq!(4, buf.len()); + assert_eq!(0..4, buf); } } diff --git a/embassy-sync/src/zerocopy_channel.rs b/embassy-sync/src/zerocopy_channel.rs new file mode 100644 index 00000000..f704cbd5 --- /dev/null +++ b/embassy-sync/src/zerocopy_channel.rs @@ -0,0 +1,260 @@ +//! A zero-copy queue for sending values between asynchronous tasks. +//! +//! It can be used concurrently by multiple producers (senders) and multiple +//! consumers (receivers), i.e. it is an "MPMC channel". +//! +//! Receivers are competing for messages. So a message that is received by +//! one receiver is not received by any other. +//! +//! This queue takes a Mutex type so that various +//! targets can be attained. For example, a ThreadModeMutex can be used +//! for single-core Cortex-M targets where messages are only passed +//! between tasks running in thread mode. Similarly, a CriticalSectionMutex +//! can also be used for single-core targets where messages are to be +//! passed from exception mode e.g. out of an interrupt handler. +//! +//! This module provides a bounded channel that has a limit on the number of +//! messages that it can store, and if this limit is reached, trying to send +//! another message will result in an error being returned. + +use core::cell::RefCell; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::{Context, Poll}; + +use crate::blocking_mutex::raw::RawMutex; +use crate::blocking_mutex::Mutex; +use crate::waitqueue::WakerRegistration; + +/// A bounded zero-copy channel for communicating between asynchronous tasks +/// with backpressure. +/// +/// The channel will buffer up to the provided number of messages. Once the +/// buffer is full, attempts to `send` new messages will wait until a message is +/// received from the channel. +/// +/// All data sent will become available in the same order as it was sent. +/// +/// The channel requires a buffer of recyclable elements. Writing to the channel is done through +/// an `&mut T`. +pub struct Channel<'a, M: RawMutex, T> { + buf: *mut T, + phantom: PhantomData<&'a mut T>, + state: Mutex>, +} + +impl<'a, M: RawMutex, T> Channel<'a, M, T> { + /// Initialize a new [`Channel`]. + /// + /// The provided buffer will be used and reused by the channel's logic, and thus dictates the + /// channel's capacity. + pub fn new(buf: &'a mut [T]) -> Self { + let len = buf.len(); + assert!(len != 0); + + Self { + buf: buf.as_mut_ptr(), + phantom: PhantomData, + state: Mutex::new(RefCell::new(State { + len, + front: 0, + back: 0, + full: false, + send_waker: WakerRegistration::new(), + receive_waker: WakerRegistration::new(), + })), + } + } + + /// Creates a [`Sender`] and [`Receiver`] from an existing channel. + /// + /// Further Senders and Receivers can be created through [`Sender::borrow`] and + /// [`Receiver::borrow`] respectively. + pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { + (Sender { channel: self }, Receiver { channel: self }) + } +} + +/// Send-only access to a [`Channel`]. +pub struct Sender<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, +} + +impl<'a, M: RawMutex, T> Sender<'a, M, T> { + /// Creates one further [`Sender`] over the same channel. + pub fn borrow(&mut self) -> Sender<'_, M, T> { + Sender { channel: self.channel } + } + + /// Attempts to send a value over the channel. + pub fn try_send(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + /// Attempts to send a value over the channel. + pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.receive_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + /// Asynchronously send a value over the channel. + pub async fn send(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(i), + None => { + s.receive_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + /// Notify the channel that the sending of the value has been finalized. + pub fn send_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().push_done()) + } +} + +/// Receive-only access to a [`Channel`]. +pub struct Receiver<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, +} + +impl<'a, M: RawMutex, T> Receiver<'a, M, T> { + /// Creates one further [`Sender`] over the same channel. + pub fn borrow(&mut self) -> Receiver<'_, M, T> { + Receiver { channel: self.channel } + } + + /// Attempts to receive a value over the channel. + pub fn try_receive(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + /// Attempts to asynchronously receive a value over the channel. + pub fn poll_receive(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + /// Asynchronously receive a value over the channel. + pub async fn receive(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(i), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + /// Notify the channel that the receiving of the value has been finalized. + pub fn receive_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().pop_done()) + } +} + +struct State { + len: usize, + + /// Front index. Always 0..=(N-1) + front: usize, + /// Back index. Always 0..=(N-1). + back: usize, + + /// Used to distinguish "empty" and "full" cases when `front == back`. + /// May only be `true` if `front == back`, always `false` otherwise. + full: bool, + + send_waker: WakerRegistration, + receive_waker: WakerRegistration, +} + +impl State { + fn increment(&self, i: usize) -> usize { + if i + 1 == self.len { + 0 + } else { + i + 1 + } + } + + fn is_full(&self) -> bool { + self.full + } + + fn is_empty(&self) -> bool { + self.front == self.back && !self.full + } + + fn push_index(&mut self) -> Option { + match self.is_full() { + true => None, + false => Some(self.back), + } + } + + fn push_done(&mut self) { + assert!(!self.is_full()); + self.back = self.increment(self.back); + if self.back == self.front { + self.full = true; + } + self.send_waker.wake(); + } + + fn pop_index(&mut self) -> Option { + match self.is_empty() { + true => None, + false => Some(self.front), + } + } + + fn pop_done(&mut self) { + assert!(!self.is_empty()); + self.front = self.increment(self.front); + self.full = false; + self.receive_waker.wake(); + } +} diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index 26640d93..e3b38455 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.1.4 - ??? + +- Added more tick rates + +## 0.1.3 - 2023-08-28 + +- Update `embedded-hal-async` to `1.0.0-rc.1` +- Update `embedded-hal v1` to `1.0.0-rc.1` + ## 0.1.2 - 2023-07-05 - Update `embedded-hal-async` to `0.2.0-alpha.2`. @@ -26,4 +35,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.1.0 - 2022-08-26 -- First release \ No newline at end of file +- First release diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 00d31d30..8f034a9d 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-time" -version = "0.1.2" +version = "0.1.4" edition = "2021" description = "Instant and Duration for embedded no-std systems, with async timer support" repository = "https://github.com/embassy-rs/embassy" @@ -145,6 +145,68 @@ tick-hz-384_000_000 = [] tick-hz-512_000_000 = [] tick-hz-576_000_000 = [] tick-hz-768_000_000 = [] +tick-hz-20_000_000 = [] +tick-hz-30_000_000 = [] +tick-hz-40_000_000 = [] +tick-hz-50_000_000 = [] +tick-hz-60_000_000 = [] +tick-hz-70_000_000 = [] +tick-hz-80_000_000 = [] +tick-hz-90_000_000 = [] +tick-hz-110_000_000 = [] +tick-hz-120_000_000 = [] +tick-hz-130_000_000 = [] +tick-hz-140_000_000 = [] +tick-hz-150_000_000 = [] +tick-hz-160_000_000 = [] +tick-hz-170_000_000 = [] +tick-hz-180_000_000 = [] +tick-hz-190_000_000 = [] +tick-hz-200_000_000 = [] +tick-hz-210_000_000 = [] +tick-hz-220_000_000 = [] +tick-hz-230_000_000 = [] +tick-hz-240_000_000 = [] +tick-hz-250_000_000 = [] +tick-hz-260_000_000 = [] +tick-hz-270_000_000 = [] +tick-hz-280_000_000 = [] +tick-hz-290_000_000 = [] +tick-hz-300_000_000 = [] +tick-hz-320_000_000 = [] +tick-hz-340_000_000 = [] +tick-hz-360_000_000 = [] +tick-hz-380_000_000 = [] +tick-hz-400_000_000 = [] +tick-hz-420_000_000 = [] +tick-hz-440_000_000 = [] +tick-hz-460_000_000 = [] +tick-hz-480_000_000 = [] +tick-hz-500_000_000 = [] +tick-hz-520_000_000 = [] +tick-hz-540_000_000 = [] +tick-hz-560_000_000 = [] +tick-hz-580_000_000 = [] +tick-hz-600_000_000 = [] +tick-hz-620_000_000 = [] +tick-hz-640_000_000 = [] +tick-hz-660_000_000 = [] +tick-hz-680_000_000 = [] +tick-hz-700_000_000 = [] +tick-hz-720_000_000 = [] +tick-hz-740_000_000 = [] +tick-hz-760_000_000 = [] +tick-hz-780_000_000 = [] +tick-hz-800_000_000 = [] +tick-hz-820_000_000 = [] +tick-hz-840_000_000 = [] +tick-hz-860_000_000 = [] +tick-hz-880_000_000 = [] +tick-hz-900_000_000 = [] +tick-hz-920_000_000 = [] +tick-hz-940_000_000 = [] +tick-hz-960_000_000 = [] +tick-hz-980_000_000 = [] # END TICKS [dependencies] diff --git a/embassy-time/gen_tick.py b/embassy-time/gen_tick.py index 15e65187..67a4c79c 100644 --- a/embassy-time/gen_tick.py +++ b/embassy-time/gen_tick.py @@ -17,6 +17,10 @@ for i in range(1, 10): ticks.append(2**i * 1000000) ticks.append(2**i * 9 // 8 * 1000000) ticks.append(2**i * 3 // 2 * 1000000) +for i in range(1, 30): + ticks.append(10 * i * 1_000_000) +for i in range(15, 50): + ticks.append(20 * i * 1_000_000) seen = set() ticks = [x for x in ticks if not (x in seen or seen.add(x))] diff --git a/embassy-time/src/fmt.rs b/embassy-time/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-time/src/fmt.rs +++ b/embassy-time/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs index 44f226f6..5571cdd1 100644 --- a/embassy-time/src/instant.rs +++ b/embassy-time/src/instant.rs @@ -71,7 +71,7 @@ impl Instant { /// Panics on over/underflow. pub fn duration_since(&self, earlier: Instant) -> Duration { Duration { - ticks: self.ticks.checked_sub(earlier.ticks).unwrap(), + ticks: unwrap!(self.ticks.checked_sub(earlier.ticks)), } } diff --git a/embassy-time/src/tick.rs b/embassy-time/src/tick.rs index 608bc44f..be544181 100644 --- a/embassy-time/src/tick.rs +++ b/embassy-time/src/tick.rs @@ -156,6 +156,130 @@ pub const TICK_HZ: u64 = 512_000_000; pub const TICK_HZ: u64 = 576_000_000; #[cfg(feature = "tick-hz-768_000_000")] pub const TICK_HZ: u64 = 768_000_000; +#[cfg(feature = "tick-hz-20_000_000")] +pub const TICK_HZ: u64 = 20_000_000; +#[cfg(feature = "tick-hz-30_000_000")] +pub const TICK_HZ: u64 = 30_000_000; +#[cfg(feature = "tick-hz-40_000_000")] +pub const TICK_HZ: u64 = 40_000_000; +#[cfg(feature = "tick-hz-50_000_000")] +pub const TICK_HZ: u64 = 50_000_000; +#[cfg(feature = "tick-hz-60_000_000")] +pub const TICK_HZ: u64 = 60_000_000; +#[cfg(feature = "tick-hz-70_000_000")] +pub const TICK_HZ: u64 = 70_000_000; +#[cfg(feature = "tick-hz-80_000_000")] +pub const TICK_HZ: u64 = 80_000_000; +#[cfg(feature = "tick-hz-90_000_000")] +pub const TICK_HZ: u64 = 90_000_000; +#[cfg(feature = "tick-hz-110_000_000")] +pub const TICK_HZ: u64 = 110_000_000; +#[cfg(feature = "tick-hz-120_000_000")] +pub const TICK_HZ: u64 = 120_000_000; +#[cfg(feature = "tick-hz-130_000_000")] +pub const TICK_HZ: u64 = 130_000_000; +#[cfg(feature = "tick-hz-140_000_000")] +pub const TICK_HZ: u64 = 140_000_000; +#[cfg(feature = "tick-hz-150_000_000")] +pub const TICK_HZ: u64 = 150_000_000; +#[cfg(feature = "tick-hz-160_000_000")] +pub const TICK_HZ: u64 = 160_000_000; +#[cfg(feature = "tick-hz-170_000_000")] +pub const TICK_HZ: u64 = 170_000_000; +#[cfg(feature = "tick-hz-180_000_000")] +pub const TICK_HZ: u64 = 180_000_000; +#[cfg(feature = "tick-hz-190_000_000")] +pub const TICK_HZ: u64 = 190_000_000; +#[cfg(feature = "tick-hz-200_000_000")] +pub const TICK_HZ: u64 = 200_000_000; +#[cfg(feature = "tick-hz-210_000_000")] +pub const TICK_HZ: u64 = 210_000_000; +#[cfg(feature = "tick-hz-220_000_000")] +pub const TICK_HZ: u64 = 220_000_000; +#[cfg(feature = "tick-hz-230_000_000")] +pub const TICK_HZ: u64 = 230_000_000; +#[cfg(feature = "tick-hz-240_000_000")] +pub const TICK_HZ: u64 = 240_000_000; +#[cfg(feature = "tick-hz-250_000_000")] +pub const TICK_HZ: u64 = 250_000_000; +#[cfg(feature = "tick-hz-260_000_000")] +pub const TICK_HZ: u64 = 260_000_000; +#[cfg(feature = "tick-hz-270_000_000")] +pub const TICK_HZ: u64 = 270_000_000; +#[cfg(feature = "tick-hz-280_000_000")] +pub const TICK_HZ: u64 = 280_000_000; +#[cfg(feature = "tick-hz-290_000_000")] +pub const TICK_HZ: u64 = 290_000_000; +#[cfg(feature = "tick-hz-300_000_000")] +pub const TICK_HZ: u64 = 300_000_000; +#[cfg(feature = "tick-hz-320_000_000")] +pub const TICK_HZ: u64 = 320_000_000; +#[cfg(feature = "tick-hz-340_000_000")] +pub const TICK_HZ: u64 = 340_000_000; +#[cfg(feature = "tick-hz-360_000_000")] +pub const TICK_HZ: u64 = 360_000_000; +#[cfg(feature = "tick-hz-380_000_000")] +pub const TICK_HZ: u64 = 380_000_000; +#[cfg(feature = "tick-hz-400_000_000")] +pub const TICK_HZ: u64 = 400_000_000; +#[cfg(feature = "tick-hz-420_000_000")] +pub const TICK_HZ: u64 = 420_000_000; +#[cfg(feature = "tick-hz-440_000_000")] +pub const TICK_HZ: u64 = 440_000_000; +#[cfg(feature = "tick-hz-460_000_000")] +pub const TICK_HZ: u64 = 460_000_000; +#[cfg(feature = "tick-hz-480_000_000")] +pub const TICK_HZ: u64 = 480_000_000; +#[cfg(feature = "tick-hz-500_000_000")] +pub const TICK_HZ: u64 = 500_000_000; +#[cfg(feature = "tick-hz-520_000_000")] +pub const TICK_HZ: u64 = 520_000_000; +#[cfg(feature = "tick-hz-540_000_000")] +pub const TICK_HZ: u64 = 540_000_000; +#[cfg(feature = "tick-hz-560_000_000")] +pub const TICK_HZ: u64 = 560_000_000; +#[cfg(feature = "tick-hz-580_000_000")] +pub const TICK_HZ: u64 = 580_000_000; +#[cfg(feature = "tick-hz-600_000_000")] +pub const TICK_HZ: u64 = 600_000_000; +#[cfg(feature = "tick-hz-620_000_000")] +pub const TICK_HZ: u64 = 620_000_000; +#[cfg(feature = "tick-hz-640_000_000")] +pub const TICK_HZ: u64 = 640_000_000; +#[cfg(feature = "tick-hz-660_000_000")] +pub const TICK_HZ: u64 = 660_000_000; +#[cfg(feature = "tick-hz-680_000_000")] +pub const TICK_HZ: u64 = 680_000_000; +#[cfg(feature = "tick-hz-700_000_000")] +pub const TICK_HZ: u64 = 700_000_000; +#[cfg(feature = "tick-hz-720_000_000")] +pub const TICK_HZ: u64 = 720_000_000; +#[cfg(feature = "tick-hz-740_000_000")] +pub const TICK_HZ: u64 = 740_000_000; +#[cfg(feature = "tick-hz-760_000_000")] +pub const TICK_HZ: u64 = 760_000_000; +#[cfg(feature = "tick-hz-780_000_000")] +pub const TICK_HZ: u64 = 780_000_000; +#[cfg(feature = "tick-hz-800_000_000")] +pub const TICK_HZ: u64 = 800_000_000; +#[cfg(feature = "tick-hz-820_000_000")] +pub const TICK_HZ: u64 = 820_000_000; +#[cfg(feature = "tick-hz-840_000_000")] +pub const TICK_HZ: u64 = 840_000_000; +#[cfg(feature = "tick-hz-860_000_000")] +pub const TICK_HZ: u64 = 860_000_000; +#[cfg(feature = "tick-hz-880_000_000")] +pub const TICK_HZ: u64 = 880_000_000; +#[cfg(feature = "tick-hz-900_000_000")] +pub const TICK_HZ: u64 = 900_000_000; +#[cfg(feature = "tick-hz-920_000_000")] +pub const TICK_HZ: u64 = 920_000_000; +#[cfg(feature = "tick-hz-940_000_000")] +pub const TICK_HZ: u64 = 940_000_000; +#[cfg(feature = "tick-hz-960_000_000")] +pub const TICK_HZ: u64 = 960_000_000; +#[cfg(feature = "tick-hz-980_000_000")] +pub const TICK_HZ: u64 = 980_000_000; #[cfg(not(any( feature = "tick-hz-1", feature = "tick-hz-10", @@ -235,5 +359,67 @@ pub const TICK_HZ: u64 = 768_000_000; feature = "tick-hz-512_000_000", feature = "tick-hz-576_000_000", feature = "tick-hz-768_000_000", + feature = "tick-hz-20_000_000", + feature = "tick-hz-30_000_000", + feature = "tick-hz-40_000_000", + feature = "tick-hz-50_000_000", + feature = "tick-hz-60_000_000", + feature = "tick-hz-70_000_000", + feature = "tick-hz-80_000_000", + feature = "tick-hz-90_000_000", + feature = "tick-hz-110_000_000", + feature = "tick-hz-120_000_000", + feature = "tick-hz-130_000_000", + feature = "tick-hz-140_000_000", + feature = "tick-hz-150_000_000", + feature = "tick-hz-160_000_000", + feature = "tick-hz-170_000_000", + feature = "tick-hz-180_000_000", + feature = "tick-hz-190_000_000", + feature = "tick-hz-200_000_000", + feature = "tick-hz-210_000_000", + feature = "tick-hz-220_000_000", + feature = "tick-hz-230_000_000", + feature = "tick-hz-240_000_000", + feature = "tick-hz-250_000_000", + feature = "tick-hz-260_000_000", + feature = "tick-hz-270_000_000", + feature = "tick-hz-280_000_000", + feature = "tick-hz-290_000_000", + feature = "tick-hz-300_000_000", + feature = "tick-hz-320_000_000", + feature = "tick-hz-340_000_000", + feature = "tick-hz-360_000_000", + feature = "tick-hz-380_000_000", + feature = "tick-hz-400_000_000", + feature = "tick-hz-420_000_000", + feature = "tick-hz-440_000_000", + feature = "tick-hz-460_000_000", + feature = "tick-hz-480_000_000", + feature = "tick-hz-500_000_000", + feature = "tick-hz-520_000_000", + feature = "tick-hz-540_000_000", + feature = "tick-hz-560_000_000", + feature = "tick-hz-580_000_000", + feature = "tick-hz-600_000_000", + feature = "tick-hz-620_000_000", + feature = "tick-hz-640_000_000", + feature = "tick-hz-660_000_000", + feature = "tick-hz-680_000_000", + feature = "tick-hz-700_000_000", + feature = "tick-hz-720_000_000", + feature = "tick-hz-740_000_000", + feature = "tick-hz-760_000_000", + feature = "tick-hz-780_000_000", + feature = "tick-hz-800_000_000", + feature = "tick-hz-820_000_000", + feature = "tick-hz-840_000_000", + feature = "tick-hz-860_000_000", + feature = "tick-hz-880_000_000", + feature = "tick-hz-900_000_000", + feature = "tick-hz-920_000_000", + feature = "tick-hz-940_000_000", + feature = "tick-hz-960_000_000", + feature = "tick-hz-980_000_000", )))] pub const TICK_HZ: u64 = 1_000_000; diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index ad5026e6..07ddf473 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -133,7 +133,13 @@ impl Ticker { Self { expires_at, duration } } - /// Waits for the next tick + /// Resets the ticker back to its original state. + /// This causes the ticker to go back to zero, even if the current tick isn't over yet. + pub fn reset(&mut self) { + self.expires_at = Instant::now() + self.duration; + } + + /// Waits for the next tick. pub fn next(&mut self) -> impl Future + '_ { poll_fn(|cx| { if self.expires_at <= Instant::now() { diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 0f91cd36..944a48a5 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -10,7 +10,7 @@ target = "thumbv7em-none-eabi" [dependencies] embassy-usb = { version = "0.1.0", path = "../embassy-usb" } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } futures = { version = "0.3", default-features = false } static_cell = "1" diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 03cf96a1..0e7e0e70 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -41,7 +41,7 @@ max-handler-count-8 = [] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../embassy-sync" } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } defmt = { version = "0.3", optional = true } diff --git a/embassy-usb/src/fmt.rs b/embassy-usb/src/fmt.rs index 06697081..78e583c1 100644 --- a/embassy-usb/src/fmt.rs +++ b/embassy-usb/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -81,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -223,3 +228,31 @@ impl Try for Result { self } } + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 46ce7ba9..435a3391 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] } embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf", features = ["nightly"] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index be85f443..01cfc599 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly"] } embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -27,6 +27,7 @@ default = ["panic-reset"] debug = [ "embassy-rp/defmt", "embassy-boot-rp/defmt", + "embassy-sync/defmt", "panic-probe" ] skip-include = [] diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 15fdaca8..a4602a7e 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -38,7 +38,7 @@ async fn main(_s: Spawner) { let flash = Mutex::new(RefCell::new(flash)); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); - let mut aligned = AlignedBuffer([0; 4]); + let mut aligned = AlignedBuffer([0; 1]); let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0); Timer::after(Duration::from_secs(5)).await; diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index ea878933..a0649cf8 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -25,5 +25,6 @@ defmt = [ "dep:defmt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", + "embassy-sync/defmt", ] skip-include = [] diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index b39bc292..ca1c0c42 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -26,5 +26,6 @@ defmt = [ "dep:defmt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", + "embassy-sync/defmt", ] skip-include = [] diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index f015b2ca..e50c8c41 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -26,5 +26,6 @@ defmt = [ "dep:defmt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", + "embassy-sync/defmt", ] skip-include = [] diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index f221e1de..1f1a0f3c 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -25,5 +25,6 @@ defmt = [ "dep:defmt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", + "embassy-sync/defmt", ] skip-include = [] diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 2896afa3..45b12813 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -25,5 +25,6 @@ defmt = [ "dep:defmt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", + "embassy-sync/defmt", ] skip-include = [] diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 50d8967a..d0d0d0fb 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -25,5 +25,6 @@ defmt = [ "dep:defmt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", + "embassy-sync/defmt", ] skip-include = [] diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 27541439..3265b9f1 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -25,5 +25,6 @@ defmt = [ "dep:defmt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", + "embassy-sync/defmt", ] skip-include = [] diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 40656f35..42391778 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -25,7 +25,7 @@ defmt = [ softdevice = [ "embassy-boot-nrf/softdevice", ] -debug = ["defmt-rtt"] +debug = ["defmt-rtt", "defmt"] [profile.dev] debug = 2 diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 6436f2fe..9175768d 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -24,7 +24,7 @@ defmt = [ "embassy-boot-stm32/defmt", "embassy-stm32/defmt", ] -debug = ["defmt-rtt"] +debug = ["defmt-rtt", "defmt"] [profile.dev] debug = 2 diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index b06722f5..e402e49f 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -16,9 +16,9 @@ log = [ ] [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time" } +embassy-time = { version = "0.1.3", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 715f1ecf..c588b807 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" rtic = { version = "2", features = ["thumbv7-backend"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "generic-queue"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "generic-queue"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nightly", "unstable-traits", "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "0.3" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 1b2f1eb1..d45e006c 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -29,18 +29,18 @@ nightly = [ [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = { version = "0.5.0", features = ["defmt-03"] } embedded-io-async = { version = "0.5.0", optional = true, features = ["defmt-03"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } -lora-phy = { version = "1", optional = true } -lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } -lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } +lora-phy = { version = "2", optional = true } +lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } +lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"], optional = true } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true } @@ -65,6 +65,3 @@ microfft = "0.5.0" [profile.release] debug = 2 - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/nrf52840/src/bin/lora_cad.rs b/examples/nrf52840/src/bin/lora_cad.rs index beca061e..3a98133c 100644 --- a/examples/nrf52840/src/bin/lora_cad.rs +++ b/examples/nrf52840/src/bin/lora_cad.rs @@ -41,10 +41,8 @@ async fn main(_spawner: Spawner) { let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); diff --git a/examples/nrf52840/src/bin/lora_lorawan.rs b/examples/nrf52840/src/bin/lora_lorawan.rs index c953680c..666330ba 100644 --- a/examples/nrf52840/src/bin/lora_lorawan.rs +++ b/examples/nrf52840/src/bin/lora_lorawan.rs @@ -20,6 +20,7 @@ use lora_phy::LoRa; use lorawan::default_crypto::DefaultFactory as Crypto; use lorawan_device::async_device::lora_radio::LoRaRadio; use lorawan_device::async_device::{region, Device, JoinMode}; +use lorawan_device::{AppEui, AppKey, DevEui}; use {defmt_rtt as _, panic_probe as _}; const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region @@ -47,10 +48,8 @@ async fn main(_spawner: Spawner) { let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); - let mut delay = Delay; - let lora = { - match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), true, &mut delay).await { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), true, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -68,9 +67,9 @@ async fn main(_spawner: Spawner) { // TODO: Adjust the EUI and Keys according to your network credentials match device .join(&JoinMode::OTAA { - deveui: [0, 0, 0, 0, 0, 0, 0, 0], - appeui: [0, 0, 0, 0, 0, 0, 0, 0], - appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]), + appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]), + appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), }) .await { diff --git a/examples/nrf52840/src/bin/lora_p2p_receive.rs b/examples/nrf52840/src/bin/lora_p2p_receive.rs index 563fe42e..1d293c6b 100644 --- a/examples/nrf52840/src/bin/lora_p2p_receive.rs +++ b/examples/nrf52840/src/bin/lora_p2p_receive.rs @@ -41,10 +41,8 @@ async fn main(_spawner: Spawner) { let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -88,7 +86,7 @@ async fn main(_spawner: Spawner) { }; match lora - .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) + .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false) .await { Ok(()) => {} diff --git a/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs index 1fd8f61a..eee4d20e 100644 --- a/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs +++ b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs @@ -41,10 +41,8 @@ async fn main(_spawner: Spawner) { let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -92,14 +90,12 @@ async fn main(_spawner: Spawner) { .prepare_for_rx( &mdltn_params, &rx_pkt_params, + None, Some(&DutyCycleParams { rx_time: 300_000, // 300_000 units * 15.625 us/unit = 4.69 s sleep_time: 200_000, // 200_000 units * 15.625 us/unit = 3.13 s }), false, - false, - 0, - 0, ) .await { diff --git a/examples/nrf52840/src/bin/lora_p2p_send.rs b/examples/nrf52840/src/bin/lora_p2p_send.rs index 1c8bbc27..676221a2 100644 --- a/examples/nrf52840/src/bin/lora_p2p_send.rs +++ b/examples/nrf52840/src/bin/lora_p2p_send.rs @@ -41,10 +41,8 @@ async fn main(_spawner: Spawner) { let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -97,7 +95,7 @@ async fn main(_spawner: Spawner) { } }; - match lora.sleep(&mut delay).await { + match lora.sleep(false).await { Ok(()) => info!("Sleep successful"), Err(err) => info!("Sleep unsuccessful = {}", err), } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 4968a79a..86d969ed 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = [ +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = [ "defmt", ] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", @@ -14,7 +14,7 @@ embassy-executor = { version = "0.3.0", path = "../../embassy-executor", feature "defmt", "integrated-timers", ] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = [ +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", ] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 3efc804a..2677e040 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } @@ -17,9 +17,9 @@ embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", fea embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } -lora-phy = { version = "1" } -lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } -lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } +lora-phy = { version = "2" } +lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"] } +lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"] } cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } @@ -55,6 +55,3 @@ rand = { version = "0.8.5", default-features = false } [profile.release] debug = 2 - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/rp/src/bin/i2c_slave.rs b/examples/rp/src/bin/i2c_slave.rs new file mode 100644 index 00000000..7de300fb --- /dev/null +++ b/examples/rp/src/bin/i2c_slave.rs @@ -0,0 +1,118 @@ +//! This example shows how to use the 2040 as an i2c slave. +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::peripherals::{I2C0, I2C1}; +use embassy_rp::{bind_interrupts, i2c, i2c_slave}; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::i2c::I2c; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + I2C0_IRQ => i2c::InterruptHandler; + I2C1_IRQ => i2c::InterruptHandler; +}); + +const DEV_ADDR: u8 = 0x42; + +#[embassy_executor::task] +async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { + info!("Device start"); + + let mut state = 0; + + loop { + let mut buf = [0u8; 128]; + match dev.listen(&mut buf).await { + Ok(i2c_slave::Command::GeneralCall(len)) => info!("Device recieved general call write: {}", buf[..len]), + Ok(i2c_slave::Command::Read) => loop { + match dev.respond_to_read(&[state]).await { + Ok(x) => match x { + i2c_slave::ReadStatus::Done => break, + i2c_slave::ReadStatus::NeedMoreBytes => (), + i2c_slave::ReadStatus::LeftoverBytes(x) => { + info!("tried to write {} extra bytes", x); + break; + } + }, + Err(e) => error!("error while responding {}", e), + } + }, + Ok(i2c_slave::Command::Write(len)) => info!("Device recieved write: {}", buf[..len]), + Ok(i2c_slave::Command::WriteRead(len)) => { + info!("device recieved write read: {:x}", buf[..len]); + match buf[0] { + // Set the state + 0xC2 => { + state = buf[1]; + match dev.respond_and_fill(&[state], 0x00).await { + Ok(read_status) => info!("response read status {}", read_status), + Err(e) => error!("error while responding {}", e), + } + } + // Reset State + 0xC8 => { + state = 0; + match dev.respond_and_fill(&[state], 0x00).await { + Ok(read_status) => info!("response read status {}", read_status), + Err(e) => error!("error while responding {}", e), + } + } + x => error!("Invalid Write Read {:x}", x), + } + } + Err(e) => error!("{}", e), + } + } +} + +#[embassy_executor::task] +async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { + info!("Controller start"); + + loop { + let mut resp_buff = [0u8; 2]; + for i in 0..10 { + match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await { + Ok(_) => info!("write_read response: {}", resp_buff), + Err(e) => error!("Error writing {}", e), + } + + Timer::after(Duration::from_millis(100)).await; + } + match con.read(DEV_ADDR, &mut resp_buff).await { + Ok(_) => info!("read response: {}", resp_buff), + Err(e) => error!("Error writing {}", e), + } + match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await { + Ok(_) => info!("write_read response: {}", resp_buff), + Err(e) => error!("Error writing {}", e), + } + Timer::after(Duration::from_millis(100)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let d_sda = p.PIN_3; + let d_scl = p.PIN_2; + let mut config = i2c_slave::Config::default(); + config.addr = DEV_ADDR as u16; + let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); + + unwrap!(spawner.spawn(device_task(device))); + + let c_sda = p.PIN_1; + let c_scl = p.PIN_0; + let mut config = i2c::Config::default(); + config.frequency = 5_000; + let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); + + unwrap!(spawner.spawn(controller_task(controller))); +} diff --git a/examples/rp/src/bin/lora_lorawan.rs b/examples/rp/src/bin/lora_lorawan.rs index d631fafa..e7e81863 100644 --- a/examples/rp/src/bin/lora_lorawan.rs +++ b/examples/rp/src/bin/lora_lorawan.rs @@ -19,6 +19,7 @@ use lora_phy::LoRa; use lorawan::default_crypto::DefaultFactory as Crypto; use lorawan_device::async_device::lora_radio::LoRaRadio; use lorawan_device::async_device::{region, Device, JoinMode}; +use lorawan_device::{AppEui, AppKey, DevEui}; use {defmt_rtt as _, panic_probe as _}; const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region @@ -39,16 +40,8 @@ async fn main(_spawner: Spawner) { let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); - let mut delay = Delay; - let lora = { - match LoRa::new( - SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), - true, - &mut delay, - ) - .await - { + match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), true, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -66,9 +59,9 @@ async fn main(_spawner: Spawner) { // TODO: Adjust the EUI and Keys according to your network credentials match device .join(&JoinMode::OTAA { - deveui: [0, 0, 0, 0, 0, 0, 0, 0], - appeui: [0, 0, 0, 0, 0, 0, 0, 0], - appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]), + appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]), + appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), }) .await { diff --git a/examples/rp/src/bin/lora_p2p_receive.rs b/examples/rp/src/bin/lora_p2p_receive.rs index 396d669d..5891826f 100644 --- a/examples/rp/src/bin/lora_p2p_receive.rs +++ b/examples/rp/src/bin/lora_p2p_receive.rs @@ -35,16 +35,8 @@ async fn main(_spawner: Spawner) { let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new( - SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), - false, - &mut delay, - ) - .await - { + match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -83,7 +75,7 @@ async fn main(_spawner: Spawner) { }; match lora - .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) + .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false) .await { Ok(()) => {} diff --git a/examples/rp/src/bin/lora_p2p_send.rs b/examples/rp/src/bin/lora_p2p_send.rs index a0f70fa5..94bdb4e9 100644 --- a/examples/rp/src/bin/lora_p2p_send.rs +++ b/examples/rp/src/bin/lora_p2p_send.rs @@ -35,16 +35,8 @@ async fn main(_spawner: Spawner) { let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new( - SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), - false, - &mut delay, - ) - .await - { + match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -97,7 +89,7 @@ async fn main(_spawner: Spawner) { } }; - match lora.sleep(&mut delay).await { + match lora.sleep(false).await { Ok(()) => info!("Sleep successful"), Err(err) => info!("Sleep unsuccessful = {}", err), } diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs index b54cc92f..e31aa62a 100644 --- a/examples/rp/src/bin/lora_p2p_send_multicore.rs +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs @@ -69,16 +69,9 @@ async fn core1_task( iv: GenericSx126xInterfaceVariant, Input<'static, AnyPin>>, ) { info!("Hello from core 1"); - let mut delay = Delay; let mut lora = { - match LoRa::new( - SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), - false, - &mut delay, - ) - .await - { + match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -132,7 +125,7 @@ async fn core1_task( } }; - match lora.sleep(&mut delay).await { + match lora.sleep(false).await { Ok(()) => info!("Sleep successful"), Err(err) => info!("Sleep unsuccessful = {}", err), } diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 707c99b7..aa9e52cb 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -91,13 +91,11 @@ async fn main(_spawner: Spawner) { let (mut uart_tx, mut uart_rx) = uart.split(); // Pipe setup - let usb_pipe: Pipe = Pipe::new(); - let mut usb_pipe_writer = usb_pipe.writer(); - let mut usb_pipe_reader = usb_pipe.reader(); + let mut usb_pipe: Pipe = Pipe::new(); + let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split(); - let uart_pipe: Pipe = Pipe::new(); - let mut uart_pipe_writer = uart_pipe.writer(); - let mut uart_pipe_reader = uart_pipe.reader(); + let mut uart_pipe: Pipe = Pipe::new(); + let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split(); let (mut usb_tx, mut usb_rx) = class.split(); diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index bc87016e..5c0c6024 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -138,8 +138,9 @@ async fn main(_spawner: Spawner) { const NUM_LEDS: usize = 1; let mut data = [RGB8::default(); NUM_LEDS]; - // For the thing plus, use pin 8 - // For the feather, use pin 16 + // Common neopixel pins: + // Thing plus: 8 + // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); // Loop forever making RGB values and pushing them out to the WS2812. @@ -152,7 +153,7 @@ async fn main(_spawner: Spawner) { } ws2812.write(&data).await; - Timer::after(Duration::from_micros(5)).await; + Timer::after(Duration::from_millis(10)).await; } } } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 0d4d5fa1..e54f3698 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -5,14 +5,16 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "std", "nightly"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["log", "std", "nightly"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } +embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]} embedded-io-async = { version = "0.5.0" } embedded-io-adapters = { version = "0.5.0", features = ["futures-03"] } critical-section = { version = "1.1", features = ["std"] } +smoltcp = { version = "0.10.0", features = ["dns-max-server-count-4"] } async-io = "1.6.0" env_logger = "0.9.0" diff --git a/examples/std/src/bin/net_ppp.rs b/examples/std/src/bin/net_ppp.rs new file mode 100644 index 00000000..9cf6e19d --- /dev/null +++ b/examples/std/src/bin/net_ppp.rs @@ -0,0 +1,218 @@ +//! Testing against pppd: +//! +//! echo myuser $(hostname) mypass 192.168.7.10 >> /etc/ppp/pap-secrets +//! socat -v -x PTY,link=pty1,rawer PTY,link=pty2,rawer +//! sudo pppd $PWD/pty1 115200 192.168.7.1: ms-dns 8.8.4.4 ms-dns 8.8.8.8 nodetach debug local persist silent noproxyarp +//! RUST_LOG=trace cargo run --bin net_ppp -- --device pty2 +//! ping 192.168.7.10 +//! nc 192.168.7.10 1234 + +#![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait, impl_trait_projections)] + +#[path = "../serial_port.rs"] +mod serial_port; + +use async_io::Async; +use clap::Parser; +use embassy_executor::{Executor, Spawner}; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Config, ConfigV4, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_ppp::Runner; +use embedded_io_async::Write; +use futures::io::BufReader; +use heapless::Vec; +use log::*; +use nix::sys::termios; +use rand_core::{OsRng, RngCore}; +use static_cell::{make_static, StaticCell}; + +use crate::serial_port::SerialPort; + +#[derive(Parser)] +#[clap(version = "1.0")] +struct Opts { + /// Serial port device name + #[clap(short, long)] + device: String, +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::task] +async fn ppp_task( + stack: &'static Stack>, + mut runner: Runner<'static>, + port: SerialPort, +) -> ! { + let port = Async::new(port).unwrap(); + let port = BufReader::new(port); + let port = adapter::FromFutures::new(port); + + let config = embassy_net_ppp::Config { + username: b"myuser", + password: b"mypass", + }; + + runner + .run(port, config, |ipv4| { + let Some(addr) = ipv4.address else { + warn!("PPP did not provide an IP address."); + return; + }; + let mut dns_servers = Vec::new(); + for s in ipv4.dns_servers.iter().flatten() { + let _ = dns_servers.push(Ipv4Address::from_bytes(&s.0)); + } + let config = ConfigV4::Static(embassy_net::StaticConfigV4 { + address: Ipv4Cidr::new(Ipv4Address::from_bytes(&addr.0), 0), + gateway: None, + dns_servers, + }); + stack.set_config_v4(config); + }) + .await + .unwrap(); + unreachable!() +} + +#[embassy_executor::task] +async fn main_task(spawner: Spawner) { + let opts: Opts = Opts::parse(); + + // Open serial port + let baudrate = termios::BaudRate::B115200; + let port = SerialPort::new(opts.device.as_str(), baudrate).unwrap(); + + // Init network device + let state = make_static!(embassy_net_ppp::State::<4, 4>::new()); + let (device, runner) = embassy_net_ppp::new(state); + + // Generate random seed + let mut seed = [0; 8]; + OsRng.fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + Config::default(), // don't configure IP yet + make_static!(StackResources::<3>::new()), + seed + )); + + // Launch network task + spawner.spawn(net_task(stack)).unwrap(); + spawner.spawn(ppp_task(stack, runner, port)).unwrap(); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x?}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Trace) + .filter_module("polling", log::LevelFilter::Info) + .filter_module("async_io", log::LevelFilter::Info) + .format_timestamp_nanos() + .init(); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(main_task(spawner)).unwrap(); + }); +} + +mod adapter { + use core::future::poll_fn; + use core::pin::Pin; + + use futures::AsyncBufReadExt; + + /// Adapter from `futures::io` traits. + #[derive(Clone)] + pub struct FromFutures { + inner: T, + } + + impl FromFutures { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + } + + impl embedded_io_async::ErrorType for FromFutures { + type Error = std::io::Error; + } + + impl embedded_io_async::Read for FromFutures { + async fn read(&mut self, buf: &mut [u8]) -> Result { + poll_fn(|cx| Pin::new(&mut self.inner).poll_read(cx, buf)).await + } + } + + impl embedded_io_async::BufRead for FromFutures { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + Pin::new(&mut self.inner).consume(amt) + } + } + + impl embedded_io_async::Write for FromFutures { + async fn write(&mut self, buf: &[u8]) -> Result { + poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + poll_fn(|cx| Pin::new(&mut self.inner).poll_flush(cx)).await + } + } +} diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 9d188513..89ecc499 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32c031c6 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index ca9ab1cf..db9a24d7 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -14,9 +14,9 @@ cortex-m-rt = "0.7.0" defmt = "0.3" defmt-rtt = "0.4" panic-probe = "0.3" -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } static_cell = { version = "1.1", features = ["nightly"]} [profile.release] diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index 8ed9f98f..1564ecfc 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -5,20 +5,26 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::peripherals::ADC; +use embassy_stm32::{adc, bind_interrupts}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + ADC1_COMP => adc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC, &mut Delay); + let mut adc = Adc::new(p.ADC, Irqs, &mut Delay); adc.set_sample_time(SampleTime::Cycles71_5); let mut pin = p.PA1; let mut vrefint = adc.enable_vref(&mut Delay); - let vrefint_sample = adc.read_internal(&mut vrefint); + let vrefint_sample = adc.read(&mut vrefint).await; let convert_to_millivolts = |sample| { // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf // 6.3.4 Embedded reference voltage @@ -28,7 +34,7 @@ async fn main(_spawner: Spawner) { }; loop { - let v = adc.read(&mut pin); + let v = adc.read(&mut pin).await; info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 16796841..b032ba81 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f103c8 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index ed59e279..30947c3c 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -5,9 +5,15 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::Adc; +use embassy_stm32::peripherals::ADC1; +use embassy_stm32::{adc, bind_interrupts}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + ADC1_2 => adc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); @@ -17,7 +23,7 @@ async fn main(_spawner: Spawner) { let mut pin = p.PB1; let mut vrefint = adc.enable_vref(&mut Delay); - let vrefint_sample = adc.read(&mut vrefint); + let vrefint_sample = adc.read(&mut vrefint).await; let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/CD00161566.pdf // 5.3.4 Embedded reference voltage @@ -27,7 +33,7 @@ async fn main(_spawner: Spawner) { }; loop { - let v = adc.read(&mut pin); + let v = adc.read(&mut pin).await; info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 54eadd1a..1314b6b1 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f207zg to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f2/src/bin/pll.rs b/examples/stm32f2/src/bin/pll.rs index 17f09538..89493761 100644 --- a/examples/stm32f2/src/bin/pll.rs +++ b/examples/stm32f2/src/bin/pll.rs @@ -39,9 +39,9 @@ async fn main(_spawner: Spawner) { // System clock comes from PLL (= the 120 MHz main PLL output) config.rcc.mux = ClockSrc::PLL; // 120 MHz / 4 = 30 MHz APB1 frequency - config.rcc.apb1_pre = APBPrescaler::Div4; + config.rcc.apb1_pre = APBPrescaler::DIV4; // 120 MHz / 2 = 60 MHz APB2 frequency - config.rcc.apb2_pre = APBPrescaler::Div2; + config.rcc.apb2_pre = APBPrescaler::DIV2; let _p = embassy_stm32::init(config); diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 5d8f5f74..534e783d 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f303ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 85f01a69..ce8c212a 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, Irqs, p.DMA1_CH4, NoDma, config); + let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, Irqs, p.DMA1_CH4, NoDma, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index 65183b88..239c58b3 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f334/src/bin/adc.rs b/examples/stm32f334/src/bin/adc.rs new file mode 100644 index 00000000..ed246a7d --- /dev/null +++ b/examples/stm32f334/src/bin/adc.rs @@ -0,0 +1,56 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::peripherals::ADC1; +use embassy_stm32::rcc::AdcClockSource; +use embassy_stm32::time::mhz; +use embassy_stm32::{adc, bind_interrupts, Config}; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC1_2 => adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.sysclk = Some(mhz(64)); + config.rcc.hclk = Some(mhz(64)); + config.rcc.pclk1 = Some(mhz(32)); + config.rcc.pclk2 = Some(mhz(64)); + config.rcc.adc = Some(AdcClockSource::PllDiv1); + + let mut p = embassy_stm32::init(config); + + info!("create adc..."); + + let mut adc = Adc::new(p.ADC1, Irqs, &mut Delay); + + adc.set_sample_time(SampleTime::Cycles601_5); + + info!("enable vrefint..."); + + let mut vrefint = adc.enable_vref(&mut Delay); + let mut temperature = adc.enable_temperature(); + + loop { + let vref = adc.read(&mut vrefint).await; + info!("read vref: {} (should be {})", vref, vrefint.value()); + + let temp = adc.read(&mut temperature).await; + info!("read temperature: {}", temp); + + let pin = adc.read(&mut p.PA0).await; + info!("read pin: {}", pin); + + let pin_mv = (pin as u32 * vrefint.value() as u32 / vref as u32) * 3300 / 4095; + info!("computed pin mv: {}", pin_mv); + + Timer::after(Duration::from_millis(500)).await; + } +} diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index 2660b10c..aebc421b 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -5,6 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::hrtim::*; +use embassy_stm32::rcc::HrtimClockSource; use embassy_stm32::time::{khz, mhz}; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; @@ -17,6 +18,7 @@ async fn main(_spawner: Spawner) { config.rcc.hclk = Some(mhz(64)); config.rcc.pclk1 = Some(mhz(32)); config.rcc.pclk2 = Some(mhz(64)); + config.rcc.hrtim = HrtimClockSource::PllClk; let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index ed456c5c..4b4fb479 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f429zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 1c9a0b35..dd10385c 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { // Startup delay can be combined to the maximum of either delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); - let vrefint_sample = adc.read_internal(&mut vrefint); + let vrefint_sample = adc.read(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/DM00071990.pdf @@ -55,12 +55,12 @@ async fn main(_spawner: Spawner) { info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); // Read internal temperature - let v = adc.read_internal(&mut temp); + let v = adc.read(&mut temp); let celcius = convert_to_celcius(v); info!("Internal temp: {} ({} C)", v, celcius); // Read internal voltage reference - let v = adc.read_internal(&mut vrefint); + let v = adc.read(&mut vrefint); info!("VrefInt: {}", v); Timer::after(Duration::from_millis(100)).await; diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 393e60b7..16bf5d94 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -79,7 +79,10 @@ async fn main(spawner: Spawner) -> ! { )); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(stack))); + + // Ensure DHCP configuration is up before trying connect + stack.wait_config_up().await; info!("Network task initialized"); @@ -97,6 +100,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.connect(remote_endpoint).await; if let Err(e) = r { info!("connect error: {:?}", e); + Timer::after(Duration::from_secs(1)).await; continue; } info!("connected!"); @@ -105,7 +109,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(&buf).await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32f4/src/bin/rtc.rs b/examples/stm32f4/src/bin/rtc.rs index 0eca5820..e3374600 100644 --- a/examples/stm32f4/src/bin/rtc.rs +++ b/examples/stm32f4/src/bin/rtc.rs @@ -5,13 +5,18 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig}; +use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(Default::default()); + let mut config = Config::default(); + config.rcc.lsi = true; + config.rcc.rtc = Option::Some(RtcClockSource::LSI); + let p = embassy_stm32::init(config); + info!("Hello World!"); let now = NaiveDate::from_ymd_opt(2020, 5, 15) @@ -23,8 +28,11 @@ async fn main(_spawner: Spawner) { rtc.set_datetime(now.into()).expect("datetime not set"); - // In reality the delay would be much longer - Timer::after(Duration::from_millis(20000)).await; + loop { + let now: NaiveDateTime = rtc.now().unwrap().into(); - let _then: NaiveDateTime = rtc.now().unwrap().into(); + info!("{}", now.timestamp()); + + Timer::after(Duration::from_millis(1000)).await; + } } diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 7680fe84..45e94715 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs @@ -20,7 +20,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, NoDma, NoDma, config); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, NoDma, NoDma, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index c0a64d94..71abc289 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { let mut tx_buf = [0u8; 32]; let mut rx_buf = [0u8; 32]; - let mut buf_usart = BufferedUart::new(p.USART3, Irqs, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config); + let mut buf_usart = BufferedUart::new(p.USART3, Irqs, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config).unwrap(); loop { let buf = buf_usart.fill_buf().await.unwrap(); diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index 3408ec37..dca25a78 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, p.DMA1_CH3, NoDma, config); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, p.DMA1_CH3, NoDma, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index b658a964..bf8f413d 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f767zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io-async = { version = "0.5.0" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index 70b3b2a7..bc4ed289 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { let mut pin = p.PA3; let mut vrefint = adc.enable_vrefint(); - let vrefint_sample = adc.read_internal(&mut vrefint); + let vrefint_sample = adc.read(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/DM00273119.pdf // 6.3.27 Reference voltage diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index f0e280c3..93c97c8e 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -80,7 +80,10 @@ async fn main(spawner: Spawner) -> ! { )); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(stack))); + + // Ensure DHCP configuration is up before trying connect + stack.wait_config_up().await; info!("Network task initialized"); @@ -98,6 +101,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.connect(remote_endpoint).await; if let Err(e) = r { info!("connect error: {:?}", e); + Timer::after(Duration::from_secs(1)).await; continue; } info!("connected!"); @@ -106,7 +110,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(&buf).await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index 4700287a..ba064081 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs @@ -20,7 +20,7 @@ bind_interrupts!(struct Irqs { async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, Irqs, p.DMA1_CH1, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, Irqs, p.DMA1_CH1, NoDma, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 4e88151a..b4b423d5 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32g071rb to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 7bb05dbc..59da0628 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -7,10 +7,12 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32g491re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +usbd-hid = "0.6.0" defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs new file mode 100644 index 00000000..a792748b --- /dev/null +++ b/examples/stm32g4/src/bin/adc.rs @@ -0,0 +1,41 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSrc}; +use embassy_stm32::Config; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + config.rcc.pll = Some(Pll { + source: PllSrc::HSI16, + prediv_m: PllM::Div4, + mul_n: PllN::Mul85, + div_p: None, + div_q: None, + // Main system clock at 170 MHz + div_r: Some(PllR::Div2), + }); + + config.rcc.adc12_clock_source = AdcClockSource::SysClk; + config.rcc.mux = ClockSrc::PLL; + + let mut p = embassy_stm32::init(config); + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC2, &mut Delay); + adc.set_sample_time(SampleTime::Cycles32_5); + + loop { + let measured = adc.read(&mut p.PA7); + info!("measured: {}", measured); + Timer::after(Duration::from_millis(500)).await; + } +} diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index f7a1de63..42a42618 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h563zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } embedded-io-async = { version = "0.5.0" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } @@ -29,7 +29,7 @@ heapless = { version = "0.7.5", default-features = false } rand_core = "0.6.3" critical-section = "1.1" micromath = "2.0.0" -stm32-fmc = "0.2.4" +stm32-fmc = "0.3.0" embedded-storage = "0.3.0" static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 763520ab..4e92d064 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -48,10 +48,10 @@ async fn main(spawner: Spawner) -> ! { divq: Some(2), divr: None, }); - config.rcc.ahb_pre = AHBPrescaler::NotDivided; - config.rcc.apb1_pre = APBPrescaler::NotDivided; - config.rcc.apb2_pre = APBPrescaler::NotDivided; - config.rcc.apb3_pre = APBPrescaler::NotDivided; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV1; + config.rcc.apb2_pre = APBPrescaler::DIV1; + config.rcc.apb3_pre = APBPrescaler::DIV1; config.rcc.sys = Sysclk::Pll1P; config.rcc.voltage_scale = VoltageScale::Scale0; let p = embassy_stm32::init(config); @@ -101,6 +101,9 @@ async fn main(spawner: Spawner) -> ! { // Launch network task unwrap!(spawner.spawn(net_task(&stack))); + // Ensure DHCP configuration is up before trying connect + stack.wait_config_up().await; + info!("Network task initialized"); // Then we can use it! @@ -125,7 +128,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(b"Hello\n").await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs index 0abb94ab..db04d4e5 100644 --- a/examples/stm32h5/src/bin/usart.rs +++ b/examples/stm32h5/src/bin/usart.rs @@ -20,7 +20,7 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs index 48264f88..bafe5083 100644 --- a/examples/stm32h5/src/bin/usart_dma.rs +++ b/examples/stm32h5/src/bin/usart_dma.rs @@ -23,7 +23,7 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, NoDma, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs index a6b2e690..d9037c01 100644 --- a/examples/stm32h5/src/bin/usart_split.rs +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -36,7 +36,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1, config).unwrap(); unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); let (mut tx, rx) = usart.split(); diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 336eed64..cbe540a0 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -35,10 +35,10 @@ async fn main(_spawner: Spawner) { divq: None, divr: None, }); - config.rcc.ahb_pre = AHBPrescaler::Div2; - config.rcc.apb1_pre = APBPrescaler::Div4; - config.rcc.apb2_pre = APBPrescaler::Div2; - config.rcc.apb3_pre = APBPrescaler::Div4; + config.rcc.ahb_pre = AHBPrescaler::DIV2; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.apb3_pre = APBPrescaler::DIV4; config.rcc.sys = Sysclk::Pll1P; config.rcc.voltage_scale = VoltageScale::Scale0; let p = embassy_stm32::init(config); diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 1ee11b07..c1d49963 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "memory-x", "unstable-pac", "unstable-traits"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } embedded-io-async = { version = "0.5.0" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } @@ -29,7 +29,7 @@ heapless = { version = "0.7.5", default-features = false } rand_core = "0.6.3" critical-section = "1.1" micromath = "2.0.0" -stm32-fmc = "0.2.4" +stm32-fmc = "0.3.0" embedded-storage = "0.3.0" static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index 0e1e28c7..77922d4b 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs @@ -5,8 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; -use embassy_stm32::rcc::AdcClockSource; -use embassy_stm32::time::mhz; use embassy_stm32::Config; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,10 +12,34 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.per_ck = Some(mhz(64)); - config.rcc.adc_clock_source = AdcClockSource::PerCk; + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.adc_clock_source = AdcClockSource::PLL2_P; + } let mut p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 6f75a063..de8ddc29 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs @@ -6,8 +6,8 @@ use embassy_executor::Spawner; use embassy_stm32::dcmi::{self, *}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::i2c::I2c; -use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; -use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::rcc::{Mco, Mco1Source}; +use embassy_stm32::time::khz; use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; use embassy_time::{Duration, Timer}; use ov7725::*; @@ -26,17 +26,30 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(400)); - config.rcc.pll1.q_ck = Some(mhz(100)); - config.rcc.pclk1 = Some(mhz(100)); - config.rcc.pclk2 = Some(mhz(100)); - config.rcc.pclk3 = Some(mhz(100)); - config.rcc.pclk4 = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // 100mhz + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); defmt::info!("Hello World!"); - let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3)); + let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, 3); let mut led = Output::new(p.PE3, Level::High, Speed::Low); let cam_i2c = I2c::new( diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index ee078286..93df7a31 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs @@ -6,7 +6,6 @@ use cortex_m_rt::entry; use defmt::*; use embassy_stm32::dac::{DacCh1, DacChannel, Value}; use embassy_stm32::dma::NoDma; -use embassy_stm32::time::mhz; use embassy_stm32::Config; use {defmt_rtt as _, panic_probe as _}; @@ -15,9 +14,34 @@ fn main() -> ! { info!("Hello World, dude!"); let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.adc_clock_source = AdcClockSource::PLL2_P; + } let p = embassy_stm32::init(config); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index a9cb5d1e..8c921abc 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -8,7 +8,7 @@ use embassy_stm32::dac::{DacChannel, ValueArray}; use embassy_stm32::pac::timer::vals::{Mms, Opm}; use embassy_stm32::peripherals::{TIM6, TIM7}; use embassy_stm32::rcc::low_level::RccPeripheral; -use embassy_stm32::time::{mhz, Hertz}; +use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Basic16bitInstance; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; @@ -22,9 +22,34 @@ pub type Dac2Type = #[embassy_executor::main] async fn main(spawner: Spawner) { let mut config = embassy_stm32::Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(100)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.adc_clock_source = AdcClockSource::PLL2_P; + } // Initialize the board and obtain a Peripherals instance let p: embassy_stm32::Peripherals = embassy_stm32::init(config); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 26a386e4..1b5d71ed 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -10,7 +10,6 @@ use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; -use embassy_stm32::time::mhz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io_async::Write; @@ -33,9 +32,27 @@ async fn net_task(stack: &'static Stack) -> ! { #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.hsi48 = true; // needed for RNG + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); @@ -83,6 +100,9 @@ async fn main(spawner: Spawner) -> ! { // Launch network task unwrap!(spawner.spawn(net_task(&stack))); + // Ensure DHCP configuration is up before trying connect + stack.wait_config_up().await; + info!("Network task initialized"); // Then we can use it! @@ -99,6 +119,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.connect(remote_endpoint).await; if let Err(e) = r { info!("connect error: {:?}", e); + Timer::after(Duration::from_secs(1)).await; continue; } info!("connected!"); @@ -106,7 +127,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(b"Hello\n").await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 6664410c..3abd31c7 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -10,7 +10,6 @@ use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; -use embassy_stm32::time::mhz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io_async::Write; @@ -34,9 +33,27 @@ async fn net_task(stack: &'static Stack) -> ! { #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.hsi48 = true; // needed for RNG + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); @@ -82,13 +99,13 @@ async fn main(spawner: Spawner) -> ! { )); // Launch network task - unwrap!(spawner.spawn(net_task(&stack))); + unwrap!(spawner.spawn(net_task(stack))); + + // Ensure DHCP configuration is up before trying connect + stack.wait_config_up().await; info!("Network task initialized"); - // To ensure DHCP configuration before trying connect - Timer::after(Duration::from_secs(20)).await; - static STATE: TcpClientState<1, 1024, 1024> = TcpClientState::new(); let client = TcpClient::new(&stack, &STATE); @@ -108,7 +125,7 @@ async fn main(spawner: Spawner) -> ! { let r = connection.write_all(b"Hello\n").await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h7/src/bin/fmc.rs b/examples/stm32h7/src/bin/fmc.rs index 85c690fe..de0b351d 100644 --- a/examples/stm32h7/src/bin/fmc.rs +++ b/examples/stm32h7/src/bin/fmc.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::fmc::Fmc; -use embassy_stm32::time::mhz; use embassy_stm32::Config; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -13,9 +12,26 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // 100mhz + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 45b0872b..a1e955c3 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::low_level::AFType; use embassy_stm32::gpio::Speed; -use embassy_stm32::time::{khz, mhz, Hertz}; +use embassy_stm32::time::{khz, Hertz}; use embassy_stm32::timer::*; use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; @@ -15,13 +15,27 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(400)); - config.rcc.pll1.q_ck = Some(mhz(100)); - config.rcc.pclk1 = Some(mhz(100)); - config.rcc.pclk2 = Some(mhz(100)); - config.rcc.pclk3 = Some(mhz(100)); - config.rcc.pclk4 = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.hsi48 = true; // needed for RNG + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // 100 Mhz + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/mco.rs b/examples/stm32h7/src/bin/mco.rs index 036455d5..9d6d805a 100644 --- a/examples/stm32h7/src/bin/mco.rs +++ b/examples/stm32h7/src/bin/mco.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; +use embassy_stm32::rcc::{Mco, Mco1Source}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::High, Speed::Low); - let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(8)); + let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, 8); loop { info!("high"); diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index aa5ec1bc..5c8e57aa 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; -use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; use embassy_stm32::Config; @@ -15,13 +15,26 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(400)); - config.rcc.pll1.q_ck = Some(mhz(100)); - config.rcc.pclk1 = Some(mhz(100)); - config.rcc.pclk2 = Some(mhz(100)); - config.rcc.pclk3 = Some(mhz(100)); - config.rcc.pclk4 = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index 7c8c50ec..af1d6ebb 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, peripherals, rng}; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -14,7 +14,9 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(Default::default()); + let mut config = Config::default(); + config.rcc.hsi48 = true; // needed for RNG. + let p = embassy_stm32::init(config); info!("Hello World!"); let mut rng = Rng::new(p.RNG, Irqs); diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index ce91b6b1..752aefdf 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs @@ -16,7 +16,26 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(200)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(4), // default clock chosen by SDMMCSEL. 200 Mhz + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index 28bba2b8..9fe46f03 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -38,9 +38,26 @@ fn main() -> ! { info!("Hello World!"); let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(4), // used by SPI3. 100Mhz. + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); let mut spi_config = spi::Config::default(); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index f6e30cfa..88d65d5b 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -34,9 +34,26 @@ fn main() -> ! { info!("Hello World!"); let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(4), // used by SPI3. 100Mhz. + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); let mut spi_config = spi::Config::default(); diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index 0abb94ab..db04d4e5 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs @@ -20,7 +20,7 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index f1fe7fce..249050fd 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs @@ -23,7 +23,7 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, NoDma, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index aa075345..61c9f195 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -36,7 +36,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, p.DMA1_CH1, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, p.DMA1_CH1, config).unwrap(); unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); let (mut tx, rx) = usart.split(); diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 97291f60..14de4356 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -4,7 +4,6 @@ use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_stm32::time::mhz; use embassy_stm32::usb_otg::{Driver, Instance}; use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; @@ -22,9 +21,27 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.hsi48 = true; // needed for USB + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); // Create the driver, from the HAL. diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index d4e0da0b..502ebfc8 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -12,13 +12,13 @@ nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstab [dependencies] # Change stm32l072cz to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } -lora-phy = { version = "1", optional = true } -lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } -lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } +lora-phy = { version = "2", optional = true } +lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } +lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" @@ -37,6 +37,3 @@ static_cell = "1.1" [profile.release] debug = 2 - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs index 7729b416..900848fd 100644 --- a/examples/stm32l0/src/bin/lora_cad.rs +++ b/examples/stm32l0/src/bin/lora_cad.rs @@ -41,10 +41,8 @@ async fn main(_spawner: Spawner) { let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { + match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index 10608aeb..7a93737e 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -21,6 +21,7 @@ use lora_phy::LoRa; use lorawan::default_crypto::DefaultFactory as Crypto; use lorawan_device::async_device::lora_radio::LoRaRadio; use lorawan_device::async_device::{region, Device, JoinMode}; +use lorawan_device::{AppEui, AppKey, DevEui}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -50,10 +51,8 @@ async fn main(_spawner: Spawner) { let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); - let mut delay = Delay; - let lora = { - match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), true, &mut delay).await { + match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), true, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -71,9 +70,9 @@ async fn main(_spawner: Spawner) { // TODO: Adjust the EUI and Keys according to your network credentials match device .join(&JoinMode::OTAA { - deveui: [0, 0, 0, 0, 0, 0, 0, 0], - appeui: [0, 0, 0, 0, 0, 0, 0, 0], - appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]), + appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]), + appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), }) .await { diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs index 0f9f6095..edd14bb8 100644 --- a/examples/stm32l0/src/bin/lora_p2p_receive.rs +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -41,10 +41,8 @@ async fn main(_spawner: Spawner) { let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { + match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -88,7 +86,7 @@ async fn main(_spawner: Spawner) { }; match lora - .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) + .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false) .await { Ok(()) => {} diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs index c85c3c2b..23cc1c6f 100644 --- a/examples/stm32l0/src/bin/lora_p2p_send.rs +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs @@ -41,10 +41,8 @@ async fn main(_spawner: Spawner) { let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { + match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -97,7 +95,7 @@ async fn main(_spawner: Spawner) { } }; - match lora.sleep(&mut delay).await { + match lora.sleep(false).await { Ok(()) => info!("Sleep successful"), Err(err) => info!("Sleep unsuccessful = {}", err), } diff --git a/examples/stm32l0/src/bin/usart_dma.rs b/examples/stm32l0/src/bin/usart_dma.rs index eae8f345..62c9b559 100644 --- a/examples/stm32l0/src/bin/usart_dma.rs +++ b/examples/stm32l0/src/bin/usart_dma.rs @@ -15,7 +15,7 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, Irqs, p.DMA1_CH2, p.DMA1_CH3, Config::default()); + let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, Irqs, p.DMA1_CH2, p.DMA1_CH3, Config::default()).unwrap(); usart.write(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index f5dabcc4..5107a1a0 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -18,13 +18,11 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hi!"); - static mut TX_BUFFER: [u8; 8] = [0; 8]; - static mut RX_BUFFER: [u8; 256] = [0; 256]; - let mut config = Config::default(); config.baudrate = 9600; - - let mut usart = unsafe { BufferedUart::new(p.USART2, Irqs, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) }; + let mut tx_buf = [0u8; 256]; + let mut rx_buf = [0u8; 256]; + let mut usart = BufferedUart::new(p.USART2, Irqs, p.PA3, p.PA2, &mut tx_buf, &mut rx_buf, config).unwrap(); usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 192fd3e3..a75275a0 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -5,9 +5,9 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } defmt = "0.3" diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index 36e74e5a..db3a7cef 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,7 +2,7 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` #runner = "probe-rs run --chip STM32L475VGT6" #runner = "probe-rs run --chip STM32L475VG" -runner = "probe-rs run --chip STM32L4S5VI" +runner = "probe-run --chip STM32L4S5QI" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index f552a610..59e89c53 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -1,17 +1,22 @@ [package] edition = "2021" name = "embassy-stm32l4-examples" -version = "0.1.0" +version = "0.1.1" license = "MIT OR Apache-2.0" [dependencies] # Change stm32l4s5vi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "memory-x", "time-driver-any", "exti", "unstable-traits", "chrono"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "unstable-traits", "chrono"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", "unstable-traits", "nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "udp", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +embedded-io-async = { version = "0.5.0", features = ["defmt-03"] } +embedded-io = { version = "0.5.0", features = ["defmt-03"] } defmt = "0.3" defmt-rtt = "0.4" @@ -21,10 +26,13 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } embedded-hal-async = { version = "=1.0.0-rc.1" } +embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } chrono = { version = "^0.4", default-features = false } +rand = { version = "0.8.5", default-features = false } +static_cell = {version = "1.1", features = ["nightly"]} micromath = "2.0.0" diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index 294ea456..eb1eed01 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -23,7 +23,8 @@ async fn main(_spawner: Spawner) { PLLMul::Mul20, None, ); - config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; + config.rcc.lse = Some(Hertz(32_768)); + config.rcc.rtc_mux = rcc::RtcClockSource::LSE; embassy_stm32::init(config) }; info!("Hello World!"); diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs new file mode 100644 index 00000000..28752158 --- /dev/null +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -0,0 +1,450 @@ +#![deny(clippy::pedantic)] +#![allow(clippy::doc_markdown)] +#![no_main] +#![no_std] +// Needed unitl https://github.com/rust-lang/rust/issues/63063 is stablised. +#![feature(type_alias_impl_trait)] +#![feature(associated_type_bounds)] +#![allow(clippy::missing_errors_doc)] + +// This example works on a ANALOG DEVICE EVAL-ADIN110EBZ board. +// Settings switch S201 "HW CFG": +// - Without SPI CRC: OFF-ON-OFF-OFF-OFF +// - With SPI CRC: ON -ON-OFF-OFF-OFF +// Settings switch S303 "uC CFG": +// - CFG0: On = static ip, Off = Dhcp +// - CFG1: Ethernet `FCS` on TX path: On, Off +// The webserver shows the actual temperature of the onboard i2c temp sensor. + +use core::marker::PhantomData; +use core::sync::atomic::{AtomicI32, Ordering}; + +use defmt::{error, info, println, unwrap, Format}; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_futures::select::{select, Either}; +use embassy_futures::yield_now; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4}; +use embassy_time::{Delay, Duration, Ticker, Timer}; +use embedded_hal_async::i2c::I2c as I2cBus; +use embedded_io::Write as bWrite; +use embedded_io_async::Write; +use hal::gpio::{Input, Level, Output, Speed}; +use hal::i2c::{self, I2c}; +use hal::rcc::{self}; +use hal::rng::{self, Rng}; +use hal::{bind_interrupts, exti, pac, peripherals}; +use heapless::Vec; +use rand::RngCore; +use static_cell::make_static; +use {embassy_stm32 as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + I2C3_EV => i2c::InterruptHandler; + RNG => rng::InterruptHandler; +}); + +use embassy_net_adin1110::{self, Device, Runner, ADIN1110}; +use embedded_hal_bus::spi::ExclusiveDevice; +use hal::gpio::Pull; +use hal::i2c::Config as I2C_Config; +use hal::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; +use hal::spi::{Config as SPI_Config, Spi}; +use hal::time::Hertz; + +// Basic settings +// MAC-address used by the adin1110 +const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; +// Static IP settings +const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); +// Listen port for the webserver +const HTTP_LISTEN_PORT: u16 = 80; + +pub type SpeSpi = Spi<'static, peripherals::SPI2, peripherals::DMA1_CH1, peripherals::DMA1_CH2>; +pub type SpeSpiCs = ExclusiveDevice, Delay>; +pub type SpeInt = exti::ExtiInput<'static, peripherals::PB11>; +pub type SpeRst = Output<'static, peripherals::PC7>; +pub type Adin1110T = ADIN1110; +pub type TempSensI2c = I2c<'static, peripherals::I2C3, peripherals::DMA1_CH6, peripherals::DMA1_CH7>; + +static TEMP: AtomicI32 = AtomicI32::new(0); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + defmt::println!("Start main()"); + + let mut config = embassy_stm32::Config::default(); + + // 80Mhz clock (Source: 8 / SrcDiv: 1 * PLLMul 20 / ClkDiv 2) + // 80MHz highest frequency for flash 0 wait. + config.rcc.mux = ClockSrc::PLL( + PLLSource::HSE(Hertz(8_000_000)), + PLLClkDiv::Div2, + PLLSrcDiv::Div1, + PLLMul::Mul20, + None, + ); + config.rcc.hsi48 = true; // needed for rng + config.rcc.rtc_mux = rcc::RtcClockSource::LSI; + + let dp = embassy_stm32::init(config); + + // RM0432rev9, 5.1.2: Independent I/O supply rail + // After reset, the I/Os supplied by VDDIO2 are logically and electrically isolated and + // therefore are not available. The isolation must be removed before using any I/O from + // PG[15:2], by setting the IOSV bit in the PWR_CR2 register, once the VDDIO2 supply is present + pac::PWR.cr2().modify(|w| w.set_iosv(true)); + + let reset_status = pac::RCC.bdcr().read().0; + defmt::println!("bdcr before: 0x{:X}", reset_status); + + defmt::println!("Setup IO pins"); + + // Setup LEDs + let _led_uc1_green = Output::new(dp.PC13, Level::Low, Speed::Low); + let mut led_uc2_red = Output::new(dp.PE2, Level::High, Speed::Low); + let led_uc3_yellow = Output::new(dp.PE6, Level::High, Speed::Low); + let led_uc4_blue = Output::new(dp.PG15, Level::High, Speed::Low); + + // Read the uc_cfg switches + let uc_cfg0 = Input::new(dp.PB2, Pull::None); + let uc_cfg1 = Input::new(dp.PF11, Pull::None); + let _uc_cfg2 = Input::new(dp.PG6, Pull::None); + let _uc_cfg3 = Input::new(dp.PG11, Pull::None); + + // Setup I2C pins + let temp_sens_i2c = I2c::new( + dp.I2C3, + dp.PG7, + dp.PG8, + Irqs, + dp.DMA1_CH6, + dp.DMA1_CH7, + Hertz(100_000), + I2C_Config::default(), + ); + + // Setup IO and SPI for the SPE chip + let spe_reset_n = Output::new(dp.PC7, Level::Low, Speed::Low); + let spe_cfg0 = Input::new(dp.PC8, Pull::None); + let spe_cfg1 = Input::new(dp.PC9, Pull::None); + let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low); + + let spe_int = Input::new(dp.PB11, Pull::None); + let spe_int = exti::ExtiInput::new(spe_int, dp.EXTI11); + + let spe_spi_cs_n = Output::new(dp.PB12, Level::High, Speed::High); + let spe_spi_sclk = dp.PB13; + let spe_spi_miso = dp.PB14; + let spe_spi_mosi = dp.PB15; + + // Don't turn the clock to high, clock must fit within the system clock as we get a runtime panic. + let mut spi_config = SPI_Config::default(); + spi_config.frequency = Hertz(25_000_000); + + let spe_spi: SpeSpi = Spi::new( + dp.SPI2, + spe_spi_sclk, + spe_spi_mosi, + spe_spi_miso, + dp.DMA1_CH1, + dp.DMA1_CH2, + spi_config, + ); + let spe_spi = SpeSpiCs::new(spe_spi, spe_spi_cs_n, Delay); + + let cfg0_without_crc = spe_cfg0.is_high(); + let cfg1_spi_mode = spe_cfg1.is_high(); + let uc_cfg1_fcs_en = uc_cfg1.is_low(); + + defmt::println!( + "ADIN1110: CFG SPI-MODE 1-{}, CRC-bit 0-{} FCS-{}", + cfg1_spi_mode, + cfg0_without_crc, + uc_cfg1_fcs_en + ); + + // Check the SPI mode selected with the "HW CFG" dip-switch + if !cfg1_spi_mode { + error!("Driver doesn´t support SPI Protolcol \"OPEN Alliance\".\nplease use the \"Generic SPI\"! Turn On \"HW CFG\": \"SPI_CFG1\""); + loop { + led_uc2_red.toggle(); + Timer::after(Duration::from_hz(10)).await; + } + }; + + let state = make_static!(embassy_net_adin1110::State::<8, 8>::new()); + + let (device, runner) = embassy_net_adin1110::new( + MAC, + state, + spe_spi, + spe_int, + spe_reset_n, + !cfg0_without_crc, + uc_cfg1_fcs_en, + ) + .await; + + // Start task blink_led + unwrap!(spawner.spawn(heartbeat_led(led_uc3_yellow))); + // Start task temperature measurement + unwrap!(spawner.spawn(temp_task(temp_sens_i2c, led_uc4_blue))); + // Start ethernet task + unwrap!(spawner.spawn(ethernet_task(runner))); + + let mut rng = Rng::new(dp.RNG, Irqs); + // Generate random seed + let seed = rng.next_u64(); + + let ip_cfg = if uc_cfg0.is_low() { + println!("Waiting for DHCP..."); + let dhcp4_config = embassy_net::DhcpConfig::default(); + embassy_net::Config::dhcpv4(dhcp4_config) + } else { + embassy_net::Config::ipv4_static(StaticConfigV4 { + address: IP_ADDRESS, + gateway: None, + dns_servers: Vec::new(), + }) + }; + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + ip_cfg, + make_static!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(stack))); + + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut mb_buf = [0; 4096]; + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(1))); + + info!("Listening on http://{}:{}...", local_addr, HTTP_LISTEN_PORT); + if let Err(e) = socket.accept(HTTP_LISTEN_PORT).await { + defmt::error!("accept error: {:?}", e); + continue; + } + + loop { + let _n = match socket.read(&mut mb_buf).await { + Ok(0) => { + defmt::info!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + defmt::error!("{:?}", e); + break; + } + }; + led_uc2_red.set_low(); + + let status_line = "HTTP/1.1 200 OK"; + let contents = PAGE; + let length = contents.len(); + + let _ = write!( + &mut mb_buf[..], + "{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}\r\n\0" + ); + let loc = mb_buf.iter().position(|v| *v == b'#').unwrap(); + + let temp = TEMP.load(Ordering::Relaxed); + let cel = temp / 1000; + let mcel = temp % 1000; + + info!("{}.{}", cel, mcel); + + let _ = write!(&mut mb_buf[loc..loc + 7], "{cel}.{mcel}"); + + let n = mb_buf.iter().position(|v| *v == 0).unwrap(); + + if let Err(e) = socket.write_all(&mb_buf[..n]).await { + error!("write error: {:?}", e); + break; + } + + led_uc2_red.set_high(); + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { + loop { + if let Some(config) = stack.config_v4() { + return config; + } + yield_now().await; + } +} + +#[embassy_executor::task] +async fn heartbeat_led(mut led: Output<'static, peripherals::PE6>) { + let mut tmr = Ticker::every(Duration::from_hz(3)); + loop { + led.toggle(); + tmr.next().await; + } +} + +// ADT7422 +#[embassy_executor::task] +async fn temp_task(temp_dev_i2c: TempSensI2c, mut led: Output<'static, peripherals::PG15>) -> ! { + let mut tmr = Ticker::every(Duration::from_hz(1)); + let mut temp_sens = ADT7422::new(temp_dev_i2c, 0x48).unwrap(); + + loop { + led.set_low(); + match select(temp_sens.read_temp(), Timer::after(Duration::from_millis(500))).await { + Either::First(i2c_ret) => match i2c_ret { + Ok(value) => { + led.set_high(); + let temp = i32::from(value); + println!("TEMP: {:04x}, {}", temp, temp * 78 / 10); + TEMP.store(temp * 78 / 10, Ordering::Relaxed); + } + Err(e) => defmt::println!("ADT7422: {}", e), + }, + Either::Second(_) => println!("Timeout"), + } + + tmr.next().await; + } +} + +#[embassy_executor::task] +async fn ethernet_task(runner: Runner<'static, SpeSpiCs, SpeInt, SpeRst>) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +// same panicking *behavior* as `panic-probe` but doesn't print a panic message +// this prevents the panic message being printed *twice* when `defmt::panic` is invoked +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +#[allow(non_camel_case_types)] +#[repr(C)] +pub enum Registers { + Temp_MSB = 0x00, + Temp_LSB, + Status, + Cfg, + T_HIGH_MSB, + T_HIGH_LSB, + T_LOW_MSB, + T_LOW_LSB, + T_CRIT_MSB, + T_CRIT_LSB, + T_HYST, + ID, + SW_RESET = 0x2F, +} + +pub struct ADT7422<'d, BUS: I2cBus> { + addr: u8, + phantom: PhantomData<&'d ()>, + bus: BUS, +} + +#[derive(Debug, Format)] +pub enum Error { + I2c(I2cError), + Address, +} + +impl<'d, BUS> ADT7422<'d, BUS> +where + BUS: I2cBus, + BUS::Error: Format, +{ + pub fn new(bus: BUS, addr: u8) -> Result> { + if !(0x48..=0x4A).contains(&addr) { + return Err(Error::Address); + } + + Ok(Self { + bus, + phantom: PhantomData, + addr, + }) + } + + pub async fn init(&mut self) -> Result<(), Error> { + let mut cfg = 0b000_0000; + // if self.int.is_some() { + // // Set 1 SPS mode + // cfg |= 0b10 << 5; + // } else { + // One shot mode + cfg |= 0b01 << 5; + // } + + self.write_cfg(cfg).await + } + + pub async fn read(&mut self, reg: Registers) -> Result> { + let mut buffer = [0u8; 1]; + self.bus + .write_read(self.addr, &[reg as u8], &mut buffer) + .await + .map_err(Error::I2c)?; + Ok(buffer[0]) + } + + pub async fn write_cfg(&mut self, cfg: u8) -> Result<(), Error> { + let buf = [Registers::Cfg as u8, cfg]; + self.bus.write(self.addr, &buf).await.map_err(Error::I2c) + } + + pub async fn read_temp(&mut self) -> Result> { + let mut buffer = [0u8; 2]; + + // if let Some(int) = &mut self.int { + // // Wait for interrupt + // int.wait_for_low().await.unwrap(); + // } else { + // Start: One shot + let cfg = 0b01 << 5; + self.write_cfg(cfg).await?; + Timer::after(Duration::from_millis(250)).await; + self.bus + .write_read(self.addr, &[Registers::Temp_MSB as u8], &mut buffer) + .await + .map_err(Error::I2c)?; + Ok(i16::from_be_bytes(buffer)) + } +} + +// Web page +const PAGE: &str = r#" + + + + + ADIN1110 with Rust + + +

EVAL-ADIN1110EBZ

+
Temp Sensor ADT7422: #00.00 °C
+ +"#; diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index beb5ec55..f4da6b5a 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs @@ -19,7 +19,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, NoDma, NoDma, config); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, NoDma, NoDma, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index b7d4cb01..2f3b2a0f 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, NoDma, config); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, NoDma, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index b46c2570..583e1a77 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l552ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index f928e7a6..e361856c 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32u585ai to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index f58a5189..320678dd 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -8,10 +8,10 @@ license = "MIT OR Apache-2.0" # Change stm32wb55rg to your chip name in both dependencies, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ieee802154", "nightly"], optional=true } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", "nightly"], optional=true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index ea150c67..e58da8e3 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index dd67249c..80e835c1 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -53,7 +53,7 @@ async fn main(_spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index fc49c3c4..2f53f5df 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index 5745ebd0..12c6aeeb 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -31,7 +31,7 @@ async fn main(_spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. diff --git a/examples/stm32wba/.cargo/config.toml b/examples/stm32wba/.cargo/config.toml new file mode 100644 index 00000000..47741339 --- /dev/null +++ b/examples/stm32wba/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs run --chip STM32WBA52CGUxT" + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml new file mode 100644 index 00000000..26fcce26 --- /dev/null +++ b/examples/stm32wba/Cargo.toml @@ -0,0 +1,26 @@ +[package] +edition = "2021" +name = "embassy-stm32wba-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", "nightly"], optional=true } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +heapless = { version = "0.7.5", default-features = false } +static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32wba/build.rs b/examples/stm32wba/build.rs new file mode 100644 index 00000000..8fc6faab --- /dev/null +++ b/examples/stm32wba/build.rs @@ -0,0 +1,10 @@ +use std::error::Error; + +fn main() -> Result<(), Box> { + println!("cargo:rerun-if-changed=link.x"); + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + + Ok(()) +} diff --git a/examples/stm32wba/src/bin/blinky.rs b/examples/stm32wba/src/bin/blinky.rs new file mode 100644 index 00000000..53074629 --- /dev/null +++ b/examples/stm32wba/src/bin/blinky.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut led = Output::new(p.PB4, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after(Duration::from_millis(500)).await; + + info!("low"); + led.set_low(); + Timer::after(Duration::from_millis(500)).await; + } +} diff --git a/examples/stm32wba/src/bin/button_exti.rs b/examples/stm32wba/src/bin/button_exti.rs new file mode 100644 index 00000000..ef32d4c4 --- /dev/null +++ b/examples/stm32wba/src/bin/button_exti.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let button = Input::new(p.PC13, Pull::Up); + let mut button = ExtiInput::new(button, p.EXTI13); + + info!("Press the USER button..."); + + loop { + button.wait_for_falling_edge().await; + info!("Pressed!"); + button.wait_for_rising_edge().await; + info!("Released!"); + } +} diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 1c771ddc..f47a9a90 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -7,14 +7,14 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32wl55jc-cm4 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } -lora-phy = { version = "1" } -lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } -lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } +lora-phy = { version = "2" } +lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"] } +lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" @@ -30,6 +30,3 @@ chrono = { version = "^0.4", default-features = false } [profile.release] debug = 2 - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index 2c9c9886..fb249532 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -20,6 +20,7 @@ use lora_phy::LoRa; use lorawan::default_crypto::DefaultFactory as Crypto; use lorawan_device::async_device::lora_radio::LoRaRadio; use lorawan_device::async_device::{region, Device, JoinMode}; +use lorawan_device::{AppEui, AppKey, DevEui}; use {defmt_rtt as _, panic_probe as _}; const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region @@ -33,7 +34,7 @@ bind_interrupts!(struct Irqs{ async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; - config.rcc.enable_lsi = true; // enable RNG + config.rcc.rtc_mux = embassy_stm32::rcc::RtcClockSource::LSI; let p = embassy_stm32::init(config); pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)); @@ -46,10 +47,8 @@ async fn main(_spawner: Spawner) { let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); - let mut delay = Delay; - let lora = { - match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), true, &mut delay).await { + match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), true, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -66,9 +65,9 @@ async fn main(_spawner: Spawner) { // TODO: Adjust the EUI and Keys according to your network credentials match device .join(&JoinMode::OTAA { - deveui: [0, 0, 0, 0, 0, 0, 0, 0], - appeui: [0, 0, 0, 0, 0, 0, 0, 0], - appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]), + appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]), + appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), }) .await { diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs index d3f051b1..3d8c31ff 100644 --- a/examples/stm32wl/src/bin/lora_p2p_receive.rs +++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs @@ -37,10 +37,8 @@ async fn main(_spawner: Spawner) { let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await { + match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -84,7 +82,7 @@ async fn main(_spawner: Spawner) { }; match lora - .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) + .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false) .await { Ok(()) => {} diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs index fc5205c8..fbd0b032 100644 --- a/examples/stm32wl/src/bin/lora_p2p_send.rs +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs @@ -37,10 +37,8 @@ async fn main(_spawner: Spawner) { let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); - let mut delay = Delay; - let mut lora = { - match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await { + match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, Delay).await { Ok(l) => l, Err(err) => { info!("Radio error = {}", err); @@ -93,7 +91,7 @@ async fn main(_spawner: Spawner) { } }; - match lora.sleep(&mut delay).await { + match lora.sleep(false).await { Ok(()) => info!("Sleep successful"), Err(err) => info!("Sleep unsuccessful = {}", err), } diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs index 592e65f4..18eeac4f 100644 --- a/examples/stm32wl/src/bin/random.rs +++ b/examples/stm32wl/src/bin/random.rs @@ -16,7 +16,7 @@ bind_interrupts!(struct Irqs{ async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; - config.rcc.enable_lsi = true; //Needed for RNG to work + config.rcc.rtc_mux = embassy_stm32::rcc::RtcClockSource::LSI; let p = embassy_stm32::init(config); pac::RCC.ccipr().modify(|w| { diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs index fb1bc6e3..11734e4b 100644 --- a/examples/stm32wl/src/bin/rtc.rs +++ b/examples/stm32wl/src/bin/rtc.rs @@ -5,8 +5,9 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rcc::{self, ClockSrc}; -use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::rcc::ClockSrc; +use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig}; +use embassy_stm32::time::Hertz; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -16,8 +17,8 @@ async fn main(_spawner: Spawner) { let p = { let mut config = Config::default(); config.rcc.mux = ClockSrc::HSE32; - config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; - config.rcc.enable_rtc_apb = true; + config.rcc.lse = Some(Hertz(32_768)); + config.rcc.rtc_mux = RtcClockSource::LSE; embassy_stm32::init(config) }; info!("Hello World!"); diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs index 07b0f9d2..2c9b7c69 100644 --- a/examples/stm32wl/src/bin/uart_async.rs +++ b/examples/stm32wl/src/bin/uart_async.rs @@ -33,10 +33,10 @@ async fn main(_spawner: Spawner) { config2.baudrate = 9600; //RX/TX connected to USB/UART Bridge on LoRa-E5 mini v1.0 - let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, Irqs, p.DMA1_CH3, p.DMA1_CH4, config1); + let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, Irqs, p.DMA1_CH3, p.DMA1_CH4, config1).unwrap(); //RX1/TX1 (LPUART) on LoRa-E5 mini v1.0 - let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, Irqs, p.DMA1_CH5, p.DMA1_CH6, config2); + let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, Irqs, p.DMA1_CH5, p.DMA1_CH6, config2).unwrap(); unwrap!(usart1.write(b"Hello Embassy World!\r\n").await); unwrap!(usart2.write(b"Hello Embassy World!\r\n").await); diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 5b206d71..12b2e2bd 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -8,9 +8,9 @@ license = "MIT OR Apache-2.0" crate-type = ["cdylib"] [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } wasm-logger = "0.2.0" wasm-bindgen = "0.2" diff --git a/tests/nrf/.cargo/config.toml b/tests/nrf/.cargo/config.toml index 03995f96..9d6b0313 100644 --- a/tests/nrf/.cargo/config.toml +++ b/tests/nrf/.cargo/config.toml @@ -6,4 +6,4 @@ runner = "teleprobe client run" target = "thumbv7em-none-eabi" [env] -DEFMT_LOG = "trace" +DEFMT_LOG = "trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,smoltcp=info" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 4e31bed5..08fe1a4b 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -8,9 +8,9 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.5.0" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } @@ -19,10 +19,11 @@ embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", embedded-hal-async = { version = "1.0.0-rc.1" } embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } static_cell = { version = "1.1", features = [ "nightly" ] } +perf-client = { path = "../perf-client" } defmt = "0.3" defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" -panic-probe = { version = "0.3", features = ["print-defmt"] } \ No newline at end of file +panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs index 0446d39a..60d30a2f 100644 --- a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs +++ b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs @@ -4,17 +4,15 @@ teleprobe_meta::target!(b"ak-gwe-r7"); teleprobe_meta::timeout!(120); -use defmt::{error, info, unwrap}; +use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_futures::join::join; -use embassy_net::tcp::TcpSocket; -use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net::{Stack, StackResources}; use embassy_net_enc28j60::Enc28j60; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; -use embassy_time::{with_timeout, Delay, Duration, Timer}; +use embassy_time::Delay; use embedded_hal_bus::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; @@ -79,172 +77,16 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(net_task(stack))); - info!("Waiting for DHCP up..."); - while stack.config_v4().is_none() { - Timer::after(Duration::from_millis(100)).await; - } - info!("IP addressing up!"); - - let down = test_download(stack).await; - let up = test_upload(stack).await; - let updown = test_upload_download(stack).await; - - assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); - assert!(up > TEST_EXPECTED_UPLOAD_KBPS); - assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + perf_client::run( + stack, + perf_client::Expected { + down_kbps: 200, + up_kbps: 200, + updown_kbps: 150, + }, + ) + .await; info!("Test OK"); cortex_m::asm::bkpt(); } - -const TEST_DURATION: usize = 10; -const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 200; -const TEST_EXPECTED_UPLOAD_KBPS: usize = 200; -const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150; -const RX_BUFFER_SIZE: usize = 4096; -const TX_BUFFER_SIZE: usize = 4096; -const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); -const DOWNLOAD_PORT: u16 = 4321; -const UPLOAD_PORT: u16 = 4322; -const UPLOAD_DOWNLOAD_PORT: u16 = 4323; - -async fn test_download(stack: &'static Stack) -> usize { - info!("Testing download..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let mut rx_buf = [0; 4096]; - let mut total: usize = 0; - with_timeout(Duration::from_secs(TEST_DURATION as _), async { - loop { - match socket.read(&mut rx_buf).await { - Ok(0) => { - error!("read EOF"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("read error: {:?}", e); - return 0; - } - } - } - }) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("download: {} kB/s", kbps); - kbps -} - -async fn test_upload(stack: &'static Stack) -> usize { - info!("Testing upload..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let buf = [0; 4096]; - let mut total: usize = 0; - with_timeout(Duration::from_secs(TEST_DURATION as _), async { - loop { - match socket.write(&buf).await { - Ok(0) => { - error!("write zero?!??!?!"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("write error: {:?}", e); - return 0; - } - } - } - }) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("upload: {} kB/s", kbps); - kbps -} - -async fn test_upload_download(stack: &'static Stack) -> usize { - info!("Testing upload+download..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let (mut reader, mut writer) = socket.split(); - - let tx_buf = [0; 4096]; - let mut rx_buf = [0; 4096]; - let mut total: usize = 0; - let tx_fut = async { - loop { - match writer.write(&tx_buf).await { - Ok(0) => { - error!("write zero?!??!?!"); - return 0; - } - Ok(_) => {} - Err(e) => { - error!("write error: {:?}", e); - return 0; - } - } - } - }; - - let rx_fut = async { - loop { - match reader.read(&mut rx_buf).await { - Ok(0) => { - error!("read EOF"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("read error: {:?}", e); - return 0; - } - } - } - }; - - with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("upload+download: {} kB/s", kbps); - kbps -} diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 97ebafec..9eee39cc 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -4,16 +4,14 @@ teleprobe_meta::target!(b"nrf52840-dk"); teleprobe_meta::timeout!(120); -use defmt::{error, info, unwrap}; +use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_futures::join::join; -use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, Ipv4Address, Stack, StackResources}; +use embassy_net::{Config, Stack, StackResources}; use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; -use embassy_time::{with_timeout, Delay, Duration, Timer}; +use embassy_time::Delay; use embedded_hal_bus::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; @@ -23,6 +21,10 @@ bind_interrupts!(struct Irqs { RNG => embassy_nrf::rng::InterruptHandler; }); +// Test-only wifi network, no internet access! +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + #[embassy_executor::task] async fn wifi_task( runner: hosted::Runner< @@ -92,176 +94,16 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(net_task(stack))); - info!("Waiting for DHCP up..."); - while stack.config_v4().is_none() { - Timer::after(Duration::from_millis(100)).await; - } - info!("IP addressing up!"); - - let down = test_download(stack).await; - let up = test_upload(stack).await; - let updown = test_upload_download(stack).await; - - assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); - assert!(up > TEST_EXPECTED_UPLOAD_KBPS); - assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + perf_client::run( + stack, + perf_client::Expected { + down_kbps: 50, + up_kbps: 50, + updown_kbps: 50, + }, + ) + .await; info!("Test OK"); cortex_m::asm::bkpt(); } - -// Test-only wifi network, no internet access! -const WIFI_NETWORK: &str = "EmbassyTest"; -const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; - -const TEST_DURATION: usize = 10; -const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 50; -const TEST_EXPECTED_UPLOAD_KBPS: usize = 50; -const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 50; -const RX_BUFFER_SIZE: usize = 4096; -const TX_BUFFER_SIZE: usize = 4096; -const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); -const DOWNLOAD_PORT: u16 = 4321; -const UPLOAD_PORT: u16 = 4322; -const UPLOAD_DOWNLOAD_PORT: u16 = 4323; - -async fn test_download(stack: &'static Stack) -> usize { - info!("Testing download..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let mut rx_buf = [0; 4096]; - let mut total: usize = 0; - with_timeout(Duration::from_secs(TEST_DURATION as _), async { - loop { - match socket.read(&mut rx_buf).await { - Ok(0) => { - error!("read EOF"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("read error: {:?}", e); - return 0; - } - } - } - }) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("download: {} kB/s", kbps); - kbps -} - -async fn test_upload(stack: &'static Stack) -> usize { - info!("Testing upload..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let buf = [0; 4096]; - let mut total: usize = 0; - with_timeout(Duration::from_secs(TEST_DURATION as _), async { - loop { - match socket.write(&buf).await { - Ok(0) => { - error!("write zero?!??!?!"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("write error: {:?}", e); - return 0; - } - } - } - }) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("upload: {} kB/s", kbps); - kbps -} - -async fn test_upload_download(stack: &'static Stack) -> usize { - info!("Testing upload+download..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let (mut reader, mut writer) = socket.split(); - - let tx_buf = [0; 4096]; - let mut rx_buf = [0; 4096]; - let mut total: usize = 0; - let tx_fut = async { - loop { - match writer.write(&tx_buf).await { - Ok(0) => { - error!("write zero?!??!?!"); - return 0; - } - Ok(_) => {} - Err(e) => { - error!("write error: {:?}", e); - return 0; - } - } - } - }; - - let rx_fut = async { - loop { - match reader.read(&mut rx_buf).await { - Ok(0) => { - error!("read EOF"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("read error: {:?}", e); - return 0; - } - } - } - }; - - with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("upload+download: {} kB/s", kbps); - kbps -} diff --git a/tests/perf-client/Cargo.toml b/tests/perf-client/Cargo.toml new file mode 100644 index 00000000..3284664d --- /dev/null +++ b/tests/perf-client/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "perf-client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +defmt = "0.3.0" diff --git a/tests/perf-client/src/lib.rs b/tests/perf-client/src/lib.rs new file mode 100644 index 00000000..a36147db --- /dev/null +++ b/tests/perf-client/src/lib.rs @@ -0,0 +1,179 @@ +#![no_std] + +use defmt::{assert, *}; +use embassy_futures::join::join; +use embassy_net::driver::Driver; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack}; +use embassy_time::{with_timeout, Duration, Timer}; + +pub struct Expected { + pub down_kbps: usize, + pub up_kbps: usize, + pub updown_kbps: usize, +} + +pub async fn run(stack: &Stack, expected: Expected) { + info!("Waiting for DHCP up..."); + while stack.config_v4().is_none() { + Timer::after(Duration::from_millis(100)).await; + } + info!("IP addressing up!"); + + let down = test_download(stack).await; + let up = test_upload(stack).await; + let updown = test_upload_download(stack).await; + + assert!(down > expected.down_kbps); + assert!(up > expected.up_kbps); + assert!(updown > expected.updown_kbps); +} + +const TEST_DURATION: usize = 10; +const RX_BUFFER_SIZE: usize = 4096; +const TX_BUFFER_SIZE: usize = 4096; +const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); +const DOWNLOAD_PORT: u16 = 4321; +const UPLOAD_PORT: u16 = 4322; +const UPLOAD_DOWNLOAD_PORT: u16 = 4323; + +async fn test_download(stack: &Stack) -> usize { + info!("Testing download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("download: {} kB/s", kbps); + kbps +} + +async fn test_upload(stack: &Stack) -> usize { + info!("Testing upload..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.write(&buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload: {} kB/s", kbps); + kbps +} + +async fn test_upload_download(stack: &Stack) -> usize { + info!("Testing upload+download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let (mut reader, mut writer) = socket.split(); + + let tx_buf = [0; 4096]; + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + let tx_fut = async { + loop { + match writer.write(&tx_buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(_) => {} + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }; + + let rx_fut = async { + loop { + match reader.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }; + + with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload+download: {} kB/s", kbps); + kbps +} diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index be610b1c..490f037b 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0" [dependencies] critical-section = { version = "1.1.1", features = ["restore-state-bool"] } -embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync" } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time" } +embassy-time = { version = "0.1.3", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } riscv-rt = "0.11" diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml index bc92e788..40b5d700 100644 --- a/tests/rp/.cargo/config.toml +++ b/tests/rp/.cargo/config.toml @@ -19,4 +19,4 @@ rustflags = [ target = "thumbv6m-none-eabi" [env] -DEFMT_LOG = "trace" +DEFMT_LOG = "trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index c494b66e..8bb0de6c 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -7,15 +7,16 @@ license = "MIT OR Apache-2.0" [dependencies] teleprobe-meta = "1.1" -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } +perf-client = { path = "../perf-client" } defmt = "0.3.0" defmt-rtt = "0.4" diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 1c665f95..de29c06d 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -4,16 +4,13 @@ teleprobe_meta::target!(b"rpi-pico"); use cyw43_pio::PioSpi; -use defmt::{assert, panic, *}; +use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_futures::join::join; -use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, Ipv4Address, Stack, StackResources}; +use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_rp::{bind_interrupts, rom_data}; -use embassy_time::{with_timeout, Duration, Timer}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; @@ -23,6 +20,10 @@ bind_interrupts!(struct Irqs { teleprobe_meta::timeout!(120); +// Test-only wifi network, no internet access! +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -88,176 +89,16 @@ async fn main(spawner: Spawner) { } } - info!("Waiting for DHCP up..."); - while stack.config_v4().is_none() { - Timer::after(Duration::from_millis(100)).await; - } - info!("IP addressing up!"); - - let down = test_download(stack).await; - let up = test_upload(stack).await; - let updown = test_upload_download(stack).await; - - assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); - assert!(up > TEST_EXPECTED_UPLOAD_KBPS); - assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + perf_client::run( + stack, + perf_client::Expected { + down_kbps: 300, + up_kbps: 300, + updown_kbps: 300, + }, + ) + .await; info!("Test OK"); cortex_m::asm::bkpt(); } - -// Test-only wifi network, no internet access! -const WIFI_NETWORK: &str = "EmbassyTest"; -const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; - -const TEST_DURATION: usize = 10; -const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 300; -const TEST_EXPECTED_UPLOAD_KBPS: usize = 300; -const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 300; -const RX_BUFFER_SIZE: usize = 4096; -const TX_BUFFER_SIZE: usize = 4096; -const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); -const DOWNLOAD_PORT: u16 = 4321; -const UPLOAD_PORT: u16 = 4322; -const UPLOAD_DOWNLOAD_PORT: u16 = 4323; - -async fn test_download(stack: &'static Stack>) -> usize { - info!("Testing download..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let mut rx_buf = [0; 4096]; - let mut total: usize = 0; - with_timeout(Duration::from_secs(TEST_DURATION as _), async { - loop { - match socket.read(&mut rx_buf).await { - Ok(0) => { - error!("read EOF"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("read error: {:?}", e); - return 0; - } - } - } - }) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("download: {} kB/s", kbps); - kbps -} - -async fn test_upload(stack: &'static Stack>) -> usize { - info!("Testing upload..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let buf = [0; 4096]; - let mut total: usize = 0; - with_timeout(Duration::from_secs(TEST_DURATION as _), async { - loop { - match socket.write(&buf).await { - Ok(0) => { - error!("write zero?!??!?!"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("write error: {:?}", e); - return 0; - } - } - } - }) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("upload: {} kB/s", kbps); - kbps -} - -async fn test_upload_download(stack: &'static Stack>) -> usize { - info!("Testing upload+download..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let (mut reader, mut writer) = socket.split(); - - let tx_buf = [0; 4096]; - let mut rx_buf = [0; 4096]; - let mut total: usize = 0; - let tx_fut = async { - loop { - match writer.write(&tx_buf).await { - Ok(0) => { - error!("write zero?!??!?!"); - return 0; - } - Ok(_) => {} - Err(e) => { - error!("write error: {:?}", e); - return 0; - } - } - } - }; - - let rx_fut = async { - loop { - match reader.read(&mut rx_buf).await { - Ok(0) => { - error!("read EOF"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("read error: {:?}", e); - return 0; - } - } - } - }; - - with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("upload+download: {} kB/s", kbps); - kbps -} diff --git a/tests/rp/src/bin/ethernet_w5100s_perf.rs b/tests/rp/src/bin/ethernet_w5100s_perf.rs index faa8638c..a4d253b3 100644 --- a/tests/rp/src/bin/ethernet_w5100s_perf.rs +++ b/tests/rp/src/bin/ethernet_w5100s_perf.rs @@ -4,18 +4,16 @@ teleprobe_meta::target!(b"w5100s-evb-pico"); teleprobe_meta::timeout!(120); -use defmt::{assert, *}; +use defmt::*; use embassy_executor::Spawner; -use embassy_futures::join::join; -use embassy_net::tcp::TcpSocket; -use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net::{Stack, StackResources}; use embassy_net_wiznet::chip::W5100S; use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; -use embassy_time::{with_timeout, Delay, Duration, Timer}; +use embassy_time::Delay; use embedded_hal_bus::spi::ExclusiveDevice; use rand::RngCore; use static_cell::make_static; @@ -78,172 +76,16 @@ async fn main(spawner: Spawner) { // Launch network task unwrap!(spawner.spawn(net_task(&stack))); - info!("Waiting for DHCP up..."); - while stack.config_v4().is_none() { - Timer::after(Duration::from_millis(100)).await; - } - info!("IP addressing up!"); - - let down = test_download(stack).await; - let up = test_upload(stack).await; - let updown = test_upload_download(stack).await; - - assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); - assert!(up > TEST_EXPECTED_UPLOAD_KBPS); - assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + perf_client::run( + stack, + perf_client::Expected { + down_kbps: 500, + up_kbps: 500, + updown_kbps: 300, + }, + ) + .await; info!("Test OK"); cortex_m::asm::bkpt(); } - -const TEST_DURATION: usize = 10; -const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 500; -const TEST_EXPECTED_UPLOAD_KBPS: usize = 500; -const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 300; -const RX_BUFFER_SIZE: usize = 4096; -const TX_BUFFER_SIZE: usize = 4096; -const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); -const DOWNLOAD_PORT: u16 = 4321; -const UPLOAD_PORT: u16 = 4322; -const UPLOAD_DOWNLOAD_PORT: u16 = 4323; - -async fn test_download(stack: &'static Stack>) -> usize { - info!("Testing download..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let mut rx_buf = [0; 4096]; - let mut total: usize = 0; - with_timeout(Duration::from_secs(TEST_DURATION as _), async { - loop { - match socket.read(&mut rx_buf).await { - Ok(0) => { - error!("read EOF"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("read error: {:?}", e); - return 0; - } - } - } - }) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("download: {} kB/s", kbps); - kbps -} - -async fn test_upload(stack: &'static Stack>) -> usize { - info!("Testing upload..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let buf = [0; 4096]; - let mut total: usize = 0; - with_timeout(Duration::from_secs(TEST_DURATION as _), async { - loop { - match socket.write(&buf).await { - Ok(0) => { - error!("write zero?!??!?!"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("write error: {:?}", e); - return 0; - } - } - } - }) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("upload: {} kB/s", kbps); - kbps -} - -async fn test_upload_download(stack: &'static Stack>) -> usize { - info!("Testing upload+download..."); - - let mut rx_buffer = [0; RX_BUFFER_SIZE]; - let mut tx_buffer = [0; TX_BUFFER_SIZE]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - - info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); - if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { - error!("connect error: {:?}", e); - return 0; - } - info!("connected, testing..."); - - let (mut reader, mut writer) = socket.split(); - - let tx_buf = [0; 4096]; - let mut rx_buf = [0; 4096]; - let mut total: usize = 0; - let tx_fut = async { - loop { - match writer.write(&tx_buf).await { - Ok(0) => { - error!("write zero?!??!?!"); - return 0; - } - Ok(_) => {} - Err(e) => { - error!("write error: {:?}", e); - return 0; - } - } - } - }; - - let rx_fut = async { - loop { - match reader.read(&mut rx_buf).await { - Ok(0) => { - error!("read EOF"); - return 0; - } - Ok(n) => total += n, - Err(e) => { - error!("read error: {:?}", e); - return 0; - } - } - } - }; - - with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) - .await - .ok(); - - let kbps = (total + 512) / 1024 / TEST_DURATION; - info!("upload+download: {} kB/s", kbps); - kbps -} diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs new file mode 100644 index 00000000..425f2d08 --- /dev/null +++ b/tests/rp/src/bin/i2c.rs @@ -0,0 +1,212 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"rpi-pico"); + +use defmt::{assert_eq, info, panic, unwrap}; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::peripherals::{I2C0, I2C1}; +use embassy_rp::{bind_interrupts, i2c, i2c_slave}; +use embedded_hal_1::i2c::Operation; +use embedded_hal_async::i2c::I2c; +use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<1024> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); + +use crate::i2c::AbortReason; + +bind_interrupts!(struct Irqs { + I2C0_IRQ => i2c::InterruptHandler; + I2C1_IRQ => i2c::InterruptHandler; +}); + +const DEV_ADDR: u8 = 0x42; + +#[embassy_executor::task] +async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { + info!("Device start"); + + let mut count = 0xD0; + + loop { + let mut buf = [0u8; 128]; + match dev.listen(&mut buf).await { + Ok(i2c_slave::Command::GeneralCall(len)) => { + assert_eq!(buf[..len], [0xCA, 0x11], "recieving the general call failed"); + info!("General Call - OK"); + } + Ok(i2c_slave::Command::Read) => { + loop { + match dev.respond_to_read(&[count]).await { + Ok(x) => match x { + i2c_slave::ReadStatus::Done => break, + i2c_slave::ReadStatus::NeedMoreBytes => count += 1, + i2c_slave::ReadStatus::LeftoverBytes(x) => { + info!("tried to write {} extra bytes", x); + break; + } + }, + Err(e) => match e { + embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), + _ => panic!("{}", e), + }, + } + } + count += 1; + } + Ok(i2c_slave::Command::Write(len)) => match len { + 1 => { + assert_eq!(buf[..len], [0xAA], "recieving a single byte failed"); + info!("Single Byte Write - OK") + } + 4 => { + assert_eq!(buf[..len], [0xAA, 0xBB, 0xCC, 0xDD], "recieving 4 bytes failed"); + info!("4 Byte Write - OK") + } + 32 => { + assert_eq!( + buf[..len], + [ + 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, 30, 31 + ], + "recieving 32 bytes failed" + ); + info!("32 Byte Write - OK") + } + _ => panic!("Invalid write length {}", len), + }, + Ok(i2c_slave::Command::WriteRead(len)) => { + info!("device recieved write read: {:x}", buf[..len]); + match buf[0] { + 0xC2 => { + let resp_buff = [0xD1, 0xD2, 0xD3, 0xD4]; + dev.respond_to_read(&resp_buff).await.unwrap(); + } + 0xC8 => { + let mut resp_buff = [0u8; 32]; + for i in 0..32 { + resp_buff[i] = i as u8; + } + dev.respond_to_read(&resp_buff).await.unwrap(); + } + x => panic!("Invalid Write Read {:x}", x), + } + } + Err(e) => match e { + embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), + _ => panic!("{}", e), + }, + } + } +} + +#[embassy_executor::task] +async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { + info!("Device start"); + + { + let buf = [0xCA, 0x11]; + con.write(0u16, &buf).await.unwrap(); + info!("Controler general call write"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8]; + con.read(DEV_ADDR, &mut buf).await.unwrap(); + assert_eq!(buf, [0xD0], "single byte read failed"); + info!("single byte read - OK"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8; 4]; + con.read(DEV_ADDR, &mut buf).await.unwrap(); + assert_eq!(buf, [0xD1, 0xD2, 0xD3, 0xD4], "single byte read failed"); + info!("4 byte read - OK"); + embassy_futures::yield_now().await; + } + + { + let buf = [0xAA]; + con.write(DEV_ADDR, &buf).await.unwrap(); + info!("Controler single byte write"); + embassy_futures::yield_now().await; + } + + { + let buf = [0xAA, 0xBB, 0xCC, 0xDD]; + con.write(DEV_ADDR, &buf).await.unwrap(); + info!("Controler 4 byte write"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8; 32]; + for i in 0..32 { + buf[i] = i as u8; + } + con.write(DEV_ADDR, &buf).await.unwrap(); + info!("Controler 32 byte write"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8; 4]; + let mut ops = [Operation::Write(&[0xC2]), Operation::Read(&mut buf)]; + con.transaction(DEV_ADDR, &mut ops).await.unwrap(); + assert_eq!(buf, [0xD1, 0xD2, 0xD3, 0xD4], "write_read failed"); + info!("write_read - OK"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8; 32]; + let mut ops = [Operation::Write(&[0xC8]), Operation::Read(&mut buf)]; + con.transaction(DEV_ADDR, &mut ops).await.unwrap(); + assert_eq!( + buf, + [ + 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, 30, 31 + ], + "write_read of 32 bytes failed" + ); + info!("large write_read - OK") + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let d_sda = p.PIN_19; + let d_scl = p.PIN_18; + let mut config = i2c_slave::Config::default(); + config.addr = DEV_ADDR as u16; + let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); + + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device)))); + }); + + let executor0 = EXECUTOR0.init(Executor::new()); + + let c_sda = p.PIN_21; + let c_scl = p.PIN_20; + let mut config = i2c::Config::default(); + config.frequency = 5_000; + let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); + + executor0.run(|spawner| unwrap!(spawner.spawn(controller_task(controller)))); +} diff --git a/tests/stm32/.cargo/config.toml b/tests/stm32/.cargo/config.toml index 07761b01..2e3f055d 100644 --- a/tests/stm32/.cargo/config.toml +++ b/tests/stm32/.cargo/config.toml @@ -14,7 +14,10 @@ rustflags = [ ] [build] -target = "thumbv7m-none-eabi" +target = "thumbv6m-none-eabi" +#target = "thumbv7m-none-eabi" +#target = "thumbv7em-none-eabi" +#target = "thumbv8m.main-none-eabihf" [env] -DEFMT_LOG = "trace" \ No newline at end of file +DEFMT_LOG = "trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,smoltcp=info" \ No newline at end of file diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 754356cb..bfe5bc82 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,7 +7,7 @@ autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "can", "not-gpdma", "dac-adc-pin"] # Nucleo "sdmmc" +stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "stop", "can", "not-gpdma", "dac-adc-pin"] # Nucleo "sdmmc" stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma", "dac-adc-pin"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo @@ -15,8 +15,14 @@ stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma", "dac-adc-pin"] # Nu stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "mac" ] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board +stm32l073rz = ["embassy-stm32/stm32l073rz", "not-gpdma"] # Nucleo +stm32l152re = ["embassy-stm32/stm32l152re", "not-gpdma"] # Nucleo +stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "not-gpdma"] # Nucleo +stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "not-gpdma"] # Nucleo +stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma"] # Nucleo sdmmc = [] +stop = ["embassy-stm32/low-power"] chrono = ["embassy-stm32/chrono", "dep:chrono"] can = [] ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] @@ -28,9 +34,9 @@ dac-adc-pin = [] [dependencies] teleprobe-meta = "1" -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] } @@ -47,6 +53,7 @@ micromath = "2.0.0" panic-probe = { version = "0.3.0", features = ["print-defmt"] } rand_core = { version = "0.6", default-features = false } rand_chacha = { version = "0.3", default-features = false } +static_cell = {version = "1.1", features = ["nightly"] } chrono = { version = "^0.4", default-features = false, optional = true} @@ -87,6 +94,11 @@ name = "spi_dma" path = "src/bin/spi_dma.rs" required-features = [] +[[bin]] +name = "stop" +path = "src/bin/stop.rs" +required-features = [ "stop", "chrono",] + [[bin]] name = "timer" path = "src/bin/timer.rs" diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 2e71954d..9aabf854 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -12,7 +12,8 @@ fn main() -> Result<(), Box> { if cfg!(any( feature = "stm32f103c8", feature = "stm32c031c6", - feature = "stm32wb55rg" + feature = "stm32wb55rg", + feature = "stm32l073rz", )) { println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rerun-if-changed=link.x"); diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index aad17443..49d9a60f 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -16,24 +16,8 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. - #[cfg(feature = "stm32f103c8")] - let (mut a, mut b) = (p.PA9, p.PA10); - #[cfg(feature = "stm32g491re")] - let (mut a, mut b) = (p.PC4, p.PC5); - #[cfg(feature = "stm32g071rb")] - let (mut a, mut b) = (p.PC4, p.PC5); - #[cfg(feature = "stm32f429zi")] - let (mut a, mut b) = (p.PG14, p.PG9); - #[cfg(feature = "stm32wb55rg")] - let (mut a, mut b) = (p.PA3, p.PA2); - #[cfg(feature = "stm32h755zi")] - let (mut a, mut b) = (p.PB6, p.PB7); - #[cfg(feature = "stm32u585ai")] - let (mut a, mut b) = (p.PD9, p.PD8); - #[cfg(feature = "stm32h563zi")] - let (mut a, mut b) = (p.PB6, p.PB7); - #[cfg(feature = "stm32c031c6")] - let (mut a, mut b) = (p.PB6, p.PB7); + let mut a = peri!(p, UART_RX); + let mut b = peri!(p, UART_TX); // Test initial output { diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index 7df415b4..22be6fac 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -10,14 +10,17 @@ use chrono::{NaiveDate, NaiveDateTime}; use common::*; use defmt::assert; use embassy_executor::Spawner; -use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig}; +use embassy_stm32::rcc::RtcClockSource; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::time::Hertz; use embassy_time::{Duration, Timer}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = config(); - config.rcc.rtc = Some(RtcClockSource::LSI); + config.rcc.lse = Some(Hertz(32_768)); + config.rcc.rtc = Some(RtcClockSource::LSE); let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index e51dd5bf..b0fb75d6 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -16,24 +16,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); - #[cfg(feature = "stm32f103c8")] - let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); - #[cfg(feature = "stm32f429zi")] - let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); - #[cfg(feature = "stm32h755zi")] - let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PB5, p.PA6); - #[cfg(feature = "stm32g491re")] - let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); - #[cfg(feature = "stm32g071rb")] - let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); - #[cfg(feature = "stm32wb55rg")] - let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); - #[cfg(feature = "stm32u585ai")] - let (spi, sck, mosi, miso) = (p.SPI1, p.PE13, p.PE15, p.PE14); - #[cfg(feature = "stm32h563zi")] - let (spi, sck, mosi, miso) = (p.SPI4, p.PE12, p.PE14, p.PE13); - #[cfg(feature = "stm32c031c6")] - let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); + let spi = peri!(p, SPI); + let sck = peri!(p, SPI_SCK); + let mosi = peri!(p, SPI_MOSI); + let miso = peri!(p, SPI_MISO); let mut spi_config = spi::Config::default(); spi_config.frequency = Hertz(1_000_000); diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index d45cbe45..212cfae5 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -15,24 +15,12 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); - #[cfg(feature = "stm32f103c8")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2); - #[cfg(feature = "stm32f429zi")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA2_CH3, p.DMA2_CH2); - #[cfg(feature = "stm32h755zi")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PB5, p.PA6, p.DMA1_CH0, p.DMA1_CH1); - #[cfg(feature = "stm32g491re")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32g071rb")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32wb55rg")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32u585ai")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1); - #[cfg(feature = "stm32h563zi")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI4, p.PE12, p.PE14, p.PE13, p.GPDMA1_CH0, p.GPDMA1_CH1); - #[cfg(feature = "stm32c031c6")] - let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let spi = peri!(p, SPI); + let sck = peri!(p, SPI_SCK); + let mosi = peri!(p, SPI_MOSI); + let miso = peri!(p, SPI_MISO); + let tx_dma = peri!(p, SPI_TX_DMA); + let rx_dma = peri!(p, SPI_RX_DMA); let mut spi_config = spi::Config::default(); spi_config.frequency = Hertz(1_000_000); diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs new file mode 100644 index 00000000..48d59b79 --- /dev/null +++ b/tests/stm32/src/bin/stop.rs @@ -0,0 +1,71 @@ +// required-features: stop,chrono + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use chrono::NaiveDate; +use common::*; +use cortex_m_rt::entry; +use embassy_executor::Spawner; +use embassy_stm32::low_power::{stop_with_rtc, Executor}; +use embassy_stm32::rcc::RtcClockSource; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::time::Hertz; +use embassy_time::{Duration, Timer}; +use static_cell::make_static; + +#[entry] +fn main() -> ! { + Executor::take().run(|spawner| { + unwrap!(spawner.spawn(async_main(spawner))); + }); +} + +#[embassy_executor::task] +async fn task_1() { + for _ in 0..9 { + info!("task 1: waiting for 500ms..."); + Timer::after(Duration::from_millis(500)).await; + } +} + +#[embassy_executor::task] +async fn task_2() { + for _ in 0..5 { + info!("task 2: waiting for 1000ms..."); + Timer::after(Duration::from_millis(1000)).await; + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[embassy_executor::task] +async fn async_main(spawner: Spawner) { + let mut config = config(); + + config.rcc.lse = Some(Hertz(32_768)); + config.rcc.rtc = Some(RtcClockSource::LSE); + + let p = embassy_stm32::init(config); + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + let rtc = make_static!(rtc); + + stop_with_rtc(rtc); + + spawner.spawn(task_1()).unwrap(); + spawner.spawn(task_2()).unwrap(); +} diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 394005b8..74a81b4e 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -5,38 +5,11 @@ mod common; use common::*; -use defmt::assert_eq; +use defmt::{assert, assert_eq, unreachable}; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; -use embassy_stm32::usart::{Config, Error, Uart}; -use embassy_stm32::{bind_interrupts, peripherals, usart}; -use embassy_time::{Duration, Instant}; - -#[cfg(any( - feature = "stm32f103c8", - feature = "stm32g491re", - feature = "stm32g071rb", - feature = "stm32h755zi", - feature = "stm32c031c6", -))] -bind_interrupts!(struct Irqs { - USART1 => usart::InterruptHandler; -}); - -#[cfg(feature = "stm32u585ai")] -bind_interrupts!(struct Irqs { - USART3 => usart::InterruptHandler; -}); - -#[cfg(feature = "stm32f429zi")] -bind_interrupts!(struct Irqs { - USART6 => usart::InterruptHandler; -}); - -#[cfg(any(feature = "stm32wb55rg", feature = "stm32h563zi"))] -bind_interrupts!(struct Irqs { - LPUART1 => usart::InterruptHandler; -}); +use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; +use embassy_time::{block_for, Duration, Instant}; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -45,28 +18,14 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. - #[cfg(feature = "stm32f103c8")] - let (mut tx, mut rx, mut usart) = (p.PA9, p.PA10, p.USART1); - #[cfg(feature = "stm32g491re")] - let (mut tx, mut rx, mut usart) = (p.PC4, p.PC5, p.USART1); - #[cfg(feature = "stm32g071rb")] - let (mut tx, mut rx, mut usart) = (p.PC4, p.PC5, p.USART1); - #[cfg(feature = "stm32f429zi")] - let (mut tx, mut rx, mut usart) = (p.PG14, p.PG9, p.USART6); - #[cfg(feature = "stm32wb55rg")] - let (mut tx, mut rx, mut usart) = (p.PA2, p.PA3, p.LPUART1); - #[cfg(feature = "stm32h755zi")] - let (mut tx, mut rx, mut usart) = (p.PB6, p.PB7, p.USART1); - #[cfg(feature = "stm32u585ai")] - let (mut tx, mut rx, mut usart) = (p.PD8, p.PD9, p.USART3); - #[cfg(feature = "stm32h563zi")] - let (mut tx, mut rx, mut usart) = (p.PB6, p.PB7, p.LPUART1); - #[cfg(feature = "stm32c031c6")] - let (mut tx, mut rx, mut usart) = (p.PB6, p.PB7, p.USART1); + let mut usart = peri!(p, UART); + let mut rx = peri!(p, UART_RX); + let mut tx = peri!(p, UART_TX); + let irq = irqs!(UART); { let config = Config::default(); - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, Irqs, NoDma, NoDma, config); + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config).unwrap(); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. @@ -82,13 +41,19 @@ async fn main(_spawner: Spawner) { // Test error handling with with an overflow error { let config = Config::default(); - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, Irqs, NoDma, NoDma, config); + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config).unwrap(); // Send enough bytes to fill the RX FIFOs off all USART versions. - let data = [0xC0, 0xDE, 0x12, 0x23, 0x34]; + let data = [0; 64]; usart.blocking_write(&data).unwrap(); usart.blocking_flush().unwrap(); + // USART can still take up to 1 bit time (?) to receive the last byte + // that we just flushed, so wait a bit. + // otherwise, we might clear the overrun flag from an *earlier* byte and + // it gets set again when receiving the last byte is done. + block_for(Duration::from_millis(1)); + // The error should be reported first. let mut buf = [0; 1]; let err = usart.blocking_read(&mut buf); @@ -101,22 +66,25 @@ async fn main(_spawner: Spawner) { // Test that baudrate divider is calculated correctly. // Do it by comparing the time it takes to send a known number of bytes. - for baudrate in [ - 300, - 9600, - 115200, - 250_000, - 337_934, - #[cfg(not(feature = "stm32f103c8"))] - 1_000_000, - #[cfg(not(feature = "stm32f103c8"))] - 2_000_000, - ] { + for baudrate in [300, 9600, 115200, 250_000, 337_934, 1_000_000, 2_000_000] { info!("testing baudrate {}", baudrate); let mut config = Config::default(); config.baudrate = baudrate; - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, Irqs, NoDma, NoDma, config); + let mut usart = match Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config) { + Ok(x) => x, + Err(ConfigError::BaudrateTooHigh) => { + info!("baudrate too high"); + assert!(baudrate >= 1_000_000); + continue; + } + Err(ConfigError::BaudrateTooLow) => { + info!("baudrate too low"); + assert!(baudrate <= 300); + continue; + } + Err(_) => unreachable!(), + }; let n = (baudrate as usize / 100).max(64); @@ -124,6 +92,7 @@ async fn main(_spawner: Spawner) { for _ in 0..n { usart.blocking_write(&[0x00]).unwrap(); } + usart.blocking_flush().unwrap(); let dur = Instant::now() - start; let want_dur = Duration::from_micros(n as u64 * 10 * 1_000_000 / (baudrate as u64)); let fuzz = want_dur / 5; diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index c34d9574..1421f660 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -9,33 +9,6 @@ use defmt::assert_eq; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usart::{Config, Uart}; -use embassy_stm32::{bind_interrupts, peripherals, usart}; - -#[cfg(any( - feature = "stm32f103c8", - feature = "stm32g491re", - feature = "stm32g071rb", - feature = "stm32h755zi", - feature = "stm32c031c6", -))] -bind_interrupts!(struct Irqs { - USART1 => usart::InterruptHandler; -}); - -#[cfg(feature = "stm32u585ai")] -bind_interrupts!(struct Irqs { - USART3 => usart::InterruptHandler; -}); - -#[cfg(feature = "stm32f429zi")] -bind_interrupts!(struct Irqs { - USART6 => usart::InterruptHandler; -}); - -#[cfg(any(feature = "stm32wb55rg", feature = "stm32h563zi"))] -bind_interrupts!(struct Irqs { - LPUART1 => usart::InterruptHandler; -}); #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -44,27 +17,15 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. - #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PA9, p.PA10, p.USART1, Irqs, p.DMA1_CH4, p.DMA1_CH5); - #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, Irqs, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, Irqs, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PG14, p.PG9, p.USART6, Irqs, p.DMA2_CH6, p.DMA2_CH1); - #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PA2, p.PA3, p.LPUART1, Irqs, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, Irqs, p.DMA1_CH0, p.DMA1_CH1); - #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PD8, p.PD9, p.USART3, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1); - #[cfg(feature = "stm32h563zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PB6, p.PB7, p.LPUART1, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1); - #[cfg(feature = "stm32c031c6")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, Irqs, p.DMA1_CH1, p.DMA1_CH2); + let usart = peri!(p, UART); + let rx = peri!(p, UART_RX); + let tx = peri!(p, UART_TX); + let rx_dma = peri!(p, UART_RX_DMA); + let tx_dma = peri!(p, UART_TX_DMA); + let irq = irqs!(UART); let config = Config::default(); - let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); + let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config).unwrap(); const LEN: usize = 128; let mut tx_buf = [0; LEN]; diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index c8dd2643..1ee7e596 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -10,87 +10,10 @@ use common::*; use defmt::{assert_eq, panic}; use embassy_executor::Spawner; use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; -use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_time::{Duration, Timer}; use rand_chacha::ChaCha8Rng; use rand_core::{RngCore, SeedableRng}; -#[cfg(any( - feature = "stm32f103c8", - feature = "stm32g491re", - feature = "stm32g071rb", - feature = "stm32h755zi", - feature = "stm32c031c6", -))] -bind_interrupts!(struct Irqs { - USART1 => usart::InterruptHandler; -}); - -#[cfg(feature = "stm32u585ai")] -bind_interrupts!(struct Irqs { - USART3 => usart::InterruptHandler; -}); - -#[cfg(feature = "stm32f429zi")] -bind_interrupts!(struct Irqs { - USART1 => usart::InterruptHandler; - USART6 => usart::InterruptHandler; -}); - -#[cfg(any(feature = "stm32wb55rg", feature = "stm32h563zi"))] -bind_interrupts!(struct Irqs { - LPUART1 => usart::InterruptHandler; -}); - -#[cfg(feature = "stm32f103c8")] -mod board { - pub type Uart = embassy_stm32::peripherals::USART1; - pub type TxDma = embassy_stm32::peripherals::DMA1_CH4; - pub type RxDma = embassy_stm32::peripherals::DMA1_CH5; -} -#[cfg(feature = "stm32g491re")] -mod board { - pub type Uart = embassy_stm32::peripherals::USART1; - pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; - pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; -} -#[cfg(feature = "stm32g071rb")] -mod board { - pub type Uart = embassy_stm32::peripherals::USART1; - pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; - pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; -} -#[cfg(feature = "stm32f429zi")] -mod board { - pub type Uart = embassy_stm32::peripherals::USART6; - pub type TxDma = embassy_stm32::peripherals::DMA2_CH6; - pub type RxDma = embassy_stm32::peripherals::DMA2_CH1; -} -#[cfg(feature = "stm32wb55rg")] -mod board { - pub type Uart = embassy_stm32::peripherals::LPUART1; - pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; - pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; -} -#[cfg(feature = "stm32h755zi")] -mod board { - pub type Uart = embassy_stm32::peripherals::USART1; - pub type TxDma = embassy_stm32::peripherals::DMA1_CH0; - pub type RxDma = embassy_stm32::peripherals::DMA1_CH1; -} -#[cfg(feature = "stm32u585ai")] -mod board { - pub type Uart = embassy_stm32::peripherals::USART3; - pub type TxDma = embassy_stm32::peripherals::GPDMA1_CH0; - pub type RxDma = embassy_stm32::peripherals::GPDMA1_CH1; -} -#[cfg(feature = "stm32c031c6")] -mod board { - pub type Uart = embassy_stm32::peripherals::USART1; - pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; - pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; -} - const DMA_BUF_SIZE: usize = 256; #[embassy_executor::main] @@ -100,22 +23,12 @@ async fn main(spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. - #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PA9, p.PA10, p.USART1, p.DMA1_CH4, p.DMA1_CH5); - #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PG14, p.PG9, p.USART6, p.DMA2_CH6, p.DMA2_CH1); - #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PA2, p.PA3, p.LPUART1, p.DMA1_CH1, p.DMA1_CH2); - #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH0, p.DMA1_CH1); - #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PD8, p.PD9, p.USART3, p.GPDMA1_CH0, p.GPDMA1_CH1); - #[cfg(feature = "stm32c031c6")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH1, p.DMA1_CH2); + let usart = peri!(p, UART); + let rx = peri!(p, UART_RX); + let tx = peri!(p, UART_TX); + let rx_dma = peri!(p, UART_RX_DMA); + let tx_dma = peri!(p, UART_TX_DMA); + let irq = irqs!(UART); // To run this test, use the saturating_serial test utility to saturate the serial port @@ -127,7 +40,7 @@ async fn main(spawner: Spawner) { config.stop_bits = StopBits::STOP1; config.parity = Parity::ParityNone; - let usart = Uart::new(usart, rx, tx, Irqs, tx_dma, rx_dma, config); + let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config).unwrap(); let (tx, rx) = usart.split(); static mut DMA_BUF: [u8; DMA_BUF_SIZE] = [0; DMA_BUF_SIZE]; let dma_buf = unsafe { DMA_BUF.as_mut() }; @@ -139,7 +52,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn transmit_task(mut tx: UartTx<'static, board::Uart, board::TxDma>) { +async fn transmit_task(mut tx: UartTx<'static, peris::UART, peris::UART_TX_DMA>) { // workaround https://github.com/embassy-rs/embassy/issues/1426 Timer::after(Duration::from_millis(100) as _).await; @@ -162,7 +75,7 @@ async fn transmit_task(mut tx: UartTx<'static, board::Uart, board::TxDma>) { } #[embassy_executor::task] -async fn receive_task(mut rx: RingBufferedUartRx<'static, board::Uart, board::RxDma>) { +async fn receive_task(mut rx: RingBufferedUartRx<'static, peris::UART, peris::UART_RX_DMA>) { info!("Ready to receive..."); let mut rng = ChaCha8Rng::seed_from_u64(1337); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index ca5cb43a..9c0b8c39 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -24,6 +24,131 @@ teleprobe_meta::target!(b"iot-stm32u585ai"); teleprobe_meta::target!(b"nucleo-stm32h563zi"); #[cfg(feature = "stm32c031c6")] teleprobe_meta::target!(b"nucleo-stm32c031c6"); +#[cfg(feature = "stm32l073rz")] +teleprobe_meta::target!(b"nucleo-stm32l073rz"); +#[cfg(feature = "stm32l152re")] +teleprobe_meta::target!(b"nucleo-stm32l152re"); +#[cfg(feature = "stm32l4a6zg")] +teleprobe_meta::target!(b"nucleo-stm32l4a6zg"); +#[cfg(feature = "stm32l4r5zi")] +teleprobe_meta::target!(b"nucleo-stm32l4r5zi"); +#[cfg(feature = "stm32l552ze")] +teleprobe_meta::target!(b"nucleo-stm32l552ze"); + +macro_rules! define_peris { + ($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => { + #[allow(unused_macros)] + macro_rules! peri { + $( + ($p:expr, $name) => { + $p.$peri + }; + )* + } + #[allow(unused_macros)] + macro_rules! irqs { + $( + ($irq_name) => {{ + embassy_stm32::bind_interrupts!(struct Irqs $irq_code); + Irqs + }}; + )* + } + + #[allow(unused)] + #[allow(non_camel_case_types)] + pub mod peris { + $( + pub type $name = embassy_stm32::peripherals::$peri; + )* + } + }; +} + +#[cfg(feature = "stm32f103c8")] +define_peris!( + UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32g491re")] +define_peris!( + UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32g071rb")] +define_peris!( + UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32f429zi")] +define_peris!( + UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2, + @irq UART = {USART6 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32wb55rg")] +define_peris!( + UART = LPUART1, UART_TX = PA2, UART_RX = PA3, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, + @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32h755zi")] +define_peris!( + UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1, + @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32u585ai")] +define_peris!( + UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, + SPI = SPI1, SPI_SCK = PE13, SPI_MOSI = PE15, SPI_MISO = PE14, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, + @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32h563zi")] +define_peris!( + UART = LPUART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, + SPI = SPI4, SPI_SCK = PE12, SPI_MOSI = PE14, SPI_MISO = PE13, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, + @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32c031c6")] +define_peris!( + UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32l4a6zg")] +define_peris!( + UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32l4r5zi")] +define_peris!( + UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32l073rz")] +define_peris!( + UART = USART4, UART_TX = PA0, UART_RX = PA1, UART_TX_DMA = DMA1_CH3, UART_RX_DMA = DMA1_CH2, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART4_5 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32l152re")] +define_peris!( + UART = USART3, UART_TX = PB10, UART_RX = PB11, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, +); +#[cfg(feature = "stm32l552ze")] +define_peris!( + UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, +); pub fn config() -> Config { #[allow(unused_mut)] @@ -31,14 +156,86 @@ pub fn config() -> Config { #[cfg(feature = "stm32h755zi")] { - config.rcc.sys_ck = Some(Hertz(400_000_000)); - config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); - config.rcc.adc_clock_source = embassy_stm32::rcc::AdcClockSource::PerCk; + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.adc_clock_source = AdcClockSource::PLL2_P; + } + + #[cfg(any(feature = "stm32l4a6zg", feature = "stm32l4r5zi"))] + { + use embassy_stm32::rcc::*; + config.rcc.mux = ClockSrc::PLL( + // 72Mhz clock (16 / 1 * 18 / 4) + PLLSource::HSI16, + PLLClkDiv::Div4, + PLLSrcDiv::Div1, + PLLMul::Mul18, + Some(PLLClkDiv::Div6), // 48Mhz (16 / 1 * 18 / 6) + ); + } + + #[cfg(any(feature = "stm32l552ze"))] + { + use embassy_stm32::rcc::*; + config.rcc.mux = ClockSrc::PLL( + // 110Mhz clock (16 / 4 * 55 / 2) + PLLSource::HSI16, + PLLClkDiv::Div2, + PLLSrcDiv::Div4, + PLLMul::Mul55, + None, + ); } #[cfg(feature = "stm32u585ai")] { - config.rcc.mux = embassy_stm32::rcc::ClockSrc::MSI(embassy_stm32::rcc::MSIRange::Range48mhz); + use embassy_stm32::rcc::*; + config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz); + } + + #[cfg(feature = "stm32l073rz")] + { + use embassy_stm32::rcc::*; + config.rcc.mux = ClockSrc::PLL( + // 32Mhz clock (16 * 4 / 2) + PLLSource::HSI16, + PLLMul::Mul4, + PLLDiv::Div2, + ); + } + + #[cfg(any(feature = "stm32l152re"))] + { + use embassy_stm32::rcc::*; + config.rcc.mux = ClockSrc::PLL( + // 32Mhz clock (16 * 4 / 2) + PLLSource::HSI, + PLLMul::Mul4, + PLLDiv::Div2, + ); } config