diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..4db9edae --- /dev/null +++ b/.gitattributes @@ -0,0 +1,41 @@ +* text=auto + +*.adoc text +*.html text +*.in text +*.json text +*.md text +*.proto text +*.py text +*.rs text +*.service text +*.sh text +*.toml text +*.txt text +*.x text +*.yml text + +*.raw binary +*.bin binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.mov binary +*.mp4 binary +*.mp3 binary +*.flv binary +*.fla binary +*.swf binary +*.gz binary +*.zip binary +*.7z binary +*.ttf binary +*.eot binary +*.woff binary +*.pyc binary +*.pdf binary +*.ez binary +*.bz2 binary +*.swp binary \ No newline at end of file diff --git a/.github/ci/crlf.sh b/.github/ci/crlf.sh new file mode 100755 index 00000000..45751040 --- /dev/null +++ b/.github/ci/crlf.sh @@ -0,0 +1,17 @@ +#!/bin/bash +## on push branch~=gh-readonly-queue/main/.* +## on pull_request + +set -euo pipefail + +FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true)) + +if [ -z "$FILES_WITH_CRLF" ]; then + echo -e "No files with CRLF endings found." + exit 0 +else + NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l) + echo -e "ERROR: Found ${NR_FILES} files with CRLF endings." + echo "$FILES_WITH_CRLF" + exit "$NR_FILES" +fi \ No newline at end of file diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 9e9c78a4..57184dc1 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -15,7 +15,6 @@ export BUILDER_COMPRESS=true # which makes rustup very sad rustc --version > /dev/null -docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup @@ -36,10 +35,20 @@ docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/g docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup -docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup +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 export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) kubectl cp webroot/crates $POD:/data -kubectl cp webroot/static $POD:/data \ No newline at end of file +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 d014e4bd..2892bcf8 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -13,7 +13,7 @@ hashtime save /ci/cache/filetime.json cargo test --manifest-path ./embassy-sync/Cargo.toml cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml -cargo test --manifest-path ./embassy-hal-common/Cargo.toml +cargo test --manifest-path ./embassy-hal-internal/Cargo.toml cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue cargo test --manifest-path ./embassy-boot/boot/Cargo.toml diff --git a/.vscode/settings.json b/.vscode/settings.json index 725fb69d..d48f7ba1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,16 +6,21 @@ "rust-analyzer.check.allTargets": false, "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.cargo.target": "thumbv7m-none-eabi", + "rust-analyzer.showUnlinkedFileNotification": false, + // uncomment the target of your chip. + //"rust-analyzer.cargo.target": "thumbv6m-none-eabi", + //"rust-analyzer.cargo.target": "thumbv7m-none-eabi", + "rust-analyzer.cargo.target": "thumbv7em-none-eabi", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - ///"nightly", + // Uncomment if the example has a "nightly" feature. + "nightly", ], "rust-analyzer.linkedProjects": [ - // Declare for the target you wish to develop - // "embassy-executor/Cargo.toml", - // "embassy-sync/Cargo.toml", - "examples/stm32wl/Cargo.toml", + // Uncomment ONE line for the chip you want to work on. + // This makes rust-analyzer work on the example crate and all its dependencies. + "examples/nrf52840/Cargo.toml", + // "examples/nrf52840-rtic/Cargo.toml", // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", @@ -25,6 +30,7 @@ // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", + // "examples/stm32f334/Cargo.toml", // "examples/stm32f4/Cargo.toml", // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", diff --git a/README.md b/README.md index b05e55aa..c4c01dfb 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ The embassy-net network stac - **Bluetooth** - The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. +The embassy-stm32-wpan crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers. - **LoRa** - embassy-lora supports LoRa networking. @@ -111,6 +112,12 @@ cargo install probe-rs --features cli cd examples/nrf52840 ``` +- Ensure `Cargo.toml` sets the right feature for the name of the chip you are programming. + If this name is incorrect, the example may fail to run or immediately crash + after being programmed. + +- Ensure `.cargo/config.toml` contains the name of the chip you are programming. + - Run the example For example: @@ -119,6 +126,8 @@ For example: cargo run --release --bin blinky ``` +For more help getting started, see [Getting Started][1] and [Running the Examples][2]. + ## Developing Embassy with Rust Analyzer based editors The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/) @@ -151,3 +160,5 @@ This work is licensed under either of at your option. +[1]: https://github.com/embassy-rs/embassy/wiki/Getting-Started +[2]: https://github.com/embassy-rs/embassy/wiki/Running-the-Examples diff --git a/ci.sh b/ci.sh index 376cc8f4..e83b3b84 100755 --- a/ci.sh +++ b/ci.sh @@ -3,7 +3,7 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings -export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info +export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info TARGET=$(rustc -vV | sed -n 's|host: ||p') @@ -20,20 +20,19 @@ cargo batch \ --- 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-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ - --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ + --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ @@ -53,6 +52,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \ + --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,qspi-as-gpio \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any \ @@ -81,11 +81,13 @@ cargo batch \ --- 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 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 \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32g474pe,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ @@ -115,6 +117,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \ --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \ --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \ + --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \ --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \ --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \ --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ diff --git a/ci_stable.sh b/ci_stable.sh index daae9896..4ee5f410 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -16,9 +16,7 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ @@ -36,6 +34,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \ + --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \ diff --git a/cyw43-firmware/43439A0.bin b/cyw43-firmware/43439A0.bin index b46b3bef..01737527 100755 Binary files a/cyw43-firmware/43439A0.bin and b/cyw43-firmware/43439A0.bin differ diff --git a/cyw43-firmware/43439A0_clm.bin b/cyw43-firmware/43439A0_clm.bin index 6e3ba786..1fedd753 100755 Binary files a/cyw43-firmware/43439A0_clm.bin and b/cyw43-firmware/43439A0_clm.bin differ diff --git a/cyw43-firmware/README.md b/cyw43-firmware/README.md index 7381fdc5..db3d9c9c 100644 --- a/cyw43-firmware/README.md +++ b/cyw43-firmware/README.md @@ -2,4 +2,8 @@ Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439 -Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt) \ No newline at end of file +Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt) + +## Changelog + +* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 ot 7.95.62 diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index dca30c74..830a5b44 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -8,7 +8,6 @@ use cyw43::SpiBusCyw43; use embassy_rp::dma::Channel; use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate}; use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef}; use fixed::FixedU32; use pio_proc::pio_asm; @@ -88,8 +87,6 @@ where ".wrap" ); - let relocated = RelocatedProgram::new(&program.program); - let mut pin_io: embassy_rp::pio::Pin = common.make_pio_pin(dio); pin_io.set_pull(Pull::None); pin_io.set_schmitt(true); @@ -102,7 +99,8 @@ where pin_clk.set_slew_rate(SlewRate::Fast); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&pin_clk]); + let loaded_program = common.load_program(&program.program); + cfg.use_program(&loaded_program, &[&pin_clk]); cfg.set_out_pins(&[&pin_io]); cfg.set_in_pins(&[&pin_io]); cfg.set_set_pins(&[&pin_io]); @@ -142,7 +140,7 @@ where sm, irq, dma: dma.into_ref(), - wrap_target: relocated.wrap().target, + wrap_target: loaded_program.wrap.target, } } diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 50fb7c5d..855d54b1 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -24,7 +24,7 @@ cortex-m = "0.7.6" cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" } num_enum = { version = "0.5.7", default-features = false } [package.metadata.embassy_docs] diff --git a/cyw43/src/bus.rs b/cyw43/src/bus.rs index e26f1112..0b5632cf 100644 --- a/cyw43/src/bus.rs +++ b/cyw43/src/bus.rs @@ -102,7 +102,7 @@ where cmd_buf[0] = cmd; cmd_buf[1..][..buf.len()].copy_from_slice(buf); - self.status = self.spi.cmd_write(&cmd_buf).await; + self.status = self.spi.cmd_write(&cmd_buf[..buf.len() + 1]).await; } #[allow(unused)] diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index fd11f367..30a3d5f2 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs @@ -216,7 +216,7 @@ where PWR: OutputPin, SPI: SpiBusCyw43, { - let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); let state_ch = ch_runner.state_runner(); let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index 237ae0ac..e94358a9 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.2.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index a7236ed5..b39f02ae 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" cortex-m = "0.7" cortex-m-rt = "0.7" embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] } -embassy-executor = { version = "0.2.0", features = ["nightly", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.3.0", features = ["nightly", "arch-cortex-m", "executor-thread"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index 20731ee0..ae713bb6 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state -pub struct FirmwareUpdater { +pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, - state: STATE, + state: FirmwareState<'d, STATE>, } #[cfg(target_os = "none")] @@ -47,22 +47,12 @@ impl<'a, FLASH: NorFlash> } } -impl FirmwareUpdater { +impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub fn new(config: FirmwareUpdaterConfig) -> Self { + pub fn new(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { Self { dfu: config.dfu, - state: config.state, - } - } - - // Make sure we are running a booted firmware to avoid reverting to a bad state. - async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - if self.get_state(aligned).await? == State::Boot { - Ok(()) - } else { - Err(FirmwareUpdaterError::BadState) + state: FirmwareState::new(config.state, aligned), } } @@ -71,14 +61,8 @@ impl FirmwareUpdater { /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result { - self.state.read(0, aligned).await?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } + pub async fn get_state(&mut self) -> Result { + self.state.get_state().await } /// Verify the DFU given a public key. If there is an error then DO NOT @@ -92,23 +76,16 @@ impl FirmwareUpdater { /// /// If no signature feature is set then this method will always return a /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. #[cfg(feature = "_verify")] pub async fn verify_and_mark_updated( &mut self, _public_key: &[u8], _signature: &[u8], _update_len: u32, - _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert!(_update_len <= self.dfu.capacity() as u32); - self.verify_booted(_aligned).await?; + self.state.verify_booted().await?; #[cfg(feature = "ed25519-dalek")] { @@ -121,8 +98,9 @@ impl FirmwareUpdater { let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + let mut chunk_buf = [0; 2]; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message).await?; + self.hash::(_update_len, &mut chunk_buf, &mut message).await?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -143,7 +121,8 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message).await?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message).await?; let r = public_key.verify(&message, &signature); trace!( @@ -156,7 +135,7 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC).await + self.state.mark_updated().await } /// Verify the update in DFU with any digest. @@ -177,49 +156,14 @@ impl FirmwareUpdater { } /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. #[cfg(not(feature = "_verify"))] - pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC).await + pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_updated().await } /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC).await - } - - async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { - self.state.read(0, aligned).await?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(STATE::WRITE_SIZE as u32, aligned).await?; - - if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - aligned.fill(!STATE_ERASE_VALUE); - self.state.write(STATE::WRITE_SIZE as u32, aligned).await?; - } - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32).await?; - - // Set magic - aligned.fill(magic); - self.state.write(0, aligned).await?; - } - Ok(()) + pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_booted().await } /// Write data to a flash page. @@ -229,16 +173,10 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - aligned: &mut [u8], - offset: usize, - data: &[u8], - ) -> Result<(), FirmwareUpdaterError> { + pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= DFU::ERASE_SIZE); - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned).await?; + self.state.verify_booted().await?; self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; @@ -252,20 +190,94 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned).await?; - + pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.state.verify_booted().await?; self.dfu.erase(0, self.dfu.capacity() as u32).await?; Ok(&mut self.dfu) } } +/// Manages the state partition of the firmware update. +/// +/// Can be used standalone for more fine grained control, or as part of the updater. +pub struct FirmwareState<'d, STATE> { + state: STATE, + aligned: &'d mut [u8], +} + +impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { + /// Create a firmware state instance with a buffer for magic content and state partition. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + Self { state, aligned } + } + + // Make sure we are running a booted firmware to avoid reverting to a bad state. + async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + if self.get_state().await? == State::Boot { + Ok(()) + } else { + Err(FirmwareUpdaterError::BadState) + } + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state(&mut self) -> Result { + self.state.read(0, &mut self.aligned).await?; + + if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Mark to trigger firmware swap on next boot. + pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(SWAP_MAGIC).await + } + + /// Mark firmware boot successful and stop rollback on reset. + pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(BOOT_MAGIC).await + } + + async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, &mut self.aligned).await?; + + if self.aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?; + + if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + self.aligned.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?; + } + + // Clear magic and progress + self.state.erase(0, self.state.capacity() as u32).await?; + + // Set magic + self.aligned.fill(magic); + self.state.write(0, &self.aligned).await?; + } + Ok(()) + } +} + #[cfg(test)] mod tests { use embassy_embedded_hal::flash::partition::Partition; @@ -288,8 +300,8 @@ mod tests { let mut to_write = [0; 4096]; to_write[..7].copy_from_slice(update.as_slice()); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); - block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index f03f53e4..76e4264a 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state -pub struct BlockingFirmwareUpdater { +pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, - state: STATE, + state: BlockingFirmwareState<'d, STATE>, } #[cfg(target_os = "none")] @@ -49,22 +49,17 @@ impl<'a, FLASH: NorFlash> } } -impl BlockingFirmwareUpdater { +impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> { /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub fn new(config: FirmwareUpdaterConfig) -> Self { + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { Self { dfu: config.dfu, - state: config.state, - } - } - - // Make sure we are running a booted firmware to avoid reverting to a bad state. - fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - if self.get_state(aligned)? == State::Boot { - Ok(()) - } else { - Err(FirmwareUpdaterError::BadState) + state: BlockingFirmwareState::new(config.state, aligned), } } @@ -73,14 +68,8 @@ impl BlockingFirmwareUpdater { /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub fn get_state(&mut self, aligned: &mut [u8]) -> Result { - self.state.read(0, aligned)?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } + pub fn get_state(&mut self) -> Result { + self.state.get_state() } /// Verify the DFU given a public key. If there is an error then DO NOT @@ -94,23 +83,16 @@ impl BlockingFirmwareUpdater { /// /// If no signature feature is set then this method will always return a /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. #[cfg(feature = "_verify")] pub fn verify_and_mark_updated( &mut self, _public_key: &[u8], _signature: &[u8], _update_len: u32, - _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert!(_update_len <= self.dfu.capacity() as u32); - self.verify_booted(_aligned)?; + self.state.verify_booted()?; #[cfg(feature = "ed25519-dalek")] { @@ -124,7 +106,8 @@ impl BlockingFirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message)?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message)?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -145,7 +128,8 @@ impl BlockingFirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message)?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message)?; let r = public_key.verify(&message, &signature); trace!( @@ -158,7 +142,7 @@ impl BlockingFirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC) + self.state.mark_updated() } /// Verify the update in DFU with any digest. @@ -179,49 +163,14 @@ impl BlockingFirmwareUpdater { } /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. #[cfg(not(feature = "_verify"))] - pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC) + pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_updated() } /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC) - } - - fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { - self.state.read(0, aligned)?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(STATE::WRITE_SIZE as u32, aligned)?; - - if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - aligned.fill(!STATE_ERASE_VALUE); - self.state.write(STATE::WRITE_SIZE as u32, aligned)?; - } - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32)?; - - // Set magic - aligned.fill(magic); - self.state.write(0, aligned)?; - } - Ok(()) + pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_booted() } /// Write data to a flash page. @@ -231,15 +180,9 @@ impl BlockingFirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware( - &mut self, - aligned: &mut [u8], - offset: usize, - data: &[u8], - ) -> Result<(), FirmwareUpdaterError> { + pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= DFU::ERASE_SIZE); - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned)?; + self.state.verify_booted()?; self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; @@ -253,19 +196,94 @@ impl BlockingFirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned)?; + pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.state.verify_booted()?; self.dfu.erase(0, self.dfu.capacity() as u32)?; Ok(&mut self.dfu) } } +/// Manages the state partition of the firmware update. +/// +/// Can be used standalone for more fine grained control, or as part of the updater. +pub struct BlockingFirmwareState<'d, STATE> { + state: STATE, + aligned: &'d mut [u8], +} + +impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { + /// Create a firmware state instance with a buffer for magic content and state partition. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + Self { state, aligned } + } + + // Make sure we are running a booted firmware to avoid reverting to a bad state. + fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + if self.get_state()? == State::Boot { + Ok(()) + } else { + Err(FirmwareUpdaterError::BadState) + } + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state(&mut self) -> Result { + self.state.read(0, &mut self.aligned)?; + + if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Mark to trigger firmware swap on next boot. + pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(SWAP_MAGIC) + } + + /// Mark firmware boot successful and stop rollback on reset. + pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(BOOT_MAGIC) + } + + fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, &mut self.aligned)?; + + if self.aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned)?; + + if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + self.aligned.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, &self.aligned)?; + } + + // Clear magic and progress + self.state.erase(0, self.state.capacity() as u32)?; + + // Set magic + self.aligned.fill(magic); + self.state.write(0, &self.aligned)?; + } + Ok(()) + } +} + #[cfg(test)] mod tests { use core::cell::RefCell; @@ -283,14 +301,14 @@ mod tests { let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); let state = BlockingPartition::new(&flash, 0, 4096); let dfu = BlockingPartition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; let mut to_write = [0; 4096]; to_write[..7].copy_from_slice(update.as_slice()); - let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); - let mut aligned = [0; 8]; - updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + updater.write_firmware(0, to_write.as_slice()).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; updater diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs index 55ce8f36..937ddcc6 100644 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ b/embassy-boot/boot/src/firmware_updater/mod.rs @@ -3,8 +3,8 @@ mod asynch; mod blocking; #[cfg(feature = "nightly")] -pub use asynch::FirmwareUpdater; -pub use blocking::BlockingFirmwareUpdater; +pub use asynch::{FirmwareState, FirmwareUpdater}; +pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater}; use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; /// Firmware updater flash configuration holding the two flashes used by the updater diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 016362b8..47f7c179 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -16,9 +16,11 @@ mod test_flash; // TODO: Use the value provided by NorFlash when available pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; +pub use firmware_updater::{ + BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError, +}; #[cfg(feature = "nightly")] -pub use firmware_updater::FirmwareUpdater; -pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError}; +pub use firmware_updater::{FirmwareState, FirmwareUpdater}; pub(crate) const BOOT_MAGIC: u8 = 0xD0; pub(crate) const SWAP_MAGIC: u8 = 0xF0; @@ -118,15 +120,18 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); // Writing after marking updated is not allowed until marked as booted. - let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)); + let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE)); assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState))); let flash = flash.into_blocking(); @@ -158,11 +163,14 @@ mod tests { // Mark as booted let flash = flash.into_async(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.mark_booted(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.mark_booted()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -190,12 +198,15 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -232,12 +243,15 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -293,18 +307,19 @@ mod tests { // On with the test let flash = flash.into_async(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - let mut aligned = [0; 4]; + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); assert!(block_on(updater.verify_and_mark_updated( &public_key.to_bytes(), &signature.to_bytes(), firmware_len as u32, - &mut aligned, )) .is_ok()); } diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index bb702073..b9d86eb1 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -3,37 +3,28 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig}; -use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; +use embassy_nrf::nvmc::PAGE_SIZE; use embassy_nrf::peripherals::WDT; use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for nRF devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(&mut self.aligned_buf.0) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware. + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error"); + Self } /// Boots the application without softdevice mechanisms. @@ -43,8 +34,6 @@ impl /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(not(feature = "softdevice"))] pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - let mut p = cortex_m::Peripherals::steal(); p.SCB.invalidate_icache(); p.SCB.vtor.write(start); @@ -57,7 +46,7 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(feature = "softdevice")] - pub unsafe fn load(&mut self, _app: u32) -> ! { + pub unsafe fn load(self, _app: u32) -> ! { use nrf_softdevice_mbr as mbr; const NRF_SUCCESS: u32 = 0; @@ -104,15 +93,15 @@ impl } } -/// A flash implementation that wraps NVMC and will pet a watchdog when touching flash. -pub struct WatchdogFlash<'d> { - flash: Nvmc<'d>, +/// A flash implementation that wraps any flash and will pet a watchdog when touching flash. +pub struct WatchdogFlash { + flash: FLASH, wdt: wdt::WatchdogHandle, } -impl<'d> WatchdogFlash<'d> { +impl WatchdogFlash { /// Start a new watchdog with a given flash and WDT peripheral and a timeout - pub fn start(flash: Nvmc<'d>, wdt: WDT, config: wdt::Config) -> Self { + pub fn start(flash: FLASH, wdt: WDT, config: wdt::Config) -> Self { let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) { Ok(x) => x, Err(_) => { @@ -127,13 +116,13 @@ impl<'d> WatchdogFlash<'d> { } } -impl<'d> ErrorType for WatchdogFlash<'d> { - type Error = as ErrorType>::Error; +impl ErrorType for WatchdogFlash { + type Error = FLASH::Error; } -impl<'d> NorFlash for WatchdogFlash<'d> { - const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; - const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; +impl NorFlash for WatchdogFlash { + const WRITE_SIZE: usize = FLASH::WRITE_SIZE; + const ERASE_SIZE: usize = FLASH::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.wdt.pet(); @@ -145,8 +134,8 @@ impl<'d> NorFlash for WatchdogFlash<'d> { } } -impl<'d> ReadNorFlash for WatchdogFlash<'d> { - const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; +impl ReadNorFlash for WatchdogFlash { + const READ_SIZE: usize = FLASH::READ_SIZE; fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.wdt.pet(); self.flash.read(offset, data) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 25329f9e..989e7521 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -3,38 +3,29 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; -use embassy_rp::flash::{Flash, ERASE_SIZE}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; +use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; use embassy_time::Duration; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(self.aligned_buf.as_mut()) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error"); + Self } /// Boots the application. @@ -43,8 +34,6 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); @@ -58,14 +47,14 @@ impl /// A flash implementation that will feed a watchdog when touching flash. pub struct WatchdogFlash<'d, const SIZE: usize> { - flash: Flash<'d, FLASH, SIZE>, + flash: Flash<'d, FLASH, Blocking, SIZE>, watchdog: Watchdog, } impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { /// Start a new watchdog with a given flash and watchdog peripheral and a timeout pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { - let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash); + let flash = Flash::<_, Blocking, SIZE>::new_blocking(flash); let mut watchdog = Watchdog::new(watchdog); watchdog.start(timeout); Self { flash, watchdog } @@ -73,28 +62,28 @@ impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { } impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> { - type Error = as ErrorType>::Error; + type Error = as ErrorType>::Error; } impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { - const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; - const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; + const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; + const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.erase(from, to) + self.flash.blocking_erase(from, to) } fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.write(offset, data) + self.flash.blocking_write(offset, data) } } impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { - const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; + const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.read(offset, data) + self.flash.blocking_read(offset, data) } fn capacity(&self) -> usize { self.flash.capacity() diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 069de0d1..c6350c49 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -3,34 +3,25 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embedded_storage::nor_flash::NorFlash; /// A bootloader for STM32 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(self.aligned_buf.as_mut()) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error"); + Self } /// Boots the application. @@ -39,8 +30,6 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index bba3d48b..fd921d27 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -25,8 +25,8 @@ embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", ] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index 98ae2b02..ae0d0a7f 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -1,4 +1,4 @@ -use embedded_hal_02::{blocking, serial}; +use embedded_hal_02::blocking; /// Wrapper that implements async traits using blocking implementations. /// @@ -103,15 +103,6 @@ where } } -// Uart implementatinos -impl embedded_hal_1::serial::ErrorType for BlockingAsync -where - T: serial::Read, - E: embedded_hal_1::serial::Error + 'static, -{ - type Error = E; -} - /// NOR flash wrapper use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 4fd3dccf..4853ba29 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,6 +5,19 @@ 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 + +- Replaced Pender. Implementations now must define an extern function called `__pender`. +- Made `raw::AvailableTask` public +- Made `SpawnToken::new_failed` public + +## 0.2.1 - 2023-08-10 + +- Avoid calling `pend()` when waking expired timers +- Properly reset finished task state with `integrated-timers` enabled +- Introduce `InterruptExecutor::spawner()` +- Fix incorrect critical section in Xtensa executor + ## 0.2.0 - 2023-04-27 - Replace unnecessary atomics in runqueue diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 590718e3..2a67f70d 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" @@ -14,7 +14,7 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["nightly", "defmt", "pender-callback"] +features = ["nightly", "defmt"] flavors = [ { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, @@ -25,7 +25,7 @@ flavors = [ [package.metadata.docs.rs] default-target = "thumbv7em-none-eabi" targets = ["thumbv7em-none-eabi"] -features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"] +features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] [features] @@ -37,9 +37,6 @@ arch-xtensa = ["_arch"] arch-riscv32 = ["_arch"] arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] -# Enable creating a `Pender` from an arbitrary function pointer callback. -pender-callback = [] - # Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs) executor-thread = [] # Enable the interrupt-mode executor (available in Cortex-M only) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 94c8134d..0806a22a 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,3 +1,49 @@ +const THREAD_PENDER: usize = usize::MAX; + +#[export_name = "__pender"] +#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] +fn __pender(context: *mut ()) { + unsafe { + // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt + // request number given to `InterruptExecutor::start`. + + let context = context as usize; + + #[cfg(feature = "executor-thread")] + // Try to make Rust optimize the branching away if we only use thread mode. + if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER { + core::arch::asm!("sev"); + return; + } + + #[cfg(feature = "executor-interrupt")] + { + use cortex_m::interrupt::InterruptNumber; + use cortex_m::peripheral::NVIC; + + #[derive(Clone, Copy)] + struct Irq(u16); + unsafe impl InterruptNumber for Irq { + fn number(self) -> u16 { + self.0 + } + } + + let irq = Irq(context as u16); + + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: NVIC = core::mem::transmute(()); + nvic.request(irq); + } + + #[cfg(armv6m)] + NVIC::pend(irq); + } + } +} + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -8,18 +54,9 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_cortex_m as main; - use crate::raw::{Pender, PenderInner}; + use crate::arch::THREAD_PENDER; use crate::{raw, Spawner}; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; - - impl ThreadPender { - pub(crate) fn pend(self) { - unsafe { core::arch::asm!("sev") } - } - } - /// Thread mode executor, using WFE/SEV. /// /// This is the simplest and most common kind of executor. It runs on @@ -39,7 +76,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + inner: raw::Executor::new(THREAD_PENDER as *mut ()), not_send: PhantomData, } } @@ -86,30 +123,7 @@ mod interrupt { use cortex_m::interrupt::InterruptNumber; use cortex_m::peripheral::NVIC; - use crate::raw::{self, Pender, PenderInner}; - - #[derive(Clone, Copy)] - pub(crate) struct InterruptPender(u16); - - impl InterruptPender { - pub(crate) fn pend(self) { - // STIR is faster, but is only available in v7 and higher. - #[cfg(not(armv6m))] - { - let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; - nvic.request(self); - } - - #[cfg(armv6m)] - cortex_m::peripheral::NVIC::pend(self); - } - } - - unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender { - fn number(self) -> u16 { - self.0 - } - } + use crate::raw; /// Interrupt mode executor. /// @@ -194,9 +208,7 @@ mod interrupt { unsafe { (&mut *self.executor.get()) .as_mut_ptr() - .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender( - irq.number(), - ))))) + .write(raw::Executor::new(irq.number() as *mut ())) } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index ff7ec157..40c6877e 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -11,22 +11,16 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_riscv as main; - use crate::raw::{Pender, PenderInner}; use crate::{raw, Spawner}; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); - } - } - /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + #[export_name = "__pender"] + fn __pender(_context: *mut ()) { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); + } + /// RISCV32 Executor pub struct Executor { inner: raw::Executor, @@ -37,7 +31,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + inner: raw::Executor::new(core::ptr::null_mut()), not_send: PhantomData, } } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 4e4a178f..5b2f7e2e 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -11,17 +11,12 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_std as main; - use crate::raw::{Pender, PenderInner}; use crate::{raw, Spawner}; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender(&'static Signaler); - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - self.0.signal() - } + #[export_name = "__pender"] + fn __pender(context: *mut ()) { + let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; + signaler.signal() } /// Single-threaded std-based executor. @@ -34,9 +29,9 @@ mod thread { impl Executor { /// Create a new Executor. pub fn new() -> Self { - let signaler = &*Box::leak(Box::new(Signaler::new())); + let signaler = Box::leak(Box::new(Signaler::new())); Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))), + inner: raw::Executor::new(signaler as *mut Signaler as *mut ()), not_send: PhantomData, signaler, } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 08ab16b9..934fd69e 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -14,14 +14,12 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::{Pender, PenderInner}; use crate::{raw, Spawner}; - /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. - pub struct Executor { - inner: raw::Executor, - ctx: &'static WasmContext, - not_send: PhantomData<*mut ()>, + #[export_name = "__pender"] + fn __pender(context: *mut ()) { + let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; + let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } pub(crate) struct WasmContext { @@ -29,16 +27,6 @@ mod thread { closure: UninitCell>, } - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender(&'static WasmContext); - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() }); - } - } - impl WasmContext { pub fn new() -> Self { Self { @@ -48,14 +36,21 @@ mod thread { } } + /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. + pub struct Executor { + inner: raw::Executor, + ctx: &'static WasmContext, + not_send: PhantomData<*mut ()>, + } + impl Executor { /// Create a new Executor. pub fn new() -> Self { - let ctx = &*Box::leak(Box::new(WasmContext::new())); + let ctx = Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))), - not_send: PhantomData, + inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()), ctx, + not_send: PhantomData, } } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 017b2c52..601d8500 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -8,22 +8,16 @@ mod thread { use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; - use crate::raw::{Pender, PenderInner}; use crate::{raw, Spawner}; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); - } - } - /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + #[export_name = "__pender"] + fn __pender(_context: *mut ()) { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); + } + /// Xtensa Executor pub struct Executor { inner: raw::Executor, @@ -34,7 +28,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + inner: raw::Executor::new(core::ptr::null_mut()), not_send: PhantomData, } } @@ -77,8 +71,8 @@ mod thread { SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); core::arch::asm!( - "wsr.ps {0}", - "rsync", in(reg) token) + "wsr.ps {0}", + "rsync", in(reg) token) } else { // waiti sets the PS.INTLEVEL when slipping into sleep // because critical sections in Xtensa are implemented via increasing diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f3760f58..c1d82e18 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -147,10 +147,7 @@ impl TaskStorage { pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { let task = AvailableTask::claim(self); match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } + Some(task) => task.initialize(future), None => SpawnToken::new_failed(), } } @@ -186,12 +183,16 @@ impl TaskStorage { } } -struct AvailableTask { +/// An uninitialized [`TaskStorage`]. +pub struct AvailableTask { task: &'static TaskStorage, } impl AvailableTask { - fn claim(task: &'static TaskStorage) -> Option { + /// Try to claim a [`TaskStorage`]. + /// + /// This function returns `None` if a task has already been spawned and has not finished running. + pub fn claim(task: &'static TaskStorage) -> Option { task.raw .state .compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire) @@ -199,61 +200,30 @@ impl AvailableTask { .map(|_| Self { task }) } - fn initialize(self, future: impl FnOnce() -> F) -> TaskRef { + fn initialize_impl(self, future: impl FnOnce() -> F) -> SpawnToken { unsafe { self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write(future()); - } - TaskRef::new(self.task) - } -} -/// Raw storage that can hold up to N tasks of the same type. -/// -/// This is essentially a `[TaskStorage; N]`. -pub struct TaskPool { - pool: [TaskStorage; N], -} + let task = TaskRef::new(self.task); -impl TaskPool { - /// Create a new TaskPool, with all tasks in non-spawned state. - pub const fn new() -> Self { - Self { - pool: [TaskStorage::NEW; N], + SpawnToken::new(task) } } - /// Try to spawn a task in the pool. - /// - /// See [`TaskStorage::spawn()`] for details. - /// - /// This will loop over the pool and spawn the task in the first storage that - /// is currently free. If none is free, a "poisoned" SpawnToken is returned, - /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. - pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - let task = self.pool.iter().find_map(AvailableTask::claim); - match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } - None => SpawnToken::new_failed(), - } + /// Initialize the [`TaskStorage`] to run the given future. + pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken { + self.initialize_impl::(future) } - /// Like spawn(), but allows the task to be send-spawned if the args are Send even if - /// the future is !Send. + /// Initialize the [`TaskStorage`] to run the given future. /// - /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used - /// by the Embassy macros ONLY. + /// # Safety /// - /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` + /// `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` /// is an `async fn`, NOT a hand-written `Future`. #[doc(hidden)] - pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken - where - FutFn: FnOnce() -> F, - { + pub unsafe fn __initialize_async_fn(self, future: impl FnOnce() -> F) -> SpawnToken { // When send-spawning a task, we construct the future in this thread, and effectively // "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory, // send-spawning should require the future `F` to be `Send`. @@ -279,66 +249,73 @@ impl TaskPool { // // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken`. + self.initialize_impl::(future) + } +} - let task = self.pool.iter().find_map(AvailableTask::claim); - match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } +/// Raw storage that can hold up to N tasks of the same type. +/// +/// This is essentially a `[TaskStorage; N]`. +pub struct TaskPool { + pool: [TaskStorage; N], +} + +impl TaskPool { + /// Create a new TaskPool, with all tasks in non-spawned state. + pub const fn new() -> Self { + Self { + pool: [TaskStorage::NEW; N], + } + } + + fn spawn_impl(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + match self.pool.iter().find_map(AvailableTask::claim) { + Some(task) => task.initialize_impl::(future), None => SpawnToken::new_failed(), } } + + /// Try to spawn a task in the pool. + /// + /// See [`TaskStorage::spawn()`] for details. + /// + /// This will loop over the pool and spawn the task in the first storage that + /// is currently free. If none is free, a "poisoned" SpawnToken is returned, + /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. + pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + self.spawn_impl::(future) + } + + /// Like spawn(), but allows the task to be send-spawned if the args are Send even if + /// the future is !Send. + /// + /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used + /// by the Embassy macros ONLY. + /// + /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` + /// is an `async fn`, NOT a hand-written `Future`. + #[doc(hidden)] + pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken + where + FutFn: FnOnce() -> F, + { + // See the comment in AvailableTask::__initialize_async_fn for explanation. + self.spawn_impl::(future) + } } #[derive(Clone, Copy)] -pub(crate) enum PenderInner { - #[cfg(feature = "executor-thread")] - Thread(crate::arch::ThreadPender), - #[cfg(feature = "executor-interrupt")] - Interrupt(crate::arch::InterruptPender), - #[cfg(feature = "pender-callback")] - Callback { func: fn(*mut ()), context: *mut () }, -} +pub(crate) struct Pender(*mut ()); -unsafe impl Send for PenderInner {} -unsafe impl Sync for PenderInner {} - -/// Platform/architecture-specific action executed when an executor has pending work. -/// -/// When a task within an executor is woken, the `Pender` is called. This does a -/// platform/architecture-specific action to signal there is pending work in the executor. -/// When this happens, you must arrange for [`Executor::poll`] to be called. -/// -/// You can think of it as a waker, but for the whole executor. -pub struct Pender(pub(crate) PenderInner); +unsafe impl Send for Pender {} +unsafe impl Sync for Pender {} impl Pender { - /// Create a `Pender` that will call an arbitrary function pointer. - /// - /// # Arguments - /// - /// - `func`: The function pointer to call. - /// - `context`: Opaque context pointer, that will be passed to the function pointer. - #[cfg(feature = "pender-callback")] - pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self { - Self(PenderInner::Callback { - func, - context: context.into(), - }) - } -} - -impl Pender { - pub(crate) fn pend(&self) { - match self.0 { - #[cfg(feature = "executor-thread")] - PenderInner::Thread(x) => x.pend(), - #[cfg(feature = "executor-interrupt")] - PenderInner::Interrupt(x) => x.pend(), - #[cfg(feature = "pender-callback")] - PenderInner::Callback { func, context } => func(context), + pub(crate) fn pend(self) { + extern "Rust" { + fn __pender(context: *mut ()); } + unsafe { __pender(self.0) }; } } @@ -409,7 +386,7 @@ impl SyncExecutor { #[allow(clippy::never_loop)] loop { #[cfg(feature = "integrated-timers")] - self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); + self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend); self.run_queue.dequeue_all(|p| { let task = p.header(); @@ -472,15 +449,31 @@ impl SyncExecutor { /// /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks /// that "want to run"). -/// - You must supply a [`Pender`]. The executor will call it to notify you it has work -/// to do. You must arrange for `poll()` to be called as soon as possible. +/// - You must supply a pender function, as shown below. The executor will call it to notify you +/// it has work to do. You must arrange for `poll()` to be called as soon as possible. +/// - Enabling `arch-xx` features will define a pender function for you. This means that you +/// are limited to using the executors provided to you by the architecture/platform +/// implementation. If you need a different executor, you must not enable `arch-xx` features. /// -/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority +/// The pender can be called from *any* context: any thread, any interrupt priority /// level, etc. It may be called synchronously from any `Executor` method call as well. /// You must deal with this correctly. /// /// In particular, you must NOT call `poll` directly from the pender callback, as this violates /// the requirement for `poll` to not be called reentrantly. +/// +/// The pender function must be exported with the name `__pender` and have the following signature: +/// +/// ```rust +/// #[export_name = "__pender"] +/// fn pender(context: *mut ()) { +/// // schedule `poll()` to be called +/// } +/// ``` +/// +/// The `context` argument is a piece of arbitrary data the executor will pass to the pender. +/// You can set the `context` when calling [`Executor::new()`]. You can use it to, for example, +/// differentiate between executors, or to pass a pointer to a callback that should be called. #[repr(transparent)] pub struct Executor { pub(crate) inner: SyncExecutor, @@ -495,12 +488,12 @@ impl Executor { /// Create a new executor. /// - /// When the executor has work to do, it will call the [`Pender`]. + /// When the executor has work to do, it will call the pender function and pass `context` to it. /// - /// See [`Executor`] docs for details on `Pender`. - pub fn new(pender: Pender) -> Self { + /// See [`Executor`] docs for details on the pender. + pub fn new(context: *mut ()) -> Self { Self { - inner: SyncExecutor::new(pender), + inner: SyncExecutor::new(Pender(context)), _not_sync: PhantomData, } } @@ -523,16 +516,16 @@ impl Executor { /// This loops over all tasks that are queued to be polled (i.e. they're /// freshly spawned or they've been woken). Other tasks are not polled. /// - /// You must call `poll` after receiving a call to the [`Pender`]. It is OK - /// to call `poll` even when not requested by the `Pender`, but it wastes + /// You must call `poll` after receiving a call to the pender. It is OK + /// to call `poll` even when not requested by the pender, but it wastes /// energy. /// /// # Safety /// /// You must NOT call `poll` reentrantly on the same executor. /// - /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you - /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to + /// In particular, note that `poll` may call the pender synchronously. Therefore, you + /// must NOT directly call `poll()` from the pender callback. Instead, the callback has to /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. pub unsafe fn poll(&'static self) { @@ -573,6 +566,31 @@ pub fn wake_task(task: TaskRef) { } } +/// Wake a task by `TaskRef` without calling pend. +/// +/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. +pub fn wake_task_no_pend(task: TaskRef) { + let header = task.header(); + + let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { + // If already scheduled, or if not started, + if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { + None + } else { + // Mark it as scheduled + Some(state | STATE_RUN_QUEUED) + } + }); + + if res.is_ok() { + // We have just marked the task as scheduled, so enqueue it. + unsafe { + let executor = header.executor.get().unwrap_unchecked(); + executor.run_queue.enqueue(task); + } + } +} + #[cfg(feature = "integrated-timers")] struct TimerQueue; diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 2b622404..5a3a0dee 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -33,7 +33,8 @@ impl SpawnToken { } } - pub(crate) fn new_failed() -> Self { + /// Return a SpawnToken that represents a failed spawn. + pub fn new_failed() -> Self { Self { raw_task: None, phantom: PhantomData, diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-internal/Cargo.toml similarity index 95% rename from embassy-hal-common/Cargo.toml rename to embassy-hal-internal/Cargo.toml index 18c758d7..42e03199 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-internal/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "embassy-hal-common" +name = "embassy-hal-internal" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/embassy-hal-internal/README.md b/embassy-hal-internal/README.md new file mode 100644 index 00000000..d6539701 --- /dev/null +++ b/embassy-hal-internal/README.md @@ -0,0 +1,16 @@ +# embassy-macros + +An [Embassy](https://embassy.dev) project. + +Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport +everything you need to use them effectively. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-hal-common/build.rs b/embassy-hal-internal/build.rs similarity index 100% rename from embassy-hal-common/build.rs rename to embassy-hal-internal/build.rs diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs similarity index 100% rename from embassy-hal-common/src/atomic_ring_buffer.rs rename to embassy-hal-internal/src/atomic_ring_buffer.rs diff --git a/embassy-hal-common/src/drop.rs b/embassy-hal-internal/src/drop.rs similarity index 100% rename from embassy-hal-common/src/drop.rs rename to embassy-hal-internal/src/drop.rs diff --git a/embassy-hal-common/src/fmt.rs b/embassy-hal-internal/src/fmt.rs similarity index 100% rename from embassy-hal-common/src/fmt.rs rename to embassy-hal-internal/src/fmt.rs diff --git a/embassy-hal-common/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs similarity index 100% rename from embassy-hal-common/src/interrupt.rs rename to embassy-hal-internal/src/interrupt.rs diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-internal/src/lib.rs similarity index 89% rename from embassy-hal-common/src/lib.rs rename to embassy-hal-internal/src/lib.rs index 235964aa..3640ea18 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-internal/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(clippy::new_without_default)] +#![doc = include_str!("../README.md")] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-internal/src/macros.rs similarity index 98% rename from embassy-hal-common/src/macros.rs rename to embassy-hal-internal/src/macros.rs index f06b4600..0eea4b66 100644 --- a/embassy-hal-common/src/macros.rs +++ b/embassy-hal-internal/src/macros.rs @@ -116,6 +116,7 @@ macro_rules! impl_peripheral { #[inline] unsafe fn clone_unchecked(&self) -> Self::P { + #[allow(clippy::needless_update)] $type { ..*self } } } diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-internal/src/peripheral.rs similarity index 100% rename from embassy-hal-common/src/peripheral.rs rename to embassy-hal-internal/src/peripheral.rs diff --git a/embassy-hal-common/src/ratio.rs b/embassy-hal-internal/src/ratio.rs similarity index 100% rename from embassy-hal-common/src/ratio.rs rename to embassy-hal-internal/src/ratio.rs diff --git a/embassy-hal-common/src/ring_buffer.rs b/embassy-hal-internal/src/ring_buffer.rs similarity index 100% rename from embassy-hal-common/src/ring_buffer.rs rename to embassy-hal-internal/src/ring_buffer.rs diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index e4524af5..feea0658 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi" [features] stm32wl = ["dep:embassy-stm32"] -time = [] +time = ["embassy-time", "lorawan-device"] defmt = ["dep:defmt", "lorawan-device/defmt"] [dependencies] @@ -20,18 +20,15 @@ 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" } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } -futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } +embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-hal = { version = "0.2", features = ["unproven"] } -bit_field = { version = "0.10" } +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"] } +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 = "ad289428fd44b02788e2fa2116445cc8f640a265" } +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/embassy-net-driver-channel/README.md b/embassy-net-driver-channel/README.md index dd90e7ad..8f904ce9 100644 --- a/embassy-net-driver-channel/README.md +++ b/embassy-net-driver-channel/README.md @@ -76,7 +76,7 @@ These `embassy-net` drivers are implemented using this crate. You can look at th - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. -- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. +- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips. - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 02a4c00d..076238ba 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -42,7 +42,7 @@ struct StateInner<'d, const MTU: usize> { struct Shared { link_state: LinkState, waker: WakerRegistration, - ethernet_address: [u8; 6], + hardware_address: driver::HardwareAddress, } pub struct Runner<'d, const MTU: usize> { @@ -85,10 +85,10 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { }); } - pub fn set_ethernet_address(&mut self, address: [u8; 6]) { + pub fn set_hardware_address(&mut self, address: driver::HardwareAddress) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.ethernet_address = address; + s.hardware_address = address; s.waker.wake(); }); } @@ -150,7 +150,15 @@ impl<'d> StateRunner<'d> { pub fn set_ethernet_address(&self, address: [u8; 6]) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.ethernet_address = address; + s.hardware_address = driver::HardwareAddress::Ethernet(address); + s.waker.wake(); + }); + } + + pub fn set_ieee802154_address(&self, address: [u8; 8]) { + self.shared.lock(|s| { + let s = &mut *s.borrow_mut(); + s.hardware_address = driver::HardwareAddress::Ieee802154(address); s.waker.wake(); }); } @@ -206,7 +214,7 @@ impl<'d, const MTU: usize> TxRunner<'d, MTU> { pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( state: &'d mut State, - ethernet_address: [u8; 6], + hardware_address: driver::HardwareAddress, ) -> (Runner<'d, MTU>, Device<'d, MTU>) { let mut caps = Capabilities::default(); caps.max_transmission_unit = MTU; @@ -222,7 +230,7 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( tx: zerocopy_channel::Channel::new(&mut state.tx[..]), shared: Mutex::new(RefCell::new(Shared { link_state: LinkState::Down, - ethernet_address, + hardware_address, waker: WakerRegistration::new(), })), }); @@ -289,8 +297,8 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { self.caps.clone() } - fn ethernet_address(&self) -> [u8; 6] { - self.shared.lock(|s| s.borrow().ethernet_address) + fn hardware_address(&self) -> driver::HardwareAddress { + self.shared.lock(|s| s.borrow().hardware_address) } fn link_state(&mut self, cx: &mut Context) -> LinkState { diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml index da6d9ad6..e25950b6 100644 --- a/embassy-net-driver/Cargo.toml +++ b/embassy-net-driver/Cargo.toml @@ -21,4 +21,4 @@ target = "thumbv7em-none-eabi" features = ["defmt"] [dependencies] -defmt = { version = "0.3", optional = true } \ No newline at end of file +defmt = { version = "0.3", optional = true } diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index 4149bf4a..b64c1000 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -4,6 +4,18 @@ use core::task::Context; +/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum HardwareAddress { + /// A six-octet Ethernet address + Ethernet([u8; 6]), + /// An eight-octet IEEE802.15.4 address + Ieee802154([u8; 8]), + /// Indicates that a Driver is IP-native, and has no hardware address + Ip, +} + /// Main `embassy-net` driver API. /// /// This is essentially an interface for sending and receiving raw network frames. @@ -51,8 +63,8 @@ pub trait Driver { /// Get a description of device capabilities. fn capabilities(&self) -> Capabilities; - /// Get the device's Ethernet address. - fn ethernet_address(&self) -> [u8; 6]; + /// Get the device's hardware address. + fn hardware_address(&self) -> HardwareAddress; } impl Driver for &mut T { @@ -75,8 +87,8 @@ impl Driver for &mut T { fn link_state(&mut self, cx: &mut Context) -> LinkState { T::link_state(self, cx) } - fn ethernet_address(&self) -> [u8; 6] { - T::ethernet_address(self) + fn hardware_address(&self) -> HardwareAddress { + T::hardware_address(self) } } @@ -164,6 +176,9 @@ pub enum Medium { /// /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. Ip, + + /// IEEE 802_15_4 medium + Ieee802154, } impl Default for Medium { diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml new file mode 100644 index 00000000..e02c984e --- /dev/null +++ b/embassy-net-enc28j60/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "embassy-net-enc28j60" +version = "0.1.0" +description = "embassy-net driver for the ENC28J60 ethernet chip" +keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[dependencies] +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-futures = { version = "0.1.0", path = "../embassy-futures" } + +defmt = { version = "0.3", optional = true } +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 diff --git a/embassy-net-enc28j60/README.md b/embassy-net-enc28j60/README.md new file mode 100644 index 00000000..39011ca1 --- /dev/null +++ b/embassy-net-enc28j60/README.md @@ -0,0 +1,19 @@ +# `embassy-net-enc28j60` + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip. + +Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate. + +## Interoperability + +This crate can run on any executor. + +## 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-enc28j60/src/bank0.rs b/embassy-net-enc28j60/src/bank0.rs new file mode 100644 index 00000000..1c1b3a7f --- /dev/null +++ b/embassy-net-enc28j60/src/bank0.rs @@ -0,0 +1,69 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + ERDPTL = 0x00, + ERDPTH = 0x01, + EWRPTL = 0x02, + EWRPTH = 0x03, + ETXSTL = 0x04, + ETXSTH = 0x05, + ETXNDL = 0x06, + ETXNDH = 0x07, + ERXSTL = 0x08, + ERXSTH = 0x09, + ERXNDL = 0x0a, + ERXNDH = 0x0b, + ERXRDPTL = 0x0c, + ERXRDPTH = 0x0d, + ERXWRPTL = 0x0e, + ERXWRPTH = 0x0f, + EDMASTL = 0x10, + EDMASTH = 0x11, + EDMANDL = 0x12, + EDMANDH = 0x13, + EDMADSTL = 0x14, + EDMADSTH = 0x15, + EDMACSL = 0x16, + EDMACSH = 0x17, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::ERDPTL => true, + Register::ERDPTH => true, + Register::EWRPTL => true, + Register::EWRPTH => true, + Register::ETXSTL => true, + Register::ETXSTH => true, + Register::ETXNDL => true, + Register::ETXNDH => true, + Register::ERXSTL => true, + Register::ERXSTH => true, + Register::ERXNDL => true, + Register::ERXNDH => true, + Register::ERXRDPTL => true, + Register::ERXRDPTH => true, + Register::ERXWRPTL => true, + Register::ERXWRPTH => true, + Register::EDMASTL => true, + Register::EDMASTH => true, + Register::EDMANDL => true, + Register::EDMANDH => true, + Register::EDMADSTL => true, + Register::EDMADSTH => true, + Register::EDMACSL => true, + Register::EDMACSH => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank0(self) + } +} diff --git a/embassy-net-enc28j60/src/bank1.rs b/embassy-net-enc28j60/src/bank1.rs new file mode 100644 index 00000000..30560edf --- /dev/null +++ b/embassy-net-enc28j60/src/bank1.rs @@ -0,0 +1,84 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + EHT0 = 0x00, + EHT1 = 0x01, + EHT2 = 0x02, + EHT3 = 0x03, + EHT4 = 0x04, + EHT5 = 0x05, + EHT6 = 0x06, + EHT7 = 0x07, + EPMM0 = 0x08, + EPMM1 = 0x09, + EPMM2 = 0x0a, + EPMM3 = 0x0b, + EPMM4 = 0x0c, + EPMM5 = 0x0d, + EPMM6 = 0x0e, + EPMM7 = 0x0f, + EPMCSL = 0x10, + EPMCSH = 0x11, + EPMOL = 0x14, + EPMOH = 0x15, + ERXFCON = 0x18, + EPKTCNT = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::EHT0 => true, + Register::EHT1 => true, + Register::EHT2 => true, + Register::EHT3 => true, + Register::EHT4 => true, + Register::EHT5 => true, + Register::EHT6 => true, + Register::EHT7 => true, + Register::EPMM0 => true, + Register::EPMM1 => true, + Register::EPMM2 => true, + Register::EPMM3 => true, + Register::EPMM4 => true, + Register::EPMM5 => true, + Register::EPMM6 => true, + Register::EPMM7 => true, + Register::EPMCSL => true, + Register::EPMCSH => true, + Register::EPMOL => true, + Register::EPMOH => true, + Register::ERXFCON => true, + Register::EPKTCNT => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank1(self) + } +} + +register!(ERXFCON, 0b1010_0001, u8, { + #[doc = "Broadcast Filter Enable bit"] + bcen @ 0, + #[doc = "Multicast Filter Enable bit"] + mcen @ 1, + #[doc = "Hash Table Filter Enable bit"] + hten @ 2, + #[doc = "Magic Packetâ„¢ Filter Enable bit"] + mpen @ 3, + #[doc = "Pattern Match Filter Enable bit"] + pmen @ 4, + #[doc = "Post-Filter CRC Check Enable bit"] + crcen @ 5, + #[doc = "AND/OR Filter Select bit"] + andor @ 6, + #[doc = "Unicast Filter Enable bit"] + ucen @ 7, +}); diff --git a/embassy-net-enc28j60/src/bank2.rs b/embassy-net-enc28j60/src/bank2.rs new file mode 100644 index 00000000..74a1d245 --- /dev/null +++ b/embassy-net-enc28j60/src/bank2.rs @@ -0,0 +1,86 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + MACON1 = 0x00, + MACON3 = 0x02, + MACON4 = 0x03, + MABBIPG = 0x04, + MAIPGL = 0x06, + MAIPGH = 0x07, + MACLCON1 = 0x08, + MACLCON2 = 0x09, + MAMXFLL = 0x0a, + MAMXFLH = 0x0b, + MICMD = 0x12, + MIREGADR = 0x14, + MIWRL = 0x16, + MIWRH = 0x17, + MIRDL = 0x18, + MIRDH = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::MACON1 => false, + Register::MACON3 => false, + Register::MACON4 => false, + Register::MABBIPG => false, + Register::MAIPGL => false, + Register::MAIPGH => false, + Register::MACLCON1 => false, + Register::MACLCON2 => false, + Register::MAMXFLL => false, + Register::MAMXFLH => false, + Register::MICMD => false, + Register::MIREGADR => false, + Register::MIWRL => false, + Register::MIWRH => false, + Register::MIRDL => false, + Register::MIRDH => false, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank2(self) + } +} + +register!(MACON1, 0, u8, { + #[doc = "Enable packets to be received by the MAC"] + marxen @ 0, + #[doc = "Control frames will be discarded after being processed by the MAC"] + passall @ 1, + #[doc = "Inhibit transmissions when pause control frames are received"] + rxpaus @ 2, + #[doc = "Allow the MAC to transmit pause control frames"] + txpaus @ 3, +}); + +register!(MACON3, 0, u8, { + #[doc = "MAC will operate in Full-Duplex mode"] + fuldpx @ 0, + #[doc = "The type/length field of transmitted and received frames will be checked"] + frmlnen @ 1, + #[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"] + hfrmen @ 2, + #[doc = "No proprietary header is present"] + phdren @ 3, + #[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"] + txcrcen @ 4, + #[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"] + padcfg @ 5..7, +}); + +register!(MICMD, 0, u8, { + #[doc = "MII Read Enable bit"] + miird @ 0, + #[doc = "MII Scan Enable bit"] + miiscan @ 1, +}); diff --git a/embassy-net-enc28j60/src/bank3.rs b/embassy-net-enc28j60/src/bank3.rs new file mode 100644 index 00000000..4f7eb940 --- /dev/null +++ b/embassy-net-enc28j60/src/bank3.rs @@ -0,0 +1,53 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + MAADR5 = 0x00, + MAADR6 = 0x01, + MAADR3 = 0x02, + MAADR4 = 0x03, + MAADR1 = 0x04, + MAADR2 = 0x05, + EBSTSD = 0x06, + EBSTCON = 0x07, + EBSTCSL = 0x08, + EBSTCSH = 0x09, + MISTAT = 0x0a, + EREVID = 0x12, + ECOCON = 0x15, + EFLOCON = 0x17, + EPAUSL = 0x18, + EPAUSH = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::MAADR5 => false, + Register::MAADR6 => false, + Register::MAADR3 => false, + Register::MAADR4 => false, + Register::MAADR1 => false, + Register::MAADR2 => false, + Register::EBSTSD => true, + Register::EBSTCON => true, + Register::EBSTCSL => true, + Register::EBSTCSH => true, + Register::MISTAT => false, + Register::EREVID => true, + Register::ECOCON => true, + Register::EFLOCON => true, + Register::EPAUSL => true, + Register::EPAUSH => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank3(self) + } +} diff --git a/embassy-net-enc28j60/src/common.rs b/embassy-net-enc28j60/src/common.rs new file mode 100644 index 00000000..ef339dd2 --- /dev/null +++ b/embassy-net-enc28j60/src/common.rs @@ -0,0 +1,106 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + ECON1 = 0x1f, + ECON2 = 0x1e, + EIE = 0x1b, + EIR = 0x1c, + ESTAT = 0x1d, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::ECON1 => true, + Register::ECON2 => true, + Register::EIE => true, + Register::EIR => true, + Register::ESTAT => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Common(self) + } +} + +register!(EIE, 0, u8, { + #[doc = "Receive Error Interrupt Enable bit"] + rxerie @ 0, + #[doc = "Transmit Error Interrupt Enable bit"] + txerie @ 1, + #[doc = "Transmit Enable bit"] + txie @ 3, + #[doc = "Link Status Change Interrupt Enable bit"] + linkie @ 4, + #[doc = "DMA Interrupt Enable bit"] + dmaie @ 5, + #[doc = "Receive Packet Pending Interrupt Enable bit"] + pktie @ 6, + #[doc = "Global INT Interrupt Enable bit"] + intie @ 7, +}); + +register!(EIR, 0, u8, { + #[doc = "Receive Error Interrupt Flag bit"] + rxerif @ 0, + #[doc = "Transmit Error Interrupt Flag bit"] + txerif @ 1, + #[doc = "Transmit Interrupt Flag bit"] + txif @ 3, + #[doc = "Link Change Interrupt Flag bit"] + linkif @ 4, + #[doc = "DMA Interrupt Flag bit"] + dmaif @ 5, + #[doc = "Receive Packet Pending Interrupt Flag bit"] + pktif @ 6, +}); + +register!(ESTAT, 0, u8, { + #[doc = "Clock Ready bit"] + clkrdy @ 0, + #[doc = "Transmit Abort Error bit"] + txabrt @ 1, + #[doc = "Receive Busy bit"] + rxbusy @ 2, + #[doc = "Late Collision Error bit"] + latecol @ 4, + #[doc = "Ethernet Buffer Error Status bit"] + bufer @ 6, + #[doc = "INT Interrupt Flag bit"] + int @ 7, +}); + +register!(ECON2, 0b1000_0000, u8, { + #[doc = "Voltage Regulator Power Save Enable bit"] + vrps @ 3, + #[doc = "Power Save Enable bit"] + pwrsv @ 5, + #[doc = "Packet Decrement bit"] + pktdec @ 6, + #[doc = "Automatic Buffer Pointer Increment Enable bit"] + autoinc @ 7, +}); + +register!(ECON1, 0, u8, { + #[doc = "Bank Select bits"] + bsel @ 0..1, + #[doc = "Receive Enable bi"] + rxen @ 2, + #[doc = "Transmit Request to Send bit"] + txrts @ 3, + #[doc = "DMA Checksum Enable bit"] + csumen @ 4, + #[doc = "DMA Start and Busy Status bit"] + dmast @ 5, + #[doc = "Receive Logic Reset bit"] + rxrst @ 6, + #[doc = "Transmit Logic Reset bit"] + txrst @ 7, +}); diff --git a/embassy-net-enc28j60/src/fmt.rs b/embassy-net-enc28j60/src/fmt.rs new file mode 100644 index 00000000..06697081 --- /dev/null +++ b/embassy-net-enc28j60/src/fmt.rs @@ -0,0 +1,225 @@ +#![macro_use] +#![allow(unused_macros)] + +#[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 _ = ($( & $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 + } +} diff --git a/embassy-net-enc28j60/src/header.rs b/embassy-net-enc28j60/src/header.rs new file mode 100644 index 00000000..c2d4e468 --- /dev/null +++ b/embassy-net-enc28j60/src/header.rs @@ -0,0 +1,30 @@ +register!(RxStatus, 0, u32, { + #[doc = "Indicates length of the received frame"] + byte_count @ 0..15, + #[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"] + long_event @ 16, + #[doc = "Indicates that at some time since the last receive, a carrier event was detected"] + carrier_event @ 18, + #[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"] + crc_error @ 20, + #[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"] + length_check_error @ 21, + #[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"] + length_out_of_range @ 22, + #[doc = "Indicates that at the packet had a valid CRC and no symbol errors"] + received_ok @ 23, + #[doc = "Indicates packet received had a valid Multicast address"] + multicast @ 24, + #[doc = "Indicates packet received had a valid Broadcast address."] + broadcast @ 25, + #[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"] + dribble_nibble @ 26, + #[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"] + receive_control_frame @ 27, + #[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"] + receive_pause_control_frame @ 28, + #[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"] + receive_unknown_opcode @ 29, + #[doc = "Current frame was recognized as a VLAN tagged frame"] + receive_vlan_type_detected @ 30, +}); diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs new file mode 100644 index 00000000..b44cefaf --- /dev/null +++ b/embassy-net-enc28j60/src/lib.rs @@ -0,0 +1,717 @@ +#![no_std] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] + +// must go first. +mod fmt; + +#[macro_use] +mod macros; +mod bank0; +mod bank1; +mod bank2; +mod bank3; +mod common; +mod header; +mod phy; +mod traits; + +use core::cmp; +use core::convert::TryInto; + +use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium}; +use embassy_time::Duration; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{Operation, SpiDevice}; +use traits::U16Ext; + +// Total buffer size (see section 3.2) +const BUF_SZ: u16 = 8 * 1024; + +// Maximum frame length +const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet + +// Size of the Frame check sequence (32-bit CRC) +const CRC_SZ: u16 = 4; + +// define the boundaries of the TX and RX buffers +// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet +// says: we place the RX buffer at address 0 and the TX buffer after it +const RXST: u16 = 0x0000; +const RXND: u16 = 0x19ff; +const TXST: u16 = 0x1a00; +const _TXND: u16 = 0x1fff; + +const MTU: usize = 1514; // 1500 IP + 14 ethernet header + +/// ENC28J60 embassy-net driver +pub struct Enc28j60 { + mac_addr: [u8; 6], + + spi: S, + rst: Option, + + bank: Bank, + + // address of the next packet in buffer memory + next_packet: u16, +} + +impl Enc28j60 +where + S: SpiDevice, + O: OutputPin, +{ + /// Create a new ENC28J60 driver instance. + /// + /// The RST pin is optional. If None, reset will be done with a SPI + /// soft reset command, instead of via the RST pin. + pub fn new(spi: S, rst: Option, mac_addr: [u8; 6]) -> Self { + let mut res = Self { + mac_addr, + spi, + rst, + + bank: Bank::Bank0, + next_packet: RXST, + }; + res.init(); + res + } + + fn init(&mut self) { + if let Some(rst) = &mut self.rst { + rst.set_low().unwrap(); + embassy_time::block_for(Duration::from_millis(5)); + rst.set_high().unwrap(); + embassy_time::block_for(Duration::from_millis(5)); + } else { + embassy_time::block_for(Duration::from_millis(5)); + self.soft_reset(); + embassy_time::block_for(Duration::from_millis(5)); + } + + debug!( + "enc28j60: erevid {=u8:x}", + self.read_control_register(bank3::Register::EREVID) + ); + debug!("enc28j60: waiting for clk"); + while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {} + debug!("enc28j60: clk ok"); + + if self.read_control_register(bank3::Register::EREVID) == 0 { + panic!("ErevidIsZero"); + } + + // disable CLKOUT output + self.write_control_register(bank3::Register::ECOCON, 0); + + self.init_rx(); + + // TX start + // "It is recommended that an even address be used for ETXST" + debug_assert_eq!(TXST % 2, 0); + self.write_control_register(bank0::Register::ETXSTL, TXST.low()); + self.write_control_register(bank0::Register::ETXSTH, TXST.high()); + + // TX end is set in `transmit` + + // MAC initialization (see section 6.5) + // 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames. + self.write_control_register( + bank2::Register::MACON1, + bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(), + ); + + // 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3. + self.write_control_register( + bank2::Register::MACON3, + bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(), + ); + + // 4. Program the MAMXFL registers with the maximum frame length to be permitted to be + // received or transmitted + self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low()); + self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high()); + + // 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG. + // Use recommended value of 0x12 + self.write_control_register(bank2::Register::MABBIPG, 0x12); + + // 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL. + // Use recommended value of 0x12 + self.write_control_register(bank2::Register::MAIPGL, 0x12); + self.write_control_register(bank2::Register::MAIPGH, 0x0c); + + // 9. Program the local MAC address into the MAADR1:MAADR6 registers + self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]); + self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]); + self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]); + self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]); + self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]); + self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]); + + // Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted + self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits()); + + // Globally enable interrupts + //self.bit_field_set(common::Register::EIE, common::EIE::mask().intie()); + + // Set the per packet control byte; we'll always use the value 0 + self.write_buffer_memory(Some(TXST), &mut [0]); + + // Enable reception + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen()); + } + + fn init_rx(&mut self) { + // RX start + // "It is recommended that the ERXST Pointer be programmed with an even address" + self.write_control_register(bank0::Register::ERXSTL, RXST.low()); + self.write_control_register(bank0::Register::ERXSTH, RXST.high()); + + // RX read pointer + // NOTE Errata #14 so we are using an *odd* address here instead of ERXST + self.write_control_register(bank0::Register::ERXRDPTL, RXND.low()); + self.write_control_register(bank0::Register::ERXRDPTH, RXND.high()); + + // RX end + self.write_control_register(bank0::Register::ERXNDL, RXND.low()); + self.write_control_register(bank0::Register::ERXNDH, RXND.high()); + + // decrease the packet count to 0 + while self.read_control_register(bank1::Register::EPKTCNT) != 0 { + self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec()); + } + + self.next_packet = RXST; + } + + fn reset_rx(&mut self) { + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxrst()); + self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().rxrst()); + self.init_rx(); + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen()); + } + + /// 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]> { + if self.pending_packets() == 0 { + // Errata #6: we can't rely on PKTIF so we check PKTCNT + return None; + } + + let curr_packet = self.next_packet; + + // read out the first 6 bytes + let mut temp_buf = [0; 6]; + self.read_buffer_memory(Some(curr_packet), &mut temp_buf); + + // next packet pointer + let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]); + // status vector + let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap())); + let len_with_crc = status.byte_count() as u16; + + if len_with_crc < CRC_SZ || len_with_crc > 1600 || next_packet > RXND { + warn!("RX buffer corrupted, resetting RX logic to recover..."); + self.reset_rx(); + return None; + } + + let len = len_with_crc - CRC_SZ; + self.read_buffer_memory(None, &mut buf[..len as usize]); + + // update ERXRDPT + // due to Errata #14 we must write an odd address to ERXRDPT + // we know that ERXST = 0, that ERXND is odd and that next_packet is even + let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 { + RXND + } else { + self.next_packet - 1 + }; + // "To move ERXRDPT, the host controller must write to ERXRDPTL first." + self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low()); + self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high()); + + // decrease the packet count + self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec()); + + self.next_packet = next_packet; + + Some(&mut buf[..len as usize]) + } + + fn wait_tx_ready(&mut self) { + for _ in 0u32..10000 { + if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 { + return; + } + } + + // work around errata #12 by resetting the transmit logic before every new + // transmission + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst()); + self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst()); + //self.bit_field_clear(common::Register::EIR, { + // let mask = common::EIR::mask(); + // mask.txerif() | mask.txif() + //}); + } + + /// Starts the transmission of `bytes` + /// + /// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will + /// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum + /// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload). + /// + /// NOTE This method will flush any previous transmission that's in progress + /// + /// # Panics + /// + /// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface, + /// or greater than the transmit buffer + pub fn transmit(&mut self, bytes: &[u8]) { + assert!(bytes.len() <= self.mtu() as usize); + + self.wait_tx_ready(); + + // NOTE the plus one is to not overwrite the per packet control byte + let wrpt = TXST + 1; + + // 1. ETXST was set during initialization + + // 2. write the frame to the IC memory + self.write_buffer_memory(Some(wrpt), bytes); + + let txnd = wrpt + bytes.len() as u16 - 1; + + // 3. Set the end address of the transmit buffer + self.write_control_register(bank0::Register::ETXNDL, txnd.low()); + self.write_control_register(bank0::Register::ETXNDH, txnd.high()); + + // 4. reset interrupt flag + //self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif()); + + // 5. start transmission + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts()); + + // Wait until transmission finishes + //while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {} + + /* + // read the transmit status vector + let mut tx_stat = [0; 7]; + self.read_buffer_memory(None, &mut tx_stat); + + let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT)); + + if stat.txabrt() == 1 { + // work around errata #12 by reading the transmit status vector + if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 { + panic!("LateCollision") + } else { + panic!("TransmitAbort") + } + }*/ + } + + /// Get whether the link is up + pub fn is_link_up(&mut self) -> bool { + let bits = self.read_phy_register(phy::Register::PHSTAT2); + phy::PHSTAT2(bits).lstat() == 1 + } + + /// Returns the interface Maximum Transmission Unit (MTU) + /// + /// The value returned by this function will never exceed 1514 bytes. The actual value depends + /// on the memory assigned to the transmission buffer when initializing the device + pub fn mtu(&self) -> u16 { + cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ) + } + + /* Miscellaneous */ + /// Returns the number of packets that have been received but have not been processed yet + pub fn pending_packets(&mut self) -> u8 { + self.read_control_register(bank1::Register::EPKTCNT) + } + + /// Adjusts the receive filter to *accept* these packet types + pub fn accept(&mut self, packets: &[Packet]) { + let mask = bank1::ERXFCON::mask(); + let mut val = 0; + for packet in packets { + match packet { + Packet::Broadcast => val |= mask.bcen(), + Packet::Multicast => val |= mask.mcen(), + Packet::Unicast => val |= mask.ucen(), + } + } + + self.bit_field_set(bank1::Register::ERXFCON, val) + } + + /// Adjusts the receive filter to *ignore* these packet types + pub fn ignore(&mut self, packets: &[Packet]) { + let mask = bank1::ERXFCON::mask(); + let mut val = 0; + for packet in packets { + match packet { + Packet::Broadcast => val |= mask.bcen(), + Packet::Multicast => val |= mask.mcen(), + Packet::Unicast => val |= mask.ucen(), + } + } + + self.bit_field_clear(bank1::Register::ERXFCON, val) + } + + /* Private */ + /* Read */ + fn read_control_register(&mut self, register: R) -> u8 + where + R: Into, + { + self._read_control_register(register.into()) + } + + fn _read_control_register(&mut self, register: Register) -> u8 { + self.change_bank(register); + + if register.is_eth_register() { + let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0]; + self.spi.transfer_in_place(&mut buffer).unwrap(); + buffer[1] + } else { + // MAC, MII regs need a dummy byte. + let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0]; + self.spi.transfer_in_place(&mut buffer).unwrap(); + buffer[2] + } + } + + fn read_phy_register(&mut self, register: phy::Register) -> u16 { + // set PHY register address + self.write_control_register(bank2::Register::MIREGADR, register.addr()); + + // start read operation + self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits()); + + // wait until the read operation finishes + while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {} + + self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits()); + + let l = self.read_control_register(bank2::Register::MIRDL); + let h = self.read_control_register(bank2::Register::MIRDH); + (l as u16) | (h as u16) << 8 + } + + /* Write */ + fn _write_control_register(&mut self, register: Register, value: u8) { + self.change_bank(register); + + let buffer = [Instruction::WCR.opcode() | register.addr(), value]; + self.spi.write(&buffer).unwrap(); + } + + fn write_control_register(&mut self, register: R, value: u8) + where + R: Into, + { + self._write_control_register(register.into(), value) + } + + fn write_phy_register(&mut self, register: phy::Register, value: u16) { + // set PHY register address + self.write_control_register(bank2::Register::MIREGADR, register.addr()); + + self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8); + // this starts the write operation + self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8); + + // wait until the write operation finishes + while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {} + } + + /* RMW */ + fn modify_control_register(&mut self, register: R, f: F) + where + F: FnOnce(u8) -> u8, + R: Into, + { + self._modify_control_register(register.into(), f) + } + + fn _modify_control_register(&mut self, register: Register, f: F) + where + F: FnOnce(u8) -> u8, + { + let r = self._read_control_register(register); + self._write_control_register(register, f(r)) + } + + /* Auxiliary */ + fn change_bank(&mut self, register: Register) { + let bank = register.bank(); + + if let Some(bank) = bank { + if self.bank == bank { + // already on the register bank + return; + } + + // change bank + self.bank = bank; + match bank { + Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11), + Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01), + Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10), + Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11), + } + } else { + // common register + } + } + + /* Primitive operations */ + fn bit_field_clear(&mut self, register: R, mask: u8) + where + R: Into, + { + self._bit_field_clear(register.into(), mask) + } + + fn _bit_field_clear(&mut self, register: Register, mask: u8) { + debug_assert!(register.is_eth_register()); + + self.change_bank(register); + + self.spi + .write(&[Instruction::BFC.opcode() | register.addr(), mask]) + .unwrap(); + } + + fn bit_field_set(&mut self, register: R, mask: u8) + where + R: Into, + { + self._bit_field_set(register.into(), mask) + } + + fn _bit_field_set(&mut self, register: Register, mask: u8) { + debug_assert!(register.is_eth_register()); + + self.change_bank(register); + + self.spi + .write(&[Instruction::BFS.opcode() | register.addr(), mask]) + .unwrap(); + } + + fn read_buffer_memory(&mut self, addr: Option, buf: &mut [u8]) { + if let Some(addr) = addr { + self.write_control_register(bank0::Register::ERDPTL, addr.low()); + self.write_control_register(bank0::Register::ERDPTH, addr.high()); + } + + self.spi + .transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)]) + .unwrap(); + } + + fn soft_reset(&mut self) { + self.spi.write(&[Instruction::SRC.opcode()]).unwrap(); + } + + fn write_buffer_memory(&mut self, addr: Option, buffer: &[u8]) { + if let Some(addr) = addr { + self.write_control_register(bank0::Register::EWRPTL, addr.low()); + self.write_control_register(bank0::Register::EWRPTH, addr.high()); + } + + self.spi + .transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)]) + .unwrap(); + } +} + +#[derive(Clone, Copy, PartialEq)] +enum Bank { + Bank0, + Bank1, + Bank2, + Bank3, +} + +#[derive(Clone, Copy)] +enum Instruction { + /// Read Control Register + RCR = 0b000_00000, + /// Read Buffer Memory + RBM = 0b001_11010, + /// Write Control Register + WCR = 0b010_00000, + /// Write Buffer Memory + WBM = 0b011_11010, + /// Bit Field Set + BFS = 0b100_00000, + /// Bit Field Clear + BFC = 0b101_00000, + /// System Reset Command + SRC = 0b111_11111, +} + +impl Instruction { + fn opcode(&self) -> u8 { + *self as u8 + } +} + +#[derive(Clone, Copy)] +enum Register { + Bank0(bank0::Register), + Bank1(bank1::Register), + Bank2(bank2::Register), + Bank3(bank3::Register), + Common(common::Register), +} + +impl Register { + fn addr(&self) -> u8 { + match *self { + Register::Bank0(r) => r.addr(), + Register::Bank1(r) => r.addr(), + Register::Bank2(r) => r.addr(), + Register::Bank3(r) => r.addr(), + Register::Common(r) => r.addr(), + } + } + + fn bank(&self) -> Option { + Some(match *self { + Register::Bank0(_) => Bank::Bank0, + Register::Bank1(_) => Bank::Bank1, + Register::Bank2(_) => Bank::Bank2, + Register::Bank3(_) => Bank::Bank3, + Register::Common(_) => return None, + }) + } + + fn is_eth_register(&self) -> bool { + match *self { + Register::Bank0(r) => r.is_eth_register(), + Register::Bank1(r) => r.is_eth_register(), + Register::Bank2(r) => r.is_eth_register(), + Register::Bank3(r) => r.is_eth_register(), + Register::Common(r) => r.is_eth_register(), + } + } +} + +/// Packet type, used to configure receive filters +#[non_exhaustive] +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Packet { + /// Broadcast packets + Broadcast, + /// Multicast packets + Multicast, + /// Unicast packets + Unicast, +} + +static mut TX_BUF: [u8; MTU] = [0; MTU]; +static mut RX_BUF: [u8; MTU] = [0; MTU]; + +impl embassy_net_driver::Driver for Enc28j60 +where + S: SpiDevice, + O: OutputPin, +{ + type RxToken<'a> = RxToken<'a> + where + Self: 'a; + + type TxToken<'a> = TxToken<'a, S, O> + where + Self: 'a; + + 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 })) + } else { + cx.waker().wake_by_ref(); + None + } + } + + fn transmit(&mut self, _cx: &mut core::task::Context) -> Option> { + let tx_buf = unsafe { &mut TX_BUF }; + Some(TxToken { buf: tx_buf, eth: self }) + } + + fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState { + cx.waker().wake_by_ref(); + match self.is_link_up() { + true => LinkState::Up, + false => LinkState::Down, + } + } + + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.medium = Medium::Ethernet; + caps + } + + fn hardware_address(&self) -> HardwareAddress { + HardwareAddress::Ethernet(self.mac_addr) + } +} + +/// embassy-net RX token. +pub struct RxToken<'a> { + buf: &'a mut [u8], +} + +impl<'a> embassy_net_driver::RxToken for RxToken<'a> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(self.buf) + } +} + +/// embassy-net TX token. +pub struct TxToken<'a, S, O> +where + S: SpiDevice, + O: OutputPin, +{ + eth: &'a mut Enc28j60, + buf: &'a mut [u8], +} + +impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O> +where + S: SpiDevice, + O: OutputPin, +{ + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + assert!(len <= self.buf.len()); + let r = f(&mut self.buf[..len]); + self.eth.transmit(&self.buf[..len]); + r + } +} diff --git a/embassy-net-enc28j60/src/macros.rs b/embassy-net-enc28j60/src/macros.rs new file mode 100644 index 00000000..8d064957 --- /dev/null +++ b/embassy-net-enc28j60/src/macros.rs @@ -0,0 +1,89 @@ +macro_rules! register { + ($REGISTER:ident, $reset_value:expr, $uxx:ty, { + $(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+ + }) => { + #[derive(Clone, Copy)] + pub(crate) struct $REGISTER { + bits: $uxx, + _mode: ::core::marker::PhantomData, + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn mask() -> $REGISTER { + $REGISTER { bits: 0, _mode: ::core::marker::PhantomData } + } + + $( + #[allow(dead_code)] + pub(crate) fn $bitfield(&self) -> $uxx { + use super::traits::OffsetSize; + + let size = $range.size(); + let offset = $range.offset(); + ((1 << size) - 1) << offset + } + )+ + } + + impl ::core::default::Default for $REGISTER { + fn default() -> Self { + $REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData } + } + } + + #[allow(non_snake_case)] + #[allow(dead_code)] + pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER { + $REGISTER { bits, _mode: ::core::marker::PhantomData } + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn modify(self) -> $REGISTER { + $REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData } + } + + $( + #[$($attr)*] + #[allow(dead_code)] + pub(crate) fn $bitfield(&self) -> $uxx { + use super::traits::OffsetSize; + + let offset = $range.offset(); + let size = $range.size(); + let mask = (1 << size) - 1; + + (self.bits >> offset) & mask + } + )+ + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn bits(self) -> $uxx { + self.bits + } + + $( + #[$($attr)*] + #[allow(dead_code)] + pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self { + use super::traits::OffsetSize; + + let offset = $range.offset(); + let size = $range.size(); + let mask = (1 << size) - 1; + + debug_assert!(bits <= mask); + bits &= mask; + + self.bits &= !(mask << offset); + self.bits |= bits << offset; + + self + } + )+ + } + } +} diff --git a/embassy-net-enc28j60/src/phy.rs b/embassy-net-enc28j60/src/phy.rs new file mode 100644 index 00000000..89144ada --- /dev/null +++ b/embassy-net-enc28j60/src/phy.rs @@ -0,0 +1,35 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + PHCON1 = 0x00, + PHSTAT1 = 0x01, + PHID1 = 0x02, + PHID2 = 0x03, + PHCON2 = 0x10, + PHSTAT2 = 0x11, + PHIE = 0x12, + PHIR = 0x13, + PHLCON = 0x14, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } +} + +register!(PHCON2, 0, u16, { + #[doc = "PHY Half-Duplex Loopback Disable bit"] + hdldis @ 8, + #[doc = "Jabber Correction Disable bit"] + jabber @ 10, + #[doc = "Twisted-Pair Transmitter Disable bit"] + txdis @ 13, + #[doc = "PHY Force Linkup bit"] + frclnk @ 14, +}); + +register!(PHSTAT2, 0, u16, { + #[doc = "Link Status bit"] + lstat @ 10, +}); diff --git a/embassy-net-enc28j60/src/traits.rs b/embassy-net-enc28j60/src/traits.rs new file mode 100644 index 00000000..08f94045 --- /dev/null +++ b/embassy-net-enc28j60/src/traits.rs @@ -0,0 +1,57 @@ +use core::ops::Range; + +pub(crate) trait OffsetSize { + fn offset(self) -> u8; + fn size(self) -> u8; +} + +impl OffsetSize for u8 { + fn offset(self) -> u8 { + self + } + + fn size(self) -> u8 { + 1 + } +} + +impl OffsetSize for Range { + fn offset(self) -> u8 { + self.start + } + + fn size(self) -> u8 { + self.end - self.start + } +} + +pub(crate) trait U16Ext { + fn from_parts(low: u8, high: u8) -> Self; + + fn low(self) -> u8; + + fn high(self) -> u8; +} + +impl U16Ext for u16 { + fn from_parts(low: u8, high: u8) -> u16 { + ((high as u16) << 8) + low as u16 + } + + fn low(self) -> u8 { + (self & 0xff) as u8 + } + + fn high(self) -> u8 { + (self >> 8) as u8 + } +} + +#[derive(Clone, Copy)] +pub struct Mask; + +#[derive(Clone, Copy)] +pub struct R; + +#[derive(Clone, Copy)] +pub struct W; diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index 26f5b40b..d334cf3f 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -12,9 +12,15 @@ embassy-sync = { version = "0.2.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"} -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } heapless = "0.7.16" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/" +src_base_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 diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 79f8cde7..ce6636a8 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -1,14 +1,16 @@ use ch::driver::LinkState; -use defmt::Debug2Format; use embassy_net_driver_channel as ch; use heapless::String; use crate::ioctl::Shared; use crate::proto::{self, CtrlMsg}; -#[derive(Debug)] -pub struct Error { - pub status: u32, +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + Failed(u32), + Timeout, + Internal, } pub struct Control<'a> { @@ -24,59 +26,78 @@ enum WifiMode { ApSta = 3, } +macro_rules! ioctl { + ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => { + let mut msg = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::$req_variant as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::$req_variant($req)), + }; + $self.ioctl(&mut msg).await?; + let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else { + warn!("unexpected response variant"); + return Err(Error::Internal); + }; + if $resp.resp != 0 { + return Err(Error::Failed($resp.resp)); + } + }; +} + impl<'a> Control<'a> { pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { Self { state_ch, shared } } - pub async fn init(&mut self) { + pub async fn init(&mut self) -> Result<(), Error> { debug!("wait for init event..."); self.shared.init_wait().await; - debug!("set wifi mode"); - self.set_wifi_mode(WifiMode::Sta as _).await; + debug!("set heartbeat"); + self.set_heartbeat(10).await?; - let mac_addr = self.get_mac_addr().await; + debug!("set wifi mode"); + self.set_wifi_mode(WifiMode::Sta as _).await?; + + let mac_addr = self.get_mac_addr().await?; debug!("mac addr: {:02x}", mac_addr); self.state_ch.set_ethernet_address(mac_addr); + + Ok(()) } - pub async fn join(&mut self, ssid: &str, password: &str) { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqConnectAp as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp { - ssid: String::from(ssid), - pwd: String::from(password), - bssid: String::new(), - listen_interval: 3, - is_wpa3_supported: false, - })), + pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> { + let req = proto::CtrlMsgReqConnectAp { + ssid: String::from(ssid), + pwd: String::from(password), + bssid: String::new(), + listen_interval: 3, + is_wpa3_supported: false, }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - debug!("======= {:?}", Debug2Format(&resp)); - assert_eq!(resp.resp, 0); + ioctl!(self, ReqConnectAp, RespConnectAp, req, resp); self.state_ch.set_link_state(LinkState::Up); + Ok(()) } - async fn get_mac_addr(&mut self) -> [u8; 6] { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqGetMacAddress as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress( - proto::CtrlMsgReqGetMacAddress { - mode: WifiMode::Sta as _, - }, - )), + pub async fn disconnect(&mut self) -> Result<(), Error> { + let req = proto::CtrlMsgReqGetStatus {}; + ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp); + self.state_ch.set_link_state(LinkState::Down); + Ok(()) + } + + /// duration in seconds, clamped to [10, 3600] + async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> { + let req = proto::CtrlMsgReqConfigHeartbeat { enable: true, duration }; + ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp); + Ok(()) + } + + async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> { + let req = proto::CtrlMsgReqGetMacAddress { + mode: WifiMode::Sta as _, }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - assert_eq!(resp.resp, 0); + ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp); // WHY IS THIS A STRING? WHYYYY fn nibble_from_hex(b: u8) -> u8 { @@ -90,32 +111,32 @@ impl<'a> Control<'a> { let mac = resp.mac.as_bytes(); let mut res = [0; 6]; - assert_eq!(mac.len(), 17); + 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]) } - res + Ok(res) } - async fn set_wifi_mode(&mut self, mode: u32) { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), - }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - assert_eq!(resp.resp, 0); + async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> { + let req = proto::CtrlMsgReqSetMode { mode }; + ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp); + + Ok(()) } - async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { - debug!("ioctl req: {:?}", &req); + async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> { + debug!("ioctl req: {:?}", &msg); let mut buf = [0u8; 128]; - let req_len = noproto::write(&req, &mut buf).unwrap(); + let req_len = noproto::write(msg, &mut buf).map_err(|_| { + warn!("failed to serialize control request"); + Error::Internal + })?; struct CancelOnDrop<'a>(&'a Shared); @@ -137,9 +158,12 @@ impl<'a> Control<'a> { ioctl.defuse(); - let res = noproto::read(&buf[..resp_len]).unwrap(); - debug!("ioctl resp: {:?}", &res); + *msg = noproto::read(&buf[..resp_len]).map_err(|_| { + warn!("failed to serialize control request"); + Error::Internal + })?; + debug!("ioctl resp: {:?}", msg); - res + Ok(()) } } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index a35adfca..4a318b20 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -1,17 +1,15 @@ #![no_std] -use control::Control; -use embassy_futures::select::{select3, Either3}; +use embassy_futures::select::{select4, Either4}; use embassy_net_driver_channel as ch; +use embassy_net_driver_channel::driver::LinkState; use embassy_time::{Duration, Instant, Timer}; use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use ioctl::Shared; -use proto::CtrlMsg; -use crate::ioctl::PendingIoctl; -use crate::proto::CtrlMsgPayload; +use crate::ioctl::{PendingIoctl, Shared}; +use crate::proto::{CtrlMsg, CtrlMsgPayload}; mod proto; @@ -21,6 +19,8 @@ mod fmt; mod control; mod ioctl; +pub use control::*; + const MTU: usize = 1514; macro_rules! impl_bytes { @@ -95,6 +95,7 @@ enum InterfaceType { } const MAX_SPI_BUFFER_SIZE: usize = 1600; +const HEARTBEAT_MAX_GAP: Duration = Duration::from_secs(20); pub struct State { shared: Shared, @@ -124,17 +125,19 @@ where IN: InputPin + Wait, OUT: OutputPin, { - let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); let state_ch = ch_runner.state_runner(); let mut runner = Runner { ch: ch_runner, + state_ch, shared: &state.shared, next_seq: 1, handshake, ready, reset, spi, + heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, }; runner.init().await; @@ -143,9 +146,11 @@ where pub struct Runner<'a, SPI, IN, OUT> { ch: ch::Runner<'a, MTU>, + state_ch: ch::StateRunner<'a>, shared: &'a Shared, next_seq: u16, + heartbeat_deadline: Instant, spi: SPI, handshake: IN, @@ -177,9 +182,10 @@ where let ioctl = self.shared.ioctl_wait_pending(); let tx = self.ch.tx_buf(); let ev = async { self.ready.wait_for_high().await.unwrap() }; + let hb = Timer::at(self.heartbeat_deadline); - match select3(ioctl, tx, ev).await { - Either3::First(PendingIoctl { buf, req_len }) => { + match select4(ioctl, tx, ev, hb).await { + Either4::First(PendingIoctl { buf, req_len }) => { tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); @@ -198,7 +204,7 @@ where header.checksum = checksum(&tx_buf[..26 + req_len]); tx_buf[0..12].copy_from_slice(&header.to_bytes()); } - Either3::Second(packet) => { + Either4::Second(packet) => { tx_buf[12..][..packet.len()].copy_from_slice(packet); let mut header = PayloadHeader { @@ -217,9 +223,12 @@ where self.ch.tx_done(); } - Either3::Third(()) => { + Either4::Third(()) => { tx_buf[..PayloadHeader::SIZE].fill(0); } + Either4::Fourth(()) => { + panic!("heartbeat from esp32 stopped") + } } if tx_buf[0] != 0 { @@ -308,7 +317,7 @@ where } } - fn handle_event(&self, data: &[u8]) { + fn handle_event(&mut self, data: &[u8]) { let Ok(event) = noproto::read::(data) else { warn!("failed to parse event"); return; @@ -323,6 +332,11 @@ where match payload { CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), + CtrlMsgPayload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP, + CtrlMsgPayload::EventStationDisconnectFromAp(e) => { + info!("disconnected, code {}", e.resp); + self.state_ch.set_link_state(LinkState::Down); + } _ => {} } } diff --git a/embassy-net-tuntap/Cargo.toml b/embassy-net-tuntap/Cargo.toml new file mode 100644 index 00000000..08d30968 --- /dev/null +++ b/embassy-net-tuntap/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "embassy-net-tuntap" +version = "0.1.0" +description = "embassy-net driver for Linux TUN/TAP interfaces." +keywords = ["embedded", "tuntap", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[dependencies] +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +async-io = "1.6.0" +log = "0.4.14" +libc = "0.2.101" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-tuntap-v$VERSION/embassy-net-tuntap/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-tuntap/src/" +target = "thumbv7em-none-eabi" \ No newline at end of file diff --git a/embassy-net-tuntap/README.md b/embassy-net-tuntap/README.md new file mode 100644 index 00000000..c5d9e746 --- /dev/null +++ b/embassy-net-tuntap/README.md @@ -0,0 +1,17 @@ +# `embassy-net` integration for Linux TUN/TAP interfaces. + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for for Linux TUN (IP medium) and TAP (Ethernet medium) interfaces. + +## Interoperability + +This crate can run on any executor. + +## 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/examples/std/src/tuntap.rs b/embassy-net-tuntap/src/lib.rs similarity index 96% rename from examples/std/src/tuntap.rs rename to embassy-net-tuntap/src/lib.rs index d918a2e6..75c54c48 100644 --- a/examples/std/src/tuntap.rs +++ b/embassy-net-tuntap/src/lib.rs @@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::task::Context; use async_io::Async; -use embassy_net_driver::{self, Capabilities, Driver, LinkState}; +use embassy_net_driver::{self, Capabilities, Driver, HardwareAddress, LinkState}; use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; @@ -19,6 +19,7 @@ const ETHERNET_HEADER_LEN: usize = 14; #[repr(C)] #[derive(Debug)] +#[allow(non_camel_case_types)] struct ifreq { ifr_name: [libc::c_char; libc::IF_NAMESIZE], ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */ @@ -180,8 +181,8 @@ impl Driver for TunTapDevice { LinkState::Up } - fn ethernet_address(&self) -> [u8; 6] { - [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] + fn hardware_address(&self) -> HardwareAddress { + HardwareAddress::Ethernet([0x02, 0x03, 0x04, 0x05, 0x06, 0x07]) } } diff --git a/embassy-net-w5500/README.md b/embassy-net-w5500/README.md deleted file mode 100644 index 9eaf4b70..00000000 --- a/embassy-net-w5500/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# WIZnet W5500 `embassy-net` integration - -[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet W5500 SPI ethernet chip, operating in MACRAW mode. - -Supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async) - -See [`examples`](https://github.com/kalkyl/embassy-net-w5500/tree/main/examples) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. \ No newline at end of file diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs deleted file mode 100644 index 9874df0d..00000000 --- a/embassy-net-w5500/src/device.rs +++ /dev/null @@ -1,131 +0,0 @@ -use embedded_hal_async::spi::SpiDevice; - -use crate::socket; -use crate::spi::SpiInterface; - -pub const MODE: u16 = 0x00; -pub const MAC: u16 = 0x09; -pub const SOCKET_INTR: u16 = 0x18; -pub const PHY_CFG: u16 = 0x2E; - -#[repr(u8)] -pub enum RegisterBlock { - Common = 0x00, - Socket0 = 0x01, - TxBuf = 0x02, - RxBuf = 0x03, -} - -/// W5500 in MACRAW mode -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct W5500 { - bus: SpiInterface, -} - -impl W5500 { - /// Create and initialize the W5500 driver - pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { - let mut bus = SpiInterface(spi); - // Reset device - bus.write_frame(RegisterBlock::Common, MODE, &[0x80]).await?; - - // Enable interrupt pin - bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]).await?; - // Enable receive interrupt - bus.write_frame( - RegisterBlock::Socket0, - socket::SOCKET_INTR_MASK, - &[socket::Interrupt::Receive as u8], - ) - .await?; - - // Set MAC address - bus.write_frame(RegisterBlock::Common, MAC, &mac_addr).await?; - - // Set the raw socket RX/TX buffer sizes to 16KB - bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16]) - .await?; - bus.write_frame(RegisterBlock::Socket0, socket::RXBUF_SIZE, &[16]) - .await?; - - // MACRAW mode with MAC filtering. - let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]).await?; - socket::command(&mut bus, socket::Command::Open).await?; - - Ok(Self { bus }) - } - - /// Read bytes from the RX buffer. Returns the number of bytes read. - async fn read_bytes(&mut self, buffer: &mut [u8], offset: u16) -> Result { - let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; - - let read_buffer = if rx_size > buffer.len() + offset as usize { - buffer - } else { - &mut buffer[..rx_size - offset as usize] - }; - - let read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?.wrapping_add(offset); - self.bus.read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer).await?; - socket::set_rx_read_ptr(&mut self.bus, read_ptr.wrapping_add(read_buffer.len() as u16)).await?; - - Ok(read_buffer.len()) - } - - /// Read an ethernet frame from the device. Returns the number of bytes read. - pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result { - let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; - if rx_size == 0 { - return Ok(0); - } - - socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?; - - // First two bytes gives the size of the received ethernet frame - let expected_frame_size: usize = { - let mut frame_bytes = [0u8; 2]; - assert!(self.read_bytes(&mut frame_bytes[..], 0).await? == 2); - u16::from_be_bytes(frame_bytes) as usize - 2 - }; - - // Read the ethernet frame - let read_buffer = if frame.len() > expected_frame_size { - &mut frame[..expected_frame_size] - } else { - frame - }; - - let recvd_frame_size = self.read_bytes(read_buffer, 2).await?; - - // Register RX as completed - socket::command(&mut self.bus, socket::Command::Receive).await?; - - // If the whole frame wasn't read, drop it - if recvd_frame_size < expected_frame_size { - Ok(0) - } else { - Ok(recvd_frame_size) - } - } - - /// Write an ethernet frame to the device. Returns number of bytes written - pub async fn write_frame(&mut self, frame: &[u8]) -> Result { - while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {} - let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; - self.bus.write_frame(RegisterBlock::TxBuf, write_ptr, frame).await?; - socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?; - socket::command(&mut self.bus, socket::Command::Send).await?; - Ok(frame.len()) - } - - pub async fn is_link_up(&mut self) -> bool { - let mut link = [0]; - self.bus - .read_frame(RegisterBlock::Common, PHY_CFG, &mut link) - .await - .ok(); - link[0] & 1 == 1 - } -} diff --git a/embassy-net-w5500/src/socket.rs b/embassy-net-w5500/src/socket.rs deleted file mode 100644 index 3d65583c..00000000 --- a/embassy-net-w5500/src/socket.rs +++ /dev/null @@ -1,80 +0,0 @@ -use embedded_hal_async::spi::SpiDevice; - -use crate::device::RegisterBlock; -use crate::spi::SpiInterface; - -pub const MODE: u16 = 0x00; -pub const COMMAND: u16 = 0x01; -pub const RXBUF_SIZE: u16 = 0x1E; -pub const TXBUF_SIZE: u16 = 0x1F; -pub const TX_FREE_SIZE: u16 = 0x20; -pub const TX_DATA_WRITE_PTR: u16 = 0x24; -pub const RECVD_SIZE: u16 = 0x26; -pub const RX_DATA_READ_PTR: u16 = 0x28; -pub const SOCKET_INTR_MASK: u16 = 0x2C; - -#[repr(u8)] -pub enum Command { - Open = 0x01, - Send = 0x20, - Receive = 0x40, -} - -pub const INTR: u16 = 0x02; -#[repr(u8)] -pub enum Interrupt { - Receive = 0b00100_u8, -} - -pub async fn reset_interrupt(bus: &mut SpiInterface, code: Interrupt) -> Result<(), SPI::Error> { - let data = [code as u8]; - bus.write_frame(RegisterBlock::Socket0, INTR, &data).await -} - -pub async fn get_tx_write_ptr(bus: &mut SpiInterface) -> Result { - let mut data = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data) - .await?; - Ok(u16::from_be_bytes(data)) -} - -pub async fn set_tx_write_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { - let data = ptr.to_be_bytes(); - bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data).await -} - -pub async fn get_rx_read_ptr(bus: &mut SpiInterface) -> Result { - let mut data = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data) - .await?; - Ok(u16::from_be_bytes(data)) -} - -pub async fn set_rx_read_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { - let data = ptr.to_be_bytes(); - bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data).await -} - -pub async fn command(bus: &mut SpiInterface, command: Command) -> Result<(), SPI::Error> { - let data = [command as u8]; - bus.write_frame(RegisterBlock::Socket0, COMMAND, &data).await -} - -pub async fn get_rx_size(bus: &mut SpiInterface) -> Result { - loop { - // Wait until two sequential reads are equal - let mut res0 = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0).await?; - let mut res1 = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1).await?; - if res0 == res1 { - break Ok(u16::from_be_bytes(res0)); - } - } -} - -pub async fn get_tx_free_size(bus: &mut SpiInterface) -> Result { - let mut data = [0; 2]; - bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data).await?; - Ok(u16::from_be_bytes(data)) -} diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs deleted file mode 100644 index 07749d6b..00000000 --- a/embassy-net-w5500/src/spi.rs +++ /dev/null @@ -1,32 +0,0 @@ -use embedded_hal_async::spi::{Operation, SpiDevice}; - -use crate::device::RegisterBlock; - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SpiInterface(pub SPI); - -impl SpiInterface { - pub async fn read_frame(&mut self, block: RegisterBlock, address: u16, data: &mut [u8]) -> Result<(), SPI::Error> { - let address_phase = address.to_be_bytes(); - let control_phase = [(block as u8) << 3]; - let operations = &mut [ - Operation::Write(&address_phase), - Operation::Write(&control_phase), - Operation::TransferInPlace(data), - ]; - self.0.transaction(operations).await - } - - pub async fn write_frame(&mut self, block: RegisterBlock, address: u16, data: &[u8]) -> Result<(), SPI::Error> { - let address_phase = address.to_be_bytes(); - let control_phase = [(block as u8) << 3 | 0b0000_0100]; - let data_phase = data; - let operations = &mut [ - Operation::Write(&address_phase[..]), - Operation::Write(&control_phase), - Operation::Write(&data_phase), - ]; - self.0.transaction(operations).await - } -} diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-wiznet/Cargo.toml similarity index 60% rename from embassy-net-w5500/Cargo.toml rename to embassy-net-wiznet/Cargo.toml index 8972b814..adf0b45f 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -1,21 +1,22 @@ [package] -name = "embassy-net-w5500" +name = "embassy-net-wiznet" version = "0.1.0" -description = "embassy-net driver for the W5500 ethernet chip" -keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"] +description = "embassy-net driver for WIZnet SPI Ethernet chips" +keywords = ["embedded", "wiznet", "embassy-net", "embedded-hal-async", "ethernet", "async"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] license = "MIT OR Apache-2.0" edition = "2021" [dependencies] -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +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-futures = { version = "0.1.0", path = "../embassy-futures" } defmt = { version = "0.3", optional = true } [package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/" -target = "thumbv7em-none-eabi" \ No newline at end of file +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 diff --git a/embassy-net-wiznet/README.md b/embassy-net-wiznet/README.md new file mode 100644 index 00000000..b8e4bdc8 --- /dev/null +++ b/embassy-net-wiznet/README.md @@ -0,0 +1,27 @@ +# WIZnet `embassy-net` integration + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet SPI ethernet chips, operating in MACRAW mode. + +See [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/rp) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. + +## Supported chips + +- W5500 +- W5100S + +## Interoperability + +This crate can run on any executor. + +It supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-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-wiznet/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs new file mode 100644 index 00000000..562db515 --- /dev/null +++ b/embassy-net-wiznet/src/chip/mod.rs @@ -0,0 +1,48 @@ +mod w5500; +pub use w5500::W5500; +mod w5100s; +pub use w5100s::W5100S; + +pub(crate) mod sealed { + use embedded_hal_async::spi::SpiDevice; + + pub trait Chip { + type Address; + + const COMMON_MODE: Self::Address; + const COMMON_MAC: Self::Address; + const COMMON_SOCKET_INTR: Self::Address; + const COMMON_PHY_CFG: Self::Address; + const SOCKET_MODE: Self::Address; + const SOCKET_COMMAND: Self::Address; + const SOCKET_RXBUF_SIZE: Self::Address; + const SOCKET_TXBUF_SIZE: Self::Address; + const SOCKET_TX_FREE_SIZE: Self::Address; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address; + const SOCKET_RECVD_SIZE: Self::Address; + const SOCKET_RX_DATA_READ_PTR: Self::Address; + const SOCKET_INTR_MASK: Self::Address; + const SOCKET_INTR: Self::Address; + + const SOCKET_MODE_VALUE: u8; + + const BUF_SIZE: u16; + const AUTO_WRAP: bool; + + fn rx_addr(addr: u16) -> Self::Address; + fn tx_addr(addr: u16) -> Self::Address; + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error>; + async fn bus_write( + spi: &mut SPI, + address: Self::Address, + data: &[u8], + ) -> Result<(), SPI::Error>; + } +} + +pub trait Chip: sealed::Chip {} diff --git a/embassy-net-wiznet/src/chip/w5100s.rs b/embassy-net-wiznet/src/chip/w5100s.rs new file mode 100644 index 00000000..07a84037 --- /dev/null +++ b/embassy-net-wiznet/src/chip/w5100s.rs @@ -0,0 +1,61 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +const SOCKET_BASE: u16 = 0x400; +const TX_BASE: u16 = 0x4000; +const RX_BASE: u16 = 0x6000; + +pub enum W5100S {} + +impl super::Chip for W5100S {} +impl super::sealed::Chip for W5100S { + type Address = u16; + + const COMMON_MODE: Self::Address = 0x00; + const COMMON_MAC: Self::Address = 0x09; + const COMMON_SOCKET_INTR: Self::Address = 0x16; + const COMMON_PHY_CFG: Self::Address = 0x3c; + + const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00; + const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01; + const SOCKET_RXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1E; + const SOCKET_TXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1F; + const SOCKET_TX_FREE_SIZE: Self::Address = SOCKET_BASE + 0x20; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = SOCKET_BASE + 0x24; + const SOCKET_RECVD_SIZE: Self::Address = SOCKET_BASE + 0x26; + const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28; + const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C; + const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02; + + const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6); + + const BUF_SIZE: u16 = 0x2000; + const AUTO_WRAP: bool = false; + + fn rx_addr(addr: u16) -> Self::Address { + RX_BASE + addr + } + + fn tx_addr(addr: u16) -> Self::Address { + TX_BASE + addr + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + spi.transaction(&mut [ + Operation::Write(&[0x0F, (address >> 8) as u8, address as u8]), + Operation::Read(data), + ]) + .await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + spi.transaction(&mut [ + Operation::Write(&[0xF0, (address >> 8) as u8, address as u8]), + Operation::Write(data), + ]) + .await + } +} diff --git a/embassy-net-wiznet/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs new file mode 100644 index 00000000..61e51294 --- /dev/null +++ b/embassy-net-wiznet/src/chip/w5500.rs @@ -0,0 +1,72 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +#[repr(u8)] +pub enum RegisterBlock { + Common = 0x00, + Socket0 = 0x01, + TxBuf = 0x02, + RxBuf = 0x03, +} + +pub enum W5500 {} + +impl super::Chip for W5500 {} +impl super::sealed::Chip for W5500 { + type Address = (RegisterBlock, u16); + + const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00); + const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09); + const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18); + const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E); + + const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00); + const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01); + const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1E); + const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1F); + const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x20); + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x24); + const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x26); + const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28); + const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); + const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); + + const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); + + const BUF_SIZE: u16 = 0x4000; + const AUTO_WRAP: bool = true; + + fn rx_addr(addr: u16) -> Self::Address { + (RegisterBlock::RxBuf, addr) + } + + fn tx_addr(addr: u16) -> Self::Address { + (RegisterBlock::TxBuf, addr) + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3]; + let operations = &mut [ + Operation::Write(&address_phase), + Operation::Write(&control_phase), + Operation::TransferInPlace(data), + ]; + spi.transaction(operations).await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; + let data_phase = data; + let operations = &mut [ + Operation::Write(&address_phase[..]), + Operation::Write(&control_phase), + Operation::Write(&data_phase), + ]; + spi.transaction(operations).await + } +} diff --git a/embassy-net-wiznet/src/device.rs b/embassy-net-wiznet/src/device.rs new file mode 100644 index 00000000..43f9512a --- /dev/null +++ b/embassy-net-wiznet/src/device.rs @@ -0,0 +1,195 @@ +use core::marker::PhantomData; + +use embedded_hal_async::spi::SpiDevice; + +use crate::chip::Chip; + +#[repr(u8)] +enum Command { + Open = 0x01, + Send = 0x20, + Receive = 0x40, +} + +#[repr(u8)] +enum Interrupt { + Receive = 0b00100_u8, +} + +/// Wiznet chip in MACRAW mode +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) struct WiznetDevice { + spi: SPI, + _phantom: PhantomData, +} + +impl WiznetDevice { + /// Create and initialize the driver + pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result { + let mut this = Self { + spi, + _phantom: PhantomData, + }; + + // Reset device + this.bus_write(C::COMMON_MODE, &[0x80]).await?; + + // Enable interrupt pin + this.bus_write(C::COMMON_SOCKET_INTR, &[0x01]).await?; + // Enable receive interrupt + this.bus_write(C::SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; + + // Set MAC address + this.bus_write(C::COMMON_MAC, &mac_addr).await?; + + // Set the raw socket RX/TX buffer sizes. + let buf_kbs = (C::BUF_SIZE / 1024) as u8; + this.bus_write(C::SOCKET_TXBUF_SIZE, &[buf_kbs]).await?; + this.bus_write(C::SOCKET_RXBUF_SIZE, &[buf_kbs]).await?; + + // MACRAW mode with MAC filtering. + this.bus_write(C::SOCKET_MODE, &[C::SOCKET_MODE_VALUE]).await?; + this.command(Command::Open).await?; + + Ok(this) + } + + async fn bus_read(&mut self, address: C::Address, data: &mut [u8]) -> Result<(), SPI::Error> { + C::bus_read(&mut self.spi, address, data).await + } + + async fn bus_write(&mut self, address: C::Address, data: &[u8]) -> Result<(), SPI::Error> { + C::bus_write(&mut self.spi, address, data).await + } + + async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { + let data = [code as u8]; + self.bus_write(C::SOCKET_INTR, &data).await + } + + async fn get_tx_write_ptr(&mut self) -> Result { + let mut data = [0u8; 2]; + self.bus_read(C::SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; + Ok(u16::from_be_bytes(data)) + } + + async fn set_tx_write_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + self.bus_write(C::SOCKET_TX_DATA_WRITE_PTR, &data).await + } + + async fn get_rx_read_ptr(&mut self) -> Result { + let mut data = [0u8; 2]; + self.bus_read(C::SOCKET_RX_DATA_READ_PTR, &mut data).await?; + Ok(u16::from_be_bytes(data)) + } + + async fn set_rx_read_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + self.bus_write(C::SOCKET_RX_DATA_READ_PTR, &data).await + } + + async fn command(&mut self, command: Command) -> Result<(), SPI::Error> { + let data = [command as u8]; + self.bus_write(C::SOCKET_COMMAND, &data).await + } + + async fn get_rx_size(&mut self) -> Result { + loop { + // Wait until two sequential reads are equal + let mut res0 = [0u8; 2]; + self.bus_read(C::SOCKET_RECVD_SIZE, &mut res0).await?; + let mut res1 = [0u8; 2]; + self.bus_read(C::SOCKET_RECVD_SIZE, &mut res1).await?; + if res0 == res1 { + break Ok(u16::from_be_bytes(res0)); + } + } + } + + async fn get_tx_free_size(&mut self) -> Result { + let mut data = [0; 2]; + self.bus_read(C::SOCKET_TX_FREE_SIZE, &mut data).await?; + Ok(u16::from_be_bytes(data)) + } + + /// Read bytes from the RX buffer. + async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { + if C::AUTO_WRAP { + self.bus_read(C::rx_addr(*read_ptr), buffer).await?; + } else { + let addr = *read_ptr % C::BUF_SIZE; + if addr as usize + buffer.len() <= C::BUF_SIZE as usize { + self.bus_read(C::rx_addr(addr), buffer).await?; + } else { + let n = C::BUF_SIZE - addr; + self.bus_read(C::rx_addr(addr), &mut buffer[..n as usize]).await?; + self.bus_read(C::rx_addr(0), &mut buffer[n as usize..]).await?; + } + } + + *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); + + Ok(()) + } + + /// Read an ethernet frame from the device. Returns the number of bytes read. + pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result { + let rx_size = self.get_rx_size().await? as usize; + if rx_size == 0 { + return Ok(0); + } + + self.reset_interrupt(Interrupt::Receive).await?; + + let mut read_ptr = self.get_rx_read_ptr().await?; + + // First two bytes gives the size of the received ethernet frame + let expected_frame_size: usize = { + let mut frame_bytes = [0u8; 2]; + self.read_bytes(&mut read_ptr, &mut frame_bytes).await?; + u16::from_be_bytes(frame_bytes) as usize - 2 + }; + + // Read the ethernet frame + self.read_bytes(&mut read_ptr, &mut frame[..expected_frame_size]) + .await?; + + // Register RX as completed + self.set_rx_read_ptr(read_ptr).await?; + self.command(Command::Receive).await?; + + Ok(expected_frame_size) + } + + /// Write an ethernet frame to the device. Returns number of bytes written + pub async fn write_frame(&mut self, frame: &[u8]) -> Result { + while self.get_tx_free_size().await? < frame.len() as u16 {} + let write_ptr = self.get_tx_write_ptr().await?; + + if C::AUTO_WRAP { + self.bus_write(C::tx_addr(write_ptr), frame).await?; + } else { + let addr = write_ptr % C::BUF_SIZE; + if addr as usize + frame.len() <= C::BUF_SIZE as usize { + self.bus_write(C::tx_addr(addr), frame).await?; + } else { + let n = C::BUF_SIZE - addr; + self.bus_write(C::tx_addr(addr), &frame[..n as usize]).await?; + self.bus_write(C::tx_addr(0), &frame[n as usize..]).await?; + } + } + + self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) + .await?; + self.command(Command::Send).await?; + Ok(frame.len()) + } + + pub async fn is_link_up(&mut self) -> bool { + let mut link = [0]; + self.bus_read(C::COMMON_PHY_CFG, &mut link).await.ok(); + link[0] & 1 == 1 + } +} diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-wiznet/src/lib.rs similarity index 65% rename from embassy-net-w5500/src/lib.rs rename to embassy-net-wiznet/src/lib.rs index efd9bed6..3030dfb9 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-wiznet/src/lib.rs @@ -1,9 +1,9 @@ -//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. +//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for WIZnet ethernet chips. #![no_std] +#![feature(async_fn_in_trait)] +pub mod chip; mod device; -mod socket; -mod spi; use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; @@ -13,10 +13,12 @@ use embedded_hal::digital::OutputPin; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use crate::device::W5500; +use crate::chip::Chip; +use crate::device::WiznetDevice; + const MTU: usize = 1514; -/// Type alias for the embassy-net driver for W5500 +/// 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. @@ -33,18 +35,18 @@ impl State { } } -/// Background runner for the W5500. +/// Background runner for the driver. /// -/// You must call `.run()` in a background task for the W5500 to operate. -pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> { - mac: W5500, +/// You must call `.run()` in a background task for the driver to operate. +pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { + mac: WiznetDevice, ch: ch::Runner<'d, MTU>, int: INT, _reset: RST, } -/// You must call this in a background task for the W5500 to operate. -impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { +/// You must call this in a background task for the driver to operate. +impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> { pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); loop { @@ -78,25 +80,31 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { } } -/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). -pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( +/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net). +/// +/// 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 async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>( mac_addr: [u8; 6], state: &'a mut State, spi_dev: SPI, int: INT, mut reset: RST, -) -> (Device<'a>, Runner<'a, SPI, INT, RST>) { - // Reset the W5500. +) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) { + // Reset the chip. reset.set_low().ok(); // Ensure the reset is registered. Timer::after(Duration::from_millis(1)).await; reset.set_high().ok(); - // Wait for the W5500 to achieve PLL lock. - Timer::after(Duration::from_millis(2)).await; - let mac = W5500::new(spi_dev, mac_addr).await.unwrap(); + // Wait for PLL lock. Some chips are slower than others. + // Slowest is w5100s which is 100ms, so let's just wait that. + Timer::after(Duration::from_millis(100)).await; - let (runner, device) = ch::new(&mut state.ch_state, mac_addr); + let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap(); + + let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); ( device, Runner { diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 6dc429dd..0c551f20 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -14,11 +14,11 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] +features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] target = "thumbv7em-none-eabi" [package.metadata.docs.rs] -features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] +features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] [features] default = [] @@ -26,8 +26,7 @@ std = [] defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] -nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"] -unstable-traits = [] +nightly = ["dep:embedded-io-async", "dep:embedded-nal-async"] udp = ["smoltcp/socket-udp"] tcp = ["smoltcp/socket-tcp"] @@ -37,6 +36,7 @@ proto-ipv4 = ["smoltcp/proto-ipv4"] proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] +medium-ieee802154 = ["smoltcp/medium-ieee802154"] igmp = ["smoltcp/proto-igmp"] [dependencies] @@ -52,7 +52,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embedded-io = { version = "0.4.0", optional = true } +embedded-io-async = { version = "0.5.0", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.7.5", default-features = false } @@ -61,5 +61,5 @@ generic-array = { version = "0.14.4", default-features = false } stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" -embedded-nal-async = { version = "0.4.0", optional = true } +embedded-nal-async = { version = "0.5.0", optional = true } atomic-polyfill = { version = "1.0" } diff --git a/embassy-net/README.md b/embassy-net/README.md index 48f9fd83..7bb2283c 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -22,7 +22,7 @@ unimplemented features of the network protocols. - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. - [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). -- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. +- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500) - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. ## Examples @@ -49,7 +49,7 @@ trait has not had breaking changes. This crate can run on any executor. -[`embassy-time`](https://crates.io/crates/embassy-net-driver) is used for timekeeping and timeouts. You must +[`embassy-time`](https://crates.io/crates/embassy-time) is used for timekeeping and timeouts. You must link an `embassy-time` driver in your project to use this crate. ## License diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 4513c86d..d29ab897 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -51,6 +51,8 @@ where Medium::Ethernet => phy::Medium::Ethernet, #[cfg(feature = "medium-ip")] Medium::Ip => phy::Medium::Ip, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => phy::Medium::Ieee802154, #[allow(unreachable_patterns)] _ => panic!( "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 94f75f10..fdd45b31 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -68,7 +68,7 @@ where } } -#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +#[cfg(feature = "nightly")] impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D> where D: Driver + 'static, diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 0d0a986f..2fb34f43 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -24,16 +24,22 @@ use embassy_net_driver::{Driver, LinkState, Medium}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; +#[allow(unused_imports)] use heapless::Vec; #[cfg(feature = "igmp")] pub use smoltcp::iface::MulticastError; +#[allow(unused_imports)] use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; +#[cfg(feature = "medium-ethernet")] +pub use smoltcp::wire::EthernetAddress; +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))] +pub use smoltcp::wire::HardwareAddress; #[cfg(feature = "udp")] pub use smoltcp::wire::IpListenEndpoint; -#[cfg(feature = "medium-ethernet")] -pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; +#[cfg(feature = "medium-ieee802154")] +pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame}; pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint}; #[cfg(feature = "proto-ipv4")] pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; @@ -224,6 +230,20 @@ pub(crate) struct SocketStack { next_local_port: u16, } +fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress { + match addr { + #[cfg(feature = "medium-ethernet")] + driver::HardwareAddress::Ethernet(eth) => HardwareAddress::Ethernet(EthernetAddress(eth)), + #[cfg(feature = "medium-ieee802154")] + driver::HardwareAddress::Ieee802154(ieee) => HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)), + #[cfg(feature = "medium-ip")] + 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), + } +} + impl Stack { /// Create a new network stack. pub fn new( @@ -232,21 +252,7 @@ impl Stack { resources: &'static mut StackResources, random_seed: u64, ) -> Self { - #[cfg(feature = "medium-ethernet")] - let medium = device.capabilities().medium; - - let hardware_addr = match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())), - #[cfg(feature = "medium-ip")] - Medium::Ip => HardwareAddress::Ip, - #[allow(unreachable_patterns)] - _ => panic!( - "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", - medium - ), - }; - let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr); + let mut iface_cfg = smoltcp::iface::Config::new(to_smoltcp_hardware_address(device.hardware_address())); iface_cfg.random_seed = random_seed; let iface = Interface::new( @@ -262,6 +268,7 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; + #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] let mut socket = SocketStack { sockets, iface, @@ -269,6 +276,7 @@ impl Stack { next_local_port, }; + #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] let mut inner = Inner { device, link_up: false, @@ -287,6 +295,9 @@ impl Stack { dns_waker: WakerRegistration::new(), }; + #[cfg(feature = "medium-ieee802154")] + let _ = config; + #[cfg(feature = "proto-ipv4")] match config.ipv4 { ConfigV4::Static(config) => { @@ -323,9 +334,9 @@ impl Stack { f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) } - /// Get the MAC address of the network interface. - pub fn ethernet_address(&self) -> [u8; 6] { - self.with(|_s, i| i.device.ethernet_address()) + /// Get the hardware address of the network interface. + pub fn hardware_address(&self) -> HardwareAddress { + self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address())) } /// Get whether the link is up. @@ -479,30 +490,78 @@ impl Stack { } #[cfg(feature = "igmp")] -impl Stack { +impl Stack { /// Join a multicast group. - pub fn join_multicast_group(&self, addr: T) -> Result + pub async fn join_multicast_group(&self, addr: T) -> Result + where + T: Into, + { + let addr = addr.into(); + + poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await + } + + /// Join a multicast group. + /// + /// When the send queue is full, this method will return `Poll::Pending` + /// and register the current task to be notified when the queue has space available. + pub fn poll_join_multicast_group(&self, addr: T, cx: &mut Context<'_>) -> Poll> where T: Into, { let addr = addr.into(); self.with_mut(|s, i| { - s.iface - .join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) + let mut smoldev = DriverAdapter { + cx: Some(cx), + inner: &mut i.device, + }; + + match s + .iface + .join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) + { + Ok(announce_sent) => Poll::Ready(Ok(announce_sent)), + Err(MulticastError::Exhausted) => Poll::Pending, + Err(other) => Poll::Ready(Err(other)), + } }) } /// Leave a multicast group. - pub fn leave_multicast_group(&self, addr: T) -> Result + pub async fn leave_multicast_group(&self, addr: T) -> Result + where + T: Into, + { + let addr = addr.into(); + + poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await + } + + /// Leave a multicast group. + /// + /// When the send queue is full, this method will return `Poll::Pending` + /// and register the current task to be notified when the queue has space available. + pub fn poll_leave_multicast_group(&self, addr: T, cx: &mut Context<'_>) -> Poll> where T: Into, { let addr = addr.into(); self.with_mut(|s, i| { - s.iface - .leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) + let mut smoldev = DriverAdapter { + cx: Some(cx), + inner: &mut i.device, + }; + + match s + .iface + .leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) + { + Ok(leave_sent) => Poll::Ready(Ok(leave_sent)), + Err(MulticastError::Exhausted) => Poll::Pending, + Err(other) => Poll::Ready(Err(other)), + } }) } @@ -524,22 +583,26 @@ impl SocketStack { impl Inner { #[cfg(feature = "proto-ipv4")] fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) { - #[cfg(feature = "medium-ethernet")] - let medium = self.device.capabilities().medium; - debug!("Acquired IP configuration:"); debug!(" IP address: {}", config.address); s.iface.update_ip_addrs(|addrs| { - if addrs.is_empty() { - addrs.push(IpCidr::Ipv4(config.address)).unwrap(); - } else { - addrs[0] = IpCidr::Ipv4(config.address); + 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-ethernet")] - if medium == Medium::Ethernet { + #[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(); @@ -570,11 +633,14 @@ impl Inner { debug!(" IP address: {}", config.address); s.iface.update_ip_addrs(|addrs| { - if addrs.is_empty() { - addrs.push(IpCidr::Ipv6(config.address)).unwrap(); - } else { - addrs[0] = IpCidr::Ipv6(config.address); + 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")] @@ -642,13 +708,21 @@ impl Inner { socket.set_retry_config(config.retry_config); } - #[allow(unused)] // used only with dhcp - fn unapply_config(&mut self, s: &mut SocketStack) { + #[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| ip_addrs.clear()); + 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); + } + }); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { #[cfg(feature = "proto-ipv4")] @@ -665,11 +739,12 @@ impl Inner { fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { s.waker.register(cx.waker()); - #[cfg(feature = "medium-ethernet")] - if self.device.capabilities().medium == Medium::Ethernet { - s.iface.set_hardware_addr(HardwareAddress::Ethernet(EthernetAddress( - self.device.ethernet_address(), - ))); + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + if self.device.capabilities().medium == Medium::Ethernet + || self.device.capabilities().medium == Medium::Ieee802154 + { + s.iface + .set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address())); } let timestamp = instant_to_smoltcp(Instant::now()); @@ -695,7 +770,7 @@ impl Inner { if self.link_up { match socket.poll() { None => {} - Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), + Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s), Some(dhcpv4::Event::Configured(config)) => { let config = StaticConfigV4 { address: config.address, @@ -707,7 +782,7 @@ impl Inner { } } else if old_link_up { socket.reset(); - self.unapply_config(s); + self.unapply_config_v4(s); } } //if old_link_up || self.link_up { diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 367675b1..c92ad2d2 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -82,6 +82,22 @@ impl<'a> TcpReader<'a> { pub async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } + + /// Call `f` with the largest contiguous slice of octets in the receive buffer, + /// and dequeue the amount of elements returned by `f`. + /// + /// If no data is available, it waits until there is at least one byte available. + pub async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.read_with(f).await + } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn recv_capacity(&self) -> usize { + self.io.recv_capacity() + } } impl<'a> TcpWriter<'a> { @@ -100,6 +116,22 @@ impl<'a> TcpWriter<'a> { pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } + + /// Call `f` with the largest contiguous slice of octets in the transmit buffer, + /// and enqueue the amount of elements returned by `f`. + /// + /// If the socket is not ready to accept data, it waits until it is. + pub async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.write_with(f).await + } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn send_capacity(&self) -> usize { + self.io.send_capacity() + } } impl<'a> TcpSocket<'a> { @@ -121,6 +153,38 @@ impl<'a> TcpSocket<'a> { } } + /// Return the maximum number of bytes inside the recv buffer. + pub fn recv_capacity(&self) -> usize { + self.io.recv_capacity() + } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn send_capacity(&self) -> usize { + self.io.send_capacity() + } + + /// Call `f` with the largest contiguous slice of octets in the transmit buffer, + /// and enqueue the amount of elements returned by `f`. + /// + /// If the socket is not ready to accept data, it waits until it is. + pub async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.write_with(f).await + } + + /// Call `f` with the largest contiguous slice of octets in the receive buffer, + /// and dequeue the amount of elements returned by `f`. + /// + /// If no data is available, it waits until there is at least one byte available. + pub async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.read_with(f).await + } + /// Split the socket into reader and a writer halves. pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { (TcpReader { io: self.io }, TcpWriter { io: self.io }) @@ -359,6 +423,64 @@ impl<'d> TcpIo<'d> { .await } + async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + let mut f = Some(f); + poll_fn(move |cx| { + self.with_mut(|s, _| { + if !s.can_send() { + if s.may_send() { + // socket buffer is full wait until it has atleast one byte free + s.register_send_waker(cx.waker()); + Poll::Pending + } else { + // if we can't transmit because the transmit half of the duplex connection is closed then return an error + Poll::Ready(Err(Error::ConnectionReset)) + } + } else { + Poll::Ready(match s.send(f.take().unwrap()) { + // Connection reset. TODO: this can also be timeouts etc, investigate. + Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset), + Ok(r) => Ok(r), + }) + } + }) + }) + .await + } + + async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + let mut f = Some(f); + poll_fn(move |cx| { + self.with_mut(|s, _| { + if !s.can_recv() { + if s.may_recv() { + // socket buffer is empty wait until it has atleast one byte has arrived + s.register_recv_waker(cx.waker()); + Poll::Pending + } else { + // if we can't receive because the recieve half of the duplex connection is closed then return an error + Poll::Ready(Err(Error::ConnectionReset)) + } + } else { + Poll::Ready(match s.recv(f.take().unwrap()) { + // Connection reset. TODO: this can also be timeouts etc, investigate. + Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => { + Err(Error::ConnectionReset) + } + Ok(r) => Ok(r), + }) + } + }) + }) + .await + } + async fn flush(&mut self) -> Result<(), Error> { poll_fn(move |cx| { self.with_mut(|s, _| { @@ -376,35 +498,50 @@ impl<'d> TcpIo<'d> { }) .await } + + fn recv_capacity(&self) -> usize { + self.with(|s, _| s.recv_capacity()) + } + + fn send_capacity(&self) -> usize { + self.with(|s, _| s.send_capacity()) + } } #[cfg(feature = "nightly")] mod embedded_io_impls { use super::*; - impl embedded_io::Error for ConnectError { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other + impl embedded_io_async::Error for ConnectError { + fn kind(&self) -> embedded_io_async::ErrorKind { + match self { + ConnectError::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset, + ConnectError::TimedOut => embedded_io_async::ErrorKind::TimedOut, + ConnectError::NoRoute => embedded_io_async::ErrorKind::NotConnected, + ConnectError::InvalidState => embedded_io_async::ErrorKind::Other, + } } } - impl embedded_io::Error for Error { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other + impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { + match self { + Error::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset, + } } } - impl<'d> embedded_io::Io for TcpSocket<'d> { + impl<'d> embedded_io_async::ErrorType for TcpSocket<'d> { type Error = Error; } - impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { + impl<'d> embedded_io_async::Read for TcpSocket<'d> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } } - impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { + impl<'d> embedded_io_async::Write for TcpSocket<'d> { async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } @@ -414,21 +551,21 @@ mod embedded_io_impls { } } - impl<'d> embedded_io::Io for TcpReader<'d> { + impl<'d> embedded_io_async::ErrorType for TcpReader<'d> { type Error = Error; } - impl<'d> embedded_io::asynch::Read for TcpReader<'d> { + impl<'d> embedded_io_async::Read for TcpReader<'d> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } } - impl<'d> embedded_io::Io for TcpWriter<'d> { + impl<'d> embedded_io_async::ErrorType for TcpWriter<'d> { type Error = Error; } - impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { + impl<'d> embedded_io_async::Write for TcpWriter<'d> { async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } @@ -440,7 +577,7 @@ mod embedded_io_impls { } /// TCP client compatible with `embedded-nal-async` traits. -#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +#[cfg(feature = "nightly")] pub mod client { use core::cell::UnsafeCell; use core::mem::MaybeUninit; @@ -527,13 +664,13 @@ pub mod client { } } - impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::Io + impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::ErrorType for TcpConnection<'d, N, TX_SZ, RX_SZ> { type Error = Error; } - impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read + impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Read for TcpConnection<'d, N, TX_SZ, RX_SZ> { async fn read(&mut self, buf: &mut [u8]) -> Result { @@ -541,7 +678,7 @@ pub mod client { } } - impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write + impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Write for TcpConnection<'d, N, TX_SZ, RX_SZ> { async fn write(&mut self, buf: &[u8]) -> Result { diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 0d97b6db..0a5a7b8f 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -184,6 +184,26 @@ impl<'a> UdpSocket<'a> { pub fn may_recv(&self) -> bool { self.with(|s, _| s.can_recv()) } + + /// Return the maximum number packets the socket can receive. + pub fn packet_recv_capacity(&self) -> usize { + self.with(|s, _| s.packet_recv_capacity()) + } + + /// Return the maximum number packets the socket can receive. + pub fn packet_send_capacity(&self) -> usize { + self.with(|s, _| s.packet_send_capacity()) + } + + /// Return the maximum number of bytes inside the recv buffer. + pub fn payload_recv_capacity(&self) -> usize { + self.with(|s, _| s.payload_recv_capacity()) + } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn payload_send_capacity(&self) -> usize { + self.with(|s, _| s.payload_send_capacity()) + } } impl Drop for UdpSocket<'_> { diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 57dd22f1..67ec4eb9 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -32,10 +32,10 @@ rt = [ time = ["dep:embassy-time"] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] +defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"] # Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] +nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io-async", "embassy-embedded-hal/nightly"] # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. @@ -93,14 +93,15 @@ _gpio-p1 = [] [dependencies] embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} -embedded-io = { version = "0.4.0", features = ["async"], optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0", optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 9bc1c1e7..10b8b0fb 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -15,8 +15,8 @@ use core::slice; use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; @@ -572,37 +572,37 @@ impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> { mod _embedded_io { use super::*; - impl embedded_io::Error for Error { - fn kind(&self) -> embedded_io::ErrorKind { + impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { match *self {} } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarte<'d, U, T> { type Error = Error; } - impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> { + impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteRx<'u, 'd, U, T> { type Error = Error; } - impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> { + impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteTx<'u, 'd, U, T> { type Error = Error; } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarte<'d, U, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.inner_read(buf).await } } - impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarteRx<'u, 'd, U, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.inner.inner_read(buf).await } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarte<'d, U, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { self.inner_fill_buf().await } @@ -612,7 +612,7 @@ mod _embedded_io { } } - impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarteRx<'u, 'd, U, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { self.inner.inner_fill_buf().await } @@ -622,7 +622,7 @@ mod _embedded_io { } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarte<'d, U, T> { async fn write(&mut self, buf: &[u8]) -> Result { self.inner_write(buf).await } @@ -632,7 +632,7 @@ mod _embedded_io { } } - impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarteTx<'u, 'd, U, T> { async fn write(&mut self, buf: &[u8]) -> Result { self.inner.inner_write(buf).await } diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 8776000c..70e4b486 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -208,7 +208,7 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 5519e895..7416d391 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -234,7 +234,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index d5367c59..58801068 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -236,7 +236,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 78517044..0ecddaf3 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 256 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -224,7 +224,7 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index b77564a5..ae39628d 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -12,7 +12,7 @@ pub const FLASH_SIZE: usize = 512 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -263,7 +263,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index bff7f4eb..b8830b33 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 512 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -306,7 +306,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 9b005082..a490cb07 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -311,7 +311,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 410ae921..afc2c4a7 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -218,7 +218,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 1024 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -506,7 +506,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( FPU, CACHE, SPU, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 6ac78308..dee666a6 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -109,7 +109,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 256 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -342,7 +342,7 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( CLOCK_POWER, RADIO, RNG, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 67ea032f..495285ba 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -169,7 +169,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 1024 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -368,7 +368,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( SPU, CLOCK_POWER, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0, diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 895ab934..ea2b7609 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -5,7 +5,7 @@ use core::convert::Infallible; use core::hint::unreachable_unchecked; use cfg_if::cfg_if; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use self::sealed::Pin as _; use crate::pac::p0 as gpio; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 6550f2ab..7488bc08 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -4,7 +4,7 @@ use core::convert::Infallible; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index fea38c4c..907acdf4 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -9,8 +9,8 @@ use core::ops::{Deref, DerefMut}; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index d23759f9..355a0049 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -98,7 +98,7 @@ mod chip; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -127,7 +127,7 @@ pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; pub use crate::chip::interrupt; pub use crate::pac::NVIC_PRIO_BITS; diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 91a5a272..de840b88 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -2,7 +2,7 @@ use core::{ptr, slice}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 0e30f700..01f41e9f 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,294 +1,500 @@ -//! Pulse Density Modulation (PDM) mirophone driver. - -#![macro_use] - -use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::Poll; - -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; - -use crate::chip::EASY_DMA_SIZE; -use crate::gpio::sealed::Pin; -use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, Peripheral}; - -/// Interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - unsafe fn on_interrupt() { - T::regs().intenclr.write(|w| w.end().clear()); - T::state().waker.wake(); - } -} - -/// PDM microphone interface -pub struct Pdm<'d, T: Instance> { - _peri: PeripheralRef<'d, T>, -} - -/// PDM error. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Error { - /// Buffer is too long. - BufferTooLong, - /// Buffer is empty - BufferZeroLength, - /// PDM is not running - NotRunning, -} - -static DUMMY_BUFFER: [i16; 1] = [0; 1]; - -impl<'d, T: Instance> Pdm<'d, T> { - /// Create PDM driver - pub fn new( - pdm: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - clk: impl Peripheral

+ 'd, - din: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(pdm, clk, din); - Self::new_inner(pdm, clk.map_into(), din.map_into(), config) - } - - fn new_inner( - pdm: PeripheralRef<'d, T>, - clk: PeripheralRef<'d, AnyPin>, - din: PeripheralRef<'d, AnyPin>, - config: Config, - ) -> Self { - into_ref!(pdm); - - let r = T::regs(); - - // setup gpio pins - din.conf().write(|w| w.input().set_bit()); - r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); - clk.set_low(); - clk.conf().write(|w| w.dir().output()); - r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); - - // configure - // use default for - // - gain right - // - gain left - // - clk - // - ratio - r.mode.write(|w| { - w.edge().bit(config.edge == Edge::LeftRising); - w.operation().bit(config.operation_mode == OperationMode::Mono); - w - }); - r.gainl.write(|w| w.gainl().default_gain()); - r.gainr.write(|w| w.gainr().default_gain()); - - // IRQ - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - r.enable.write(|w| w.enable().set_bit()); - - Self { _peri: pdm } - } - - /// Start sampling microphon data into a dummy buffer - /// Usefull to start the microphon and keep it active between recording samples - pub async fn start(&mut self) { - let r = T::regs(); - - // start dummy sampling because microphon needs some setup time - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - r.tasks_start.write(|w| unsafe { w.bits(1) }); - } - - /// Stop sampling microphon data inta a dummy buffer - pub async fn stop(&mut self) { - let r = T::regs(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - r.events_started.reset(); - } - - /// Sample data into the given buffer. - pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let r = T::regs(); - - if r.events_started.read().bits() == 0 { - return Err(Error::NotRunning); - } - - let drop = OnDrop::new(move || { - r.intenclr.write(|w| w.end().clear()); - r.events_stopped.reset(); - - // reset to dummy buffer - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - while r.events_stopped.read().bits() == 0 {} - }); - - // setup user buffer - let ptr = buffer.as_ptr(); - let len = buffer.len(); - r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); - r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); - - // wait till the current sample is finished and the user buffer sample is started - Self::wait_for_sample().await; - - // reset the buffer back to the dummy buffer - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - // wait till the user buffer is sampled - Self::wait_for_sample().await; - - drop.defuse(); - - Ok(()) - } - - async fn wait_for_sample() { - let r = T::regs(); - - r.events_end.reset(); - r.intenset.write(|w| w.end().set()); - - compiler_fence(Ordering::SeqCst); - - poll_fn(|cx| { - T::state().waker.register(cx.waker()); - if r.events_end.read().bits() != 0 { - return Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - } -} - -/// PDM microphone driver Config -pub struct Config { - /// Use stero or mono operation - pub operation_mode: OperationMode, - /// On which edge the left channel should be samples - pub edge: Edge, -} - -impl Default for Config { - fn default() -> Self { - Self { - operation_mode: OperationMode::Mono, - edge: Edge::LeftFalling, - } - } -} - -/// PDM operation mode. -#[derive(PartialEq)] -pub enum OperationMode { - /// Mono (1 channel) - Mono, - /// Stereo (2 channels) - Stereo, -} - -/// PDM edge polarity -#[derive(PartialEq)] -pub enum Edge { - /// Left edge is rising - LeftRising, - /// Left edge is falling - LeftFalling, -} - -impl<'d, T: Instance> Drop for Pdm<'d, T> { - fn drop(&mut self) { - let r = T::regs(); - - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - - r.enable.write(|w| w.enable().disabled()); - - r.psel.din.reset(); - r.psel.clk.reset(); - } -} - -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; - - /// Peripheral static state - pub struct State { - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } - } - } - - pub trait Instance { - fn regs() -> &'static crate::pac::pdm::RegisterBlock; - fn state() -> &'static State; - } -} - -/// PDM peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { - /// Interrupt for this peripheral. - type Interrupt: interrupt::typelevel::Interrupt; -} - -macro_rules! impl_pdm { - ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::pdm::sealed::Instance for peripherals::$type { - fn regs() -> &'static crate::pac::pdm::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } - } - fn state() -> &'static crate::pdm::sealed::State { - static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); - &STATE - } - } - impl crate::pdm::Instance for peripherals::$type { - type Interrupt = crate::interrupt::typelevel::$irq; - } - }; -} +//! Pulse Density Modulation (PDM) mirophone driver. + +#![macro_use] + +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; +use fixed::types::I7F1; +use futures::future::poll_fn; + +use crate::chip::EASY_DMA_SIZE; +use crate::gpio::sealed::Pin; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::typelevel::Interrupt; +use crate::pac::pdm::mode::{EDGE_A, OPERATION_A}; +pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; +#[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", +))] +pub use crate::pac::pdm::ratio::RATIO_A as Ratio; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + + if r.events_end.read().bits() != 0 { + r.intenclr.write(|w| w.end().clear()); + } + + if r.events_started.read().bits() != 0 { + r.intenclr.write(|w| w.started().clear()); + } + + if r.events_stopped.read().bits() != 0 { + r.intenclr.write(|w| w.stopped().clear()); + } + + T::state().waker.wake(); + } +} + +/// PDM microphone interface +pub struct Pdm<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +/// PDM error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Buffer is too long. + BufferTooLong, + /// Buffer is empty + BufferZeroLength, + /// PDM is not running + NotRunning, + /// PDM is already running + AlreadyRunning, +} + +static DUMMY_BUFFER: [i16; 1] = [0; 1]; + +/// The state of a continuously running sampler. While it reflects +/// the progress of a sampler, it also signals what should be done +/// next. For example, if the sampler has stopped then the Pdm implementation +/// can then tear down its infrastructure. +#[derive(PartialEq)] +pub enum SamplerState { + /// The sampler processed the samples and is ready for more. + Sampled, + /// The sampler is done processing samples. + Stopped, +} + +impl<'d, T: Instance> Pdm<'d, T> { + /// Create PDM driver + pub fn new( + pdm: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + clk: impl Peripheral

+ 'd, + din: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(pdm, clk, din); + Self::new_inner(pdm, clk.map_into(), din.map_into(), config) + } + + fn new_inner( + pdm: PeripheralRef<'d, T>, + clk: PeripheralRef<'d, AnyPin>, + din: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!(pdm); + + let r = T::regs(); + + // setup gpio pins + din.conf().write(|w| w.input().set_bit()); + r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); + clk.set_low(); + clk.conf().write(|w| w.dir().output()); + r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); + + // configure + r.pdmclkctrl.write(|w| w.freq().variant(config.frequency)); + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] + r.ratio.write(|w| w.ratio().variant(config.ratio)); + r.mode.write(|w| { + w.operation().variant(config.operation_mode.into()); + w.edge().variant(config.edge.into()); + w + }); + + Self::_set_gain(r, config.gain_left, config.gain_right); + + // Disable all events interrupts + r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); + + // IRQ + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + r.enable.write(|w| w.enable().set_bit()); + + Self { _peri: pdm } + } + + fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { + let gain_left = gain_left + .saturating_add(I7F1::from_bits(40)) + .saturating_to_num::() + .clamp(0, 0x50); + let gain_right = gain_right + .saturating_add(I7F1::from_bits(40)) + .saturating_to_num::() + .clamp(0, 0x50); + + r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); + r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); + } + + /// Adjust the gain of the PDM microphone on the fly + pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) { + Self::_set_gain(T::regs(), gain_left, gain_right) + } + + /// Start sampling microphon data into a dummy buffer + /// Usefull to start the microphon and keep it active between recording samples + pub async fn start(&mut self) { + let r = T::regs(); + + // start dummy sampling because microphon needs some setup time + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + r.tasks_start.write(|w| unsafe { w.bits(1) }); + } + + /// Stop sampling microphon data inta a dummy buffer + pub async fn stop(&mut self) { + let r = T::regs(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.events_started.reset(); + } + + /// Sample data into the given buffer. + pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + + if r.events_started.read().bits() == 0 { + return Err(Error::NotRunning); + } + + let drop = OnDrop::new(move || { + r.intenclr.write(|w| w.end().clear()); + r.events_stopped.reset(); + + // reset to dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + while r.events_stopped.read().bits() == 0 {} + }); + + // setup user buffer + let ptr = buffer.as_ptr(); + let len = buffer.len(); + r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); + + // wait till the current sample is finished and the user buffer sample is started + Self::wait_for_sample().await; + + // reset the buffer back to the dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + // wait till the user buffer is sampled + Self::wait_for_sample().await; + + drop.defuse(); + + Ok(()) + } + + async fn wait_for_sample() { + let r = T::regs(); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + T::state().waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + } + + /// Continuous sampling with double buffers. + /// + /// A sampler closure is provided that receives the buffer of samples, noting + /// that the size of this buffer can be less than the original buffer's size. + /// A command is return from the closure that indicates whether the sampling + /// should continue or stop. + /// + /// NOTE: The time spent within the callback supplied should not exceed the time + /// taken to acquire the samples into a single buffer. You should measure the + /// time taken by the callback and set the sample buffer size accordingly. + /// Exceeding this time can lead to samples becoming dropped. + pub async fn run_task_sampler( + &mut self, + bufs: &mut [[i16; N]; 2], + mut sampler: S, + ) -> Result<(), Error> + where + S: FnMut(&[i16; N]) -> SamplerState, + { + let r = T::regs(); + + if r.events_started.read().bits() != 0 { + return Err(Error::AlreadyRunning); + } + + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); + + // Reset and enable the events + r.events_end.reset(); + r.events_started.reset(); + r.events_stopped.reset(); + r.intenset.write(|w| { + w.end().set(); + w.started().set(); + w.stopped().set(); + w + }); + + // Don't reorder the start event before the previous writes. Hopefully self + // wouldn't happen anyway. + compiler_fence(Ordering::SeqCst); + + r.tasks_start.write(|w| unsafe { w.bits(1) }); + + let mut current_buffer = 0; + + let mut done = false; + + let drop = OnDrop::new(|| { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + // N.B. It would be better if this were async, but Drop only support sync code. + while r.events_stopped.read().bits() != 0 {} + }); + + // Wait for events and complete when the sampler indicates it has had enough. + poll_fn(|cx| { + let r = T::regs(); + + T::state().waker.register(cx.waker()); + + if r.events_end.read().bits() != 0 { + compiler_fence(Ordering::SeqCst); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + if !done { + // Discard the last buffer after the user requested a stop. + if sampler(&bufs[current_buffer]) == SamplerState::Sampled { + let next_buffer = 1 - current_buffer; + current_buffer = next_buffer; + } else { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + done = true; + }; + }; + } + + if r.events_started.read().bits() != 0 { + r.events_started.reset(); + r.intenset.write(|w| w.started().set()); + + let next_buffer = 1 - current_buffer; + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) }); + } + + if r.events_stopped.read().bits() != 0 { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + drop.defuse(); + Ok(()) + } +} + +/// PDM microphone driver Config +pub struct Config { + /// Use stero or mono operation + pub operation_mode: OperationMode, + /// On which edge the left channel should be samples + pub edge: Edge, + /// Clock frequency + pub frequency: Frequency, + /// Clock ratio + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] + pub ratio: Ratio, + /// Gain left in dB + pub gain_left: I7F1, + /// Gain right in dB + pub gain_right: I7F1, +} + +impl Default for Config { + fn default() -> Self { + Self { + operation_mode: OperationMode::Mono, + edge: Edge::LeftFalling, + frequency: Frequency::DEFAULT, + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] + ratio: Ratio::RATIO80, + gain_left: I7F1::ZERO, + gain_right: I7F1::ZERO, + } + } +} + +/// PDM operation mode. +#[derive(PartialEq)] +pub enum OperationMode { + /// Mono (1 channel) + Mono, + /// Stereo (2 channels) + Stereo, +} + +impl From for OPERATION_A { + fn from(mode: OperationMode) -> Self { + match mode { + OperationMode::Mono => OPERATION_A::MONO, + OperationMode::Stereo => OPERATION_A::STEREO, + } + } +} + +/// PDM edge polarity +#[derive(PartialEq)] +pub enum Edge { + /// Left edge is rising + LeftRising, + /// Left edge is falling + LeftFalling, +} + +impl From for EDGE_A { + fn from(edge: Edge) -> Self { + match edge { + Edge::LeftRising => EDGE_A::LEFT_RISING, + Edge::LeftFalling => EDGE_A::LEFT_FALLING, + } + } +} + +impl<'d, T: Instance> Drop for Pdm<'d, T> { + fn drop(&mut self) { + let r = T::regs(); + + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + + r.enable.write(|w| w.enable().disabled()); + + r.psel.din.reset(); + r.psel.clk.reset(); + } +} + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::pdm::RegisterBlock; + fn state() -> &'static State; + } +} + +/// PDM peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_pdm { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::pdm::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::pdm::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::pdm::sealed::State { + static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); + &STATE + } + } + impl crate::pdm::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +} diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 40ccb2f0..0bc7f821 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use super::{Channel, ConfigurableChannel, Event, Ppi, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index ff6593bd..5b4a6438 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -18,7 +18,7 @@ use core::marker::PhantomData; use core::ptr::NonNull; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::{peripherals, Peripheral}; diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 1fe89862..3e9e9fc8 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index c8c81fa0..2f039763 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 8bac87d3..2aa50a2b 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -6,7 +6,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index baefc796..36ee33f6 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -7,8 +7,8 @@ use core::marker::PhantomData; use core::ptr; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 923b8b46..e2803f0d 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -8,8 +8,8 @@ use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::typelevel::Interrupt; diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 23292924..662b0561 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -6,8 +6,8 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::{saadc, SAADC}; use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index b7dc332e..d131a43d 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -8,7 +8,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; @@ -378,6 +378,9 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { gpio::deconfigure_pin(r.psel.miso.read().bits()); gpio::deconfigure_pin(r.psel.mosi.read().bits()); + // Disable all events interrupts + T::Interrupt::disable(); + trace!("spim drop: done"); } } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index aa438415..21282512 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -7,7 +7,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use crate::chip::FORCE_COPY_BUFFER_SIZE; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 491e92c0..cec46d8d 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -3,8 +3,8 @@ use core::future::poll_fn; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 04748238..3dbfdac4 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -6,7 +6,7 @@ #![macro_use] -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 2ad0d19b..fdea480e 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -9,7 +9,7 @@ use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index a115d561..c6c02055 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -8,7 +8,7 @@ use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 48d57fea..95434e7a 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -18,8 +18,8 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use pac::uarte0::RegisterBlock; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; @@ -949,51 +949,3 @@ mod eh02 { } } } - -#[cfg(feature = "unstable-traits")] -mod eh1 { - use super::*; - - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { - match *self { - Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, - Self::BufferNotInRAM => embedded_hal_1::serial::ErrorKind::Other, - } - } - } - - // ===================== - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for Uarte<'d, T> { - type Error = Error; - } - - impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteTx<'d, T> { - type Error = Error; - } - - impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { - type Error = Error; - } -} diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 76cf40ac..e26b49db 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -11,7 +11,7 @@ use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use core::task::Poll; use cortex_m::peripheral::NVIC; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 8f3ed885..60143c2b 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -16,7 +16,7 @@ flavors = [ default = [ "rt" ] rt = [ "rp-pac/rt" ] -defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] +defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-internal/defmt"] # critical section that is safe for multicore use critical-section-impl = ["critical-section/restore-state-u8"] @@ -42,13 +42,17 @@ boot2-ram-memcpy = [] boot2-w25q080 = [] boot2-w25x10cl = [] +# Allow using QSPI pins as GPIO pins. This is mostly not what you want (because your flash lives there) +# and would add both code and memory overhead when enabled needlessly. +qspi-as-gpio = [] + # Indicate code is running from RAM. # Set this if all code is in RAM, and the cores never access memory-mapped flash memory through XIP. # This allows the flash driver to not force pausing execution on both cores when doing flash operations. run-from-ram = [] # Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io-async"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -58,7 +62,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] 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-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-2"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } atomic-polyfill = "1.0.1" @@ -71,17 +75,19 @@ cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } chrono = { version = "0.4", default-features = false, optional = true } -embedded-io = { version = "0.4.0", features = ["async"], optional = true } +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0", optional = true } embedded-storage = { version = "0.3" } +embedded-storage-async = { version = "0.4.0", optional = true } rand_core = "0.6.4" fixed = "1.23.1" rp-pac = { version = "6" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true} paste = "1.0" pio-proc = {version= "0.2" } @@ -89,5 +95,5 @@ pio = {version= "0.2.1" } rp2040-boot2 = "0.3" [dev-dependencies] -embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] } +embassy-executor = { version = "0.3.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] } static_cell = "1.1" diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index dfa1b877..bac45574 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -1,17 +1,18 @@ use core::future::poll_fn; use core::marker::PhantomData; +use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as GpioPin; use crate::gpio::{self, AnyPin, Pull}; use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; -use crate::peripherals::ADC; -use crate::{interrupt, pac, peripherals, Peripheral}; +use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; +use crate::{dma, interrupt, pac, peripherals, Peripheral, RegExt}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -24,12 +25,15 @@ impl Default for Config { } } -pub struct Pin<'p> { - pin: PeripheralRef<'p, AnyPin>, +enum Source<'p> { + Pin(PeripheralRef<'p, AnyPin>), + TempSensor(PeripheralRef<'p, ADC_TEMP_SENSOR>), } -impl<'p> Pin<'p> { - pub fn new(pin: impl Peripheral

+ 'p, pull: Pull) -> Self { +pub struct Channel<'p>(Source<'p>); + +impl<'p> Channel<'p> { + pub fn new_pin(pin: impl Peripheral

+ 'p, pull: Pull) -> Self { into_ref!(pin); pin.pad_ctrl().modify(|w| { // manual says: @@ -42,24 +46,55 @@ impl<'p> Pin<'p> { w.set_pue(pull == Pull::Up); w.set_pde(pull == Pull::Down); }); - Self { pin: pin.map_into() } + Self(Source::Pin(pin.map_into())) + } + + pub fn new_temp_sensor(s: impl Peripheral

+ 'p) -> Self { + let r = pac::ADC; + r.cs().write_set(|w| w.set_ts_en(true)); + Self(Source::TempSensor(s.into_ref())) } fn channel(&self) -> u8 { - // this requires adc pins to be sequential and matching the adc channels, - // which is the case for rp2040 - self.pin._pin() - 26 + match &self.0 { + // this requires adc pins to be sequential and matching the adc channels, + // which is the case for rp2040 + Source::Pin(p) => p._pin() - 26, + Source::TempSensor(_) => 4, + } } } -impl<'d> Drop for Pin<'d> { +impl<'p> Drop for Source<'p> { fn drop(&mut self) { - self.pin.pad_ctrl().modify(|w| { - w.set_ie(true); - w.set_od(false); - w.set_pue(false); - w.set_pde(true); - }); + match self { + Source::Pin(p) => { + p.pad_ctrl().modify(|w| { + w.set_ie(true); + w.set_od(false); + w.set_pue(false); + w.set_pde(true); + }); + } + Source::TempSensor(_) => { + pac::ADC.cs().write_clear(|w| w.set_ts_en(true)); + } + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(transparent)] +pub struct Sample(u16); + +impl Sample { + pub fn good(&self) -> bool { + self.0 < 0x8000 + } + + pub fn value(&self) -> u16 { + self.0 & !0x8000 } } @@ -81,6 +116,16 @@ pub struct Adc<'d, M: Mode> { phantom: PhantomData<(&'d ADC, M)>, } +impl<'d, M: Mode> Drop for Adc<'d, M> { + fn drop(&mut self) { + let r = Self::regs(); + // disable ADC. leaving it enabled comes with a ~150µA static + // current draw. the temperature sensor has already been disabled + // by the temperature-reading methods, so we don't need to touch that. + r.cs().write(|w| w.set_en(false)); + } +} + impl<'d, M: Mode> Adc<'d, M> { #[inline] fn regs() -> pac::adc::Adc { @@ -105,10 +150,10 @@ impl<'d, M: Mode> Adc<'d, M> { while !r.cs().read().ready() {} } - fn sample_blocking(channel: u8) -> Result { + pub fn blocking_read(&mut self, ch: &mut Channel) -> Result { let r = Self::regs(); r.cs().modify(|w| { - w.set_ainsel(channel); + w.set_ainsel(ch.channel()); w.set_start_once(true); w.set_err(true); }); @@ -118,19 +163,6 @@ impl<'d, M: Mode> Adc<'d, M> { false => Ok(r.result().read().result().into()), } } - - pub fn blocking_read(&mut self, pin: &mut Pin) -> Result { - Self::sample_blocking(pin.channel()) - } - - pub fn blocking_read_temperature(&mut self) -> Result { - let r = Self::regs(); - r.cs().modify(|w| w.set_ts_en(true)); - while !r.cs().read().ready() {} - let result = Self::sample_blocking(4); - r.cs().modify(|w| w.set_ts_en(false)); - result - } } impl<'d> Adc<'d, Async> { @@ -162,10 +194,10 @@ impl<'d> Adc<'d, Async> { .await; } - async fn sample_async(channel: u8) -> Result { + pub async fn read(&mut self, ch: &mut Channel<'_>) -> Result { let r = Self::regs(); r.cs().modify(|w| { - w.set_ainsel(channel); + w.set_ainsel(ch.channel()); w.set_start_once(true); w.set_err(true); }); @@ -176,19 +208,89 @@ impl<'d> Adc<'d, Async> { } } - pub async fn read(&mut self, pin: &mut Pin<'_>) -> Result { - Self::sample_async(pin.channel()).await + async fn read_many_inner( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [W], + fcs_err: bool, + dma: impl Peripheral

, + ) -> Result<(), Error> { + let r = Self::regs(); + // clear previous errors and set channel + r.cs().modify(|w| { + w.set_ainsel(ch.channel()); + w.set_err_sticky(true); // clear previous errors + w.set_start_many(false); + }); + // wait for previous conversions and drain fifo. an earlier batch read may have + // been cancelled, leaving the adc running. + while !r.cs().read().ready() {} + while !r.fcs().read().empty() { + r.fifo().read(); + } + + // set up fifo for dma + r.fcs().write(|w| { + w.set_thresh(1); + w.set_dreq_en(true); + w.set_shift(mem::size_of::() == 1); + w.set_en(true); + w.set_err(fcs_err); + }); + + // reset dma config on drop, regardless of whether it was a future being cancelled + // or the method returning normally. + struct ResetDmaConfig; + impl Drop for ResetDmaConfig { + fn drop(&mut self) { + pac::ADC.cs().write_clear(|w| w.set_start_many(true)); + while !pac::ADC.cs().read().ready() {} + pac::ADC.fcs().write_clear(|w| { + w.set_dreq_en(true); + w.set_shift(true); + w.set_en(true); + }); + } + } + let auto_reset = ResetDmaConfig; + + let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], 36) }; + // start conversions and wait for dma to finish. we can't report errors early + // because there's no interrupt to signal them, and inspecting every element + // of the fifo is too costly to do here. + r.cs().write_set(|w| w.set_start_many(true)); + dma.await; + mem::drop(auto_reset); + // we can't report errors before the conversions have ended since no interrupt + // exists to report them early, and since they're exceedingly rare we probably don't + // want to anyway. + match r.cs().read().err_sticky() { + false => Ok(()), + true => Err(Error::ConversionFailed), + } } - pub async fn read_temperature(&mut self) -> Result { - let r = Self::regs(); - r.cs().modify(|w| w.set_ts_en(true)); - if !r.cs().read().ready() { - Self::wait_for_ready().await; - } - let result = Self::sample_async(4).await; - r.cs().modify(|w| w.set_ts_en(false)); - result + #[inline] + pub async fn read_many( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [S], + dma: impl Peripheral

, + ) -> Result<(), Error> { + self.read_many_inner(ch, buf, false, dma).await + } + + #[inline] + pub async fn read_many_raw( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [Sample], + dma: impl Peripheral

, + ) { + // errors are reported in individual samples + let _ = self + .read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, dma) + .await; } } @@ -213,21 +315,26 @@ impl interrupt::typelevel::Handler for Inter } mod sealed { - pub trait AdcPin: crate::gpio::sealed::Pin { - fn channel(&mut self) -> u8; - } + pub trait AdcSample: crate::dma::Word {} + + pub trait AdcChannel {} } -pub trait AdcPin: sealed::AdcPin + gpio::Pin {} +pub trait AdcSample: sealed::AdcSample {} + +impl sealed::AdcSample for u16 {} +impl AdcSample for u16 {} + +impl sealed::AdcSample for u8 {} +impl AdcSample for u8 {} + +pub trait AdcChannel: sealed::AdcChannel {} +pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { ($pin:ident, $channel:expr) => { - impl sealed::AdcPin for peripherals::$pin { - fn channel(&mut self) -> u8 { - $channel - } - } - + impl sealed::AdcChannel for peripherals::$pin {} + impl AdcChannel for peripherals::$pin {} impl AdcPin for peripherals::$pin {} }; } @@ -236,3 +343,6 @@ impl_pin!(PIN_26, 0); impl_pin!(PIN_27, 1); impl_pin!(PIN_28, 2); impl_pin!(PIN_29, 3); + +impl sealed::AdcChannel for peripherals::ADC_TEMP_SENSOR {} +impl AdcChannel for peripherals::ADC_TEMP_SENSOR {} diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index acb21dce..7b25ecff 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,11 +1,13 @@ +use core::arch::asm; use core::marker::PhantomData; use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use pac::clocks::vals::*; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; +use crate::pac::common::{Reg, RW}; use crate::{pac, reset, Peripheral}; // NOTE: all gpin handling is commented out for future reference. @@ -702,7 +704,7 @@ impl<'d, T: Pin> Gpin<'d, T> { pub fn new(gpin: impl Peripheral

+ 'd) -> Gpin<'d, P> { into_ref!(gpin); - gpin.io().ctrl().write(|w| w.set_funcsel(0x08)); + gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08)); Gpin { gpin: gpin.map_into(), @@ -718,7 +720,7 @@ impl<'d, T: Pin> Gpin<'d, T> { impl<'d, T: Pin> Drop for Gpin<'d, T> { fn drop(&mut self) { self.gpin - .io() + .gpio() .ctrl() .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _)); } @@ -766,7 +768,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn new(gpout: impl Peripheral

+ 'd) -> Self { into_ref!(gpout); - gpout.io().ctrl().write(|w| w.set_funcsel(0x08)); + gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08)); Self { gpout } } @@ -831,7 +833,7 @@ impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { fn drop(&mut self) { self.disable(); self.gpout - .io() + .gpio() .ctrl() .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _)); } @@ -873,3 +875,131 @@ impl rand_core::RngCore for RoscRng { dest.fill_with(Self::next_u8) } } +/// Enter the `DORMANT` sleep state. This will stop *all* internal clocks +/// and can only be exited through resets, dormant-wake GPIO interrupts, +/// and RTC interrupts. If RTC is clocked from an internal clock source +/// it will be stopped and not function as a wakeup source. +#[cfg(target_arch = "arm")] +pub fn dormant_sleep() { + struct Set(Reg, T, F); + + impl Drop for Set { + fn drop(&mut self) { + self.0.write_value(self.1); + self.2(); + } + } + + fn set_with_post_restore After>( + reg: Reg, + f: F, + ) -> Set { + reg.modify(|w| { + let old = *w; + let after = f(w); + Set(reg, old, after) + }) + } + + fn set(reg: Reg, f: F) -> Set { + set_with_post_restore(reg, |r| { + f(r); + || () + }) + } + + // disable all clocks that are not vital in preparation for disabling clock sources. + // we'll keep gpout and rtc clocks untouched, gpout because we don't care about them + // and rtc because it's a possible wakeup source. if clk_rtc is not configured for + // gpin we'll never wake from rtc, but that's what the user asked for then. + let _stop_adc = set(pac::CLOCKS.clk_adc_ctrl(), |w| w.set_enable(false)); + let _stop_usb = set(pac::CLOCKS.clk_usb_ctrl(), |w| w.set_enable(false)); + let _stop_peri = set(pac::CLOCKS.clk_peri_ctrl(), |w| w.set_enable(false)); + // set up rosc. we could ask the use to tell us which clock source to wake from like + // the C SDK does, but that seems rather unfriendly. we *may* disturb rtc by changing + // rosc configuration if it's currently the rtc clock source, so we'll configure rosc + // to the slowest frequency to minimize that impact. + let _configure_rosc = ( + set(pac::ROSC.ctrl(), |w| { + w.set_enable(pac::rosc::vals::Enable::ENABLE); + w.set_freq_range(pac::rosc::vals::FreqRange::LOW); + }), + // div=32 + set(pac::ROSC.div(), |w| w.set_div(pac::rosc::vals::Div(0xaa0))), + ); + while !pac::ROSC.status().read().stable() {} + // switch over to rosc as the system clock source. this will change clock sources for + // watchdog and timer clocks, but timers won't be a concern and the watchdog won't + // speed up by enough to worry about (unless it's clocked from gpin, which we don't + // support anyway). + let _switch_clk_ref = set(pac::CLOCKS.clk_ref_ctrl(), |w| { + w.set_src(pac::clocks::vals::ClkRefCtrlSrc::ROSC_CLKSRC_PH); + }); + let _switch_clk_sys = set(pac::CLOCKS.clk_sys_ctrl(), |w| { + w.set_src(pac::clocks::vals::ClkSysCtrlSrc::CLK_REF); + }); + // oscillator dormancy does not power down plls, we have to do that ourselves. we'll + // restore them to their prior glory when woken though since the system may be clocked + // from either (and usb/adc will probably need the USB PLL anyway) + let _stop_pll_sys = set_with_post_restore(pac::PLL_SYS.pwr(), |w| { + let wake = !w.pd() && !w.vcopd(); + w.set_pd(true); + w.set_vcopd(true); + move || while wake && !pac::PLL_SYS.cs().read().lock() {} + }); + let _stop_pll_usb = set_with_post_restore(pac::PLL_USB.pwr(), |w| { + let wake = !w.pd() && !w.vcopd(); + w.set_pd(true); + w.set_vcopd(true); + move || while wake && !pac::PLL_USB.cs().read().lock() {} + }); + // dormancy only stops the oscillator we're telling to go dormant, the other remains + // running. nothing can use xosc at this point any more. not doing this costs an 200µA. + let _stop_xosc = set_with_post_restore(pac::XOSC.ctrl(), |w| { + let wake = w.enable() == pac::xosc::vals::Enable::ENABLE; + if wake { + w.set_enable(pac::xosc::vals::Enable::DISABLE); + } + move || while wake && !pac::XOSC.status().read().stable() {} + }); + let _power_down_xip_cache = set(pac::XIP_CTRL.ctrl(), |w| w.set_power_down(true)); + + // only power down memory if we're running from XIP (or ROM? how?). + // powering down memory otherwise would require a lot of exacting checks that + // are better done by the user in a local copy of this function. + // powering down memories saves ~100µA, so it's well worth doing. + unsafe { + let is_in_flash = { + // we can't rely on the address of this function as rust sees it since linker + // magic or even boot2 may place it into ram. + let pc: usize; + asm!( + "mov {pc}, pc", + pc = out (reg) pc + ); + pc < 0x20000000 + }; + if is_in_flash { + // we will be powering down memories, so we must be *absolutely* + // certain that we're running entirely from XIP and registers until + // memories are powered back up again. accessing memory that's powered + // down may corrupt memory contents (see section 2.11.4 of the manual). + // additionally a 20ns wait time is needed after powering up memories + // again. rosc is likely to run at only a few MHz at most, so the + // inter-instruction delay alone will be enough to satisfy this bound. + asm!( + "ldr {old_mem}, [{mempowerdown}]", + "str {power_down_mems}, [{mempowerdown}]", + "str {coma}, [{dormant}]", + "str {old_mem}, [{mempowerdown}]", + old_mem = out (reg) _, + mempowerdown = in (reg) pac::SYSCFG.mempowerdown().as_ptr(), + power_down_mems = in (reg) 0b11111111, + dormant = in (reg) pac::ROSC.dormant().as_ptr(), + coma = in (reg) 0x636f6d61, + ); + } else { + pac::ROSC.dormant().write_value(0x636f6d61); + } + } +} diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 1a458778..45ca21a7 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -4,7 +4,7 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma::vals::DataSize; @@ -76,7 +76,8 @@ pub unsafe fn write<'a, C: Channel, W: Word>( ) } -static DUMMY: u32 = 0; +// static mut so that this is allocated in RAM. +static mut DUMMY: u32 = 0; pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, @@ -86,7 +87,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ) -> Transfer<'a, C> { copy_inner( ch, - &DUMMY as *const u32, + &mut DUMMY as *const u32, to as *mut u32, len, W::size(), diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 96d2d454..1c1c2449 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,11 +1,15 @@ +use core::future::Future; use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; -use embassy_hal_common::Peripheral; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embedded_storage::nor_flash::{ check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; +use crate::dma::{AnyChannel, Channel, Transfer}; use crate::pac; use crate::peripherals::FLASH; @@ -24,6 +28,7 @@ pub const PAGE_SIZE: usize = 256; pub const WRITE_SIZE: usize = 1; pub const READ_SIZE: usize = 1; pub const ERASE_SIZE: usize = 4096; +pub const ASYNC_READ_SIZE: usize = 4; /// Error type for NVMC operations. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -57,14 +62,47 @@ impl NorFlashError for Error { } } -pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>); +/// Future that waits for completion of a background read +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct BackgroundRead<'a, 'd, T: Instance, const FLASH_SIZE: usize> { + flash: PhantomData<&'a mut Flash<'d, T, Async, FLASH_SIZE>>, + transfer: Transfer<'a, AnyChannel>, +} -impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { - pub fn new(_flash: impl Peripheral

+ 'd) -> Self { - Self(PhantomData) +impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Future for BackgroundRead<'a, 'd, T, FLASH_SIZE> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.transfer).poll(cx) } +} - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { +impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, 'd, T, FLASH_SIZE> { + fn drop(&mut self) { + if pac::XIP_CTRL.stream_ctr().read().0 == 0 { + return; + } + pac::XIP_CTRL + .stream_ctr() + .write_value(pac::xip_ctrl::regs::StreamCtr(0)); + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in + // flight that might effect an address written to start a new transfer. This stalls + // until after any transfer is complete, so the address will not change anymore. + const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _; + unsafe { + core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE); + } + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + } +} + +pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> { + dma: Option>, + phantom: PhantomData<(&'d mut T, M)>, +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> { + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { trace!( "Reading from 0x{:x} to 0x{:x}", FLASH_BASE as u32 + offset, @@ -82,7 +120,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { FLASH_SIZE } - pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { check_erase(self, from, to)?; trace!( @@ -98,7 +136,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { Ok(()) } - pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { check_write(self, offset, bytes.len())?; trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset); @@ -182,6 +220,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let ch = crate::pac::DMA.ch(n); while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {} } + // Wait for completion of any background reads + while pac::XIP_CTRL.stream_ctr().read().0 > 0 {} // Run our flash operation in RAM operation(); @@ -193,13 +233,13 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { } /// Read SPI flash unique ID - pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { + pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? }; Ok(()) } /// Read SPI flash JEDEC ID - pub fn jedec_id(&mut self) -> Result { + pub fn blocking_jedec_id(&mut self) -> Result { let mut jedec = None; unsafe { self.in_ram(|| { @@ -210,15 +250,117 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { } } -impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> { + pub fn new_blocking(_flash: impl Peripheral

+ 'd) -> Self { + Self { + dma: None, + phantom: PhantomData, + } + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { + pub fn new(_flash: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { + into_ref!(dma); + Self { + dma: Some(dma.map_into()), + phantom: PhantomData, + } + } + + pub fn background_read<'a>( + &'a mut self, + offset: u32, + data: &'a mut [u32], + ) -> Result, Error> { + trace!( + "Reading in background from 0x{:x} to 0x{:x}", + FLASH_BASE as u32 + offset, + FLASH_BASE as u32 + offset + (data.len() * 4) as u32 + ); + // Can't use check_read because we need to enforce 4-byte alignment + let offset = offset as usize; + let length = data.len() * 4; + if length > self.capacity() || offset > self.capacity() - length { + return Err(Error::OutOfBounds); + } + if offset % 4 != 0 { + return Err(Error::Unaligned); + } + + while !pac::XIP_CTRL.stat().read().fifo_empty() { + pac::XIP_CTRL.stream_fifo().read(); + } + + pac::XIP_CTRL + .stream_addr() + .write_value(pac::xip_ctrl::regs::StreamAddr(FLASH_BASE as u32 + offset as u32)); + pac::XIP_CTRL + .stream_ctr() + .write_value(pac::xip_ctrl::regs::StreamCtr(data.len() as u32)); + + // Use the XIP AUX bus port, rather than the FIFO register access (e.x. + // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on + // general XIP access. + const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _; + let transfer = unsafe { crate::dma::read(self.dma.as_mut().unwrap(), XIP_AUX_BASE, data, 37) }; + + Ok(BackgroundRead { + flash: PhantomData, + transfer, + }) + } + + pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + use core::mem::MaybeUninit; + + // Checked early to simplify address validity checks + if bytes.len() % 4 != 0 { + return Err(Error::Unaligned); + } + + // If the destination address is already aligned, then we can just DMA directly + if (bytes.as_ptr() as u32) % 4 == 0 { + // Safety: alignment and size have been checked for compatibility + let mut buf: &mut [u32] = + unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u32, bytes.len() / 4) }; + self.background_read(offset, &mut buf)?.await; + return Ok(()); + } + + // Destination address is unaligned, so use an intermediate buffer + const REALIGN_CHUNK: usize = PAGE_SIZE; + // Safety: MaybeUninit requires no initialization + let mut buf: [MaybeUninit; REALIGN_CHUNK / 4] = unsafe { MaybeUninit::uninit().assume_init() }; + let mut chunk_offset: usize = 0; + while chunk_offset < bytes.len() { + let chunk_size = (bytes.len() - chunk_offset).min(REALIGN_CHUNK); + let buf = &mut buf[..(chunk_size / 4)]; + + // Safety: this is written to completely by DMA before any reads + let buf = unsafe { &mut *(buf as *mut [MaybeUninit] as *mut [u32]) }; + self.background_read(offset + chunk_offset as u32, buf)?.await; + + // Safety: [u8] has more relaxed alignment and size requirements than [u32], so this is just aliasing + let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const _, buf.len() * 4) }; + bytes[chunk_offset..(chunk_offset + chunk_size)].copy_from_slice(&buf[..chunk_size]); + + chunk_offset += chunk_size; + } + + Ok(()) + } +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { type Error = Error; } -impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) + self.blocking_read(offset, bytes) } fn capacity(&self) -> usize { @@ -226,19 +368,51 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLA } } -impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} -impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) + self.blocking_erase(from, to) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) + self.blocking_write(offset, bytes) + } +} + +#[cfg(feature = "nightly")] +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ + const READ_SIZE: usize = ASYNC_READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes).await + } + + fn capacity(&self) -> usize { + self.capacity() + } +} + +#[cfg(feature = "nightly")] +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::NorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ + const WRITE_SIZE: usize = WRITE_SIZE; + + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) } } @@ -699,9 +873,24 @@ mod ram_helpers { mod sealed { pub trait Instance {} + pub trait Mode {} } pub trait Instance: sealed::Instance {} +pub trait Mode: sealed::Mode {} impl sealed::Instance for FLASH {} impl Instance for FLASH {} + +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Blocking; +pub struct Async; + +impl_mode!(Blocking); +impl_mode!(Async); diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a3d330cd..ad9d4262 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -3,7 +3,7 @@ use core::future::Future; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::InterruptExt; @@ -11,9 +11,13 @@ use crate::pac::common::{Reg, RW}; use crate::pac::SIO; use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; -const PIN_COUNT: usize = 30; const NEW_AW: AtomicWaker = AtomicWaker::new(); -static INTERRUPT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; +const BANK0_PIN_COUNT: usize = 30; +static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT]; +#[cfg(feature = "qspi-as-gpio")] +const QSPI_PIN_COUNT: usize = 6; +#[cfg(feature = "qspi-as-gpio")] +static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [NEW_AW; QSPI_PIN_COUNT]; /// Represents a digital input or output level. #[derive(Debug, Eq, PartialEq, Clone, Copy)] @@ -67,9 +71,19 @@ pub enum SlewRate { #[derive(Debug, Eq, PartialEq)] pub enum Bank { Bank0 = 0, + #[cfg(feature = "qspi-as-gpio")] Qspi = 1, } +#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DormantWakeConfig { + pub edge_high: bool, + pub edge_low: bool, + pub level_high: bool, + pub level_low: bool, +} + pub struct Input<'d, T: Pin> { pin: Flex<'d, T>, } @@ -123,6 +137,11 @@ impl<'d, T: Pin> Input<'d, T> { pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await; } + + #[inline] + pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { + self.pin.dormant_wake(cfg) + } } /// Interrupt trigger levels. @@ -140,17 +159,23 @@ pub(crate) unsafe fn init() { interrupt::IO_IRQ_BANK0.disable(); interrupt::IO_IRQ_BANK0.set_priority(interrupt::Priority::P3); interrupt::IO_IRQ_BANK0.enable(); + + #[cfg(feature = "qspi-as-gpio")] + { + interrupt::IO_IRQ_QSPI.disable(); + interrupt::IO_IRQ_QSPI.set_priority(interrupt::Priority::P3); + interrupt::IO_IRQ_QSPI.enable(); + } } #[cfg(feature = "rt")] -#[interrupt] -fn IO_IRQ_BANK0() { +fn irq_handler(bank: pac::io::Io, wakers: &[AtomicWaker; N]) { let cpu = SIO.cpuid().read() as usize; // There are two sets of interrupt registers, one for cpu0 and one for cpu1 // and here we are selecting the set that belongs to the currently executing // cpu. - let proc_intx: pac::io::Int = pac::IO_BANK0.int_proc(cpu); - for pin in 0..PIN_COUNT { + let proc_intx: pac::io::Int = bank.int_proc(cpu); + for pin in 0..N { // There are 4 raw interrupt status registers, PROCx_INTS0, PROCx_INTS1, // PROCx_INTS2, and PROCx_INTS3, and we are selecting the one that the // current pin belongs to. @@ -171,11 +196,23 @@ fn IO_IRQ_BANK0() { w.set_level_high(pin_group, true); w.set_level_low(pin_group, true); }); - INTERRUPT_WAKERS[pin as usize].wake(); + wakers[pin as usize].wake(); } } } +#[cfg(feature = "rt")] +#[interrupt] +fn IO_IRQ_BANK0() { + irq_handler(pac::IO_BANK0, &BANK0_WAKERS); +} + +#[cfg(all(feature = "rt", feature = "qspi-as-gpio"))] +#[interrupt] +fn IO_IRQ_QSPI() { + irq_handler(pac::IO_QSPI, &QSPI_WAKERS); +} + #[must_use = "futures do nothing unless you `.await` or poll them"] struct InputFuture<'a, T: Pin> { pin: PeripheralRef<'a, T>, @@ -194,7 +231,7 @@ impl<'d, T: Pin> InputFuture<'d, T> { // (the alternative being checking the current level and waiting for // its inverse, but that requires reading the current level and thus // missing anything that happened before the level was read.) - pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| { + pin.io().intr(pin.pin() as usize / 8).write(|w| { w.set_edge_high(pin_group, true); w.set_edge_low(pin_group, true); }); @@ -234,7 +271,12 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { // We need to register/re-register the waker for each poll because any // calls to wake will deregister the waker. - INTERRUPT_WAKERS[self.pin.pin() as usize].register(cx.waker()); + let waker = match self.pin.bank() { + Bank::Bank0 => &BANK0_WAKERS[self.pin.pin() as usize], + #[cfg(feature = "qspi-as-gpio")] + Bank::Qspi => &QSPI_WAKERS[self.pin.pin() as usize], + }; + waker.register(cx.waker()); // self.int_proc() will get the register offset for the current cpu, // then we want to access the interrupt enable register for our @@ -451,7 +493,7 @@ impl<'d, T: Pin> Flex<'d, T> { w.set_ie(true); }); - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _); }); @@ -611,15 +653,62 @@ impl<'d, T: Pin> Flex<'d, T> { pub async fn wait_for_any_edge(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await; } + + #[inline] + pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { + let idx = self.pin._pin() as usize; + self.pin.io().intr(idx / 8).write(|w| { + w.set_edge_high(idx % 8, cfg.edge_high); + w.set_edge_low(idx % 8, cfg.edge_low); + }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_set(|w| { + w.set_edge_high(idx % 8, cfg.edge_high); + w.set_edge_low(idx % 8, cfg.edge_low); + w.set_level_high(idx % 8, cfg.level_high); + w.set_level_low(idx % 8, cfg.level_low); + }); + DormantWake { + pin: self.pin.reborrow(), + cfg, + } + } } impl<'d, T: Pin> Drop for Flex<'d, T> { #[inline] fn drop(&mut self) { + let idx = self.pin._pin() as usize; self.pin.pad_ctrl().write(|_| {}); - self.pin.io().ctrl().write(|w| { + self.pin.gpio().ctrl().write(|w| { w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _); }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { + w.set_edge_high(idx % 8, true); + w.set_edge_low(idx % 8, true); + w.set_level_high(idx % 8, true); + w.set_level_low(idx % 8, true); + }); + } +} + +pub struct DormantWake<'w, T: Pin> { + pin: PeripheralRef<'w, T>, + cfg: DormantWakeConfig, +} + +impl<'w, T: Pin> Drop for DormantWake<'w, T> { + fn drop(&mut self) { + let idx = self.pin._pin() as usize; + self.pin.io().intr(idx / 8).write(|w| { + w.set_edge_high(idx % 8, self.cfg.edge_high); + w.set_edge_low(idx % 8, self.cfg.edge_low); + }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { + w.set_edge_high(idx % 8, true); + w.set_edge_low(idx % 8, true); + w.set_level_high(idx % 8, true); + w.set_level_low(idx % 8, true); + }); } } @@ -636,24 +725,29 @@ pub(crate) mod sealed { #[inline] fn _bank(&self) -> Bank { - if self.pin_bank() & 0x20 == 0 { - Bank::Bank0 - } else { - Bank::Qspi + match self.pin_bank() & 0x20 { + #[cfg(feature = "qspi-as-gpio")] + 1 => Bank::Qspi, + _ => Bank::Bank0, } } - fn io(&self) -> pac::io::Gpio { - let block = match self._bank() { + fn io(&self) -> pac::io::Io { + match self._bank() { Bank::Bank0 => crate::pac::IO_BANK0, + #[cfg(feature = "qspi-as-gpio")] Bank::Qspi => crate::pac::IO_QSPI, - }; - block.gpio(self._pin() as _) + } + } + + fn gpio(&self) -> pac::io::Gpio { + self.io().gpio(self._pin() as _) } fn pad_ctrl(&self) -> Reg { let block = match self._bank() { Bank::Bank0 => crate::pac::PADS_BANK0, + #[cfg(feature = "qspi-as-gpio")] Bank::Qspi => crate::pac::PADS_QSPI, }; block.gpio(self._pin() as _) @@ -672,12 +766,8 @@ pub(crate) mod sealed { } fn int_proc(&self) -> pac::io::Int { - let io_block = match self._bank() { - Bank::Bank0 => crate::pac::IO_BANK0, - Bank::Qspi => crate::pac::IO_QSPI, - }; let proc = SIO.cpuid().read(); - io_block.int_proc(proc as _) + self.io().int_proc(proc as _) } } } @@ -767,11 +857,17 @@ impl_pin!(PIN_27, Bank::Bank0, 27); impl_pin!(PIN_28, Bank::Bank0, 28); impl_pin!(PIN_29, Bank::Bank0, 29); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SCLK, Bank::Qspi, 0); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SS, Bank::Qspi, 1); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SD0, Bank::Qspi, 2); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SD1, Bank::Qspi, 3); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SD2, Bank::Qspi, 4); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SD3, Bank::Qspi, 5); // ==================== diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 791c6455..7a5ddd32 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -2,7 +2,7 @@ use core::future; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; @@ -353,8 +353,8 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { p.ic_rx_tl().write(|w| w.set_rx_tl(0)); // Configure SCL & SDA pins - scl.io().ctrl().write(|w| w.set_funcsel(3)); - sda.io().ctrl().write(|w| w.set_funcsel(3)); + 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); @@ -716,6 +716,9 @@ mod nightly { async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { let addr: u16 = address.into(); + if operations.len() > 0 { + Self::setup(addr)?; + } let mut iterator = operations.iter_mut(); while let Some(op) = iterator.next() { @@ -723,11 +726,9 @@ mod nightly { match op { Operation::Read(buffer) => { - Self::setup(addr)?; self.read_async_internal(buffer, false, last).await?; } Operation::Write(buffer) => { - Self::setup(addr)?; self.write_async_internal(buffer.into_iter().cloned(), last).await?; } } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 4f205a16..49bd3533 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -33,10 +33,10 @@ pub mod watchdog; // TODO: move `pio_instr_util` and `relocate` to inside `pio` pub mod pio; pub mod pio_instr_util; -pub mod relocate; +pub(crate) mod relocate; // Reexports -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use rp_pac as pac; #[cfg(not(feature = "unstable-pac"))] @@ -45,7 +45,7 @@ pub(crate) use rp_pac as pac; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( TIMER_IRQ_0, TIMER_IRQ_1, TIMER_IRQ_2, @@ -85,7 +85,7 @@ embassy_hal_common::interrupt_mod!( /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -107,7 +107,7 @@ macro_rules! bind_interrupts { }; } -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { PIN_0, PIN_1, PIN_2, @@ -183,6 +183,7 @@ embassy_hal_common::peripherals! { FLASH, ADC, + ADC_TEMP_SENSOR, CORE1, @@ -219,6 +220,74 @@ select_bootloader! { default => BOOT_LOADER_W25Q080 } +/// Installs a stack guard for the CORE0 stack in MPU region 0. +/// Will fail if the MPU is already confgigured. This function requires +/// a `_stack_end` symbol to be defined by the linker script, and expexcts +/// `_stack_end` to be located at the lowest address (largest depth) of +/// the stack. +/// +/// This method can *only* set up stack guards on the currently +/// executing core. Stack guards for CORE1 are set up automatically, +/// only CORE0 should ever use this. +/// +/// # Usage +/// +/// ```no_run +/// #![feature(type_alias_impl_trait)] +/// use embassy_rp::install_core0_stack_guard; +/// use embassy_executor::{Executor, Spawner}; +/// +/// #[embassy_executor::main] +/// async fn main(_spawner: Spawner) { +/// // set up by the linker as follows: +/// // +/// // MEMORY { +/// // STACK0: ORIGIN = 0x20040000, LENGTH = 4K +/// // } +/// // +/// // _stack_end = ORIGIN(STACK0); +/// // _stack_start = _stack_end + LENGTH(STACK0); +/// // +/// install_core0_stack_guard().expect("MPU already configured"); +/// let p = embassy_rp::init(Default::default()); +/// +/// // ... +/// } +/// ``` +pub fn install_core0_stack_guard() -> Result<(), ()> { + extern "C" { + static mut _stack_end: usize; + } + unsafe { install_stack_guard(&mut _stack_end as *mut usize) } +} + +#[inline(always)] +fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { + let core = unsafe { cortex_m::Peripherals::steal() }; + + // Fail if MPU is already configured + if core.MPU.ctrl.read() != 0 { + return Err(()); + } + + // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will + // just shorten the valid stack range a tad. + let addr = (stack_bottom as u32 + 31) & !31; + // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want + let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); + unsafe { + core.MPU.ctrl.write(5); // enable mpu with background default map + core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR + core.MPU.rasr.write( + 1 // enable region + | (0x7 << 1) // size 2^(7 + 1) = 256 + | (subregion_select << 8) + | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions + ); + } + Ok(()) +} + pub mod config { use crate::clocks::ClockConfig; diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 468e8470..91576180 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -52,41 +52,20 @@ use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use crate::interrupt::InterruptExt; use crate::peripherals::CORE1; -use crate::{gpio, interrupt, pac}; +use crate::{gpio, install_stack_guard, interrupt, pac}; const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); #[inline(always)] -fn install_stack_guard(stack_bottom: *mut usize) { - let core = unsafe { cortex_m::Peripherals::steal() }; - - // Trap if MPU is already configured - if core.MPU.ctrl.read() != 0 { +fn core1_setup(stack_bottom: *mut usize) { + if let Err(_) = install_stack_guard(stack_bottom) { + // currently only happens if the MPU was already set up, which + // would indicate that the core is already in use from outside + // embassy, somehow. trap if so since we can't deal with that. cortex_m::asm::udf(); } - - // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will - // just shorten the valid stack range a tad. - let addr = (stack_bottom as u32 + 31) & !31; - // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want - let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); - unsafe { - core.MPU.ctrl.write(5); // enable mpu with background default map - core.MPU.rbar.write((addr & !0xff) | 0x8); - core.MPU.rasr.write( - 1 // enable region - | (0x7 << 1) // size 2^(7 + 1) = 256 - | (subregion_select << 8) - | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions - ); - } -} - -#[inline(always)] -fn core1_setup(stack_bottom: *mut usize) { - install_stack_guard(stack_bottom); unsafe { gpio::init(); } diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 72a2f44e..c09d0914 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -5,13 +5,13 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::extra::U8; use fixed::FixedU32; use pac::io::vals::Gpio0ctrlFuncsel; use pac::pio::vals::SmExecctrlStatusSel; -use pio::{SideSet, Wrap}; +use pio::{Program, SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; @@ -734,23 +734,67 @@ pub struct InstanceMemory<'d, PIO: Instance> { pub struct LoadedProgram<'d, PIO: Instance> { pub used_memory: InstanceMemory<'d, PIO>, - origin: u8, - wrap: Wrap, - side_set: SideSet, + pub origin: u8, + pub wrap: Wrap, + pub side_set: SideSet, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum LoadError { + /// Insufficient consecutive free instruction space to load program. + InsufficientSpace, + /// Loading the program would overwrite an instruction address already + /// used by another program. + AddressInUse(usize), } impl<'d, PIO: Instance> Common<'d, PIO> { - pub fn load_program(&mut self, prog: &RelocatedProgram) -> LoadedProgram<'d, PIO> { + /// Load a PIO program. This will automatically relocate the program to + /// an available chunk of free instruction memory if the program origin + /// was not explicitly specified, otherwise it will attempt to load the + /// program only at its origin. + pub fn load_program(&mut self, prog: &Program) -> LoadedProgram<'d, PIO> { match self.try_load_program(prog) { Ok(r) => r, - Err(at) => panic!("Trying to write already used PIO instruction memory at {}", at), + Err(e) => panic!("Failed to load PIO program: {:?}", e), } } + /// Load a PIO program. This will automatically relocate the program to + /// an available chunk of free instruction memory if the program origin + /// was not explicitly specified, otherwise it will attempt to load the + /// program only at its origin. pub fn try_load_program( &mut self, - prog: &RelocatedProgram, + prog: &Program, + ) -> Result, LoadError> { + match prog.origin { + Some(origin) => self + .try_load_program_at(prog, origin) + .map_err(|a| LoadError::AddressInUse(a)), + None => { + // naively search for free space, allowing wraparound since + // PIO does support that. with only 32 instruction slots it + // doesn't make much sense to do anything more fancy. + let mut origin = 0; + while origin < 32 { + match self.try_load_program_at(prog, origin as _) { + Ok(r) => return Ok(r), + Err(a) => origin = a + 1, + } + } + Err(LoadError::InsufficientSpace) + } + } + } + + fn try_load_program_at( + &mut self, + prog: &Program, + origin: u8, ) -> Result, usize> { + let prog = RelocatedProgram::new_with_origin(prog, origin); let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?; Ok(LoadedProgram { used_memory, @@ -760,7 +804,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { }) } - pub fn try_write_instr(&mut self, start: usize, instrs: I) -> Result, usize> + fn try_write_instr(&mut self, start: usize, instrs: I) -> Result, usize> where I: Iterator, { @@ -808,7 +852,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { /// of [`Pio`] do not keep pin registrations alive.** pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); - pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); + pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); // we can be relaxed about this because we're &mut here and nothing is cached PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); Pin { diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 20bb8844..c297d69a 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -1,6 +1,6 @@ //! Pulse Width Modulation (PWM) -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use fixed::traits::ToFixed; use fixed::FixedU16; use pac::pwm::regs::{ChDiv, Intr}; @@ -79,10 +79,10 @@ impl<'d, T: Channel> Pwm<'d, T> { Self::configure(p, &config); if let Some(pin) = &a { - pin.io().ctrl().write(|w| w.set_funcsel(4)); + pin.gpio().ctrl().write(|w| w.set_funcsel(4)); } if let Some(pin) = &b { - pin.io().ctrl().write(|w| w.set_funcsel(4)); + pin.gpio().ctrl().write(|w| w.set_funcsel(4)); } Self { inner, @@ -243,10 +243,10 @@ impl<'d, T: Channel> Drop for Pwm<'d, T> { fn drop(&mut self) { self.inner.regs().csr().write_clear(|w| w.set_en(false)); if let Some(pin) = &self.pin_a { - pin.io().ctrl().write(|w| w.set_funcsel(31)); + pin.gpio().ctrl().write(|w| w.set_funcsel(31)); } if let Some(pin) = &self.pin_b { - pin.io().ctrl().write(|w| w.set_funcsel(31)); + pin.gpio().ctrl().write(|w| w.set_funcsel(31)); } } } diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index 9cb279cc..b35b4ed7 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs @@ -41,11 +41,6 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> { } impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { - pub fn new(program: &Program) -> RelocatedProgram { - let origin = program.origin.unwrap_or(0); - RelocatedProgram { program, origin } - } - pub fn new_with_origin(program: &Program, origin: u8) -> RelocatedProgram { RelocatedProgram { program, origin } } diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 805c1f09..baebe5b6 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -88,9 +88,8 @@ macro_rules! declare_rom_function { #[doc = stringify!($name)] #[doc = r"` ROM function."] pub mod $name { - /// Retrieve a function pointer. #[cfg(not(feature = "rom-func-cache"))] - pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + pub(crate) fn outer_call() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { let p: *const u32 = $lookup; unsafe { let func : $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret @@ -99,6 +98,12 @@ macro_rules! declare_rom_function { } } + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + outer_call() + } + #[cfg(feature = "rom-func-cache")] // unlike rp2040-hal we store a full word, containing the full function pointer. // rp2040-hal saves two bytes by storing only the rom offset, at the cost of @@ -119,9 +124,8 @@ macro_rules! declare_rom_function { } } - /// Retrieve a function pointer. #[cfg(feature = "rom-func-cache")] - pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + pub(crate) fn outer_call() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { use core::sync::atomic::{compiler_fence, Ordering}; // This is safe because the lookup will always resolve @@ -138,11 +142,37 @@ macro_rules! declare_rom_function { CACHE } } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{compiler_fence, Ordering}; + + // We can't just return the trampoline here because we need + // the actual resolved function address (e.x. flash operations + // can't reference a trampoline which itself is in flash). We + // can still utilize the cache, but we have to make sure it has + // been resolved already. Like the normal call path, we + // don't need anything stronger than fences because the + // final value always resolves to the same thing and SRAM + // itself is not cached. + compiler_fence(Ordering::Acquire); + #[allow(unused_unsafe)] + unsafe { + // ROM is 16kB in size at 0x0, so anything outside is cached + if CACHE as u32 >> 14 != 0 { + let p: *const u32 = $lookup; + CACHE = core::mem::transmute(p); + compiler_fence(Ordering::Release); + } + CACHE + } + } } $(#[$outer])* pub $( $maybe_unsafe )? extern "C" fn $name( $($argname: $ty),* ) -> $ret { - $name::ptr()($($argname),*) + $name::outer_call()($($argname),*) } }; } diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 1b33fdf8..60ca8627 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -1,6 +1,6 @@ mod filter; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; pub use self::filter::DateTimeFilter; diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index af101cf4..46c440b8 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; use crate::dma::{AnyChannel, Channel}; @@ -100,16 +100,16 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { p.cr1().write(|w| w.set_sse(true)); if let Some(pin) = &clk { - pin.io().ctrl().write(|w| w.set_funcsel(1)); + pin.gpio().ctrl().write(|w| w.set_funcsel(1)); } if let Some(pin) = &mosi { - pin.io().ctrl().write(|w| w.set_funcsel(1)); + pin.gpio().ctrl().write(|w| w.set_funcsel(1)); } if let Some(pin) = &miso { - pin.io().ctrl().write(|w| w.set_funcsel(1)); + pin.gpio().ctrl().write(|w| w.set_funcsel(1)); } if let Some(pin) = &cs { - pin.io().ctrl().write(|w| w.set_funcsel(1)); + pin.gpio().ctrl().write(|w| w.set_funcsel(1)); } Self { inner, diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 30eeb547..e57b7259 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -3,7 +3,7 @@ use core::slice; use core::task::Poll; use atomic_polyfill::{AtomicU8, Ordering}; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; @@ -574,31 +574,31 @@ impl embedded_io::Error for Error { } } -impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { +impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUart<'d, T> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Io for BufferedUartRx<'d, T> { +impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { +impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } -impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUart<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { BufferedUartRx::<'d, T>::read(buf).await } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUartRx<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { Self::read(buf).await } } -impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUart<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { BufferedUartRx::<'d, T>::fill_buf().await } @@ -608,7 +608,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } } -impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUartRx<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { Self::fill_buf().await } @@ -618,7 +618,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUart<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { BufferedUartTx::<'d, T>::write(buf).await } @@ -628,7 +628,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUartTx<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { Self::write(buf).await } @@ -638,19 +638,19 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> } } -impl<'d, T: Instance + 'd> embedded_io::blocking::Read for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUart<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { self.rx.blocking_read(buf) } } -impl<'d, T: Instance + 'd> embedded_io::blocking::Read for BufferedUartRx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUartRx<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { self.blocking_read(buf) } } -impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUart<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { self.tx.blocking_write(buf) } @@ -660,7 +660,7 @@ impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUart<'d, T> } } -impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUartTx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUartTx<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf) } @@ -749,15 +749,15 @@ mod eh02 { mod eh1 { use super::*; - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> { type Error = Error; } @@ -767,16 +767,6 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) @@ -793,16 +783,6 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7b94bce5..202b0883 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -4,7 +4,7 @@ use core::task::Poll; use atomic_polyfill::{AtomicU16, Ordering}; use embassy_futures::select::{select, Either}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; use pac::uart::regs::Uartris; @@ -565,7 +565,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { ) { let r = T::regs(); if let Some(pin) = &tx { - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(2); w.set_outover(if config.invert_tx { Outover::INVERT @@ -576,7 +576,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rx { - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(2); w.set_inover(if config.invert_rx { Inover::INVERT @@ -587,7 +587,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &cts { - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(2); w.set_inover(if config.invert_cts { Inover::INVERT @@ -598,7 +598,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rts { - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(2); w.set_outover(if config.invert_rts { Outover::INVERT @@ -807,26 +807,26 @@ mod eh02 { mod eh1 { use super::*; - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { + impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { match *self { - Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, - Self::Break => embedded_hal_1::serial::ErrorKind::Other, - Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, - Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, + Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Self::Break => embedded_hal_nb::serial::ErrorKind::Other, + Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, } } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartTx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> { type Error = Error; } @@ -851,16 +851,6 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for UartTx<'d, T, M> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) @@ -877,16 +867,6 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for Uart<'d, T, M> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 868bffe7..96c47484 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -15,8 +15,9 @@ 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-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } +embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" } embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true } defmt = { version = "0.3", optional = true } cortex-m = "0.7.6" @@ -25,14 +26,15 @@ aligned = "0.4.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -stm32wb-hci = { version = "0.1.3", optional = true } +stm32wb-hci = { version = "0.1.4", optional = true } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } bitflags = { version = "2.3.3", optional = true } [features] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] ble = ["dep:stm32wb-hci"] -mac = ["dep:bitflags"] +mac = ["dep:bitflags", "dep:embassy-net-driver" ] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 57f0dc4f..2a8ab725 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -1,13 +1,14 @@ #![no_std] -#![cfg_attr(feature = "ble", feature(async_fn_in_trait))] +#![cfg_attr(any(feature = "ble", feature = "mac"), feature(async_fn_in_trait))] +#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))] // This must go FIRST so that all the other modules see its macros. -pub mod fmt; +mod fmt; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32::peripherals::IPCC; diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index 8cfa0a05..8f6dcbbb 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -1,15 +1,16 @@ +use core::{mem, slice}; + use super::opcodes::OpcodeM4ToM0; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, PanId, PibId, ScanType, SecurityLevel, }; -pub trait MacCommand { +pub trait MacCommand: Sized { const OPCODE: OpcodeM4ToM0; - const SIZE: usize; - fn copy_into_slice(&self, buf: &mut [u8]) { - unsafe { core::ptr::copy(self as *const _ as *const u8, buf as *mut _ as *mut u8, Self::SIZE) }; + fn payload<'a>(&'a self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::()) } } } @@ -41,7 +42,6 @@ pub struct AssociateRequest { impl MacCommand for AssociateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq; - const SIZE: usize = 25; } /// MLME DISASSOCIATE Request sed to request a disassociation @@ -70,20 +70,22 @@ pub struct DisassociateRequest { impl MacCommand for DisassociateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq; - const SIZE: usize = 24; } /// MLME GET Request used to request a PIB value #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetRequest { /// the name of the PIB attribute to read pub pib_attribute: PibId, + + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for GetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; - const SIZE: usize = 4; } /// MLME GTS Request used to request and maintain GTSs @@ -104,19 +106,20 @@ pub struct GtsRequest { impl MacCommand for GtsRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; - const SIZE: usize = 12; } #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetRequest { /// MAC PIB attributes are set to their default values or not during reset pub set_default_pib: bool, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for ResetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq; - const SIZE: usize = 4; } /// MLME RX ENABLE Request used to request that the receiver is either enabled @@ -129,6 +132,8 @@ pub struct RxEnableRequest { /// configure the transceiver to RX with ranging for a value of /// RANGING_ON or to not enable ranging for RANGING_OFF pub ranging_rx_control: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], /// number of symbols measured before the receiver is to be enabled or disabled pub rx_on_time: [u8; 4], /// number of symbols for which the receiver is to be enabled @@ -137,19 +142,6 @@ pub struct RxEnableRequest { impl MacCommand for RxEnableRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq; - const SIZE: usize = 12; - - fn copy_into_slice(&self, buf: &mut [u8]) { - buf[0] = self.defer_permit as u8; - buf[1] = self.ranging_rx_control as u8; - - // stuffing to keep 32bit alignment - buf[2] = 0; - buf[3] = 0; - - buf[4..8].copy_from_slice(&self.rx_on_time); - buf[8..12].copy_from_slice(&self.rx_on_duration); - } } /// MLME SCAN Request used to initiate a channel scan over a given list of channels @@ -172,11 +164,12 @@ pub struct ScanRequest { pub key_id_mode: KeyIdMode, /// index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for ScanRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq; - const SIZE: usize = 20; } /// MLME SET Request used to attempt to write the given value to the indicated PIB attribute @@ -191,13 +184,12 @@ pub struct SetRequest { impl MacCommand for SetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq; - const SIZE: usize = 8; } /// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe /// configuration -#[derive(Default)] #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartRequest { /// PAN indentifier to used by the device @@ -236,7 +228,6 @@ pub struct StartRequest { impl MacCommand for StartRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq; - const SIZE: usize = 35; } /// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if @@ -253,11 +244,12 @@ pub struct SyncRequest { /// /// `false` if the MLME is to synchronize with only the next beacon pub track_beacon: bool, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 1], } impl MacCommand for SyncRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq; - const SIZE: usize = 4; } /// MLME POLL Request propmts the device to request data from the coordinator @@ -278,11 +270,12 @@ pub struct PollRequest { pub key_source: [u8; 8], /// PAN identifier of the coordinator pub coord_pan_id: PanId, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for PollRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq; - const SIZE: usize = 24; } /// MLME DPS Request allows the next higher layer to request that the PHY utilize a @@ -297,33 +290,38 @@ pub struct DpsRequest { /// the number of symbols for which the transmitter and receiver will utilize the /// respective DPS indices dps_index_duration: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 1], } impl MacCommand for DpsRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq; - const SIZE: usize = 4; } /// MLME SOUNDING request primitive which is used by the next higher layer to request that /// the PHY respond with channel sounding information #[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SoundingRequest; +pub struct SoundingRequest { + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 4], +} impl MacCommand for SoundingRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq; - const SIZE: usize = 4; } /// MLME CALIBRATE request primitive which used to obtain the results of a ranging /// calibration request from an RDEV #[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CalibrateRequest; +pub struct CalibrateRequest { + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 4], +} impl MacCommand for CalibrateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq; - const SIZE: usize = 4; } /// MCPS DATA Request used for MAC data related requests from the application @@ -370,6 +368,15 @@ pub struct DataRequest { pub datrate: u8, } +impl DataRequest { + pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &mut Self { + self.msdu_ptr = buf as *const _ as *const u8; + self.msdu_length = buf.len() as u8; + + self + } +} + impl Default for DataRequest { fn default() -> Self { Self { @@ -397,7 +404,6 @@ impl Default for DataRequest { impl MacCommand for DataRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq; - const SIZE: usize = 40; } /// for MCPS PURGE Request used to purge an MSDU from the transaction queue @@ -407,11 +413,12 @@ pub struct PurgeRequest { /// the handle associated with the MSDU to be purged from the transaction /// queue pub msdu_handle: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for PurgeRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq; - const SIZE: usize = 4; } /// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication @@ -434,11 +441,12 @@ pub struct AssociateResponse { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for AssociateResponse { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes; - const SIZE: usize = 24; } /// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication @@ -459,9 +467,10 @@ pub struct OrphanResponse { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for OrphanResponse { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes; - const SIZE: usize = 24; } diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs new file mode 100644 index 00000000..8a13de81 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -0,0 +1,95 @@ +use core::future::Future; +use core::task; +use core::task::Poll; + +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::MutexGuard; +use embassy_sync::signal::Signal; +use futures::FutureExt; + +use super::commands::MacCommand; +use super::event::MacEvent; +use super::typedefs::MacError; +use crate::mac::runner::Runner; + +pub struct Control<'a> { + runner: &'a Runner<'a>, +} + +impl<'a> Control<'a> { + pub(crate) fn new(runner: &'a Runner<'a>) -> Self { + Self { runner: runner } + } + + pub async fn send_command(&self, cmd: &T) -> Result<(), MacError> + where + T: MacCommand, + { + let _wm = self.runner.write_mutex.lock().await; + + self.runner.mac_subsystem.send_command(cmd).await + } + + pub async fn send_command_and_get_response(&self, cmd: &T) -> Result, MacError> + where + T: MacCommand, + { + let rm = self.runner.read_mutex.lock().await; + let _wm = self.runner.write_mutex.lock().await; + let token = EventToken::new(self.runner, rm); + + self.runner.mac_subsystem.send_command(cmd).await?; + + Ok(token) + } +} + +pub struct EventToken<'a> { + runner: &'a Runner<'a>, + _mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>, +} + +impl<'a> EventToken<'a> { + pub(crate) fn new(runner: &'a Runner<'a>, mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>) -> Self { + // Enable event receiving + runner.rx_event_channel.lock(|s| { + *s.borrow_mut() = Some(Signal::new()); + }); + + Self { + runner: runner, + _mutex_guard: mutex_guard, + } + } +} + +impl<'a> Future for EventToken<'a> { + type Output = MacEvent<'a>; + + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { + self.get_mut().runner.rx_event_channel.lock(|s| { + let signal = s.borrow_mut(); + let signal = match &*signal { + Some(s) => s, + _ => unreachable!(), + }; + + let result = match signal.wait().poll_unpin(cx) { + Poll::Ready(mac_event) => Poll::Ready(mac_event), + Poll::Pending => Poll::Pending, + }; + + result + }) + } +} + +impl<'a> Drop for EventToken<'a> { + fn drop(&mut self) { + // Disable event receiving + // This will also drop the contained event, if it exists, and will free up receiving the next event + self.runner.rx_event_channel.lock(|s| { + *s.borrow_mut() = None; + }); + } +} diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs new file mode 100644 index 00000000..bfc4f1ee --- /dev/null +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -0,0 +1,124 @@ +#![allow(incomplete_features)] +#![deny(unused_must_use)] + +use core::task::Context; + +use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; + +use crate::mac::event::MacEvent; +use crate::mac::runner::Runner; +use crate::mac::MTU; + +pub struct Driver<'d> { + runner: &'d Runner<'d>, +} + +impl<'d> Driver<'d> { + pub(crate) fn new(runner: &'d Runner<'d>) -> Self { + Self { runner: runner } + } +} + +impl<'d> embassy_net_driver::Driver for Driver<'d> { + // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; + type RxToken<'a> = RxToken<'d> where Self: 'a; + type TxToken<'a> = TxToken<'d> where Self: 'a; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready() + && self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() + { + Some(( + RxToken { + rx: &self.runner.rx_channel, + }, + TxToken { + tx: &self.runner.tx_channel, + tx_buf: &self.runner.tx_buf_channel, + }, + )) + } else { + None + } + } + + fn transmit(&mut self, cx: &mut Context) -> Option> { + if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { + Some(TxToken { + tx: &self.runner.tx_channel, + tx_buf: &self.runner.tx_buf_channel, + }) + } else { + None + } + } + + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + // caps.max_burst_size = Some(self.tx.len()); + + caps.medium = Medium::Ieee802154; + caps + } + + fn link_state(&mut self, _cx: &mut Context) -> LinkState { + // if self.phy.poll_link(&mut self.station_management, cx) { + // LinkState::Up + // } else { + // LinkState::Down + // } + + LinkState::Down + } + + fn hardware_address(&self) -> HardwareAddress { + // self.mac_addr + + HardwareAddress::Ieee802154([0; 8]) + } +} + +pub struct RxToken<'d> { + rx: &'d Channel, 1>, +} + +impl<'d> embassy_net_driver::RxToken for RxToken<'d> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // Only valid data events should be put into the queue + + let data_event = match self.rx.try_receive().unwrap() { + MacEvent::McpsDataInd(data_event) => data_event, + _ => unreachable!(), + }; + + f(&mut data_event.payload()) + } +} + +pub struct TxToken<'d> { + tx: &'d Channel, + tx_buf: &'d Channel, +} + +impl<'d> embassy_net_driver::TxToken for TxToken<'d> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // Only valid tx buffers should be put into the queue + let buf = self.tx_buf.try_receive().unwrap(); + let r = f(&mut buf[..len]); + + // The tx channel should always be of equal capacity to the tx_buf channel + self.tx.try_send((buf, len)).unwrap(); + + r + } +} diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index dfce21fe..9ca4f5a2 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,4 +1,5 @@ -use super::helpers::to_u16; +use core::{mem, ptr}; + use super::indications::{ AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication, @@ -7,88 +8,146 @@ use super::responses::{ AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm, PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm, }; +use crate::evt::{EvtBox, MemoryManager}; use crate::mac::opcodes::OpcodeM0ToM4; +use crate::sub::mac::{self, Mac}; -pub trait ParseableMacEvent { - const SIZE: usize; - - fn validate(buf: &[u8]) -> Result<(), ()> { - if buf.len() < Self::SIZE { - return Err(()); +pub(crate) trait ParseableMacEvent: Sized { + fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { + if buf.len() < mem::size_of::() { + Err(()) + } else { + Ok(unsafe { &*(buf as *const _ as *const Self) }) } - - Ok(()) } - - fn try_parse(buf: &[u8]) -> Result - where - Self: Sized; } #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum MacEvent { - MlmeAssociateCnf(AssociateConfirm), - MlmeDisassociateCnf(DisassociateConfirm), - MlmeGetCnf(GetConfirm), - MlmeGtsCnf(GtsConfirm), - MlmeResetCnf(ResetConfirm), - MlmeRxEnableCnf(RxEnableConfirm), - MlmeScanCnf(ScanConfirm), - MlmeSetCnf(SetConfirm), - MlmeStartCnf(StartConfirm), - MlmePollCnf(PollConfirm), - MlmeDpsCnf(DpsConfirm), - MlmeSoundingCnf(SoundingConfirm), - MlmeCalibrateCnf(CalibrateConfirm), - McpsDataCnf(DataConfirm), - McpsPurgeCnf(PurgeConfirm), - MlmeAssociateInd(AssociateIndication), - MlmeDisassociateInd(DisassociateIndication), - MlmeBeaconNotifyInd(BeaconNotifyIndication), - MlmeCommStatusInd(CommStatusIndication), - MlmeGtsInd(GtsIndication), - MlmeOrphanInd(OrphanIndication), - MlmeSyncLossInd(SyncLossIndication), - MlmeDpsInd(DpsIndication), - McpsDataInd(DataIndication), - MlmePollInd(PollIndication), +#[derive(Debug)] +pub enum MacEvent<'a> { + MlmeAssociateCnf(&'a AssociateConfirm), + MlmeDisassociateCnf(&'a DisassociateConfirm), + MlmeGetCnf(&'a GetConfirm), + MlmeGtsCnf(&'a GtsConfirm), + MlmeResetCnf(&'a ResetConfirm), + MlmeRxEnableCnf(&'a RxEnableConfirm), + MlmeScanCnf(&'a ScanConfirm), + MlmeSetCnf(&'a SetConfirm), + MlmeStartCnf(&'a StartConfirm), + MlmePollCnf(&'a PollConfirm), + MlmeDpsCnf(&'a DpsConfirm), + MlmeSoundingCnf(&'a SoundingConfirm), + MlmeCalibrateCnf(&'a CalibrateConfirm), + McpsDataCnf(&'a DataConfirm), + McpsPurgeCnf(&'a PurgeConfirm), + MlmeAssociateInd(&'a AssociateIndication), + MlmeDisassociateInd(&'a DisassociateIndication), + MlmeBeaconNotifyInd(&'a BeaconNotifyIndication), + MlmeCommStatusInd(&'a CommStatusIndication), + MlmeGtsInd(&'a GtsIndication), + MlmeOrphanInd(&'a OrphanIndication), + MlmeSyncLossInd(&'a SyncLossIndication), + MlmeDpsInd(&'a DpsIndication), + McpsDataInd(&'a DataIndication), + MlmePollInd(&'a PollIndication), } -impl TryFrom<&[u8]> for MacEvent { - type Error = (); +impl<'a> MacEvent<'a> { + pub(crate) fn new(event_box: EvtBox) -> Result { + let payload = event_box.payload(); + let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); - fn try_from(value: &[u8]) -> Result { - let opcode = to_u16(&value[0..2]); let opcode = OpcodeM0ToM4::try_from(opcode)?; + let buf = &payload[2..]; - let buf = &value[2..]; + // To avoid re-parsing the opcode, we store the result of the parse + // this requires use of unsafe because rust cannot assume that a reference will become + // invalid when the underlying result is moved. However, because we refer to a "heap" + // allocation, the underlying reference will not move until the struct is dropped. - match opcode { - OpcodeM0ToM4::MlmeAssociateCnf => Ok(Self::MlmeAssociateCnf(AssociateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDisassociateCnf => Ok(Self::MlmeDisassociateCnf(DisassociateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGetCnf => Ok(Self::MlmeGetCnf(GetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGtsCnf => Ok(Self::MlmeGtsCnf(GtsConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeResetCnf => Ok(Self::MlmeResetCnf(ResetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeRxEnableCnf => Ok(Self::MlmeRxEnableCnf(RxEnableConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeScanCnf => Ok(Self::MlmeScanCnf(ScanConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSetCnf => Ok(Self::MlmeSetCnf(SetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeStartCnf => Ok(Self::MlmeStartCnf(StartConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmePollCnf => Ok(Self::MlmePollCnf(PollConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDpsCnf => Ok(Self::MlmeDpsCnf(DpsConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSoundingCnf => Ok(Self::MlmeSoundingCnf(SoundingConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeCalibrateCnf => Ok(Self::MlmeCalibrateCnf(CalibrateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::McpsDataCnf => Ok(Self::McpsDataCnf(DataConfirm::try_parse(buf)?)), - OpcodeM0ToM4::McpsPurgeCnf => Ok(Self::McpsPurgeCnf(PurgeConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeAssociateInd => Ok(Self::MlmeAssociateInd(AssociateIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDisassociateInd => Ok(Self::MlmeDisassociateInd(DisassociateIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(Self::MlmeBeaconNotifyInd(BeaconNotifyIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeCommStatusInd => Ok(Self::MlmeCommStatusInd(CommStatusIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGtsInd => Ok(Self::MlmeGtsInd(GtsIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeOrphanInd => Ok(Self::MlmeOrphanInd(OrphanIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSyncLossInd => Ok(Self::MlmeSyncLossInd(SyncLossIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDpsInd => Ok(Self::MlmeDpsInd(DpsIndication::try_parse(buf)?)), - OpcodeM0ToM4::McpsDataInd => Ok(Self::McpsDataInd(DataIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmePollInd => Ok(Self::MlmePollInd(PollIndication::try_parse(buf)?)), - } + let mac_event = match opcode { + OpcodeM0ToM4::MlmeAssociateCnf => { + MacEvent::MlmeAssociateCnf(unsafe { &*(AssociateConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeDisassociateCnf => { + MacEvent::MlmeDisassociateCnf(unsafe { &*(DisassociateConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeGetCnf => MacEvent::MlmeGetCnf(unsafe { &*(GetConfirm::from_buffer(buf)? as *const _) }), + OpcodeM0ToM4::MlmeGtsCnf => MacEvent::MlmeGtsCnf(unsafe { &*(GtsConfirm::from_buffer(buf)? as *const _) }), + OpcodeM0ToM4::MlmeResetCnf => { + MacEvent::MlmeResetCnf(unsafe { &*(ResetConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeRxEnableCnf => { + MacEvent::MlmeRxEnableCnf(unsafe { &*(RxEnableConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeScanCnf => { + MacEvent::MlmeScanCnf(unsafe { &*(ScanConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeSetCnf => MacEvent::MlmeSetCnf(unsafe { &*(SetConfirm::from_buffer(buf)? as *const _) }), + OpcodeM0ToM4::MlmeStartCnf => { + MacEvent::MlmeStartCnf(unsafe { &*(StartConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmePollCnf => { + MacEvent::MlmePollCnf(unsafe { &*(PollConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeDpsCnf => MacEvent::MlmeDpsCnf(unsafe { &*(DpsConfirm::from_buffer(buf)? as *const _) }), + OpcodeM0ToM4::MlmeSoundingCnf => { + MacEvent::MlmeSoundingCnf(unsafe { &*(SoundingConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeCalibrateCnf => { + MacEvent::MlmeCalibrateCnf(unsafe { &*(CalibrateConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::McpsDataCnf => { + MacEvent::McpsDataCnf(unsafe { &*(DataConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::McpsPurgeCnf => { + MacEvent::McpsPurgeCnf(unsafe { &*(PurgeConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeAssociateInd => { + MacEvent::MlmeAssociateInd(unsafe { &*(AssociateIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeDisassociateInd => { + MacEvent::MlmeDisassociateInd(unsafe { &*(DisassociateIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeBeaconNotifyInd => { + MacEvent::MlmeBeaconNotifyInd(unsafe { &*(BeaconNotifyIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeCommStatusInd => { + MacEvent::MlmeCommStatusInd(unsafe { &*(CommStatusIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeGtsInd => { + MacEvent::MlmeGtsInd(unsafe { &*(GtsIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeOrphanInd => { + MacEvent::MlmeOrphanInd(unsafe { &*(OrphanIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeSyncLossInd => { + MacEvent::MlmeSyncLossInd(unsafe { &*(SyncLossIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeDpsInd => { + MacEvent::MlmeDpsInd(unsafe { &*(DpsIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::McpsDataInd => { + MacEvent::McpsDataInd(unsafe { &*(DataIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmePollInd => { + MacEvent::MlmePollInd(unsafe { &*(PollIndication::from_buffer(buf)? as *const _) }) + } + }; + + // Forget the event box so that drop isn't called + // We want to handle the lifetime ourselves + + mem::forget(event_box); + + Ok(mac_event) + } +} + +unsafe impl<'a> Send for MacEvent<'a> {} + +impl<'a> Drop for MacEvent<'a> { + fn drop(&mut self) { + unsafe { mac::Mac::drop_event_packet(ptr::null_mut()) }; } } diff --git a/embassy-stm32-wpan/src/mac/helpers.rs b/embassy-stm32-wpan/src/mac/helpers.rs deleted file mode 100644 index 5a5bf8a8..00000000 --- a/embassy-stm32-wpan/src/mac/helpers.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub fn to_u16(buf: &[u8]) -> u16 { - ((buf[1] as u16) << 8) | buf[0] as u16 -} - -pub fn to_u32(buf: &[u8]) -> u32 { - ((buf[0] as u32) << 0) + ((buf[1] as u32) << 8) + ((buf[2] as u32) << 16) + ((buf[3] as u32) << 24) -} diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 6df4aa23..c0b86d74 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -1,6 +1,7 @@ +use core::slice; + use super::consts::MAX_PENDING_ADDRESS; use super::event::ParseableMacEvent; -use super::helpers::to_u32; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, PanId, SecurityLevel, @@ -8,6 +9,8 @@ use super::typedefs::{ /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateIndication { /// Extended address of the device requesting association @@ -24,25 +27,12 @@ pub struct AssociateIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for AssociateIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - capability_information: Capabilities::from_bits(buf[8]).ok_or(())?, - security_level: SecurityLevel::try_from(buf[9])?, - key_id_mode: KeyIdMode::try_from(buf[10])?, - key_index: buf[11], - key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }) - } -} +impl ParseableMacEvent for AssociateIndication {} /// MLME DISASSOCIATE indication which will be used to send /// disassociation indication to the application. +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateIndication { /// Extended address of the device requesting association @@ -59,25 +49,12 @@ pub struct DisassociateIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for DisassociateIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - disassociation_reason: DisassociationReason::try_from(buf[8])?, - security_level: SecurityLevel::try_from(buf[9])?, - key_id_mode: KeyIdMode::try_from(buf[10])?, - key_index: buf[11], - key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }) - } -} +impl ParseableMacEvent for DisassociateIndication {} /// MLME BEACON NOTIIFY Indication which is used to send parameters contained /// within a beacon frame received by the MAC to the application +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BeaconNotifyIndication { /// he set of octets comprising the beacon payload to be transferred @@ -95,36 +72,11 @@ pub struct BeaconNotifyIndication { pub sdu_length: u8, } -impl ParseableMacEvent for BeaconNotifyIndication { - const SIZE: usize = 88; - - fn try_parse(buf: &[u8]) -> Result { - // TODO: this is unchecked - - Self::validate(buf)?; - - let addr_list = [ - MacAddress::try_from(&buf[26..34])?, - MacAddress::try_from(&buf[34..42])?, - MacAddress::try_from(&buf[42..50])?, - MacAddress::try_from(&buf[50..58])?, - MacAddress::try_from(&buf[58..66])?, - MacAddress::try_from(&buf[66..74])?, - MacAddress::try_from(&buf[74..82])?, - ]; - - Ok(Self { - sdu_ptr: to_u32(&buf[0..4]) as *const u8, - pan_descriptor: PanDescriptor::try_from(&buf[4..26])?, - addr_list, - bsn: buf[82], - pend_addr_spec: buf[83], - sdu_length: buf[83], - }) - } -} +impl ParseableMacEvent for BeaconNotifyIndication {} /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CommStatusIndication { /// The 16-bit PAN identifier of the device from which the frame @@ -150,54 +102,12 @@ pub struct CommStatusIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for CommStatusIndication { - const SIZE: usize = 32; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let src_addr_mode = AddressMode::try_from(buf[2])?; - let dst_addr_mode = AddressMode::try_from(buf[3])?; - - let src_address = match src_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[4], buf[5]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - }, - }; - - let dst_address = match dst_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[12], buf[13]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }, - }; - - Ok(Self { - pan_id: PanId([buf[0], buf[1]]), - src_addr_mode, - dst_addr_mode, - src_address, - dst_address, - status: MacStatus::try_from(buf[20])?, - security_level: SecurityLevel::try_from(buf[21])?, - key_id_mode: KeyIdMode::try_from(buf[22])?, - key_index: buf[23], - key_source: [buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31]], - }) - } -} +impl ParseableMacEvent for CommStatusIndication {} /// MLME GTS Indication indicates that a GTS has been allocated or that a /// previously allocated GTS has been deallocated +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsIndication { /// The short address of the device that has been allocated or deallocated a GTS @@ -210,30 +120,18 @@ pub struct GtsIndication { pub key_id_mode: KeyIdMode, /// Index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], /// Originator of the key to be used pub key_source: [u8; 8], } -impl ParseableMacEvent for GtsIndication { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1]], - gts_characteristics: buf[2], - security_level: SecurityLevel::try_from(buf[3])?, - key_id_mode: KeyIdMode::try_from(buf[4])?, - key_index: buf[5], - // 2 byte stuffing - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - }) - } -} +impl ParseableMacEvent for GtsIndication {} /// MLME ORPHAN Indication which is used by the coordinator to notify the /// application of the presence of an orphaned device +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OrphanIndication { /// Extended address of the orphaned device @@ -246,27 +144,16 @@ pub struct OrphanIndication { pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 1], } -impl ParseableMacEvent for OrphanIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - orphan_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - security_level: SecurityLevel::try_from(buf[16])?, - key_id_mode: KeyIdMode::try_from(buf[17])?, - key_index: buf[18], - // 1 byte stuffing - }) - } -} +impl ParseableMacEvent for OrphanIndication {} /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss /// of synchronization with the coordinator +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SyncLossIndication { /// The PAN identifier with which the device lost synchronization or to which it was realigned @@ -287,42 +174,23 @@ pub struct SyncLossIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for SyncLossIndication { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - pan_id: PanId([buf[0], buf[1]]), - loss_reason: buf[2], - channel_number: MacChannel::try_from(buf[3])?, - channel_page: buf[4], - security_level: SecurityLevel::try_from(buf[5])?, - key_id_mode: KeyIdMode::try_from(buf[6])?, - key_index: buf[7], - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - }) - } -} +impl ParseableMacEvent for SyncLossIndication {} /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration /// and the resetting of the DPS values in the PHY +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DpsIndication; - -impl ParseableMacEvent for DpsIndication { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self) - } +pub struct DpsIndication { + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 4], } +impl ParseableMacEvent for DpsIndication {} + +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C, align(8))] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated pub msdu_ptr: *const u8, @@ -347,9 +215,9 @@ pub struct DataIndication { /// The time, in symbols, at which the data were received pub time_stamp: [u8; 4], /// The security level purportedly used by the received data frame - pub security_level: SecurityLevel, + security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: KeyIdMode, + key_id_mode: KeyIdMode, /// The originator of the key pub key_source: [u8; 8], /// The index of the key @@ -374,68 +242,18 @@ pub struct DataIndication { pub rssi: u8, } -impl ParseableMacEvent for DataIndication { - const SIZE: usize = 68; +impl ParseableMacEvent for DataIndication {} - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let src_addr_mode = AddressMode::try_from(buf[4])?; - let src_address = match src_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[7], buf[8]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14]], - }, - }; - - let dst_addr_mode = AddressMode::try_from(buf[15])?; - let dst_address = match dst_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[18], buf[19]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25]], - }, - }; - - Ok(Self { - msdu_ptr: to_u32(&buf[0..4]) as *const u8, - src_addr_mode, - src_pan_id: PanId([buf[5], buf[6]]), - src_address, - dst_addr_mode, - dst_pan_id: PanId([buf[16], buf[17]]), - dst_address, - msdu_length: buf[26], - mpdu_link_quality: buf[27], - dsn: buf[28], - time_stamp: [buf[29], buf[30], buf[31], buf[32]], - security_level: SecurityLevel::try_from(buf[33]).unwrap_or(SecurityLevel::Unsecure), // TODO: this is totaly wrong, but I'm too smol brain to fix it - key_id_mode: KeyIdMode::try_from(buf[34]).unwrap_or(KeyIdMode::Implicite), // TODO: this is totaly wrong, but I'm too smol brain to fix it - key_source: [buf[35], buf[36], buf[37], buf[38], buf[39], buf[40], buf[41], buf[42]], - key_index: buf[43], - uwbprf: buf[44], - uwn_preamble_symbol_repetitions: buf[45], - datrate: buf[46], - ranging_received: buf[47], - ranging_counter_start: to_u32(&buf[48..52]), - ranging_counter_stop: to_u32(&buf[52..56]), - ranging_tracking_interval: to_u32(&buf[56..60]), - ranging_offset: to_u32(&buf[60..64]), - ranging_fom: buf[65], - rssi: buf[66], - }) +impl DataIndication { + pub fn payload<'a>(&'a self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.msdu_ptr as *mut _, self.msdu_length as usize) } } } /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollIndication { /// addressing mode used @@ -444,27 +262,4 @@ pub struct PollIndication { pub request_address: MacAddress, } -impl ParseableMacEvent for PollIndication { - const SIZE: usize = 9; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let addr_mode = AddressMode::try_from(buf[0])?; - let request_address = match addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[1], buf[2]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]], - }, - }; - - Ok(Self { - addr_mode, - request_address, - }) - } -} +impl ParseableMacEvent for PollIndication {} diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index 1af8fe6b..c847a5cc 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -1,9 +1,21 @@ pub mod commands; mod consts; +pub mod control; +mod driver; pub mod event; -mod helpers; pub mod indications; mod macros; mod opcodes; pub mod responses; +pub mod runner; pub mod typedefs; + +pub use crate::mac::control::Control; +use crate::mac::driver::Driver; +pub use crate::mac::runner::Runner; + +const MTU: usize = 127; + +pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) { + (Control::new(runner), Driver::new(runner)) +} diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index 2f6f5bf5..544fdaae 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -1,6 +1,5 @@ use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED}; use super::event::ParseableMacEvent; -use super::helpers::to_u32; use super::typedefs::{ AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType, SecurityLevel, @@ -8,6 +7,8 @@ use super::typedefs::{ /// MLME ASSOCIATE Confirm used to inform of the initiating device whether /// its request to associate was successful or unsuccessful +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateConfirm { /// short address allocated by the coordinator on successful association @@ -22,26 +23,15 @@ pub struct AssociateConfirm { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for AssociateConfirm { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - assoc_short_address: [buf[0], buf[1]], - status: AssociationStatus::try_from(buf[2])?, - security_level: SecurityLevel::try_from(buf[3])?, - key_source: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - key_id_mode: KeyIdMode::try_from(buf[12])?, - key_index: buf[13], - }) - } -} +impl ParseableMacEvent for AssociateConfirm {} /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateConfirm { /// status of the disassociation attempt @@ -54,34 +44,11 @@ pub struct DisassociateConfirm { pub device_address: MacAddress, } -impl ParseableMacEvent for DisassociateConfirm { - const SIZE: usize = 12; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let device_addr_mode = AddressMode::try_from(buf[1])?; - let device_address = match device_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[4], buf[5]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - }, - }; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - device_addr_mode, - device_pan_id: PanId([buf[2], buf[3]]), - device_address, - }) - } -} +impl ParseableMacEvent for DisassociateConfirm {} /// MLME GET Confirm which requests information about a given PIB attribute +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetConfirm { /// The pointer to the value of the PIB attribute attempted to read @@ -92,88 +59,58 @@ pub struct GetConfirm { pub pib_attribute: PibId, /// The lenght of the PIB attribute Value return pub pib_attribute_value_len: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 1], } -impl ParseableMacEvent for GetConfirm { - const SIZE: usize = 8; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let address = to_u32(&buf[0..4]); - - Ok(Self { - pib_attribute_value_ptr: address as *const u8, - status: MacStatus::try_from(buf[4])?, - pib_attribute: PibId::try_from(buf[5])?, - pib_attribute_value_len: buf[6], - }) - } -} +impl ParseableMacEvent for GetConfirm {} /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsConfirm { /// The characteristics of the GTS pub gts_characteristics: u8, /// The status of the GTS reques pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for GtsConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - gts_characteristics: buf[0], - status: MacStatus::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for GtsConfirm {} /// MLME RESET Confirm which is used to report the results of the reset operation +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation - status: MacStatus, + pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for ResetConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for ResetConfirm {} /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver - status: MacStatus, + pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for RxEnableConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for RxEnableConfirm {} /// MLME SCAN Confirm which is used to report the result of the channel scan request +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanConfirm { /// Status of the scan request @@ -196,150 +133,87 @@ pub struct ScanConfirm { pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], } -impl ParseableMacEvent for ScanConfirm { - const SIZE: usize = 185; - - fn try_parse(buf: &[u8]) -> Result { - // TODO: this is unchecked - - Self::validate(buf)?; - - let mut energy_detect_list = [0; MAX_ED_SCAN_RESULTS_SUPPORTED]; - energy_detect_list.copy_from_slice(&buf[8..24]); - - let pan_descriptor_list = [ - PanDescriptor::try_from(&buf[24..46])?, - PanDescriptor::try_from(&buf[46..68])?, - PanDescriptor::try_from(&buf[68..90])?, - PanDescriptor::try_from(&buf[90..102])?, - PanDescriptor::try_from(&buf[102..124])?, - PanDescriptor::try_from(&buf[124..146])?, - ]; - - let mut uwb_energy_detect_list = [0; MAX_ED_SCAN_RESULTS_SUPPORTED]; - uwb_energy_detect_list.copy_from_slice(&buf[147..163]); - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - scan_type: ScanType::try_from(buf[1])?, - channel_page: buf[2], - unscanned_channels: [buf[3], buf[4], buf[5], buf[6]], - result_list_size: buf[7], - energy_detect_list, - pan_descriptor_list, - detected_category: buf[146], - uwb_energy_detect_list, - }) - } -} +impl ParseableMacEvent for ScanConfirm {} /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SetConfirm { /// The result of the set operation pub status: MacStatus, /// The name of the PIB attribute that was written pub pin_attribute: PibId, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for SetConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - pin_attribute: PibId::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for SetConfirm {} /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for StartConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for StartConfirm {} /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollConfirm { /// The status of the data request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for PollConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for PollConfirm {} /// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsConfirm { /// The status of the DPS request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for DpsConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for DpsConfirm {} /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundingConfirm { /// Results of the sounding measurement - sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], + pub sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], + + status: u8, } -impl ParseableMacEvent for SoundingConfirm { - const SIZE: usize = 1; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let mut sounding_list = [0u8; MAX_SOUNDING_LIST_SUPPORTED]; - sounding_list[..buf.len()].copy_from_slice(buf); - - Ok(Self { sounding_list }) - } -} +impl ParseableMacEvent for SoundingConfirm {} /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CalibrateConfirm { /// The status of the attempt to return sounding data pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], /// A count of the propagation time from the ranging counter /// to the transmit antenna pub cal_tx_rmaker_offset: u32, @@ -348,23 +222,12 @@ pub struct CalibrateConfirm { pub cal_rx_rmaker_offset: u32, } -impl ParseableMacEvent for CalibrateConfirm { - const SIZE: usize = 12; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - // 3 byte stuffing - cal_tx_rmaker_offset: to_u32(&buf[4..8]), - cal_rx_rmaker_offset: to_u32(&buf[8..12]), - }) - } -} +impl ParseableMacEvent for CalibrateConfirm {} /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataConfirm { /// The handle associated with the MSDU being confirmed @@ -387,47 +250,24 @@ pub struct DataConfirm { pub ranging_offset: u32, /// The FoM characterizing the ranging measurement pub ranging_fom: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for DataConfirm { - const SIZE: usize = 28; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - msdu_handle: buf[0], - time_stamp: [buf[1], buf[2], buf[3], buf[4]], - ranging_received: buf[5], - status: MacStatus::try_from(buf[6])?, - ranging_counter_start: to_u32(&buf[7..11]), - ranging_counter_stop: to_u32(&buf[11..15]), - ranging_tracking_interval: to_u32(&buf[15..19]), - ranging_offset: to_u32(&buf[19..23]), - ranging_fom: buf[24], - }) - } -} +impl ParseableMacEvent for DataConfirm {} /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue +#[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PurgeConfirm { /// Handle associated with the MSDU requested to be purged from the transaction queue pub msdu_handle: u8, /// The status of the request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for PurgeConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - msdu_handle: buf[0], - status: MacStatus::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for PurgeConfirm {} diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs new file mode 100644 index 00000000..d3099b6b --- /dev/null +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -0,0 +1,109 @@ +use core::cell::RefCell; + +use embassy_futures::join; +use embassy_sync::blocking_mutex; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; +use embassy_sync::channel::Channel; +use embassy_sync::mutex::Mutex; +use embassy_sync::signal::Signal; + +use crate::mac::commands::DataRequest; +use crate::mac::event::MacEvent; +use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; +use crate::mac::MTU; +use crate::sub::mac::Mac; + +type ZeroCopyPubSub = blocking_mutex::Mutex>>>; + +pub struct Runner<'a> { + pub(crate) mac_subsystem: Mac, + // rx event backpressure is already provided through the MacEvent drop mechanism + // therefore, we don't need to worry about overwriting events + pub(crate) rx_event_channel: ZeroCopyPubSub>, + pub(crate) read_mutex: Mutex, + pub(crate) write_mutex: Mutex, + pub(crate) rx_channel: Channel, 1>, + pub(crate) tx_channel: Channel, + pub(crate) tx_buf_channel: Channel, +} + +impl<'a> Runner<'a> { + pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self { + let this = Self { + mac_subsystem: mac, + rx_event_channel: blocking_mutex::Mutex::new(RefCell::new(None)), + read_mutex: Mutex::new(()), + write_mutex: Mutex::new(()), + rx_channel: Channel::new(), + tx_channel: Channel::new(), + tx_buf_channel: Channel::new(), + }; + + for buf in tx_buf_queue { + this.tx_buf_channel.try_send(buf).unwrap(); + } + + this + } + + pub async fn run(&'a self) -> ! { + join::join( + async { + loop { + if let Ok(mac_event) = self.mac_subsystem.read().await { + match mac_event { + MacEvent::McpsDataInd(_) => { + self.rx_channel.send(mac_event).await; + } + _ => { + self.rx_event_channel.lock(|s| { + match &*s.borrow() { + Some(signal) => { + signal.signal(mac_event); + } + None => {} + }; + }); + } + } + } + } + }, + async { + let mut msdu_handle = 0x02; + + loop { + let (buf, len) = self.tx_channel.receive().await; + let _wm = self.write_mutex.lock().await; + + // The mutex should be dropped on the next loop iteration + self.mac_subsystem + .send_command( + DataRequest { + src_addr_mode: AddressMode::Short, + dst_addr_mode: AddressMode::Short, + dst_pan_id: PanId([0x1A, 0xAA]), + dst_address: MacAddress::BROADCAST, + msdu_handle: msdu_handle, + ack_tx: 0x00, + gts_tx: false, + security_level: SecurityLevel::Unsecure, + ..Default::default() + } + .set_buffer(&buf[..len]), + ) + .await + .unwrap(); + + msdu_handle = msdu_handle.wrapping_add(1); + + // The tx channel should always be of equal capacity to the tx_buf channel + self.tx_buf_channel.try_send(buf).unwrap(); + } + }, + ) + .await; + + loop {} + } +} diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs index 30c7731b..0552b8ea 100644 --- a/embassy-stm32-wpan/src/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/mac/typedefs.rs @@ -1,3 +1,5 @@ +use core::fmt::Debug; + use crate::numeric_enum; #[derive(Debug)] @@ -37,9 +39,11 @@ numeric_enum! { numeric_enum! { #[repr(u8)] /// this enum contains all the MAC PIB Ids + #[derive(Default, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PibId { // PHY + #[default] CurrentChannel = 0x00, ChannelsSupported = 0x01, TransmitPower = 0x02, @@ -94,7 +98,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy)] + #[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AddressMode { #[default] @@ -111,6 +115,18 @@ pub union MacAddress { pub extended: [u8; 8], } +impl Debug for MacAddress { + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + unsafe { + write!( + fmt, + "MacAddress {{ short: {:?}, extended: {:?} }}", + self.short, self.extended + ) + } + } +} + #[cfg(feature = "defmt")] impl defmt::Format for MacAddress { fn format(&self, fmt: defmt::Formatter) { @@ -157,7 +173,7 @@ pub struct GtsCharacteristics { /// MAC PAN Descriptor which contains the network details of the device from /// which the beacon is received -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PanDescriptor { /// PAN identifier of the coordinator @@ -221,7 +237,7 @@ impl TryFrom<&[u8]> for PanDescriptor { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy)] + #[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Building wireless applications with STM32WB series MCUs - Application note 13.10.3 pub enum MacChannel { @@ -287,7 +303,7 @@ defmt::bitflags! { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy)] + #[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum KeyIdMode { #[default] @@ -304,6 +320,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AssociationStatus { /// Association successful @@ -317,7 +334,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] - #[derive(Clone, Copy)] + #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DisassociationReason { /// The coordinator wishes the device to leave the PAN. @@ -329,7 +346,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy)] + #[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SecurityLevel { /// MAC Unsecured Mode Security @@ -344,6 +361,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ScanType { EdScan = 0x00, @@ -354,7 +372,7 @@ numeric_enum! { } /// newtype for Pan Id -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PanId(pub [u8; 2]); diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index 4893cb47..b0cf0248 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -85,12 +85,7 @@ impl Mac { where T: MacCommand, { - let mut payload = [0u8; MAX_PACKET_SIZE]; - cmd.copy_into_slice(&mut payload); - - let response = self - .tl_write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]) - .await; + let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await; if response == 0x00 { Ok(()) @@ -99,19 +94,16 @@ impl Mac { } } - pub async fn read(&self) -> Result { - let evt_box = self.tl_read().await; - let payload = evt_box.payload(); - - MacEvent::try_from(payload) + pub async fn read(&self) -> Result, ()> { + MacEvent::new(self.tl_read().await) } } -const MAX_PACKET_SIZE: usize = 255; - impl evt::MemoryManager for Mac { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(_: *mut EvtPacket) { + trace!("mac drop event"); + // Write the ack CmdPacket::write_into( MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _, @@ -121,7 +113,7 @@ impl evt::MemoryManager for Mac { ); // Clear the rx flag - let _ = poll_once(Ipcc::receive::( + let _ = poll_once(Ipcc::receive::<()>( channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || None, )); diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ec934e8b..150014af 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -8,7 +8,7 @@ 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"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time", "low-power"] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, @@ -34,15 +34,16 @@ flavors = [ embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-4"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } +embassy-executor = { version = "0.3.0", path = "../embassy-executor", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } @@ -57,16 +58,18 @@ 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-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" -embedded-io = { version = "0.4.0", features = ["async"], optional = true } +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0", optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" +document-features = "0.2.7" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -74,44 +77,69 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "12", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7", default-features = false, features = ["metadata"]} [features] default = ["rt"] + +## Enable `stm32-metapac`'s `rt` feature rt = ["stm32-metapac/rt"] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] -memory-x = ["stm32-metapac/memory-x"] -exti = [] +## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] -# Enables additional driver features that depend on embassy-time +exti = [] +low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ] +embassy-executor = [] + +## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) +memory-x = ["stm32-metapac/memory-x"] + +## Enable nightly-only features +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io-async", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] + +## Re-export stm32-metapac at `embassy_stm32::pac`. +## This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. +## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. +## There are no plans to make this stable. +unstable-pac = [] + +## Implement embedded-hal 1.0 alpha traits. +## Implement embedded-hal-async traits if `nightly` is set as well. +unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] + +#! ## Time + +## Enables additional driver features that depend on embassy-time time = ["dep:embassy-time"] # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. _time-driver = ["time"] + +## Use any time driver time-driver-any = ["_time-driver"] +## Use TIM2 as time driver time-driver-tim2 = ["_time-driver"] +## Use TIM3 as time driver time-driver-tim3 = ["_time-driver"] +## Use TIM4 as time driver time-driver-tim4 = ["_time-driver"] +## Use TIM5 as time driver time-driver-tim5 = ["_time-driver"] +## Use TIM12 as time driver time-driver-tim12 = ["_time-driver"] +## Use TIM15 as time driver time-driver-tim15 = ["_time-driver"] -# Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] -# Reexport stm32-metapac at `embassy_stm32::pac`. -# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. -# If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. -# There are no plans to make this stable. -unstable-pac = [] +#! ## Chip-selection features +#! Select your chip by specifying the model as a feature, e.g. `stm32c011d6`. +#! Check the `Cargo.toml` for the latest list of supported chips. +#! +#! **Important:** Do not forget to adapt the target chip in your toolchain, +#! e.g. in `.cargo/config.toml`. -# Implement embedded-hal 1.0 alpha traits. -# Implement embedded-hal-async traits if `nightly` is set as well. -unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] - -# Chip-selection features stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] @@ -1445,4 +1473,4 @@ stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ] stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] -stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] \ No newline at end of file +stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 995ad144..6c364f7b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -126,7 +126,7 @@ fn main() { _ => panic!("unknown time_driver {:?}", time_driver), }; - if time_driver_singleton != "" { + if !time_driver_singleton.is_empty() { println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase()); } @@ -138,7 +138,7 @@ fn main() { let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect(); g.extend(quote! { - embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*); + embassy_hal_internal::peripherals_definition!(#(#singleton_tokens),*); }); let singleton_tokens: Vec<_> = singletons @@ -148,7 +148,7 @@ fn main() { .collect(); g.extend(quote! { - embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*); + embassy_hal_internal::peripherals_struct!(#(#singleton_tokens),*); }); // ======== @@ -160,7 +160,7 @@ fn main() { } g.extend(quote! { - embassy_hal_common::interrupt_mod!( + embassy_hal_internal::interrupt_mod!( #( #irqs, )* @@ -211,7 +211,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { #[cfg(flash)] - pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); + pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); }); } @@ -243,7 +243,7 @@ fn main() { #[cfg(flash)] impl<'d, MODE> FlashLayout<'d, MODE> { - pub(crate) fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { + pub(crate) fn new(p: embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { #(#inits),*, _mode: core::marker::PhantomData, @@ -310,7 +310,11 @@ fn main() { 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")) { + 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")) + { continue; } @@ -352,6 +356,8 @@ fn main() { } fn enable() { critical_section::with(|_| { + #[cfg(feature = "low-power")] + crate::rcc::clock_refcount_add(); crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); #after_enable }) @@ -359,6 +365,8 @@ fn main() { fn disable() { critical_section::with(|_| { crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); + #[cfg(feature = "low-power")] + crate::rcc::clock_refcount_sub(); }) } fn reset() { @@ -572,21 +580,33 @@ fn main() { (("fmc", "Clk"), quote!(crate::fmc::ClkPin)), (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), - (("timer", "CH1"), quote!(crate::pwm::Channel1Pin)), - (("timer", "CH1N"), quote!(crate::pwm::Channel1ComplementaryPin)), - (("timer", "CH2"), quote!(crate::pwm::Channel2Pin)), - (("timer", "CH2N"), quote!(crate::pwm::Channel2ComplementaryPin)), - (("timer", "CH3"), quote!(crate::pwm::Channel3Pin)), - (("timer", "CH3N"), quote!(crate::pwm::Channel3ComplementaryPin)), - (("timer", "CH4"), quote!(crate::pwm::Channel4Pin)), - (("timer", "CH4N"), quote!(crate::pwm::Channel4ComplementaryPin)), - (("timer", "ETR"), quote!(crate::pwm::ExternalTriggerPin)), - (("timer", "BKIN"), quote!(crate::pwm::BreakInputPin)), - (("timer", "BKIN_COMP1"), quote!(crate::pwm::BreakInputComparator1Pin)), - (("timer", "BKIN_COMP2"), quote!(crate::pwm::BreakInputComparator2Pin)), - (("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)), - (("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)), - (("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)), + (("timer", "CH1"), quote!(crate::timer::Channel1Pin)), + (("timer", "CH1N"), quote!(crate::timer::Channel1ComplementaryPin)), + (("timer", "CH2"), quote!(crate::timer::Channel2Pin)), + (("timer", "CH2N"), quote!(crate::timer::Channel2ComplementaryPin)), + (("timer", "CH3"), quote!(crate::timer::Channel3Pin)), + (("timer", "CH3N"), quote!(crate::timer::Channel3ComplementaryPin)), + (("timer", "CH4"), quote!(crate::timer::Channel4Pin)), + (("timer", "CH4N"), quote!(crate::timer::Channel4ComplementaryPin)), + (("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)), + (("timer", "BKIN"), quote!(crate::timer::BreakInputPin)), + (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), + (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), + (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)), + (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), + (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), + (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), + (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), + (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), + (("hrtim", "CHB2"), quote!(crate::hrtim::ChannelBComplementaryPin)), + (("hrtim", "CHC1"), quote!(crate::hrtim::ChannelCPin)), + (("hrtim", "CHC2"), quote!(crate::hrtim::ChannelCComplementaryPin)), + (("hrtim", "CHD1"), quote!(crate::hrtim::ChannelDPin)), + (("hrtim", "CHD2"), quote!(crate::hrtim::ChannelDComplementaryPin)), + (("hrtim", "CHE1"), quote!(crate::hrtim::ChannelEPin)), + (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), + (("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)), + (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), @@ -622,7 +642,7 @@ fn main() { || regs.version.starts_with("h7") || regs.version.starts_with("f4") { - peri = format_ident!("{}", pin.signal.replace("_", "")); + peri = format_ident!("{}", pin.signal.replace('_', "")); } else { continue; } @@ -762,10 +782,11 @@ fn main() { .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { let settings = m.settings.as_ref().unwrap(); - let mut row = Vec::new(); - row.push(get_flash_region_type_name(m.name)); - row.push(settings.write_size.to_string()); - row.push(settings.erase_size.to_string()); + let row = vec![ + get_flash_region_type_name(m.name), + settings.write_size.to_string(), + settings.erase_size.to_string(), + ]; flash_regions_table.push(row); } @@ -775,7 +796,7 @@ fn main() { for p in METADATA.peripherals { if let Some(regs) = &p.registers { if regs.kind == "gpio" { - let port_letter = p.name.chars().skip(4).next().unwrap(); + let port_letter = p.name.chars().nth(4).unwrap(); assert_eq!(0, (p.address as u32 - gpio_base) % gpio_stride); let port_num = (p.address as u32 - gpio_base) / gpio_stride; @@ -792,18 +813,17 @@ fn main() { } for irq in p.interrupts { - let mut row = Vec::new(); - row.push(p.name.to_string()); - row.push(regs.kind.to_string()); - row.push(regs.block.to_string()); - row.push(irq.signal.to_string()); - row.push(irq.interrupt.to_ascii_uppercase()); + let row = vec![ + p.name.to_string(), + regs.kind.to_string(), + regs.block.to_string(), + irq.signal.to_string(), + irq.interrupt.to_ascii_uppercase(), + ]; interrupts_table.push(row) } - let mut row = Vec::new(); - row.push(regs.kind.to_string()); - row.push(p.name.to_string()); + let row = vec![regs.kind.to_string(), p.name.to_string()]; peripherals_table.push(row); } } @@ -963,7 +983,7 @@ macro_rules! {} {{ .unwrap(); for row in data { - write!(out, " __{}_inner!(({}));\n", name, row.join(",")).unwrap(); + writeln!(out, " __{}_inner!(({}));", name, row.join(",")).unwrap(); } write!( @@ -987,5 +1007,5 @@ fn get_flash_region_type_name(name: &str) -> String { get_flash_region_name(name) .replace("BANK", "Bank") .replace("REGION", "Region") - .replace("_", "") + .replace('_', "") } diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 2322204d..e577ec28 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, SampleTime}; diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 56ecd63c..e57889aa 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,5 +1,6 @@ #![macro_use] +#[cfg(not(any(adc_f3, adc_f3_v2)))] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] @@ -7,14 +8,16 @@ #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] mod resolution; mod sample_time; +#[cfg(not(any(adc_f3, adc_f3_v2)))] #[allow(unused)] pub use _version::*; -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] pub use resolution::Resolution; +#[cfg(not(any(adc_f3, adc_f3_v2)))] pub use sample_time::SampleTime; use crate::peripherals; @@ -22,13 +25,14 @@ use crate::peripherals; pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, + #[cfg(not(any(adc_f3, adc_f3_v2)))] sample_time: SampleTime, } pub(crate) mod sealed { pub trait Instance { fn regs() -> crate::pac::adc::Adc; - #[cfg(all(not(adc_f1), not(adc_v1)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] fn common_regs() -> crate::pac::adccommon::AdcCommon; } @@ -56,7 +60,7 @@ foreach_peripheral!( fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } - #[cfg(all(not(adc_f1), not(adc_v1)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 0faa1e3c..5480e7a7 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -1,3 +1,4 @@ +#[cfg(not(any(adc_f3, 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.")] diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index d9af0c55..e8245884 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 091c1d44..9a7acea5 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 3a6e58cf..821cc7f6 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index c51c6840..64d0f0c7 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -226,7 +226,7 @@ impl Prescaler { impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - embassy_hal_common::into_ref!(adc); + embassy_hal_internal::into_ref!(adc); T::enable(); T::reset(); diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 5a015346..7ad13cec 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -6,7 +6,7 @@ use core::task::Poll; pub use bxcan; use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use futures::FutureExt; use crate::gpio::sealed::AFType; @@ -16,6 +16,17 @@ use crate::rcc::RccPeripheral; use crate::time::Hertz; use crate::{interrupt, peripherals, Peripheral}; +/// Contains CAN frame and additional metadata. +/// +/// Timestamp is available if `time` feature is enabled. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Envelope { + #[cfg(feature = "time")] + pub ts: embassy_time::Instant, + pub frame: bxcan::Frame, +} + /// Interrupt handler. pub struct TxInterruptHandler { _phantom: PhantomData, @@ -77,6 +88,7 @@ pub struct Can<'d, T: Instance> { } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BusError { Stuff, Form, @@ -90,6 +102,22 @@ pub enum BusError { BusWarning, } +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryReadError { + /// Bus error + BusError(BusError), + /// Receive buffer is empty + Empty, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryWriteError { + /// All transmit mailboxes are full + Full, +} + impl<'d, T: Instance> Can<'d, T> { /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. /// You must call [Can::enable_non_blocking] to use the peripheral. @@ -161,61 +189,67 @@ impl<'d, T: Instance> Can<'d, T> { .leave_disabled(); } - /// Queues the message to be sent but exerts backpressure - pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { - poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); - if let Ok(status) = self.can.borrow_mut().transmit(frame) { - return Poll::Ready(status); - } - - Poll::Pending - }) - .await + /// Enables the peripheral and synchronizes with the bus. + /// + /// This will wait for 11 consecutive recessive bits (bus idle state). + /// Contrary to enable method from bxcan library, this will not freeze the executor while waiting. + pub async fn enable(&mut self) { + while self.borrow_mut().enable_non_blocking().is_err() { + // SCE interrupt is only generated for entering sleep mode, but not leaving. + // Yield to allow other tasks to execute while can bus is initializing. + embassy_futures::yield_now().await; + } } - pub async fn flush(&self, mb: bxcan::Mailbox) { - poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); - if T::regs().tsr().read().tme(mb.index()) { - return Poll::Ready(()); - } + /// Queues the message to be sent but exerts backpressure + pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { + CanTx { can: &self.can }.write(frame).await + } - Poll::Pending - }) - .await; + /// Attempts to transmit a frame without blocking. + /// + /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. + pub fn try_write(&mut self, frame: &Frame) -> Result { + CanTx { can: &self.can }.try_write(frame) + } + + /// Waits for a specific transmit mailbox to become empty + pub async fn flush(&self, mb: bxcan::Mailbox) { + CanTx { can: &self.can }.flush(mb).await + } + + /// Waits until any of the transmit mailboxes become empty + pub async fn flush_any(&self) { + CanTx { can: &self.can }.flush_any().await + } + + /// Waits until all of the transmit mailboxes become empty + pub async fn flush_all(&self) { + CanTx { can: &self.can }.flush_all().await } /// Returns a tuple of the time the message was received and the message frame - pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { - poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); - if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { - return Poll::Ready(Ok((time, frame))); - } else if let Some(err) = self.curr_error() { - return Poll::Ready(Err(err)); - } - - Poll::Pending - }) - .await + pub async fn read(&mut self) -> Result { + CanRx { can: &self.can }.read().await } - fn curr_error(&self) -> Option { - let err = { T::regs().esr().read() }; - if err.boff() { - return Some(BusError::BusOff); - } else if err.epvf() { - return Some(BusError::BusPassive); - } else if err.ewgf() { - return Some(BusError::BusWarning); - } else if let Some(err) = err.lec().into_bus_err() { - return Some(err); - } - None + /// Attempts to read a can frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result { + CanRx { can: &self.can }.try_read() + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + CanRx { can: &self.can }.wait_not_empty().await } unsafe fn receive_fifo(fifo: RxFifo) { + // Generate timestamp as early as possible + #[cfg(feature = "time")] + let ts = embassy_time::Instant::now(); + let state = T::state(); let regs = T::regs(); let fifo_idx = match fifo { @@ -237,7 +271,7 @@ impl<'d, T: Instance> Can<'d, T> { } else { let stid = (rir.stid() & 0x7FF) as u32; let exid = rir.exid() & 0x3FFFF; - let id = (stid << 18) | (exid as u32); + let id = (stid << 18) | (exid); Id::from(ExtendedId::new_unchecked(id)) }; let data_len = fifo.rdtr().read().dlc() as usize; @@ -245,15 +279,19 @@ impl<'d, T: Instance> Can<'d, T> { data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); - let time = fifo.rdtr().read().time(); let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); + let envelope = Envelope { + #[cfg(feature = "time")] + ts, + frame, + }; rfr.modify(|v| v.set_rfom(true)); /* NOTE: consensus was reached that if rx_queue is full, packets should be dropped */ - let _ = state.rx_queue.try_send((time, frame)); + let _ = state.rx_queue.try_send(envelope); } } @@ -345,7 +383,7 @@ impl<'d, T: Instance> Can<'d, T> { let sjw = 1; // Pack into BTR register values - Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1)) + Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler - 1)) } pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) { @@ -374,6 +412,14 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { .await } + /// Attempts to transmit a frame without blocking. + /// + /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. + pub fn try_write(&mut self, frame: &Frame) -> Result { + self.can.borrow_mut().transmit(frame).map_err(|_| TryWriteError::Full) + } + + /// Waits for a specific transmit mailbox to become empty pub async fn flush(&self, mb: bxcan::Mailbox) { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); @@ -385,6 +431,42 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { }) .await; } + + /// Waits until any of the transmit mailboxes become empty + pub async fn flush_any(&self) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let tsr = T::regs().tsr().read(); + if tsr.tme(bxcan::Mailbox::Mailbox0.index()) + || tsr.tme(bxcan::Mailbox::Mailbox1.index()) + || tsr.tme(bxcan::Mailbox::Mailbox2.index()) + { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } + + /// Waits until all of the transmit mailboxes become empty + pub async fn flush_all(&self) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let tsr = T::regs().tsr().read(); + if tsr.tme(bxcan::Mailbox::Mailbox0.index()) + && tsr.tme(bxcan::Mailbox::Mailbox1.index()) + && tsr.tme(bxcan::Mailbox::Mailbox2.index()) + { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } } #[allow(dead_code)] @@ -393,11 +475,11 @@ pub struct CanRx<'c, 'd, T: Instance> { } impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { - pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { + pub async fn read(&mut self) -> Result { poll_fn(|cx| { T::state().err_waker.register(cx.waker()); - if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { - return Poll::Ready(Ok((time, frame))); + if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) { + return Poll::Ready(Ok(envelope)); } else if let Some(err) = self.curr_error() { return Poll::Ready(Err(err)); } @@ -407,6 +489,26 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { .await } + /// Attempts to read a CAN frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result { + if let Ok(envelope) = T::state().rx_queue.try_receive() { + return Ok(envelope); + } + + if let Some(err) = self.curr_error() { + return Err(TryReadError::BusError(err)); + } + + Err(TryReadError::Empty) + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await + } + fn curr_error(&self) -> Option { let err = { T::regs().esr().read() }; if err.boff() { @@ -455,10 +557,12 @@ pub(crate) mod sealed { use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; + use super::Envelope; + pub struct State { pub tx_waker: AtomicWaker, pub err_waker: AtomicWaker, - pub rx_queue: Channel, + pub rx_queue: Channel, } impl State { diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs new file mode 100644 index 00000000..f77788db --- /dev/null +++ b/embassy-stm32/src/can/fdcan.rs @@ -0,0 +1,66 @@ +pub use bxcan; +use embassy_hal_internal::PeripheralRef; + +use crate::peripherals; + +pub(crate) mod sealed { + use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; + use embassy_sync::channel::Channel; + use embassy_sync::waitqueue::AtomicWaker; + + pub struct State { + pub tx_waker: AtomicWaker, + pub err_waker: AtomicWaker, + pub rx_queue: Channel, + } + + impl State { + pub const fn new() -> Self { + Self { + tx_waker: AtomicWaker::new(), + err_waker: AtomicWaker::new(), + rx_queue: Channel::new(), + } + } + } + + pub trait Instance { + const REGISTERS: *mut bxcan::RegisterBlock; + + fn regs() -> &'static crate::pac::can::Fdcan; + fn state() -> &'static State; + } +} + +pub trait InterruptableInstance {} +pub trait Instance: sealed::Instance + InterruptableInstance + 'static {} + +pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>); + +unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> { + const REGISTERS: *mut bxcan::RegisterBlock = T::REGISTERS; +} + +foreach_peripheral!( + (can, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _; + + fn regs() -> &'static crate::pac::can::Fdcan { + &crate::pac::$inst + } + + fn state() -> &'static sealed::State { + static STATE: sealed::State = sealed::State::new(); + &STATE + } + } + + impl Instance for peripherals::$inst {} + + impl InterruptableInstance for peripherals::$inst {} + }; +); + +pin_trait!(RxPin, Instance); +pin_trait!(TxPin, Instance); diff --git a/embassy-stm32/src/can/mod.rs b/embassy-stm32/src/can/mod.rs index c7e2e620..4ff5aa0d 100644 --- a/embassy-stm32/src/can/mod.rs +++ b/embassy-stm32/src/can/mod.rs @@ -1,5 +1,6 @@ #![macro_use] #[cfg_attr(can_bxcan, path = "bxcan.rs")] +#[cfg_attr(can_fdcan, path = "fdcan.rs")] mod _version; pub use _version::*; diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index 3946a2d4..154f2eb9 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::CRC as PAC_CRC; use crate::peripherals::CRC; diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index f337055a..de0c0875 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::crc::vals; use crate::pac::CRC as PAC_CRC; diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 31a2d886..a2040b85 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -3,7 +3,7 @@ //! Provide access to the STM32 digital-to-analog converter (DAC). use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::dac; use crate::rcc::RccPeripheral; @@ -38,11 +38,30 @@ impl Channel { #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Trigger sources for CH1 pub enum Ch1Trigger { - Tim6, - Tim3, - Tim7, - Tim15, + #[cfg(dac_v3)] + Tim1, Tim2, + #[cfg(not(dac_v3))] + Tim3, + #[cfg(dac_v3)] + Tim4, + #[cfg(dac_v3)] + Tim5, + Tim6, + Tim7, + #[cfg(dac_v3)] + Tim8, + Tim15, + #[cfg(dac_v3)] + Hrtim1Dactrg1, + #[cfg(dac_v3)] + Hrtim1Dactrg2, + #[cfg(dac_v3)] + Lptim1, + #[cfg(dac_v3)] + Lptim2, + #[cfg(dac_v3)] + Lptim3, Exti9, Software, } @@ -50,11 +69,30 @@ pub enum Ch1Trigger { impl Ch1Trigger { fn tsel(&self) -> dac::vals::Tsel1 { match self { - Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, - Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, - Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, - Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim1 => dac::vals::Tsel1::TIM1_TRGO, Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO, + #[cfg(not(dac_v3))] + Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim4 => dac::vals::Tsel1::TIM4_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim5 => dac::vals::Tsel1::TIM5_TRGO, + Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, + Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim8 => dac::vals::Tsel1::TIM8_TRGO, + Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Hrtim1Dactrg1 => dac::vals::Tsel1::HRTIM1_DACTRG1, + #[cfg(dac_v3)] + Ch1Trigger::Hrtim1Dactrg2 => dac::vals::Tsel1::HRTIM1_DACTRG2, + #[cfg(dac_v3)] + Ch1Trigger::Lptim1 => dac::vals::Tsel1::LPTIM1_OUT, + #[cfg(dac_v3)] + Ch1Trigger::Lptim2 => dac::vals::Tsel1::LPTIM2_OUT, + #[cfg(dac_v3)] + Ch1Trigger::Lptim3 => dac::vals::Tsel1::LPTIM3_OUT, Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9, Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE, } @@ -126,7 +164,7 @@ pub trait DacChannel { } /// Set mode register of the given channel - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { T::regs().mcr().modify(|reg| { reg.set_mode(Self::CHANNEL.index(), val); @@ -213,8 +251,9 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { pub fn new( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _pin: impl Peripheral

> + 'd, + pin: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, ) -> Self { + pin.set_as_analog(); into_ref!(peri, dma); T::enable(); T::reset(); @@ -223,7 +262,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] dac.set_channel_mode(0).unwrap(); dac.enable_channel().unwrap(); dac.set_trigger_enable(true).unwrap(); @@ -249,7 +288,6 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 1 has to be configured for the DAC instance! - #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: DmaCh1, @@ -324,8 +362,9 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { pub fn new( _peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _pin: impl Peripheral

> + 'd, + pin: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, ) -> Self { + pin.set_as_analog(); into_ref!(_peri, dma); T::enable(); T::reset(); @@ -337,7 +376,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] dac.set_channel_mode(0).unwrap(); dac.enable_channel().unwrap(); dac.set_trigger_enable(true).unwrap(); @@ -361,7 +400,6 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 2 has to be configured for the DAC instance! - #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: DmaCh2, @@ -439,9 +477,11 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { peri: impl Peripheral

+ 'd, dma_ch1: impl Peripheral

+ 'd, dma_ch2: impl Peripheral

+ 'd, - _pin_ch1: impl Peripheral

> + 'd, - _pin_ch2: impl Peripheral

> + 'd, + pin_ch1: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, + pin_ch2: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, ) -> Self { + pin_ch1.set_as_analog(); + pin_ch2.set_as_analog(); into_ref!(peri, dma_ch1, dma_ch2); T::enable(); T::reset(); @@ -458,12 +498,12 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] dac_ch1.set_channel_mode(0).unwrap(); dac_ch1.enable_channel().unwrap(); dac_ch1.set_trigger_enable(true).unwrap(); - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] dac_ch2.set_channel_mode(0).unwrap(); dac_ch2.enable_channel().unwrap(); dac_ch2.set_trigger_enable(true).unwrap(); diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 78b026cb..7497f4aa 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::Transfer; diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 5a87888b..20ff29be 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -6,10 +6,10 @@ use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; use atomic_polyfill::AtomicUsize; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; @@ -393,15 +393,19 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn reset_complete_count(&mut self) -> usize { STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) } + + fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.0.index()].register(waker); + } } -pub struct RingBuffer<'a, C: Channel, W: Word> { +pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { cr: regs::Cr, channel: PeripheralRef<'a, C>, - ringbuf: DmaRingBuffer<'a, W>, + ringbuf: ReadableDmaRingBuffer<'a, W>, } -impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { pub unsafe fn new_read( channel: impl Peripheral

+ 'a, _request: Request, @@ -442,7 +446,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let mut this = Self { channel, cr: w, - ringbuf: DmaRingBuffer::new(buffer), + ringbuf: ReadableDmaRingBuffer::new(buffer), }; this.clear_irqs(); @@ -463,25 +467,42 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } - /// Read bytes from the ring buffer + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) + self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } - /// The capacity of the ringbuffer - pub fn cap(&self) -> usize { + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { + self.ringbuf + .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await + } + + /// The capacity of the ringbuffer. + pub const fn cap(&self) -> usize { self.ringbuf.cap() } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { @@ -512,7 +533,138 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } } -impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} + +pub struct WritableRingBuffer<'a, C: Channel, W: Word> { + cr: regs::Cr, + channel: PeripheralRef<'a, C>, + ringbuf: WritableDmaRingBuffer<'a, W>, +} + +impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buffer: &'a mut [W], + _options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let len = buffer.len(); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::MemoryToPeripheral; + let data_size = W::size(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + #[cfg(bdma_v2)] + critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request))); + + let mut w = regs::Cr(0); + w.set_psize(data_size.into()); + w.set_msize(data_size.into()); + w.set_minc(vals::Inc::ENABLED); + w.set_dir(dir.into()); + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + w.set_circ(vals::Circ::ENABLED); + w.set_pl(vals::Pl::VERYHIGH); + w.set_en(true); + + let buffer_ptr = buffer.as_mut_ptr(); + let mut this = Self { + channel, + cr: w, + ringbuf: WritableDmaRingBuffer::new(buffer), + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.ch(channel_number); + ch.par().write_value(peri_addr as u32); + ch.mar().write_value(buffer_ptr as u32); + ch.ndtr().write(|w| w.set_ndt(len as u16)); + + this + } + + pub fn start(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + ch.cr().write_value(self.cr) + } + + pub fn clear(&mut self) { + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); + } + + /// Write elements to the ring buffer + /// Return a tuple of the length written and the length remaining in the buffer + pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { + self.ringbuf + .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await + } + + /// The capacity of the ringbuffer. + pub const fn cap(&self) -> usize { + self.ringbuf.cap() + } + + pub fn set_waker(&mut self, waker: &Waker) { + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); + } + + fn clear_irqs(&mut self) { + let dma = self.channel.regs(); + dma.ifcr().write(|w| { + w.set_htif(self.channel.num(), true); + w.set_tcif(self.channel.num(), true); + w.set_teif(self.channel.num(), true); + }); + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + // If the channel is enabled and transfer is not completed, we need to perform + // two separate write access to the CR register to disable the channel. + ch.cr().write(|w| { + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + }); + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().ch(self.channel.num()); + ch.cr().read().en() + } +} + +impl<'a, C: Channel, W: Word> Drop for WritableRingBuffer<'a, C, W> { fn drop(&mut self) { self.request_stop(); while self.is_running() {} diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 58d438af..5033ae47 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -4,10 +4,10 @@ use core::pin::Pin; use core::sync::atomic::{fence, AtomicUsize, Ordering}; use core::task::{Context, Poll, Waker}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; @@ -28,6 +28,12 @@ pub struct TransferOptions { pub flow_ctrl: FlowControl, /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. pub fifo_threshold: Option, + /// Enable circular DMA + pub circular: bool, + /// Enable half transfer interrupt + pub half_transfer_ir: bool, + /// Enable transfer complete interrupt + pub complete_transfer_ir: bool, } impl Default for TransferOptions { @@ -37,6 +43,9 @@ impl Default for TransferOptions { mburst: Burst::Single, flow_ctrl: FlowControl::Dma, fifo_threshold: None, + circular: false, + half_transfer_ir: false, + complete_transfer_ir: true, } } } @@ -365,7 +374,13 @@ impl<'a, C: Channel> Transfer<'a, C> { }); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_tcie(true); + w.set_tcie(options.complete_transfer_ir); + if options.circular { + w.set_circ(vals::Circ::ENABLED); + debug!("Setting circular mode"); + } else { + w.set_circ(vals::Circ::DISABLED); + } #[cfg(dma_v1)] w.set_trbuff(true); @@ -608,15 +623,19 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn reset_complete_count(&mut self) -> usize { STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) } + + fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.0.index()].register(waker); + } } -pub struct RingBuffer<'a, C: Channel, W: Word> { +pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { cr: regs::Cr, channel: PeripheralRef<'a, C>, - ringbuf: DmaRingBuffer<'a, W>, + ringbuf: ReadableDmaRingBuffer<'a, W>, } -impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { pub unsafe fn new_read( channel: impl Peripheral

+ 'a, _request: Request, @@ -646,7 +665,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { w.set_minc(vals::Inc::INCREMENTED); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_htie(true); + w.set_htie(options.half_transfer_ir); w.set_tcie(true); w.set_circ(vals::Circ::ENABLED); #[cfg(dma_v1)] @@ -662,7 +681,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let mut this = Self { channel, cr: w, - ringbuf: DmaRingBuffer::new(buffer), + ringbuf: ReadableDmaRingBuffer::new(buffer), }; this.clear_irqs(); @@ -693,25 +712,42 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } - /// Read bytes from the ring buffer + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) + self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) + } + + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { + self.ringbuf + .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } // The capacity of the ringbuffer - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { @@ -744,7 +780,155 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } } -impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} + +pub struct WritableRingBuffer<'a, C: Channel, W: Word> { + cr: regs::Cr, + channel: PeripheralRef<'a, C>, + ringbuf: WritableDmaRingBuffer<'a, W>, +} + +impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buffer: &'a mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let len = buffer.len(); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::MemoryToPeripheral; + let data_size = W::size(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + let mut w = regs::Cr(0); + w.set_dir(dir.into()); + w.set_msize(data_size.into()); + w.set_psize(data_size.into()); + w.set_pl(vals::Pl::VERYHIGH); + w.set_minc(vals::Inc::INCREMENTED); + w.set_pinc(vals::Inc::FIXED); + w.set_teie(true); + w.set_htie(options.half_transfer_ir); + w.set_tcie(true); + w.set_circ(vals::Circ::ENABLED); + #[cfg(dma_v1)] + w.set_trbuff(true); + #[cfg(dma_v2)] + w.set_chsel(_request); + w.set_pburst(options.pburst.into()); + w.set_mburst(options.mburst.into()); + w.set_pfctrl(options.flow_ctrl.into()); + w.set_en(true); + + let buffer_ptr = buffer.as_mut_ptr(); + let mut this = Self { + channel, + cr: w, + ringbuf: WritableDmaRingBuffer::new(buffer), + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.st(channel_number); + ch.par().write_value(peri_addr as u32); + ch.m0ar().write_value(buffer_ptr as u32); + ch.ndtr().write_value(regs::Ndtr(len as _)); + ch.fcr().write(|w| { + if let Some(fth) = options.fifo_threshold { + // FIFO mode + w.set_dmdis(vals::Dmdis::DISABLED); + w.set_fth(fth.into()); + } else { + // Direct mode + w.set_dmdis(vals::Dmdis::ENABLED); + } + }); + + this + } + + pub fn start(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + ch.cr().write_value(self.cr); + } + + pub fn clear(&mut self) { + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); + } + + /// Write elements from the ring buffer + /// Return a tuple of the length written and the length remaining in the buffer + pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { + self.ringbuf + .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await + } + + // The capacity of the ringbuffer + pub const fn cap(&self) -> usize { + self.ringbuf.cap() + } + + pub fn set_waker(&mut self, waker: &Waker) { + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); + } + + fn clear_irqs(&mut self) { + let channel_number = self.channel.num(); + let dma = self.channel.regs(); + let isrn = channel_number / 4; + let isrbit = channel_number % 4; + + dma.ifcr(isrn).write(|w| { + w.set_htif(isrbit, true); + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }); + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + ch.cr().write(|w| { + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + }); + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + ch.cr().read().en() + } +} + +impl<'a, C: Channel, W: Word> Drop for WritableRingBuffer<'a, C, W> { fn drop(&mut self) { self.request_stop(); while self.is_running() {} diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index b7bcf779..97cc200d 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -5,7 +5,7 @@ use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use super::word::{Word, WordSize}; diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 0858587b..4f1a58ae 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -26,7 +26,7 @@ pub mod word; use core::mem; -use embassy_hal_common::impl_peripheral; +use embassy_hal_internal::impl_peripheral; #[cfg(dmamux)] pub use self::dmamux::*; diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index a2bde986..c9f7a302 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -1,7 +1,9 @@ #![cfg_attr(gpdma, allow(unused))] +use core::future::poll_fn; use core::ops::Range; use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Poll, Waker}; use super::word::Word; @@ -29,7 +31,7 @@ use super::word::Word; /// | | | | /// +- end --------------------+ +- start ----------------+ /// ``` -pub struct DmaRingBuffer<'a, W: Word> { +pub struct ReadableDmaRingBuffer<'a, W: Word> { pub(crate) dma_buf: &'a mut [W], start: usize, } @@ -49,15 +51,18 @@ pub trait DmaCtrl { /// Reset the transfer completed counter to 0 and return the value just prior to the reset. fn reset_complete_count(&mut self) -> usize; + + /// Set the waker for a running poll_fn + fn set_waker(&mut self, waker: &Waker); } -impl<'a, W: Word> DmaRingBuffer<'a, W> { +impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { pub fn new(dma_buf: &'a mut [W]) -> Self { Self { dma_buf, start: 0 } } /// Reset the ring buffer to its initial state - pub fn clear(&mut self, mut dma: impl DmaCtrl) { + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { self.start = 0; dma.reset_complete_count(); } @@ -68,16 +73,51 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { } /// The current position of the ringbuffer - fn pos(&self, remaining_transfers: usize) -> usize { - self.cap() - remaining_transfers + fn pos(&self, dma: &mut impl DmaCtrl) -> usize { + self.cap() - dma.get_remaining_transfers() } - /// Read bytes from the ring buffer - /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result { + let mut read_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(dma, &mut buffer[read_data..buffer_len]) { + Ok((len, remaining)) => { + read_data += len; + if read_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + + /// Read elements from the ring buffer + /// Return a tuple of the length read and the length remaining in the buffer + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { /* This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed @@ -93,13 +133,13 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { rather than the data we actually copied because it costs nothing and confirms an error condition earlier. */ - let end = self.pos(dma.get_remaining_transfers()); + let end = self.pos(dma); if self.start == end && dma.get_complete_count() == 0 { - // No bytes are available in the buffer + // No elements are available in the buffer Ok((0, self.cap())) } else if self.start < end { // The available, unread portion in the ring buffer DOES NOT wrap - // Copy out the bytes from the dma buffer + // Copy out the elements from the dma buffer let len = self.copy_to(buf, self.start..end); compiler_fence(Ordering::SeqCst); @@ -114,8 +154,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let (pos, complete_count) = - critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count())); + let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 { Err(OverrunError) } else { @@ -128,7 +167,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { // The DMA writer has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. - // The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. + // The provided read buffer is not large enough to include all elements from the tail of the dma buffer. // Copy out from the dma buffer let len = self.copy_to(buf, self.start..self.cap()); @@ -141,7 +180,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.start || pos < end || dma.get_complete_count() > 1 { Err(OverrunError) } else { @@ -154,8 +193,8 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { // The DMA writer has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. - // The provided read buffer is large enough to include all bytes from the tail of the dma buffer, - // so the next read will not have any unread tail bytes in the ring buffer. + // The provided read buffer is large enough to include all elements from the tail of the dma buffer, + // so the next read will not have any unread tail elements in the ring buffer. // Copy out from the dma buffer let tail = self.copy_to(buf, self.start..self.cap()); @@ -169,7 +208,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.start || pos < end || dma.reset_complete_count() > 1 { Err(OverrunError) } else { @@ -180,7 +219,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { } /// Copy from the dma buffer at `data_range` into `buf` fn copy_to(&mut self, buf: &mut [W], data_range: Range) -> usize { - // Limit the number of bytes that can be copied + // Limit the number of elements that can be copied let length = usize::min(data_range.len(), buf.len()); // Copy from dma buffer into read buffer @@ -197,6 +236,136 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { length } } + +pub struct WritableDmaRingBuffer<'a, W: Word> { + pub(crate) dma_buf: &'a mut [W], + end: usize, +} + +impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { + pub fn new(dma_buf: &'a mut [W]) -> Self { + Self { dma_buf, end: 0 } + } + + /// Reset the ring buffer to its initial state + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { + self.end = 0; + dma.reset_complete_count(); + } + + /// The capacity of the ringbuffer + pub const fn cap(&self) -> usize { + self.dma_buf.len() + } + + /// The current position of the ringbuffer + fn pos(&self, dma: &mut impl DmaCtrl) -> usize { + self.cap() - dma.get_remaining_transfers() + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { + let mut written_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.write(dma, &buffer[written_data..buffer_len]) { + Ok((len, remaining)) => { + written_data += len; + if written_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + + /// Write elements from the ring buffer + /// Return a tuple of the length written and the capacity remaining to be written in the buffer + pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + let start = self.pos(dma); + if start > self.end { + // The occupied portion in the ring buffer DOES wrap + let len = self.copy_from(buf, self.end..start); + + compiler_fence(Ordering::SeqCst); + + // Confirm that the DMA is not inside data we could have written + let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); + if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 { + Err(OverrunError) + } else { + self.end = (self.end + len) % self.cap(); + + Ok((len, self.cap() - (start - self.end))) + } + } else if start == self.end && dma.get_complete_count() == 0 { + Ok((0, 0)) + } else if start <= self.end && self.end + buf.len() < self.cap() { + // The occupied portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer WILL NOT cause it to + + // Copy into the dma buffer + let len = self.copy_from(buf, self.end..self.cap()); + + compiler_fence(Ordering::SeqCst); + + // Confirm that the DMA is not inside data we could have written + let pos = self.pos(dma); + if pos > self.end || pos < start || dma.get_complete_count() > 1 { + Err(OverrunError) + } else { + self.end = (self.end + len) % self.cap(); + + Ok((len, self.cap() - (self.end - start))) + } + } else { + // The occupied portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer WILL cause it to + + let tail = self.copy_from(buf, self.end..self.cap()); + let head = self.copy_from(&buf[tail..], 0..start); + + compiler_fence(Ordering::SeqCst); + + // Confirm that the DMA is not inside data we could have written + let pos = self.pos(dma); + if pos > self.end || pos < start || dma.reset_complete_count() > 1 { + Err(OverrunError) + } else { + self.end = head; + + Ok((tail + head, self.cap() - (start - self.end))) + } + } + } + /// Copy into the dma buffer at `data_range` from `buf` + fn copy_from(&mut self, buf: &[W], data_range: Range) -> usize { + // Limit the number of elements that can be copied + let length = usize::min(data_range.len(), buf.len()); + + // Copy into dma buffer from read buffer + // We need to do it like this instead of a simple copy_from_slice() because + // reading from a part of memory that may be simultaneously written to is unsafe + unsafe { + let dma_buf = self.dma_buf.as_mut_ptr(); + + for i in 0..length { + core::ptr::write_volatile(dma_buf.offset((data_range.start + i) as isize), buf[i]); + } + } + + length + } +} #[cfg(test)] mod tests { use core::array; @@ -217,7 +386,7 @@ mod tests { requests: cell::RefCell>, } - impl DmaCtrl for &mut TestCircularTransfer { + impl DmaCtrl for TestCircularTransfer { fn get_remaining_transfers(&self) -> usize { match self.requests.borrow_mut().pop().unwrap() { TestCircularTransferRequest::PositionRequest(pos) => { @@ -244,13 +413,15 @@ mod tests { _ => unreachable!(), } } + + fn set_waker(&mut self, waker: &Waker) {} } impl TestCircularTransfer { pub fn new(len: usize) -> Self { Self { requests: cell::RefCell::new(vec![]), - len: len, + len, } } @@ -263,7 +434,7 @@ mod tests { #[test] fn empty_and_read_not_started() { let mut dma_buf = [0u8; 16]; - let ringbuf = DmaRingBuffer::new(&mut dma_buf); + let ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); } @@ -273,7 +444,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -314,7 +485,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -349,7 +520,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -384,7 +555,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -420,7 +591,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -454,7 +625,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 90631b17..2ed46ca2 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs @@ -45,20 +45,19 @@ use self::phy_consts::*; pub struct GenericSMI { #[cfg(feature = "time")] poll_interval: Duration, + #[cfg(not(feature = "time"))] + _private: (), } impl GenericSMI { - #[cfg(feature = "time")] pub fn new() -> Self { Self { + #[cfg(feature = "time")] poll_interval: Duration::from_millis(500), + #[cfg(not(feature = "time"))] + _private: (), } } - - #[cfg(not(feature = "time"))] - pub fn new() -> Self { - Self {} - } } unsafe impl PHY for GenericSMI { @@ -102,6 +101,7 @@ unsafe impl PHY for GenericSMI { /// Public functions for the PHY impl GenericSMI { + #[cfg(feature = "time")] pub fn set_poll_interval(&mut self, poll_interval: Duration) { self.poll_interval = poll_interval } diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 1687cb31..1e057235 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -8,7 +8,7 @@ pub mod generic_smi; use core::mem::MaybeUninit; use core::task::Context; -use embassy_net_driver::{Capabilities, LinkState}; +use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; use embassy_sync::waitqueue::AtomicWaker; pub use self::_version::{InterruptHandler, *}; @@ -88,8 +88,8 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> } } - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr + fn hardware_address(&self) -> HardwareAddress { + HardwareAddress::Ethernet(self.mac_addr) } } diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 2a6ea35f..a1e0240c 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -6,7 +6,7 @@ mod tx_desc; use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; pub(crate) use self::rx_desc::{RDes, RDesRing}; diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index bb681c42..ada495fd 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -3,7 +3,7 @@ mod descriptors; use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; use super::*; diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 3ff92c9e..925cf39b 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::pin::Pin; use core::task::{Context, Poll}; -use embassy_hal_common::impl_peripheral; +use embassy_hal_internal::impl_peripheral; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Input, Pin as GpioPin}; diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index f175349c..e966e2a7 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,8 +1,8 @@ use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::into_ref; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 2a374733..8acad1c7 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,8 +1,8 @@ use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; use super::{ @@ -147,7 +147,7 @@ pub(super) unsafe fn erase_sector_unlocked(sector: &FlashSector) -> Result<(), E let _on_drop = OnDrop::new(|| family::lock()); - family::blocking_erase_sector(§or) + family::blocking_erase_sector(sector) } pub(super) unsafe fn erase_sector_with_critical_section(sector: &FlashSector) -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index ec8343e7..d011522b 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -76,17 +76,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - if w.pgerr() { - w.set_pgerr(true); - } - if w.wrprt() { - w.set_wrprt(true) - }; - if w.eop() { - w.set_eop(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } unsafe fn wait_ready_blocking() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 40335d64..065369f6 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -76,17 +76,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - if w.pgerr() { - w.set_pgerr(true); - } - if w.wrprterr() { - w.set_wrprterr(true); - } - if w.eop() { - w.set_eop(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } unsafe fn wait_ready_blocking() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 4cb39e03..913950fe 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -14,7 +14,7 @@ use crate::pac; mod alt_regions { use core::marker::PhantomData; - use embassy_hal_common::PeripheralRef; + use embassy_hal_internal::PeripheralRef; use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; @@ -336,12 +336,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) fn clear_all_err() { - pac::FLASH.sr().write(|w| { - w.set_pgserr(true); - w.set_pgperr(true); - w.set_pgaerr(true); - w.set_wrperr(true); - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } pub(crate) async fn wait_ready() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 1a6d6bb0..3a5bdf9c 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -66,23 +66,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - if w.erserr() { - w.set_erserr(true); - } - if w.pgperr() { - w.set_pgperr(true); - } - if w.pgaerr() { - w.set_pgaerr(true); - } - if w.wrperr() { - w.set_wrperr(true); - } - if w.eop() { - w.set_eop(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } unsafe fn blocking_wait_ready() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/g0.rs b/embassy-stm32/src/flash/g0.rs new file mode 100644 index 00000000..3a457601 --- /dev/null +++ b/embassy-stm32/src/flash/g0.rs @@ -0,0 +1,95 @@ +use core::convert::TryInto; +use core::ptr::write_volatile; +use core::sync::atomic::{fence, Ordering}; + +use cortex_m::interrupt; + +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use crate::flash::Error; +use crate::pac; + +pub const fn is_default_layout() -> bool { + true +} + +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + +pub(crate) unsafe fn lock() { + pac::FLASH.cr().modify(|w| w.set_lock(true)); +} +pub(crate) unsafe fn unlock() { + // Wait, while the memory interface is busy. + while pac::FLASH.sr().read().bsy() {} + + // Unlock flash + pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123)); + pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB)); +} + +pub(crate) unsafe fn enable_blocking_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| w.set_pg(true)); +} + +pub(crate) unsafe fn disable_blocking_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + wait_ready_blocking() +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; + while pac::FLASH.sr().read().bsy() {} + clear_all_err(); + + interrupt::free(|_| { + pac::FLASH.cr().modify(|w| { + w.set_per(true); + w.set_pnb(idx as u8); + w.set_strt(true); + }); + }); + + let ret: Result<(), Error> = wait_ready_blocking(); + pac::FLASH.cr().modify(|w| w.set_per(false)); + ret +} + +pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { + while pac::FLASH.sr().read().bsy() {} + + let sr = pac::FLASH.sr().read(); + + if sr.progerr() { + return Err(Error::Prog); + } + + if sr.wrperr() { + return Err(Error::Protected); + } + + if sr.pgaerr() { + return Err(Error::Unaligned); + } + + Ok(()) +} + +pub(crate) unsafe fn clear_all_err() { + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); +} diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index bf17b5b1..625bf13f 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -11,7 +11,7 @@ pub const fn is_default_layout() -> bool { } const fn is_dual_bank() -> bool { - FLASH_REGIONS.len() == 2 + FLASH_REGIONS.len() >= 2 } pub fn get_flash_regions() -> &'static [&'static FlashRegion] { @@ -49,6 +49,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) }; bank.cr().write(|w| { w.set_pg(true); + #[cfg(flash_h7)] w.set_psize(2); // 32 bits at once }); cortex_m::asm::isb(); @@ -85,7 +86,10 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector.index_in_bank) + #[cfg(flash_h7)] + w.set_snb(sector.index_in_bank); + #[cfg(flash_h7ab)] + w.set_ssn(sector.index_in_bank); }); bank.cr().modify(|w| { @@ -104,39 +108,9 @@ pub(crate) unsafe fn clear_all_err() { } unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { - bank.sr().modify(|w| { - if w.wrperr() { - w.set_wrperr(true); - } - if w.pgserr() { - w.set_pgserr(true); - } - if w.strberr() { - // single address was written multiple times, can be ignored - w.set_strberr(true); - } - if w.incerr() { - // writing to a different address when programming 256 bit word was not finished - w.set_incerr(true); - } - if w.operr() { - w.set_operr(true); - } - if w.sneccerr1() { - // single ECC error - w.set_sneccerr1(true); - } - if w.dbeccerr() { - // double ECC error - w.set_dbeccerr(true); - } - if w.rdperr() { - w.set_rdperr(true); - } - if w.rdserr() { - w.set_rdserr(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + bank.sr().modify(|_| {}); } unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { @@ -156,6 +130,10 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { error!("incerr"); return Err(Error::Seq); } + if sr.crcrderr() { + error!("crcrderr"); + return Err(Error::Seq); + } if sr.operr() { return Err(Error::Prog); } diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 243c8b51..24dcf99b 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -113,41 +113,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))] - if w.rderr() { - w.set_rderr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.fasterr() { - w.set_fasterr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.miserr() { - w.set_miserr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.pgserr() { - w.set_pgserr(true); - } - if w.sizerr() { - w.set_sizerr(true); - } - if w.pgaerr() { - w.set_pgaerr(true); - } - if w.wrperr() { - w.set_wrperr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.progerr() { - w.set_progerr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.operr() { - w.set_operr(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } unsafe fn wait_ready_blocking() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index f44ef2c1..fb20dcd3 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -63,10 +63,13 @@ impl FlashRegion { #[cfg_attr(flash_f3, path = "f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] +#[cfg_attr(flash_g0, path = "g0.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] +#[cfg_attr(flash_h7ab, path = "h7.rs")] #[cfg_attr( not(any( - flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_h7 + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7, + flash_h7ab )), path = "other.rs" )] diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 60d7a00e..177e66a9 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use crate::gpio::sealed::AFType; use crate::gpio::{Pull, Speed}; diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index af3a8eac..a382cb74 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -1,7 +1,7 @@ #![macro_use] use core::convert::Infallible; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::pac::gpio::{self, vals}; use crate::{pac, peripherals, Peripheral}; @@ -351,6 +351,10 @@ impl From for bool { } /// GPIO output driver. +/// +/// Note that pins will **return to their floating state** when `Output` is dropped. +/// If pins should retain their state indefinitely, either keep ownership of the +/// `Output`, or pass it to [`core::mem::forget`]. pub struct Output<'d, T: Pin> { pub(crate) pin: Flex<'d, T>, } @@ -418,6 +422,10 @@ impl<'d, T: Pin> Output<'d, T> { } /// GPIO output open-drain driver. +/// +/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped. +/// If pins should retain their state indefinitely, either keep ownership of the +/// `OutputOpenDrain`, or pass it to [`core::mem::forget`]. pub struct OutputOpenDrain<'d, T: Pin> { pub(crate) pin: Flex<'d, T>, } @@ -502,6 +510,20 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { } } +pub enum OutputType { + PushPull, + OpenDrain, +} + +impl From for sealed::AFType { + fn from(value: OutputType) -> Self { + match value { + OutputType::OpenDrain => sealed::AFType::OutputOpenDrain, + OutputType::PushPull => sealed::AFType::OutputPushPull, + } + } +} + pub(crate) mod sealed { use super::*; @@ -763,12 +785,14 @@ mod eh02 { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - Ok(self.set_high()) + self.set_high(); + Ok(()) } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - Ok(self.set_low()) + self.set_low(); + Ok(()) } } @@ -789,7 +813,8 @@ mod eh02 { type Error = Infallible; #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { - Ok(self.toggle()) + self.toggle(); + Ok(()) } } @@ -798,12 +823,14 @@ mod eh02 { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - Ok(self.set_high()) + self.set_high(); + Ok(()) } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - Ok(self.set_low()) + self.set_low(); + Ok(()) } } @@ -824,7 +851,8 @@ mod eh02 { type Error = Infallible; #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { - Ok(self.toggle()) + self.toggle(); + Ok(()) } } @@ -847,12 +875,14 @@ mod eh02 { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - Ok(self.set_high()) + self.set_high(); + Ok(()) } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - Ok(self.set_low()) + self.set_low(); + Ok(()) } } @@ -873,7 +903,8 @@ mod eh02 { type Error = Infallible; #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { - Ok(self.toggle()) + self.toggle(); + Ok(()) } } } diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs new file mode 100644 index 00000000..31c48814 --- /dev/null +++ b/embassy-stm32/src/hrtim/mod.rs @@ -0,0 +1,427 @@ +mod traits; + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use traits::Instance; + +#[allow(unused_imports)] +use crate::gpio::sealed::{AFType, Pin}; +use crate::gpio::AnyPin; +use crate::time::Hertz; +use crate::Peripheral; + +pub enum Source { + Master, + ChA, + ChB, + ChC, + ChD, + ChE, + #[cfg(hrtim_v2)] + ChF, +} + +pub struct BurstController { + phantom: PhantomData, +} +pub struct Master { + phantom: PhantomData, +} +pub struct ChA { + phantom: PhantomData, +} +pub struct ChB { + phantom: PhantomData, +} +pub struct ChC { + phantom: PhantomData, +} +pub struct ChD { + phantom: PhantomData, +} +pub struct ChE { + phantom: PhantomData, +} +#[cfg(hrtim_v2)] +pub struct ChF { + phantom: PhantomData, +} + +mod sealed { + use super::Instance; + + pub trait AdvancedChannel { + fn raw() -> usize; + } +} + +pub trait AdvancedChannel: sealed::AdvancedChannel {} + +pub struct PwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +pub struct ComplementaryPwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +macro_rules! advanced_channel_impl { + ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { + impl<'d, Perip: Instance> PwmPin<'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::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + PwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl<'d, Perip: Instance> ComplementaryPwmPin<'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::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + ComplementaryPwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl sealed::AdvancedChannel for $channel { + fn raw() -> usize { + $ch_num + } + } + impl AdvancedChannel for $channel {} + }; +} + +advanced_channel_impl!(new_cha, ChA, 0, ChannelAPin, ChannelAComplementaryPin); +advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin); +advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin); +advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); +advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); +#[cfg(hrtim_v2)] +advanced_channel_impl!(new_chf, ChF, 5, ChannelFPin, ChannelFComplementaryPin); + +/// Struct used to divide a high resolution timer into multiple channels +pub struct AdvancedPwm<'d, T: Instance> { + _inner: PeripheralRef<'d, T>, + pub master: Master, + pub burst_controller: BurstController, + pub ch_a: ChA, + pub ch_b: ChB, + pub ch_c: ChC, + pub ch_d: ChD, + pub ch_e: ChE, + #[cfg(hrtim_v2)] + pub ch_f: ChF, +} + +impl<'d, T: Instance> AdvancedPwm<'d, T> { + pub fn new( + tim: impl Peripheral

+ 'd, + _cha: Option>>, + _chan: Option>>, + _chb: Option>>, + _chbn: Option>>, + _chc: Option>>, + _chcn: Option>>, + _chd: Option>>, + _chdn: Option>>, + _che: Option>>, + _chen: Option>>, + #[cfg(hrtim_v2)] _chf: Option>>, + #[cfg(hrtim_v2)] _chfn: Option>>, + ) -> Self { + Self::new_inner(tim) + } + + fn new_inner(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + + 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"); + + Self { + _inner: tim, + master: Master { phantom: PhantomData }, + burst_controller: BurstController { phantom: PhantomData }, + ch_a: ChA { phantom: PhantomData }, + ch_b: ChB { phantom: PhantomData }, + ch_c: ChC { phantom: PhantomData }, + ch_d: ChD { phantom: PhantomData }, + ch_e: ChE { phantom: PhantomData }, + #[cfg(hrtim_v2)] + ch_f: ChF { phantom: PhantomData }, + } + } +} + +impl BurstController { + pub fn set_source(&mut self, _source: Source) { + todo!("burst mode control registers not implemented") + } +} + +/// Represents a fixed-frequency bridge converter +/// +/// Our implementation of the bridge converter uses a single channel and three compare registers, +/// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous +/// conduction mode. +/// +/// It is important to remember that in synchronous topologies, energy can flow in reverse during +/// light loading conditions, and that the low-side switch must be active for a short time to drive +/// a bootstrapped high-side switch. +pub struct BridgeConverter> { + timer: PhantomData, + channel: PhantomData, + dead_time: u16, + primary_duty: u16, + min_secondary_duty: u16, + max_secondary_duty: u16, +} + +impl> BridgeConverter { + pub fn new(_channel: C, frequency: Hertz) -> Self { + use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect}; + + T::set_channel_frequency(C::raw(), frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + w.set_repu(true); + w.set_cont(true); + }); + + // Enable timer outputs + T::regs().oenr().modify(|w| { + w.set_t1oen(C::raw(), true); + w.set_t2oen(C::raw(), true); + }); + + // The dead-time generation unit cannot be used because it forces the other output + // to be completely complementary to the first output, which restricts certain waveforms + // Therefore, software-implemented dead time must be used when setting the duty cycles + + // Set output 1 to active on a period event + T::regs() + .tim(C::raw()) + .setr(0) + .modify(|w| w.set_per(Activeeffect::SETACTIVE)); + + // Set output 1 to inactive on a compare 1 event + T::regs() + .tim(C::raw()) + .rstr(0) + .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); + + // Set output 2 to active on a compare 2 event + T::regs() + .tim(C::raw()) + .setr(1) + .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE)); + + // Set output 2 to inactive on a compare 3 event + T::regs() + .tim(C::raw()) + .rstr(1) + .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE)); + + Self { + timer: PhantomData, + channel: PhantomData, + dead_time: 0, + primary_duty: 0, + min_secondary_duty: 0, + max_secondary_duty: 0, + } + } + + pub fn start(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), true)); + } + + pub fn stop(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false)); + } + + pub fn enable_burst_mode(&mut self) { + T::regs().tim(C::raw()).outr().modify(|w| { + // Enable Burst Mode + w.set_idlem(0, true); + w.set_idlem(1, true); + + // Set output to active during the burst + w.set_idles(0, true); + w.set_idles(1, true); + }) + } + + pub fn disable_burst_mode(&mut self) { + T::regs().tim(C::raw()).outr().modify(|w| { + // Disable Burst Mode + w.set_idlem(0, false); + w.set_idlem(1, false); + }) + } + + fn update_primary_duty_or_dead_time(&mut self) { + self.min_secondary_duty = self.primary_duty + self.dead_time; + + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty)); + T::regs() + .tim(C::raw()) + .cmp(1) + .modify(|w| w.set_cmp(self.min_secondary_duty)); + } + + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, dead_time: u16) { + self.dead_time = dead_time; + self.max_secondary_duty = self.get_max_compare_value() - dead_time; + self.update_primary_duty_or_dead_time(); + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_compare_value(&mut self) -> u16 { + T::regs().tim(C::raw()).per().read().per() + } + + /// The primary duty is the period in which the primary switch is active + /// + /// In the case of a buck converter, this is the high-side switch + /// In the case of a boost converter, this is the low-side switch + pub fn set_primary_duty(&mut self, primary_duty: u16) { + self.primary_duty = primary_duty; + self.update_primary_duty_or_dead_time(); + } + + /// The secondary duty is the period in any switch is active + /// + /// If less than or equal to the primary duty, the secondary switch will be active for one tick + /// If a fully complementary output is desired, the secondary duty can be set to the max compare + pub fn set_secondary_duty(&mut self, secondary_duty: u16) { + let secondary_duty = if secondary_duty > self.max_secondary_duty { + self.max_secondary_duty + } else if secondary_duty <= self.min_secondary_duty { + self.min_secondary_duty + 1 + } else { + secondary_duty + }; + + T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty)); + } +} + +/// Represents a variable-frequency resonant converter +/// +/// This implementation of a resonsant converter is appropriate for a half or full bridge, +/// but does not include secondary rectification, which is appropriate for applications +/// with a low-voltage on the secondary side. +pub struct ResonantConverter> { + timer: PhantomData, + channel: PhantomData, + min_period: u16, + max_period: u16, +} + +impl> ResonantConverter { + pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { + T::set_channel_frequency(C::raw(), min_frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + w.set_repu(true); + + w.set_cont(true); + w.set_half(true); + }); + + // Enable timer outputs + T::regs().oenr().modify(|w| { + w.set_t1oen(C::raw(), true); + w.set_t2oen(C::raw(), true); + }); + + // Dead-time generator can be used in this case because the primary fets + // of a resonant converter are always complementary + T::regs().tim(C::raw()).outr().modify(|w| w.set_dten(true)); + + let max_period = T::regs().tim(C::raw()).per().read().per(); + let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16; + + Self { + timer: PhantomData, + channel: PhantomData, + min_period: min_period, + max_period: max_period, + } + } + + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, value: u16) { + T::set_channel_dead_time(C::raw(), value); + } + + pub fn set_period(&mut self, period: u16) { + assert!(period < self.max_period); + assert!(period > self.min_period); + + T::regs().tim(C::raw()).per().modify(|w| w.set_per(period)); + } + + /// Get the minimum compare value of a duty cycle + pub fn get_min_period(&mut self) -> u16 { + self.min_period + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_period(&mut self) -> u16 { + self.max_period + } +} + +pin_trait!(ChannelAPin, Instance); +pin_trait!(ChannelAComplementaryPin, Instance); +pin_trait!(ChannelBPin, Instance); +pin_trait!(ChannelBComplementaryPin, Instance); +pin_trait!(ChannelCPin, Instance); +pin_trait!(ChannelCComplementaryPin, Instance); +pin_trait!(ChannelDPin, Instance); +pin_trait!(ChannelDComplementaryPin, Instance); +pin_trait!(ChannelEPin, Instance); +pin_trait!(ChannelEComplementaryPin, Instance); +#[cfg(hrtim_v2)] +pin_trait!(ChannelFPin, Instance); +#[cfg(hrtim_v2)] +pin_trait!(ChannelFComplementaryPin, Instance); diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs new file mode 100644 index 00000000..158a6886 --- /dev/null +++ b/embassy-stm32/src/hrtim/traits.rs @@ -0,0 +1,193 @@ +use crate::rcc::sealed::RccPeripheral; +use crate::time::Hertz; + +#[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, + } + } +} + +impl From for u8 { + fn from(val: Prescaler) -> Self { + match val { + Prescaler::Div1 => 0b000, + Prescaler::Div2 => 0b001, + Prescaler::Div4 => 0b010, + Prescaler::Div8 => 0b011, + Prescaler::Div16 => 0b100, + Prescaler::Div32 => 0b101, + Prescaler::Div64 => 0b110, + Prescaler::Div128 => 0b111, + } + } +} + +impl From for Prescaler { + fn from(val: u8) -> Self { + match val { + 0b000 => Prescaler::Div1, + 0b001 => Prescaler::Div2, + 0b010 => Prescaler::Div4, + 0b011 => Prescaler::Div8, + 0b100 => Prescaler::Div16, + 0b101 => Prescaler::Div32, + 0b110 => Prescaler::Div64, + 0b111 => Prescaler::Div128, + _ => unreachable!(), + } + } +} + +impl Prescaler { + pub fn compute_min_high_res(val: u32) -> Self { + *[ + Prescaler::Div1, + Prescaler::Div2, + Prescaler::Div4, + Prescaler::Div8, + Prescaler::Div16, + Prescaler::Div32, + Prescaler::Div64, + Prescaler::Div128, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } + + pub fn compute_min_low_res(val: u32) -> Self { + *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance: RccPeripheral { + fn regs() -> crate::pac::hrtim::Hrtim; + + fn set_master_frequency(frequency: Hertz); + + fn set_channel_frequency(channnel: usize, frequency: Hertz); + + /// Set the dead time as a proportion of max_duty + fn set_channel_dead_time(channnel: usize, dead_time: u16); + + // fn enable_outputs(enable: bool); + // + // fn enable_channel(&mut self, channel: usize, enable: bool); + } +} + +pub trait Instance: sealed::Instance + 'static {} + +foreach_interrupt! { + ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { + impl sealed::Instance for crate::peripherals::$inst { + 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/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 939e2750..103017cd 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -6,8 +6,8 @@ use super::{Error, I2c, Instance}; /// /// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state. /// A regular [I2c] would freeze until condition is removed. -pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { - i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, +pub struct TimeoutI2c<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> { + i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration, } @@ -22,11 +22,93 @@ fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { } } -impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { - pub fn new(i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { +impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { + pub fn new(i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { Self { i2c, timeout } } + // ========================= + // Async public API + + #[cfg(i2c_v2)] + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_timeout(address, write, self.timeout).await + } + + #[cfg(i2c_v2)] + pub async fn write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.i2c.write_timeout(address, write, timeout_fn(timeout)).await + } + + #[cfg(i2c_v2)] + pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_vectored_timeout(address, write, self.timeout).await + } + + #[cfg(i2c_v2)] + pub async fn write_vectored_timeout(&mut self, address: u8, write: &[&[u8]], timeout: Duration) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.i2c + .write_vectored_timeout(address, write, timeout_fn(timeout)) + .await + } + + #[cfg(i2c_v2)] + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.read_timeout(address, buffer, self.timeout).await + } + + #[cfg(i2c_v2)] + pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.i2c.read_timeout(address, buffer, timeout_fn(timeout)).await + } + + #[cfg(i2c_v2)] + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + self.write_read_timeout(address, write, read, self.timeout).await + } + + #[cfg(i2c_v2)] + pub async fn write_read_timeout( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + timeout: Duration, + ) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + self.i2c + .write_read_timeout(address, write, read, timeout_fn(timeout)) + .await + } + + // ========================= + // Blocking public API + /// Blocking read with a custom timeout pub fn blocking_read_timeout(&mut self, addr: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> { self.i2c.blocking_read_timeout(addr, read, timeout_fn(timeout)) @@ -65,7 +147,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read + for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> +{ type Error = Error; fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { @@ -73,7 +157,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for Tim } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write + for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> +{ type Error = Error; fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { @@ -81,7 +167,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for Ti } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead + for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> +{ type Error = Error; fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { @@ -93,11 +181,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead fo mod eh1 { use super::*; - impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> { + impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; } - impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { + impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index aa485cd8..618d85af 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::dma::NoDma; use crate::gpio::sealed::AFType; @@ -21,21 +21,12 @@ impl interrupt::typelevel::Handler for InterruptHandl } #[non_exhaustive] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct Config { pub sda_pullup: bool, pub scl_pullup: bool, } -impl Default for Config { - fn default() -> Self { - Self { - sda_pullup: false, - scl_pullup: false, - } - } -} - pub struct State {} impl State { @@ -90,7 +81,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { //reg.set_anfoff(false); }); - let timings = Timings::new(T::frequency(), freq.into()); + let timings = Timings::new(T::frequency(), freq); T::regs().cr2().modify(|reg| { reg.set_freq(timings.freq); @@ -461,7 +452,7 @@ impl Timings { let speed = speed.0; let clock = i2cclk.0; let freq = clock / 1_000_000; - assert!(freq >= 2 && freq <= 50); + assert!((2..=50).contains(&freq)); // Configure bus frequency into I2C peripheral let trise = if speed <= 100_000 { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 208d1527..4327899b 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -4,8 +4,8 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::{NoDma, Transfer}; @@ -595,17 +595,41 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Async public API pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_timeout(address, write, || Ok(())).await + } + + pub async fn write_timeout( + &mut self, + address: u8, + write: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { if write.is_empty() { - self.write_internal(address, write, true, || Ok(())) + self.write_internal(address, write, true, check_timeout) } else { - self.write_dma_internal(address, write, true, true, || Ok(())).await + self.write_dma_internal(address, write, true, true, check_timeout).await } } pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_vectored_timeout(address, write, || Ok(())).await + } + + pub async fn write_vectored_timeout( + &mut self, + address: u8, + write: &[&[u8]], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -620,7 +644,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let next = iter.next(); let is_last = next.is_none(); - self.write_dma_internal(address, c, first, is_last, || Ok(())).await?; + self.write_dma_internal(address, c, first, is_last, || check_timeout()) + .await?; first = false; current = next; } @@ -628,31 +653,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.read_timeout(address, buffer, || Ok(())).await + } + + pub async fn read_timeout( + &mut self, + address: u8, + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { if buffer.is_empty() { - self.read_internal(address, buffer, false, || Ok(())) + self.read_internal(address, buffer, false, check_timeout) } else { - self.read_dma_internal(address, buffer, false, || Ok(())).await + self.read_dma_internal(address, buffer, false, check_timeout).await } } pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + self.write_read_timeout(address, write, read, || Ok(())).await + } + + pub async fn write_read_timeout( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where TXDMA: super::TxDma, RXDMA: super::RxDma, { if write.is_empty() { - self.write_internal(address, write, false, || Ok(()))?; + self.write_internal(address, write, false, || check_timeout())?; } else { - self.write_dma_internal(address, write, true, true, || Ok(())).await?; + self.write_dma_internal(address, write, true, true, || check_timeout()) + .await?; } if read.is_empty() { - self.read_internal(address, read, true, || Ok(()))?; + self.read_internal(address, read, true, check_timeout)?; } else { - self.read_dma_internal(address, read, true, || Ok(())).await?; + self.read_dma_internal(address, read, true, check_timeout).await?; } Ok(()) diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 62dda69b..8fd3a8c6 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::AnyPin; @@ -165,7 +165,9 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { mck.set_as_af(mck.af_num(), AFType::OutputPushPull); mck.set_speed(crate::gpio::Speed::VeryHigh); - let spi = Spi::new_internal(peri, txdma, rxdma, freq, SpiConfig::default()); + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = freq; + let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); #[cfg(all(rcc_f4, not(stm32f410)))] let pclk = unsafe { get_freqs() }.plli2s.unwrap(); diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index a24cba9f..e100ca5c 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -265,63 +265,9 @@ pub(crate) mod sealed { } fn _configure_pwr() { - // TODO: move this to RCC - - let pwr = crate::pac::PWR; + // TODO: move the rest of this to rcc let rcc = crate::pac::RCC; - rcc.cfgr().modify(|w| w.set_stopwuck(true)); - - pwr.cr1().modify(|w| w.set_dbp(true)); - pwr.cr1().modify(|w| w.set_dbp(true)); - - // configure LSE - rcc.bdcr().modify(|w| w.set_lseon(true)); - - // select system clock source = PLL - // set PLL coefficients - // m: 2, - // n: 12, - // r: 3, - // q: 4, - // p: 3, - let src_bits = 0b11; - let pllp = (3 - 1) & 0b11111; - let pllq = (4 - 1) & 0b111; - let pllr = (3 - 1) & 0b111; - let plln = 12 & 0b1111111; - let pllm = (2 - 1) & 0b111; - rcc.pllcfgr().modify(|w| { - w.set_pllsrc(src_bits); - w.set_pllm(pllm); - w.set_plln(plln); - w.set_pllr(pllr); - w.set_pllp(pllp); - w.set_pllpen(true); - w.set_pllq(pllq); - w.set_pllqen(true); - }); - // enable PLL - rcc.cr().modify(|w| w.set_pllon(true)); - rcc.cr().write(|w| w.set_hsion(false)); - // while !rcc.cr().read().pllrdy() {} - - // configure SYSCLK mux to use PLL clocl - rcc.cfgr().modify(|w| w.set_sw(0b11)); - - // configure CPU1 & CPU2 dividers - rcc.cfgr().modify(|w| w.set_hpre(0)); // not divided - rcc.extcfgr().modify(|w| { - w.set_c2hpre(0b1000); // div2 - w.set_shdhpre(0); // not divided - }); - - // apply APB1 / APB2 values - rcc.cfgr().modify(|w| { - w.set_ppre1(0b000); // not divided - w.set_ppre2(0b000); // not divided - }); - // TODO: required // set RF wake-up clock = LSE rcc.csr().modify(|w| w.set_rfwkpsel(0b01)); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 45a7b547..8c87ea7d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,8 +1,11 @@ #![cfg_attr(not(test), no_std)] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] +//! ## Feature flags +#![doc = document_features::document_features!(feature_label = r#"{feature}"#)] + // This must go FIRST so that all the other modules see its macros. -pub mod fmt; +mod fmt; include!(concat!(env!("OUT_DIR"), "/_macros.rs")); // Utilities @@ -23,6 +26,8 @@ pub mod timer; pub mod adc; #[cfg(can)] pub mod can; +#[cfg(crc)] +pub mod crc; #[cfg(dac)] pub mod dac; #[cfg(dcmi)] @@ -31,19 +36,19 @@ pub mod dcmi; pub mod eth; #[cfg(feature = "exti")] pub mod exti; +pub mod flash; #[cfg(fmc)] pub mod fmc; +#[cfg(hrtim)] +pub mod hrtim; #[cfg(i2c)] pub mod i2c; - -#[cfg(crc)] -pub mod crc; -pub mod flash; #[cfg(all(spi_v1, rcc_f4))] pub mod i2s; #[cfg(stm32wb)] pub mod ipcc; -pub mod pwm; +#[cfg(feature = "low-power")] +pub mod low_power; #[cfg(quadspi)] pub mod qspi; #[cfg(rng)] @@ -79,7 +84,7 @@ pub use crate::_generated::interrupt; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -103,7 +108,7 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use stm32_metapac as pac; #[cfg(not(feature = "unstable-pac"))] diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs new file mode 100644 index 00000000..7814fa38 --- /dev/null +++ b/embassy-stm32/src/low_power.rs @@ -0,0 +1,139 @@ +use core::arch::asm; +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; + +const THREAD_PENDER: usize = usize::MAX; +const THRESHOLD: Duration = Duration::from_millis(500); + +use crate::rtc::{Rtc, RtcInstant}; + +static mut RTC: Option<&'static Rtc> = None; + +foreach_interrupt! { + (RTC, rtc, $block:ident, WKUP, $irq:ident) => { + #[interrupt] + unsafe fn $irq() { + Executor::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() +} + +/// Thread mode executor, using WFE/SEV. +/// +/// This is the simplest and most common kind of executor. It runs on +/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction +/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction +/// is executed, to make the `WFE` exit from sleep and poll the task. +/// +/// This executor allows for ultra low power consumption for chips where `WFE` +/// triggers low-power sleep without extra steps. If your chip requires extra steps, +/// you may use [`raw::Executor`] directly to program custom behavior. +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(THREAD_PENDER as *mut ()), + not_send: PhantomData, + } + } + + unsafe fn on_wakeup_irq() { + info!("on wakeup irq"); + + cortex_m::asm::bkpt(); + } + + fn time_until_next_alarm(&self) -> Duration { + Duration::from_secs(3) + } + + fn get_scb() -> SCB { + unsafe { cortex_m::Peripherals::steal() }.SCB + } + + fn configure_pwr(&self) { + trace!("configure_pwr"); + + if !low_power_ready() { + return; + } + + let time_until_next_alarm = self.time_until_next_alarm(); + if time_until_next_alarm < THRESHOLD { + 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(); + }); + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + self.configure_pwr(); + asm!("wfe"); + }; + } + } +} diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs deleted file mode 100644 index 5aba2663..00000000 --- a/embassy-stm32/src/pwm/mod.rs +++ /dev/null @@ -1,269 +0,0 @@ -pub mod complementary_pwm; -pub mod simple_pwm; - -use stm32_metapac::timer::vals::Ckd; - -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::sealed::*; -} - -#[derive(Clone, Copy)] -pub enum Channel { - Ch1, - Ch2, - Ch3, - Ch4, -} - -impl Channel { - pub fn raw(&self) -> usize { - match self { - Channel::Ch1 => 0, - Channel::Ch2 => 1, - Channel::Ch3 => 2, - Channel::Ch4 => 3, - } - } -} - -#[derive(Clone, Copy)] -pub enum OutputCompareMode { - Frozen, - ActiveOnMatch, - InactiveOnMatch, - Toggle, - ForceInactive, - ForceActive, - PwmMode1, - PwmMode2, -} - -impl From for stm32_metapac::timer::vals::Ocm { - fn from(mode: OutputCompareMode) -> Self { - match mode { - OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, - OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, - OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, - OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, - OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, - OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, - OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, - OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance { - /// Global output enable. Does not do anything on non-advanced timers. - fn enable_outputs(&mut self, enable: bool); - - fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); - - fn enable_channel(&mut self, channel: Channel, enable: bool); - - fn set_compare_value(&mut self, channel: Channel, value: u16); - - fn get_max_compare_value(&self) -> u16; - } - - pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { - fn set_dead_time_clock_division(&mut self, value: Ckd); - - fn set_dead_time_value(&mut self, value: u8); - - fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); - } - - pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { - fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); - - fn enable_channel(&mut self, channel: Channel, enable: bool); - - fn set_compare_value(&mut self, channel: Channel, value: u32); - - fn get_max_compare_value(&self) -> u32; - } -} - -pub trait CaptureCompare16bitInstance: - sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static -{ -} - -pub trait ComplementaryCaptureCompare16bitInstance: - sealed::ComplementaryCaptureCompare16bitInstance + crate::timer::AdvancedControlInstance + 'static -{ -} - -pub trait CaptureCompare32bitInstance: - sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static -{ -} - -#[allow(unused)] -macro_rules! impl_compare_capable_16bit { - ($inst:ident) => { - impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self, _enable: bool) {} - - fn set_output_compare_mode(&mut self, channel: crate::pwm::Channel, mode: OutputCompareMode) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - let r = Self::regs_gp16(); - let raw_channel: usize = channel.raw(); - r.ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16() - .ccer() - .modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u16) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u16 { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16().arr().read().arr() - } - } - }; -} - -foreach_interrupt! { - ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { - impl_compare_capable_16bit!($inst); - - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - }; - - ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { - impl_compare_capable_16bit!($inst); - impl crate::pwm::sealed::CaptureCompare32bitInstance for crate::peripherals::$inst { - fn set_output_compare_mode( - &mut self, - channel: crate::pwm::Channel, - mode: OutputCompareMode, - ) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - let raw_channel = channel.raw(); - Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u32) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u32 { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().arr().read().arr() as u32 - } - } - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - impl CaptureCompare32bitInstance for crate::peripherals::$inst { - - } - }; - - ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { - impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - let r = Self::regs_advanced(); - r.bdtr().modify(|w| w.set_moe(enable)); - } - - fn set_output_compare_mode( - &mut self, - channel: crate::pwm::Channel, - mode: OutputCompareMode, - ) { - use crate::timer::sealed::AdvancedControlInstance; - let r = Self::regs_advanced(); - let raw_channel: usize = channel.raw(); - r.ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccer() - .modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u16) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccr(channel.raw()) - .modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u16 { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().arr().read().arr() - } - } - - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - - impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { - fn set_dead_time_clock_division(&mut self, value: Ckd) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); - } - - fn set_dead_time_value(&mut self, value: u8) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); - } - - fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccer() - .modify(|w| w.set_ccne(channel.raw(), enable)); - } - } - - impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { - - } - }; -} - -pin_trait!(Channel1Pin, CaptureCompare16bitInstance); -pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel2Pin, CaptureCompare16bitInstance); -pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel3Pin, CaptureCompare16bitInstance); -pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel4Pin, CaptureCompare16bitInstance); -pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance); -pin_trait!(BreakInputPin, CaptureCompare16bitInstance); -pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index e9db934b..32382fb2 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -1,332 +1,332 @@ -#![macro_use] - -pub mod enums; - -use embassy_hal_common::{into_ref, PeripheralRef}; -use enums::*; - -use crate::dma::Transfer; -use crate::gpio::sealed::AFType; -use crate::gpio::AnyPin; -use crate::pac::quadspi::Quadspi as Regs; -use crate::rcc::RccPeripheral; -use crate::{peripherals, Peripheral}; - -pub struct TransferConfig { - /// Instraction width (IMODE) - pub iwidth: QspiWidth, - /// Address width (ADMODE) - pub awidth: QspiWidth, - /// Data width (DMODE) - pub dwidth: QspiWidth, - /// Instruction Id - pub instruction: u8, - /// Flash memory address - pub address: Option, - /// Number of dummy cycles (DCYC) - pub dummy: DummyCycles, - /// Length of data - pub data_len: Option, -} - -impl Default for TransferConfig { - fn default() -> Self { - Self { - iwidth: QspiWidth::NONE, - awidth: QspiWidth::NONE, - dwidth: QspiWidth::NONE, - instruction: 0, - address: None, - dummy: DummyCycles::_0, - data_len: None, - } - } -} - -pub struct Config { - /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. - /// If you need other value the whose predefined use `Other` variant. - pub memory_size: MemorySize, - /// Address size (8/16/24/32-bit) - pub address_size: AddressSize, - /// Scalar factor for generating CLK [0-255] - pub prescaler: u8, - /// Number of bytes to trigger FIFO threshold flag. - pub fifo_threshold: FIFOThresholdLevel, - /// Minimum number of cycles that chip select must be high between issued commands - pub cs_high_time: ChipSelectHightTime, -} - -impl Default for Config { - fn default() -> Self { - Self { - memory_size: MemorySize::Other(0), - address_size: AddressSize::_24bit, - prescaler: 128, - fifo_threshold: FIFOThresholdLevel::_17Bytes, - cs_high_time: ChipSelectHightTime::_5Cycle, - } - } -} - -#[allow(dead_code)] -pub struct Qspi<'d, T: Instance, Dma> { - _peri: PeripheralRef<'d, T>, - sck: Option>, - d0: Option>, - d1: Option>, - d2: Option>, - d3: Option>, - nss: Option>, - dma: PeripheralRef<'d, Dma>, - config: Config, -} - -impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { - pub fn new( - peri: impl Peripheral

+ 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - sck: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, d0, d1, d2, d3, sck, nss); - - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af(nss.af_num(), AFType::OutputPushPull); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af(d0.af_num(), AFType::OutputPushPull); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af(d1.af_num(), AFType::OutputPushPull); - d1.set_speed(crate::gpio::Speed::VeryHigh); - d2.set_as_af(d2.af_num(), AFType::OutputPushPull); - d2.set_speed(crate::gpio::Speed::VeryHigh); - d3.set_as_af(d3.af_num(), AFType::OutputPushPull); - d3.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - Some(d2.map_into()), - Some(d3.map_into()), - Some(sck.map_into()), - Some(nss.map_into()), - dma, - config, - ) - } - - fn new_inner( - peri: impl Peripheral

+ 'd, - d0: Option>, - d1: Option>, - d2: Option>, - d3: Option>, - sck: Option>, - nss: Option>, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, dma); - - T::enable(); - T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); - - while T::REGS.sr().read().busy() {} - - T::REGS.cr().write(|w| { - w.set_prescaler(config.prescaler); - w.set_en(true); - }); - T::REGS.dcr().write(|w| { - w.set_fsize(config.memory_size.into()); - w.set_csht(config.cs_high_time.into()); - w.set_ckmode(false); - }); - - Self { - _peri: peri, - sck, - d0, - d1, - d2, - d3, - nss, - dma, - config, - } - } - - pub fn command(&mut self, transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - if let Some(len) = transaction.data_len { - let current_ar = T::REGS.ar().read().address(); - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); - - for idx in 0..len { - while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; - } - } - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - if let Some(len) = transaction.data_len { - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); - - for idx in 0..len { - while !T::REGS.sr().read().ftf() {} - unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; - } - } - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) - where - Dma: QuadDma, - { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - let current_ar = T::REGS.ar().read().address(); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_read( - &mut self.dma, - request, - T::REGS.dr().as_ptr() as *mut u8, - buf, - Default::default(), - ) - }; - - T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); - } - - pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) - where - Dma: QuadDma, - { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_write( - &mut self.dma, - request, - buf, - T::REGS.dr().as_ptr() as *mut u8, - Default::default(), - ) - }; - - T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); - } - - fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { - T::REGS.fcr().modify(|v| { - v.set_csmf(true); - v.set_ctcf(true); - v.set_ctef(true); - v.set_ctof(true); - }); - - while T::REGS.sr().read().busy() {} - - if let Some(len) = transaction.data_len { - T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); - } - - T::REGS.ccr().write(|v| { - v.set_fmode(fmode.into()); - v.set_imode(transaction.iwidth.into()); - v.set_instruction(transaction.instruction); - v.set_admode(transaction.awidth.into()); - v.set_adsize(self.config.address_size.into()); - v.set_dmode(transaction.dwidth.into()); - v.set_abmode(QspiWidth::NONE.into()); - v.set_dcyc(transaction.dummy.into()); - }); - - if let Some(addr) = transaction.address { - T::REGS.ar().write(|v| { - v.set_address(addr); - }); - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - const REGS: Regs; - } -} - -pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} - -pin_trait!(SckPin, Instance); -pin_trait!(D0Pin, Instance); -pin_trait!(D1Pin, Instance); -pin_trait!(D2Pin, Instance); -pin_trait!(D3Pin, Instance); -pin_trait!(NSSPin, Instance); - -dma_trait!(QuadDma, Instance); - -foreach_peripheral!( - (quadspi, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { - const REGS: Regs = crate::pac::$inst; - } - - impl Instance for peripherals::$inst {} - }; -); +#![macro_use] + +pub mod enums; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +use enums::*; + +use crate::dma::Transfer; +use crate::gpio::sealed::AFType; +use crate::gpio::AnyPin; +use crate::pac::quadspi::Quadspi as Regs; +use crate::rcc::RccPeripheral; +use crate::{peripherals, Peripheral}; + +pub struct TransferConfig { + /// Instraction width (IMODE) + pub iwidth: QspiWidth, + /// Address width (ADMODE) + pub awidth: QspiWidth, + /// Data width (DMODE) + pub dwidth: QspiWidth, + /// Instruction Id + pub instruction: u8, + /// Flash memory address + pub address: Option, + /// Number of dummy cycles (DCYC) + pub dummy: DummyCycles, + /// Length of data + pub data_len: Option, +} + +impl Default for TransferConfig { + fn default() -> Self { + Self { + iwidth: QspiWidth::NONE, + awidth: QspiWidth::NONE, + dwidth: QspiWidth::NONE, + instruction: 0, + address: None, + dummy: DummyCycles::_0, + data_len: None, + } + } +} + +pub struct Config { + /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. + /// If you need other value the whose predefined use `Other` variant. + pub memory_size: MemorySize, + /// Address size (8/16/24/32-bit) + pub address_size: AddressSize, + /// Scalar factor for generating CLK [0-255] + pub prescaler: u8, + /// Number of bytes to trigger FIFO threshold flag. + pub fifo_threshold: FIFOThresholdLevel, + /// Minimum number of cycles that chip select must be high between issued commands + pub cs_high_time: ChipSelectHightTime, +} + +impl Default for Config { + fn default() -> Self { + Self { + memory_size: MemorySize::Other(0), + address_size: AddressSize::_24bit, + prescaler: 128, + fifo_threshold: FIFOThresholdLevel::_17Bytes, + cs_high_time: ChipSelectHightTime::_5Cycle, + } + } +} + +#[allow(dead_code)] +pub struct Qspi<'d, T: Instance, Dma> { + _peri: PeripheralRef<'d, T>, + sck: Option>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + nss: Option>, + dma: PeripheralRef<'d, Dma>, + config: Config, +} + +impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { + pub fn new( + peri: impl Peripheral

+ 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + sck: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, d0, d1, d2, d3, sck, nss); + + sck.set_as_af(sck.af_num(), AFType::OutputPushPull); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af(nss.af_num(), AFType::OutputPushPull); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af(d0.af_num(), AFType::OutputPushPull); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af(d1.af_num(), AFType::OutputPushPull); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af(d2.af_num(), AFType::OutputPushPull); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af(d3.af_num(), AFType::OutputPushPull); + d3.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + Some(sck.map_into()), + Some(nss.map_into()), + dma, + config, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + sck: Option>, + nss: Option>, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, dma); + + T::enable(); + T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); + + while T::REGS.sr().read().busy() {} + + T::REGS.cr().write(|w| { + w.set_prescaler(config.prescaler); + w.set_en(true); + }); + T::REGS.dcr().write(|w| { + w.set_fsize(config.memory_size.into()); + w.set_csht(config.cs_high_time.into()); + w.set_ckmode(false); + }); + + Self { + _peri: peri, + sck, + d0, + d1, + d2, + d3, + nss, + dma, + config, + } + } + + pub fn command(&mut self, transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + if let Some(len) = transaction.data_len { + let current_ar = T::REGS.ar().read().address(); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + for idx in 0..len { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + if let Some(len) = transaction.data_len { + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); + + for idx in 0..len { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) + where + Dma: QuadDma, + { + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + let current_ar = T::REGS.ar().read().address(); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().as_ptr() as *mut u8, + buf, + Default::default(), + ) + }; + + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + transfer.blocking_wait(); + } + + pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) + where + Dma: QuadDma, + { + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().as_ptr() as *mut u8, + Default::default(), + ) + }; + + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + transfer.blocking_wait(); + } + + fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { + T::REGS.fcr().modify(|v| { + v.set_csmf(true); + v.set_ctcf(true); + v.set_ctef(true); + v.set_ctof(true); + }); + + while T::REGS.sr().read().busy() {} + + if let Some(len) = transaction.data_len { + T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); + } + + T::REGS.ccr().write(|v| { + v.set_fmode(fmode.into()); + v.set_imode(transaction.iwidth.into()); + v.set_instruction(transaction.instruction); + v.set_admode(transaction.awidth.into()); + v.set_adsize(self.config.address_size.into()); + v.set_dmode(transaction.dwidth.into()); + v.set_abmode(QspiWidth::NONE.into()); + v.set_dcyc(transaction.dummy.into()); + }); + + if let Some(addr) = transaction.address { + T::REGS.ar().write(|v| { + v.set_address(addr); + }); + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + const REGS: Regs; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} + +pin_trait!(SckPin, Instance); +pin_trait!(D0Pin, Instance); +pin_trait!(D1Pin, Instance); +pin_trait!(D2Pin, Instance); +pin_trait!(D3Pin, Instance); +pin_trait!(NSSPin, Instance); + +dma_trait!(QuadDma, Instance); + +foreach_peripheral!( + (quadspi, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index df6e9047..6a932634 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -1,5 +1,6 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw}; +use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -45,58 +46,6 @@ impl Into for HSIPrescaler { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -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, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, diff --git a/embassy-stm32/src/rcc/common.rs b/embassy-stm32/src/rcc/common.rs new file mode 100644 index 00000000..62736a43 --- /dev/null +++ b/embassy-stm32/src/rcc/common.rs @@ -0,0 +1,174 @@ +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/f2.rs b/embassy-stm32/src/rcc/f2.rs index 1525cc3c..ec4ed99b 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -1,8 +1,9 @@ use core::convert::TryFrom; use core::ops::{Div, Mul}; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{Hpre, Pllp, Pllsrc, Ppre, Sw}; +use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -58,7 +59,7 @@ impl Default for PLLConfig { impl PLLConfig { pub fn clocks(&self, src_freq: Hertz) -> PLLClocks { let in_freq = src_freq / self.pre_div; - let vco_freq = src_freq * self.mul / self.pre_div; + let vco_freq = Hertz((src_freq.0 as u64 * self.mul.0 as u64 / self.pre_div.0 as u64) as u32); let main_freq = vco_freq / self.main_div; let pll48_freq = vco_freq / self.pll48_div; PLLClocks { @@ -200,114 +201,15 @@ pub struct PLLClocks { pub pll48_freq: Hertz, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} +pub use super::common::VoltageScale; -impl Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: AHBPrescaler) -> Self::Output { - let divisor = match rhs { - 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, - }; - Hertz(self.0 / divisor) - } -} - -/// 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) - } -} - -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, - } - } -} - -/// Voltage Range -/// -/// Represents the system supply voltage range -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageRange { - /// 1.8 to 3.6 V - Min1V8, - /// 2.1 to 3.6 V - Min2V1, - /// 2.4 to 3.6 V - Min2V4, - /// 2.7 to 3.6 V - Min2V7, -} - -impl VoltageRange { +impl VoltageScale { const fn wait_states(&self, ahb_freq: Hertz) -> Option { let ahb_freq = ahb_freq.0; // Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock // frequency match self { - VoltageRange::Min1V8 => { + VoltageScale::Scale3 => { if ahb_freq <= 16_000_000 { Some(Latency::WS0) } else if ahb_freq <= 32_000_000 { @@ -328,7 +230,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V1 => { + VoltageScale::Scale2 => { if ahb_freq <= 18_000_000 { Some(Latency::WS0) } else if ahb_freq <= 36_000_000 { @@ -347,7 +249,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V4 => { + VoltageScale::Scale1 => { if ahb_freq <= 24_000_000 { Some(Latency::WS0) } else if ahb_freq <= 48_000_000 { @@ -362,7 +264,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V7 => { + VoltageScale::Scale0 => { if ahb_freq <= 30_000_000 { Some(Latency::WS0) } else if ahb_freq <= 60_000_000 { @@ -386,7 +288,7 @@ pub struct Config { pub pll_mux: PLLSrc, pub pll: PLLConfig, pub mux: ClockSrc, - pub voltage: VoltageRange, + pub voltage: VoltageScale, pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, @@ -400,7 +302,7 @@ impl Default for Config { hsi: true, pll_mux: PLLSrc::HSI, pll: PLLConfig::default(), - voltage: VoltageRange::Min1V8, + voltage: VoltageScale::Scale3, mux: ClockSrc::HSI, ahb_pre: AHBPrescaler::NotDivided, apb1_pre: APBPrescaler::NotDivided, @@ -476,22 +378,6 @@ pub(crate) unsafe fn init(config: Config) { // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions assert!(ahb_freq <= Hertz(120_000_000)); - let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq)); - FLASH.acr().modify(|w| w.set_latency(flash_ws)); - - RCC.cfgr().modify(|w| { - w.set_sw(sw.into()); - w.set_hpre(config.ahb_pre.into()); - w.set_ppre1(config.apb1_pre.into()); - w.set_ppre2(config.apb2_pre.into()); - }); - while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} - - // Turn off HSI to save power if we don't need it - if !config.hsi { - RCC.cr().modify(|w| w.set_hsion(false)); - } - let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { @@ -512,6 +398,22 @@ pub(crate) unsafe fn init(config: Config) { // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions assert!(apb2_freq <= Hertz(60_000_000)); + let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq)); + FLASH.acr().modify(|w| w.set_latency(flash_ws)); + + RCC.cfgr().modify(|w| { + w.set_sw(sw.into()); + w.set_hpre(config.ahb_pre.into()); + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} + + // Turn off HSI to save power if we don't need it + if !config.hsi { + RCC.cr().modify(|w| w.set_hsion(false)); + } + 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 2deee80d..321270a7 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -264,6 +264,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { } #[inline] +#[allow(unused_variables)] fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option) -> Usbpre { cfg_if::cfg_if! { // Some chips do not have USB diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index b8447044..ee9cb289 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre}; use super::sealed::RccPeripheral; @@ -9,6 +9,7 @@ use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; +use crate::rtc::{Rtc, RtcClockSource}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -33,6 +34,7 @@ pub struct Config { pub plli2s: Option, pub pll48: bool, + pub rtc: Option, } #[cfg(stm32f410)] @@ -459,6 +461,23 @@ 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); + }); + + let rtc = match config.rtc { + Some(RtcClockSource::LSI) => Some(LSI_FREQ), + _ => None, + }; + set_freqs(Clocks { sys: Hertz(sysclk), apb1: Hertz(pclk1), @@ -478,6 +497,8 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] pllsai: None, + + rtc: rtc, }); } diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 5e3a7911..bf2d5199 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -1,5 +1,6 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{self, Hpre, Hsidiv, Ppre, Sw}; +use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -172,58 +173,6 @@ impl From for u32 { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -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, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, @@ -425,25 +374,14 @@ pub(crate) unsafe fn init(config: Config) { FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); } - let ahb_div = match config.ahb_pre { - AHBPrescaler::NotDivided => 1, - AHBPrescaler::Div2 => 2, - AHBPrescaler::Div4 => 4, - AHBPrescaler::Div8 => 8, - AHBPrescaler::Div16 => 16, - AHBPrescaler::Div64 => 64, - AHBPrescaler::Div128 => 128, - AHBPrescaler::Div256 => 256, - AHBPrescaler::Div512 => 512, - }; - let ahb_freq = sys_clk / ahb_div; + let ahb_freq = Hertz(sys_clk) / config.ahb_pre; let (apb_freq, apb_tim_freq) = match config.apb_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + APBPrescaler::NotDivided => (ahb_freq.0, ahb_freq.0), pre => { let pre: Ppre = pre.into(); let pre: u8 = 1 << (pre.to_bits() - 3); - let freq = ahb_freq / pre as u32; + let freq = ahb_freq.0 / pre as u32; (freq, freq * 2) } }; @@ -455,7 +393,7 @@ pub(crate) unsafe fn init(config: Config) { set_freqs(Clocks { sys: Hertz(sys_clk), - ahb1: Hertz(ahb_freq), + ahb1: ahb_freq, apb1: Hertz(apb_freq), apb1_tim: Hertz(apb_tim_freq), }); diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index ff8f9754..dff04023 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -2,6 +2,7 @@ use stm32_metapac::flash::vals::Latency; use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; use stm32_metapac::FLASH; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::{PWR, RCC}; use crate::rcc::sealed::RccPeripheral; use crate::rcc::{set_freqs, Clocks}; @@ -21,30 +22,6 @@ pub enum ClockSrc { PLL, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy, Debug)] pub enum PllSrc { diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs index 7e2f75ab..2e72b193 100644 --- a/embassy-stm32/src/rcc/h5.rs +++ b/embassy-stm32/src/rcc/h5.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use stm32_metapac::rcc::vals::{Hpre, Ppre, Timpre}; +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}; @@ -26,21 +26,7 @@ const VCO_MAX: u32 = 420_000_000; const VCO_WIDE_MIN: u32 = 128_000_000; const VCO_WIDE_MAX: u32 = 560_000_000; -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - /// VOS 0 range VCORE 1.30V - 1.40V - Scale0, - /// VOS 1 range VCORE 1.15V - 1.26V - Scale1, - /// VOS 2 range VCORE 1.05V - 1.15V - Scale2, - /// VOS 3 range VCORE 0.95V - 1.05V - Scale3, -} +pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; pub enum HseMode { /// crystal/ceramic oscillator (HSEBYP=0) @@ -105,57 +91,7 @@ pub struct Pll { pub divr: Option, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -impl AHBPrescaler { - fn div(&self, clk: Hertz) -> Hertz { - match self { - Self::NotDivided => clk, - Self::Div2 => clk / 2u32, - Self::Div4 => clk / 4u32, - Self::Div8 => clk / 8u32, - Self::Div16 => clk / 16u32, - Self::Div64 => clk / 64u32, - Self::Div128 => clk / 128u32, - Self::Div256 => clk / 256u32, - Self::Div512 => clk / 512u32, - } - } -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - impl APBPrescaler { - fn div(&self, clk: Hertz) -> Hertz { - match self { - Self::NotDivided => clk, - Self::Div2 => clk / 2u32, - Self::Div4 => clk / 4u32, - Self::Div8 => clk / 8u32, - Self::Div16 => clk / 16u32, - } - } - 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 @@ -193,34 +129,6 @@ impl From for Timpre { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - 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, - } - } -} - /// Configuration of the core clocks #[non_exhaustive] pub struct Config { @@ -406,13 +314,13 @@ pub(crate) unsafe fn init(config: Config) { }; assert!(sys <= max_clk); - let hclk = config.ahb_pre.div(sys); + let hclk = sys / config.ahb_pre; - let apb1 = config.apb1_pre.div(hclk); + let apb1 = hclk / config.apb1_pre; let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler); - let apb2 = config.apb2_pre.div(hclk); + let apb2 = hclk / config.apb2_pre; let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler); - let apb3 = config.apb3_pre.div(hclk); + let apb3 = hclk / config.apb3_pre; flash_setup(hclk, config.voltage_scale); diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index 7e5cd0d1..7fb4fb95 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; pub use pll::PllConfig; use stm32_metapac::rcc::vals::{Mco1, Mco2}; @@ -24,21 +24,7 @@ pub const HSI48_FREQ: Hertz = Hertz(48_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - /// VOS 0 range VCORE 1.26V - 1.40V - Scale0, - /// VOS 1 range VCORE 1.15V - 1.26V - Scale1, - /// VOS 2 range VCORE 1.05V - 1.15V - Scale2, - /// VOS 3 range VCORE 0.95V - 1.05V - Scale3, -} +pub use super::common::VoltageScale; #[derive(Clone, Copy)] pub enum AdcClockSource { @@ -214,6 +200,7 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { // 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 { @@ -253,6 +240,50 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { }, }; + // 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) @@ -552,8 +583,6 @@ pub(crate) unsafe fn init(mut config: Config) { 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); - flash_setup(rcc_aclk, pwr_vos); - // Start switching clocks ------------------- // Ensure CSI is on and stable @@ -609,6 +638,8 @@ pub(crate) unsafe fn init(mut config: Config) { // 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)); diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs index 46a528e3..46b58ca7 100644 --- a/embassy-stm32/src/rcc/l0.rs +++ b/embassy-stm32/src/rcc/l0.rs @@ -1,3 +1,4 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; use crate::pac::RCC; #[cfg(crs)] @@ -70,30 +71,6 @@ pub enum PLLMul { Mul48, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -136,34 +113,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - 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, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/l1.rs b/embassy-stm32/src/rcc/l1.rs index 59a6eac8..bdfc5b87 100644 --- a/embassy-stm32/src/rcc/l1.rs +++ b/embassy-stm32/src/rcc/l1.rs @@ -1,3 +1,4 @@ +pub use super::common::{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}; @@ -68,30 +69,6 @@ pub enum PLLMul { Mul48, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -134,34 +111,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - 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, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 8a9b4adb..b34b8caa 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,14 +1,16 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use stm32_metapac::rcc::regs::Cfgr; use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; +pub use super::common::{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::rcc::{set_freqs, Clocks}; +use crate::rtc::{Rtc, RtcClockSource as RCS}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -78,30 +80,6 @@ pub enum PLLDiv { Div4, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -209,34 +187,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - 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, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { @@ -460,6 +410,8 @@ 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 @@ -476,6 +428,8 @@ pub(crate) unsafe fn init(config: Config) { // 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 @@ -483,6 +437,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSI is running while !RCC.csr().read().lsirdy() {} + + Rtc::set_clock_source(RCS::LSI); } } diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs index 16da65d5..a85e1488 100644 --- a/embassy-stm32/src/rcc/l5.rs +++ b/embassy-stm32/src/rcc/l5.rs @@ -1,5 +1,6 @@ use stm32_metapac::PWR; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -71,30 +72,6 @@ pub enum PLLDiv { Div4, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -202,34 +179,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - 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, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 886fc0b9..3c75923e 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -1,5 +1,7 @@ #![macro_use] +pub mod common; + use core::mem::MaybeUninit; use crate::time::Hertz; @@ -7,7 +9,7 @@ use crate::time::Hertz; #[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")] -#[cfg_attr(rcc_f3, path = "f3.rs")] +#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")] #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] #[cfg_attr(rcc_f7, path = "f7.rs")] #[cfg_attr(rcc_c0, path = "c0.rs")] @@ -24,6 +26,8 @@ use crate::time::Hertz; #[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] mod _version; pub use _version::*; +#[cfg(feature = "low-power")] +use atomic_polyfill::{AtomicU32, Ordering}; #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -71,6 +75,29 @@ pub struct Clocks { #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub adc: Option, + + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] + /// Set only if the lsi or lse is configured + pub rtc: Option, +} + +#[cfg(feature = "low-power")] +static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0); + +#[cfg(feature = "low-power")] +pub fn low_power_ready() -> bool { + CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 +} + +#[cfg(feature = "low-power")] +pub(crate) fn clock_refcount_add() { + // We don't check for overflow because constructing more than u32 peripherals is unlikely + CLOCK_REFCOUNT.fetch_add(1, Ordering::Relaxed); +} + +#[cfg(feature = "low-power")] +pub(crate) fn clock_refcount_sub() { + assert!(CLOCK_REFCOUNT.fetch_sub(1, Ordering::Relaxed) != 0); } /// Frozen clock frequencies @@ -78,6 +105,14 @@ pub struct Clocks { /// The existence of this value indicates that the clock configuration can no longer be changed static mut CLOCK_FREQS: MaybeUninit = MaybeUninit::uninit(); +#[cfg(stm32wb)] +/// RCC initialization function +pub(crate) unsafe fn init(config: Config) { + set_freqs(compute_clocks(&config)); + + configure_clocks(&config); +} + /// Sets the clock frequencies /// /// Safety: Sets a mutable global. diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index cfc07f06..b5feeb0c 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -1,5 +1,6 @@ -use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw}; +use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw}; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -10,19 +11,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - // Highest frequency - Range1, - Range2, - Range3, - // Lowest power - Range4, -} +pub use super::common::VoltageScale; #[derive(Copy, Clone)] pub enum ClockSrc { @@ -130,36 +119,6 @@ impl Into for PllM { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - 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, - } - } -} - impl Into for AHBPrescaler { fn into(self) -> u8 { match self { @@ -182,28 +141,6 @@ impl Default for AHBPrescaler { } } -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::NONE, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - impl Default for APBPrescaler { fn default() -> Self { APBPrescaler::NotDivided @@ -389,12 +326,12 @@ pub(crate) unsafe fn init(config: Config) { } // TODO make configurable - let power_vos = VoltageScale::Range4; + let power_vos = VoltageScale::Scale3; // states and programming delay let wait_states = match power_vos { // VOS 0 range VCORE 1.26V - 1.40V - VoltageScale::Range1 => { + VoltageScale::Scale0 => { if sys_clk < 32_000_000 { 0 } else if sys_clk < 64_000_000 { @@ -408,7 +345,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 1 range VCORE 1.15V - 1.26V - VoltageScale::Range2 => { + VoltageScale::Scale1 => { if sys_clk < 30_000_000 { 0 } else if sys_clk < 60_000_000 { @@ -420,7 +357,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 2 range VCORE 1.05V - 1.15V - VoltageScale::Range3 => { + VoltageScale::Scale2 => { if sys_clk < 24_000_000 { 0 } else if sys_clk < 48_000_000 { @@ -430,7 +367,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 3 range VCORE 0.95V - 1.05V - VoltageScale::Range4 => { + VoltageScale::Scale3 => { if sys_clk < 12_000_000 { 0 } else { diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index e6123821..ae708b37 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,6 +1,7 @@ -use crate::pac::RCC; -use crate::rcc::{set_freqs, Clocks}; -use crate::time::Hertz; +pub use super::common::{AHBPrescaler, APBPrescaler}; +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, /// and with the addition of the init function to configure a system clock. @@ -13,157 +14,366 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); +#[derive(Clone, Copy)] +pub enum HsePrescaler { + NotDivided, + Div2, +} + +impl From for bool { + fn from(value: HsePrescaler) -> Self { + match value { + HsePrescaler::NotDivided => false, + HsePrescaler::Div2 => true, + } + } +} + +pub struct Hse { + pub prediv: HsePrescaler, + + pub frequency: Hertz, +} + /// System clock mux source -#[derive(Clone, Copy)] -pub enum ClockSrc { - HSE(Hertz), - HSI16, -} - -/// AHB prescaler #[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div3, - Div4, - Div5, - Div6, - Div8, - Div10, - Div16, - Div32, - Div64, - Div128, - Div256, - Div512, +pub enum Sysclk { + /// MSI selected as sysclk + MSI, + /// HSI selected as sysclk + HSI, + /// HSE selected as sysclk + HSE, + /// PLL selected as sysclk + Pll, } -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -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 From for u8 { + fn from(value: Sysclk) -> Self { + match value { + Sysclk::MSI => 0b00, + Sysclk::HSI => 0b01, + Sysclk::HSE => 0b10, + Sysclk::Pll => 0b11, } } } -impl Into for AHBPrescaler { - fn into(self) -> u8 { - match self { - 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, +#[derive(Clone, Copy, PartialEq)] +pub enum PllSource { + Hsi, + Msi, + Hse, +} + +impl From for u8 { + fn from(value: PllSource) -> Self { + match value { + PllSource::Msi => 0b01, + PllSource::Hsi => 0b10, + PllSource::Hse => 0b11, } } } +pub enum Pll48Source { + PllSai, + Pll, + Msi, + Hsi48, +} + +pub struct PllMux { + /// Source clock selection. + pub source: PllSource, + + /// PLL pre-divider (DIVM). Must be between 1 and 63. + pub prediv: u8, +} + +pub struct Pll { + /// 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, +} + /// Clocks configutation pub struct Config { - pub mux: ClockSrc, - pub ahb_pre: AHBPrescaler, + pub hse: Option, + pub lse: Option, + pub sys: Sysclk, + pub mux: Option, + pub pll48: Option, + pub rtc: Option, + + pub pll: Option, + pub pllsai: Option, + + pub ahb1_pre: AHBPrescaler, + pub ahb2_pre: AHBPrescaler, + pub ahb3_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, } +pub const WPAN_DEFAULT: Config = Config { + hse: Some(Hse { + frequency: mhz(32), + prediv: HsePrescaler::NotDivided, + }), + lse: Some(khz(32)), + sys: Sysclk::Pll, + mux: Some(PllMux { + source: PllSource::Hse, + prediv: 2, + }), + pll48: None, + rtc: None, + + pll: Some(Pll { + mul: 12, + divp: Some(3), + divq: Some(4), + divr: Some(3), + }), + pllsai: None, + + ahb1_pre: AHBPrescaler::NotDivided, + ahb2_pre: AHBPrescaler::Div2, + ahb3_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, +}; + impl Default for Config { #[inline] fn default() -> Config { Config { - mux: ClockSrc::HSI16, - ahb_pre: AHBPrescaler::NotDivided, + hse: None, + lse: None, + sys: Sysclk::HSI, + mux: None, + pll48: None, + pll: None, + pllsai: None, + rtc: None, + + ahb1_pre: AHBPrescaler::NotDivided, + ahb2_pre: AHBPrescaler::NotDivided, + ahb3_pre: AHBPrescaler::NotDivided, apb1_pre: APBPrescaler::NotDivided, apb2_pre: APBPrescaler::NotDivided, } } } -pub(crate) unsafe fn init(config: Config) { - let (sys_clk, sw) = match config.mux { - ClockSrc::HSI16 => { - // Enable HSI16 - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - - (HSI_FREQ.0, 0x01) - } - ClockSrc::HSE(freq) => { - // Enable HSE - RCC.cr().write(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - - (freq.0, 0x02) - } - }; - - RCC.cfgr().modify(|w| { - w.set_sw(sw.into()); - w.set_hpre(config.ahb_pre.into()); - w.set_ppre1(config.apb1_pre.into()); - w.set_ppre2(config.apb2_pre.into()); +pub(crate) fn compute_clocks(config: &Config) -> Clocks { + let hse_clk = config.hse.as_ref().map(|hse| match hse.prediv { + HsePrescaler::NotDivided => hse.frequency, + HsePrescaler::Div2 => hse.frequency / 2u32, }); - let ahb_freq: u32 = match config.ahb_pre { + let mux_clk = config.mux.as_ref().map(|pll_mux| { + (match pll_mux.source { + PllSource::Hse => hse_clk.unwrap(), + PllSource::Hsi => HSI_FREQ, + _ => unreachable!(), + } / pll_mux.prediv) + }); + + let (pll_r, _pll_q, _pll_p) = match &config.pll { + Some(pll) => { + let pll_vco = mux_clk.unwrap() * pll.mul as u32; + + ( + pll.divr.map(|divr| pll_vco / divr), + pll.divq.map(|divq| pll_vco / divq), + pll.divp.map(|divp| pll_vco / divp), + ) + } + None => (None, None, None), + }; + + let sys_clk = match config.sys { + Sysclk::HSE => hse_clk.unwrap(), + Sysclk::HSI => HSI_FREQ, + Sysclk::Pll => pll_r.unwrap(), + _ => unreachable!(), + }; + + let ahb1_clk = match config.ahb1_pre { AHBPrescaler::NotDivided => sys_clk, pre => { let pre: u8 = pre.into(); - let pre = 1 << (pre as u32 - 7); + let pre = 1u32 << (pre as u32 - 7); sys_clk / pre } }; - let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + let ahb2_clk = match config.ahb2_pre { + AHBPrescaler::NotDivided => sys_clk, pre => { let pre: u8 = pre.into(); - let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / pre as u32; - (freq, freq * 2) + let pre = 1u32 << (pre as u32 - 7); + sys_clk / pre } }; - let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + let ahb3_clk = match config.ahb3_pre { + AHBPrescaler::NotDivided => sys_clk, pre => { let pre: u8 = pre.into(); - let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / pre as u32; - (freq, freq * 2) + let pre = 1u32 << (pre as u32 - 7); + sys_clk / pre } }; - set_freqs(Clocks { - sys: Hertz(sys_clk), - ahb1: Hertz(ahb_freq), - ahb2: Hertz(ahb_freq), - ahb3: Hertz(ahb_freq), - apb1: Hertz(apb1_freq), - apb2: Hertz(apb2_freq), - apb1_tim: Hertz(apb1_tim_freq), - apb2_tim: Hertz(apb2_tim_freq), - }); + let (apb1_clk, apb1_tim_clk) = match config.apb1_pre { + APBPrescaler::NotDivided => (ahb1_clk, ahb1_clk), + pre => { + let pre: u8 = pre.into(); + let pre: u8 = 1 << (pre - 3); + let freq = ahb1_clk / pre as u32; + (freq, freq * 2u32) + } + }; + + let (apb2_clk, apb2_tim_clk) = match config.apb2_pre { + APBPrescaler::NotDivided => (ahb1_clk, ahb1_clk), + pre => { + let pre: u8 = pre.into(); + let pre: u8 = 1 << (pre - 3); + let freq = ahb1_clk / pre as u32; + (freq, freq * 2u32) + } + }; + + let rtc_clk = match config.rtc { + Some(RtcClockSource::LSI) => Some(LSI_FREQ), + Some(RtcClockSource::LSE) => Some(config.lse.unwrap()), + _ => None, + }; + + Clocks { + sys: sys_clk, + ahb1: ahb1_clk, + ahb2: ahb2_clk, + ahb3: ahb3_clk, + apb1: apb1_clk, + apb2: apb2_clk, + apb1_tim: apb1_tim_clk, + apb2_tim: apb2_tim_clk, + rtc: rtc_clk, + } +} + +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 { + pll_mux.source == PllSource::Hsi + } else { + false + }; + + if needs_hsi || config.sys == Sysclk::HSI { + rcc.cr().modify(|w| { + w.set_hsion(true); + }); + + while !rcc.cr().read().hsirdy() {} + } + + let needs_lsi = if let Some(rtc_mux) = &config.rtc { + *rtc_mux == RtcClockSource::LSI + } else { + false + }; + + 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)); + } + _ => {} + } + + match &config.hse { + Some(hse) => { + rcc.cr().modify(|w| { + w.set_hsepre(hse.prediv.into()); + w.set_hseon(true); + }); + + while !rcc.cr().read().hserdy() {} + } + _ => {} + } + + match &config.mux { + Some(pll_mux) => { + rcc.pllcfgr().modify(|w| { + w.set_pllm(pll_mux.prediv); + w.set_pllsrc(pll_mux.source.into()); + }); + } + _ => {} + }; + + match &config.pll { + Some(pll) => { + rcc.pllcfgr().modify(|w| { + w.set_plln(pll.mul as u8); + pll.divp.map(|divp| { + w.set_pllpen(true); + w.set_pllp((divp - 1) as u8) + }); + pll.divq.map(|divq| { + w.set_pllqen(true); + w.set_pllq((divq - 1) as u8) + }); + pll.divr.map(|divr| { + // w.set_pllren(true); + w.set_pllr((divr - 1) as u8); + }); + }); + + rcc.cr().modify(|w| w.set_pllon(true)); + + while !rcc.cr().read().pllrdy() {} + } + _ => {} + } + + rcc.cfgr().modify(|w| { + w.set_sw(config.sys.into()); + w.set_hpre(config.ahb1_pre.into()); + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + + rcc.extcfgr().modify(|w| { + 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/wl.rs b/embassy-stm32/src/rcc/wl.rs index 6b69bb1c..f1dd2bd7 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -1,6 +1,8 @@ +pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; use crate::pac::pwr::vals::Dbp; use crate::pac::{FLASH, PWR, RCC}; 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, @@ -73,9 +75,9 @@ impl MSIRange { fn vos(&self) -> VoltageScale { if self > &MSIRange::Range8 { - VoltageScale::Range1 + VoltageScale::Scale0 } else { - VoltageScale::Range2 + VoltageScale::Scale1 } } } @@ -105,78 +107,6 @@ impl Into for MSIRange { } } -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - Range1, - Range2, -} - -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div3, - Div4, - Div5, - Div6, - Div8, - Div10, - Div16, - Div32, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -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 AHBPrescaler { - fn into(self) -> u8 { - match self { - 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, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, @@ -220,8 +150,8 @@ 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::Range2), - ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Range1), + ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Scale1), + ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Scale0), ClockSrc::MSI(range) => (range.freq(), 0x00, range.vos()), }; @@ -266,12 +196,12 @@ pub(crate) unsafe fn init(config: Config) { // Adjust flash latency let flash_clk_src_freq: u32 = shd_ahb_freq; let ws = match vos { - VoltageScale::Range1 => match flash_clk_src_freq { + VoltageScale::Scale0 => match flash_clk_src_freq { 0..=18_000_000 => 0b000, 18_000_001..=36_000_000 => 0b001, _ => 0b010, }, - VoltageScale::Range2 => match flash_clk_src_freq { + VoltageScale::Scale1 => match flash_clk_src_freq { 0..=6_000_000 => 0b000, 6_000_001..=12_000_000 => 0b001, _ => 0b010, @@ -300,6 +230,8 @@ pub(crate) unsafe fn init(config: Config) { // 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 @@ -307,6 +239,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSI is running while !RCC.csr().read().lsirdy() {} + + Rtc::set_clock_source(RCS::LSI); } } diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index b2faec53..30816e43 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -1,15 +1,17 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use rand_core::{CryptoRng, RngCore}; -use crate::{pac, peripherals, Peripheral}; +use crate::interrupt::typelevel::Interrupt; +use crate::{interrupt, pac, peripherals, Peripheral}; -pub(crate) static RNG_WAKER: AtomicWaker = AtomicWaker::new(); +static RNG_WAKER: AtomicWaker = AtomicWaker::new(); #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -17,68 +19,150 @@ pub enum Error { ClockError, } +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + T::regs().cr().modify(|reg| reg.set_ie(false)); + RNG_WAKER.wake(); + } + } +} + pub struct Rng<'d, T: Instance> { _inner: PeripheralRef<'d, T>, } impl<'d, T: Instance> Rng<'d, T> { - pub fn new(inner: impl Peripheral

+ 'd) -> Self { + pub fn new( + inner: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { T::enable(); T::reset(); into_ref!(inner); let mut random = Self { _inner: inner }; random.reset(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + random } + #[cfg(rng_v1)] pub fn reset(&mut self) { - // rng_v2 locks up on seed error, needs reset - #[cfg(rng_v2)] - if T::regs().sr().read().seis() { - T::reset(); - } - T::regs().cr().modify(|reg| { - reg.set_rngen(true); - reg.set_ie(true); + T::regs().cr().write(|reg| { + reg.set_rngen(false); }); T::regs().sr().modify(|reg| { reg.set_seis(false); reg.set_ceis(false); }); + T::regs().cr().modify(|reg| { + reg.set_rngen(true); + }); // Reference manual says to discard the first. let _ = self.next_u32(); } - pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - T::regs().cr().modify(|reg| { + #[cfg(not(rng_v1))] + pub fn reset(&mut self) { + T::regs().cr().write(|reg| { + reg.set_condrst(true); + reg.set_nistc(pac::rng::vals::Nistc::CUSTOM); + // set RNG config "A" according to reference manual + // this has to be written within the same write access as setting the CONDRST bit + reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIGA); + reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); + reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B); + reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIGA); + reg.set_ced(true); + reg.set_ie(false); reg.set_rngen(true); }); + T::regs().cr().write(|reg| { + reg.set_ced(false); + }); + // wait for CONDRST to be set + while !T::regs().cr().read().condrst() {} + // magic number must be written immediately before every read or write access to HTCR + T::regs().htcr().write(|w| w.set_htcfg(pac::rng::vals::Htcfg::MAGIC)); + // write recommended value according to reference manual + // note: HTCR can only be written during conditioning + T::regs() + .htcr() + .write(|w| w.set_htcfg(pac::rng::vals::Htcfg::RECOMMENDED)); + // finish conditioning + T::regs().cr().modify(|reg| { + reg.set_rngen(true); + reg.set_condrst(false); + }); + // wait for CONDRST to be reset + while T::regs().cr().read().condrst() {} + } + pub fn recover_seed_error(&mut self) -> () { + self.reset(); + // reset should also clear the SEIS flag + if T::regs().sr().read().seis() { + warn!("recovering from seed error failed"); + return; + } + // wait for SECS to be cleared by RNG + while T::regs().sr().read().secs() {} + } + + pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { for chunk in dest.chunks_mut(4) { - poll_fn(|cx| { - RNG_WAKER.register(cx.waker()); - T::regs().cr().modify(|reg| { - reg.set_ie(true); - }); - - let bits = T::regs().sr().read(); - - if bits.drdy() { - Poll::Ready(Ok(())) - } else if bits.seis() { - self.reset(); - Poll::Ready(Err(Error::SeedError)) - } else if bits.ceis() { - self.reset(); - Poll::Ready(Err(Error::ClockError)) - } else { - Poll::Pending + let 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 + // to leave retry-logic to the application + self.recover_seed_error(); + return Err(Error::SeedError); + } else if bits.ceis() { + // clock error detected, DR could still be used but keep it safe, + // clear the error and abort + T::regs().sr().modify(|sr| sr.set_ceis(false)); + return Err(Error::ClockError); + } else if bits.drdy() { + // DR can be read up to four times until the output buffer is empty + // DRDY is cleared automatically when that happens + let random_word = T::regs().dr().read(); + // reference manual: always check if DR is zero + if random_word == 0 { + return Err(Error::SeedError); } - }) - .await?; - let random_bytes = T::regs().dr().read().to_be_bytes(); - for (dest, src) in chunk.iter_mut().zip(random_bytes.iter()) { - *dest = *src + // write bytes to chunk + 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; } } @@ -129,57 +213,20 @@ pub(crate) mod sealed { } } -pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral {} +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { + type Interrupt: interrupt::typelevel::Interrupt; +} -foreach_peripheral!( - (rng, $inst:ident) => { - impl Instance for peripherals::$inst {} +foreach_interrupt!( + ($inst:ident, rng, RNG, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } impl sealed::Instance for peripherals::$inst { fn regs() -> crate::pac::rng::Rng { - crate::pac::RNG + crate::pac::$inst } } }; ); - -#[cfg(feature = "rt")] -macro_rules! irq { - ($irq:ident) => { - mod rng_irq { - use crate::interrupt; - - #[interrupt] - unsafe fn $irq() { - let bits = $crate::pac::RNG.sr().read(); - if bits.drdy() || bits.seis() || bits.ceis() { - $crate::pac::RNG.cr().write(|reg| reg.set_ie(false)); - $crate::rng::RNG_WAKER.wake(); - } - } - } - }; -} - -#[cfg(feature = "rt")] -foreach_interrupt!( - (RNG) => { - irq!(RNG); - }; - - (RNG_LPUART1) => { - irq!(RNG_LPUART1); - }; - - (AES_RNG_LPUART1) => { - irq!(AES_RNG_LPUART1); - }; - - (AES_RNG) => { - irq!(AES_RNG); - }; - - (HASH_RNG) => { - irq!(HASH_RNG); - }; -); diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 12a2ac79..a6102077 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -1,5 +1,4 @@ //! RTC peripheral abstraction -use core::marker::PhantomData; mod datetime; pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; @@ -14,8 +13,12 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; )] #[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] mod _version; +#[allow(unused_imports)] pub use _version::*; -use embassy_hal_common::Peripheral; +use embassy_hal_internal::Peripheral; + +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; /// Errors that can occur on methods on [RtcClock] #[derive(Clone, Debug, PartialEq, Eq)] @@ -28,8 +31,7 @@ pub enum RtcError { } /// RTC Abstraction -pub struct Rtc<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, +pub struct Rtc { rtc_config: RtcConfig, } @@ -48,8 +50,6 @@ pub enum RtcClockSource { #[derive(Copy, Clone, PartialEq)] pub struct RtcConfig { - /// RTC clock source - clock_config: RtcClockSource, /// Asynchronous prescaler factor /// This is the asynchronous division factor: /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) @@ -67,7 +67,6 @@ impl Default for RtcConfig { /// Raw sub-seconds in 1/256. fn default() -> Self { RtcConfig { - clock_config: RtcClockSource::LSI, async_prescaler: 127, sync_prescaler: 255, } @@ -75,12 +74,6 @@ impl Default for RtcConfig { } impl RtcConfig { - /// Sets the clock source of RTC config - pub fn clock_config(mut self, cfg: RtcClockSource) -> Self { - self.clock_config = cfg; - self - } - /// Set the asynchronous prescaler of RTC config pub fn async_prescaler(mut self, prescaler: u8) -> Self { self.async_prescaler = prescaler; @@ -111,16 +104,16 @@ impl Default for RtcCalibrationCyclePeriod { } } -impl<'d, T: Instance> Rtc<'d, T> { - pub fn new(_rtc: impl Peripheral

+ 'd, rtc_config: RtcConfig) -> Self { - T::enable_peripheral_clk(); +impl Rtc { + pub fn new(_rtc: impl Peripheral

, rtc_config: RtcConfig) -> Self { + RTC::enable_peripheral_clk(); - let mut rtc_struct = Self { - phantom: PhantomData, - rtc_config, - }; + let mut rtc_struct = Self { rtc_config }; - rtc_struct.apply_config(rtc_config); + Self::enable(); + + rtc_struct.configure(rtc_config); + rtc_struct.rtc_config = rtc_config; rtc_struct } @@ -143,7 +136,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. pub fn now(&self) -> Result { - let r = T::regs(); + let r = RTC::regs(); let tr = r.tr().read(); let second = bcd2_to_byte((tr.st(), tr.su())); let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); @@ -162,7 +155,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// Check if daylight savings time is active. pub fn get_daylight_savings(&self) -> bool { - let cr = T::regs().cr().read(); + let cr = RTC::regs().cr().read(); cr.bkp() } @@ -177,14 +170,14 @@ impl<'d, T: Instance> Rtc<'d, T> { self.rtc_config } - pub const BACKUP_REGISTER_COUNT: usize = T::BACKUP_REGISTER_COUNT; + pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT; /// Read content of the backup register. /// /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn read_backup_register(&self, register: usize) -> Option { - T::read_backup_register(&T::regs(), register) + RTC::read_backup_register(&RTC::regs(), register) } /// Set content of the backup register. @@ -192,7 +185,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn write_backup_register(&self, register: usize, value: u32) { - T::write_backup_register(&T::regs(), register, value) + RTC::write_backup_register(&RTC::regs(), register, value) } } @@ -243,5 +236,3 @@ pub(crate) mod sealed { // fn apply_config(&mut self, rtc_config: RtcConfig); } } - -pub trait Instance: sealed::Instance + 'static {} diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index a2eace6d..bcb127ec 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,18 +1,134 @@ use stm32_metapac::rtc::vals::{Init, Osel, Pol}; -use super::{sealed, Instance, RtcConfig}; +use super::{sealed, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; -impl<'d, T: Instance> super::Rtc<'d, T> { - /// Applies the RTC config - /// It this changes the RTC clock source the time will be reset - pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { - // Unlock the backup domain - let clock_config = rtc_config.clock_config as u8; +#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] +pub struct RtcInstant { + ssr: u16, + st: u8, +} - #[cfg(not(rtc_v2wb))] - use stm32_metapac::rcc::vals::Rtcsel; +#[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)] +#[derive(Clone, Copy, Debug)] +pub(crate) enum WakeupPrescaler { + Div2, + Div4, + Div8, + Div16, +} + +#[cfg(any(stm32wb, stm32f4))] +impl From for crate::pac::rtc::vals::Wucksel { + fn from(val: WakeupPrescaler) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + WakeupPrescaler::Div2 => Wucksel::DIV2, + WakeupPrescaler::Div4 => Wucksel::DIV4, + WakeupPrescaler::Div8 => Wucksel::DIV8, + WakeupPrescaler::Div16 => Wucksel::DIV16, + } + } +} + +#[cfg(any(stm32wb, stm32f4))] +impl From for WakeupPrescaler { + fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + Wucksel::DIV2 => WakeupPrescaler::Div2, + Wucksel::DIV4 => WakeupPrescaler::Div4, + Wucksel::DIV8 => WakeupPrescaler::Div8, + Wucksel::DIV16 => WakeupPrescaler::Div16, + _ => unreachable!(), + } + } +} + +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 { + *[ + WakeupPrescaler::Div2, + WakeupPrescaler::Div4, + WakeupPrescaler::Div8, + WakeupPrescaler::Div16, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= 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))] @@ -21,10 +137,98 @@ impl<'d, T: Instance> super::Rtc<'d, T> { // TODO: Missing from PAC for l0 and f0? #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] { - cr.modify(|w| w.set_dbp(true)); - while !cr.read().dbp() {} + 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 { + use embassy_time::{Duration, TICK_HZ}; + + use crate::rcc::get_freqs; + + let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; + + let rtc_ticks = requested_duration.as_ticks() * 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()); + + 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.cr().modify(|w| w.set_wute(true)); + }); + + RtcInstant::now() + } + + #[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..."); + + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wute(false)); + regs.isr().modify(|w| w.set_wutf(false)); + }); + + 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))] @@ -33,15 +237,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - #[cfg(rtc_v2wb)] - let rtcsel = reg.rtcsel(); - #[cfg(not(rtc_v2wb))] - let rtcsel = reg.rtcsel().to_bits(); + if !reg.rtcen() { + Self::unlock_registers(); - if !reg.rtcen() || rtcsel != clock_config { - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + #[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))] @@ -52,12 +252,8 @@ impl<'d, T: Instance> super::Rtc<'d, T> { #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] w.set_bdrst(false); - // Select RTC source - #[cfg(not(rtc_v2wb))] - w.set_rtcsel(Rtcsel::from_bits(clock_config)); - #[cfg(rtc_v2wb)] - w.set_rtcsel(clock_config); w.set_rtcen(true); + w.set_rtcsel(reg.rtcsel()); // Restore bcdr #[cfg(any(rtc_v2l4, rtc_v2wb))] @@ -72,7 +268,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { 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) { self.write(true, |rtc| { rtc.cr().modify(|w| { #[cfg(rtc_v2f2)] @@ -88,8 +288,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_prediv_a(rtc_config.async_prescaler); }); }); - - self.rtc_config = rtc_config; } /// Calibrate the clock drift. @@ -157,11 +355,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { }) } - pub(super) fn write(&mut self, init_mode: bool, f: F) -> R + pub(super) fn write(&self, init_mode: bool, f: F) -> R where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { - let r = T::regs(); + let r = RTC::regs(); // Disable write protection. // This is safe, as we're only writin the correct and expected values. r.wpr().write(|w| w.set_key(0xca)); @@ -201,6 +399,11 @@ impl sealed::Instance for crate::peripherals::RTC { // 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(); + } } fn read_backup_register(rtc: &Rtc, register: usize) -> Option { @@ -217,5 +420,3 @@ impl sealed::Instance for crate::peripherals::RTC { } } } - -impl Instance for crate::peripherals::RTC {} diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 8ef0ec51..3297303e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -1,44 +1,62 @@ use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; -use super::{sealed, Instance, RtcCalibrationCyclePeriod, RtcConfig}; +use super::{sealed, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; -impl<'d, T: Instance> super::Rtc<'d, T> { - /// Applies the RTC config - /// It this changes the RTC clock source the time will be reset - pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { +impl super::Rtc { + fn unlock_registers() { // Unlock the backup domain #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} + 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; - crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); - while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} + 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 {} + } } + } - let reg = crate::pac::RCC.bdcr().read(); + #[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."); - let config_rtcsel = rtc_config.clock_config as u8; - #[cfg(not(any(rcc_wl5, rcc_wle)))] - let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(config_rtcsel); + if !reg.rtcen() { + Self::unlock_registers(); - if !reg.rtcen() || reg.rtcsel() != config_rtcsel { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + bdcr.modify(|w| w.set_bdrst(true)); - crate::pac::RCC.bdcr().modify(|w| { + bdcr.modify(|w| { // Reset w.set_bdrst(false); - // Select RTC source - w.set_rtcsel(config_rtcsel); - w.set_rtcen(true); + w.set_rtcsel(reg.rtcsel()); // Restore bcdr w.set_lscosel(reg.lscosel()); @@ -49,7 +67,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { 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) { self.write(true, |rtc| { rtc.cr().modify(|w| { w.set_fmt(Fmt::TWENTYFOURHOUR); @@ -69,8 +91,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_tampalrm_pu(TampalrmPu::NOPULLUP); }); }); - - self.rtc_config = rtc_config; } const RTC_CALR_MIN_PPM: f32 = -487.1; @@ -141,7 +161,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { - let r = T::regs(); + let r = RTC::regs(); // Disable write protection. // This is safe, as we're only writin the correct and expected values. r.wpr().write(|w| w.set_key(Key::DEACTIVATE1)); @@ -188,5 +208,3 @@ impl sealed::Instance for crate::peripherals::RTC { } } } - -impl Instance for crate::peripherals::RTC {} diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 698292bf..6b532363 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -6,8 +6,8 @@ use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; @@ -225,6 +225,9 @@ const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOp mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, fifo_threshold: Some(crate::dma::FifoThreshold::Full), + circular: false, + half_transfer_ir: false, + complete_transfer_ir: true, }; #[cfg(all(sdmmc_v1, not(dma)))] const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index d5f63f84..e2bc8d7f 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -4,7 +4,7 @@ use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use crate::dma::{slice_ptr_parts, word, Transfer}; @@ -36,6 +36,7 @@ pub enum BitOrder { pub struct Config { pub mode: Mode, pub bit_order: BitOrder, + pub frequency: Hertz, } impl Default for Config { @@ -43,6 +44,7 @@ impl Default for Config { Self { mode: MODE_0, bit_order: BitOrder::MsbFirst, + frequency: Hertz(1_000_000), } } } @@ -88,7 +90,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(peri, sck, mosi, miso); @@ -112,7 +113,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Some(miso.map_into()), txdma, rxdma, - freq, config, ) } @@ -123,7 +123,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, // TODO remove rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(sck, miso); @@ -139,7 +138,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Some(miso.map_into()), txdma, rxdma, - freq, config, ) } @@ -150,7 +148,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { mosi: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, // TODO remove - freq: Hertz, config: Config, ) -> Self { into_ref!(sck, mosi); @@ -166,7 +163,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { None, txdma, rxdma, - freq, config, ) } @@ -176,14 +172,13 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { mosi: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, // TODO: remove - freq: Hertz, config: Config, ) -> Self { into_ref!(mosi); mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); mosi.set_speed(crate::gpio::Speed::Medium); - Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) + Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, config) } #[cfg(stm32wl)] @@ -201,7 +196,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let mut config = Config::default(); config.mode = MODE_0; config.bit_order = BitOrder::MsbFirst; - Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + config.frequency = freq; + Self::new_inner(peri, None, None, None, txdma, rxdma, config) } #[allow(dead_code)] @@ -209,10 +205,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { - Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + Self::new_inner(peri, None, None, None, txdma, rxdma, config) } fn new_inner( @@ -222,13 +217,13 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: Option>, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(peri, txdma, rxdma); let pclk = T::frequency(); - let br = compute_baud_rate(pclk, freq.into()); + let freq = config.frequency; + let br = compute_baud_rate(pclk, freq); let cpha = config.raw_phase(); let cpol = config.raw_polarity(); @@ -334,19 +329,29 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let lsbfirst = config.raw_byte_order(); + let pclk = T::frequency(); + let freq = config.frequency; + let br = compute_baud_rate(pclk, freq); + #[cfg(any(spi_v1, spi_f1, spi_v2))] T::REGS.cr1().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); + w.set_br(br); w.set_lsbfirst(lsbfirst); }); #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cfg2().modify(|w| { - w.set_cpha(cpha); - w.set_cpol(cpol); - w.set_lsbfirst(lsbfirst); - }); + { + T::REGS.cfg2().modify(|w| { + w.set_cpha(cpha); + w.set_cpol(cpol); + w.set_lsbfirst(lsbfirst); + }); + T::REGS.cfg1().modify(|w| { + w.set_mbr(br); + }); + } } pub fn get_current_config(&self) -> Config { @@ -354,6 +359,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let cfg = T::REGS.cr1().read(); #[cfg(any(spi_v3, spi_v4, spi_v5))] let cfg = T::REGS.cfg2().read(); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + let cfg1 = T::REGS.cfg1().read(); + let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { Polarity::IdleLow } else { @@ -371,9 +379,18 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { BitOrder::MsbFirst }; + #[cfg(any(spi_v1, spi_f1, spi_v2))] + let br = cfg.br(); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + let br = cfg1.mbr(); + + let pclk = T::frequency(); + let frequency = compute_frequency(pclk, br); + Config { mode: Mode { polarity, phase }, bit_order, + frequency, } } @@ -430,7 +447,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { where Tx: TxDma, { - if data.len() == 0 { + if data.is_empty() { return Ok(()); } @@ -464,7 +481,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Tx: TxDma, Rx: RxDma, { - if data.len() == 0 { + if data.is_empty() { return Ok(()); } @@ -653,6 +670,21 @@ fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { Br::from_bits(val) } +fn compute_frequency(clocks: Hertz, br: Br) -> Hertz { + let div: u16 = match br { + Br::DIV2 => 2, + Br::DIV4 => 4, + Br::DIV8 => 8, + Br::DIV16 => 16, + Br::DIV32 => 32, + Br::DIV64 => 64, + Br::DIV128 => 128, + Br::DIV256 => 256, + }; + + clocks / div +} + trait RegsExt { fn tx_ptr(&self) -> *mut W; fn rx_ptr(&self) -> *mut W; @@ -811,7 +843,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { spin_until_rx_ready(regs)?; let rx_word = unsafe { ptr::read_volatile(regs.rx_ptr()) }; - return Ok(rx_word); + Ok(rx_word) } mod eh02 { @@ -946,7 +978,7 @@ mod word_impl { impl_word!(u16, vals::Dff::SIXTEENBIT); } -#[cfg(any(spi_v2))] +#[cfg(spi_v2)] mod word_impl { use super::*; diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs similarity index 87% rename from embassy-stm32/src/pwm/complementary_pwm.rs rename to embassy-stm32/src/timer/complementary_pwm.rs index 4d64d005..4f033e3a 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -1,13 +1,13 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::timer::vals::Ckd; use super::simple_pwm::*; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; @@ -17,13 +17,13 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { } macro_rules! complementary_channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { + ($new_chx:ident, $channel:ident, $pin_trait:ident) => { impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_as_af(pin.af_num(), output_type.into()); #[cfg(gpio_v2)] pin.set_speed(crate::gpio::Speed::VeryHigh); }); @@ -36,10 +36,10 @@ macro_rules! complementary_channel_impl { }; } -complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); -complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); -complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); -complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); +complementary_channel_impl!(new_ch1, Ch1, Channel1ComplementaryPin); +complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin); +complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); +complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); pub struct ComplementaryPwm<'d, T> { inner: PeripheralRef<'d, T>, @@ -100,14 +100,19 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { } pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() + self.inner.get_max_compare_value() + 1 } pub fn set_duty(&mut self, channel: Channel, duty: u16) { - assert!(duty < self.get_max_duty()); + assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } + pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + self.inner.set_output_polarity(channel, polarity); + self.inner.set_complementary_output_polarity(channel, polarity); + } + /// Set the dead time as a proportion of max_duty pub fn set_dead_time(&mut self, value: u16) { let (ckd, value) = compute_dead_time_value(value); @@ -186,9 +191,8 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) { error = this_error; } - match error { - 0 => break, - _ => {} + if error == 0 { + break; } } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 09b7a377..4ffb2a28 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,3 +1,6 @@ +pub mod complementary_pwm; +pub mod simple_pwm; + use stm32_metapac::timer::vals; use crate::interrupt; @@ -43,15 +46,144 @@ pub(crate) mod sealed { pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { fn regs_advanced() -> crate::pac::timer::TimAdv; } + + pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { + /// Global output enable. Does not do anything on non-advanced timers. + fn enable_outputs(&mut self, enable: bool); + + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + + fn enable_channel(&mut self, channel: Channel, enable: bool); + + fn set_compare_value(&mut self, channel: Channel, value: u16); + + fn get_max_compare_value(&self) -> u16; + } + + pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + + fn set_dead_time_clock_division(&mut self, value: vals::Ckd); + + fn set_dead_time_value(&mut self, value: u8); + + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); + } + + pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance { + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + + fn enable_channel(&mut self, channel: Channel, enable: bool); + + fn set_compare_value(&mut self, channel: Channel, value: u32); + + fn get_max_compare_value(&self) -> u32; + } } +#[derive(Clone, Copy)] +pub enum Channel { + Ch1, + Ch2, + Ch3, + Ch4, +} + +impl Channel { + pub fn raw(&self) -> usize { + match self { + Channel::Ch1 => 0, + Channel::Ch2 => 1, + Channel::Ch3 => 2, + Channel::Ch4 => 3, + } + } +} + +#[derive(Clone, Copy)] +pub enum OutputCompareMode { + Frozen, + ActiveOnMatch, + InactiveOnMatch, + Toggle, + ForceInactive, + ForceActive, + PwmMode1, + PwmMode2, +} + +impl From for stm32_metapac::timer::vals::Ocm { + fn from(mode: OutputCompareMode) -> Self { + match mode { + OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, + OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, + OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, + OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, + OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, + OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, + OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, + OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, + } + } +} + +#[derive(Clone, Copy)] +pub enum OutputPolarity { + ActiveHigh, + ActiveLow, +} + +impl From for bool { + fn from(mode: OutputPolarity) -> Self { + match mode { + OutputPolarity::ActiveHigh => false, + OutputPolarity::ActiveLow => true, + } + } +} + +pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} + pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} -pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} +pub trait CaptureCompare16bitInstance: + sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static +{ +} + +pub trait ComplementaryCaptureCompare16bitInstance: + sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static +{ +} + +pub trait CaptureCompare32bitInstance: + sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static +{ +} + +pin_trait!(Channel1Pin, CaptureCompare16bitInstance); +pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel2Pin, CaptureCompare16bitInstance); +pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel3Pin, CaptureCompare16bitInstance); +pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel4Pin, CaptureCompare16bitInstance); +pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance); +pin_trait!(BreakInputPin, CaptureCompare16bitInstance); +pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); #[allow(unused)] macro_rules! impl_basic_16bit_timer { @@ -140,33 +272,108 @@ macro_rules! impl_32bit_timer { }; } +#[allow(unused)] +macro_rules! impl_compare_capable_16bit { + ($inst:ident) => { + impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { + fn enable_outputs(&mut self, _enable: bool) {} + + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { + use sealed::GeneralPurpose16bitInstance; + let r = Self::regs_gp16(); + let raw_channel: usize = channel.raw(); + r.ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16() + .ccer() + .modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u16) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u16 { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16().arr().read().arr() + } + } + }; +} + foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl Basic16bitInstance for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl_compare_capable_16bit!($inst); + impl Basic16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { fn regs_gp16() -> crate::pac::timer::TimGp16 { crate::pac::$inst } } - - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } }; ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); + impl_32bit_timer!($inst); + impl_compare_capable_16bit!($inst); + impl Basic16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare32bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose32bitInstance for crate::peripherals::$inst {} - impl Basic16bitInstance for crate::peripherals::$inst { + impl sealed::CaptureCompare32bitInstance for crate::peripherals::$inst { + fn set_output_compare_mode( + &mut self, + channel: Channel, + mode: OutputCompareMode, + ) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u32) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u32 { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().arr().read().arr() as u32 + } } impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { @@ -174,21 +381,16 @@ foreach_interrupt! { unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } } } - - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } - - impl_32bit_timer!($inst); - - impl GeneralPurpose32bitInstance for crate::peripherals::$inst { - } }; ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl Basic16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} + impl AdvancedControlInstance for crate::peripherals::$inst {} impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { fn regs_gp16() -> crate::pac::timer::TimGp16 { @@ -196,16 +398,84 @@ foreach_interrupt! { } } - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } - impl sealed::AdvancedControlInstance for crate::peripherals::$inst { fn regs_advanced() -> crate::pac::timer::TimAdv { crate::pac::$inst } } - impl AdvancedControlInstance for crate::peripherals::$inst { + impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { + fn enable_outputs(&mut self, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + let r = Self::regs_advanced(); + r.bdtr().modify(|w| w.set_moe(enable)); + } + + fn set_output_compare_mode( + &mut self, + channel: Channel, + mode: OutputCompareMode, + ) { + use crate::timer::sealed::AdvancedControlInstance; + let r = Self::regs_advanced(); + let raw_channel: usize = channel.raw(); + r.ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u16) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccr(channel.raw()) + .modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u16 { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().arr().read().arr() + } } + + impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccnp(channel.raw(), polarity.into())); + } + + fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); + } + + fn set_dead_time_value(&mut self, value: u8) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); + } + + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccne(channel.raw(), enable)); + } + } + + }; } diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs similarity index 86% rename from embassy-stm32/src/pwm/simple_pwm.rs rename to embassy-stm32/src/timer/simple_pwm.rs index 995f59c2..9e28878b 100644 --- a/embassy-stm32/src/pwm/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -1,11 +1,11 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; @@ -22,11 +22,11 @@ pub struct PwmPin<'d, Perip, Channel> { macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_as_af(pin.af_num(), output_type.into()); #[cfg(gpio_v2)] pin.set_speed(crate::gpio::Speed::VeryHigh); }); @@ -97,11 +97,15 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { } pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() + self.inner.get_max_compare_value() + 1 } pub fn set_duty(&mut self, channel: Channel, duty: u16) { - assert!(duty < self.get_max_duty()); + assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } + + pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + self.inner.set_output_polarity(channel, polarity); + } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 433ad299..596d40bf 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::slice; use core::task::Poll; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use super::*; @@ -400,37 +400,37 @@ impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> { } } -impl embedded_io::Error for Error { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other +impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { + embedded_io_async::ErrorKind::Other } } -impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::ErrorType for BufferedUart<'d, T> { type Error = Error; } -impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartRx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } -impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartTx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } -impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::Read for BufferedUart<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.rx.read(buf).await } } -impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::Read for BufferedUartRx<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { Self::read(self, buf).await } } -impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::BufRead for BufferedUart<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { self.rx.fill_buf().await } @@ -440,7 +440,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> } } -impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::BufRead for BufferedUartRx<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { Self::fill_buf(self).await } @@ -450,7 +450,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } } -impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::Write for BufferedUart<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { self.tx.write(buf).await } @@ -460,7 +460,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { } } -impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::Write for BufferedUartTx<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { Self::write(self, buf).await } @@ -470,19 +470,19 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> } } -impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io::Read for BufferedUart<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { self.rx.blocking_read(buf) } } -impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'d, T> { +impl<'d, T: BasicInstance> embedded_io::Read for BufferedUartRx<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { self.blocking_read(buf) } } -impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io::Write for BufferedUart<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { self.tx.blocking_write(buf) } @@ -492,7 +492,7 @@ impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> } } -impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'d, T> { +impl<'d, T: BasicInstance> embedded_io::Write for BufferedUartTx<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { Self::blocking_write(self, buf) } @@ -584,15 +584,15 @@ mod eh02 { mod eh1 { use super::*; - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> { type Error = Error; } - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } @@ -602,16 +602,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) @@ -628,16 +618,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.tx.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.tx.blocking_flush() - } - } - impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index c97efbf0..255ddfd4 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -5,8 +5,8 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; @@ -116,6 +116,10 @@ pub struct Config { /// but will effectively disable noise detection. #[cfg(not(usart_v1))] pub assume_noise_free: bool, + + /// Set this to true to swap the RX and TX pins. + #[cfg(any(usart_v3, usart_v4))] + pub swap_rx_tx: bool, } impl Default for Config { @@ -129,6 +133,8 @@ impl Default for Config { detect_previous_overrun: false, #[cfg(not(usart_v1))] assume_noise_free: false, + #[cfg(any(usart_v3, usart_v4))] + swap_rx_tx: false, } } } @@ -688,8 +694,22 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let r = T::regs(); - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + // Some chips do not have swap_rx_tx bit + cfg_if::cfg_if! { + if #[cfg(any(usart_v3, usart_v4))] { + if config.swap_rx_tx { + let (rx, tx) = (tx, rx); + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } else { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } + } else { + 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); @@ -789,45 +809,57 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: Kind::Uart => (1, 0x10, 0x1_0000), }; + fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { + // The calculation to be done to get the BRR is `mul * pclk / presc / baud` + // To do this in 32-bit only we can't multiply `mul` and `pclk` + let clock = pclk / presc; + + // The mul is applied as the last operation to prevent overflow + let brr = clock / baud * mul; + + // The BRR calculation will be a bit off because of integer rounding. + // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. + let rounding = ((clock % baud) * mul + (baud / 2)) / baud; + + brr + rounding + } + #[cfg(not(usart_v1))] let mut over8 = false; - let mut found = None; + let mut found_brr = None; for &(presc, _presc_val) in &DIVS { - let denom = (config.baudrate * presc as u32) as u64; - let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom; + let brr = calculate_brr(config.baudrate, pclk_freq.0, presc as u32, mul); trace!( "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", presc, - div, - div >> 4, - div & 0x0F + brr, + brr >> 4, + brr & 0x0F ); - if div < brr_min { + if brr < brr_min { #[cfg(not(usart_v1))] - if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { over8 = true; - let div = div as u32; - r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07))); + r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); - found = Some(div); + found_brr = Some(brr); break; } panic!("USART: baudrate too high"); } - if div < brr_max { - let div = div as u32; - r.brr().write_value(regs::Brr(div)); + if brr < brr_max { + r.brr().write_value(regs::Brr(brr)); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); - found = Some(div); + found_brr = Some(brr); break; } } - let div = found.expect("USART: baudrate too low"); + let brr = found_brr.expect("USART: baudrate too low"); #[cfg(not(usart_v1))] let oversampling = if over8 { "8 bit" } else { "16 bit" }; @@ -837,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", oversampling, config.baudrate, - pclk_freq.0 / div + pclk_freq.0 / brr * mul ); r.cr2().write(|w| { @@ -847,6 +879,9 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: StopBits::STOP1P5 => vals::Stop::STOP1P5, StopBits::STOP2 => vals::Stop::STOP2, }); + + #[cfg(any(usart_v3, usart_v4))] + w.set_swap(config.swap_rx_tx); }); r.cr1().write(|w| { // enable uart @@ -920,27 +955,27 @@ mod eh02 { mod eh1 { use super::*; - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { + impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { match *self { - Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, - Self::Noise => embedded_hal_1::serial::ErrorKind::Noise, - Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, - Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, - Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, + Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise, + Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, + Self::BufferTooLong => embedded_hal_nb::serial::ErrorKind::Other, } } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { type Error = Error; } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::ErrorType for UartTx<'d, T, TxDma> { + impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, TxDma> { type Error = Error; } - impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> { + impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, RxDma> { type Error = Error; } @@ -950,16 +985,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::Write for UartTx<'d, T, TxDma> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) @@ -976,16 +1001,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::Write for Uart<'d, T, TxDma, RxDma> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) @@ -999,12 +1014,11 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { - use embedded_io::asynch::Write; - use embedded_io::Io; + use embedded_io_async::{ErrorType, Write}; use super::*; - impl Io for Uart<'_, T, TxDma, RxDma> + impl ErrorType for Uart<'_, T, TxDma, RxDma> where T: BasicInstance, { @@ -1026,7 +1040,7 @@ mod eio { } } - impl Io for UartTx<'_, T, TxDma> + impl ErrorType for UartTx<'_, T, TxDma> where T: BasicInstance, { diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index c74d7e09..b3f57062 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -2,16 +2,16 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::PeripheralRef; +use embassy_hal_internal::PeripheralRef; use futures::future::{select, Either}; use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; -use crate::dma::RingBuffer; +use crate::dma::ReadableRingBuffer; use crate::usart::{Regs, Sr}; pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma> { _peri: PeripheralRef<'d, T>, - ring_buf: RingBuffer<'d, RxDma, u8>, + ring_buf: ReadableRingBuffer<'d, RxDma, u8>, } impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { @@ -19,12 +19,12 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { /// without the possibility of loosing bytes. The `dma_buf` is a buffer registered to the /// DMA controller, and must be sufficiently large, such that it will not overflow. pub fn into_ring_buffered(self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T, RxDma> { - assert!(dma_buf.len() > 0 && dma_buf.len() <= 0xFFFF); + assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); let request = self.rx_dma.request(); let opts = Default::default(); - let ring_buf = unsafe { RingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; + let ring_buf = unsafe { ReadableRingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; RingBufferedUartRx { _peri: self._peri, @@ -111,10 +111,9 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD let r = T::regs(); // Start background receive if it was not already started - match r.cr3().read().dmar() { - false => self.start()?, - _ => {} - }; + if !r.cr3().read().dmar() { + self.start()?; + } check_for_errors(clear_idle_flag(T::regs()))?; @@ -221,13 +220,12 @@ fn clear_idle_flag(r: Regs) -> Sr { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { - use embedded_io::asynch::Read; - use embedded_io::Io; + use embedded_io_async::{ErrorType, Read}; use super::RingBufferedUartRx; use crate::usart::{BasicInstance, Error, RxDma}; - impl Io for RingBufferedUartRx<'_, T, Rx> + impl ErrorType for RingBufferedUartRx<'_, T, Rx> where T: BasicInstance, Rx: RxDma, diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index ecdd1d0b..cef19635 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{ diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index fd0e22ad..348f0f79 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; use core::task::Poll; -use embassy_hal_common::{into_ref, Peripheral}; +use embassy_hal_internal::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index b03e81d6..c7c2694e 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, Peripheral}; +use embassy_hal_internal::{into_ref, Peripheral}; use stm32_metapac::iwdg::vals::{Key, Pr}; use crate::rcc::LSI_FREQ; @@ -60,9 +60,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { rl ); - IndependentWatchdog { - wdg: PhantomData::default(), - } + IndependentWatchdog { wdg: PhantomData } } pub fn unleash(&mut self) { @@ -104,16 +102,16 @@ mod tests { assert_eq!(512_000, get_timeout_us(4, MAX_RL)); assert_eq!(8_000, get_timeout_us(256, 0)); - assert_eq!(32768_000, get_timeout_us(256, MAX_RL)); + assert_eq!(32_768_000, get_timeout_us(256, MAX_RL)); - assert_eq!(8000_000, get_timeout_us(64, 3999)); + assert_eq!(8_000_000, get_timeout_us(64, 3999)); } #[test] fn can_compute_reload_value() { assert_eq!(0xFFF, reload_value(4, 512_000)); - assert_eq!(0xFFF, reload_value(256, 32768_000)); + assert_eq!(0xFFF, reload_value(256, 32_768_000)); - assert_eq!(3999, reload_value(64, 8000_000)); + assert_eq!(3999, reload_value(64, 8_000_000)); } } diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 340724ea..94d6799e 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -23,7 +23,7 @@ target = "thumbv7em-none-eabi" features = ["nightly"] [features] -nightly = ["embedded-io/async"] +nightly = ["dep:embedded-io-async"] std = [] turbowakers = [] @@ -35,7 +35,7 @@ futures-util = { version = "0.3.17", default-features = false } critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" -embedded-io = "0.4.0" +embedded-io-async = { version = "0.5.0", optional = true } [dev-dependencies] futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } diff --git a/embassy-sync/src/blocking_mutex/raw.rs b/embassy-sync/src/blocking_mutex/raw.rs index 15796f1b..a8afcad3 100644 --- a/embassy-sync/src/blocking_mutex/raw.rs +++ b/embassy-sync/src/blocking_mutex/raw.rs @@ -11,7 +11,7 @@ use core::marker::PhantomData; /// /// Note that, unlike other mutexes, implementations only guarantee no /// concurrent access from other threads: concurrent access from the current -/// thread is allwed. For example, it's possible to lock the same mutex multiple times reentrantly. +/// thread is allowed. For example, it's possible to lock the same mutex multiple times reentrantly. /// /// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access /// to the data, it is not enough to guarantee exclusive (`&mut`) access. diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 77352874..62ea1307 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -30,7 +30,6 @@ use crate::blocking_mutex::Mutex; use crate::waitqueue::WakerRegistration; /// Send-only access to a [`Channel`]. -#[derive(Copy)] pub struct Sender<'ch, M, T, const N: usize> where M: RawMutex, @@ -47,6 +46,8 @@ where } } +impl<'ch, M, T, const N: usize> Copy for Sender<'ch, M, T, N> where M: RawMutex {} + impl<'ch, M, T, const N: usize> Sender<'ch, M, T, N> where M: RawMutex, @@ -64,10 +65,16 @@ where pub fn try_send(&self, message: T) -> Result<(), TrySendError> { self.channel.try_send(message) } + + /// Allows a poll_fn to poll until the channel is ready to send + /// + /// See [`Channel::poll_ready_to_send()`] + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { + self.channel.poll_ready_to_send(cx) + } } /// Send-only access to a [`Channel`] without knowing channel size. -#[derive(Copy)] pub struct DynamicSender<'ch, T> { channel: &'ch dyn DynamicChannel, } @@ -78,6 +85,8 @@ impl<'ch, T> Clone for DynamicSender<'ch, T> { } } +impl<'ch, T> Copy for DynamicSender<'ch, T> {} + impl<'ch, M, T, const N: usize> From> for DynamicSender<'ch, T> where M: RawMutex, @@ -104,10 +113,16 @@ impl<'ch, T> DynamicSender<'ch, T> { pub fn try_send(&self, message: T) -> Result<(), TrySendError> { self.channel.try_send_with_context(message, None) } + + /// Allows a poll_fn to poll until the channel is ready to send + /// + /// See [`Channel::poll_ready_to_send()`] + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { + self.channel.poll_ready_to_send(cx) + } } /// Receive-only access to a [`Channel`]. -#[derive(Copy)] pub struct Receiver<'ch, M, T, const N: usize> where M: RawMutex, @@ -124,27 +139,42 @@ where } } +impl<'ch, M, T, const N: usize> Copy for Receiver<'ch, M, T, N> where M: RawMutex {} + impl<'ch, M, T, const N: usize> Receiver<'ch, M, T, N> where M: RawMutex, { /// Receive the next value. /// - /// See [`Channel::recv()`]. - pub fn recv(&self) -> RecvFuture<'_, M, T, N> { - self.channel.recv() + /// See [`Channel::receive()`]. + pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> { + self.channel.receive() } /// Attempt to immediately receive the next value. /// - /// See [`Channel::try_recv()`] - pub fn try_recv(&self) -> Result { - self.channel.try_recv() + /// See [`Channel::try_receive()`] + pub fn try_receive(&self) -> Result { + self.channel.try_receive() + } + + /// Allows a poll_fn to poll until the channel is ready to receive + /// + /// See [`Channel::poll_ready_to_receive()`] + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { + self.channel.poll_ready_to_receive(cx) + } + + /// Poll the channel for the next item + /// + /// See [`Channel::poll_receive()`] + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.channel.poll_receive(cx) } } /// Receive-only access to a [`Channel`] without knowing channel size. -#[derive(Copy)] pub struct DynamicReceiver<'ch, T> { channel: &'ch dyn DynamicChannel, } @@ -155,19 +185,35 @@ impl<'ch, T> Clone for DynamicReceiver<'ch, T> { } } +impl<'ch, T> Copy for DynamicReceiver<'ch, T> {} + impl<'ch, T> DynamicReceiver<'ch, T> { /// Receive the next value. /// - /// See [`Channel::recv()`]. - pub fn recv(&self) -> DynamicRecvFuture<'_, T> { - DynamicRecvFuture { channel: self.channel } + /// See [`Channel::receive()`]. + pub fn receive(&self) -> DynamicReceiveFuture<'_, T> { + DynamicReceiveFuture { channel: self.channel } } /// Attempt to immediately receive the next value. /// - /// See [`Channel::try_recv()`] - pub fn try_recv(&self) -> Result { - self.channel.try_recv_with_context(None) + /// See [`Channel::try_receive()`] + pub fn try_receive(&self) -> Result { + self.channel.try_receive_with_context(None) + } + + /// Allows a poll_fn to poll until the channel is ready to receive + /// + /// See [`Channel::poll_ready_to_receive()`] + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { + self.channel.poll_ready_to_receive(cx) + } + + /// Poll the channel for the next item + /// + /// See [`Channel::poll_receive()`] + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.channel.poll_receive(cx) } } @@ -180,42 +226,39 @@ where } } -/// Future returned by [`Channel::recv`] and [`Receiver::recv`]. +/// Future returned by [`Channel::receive`] and [`Receiver::receive`]. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct RecvFuture<'ch, M, T, const N: usize> +pub struct ReceiveFuture<'ch, M, T, const N: usize> where M: RawMutex, { channel: &'ch Channel, } -impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N> +impl<'ch, M, T, const N: usize> Future for ReceiveFuture<'ch, M, T, N> where M: RawMutex, { type Output = T; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.channel.try_recv_with_context(Some(cx)) { - Ok(v) => Poll::Ready(v), - Err(TryRecvError::Empty) => Poll::Pending, - } + self.channel.poll_receive(cx) } } -/// Future returned by [`DynamicReceiver::recv`]. +/// Future returned by [`DynamicReceiver::receive`]. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct DynamicRecvFuture<'ch, T> { +pub struct DynamicReceiveFuture<'ch, T> { channel: &'ch dyn DynamicChannel, } -impl<'ch, T> Future for DynamicRecvFuture<'ch, T> { +impl<'ch, T> Future for DynamicReceiveFuture<'ch, T> { type Output = T; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.channel.try_recv_with_context(Some(cx)) { + match self.channel.try_receive_with_context(Some(cx)) { Ok(v) => Poll::Ready(v), - Err(TryRecvError::Empty) => Poll::Pending, + Err(TryReceiveError::Empty) => Poll::Pending, } } } @@ -281,13 +324,18 @@ impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {} trait DynamicChannel { fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError>; - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; + + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>; + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>; + + fn poll_receive(&self, cx: &mut Context<'_>) -> Poll; } -/// Error returned by [`try_recv`](Channel::try_recv). +/// Error returned by [`try_receive`](Channel::try_receive). #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TryRecvError { +pub enum TryReceiveError { /// A message could not be received because the channel is empty. Empty, } @@ -316,11 +364,11 @@ impl ChannelState { } } - fn try_recv(&mut self) -> Result { - self.try_recv_with_context(None) + fn try_receive(&mut self) -> Result { + self.try_receive_with_context(None) } - fn try_recv_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result { + fn try_receive_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result { if self.queue.is_full() { self.senders_waker.wake(); } @@ -331,7 +379,30 @@ impl ChannelState { if let Some(cx) = cx { self.receiver_waker.register(cx.waker()); } - Err(TryRecvError::Empty) + Err(TryReceiveError::Empty) + } + } + + fn poll_receive(&mut self, cx: &mut Context<'_>) -> Poll { + if self.queue.is_full() { + self.senders_waker.wake(); + } + + if let Some(message) = self.queue.pop_front() { + Poll::Ready(message) + } else { + self.receiver_waker.register(cx.waker()); + Poll::Pending + } + } + + fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> { + self.receiver_waker.register(cx.waker()); + + if !self.queue.is_empty() { + Poll::Ready(()) + } else { + Poll::Pending } } @@ -353,6 +424,16 @@ impl ChannelState { } } } + + fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> Poll<()> { + self.senders_waker.register(cx.waker()); + + if !self.queue.is_full() { + Poll::Ready(()) + } else { + Poll::Pending + } + } } /// A bounded channel for communicating between asynchronous tasks @@ -393,14 +474,29 @@ where self.inner.lock(|rc| f(&mut *rc.borrow_mut())) } - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { - self.lock(|c| c.try_recv_with_context(cx)) + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { + self.lock(|c| c.try_receive_with_context(cx)) + } + + /// Poll the channel for the next message + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.lock(|c| c.poll_receive(cx)) } fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { self.lock(|c| c.try_send_with_context(m, cx)) } + /// Allows a poll_fn to poll until the channel is ready to receive + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { + self.lock(|c| c.poll_ready_to_receive(cx)) + } + + /// Allows a poll_fn to poll until the channel is ready to send + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { + self.lock(|c| c.poll_ready_to_send(cx)) + } + /// Get a sender for this channel. pub fn sender(&self) -> Sender<'_, M, T, N> { Sender { channel: self } @@ -440,16 +536,16 @@ where /// /// If there are no messages in the channel's buffer, this method will /// wait until a message is sent. - pub fn recv(&self) -> RecvFuture<'_, M, T, N> { - RecvFuture { channel: self } + pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> { + ReceiveFuture { channel: self } } /// Attempt to immediately receive a message. /// /// This method will either receive a message from the channel immediately or return an error /// if the channel is empty. - pub fn try_recv(&self) -> Result { - self.lock(|c| c.try_recv()) + pub fn try_receive(&self) -> Result { + self.lock(|c| c.try_receive()) } } @@ -463,8 +559,20 @@ where Channel::try_send_with_context(self, m, cx) } - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { - Channel::try_recv_with_context(self, cx) + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { + Channel::try_receive_with_context(self, cx) + } + + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { + Channel::poll_ready_to_send(self, cx) + } + + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { + Channel::poll_ready_to_receive(self, cx) + } + + fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + Channel::poll_receive(self, cx) } } @@ -508,15 +616,15 @@ mod tests { fn receiving_once_with_one_send() { let mut c = ChannelState::::new(); assert!(c.try_send(1).is_ok()); - assert_eq!(c.try_recv().unwrap(), 1); + assert_eq!(c.try_receive().unwrap(), 1); assert_eq!(capacity(&c), 3); } #[test] fn receiving_when_empty() { let mut c = ChannelState::::new(); - match c.try_recv() { - Err(TryRecvError::Empty) => assert!(true), + match c.try_receive() { + Err(TryReceiveError::Empty) => assert!(true), _ => assert!(false), } assert_eq!(capacity(&c), 3); @@ -526,7 +634,7 @@ mod tests { fn simple_send_and_receive() { let c = Channel::::new(); assert!(c.try_send(1).is_ok()); - assert_eq!(c.try_recv().unwrap(), 1); + assert_eq!(c.try_receive().unwrap(), 1); } #[test] @@ -546,7 +654,7 @@ mod tests { let r: DynamicReceiver<'_, u32> = c.receiver().into(); assert!(s.try_send(1).is_ok()); - assert_eq!(r.try_recv().unwrap(), 1); + assert_eq!(r.try_receive().unwrap(), 1); } #[futures_test::test] @@ -561,14 +669,14 @@ mod tests { assert!(c2.try_send(1).is_ok()); }) .is_ok()); - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); } #[futures_test::test] async fn sender_send_completes_if_capacity() { let c = Channel::::new(); c.send(1).await; - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); } #[futures_test::test] @@ -586,11 +694,11 @@ mod tests { // Wish I could think of a means of determining that the async send is waiting instead. // However, I've used the debugger to observe that the send does indeed wait. Delay::new(Duration::from_millis(500)).await; - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); assert!(executor .spawn(async move { loop { - c.recv().await; + c.receive().await; } }) .is_ok()); diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 13bf4ef0..21d451ea 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -11,7 +11,6 @@ use crate::ring_buffer::RingBuffer; use crate::waitqueue::WakerRegistration; /// Write-only access to a [`Pipe`]. -#[derive(Copy)] pub struct Writer<'p, M, const N: usize> where M: RawMutex, @@ -28,6 +27,8 @@ where } } +impl<'p, M, const N: usize> Copy for Writer<'p, M, N> where M: RawMutex {} + impl<'p, M, const N: usize> Writer<'p, M, N> where M: RawMutex, @@ -74,7 +75,6 @@ where impl<'p, M, const N: usize> Unpin for WriteFuture<'p, M, N> where M: RawMutex {} /// Read-only access to a [`Pipe`]. -#[derive(Copy)] pub struct Reader<'p, M, const N: usize> where M: RawMutex, @@ -91,6 +91,8 @@ where } } +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, @@ -381,17 +383,17 @@ mod io_impls { use super::*; - impl embedded_io::Io for Pipe { + impl embedded_io_async::ErrorType for Pipe { type Error = Infallible; } - impl embedded_io::asynch::Read for Pipe { + impl embedded_io_async::Read for Pipe { async fn read(&mut self, buf: &mut [u8]) -> Result { Ok(Pipe::read(self, buf).await) } } - impl embedded_io::asynch::Write for Pipe { + impl embedded_io_async::Write for Pipe { async fn write(&mut self, buf: &[u8]) -> Result { Ok(Pipe::write(self, buf).await) } @@ -401,17 +403,17 @@ mod io_impls { } } - impl embedded_io::Io for &Pipe { + impl embedded_io_async::ErrorType for &Pipe { type Error = Infallible; } - impl embedded_io::asynch::Read for &Pipe { + impl embedded_io_async::Read for &Pipe { async fn read(&mut self, buf: &mut [u8]) -> Result { Ok(Pipe::read(self, buf).await) } } - impl embedded_io::asynch::Write for &Pipe { + impl embedded_io_async::Write for &Pipe { async fn write(&mut self, buf: &[u8]) -> Result { Ok(Pipe::write(self, buf).await) } @@ -421,21 +423,21 @@ mod io_impls { } } - impl embedded_io::Io for Reader<'_, M, N> { + impl embedded_io_async::ErrorType for Reader<'_, M, N> { type Error = Infallible; } - impl embedded_io::asynch::Read for Reader<'_, M, N> { + impl embedded_io_async::Read for Reader<'_, M, N> { async fn read(&mut self, buf: &mut [u8]) -> Result { Ok(Reader::read(self, buf).await) } } - impl embedded_io::Io for Writer<'_, M, N> { + impl embedded_io_async::ErrorType for Writer<'_, M, N> { type Error = Infallible; } - impl embedded_io::asynch::Write for Writer<'_, M, N> { + impl embedded_io_async::Write for Writer<'_, M, N> { async fn write(&mut self, buf: &[u8]) -> Result { Ok(Writer::write(self, buf).await) } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0afb1103..00d31d30 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} futures-util = { version = "0.3.17", default-features = false } atomic-polyfill = "1.0.1" @@ -169,4 +169,4 @@ wasm-timer = { version = "0.2.5", optional = true } [dev-dependencies] serial_test = "0.9" critical-section = { version = "1.1", features = ["std"] } -embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly"] } +embassy-executor = { version = "0.3.0", path = "../embassy-executor", features = ["nightly"] } diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index d3d1f9f5..ad5026e6 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -3,6 +3,7 @@ use core::pin::Pin; use core::task::{Context, Poll, Waker}; use futures_util::future::{select, Either}; +use futures_util::stream::FusedStream; use futures_util::{pin_mut, Stream}; use crate::{Duration, Instant}; @@ -163,6 +164,13 @@ impl Stream for Ticker { } } +impl FusedStream for Ticker { + fn is_terminated(&self) -> bool { + // `Ticker` keeps yielding values until dropped, it never terminates. + false + } +} + extern "Rust" { fn _embassy_time_schedule_wake(at: Instant, waker: &Waker); } diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 6e50c40b..9178dd6d 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -77,22 +77,29 @@ impl UsbLogger { ); // Create classes on the builder. - let mut class = CdcAcmClass::new(&mut builder, &mut state.state, MAX_PACKET_SIZE as u16); + let class = CdcAcmClass::new(&mut builder, &mut state.state, MAX_PACKET_SIZE as u16); + let (mut sender, mut receiver) = class.split(); // Build the builder. let mut device = builder.build(); - loop { let run_fut = device.run(); let log_fut = async { let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; - class.wait_connection().await; + sender.wait_connection().await; loop { let len = self.buffer.read(&mut rx[..]).await; - let _ = class.write_packet(&rx[..len]).await; + let _ = sender.write_packet(&rx[..len]).await; } }; - join(run_fut, log_fut).await; + let discard_fut = async { + let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + receiver.wait_connection().await; + loop { + let _ = receiver.read_packet(&mut discard_buf).await; + } + }; + join(run_fut, join(log_fut, discard_fut)).await; } } } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 67070902..57d32294 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -87,7 +87,10 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { ethernet_address: [u8; 6], ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { let (tx_usb, rx_usb) = self.split(); - let (runner, device) = ch::new(&mut state.ch_state, ethernet_address); + let (runner, device) = ch::new( + &mut state.ch_state, + ch::driver::HardwareAddress::Ethernet(ethernet_address), + ); ( Runner { diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index fcfa0bfc..830e9b76 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -248,6 +248,8 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> { write_ep: D::EndpointIn, _control: &'d ControlShared, + + max_packet_size: usize, } impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { @@ -338,6 +340,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { read_ep, write_ep, _control: &state.shared, + max_packet_size: max_packet_size as usize, } } @@ -349,6 +352,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { Sender { write_ep: self.write_ep, seq: 0, + max_packet_size: self.max_packet_size, }, Receiver { data_if: self.data_if, @@ -365,6 +369,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { pub struct Sender<'d, D: Driver<'d>> { write_ep: D::EndpointIn, seq: u16, + max_packet_size: usize, } impl<'d, D: Driver<'d>> Sender<'d, D> { @@ -375,8 +380,8 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { let seq = self.seq; self.seq = self.seq.wrapping_add(1); - const MAX_PACKET_SIZE: usize = 64; // TODO unhardcode const OUT_HEADER_LEN: usize = 28; + const ABS_MAX_PACKET_SIZE: usize = 512; let header = NtbOutHeader { nth_sig: SIG_NTH, @@ -395,27 +400,27 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { }; // Build first packet on a buffer, send next packets straight from `data`. - let mut buf = [0; MAX_PACKET_SIZE]; + let mut buf = [0; ABS_MAX_PACKET_SIZE]; let n = byteify(&mut buf, header); assert_eq!(n.len(), OUT_HEADER_LEN); - if OUT_HEADER_LEN + data.len() < MAX_PACKET_SIZE { + if OUT_HEADER_LEN + data.len() < self.max_packet_size { // First packet is not full, just send it. // No need to send ZLP because it's short for sure. buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data); self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?; } else { - let (d1, d2) = data.split_at(MAX_PACKET_SIZE - OUT_HEADER_LEN); + let (d1, d2) = data.split_at(self.max_packet_size - OUT_HEADER_LEN); - buf[OUT_HEADER_LEN..].copy_from_slice(d1); - self.write_ep.write(&buf).await?; + buf[OUT_HEADER_LEN..self.max_packet_size].copy_from_slice(d1); + self.write_ep.write(&buf[..self.max_packet_size]).await?; - for chunk in d2.chunks(MAX_PACKET_SIZE) { + for chunk in d2.chunks(self.max_packet_size) { self.write_ep.write(&chunk).await?; } // Send ZLP if needed. - if d2.len() % MAX_PACKET_SIZE == 0 { + if d2.len() % self.max_packet_size == 0 { self.write_ep.write(&[]).await?; } } diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 187b2ff8..847338e5 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -526,7 +526,7 @@ impl<'a> PropertyData<'a> { PropertyData::Binary(val) => val.len(), PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val), PropertyData::RegMultiSz(val) => { - core::mem::size_of::() * val.iter().map(|x| x.encode_utf16().count() + 1).sum::() + 1 + core::mem::size_of::() * (val.iter().map(|x| x.encode_utf16().count() + 1).sum::() + 1) } } } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 2a0cf781..46ce7ba9 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +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-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"] } diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 021d77f3..8b510ed3 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -52,20 +52,20 @@ async fn main(_spawner: Spawner) { let nvmc = Mutex::new(BlockingAsync::new(nvmc)); let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); - let mut updater = FirmwareUpdater::new(config); + let mut magic = [0; 4]; + let mut updater = FirmwareUpdater::new(config, &mut magic); loop { led.set_low(); button.wait_for_any_edge().await; if button.is_low() { let mut offset = 0; - let mut magic = [0; 4]; for chunk in APP_B.chunks(4096) { let mut buf: [u8; 4096] = [0; 4096]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(&mut magic, offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(&mut magic).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_high(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 95b2da95..be85f443 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +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-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"] } diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index c8497494..15fdaca8 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -34,11 +34,12 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); - let mut updater = BlockingFirmwareUpdater::new(config); + let mut aligned = AlignedBuffer([0; 4]); + let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0); Timer::after(Duration::from_secs(5)).await; watchdog.feed(); @@ -47,7 +48,7 @@ async fn main(_s: Spawner) { let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); defmt::info!("preparing update"); let writer = updater - .prepare_update(&mut buf.0[..1]) + .prepare_update() .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) .unwrap(); defmt::info!("writer created, starting write"); @@ -59,7 +60,7 @@ async fn main(_s: Spawner) { } watchdog.feed(); defmt::info!("firmware written, marking update"); - updater.mark_updated(&mut buf.0[..1]).unwrap(); + updater.mark_updated().unwrap(); Timer::after(Duration::from_secs(2)).await; led.set_low(); defmt::info!("update marked, resetting"); diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 3b0fc4d9..ea878933 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +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-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"] } diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index c0a11d69..8be39bfb 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -31,17 +31,17 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; let mut offset = 0; - let mut magic = AlignedBuffer([0; WRITE_SIZE]); for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 323b4ab2..b39bc292 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +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-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"] } diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index dea682a9..0c3819be 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -33,9 +33,9 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); - let mut updater = BlockingFirmwareUpdater::new(config); let mut magic = AlignedBuffer([0; WRITE_SIZE]); - let writer = updater.prepare_update(magic.as_mut()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0); + let writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len() as u32; } - updater.mark_updated(magic.as_mut()).unwrap(); + updater.mark_updated().unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index b2abdc89..f015b2ca 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +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-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"] } diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 71917669..f239e373 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -34,8 +34,8 @@ async fn main(_spawner: Spawner) { let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); - let mut updater = BlockingFirmwareUpdater::new(config); - let writer = updater.prepare_update(magic.as_mut()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0); + let writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len() as u32; } - updater.mark_updated(magic.as_mut()).unwrap(); + updater.mark_updated().unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 0b7e72d5..f221e1de 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +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-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"] } diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index ce80056e..b4cdcd44 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -33,18 +33,18 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; let mut offset = 0; - let mut magic = AlignedBuffer([0; WRITE_SIZE]); for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 5f3f365c..2896afa3 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +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-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"] } diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 1e9bf3cb..b4cdcd44 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -33,18 +33,18 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); - button.wait_for_falling_edge().await; let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); + button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 44eb5aba..50d8967a 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +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-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"] } diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index a514ab5b..eefa25f7 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -31,17 +31,17 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); - button.wait_for_falling_edge().await; let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); + button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index fdad5506..27541439 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +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-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"] } diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 52a197a5..c837e47b 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -31,19 +31,19 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; //defmt::info!("Starting update"); - let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); // defmt::info!("Writing chunk at 0x{:x}", offset); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); //defmt::info!("Marked as updated"); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 72c95c02..74e2e293 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -33,9 +33,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _> = BootLoader::new(config); - - bl.prepare(); + let bl: BootLoader = BootLoader::prepare(config); unsafe { bl.load(active_offset) } } diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 6a81db80..c0e75d1e 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -29,9 +29,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _> = BootLoader::new(config); - - bl.prepare(); + let bl: BootLoader = BootLoader::prepare(config); unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 262eed20..5fd9ea58 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -27,9 +27,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config); - - bl.prepare(); + let bl = BootLoader::prepare::<_, _, _, 2048>(config); unsafe { bl.load(BANK1_REGION.base + active_offset) } } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 30b67b7b..b06722f5 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -17,7 +17,7 @@ log = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } +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-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } @@ -34,3 +34,6 @@ log = { version = "0.4.17", optional = true } [[bin]] name = "rtos_trace" required-features = ["nightly"] + +[profile.release] +debug = 2 diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index ded3b7db..715f1ecf 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -19,3 +19,6 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } + +[profile.release] +debug = 2 diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 7b9c371b..1b2f1eb1 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -12,12 +12,14 @@ nightly = [ "embassy-nrf/nightly", "embassy-net/nightly", "embassy-net-esp-hosted", + "embassy-net-enc28j60", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", "static_cell/nightly", "embassy-usb", - "embedded-io/async", + "embedded-io-async", + "embedded-hal-bus/async", "embassy-net", "embassy-lora", "lora-phy", @@ -28,21 +30,24 @@ nightly = [ [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +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-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 = "0.4.0" +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 } 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 } defmt = "0.3" defmt-rtt = "0.4" +fixed = "1.10.0" static_cell = "1.1" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -52,7 +57,14 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } -embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "1.0.0-rc.1", optional = true } +embedded-hal-bus = { version = "0.1.0-rc.1" } +num-integer = { version = "0.1.45", default-features = false } +microfft = "0.5.0" + +[profile.release] +debug = 2 [patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } +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/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 23869537..d9c50578 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::buffered_uarte::{self, BufferedUarte}; use embassy_nrf::{bind_interrupts, peripherals, uarte}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/nrf52840/src/bin/channel.rs b/examples/nrf52840/src/bin/channel.rs index d782a79e..bd9c909d 100644 --- a/examples/nrf52840/src/bin/channel.rs +++ b/examples/nrf52840/src/bin/channel.rs @@ -35,7 +35,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(my_task())); loop { - match CHANNEL.recv().await { + match CHANNEL.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/nrf52840/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs index fcccdaed..ec4f1d80 100644 --- a/examples/nrf52840/src/bin/channel_sender_receiver.rs +++ b/examples/nrf52840/src/bin/channel_sender_receiver.rs @@ -33,7 +33,7 @@ async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedSta let mut led = Output::new(led, Level::Low, OutputDrive::Standard); loop { - match receiver.recv().await { + match receiver.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/nrf52840/src/bin/ethernet_enc28j60.rs b/examples/nrf52840/src/bin/ethernet_enc28j60.rs new file mode 100644 index 00000000..d1b796fa --- /dev/null +++ b/examples/nrf52840/src/bin/ethernet_enc28j60.rs @@ -0,0 +1,124 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +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::Spim; +use embassy_nrf::{bind_interrupts, peripherals, spim}; +use embassy_time::Delay; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn net_task( + stack: &'static Stack< + Enc28j60< + ExclusiveDevice, Output<'static, peripherals::P0_15>, Delay>, + Output<'static, peripherals::P0_13>, + >, + >, +) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("running!"); + + let eth_sck = p.P0_20; + let eth_mosi = p.P0_22; + let eth_miso = p.P0_24; + let eth_cs = p.P0_15; + let eth_rst = p.P0_13; + let _eth_irq = p.P0_12; + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M16; + let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config); + let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard); + let spi = ExclusiveDevice::new(spi, cs, Delay); + + let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard); + let mac_addr = [2, 3, 4, 5, 6, 7]; + let device = Enc28j60::new(spi, Some(rst), mac_addr); + + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + // }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now 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; + } + }; + } + } +} diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 6b41320c..444b9137 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs @@ -7,6 +7,8 @@ use embassy_executor::Spawner; use embassy_nrf::pdm::{self, Config, Pdm}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; +use fixed::types::I7F1; +use num_integer::Roots; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -20,18 +22,36 @@ async fn main(_p: Spawner) { let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); loop { - pdm.start().await; + for gain in [I7F1::from_num(-20), I7F1::from_num(0), I7F1::from_num(20)] { + pdm.set_gain(gain, gain); + info!("Gain = {} dB", defmt::Debug2Format(&gain)); + pdm.start().await; - // wait some time till the microphon settled - Timer::after(Duration::from_millis(1000)).await; + // wait some time till the microphon settled + Timer::after(Duration::from_millis(1000)).await; - const SAMPLES: usize = 2048; - let mut buf = [0i16; SAMPLES]; - pdm.sample(&mut buf).await.unwrap(); + const SAMPLES: usize = 2048; + let mut buf = [0i16; SAMPLES]; + pdm.sample(&mut buf).await.unwrap(); - info!("samples: {:?}", &buf); + let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; + info!( + "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}", + buf.len(), + buf.iter().min().unwrap(), + buf.iter().max().unwrap(), + mean, + (buf.iter() + .map(|v| i32::from(*v - mean).pow(2)) + .fold(0i32, |a, b| a.saturating_add(b)) + / buf.len() as i32) + .sqrt() as i16, + ); - pdm.stop().await; - Timer::after(Duration::from_millis(100)).await; + info!("samples: {:?}", &buf); + + pdm.stop().await; + Timer::after(Duration::from_millis(100)).await; + } } } diff --git a/examples/nrf52840/src/bin/pdm_continuous.rs b/examples/nrf52840/src/bin/pdm_continuous.rs new file mode 100644 index 00000000..7d853147 --- /dev/null +++ b/examples/nrf52840/src/bin/pdm_continuous.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::cmp::Ordering; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::pdm::{self, Config, Frequency, OperationMode, Pdm, Ratio, SamplerState}; +use embassy_nrf::{bind_interrupts, peripherals}; +use fixed::types::I7F1; +use microfft::real::rfft_1024; +use num_integer::Roots; +use {defmt_rtt as _, panic_probe as _}; + +// Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer + +bind_interrupts!(struct Irqs { + PDM => pdm::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_p: Spawner) { + let mut p = embassy_nrf::init(Default::default()); + let mut config = Config::default(); + // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. + config.frequency = Frequency::_1280K; // 16 kHz sample rate + config.ratio = Ratio::RATIO80; + config.operation_mode = OperationMode::Mono; + config.gain_left = I7F1::from_bits(5); // 2.5 dB + let mut pdm = Pdm::new(p.PDM, Irqs, &mut p.P0_00, &mut p.P0_01, config); + + let mut bufs = [[0; 1024]; 2]; + + pdm.run_task_sampler(&mut bufs, move |buf| { + // NOTE: It is important that the time spent within this callback + // does not exceed the time taken to acquire the 1500 samples we + // have in this example, which would be 10us + 2us per + // sample * 1500 = 18ms. You need to measure the time taken here + // and set the sample buffer size accordingly. Exceeding this + // time can lead to the peripheral re-writing the other buffer. + let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; + let (peak_freq_index, peak_mag) = fft_peak_freq(&buf); + let peak_freq = peak_freq_index * 16000 / buf.len(); + info!( + "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}, peak {} @ {} Hz", + buf.len(), + buf.iter().min().unwrap(), + buf.iter().max().unwrap(), + mean, + (buf.iter() + .map(|v| i32::from(*v - mean).pow(2)) + .fold(0i32, |a, b| a.saturating_add(b)) + / buf.len() as i32) + .sqrt() as i16, + peak_mag, + peak_freq, + ); + SamplerState::Sampled + }) + .await + .unwrap(); +} + +fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { + let mut f = [0f32; 1024]; + for i in 0..input.len() { + f[i] = (input[i] as f32) / 32768.0; + } + // N.B. rfft_1024 does the FFT in-place so result is actually also a reference to f. + let result = rfft_1024(&mut f); + result[0].im = 0.0; + + result + .iter() + .map(|c| c.norm_sqr()) + .enumerate() + .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal)) + .map(|(i, v)| (i, ((v * 32768.0) as u32).sqrt())) + .unwrap() +} diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 9979a1d5..b748bfcd 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs @@ -46,7 +46,7 @@ async fn main(spawner: Spawner) { // back out the buffer we receive from the read // task. loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index f527c0d7..b7806f41 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -15,7 +15,7 @@ use embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index 112e41bc..a60822fd 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -11,8 +11,8 @@ use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::Delay; -use embedded_hal_async::spi::ExclusiveDevice; -use embedded_io::asynch::Write; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; @@ -72,8 +72,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); - control.init().await; - control.join(WIFI_NETWORK, WIFI_PASSWORD).await; + unwrap!(control.init().await); + unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await); let config = embassy_net::Config::dhcpv4(Default::default()); // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index f1d45f33..4968a79a 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = [ "defmt", ] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "defmt", "integrated-timers", @@ -37,7 +37,7 @@ embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ "defmt", ] } -embedded-io = { version = "0.4.0", features = [ "async" ]} +embedded-io-async = { version = "0.5.0" } defmt = "0.3" defmt-rtt = "0.4" @@ -53,3 +53,6 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c812cb3e..3efc804a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,12 +8,12 @@ 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-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +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-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"] } -embassy-net-w5500 = { version = "0.1.0", path = "../../embassy-net-w5500", features = ["defmt"] } +embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } 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"] } @@ -42,9 +42,10 @@ smart-leds = "0.3.0" heapless = "0.7.15" usbd-hid = "0.6.1" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = "0.2.0-alpha.2" -embedded-io = { version = "0.4.0", features = ["async", "defmt"] } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = "1.0.0-rc.1" +embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } +embedded-io-async = { version = "0.5.0", features = ["defmt-03"] } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} log = "0.4" @@ -53,7 +54,7 @@ pio = "0.2.1" rand = { version = "0.8.5", default-features = false } [profile.release] -debug = true +debug = 2 [patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } +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/adc.rs b/examples/rp/src/bin/adc.rs index 81a8b834..02bc493b 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; +use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; use embassy_rp::bind_interrupts; use embassy_rp::gpio::Pull; use embassy_time::{Duration, Timer}; @@ -22,9 +22,10 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut adc = Adc::new(p.ADC, Irqs, Config::default()); - let mut p26 = Pin::new(p.PIN_26, Pull::None); - let mut p27 = Pin::new(p.PIN_27, Pull::None); - let mut p28 = Pin::new(p.PIN_28, Pull::None); + let mut p26 = Channel::new_pin(p.PIN_26, Pull::None); + let mut p27 = Channel::new_pin(p.PIN_27, Pull::None); + let mut p28 = Channel::new_pin(p.PIN_28, Pull::None); + let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR); loop { let level = adc.read(&mut p26).await.unwrap(); @@ -33,7 +34,7 @@ async fn main(_spawner: Spawner) { info!("Pin 27 ADC: {}", level); let level = adc.read(&mut p28).await.unwrap(); info!("Pin 28 ADC: {}", level); - let temp = adc.read_temperature().await.unwrap(); + let temp = adc.read(&mut ts).await.unwrap(); info!("Temp: {} degrees", convert_to_celsius(temp)); Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index e81da177..c0fde62a 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -10,14 +10,15 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +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::{Delay, Duration}; -use embedded_hal_async::spi::ExclusiveDevice; -use embedded_io::asynch::Write; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; @@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, @@ -54,7 +56,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 9dd7ae97..e593acae 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -12,14 +12,15 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +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::{Delay, Duration, Timer}; -use embedded_hal_async::spi::ExclusiveDevice; -use embedded_io::asynch::Write; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; @@ -28,6 +29,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, @@ -57,7 +59,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index db21c2b6..c62caed7 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -11,21 +11,24 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +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::{Delay, Duration}; -use embedded_hal_async::spi::ExclusiveDevice; -use embedded_io::asynch::Write; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + #[embassy_executor::task] async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, @@ -55,7 +58,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 038432b1..76dabce1 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -11,20 +11,23 @@ use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +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::Delay; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + #[embassy_executor::task] async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, @@ -53,7 +56,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 4c4982ac..911a657e 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; use embassy_rp::peripherals::FLASH; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -25,63 +25,65 @@ async fn main(_spawner: Spawner) { // https://github.com/knurling-rs/defmt/pull/683 Timer::after(Duration::from_millis(10)).await; - let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); + let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id - let jedec = flash.jedec_id().unwrap(); + let jedec = flash.blocking_jedec_id().unwrap(); info!("jedec id: 0x{:x}", jedec); // Get unique id let mut uid = [0; 8]; - flash.unique_id(&mut uid).unwrap(); + flash.blocking_unique_id(&mut uid).unwrap(); info!("unique id: {:?}", uid); erase_write_sector(&mut flash, 0x00); multiwrite_bytes(&mut flash, ERASE_SIZE as u32); + background_read(&mut flash, (ERASE_SIZE * 2) as u32).await; + loop {} } -fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { +fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [multiwrite_bytes]"); let mut read_buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); info!("Contents start with {=[u8]}", read_buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Contents after erase starts with {=[u8]}", read_buf[0..4]); if read_buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); } - defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04])); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Contents after write starts with {=[u8]}", read_buf[0..4]); if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { defmt::panic!("unexpected"); } } -fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { +fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [erase_write_sector]"); let mut buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); info!("Contents start with {=[u8]}", buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Contents after erase starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); @@ -91,11 +93,43 @@ fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE *b = 0xDA; } - defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf)); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Contents after write starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xDA) { defmt::panic!("unexpected"); } } + +async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { + info!(">>>> [background_read]"); + + let mut buf = [0u32; 8]; + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + + info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); + info!("Contents start with {=u32:x}", buf[0]); + + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + info!("Contents after erase starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xFFFFFFFF) { + defmt::panic!("unexpected"); + } + + for b in buf.iter_mut() { + *b = 0xDABA1234; + } + + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe { + core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4) + })); + + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + info!("Contents after write starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xDABA1234) { + defmt::panic!("unexpected"); + } +} diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs index 89a62818..b54cc92f 100644 --- a/examples/rp/src/bin/lora_p2p_send_multicore.rs +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs @@ -113,7 +113,7 @@ async fn core1_task( }; loop { - let buffer: [u8; 3] = CHANNEL.recv().await; + let buffer: [u8; 3] = CHANNEL.receive().await; match lora.prepare_for_tx(&mdltn_params, 20, false).await { Ok(()) => {} Err(err) => { diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 893b724b..bf017f6a 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -56,7 +56,7 @@ async fn core0_task() { async fn core1_task(mut led: Output<'static, PIN_25>) { info!("Hello from core 1"); loop { - match CHANNEL.recv().await { + match CHANNEL.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index c001d644..a6d6144b 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -8,7 +8,6 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::relocate::RelocatedProgram; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; @@ -29,9 +28,8 @@ fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); let out_pin = pio.make_pio_pin(pin); cfg.set_out_pins(&[&out_pin]); cfg.set_set_pins(&[&out_pin]); @@ -65,9 +63,8 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); cfg.shift_in.auto_fill = true; cfg.shift_in.direction = ShiftDirection::Right; @@ -96,9 +93,8 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, "irq 3 [15]", ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); sm.set_config(&cfg); } diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 9ab72e1f..86e5017a 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -8,7 +8,6 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, Peripheral}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; @@ -46,9 +45,8 @@ async fn main(_spawner: Spawner) { ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[]); + cfg.use_program(&common.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); cfg.shift_in = ShiftConfig { auto_fill: true, diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 8aedd24b..d80c5c24 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -14,7 +14,6 @@ use embassy_rp::pio::{ Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; use embassy_rp::pwm::{self, Pwm}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -127,9 +126,8 @@ impl<'l> HD44780<'l> { sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.use_program(&common.load_program(&prg.program), &[&e]); cfg.clock_divider = 125u8.into(); cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); cfg.shift_out = ShiftConfig { @@ -201,9 +199,8 @@ impl<'l> HD44780<'l> { "# ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.use_program(&common.load_program(&prg.program), &[&e]); cfg.clock_divider = 8u8.into(); // ~64ns/insn cfg.set_jmp_pin(&db7); cfg.set_set_pins(&[&rs, &rw]); diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs new file mode 100644 index 00000000..707c99b7 --- /dev/null +++ b/examples/rp/src/bin/pio_uart.rs @@ -0,0 +1,392 @@ +//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART. +//! The PIO module is a very powerful peripheral that can be used to implement many different +//! protocols. It is a very flexible state machine that can be programmed to do almost anything. +//! +//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the +//! PIO module to implement a UART that is connected to the USB serial port. This allows you to +//! communicate with a device connected to the RP2040 over USB serial. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait)] + +use defmt::{info, panic, trace}; +use embassy_executor::Spawner; +use embassy_futures::join::{join, join3}; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::{PIO0, USB}; +use embassy_rp::pio::InterruptHandler as PioInterruptHandler; +use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::pipe::Pipe; +use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::{Builder, Config}; +use embedded_io_async::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +use crate::uart::PioUart; +use crate::uart_rx::PioUartRx; +use crate::uart_tx::PioUartTx; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; + PIO0_IRQ_0 => PioInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello there!"); + + let p = embassy_rp::init(Default::default()); + + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, Irqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("PIO UART example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + // Create classes on the builder. + let class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // PIO UART setup + let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); + 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 uart_pipe: Pipe = Pipe::new(); + let mut uart_pipe_writer = uart_pipe.writer(); + let mut uart_pipe_reader = uart_pipe.reader(); + + let (mut usb_tx, mut usb_rx) = class.split(); + + // Read + write from USB + let usb_future = async { + loop { + info!("Wait for USB connection"); + usb_rx.wait_connection().await; + info!("Connected"); + let _ = join( + usb_read(&mut usb_rx, &mut uart_pipe_writer), + usb_write(&mut usb_tx, &mut usb_pipe_reader), + ) + .await; + info!("Disconnected"); + } + }; + + // Read + write from UART + let uart_future = join( + uart_read(&mut uart_rx, &mut usb_pipe_writer), + uart_write(&mut uart_tx, &mut uart_pipe_reader), + ); + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join3(usb_fut, usb_future, uart_future).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +/// Read from the USB and write it to the UART TX pipe +async fn usb_read<'d, T: Instance + 'd>( + usb_rx: &mut Receiver<'d, Driver<'d, T>>, + uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, +) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = usb_rx.read_packet(&mut buf).await?; + let data = &buf[..n]; + trace!("USB IN: {:x}", data); + uart_pipe_writer.write(data).await; + } +} + +/// Read from the USB TX pipe and write it to the USB +async fn usb_write<'d, T: Instance + 'd>( + usb_tx: &mut Sender<'d, Driver<'d, T>>, + usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, +) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = usb_pipe_reader.read(&mut buf).await; + let data = &buf[..n]; + trace!("USB OUT: {:x}", data); + usb_tx.write_packet(&data).await?; + } +} + +/// Read from the UART and write it to the USB TX pipe +async fn uart_read( + uart_rx: &mut PioUartRx<'_>, + usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, +) -> ! { + let mut buf = [0; 64]; + loop { + let n = uart_rx.read(&mut buf).await.expect("UART read error"); + if n == 0 { + continue; + } + let data = &buf[..n]; + trace!("UART IN: {:x}", buf); + usb_pipe_writer.write(data).await; + } +} + +/// Read from the UART TX pipe and write it to the UART +async fn uart_write( + uart_tx: &mut PioUartTx<'_>, + uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, +) -> ! { + let mut buf = [0; 64]; + loop { + let n = uart_pipe_reader.read(&mut buf).await; + let data = &buf[..n]; + trace!("UART OUT: {:x}", data); + let _ = uart_tx.write(&data).await; + } +} + +mod uart { + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Pio, PioPin}; + use embassy_rp::Peripheral; + + use crate::uart_rx::PioUartRx; + use crate::uart_tx::PioUartTx; + use crate::Irqs; + + pub struct PioUart<'a> { + tx: PioUartTx<'a>, + rx: PioUartRx<'a>, + } + + impl<'a> PioUart<'a> { + pub fn new( + baud: u64, + pio: impl Peripheral

+ 'a, + tx_pin: impl PioPin, + rx_pin: impl PioPin, + ) -> PioUart<'a> { + let Pio { + mut common, sm0, sm1, .. + } = Pio::new(pio, Irqs); + + let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud); + let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud); + + PioUart { tx, rx } + } + + pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) { + (self.tx, self.rx) + } + } +} + +mod uart_tx { + use core::convert::Infallible; + + use embassy_rp::gpio::Level; + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; + use embedded_io_async::{ErrorType, Write}; + use fixed::traits::ToFixed; + use fixed_macro::types::U56F8; + + pub struct PioUartTx<'a> { + sm_tx: StateMachine<'a, PIO0, 0>, + } + + impl<'a> PioUartTx<'a> { + pub fn new( + common: &mut Common<'a, PIO0>, + mut sm_tx: StateMachine<'a, PIO0, 0>, + tx_pin: impl PioPin, + baud: u64, + ) -> Self { + let prg = pio_proc::pio_asm!( + r#" + .side_set 1 opt + + ; An 8n1 UART transmit program. + ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. + + pull side 1 [7] ; Assert stop bit, or stall with line in idle state + set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks + bitloop: ; This loop will run 8 times (8n1 UART) + out pins, 1 ; Shift 1 bit from OSR to the first OUT pin + jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. + "# + ); + let tx_pin = common.make_pio_pin(tx_pin); + sm_tx.set_pins(Level::High, &[&tx_pin]); + sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); + + let mut cfg = Config::default(); + + cfg.set_out_pins(&[&tx_pin]); + cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::TxOnly; + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + sm_tx.set_config(&cfg); + sm_tx.set_enable(true); + + Self { sm_tx } + } + + pub async fn write_u8(&mut self, data: u8) { + self.sm_tx.tx().wait_push(data as u32).await; + } + } + + impl ErrorType for PioUartTx<'_> { + type Error = Infallible; + } + + impl Write for PioUartTx<'_> { + async fn write(&mut self, buf: &[u8]) -> Result { + for byte in buf { + self.write_u8(*byte).await; + } + Ok(buf.len()) + } + } +} + +mod uart_rx { + use core::convert::Infallible; + + use embassy_rp::gpio::Level; + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; + use embedded_io_async::{ErrorType, Read}; + use fixed::traits::ToFixed; + use fixed_macro::types::U56F8; + + pub struct PioUartRx<'a> { + sm_rx: StateMachine<'a, PIO0, 1>, + } + + impl<'a> PioUartRx<'a> { + pub fn new( + common: &mut Common<'a, PIO0>, + mut sm_rx: StateMachine<'a, PIO0, 1>, + rx_pin: impl PioPin, + baud: u64, + ) -> Self { + let prg = pio_proc::pio_asm!( + r#" + ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and + ; break conditions more gracefully. + ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. + + start: + wait 0 pin 0 ; Stall until start bit is asserted + set x, 7 [10] ; Preload bit counter, then delay until halfway through + rx_bitloop: ; the first data bit (12 cycles incl wait, set). + in pins, 1 ; Shift data bit into ISR + jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles + jmp pin good_rx_stop ; Check stop bit (should be high) + + irq 4 rel ; Either a framing error or a break. Set a sticky flag, + wait 1 pin 0 ; and wait for line to return to idle state. + jmp start ; Don't push data if we didn't see good framing. + + good_rx_stop: ; No delay before returning to start; a little slack is + in null 24 + push ; important in case the TX clock is slightly too fast. + "# + ); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&prg.program), &[]); + + let rx_pin = common.make_pio_pin(rx_pin); + sm_rx.set_pins(Level::High, &[&rx_pin]); + cfg.set_in_pins(&[&rx_pin]); + cfg.set_jmp_pin(&rx_pin); + sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); + + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + cfg.shift_in.auto_fill = false; + cfg.shift_in.direction = ShiftDirection::Right; + cfg.shift_in.threshold = 32; + cfg.fifo_join = FifoJoin::RxOnly; + sm_rx.set_config(&cfg); + sm_rx.set_enable(true); + + Self { sm_rx } + } + + pub async fn read_u8(&mut self) -> u8 { + self.sm_rx.rx().wait_pull().await as u8 + } + } + + impl ErrorType for PioUartRx<'_> { + type Error = Infallible; + } + + impl Read for PioUartRx<'_> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + let mut i = 0; + while i < buf.len() { + buf[i] = self.read_u8().await; + i += 1; + } + Ok(i) + } + } +} diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 3de2bd48..bc87016e 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -12,7 +12,6 @@ use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; use fixed::types::U24F8; @@ -73,8 +72,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { cfg.set_out_pins(&[&out_pin]); cfg.set_set_pins(&[&out_pin]); - let relocated = RelocatedProgram::new(&prg); - cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); + cfg.use_program(&pio.load_program(&prg), &[&out_pin]); // Clock config, measured in kHz to avoid overflows // TODO CLOCK_FREQ should come from embassy_rp diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index 73520171..d3e67c8e 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -14,7 +14,7 @@ use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::{Read, Write}; +use embedded_io_async::{Read, Write}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 0a08f667..6c2f27ac 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -16,7 +16,7 @@ use embassy_rp::{bind_interrupts, peripherals}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index e3e39344..cd61ad78 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -19,7 +19,7 @@ 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_time::Duration; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 0223a363..55fcb4a6 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -19,7 +19,7 @@ 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_time::Duration; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 92933ab5..0d4d5fa1 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,11 +6,12 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } +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", "unstable-traits", "proto-ipv6"] } -embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } -embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } +embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } +embedded-io-async = { version = "0.5.0" } +embedded-io-adapters = { version = "0.5.0", features = ["futures-03"] } critical-section = { version = "1.1", features = ["std"] } async-io = "1.6.0" @@ -18,8 +19,10 @@ env_logger = "0.9.0" futures = { version = "0.3.17" } log = "0.4.14" nix = "0.26.2" -libc = "0.2.101" clap = { version = "3.0.0-beta.5", features = ["derive"] } rand_core = { version = "0.6.3", features = ["std"] } heapless = { version = "0.7.5", default-features = false } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 3aadb029..8d834505 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -6,17 +6,14 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use embassy_time::Duration; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index 65b5a2cd..6c19874d 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -6,15 +6,12 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::dns::DnsQueryType; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 3fc46156..98dcc992 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -4,15 +4,12 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/serial.rs b/examples/std/src/bin/serial.rs index 85ee54f7..0b289c74 100644 --- a/examples/std/src/bin/serial.rs +++ b/examples/std/src/bin/serial.rs @@ -5,7 +5,7 @@ mod serial_port; use async_io::Async; use embassy_executor::Executor; -use embedded_io::asynch::Read; +use embedded_io_async::Read; use log::*; use nix::sys::termios; use static_cell::StaticCell; @@ -29,7 +29,7 @@ async fn run() { // // This is not really needed, you could write the code below using futures::io directly. // It's useful if you want to have portable code across embedded and std. - let mut port = embedded_io::adapters::FromFutures::new(port); + let mut port = embedded_io_adapters::futures_03::FromFutures::new(port); info!("Serial opened!"); diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index df09986a..199e4c9e 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -7,17 +7,14 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write as _; +use embedded_io_async::Write as _; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 26837abe..9d188513 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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"] } defmt = "0.3" defmt-rtt = "0.4" @@ -19,3 +20,6 @@ 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 } + +[profile.release] +debug = 2 diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index b7b5eaa9..ca9ab1cf 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -7,13 +7,17 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Change stm32f091rc to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 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-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +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-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 29cad5b6..16796841 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -25,3 +26,6 @@ nb = "1.0.0" [profile.dev] opt-level = "s" + +[profile.release] +debug = 2 diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 652210c7..54eadd1a 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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"] } defmt = "0.3" defmt-rtt = "0.4" @@ -20,3 +21,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 } nb = "1.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 489d0ff4..5d8f5f74 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -24,3 +25,6 @@ heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" embedded-storage = "0.3.0" static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index 02c475f6..8e97e85e 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs @@ -49,12 +49,12 @@ impl<'a> Leds<'a> { async fn show(&mut self) { self.leds[self.current_led].set_high(); - if let Ok(new_message) = with_timeout(Duration::from_millis(500), CHANNEL.recv()).await { + if let Ok(new_message) = with_timeout(Duration::from_millis(500), CHANNEL.receive()).await { self.leds[self.current_led].set_low(); self.process_event(new_message).await; } else { self.leds[self.current_led].set_low(); - if let Ok(new_message) = with_timeout(Duration::from_millis(200), CHANNEL.recv()).await { + if let Ok(new_message) = with_timeout(Duration::from_millis(200), CHANNEL.receive()).await { self.process_event(new_message).await; } } diff --git a/examples/stm32f3/src/bin/spi_dma.rs b/examples/stm32f3/src/bin/spi_dma.rs index 95b2b686..a27c1d54 100644 --- a/examples/stm32f3/src/bin/spi_dma.rs +++ b/examples/stm32f3/src/bin/spi_dma.rs @@ -17,16 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PB5, - p.PB4, - p.DMA1_CH3, - p.DMA1_CH2, - Hertz(1_000_000), - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH2, spi_config); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml new file mode 100644 index 00000000..caf947be --- /dev/null +++ b/examples/stm32f334/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-run --chip STM32F334R8" + +[build] +target = "thumbv7em-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml new file mode 100644 index 00000000..65183b88 --- /dev/null +++ b/examples/stm32f334/Cargo.toml @@ -0,0 +1,26 @@ +[package] +edition = "2021" +name = "embassy-stm32f334-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-sync = { version = "0.2.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"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } + +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 } +nb = "1.0.0" +embedded-storage = "0.3.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f334/build.rs b/examples/stm32f334/build.rs new file mode 100644 index 00000000..8cd32d7e --- /dev/null +++ b/examples/stm32f334/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32f334/src/bin/button.rs b/examples/stm32f334/src/bin/button.rs new file mode 100644 index 00000000..599c0f27 --- /dev/null +++ b/examples/stm32f334/src/bin/button.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) { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let mut out1 = Output::new(p.PA8, Level::Low, Speed::High); + + out1.set_high(); + Timer::after(Duration::from_millis(500)).await; + out1.set_low(); + + Timer::after(Duration::from_millis(500)).await; + info!("end program"); + + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32f334/src/bin/hello.rs b/examples/stm32f334/src/bin/hello.rs new file mode 100644 index 00000000..65773210 --- /dev/null +++ b/examples/stm32f334/src/bin/hello.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::time::Hertz; +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 mut config = Config::default(); + config.rcc.hse = Some(Hertz(8_000_000)); + config.rcc.sysclk = Some(Hertz(16_000_000)); + let _p = embassy_stm32::init(config); + + loop { + info!("Hello World!"); + Timer::after(Duration::from_secs(1)).await; + } +} diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs new file mode 100644 index 00000000..2660b10c --- /dev/null +++ b/examples/stm32f334/src/bin/pwm.rs @@ -0,0 +1,71 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::hrtim::*; +use embassy_stm32::time::{khz, mhz}; +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 mut config: Config = Default::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)); + + let p = embassy_stm32::init(config); + info!("Hello World!"); + + let ch1 = PwmPin::new_cha(p.PA8); + let ch1n = ComplementaryPwmPin::new_cha(p.PA9); + let pwm = AdvancedPwm::new( + p.HRTIM1, + Some(ch1), + Some(ch1n), + None, + None, + None, + None, + None, + None, + None, + None, + ); + + info!("pwm constructed"); + + let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(5)); + + // embassy_stm32::pac::HRTIM1 + // .tim(0) + // .setr(0) + // .modify(|w| w.set_sst(Activeeffect::SETACTIVE)); + // + // Timer::after(Duration::from_millis(500)).await; + // + // embassy_stm32::pac::HRTIM1 + // .tim(0) + // .rstr(0) + // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE)); + + let max_duty = buck_converter.get_max_compare_value(); + + info!("max compare value: {}", max_duty); + + buck_converter.set_dead_time(max_duty / 20); + buck_converter.set_primary_duty(max_duty / 2); + buck_converter.set_secondary_duty(3 * max_duty / 4); + + buck_converter.start(); + + Timer::after(Duration::from_millis(500)).await; + + info!("end program"); + + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index c1c82136..ed456c5c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +# 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-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-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"] } @@ -18,7 +19,8 @@ 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" -embedded-io = "0.4.0" +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0" } 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 } diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index 08bed88d..20ce4edc 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -10,6 +10,7 @@ use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; +use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -40,17 +41,33 @@ async fn main(_spawner: Spawner) { can.as_mut() .modify_config() - .set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/ .set_loopback(true) // Receive own frames .set_silent(true) - .enable(); + .leave_disabled(); + + can.set_bitrate(1_000_000); + + can.enable().await; let mut i: u8 = 0; loop { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); + let tx_ts = Instant::now(); can.write(&tx_frame).await; - let (_, rx_frame) = can.read().await.unwrap(); - info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); + + let envelope = can.read().await.unwrap(); + + // We can measure loopback latency by using receive timestamp in the `Envelope`. + // Our frame is ~55 bits long (exlcuding bit stuffing), so at 1mbps loopback delay is at least 55 us. + // When measured with `tick-hz-1_000_000` actual latency is 80~83 us, giving a combined hardware and software + // overhead of ~25 us. Note that CPU frequency can greatly affect the result. + let latency = envelope.ts.saturating_duration_since(tx_ts); + + info!( + "loopback frame {=u8}, latency: {} us", + unwrap!(envelope.frame.data())[0], + latency.as_micros() + ); i += 1; } } diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs index 3a621671..aaedcfec 100644 --- a/examples/stm32f4/src/bin/dac.rs +++ b/examples/stm32f4/src/bin/dac.rs @@ -14,11 +14,11 @@ async fn main(_spawner: Spawner) -> ! { info!("Hello World, dude!"); let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); - dac.trigger(); } } } diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index d0b16439..393e60b7 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -11,13 +11,15 @@ 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, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + HASH_RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -36,7 +38,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; let _ = rng.async_fill_bytes(&mut seed).await; let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 7c590205..1013a844 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -4,9 +4,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PE9); + let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index a8a68ed6..83a3c753 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -4,10 +4,11 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; -use embassy_stm32::pwm::simple_pwm::PwmPin; -use embassy_stm32::pwm::Channel; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; +use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -16,8 +17,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PE9); - let ch1n = ComplementaryPwmPin::new_ch1(p.PA7); + let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); + let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); let mut pwm = ComplementaryPwm::new( p.TIM1, Some(ch1), diff --git a/examples/stm32f4/src/bin/spi.rs b/examples/stm32f4/src/bin/spi.rs index 05b48f47..0919e987 100644 --- a/examples/stm32f4/src/bin/spi.rs +++ b/examples/stm32f4/src/bin/spi.rs @@ -16,16 +16,10 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32f4/src/bin/spi_dma.rs b/examples/stm32f4/src/bin/spi_dma.rs index 3d2a1a1a..f291f7db 100644 --- a/examples/stm32f4/src/bin/spi_dma.rs +++ b/examples/stm32f4/src/bin/spi_dma.rs @@ -17,16 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PB5, - p.PB4, - p.DMA2_CH3, - p.DMA2_CH2, - Hertz(1_000_000), - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA2_CH3, p.DMA2_CH2, spi_config); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index c573dc3a..c0a64d94 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::usart::{BufferedUart, Config}; use embassy_stm32::{bind_interrupts, peripherals, usart}; -use embedded_io::asynch::BufRead; +use embedded_io_async::BufRead; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index b1f01417..763e3a9e 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -6,14 +6,14 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; use embassy_stm32::time::mhz; use embassy_stm32::usb_otg::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; @@ -38,6 +38,7 @@ async fn net_task(stack: &'static Stack>) -> ! { bind_interrupts!(struct Irqs { OTG_FS => usb_otg::InterruptHandler; + HASH_RNG => rng::InterruptHandler; }); #[embassy_executor::main] @@ -104,7 +105,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; unwrap!(rng.async_fill_bytes(&mut seed).await); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 84d7b79c..b658a964 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -5,12 +5,13 @@ version = "0.1.0" 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-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +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-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } -embedded-io = { version = "0.4.0", features = ["async"] } +embedded-io-async = { version = "0.5.0" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" @@ -27,3 +28,6 @@ rand_core = "0.6.3" critical-section = "1.1" embedded-storage = "0.3.0" static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32f7/build.rs b/examples/stm32f7/build.rs index 2b5d412a..8cd32d7e 100644 --- a/examples/stm32f7/build.rs +++ b/examples/stm32f7/build.rs @@ -1,43 +1,5 @@ -//! adapted from https://github.com/stm32-rs/stm32f7xx-hal/blob/master/build.rs -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; -use std::{env, io}; - -#[derive(Debug)] -enum Error { - Env(env::VarError), - Io(io::Error), -} - -impl From for Error { - fn from(error: env::VarError) -> Self { - Self::Env(error) - } -} - -impl From for Error { - fn from(error: io::Error) -> Self { - Self::Io(error) - } -} - -fn main() -> Result<(), Error> { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=memory.x"); - - let out_dir = env::var("OUT_DIR")?; - let out_dir = PathBuf::from(out_dir); - - let memory_x = include_bytes!("memory.x").as_ref(); - File::create(out_dir.join("memory.x"))?.write_all(memory_x)?; - - // Tell Cargo where to find the file. - println!("cargo:rustc-link-search={}", out_dir.display()); - +fn main() { 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/stm32f7/memory.x b/examples/stm32f7/memory.x deleted file mode 100644 index 899f7a4b..00000000 --- a/examples/stm32f7/memory.x +++ /dev/null @@ -1,12 +0,0 @@ -/* For STM32F765,767,768,769,777,778,779 devices */ -MEMORY -{ - /* NOTE K = KiBi = 1024 bytes */ - FLASH : ORIGIN = 0x08000000, LENGTH = 2M - RAM : ORIGIN = 0x20000000, LENGTH = 368K + 16K -} - -/* This is where the call stack will be allocated. */ -/* The stack is of the full descending type. */ -/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ -_stack_start = ORIGIN(RAM) + LENGTH(RAM); diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index 1b5b377e..e9650f23 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -60,7 +60,7 @@ async fn main(spawner: Spawner) { spawner.spawn(send_can_message(tx)).unwrap(); loop { - let frame = rx.read().await.unwrap(); - println!("Received: {:?}", frame); + let envelope = rx.read().await.unwrap(); + println!("Received: {:?}", envelope); } } diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index c6b2ba45..f0e280c3 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -11,14 +11,16 @@ 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, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -37,7 +39,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index c88282d9..4e88151a 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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"] } defmt = "0.3" defmt-rtt = "0.4" @@ -19,3 +20,6 @@ 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 } + +[profile.release] +debug = 2 diff --git a/examples/stm32g0/src/bin/flash.rs b/examples/stm32g0/src/bin/flash.rs new file mode 100644 index 00000000..ed9f2e84 --- /dev/null +++ b/examples/stm32g0/src/bin/flash.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::flash::Flash; +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 addr: u32 = 0x8000; + + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read: {=[u8]:x}", buf); + info!("Erasing..."); + unwrap!(f.blocking_erase(addr, addr + 2 * 1024)); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!(f.blocking_write( + addr, + &[ + 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, 32 + ] + )); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read: {=[u8]:x}", buf); +} diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs index 81fdd15c..ee7aaf33 100644 --- a/examples/stm32g0/src/bin/spi_neopixel.rs +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -76,7 +76,9 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Start test using spi as neopixel driver"); - let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default()); + let mut config = Config::default(); + config.frequency = Hertz(4_000_000); + let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, config); let mut neopixels = Ws2812::new(); diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 18bd03c3..7bb05dbc 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -5,11 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } +embassy-sync = { version = "0.2.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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" @@ -21,3 +21,6 @@ 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 } + +[profile.release] +debug = 2 diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 8f7842ed..01e9cb47 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -4,9 +4,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PC0); + let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 227bc28b..f7a1de63 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -5,12 +5,13 @@ version = "0.1.0" 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-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +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-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } -embedded-io = { version = "0.4.0", features = ["async"] } +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"] } defmt = "0.3" @@ -19,9 +20,9 @@ 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" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } -embedded-nal-async = "0.4.0" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } +embedded-nal-async = { version = "0.5.0" } 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 } diff --git a/examples/stm32h5/memory.x b/examples/stm32h5/memory.x deleted file mode 100644 index 45606150..00000000 --- a/examples/stm32h5/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY -{ - FLASH : ORIGIN = 0x08000000, LENGTH = 0x200000 - RAM : ORIGIN = 0x20000000, LENGTH = 0x50000 -} diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 0bff85ed..763520ab 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -12,14 +12,16 @@ use embassy_stm32::peripherals::ETH; use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -56,7 +58,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h5/src/bin/rng.rs b/examples/stm32h5/src/bin/rng.rs index af9be0b6..7c8c50ec 100644 --- a/examples/stm32h5/src/bin/rng.rs +++ b/examples/stm32h5/src/bin/rng.rs @@ -5,14 +5,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs index debd6f45..a6b2e690 100644 --- a/examples/stm32h5/src/bin/usart_split.rs +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -44,7 +44,7 @@ async fn main(spawner: Spawner) -> ! { unwrap!(spawner.spawn(reader(rx))); loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 768702fa..1ee11b07 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -5,12 +5,13 @@ version = "0.1.0" 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-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +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-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } -embedded-io = { version = "0.4.0", features = ["async"] } +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"] } defmt = "0.3" @@ -19,9 +20,9 @@ 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" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } -embedded-nal-async = "0.4.0" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } +embedded-nal-async = { version = "0.5.0" } 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 } diff --git a/examples/stm32h7/memory.x b/examples/stm32h7/memory.x deleted file mode 100644 index 026b14b9..00000000 --- a/examples/stm32h7/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY -{ - FLASH : ORIGIN = 0x8000000, LENGTH = 1024K - RAM : ORIGIN = 0x24000000, LENGTH = 384K -} diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index 586b4154..ee078286 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs @@ -21,11 +21,11 @@ fn main() -> ! { let p = embassy_stm32::init(config); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); - dac.trigger(); } } } diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs new file mode 100644 index 00000000..a9cb5d1e --- /dev/null +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -0,0 +1,140 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +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::timer::low_level::Basic16bitInstance; +use micromath::F32Ext; +use {defmt_rtt as _, panic_probe as _}; + +pub type Dac1Type = + embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; + +pub type Dac2Type = + embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; + +#[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)); + + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config); + + // Obtain two independent channels (p.DAC1 can only be consumed once, though!) + let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); + + spawner.spawn(dac_task1(dac_ch1)).ok(); + spawner.spawn(dac_task2(dac_ch2)).ok(); +} + +#[embassy_executor::task] +async fn dac_task1(mut dac: Dac1Type) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM6 frequency is {}", TIM6::frequency()); + const FREQUENCY: Hertz = Hertz::hz(200); + + // Compute the reload value such that we obtain the FREQUENCY for the sine + let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; + + // Depends on your clock and on the specific chip used, you may need higher or lower values here + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); + dac.enable_channel().unwrap(); + + TIM6::enable(); + TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM6::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + debug!( + "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM6::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + // Loop technically not necessary if DMA circular mode is enabled + loop { + info!("Loop DAC1"); + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } + } +} + +#[embassy_executor::task] +async fn dac_task2(mut dac: Dac2Type) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM7 frequency is {}", TIM7::frequency()); + + const FREQUENCY: Hertz = Hertz::hz(600); + let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; + + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + TIM7::enable(); + TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM7::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); + + debug!( + "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM7::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } +} + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = 3.14 * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = 3.14 + 3.14 * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} + +fn calculate_array() -> [u8; N] { + let mut res = [0; N]; + let mut i = 0; + while i < N { + res[i] = to_sine_wave(i as u8); + i += 1; + } + res +} diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index cfafcaed..26a386e4 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -11,14 +11,16 @@ 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, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -38,7 +40,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 4ed73757..6664410c 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -11,15 +11,17 @@ 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, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -39,7 +41,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index d360df08..45b0872b 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -6,8 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::low_level::AFType; use embassy_stm32::gpio::Speed; -use embassy_stm32::pwm::*; use embassy_stm32::time::{khz, mhz, Hertz}; +use embassy_stm32::timer::*; use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index c5c0dd29..aa5ec1bc 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -4,9 +4,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +25,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PA6); + let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index af9be0b6..7c8c50ec 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.rs @@ -5,14 +5,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index 1f407f00..28bba2b8 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -43,16 +43,10 @@ fn main() -> ! { config.rcc.pll1.q_ck = Some(mhz(100)); let p = embassy_stm32::init(config); - let spi = spi::Spi::new( - p.SPI3, - p.PB3, - p.PB5, - p.PB4, - NoDma, - NoDma, - mhz(1), - spi::Config::default(), - ); + let mut spi_config = spi::Config::default(); + spi_config.frequency = mhz(1); + + let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, NoDma, NoDma, spi_config); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 53004fc9..f6e30cfa 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -39,16 +39,10 @@ fn main() -> ! { config.rcc.pll1.q_ck = Some(mhz(100)); let p = embassy_stm32::init(config); - let spi = spi::Spi::new( - p.SPI3, - p.PB3, - p.PB5, - p.PB4, - p.DMA1_CH3, - p.DMA1_CH4, - mhz(1), - spi::Config::default(), - ); + let mut spi_config = spi::Config::default(); + spi_config.frequency = mhz(1); + + let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH4, spi_config); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index 330d1ce0..aa075345 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -44,7 +44,7 @@ async fn main(spawner: Spawner) -> ! { unwrap!(spawner.spawn(reader(rx))); loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 747cec7b..d4e0da0b 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -7,13 +7,14 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", "embassy-executor/nightly", - "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "embedded-io/async"] + "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "dep:embedded-io-async"] [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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-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 } @@ -23,7 +24,8 @@ defmt = "0.3" defmt-rtt = "0.4" embedded-storage = "0.3.0" -embedded-io = "0.4.0" +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0", optional = true } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -33,5 +35,8 @@ heapless = { version = "0.7.5", default-features = false } embedded-hal = "0.2.6" static_cell = "1.1" +[profile.release] +debug = 2 + [patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } +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 588cea1e..7729b416 100644 --- a/examples/stm32l0/src/bin/lora_cad.rs +++ b/examples/stm32l0/src/bin/lora_cad.rs @@ -27,17 +27,11 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - khz(200), - spi::Config::default(), - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index c397edd5..10608aeb 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -12,8 +12,8 @@ use embassy_lora::LoraTimer; use embassy_stm32::exti::{Channel, ExtiInput}; use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; use embassy_stm32::rng::Rng; -use embassy_stm32::spi; use embassy_stm32::time::khz; +use embassy_stm32::{bind_interrupts, peripherals, rng, spi}; use embassy_time::Delay; use lora_phy::mod_params::*; use lora_phy::sx1276_7_8_9::SX1276_7_8_9; @@ -23,6 +23,10 @@ use lorawan_device::async_device::lora_radio::LoRaRadio; use lorawan_device::async_device::{region, Device, JoinMode}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG_LPUART1 => rng::InterruptHandler; +}); + const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region #[embassy_executor::main] @@ -32,17 +36,11 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - khz(200), - spi::Config::default(), - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); @@ -66,7 +64,7 @@ async fn main(_spawner: Spawner) { let radio = LoRaRadio::new(lora); let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs index bb750950..0f9f6095 100644 --- a/examples/stm32l0/src/bin/lora_p2p_receive.rs +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -27,17 +27,11 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - khz(200), - spi::Config::default(), - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs index e6fadc01..c85c3c2b 100644 --- a/examples/stm32l0/src/bin/lora_p2p_send.rs +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs @@ -27,17 +27,11 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - khz(200), - spi::Config::default(), - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index 9b5b3e27..583e3d12 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -15,16 +15,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index f2c72a10..f5dabcc4 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::usart::{BufferedUart, Config}; use embassy_stm32::{bind_interrupts, peripherals, usart}; -use embedded_io::asynch::{Read, Write}; +use embedded_io_async::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index dcca1cc3..192fd3e3 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +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-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } @@ -20,3 +20,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 } embedded-storage = "0.3.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 0a532e8e..905b4d75 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -15,16 +15,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new( - p.SPI1, - p.PA5, - p.PA7, - p.PA6, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index c5555851..f552a610 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -5,11 +5,12 @@ version = "0.1.0" 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-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +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-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" @@ -18,11 +19,14 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } 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 } micromath = "2.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32l4/build.rs b/examples/stm32l4/build.rs index 30691aa9..8cd32d7e 100644 --- a/examples/stm32l4/build.rs +++ b/examples/stm32l4/build.rs @@ -1,34 +1,4 @@ -//! This build script copies the `memory.x` file from the crate root into -//! a directory where the linker can always find it at build time. -//! For many projects this is optional, as the linker always searches the -//! project root directory -- wherever `Cargo.toml` is. However, if you -//! are using a workspace or have a more complicated build setup, this -//! build script becomes required. Additionally, by requesting that -//! Cargo re-run the build script whenever `memory.x` is changed, -//! updating `memory.x` ensures a rebuild of the application with the -//! new memory settings. - -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; - fn main() { - // Put `memory.x` in our output directory and ensure it's - // on the linker search path. - let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - File::create(out.join("memory.x")) - .unwrap() - .write_all(include_bytes!("memory.x")) - .unwrap(); - println!("cargo:rustc-link-search={}", out.display()); - - // By default, Cargo will re-run a build script whenever - // any file in the project changes. By specifying `memory.x` - // here, we ensure the build script is only re-run when - // `memory.x` is changed. - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); diff --git a/examples/stm32l4/memory.x b/examples/stm32l4/memory.x deleted file mode 100644 index eb87d1b5..00000000 --- a/examples/stm32l4/memory.x +++ /dev/null @@ -1,7 +0,0 @@ -MEMORY -{ - /* NOTE 1 K = 1 KiBi = 1024 bytes */ - /* These values correspond to the STM32L4S5 */ - FLASH : ORIGIN = 0x08000000, LENGTH = 1024K - RAM : ORIGIN = 0x20000000, LENGTH = 128K -} diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index ade43eb3..0193a248 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs @@ -13,11 +13,11 @@ fn main() -> ! { info!("Hello World!"); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); - dac.trigger(); } } } diff --git a/examples/stm32l4/src/bin/rng.rs b/examples/stm32l4/src/bin/rng.rs index c9302bb9..806e49f5 100644 --- a/examples/stm32l4/src/bin/rng.rs +++ b/examples/stm32l4/src/bin/rng.rs @@ -6,9 +6,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; use embassy_stm32::rng::Rng; -use embassy_stm32::Config; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -24,7 +28,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index d72d5ddb..294ea456 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -33,10 +33,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new( - p.RTC, - RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), - ); + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); diff --git a/examples/stm32l4/src/bin/spi.rs b/examples/stm32l4/src/bin/spi.rs index 76e316a2..54cf68f7 100644 --- a/examples/stm32l4/src/bin/spi.rs +++ b/examples/stm32l4/src/bin/spi.rs @@ -15,16 +15,10 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 62ef0130..f1b80087 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -17,16 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut spi = BlockingAsync::new(spi); diff --git a/examples/stm32l4/src/bin/spi_dma.rs b/examples/stm32l4/src/bin/spi_dma.rs index 89471db5..ff9b5b43 100644 --- a/examples/stm32l4/src/bin/spi_dma.rs +++ b/examples/stm32l4/src/bin/spi_dma.rs @@ -14,16 +14,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - p.DMA1_CH1, - p.DMA1_CH2, - Hertz(1_000_000), - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, p.DMA1_CH1, p.DMA1_CH2, spi_config); // These are the pins for the Inventek eS-Wifi SPI Wifi Adapter. diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 54911482..b46c2570 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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-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" } @@ -24,5 +25,8 @@ embedded-hal = "0.2.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } rand_core = { version = "0.6.3", default-features = false } -embedded-io = { version = "0.4.0", features = ["async"] } +embedded-io-async = { version = "0.5.0" } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs index d359847e..9549d64d 100644 --- a/examples/stm32l5/src/bin/rng.rs +++ b/examples/stm32l5/src/bin/rng.rs @@ -6,9 +6,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; use embassy_stm32::rng::Rng; -use embassy_stm32::Config; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -23,7 +27,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 32eba427..15b84761 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -9,11 +9,11 @@ use embassy_net::{Stack, StackResources}; use embassy_stm32::rcc::*; use embassy_stm32::rng::Rng; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{bind_interrupts, peripherals, rng, usb, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; @@ -24,6 +24,7 @@ const MTU: usize = 1514; bind_interrupts!(struct Irqs { USB_FS => usb::InterruptHandler; + RNG => rng::InterruptHandler; }); #[embassy_executor::task] @@ -99,7 +100,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let seed = rng.next_u64(); // Init network stack diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 835e3294..f928e7a6 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" @@ -22,3 +23,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index becf2d3f..f58a5189 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -5,11 +5,13 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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 } defmt = "0.3" defmt-rtt = "0.4" @@ -20,11 +22,11 @@ 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"]} [features] default = ["ble", "mac"] -mac = ["embassy-stm32-wpan/mac"] +mac = ["embassy-stm32-wpan/mac", "dep:embassy-net"] ble = ["embassy-stm32-wpan/ble"] [[bin]] @@ -39,10 +41,17 @@ required-features = ["mac"] name = "mac_ffd" required-features = ["mac"] +[[bin]] +name = "mac_ffd_net" +required-features = ["mac"] + [[bin]] name = "eddystone_beacon" required-features = ["ble"] [[bin]] name = "gatt_server" -required-features = ["ble"] \ No newline at end of file +required-features = ["ble"] + +[profile.release] +debug = 2 diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index 451bd7d2..ea150c67 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -8,6 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::hci::host::uart::UartHci; use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; use embassy_stm32_wpan::hci::types::AdvertisingType; @@ -54,7 +55,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 0f6419d4..dd67249c 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -8,6 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::hci::event::command::{CommandComplete, ReturnParameters}; use embassy_stm32_wpan::hci::host::uart::{Packet, UartHci}; use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; @@ -62,7 +63,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index e4d81997..881dc488 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; @@ -30,7 +31,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. @@ -39,7 +40,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. @@ -49,7 +50,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); @@ -67,11 +70,13 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; @@ -82,8 +87,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting short address"); let short_address: u16 = 0x1122; @@ -94,8 +98,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting association permit"); let association_permit: bool = true; @@ -106,8 +109,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting TX power"); let transmit_power: i8 = 2; @@ -118,8 +120,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("starting FFD device"); mbox.mac_subsystem @@ -134,8 +135,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting RX on when idle"); let rx_on_while_idle: bool = true; @@ -146,14 +146,14 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); loop { let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); - if let Ok(evt) = evt { + defmt::info!("parsed mac event"); + defmt::info!("{:#x}", evt); + match evt { MacEvent::MlmeAssociateInd(association) => mbox .mac_subsystem @@ -167,17 +167,22 @@ async fn main(spawner: Spawner) { .await .unwrap(), MacEvent::McpsDataInd(data_ind) => { - let data_addr = data_ind.msdu_ptr; - let mut data = [0u8; 256]; - unsafe { data_addr.copy_to(&mut data as *mut _, data_ind.msdu_length as usize) } - info!("{}", data[..data_ind.msdu_length as usize]); + let payload = data_ind.payload(); + let ref_payload = b"Hello from embassy!"; + info!("{}", payload); - if &data[..data_ind.msdu_length as usize] == b"Hello from embassy!" { + if payload == ref_payload { info!("success"); + } else { + info!("ref payload: {}", ref_payload); } } - _ => {} + _ => { + defmt::info!("other mac event"); + } } + } else { + defmt::info!("failed to parse mac event"); } } } diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs new file mode 100644 index 00000000..f8c76b5a --- /dev/null +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -0,0 +1,173 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; +use embassy_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId}; +use embassy_stm32_wpan::mac::{self, Runner}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::task] +async fn run_mac(runner: &'static Runner<'static>) { + runner.run().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - 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. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - 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_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. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + mbox.mac_subsystem + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000001; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting short address"); + let short_address: u16 = 0x1122; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &short_address as *const _ as *const u8, + pib_attribute: PibId::ShortAddress, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting association permit"); + let association_permit: bool = true; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &association_permit as *const _ as *const u8, + pib_attribute: PibId::AssociationPermit, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting TX power"); + let transmit_power: i8 = 2; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &transmit_power as *const _ as *const u8, + pib_attribute: PibId::TransmitPower, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("starting FFD device"); + mbox.mac_subsystem + .send_command(&StartRequest { + pan_id: PanId([0x1A, 0xAA]), + channel_number: MacChannel::Channel16, + beacon_order: 0x0F, + superframe_order: 0x0F, + pan_coordinator: true, + battery_life_extension: false, + ..Default::default() + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting RX on when idle"); + let rx_on_while_idle: bool = true; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, + pib_attribute: PibId::RxOnWhenIdle, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + let tx_queue = [ + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + ]; + + let runner = make_static!(Runner::new(mbox.mac_subsystem, tx_queue)); + + spawner.spawn(run_mac(runner)).unwrap(); + + let (driver, control) = mac::new(runner).await; + + let _ = driver; + let _ = control; +} diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index b2dac72c..000355de 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{ @@ -32,7 +33,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. @@ -41,7 +42,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. @@ -51,7 +52,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); @@ -69,11 +72,13 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; @@ -84,24 +89,27 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("getting extended address"); mbox.mac_subsystem .send_command(&GetRequest { pib_attribute: PibId::ExtendedAddress, + ..Default::default() }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); - if let Ok(MacEvent::MlmeGetCnf(evt)) = evt { - if evt.pib_attribute_value_len == 8 { - let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); - info!("value {:#x}", value) + if let MacEvent::MlmeGetCnf(evt) = evt { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + + info!("value {:#x}", value) + } } } @@ -120,13 +128,15 @@ async fn main(spawner: Spawner) { }; info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + let short_addr = { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); - let short_addr = if let Ok(MacEvent::MlmeAssociateCnf(conf)) = evt { - conf.assoc_short_address - } else { - defmt::panic!() + if let MacEvent::MlmeAssociateCnf(conf) = evt { + conf.assoc_short_address + } else { + defmt::panic!() + } }; info!("setting short address"); @@ -137,34 +147,39 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); - - info!("sending data"); - let mut data_buffer = [0u8; 256]; - let data = b"Hello from embassy!"; - data_buffer[..data.len()].copy_from_slice(data); - mbox.mac_subsystem - .send_command(&DataRequest { - src_addr_mode: AddressMode::Short, - dst_addr_mode: AddressMode::Short, - dst_pan_id: PanId([0x1A, 0xAA]), - dst_address: MacAddress::BROADCAST, - msdu_handle: 0x02, - ack_tx: 0x00, - gts_tx: false, - msdu_ptr: &data_buffer as *const _ as *const u8, - msdu_length: data.len() as u8, - security_level: SecurityLevel::Unsecure, - ..Default::default() - }) - .await - .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); - - loop { - let evt = mbox.mac_subsystem.read().await; + { + let evt = mbox.mac_subsystem.read().await.unwrap(); info!("{:#x}", evt); } + + info!("sending data"); + let data = b"Hello from embassy!"; + mbox.mac_subsystem + .send_command( + DataRequest { + src_addr_mode: AddressMode::Short, + dst_addr_mode: AddressMode::Short, + dst_pan_id: PanId([0x1A, 0xAA]), + dst_address: MacAddress::BROADCAST, + msdu_handle: 0x02, + ack_tx: 0x00, + gts_tx: false, + security_level: SecurityLevel::Unsecure, + ..Default::default() + } + .set_buffer(data), + ) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + } + + loop { + match mbox.mac_subsystem.read().await { + Ok(evt) => info!("{:#x}", evt), + _ => continue, + }; + } } diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 9fc4b8aa..fc49c3c4 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::TlMbox; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -41,7 +42,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index 90349422..5745ebd0 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -40,7 +41,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index 5931c392..f32e07d9 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -27,7 +28,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. @@ -36,7 +37,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. @@ -46,7 +47,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index e2c66f45..1c771ddc 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +# 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-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-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" } @@ -27,5 +28,8 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } 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 = "ad289428fd44b02788e2fa2116445cc8f640a265" } +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 805d2141..2c9c9886 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -10,9 +10,9 @@ use embassy_executor::Spawner; use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; use embassy_lora::LoraTimer; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; use embassy_stm32::spi::Spi; -use embassy_stm32::{bind_interrupts, pac}; +use embassy_stm32::{bind_interrupts, pac, peripherals}; use embassy_time::Delay; use lora_phy::mod_params::*; use lora_phy::sx1261_2::SX1261_2; @@ -26,6 +26,7 @@ const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set th bind_interrupts!(struct Irqs{ SUBGHZ_RADIO => InterruptHandler; + RNG => rng::InterruptHandler; }); #[embassy_executor::main] @@ -58,7 +59,7 @@ async fn main(_spawner: Spawner) { }; let radio = LoRaRadio::new(lora); let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs index d8562fca..592e65f4 100644 --- a/examples/stm32wl/src/bin/random.rs +++ b/examples/stm32wl/src/bin/random.rs @@ -4,10 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pac; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; +use embassy_stm32::{bind_interrupts, pac, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs{ + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -21,7 +25,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs index e1182549..fb1bc6e3 100644 --- a/examples/stm32wl/src/bin/rtc.rs +++ b/examples/stm32wl/src/bin/rtc.rs @@ -27,10 +27,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new( - p.RTC, - RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), - ); + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 3679e385..5b206d71 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } +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"] } wasm-logger = "0.2.0" @@ -17,3 +17,6 @@ wasm-bindgen = "0.2" web-sys = { version = "0.3", features = ["Document", "Element", "HtmlElement", "Node", "Window" ] } log = "0.4.11" critical-section = { version = "1.1", features = ["std"] } + +[profile.release] +debug = 2 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 179ed1d6..7b34afa2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-06-28" +channel = "nightly-2023-08-19" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 7ce51aa5..4e31bed5 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -9,13 +9,15 @@ 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-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } +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-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } -embedded-io = { version = "0.4.0", features = ["async"] } +embedded-io-async = { version = "0.5.0" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -embedded-hal-async = { version = "0.2.0-alpha.2" } +embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] } +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" ] } defmt = "0.3" diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs index 72a4cb4e..932e5926 100644 --- a/tests/nrf/src/bin/buffered_uart.rs +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 50960206..8abeae6d 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use core::mem; use core::ptr::NonNull; diff --git a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs new file mode 100644 index 00000000..0446d39a --- /dev/null +++ b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs @@ -0,0 +1,250 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"ak-gwe-r7"); +teleprobe_meta::timeout!(120); + +use defmt::{error, 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_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 embedded_hal_bus::spi::ExclusiveDevice; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +type MyDriver = Enc28j60< + ExclusiveDevice, Output<'static, peripherals::P0_15>, Delay>, + Output<'static, peripherals::P0_13>, +>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("running!"); + + let eth_sck = p.P0_20; + let eth_mosi = p.P0_22; + let eth_miso = p.P0_24; + let eth_cs = p.P0_15; + let eth_rst = p.P0_13; + let _eth_irq = p.P0_12; + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M16; + let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config); + let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard); + let spi = ExclusiveDevice::new(spi, cs, Delay); + + let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard); + let mac_addr = [2, 3, 4, 5, 6, 7]; + let device = Enc28j60::new(spi, Some(rst), mac_addr); + + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + // }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + 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); + + 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/timer.rs b/tests/nrf/src/bin/timer.rs index 607c5bbf..c00f35fd 100644 --- a/tests/nrf/src/bin/timer.rs +++ b/tests/nrf/src/bin/timer.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use defmt::{assert, info}; use embassy_executor::Spawner; diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 398ab9d2..97ebafec 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -1,9 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] - -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); +teleprobe_meta::timeout!(120); use defmt::{error, info, unwrap}; use embassy_executor::Spawner; @@ -15,12 +14,10 @@ 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 embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; -teleprobe_meta::timeout!(120); - bind_interrupts!(struct Irqs { SPIM3 => spim::InterruptHandler; RNG => embassy_nrf::rng::InterruptHandler; @@ -76,8 +73,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); - control.init().await; - control.join(WIFI_NETWORK, WIFI_PASSWORD).await; + unwrap!(control.init().await); + unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await); // Generate random seed let mut rng = Rng::new(p.RNG, Irqs); @@ -118,9 +115,9 @@ const WIFI_NETWORK: &str = "EmbassyTest"; const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; const TEST_DURATION: usize = 10; -const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 150; -const TEST_EXPECTED_UPLOAD_KBPS: usize = 150; -const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150; +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); diff --git a/tests/nrf/src/common.rs b/tests/nrf/src/common.rs deleted file mode 100644 index 1a05ac1c..00000000 --- a/tests/nrf/src/common.rs +++ /dev/null @@ -1 +0,0 @@ -teleprobe_meta::target!(b"nrf52840-dk"); diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 61f886c0..be610b1c 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -7,7 +7,7 @@ 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-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } +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-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index f2c90278..c494b66e 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,11 +8,12 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1.1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.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"] } +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-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"] } @@ -22,15 +23,17 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +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.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } -embedded-io = { version = "0.4.0", features = ["async"] } +embedded-io-async = { version = "0.5.0" } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} pio = "0.2" pio-proc = "0.2" +rand = { version = "0.8.5", default-features = false } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index e659844a..0250fd5f 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs @@ -1,14 +1,13 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; +use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler, Sample}; use embassy_rp::bind_interrupts; -use embassy_rp::gpio::Pull; +use embassy_rp::gpio::{Level, Output, Pull}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -18,16 +17,18 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut p = embassy_rp::init(Default::default()); + let _power_reg_pwm_mode = Output::new(p.PIN_23, Level::High); + let _wifi_off = Output::new(p.PIN_25, Level::High); let mut adc = Adc::new(p.ADC, Irqs, Config::default()); { { - let mut p = Pin::new(&mut p.PIN_26, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_26, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); defmt::assert!(adc.read(&mut p).await.unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_26, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_26, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); defmt::assert!(adc.read(&mut p).await.unwrap() > 0b11_0000_0000); } @@ -35,21 +36,21 @@ async fn main(_spawner: Spawner) { // not bothering with async reads from now on { { - let mut p = Pin::new(&mut p.PIN_27, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_27, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_27, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_27, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); } } { { - let mut p = Pin::new(&mut p.PIN_28, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_28, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_28, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_28, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); } } @@ -57,24 +58,71 @@ async fn main(_spawner: Spawner) { // gp29 is connected to vsys through a 200k/100k divider, // adding pulls should change the value let low = { - let mut p = Pin::new(&mut p.PIN_29, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::Down); adc.blocking_read(&mut p).unwrap() }; let none = { - let mut p = Pin::new(&mut p.PIN_29, Pull::None); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::None); adc.blocking_read(&mut p).unwrap() }; let up = { - let mut p = Pin::new(&mut p.PIN_29, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::Up); adc.blocking_read(&mut p).unwrap() }; defmt::assert!(low < none); defmt::assert!(none < up); } + { + let temp = convert_to_celsius( + adc.read(&mut Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR)) + .await + .unwrap(), + ); + defmt::assert!(temp > 0.0); + defmt::assert!(temp < 60.0); + } - let temp = convert_to_celsius(adc.read_temperature().await.unwrap()); - defmt::assert!(temp > 0.0); - defmt::assert!(temp < 60.0); + // run a bunch of conversions. we'll only check gp29 and the temp + // sensor here for brevity, if those two work the rest will too. + { + // gp29 is connected to vsys through a 200k/100k divider, + // adding pulls should change the value + let mut low = [0u16; 16]; + let mut none = [0u8; 16]; + let mut up = [Sample::default(); 16]; + adc.read_many( + &mut Channel::new_pin(&mut p.PIN_29, Pull::Down), + &mut low, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + adc.read_many( + &mut Channel::new_pin(&mut p.PIN_29, Pull::None), + &mut none, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + adc.read_many_raw(&mut Channel::new_pin(&mut p.PIN_29, Pull::Up), &mut up, &mut p.DMA_CH0) + .await; + defmt::assert!(low.iter().zip(none.iter()).all(|(l, n)| *l >> 4 < *n as u16)); + defmt::assert!(up.iter().all(|s| s.good())); + defmt::assert!(none.iter().zip(up.iter()).all(|(n, u)| (*n as u16) < u.value())); + } + { + let mut temp = [0u16; 16]; + adc.read_many( + &mut Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR), + &mut temp, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + let temp = temp.map(convert_to_celsius); + defmt::assert!(temp.iter().all(|t| *t > 0.0)); + defmt::assert!(temp.iter().all(|t| *t < 60.0)); + } info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index bc127e2e..1c665f95 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use cyw43_pio::PioSpi; use defmt::{assert, panic, *}; @@ -48,9 +47,9 @@ async fn main(spawner: Spawner) { } // cyw43 firmware needs to be flashed manually: - // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x101c0000 + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x101b0000 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x101f8000 - let fw = unsafe { core::slice::from_raw_parts(0x101c0000 as *const u8, 224190) }; + let fw = unsafe { core::slice::from_raw_parts(0x101b0000 as *const u8, 230321) }; let clm = unsafe { core::slice::from_raw_parts(0x101f8000 as *const u8, 4752) }; let pwr = Output::new(p.PIN_23, Level::Low); diff --git a/tests/rp/src/bin/dma_copy_async.rs b/tests/rp/src/bin/dma_copy_async.rs index 2c0b559a..b071ed9d 100644 --- a/tests/rp/src/bin/dma_copy_async.rs +++ b/tests/rp/src/bin/dma_copy_async.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/ethernet_w5100s_perf.rs b/tests/rp/src/bin/ethernet_w5100s_perf.rs new file mode 100644 index 00000000..faa8638c --- /dev/null +++ b/tests/rp/src/bin/ethernet_w5100s_perf.rs @@ -0,0 +1,249 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"w5100s-evb-pico"); +teleprobe_meta::timeout!(120); + +use defmt::{assert, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, 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 embedded_hal_bus::spi::ExclusiveDevice; +use rand::RngCore; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + W5100S, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = make_static!(State::<8, 8>::new()); + let (device, runner) = embassy_net_wiznet::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + embassy_net::Config::dhcpv4(Default::default()), + make_static!(StackResources::<2>::new()), + seed + )); + + // 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); + + 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/flash.rs b/tests/rp/src/bin/flash.rs index cf9b86df..75be2bf0 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -1,16 +1,15 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; -use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; -const ADDR_OFFSET: u32 = 0x4000; +const ADDR_OFFSET: u32 = 0x8000; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -23,26 +22,26 @@ async fn main(_spawner: Spawner) { // https://github.com/knurling-rs/defmt/pull/683 Timer::after(Duration::from_millis(10)).await; - let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); + let mut flash = embassy_rp::flash::Flash::<_, Async, { 2 * 1024 * 1024 }>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id - let jedec = defmt::unwrap!(flash.jedec_id()); + let jedec = defmt::unwrap!(flash.blocking_jedec_id()); info!("jedec id: 0x{:x}", jedec); // Get unique id let mut uid = [0; 8]; - defmt::unwrap!(flash.unique_id(&mut uid)); + defmt::unwrap!(flash.blocking_unique_id(&mut uid)); info!("unique id: {:?}", uid); let mut buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + FLASH_BASE as u32); info!("Contents start with {=[u8]}", buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Contents after erase starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); @@ -52,14 +51,22 @@ async fn main(_spawner: Spawner) { *b = 0xDA; } - defmt::unwrap!(flash.write(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET, &mut buf)); - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Contents after write starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xDA) { defmt::panic!("unexpected"); } + let mut buf = [0u32; ERASE_SIZE / 4]; + + defmt::unwrap!(flash.background_read(ADDR_OFFSET, &mut buf)).await; + info!("Contents after write starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xDADADADA) { + defmt::panic!("unexpected"); + } + info!("Test OK"); cortex_m::asm::bkpt(); } diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs index 0e0de85f..2874aa91 100644 --- a/tests/rp/src/bin/float.rs +++ b/tests/rp/src/bin/float.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index 946b7dc8..1a43a928 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index 532494de..60c65b7a 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs index 780112bc..6ab7f671 100644 --- a/tests/rp/src/bin/gpio_multicore.rs +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{info, unwrap}; use embassy_executor::Executor; @@ -38,11 +37,11 @@ async fn core0_task(p: PIN_0) { let mut pin = Output::new(p, Level::Low); CHANNEL0.send(()).await; - CHANNEL1.recv().await; + CHANNEL1.receive().await; pin.set_high(); - CHANNEL1.recv().await; + CHANNEL1.receive().await; info!("Test OK"); cortex_m::asm::bkpt(); @@ -52,7 +51,7 @@ async fn core0_task(p: PIN_0) { async fn core1_task(p: PIN_1) { info!("CORE1 is running"); - CHANNEL0.recv().await; + CHANNEL0.receive().await; let mut pin = Input::new(p, Pull::Down); let wait = pin.wait_for_rising_edge(); diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs index 114889de..f4188135 100644 --- a/tests/rp/src/bin/multicore.rs +++ b/tests/rp/src/bin/multicore.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{info, unwrap}; use embassy_executor::Executor; @@ -34,7 +33,7 @@ async fn core0_task() { info!("CORE0 is running"); let ping = true; CHANNEL0.send(ping).await; - let pong = CHANNEL1.recv().await; + let pong = CHANNEL1.receive().await; assert_eq!(ping, pong); info!("Test OK"); @@ -44,6 +43,6 @@ async fn core0_task() { #[embassy_executor::task] async fn core1_task() { info!("CORE1 is running"); - let ping = CHANNEL0.recv().await; + let ping = CHANNEL0.receive().await; CHANNEL1.send(ping).await; } diff --git a/tests/rp/src/bin/pio_irq.rs b/tests/rp/src/bin/pio_irq.rs index 45004424..c71e55d9 100644 --- a/tests/rp/src/bin/pio_irq.rs +++ b/tests/rp/src/bin/pio_irq.rs @@ -1,15 +1,13 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::info; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Config, InterruptHandler, Pio}; -use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -35,9 +33,8 @@ async fn main(_spawner: Spawner) { "irq wait 1", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[]); + cfg.use_program(&common.load_program(&prg.program), &[]); sm.set_config(&cfg); sm.set_enable(true); diff --git a/tests/rp/src/bin/pio_multi_load.rs b/tests/rp/src/bin/pio_multi_load.rs new file mode 100644 index 00000000..6343ff3a --- /dev/null +++ b/tests/rp/src/bin/pio_multi_load.rs @@ -0,0 +1,125 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"rpi-pico"); + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{Config, InterruptHandler, LoadError, Pio}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + let Pio { + mut common, + mut sm0, + mut sm1, + mut sm2, + irq_flags, + .. + } = Pio::new(pio, Irqs); + + // load with explicit origin works + let prg1 = pio_proc::pio_asm!( + ".origin 4" + "nop", + "nop", + "nop", + "nop", + "nop", + "nop", + "nop", + "irq 0", + "nop", + "nop", + ); + let loaded1 = common.load_program(&prg1.program); + assert_eq!(loaded1.origin, 4); + assert_eq!(loaded1.wrap.source, 13); + assert_eq!(loaded1.wrap.target, 4); + + // load without origin chooses a free space + let prg2 = pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 1", "nop", "nop",); + let loaded2 = common.load_program(&prg2.program); + assert_eq!(loaded2.origin, 14); + assert_eq!(loaded2.wrap.source, 23); + assert_eq!(loaded2.wrap.target, 14); + + // wrapping around the end of program space automatically works + let prg3 = + pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 2",); + let loaded3 = common.load_program(&prg3.program); + assert_eq!(loaded3.origin, 24); + assert_eq!(loaded3.wrap.source, 3); + assert_eq!(loaded3.wrap.target, 24); + + // check that the programs actually work + { + let mut cfg = Config::default(); + cfg.use_program(&loaded1, &[]); + sm0.set_config(&cfg); + sm0.set_enable(true); + while !irq_flags.check(0) {} + sm0.set_enable(false); + } + { + let mut cfg = Config::default(); + cfg.use_program(&loaded2, &[]); + sm1.set_config(&cfg); + sm1.set_enable(true); + while !irq_flags.check(1) {} + sm1.set_enable(false); + } + { + let mut cfg = Config::default(); + cfg.use_program(&loaded3, &[]); + sm2.set_config(&cfg); + sm2.set_enable(true); + while !irq_flags.check(2) {} + sm2.set_enable(false); + } + + // instruction memory is full now. all loads should fail. + { + let prg = pio_proc::pio_asm!(".origin 0", "nop"); + match common.try_load_program(&prg.program) { + Err(LoadError::AddressInUse(0)) => (), + _ => panic!("program loaded when it shouldn't"), + }; + + let prg = pio_proc::pio_asm!("nop"); + match common.try_load_program(&prg.program) { + Err(LoadError::InsufficientSpace) => (), + _ => panic!("program loaded when it shouldn't"), + }; + } + + // freeing some memory should allow further loads though. + unsafe { + common.free_instr(loaded3.used_memory); + } + { + let prg = pio_proc::pio_asm!(".origin 0", "nop"); + match common.try_load_program(&prg.program) { + Ok(_) => (), + _ => panic!("program didn't loaded when it shouldn"), + }; + + let prg = pio_proc::pio_asm!("nop"); + match common.try_load_program(&prg.program) { + Ok(_) => (), + _ => panic!("program didn't loaded when it shouldn"), + }; + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/rp/src/bin/pwm.rs b/tests/rp/src/bin/pwm.rs index c71d21ef..8c02b844 100644 --- a/tests/rp/src/bin/pwm.rs +++ b/tests/rp/src/bin/pwm.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, assert_eq, assert_ne, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/spi.rs b/tests/rp/src/bin/spi.rs index 84dfa5a2..f347b80c 100644 --- a/tests/rp/src/bin/spi.rs +++ b/tests/rp/src/bin/spi.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs index a4080b03..80d5a714 100644 --- a/tests/rp/src/bin/spi_async.rs +++ b/tests/rp/src/bin/spi_async.rs @@ -4,8 +4,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart.rs b/tests/rp/src/bin/uart.rs index 2331c7d3..00f3e194 100644 --- a/tests/rp/src/bin/uart.rs +++ b/tests/rp/src/bin/uart.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index e74e9986..6ab7de29 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, panic, *}; use embassy_executor::Spawner; @@ -11,7 +10,7 @@ use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::{Read, ReadExactError, Write}; +use embedded_io_async::{Read, ReadExactError, Write}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index fee6c825..cd4af1ef 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_upgrade.rs b/tests/rp/src/bin/uart_upgrade.rs index 760e5395..c07fc08c 100644 --- a/tests/rp/src/bin/uart_upgrade.rs +++ b/tests/rp/src/bin/uart_upgrade.rs @@ -1,15 +1,14 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{BufferedInterruptHandler, Config, Uart}; -use embedded_io::asynch::{Read, Write}; +use embedded_io_async::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/tests/rp/src/common.rs b/tests/rp/src/common.rs deleted file mode 100644 index 955674f2..00000000 --- a/tests/rp/src/common.rs +++ /dev/null @@ -1 +0,0 @@ -teleprobe_meta::target!(b"rpi-pico"); diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 3007cd1e..754356cb 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,11 +7,11 @@ autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "can", "not-gpdma"] # Nucleo "sdmmc" -stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo +stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "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 -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma", "dac-adc-pin"] # Nucleo stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "mac" ] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board @@ -23,12 +23,13 @@ ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] embassy-stm32-wpan = [] not-gpdma = [] +dac-adc-pin = [] [dependencies] teleprobe-meta = "1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +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-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" } @@ -40,8 +41,9 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } +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 } @@ -55,6 +57,11 @@ name = "can" path = "src/bin/can.rs" required-features = [ "can",] +[[bin]] +name = "dac" +path = "src/bin/dac.rs" +required-features = [ "dac-adc-pin",] + [[bin]] name = "gpio" path = "src/bin/gpio.rs" diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 8bdd3c24..acf54521 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -7,6 +7,7 @@ #[path = "../common.rs"] mod common; use common::*; +use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; @@ -14,6 +15,7 @@ use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; +use embassy_time::{Duration, Instant}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -62,13 +64,28 @@ async fn main(_spawner: Spawner) { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); info!("Transmitting frame..."); + let tx_ts = Instant::now(); can.write(&tx_frame).await; - info!("Receiving frame..."); - let (time, rx_frame) = can.read().await.unwrap(); + let envelope = can.read().await.unwrap(); + info!("Frame received!"); - info!("loopback time {}", time); - info!("loopback frame {=u8}", rx_frame.data().unwrap()[0]); + info!("loopback time {}", envelope.ts); + info!("loopback frame {=u8}", envelope.frame.data().unwrap()[0]); + + let latency = envelope.ts.saturating_duration_since(tx_ts); + info!("loopback latency {} us", latency.as_micros()); + + // Theoretical minimum latency is 55us, actual is usually ~80us + const MIN_LATENCY: Duration = Duration::from_micros(50); + const MAX_LATENCY: Duration = Duration::from_micros(150); + assert!( + MIN_LATENCY <= latency && latency <= MAX_LATENCY, + "{} <= {} <= {}", + MIN_LATENCY, + latency, + MAX_LATENCY + ); i += 1; if i > 10 { diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs new file mode 100644 index 00000000..67a7d5b5 --- /dev/null +++ b/tests/stm32/src/bin/dac.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// required-features: dac-adc-pin + +#[path = "../common.rs"] +mod common; +use common::*; +use defmt::assert; +use embassy_executor::Spawner; +use embassy_stm32::adc::Adc; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); + + #[cfg(feature = "stm32f429zi")] + let dac_peripheral = p.DAC; + + #[cfg(any(feature = "stm32h755zi", feature = "stm32g071rb"))] + let dac_peripheral = p.DAC1; + + let mut dac: DacCh1<'_, _, NoDma> = DacCh1::new(dac_peripheral, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); + + let mut adc = Adc::new(p.ADC1, &mut Delay); + + #[cfg(feature = "stm32h755zi")] + let normalization_factor = 256; + #[cfg(any(feature = "stm32f429zi", feature = "stm32g071rb"))] + let normalization_factor: i32 = 16; + + unwrap!(dac.set(Value::Bit8(0))); + // Now wait a little to obtain a stable value + Timer::after(Duration::from_millis(30)).await; + let offset = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4); + + for v in 0..=255 { + // First set the DAC output value + let dac_output_val = to_sine_wave(v); + unwrap!(dac.set(Value::Bit8(dac_output_val))); + + // Now wait a little to obtain a stable value + Timer::after(Duration::from_millis(30)).await; + + // Need to steal the peripherals here because PA4 is obviously in use already + let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4); + // Calibrate and normalize the measurement to get close to the dac_output_val + let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; + + info!("value / measured: {} / {}", dac_output_val, measured_normalized); + + // The deviations are quite enormous but that does not matter since this is only a quick test + assert!((dac_output_val as i16 - measured_normalized).abs() < 15); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +use core::f32::consts::PI; + +use micromath::F32Ext; + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = PI * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = PI + PI * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index 194b153d..7df415b4 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -10,13 +10,16 @@ use chrono::{NaiveDate, NaiveDateTime}; use common::*; use defmt::assert; use embassy_executor::Spawner; -use embassy_stm32::pac; -use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig}; use embassy_time::{Duration, Timer}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(config()); + let mut config = config(); + + config.rcc.rtc = Some(RtcClockSource::LSI); + + let p = embassy_stm32::init(config); info!("Hello World!"); let now = NaiveDate::from_ymd_opt(2020, 5, 15) @@ -24,13 +27,6 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - info!("Starting LSI"); - - pac::RCC.csr().modify(|w| w.set_lsion(true)); - while !pac::RCC.csr().read().lsirdy() {} - - info!("Started LSI"); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); rtc.set_datetime(now.into()).expect("datetime not set"); diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 819ecae3..e51dd5bf 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -35,15 +35,14 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "stm32c031c6")] let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); + let mut spi_config = spi::Config::default(); + spi_config.frequency = Hertz(1_000_000); + let mut spi = Spi::new( - spi, - sck, // Arduino D13 + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 - NoDma, - NoDma, - Hertz(1_000_000), - spi::Config::default(), + NoDma, NoDma, spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 78aad24e..d45cbe45 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -34,15 +34,14 @@ async fn main(_spawner: Spawner) { #[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 mut spi_config = spi::Config::default(); + spi_config.frequency = Hertz(1_000_000); + let mut spi = Spi::new( - spi, - sck, // Arduino D13 + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 - tx_dma, - rx_dma, - Hertz(1_000_000), - spi::Config::default(), + tx_dma, rx_dma, spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; diff --git a/tests/stm32/src/bin/wpan_ble.rs b/tests/stm32/src/bin/wpan_ble.rs index 3ad8aca4..452da77a 100644 --- a/tests/stm32/src/bin/wpan_ble.rs +++ b/tests/stm32/src/bin/wpan_ble.rs @@ -12,6 +12,7 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::hci::host::uart::UartHci; use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; use embassy_stm32_wpan::hci::types::AdvertisingType; @@ -40,7 +41,10 @@ async fn run_mm_queue(memory_manager: mm::MemoryManager) { #[embassy_executor::main] async fn main(spawner: Spawner) { - let p = embassy_stm32::init(config()); + let mut config = config(); + config.rcc = WPAN_DEFAULT; + + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index cfa0aca3..7eab2fd3 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -10,6 +10,7 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{ @@ -31,7 +32,10 @@ async fn run_mm_queue(memory_manager: mm::MemoryManager) { #[embassy_executor::main] async fn main(spawner: Spawner) { - let p = embassy_stm32::init(config()); + let mut config = config(); + config.rcc = WPAN_DEFAULT; + + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); @@ -49,11 +53,16 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + } info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; @@ -64,24 +73,30 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + } info!("getting extended address"); mbox.mac_subsystem .send_command(&GetRequest { pib_attribute: PibId::ExtendedAddress, + ..Default::default() }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); - if let Ok(MacEvent::MlmeGetCnf(evt)) = evt { - if evt.pib_attribute_value_len == 8 { - let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); - info!("value {:#x}", value) + if let MacEvent::MlmeGetCnf(evt) = evt { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + + info!("value {:#x}", value) + } } } @@ -100,8 +115,13 @@ async fn main(spawner: Spawner) { }; info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + let short_addr = if let MacEvent::MlmeAssociateCnf(conf) = mbox.mac_subsystem.read().await.unwrap() { + conf.assoc_short_address + } else { + defmt::panic!() + }; + + info!("{}", short_addr); info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 3d2a9b8e..ca5cb43a 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -33,6 +33,7 @@ pub fn config() -> Config { { 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; } #[cfg(feature = "stm32u585ai")]