aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/doc.yml86
-rw-r--r--.github/workflows/rust.yml2
-rw-r--r--.vscode/settings.json6
-rw-r--r--README.md2
-rw-r--r--docs/modules/ROOT/examples/basic/Cargo.toml6
-rw-r--r--docs/modules/ROOT/examples/basic/build.rs35
-rw-r--r--docs/modules/ROOT/examples/basic/memory.x7
-rw-r--r--docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml1
-rw-r--r--docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml1
-rw-r--r--docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml1
-rw-r--r--docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml1
-rw-r--r--docs/modules/ROOT/pages/bootloader.adoc17
-rw-r--r--embassy-boot/boot/Cargo.toml1
-rw-r--r--embassy-boot/nrf/Cargo.toml1
-rw-r--r--embassy-boot/stm32/Cargo.toml1
-rw-r--r--embassy-cortex-m/Cargo.toml1
-rw-r--r--embassy-embedded-hal/Cargo.toml3
-rw-r--r--embassy-executor/Cargo.toml3
-rw-r--r--embassy-hal-common/Cargo.toml1
-rw-r--r--embassy-lora/Cargo.toml3
-rw-r--r--embassy-lora/src/lib.rs2
-rw-r--r--embassy-lora/src/sx126x/mod.rs153
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/board_specific.rs256
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/mod.rs732
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/mod_params.rs469
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/subroutine.rs674
-rw-r--r--embassy-macros/Cargo.toml1
-rw-r--r--embassy-net/Cargo.toml1
-rw-r--r--embassy-nrf/Cargo.toml1
-rw-r--r--embassy-nrf/src/chips/nrf52810.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52811.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs3
-rw-r--r--embassy-nrf/src/chips/nrf9160.rs3
-rw-r--r--embassy-nrf/src/lib.rs8
-rw-r--r--embassy-nrf/src/pdm.rs242
-rw-r--r--embassy-nrf/src/uarte.rs398
-rw-r--r--embassy-rp/Cargo.toml1
-rw-r--r--embassy-rp/src/gpio.rs32
-rw-r--r--embassy-rp/src/i2c.rs407
-rw-r--r--embassy-rp/src/rtc/mod.rs2
-rw-r--r--embassy-rp/src/usb.rs16
-rw-r--r--embassy-stm32/Cargo.toml6
-rw-r--r--embassy-stm32/src/adc/mod.rs9
-rw-r--r--embassy-stm32/src/adc/v2.rs161
-rw-r--r--embassy-stm32/src/adc/v4.rs16
-rw-r--r--embassy-stm32/src/flash/h7.rs8
-rw-r--r--embassy-stm32/src/flash/mod.rs27
-rw-r--r--embassy-stm32/src/usart/mod.rs104
-rw-r--r--embassy-sync/Cargo.toml10
-rw-r--r--embassy-sync/README.md24
-rw-r--r--embassy-sync/src/pubsub/mod.rs73
-rw-r--r--embassy-sync/src/pubsub/publisher.rs17
-rw-r--r--embassy-sync/src/pubsub/subscriber.rs6
-rw-r--r--embassy-time/Cargo.toml1
-rw-r--r--embassy-usb-driver/Cargo.toml3
-rw-r--r--embassy-usb-driver/src/lib.rs8
-rw-r--r--embassy-usb/Cargo.toml3
-rw-r--r--examples/boot/application/nrf/Cargo.toml1
-rw-r--r--examples/boot/application/stm32f3/Cargo.toml1
-rw-r--r--examples/boot/application/stm32f3/src/bin/a.rs2
-rw-r--r--examples/boot/application/stm32f7/Cargo.toml1
-rw-r--r--examples/boot/application/stm32f7/src/bin/a.rs2
-rw-r--r--examples/boot/application/stm32h7/Cargo.toml1
-rw-r--r--examples/boot/application/stm32h7/src/bin/a.rs2
-rw-r--r--examples/boot/application/stm32l0/Cargo.toml1
-rw-r--r--examples/boot/application/stm32l0/src/bin/a.rs2
-rw-r--r--examples/boot/application/stm32l1/Cargo.toml1
-rw-r--r--examples/boot/application/stm32l1/src/bin/a.rs2
-rw-r--r--examples/boot/application/stm32l4/Cargo.toml1
-rw-r--r--examples/boot/application/stm32l4/src/bin/a.rs2
-rw-r--r--examples/boot/application/stm32wl/Cargo.toml1
-rw-r--r--examples/boot/application/stm32wl/src/bin/a.rs2
-rw-r--r--examples/boot/bootloader/nrf/Cargo.toml1
-rw-r--r--examples/boot/bootloader/stm32/Cargo.toml1
-rw-r--r--examples/boot/bootloader/stm32/src/main.rs2
-rw-r--r--examples/nrf-rtos-trace/Cargo.toml1
-rw-r--r--examples/nrf/Cargo.toml8
-rw-r--r--examples/nrf/src/bin/lora_p2p_report.rs78
-rw-r--r--examples/nrf/src/bin/lora_p2p_sense.rs125
-rw-r--r--examples/nrf/src/bin/pdm.rs33
-rw-r--r--examples/nrf/src/bin/uart_idle.rs7
-rw-r--r--examples/rp/Cargo.toml6
-rw-r--r--examples/rp/src/bin/i2c_async.rs102
-rw-r--r--examples/rp/src/bin/i2c_blocking.rs70
-rw-r--r--examples/std/Cargo.toml1
-rw-r--r--examples/stm32f0/Cargo.toml1
-rw-r--r--examples/stm32f1/Cargo.toml1
-rw-r--r--examples/stm32f2/Cargo.toml1
-rw-r--r--examples/stm32f3/Cargo.toml1
-rw-r--r--examples/stm32f3/src/bin/flash.rs2
-rw-r--r--examples/stm32f4/Cargo.toml1
-rw-r--r--examples/stm32f4/src/bin/adc.rs25
-rw-r--r--examples/stm32f4/src/bin/flash.rs2
-rw-r--r--examples/stm32f7/Cargo.toml1
-rw-r--r--examples/stm32f7/src/bin/flash.rs2
-rw-r--r--examples/stm32g0/Cargo.toml1
-rw-r--r--examples/stm32g4/Cargo.toml1
-rw-r--r--examples/stm32h7/Cargo.toml1
-rw-r--r--examples/stm32h7/src/bin/flash.rs2
-rw-r--r--examples/stm32l0/Cargo.toml1
-rw-r--r--examples/stm32l0/src/bin/flash.rs2
-rw-r--r--examples/stm32l1/Cargo.toml1
-rw-r--r--examples/stm32l1/src/bin/flash.rs2
-rw-r--r--examples/stm32l4/Cargo.toml1
-rw-r--r--examples/stm32l5/Cargo.toml1
-rw-r--r--examples/stm32u5/Cargo.toml1
-rw-r--r--examples/stm32wb/Cargo.toml1
-rw-r--r--examples/stm32wl/Cargo.toml1
-rw-r--r--examples/stm32wl/src/bin/flash.rs2
-rw-r--r--examples/wasm/Cargo.toml1
-rw-r--r--stm32-gen-features/Cargo.toml1
-rw-r--r--stm32-metapac-gen/Cargo.toml1
-rw-r--r--tests/rp/Cargo.toml1
-rw-r--r--tests/stm32/Cargo.toml1
-rw-r--r--xtask/Cargo.toml1
116 files changed, 4136 insertions, 447 deletions
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
new file mode 100644
index 000000000..eb460e738
--- /dev/null
+++ b/.github/workflows/doc.yml
@@ -0,0 +1,86 @@
1name: Docs
2
3on:
4 push:
5 branches: [master]
6
7env:
8 BUILDER_THREADS: '1'
9
10jobs:
11 doc:
12 runs-on: ubuntu-latest
13
14 # Since stm32 crates take SO LONG to build, we split them
15 # into a separate job. This way it doesn't slow down updating
16 # the rest.
17 strategy:
18 matrix:
19 crates:
20 - stm32
21 - rest
22
23 # This will ensure at most one doc build job is running at a time
24 # (for stm32 and non-stm32 independently).
25 # If another job is already running, the new job will wait.
26 # If another job is already waiting, it'll be canceled.
27 # This means some commits will be skipped, but that's fine because
28 # we only care that the latest gets built.
29 concurrency: doc-${{ matrix.crates }}
30
31 steps:
32 - uses: actions/checkout@v2
33 with:
34 submodules: true
35 - name: Install Rust targets
36 run: |
37 rustup target add x86_64-unknown-linux-gnu
38 rustup target add wasm32-unknown-unknown
39 rustup target add thumbv6m-none-eabi
40 rustup target add thumbv7m-none-eabi
41 rustup target add thumbv7em-none-eabi
42 rustup target add thumbv7em-none-eabihf
43 rustup target add thumbv8m.base-none-eabi
44 rustup target add thumbv8m.main-none-eabi
45 rustup target add thumbv8m.main-none-eabihf
46
47 - name: Install docserver
48 run: |
49 wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.3/builder"
50 chmod +x /usr/local/bin/builder
51
52 - name: build-stm32
53 if: ${{ matrix.crates=='stm32' }}
54 run: |
55 mkdir crates
56 builder ./embassy-stm32 crates/embassy-stm32/git.zup
57 builder ./stm32-metapac crates/stm32-metapac/git.zup
58
59 - name: build-rest
60 if: ${{ matrix.crates=='rest' }}
61 run: |
62 mkdir crates
63 builder ./embassy-boot/boot crates/embassy-boot/git.zup
64 builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup
65 builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup
66 builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup
67 builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup
68 builder ./embassy-executor crates/embassy-executor/git.zup
69 builder ./embassy-futures crates/embassy-futures/git.zup
70 builder ./embassy-lora crates/embassy-lora/git.zup
71 builder ./embassy-net crates/embassy-net/git.zup
72 builder ./embassy-nrf crates/embassy-nrf/git.zup
73 builder ./embassy-rp crates/embassy-rp/git.zup
74 builder ./embassy-sync crates/embassy-sync/git.zup
75 builder ./embassy-time crates/embassy-time/git.zup
76 builder ./embassy-usb crates/embassy-usb/git.zup
77 builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup
78
79 - name: upload
80 run: |
81 mkdir -p ~/.kube
82 echo "${{secrets.KUBECONFIG}}" > ~/.kube/config
83 POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
84 kubectl cp crates $POD:/data
85
86 \ No newline at end of file
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index d2e8e316b..b93c8783d 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -11,7 +11,7 @@ env:
11 11
12jobs: 12jobs:
13 all: 13 all:
14 runs-on: ubuntu-20.04 14 runs-on: ubuntu-latest
15 needs: [build-nightly, build-stable, test] 15 needs: [build-nightly, build-stable, test]
16 steps: 16 steps:
17 - name: Done 17 - name: Done
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 5e9e51799..62e5a362f 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -20,10 +20,13 @@
20 //"embassy-executor/Cargo.toml", 20 //"embassy-executor/Cargo.toml",
21 //"embassy-sync/Cargo.toml", 21 //"embassy-sync/Cargo.toml",
22 "examples/nrf/Cargo.toml", 22 "examples/nrf/Cargo.toml",
23 // "examples/nrf-rtos-trace/Cargo.toml",
23 // "examples/rp/Cargo.toml", 24 // "examples/rp/Cargo.toml",
24 // "examples/std/Cargo.toml", 25 // "examples/std/Cargo.toml",
25 // "examples/stm32f0/Cargo.toml", 26 // "examples/stm32f0/Cargo.toml",
26 // "examples/stm32f1/Cargo.toml", 27 // "examples/stm32f1/Cargo.toml",
28 // "examples/stm32f2/Cargo.toml",
29 // "examples/stm32f3/Cargo.toml",
27 // "examples/stm32f4/Cargo.toml", 30 // "examples/stm32f4/Cargo.toml",
28 // "examples/stm32f7/Cargo.toml", 31 // "examples/stm32f7/Cargo.toml",
29 // "examples/stm32g0/Cargo.toml", 32 // "examples/stm32g0/Cargo.toml",
@@ -32,8 +35,11 @@
32 // "examples/stm32l0/Cargo.toml", 35 // "examples/stm32l0/Cargo.toml",
33 // "examples/stm32l1/Cargo.toml", 36 // "examples/stm32l1/Cargo.toml",
34 // "examples/stm32l4/Cargo.toml", 37 // "examples/stm32l4/Cargo.toml",
38 // "examples/stm32l5/Cargo.toml",
35 // "examples/stm32u5/Cargo.toml", 39 // "examples/stm32u5/Cargo.toml",
40 // "examples/stm32wb/Cargo.toml",
36 // "examples/stm32wb55/Cargo.toml", 41 // "examples/stm32wb55/Cargo.toml",
42 // "examples/stm32wl/Cargo.toml",
37 // "examples/stm32wl55/Cargo.toml", 43 // "examples/stm32wl55/Cargo.toml",
38 // "examples/wasm/Cargo.toml", 44 // "examples/wasm/Cargo.toml",
39 ], 45 ],
diff --git a/README.md b/README.md
index 9f08bf676..eaa91012c 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
31The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. 31The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
32 32
33- **LoRa** - 33- **LoRa** -
34<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers. 34<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers.
35 35
36- **USB** - 36- **USB** -
37<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. 37<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml
index ae124a871..d9f8a285a 100644
--- a/docs/modules/ROOT/examples/basic/Cargo.toml
+++ b/docs/modules/ROOT/examples/basic/Cargo.toml
@@ -3,16 +3,16 @@ authors = ["Dario Nieuwenhuis <[email protected]>"]
3edition = "2018" 3edition = "2018"
4name = "embassy-basic-example" 4name = "embassy-basic-example"
5version = "0.1.0" 5version = "0.1.0"
6license = "MIT OR Apache-2.0"
6 7
7[dependencies] 8[dependencies]
8embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] } 9embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] }
9embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } 10embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
10embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } 11embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
11 12
12defmt = "0.3" 13defmt = "0.3"
13defmt-rtt = "0.3" 14defmt-rtt = "0.3"
14 15
15cortex-m = "0.7.3" 16cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
16cortex-m-rt = "0.7.0" 17cortex-m-rt = "0.7.0"
17embedded-hal = "0.2.6"
18panic-probe = { version = "0.3", features = ["print-defmt"] } 18panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/docs/modules/ROOT/examples/basic/build.rs b/docs/modules/ROOT/examples/basic/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/docs/modules/ROOT/examples/basic/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/docs/modules/ROOT/examples/basic/memory.x b/docs/modules/ROOT/examples/basic/memory.x
new file mode 100644
index 000000000..9b04edec0
--- /dev/null
+++ b/docs/modules/ROOT/examples/basic/memory.x
@@ -0,0 +1,7 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
5 FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
6 RAM : ORIGIN = 0x20000000, LENGTH = 256K
7}
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 e2933076f..c9a963d4d 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
@@ -2,6 +2,7 @@
2name = "blinky-async" 2name = "blinky-async"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7cortex-m = "0.7" 8cortex-m = "0.7"
diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml
index dbd3aba8b..f86361dd5 100644
--- a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml
+++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml
@@ -2,6 +2,7 @@
2name = "blinky-hal" 2name = "blinky-hal"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7cortex-m = "0.7" 8cortex-m = "0.7"
diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml
index 0dd326015..9733658b6 100644
--- a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml
+++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml
@@ -2,6 +2,7 @@
2name = "blinky-irq" 2name = "blinky-irq"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7cortex-m = "0.7" 8cortex-m = "0.7"
diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml
index e7f4f5d1f..a077f1828 100644
--- a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml
+++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml
@@ -2,6 +2,7 @@
2name = "blinky-pac" 2name = "blinky-pac"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7cortex-m = "0.7" 8cortex-m = "0.7"
diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc
index ae92e9d5d..7dbfeb3eb 100644
--- a/docs/modules/ROOT/pages/bootloader.adoc
+++ b/docs/modules/ROOT/pages/bootloader.adoc
@@ -25,10 +25,19 @@ image::bootloader_flash.png[Bootloader flash layout]
25The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader 25The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader
26instance or via linker scripts: 26instance or via linker scripts:
27 27
28* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash. 28* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs.
29* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. 29* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. The size required for this partition depends on the size of your application.
30* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. 30* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition, since the swap algorithm uses the extra space to ensure power safe copy of data:
31* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped. 31+
32Partition Size~dfu~= Partition Size~active~+ Page Size~active~
33+
34All values are specified in bytes.
35
36* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by:
37+
38Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~)
39+
40All values are specified in bytes.
32 41
33The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. 42The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.
34The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. 43The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined.
diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml
index a42f88688..54c67a375 100644
--- a/embassy-boot/boot/Cargo.toml
+++ b/embassy-boot/boot/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2021"
3name = "embassy-boot" 3name = "embassy-boot"
4version = "0.1.0" 4version = "0.1.0"
5description = "Bootloader using Embassy" 5description = "Bootloader using Embassy"
6license = "MIT OR Apache-2.0"
6 7
7[package.metadata.embassy_docs] 8[package.metadata.embassy_docs]
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/" 9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/"
diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml
index 234393e7c..c6af70144 100644
--- a/embassy-boot/nrf/Cargo.toml
+++ b/embassy-boot/nrf/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2021"
3name = "embassy-boot-nrf" 3name = "embassy-boot-nrf"
4version = "0.1.0" 4version = "0.1.0"
5description = "Bootloader lib for nRF chips" 5description = "Bootloader lib for nRF chips"
6license = "MIT OR Apache-2.0"
6 7
7[package.metadata.embassy_docs] 8[package.metadata.embassy_docs]
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/" 9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/"
diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml
index ad4657e0d..9d12c6cfd 100644
--- a/embassy-boot/stm32/Cargo.toml
+++ b/embassy-boot/stm32/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2021"
3name = "embassy-boot-stm32" 3name = "embassy-boot-stm32"
4version = "0.1.0" 4version = "0.1.0"
5description = "Bootloader lib for STM32 chips" 5description = "Bootloader lib for STM32 chips"
6license = "MIT OR Apache-2.0"
6 7
7[package.metadata.embassy_docs] 8[package.metadata.embassy_docs]
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/" 9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/"
diff --git a/embassy-cortex-m/Cargo.toml b/embassy-cortex-m/Cargo.toml
index 7efced669..5c5718d50 100644
--- a/embassy-cortex-m/Cargo.toml
+++ b/embassy-cortex-m/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-cortex-m" 2name = "embassy-cortex-m"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[package.metadata.embassy_docs] 7[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/"
diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml
index fe8fac7c8..d0be6d195 100644
--- a/embassy-embedded-hal/Cargo.toml
+++ b/embassy-embedded-hal/Cargo.toml
@@ -2,13 +2,14 @@
2name = "embassy-embedded-hal" 2name = "embassy-embedded-hal"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6 7
7[package.metadata.embassy_docs] 8[package.metadata.embassy_docs]
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/" 9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/"
9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/" 10src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/"
10features = ["nightly", "std"] 11features = ["nightly", "std"]
11target = "thumbv7em-none-eabi" 12target = "x86_64-unknown-linux-gnu"
12 13
13[features] 14[features]
14std = [] 15std = []
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index fa3d0b2b6..d0f51646d 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -2,12 +2,13 @@
2name = "embassy-executor" 2name = "embassy-executor"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6 7
7[package.metadata.embassy_docs] 8[package.metadata.embassy_docs]
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" 9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" 10src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
10features = ["nightly", "defmt", "unstable-traits"] 11features = ["nightly", "defmt"]
11flavors = [ 12flavors = [
12 { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, 13 { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] },
13 { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, 14 { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] },
diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-common/Cargo.toml
index 58f0af6ab..e8617c02f 100644
--- a/embassy-hal-common/Cargo.toml
+++ b/embassy-hal-common/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-hal-common" 2name = "embassy-hal-common"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[features] 7[features]
7 8
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml
index dcb0d8245..ea2c3fe67 100644
--- a/embassy-lora/Cargo.toml
+++ b/embassy-lora/Cargo.toml
@@ -2,12 +2,14 @@
2name = "embassy-lora" 2name = "embassy-lora"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[package.metadata.embassy_docs] 7[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/"
8src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" 9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
9features = ["time", "defmt"] 10features = ["time", "defmt"]
10flavors = [ 11flavors = [
12 { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] },
11 { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, 13 { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
12 { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, 14 { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
13] 15]
@@ -15,6 +17,7 @@ flavors = [
15[lib] 17[lib]
16 18
17[features] 19[features]
20sx126x = []
18sx127x = [] 21sx127x = []
19stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] 22stm32wl = ["embassy-stm32", "embassy-stm32/subghz"]
20time = [] 23time = []
diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs
index 90ba0d1d4..3e4748430 100644
--- a/embassy-lora/src/lib.rs
+++ b/embassy-lora/src/lib.rs
@@ -7,6 +7,8 @@ pub(crate) mod fmt;
7 7
8#[cfg(feature = "stm32wl")] 8#[cfg(feature = "stm32wl")]
9pub mod stm32wl; 9pub mod stm32wl;
10#[cfg(feature = "sx126x")]
11pub mod sx126x;
10#[cfg(feature = "sx127x")] 12#[cfg(feature = "sx127x")]
11pub mod sx127x; 13pub mod sx127x;
12 14
diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs
new file mode 100644
index 000000000..ed8cb4059
--- /dev/null
+++ b/embassy-lora/src/sx126x/mod.rs
@@ -0,0 +1,153 @@
1use core::future::Future;
2
3use defmt::Format;
4use embedded_hal::digital::v2::OutputPin;
5use embedded_hal_async::digital::Wait;
6use embedded_hal_async::spi::*;
7use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig};
8use lorawan_device::async_device::Timings;
9
10mod sx126x_lora;
11use sx126x_lora::LoRa;
12
13use self::sx126x_lora::mod_params::RadioError;
14
15/// Semtech Sx126x LoRa peripheral
16pub struct Sx126xRadio<SPI, CTRL, WAIT, BUS>
17where
18 SPI: SpiBus<u8, Error = BUS> + 'static,
19 CTRL: OutputPin + 'static,
20 WAIT: Wait + 'static,
21 BUS: Error + Format + 'static,
22{
23 pub lora: LoRa<SPI, CTRL, WAIT>,
24}
25
26impl<SPI, CTRL, WAIT, BUS> Sx126xRadio<SPI, CTRL, WAIT, BUS>
27where
28 SPI: SpiBus<u8, Error = BUS> + 'static,
29 CTRL: OutputPin + 'static,
30 WAIT: Wait + 'static,
31 BUS: Error + Format + 'static,
32{
33 pub async fn new(
34 spi: SPI,
35 cs: CTRL,
36 reset: CTRL,
37 antenna_rx: CTRL,
38 antenna_tx: CTRL,
39 dio1: WAIT,
40 busy: WAIT,
41 enable_public_network: bool,
42 ) -> Result<Self, RadioError<BUS>> {
43 let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy);
44 lora.init().await?;
45 lora.set_lora_modem(enable_public_network).await?;
46 Ok(Self { lora })
47 }
48}
49
50impl<SPI, CTRL, WAIT, BUS> Timings for Sx126xRadio<SPI, CTRL, WAIT, BUS>
51where
52 SPI: SpiBus<u8, Error = BUS> + 'static,
53 CTRL: OutputPin + 'static,
54 WAIT: Wait + 'static,
55 BUS: Error + Format + 'static,
56{
57 fn get_rx_window_offset_ms(&self) -> i32 {
58 -500
59 }
60 fn get_rx_window_duration_ms(&self) -> u32 {
61 800
62 }
63}
64
65impl<SPI, CTRL, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CTRL, WAIT, BUS>
66where
67 SPI: SpiBus<u8, Error = BUS> + 'static,
68 CTRL: OutputPin + 'static,
69 WAIT: Wait + 'static,
70 BUS: Error + Format + 'static,
71{
72 type PhyError = RadioError<BUS>;
73
74 type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
75 where
76 SPI: 'm,
77 CTRL: 'm,
78 WAIT: 'm,
79 BUS: 'm;
80
81 fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> {
82 trace!("TX START");
83 async move {
84 self.lora
85 .set_tx_config(
86 config.pw,
87 config.rf.spreading_factor.into(),
88 config.rf.bandwidth.into(),
89 config.rf.coding_rate.into(),
90 4,
91 false,
92 true,
93 false,
94 0,
95 false,
96 )
97 .await?;
98 self.lora.set_max_payload_length(buffer.len() as u8).await?;
99 self.lora.set_channel(config.rf.frequency).await?;
100 self.lora.send(buffer, 0xffffff).await?;
101 self.lora.process_irq(None, None, None).await?;
102 trace!("TX DONE");
103 return Ok(0);
104 }
105 }
106
107 type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
108 where
109 SPI: 'm,
110 CTRL: 'm,
111 WAIT: 'm,
112 BUS: 'm;
113
114 fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> {
115 trace!("RX START");
116 async move {
117 self.lora
118 .set_rx_config(
119 config.spreading_factor.into(),
120 config.bandwidth.into(),
121 config.coding_rate.into(),
122 4,
123 4,
124 false,
125 0u8,
126 true,
127 false,
128 0,
129 false,
130 true,
131 )
132 .await?;
133 self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
134 self.lora.set_channel(config.frequency).await?;
135 self.lora.rx(90 * 1000).await?;
136 let mut received_len = 0u8;
137 self.lora
138 .process_irq(Some(receiving_buffer), Some(&mut received_len), None)
139 .await?;
140 trace!("RX DONE");
141
142 let packet_status = self.lora.get_latest_packet_status();
143 let mut rssi = 0i16;
144 let mut snr = 0i8;
145 if packet_status.is_some() {
146 rssi = packet_status.unwrap().rssi as i16;
147 snr = packet_status.unwrap().snr;
148 }
149
150 Ok((received_len as usize, RxQuality::new(rssi, snr)))
151 }
152 }
153}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
new file mode 100644
index 000000000..a7b9e1486
--- /dev/null
+++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
@@ -0,0 +1,256 @@
1use embassy_time::{Duration, Timer};
2use embedded_hal::digital::v2::OutputPin;
3use embedded_hal_async::digital::Wait;
4use embedded_hal_async::spi::SpiBus;
5
6use super::mod_params::RadioError::*;
7use super::mod_params::*;
8use super::LoRa;
9
10// Defines the time required for the TCXO to wakeup [ms].
11const BRD_TCXO_WAKEUP_TIME: u32 = 10;
12
13// Provides board-specific functionality for Semtech SX126x-based boards.
14
15impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
16where
17 SPI: SpiBus<u8, Error = BUS>,
18 CTRL: OutputPin,
19 WAIT: Wait,
20{
21 // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes.
22 pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> {
23 Ok(()) // no operation currently
24 }
25
26 // Initialize the TCXO power pin
27 pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> {
28 let timeout = self.brd_get_board_tcxo_wakeup_time() << 6;
29 self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout)
30 .await?;
31 Ok(())
32 }
33
34 // Initialize RF switch control pins
35 pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> {
36 self.sub_set_dio2_as_rf_switch_ctrl(true).await?;
37 Ok(())
38 }
39
40 // Initialize the radio debug pins
41 pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> {
42 Ok(()) // no operation currently
43 }
44
45 // Hardware reset of the radio
46 pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> {
47 Timer::after(Duration::from_millis(10)).await;
48 self.reset.set_low().map_err(|_| Reset)?;
49 Timer::after(Duration::from_millis(20)).await;
50 self.reset.set_high().map_err(|_| Reset)?;
51 Timer::after(Duration::from_millis(10)).await;
52 Ok(())
53 }
54
55 // Wait while the busy pin is high
56 pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> {
57 self.busy.wait_for_low().await.map_err(|_| Busy)?;
58 Ok(())
59 }
60
61 // Wake up the radio
62 pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> {
63 self.cs.set_low().map_err(|_| CS)?;
64 self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?;
65 self.spi.write(&[0x00]).await.map_err(SPI)?;
66 self.cs.set_high().map_err(|_| CS)?;
67
68 self.brd_wait_on_busy().await?;
69 self.brd_set_operating_mode(RadioMode::StandbyRC);
70 Ok(())
71 }
72
73 // Send a command that writes data to the radio
74 pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
75 self.sub_check_device_ready().await?;
76
77 self.cs.set_low().map_err(|_| CS)?;
78 self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
79 self.spi.write(buffer).await.map_err(SPI)?;
80 self.cs.set_high().map_err(|_| CS)?;
81
82 if op_code != OpCode::SetSleep {
83 self.brd_wait_on_busy().await?;
84 }
85 Ok(())
86 }
87
88 // Send a command that reads data from the radio, filling the provided buffer and returning a status
89 pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
90 let mut status = [0u8];
91 let mut input = [0u8];
92
93 self.sub_check_device_ready().await?;
94
95 self.cs.set_low().map_err(|_| CS)?;
96 self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
97 self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?;
98 for i in 0..buffer.len() {
99 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
100 buffer[i] = input[0];
101 }
102 self.cs.set_high().map_err(|_| CS)?;
103
104 self.brd_wait_on_busy().await?;
105
106 Ok(status[0])
107 }
108
109 // Write one or more bytes of data to the radio memory
110 pub(super) async fn brd_write_registers(
111 &mut self,
112 start_register: Register,
113 buffer: &[u8],
114 ) -> Result<(), RadioError<BUS>> {
115 self.sub_check_device_ready().await?;
116
117 self.cs.set_low().map_err(|_| CS)?;
118 self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?;
119 self.spi
120 .write(&[
121 ((start_register.addr() & 0xFF00) >> 8) as u8,
122 (start_register.addr() & 0x00FF) as u8,
123 ])
124 .await
125 .map_err(SPI)?;
126 self.spi.write(buffer).await.map_err(SPI)?;
127 self.cs.set_high().map_err(|_| CS)?;
128
129 self.brd_wait_on_busy().await?;
130 Ok(())
131 }
132
133 // Read one or more bytes of data from the radio memory
134 pub(super) async fn brd_read_registers(
135 &mut self,
136 start_register: Register,
137 buffer: &mut [u8],
138 ) -> Result<(), RadioError<BUS>> {
139 let mut input = [0u8];
140
141 self.sub_check_device_ready().await?;
142
143 self.cs.set_low().map_err(|_| CS)?;
144 self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?;
145 self.spi
146 .write(&[
147 ((start_register.addr() & 0xFF00) >> 8) as u8,
148 (start_register.addr() & 0x00FF) as u8,
149 0x00u8,
150 ])
151 .await
152 .map_err(SPI)?;
153 for i in 0..buffer.len() {
154 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
155 buffer[i] = input[0];
156 }
157 self.cs.set_high().map_err(|_| CS)?;
158
159 self.brd_wait_on_busy().await?;
160 Ok(())
161 }
162
163 // Write data to the buffer holding the payload in the radio
164 pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
165 self.sub_check_device_ready().await?;
166
167 self.cs.set_low().map_err(|_| CS)?;
168 self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?;
169 self.spi.write(&[offset]).await.map_err(SPI)?;
170 self.spi.write(buffer).await.map_err(SPI)?;
171 self.cs.set_high().map_err(|_| CS)?;
172
173 self.brd_wait_on_busy().await?;
174 Ok(())
175 }
176
177 // Read data from the buffer holding the payload in the radio
178 pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> {
179 let mut input = [0u8];
180
181 self.sub_check_device_ready().await?;
182
183 self.cs.set_low().map_err(|_| CS)?;
184 self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?;
185 self.spi.write(&[offset]).await.map_err(SPI)?;
186 self.spi.write(&[0x00]).await.map_err(SPI)?;
187 for i in 0..buffer.len() {
188 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
189 buffer[i] = input[0];
190 }
191 self.cs.set_high().map_err(|_| CS)?;
192
193 self.brd_wait_on_busy().await?;
194 Ok(())
195 }
196
197 // Set the radio output power
198 pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> {
199 self.sub_set_tx_params(power, RampTime::Ramp40Us).await?;
200 Ok(())
201 }
202
203 // Get the radio type
204 pub(super) fn brd_get_radio_type(&mut self) -> RadioType {
205 RadioType::SX1262
206 }
207
208 // Quiesce the antenna(s).
209 pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError<BUS>> {
210 self.antenna_tx.set_low().map_err(|_| AntTx)?;
211 self.antenna_rx.set_low().map_err(|_| AntRx)?;
212 Ok(())
213 }
214
215 // Prepare the antenna(s) for a receive operation
216 pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError<BUS>> {
217 self.antenna_tx.set_low().map_err(|_| AntTx)?;
218 self.antenna_rx.set_high().map_err(|_| AntRx)?;
219 Ok(())
220 }
221
222 // Prepare the antenna(s) for a send operation
223 pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError<BUS>> {
224 self.antenna_rx.set_low().map_err(|_| AntRx)?;
225 self.antenna_tx.set_high().map_err(|_| AntTx)?;
226 Ok(())
227 }
228
229 // Check if the given RF frequency is supported by the hardware
230 pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> {
231 Ok(true)
232 }
233
234 // Get the duration required for the TCXO to wakeup [ms].
235 pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 {
236 BRD_TCXO_WAKEUP_TIME
237 }
238
239 /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process
240 pub(super) async fn brd_get_dio1_pin_state(
241 &mut self,
242 ) -> Result<u32, RadioError<BUS>> {
243 Ok(0)
244 }
245 */
246
247 // Get the current radio operatiing mode
248 pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode {
249 self.operating_mode
250 }
251
252 // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts.
253 pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) {
254 self.operating_mode = mode;
255 }
256}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs
new file mode 100644
index 000000000..280f26d51
--- /dev/null
+++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs
@@ -0,0 +1,732 @@
1#![allow(dead_code)]
2
3use embassy_time::{Duration, Timer};
4use embedded_hal::digital::v2::OutputPin;
5use embedded_hal_async::digital::Wait;
6use embedded_hal_async::spi::SpiBus;
7
8mod board_specific;
9pub mod mod_params;
10mod subroutine;
11
12use mod_params::RadioError::*;
13use mod_params::*;
14
15// Syncwords for public and private networks
16const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444;
17const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424;
18
19// Maximum number of registers that can be added to the retention list
20const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4;
21
22// Possible LoRa bandwidths
23const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz];
24
25// Radio complete wakeup time with margin for temperature compensation [ms]
26const RADIO_WAKEUP_TIME: u32 = 3;
27
28/// Provides high-level access to Semtech SX126x-based boards
29pub struct LoRa<SPI, CTRL, WAIT> {
30 spi: SPI,
31 cs: CTRL,
32 reset: CTRL,
33 antenna_rx: CTRL,
34 antenna_tx: CTRL,
35 dio1: WAIT,
36 busy: WAIT,
37 operating_mode: RadioMode,
38 rx_continuous: bool,
39 max_payload_length: u8,
40 modulation_params: Option<ModulationParams>,
41 packet_type: PacketType,
42 packet_params: Option<PacketParams>,
43 packet_status: Option<PacketStatus>,
44 image_calibrated: bool,
45 frequency_error: u32,
46}
47
48impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
49where
50 SPI: SpiBus<u8, Error = BUS>,
51 CTRL: OutputPin,
52 WAIT: Wait,
53{
54 /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time ()
55 pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self {
56 Self {
57 spi,
58 cs,
59 reset,
60 antenna_rx,
61 antenna_tx,
62 dio1,
63 busy,
64 operating_mode: RadioMode::Sleep,
65 rx_continuous: false,
66 max_payload_length: 0xFFu8,
67 modulation_params: None,
68 packet_type: PacketType::LoRa,
69 packet_params: None,
70 packet_status: None,
71 image_calibrated: false,
72 frequency_error: 0u32, // where is volatile FrequencyError modified ???
73 }
74 }
75
76 /// Initialize the radio
77 pub async fn init(&mut self) -> Result<(), RadioError<BUS>> {
78 self.sub_init().await?;
79 self.sub_set_standby(StandbyMode::RC).await?;
80 self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?;
81 self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?;
82 self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?;
83 self.sub_set_dio_irq_params(
84 IrqMask::All.value(),
85 IrqMask::All.value(),
86 IrqMask::None.value(),
87 IrqMask::None.value(),
88 )
89 .await?;
90 self.add_register_to_retention_list(Register::RxGain.addr()).await?;
91 self.add_register_to_retention_list(Register::TxModulation.addr())
92 .await?;
93 Ok(())
94 }
95
96 /// Return current radio state
97 pub fn get_status(&mut self) -> RadioState {
98 match self.brd_get_operating_mode() {
99 RadioMode::Transmit => RadioState::TxRunning,
100 RadioMode::Receive => RadioState::RxRunning,
101 RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting,
102 _ => RadioState::Idle,
103 }
104 }
105
106 /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired)
107 pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> {
108 self.sub_set_packet_type(PacketType::LoRa).await?;
109 if enable_public_network {
110 self.brd_write_registers(
111 Register::LoRaSyncword,
112 &[
113 ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8,
114 (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8,
115 ],
116 )
117 .await?;
118 } else {
119 self.brd_write_registers(
120 Register::LoRaSyncword,
121 &[
122 ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8,
123 (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8,
124 ],
125 )
126 .await?;
127 }
128
129 Ok(())
130 }
131
132 /// Sets the channel frequency
133 pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
134 self.sub_set_rf_frequency(frequency).await?;
135 Ok(())
136 }
137
138 /* Checks if the channel is free for the given time. This is currently not implemented until a substitute
139 for switching to the FSK modem is found.
140
141 pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool;
142 */
143
144 /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand.
145 /// After calling this function either set_rx_config() or set_tx_config() must be called.
146 pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> {
147 self.sub_set_dio_irq_params(
148 IrqMask::None.value(),
149 IrqMask::None.value(),
150 IrqMask::None.value(),
151 IrqMask::None.value(),
152 )
153 .await?;
154
155 let result = self.sub_get_random().await?;
156 Ok(result)
157 }
158
159 /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand.
160 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
161 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
162 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
163 /// preamble_length length in symbols (the hardware adds 4 more symbols)
164 /// symb_timeout RxSingle timeout value in symbols
165 /// fixed_len fixed length packets [0: variable, 1: fixed]
166 /// payload_len payload length when fixed length is used
167 /// crc_on [0: OFF, 1: ON]
168 /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
169 /// hop_period number of symbols between each hop
170 /// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
171 /// rx_continuous reception mode [false: single mode, true: continuous mode]
172 pub async fn set_rx_config(
173 &mut self,
174 spreading_factor: SpreadingFactor,
175 bandwidth: Bandwidth,
176 coding_rate: CodingRate,
177 preamble_length: u16,
178 symb_timeout: u16,
179 fixed_len: bool,
180 payload_len: u8,
181 crc_on: bool,
182 _freq_hop_on: bool,
183 _hop_period: u8,
184 iq_inverted: bool,
185 rx_continuous: bool,
186 ) -> Result<(), RadioError<BUS>> {
187 let mut symb_timeout_final = symb_timeout;
188
189 self.rx_continuous = rx_continuous;
190 if self.rx_continuous {
191 symb_timeout_final = 0;
192 }
193 if fixed_len {
194 self.max_payload_length = payload_len;
195 } else {
196 self.max_payload_length = 0xFFu8;
197 }
198
199 self.sub_set_stop_rx_timer_on_preamble_detect(false).await?;
200
201 let mut low_data_rate_optimize = 0x00u8;
202 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
203 && (bandwidth == Bandwidth::_125KHz))
204 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
205 {
206 low_data_rate_optimize = 0x01u8;
207 }
208
209 let modulation_params = ModulationParams {
210 spreading_factor: spreading_factor,
211 bandwidth: bandwidth,
212 coding_rate: coding_rate,
213 low_data_rate_optimize: low_data_rate_optimize,
214 };
215
216 let mut preamble_length_final = preamble_length;
217 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
218 && (preamble_length < 12)
219 {
220 preamble_length_final = 12;
221 }
222
223 let packet_params = PacketParams {
224 preamble_length: preamble_length_final,
225 implicit_header: fixed_len,
226 payload_length: self.max_payload_length,
227 crc_on: crc_on,
228 iq_inverted: iq_inverted,
229 };
230
231 self.modulation_params = Some(modulation_params);
232 self.packet_params = Some(packet_params);
233
234 self.standby().await?;
235 self.sub_set_modulation_params().await?;
236 self.sub_set_packet_params().await?;
237 self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?;
238
239 // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
240 let mut iq_polarity = [0x00u8];
241 self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?;
242 if iq_inverted {
243 self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))])
244 .await?;
245 } else {
246 self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)])
247 .await?;
248 }
249 Ok(())
250 }
251
252 /// Set the transmission parameters for the LoRa modem (only).
253 /// power output power [dBm]
254 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
255 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
256 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
257 /// preamble_length length in symbols (the hardware adds 4 more symbols)
258 /// fixed_len fixed length packets [0: variable, 1: fixed]
259 /// crc_on [0: OFF, 1: ON]
260 /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
261 /// hop_period number of symbols between each hop
262 /// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
263 pub async fn set_tx_config(
264 &mut self,
265 power: i8,
266 spreading_factor: SpreadingFactor,
267 bandwidth: Bandwidth,
268 coding_rate: CodingRate,
269 preamble_length: u16,
270 fixed_len: bool,
271 crc_on: bool,
272 _freq_hop_on: bool,
273 _hop_period: u8,
274 iq_inverted: bool,
275 ) -> Result<(), RadioError<BUS>> {
276 let mut low_data_rate_optimize = 0x00u8;
277 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
278 && (bandwidth == Bandwidth::_125KHz))
279 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
280 {
281 low_data_rate_optimize = 0x01u8;
282 }
283
284 let modulation_params = ModulationParams {
285 spreading_factor: spreading_factor,
286 bandwidth: bandwidth,
287 coding_rate: coding_rate,
288 low_data_rate_optimize: low_data_rate_optimize,
289 };
290
291 let mut preamble_length_final = preamble_length;
292 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
293 && (preamble_length < 12)
294 {
295 preamble_length_final = 12;
296 }
297
298 let packet_params = PacketParams {
299 preamble_length: preamble_length_final,
300 implicit_header: fixed_len,
301 payload_length: self.max_payload_length,
302 crc_on: crc_on,
303 iq_inverted: iq_inverted,
304 };
305
306 self.modulation_params = Some(modulation_params);
307 self.packet_params = Some(packet_params);
308
309 self.standby().await?;
310 self.sub_set_modulation_params().await?;
311 self.sub_set_packet_params().await?;
312
313 // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
314
315 let mut tx_modulation = [0x00u8];
316 self.brd_read_registers(Register::TxModulation, &mut tx_modulation)
317 .await?;
318 if bandwidth == Bandwidth::_500KHz {
319 self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))])
320 .await?;
321 } else {
322 self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)])
323 .await?;
324 }
325
326 self.brd_set_rf_tx_power(power).await?;
327 Ok(())
328 }
329
330 /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported]
331 pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> {
332 Ok(self.brd_check_rf_frequency(frequency).await?)
333 }
334
335 /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called)
336 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
337 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
338 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
339 /// preamble_length length in symbols (the hardware adds 4 more symbols)
340 /// fixed_len fixed length packets [0: variable, 1: fixed]
341 /// payload_len sets payload length when fixed length is used
342 /// crc_on [0: OFF, 1: ON]
343 pub fn get_time_on_air(
344 &mut self,
345 spreading_factor: SpreadingFactor,
346 bandwidth: Bandwidth,
347 coding_rate: CodingRate,
348 preamble_length: u16,
349 fixed_len: bool,
350 payload_len: u8,
351 crc_on: bool,
352 ) -> Result<u32, RadioError<BUS>> {
353 let numerator = 1000
354 * Self::get_lora_time_on_air_numerator(
355 spreading_factor,
356 bandwidth,
357 coding_rate,
358 preamble_length,
359 fixed_len,
360 payload_len,
361 crc_on,
362 );
363 let denominator = bandwidth.value_in_hz();
364 if denominator == 0 {
365 Err(RadioError::InvalidBandwidth)
366 } else {
367 Ok((numerator + denominator - 1) / denominator)
368 }
369 }
370
371 /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms]
372 pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
373 if self.packet_params.is_some() {
374 self.sub_set_dio_irq_params(
375 IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
376 IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
377 IrqMask::None.value(),
378 IrqMask::None.value(),
379 )
380 .await?;
381
382 let mut packet_params = self.packet_params.as_mut().unwrap();
383 packet_params.payload_length = buffer.len() as u8;
384 self.sub_set_packet_params().await?;
385 self.sub_send_payload(buffer, timeout).await?;
386 Ok(())
387 } else {
388 Err(RadioError::PacketParamsMissing)
389 }
390 }
391
392 /// Set the radio in sleep mode
393 pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> {
394 self.sub_set_sleep(SleepParams {
395 wakeup_rtc: false,
396 reset: false,
397 warm_start: true,
398 })
399 .await?;
400 Timer::after(Duration::from_millis(2)).await;
401 Ok(())
402 }
403
404 /// Set the radio in standby mode
405 pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> {
406 self.sub_set_standby(StandbyMode::RC).await?;
407 Ok(())
408 }
409
410 /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)]
411 pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
412 self.sub_set_dio_irq_params(
413 IrqMask::All.value(),
414 IrqMask::All.value(),
415 IrqMask::None.value(),
416 IrqMask::None.value(),
417 )
418 .await?;
419
420 if self.rx_continuous {
421 self.sub_set_rx(0xFFFFFF).await?;
422 } else {
423 self.sub_set_rx(timeout << 6).await?;
424 }
425
426 Ok(())
427 }
428
429 /// Start a Channel Activity Detection
430 pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> {
431 self.sub_set_dio_irq_params(
432 IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
433 IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
434 IrqMask::None.value(),
435 IrqMask::None.value(),
436 )
437 .await?;
438 self.sub_set_cad().await?;
439 Ok(())
440 }
441
442 /// Sets the radio in continuous wave transmission mode
443 /// frequency channel RF frequency
444 /// power output power [dBm]
445 /// timeout transmission mode timeout [s]
446 pub async fn set_tx_continuous_wave(
447 &mut self,
448 frequency: u32,
449 power: i8,
450 _timeout: u16,
451 ) -> Result<(), RadioError<BUS>> {
452 self.sub_set_rf_frequency(frequency).await?;
453 self.brd_set_rf_tx_power(power).await?;
454 self.sub_set_tx_continuous_wave().await?;
455
456 Ok(())
457 }
458
459 /// Read the current RSSI value for the LoRa modem (only) [dBm]
460 pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> {
461 let value = self.sub_get_rssi_inst().await?;
462 Ok(value as i16)
463 }
464
465 /// Write one or more radio registers with a buffer of a given size, starting at the first register address
466 pub async fn write_registers_from_buffer(
467 &mut self,
468 start_register: Register,
469 buffer: &[u8],
470 ) -> Result<(), RadioError<BUS>> {
471 self.brd_write_registers(start_register, buffer).await?;
472 Ok(())
473 }
474
475 /// Read one or more radio registers into a buffer of a given size, starting at the first register address
476 pub async fn read_registers_into_buffer(
477 &mut self,
478 start_register: Register,
479 buffer: &mut [u8],
480 ) -> Result<(), RadioError<BUS>> {
481 self.brd_read_registers(start_register, buffer).await?;
482 Ok(())
483 }
484
485 /// Set the maximum payload length (in bytes) for a LoRa modem (only).
486 pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> {
487 if self.packet_params.is_some() {
488 let packet_params = self.packet_params.as_mut().unwrap();
489 self.max_payload_length = max;
490 packet_params.payload_length = max;
491 self.sub_set_packet_params().await?;
492 Ok(())
493 } else {
494 Err(RadioError::PacketParamsMissing)
495 }
496 }
497
498 /// Get the time required for the board plus radio to get out of sleep [ms]
499 pub fn get_wakeup_time(&mut self) -> u32 {
500 self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME
501 }
502
503 /// Process the radio irq
504 pub async fn process_irq(
505 &mut self,
506 receiving_buffer: Option<&mut [u8]>,
507 received_len: Option<&mut u8>,
508 cad_activity_detected: Option<&mut bool>,
509 ) -> Result<(), RadioError<BUS>> {
510 loop {
511 trace!("process_irq loop entered");
512
513 let de = self.sub_get_device_errors().await?;
514 trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}",
515 de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp);
516 let st = self.sub_get_status().await?;
517 trace!(
518 "radio status: cmd_status: {:x}, chip_mode: {:x}",
519 st.cmd_status,
520 st.chip_mode
521 );
522
523 self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
524 let operating_mode = self.brd_get_operating_mode();
525 let irq_flags = self.sub_get_irq_status().await?;
526 self.sub_clear_irq_status(irq_flags).await?;
527 trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags);
528
529 // check for errors and unexpected interrupt masks (based on operation mode)
530 if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() {
531 if !self.rx_continuous {
532 self.brd_set_operating_mode(RadioMode::StandbyRC);
533 }
534 return Err(RadioError::HeaderError);
535 } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() {
536 if operating_mode == RadioMode::Receive {
537 if !self.rx_continuous {
538 self.brd_set_operating_mode(RadioMode::StandbyRC);
539 }
540 return Err(RadioError::CRCErrorOnReceive);
541 } else {
542 return Err(RadioError::CRCErrorUnexpected);
543 }
544 } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() {
545 if operating_mode == RadioMode::Transmit {
546 self.brd_set_operating_mode(RadioMode::StandbyRC);
547 return Err(RadioError::TransmitTimeout);
548 } else if operating_mode == RadioMode::Receive {
549 self.brd_set_operating_mode(RadioMode::StandbyRC);
550 return Err(RadioError::ReceiveTimeout);
551 } else {
552 return Err(RadioError::TimeoutUnexpected);
553 }
554 } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value())
555 && (operating_mode != RadioMode::Transmit)
556 {
557 return Err(RadioError::TransmitDoneUnexpected);
558 } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value())
559 && (operating_mode != RadioMode::Receive)
560 {
561 return Err(RadioError::ReceiveDoneUnexpected);
562 } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value())
563 || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value()))
564 && (operating_mode != RadioMode::ChannelActivityDetection)
565 {
566 return Err(RadioError::CADUnexpected);
567 }
568
569 if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() {
570 trace!("HeaderValid");
571 } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() {
572 trace!("PreambleDetected");
573 } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() {
574 trace!("SyncwordValid");
575 }
576
577 // handle completions
578 if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() {
579 self.brd_set_operating_mode(RadioMode::StandbyRC);
580 return Ok(());
581 } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() {
582 if !self.rx_continuous {
583 self.brd_set_operating_mode(RadioMode::StandbyRC);
584
585 // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3)
586 self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?;
587 let mut evt_clr = [0x00u8];
588 self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?;
589 evt_clr[0] |= 1 << 1;
590 self.brd_write_registers(Register::EvtClr, &evt_clr).await?;
591 }
592
593 if receiving_buffer.is_some() && received_len.is_some() {
594 *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?;
595 }
596 self.packet_status = self.sub_get_packet_status().await?.into();
597 return Ok(());
598 } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() {
599 if cad_activity_detected.is_some() {
600 *(cad_activity_detected.unwrap()) =
601 (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value();
602 }
603 self.brd_set_operating_mode(RadioMode::StandbyRC);
604 return Ok(());
605 }
606
607 // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid
608 // are in that category), loop to wait again
609 }
610 }
611
612 // SX126x-specific functions
613
614 /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms]
615 pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
616 self.sub_set_dio_irq_params(
617 IrqMask::All.value(),
618 IrqMask::All.value(),
619 IrqMask::None.value(),
620 IrqMask::None.value(),
621 )
622 .await?;
623
624 if self.rx_continuous {
625 self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous
626 } else {
627 self.sub_set_rx_boosted(timeout << 6).await?;
628 }
629
630 Ok(())
631 }
632
633 /// Set the Rx duty cycle management parameters (SX126x radios only)
634 /// rx_time structure describing reception timeout value
635 /// sleep_time structure describing sleep timeout value
636 pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
637 self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?;
638 Ok(())
639 }
640
641 pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> {
642 self.packet_status
643 }
644
645 // Utilities
646
647 async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> {
648 let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize];
649
650 // Read the address and registers already added to the list
651 self.brd_read_registers(Register::RetentionList, &mut buffer).await?;
652
653 let number_of_registers = buffer[0];
654 for i in 0..number_of_registers {
655 if register_address
656 == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16)
657 {
658 return Ok(()); // register already in list
659 }
660 }
661
662 if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION {
663 buffer[0] += 1; // increment number of registers
664
665 buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8;
666 buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8;
667 self.brd_write_registers(Register::RetentionList, &buffer).await?;
668
669 Ok(())
670 } else {
671 Err(RadioError::RetentionListExceeded)
672 }
673 }
674
675 fn get_lora_time_on_air_numerator(
676 spreading_factor: SpreadingFactor,
677 bandwidth: Bandwidth,
678 coding_rate: CodingRate,
679 preamble_length: u16,
680 fixed_len: bool,
681 payload_len: u8,
682 crc_on: bool,
683 ) -> u32 {
684 let cell_denominator;
685 let cr_denominator = (coding_rate.value() as i32) + 4;
686
687 // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6
688 let mut preamble_length_final = preamble_length;
689 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
690 && (preamble_length < 12)
691 {
692 preamble_length_final = 12;
693 }
694
695 let mut low_data_rate_optimize = false;
696 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
697 && (bandwidth == Bandwidth::_125KHz))
698 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
699 {
700 low_data_rate_optimize = true;
701 }
702
703 let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 })
704 - (4 * spreading_factor.value() as i32)
705 + (if fixed_len { 0 } else { 20 });
706
707 if spreading_factor.value() <= 6 {
708 cell_denominator = 4 * (spreading_factor.value() as i32);
709 } else {
710 cell_numerator += 8;
711 if low_data_rate_optimize {
712 cell_denominator = 4 * ((spreading_factor.value() as i32) - 2);
713 } else {
714 cell_denominator = 4 * (spreading_factor.value() as i32);
715 }
716 }
717
718 if cell_numerator < 0 {
719 cell_numerator = 0;
720 }
721
722 let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator)
723 + (preamble_length_final as i32)
724 + 12;
725
726 if spreading_factor.value() <= 6 {
727 intermediate = intermediate + 2;
728 }
729
730 (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32
731 }
732}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
new file mode 100644
index 000000000..e270b2a09
--- /dev/null
+++ b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
@@ -0,0 +1,469 @@
1use core::fmt::Debug;
2
3use lorawan_device::async_device::radio as device;
4
5#[allow(clippy::upper_case_acronyms)]
6#[derive(Debug)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub enum RadioError<BUS> {
9 SPI(BUS),
10 CS,
11 Reset,
12 AntRx,
13 AntTx,
14 Busy,
15 DIO1,
16 PayloadSizeMismatch(usize, usize),
17 RetentionListExceeded,
18 InvalidBandwidth,
19 ModulationParamsMissing,
20 PacketParamsMissing,
21 HeaderError,
22 CRCErrorUnexpected,
23 CRCErrorOnReceive,
24 TransmitTimeout,
25 ReceiveTimeout,
26 TimeoutUnexpected,
27 TransmitDoneUnexpected,
28 ReceiveDoneUnexpected,
29 CADUnexpected,
30}
31
32pub struct RadioSystemError {
33 pub rc_64khz_calibration: bool,
34 pub rc_13mhz_calibration: bool,
35 pub pll_calibration: bool,
36 pub adc_calibration: bool,
37 pub image_calibration: bool,
38 pub xosc_start: bool,
39 pub pll_lock: bool,
40 pub pa_ramp: bool,
41}
42
43#[derive(Clone, Copy, PartialEq)]
44pub enum PacketType {
45 GFSK = 0x00,
46 LoRa = 0x01,
47 None = 0x0F,
48}
49
50impl PacketType {
51 pub const fn value(self) -> u8 {
52 self as u8
53 }
54 pub fn to_enum(value: u8) -> Self {
55 if value == 0x00 {
56 PacketType::GFSK
57 } else if value == 0x01 {
58 PacketType::LoRa
59 } else {
60 PacketType::None
61 }
62 }
63}
64
65#[derive(Clone, Copy)]
66pub struct PacketStatus {
67 pub rssi: i8,
68 pub snr: i8,
69 pub signal_rssi: i8,
70 pub freq_error: u32,
71}
72
73#[derive(Clone, Copy, PartialEq)]
74pub enum RadioType {
75 SX1261,
76 SX1262,
77}
78
79#[derive(Clone, Copy, PartialEq)]
80pub enum RadioMode {
81 Sleep = 0x00, // sleep mode
82 StandbyRC = 0x01, // standby mode with RC oscillator
83 StandbyXOSC = 0x02, // standby mode with XOSC oscillator
84 FrequencySynthesis = 0x03, // frequency synthesis mode
85 Transmit = 0x04, // transmit mode
86 Receive = 0x05, // receive mode
87 ReceiveDutyCycle = 0x06, // receive duty cycle mode
88 ChannelActivityDetection = 0x07, // channel activity detection mode
89}
90
91impl RadioMode {
92 /// Returns the value of the mode.
93 pub const fn value(self) -> u8 {
94 self as u8
95 }
96 pub fn to_enum(value: u8) -> Self {
97 if value == 0x00 {
98 RadioMode::Sleep
99 } else if value == 0x01 {
100 RadioMode::StandbyRC
101 } else if value == 0x02 {
102 RadioMode::StandbyXOSC
103 } else if value == 0x03 {
104 RadioMode::FrequencySynthesis
105 } else if value == 0x04 {
106 RadioMode::Transmit
107 } else if value == 0x05 {
108 RadioMode::Receive
109 } else if value == 0x06 {
110 RadioMode::ReceiveDutyCycle
111 } else if value == 0x07 {
112 RadioMode::ChannelActivityDetection
113 } else {
114 RadioMode::Sleep
115 }
116 }
117}
118
119pub enum RadioState {
120 Idle = 0x00,
121 RxRunning = 0x01,
122 TxRunning = 0x02,
123 ChannelActivityDetecting = 0x03,
124}
125
126impl RadioState {
127 /// Returns the value of the state.
128 pub fn value(self) -> u8 {
129 self as u8
130 }
131}
132
133pub struct RadioStatus {
134 pub cmd_status: u8,
135 pub chip_mode: u8,
136}
137
138impl RadioStatus {
139 pub fn value(self) -> u8 {
140 (self.chip_mode << 4) | (self.cmd_status << 1)
141 }
142}
143
144#[derive(Clone, Copy)]
145pub enum IrqMask {
146 None = 0x0000,
147 TxDone = 0x0001,
148 RxDone = 0x0002,
149 PreambleDetected = 0x0004,
150 SyncwordValid = 0x0008,
151 HeaderValid = 0x0010,
152 HeaderError = 0x0020,
153 CRCError = 0x0040,
154 CADDone = 0x0080,
155 CADActivityDetected = 0x0100,
156 RxTxTimeout = 0x0200,
157 All = 0xFFFF,
158}
159
160impl IrqMask {
161 pub fn value(self) -> u16 {
162 self as u16
163 }
164}
165
166#[derive(Clone, Copy)]
167pub enum Register {
168 PacketParams = 0x0704, // packet configuration
169 PayloadLength = 0x0702, // payload size
170 SynchTimeout = 0x0706, // recalculated number of symbols
171 Syncword = 0x06C0, // Syncword values
172 LoRaSyncword = 0x0740, // LoRa Syncword value
173 GeneratedRandomNumber = 0x0819, //32-bit generated random number
174 AnaLNA = 0x08E2, // disable the LNA
175 AnaMixer = 0x08E5, // disable the mixer
176 RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted)
177 XTATrim = 0x0911, // device internal trimming capacitor
178 OCP = 0x08E7, // over current protection max value
179 RetentionList = 0x029F, // retention list
180 IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
181 TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
182 TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
183 RTCCtrl = 0x0902, // RTC control
184 EvtClr = 0x0944, // event clear
185}
186
187impl Register {
188 pub fn addr(self) -> u16 {
189 self as u16
190 }
191}
192
193#[derive(Clone, Copy, PartialEq)]
194pub enum OpCode {
195 GetStatus = 0xC0,
196 WriteRegister = 0x0D,
197 ReadRegister = 0x1D,
198 WriteBuffer = 0x0E,
199 ReadBuffer = 0x1E,
200 SetSleep = 0x84,
201 SetStandby = 0x80,
202 SetFS = 0xC1,
203 SetTx = 0x83,
204 SetRx = 0x82,
205 SetRxDutyCycle = 0x94,
206 SetCAD = 0xC5,
207 SetTxContinuousWave = 0xD1,
208 SetTxContinuousPremable = 0xD2,
209 SetPacketType = 0x8A,
210 GetPacketType = 0x11,
211 SetRFFrequency = 0x86,
212 SetTxParams = 0x8E,
213 SetPAConfig = 0x95,
214 SetCADParams = 0x88,
215 SetBufferBaseAddress = 0x8F,
216 SetModulationParams = 0x8B,
217 SetPacketParams = 0x8C,
218 GetRxBufferStatus = 0x13,
219 GetPacketStatus = 0x14,
220 GetRSSIInst = 0x15,
221 GetStats = 0x10,
222 ResetStats = 0x00,
223 CfgDIOIrq = 0x08,
224 GetIrqStatus = 0x12,
225 ClrIrqStatus = 0x02,
226 Calibrate = 0x89,
227 CalibrateImage = 0x98,
228 SetRegulatorMode = 0x96,
229 GetErrors = 0x17,
230 ClrErrors = 0x07,
231 SetTCXOMode = 0x97,
232 SetTxFallbackMode = 0x93,
233 SetRFSwitchMode = 0x9D,
234 SetStopRxTimerOnPreamble = 0x9F,
235 SetLoRaSymbTimeout = 0xA0,
236}
237
238impl OpCode {
239 pub fn value(self) -> u8 {
240 self as u8
241 }
242}
243
244pub struct SleepParams {
245 pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC
246 pub reset: bool,
247 pub warm_start: bool,
248}
249
250impl SleepParams {
251 pub fn value(self) -> u8 {
252 ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8)
253 }
254}
255
256#[derive(Clone, Copy, PartialEq)]
257pub enum StandbyMode {
258 RC = 0x00,
259 XOSC = 0x01,
260}
261
262impl StandbyMode {
263 pub fn value(self) -> u8 {
264 self as u8
265 }
266}
267
268#[derive(Clone, Copy)]
269pub enum RegulatorMode {
270 UseLDO = 0x00,
271 UseDCDC = 0x01,
272}
273
274impl RegulatorMode {
275 pub fn value(self) -> u8 {
276 self as u8
277 }
278}
279
280#[derive(Clone, Copy)]
281pub struct CalibrationParams {
282 pub rc64k_enable: bool, // calibrate RC64K clock
283 pub rc13m_enable: bool, // calibrate RC13M clock
284 pub pll_enable: bool, // calibrate PLL
285 pub adc_pulse_enable: bool, // calibrate ADC Pulse
286 pub adc_bulkn_enable: bool, // calibrate ADC bulkN
287 pub adc_bulkp_enable: bool, // calibrate ADC bulkP
288 pub img_enable: bool,
289}
290
291impl CalibrationParams {
292 pub fn value(self) -> u8 {
293 ((self.img_enable as u8) << 6)
294 | ((self.adc_bulkp_enable as u8) << 5)
295 | ((self.adc_bulkn_enable as u8) << 4)
296 | ((self.adc_pulse_enable as u8) << 3)
297 | ((self.pll_enable as u8) << 2)
298 | ((self.rc13m_enable as u8) << 1)
299 | ((self.rc64k_enable as u8) << 0)
300 }
301}
302
303#[derive(Clone, Copy)]
304pub enum TcxoCtrlVoltage {
305 Ctrl1V6 = 0x00,
306 Ctrl1V7 = 0x01,
307 Ctrl1V8 = 0x02,
308 Ctrl2V2 = 0x03,
309 Ctrl2V4 = 0x04,
310 Ctrl2V7 = 0x05,
311 Ctrl3V0 = 0x06,
312 Ctrl3V3 = 0x07,
313}
314
315impl TcxoCtrlVoltage {
316 pub fn value(self) -> u8 {
317 self as u8
318 }
319}
320
321#[derive(Clone, Copy)]
322pub enum RampTime {
323 Ramp10Us = 0x00,
324 Ramp20Us = 0x01,
325 Ramp40Us = 0x02,
326 Ramp80Us = 0x03,
327 Ramp200Us = 0x04,
328 Ramp800Us = 0x05,
329 Ramp1700Us = 0x06,
330 Ramp3400Us = 0x07,
331}
332
333impl RampTime {
334 pub fn value(self) -> u8 {
335 self as u8
336 }
337}
338
339#[derive(Clone, Copy, PartialEq)]
340pub enum SpreadingFactor {
341 _5 = 0x05,
342 _6 = 0x06,
343 _7 = 0x07,
344 _8 = 0x08,
345 _9 = 0x09,
346 _10 = 0x0A,
347 _11 = 0x0B,
348 _12 = 0x0C,
349}
350
351impl SpreadingFactor {
352 pub fn value(self) -> u8 {
353 self as u8
354 }
355}
356
357impl From<device::SpreadingFactor> for SpreadingFactor {
358 fn from(sf: device::SpreadingFactor) -> Self {
359 match sf {
360 device::SpreadingFactor::_7 => SpreadingFactor::_7,
361 device::SpreadingFactor::_8 => SpreadingFactor::_8,
362 device::SpreadingFactor::_9 => SpreadingFactor::_9,
363 device::SpreadingFactor::_10 => SpreadingFactor::_10,
364 device::SpreadingFactor::_11 => SpreadingFactor::_11,
365 device::SpreadingFactor::_12 => SpreadingFactor::_12,
366 }
367 }
368}
369
370#[derive(Clone, Copy, PartialEq)]
371pub enum Bandwidth {
372 _500KHz = 0x06,
373 _250KHz = 0x05,
374 _125KHz = 0x04,
375}
376
377impl Bandwidth {
378 pub fn value(self) -> u8 {
379 self as u8
380 }
381
382 pub fn value_in_hz(self) -> u32 {
383 match self {
384 Bandwidth::_125KHz => 125000u32,
385 Bandwidth::_250KHz => 250000u32,
386 Bandwidth::_500KHz => 500000u32,
387 }
388 }
389}
390
391impl From<device::Bandwidth> for Bandwidth {
392 fn from(bw: device::Bandwidth) -> Self {
393 match bw {
394 device::Bandwidth::_500KHz => Bandwidth::_500KHz,
395 device::Bandwidth::_250KHz => Bandwidth::_250KHz,
396 device::Bandwidth::_125KHz => Bandwidth::_125KHz,
397 }
398 }
399}
400
401#[derive(Clone, Copy)]
402pub enum CodingRate {
403 _4_5 = 0x01,
404 _4_6 = 0x02,
405 _4_7 = 0x03,
406 _4_8 = 0x04,
407}
408
409impl CodingRate {
410 pub fn value(self) -> u8 {
411 self as u8
412 }
413}
414
415impl From<device::CodingRate> for CodingRate {
416 fn from(cr: device::CodingRate) -> Self {
417 match cr {
418 device::CodingRate::_4_5 => CodingRate::_4_5,
419 device::CodingRate::_4_6 => CodingRate::_4_6,
420 device::CodingRate::_4_7 => CodingRate::_4_7,
421 device::CodingRate::_4_8 => CodingRate::_4_8,
422 }
423 }
424}
425
426#[derive(Clone, Copy)]
427pub struct ModulationParams {
428 pub spreading_factor: SpreadingFactor,
429 pub bandwidth: Bandwidth,
430 pub coding_rate: CodingRate,
431 pub low_data_rate_optimize: u8,
432}
433
434#[derive(Clone, Copy)]
435pub struct PacketParams {
436 pub preamble_length: u16, // number of LoRa symbols in the preamble
437 pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length)
438 pub payload_length: u8,
439 pub crc_on: bool,
440 pub iq_inverted: bool,
441}
442
443#[derive(Clone, Copy)]
444pub enum CADSymbols {
445 _1 = 0x00,
446 _2 = 0x01,
447 _4 = 0x02,
448 _8 = 0x03,
449 _16 = 0x04,
450}
451
452impl CADSymbols {
453 pub fn value(self) -> u8 {
454 self as u8
455 }
456}
457
458#[derive(Clone, Copy)]
459pub enum CADExitMode {
460 CADOnly = 0x00,
461 CADRx = 0x01,
462 CADLBT = 0x10,
463}
464
465impl CADExitMode {
466 pub fn value(self) -> u8 {
467 self as u8
468 }
469}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
new file mode 100644
index 000000000..2e78b919b
--- /dev/null
+++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
@@ -0,0 +1,674 @@
1use embedded_hal::digital::v2::OutputPin;
2use embedded_hal_async::digital::Wait;
3use embedded_hal_async::spi::SpiBus;
4
5use super::mod_params::*;
6use super::LoRa;
7
8// Internal frequency of the radio
9const SX126X_XTAL_FREQ: u32 = 32000000;
10
11// Scaling factor used to perform fixed-point operations
12const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14;
13
14// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT
15const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT);
16
17// Maximum value for parameter symbNum
18const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248;
19
20// Provides board-specific functionality for Semtech SX126x-based boards
21
22impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
23where
24 SPI: SpiBus<u8, Error = BUS>,
25 CTRL: OutputPin,
26 WAIT: Wait,
27{
28 // Initialize the radio driver
29 pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> {
30 self.brd_reset().await?;
31 self.brd_wakeup().await?;
32 self.sub_set_standby(StandbyMode::RC).await?;
33 self.brd_io_tcxo_init().await?;
34 self.brd_io_rf_switch_init().await?;
35 self.image_calibrated = false;
36 Ok(())
37 }
38
39 // Wakeup the radio if it is in Sleep mode and check that Busy is low
40 pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> {
41 let operating_mode = self.brd_get_operating_mode();
42 if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle {
43 self.brd_wakeup().await?;
44 }
45 self.brd_wait_on_busy().await?;
46 Ok(())
47 }
48
49 // Save the payload to be sent in the radio buffer
50 pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> {
51 self.brd_write_buffer(0x00, payload).await?;
52 Ok(())
53 }
54
55 // Read the payload received.
56 pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
57 let (size, offset) = self.sub_get_rx_buffer_status().await?;
58 if (size as usize) > buffer.len() {
59 Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len()))
60 } else {
61 self.brd_read_buffer(offset, buffer).await?;
62 Ok(size)
63 }
64 }
65
66 // Send a payload
67 pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
68 self.sub_set_payload(payload).await?;
69 self.sub_set_tx(timeout).await?;
70 Ok(())
71 }
72
73 // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command.
74 //
75 // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of
76 // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts
77 // generated during this process will not cause undesired side-effects in the software.
78 //
79 // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing.
80 pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> {
81 let mut reg_ana_lna_buffer_original = [0x00u8];
82 let mut reg_ana_mixer_buffer_original = [0x00u8];
83 let mut reg_ana_lna_buffer = [0x00u8];
84 let mut reg_ana_mixer_buffer = [0x00u8];
85 let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8];
86
87 self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original)
88 .await?;
89 reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0));
90 self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer).await?;
91
92 self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original)
93 .await?;
94 reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7));
95 self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer)
96 .await?;
97
98 // Set radio in continuous reception
99 self.sub_set_rx(0xFFFFFFu32).await?;
100
101 self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer)
102 .await?;
103
104 self.sub_set_standby(StandbyMode::RC).await?;
105
106 self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer_original)
107 .await?;
108 self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer_original)
109 .await?;
110
111 Ok(Self::convert_u8_buffer_to_u32(&number_buffer))
112 }
113
114 // Set the radio in sleep mode
115 pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> {
116 self.brd_ant_sleep()?;
117
118 if !sleep_config.warm_start {
119 self.image_calibrated = false;
120 }
121
122 self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()])
123 .await?;
124 self.brd_set_operating_mode(RadioMode::Sleep);
125 Ok(())
126 }
127
128 // Set the radio in configuration mode
129 pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> {
130 self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?;
131 if mode == StandbyMode::RC {
132 self.brd_set_operating_mode(RadioMode::StandbyRC);
133 } else {
134 self.brd_set_operating_mode(RadioMode::StandbyXOSC);
135 }
136
137 self.brd_ant_sleep()?;
138 Ok(())
139 }
140
141 // Set the radio in FS mode
142 pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> {
143 // antenna settings ???
144 self.brd_write_command(OpCode::SetFS, &[]).await?;
145 self.brd_set_operating_mode(RadioMode::FrequencySynthesis);
146 Ok(())
147 }
148
149 // Set the radio in transmission mode with timeout specified
150 pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
151 let buffer = [
152 Self::timeout_1(timeout),
153 Self::timeout_2(timeout),
154 Self::timeout_3(timeout),
155 ];
156
157 self.brd_ant_set_tx()?;
158
159 self.brd_set_operating_mode(RadioMode::Transmit);
160 self.brd_write_command(OpCode::SetTx, &buffer).await?;
161 Ok(())
162 }
163
164 // Set the radio in reception mode with timeout specified
165 pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
166 let buffer = [
167 Self::timeout_1(timeout),
168 Self::timeout_2(timeout),
169 Self::timeout_3(timeout),
170 ];
171
172 self.brd_ant_set_rx()?;
173
174 self.brd_set_operating_mode(RadioMode::Receive);
175 self.brd_write_registers(Register::RxGain, &[0x94u8]).await?;
176 self.brd_write_command(OpCode::SetRx, &buffer).await?;
177 Ok(())
178 }
179
180 // Set the radio in reception mode with Boosted LNA gain and timeout specified
181 pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
182 let buffer = [
183 Self::timeout_1(timeout),
184 Self::timeout_2(timeout),
185 Self::timeout_3(timeout),
186 ];
187
188 self.brd_ant_set_rx()?;
189
190 self.brd_set_operating_mode(RadioMode::Receive);
191 // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity
192 self.brd_write_registers(Register::RxGain, &[0x96u8]).await?;
193 self.brd_write_command(OpCode::SetRx, &buffer).await?;
194 Ok(())
195 }
196
197 // Set the Rx duty cycle management parameters
198 pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
199 let buffer = [
200 ((rx_time >> 16) & 0xFF) as u8,
201 ((rx_time >> 8) & 0xFF) as u8,
202 (rx_time & 0xFF) as u8,
203 ((sleep_time >> 16) & 0xFF) as u8,
204 ((sleep_time >> 8) & 0xFF) as u8,
205 (sleep_time & 0xFF) as u8,
206 ];
207
208 // antenna settings ???
209
210 self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?;
211 self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle);
212 Ok(())
213 }
214
215 // Set the radio in CAD mode
216 pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> {
217 self.brd_ant_set_rx()?;
218
219 self.brd_write_command(OpCode::SetCAD, &[]).await?;
220 self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
221 Ok(())
222 }
223
224 // Set the radio in continuous wave transmission mode
225 pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> {
226 self.brd_ant_set_tx()?;
227
228 self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?;
229 self.brd_set_operating_mode(RadioMode::Transmit);
230 Ok(())
231 }
232
233 // Set the radio in continuous preamble transmission mode
234 pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> {
235 self.brd_ant_set_tx()?;
236
237 self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?;
238 self.brd_set_operating_mode(RadioMode::Transmit);
239 Ok(())
240 }
241
242 // Decide which interrupt will stop the internal radio rx timer.
243 // false timer stop after header/syncword detection
244 // true timer stop after preamble detection
245 pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect(
246 &mut self,
247 enable: bool,
248 ) -> Result<(), RadioError<BUS>> {
249 self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8])
250 .await?;
251 Ok(())
252 }
253
254 // Set the number of symbols the radio will wait to validate a reception
255 pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> {
256 let mut exp = 0u8;
257 let mut reg;
258 let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1;
259 while mant > 31 {
260 mant = (mant + 3) >> 2;
261 exp += 1;
262 }
263 reg = mant << ((2 * exp) + 1);
264
265 self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?;
266
267 if symb_num != 0 {
268 reg = exp + (mant << 3);
269 self.brd_write_registers(Register::SynchTimeout, &[reg]).await?;
270 }
271
272 Ok(())
273 }
274
275 // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled
276 pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> {
277 self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()])
278 .await?;
279 Ok(())
280 }
281
282 // Calibrate the given radio block
283 pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> {
284 self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()])
285 .await?;
286 Ok(())
287 }
288
289 // Calibrate the image rejection based on the given frequency
290 pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> {
291 let mut cal_freq = [0x00u8, 0x00u8];
292
293 if freq > 900000000 {
294 cal_freq[0] = 0xE1;
295 cal_freq[1] = 0xE9;
296 } else if freq > 850000000 {
297 cal_freq[0] = 0xD7;
298 cal_freq[1] = 0xDB;
299 } else if freq > 770000000 {
300 cal_freq[0] = 0xC1;
301 cal_freq[1] = 0xC5;
302 } else if freq > 460000000 {
303 cal_freq[0] = 0x75;
304 cal_freq[1] = 0x81;
305 } else if freq > 425000000 {
306 cal_freq[0] = 0x6B;
307 cal_freq[1] = 0x6F;
308 }
309 self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?;
310 Ok(())
311 }
312
313 // Activate the extention of the timeout when a long preamble is used
314 pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> {
315 Ok(()) // no operation currently
316 }
317
318 // Set the transmission parameters
319 // hp_max 0 for sx1261, 7 for sx1262
320 // device_sel 1 for sx1261, 0 for sx1262
321 // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT
322 pub(super) async fn sub_set_pa_config(
323 &mut self,
324 pa_duty_cycle: u8,
325 hp_max: u8,
326 device_sel: u8,
327 pa_lut: u8,
328 ) -> Result<(), RadioError<BUS>> {
329 self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut])
330 .await?;
331 Ok(())
332 }
333
334 // Define into which mode the chip goes after a TX / RX done
335 pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> {
336 self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode])
337 .await?;
338 Ok(())
339 }
340
341 // Set the IRQ mask and DIO masks
342 pub(super) async fn sub_set_dio_irq_params(
343 &mut self,
344 irq_mask: u16,
345 dio1_mask: u16,
346 dio2_mask: u16,
347 dio3_mask: u16,
348 ) -> Result<(), RadioError<BUS>> {
349 let mut buffer = [0x00u8; 8];
350
351 buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8;
352 buffer[1] = (irq_mask & 0x00FF) as u8;
353 buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8;
354 buffer[3] = (dio1_mask & 0x00FF) as u8;
355 buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8;
356 buffer[5] = (dio2_mask & 0x00FF) as u8;
357 buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8;
358 buffer[7] = (dio3_mask & 0x00FF) as u8;
359 self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?;
360 Ok(())
361 }
362
363 // Return the current IRQ status
364 pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> {
365 let mut irq_status = [0x00u8, 0x00u8];
366 self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?;
367 Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16))
368 }
369
370 // Indicate if DIO2 is used to control an RF Switch
371 pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> {
372 self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?;
373 Ok(())
374 }
375
376 // Indicate if the radio main clock is supplied from a TCXO
377 // tcxo_voltage voltage used to control the TCXO on/off from DIO3
378 // timeout duration given to the TCXO to go to 32MHz
379 pub(super) async fn sub_set_dio3_as_tcxo_ctrl(
380 &mut self,
381 tcxo_voltage: TcxoCtrlVoltage,
382 timeout: u32,
383 ) -> Result<(), RadioError<BUS>> {
384 let buffer = [
385 tcxo_voltage.value() & 0x07,
386 Self::timeout_1(timeout),
387 Self::timeout_2(timeout),
388 Self::timeout_3(timeout),
389 ];
390 self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?;
391
392 Ok(())
393 }
394
395 // Set the RF frequency (Hz)
396 pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
397 let mut buffer = [0x00u8; 4];
398
399 if !self.image_calibrated {
400 self.sub_calibrate_image(frequency).await?;
401 self.image_calibrated = true;
402 }
403
404 let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency);
405
406 buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8;
407 buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8;
408 buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8;
409 buffer[3] = (freq_in_pll_steps & 0xFF) as u8;
410 self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?;
411 Ok(())
412 }
413
414 // Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters.
415 pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> {
416 self.packet_type = packet_type;
417 self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()])
418 .await?;
419 Ok(())
420 }
421
422 // Get the current radio protocol (LoRa or GFSK)
423 pub(super) fn sub_get_packet_type(&mut self) -> PacketType {
424 self.packet_type
425 }
426
427 // Set the transmission parameters
428 // power RF output power [-18..13] dBm
429 // ramp_time transmission ramp up time
430 pub(super) async fn sub_set_tx_params(
431 &mut self,
432 mut power: i8,
433 ramp_time: RampTime,
434 ) -> Result<(), RadioError<BUS>> {
435 if self.brd_get_radio_type() == RadioType::SX1261 {
436 if power == 15 {
437 self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?;
438 } else {
439 self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?;
440 }
441
442 if power >= 14 {
443 power = 14;
444 } else if power < -17 {
445 power = -17;
446 }
447 } else {
448 // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
449 let mut tx_clamp_cfg = [0x00u8];
450 self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?;
451 tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1);
452 self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?;
453
454 self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?;
455
456 if power > 22 {
457 power = 22;
458 } else if power < -9 {
459 power = -9;
460 }
461 }
462
463 // power conversion of negative number from i8 to u8 ???
464 self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()])
465 .await?;
466 Ok(())
467 }
468
469 // Set the modulation parameters
470 pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> {
471 if self.modulation_params.is_some() {
472 let mut buffer = [0x00u8; 4];
473
474 // Since this driver only supports LoRa, ensure the packet type is set accordingly
475 self.sub_set_packet_type(PacketType::LoRa).await?;
476
477 let modulation_params = self.modulation_params.unwrap();
478 buffer[0] = modulation_params.spreading_factor.value();
479 buffer[1] = modulation_params.bandwidth.value();
480 buffer[2] = modulation_params.coding_rate.value();
481 buffer[3] = modulation_params.low_data_rate_optimize;
482
483 self.brd_write_command(OpCode::SetModulationParams, &buffer).await?;
484 Ok(())
485 } else {
486 Err(RadioError::ModulationParamsMissing)
487 }
488 }
489
490 // Set the packet parameters
491 pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> {
492 if self.packet_params.is_some() {
493 let mut buffer = [0x00u8; 6];
494
495 // Since this driver only supports LoRa, ensure the packet type is set accordingly
496 self.sub_set_packet_type(PacketType::LoRa).await?;
497
498 let packet_params = self.packet_params.unwrap();
499 buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8;
500 buffer[1] = (packet_params.preamble_length & 0xFF) as u8;
501 buffer[2] = packet_params.implicit_header as u8;
502 buffer[3] = packet_params.payload_length;
503 buffer[4] = packet_params.crc_on as u8;
504 buffer[5] = packet_params.iq_inverted as u8;
505
506 self.brd_write_command(OpCode::SetPacketParams, &buffer).await?;
507 Ok(())
508 } else {
509 Err(RadioError::PacketParamsMissing)
510 }
511 }
512
513 // Set the channel activity detection (CAD) parameters
514 // symbols number of symbols to use for CAD operations
515 // det_peak limit for detection of SNR peak used in the CAD
516 // det_min minimum symbol recognition for CAD
517 // exit_mode operation to be done at the end of CAD action
518 // timeout timeout value to abort the CAD activity
519
520 pub(super) async fn sub_set_cad_params(
521 &mut self,
522 symbols: CADSymbols,
523 det_peak: u8,
524 det_min: u8,
525 exit_mode: CADExitMode,
526 timeout: u32,
527 ) -> Result<(), RadioError<BUS>> {
528 let mut buffer = [0x00u8; 7];
529
530 buffer[0] = symbols.value();
531 buffer[1] = det_peak;
532 buffer[2] = det_min;
533 buffer[3] = exit_mode.value();
534 buffer[4] = Self::timeout_1(timeout);
535 buffer[5] = Self::timeout_2(timeout);
536 buffer[6] = Self::timeout_3(timeout);
537
538 self.brd_write_command(OpCode::SetCADParams, &buffer).await?;
539 self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
540 Ok(())
541 }
542
543 // Set the data buffer base address for transmission and reception
544 pub(super) async fn sub_set_buffer_base_address(
545 &mut self,
546 tx_base_address: u8,
547 rx_base_address: u8,
548 ) -> Result<(), RadioError<BUS>> {
549 self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address])
550 .await?;
551 Ok(())
552 }
553
554 // Get the current radio status
555 pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> {
556 let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?;
557 Ok(RadioStatus {
558 cmd_status: (status & (0x07 << 1)) >> 1,
559 chip_mode: (status & (0x07 << 4)) >> 4,
560 })
561 }
562
563 // Get the instantaneous RSSI value for the last packet received
564 pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> {
565 let mut buffer = [0x00u8];
566 self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?;
567 let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ???
568 Ok(rssi)
569 }
570
571 // Get the last received packet buffer status
572 pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> {
573 if self.packet_params.is_some() {
574 let mut status = [0x00u8; 2];
575 let mut payload_length_buffer = [0x00u8];
576
577 self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?;
578 if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header {
579 self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer)
580 .await?;
581 } else {
582 payload_length_buffer[0] = status[0];
583 }
584
585 let payload_length = payload_length_buffer[0];
586 let offset = status[1];
587
588 Ok((payload_length, offset))
589 } else {
590 Err(RadioError::PacketParamsMissing)
591 }
592 }
593
594 // Get the last received packet payload status
595 pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> {
596 let mut status = [0x00u8; 3];
597 self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?;
598
599 // check this ???
600 let rssi = ((-(status[0] as i32)) >> 1) as i8;
601 let snr = ((status[1] as i8) + 2) >> 2;
602 let signal_rssi = ((-(status[2] as i32)) >> 1) as i8;
603 let freq_error = self.frequency_error;
604
605 Ok(PacketStatus {
606 rssi,
607 snr,
608 signal_rssi,
609 freq_error,
610 })
611 }
612
613 // Get the possible system errors
614 pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> {
615 let mut errors = [0x00u8; 2];
616 self.brd_read_command(OpCode::GetErrors, &mut errors).await?;
617
618 Ok(RadioSystemError {
619 rc_64khz_calibration: (errors[1] & (1 << 0)) != 0,
620 rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0,
621 pll_calibration: (errors[1] & (1 << 2)) != 0,
622 adc_calibration: (errors[1] & (1 << 3)) != 0,
623 image_calibration: (errors[1] & (1 << 4)) != 0,
624 xosc_start: (errors[1] & (1 << 5)) != 0,
625 pll_lock: (errors[1] & (1 << 6)) != 0,
626 pa_ramp: (errors[0] & (1 << 0)) != 0,
627 })
628 }
629
630 // Clear all the errors in the device
631 pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> {
632 self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?;
633 Ok(())
634 }
635
636 // Clear the IRQs
637 pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> {
638 let mut buffer = [0x00u8, 0x00u8];
639 buffer[0] = ((irq >> 8) & 0xFF) as u8;
640 buffer[1] = (irq & 0xFF) as u8;
641 self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?;
642 Ok(())
643 }
644
645 // Utility functions
646
647 fn timeout_1(timeout: u32) -> u8 {
648 ((timeout >> 16) & 0xFF) as u8
649 }
650 fn timeout_2(timeout: u32) -> u8 {
651 ((timeout >> 8) & 0xFF) as u8
652 }
653 fn timeout_3(timeout: u32) -> u8 {
654 (timeout & 0xFF) as u8
655 }
656
657 // check this ???
658 fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 {
659 let b0 = buffer[0] as u32;
660 let b1 = buffer[1] as u32;
661 let b2 = buffer[2] as u32;
662 let b3 = buffer[3] as u32;
663 (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
664 }
665
666 fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 {
667 // Get integer and fractional parts of the frequency computed with a PLL step scaled value
668 let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED;
669 let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
670
671 (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT)
672 + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED)
673 }
674}
diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml
index 03fa79dd8..91d5ec8a3 100644
--- a/embassy-macros/Cargo.toml
+++ b/embassy-macros/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-macros" 2name = "embassy-macros"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7syn = { version = "1.0.76", features = ["full", "extra-traits"] } 8syn = { version = "1.0.76", features = ["full", "extra-traits"] }
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index d5b13204e..967ef26a7 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-net" 2name = "embassy-net"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6 7
7[package.metadata.embassy_docs] 8[package.metadata.embassy_docs]
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 58b820242..5459bc90c 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-nrf" 2name = "embassy-nrf"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[package.metadata.embassy_docs] 7[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index faa52d8fb..3e500098c 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
128 128
129 // QDEC 129 // QDEC
130 QDEC, 130 QDEC,
131
132 // PDM
133 PDM,
131} 134}
132 135
133impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 136impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index bbdf1cbe5..25c7c0d91 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
128 128
129 // QDEC 129 // QDEC
130 QDEC, 130 QDEC,
131
132 // PDM
133 PDM,
131} 134}
132 135
133impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 136impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 39a0f93f9..3b33907d2 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -158,6 +158,9 @@ embassy_hal_common::peripherals! {
158 158
159 // QDEC 159 // QDEC
160 QDEC, 160 QDEC,
161
162 // PDM
163 PDM,
161} 164}
162 165
163#[cfg(feature = "nightly")] 166#[cfg(feature = "nightly")]
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index e3d8f34a1..ae59f8b25 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -161,6 +161,9 @@ embassy_hal_common::peripherals! {
161 161
162 // TEMP 162 // TEMP
163 TEMP, 163 TEMP,
164
165 // PDM
166 PDM,
164} 167}
165 168
166#[cfg(feature = "nightly")] 169#[cfg(feature = "nightly")]
diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs
index a4be8564e..f8ed11e03 100644
--- a/embassy-nrf/src/chips/nrf9160.rs
+++ b/embassy-nrf/src/chips/nrf9160.rs
@@ -260,6 +260,9 @@ embassy_hal_common::peripherals! {
260 P0_29, 260 P0_29,
261 P0_30, 261 P0_30,
262 P0_31, 262 P0_31,
263
264 // PDM
265 PDM,
263} 266}
264 267
265impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); 268impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index d7bd21702..bc70fc2f6 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -76,6 +76,14 @@ pub mod gpio;
76pub mod gpiote; 76pub mod gpiote;
77#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] 77#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
78pub mod nvmc; 78pub mod nvmc;
79#[cfg(any(
80 feature = "nrf52810",
81 feature = "nrf52811",
82 feature = "nrf52833",
83 feature = "nrf52840",
84 feature = "_nrf9160"
85))]
86pub mod pdm;
79pub mod ppi; 87pub mod ppi;
80#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] 88#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
81pub mod pwm; 89pub mod pwm;
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs
new file mode 100644
index 000000000..b7c7022cf
--- /dev/null
+++ b/embassy-nrf/src/pdm.rs
@@ -0,0 +1,242 @@
1//! PDM mirophone interface
2
3use core::marker::PhantomData;
4use core::sync::atomic::{compiler_fence, Ordering};
5use core::task::Poll;
6
7use embassy_hal_common::drop::OnDrop;
8use embassy_hal_common::{into_ref, PeripheralRef};
9use embassy_sync::waitqueue::AtomicWaker;
10use futures::future::poll_fn;
11
12use crate::chip::EASY_DMA_SIZE;
13use crate::gpio::sealed::Pin;
14use crate::gpio::{AnyPin, Pin as GpioPin};
15use crate::interrupt::{self, InterruptExt};
16use crate::peripherals::PDM;
17use crate::{pac, Peripheral};
18
19/// PDM microphone interface
20pub struct Pdm<'d> {
21 irq: PeripheralRef<'d, interrupt::PDM>,
22 phantom: PhantomData<&'d PDM>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27#[non_exhaustive]
28pub enum Error {
29 BufferTooLong,
30 BufferZeroLength,
31 NotRunning,
32}
33
34static WAKER: AtomicWaker = AtomicWaker::new();
35static DUMMY_BUFFER: [i16; 1] = [0; 1];
36
37impl<'d> Pdm<'d> {
38 /// Create PDM driver
39 pub fn new(
40 pdm: impl Peripheral<P = PDM> + 'd,
41 irq: impl Peripheral<P = interrupt::PDM> + 'd,
42 clk: impl Peripheral<P = impl GpioPin> + 'd,
43 din: impl Peripheral<P = impl GpioPin> + 'd,
44 config: Config,
45 ) -> Self {
46 into_ref!(clk, din);
47 Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config)
48 }
49
50 fn new_inner(
51 _pdm: impl Peripheral<P = PDM> + 'd,
52 irq: impl Peripheral<P = interrupt::PDM> + 'd,
53 clk: PeripheralRef<'d, AnyPin>,
54 din: PeripheralRef<'d, AnyPin>,
55 config: Config,
56 ) -> Self {
57 into_ref!(irq);
58
59 let r = Self::regs();
60
61 // setup gpio pins
62 din.conf().write(|w| w.input().set_bit());
63 r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
64 clk.set_low();
65 clk.conf().write(|w| w.dir().output());
66 r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
67
68 // configure
69 // use default for
70 // - gain right
71 // - gain left
72 // - clk
73 // - ratio
74 r.mode.write(|w| {
75 w.edge().bit(config.edge == Edge::LeftRising);
76 w.operation().bit(config.operation_mode == OperationMode::Mono);
77 w
78 });
79 r.gainl.write(|w| w.gainl().default_gain());
80 r.gainr.write(|w| w.gainr().default_gain());
81
82 // IRQ
83 irq.disable();
84 irq.set_handler(|_| {
85 let r = Self::regs();
86 r.intenclr.write(|w| w.end().clear());
87 WAKER.wake();
88 });
89 irq.enable();
90
91 r.enable.write(|w| w.enable().set_bit());
92
93 Self {
94 phantom: PhantomData,
95 irq,
96 }
97 }
98
99 /// Start sampling microphon data into a dummy buffer
100 /// Usefull to start the microphon and keep it active between recording samples
101 pub async fn start(&mut self) {
102 let r = Self::regs();
103
104 // start dummy sampling because microphon needs some setup time
105 r.sample
106 .ptr
107 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
108 r.sample
109 .maxcnt
110 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
111
112 r.tasks_start.write(|w| w.tasks_start().set_bit());
113 }
114
115 /// Stop sampling microphon data inta a dummy buffer
116 pub async fn stop(&mut self) {
117 let r = Self::regs();
118 r.tasks_stop.write(|w| w.tasks_stop().set_bit());
119 r.events_started.reset();
120 }
121
122 pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
123 if buffer.len() == 0 {
124 return Err(Error::BufferZeroLength);
125 }
126 if buffer.len() > EASY_DMA_SIZE {
127 return Err(Error::BufferTooLong);
128 }
129
130 let r = Self::regs();
131
132 if r.events_started.read().events_started().bit_is_clear() {
133 return Err(Error::NotRunning);
134 }
135
136 let drop = OnDrop::new(move || {
137 r.intenclr.write(|w| w.end().clear());
138 r.events_stopped.reset();
139
140 // reset to dummy buffer
141 r.sample
142 .ptr
143 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
144 r.sample
145 .maxcnt
146 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
147
148 while r.events_stopped.read().bits() == 0 {}
149 });
150
151 // setup user buffer
152 let ptr = buffer.as_ptr();
153 let len = buffer.len();
154 r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
155 r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
156
157 // wait till the current sample is finished and the user buffer sample is started
158 Self::wait_for_sample().await;
159
160 // reset the buffer back to the dummy buffer
161 r.sample
162 .ptr
163 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
164 r.sample
165 .maxcnt
166 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
167
168 // wait till the user buffer is sampled
169 Self::wait_for_sample().await;
170
171 drop.defuse();
172
173 Ok(())
174 }
175
176 async fn wait_for_sample() {
177 let r = Self::regs();
178
179 r.events_end.reset();
180 r.intenset.write(|w| w.end().set());
181
182 compiler_fence(Ordering::SeqCst);
183
184 poll_fn(|cx| {
185 WAKER.register(cx.waker());
186 if r.events_end.read().events_end().bit_is_set() {
187 return Poll::Ready(());
188 }
189 Poll::Pending
190 })
191 .await;
192
193 compiler_fence(Ordering::SeqCst);
194 }
195
196 fn regs() -> &'static pac::pdm::RegisterBlock {
197 unsafe { &*pac::PDM::ptr() }
198 }
199}
200
201/// PDM microphone driver Config
202pub struct Config {
203 /// Use stero or mono operation
204 pub operation_mode: OperationMode,
205 /// On which edge the left channel should be samples
206 pub edge: Edge,
207}
208
209impl Default for Config {
210 fn default() -> Self {
211 Self {
212 operation_mode: OperationMode::Mono,
213 edge: Edge::LeftFalling,
214 }
215 }
216}
217
218#[derive(PartialEq)]
219pub enum OperationMode {
220 Mono,
221 Stereo,
222}
223#[derive(PartialEq)]
224pub enum Edge {
225 LeftRising,
226 LeftFalling,
227}
228
229impl<'d> Drop for Pdm<'d> {
230 fn drop(&mut self) {
231 let r = Self::regs();
232
233 r.tasks_stop.write(|w| w.tasks_stop().set_bit());
234
235 self.irq.disable();
236
237 r.enable.write(|w| w.enable().disabled());
238
239 r.psel.din.reset();
240 r.psel.clk.reset();
241 }
242}
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index d99599112..636d6c7a3 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -173,6 +173,61 @@ impl<'d, T: Instance> Uarte<'d, T> {
173 (self.tx, self.rx) 173 (self.tx, self.rx)
174 } 174 }
175 175
176 /// Split the Uarte into a transmitter and receiver that will
177 /// return on idle, which is determined as the time it takes
178 /// for two bytes to be received.
179 pub fn split_with_idle<U: TimerInstance>(
180 self,
181 timer: impl Peripheral<P = U> + 'd,
182 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
183 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
184 ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) {
185 let mut timer = Timer::new(timer);
186
187 into_ref!(ppi_ch1, ppi_ch2);
188
189 let r = T::regs();
190
191 // BAUDRATE register values are `baudrate * 2^32 / 16000000`
192 // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
193 //
194 // We want to stop RX if line is idle for 2 bytes worth of time
195 // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
196 // This gives us the amount of 16M ticks for 20 bits.
197 let baudrate = r.baudrate.read().baudrate().variant().unwrap();
198 let timeout = 0x8000_0000 / (baudrate as u32 / 40);
199
200 timer.set_frequency(Frequency::F16MHz);
201 timer.cc(0).write(timeout);
202 timer.cc(0).short_compare_clear();
203 timer.cc(0).short_compare_stop();
204
205 let mut ppi_ch1 = Ppi::new_one_to_two(
206 ppi_ch1.map_into(),
207 Event::from_reg(&r.events_rxdrdy),
208 timer.task_clear(),
209 timer.task_start(),
210 );
211 ppi_ch1.enable();
212
213 let mut ppi_ch2 = Ppi::new_one_to_one(
214 ppi_ch2.map_into(),
215 timer.cc(0).event_compare(),
216 Task::from_reg(&r.tasks_stoprx),
217 );
218 ppi_ch2.enable();
219
220 (
221 self.tx,
222 UarteRxWithIdle {
223 rx: self.rx,
224 timer,
225 ppi_ch1: ppi_ch1,
226 _ppi_ch2: ppi_ch2,
227 },
228 )
229 }
230
176 /// Return the endtx event for use with PPI 231 /// Return the endtx event for use with PPI
177 pub fn event_endtx(&self) -> Event { 232 pub fn event_endtx(&self) -> Event {
178 let r = T::regs(); 233 let r = T::regs();
@@ -597,236 +652,14 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> {
597 } 652 }
598} 653}
599 654
600#[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))] 655pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> {
601pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { 656 rx: UarteRx<'d, T>,
602 // Do nothing 657 timer: Timer<'d, U>,
603}
604
605#[cfg(any(feature = "_nrf9160", feature = "nrf5340"))]
606pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::RegisterBlock) {
607 use core::ops::Deref;
608
609 // Apply workaround for anomalies:
610 // - nRF9160 - anomaly 23
611 // - nRF5340 - anomaly 44
612 let rxenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x564) as *const u32;
613 let txenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x568) as *const u32;
614
615 // NB Safety: This is taken from Nordic's driver -
616 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
617 if unsafe { core::ptr::read_volatile(txenable_reg) } == 1 {
618 r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
619 }
620
621 // NB Safety: This is taken from Nordic's driver -
622 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
623 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 1 {
624 r.enable.write(|w| w.enable().enabled());
625 r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
626
627 let mut workaround_succeded = false;
628 // The UARTE is able to receive up to four bytes after the STOPRX task has been triggered.
629 // On lowest supported baud rate (1200 baud), with parity bit and two stop bits configured
630 // (resulting in 12 bits per data byte sent), this may take up to 40 ms.
631 for _ in 0..40000 {
632 // NB Safety: This is taken from Nordic's driver -
633 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
634 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 0 {
635 workaround_succeded = true;
636 break;
637 } else {
638 // Need to sleep for 1us here
639 }
640 }
641
642 if !workaround_succeded {
643 panic!("Failed to apply workaround for UART");
644 }
645
646 let errors = r.errorsrc.read().bits();
647 // NB Safety: safe to write back the bits we just read to clear them
648 r.errorsrc.write(|w| unsafe { w.bits(errors) });
649 r.enable.write(|w| w.enable().disabled());
650 }
651}
652
653pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
654 if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 {
655 // Finally we can disable, and we do so for the peripheral
656 // i.e. not just rx concerns.
657 r.enable.write(|w| w.enable().disabled());
658
659 gpio::deconfigure_pin(r.psel.rxd.read().bits());
660 gpio::deconfigure_pin(r.psel.txd.read().bits());
661 gpio::deconfigure_pin(r.psel.rts.read().bits());
662 gpio::deconfigure_pin(r.psel.cts.read().bits());
663
664 trace!("uarte tx and rx drop: done");
665 }
666}
667
668/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels,
669/// allowing it to implement the ReadUntilIdle trait.
670pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
671 tx: UarteTx<'d, U>,
672 rx: UarteRxWithIdle<'d, U, T>,
673}
674
675impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
676 /// Create a new UARTE without hardware flow control
677 pub fn new(
678 uarte: impl Peripheral<P = U> + 'd,
679 timer: impl Peripheral<P = T> + 'd,
680 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
681 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
682 irq: impl Peripheral<P = U::Interrupt> + 'd,
683 rxd: impl Peripheral<P = impl GpioPin> + 'd,
684 txd: impl Peripheral<P = impl GpioPin> + 'd,
685 config: Config,
686 ) -> Self {
687 into_ref!(rxd, txd);
688 Self::new_inner(
689 uarte,
690 timer,
691 ppi_ch1,
692 ppi_ch2,
693 irq,
694 rxd.map_into(),
695 txd.map_into(),
696 None,
697 None,
698 config,
699 )
700 }
701
702 /// Create a new UARTE with hardware flow control (RTS/CTS)
703 pub fn new_with_rtscts(
704 uarte: impl Peripheral<P = U> + 'd,
705 timer: impl Peripheral<P = T> + 'd,
706 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
707 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
708 irq: impl Peripheral<P = U::Interrupt> + 'd,
709 rxd: impl Peripheral<P = impl GpioPin> + 'd,
710 txd: impl Peripheral<P = impl GpioPin> + 'd,
711 cts: impl Peripheral<P = impl GpioPin> + 'd,
712 rts: impl Peripheral<P = impl GpioPin> + 'd,
713 config: Config,
714 ) -> Self {
715 into_ref!(rxd, txd, cts, rts);
716 Self::new_inner(
717 uarte,
718 timer,
719 ppi_ch1,
720 ppi_ch2,
721 irq,
722 rxd.map_into(),
723 txd.map_into(),
724 Some(cts.map_into()),
725 Some(rts.map_into()),
726 config,
727 )
728 }
729
730 fn new_inner(
731 uarte: impl Peripheral<P = U> + 'd,
732 timer: impl Peripheral<P = T> + 'd,
733 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
734 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
735 irq: impl Peripheral<P = U::Interrupt> + 'd,
736 rxd: PeripheralRef<'d, AnyPin>,
737 txd: PeripheralRef<'d, AnyPin>,
738 cts: Option<PeripheralRef<'d, AnyPin>>,
739 rts: Option<PeripheralRef<'d, AnyPin>>,
740 config: Config,
741 ) -> Self {
742 let baudrate = config.baudrate;
743 let (tx, rx) = Uarte::new_inner(uarte, irq, rxd, txd, cts, rts, config).split();
744
745 let mut timer = Timer::new(timer);
746
747 into_ref!(ppi_ch1, ppi_ch2);
748
749 let r = U::regs();
750
751 // BAUDRATE register values are `baudrate * 2^32 / 16000000`
752 // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
753 //
754 // We want to stop RX if line is idle for 2 bytes worth of time
755 // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
756 // This gives us the amount of 16M ticks for 20 bits.
757 let timeout = 0x8000_0000 / (baudrate as u32 / 40);
758
759 timer.set_frequency(Frequency::F16MHz);
760 timer.cc(0).write(timeout);
761 timer.cc(0).short_compare_clear();
762 timer.cc(0).short_compare_stop();
763
764 let mut ppi_ch1 = Ppi::new_one_to_two(
765 ppi_ch1.map_into(),
766 Event::from_reg(&r.events_rxdrdy),
767 timer.task_clear(),
768 timer.task_start(),
769 );
770 ppi_ch1.enable();
771
772 let mut ppi_ch2 = Ppi::new_one_to_one(
773 ppi_ch2.map_into(),
774 timer.cc(0).event_compare(),
775 Task::from_reg(&r.tasks_stoprx),
776 );
777 ppi_ch2.enable();
778
779 Self {
780 tx,
781 rx: UarteRxWithIdle {
782 rx,
783 timer,
784 ppi_ch1: ppi_ch1,
785 _ppi_ch2: ppi_ch2,
786 },
787 }
788 }
789
790 /// Split the Uarte into a transmitter and receiver, which is
791 /// particuarly useful when having two tasks correlating to
792 /// transmitting and receiving.
793 pub fn split(self) -> (UarteTx<'d, U>, UarteRxWithIdle<'d, U, T>) {
794 (self.tx, self.rx)
795 }
796
797 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
798 self.rx.read(buffer).await
799 }
800
801 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
802 self.tx.write(buffer).await
803 }
804
805 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
806 self.rx.blocking_read(buffer)
807 }
808
809 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
810 self.tx.blocking_write(buffer)
811 }
812
813 pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
814 self.rx.read_until_idle(buffer).await
815 }
816
817 pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
818 self.rx.blocking_read_until_idle(buffer)
819 }
820}
821
822pub struct UarteRxWithIdle<'d, U: Instance, T: TimerInstance> {
823 rx: UarteRx<'d, U>,
824 timer: Timer<'d, T>,
825 ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, 658 ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
826 _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, 659 _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
827} 660}
828 661
829impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> { 662impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
830 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 663 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
831 self.ppi_ch1.disable(); 664 self.ppi_ch1.disable();
832 self.rx.read(buffer).await 665 self.rx.read(buffer).await
@@ -848,8 +681,8 @@ impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
848 let ptr = buffer.as_ptr(); 681 let ptr = buffer.as_ptr();
849 let len = buffer.len(); 682 let len = buffer.len();
850 683
851 let r = U::regs(); 684 let r = T::regs();
852 let s = U::state(); 685 let s = T::state();
853 686
854 self.ppi_ch1.enable(); 687 self.ppi_ch1.enable();
855 688
@@ -904,7 +737,7 @@ impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
904 let ptr = buffer.as_ptr(); 737 let ptr = buffer.as_ptr();
905 let len = buffer.len(); 738 let len = buffer.len();
906 739
907 let r = U::regs(); 740 let r = T::regs();
908 741
909 self.ppi_ch1.enable(); 742 self.ppi_ch1.enable();
910 743
@@ -929,6 +762,75 @@ impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
929 Ok(n) 762 Ok(n)
930 } 763 }
931} 764}
765
766#[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))]
767pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) {
768 // Do nothing
769}
770
771#[cfg(any(feature = "_nrf9160", feature = "nrf5340"))]
772pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::RegisterBlock) {
773 use core::ops::Deref;
774
775 // Apply workaround for anomalies:
776 // - nRF9160 - anomaly 23
777 // - nRF5340 - anomaly 44
778 let rxenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x564) as *const u32;
779 let txenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x568) as *const u32;
780
781 // NB Safety: This is taken from Nordic's driver -
782 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
783 if unsafe { core::ptr::read_volatile(txenable_reg) } == 1 {
784 r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
785 }
786
787 // NB Safety: This is taken from Nordic's driver -
788 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
789 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 1 {
790 r.enable.write(|w| w.enable().enabled());
791 r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
792
793 let mut workaround_succeded = false;
794 // The UARTE is able to receive up to four bytes after the STOPRX task has been triggered.
795 // On lowest supported baud rate (1200 baud), with parity bit and two stop bits configured
796 // (resulting in 12 bits per data byte sent), this may take up to 40 ms.
797 for _ in 0..40000 {
798 // NB Safety: This is taken from Nordic's driver -
799 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
800 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 0 {
801 workaround_succeded = true;
802 break;
803 } else {
804 // Need to sleep for 1us here
805 }
806 }
807
808 if !workaround_succeded {
809 panic!("Failed to apply workaround for UART");
810 }
811
812 let errors = r.errorsrc.read().bits();
813 // NB Safety: safe to write back the bits we just read to clear them
814 r.errorsrc.write(|w| unsafe { w.bits(errors) });
815 r.enable.write(|w| w.enable().disabled());
816 }
817}
818
819pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
820 if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 {
821 // Finally we can disable, and we do so for the peripheral
822 // i.e. not just rx concerns.
823 r.enable.write(|w| w.enable().disabled());
824
825 gpio::deconfigure_pin(r.psel.rxd.read().bits());
826 gpio::deconfigure_pin(r.psel.txd.read().bits());
827 gpio::deconfigure_pin(r.psel.rts.read().bits());
828 gpio::deconfigure_pin(r.psel.cts.read().bits());
829
830 trace!("uarte tx and rx drop: done");
831 }
832}
833
932pub(crate) mod sealed { 834pub(crate) mod sealed {
933 use core::sync::atomic::AtomicU8; 835 use core::sync::atomic::AtomicU8;
934 836
@@ -1006,18 +908,6 @@ mod eh02 {
1006 Ok(()) 908 Ok(())
1007 } 909 }
1008 } 910 }
1009
1010 impl<'d, U: Instance, T: TimerInstance> embedded_hal_02::blocking::serial::Write<u8> for UarteWithIdle<'d, U, T> {
1011 type Error = Error;
1012
1013 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
1014 self.blocking_write(buffer)
1015 }
1016
1017 fn bflush(&mut self) -> Result<(), Self::Error> {
1018 Ok(())
1019 }
1020 }
1021} 911}
1022 912
1023#[cfg(feature = "unstable-traits")] 913#[cfg(feature = "unstable-traits")]
@@ -1067,10 +957,6 @@ mod eh1 {
1067 impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { 957 impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> {
1068 type Error = Error; 958 type Error = Error;
1069 } 959 }
1070
1071 impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType for UarteWithIdle<'d, U, T> {
1072 type Error = Error;
1073 }
1074} 960}
1075 961
1076#[cfg(all( 962#[cfg(all(
@@ -1126,26 +1012,4 @@ mod eha {
1126 self.read(buffer) 1012 self.read(buffer)
1127 } 1013 }
1128 } 1014 }
1129
1130 impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> {
1131 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1132
1133 fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
1134 self.read(buffer)
1135 }
1136 }
1137
1138 impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> {
1139 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1140
1141 fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
1142 self.write(buffer)
1143 }
1144
1145 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1146
1147 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
1148 async move { Ok(()) }
1149 }
1150 }
1151} 1015}
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 3aca5dbb4..c56858415 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-rp" 2name = "embassy-rp"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[package.metadata.embassy_docs] 7[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/"
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index a28bae96b..f79f592b4 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -599,12 +599,12 @@ pub(crate) mod sealed {
599 fn pin_bank(&self) -> u8; 599 fn pin_bank(&self) -> u8;
600 600
601 #[inline] 601 #[inline]
602 fn pin(&self) -> u8 { 602 fn _pin(&self) -> u8 {
603 self.pin_bank() & 0x1f 603 self.pin_bank() & 0x1f
604 } 604 }
605 605
606 #[inline] 606 #[inline]
607 fn bank(&self) -> Bank { 607 fn _bank(&self) -> Bank {
608 if self.pin_bank() & 0x20 == 0 { 608 if self.pin_bank() & 0x20 == 0 {
609 Bank::Bank0 609 Bank::Bank0
610 } else { 610 } else {
@@ -613,35 +613,35 @@ pub(crate) mod sealed {
613 } 613 }
614 614
615 fn io(&self) -> pac::io::Gpio { 615 fn io(&self) -> pac::io::Gpio {
616 let block = match self.bank() { 616 let block = match self._bank() {
617 Bank::Bank0 => crate::pac::IO_BANK0, 617 Bank::Bank0 => crate::pac::IO_BANK0,
618 Bank::Qspi => crate::pac::IO_QSPI, 618 Bank::Qspi => crate::pac::IO_QSPI,
619 }; 619 };
620 block.gpio(self.pin() as _) 620 block.gpio(self._pin() as _)
621 } 621 }
622 622
623 fn pad_ctrl(&self) -> Reg<pac::pads::regs::GpioCtrl, RW> { 623 fn pad_ctrl(&self) -> Reg<pac::pads::regs::GpioCtrl, RW> {
624 let block = match self.bank() { 624 let block = match self._bank() {
625 Bank::Bank0 => crate::pac::PADS_BANK0, 625 Bank::Bank0 => crate::pac::PADS_BANK0,
626 Bank::Qspi => crate::pac::PADS_QSPI, 626 Bank::Qspi => crate::pac::PADS_QSPI,
627 }; 627 };
628 block.gpio(self.pin() as _) 628 block.gpio(self._pin() as _)
629 } 629 }
630 630
631 fn sio_out(&self) -> pac::sio::Gpio { 631 fn sio_out(&self) -> pac::sio::Gpio {
632 SIO.gpio_out(self.bank() as _) 632 SIO.gpio_out(self._bank() as _)
633 } 633 }
634 634
635 fn sio_oe(&self) -> pac::sio::Gpio { 635 fn sio_oe(&self) -> pac::sio::Gpio {
636 SIO.gpio_oe(self.bank() as _) 636 SIO.gpio_oe(self._bank() as _)
637 } 637 }
638 638
639 fn sio_in(&self) -> Reg<u32, RW> { 639 fn sio_in(&self) -> Reg<u32, RW> {
640 SIO.gpio_in(self.bank() as _) 640 SIO.gpio_in(self._bank() as _)
641 } 641 }
642 642
643 fn int_proc(&self) -> pac::io::Int { 643 fn int_proc(&self) -> pac::io::Int {
644 let io_block = match self.bank() { 644 let io_block = match self._bank() {
645 Bank::Bank0 => crate::pac::IO_BANK0, 645 Bank::Bank0 => crate::pac::IO_BANK0,
646 Bank::Qspi => crate::pac::IO_QSPI, 646 Bank::Qspi => crate::pac::IO_QSPI,
647 }; 647 };
@@ -658,6 +658,18 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
658 pin_bank: self.pin_bank(), 658 pin_bank: self.pin_bank(),
659 } 659 }
660 } 660 }
661
662 /// Returns the pin number within a bank
663 #[inline]
664 fn pin(&self) -> u8 {
665 self._pin()
666 }
667
668 /// Returns the bank of this pin
669 #[inline]
670 fn bank(&self) -> Bank {
671 self._bank()
672 }
661} 673}
662 674
663pub struct AnyPin { 675pub struct AnyPin {
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
index 52f910cef..d6742f6a6 100644
--- a/embassy-rp/src/i2c.rs
+++ b/embassy-rp/src/i2c.rs
@@ -1,9 +1,12 @@
1use core::future;
1use core::marker::PhantomData; 2use core::marker::PhantomData;
3use core::task::Poll;
2 4
5use embassy_cortex_m::interrupt::InterruptExt;
3use embassy_hal_common::{into_ref, PeripheralRef}; 6use embassy_hal_common::{into_ref, PeripheralRef};
7use embassy_sync::waitqueue::AtomicWaker;
4use pac::i2c; 8use pac::i2c;
5 9
6use crate::dma::AnyChannel;
7use crate::gpio::sealed::Pin; 10use crate::gpio::sealed::Pin;
8use crate::gpio::AnyPin; 11use crate::gpio::AnyPin;
9use crate::{pac, peripherals, Peripheral}; 12use crate::{pac, peripherals, Peripheral};
@@ -52,31 +55,276 @@ impl Default for Config {
52const FIFO_SIZE: u8 = 16; 55const FIFO_SIZE: u8 = 16;
53 56
54pub struct I2c<'d, T: Instance, M: Mode> { 57pub struct I2c<'d, T: Instance, M: Mode> {
55 _tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
56 _rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
57 _dma_buf: [u16; 256],
58 phantom: PhantomData<(&'d mut T, M)>, 58 phantom: PhantomData<(&'d mut T, M)>,
59} 59}
60 60
61impl<'d, T: Instance> I2c<'d, T, Blocking> { 61impl<'d, T: Instance> I2c<'d, T, Blocking> {
62 pub fn new_blocking( 62 pub fn new_blocking(
63 _peri: impl Peripheral<P = T> + 'd, 63 peri: impl Peripheral<P = T> + 'd,
64 scl: impl Peripheral<P = impl SclPin<T>> + 'd, 64 scl: impl Peripheral<P = impl SclPin<T>> + 'd,
65 sda: impl Peripheral<P = impl SdaPin<T>> + 'd, 65 sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
66 config: Config, 66 config: Config,
67 ) -> Self { 67 ) -> Self {
68 into_ref!(scl, sda); 68 into_ref!(scl, sda);
69 Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config) 69 Self::new_inner(peri, scl.map_into(), sda.map_into(), config)
70 } 70 }
71} 71}
72 72
73impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { 73impl<'d, T: Instance> I2c<'d, T, Async> {
74 pub fn new_async(
75 peri: impl Peripheral<P = T> + 'd,
76 scl: impl Peripheral<P = impl SclPin<T>> + 'd,
77 sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
78 irq: impl Peripheral<P = T::Interrupt> + 'd,
79 config: Config,
80 ) -> Self {
81 into_ref!(scl, sda, irq);
82
83 let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config);
84
85 irq.set_handler(Self::on_interrupt);
86 unsafe {
87 let i2c = T::regs();
88
89 // mask everything initially
90 i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
91 }
92 irq.unpend();
93 debug_assert!(!irq.is_pending());
94 irq.enable();
95
96 i2c
97 }
98
99 /// Calls `f` to check if we are ready or not.
100 /// If not, `g` is called once the waker is set (to eg enable the required interrupts).
101 async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U
102 where
103 F: FnMut(&mut Self) -> Poll<U>,
104 G: FnMut(&mut Self),
105 {
106 future::poll_fn(|cx| {
107 let r = f(self);
108
109 if r.is_pending() {
110 T::waker().register(cx.waker());
111 g(self);
112 }
113 r
114 })
115 .await
116 }
117
118 // Mask interrupts and wake any task waiting for this interrupt
119 unsafe fn on_interrupt(_: *mut ()) {
120 let i2c = T::regs();
121 i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default());
122
123 T::waker().wake();
124 }
125
126 async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
127 if buffer.is_empty() {
128 return Err(Error::InvalidReadBufferLength);
129 }
130
131 let p = T::regs();
132
133 let mut remaining = buffer.len();
134 let mut remaining_queue = buffer.len();
135
136 let mut abort_reason = Ok(());
137
138 while remaining > 0 {
139 // Waggle SCK - basically the same as write
140 let tx_fifo_space = Self::tx_fifo_capacity();
141 let mut batch = 0;
142
143 debug_assert!(remaining_queue > 0);
144
145 for _ in 0..remaining_queue.min(tx_fifo_space as usize) {
146 remaining_queue -= 1;
147 let last = remaining_queue == 0;
148 batch += 1;
149
150 unsafe {
151 p.ic_data_cmd().write(|w| {
152 w.set_restart(restart && remaining_queue == buffer.len() - 1);
153 w.set_stop(last && send_stop);
154 w.set_cmd(true);
155 });
156 }
157 }
158
159 // We've either run out of txfifo or just plain finished setting up
160 // the clocks for the message - either way we need to wait for rx
161 // data.
162
163 debug_assert!(batch > 0);
164 let res = self
165 .wait_on(
166 |me| {
167 let rxfifo = Self::rx_fifo_len();
168 if let Err(abort_reason) = me.read_and_clear_abort_reason() {
169 Poll::Ready(Err(abort_reason))
170 } else if rxfifo >= batch {
171 Poll::Ready(Ok(rxfifo))
172 } else {
173 Poll::Pending
174 }
175 },
176 |_me| unsafe {
177 // Set the read threshold to the number of bytes we're
178 // expecting so we don't get spurious interrupts.
179 p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1));
180
181 p.ic_intr_mask().modify(|w| {
182 w.set_m_rx_full(true);
183 w.set_m_tx_abrt(true);
184 });
185 },
186 )
187 .await;
188
189 match res {
190 Err(reason) => {
191 abort_reason = Err(reason);
192 break;
193 }
194 Ok(rxfifo) => {
195 // Fetch things from rx fifo. We're assuming we're the only
196 // rxfifo reader, so nothing else can take things from it.
197 let rxbytes = (rxfifo as usize).min(remaining);
198 let received = buffer.len() - remaining;
199 for b in &mut buffer[received..received + rxbytes] {
200 *b = unsafe { p.ic_data_cmd().read().dat() };
201 }
202 remaining -= rxbytes;
203 }
204 };
205 }
206
207 self.wait_stop_det(abort_reason, send_stop).await
208 }
209
210 async fn write_async_internal(
211 &mut self,
212 bytes: impl IntoIterator<Item = u8>,
213 send_stop: bool,
214 ) -> Result<(), Error> {
215 let p = T::regs();
216
217 let mut bytes = bytes.into_iter().peekable();
218
219 let res = 'xmit: loop {
220 let tx_fifo_space = Self::tx_fifo_capacity();
221
222 for _ in 0..tx_fifo_space {
223 if let Some(byte) = bytes.next() {
224 let last = bytes.peek().is_none();
225
226 unsafe {
227 p.ic_data_cmd().write(|w| {
228 w.set_stop(last && send_stop);
229 w.set_cmd(false);
230 w.set_dat(byte);
231 });
232 }
233 } else {
234 break 'xmit Ok(());
235 }
236 }
237
238 let res = self
239 .wait_on(
240 |me| {
241 if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() {
242 Poll::Ready(abort_reason)
243 } else if !Self::tx_fifo_full() {
244 // resume if there's any space free in the tx fifo
245 Poll::Ready(Ok(()))
246 } else {
247 Poll::Pending
248 }
249 },
250 |_me| unsafe {
251 // Set tx "free" threshold a little high so that we get
252 // woken before the fifo completely drains to minimize
253 // transfer stalls.
254 p.ic_tx_tl().write(|w| w.set_tx_tl(1));
255
256 p.ic_intr_mask().modify(|w| {
257 w.set_m_tx_empty(true);
258 w.set_m_tx_abrt(true);
259 })
260 },
261 )
262 .await;
263 if res.is_err() {
264 break res;
265 }
266 };
267
268 self.wait_stop_det(res, send_stop).await
269 }
270
271 /// Helper to wait for a stop bit, for both tx and rx. If we had an abort,
272 /// then we'll get a hardware-generated stop, otherwise wait for a stop if
273 /// we're expecting it.
274 ///
275 /// Also handles an abort which arises while processing the tx fifo.
276 async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> {
277 if had_abort.is_err() || do_stop {
278 let p = T::regs();
279
280 let had_abort2 = self
281 .wait_on(
282 |me| unsafe {
283 // We could see an abort while processing fifo backlog,
284 // so handle it here.
285 let abort = me.read_and_clear_abort_reason();
286 if had_abort.is_ok() && abort.is_err() {
287 Poll::Ready(abort)
288 } else if p.ic_raw_intr_stat().read().stop_det() {
289 Poll::Ready(Ok(()))
290 } else {
291 Poll::Pending
292 }
293 },
294 |_me| unsafe {
295 p.ic_intr_mask().modify(|w| {
296 w.set_m_stop_det(true);
297 w.set_m_tx_abrt(true);
298 });
299 },
300 )
301 .await;
302 unsafe {
303 p.ic_clr_stop_det().read();
304 }
305
306 had_abort.and(had_abort2)
307 } else {
308 had_abort
309 }
310 }
311
312 pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> {
313 Self::setup(addr)?;
314 self.read_async_internal(buffer, false, true).await
315 }
316
317 pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator<Item = u8>) -> Result<(), Error> {
318 Self::setup(addr)?;
319 self.write_async_internal(bytes, true).await
320 }
321}
322
323impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
74 fn new_inner( 324 fn new_inner(
75 _peri: impl Peripheral<P = T> + 'd, 325 _peri: impl Peripheral<P = T> + 'd,
76 scl: PeripheralRef<'d, AnyPin>, 326 scl: PeripheralRef<'d, AnyPin>,
77 sda: PeripheralRef<'d, AnyPin>, 327 sda: PeripheralRef<'d, AnyPin>,
78 _tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
79 _rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
80 config: Config, 328 config: Config,
81 ) -> Self { 329 ) -> Self {
82 into_ref!(_peri); 330 into_ref!(_peri);
@@ -87,6 +335,10 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
87 let p = T::regs(); 335 let p = T::regs();
88 336
89 unsafe { 337 unsafe {
338 let reset = T::reset();
339 crate::reset::reset(reset);
340 crate::reset::unreset_wait(reset);
341
90 p.ic_enable().write(|w| w.set_enable(false)); 342 p.ic_enable().write(|w| w.set_enable(false));
91 343
92 // Select controller mode & speed 344 // Select controller mode & speed
@@ -172,12 +424,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
172 p.ic_enable().write(|w| w.set_enable(true)); 424 p.ic_enable().write(|w| w.set_enable(true));
173 } 425 }
174 426
175 Self { 427 Self { phantom: PhantomData }
176 _tx_dma,
177 _rx_dma,
178 _dma_buf: [0; 256],
179 phantom: PhantomData,
180 }
181 } 428 }
182 429
183 fn setup(addr: u16) -> Result<(), Error> { 430 fn setup(addr: u16) -> Result<(), Error> {
@@ -198,6 +445,23 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
198 Ok(()) 445 Ok(())
199 } 446 }
200 447
448 #[inline]
449 fn tx_fifo_full() -> bool {
450 Self::tx_fifo_capacity() == 0
451 }
452
453 #[inline]
454 fn tx_fifo_capacity() -> u8 {
455 let p = T::regs();
456 unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() }
457 }
458
459 #[inline]
460 fn rx_fifo_len() -> u8 {
461 let p = T::regs();
462 unsafe { p.ic_rxflr().read().rxflr() }
463 }
464
201 fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { 465 fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
202 let p = T::regs(); 466 let p = T::regs();
203 unsafe { 467 unsafe {
@@ -240,7 +504,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
240 // NOTE(unsafe) We have &mut self 504 // NOTE(unsafe) We have &mut self
241 unsafe { 505 unsafe {
242 // wait until there is space in the FIFO to write the next byte 506 // wait until there is space in the FIFO to write the next byte
243 while p.ic_txflr().read().txflr() == FIFO_SIZE {} 507 while Self::tx_fifo_full() {}
244 508
245 p.ic_data_cmd().write(|w| { 509 p.ic_data_cmd().write(|w| {
246 w.set_restart(restart && first); 510 w.set_restart(restart && first);
@@ -249,7 +513,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
249 w.set_cmd(true); 513 w.set_cmd(true);
250 }); 514 });
251 515
252 while p.ic_rxflr().read().rxflr() == 0 { 516 while Self::rx_fifo_len() == 0 {
253 self.read_and_clear_abort_reason()?; 517 self.read_and_clear_abort_reason()?;
254 } 518 }
255 519
@@ -451,6 +715,91 @@ mod eh1 {
451 } 715 }
452 } 716 }
453} 717}
718#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
719mod nightly {
720 use core::future::Future;
721
722 use embedded_hal_1::i2c::Operation;
723 use embedded_hal_async::i2c::AddressMode;
724
725 use super::*;
726
727 impl<'d, A, T> embedded_hal_async::i2c::I2c<A> for I2c<'d, T, Async>
728 where
729 A: AddressMode + Into<u16> + 'static,
730 T: Instance + 'd,
731 {
732 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
733 where Self: 'a;
734 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
735 where Self: 'a;
736 type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
737 where Self: 'a;
738 type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Error>> + 'a
739 where Self: 'a, 'b: 'a;
740
741 fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
742 let addr: u16 = address.into();
743
744 async move {
745 Self::setup(addr)?;
746 self.read_async_internal(buffer, false, true).await
747 }
748 }
749
750 fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> {
751 let addr: u16 = address.into();
752
753 async move {
754 Self::setup(addr)?;
755 self.write_async_internal(write.iter().copied(), true).await
756 }
757 }
758
759 fn write_read<'a>(
760 &'a mut self,
761 address: A,
762 bytes: &'a [u8],
763 buffer: &'a mut [u8],
764 ) -> Self::WriteReadFuture<'a> {
765 let addr: u16 = address.into();
766
767 async move {
768 Self::setup(addr)?;
769 self.write_async_internal(bytes.iter().cloned(), false).await?;
770 self.read_async_internal(buffer, false, true).await
771 }
772 }
773
774 fn transaction<'a, 'b>(
775 &'a mut self,
776 address: A,
777 operations: &'a mut [Operation<'b>],
778 ) -> Self::TransactionFuture<'a, 'b> {
779 let addr: u16 = address.into();
780
781 async move {
782 let mut iterator = operations.iter_mut();
783
784 while let Some(op) = iterator.next() {
785 let last = iterator.len() == 0;
786
787 match op {
788 Operation::Read(buffer) => {
789 Self::setup(addr)?;
790 self.read_async_internal(buffer, false, last).await?;
791 }
792 Operation::Write(buffer) => {
793 Self::setup(addr)?;
794 self.write_async_internal(buffer.into_iter().cloned(), last).await?;
795 }
796 }
797 }
798 Ok(())
799 }
800 }
801 }
802}
454 803
455fn i2c_reserved_addr(addr: u16) -> bool { 804fn i2c_reserved_addr(addr: u16) -> bool {
456 (addr & 0x78) == 0 || (addr & 0x78) == 0x78 805 (addr & 0x78) == 0 || (addr & 0x78) == 0x78
@@ -458,6 +807,7 @@ fn i2c_reserved_addr(addr: u16) -> bool {
458 807
459mod sealed { 808mod sealed {
460 use embassy_cortex_m::interrupt::Interrupt; 809 use embassy_cortex_m::interrupt::Interrupt;
810 use embassy_sync::waitqueue::AtomicWaker;
461 811
462 pub trait Instance { 812 pub trait Instance {
463 const TX_DREQ: u8; 813 const TX_DREQ: u8;
@@ -466,6 +816,8 @@ mod sealed {
466 type Interrupt: Interrupt; 816 type Interrupt: Interrupt;
467 817
468 fn regs() -> crate::pac::i2c::I2c; 818 fn regs() -> crate::pac::i2c::I2c;
819 fn reset() -> crate::pac::resets::regs::Peripherals;
820 fn waker() -> &'static AtomicWaker;
469 } 821 }
470 822
471 pub trait Mode {} 823 pub trait Mode {}
@@ -492,23 +844,38 @@ impl_mode!(Async);
492pub trait Instance: sealed::Instance {} 844pub trait Instance: sealed::Instance {}
493 845
494macro_rules! impl_instance { 846macro_rules! impl_instance {
495 ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { 847 ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => {
496 impl sealed::Instance for peripherals::$type { 848 impl sealed::Instance for peripherals::$type {
497 const TX_DREQ: u8 = $tx_dreq; 849 const TX_DREQ: u8 = $tx_dreq;
498 const RX_DREQ: u8 = $rx_dreq; 850 const RX_DREQ: u8 = $rx_dreq;
499 851
500 type Interrupt = crate::interrupt::$irq; 852 type Interrupt = crate::interrupt::$irq;
501 853
854 #[inline]
502 fn regs() -> pac::i2c::I2c { 855 fn regs() -> pac::i2c::I2c {
503 pac::$type 856 pac::$type
504 } 857 }
858
859 #[inline]
860 fn reset() -> pac::resets::regs::Peripherals {
861 let mut ret = pac::resets::regs::Peripherals::default();
862 ret.$reset(true);
863 ret
864 }
865
866 #[inline]
867 fn waker() -> &'static AtomicWaker {
868 static WAKER: AtomicWaker = AtomicWaker::new();
869
870 &WAKER
871 }
505 } 872 }
506 impl Instance for peripherals::$type {} 873 impl Instance for peripherals::$type {}
507 }; 874 };
508} 875}
509 876
510impl_instance!(I2C0, I2C0_IRQ, 32, 33); 877impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33);
511impl_instance!(I2C1, I2C1_IRQ, 34, 35); 878impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35);
512 879
513pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {} 880pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {}
514pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {} 881pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {}
diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs
index 7f3bbbe73..e4b6f0b1d 100644
--- a/embassy-rp/src/rtc/mod.rs
+++ b/embassy-rp/src/rtc/mod.rs
@@ -145,6 +145,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
145 filter.write_setup_1(w); 145 filter.write_setup_1(w);
146 }); 146 });
147 147
148 self.inner.regs().inte().modify(|w| w.set_rtc(true));
149
148 // Set the enable bit and check if it is set 150 // Set the enable bit and check if it is set
149 self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); 151 self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true));
150 while !self.inner.regs().irq_setup_0().read().match_active() { 152 while !self.inner.regs().irq_setup_0().read().match_active() {
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs
index 0a904aab3..6dc90b98e 100644
--- a/embassy-rp/src/usb.rs
+++ b/embassy-rp/src/usb.rs
@@ -522,7 +522,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
522 trace!("wait_enabled IN WAITING"); 522 trace!("wait_enabled IN WAITING");
523 let index = self.info.addr.index(); 523 let index = self.info.addr.index();
524 poll_fn(|cx| { 524 poll_fn(|cx| {
525 EP_OUT_WAKERS[index].register(cx.waker()); 525 EP_IN_WAKERS[index].register(cx.waker());
526 let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() }; 526 let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() };
527 if val.enable() { 527 if val.enable() {
528 Poll::Ready(()) 528 Poll::Ready(())
@@ -811,8 +811,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
811 async move { 811 async move {
812 trace!("control: accept"); 812 trace!("control: accept");
813 813
814 let bufcontrol = T::dpram().ep_in_buffer_control(0);
814 unsafe { 815 unsafe {
815 let bufcontrol = T::dpram().ep_in_buffer_control(0);
816 bufcontrol.write(|w| { 816 bufcontrol.write(|w| {
817 w.set_length(0, 0); 817 w.set_length(0, 0);
818 w.set_pid(0, true); 818 w.set_pid(0, true);
@@ -826,6 +826,18 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
826 w.set_available(0, true); 826 w.set_available(0, true);
827 }); 827 });
828 } 828 }
829
830 // wait for completion before returning, needed so
831 // set_address() doesn't happen early.
832 poll_fn(|cx| {
833 EP_IN_WAKERS[0].register(cx.waker());
834 if unsafe { bufcontrol.read().available(0) } {
835 Poll::Pending
836 } else {
837 Poll::Ready(())
838 }
839 })
840 .await;
829 } 841 }
830 } 842 }
831 843
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 9566dbcaf..9194ae788 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-stm32" 2name = "embassy-stm32"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[package.metadata.embassy_docs] 7[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
@@ -44,6 +45,7 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona
44embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 45embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
45embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} 46embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
46embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} 47embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true}
48embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true}
47 49
48embedded-storage = "0.3.0" 50embedded-storage = "0.3.0"
49embedded-storage-async = { version = "0.3.0", optional = true } 51embedded-storage-async = { version = "0.3.0", optional = true }
@@ -73,7 +75,7 @@ quote = "1.0.15"
73stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} 75stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
74 76
75[features] 77[features]
76defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] 78defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"]
77sdmmc-rs = ["embedded-sdmmc"] 79sdmmc-rs = ["embedded-sdmmc"]
78net = ["embassy-net" ] 80net = ["embassy-net" ]
79memory-x = ["stm32-metapac/memory-x"] 81memory-x = ["stm32-metapac/memory-x"]
@@ -102,7 +104,7 @@ unstable-pac = []
102 104
103# Implement embedded-hal 1.0 alpha traits. 105# Implement embedded-hal 1.0 alpha traits.
104# Implement embedded-hal-async traits if `nightly` is set as well. 106# Implement embedded-hal-async traits if `nightly` is set as well.
105unstable-traits = ["embedded-hal-1"] 107unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"]
106 108
107# BEGIN GENERATED FEATURES 109# BEGIN GENERATED FEATURES
108# Generated by stm32-gen-features. DO NOT EDIT. 110# Generated by stm32-gen-features. DO NOT EDIT.
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 8da13073e..0eb4eba73 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -28,15 +28,20 @@ pub(crate) mod sealed {
28 pub trait AdcPin<T: Instance> { 28 pub trait AdcPin<T: Instance> {
29 fn channel(&self) -> u8; 29 fn channel(&self) -> u8;
30 } 30 }
31
32 pub trait InternalChannel<T> {
33 fn channel(&self) -> u8;
34 }
31} 35}
32 36
33#[cfg(not(adc_f1))] 37#[cfg(not(any(adc_f1, adc_v2)))]
34pub trait Instance: sealed::Instance + 'static {} 38pub trait Instance: sealed::Instance + 'static {}
35#[cfg(adc_f1)] 39#[cfg(any(adc_f1, adc_v2))]
36pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} 40pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {}
37#[cfg(all(not(adc_f1), not(adc_v1)))] 41#[cfg(all(not(adc_f1), not(adc_v1)))]
38pub trait Common: sealed::Common + 'static {} 42pub trait Common: sealed::Common + 'static {}
39pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} 43pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
44pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
40 45
41#[cfg(not(stm32h7))] 46#[cfg(not(stm32h7))]
42foreach_peripheral!( 47foreach_peripheral!(
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index 25b7ba967..4fe4ad1f0 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -3,7 +3,9 @@ use core::marker::PhantomData;
3use embassy_hal_common::into_ref; 3use embassy_hal_common::into_ref;
4use embedded_hal_02::blocking::delay::DelayUs; 4use embedded_hal_02::blocking::delay::DelayUs;
5 5
6use super::InternalChannel;
6use crate::adc::{AdcPin, Instance}; 7use crate::adc::{AdcPin, Instance};
8use crate::peripherals::ADC1;
7use crate::time::Hertz; 9use crate::time::Hertz;
8use crate::Peripheral; 10use crate::Peripheral;
9 11
@@ -12,20 +14,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
12/// VREF voltage used for factory calibration of VREFINTCAL register. 14/// VREF voltage used for factory calibration of VREFINTCAL register.
13pub const VREF_CALIB_MV: u32 = 3300; 15pub const VREF_CALIB_MV: u32 = 3300;
14 16
15#[cfg(not(any(rcc_f4, rcc_f7)))] 17/// ADC turn-on time
16fn enable() { 18pub const ADC_POWERUP_TIME_US: u32 = 3;
17 todo!()
18}
19
20#[cfg(any(rcc_f4, rcc_f7))]
21fn enable() {
22 critical_section::with(|_| unsafe {
23 // TODO do not enable all adc clocks if not needed
24 crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true));
25 crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true));
26 crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true));
27 });
28}
29 19
30pub enum Resolution { 20pub enum Resolution {
31 TwelveBit, 21 TwelveBit,
@@ -61,24 +51,53 @@ impl Resolution {
61} 51}
62 52
63pub struct VrefInt; 53pub struct VrefInt;
64impl<T: Instance> AdcPin<T> for VrefInt {} 54impl InternalChannel<ADC1> for VrefInt {}
65impl<T: Instance> super::sealed::AdcPin<T> for VrefInt { 55impl super::sealed::InternalChannel<ADC1> for VrefInt {
66 fn channel(&self) -> u8 { 56 fn channel(&self) -> u8 {
67 17 57 17
68 } 58 }
69} 59}
70 60
61impl VrefInt {
62 /// Time needed for internal voltage reference to stabilize
63 pub fn start_time_us() -> u32 {
64 10
65 }
66}
67
71pub struct Temperature; 68pub struct Temperature;
72impl<T: Instance> AdcPin<T> for Temperature {} 69impl InternalChannel<ADC1> for Temperature {}
73impl<T: Instance> super::sealed::AdcPin<T> for Temperature { 70impl super::sealed::InternalChannel<ADC1> for Temperature {
74 fn channel(&self) -> u8 { 71 fn channel(&self) -> u8 {
75 16 72 cfg_if::cfg_if! {
73 if #[cfg(any(stm32f40, stm32f41))] {
74 16
75 } else {
76 18
77 }
78 }
79 }
80}
81
82impl Temperature {
83 /// Converts temperature sensor reading in millivolts to degrees celcius
84 pub fn to_celcius(sample_mv: u16) -> f32 {
85 // From 6.3.22 Temperature sensor characteristics
86 const V25: i32 = 760; // mV
87 const AVG_SLOPE: f32 = 2.5; // mV/C
88
89 (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0
90 }
91
92 /// Time needed for temperature sensor readings to stabilize
93 pub fn start_time_us() -> u32 {
94 10
76 } 95 }
77} 96}
78 97
79pub struct Vbat; 98pub struct Vbat;
80impl<T: Instance> AdcPin<T> for Vbat {} 99impl InternalChannel<ADC1> for Vbat {}
81impl<T: Instance> super::sealed::AdcPin<T> for Vbat { 100impl super::sealed::InternalChannel<ADC1> for Vbat {
82 fn channel(&self) -> u8 { 101 fn channel(&self) -> u8 {
83 18 102 18
84 } 103 }
@@ -164,21 +183,19 @@ where
164{ 183{
165 pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { 184 pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
166 into_ref!(_peri); 185 into_ref!(_peri);
167 enable(); 186 T::enable();
187 T::reset();
168 188
169 let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) }; 189 let presc = Prescaler::from_pclk2(T::frequency());
170 unsafe { 190 unsafe {
171 T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); 191 T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre()));
172 }
173 192
174 unsafe {
175 // disable before config is set
176 T::regs().cr2().modify(|reg| { 193 T::regs().cr2().modify(|reg| {
177 reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); 194 reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
178 }); 195 });
179 } 196 }
180 197
181 delay.delay_us(20); // TODO? 198 delay.delay_us(ADC_POWERUP_TIME_US);
182 199
183 Self { 200 Self {
184 sample_time: Default::default(), 201 sample_time: Default::default(),
@@ -208,6 +225,45 @@ where
208 ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 225 ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
209 } 226 }
210 227
228 /// Enables internal voltage reference and returns [VrefInt], which can be used in
229 /// [Adc::read_internal()] to perform conversion.
230 pub fn enable_vrefint(&self) -> VrefInt {
231 unsafe {
232 T::common_regs().ccr().modify(|reg| {
233 reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
234 });
235 }
236
237 VrefInt {}
238 }
239
240 /// Enables internal temperature sensor and returns [Temperature], which can be used in
241 /// [Adc::read_internal()] to perform conversion.
242 ///
243 /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled,
244 /// temperature sensor will return vbat value.
245 pub fn enable_temperature(&self) -> Temperature {
246 unsafe {
247 T::common_regs().ccr().modify(|reg| {
248 reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
249 });
250 }
251
252 Temperature {}
253 }
254
255 /// Enables vbat input and returns [Vbat], which can be used in
256 /// [Adc::read_internal()] to perform conversion.
257 pub fn enable_vbat(&self) -> Vbat {
258 unsafe {
259 T::common_regs().ccr().modify(|reg| {
260 reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED);
261 });
262 }
263
264 Vbat {}
265 }
266
211 /// Perform a single conversion. 267 /// Perform a single conversion.
212 fn convert(&mut self) -> u16 { 268 fn convert(&mut self) -> u16 {
213 unsafe { 269 unsafe {
@@ -238,42 +294,29 @@ where
238 P: crate::gpio::sealed::Pin, 294 P: crate::gpio::sealed::Pin,
239 { 295 {
240 unsafe { 296 unsafe {
241 // dissable ADC
242 T::regs().cr2().modify(|reg| {
243 reg.set_swstart(false);
244 });
245 T::regs().cr2().modify(|reg| {
246 reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
247 });
248
249 pin.set_as_analog(); 297 pin.set_as_analog();
250 298
251 // Configure ADC 299 self.read_channel(pin.channel())
252 T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); 300 }
301 }
253 302
254 // Select channel 303 pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
255 T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())); 304 unsafe { self.read_channel(channel.channel()) }
305 }
256 306
257 // Configure channel 307 unsafe fn read_channel(&mut self, channel: u8) -> u16 {
258 Self::set_channel_sample_time(pin.channel(), self.sample_time); 308 // Configure ADC
309 T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
259 310
260 // enable adc 311 // Select channel
261 T::regs().cr2().modify(|reg| { 312 T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
262 reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
263 });
264 313
265 let val = self.convert(); 314 // Configure channel
315 Self::set_channel_sample_time(channel, self.sample_time);
266 316
267 // dissable ADC 317 let val = self.convert();
268 T::regs().cr2().modify(|reg| {
269 reg.set_swstart(false);
270 });
271 T::regs().cr2().modify(|reg| {
272 reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
273 });
274 318
275 val 319 val
276 }
277 } 320 }
278 321
279 unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { 322 unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
@@ -288,3 +331,9 @@ where
288 } 331 }
289 } 332 }
290} 333}
334
335impl<'d, T: Instance> Drop for Adc<'d, T> {
336 fn drop(&mut self) {
337 T::disable();
338 }
339}
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index d356d7b66..eda2b2a72 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs;
5use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; 5use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
6use pac::adccommon::vals::Presc; 6use pac::adccommon::vals::Presc;
7 7
8use super::{AdcPin, Instance}; 8use super::{AdcPin, Instance, InternalChannel};
9use crate::time::Hertz; 9use crate::time::Hertz;
10use crate::{pac, Peripheral}; 10use crate::{pac, Peripheral};
11 11
@@ -50,18 +50,10 @@ impl Resolution {
50 } 50 }
51} 51}
52 52
53pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
54
55mod sealed {
56 pub trait InternalChannel<T> {
57 fn channel(&self) -> u8;
58 }
59}
60
61// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs 53// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
62pub struct VrefInt; 54pub struct VrefInt;
63impl<T: Instance> InternalChannel<T> for VrefInt {} 55impl<T: Instance> InternalChannel<T> for VrefInt {}
64impl<T: Instance> sealed::InternalChannel<T> for VrefInt { 56impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
65 fn channel(&self) -> u8 { 57 fn channel(&self) -> u8 {
66 19 58 19
67 } 59 }
@@ -69,7 +61,7 @@ impl<T: Instance> sealed::InternalChannel<T> for VrefInt {
69 61
70pub struct Temperature; 62pub struct Temperature;
71impl<T: Instance> InternalChannel<T> for Temperature {} 63impl<T: Instance> InternalChannel<T> for Temperature {}
72impl<T: Instance> sealed::InternalChannel<T> for Temperature { 64impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
73 fn channel(&self) -> u8 { 65 fn channel(&self) -> u8 {
74 18 66 18
75 } 67 }
@@ -77,7 +69,7 @@ impl<T: Instance> sealed::InternalChannel<T> for Temperature {
77 69
78pub struct Vbat; 70pub struct Vbat;
79impl<T: Instance> InternalChannel<T> for Vbat {} 71impl<T: Instance> InternalChannel<T> for Vbat {}
80impl<T: Instance> sealed::InternalChannel<T> for Vbat { 72impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
81 fn channel(&self) -> u8 { 73 fn channel(&self) -> u8 {
82 // TODO this should be 14 for H7a/b/35 74 // TODO this should be 14 for H7a/b/35
83 17 75 17
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs
index 7ce0ac776..3f2129de8 100644
--- a/embassy-stm32/src/flash/h7.rs
+++ b/embassy-stm32/src/flash/h7.rs
@@ -39,6 +39,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error
39 w.set_psize(2); // 32 bits at once 39 w.set_psize(2); // 32 bits at once
40 }); 40 });
41 41
42 cortex_m::asm::isb();
43 cortex_m::asm::dsb();
44 atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst);
45
42 let ret = { 46 let ret = {
43 let mut ret: Result<(), Error> = Ok(()); 47 let mut ret: Result<(), Error> = Ok(());
44 let mut offset = offset; 48 let mut offset = offset;
@@ -64,6 +68,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error
64 68
65 bank.cr().write(|w| w.set_pg(false)); 69 bank.cr().write(|w| w.set_pg(false));
66 70
71 cortex_m::asm::isb();
72 cortex_m::asm::dsb();
73 atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst);
74
67 ret 75 ret
68} 76}
69 77
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index 5258c9b04..988cf9fae 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -23,17 +23,6 @@ impl<'d> Flash<'d> {
23 Self { _inner: p } 23 Self { _inner: p }
24 } 24 }
25 25
26 pub fn unlock(p: impl Peripheral<P = FLASH> + 'd) -> Self {
27 let flash = Self::new(p);
28
29 unsafe { family::unlock() };
30 flash
31 }
32
33 pub fn lock(&mut self) {
34 unsafe { family::lock() };
35 }
36
37 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { 26 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
38 let offset = FLASH_BASE as u32 + offset; 27 let offset = FLASH_BASE as u32 + offset;
39 if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END { 28 if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END {
@@ -57,7 +46,12 @@ impl<'d> Flash<'d> {
57 46
58 self.clear_all_err(); 47 self.clear_all_err();
59 48
60 unsafe { family::blocking_write(offset, buf) } 49 unsafe {
50 family::unlock();
51 let res = family::blocking_write(offset, buf);
52 family::lock();
53 res
54 }
61 } 55 }
62 56
63 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 57 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
@@ -72,7 +66,12 @@ impl<'d> Flash<'d> {
72 66
73 self.clear_all_err(); 67 self.clear_all_err();
74 68
75 unsafe { family::blocking_erase(from, to) } 69 unsafe {
70 family::unlock();
71 let res = family::blocking_erase(from, to);
72 family::lock();
73 res
74 }
76 } 75 }
77 76
78 fn clear_all_err(&mut self) { 77 fn clear_all_err(&mut self) {
@@ -82,7 +81,7 @@ impl<'d> Flash<'d> {
82 81
83impl Drop for Flash<'_> { 82impl Drop for Flash<'_> {
84 fn drop(&mut self) { 83 fn drop(&mut self) {
85 self.lock(); 84 unsafe { family::lock() };
86 } 85 }
87} 86}
88 87
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 22de6d180..074061218 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -160,6 +160,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
160 Ok(()) 160 Ok(())
161 } 161 }
162 162
163 pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
164 let r = T::regs();
165 unsafe {
166 let sr = sr(r).read();
167 if sr.pe() {
168 rdr(r).read_volatile();
169 Err(nb::Error::Other(Error::Parity))
170 } else if sr.fe() {
171 rdr(r).read_volatile();
172 Err(nb::Error::Other(Error::Framing))
173 } else if sr.ne() {
174 rdr(r).read_volatile();
175 Err(nb::Error::Other(Error::Noise))
176 } else if sr.ore() {
177 rdr(r).read_volatile();
178 Err(nb::Error::Other(Error::Overrun))
179 } else if sr.rxne() {
180 Ok(rdr(r).read_volatile())
181 } else {
182 Err(nb::Error::WouldBlock)
183 }
184 }
185 }
186
163 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 187 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
164 unsafe { 188 unsafe {
165 let r = T::regs(); 189 let r = T::regs();
@@ -285,6 +309,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
285 self.rx.read(buffer).await 309 self.rx.read(buffer).await
286 } 310 }
287 311
312 pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
313 self.rx.nb_read()
314 }
315
288 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 316 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
289 self.rx.blocking_read(buffer) 317 self.rx.blocking_read(buffer)
290 } 318 }
@@ -303,27 +331,7 @@ mod eh02 {
303 impl<'d, T: BasicInstance, RxDma> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, RxDma> { 331 impl<'d, T: BasicInstance, RxDma> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, RxDma> {
304 type Error = Error; 332 type Error = Error;
305 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> { 333 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
306 let r = T::regs(); 334 self.nb_read()
307 unsafe {
308 let sr = sr(r).read();
309 if sr.pe() {
310 rdr(r).read_volatile();
311 Err(nb::Error::Other(Error::Parity))
312 } else if sr.fe() {
313 rdr(r).read_volatile();
314 Err(nb::Error::Other(Error::Framing))
315 } else if sr.ne() {
316 rdr(r).read_volatile();
317 Err(nb::Error::Other(Error::Noise))
318 } else if sr.ore() {
319 rdr(r).read_volatile();
320 Err(nb::Error::Other(Error::Overrun))
321 } else if sr.rxne() {
322 Ok(rdr(r).read_volatile())
323 } else {
324 Err(nb::Error::WouldBlock)
325 }
326 }
327 } 335 }
328 } 336 }
329 337
@@ -340,7 +348,7 @@ mod eh02 {
340 impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::serial::Read<u8> for Uart<'d, T, TxDma, RxDma> { 348 impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::serial::Read<u8> for Uart<'d, T, TxDma, RxDma> {
341 type Error = Error; 349 type Error = Error;
342 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> { 350 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
343 embedded_hal_02::serial::Read::read(&mut self.rx) 351 self.nb_read()
344 } 352 }
345 } 353 }
346 354
@@ -381,6 +389,58 @@ mod eh1 {
381 impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> { 389 impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> {
382 type Error = Error; 390 type Error = Error;
383 } 391 }
392
393 impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::Read for UartRx<'d, T, RxDma> {
394 fn read(&mut self) -> nb::Result<u8, Self::Error> {
395 self.nb_read()
396 }
397 }
398
399 impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::Write for UartTx<'d, T, TxDma> {
400 fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
401 self.blocking_write(buffer)
402 }
403
404 fn flush(&mut self) -> Result<(), Self::Error> {
405 self.blocking_flush()
406 }
407 }
408
409 impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> {
410 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
411 self.blocking_write(&[char]).map_err(nb::Error::Other)
412 }
413
414 fn flush(&mut self) -> nb::Result<(), Self::Error> {
415 self.blocking_flush().map_err(nb::Error::Other)
416 }
417 }
418
419 impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Read for Uart<'d, T, TxDma, RxDma> {
420 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
421 self.nb_read()
422 }
423 }
424
425 impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::Write for Uart<'d, T, TxDma, RxDma> {
426 fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
427 self.blocking_write(buffer)
428 }
429
430 fn flush(&mut self) -> Result<(), Self::Error> {
431 self.blocking_flush()
432 }
433 }
434
435 impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> {
436 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
437 self.blocking_write(&[char]).map_err(nb::Error::Other)
438 }
439
440 fn flush(&mut self) -> nb::Result<(), Self::Error> {
441 self.blocking_flush().map_err(nb::Error::Other)
442 }
443 }
384} 444}
385 445
386#[cfg(all( 446#[cfg(all(
diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml
index 14ab1d003..584d5ba9f 100644
--- a/embassy-sync/Cargo.toml
+++ b/embassy-sync/Cargo.toml
@@ -2,6 +2,16 @@
2name = "embassy-sync" 2name = "embassy-sync"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5description = "no-std, no-alloc synchronization primitives with async support"
6repository = "https://github.com/embassy-rs/embassy"
7readme = "README.md"
8license = "MIT OR Apache-2.0"
9categories = [
10 "embedded",
11 "no-std",
12 "concurrency",
13 "asynchronous",
14]
5 15
6[package.metadata.embassy_docs] 16[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-sync-v$VERSION/embassy-sync/src/" 17src_base = "https://github.com/embassy-rs/embassy/blob/embassy-sync-v$VERSION/embassy-sync/src/"
diff --git a/embassy-sync/README.md b/embassy-sync/README.md
index 106295c0d..cc65cf6ef 100644
--- a/embassy-sync/README.md
+++ b/embassy-sync/README.md
@@ -1,12 +1,32 @@
1# embassy-sync 1# embassy-sync
2 2
3Synchronization primitives and data structures with an async API: 3An [Embassy](https://embassy.dev) project.
4
5Synchronization primitives and data structures with async support:
4 6
5- [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer. 7- [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer.
6- [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers. 8- [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers.
7- [`Signal`](signal::Signal) - Signalling latest value to a single consumer. 9- [`Signal`](signal::Signal) - Signalling latest value to a single consumer.
8- [`Mutex`](mutex::Mutex) - A Mutex for synchronizing state between asynchronous tasks. 10- [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks.
9- [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits. 11- [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits.
10- [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`. 12- [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`.
11- [`AtomicWaker`](waitqueue::AtomicWaker) - A variant of `WakerRegistration` accessible using a non-mut API. 13- [`AtomicWaker`](waitqueue::AtomicWaker) - A variant of `WakerRegistration` accessible using a non-mut API.
12- [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s. 14- [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s.
15
16## Interoperability
17
18Futures from this crate can run on any executor.
19
20## Minimum supported Rust version (MSRV)
21
22Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
23
24## License
25
26This work is licensed under either of
27
28- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
29 <http://www.apache.org/licenses/LICENSE-2.0>)
30- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
31
32at your option.
diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs
index 62a9e4763..faaf99dc6 100644
--- a/embassy-sync/src/pubsub/mod.rs
+++ b/embassy-sync/src/pubsub/mod.rs
@@ -192,6 +192,10 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
192 }) 192 })
193 } 193 }
194 194
195 fn available(&self, next_message_id: u64) -> u64 {
196 self.inner.lock(|s| s.borrow().next_message_id - next_message_id)
197 }
198
195 fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T> { 199 fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T> {
196 self.inner.lock(|s| { 200 self.inner.lock(|s| {
197 let mut s = s.borrow_mut(); 201 let mut s = s.borrow_mut();
@@ -217,6 +221,13 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
217 }) 221 })
218 } 222 }
219 223
224 fn space(&self) -> usize {
225 self.inner.lock(|s| {
226 let s = s.borrow();
227 s.queue.capacity() - s.queue.len()
228 })
229 }
230
220 fn unregister_subscriber(&self, subscriber_next_message_id: u64) { 231 fn unregister_subscriber(&self, subscriber_next_message_id: u64) {
221 self.inner.lock(|s| { 232 self.inner.lock(|s| {
222 let mut s = s.borrow_mut(); 233 let mut s = s.borrow_mut();
@@ -388,6 +399,10 @@ pub trait PubSubBehavior<T> {
388 /// If the message is not yet present and a context is given, then its waker is registered in the subsriber wakers. 399 /// If the message is not yet present and a context is given, then its waker is registered in the subsriber wakers.
389 fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll<WaitResult<T>>; 400 fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll<WaitResult<T>>;
390 401
402 /// Get the amount of messages that are between the given the next_message_id and the most recent message.
403 /// This is not necessarily the amount of messages a subscriber can still received as it may have lagged.
404 fn available(&self, next_message_id: u64) -> u64;
405
391 /// Try to publish a message to the queue. 406 /// Try to publish a message to the queue.
392 /// 407 ///
393 /// If the queue is full and a context is given, then its waker is registered in the publisher wakers. 408 /// If the queue is full and a context is given, then its waker is registered in the publisher wakers.
@@ -396,6 +411,9 @@ pub trait PubSubBehavior<T> {
396 /// Publish a message immediately 411 /// Publish a message immediately
397 fn publish_immediate(&self, message: T); 412 fn publish_immediate(&self, message: T);
398 413
414 /// The amount of messages that can still be published without having to wait or without having to lag the subscribers
415 fn space(&self) -> usize;
416
399 /// Let the channel know that a subscriber has dropped 417 /// Let the channel know that a subscriber has dropped
400 fn unregister_subscriber(&self, subscriber_next_message_id: u64); 418 fn unregister_subscriber(&self, subscriber_next_message_id: u64);
401 419
@@ -539,4 +557,59 @@ mod tests {
539 557
540 drop(sub0); 558 drop(sub0);
541 } 559 }
560
561 #[futures_test::test]
562 async fn correct_available() {
563 let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();
564
565 let sub0 = channel.subscriber().unwrap();
566 let mut sub1 = channel.subscriber().unwrap();
567 let pub0 = channel.publisher().unwrap();
568
569 assert_eq!(sub0.available(), 0);
570 assert_eq!(sub1.available(), 0);
571
572 pub0.publish(42).await;
573
574 assert_eq!(sub0.available(), 1);
575 assert_eq!(sub1.available(), 1);
576
577 sub1.next_message().await;
578
579 assert_eq!(sub1.available(), 0);
580
581 pub0.publish(42).await;
582
583 assert_eq!(sub0.available(), 2);
584 assert_eq!(sub1.available(), 1);
585 }
586
587 #[futures_test::test]
588 async fn correct_space() {
589 let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();
590
591 let mut sub0 = channel.subscriber().unwrap();
592 let mut sub1 = channel.subscriber().unwrap();
593 let pub0 = channel.publisher().unwrap();
594
595 assert_eq!(pub0.space(), 4);
596
597 pub0.publish(42).await;
598
599 assert_eq!(pub0.space(), 3);
600
601 pub0.publish(42).await;
602
603 assert_eq!(pub0.space(), 2);
604
605 sub0.next_message().await;
606 sub0.next_message().await;
607
608 assert_eq!(pub0.space(), 2);
609
610 sub1.next_message().await;
611 assert_eq!(pub0.space(), 3);
612 sub1.next_message().await;
613 assert_eq!(pub0.space(), 4);
614 }
542} 615}
diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs
index 705797f60..e1edc9eb9 100644
--- a/embassy-sync/src/pubsub/publisher.rs
+++ b/embassy-sync/src/pubsub/publisher.rs
@@ -42,6 +42,14 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Pub<'a, PSB, T> {
42 pub fn try_publish(&self, message: T) -> Result<(), T> { 42 pub fn try_publish(&self, message: T) -> Result<(), T> {
43 self.channel.publish_with_context(message, None) 43 self.channel.publish_with_context(message, None)
44 } 44 }
45
46 /// The amount of messages that can still be published without having to wait or without having to lag the subscribers
47 ///
48 /// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something.
49 /// So checking doesn't give any guarantees.*
50 pub fn space(&self) -> usize {
51 self.channel.space()
52 }
45} 53}
46 54
47impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> { 55impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> {
@@ -115,6 +123,14 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> ImmediatePub<'a, PSB, T> {
115 pub fn try_publish(&self, message: T) -> Result<(), T> { 123 pub fn try_publish(&self, message: T) -> Result<(), T> {
116 self.channel.publish_with_context(message, None) 124 self.channel.publish_with_context(message, None)
117 } 125 }
126
127 /// The amount of messages that can still be published without having to wait or without having to lag the subscribers
128 ///
129 /// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something.
130 /// So checking doesn't give any guarantees.*
131 pub fn space(&self) -> usize {
132 self.channel.space()
133 }
118} 134}
119 135
120/// An immediate publisher that holds a dynamic reference to the channel 136/// An immediate publisher that holds a dynamic reference to the channel
@@ -158,6 +174,7 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS:
158} 174}
159 175
160/// Future for the publisher wait action 176/// Future for the publisher wait action
177#[must_use = "futures do nothing unless you `.await` or poll them"]
161pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> { 178pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {
162 /// The message we need to publish 179 /// The message we need to publish
163 message: Option<T>, 180 message: Option<T>,
diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs
index b9a2cbe18..f420a75f0 100644
--- a/embassy-sync/src/pubsub/subscriber.rs
+++ b/embassy-sync/src/pubsub/subscriber.rs
@@ -64,6 +64,11 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Sub<'a, PSB, T> {
64 } 64 }
65 } 65 }
66 } 66 }
67
68 /// The amount of messages this subscriber hasn't received yet
69 pub fn available(&self) -> u64 {
70 self.channel.available(self.next_message_id)
71 }
67} 72}
68 73
69impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> { 74impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> {
@@ -135,6 +140,7 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS:
135} 140}
136 141
137/// Future for the subscriber wait action 142/// Future for the subscriber wait action
143#[must_use = "futures do nothing unless you `.await` or poll them"]
138pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> { 144pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {
139 subscriber: &'s mut Sub<'a, PSB, T>, 145 subscriber: &'s mut Sub<'a, PSB, T>,
140} 146}
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
index c3b361b8a..c51a71d01 100644
--- a/embassy-time/Cargo.toml
+++ b/embassy-time/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-time" 2name = "embassy-time"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6 7
7[package.metadata.embassy_docs] 8[package.metadata.embassy_docs]
diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml
index b525df337..d22bf7d72 100644
--- a/embassy-usb-driver/Cargo.toml
+++ b/embassy-usb-driver/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-usb-driver" 2name = "embassy-usb-driver"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 8
@@ -13,4 +14,4 @@ target = "thumbv7em-none-eabi"
13 14
14[dependencies] 15[dependencies]
15defmt = { version = "0.3", optional = true } 16defmt = { version = "0.3", optional = true }
16log = { version = "0.4.14", optional = true } \ No newline at end of file 17log = { version = "0.4.14", optional = true }
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs
index fc29786fc..931e9c318 100644
--- a/embassy-usb-driver/src/lib.rs
+++ b/embassy-usb-driver/src/lib.rs
@@ -54,12 +54,16 @@ impl From<EndpointAddress> for u8 {
54} 54}
55 55
56impl EndpointAddress { 56impl EndpointAddress {
57 const INBITS: u8 = Direction::In as u8; 57 const INBITS: u8 = 0x80;
58 58
59 /// Constructs a new EndpointAddress with the given index and direction. 59 /// Constructs a new EndpointAddress with the given index and direction.
60 #[inline] 60 #[inline]
61 pub fn from_parts(index: usize, dir: Direction) -> Self { 61 pub fn from_parts(index: usize, dir: Direction) -> Self {
62 EndpointAddress(index as u8 | dir as u8) 62 let dir_u8 = match dir {
63 Direction::Out => 0x00,
64 Direction::In => Self::INBITS,
65 };
66 EndpointAddress(index as u8 | dir_u8)
63 } 67 }
64 68
65 /// Gets the direction part of the address. 69 /// Gets the direction part of the address.
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index aad54dbaf..b59ba8a22 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -2,11 +2,12 @@
2name = "embassy-usb" 2name = "embassy-usb"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6[package.metadata.embassy_docs] 7[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/"
8src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/src/" 9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/src/"
9features = ["defmt"] 10features = ["defmt", "usbd-hid"]
10target = "thumbv7em-none-eabi" 11target = "thumbv7em-none-eabi"
11 12
12[features] 13[features]
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml
index b9ff92578..a5d82b601 100644
--- a/examples/boot/application/nrf/Cargo.toml
+++ b/examples/boot/application/nrf/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-nrf-examples" 3name = "embassy-boot-nrf-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml
index ce1e6fe4a..3a1843562 100644
--- a/examples/boot/application/stm32f3/Cargo.toml
+++ b/examples/boot/application/stm32f3/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-stm32f3-examples" 3name = "embassy-boot-stm32f3-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs
index fdbd5ab99..d92d59b29 100644
--- a/examples/boot/application/stm32f3/src/bin/a.rs
+++ b/examples/boot/application/stm32f3/src/bin/a.rs
@@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
17#[embassy_executor::main] 17#[embassy_executor::main]
18async fn main(_spawner: Spawner) { 18async fn main(_spawner: Spawner) {
19 let p = embassy_stm32::init(Default::default()); 19 let p = embassy_stm32::init(Default::default());
20 let flash = Flash::unlock(p.FLASH); 20 let flash = Flash::new(p.FLASH);
21 let mut flash = BlockingAsync::new(flash); 21 let mut flash = BlockingAsync::new(flash);
22 22
23 let button = Input::new(p.PC13, Pull::Up); 23 let button = Input::new(p.PC13, Pull::Up);
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml
index 2fc7ae834..8d9c4490e 100644
--- a/examples/boot/application/stm32f7/Cargo.toml
+++ b/examples/boot/application/stm32f7/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-stm32f7-examples" 3name = "embassy-boot-stm32f7-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs
index 77b897b0f..79ab80e09 100644
--- a/examples/boot/application/stm32f7/src/bin/a.rs
+++ b/examples/boot/application/stm32f7/src/bin/a.rs
@@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
16#[embassy_executor::main] 16#[embassy_executor::main]
17async fn main(_spawner: Spawner) { 17async fn main(_spawner: Spawner) {
18 let p = embassy_stm32::init(Default::default()); 18 let p = embassy_stm32::init(Default::default());
19 let mut flash = Flash::unlock(p.FLASH); 19 let mut flash = Flash::new(p.FLASH);
20 20
21 let button = Input::new(p.PC13, Pull::Down); 21 let button = Input::new(p.PC13, Pull::Down);
22 let mut button = ExtiInput::new(button, p.EXTI13); 22 let mut button = ExtiInput::new(button, p.EXTI13);
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml
index fd809714a..b4314aa72 100644
--- a/examples/boot/application/stm32h7/Cargo.toml
+++ b/examples/boot/application/stm32h7/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-stm32h7-examples" 3name = "embassy-boot-stm32h7-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs
index 0fe598a5d..8b452be34 100644
--- a/examples/boot/application/stm32h7/src/bin/a.rs
+++ b/examples/boot/application/stm32h7/src/bin/a.rs
@@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
16#[embassy_executor::main] 16#[embassy_executor::main]
17async fn main(_spawner: Spawner) { 17async fn main(_spawner: Spawner) {
18 let p = embassy_stm32::init(Default::default()); 18 let p = embassy_stm32::init(Default::default());
19 let mut flash = Flash::unlock(p.FLASH); 19 let mut flash = Flash::new(p.FLASH);
20 20
21 let button = Input::new(p.PC13, Pull::Down); 21 let button = Input::new(p.PC13, Pull::Down);
22 let mut button = ExtiInput::new(button, p.EXTI13); 22 let mut button = ExtiInput::new(button, p.EXTI13);
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml
index 470eca52a..a17d336a6 100644
--- a/examples/boot/application/stm32l0/Cargo.toml
+++ b/examples/boot/application/stm32l0/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-stm32l0-examples" 3name = "embassy-boot-stm32l0-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs
index f0b0b80e3..59ca34386 100644
--- a/examples/boot/application/stm32l0/src/bin/a.rs
+++ b/examples/boot/application/stm32l0/src/bin/a.rs
@@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
18#[embassy_executor::main] 18#[embassy_executor::main]
19async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
20 let p = embassy_stm32::init(Default::default()); 20 let p = embassy_stm32::init(Default::default());
21 let flash = Flash::unlock(p.FLASH); 21 let flash = Flash::new(p.FLASH);
22 let mut flash = BlockingAsync::new(flash); 22 let mut flash = BlockingAsync::new(flash);
23 23
24 let button = Input::new(p.PB2, Pull::Up); 24 let button = Input::new(p.PB2, Pull::Up);
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml
index 2b4b29357..683f2c860 100644
--- a/examples/boot/application/stm32l1/Cargo.toml
+++ b/examples/boot/application/stm32l1/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-stm32l1-examples" 3name = "embassy-boot-stm32l1-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs
index f0b0b80e3..59ca34386 100644
--- a/examples/boot/application/stm32l1/src/bin/a.rs
+++ b/examples/boot/application/stm32l1/src/bin/a.rs
@@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
18#[embassy_executor::main] 18#[embassy_executor::main]
19async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
20 let p = embassy_stm32::init(Default::default()); 20 let p = embassy_stm32::init(Default::default());
21 let flash = Flash::unlock(p.FLASH); 21 let flash = Flash::new(p.FLASH);
22 let mut flash = BlockingAsync::new(flash); 22 let mut flash = BlockingAsync::new(flash);
23 23
24 let button = Input::new(p.PB2, Pull::Up); 24 let button = Input::new(p.PB2, Pull::Up);
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml
index 40bddd194..b879c0d76 100644
--- a/examples/boot/application/stm32l4/Cargo.toml
+++ b/examples/boot/application/stm32l4/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-stm32l4-examples" 3name = "embassy-boot-stm32l4-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs
index 5119bad2e..6cddc6cc8 100644
--- a/examples/boot/application/stm32l4/src/bin/a.rs
+++ b/examples/boot/application/stm32l4/src/bin/a.rs
@@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
17#[embassy_executor::main] 17#[embassy_executor::main]
18async fn main(_spawner: Spawner) { 18async fn main(_spawner: Spawner) {
19 let p = embassy_stm32::init(Default::default()); 19 let p = embassy_stm32::init(Default::default());
20 let flash = Flash::unlock(p.FLASH); 20 let flash = Flash::new(p.FLASH);
21 let mut flash = BlockingAsync::new(flash); 21 let mut flash = BlockingAsync::new(flash);
22 22
23 let button = Input::new(p.PC13, Pull::Up); 23 let button = Input::new(p.PC13, Pull::Up);
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml
index 5b4a61e8f..e3bc0e49c 100644
--- a/examples/boot/application/stm32wl/Cargo.toml
+++ b/examples/boot/application/stm32wl/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-boot-stm32wl-examples" 3name = "embassy-boot-stm32wl-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs
index faa650778..1ff47eddd 100644
--- a/examples/boot/application/stm32wl/src/bin/a.rs
+++ b/examples/boot/application/stm32wl/src/bin/a.rs
@@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
17#[embassy_executor::main] 17#[embassy_executor::main]
18async fn main(_spawner: Spawner) { 18async fn main(_spawner: Spawner) {
19 let p = embassy_stm32::init(Default::default()); 19 let p = embassy_stm32::init(Default::default());
20 let flash = Flash::unlock(p.FLASH); 20 let flash = Flash::new(p.FLASH);
21 let mut flash = BlockingAsync::new(flash); 21 let mut flash = BlockingAsync::new(flash);
22 22
23 let button = Input::new(p.PA0, Pull::Up); 23 let button = Input::new(p.PA0, Pull::Up);
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml
index aa2a13ecb..b417a40d1 100644
--- a/examples/boot/bootloader/nrf/Cargo.toml
+++ b/examples/boot/bootloader/nrf/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2021"
3name = "nrf-bootloader-example" 3name = "nrf-bootloader-example"
4version = "0.1.0" 4version = "0.1.0"
5description = "Bootloader for nRF chips" 5description = "Bootloader for nRF chips"
6license = "MIT OR Apache-2.0"
6 7
7[dependencies] 8[dependencies]
8defmt = { version = "0.3", optional = true } 9defmt = { version = "0.3", optional = true }
diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml
index 491777103..4ddd1c99c 100644
--- a/examples/boot/bootloader/stm32/Cargo.toml
+++ b/examples/boot/bootloader/stm32/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2021"
3name = "stm32-bootloader-example" 3name = "stm32-bootloader-example"
4version = "0.1.0" 4version = "0.1.0"
5description = "Example bootloader for STM32 chips" 5description = "Example bootloader for STM32 chips"
6license = "MIT OR Apache-2.0"
6 7
7[dependencies] 8[dependencies]
8defmt = { version = "0.3", optional = true } 9defmt = { version = "0.3", optional = true }
diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs
index 294464d1c..4b17cd799 100644
--- a/examples/boot/bootloader/stm32/src/main.rs
+++ b/examples/boot/bootloader/stm32/src/main.rs
@@ -20,7 +20,7 @@ fn main() -> ! {
20 */ 20 */
21 21
22 let mut bl: BootLoader<ERASE_SIZE, WRITE_SIZE> = BootLoader::default(); 22 let mut bl: BootLoader<ERASE_SIZE, WRITE_SIZE> = BootLoader::default();
23 let flash = Flash::unlock(p.FLASH); 23 let flash = Flash::new(p.FLASH);
24 let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); 24 let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash);
25 let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); 25 let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash));
26 core::mem::drop(flash); 26 core::mem::drop(flash);
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml
index 87c9f33f5..d8c24dfad 100644
--- a/examples/nrf-rtos-trace/Cargo.toml
+++ b/examples/nrf-rtos-trace/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-nrf-rtos-trace-examples" 3name = "embassy-nrf-rtos-trace-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[features] 7[features]
7default = ["log", "nightly"] 8default = ["log", "nightly"]
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml
index a5d340c69..6949042e2 100644
--- a/examples/nrf/Cargo.toml
+++ b/examples/nrf/Cargo.toml
@@ -2,10 +2,12 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-nrf-examples" 3name = "embassy-nrf-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[features] 7[features]
7default = ["nightly"] 8default = ["nightly"]
8nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] 9nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net",
10 "embassy-lora", "lorawan-device", "lorawan"]
9 11
10[dependencies] 12[dependencies]
11embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@@ -16,6 +18,10 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm
16embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } 18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true }
17embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } 19embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
18embedded-io = "0.3.0" 20embedded-io = "0.3.0"
21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true }
22
23lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true }
24lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true }
19 25
20defmt = "0.3" 26defmt = "0.3"
21defmt-rtt = "0.3" 27defmt-rtt = "0.3"
diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs
new file mode 100644
index 000000000..d512b83f6
--- /dev/null
+++ b/examples/nrf/src/bin/lora_p2p_report.rs
@@ -0,0 +1,78 @@
1//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
2//! Other nrf/sx126x combinations may work with appropriate pin modifications.
3//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs.
4#![no_std]
5#![no_main]
6#![macro_use]
7#![allow(dead_code)]
8#![feature(type_alias_impl_trait)]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_lora::sx126x::*;
13use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
14use embassy_nrf::{interrupt, spim};
15use embassy_time::{Duration, Timer};
16use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor};
17use {defmt_rtt as _, panic_probe as _};
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 let p = embassy_nrf::init(Default::default());
22 let mut spi_config = spim::Config::default();
23 spi_config.frequency = spim::Frequency::M16;
24
25 let mut radio = {
26 let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
27 let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config);
28
29 let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
30 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
31 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
32 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
33 let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
34 let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
35
36 match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
37 Ok(r) => r,
38 Err(err) => {
39 info!("Sx126xRadio error = {}", err);
40 return;
41 }
42 }
43 };
44
45 let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
46 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
47
48 start_indicator.set_high();
49 Timer::after(Duration::from_secs(5)).await;
50 start_indicator.set_low();
51
52 loop {
53 let rf_config = RfConfig {
54 frequency: 903900000, // channel in Hz
55 bandwidth: Bandwidth::_250KHz,
56 spreading_factor: SpreadingFactor::_10,
57 coding_rate: CodingRate::_4_8,
58 };
59
60 let mut buffer = [00u8; 100];
61
62 // P2P receive
63 match radio.rx(rf_config, &mut buffer).await {
64 Ok((buffer_len, rx_quality)) => info!(
65 "RX received = {:?} with length = {} rssi = {} snr = {}",
66 &buffer[0..buffer_len],
67 buffer_len,
68 rx_quality.rssi(),
69 rx_quality.snr()
70 ),
71 Err(err) => info!("RX error = {}", err),
72 }
73
74 debug_indicator.set_high();
75 Timer::after(Duration::from_secs(2)).await;
76 debug_indicator.set_low();
77 }
78}
diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs
new file mode 100644
index 000000000..b9768874b
--- /dev/null
+++ b/examples/nrf/src/bin/lora_p2p_sense.rs
@@ -0,0 +1,125 @@
1//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
2//! Other nrf/sx126x combinations may work with appropriate pin modifications.
3//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs.
4#![no_std]
5#![no_main]
6#![macro_use]
7#![feature(type_alias_impl_trait)]
8#![feature(alloc_error_handler)]
9#![allow(incomplete_features)]
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_lora::sx126x::*;
14use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
15use embassy_nrf::{interrupt, spim};
16use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
17use embassy_sync::pubsub::{PubSubChannel, Publisher};
18use embassy_time::{Duration, Timer};
19use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig};
20use {defmt_rtt as _, panic_probe as _, panic_probe as _};
21
22// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection)
23static MESSAGE_BUS: PubSubChannel<CriticalSectionRawMutex, Message, 2, 1, 2> = PubSubChannel::new();
24
25#[derive(Clone, defmt::Format)]
26enum Message {
27 Temperature(i32),
28 MotionDetected,
29}
30
31#[embassy_executor::task]
32async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) {
33 // Publish a fake temperature every 43 seconds, minimizing LORA traffic.
34 loop {
35 Timer::after(Duration::from_secs(43)).await;
36 publisher.publish(Message::Temperature(9)).await;
37 }
38}
39
40#[embassy_executor::task]
41async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) {
42 // Publish a fake motion detection every 79 seconds, minimizing LORA traffic.
43 loop {
44 Timer::after(Duration::from_secs(79)).await;
45 publisher.publish(Message::MotionDetected).await;
46 }
47}
48
49#[embassy_executor::main]
50async fn main(spawner: Spawner) {
51 let p = embassy_nrf::init(Default::default());
52 // set up to funnel temperature and motion detection events to the Lora Tx task
53 let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber());
54 let temperature_publisher = unwrap!(MESSAGE_BUS.publisher());
55 let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher());
56
57 let mut spi_config = spim::Config::default();
58 spi_config.frequency = spim::Frequency::M16;
59
60 let mut radio = {
61 let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
62 let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config);
63
64 let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
65 let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
66 let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
67 let busy = Input::new(p.P1_14.degrade(), Pull::Down);
68 let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
69 let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
70
71 match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
72 Ok(r) => r,
73 Err(err) => {
74 info!("Sx126xRadio error = {}", err);
75 return;
76 }
77 }
78 };
79
80 let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
81
82 start_indicator.set_high();
83 Timer::after(Duration::from_secs(5)).await;
84 start_indicator.set_low();
85
86 match radio.lora.sleep().await {
87 Ok(()) => info!("Sleep successful"),
88 Err(err) => info!("Sleep unsuccessful = {}", err),
89 }
90
91 unwrap!(spawner.spawn(temperature_task(temperature_publisher)));
92 unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher)));
93
94 loop {
95 let message = lora_tx_subscriber.next_message_pure().await;
96
97 let tx_config = TxConfig {
98 // 11 byte maximum payload for Bandwidth 125 and SF 10
99 pw: 10, // up to 20
100 rf: RfConfig {
101 frequency: 903900000, // channel in Hz, not MHz
102 bandwidth: Bandwidth::_250KHz,
103 spreading_factor: SpreadingFactor::_10,
104 coding_rate: CodingRate::_4_8,
105 },
106 };
107
108 let mut buffer = [0x00u8];
109 match message {
110 Message::Temperature(temperature) => buffer[0] = temperature as u8,
111 Message::MotionDetected => buffer[0] = 0x01u8,
112 };
113
114 // unencrypted
115 match radio.tx(tx_config, &buffer).await {
116 Ok(ret_val) => info!("TX ret_val = {}", ret_val),
117 Err(err) => info!("TX error = {}", err),
118 }
119
120 match radio.lora.sleep().await {
121 Ok(()) => info!("Sleep successful"),
122 Err(err) => info!("Sleep unsuccessful = {}", err),
123 }
124 }
125}
diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs
new file mode 100644
index 000000000..7388580fb
--- /dev/null
+++ b/examples/nrf/src/bin/pdm.rs
@@ -0,0 +1,33 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_nrf::interrupt;
8use embassy_nrf::pdm::{Config, Pdm};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_p: Spawner) {
14 let p = embassy_nrf::init(Default::default());
15 let config = Config::default();
16 let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config);
17
18 loop {
19 pdm.start().await;
20
21 // wait some time till the microphon settled
22 Timer::after(Duration::from_millis(1000)).await;
23
24 const SAMPLES: usize = 2048;
25 let mut buf = [0i16; SAMPLES];
26 pdm.sample(&mut buf).await.unwrap();
27
28 info!("samples: {:?}", &buf);
29
30 pdm.stop().await;
31 Timer::after(Duration::from_millis(100)).await;
32 }
33}
diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf/src/bin/uart_idle.rs
index 09ec624c0..6af4f7097 100644
--- a/examples/nrf/src/bin/uart_idle.rs
+++ b/examples/nrf/src/bin/uart_idle.rs
@@ -15,7 +15,8 @@ async fn main(_spawner: Spawner) {
15 config.baudrate = uarte::Baudrate::BAUD115200; 15 config.baudrate = uarte::Baudrate::BAUD115200;
16 16
17 let irq = interrupt::take!(UARTE0_UART0); 17 let irq = interrupt::take!(UARTE0_UART0);
18 let mut uart = uarte::UarteWithIdle::new(p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, config); 18 let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config);
19 let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1);
19 20
20 info!("uarte initialized!"); 21 info!("uarte initialized!");
21 22
@@ -23,12 +24,12 @@ async fn main(_spawner: Spawner) {
23 let mut buf = [0; 8]; 24 let mut buf = [0; 8];
24 buf.copy_from_slice(b"Hello!\r\n"); 25 buf.copy_from_slice(b"Hello!\r\n");
25 26
26 unwrap!(uart.write(&buf).await); 27 unwrap!(tx.write(&buf).await);
27 info!("wrote hello in uart!"); 28 info!("wrote hello in uart!");
28 29
29 loop { 30 loop {
30 info!("reading..."); 31 info!("reading...");
31 let n = unwrap!(uart.read_until_idle(&mut buf).await); 32 let n = unwrap!(rx.read_until_idle(&mut buf).await);
32 info!("got {} bytes", n); 33 info!("got {} bytes", n);
33 } 34 }
34} 35}
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 3c8f923e7..747dde515 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-rp-examples" 3name = "embassy-rp-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6 7
7[dependencies] 8[dependencies]
@@ -26,7 +27,10 @@ st7789 = "0.6.1"
26display-interface = "0.4.1" 27display-interface = "0.4.1"
27byte-slice-cast = { version = "1.2.0", default-features = false } 28byte-slice-cast = { version = "1.2.0", default-features = false }
28 29
29embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } 30embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
30embedded-hal-async = { version = "0.1.0-alpha.1" } 31embedded-hal-async = { version = "0.1.0-alpha.1" }
31embedded-io = { version = "0.3.0", features = ["async", "defmt"] } 32embedded-io = { version = "0.3.0", features = ["async", "defmt"] }
32static_cell = "1.0.0" 33static_cell = "1.0.0"
34
35[profile.release]
36debug = true
diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs
new file mode 100644
index 000000000..d1a2e3cd7
--- /dev/null
+++ b/examples/rp/src/bin/i2c_async.rs
@@ -0,0 +1,102 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::i2c::{self, Config};
8use embassy_rp::interrupt;
9use embassy_time::{Duration, Timer};
10use embedded_hal_async::i2c::I2c;
11use {defmt_rtt as _, panic_probe as _};
12
13#[allow(dead_code)]
14mod mcp23017 {
15 pub const ADDR: u8 = 0x20; // default addr
16
17 macro_rules! mcpregs {
18 ($($name:ident : $val:expr),* $(,)?) => {
19 $(
20 pub const $name: u8 = $val;
21 )*
22
23 pub fn regname(reg: u8) -> &'static str {
24 match reg {
25 $(
26 $val => stringify!($name),
27 )*
28 _ => panic!("bad reg"),
29 }
30 }
31 }
32 }
33
34 // These are correct for IOCON.BANK=0
35 mcpregs! {
36 IODIRA: 0x00,
37 IPOLA: 0x02,
38 GPINTENA: 0x04,
39 DEFVALA: 0x06,
40 INTCONA: 0x08,
41 IOCONA: 0x0A,
42 GPPUA: 0x0C,
43 INTFA: 0x0E,
44 INTCAPA: 0x10,
45 GPIOA: 0x12,
46 OLATA: 0x14,
47 IODIRB: 0x01,
48 IPOLB: 0x03,
49 GPINTENB: 0x05,
50 DEFVALB: 0x07,
51 INTCONB: 0x09,
52 IOCONB: 0x0B,
53 GPPUB: 0x0D,
54 INTFB: 0x0F,
55 INTCAPB: 0x11,
56 GPIOB: 0x13,
57 OLATB: 0x15,
58 }
59}
60
61#[embassy_executor::main]
62async fn main(_spawner: Spawner) {
63 let p = embassy_rp::init(Default::default());
64
65 let sda = p.PIN_14;
66 let scl = p.PIN_15;
67 let irq = interrupt::take!(I2C1_IRQ);
68
69 info!("set up i2c ");
70 let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, irq, Config::default());
71
72 use mcp23017::*;
73
74 info!("init mcp23017 config for IxpandO");
75 // init - a outputs, b inputs
76 i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();
77 i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();
78 i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups
79
80 let mut val = 1;
81 loop {
82 let mut portb = [0];
83
84 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();
85 info!("portb = {:02x}", portb[0]);
86 i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();
87 val = val.rotate_left(1);
88
89 // get a register dump
90 info!("getting register dump");
91 let mut regs = [0; 22];
92 i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();
93 // always get the regdump but only display it if portb'0 is set
94 if portb[0] & 1 != 0 {
95 for (idx, reg) in regs.into_iter().enumerate() {
96 info!("{} => {:02x}", regname(idx as u8), reg);
97 }
98 }
99
100 Timer::after(Duration::from_millis(100)).await;
101 }
102}
diff --git a/examples/rp/src/bin/i2c_blocking.rs b/examples/rp/src/bin/i2c_blocking.rs
new file mode 100644
index 000000000..7623e33c8
--- /dev/null
+++ b/examples/rp/src/bin/i2c_blocking.rs
@@ -0,0 +1,70 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::i2c::{self, Config};
8use embassy_time::{Duration, Timer};
9use embedded_hal_1::i2c::I2c;
10use {defmt_rtt as _, panic_probe as _};
11
12#[allow(dead_code)]
13mod mcp23017 {
14 pub const ADDR: u8 = 0x20; // default addr
15
16 pub const IODIRA: u8 = 0x00;
17 pub const IPOLA: u8 = 0x02;
18 pub const GPINTENA: u8 = 0x04;
19 pub const DEFVALA: u8 = 0x06;
20 pub const INTCONA: u8 = 0x08;
21 pub const IOCONA: u8 = 0x0A;
22 pub const GPPUA: u8 = 0x0C;
23 pub const INTFA: u8 = 0x0E;
24 pub const INTCAPA: u8 = 0x10;
25 pub const GPIOA: u8 = 0x12;
26 pub const OLATA: u8 = 0x14;
27 pub const IODIRB: u8 = 0x01;
28 pub const IPOLB: u8 = 0x03;
29 pub const GPINTENB: u8 = 0x05;
30 pub const DEFVALB: u8 = 0x07;
31 pub const INTCONB: u8 = 0x09;
32 pub const IOCONB: u8 = 0x0B;
33 pub const GPPUB: u8 = 0x0D;
34 pub const INTFB: u8 = 0x0F;
35 pub const INTCAPB: u8 = 0x11;
36 pub const GPIOB: u8 = 0x13;
37 pub const OLATB: u8 = 0x15;
38}
39
40#[embassy_executor::main]
41async fn main(_spawner: Spawner) {
42 let p = embassy_rp::init(Default::default());
43
44 let sda = p.PIN_14;
45 let scl = p.PIN_15;
46
47 info!("set up i2c ");
48 let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
49
50 use mcp23017::*;
51
52 info!("init mcp23017 config for IxpandO");
53 // init - a outputs, b inputs
54 i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
55 i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
56 i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups
57
58 let mut val = 0xaa;
59 loop {
60 let mut portb = [0];
61
62 i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
63 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();
64
65 info!("portb = {:02x}", portb[0]);
66 val = !val;
67
68 Timer::after(Duration::from_secs(1)).await;
69 }
70}
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml
index dbfd9d625..b9bd1e718 100644
--- a/examples/std/Cargo.toml
+++ b/examples/std/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-std-examples" 3name = "embassy-std-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml
index c82b79c86..a56c546ee 100644
--- a/examples/stm32f0/Cargo.toml
+++ b/examples/stm32f0/Cargo.toml
@@ -2,6 +2,7 @@
2name = "embassy-stm32f0-examples" 2name = "embassy-stm32f0-examples"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 8
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml
index e6553789a..6be131f30 100644
--- a/examples/stm32f1/Cargo.toml
+++ b/examples/stm32f1/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32f1-examples" 3name = "embassy-stm32f1-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml
index 60cd54bd9..f6adda2a3 100644
--- a/examples/stm32f2/Cargo.toml
+++ b/examples/stm32f2/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32f2-examples" 3name = "embassy-stm32f2-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index f5b0b880c..27188dd19 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32f3-examples" 3name = "embassy-stm32f3-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs
index 2cf24dbd3..baa7484d0 100644
--- a/examples/stm32f3/src/bin/flash.rs
+++ b/examples/stm32f3/src/bin/flash.rs
@@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
15 15
16 const ADDR: u32 = 0x26000; 16 const ADDR: u32 = 0x26000;
17 17
18 let mut f = Flash::unlock(p.FLASH); 18 let mut f = Flash::new(p.FLASH);
19 19
20 info!("Reading..."); 20 info!("Reading...");
21 let mut buf = [0u8; 8]; 21 let mut buf = [0u8; 8];
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index ea5c47a43..6d4f09fba 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32f4-examples" 3name = "embassy-stm32f4-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6 7
7[dependencies] 8[dependencies]
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs
index 871185074..1d030f7dc 100644
--- a/examples/stm32f4/src/bin/adc.rs
+++ b/examples/stm32f4/src/bin/adc.rs
@@ -2,9 +2,10 @@
2#![no_main] 2#![no_main]
3#![feature(type_alias_impl_trait)] 3#![feature(type_alias_impl_trait)]
4 4
5use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
5use defmt::*; 6use defmt::*;
6use embassy_executor::Spawner; 7use embassy_executor::Spawner;
7use embassy_stm32::adc::Adc; 8use embassy_stm32::adc::{Adc, Temperature, VrefInt};
8use embassy_time::{Delay, Duration, Timer}; 9use embassy_time::{Delay, Duration, Timer};
9use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
10 11
@@ -13,12 +14,30 @@ async fn main(_spawner: Spawner) {
13 let p = embassy_stm32::init(Default::default()); 14 let p = embassy_stm32::init(Default::default());
14 info!("Hello World!"); 15 info!("Hello World!");
15 16
16 let mut adc = Adc::new(p.ADC1, &mut Delay); 17 let mut delay = Delay;
18 let mut adc = Adc::new(p.ADC1, &mut delay);
17 let mut pin = p.PC1; 19 let mut pin = p.PC1;
18 20
21 let mut vrefint = adc.enable_vrefint();
22 let mut temp = adc.enable_temperature();
23
24 // Startup delay can be combined to the maximum of either
25 delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us()));
26
19 loop { 27 loop {
28 // Read pin
20 let v = adc.read(&mut pin); 29 let v = adc.read(&mut pin);
21 info!("--> {} - {} mV", v, adc.to_millivolts(v)); 30 info!("PC1: {} ({} mV)", v, adc.to_millivolts(v));
31
32 // Read internal temperature
33 let v = adc.read_internal(&mut temp);
34 let celcius = Temperature::to_celcius(adc.to_millivolts(v));
35 info!("Internal temp: {} ({} C)", v, celcius);
36
37 // Read internal voltage reference
38 let v = adc.read_internal(&mut vrefint);
39 info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v));
40
22 Timer::after(Duration::from_millis(100)).await; 41 Timer::after(Duration::from_millis(100)).await;
23 } 42 }
24} 43}
diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs
index 393d61e86..7ea068a42 100644
--- a/examples/stm32f4/src/bin/flash.rs
+++ b/examples/stm32f4/src/bin/flash.rs
@@ -13,7 +13,7 @@ async fn main(_spawner: Spawner) {
13 let p = embassy_stm32::init(Default::default()); 13 let p = embassy_stm32::init(Default::default());
14 info!("Hello Flash!"); 14 info!("Hello Flash!");
15 15
16 let mut f = Flash::unlock(p.FLASH); 16 let mut f = Flash::new(p.FLASH);
17 17
18 // Sector 5 18 // Sector 5
19 test_flash(&mut f, 128 * 1024, 128 * 1024); 19 test_flash(&mut f, 128 * 1024, 128 * 1024);
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index 6c2f846fe..dad92c0fc 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32f7-examples" 3name = "embassy-stm32f7-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs
index c10781d0c..4a7bca1fa 100644
--- a/examples/stm32f7/src/bin/flash.rs
+++ b/examples/stm32f7/src/bin/flash.rs
@@ -19,7 +19,7 @@ async fn main(_spawner: Spawner) {
19 // wait a bit before accessing the flash 19 // wait a bit before accessing the flash
20 Timer::after(Duration::from_millis(300)).await; 20 Timer::after(Duration::from_millis(300)).await;
21 21
22 let mut f = Flash::unlock(p.FLASH); 22 let mut f = Flash::new(p.FLASH);
23 23
24 info!("Reading..."); 24 info!("Reading...");
25 let mut buf = [0u8; 32]; 25 let mut buf = [0u8; 32];
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml
index 6baf17f36..f5673718d 100644
--- a/examples/stm32g0/Cargo.toml
+++ b/examples/stm32g0/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32g0-examples" 3name = "embassy-stm32g0-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index d8c05a979..ecda28805 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32g4-examples" 3name = "embassy-stm32g4-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index e725e03cb..1a05b9ecb 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32h7-examples" 3name = "embassy-stm32h7-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs
index 6682c64d5..ee86bdbf6 100644
--- a/examples/stm32h7/src/bin/flash.rs
+++ b/examples/stm32h7/src/bin/flash.rs
@@ -19,7 +19,7 @@ async fn main(_spawner: Spawner) {
19 // wait a bit before accessing the flash 19 // wait a bit before accessing the flash
20 Timer::after(Duration::from_millis(300)).await; 20 Timer::after(Duration::from_millis(300)).await;
21 21
22 let mut f = Flash::unlock(p.FLASH); 22 let mut f = Flash::new(p.FLASH);
23 23
24 info!("Reading..."); 24 info!("Reading...");
25 let mut buf = [0u8; 32]; 25 let mut buf = [0u8; 32];
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 7e61f0c19..7e1120f48 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32l0-examples" 3name = "embassy-stm32l0-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[features] 7[features]
7default = ["nightly"] 8default = ["nightly"]
diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs
index 867cb4d3e..ffe4fb10b 100644
--- a/examples/stm32l0/src/bin/flash.rs
+++ b/examples/stm32l0/src/bin/flash.rs
@@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
15 15
16 const ADDR: u32 = 0x26000; 16 const ADDR: u32 = 0x26000;
17 17
18 let mut f = Flash::unlock(p.FLASH); 18 let mut f = Flash::new(p.FLASH);
19 19
20 info!("Reading..."); 20 info!("Reading...");
21 let mut buf = [0u8; 8]; 21 let mut buf = [0u8; 8];
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml
index a943c73d2..9460febf5 100644
--- a/examples/stm32l1/Cargo.toml
+++ b/examples/stm32l1/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32l1-examples" 3name = "embassy-stm32l1-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs
index a76b9879f..476ed51a4 100644
--- a/examples/stm32l1/src/bin/flash.rs
+++ b/examples/stm32l1/src/bin/flash.rs
@@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
15 15
16 const ADDR: u32 = 0x26000; 16 const ADDR: u32 = 0x26000;
17 17
18 let mut f = Flash::unlock(p.FLASH); 18 let mut f = Flash::new(p.FLASH);
19 19
20 info!("Reading..."); 20 info!("Reading...");
21 let mut buf = [0u8; 8]; 21 let mut buf = [0u8; 8];
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml
index 2e2d07dc3..657605ebe 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32l4-examples" 3name = "embassy-stm32l4-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[features] 7[features]
7 8
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index 9ebab6476..63eac3ed2 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32l5-examples" 3name = "embassy-stm32l5-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[features] 7[features]
7 8
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml
index 164940586..3d704011b 100644
--- a/examples/stm32u5/Cargo.toml
+++ b/examples/stm32u5/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32u5-examples" 3name = "embassy-stm32u5-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index 923833e46..5b96fa191 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32wb-examples" 3name = "embassy-stm32wb-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 94e0fb830..c827d2b71 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32wl-examples" 3name = "embassy-stm32wl-examples"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs
index eb7489760..2a8880624 100644
--- a/examples/stm32wl/src/bin/flash.rs
+++ b/examples/stm32wl/src/bin/flash.rs
@@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
15 15
16 const ADDR: u32 = 0x36000; 16 const ADDR: u32 = 0x36000;
17 17
18 let mut f = Flash::unlock(p.FLASH); 18 let mut f = Flash::new(p.FLASH);
19 19
20 info!("Reading..."); 20 info!("Reading...");
21 let mut buf = [0u8; 8]; 21 let mut buf = [0u8; 8];
diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml
index ea61fb921..e0e799a34 100644
--- a/examples/wasm/Cargo.toml
+++ b/examples/wasm/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-wasm-example" 3name = "embassy-wasm-example"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[lib] 7[lib]
7crate-type = ["cdylib"] 8crate-type = ["cdylib"]
diff --git a/stm32-gen-features/Cargo.toml b/stm32-gen-features/Cargo.toml
index f92d127ea..f4aa08ebe 100644
--- a/stm32-gen-features/Cargo.toml
+++ b/stm32-gen-features/Cargo.toml
@@ -2,6 +2,7 @@
2name = "gen_features" 2name = "gen_features"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 8
diff --git a/stm32-metapac-gen/Cargo.toml b/stm32-metapac-gen/Cargo.toml
index 0ec2075f3..3c1dab57a 100644
--- a/stm32-metapac-gen/Cargo.toml
+++ b/stm32-metapac-gen/Cargo.toml
@@ -2,6 +2,7 @@
2name = "stm32-metapac-gen" 2name = "stm32-metapac-gen"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
5 6
6 7
7[dependencies] 8[dependencies]
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index 2745aef06..d6770d6e9 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-rp-tests" 3name = "embassy-rp-tests"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index daae3d464..bebbf557e 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "embassy-stm32-tests" 3name = "embassy-stm32-tests"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[features] 7[features]
7stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill 8stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index d9d6c9b26..696cfdafe 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -2,6 +2,7 @@
2edition = "2021" 2edition = "2021"
3name = "xtask" 3name = "xtask"
4version = "0.1.0" 4version = "0.1.0"
5license = "MIT OR Apache-2.0"
5 6
6[dependencies] 7[dependencies]
7anyhow = "1.0.43" 8anyhow = "1.0.43"