Compare commits
	
		
			38 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 4634316749 | ||
|  | 1b9925e3da | ||
|  | b4bc9ac028 | ||
|  | a3c99f0324 | ||
|  | 76f3a11f43 | ||
|  | d63c052f0e | ||
|  | 2c9f4bce01 | ||
|  | 74a6855f01 | ||
|  | f88aa9ce48 | ||
|  | 3a8a950bb8 | ||
|  | 02305451b1 | ||
|  | a42aef7759 | ||
|  | 142f42fe90 | ||
|  | 5844f5ce2d | ||
|  | e5fdd35bd6 | ||
|  | 0bd47c779b | ||
|  | 4481631326 | ||
|  | cf13f70ea9 | ||
|  | 6bdacb4f69 | ||
|  | ff3baf1e90 | ||
|  | d5dcbadbbe | ||
|  | 0ad0d27150 | ||
|  | 03d500f548 | ||
|  | 8b46343b34 | ||
|  | a3ea01473a | ||
|  | cf84c8bfd1 | ||
|  | 09d7950313 | ||
|  | 267cbaebe6 | ||
|  | 31fc337e2f | ||
|  | 135f350020 | ||
|  | 897663e023 | ||
|  | 2218d30c80 | ||
|  | f5c9e3baa6 | ||
|  | 47bac9df70 | ||
|  | 3efc3eee57 | ||
|  | bc65b8f7ec | ||
|  | 78f8e6112a | ||
|  | 5528c33649 | 
							
								
								
									
										3
									
								
								ci.sh
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								ci.sh
									
									
									
									
									
								
							| @@ -190,6 +190,7 @@ cargo batch  \ | ||||
|     --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ | ||||
|     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \ | ||||
|     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \ | ||||
|     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --out-dir out/tests/stm32f446re \ | ||||
|     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \ | ||||
|     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \ | ||||
|     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \ | ||||
| @@ -220,6 +221,8 @@ cargo batch  \ | ||||
| rm out/tests/stm32wb55rg/wpan_mac | ||||
| rm out/tests/stm32wb55rg/wpan_ble | ||||
|  | ||||
| # not in CI yet. | ||||
| rm -rf out/tests/stm32f446re | ||||
|  | ||||
| # unstable, I think it's running out of RAM? | ||||
| rm out/tests/stm32f207zg/eth | ||||
|   | ||||
| @@ -23,7 +23,7 @@ cortex-m = "0.7.6" | ||||
| cortex-m-rt = "0.7.0" | ||||
| futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } | ||||
|  | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.2" } | ||||
| num_enum = { version = "0.5.7", default-features = false } | ||||
|  | ||||
| [package.metadata.embassy_docs] | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)] | ||||
| #![feature(async_fn_in_trait, type_alias_impl_trait)] | ||||
| #![allow(stable_features, unknown_lints, async_fn_in_trait)] | ||||
| #![deny(unused_must_use)] | ||||
|  | ||||
|   | ||||
| @@ -1,54 +1,48 @@ | ||||
| macro_rules! nvram { | ||||
|     ($($s:literal,)*) => { | ||||
|         concat_bytes!($($s, b"\x00",)* b"\x00\x00") | ||||
|     }; | ||||
| } | ||||
|  | ||||
| pub static NVRAM: &'static [u8] = &*nvram!( | ||||
|     b"NVRAMRev=$Rev$", | ||||
|     b"manfid=0x2d0", | ||||
|     b"prodid=0x0727", | ||||
|     b"vendid=0x14e4", | ||||
|     b"devid=0x43e2", | ||||
|     b"boardtype=0x0887", | ||||
|     b"boardrev=0x1100", | ||||
|     b"boardnum=22", | ||||
|     b"macaddr=00:A0:50:b5:59:5e", | ||||
|     b"sromrev=11", | ||||
|     b"boardflags=0x00404001", | ||||
|     b"boardflags3=0x04000000", | ||||
|     b"xtalfreq=37400", | ||||
|     b"nocrc=1", | ||||
|     b"ag0=255", | ||||
|     b"aa2g=1", | ||||
|     b"ccode=ALL", | ||||
|     b"pa0itssit=0x20", | ||||
|     b"extpagain2g=0", | ||||
|     b"pa2ga0=-168,6649,-778", | ||||
|     b"AvVmid_c0=0x0,0xc8", | ||||
|     b"cckpwroffset0=5", | ||||
|     b"maxp2ga0=84", | ||||
|     b"txpwrbckof=6", | ||||
|     b"cckbw202gpo=0", | ||||
|     b"legofdmbw202gpo=0x66111111", | ||||
|     b"mcsbw202gpo=0x77711111", | ||||
|     b"propbw202gpo=0xdd", | ||||
|     b"ofdmdigfilttype=18", | ||||
|     b"ofdmdigfilttypebe=18", | ||||
|     b"papdmode=1", | ||||
|     b"papdvalidtest=1", | ||||
|     b"pacalidx2g=45", | ||||
|     b"papdepsoffset=-30", | ||||
|     b"papdendidx=58", | ||||
|     b"ltecxmux=0", | ||||
|     b"ltecxpadnum=0x0102", | ||||
|     b"ltecxfnsel=0x44", | ||||
|     b"ltecxgcigpio=0x01", | ||||
|     b"il0macaddr=00:90:4c:c5:12:38", | ||||
|     b"wl0id=0x431b", | ||||
|     b"deadman_to=0xffffffff", | ||||
|     b"muxenab=0x100", | ||||
|     b"spurconfig=0x3", | ||||
|     b"glitch_based_crsmin=1", | ||||
|     b"btc_mode=1", | ||||
| ); | ||||
| pub static NVRAM: &'static [u8] = b" | ||||
|     NVRAMRev=$Rev$\x00\ | ||||
|     manfid=0x2d0\x00\ | ||||
|     prodid=0x0727\x00\ | ||||
|     vendid=0x14e4\x00\ | ||||
|     devid=0x43e2\x00\ | ||||
|     boardtype=0x0887\x00\ | ||||
|     boardrev=0x1100\x00\ | ||||
|     boardnum=22\x00\ | ||||
|     macaddr=00:A0:50:b5:59:5e\x00\ | ||||
|     sromrev=11\x00\ | ||||
|     boardflags=0x00404001\x00\ | ||||
|     boardflags3=0x04000000\x00\ | ||||
|     xtalfreq=37400\x00\ | ||||
|     nocrc=1\x00\ | ||||
|     ag0=255\x00\ | ||||
|     aa2g=1\x00\ | ||||
|     ccode=ALL\x00\ | ||||
|     pa0itssit=0x20\x00\ | ||||
|     extpagain2g=0\x00\ | ||||
|     pa2ga0=-168,6649,-778\x00\ | ||||
|     AvVmid_c0=0x0,0xc8\x00\ | ||||
|     cckpwroffset0=5\x00\ | ||||
|     maxp2ga0=84\x00\ | ||||
|     txpwrbckof=6\x00\ | ||||
|     cckbw202gpo=0\x00\ | ||||
|     legofdmbw202gpo=0x66111111\x00\ | ||||
|     mcsbw202gpo=0x77711111\x00\ | ||||
|     propbw202gpo=0xdd\x00\ | ||||
|     ofdmdigfilttype=18\x00\ | ||||
|     ofdmdigfilttypebe=18\x00\ | ||||
|     papdmode=1\x00\ | ||||
|     papdvalidtest=1\x00\ | ||||
|     pacalidx2g=45\x00\ | ||||
|     papdepsoffset=-30\x00\ | ||||
|     papdendidx=58\x00\ | ||||
|     ltecxmux=0\x00\ | ||||
|     ltecxpadnum=0x0102\x00\ | ||||
|     ltecxfnsel=0x44\x00\ | ||||
|     ltecxgcigpio=0x01\x00\ | ||||
|     il0macaddr=00:90:4c:c5:12:38\x00\ | ||||
|     wl0id=0x431b\x00\ | ||||
|     deadman_to=0xffffffff\x00\ | ||||
|     muxenab=0x100\x00\ | ||||
|     spurconfig=0x3\x00\ | ||||
|     glitch_based_crsmin=1\x00\ | ||||
|     btc_mode=1\x00\ | ||||
|     \x00"; | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| * xref:getting_started.adoc[Getting started] | ||||
| ** xref:basic_application.adoc[Basic application] | ||||
| ** xref:project_structure.adoc[Project Structure] | ||||
| * xref:layer_by_layer.adoc[Bare metal to async] | ||||
| * xref:runtime.adoc[Executor] | ||||
| * xref:hal.adoc[HAL] | ||||
|   | ||||
| @@ -36,3 +36,102 @@ For Cortex-M targets, consider making sure that ALL of the following features ar | ||||
| * `nightly` | ||||
|  | ||||
| For Xtensa ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate]. | ||||
|  | ||||
| == Why is my binary so big? | ||||
|  | ||||
| The first step to managing your binary size is to set up your link:https://doc.rust-lang.org/cargo/reference/profiles.html[profiles]. | ||||
|  | ||||
| [source,toml] | ||||
| ---- | ||||
| [profile.release] | ||||
| debug = false | ||||
| lto = true | ||||
| opt-level = "s" | ||||
| incremental = true | ||||
| ---- | ||||
|  | ||||
| All of these flags are elaborated on in the Rust Book page linked above. | ||||
|  | ||||
| === My binary is still big... filled with `std::fmt` stuff! | ||||
|  | ||||
| This means your code is sufficiently complex that `panic!` invocation's formatting requirements could not be optimized out, despite your usage of `panic-halt` or `panic-reset`. | ||||
|  | ||||
| You can remedy this by adding the following to your `.cargo/config.toml`: | ||||
|  | ||||
| [source,toml] | ||||
| ---- | ||||
| [unstable] | ||||
| build-std = ["core"] | ||||
| build-std-features = ["panic_immediate_abort"] | ||||
| ---- | ||||
|  | ||||
| This replaces all panics with a `UDF` (undefined) instruction. | ||||
|  | ||||
| Depending on your chipset, this will exhibit different behavior. | ||||
|  | ||||
| Refer to the spec for your chipset, but for `thumbv6m`, it results in a hardfault. Which can be configured like so: | ||||
|  | ||||
| [source,rust] | ||||
| ---- | ||||
| #[exception] | ||||
| unsafe fn HardFault(_frame: &ExceptionFrame) -> ! { | ||||
|     SCB::sys_reset() // <- you could do something other than reset | ||||
| } | ||||
| ---- | ||||
|  | ||||
| Refer to cortex-m's link:https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.exception.html[exception handling] for more info. | ||||
|  | ||||
| == `embassy-time` throws linker errors | ||||
|  | ||||
| If you see linker error like this: | ||||
|  | ||||
| [source,text] | ||||
| ---- | ||||
|   = note: rust-lld: error: undefined symbol: _embassy_time_now | ||||
|           >>> referenced by driver.rs:127 (src/driver.rs:127) | ||||
|           >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::now::hefb1f99d6e069842) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib | ||||
|  | ||||
|           rust-lld: error: undefined symbol: _embassy_time_allocate_alarm | ||||
|           >>> referenced by driver.rs:134 (src/driver.rs:134) | ||||
|           >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::allocate_alarm::hf5145b6bd46706b2) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib | ||||
|  | ||||
|           rust-lld: error: undefined symbol: _embassy_time_set_alarm_callback | ||||
|           >>> referenced by driver.rs:139 (src/driver.rs:139) | ||||
|           >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::set_alarm_callback::h24f92388d96eafd2) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib | ||||
|  | ||||
|           rust-lld: error: undefined symbol: _embassy_time_set_alarm | ||||
|           >>> referenced by driver.rs:144 (src/driver.rs:144) | ||||
|           >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::set_alarm::h530a5b1f444a6d5b) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib | ||||
| ---- | ||||
|  | ||||
| You probably need to enable a time driver for your HAL (not in `embassy-time`!). For example with `embassy-stm32`, you might need to enable `time-driver-any`: | ||||
|  | ||||
| [source,toml] | ||||
| ---- | ||||
| [dependencies.embassy-stm32] | ||||
| version = "0.1.0" | ||||
| features = [ | ||||
|     # ... | ||||
|     "time-driver-any", # Add this line! | ||||
|     # ... | ||||
| ] | ||||
| ---- | ||||
|  | ||||
| == Error: `Only one package in the dependency graph may specify the same links value.` | ||||
|  | ||||
| You have multiple versions of the same crate in your dependency tree. This means that some of your | ||||
| embassy crates are coming from crates.io, and some from git, each of them pulling in a different set | ||||
| of dependencies. | ||||
|  | ||||
| To resolve this issue, make sure to only use a single source for all your embassy crates! To do this, | ||||
| you should patch your dependencies to use git sources using `[patch.crates.io]` and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`. | ||||
|  | ||||
| Example: | ||||
|  | ||||
| [source,toml] | ||||
| ---- | ||||
| [patch.crates-io] | ||||
| embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } | ||||
| ---- | ||||
|  | ||||
| Note that the git revision should match any other embassy patches or git dependencies that you are using! | ||||
|   | ||||
							
								
								
									
										80
									
								
								docs/modules/ROOT/pages/project_structure.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								docs/modules/ROOT/pages/project_structure.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| = Project Structure | ||||
|  | ||||
| There are many ways to configure embassy and its components for your exact application. The link:https://github.com/embassy-rs/embassy/tree/main/examples[examples] directory for each chipset demonstrates how your project structure should look. Let's break it down: | ||||
|  | ||||
| The toplevel file structure of your project should look like this: | ||||
| [source,plain] | ||||
| ---- | ||||
| {} = Maybe | ||||
|  | ||||
| my-project | ||||
| |- .cargo | ||||
| |  |- config.toml | ||||
| |- src | ||||
| |  |- main.rs | ||||
| |- build.rs | ||||
| |- Cargo.toml | ||||
| |- {memory.x} | ||||
| |- rust-toolchain.toml | ||||
| ---- | ||||
|  | ||||
| === .cargo/config.toml | ||||
|  | ||||
| This directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device. | ||||
|  | ||||
| Here is a minimal example: | ||||
|  | ||||
| [source,toml] | ||||
| ---- | ||||
| [target.thumbv6m-none-eabi] # <-change for your platform | ||||
| runner = 'probe-rs run --chip STM32F031K6Tx' # <- change for your chip | ||||
|  | ||||
| [build] | ||||
| target = "thumbv6m-none-eabi" # <-change for your platform | ||||
|  | ||||
| [env] | ||||
| DEFMT_LOG = "trace" # <- can change to info, warn, or error | ||||
| ---- | ||||
|  | ||||
| === build.rs | ||||
|  | ||||
| This is the build script for your project. It links defmt (what is defmt?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example]. | ||||
|  | ||||
| === Cargo.toml | ||||
|  | ||||
| This is your manifest file, where you can configure all of the embassy components to use the features you need. | ||||
|  | ||||
| TODO: someone should exhaustively describe every feature for every component! | ||||
|  | ||||
| === memory.x | ||||
|  | ||||
| This file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x. | ||||
|  | ||||
| Here is an example for using S140 with an nRF52840: | ||||
|  | ||||
| [source,x] | ||||
| ---- | ||||
| MEMORY | ||||
| { | ||||
|   /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||||
|   /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */ | ||||
|   FLASH : ORIGIN = 0x00027000, LENGTH = 868K | ||||
|   RAM : ORIGIN = 0x20020000, LENGTH = 128K | ||||
| } | ||||
| ---- | ||||
|  | ||||
| === rust-toolchain.toml | ||||
|  | ||||
| This file configures the rust version and configuration to use. | ||||
|  | ||||
| A minimal example: | ||||
|  | ||||
| [source,toml] | ||||
| ---- | ||||
| [toolchain] | ||||
| channel = "nightly-2023-08-19" # <- as of writing, this is the exact rust version embassy uses | ||||
| components = [ "rust-src", "rustfmt" ] # <- optionally add "llvm-tools-preview" for some extra features like "cargo size" | ||||
| targets = [ | ||||
|     "thumbv6m-none-eabi" # <-change for your platform | ||||
| ] | ||||
| ---- | ||||
| @@ -25,8 +25,8 @@ embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true } | ||||
| embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ | ||||
|     "unproven", | ||||
| ] } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1", optional = true } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2", optional = true } | ||||
| embedded-storage = "0.3.0" | ||||
| embedded-storage-async = { version = "0.4.0", optional = true } | ||||
| nb = "1.0.0" | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| #![cfg_attr(not(feature = "std"), no_std)] | ||||
| #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))] | ||||
| #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] | ||||
| #![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))] | ||||
| #![warn(missing_docs)] | ||||
|  | ||||
|   | ||||
| @@ -63,22 +63,36 @@ where | ||||
|     CS: OutputPin, | ||||
| { | ||||
|     async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { | ||||
|         if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) { | ||||
|             return Err(SpiDeviceError::DelayNotSupported); | ||||
|         } | ||||
|  | ||||
|         let mut bus = self.bus.lock().await; | ||||
|         self.cs.set_low().map_err(SpiDeviceError::Cs)?; | ||||
|  | ||||
|         let op_res: Result<(), BUS::Error> = try { | ||||
|         let op_res = 'ops: { | ||||
|             for op in operations { | ||||
|                 match op { | ||||
|                     Operation::Read(buf) => bus.read(buf).await?, | ||||
|                     Operation::Write(buf) => bus.write(buf).await?, | ||||
|                     Operation::Transfer(read, write) => bus.transfer(read, write).await?, | ||||
|                     Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, | ||||
|                 let res = match op { | ||||
|                     Operation::Read(buf) => bus.read(buf).await, | ||||
|                     Operation::Write(buf) => bus.write(buf).await, | ||||
|                     Operation::Transfer(read, write) => bus.transfer(read, write).await, | ||||
|                     Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await, | ||||
|                     #[cfg(not(feature = "time"))] | ||||
|                     Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported), | ||||
|                     Operation::DelayNs(_) => unreachable!(), | ||||
|                     #[cfg(feature = "time")] | ||||
|                     Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await, | ||||
|                     Operation::DelayNs(ns) => match bus.flush().await { | ||||
|                         Err(e) => Err(e), | ||||
|                         Ok(()) => { | ||||
|                             embassy_time::Timer::after_nanos(*ns as _).await; | ||||
|                             Ok(()) | ||||
|                         } | ||||
|                     }, | ||||
|                 }; | ||||
|                 if let Err(e) = res { | ||||
|                     break 'ops Err(e); | ||||
|                 } | ||||
|             } | ||||
|             Ok(()) | ||||
|         }; | ||||
|  | ||||
|         // On failure, it's important to still flush and deassert CS. | ||||
| @@ -127,23 +141,37 @@ where | ||||
|     CS: OutputPin, | ||||
| { | ||||
|     async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { | ||||
|         if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) { | ||||
|             return Err(SpiDeviceError::DelayNotSupported); | ||||
|         } | ||||
|  | ||||
|         let mut bus = self.bus.lock().await; | ||||
|         bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?; | ||||
|         self.cs.set_low().map_err(SpiDeviceError::Cs)?; | ||||
|  | ||||
|         let op_res: Result<(), BUS::Error> = try { | ||||
|         let op_res = 'ops: { | ||||
|             for op in operations { | ||||
|                 match op { | ||||
|                     Operation::Read(buf) => bus.read(buf).await?, | ||||
|                     Operation::Write(buf) => bus.write(buf).await?, | ||||
|                     Operation::Transfer(read, write) => bus.transfer(read, write).await?, | ||||
|                     Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, | ||||
|                 let res = match op { | ||||
|                     Operation::Read(buf) => bus.read(buf).await, | ||||
|                     Operation::Write(buf) => bus.write(buf).await, | ||||
|                     Operation::Transfer(read, write) => bus.transfer(read, write).await, | ||||
|                     Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await, | ||||
|                     #[cfg(not(feature = "time"))] | ||||
|                     Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported), | ||||
|                     Operation::DelayNs(_) => unreachable!(), | ||||
|                     #[cfg(feature = "time")] | ||||
|                     Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await, | ||||
|                     Operation::DelayNs(ns) => match bus.flush().await { | ||||
|                         Err(e) => Err(e), | ||||
|                         Ok(()) => { | ||||
|                             embassy_time::Timer::after_nanos(*ns as _).await; | ||||
|                             Ok(()) | ||||
|                         } | ||||
|                     }, | ||||
|                 }; | ||||
|                 if let Err(e) = res { | ||||
|                     break 'ops Err(e); | ||||
|                 } | ||||
|             } | ||||
|             Ok(()) | ||||
|         }; | ||||
|  | ||||
|         // On failure, it's important to still flush and deassert CS. | ||||
|   | ||||
| @@ -55,6 +55,10 @@ where | ||||
|     CS: OutputPin, | ||||
| { | ||||
|     fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { | ||||
|         if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) { | ||||
|             return Err(SpiDeviceError::DelayNotSupported); | ||||
|         } | ||||
|  | ||||
|         self.bus.lock(|bus| { | ||||
|             let mut bus = bus.borrow_mut(); | ||||
|             self.cs.set_low().map_err(SpiDeviceError::Cs)?; | ||||
| @@ -65,10 +69,10 @@ where | ||||
|                 Operation::Transfer(read, write) => bus.transfer(read, write), | ||||
|                 Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), | ||||
|                 #[cfg(not(feature = "time"))] | ||||
|                 Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported), | ||||
|                 Operation::DelayNs(_) => unreachable!(), | ||||
|                 #[cfg(feature = "time")] | ||||
|                 Operation::DelayUs(us) => { | ||||
|                     embassy_time::block_for(embassy_time::Duration::from_micros(*us as _)); | ||||
|                 Operation::DelayNs(ns) => { | ||||
|                     embassy_time::block_for(embassy_time::Duration::from_nanos(*ns as _)); | ||||
|                     Ok(()) | ||||
|                 } | ||||
|             }); | ||||
| @@ -161,6 +165,10 @@ where | ||||
|     CS: OutputPin, | ||||
| { | ||||
|     fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { | ||||
|         if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) { | ||||
|             return Err(SpiDeviceError::DelayNotSupported); | ||||
|         } | ||||
|  | ||||
|         self.bus.lock(|bus| { | ||||
|             let mut bus = bus.borrow_mut(); | ||||
|             bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?; | ||||
| @@ -172,10 +180,10 @@ where | ||||
|                 Operation::Transfer(read, write) => bus.transfer(read, write), | ||||
|                 Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), | ||||
|                 #[cfg(not(feature = "time"))] | ||||
|                 Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported), | ||||
|                 Operation::DelayNs(_) => unreachable!(), | ||||
|                 #[cfg(feature = "time")] | ||||
|                 Operation::DelayUs(us) => { | ||||
|                     embassy_time::block_for(embassy_time::Duration::from_micros(*us as _)); | ||||
|                 Operation::DelayNs(ns) => { | ||||
|                     embassy_time::block_for(embassy_time::Duration::from_nanos(*ns as _)); | ||||
|                     Ok(()) | ||||
|                 } | ||||
|             }); | ||||
|   | ||||
| @@ -39,8 +39,8 @@ pub enum SpiDeviceError<BUS, CS> { | ||||
|     Spi(BUS), | ||||
|     /// Setting the value of the Chip Select (CS) pin failed. | ||||
|     Cs(CS), | ||||
|     /// DelayUs operations are not supported when the `time` Cargo feature is not enabled. | ||||
|     DelayUsNotSupported, | ||||
|     /// Delay operations are not supported when the `time` Cargo feature is not enabled. | ||||
|     DelayNotSupported, | ||||
|     /// The SPI bus could not be configured. | ||||
|     Config, | ||||
| } | ||||
| @@ -54,7 +54,7 @@ where | ||||
|         match self { | ||||
|             Self::Spi(e) => e.kind(), | ||||
|             Self::Cs(_) => spi::ErrorKind::Other, | ||||
|             Self::DelayUsNotSupported => spi::ErrorKind::Other, | ||||
|             Self::DelayNotSupported => spi::ErrorKind::Other, | ||||
|             Self::Config => spi::ErrorKind::Other, | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,31 +0,0 @@ | ||||
| [package] | ||||
| name = "embassy-lora" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| license = "MIT OR Apache-2.0" | ||||
|  | ||||
| [package.metadata.embassy_docs] | ||||
| src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/" | ||||
| src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" | ||||
| features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/unstable-pac", "time", "defmt"] | ||||
| target = "thumbv7em-none-eabi" | ||||
|  | ||||
| [features] | ||||
| stm32wl = ["dep:embassy-stm32"] | ||||
| time = ["embassy-time", "lorawan-device"] | ||||
| defmt = ["dep:defmt", "lorawan-device/defmt"] | ||||
|  | ||||
| [dependencies] | ||||
|  | ||||
| defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4.14", optional = true } | ||||
|  | ||||
| embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true } | ||||
| embassy-sync = { version = "0.4.0", path = "../embassy-sync" } | ||||
| embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-hal = { version = "0.2", features = ["unproven"] } | ||||
|  | ||||
| futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } | ||||
| lora-phy = { version = "2" } | ||||
| lorawan-device = { version = "0.11.0", default-features = false, features = ["async"], optional = true } | ||||
| @@ -1,258 +0,0 @@ | ||||
| #![macro_use] | ||||
| #![allow(unused_macros)] | ||||
|  | ||||
| use core::fmt::{Debug, Display, LowerHex}; | ||||
|  | ||||
| #[cfg(all(feature = "defmt", feature = "log"))] | ||||
| compile_error!("You may not enable both `defmt` and `log` features."); | ||||
|  | ||||
| macro_rules! assert { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::assert!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::assert!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! assert_eq { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::assert_eq!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::assert_eq!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! assert_ne { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::assert_ne!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::assert_ne!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! debug_assert { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::debug_assert!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::debug_assert!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! debug_assert_eq { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::debug_assert_eq!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::debug_assert_eq!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! debug_assert_ne { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::debug_assert_ne!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::debug_assert_ne!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! todo { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::todo!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::todo!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::core::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unreachable { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unreachable!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! panic { | ||||
|     ($($x:tt)*) => { | ||||
|         { | ||||
|             #[cfg(not(feature = "defmt"))] | ||||
|             ::core::panic!($($x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::panic!($($x)*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! trace { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::trace!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::trace!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! debug { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::debug!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::debug!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! info { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::info!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::info!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! warn { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::warn!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::warn!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| macro_rules! error { | ||||
|     ($s:literal $(, $x:expr)* $(,)?) => { | ||||
|         { | ||||
|             #[cfg(feature = "log")] | ||||
|             ::log::error!($s $(, $x)*); | ||||
|             #[cfg(feature = "defmt")] | ||||
|             ::defmt::error!($s $(, $x)*); | ||||
|             #[cfg(not(any(feature = "log", feature="defmt")))] | ||||
|             let _ = ($( & $x ),*); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| macro_rules! unwrap { | ||||
|     ($($x:tt)*) => { | ||||
|         ::defmt::unwrap!($($x)*) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[cfg(not(feature = "defmt"))] | ||||
| macro_rules! unwrap { | ||||
|     ($arg:expr) => { | ||||
|         match $crate::fmt::Try::into_result($arg) { | ||||
|             ::core::result::Result::Ok(t) => t, | ||||
|             ::core::result::Result::Err(e) => { | ||||
|                 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||||
|         match $crate::fmt::Try::into_result($arg) { | ||||
|             ::core::result::Result::Ok(t) => t, | ||||
|             ::core::result::Result::Err(e) => { | ||||
|                 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| pub struct NoneError; | ||||
|  | ||||
| pub trait Try { | ||||
|     type Ok; | ||||
|     type Error; | ||||
|     fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||||
| } | ||||
|  | ||||
| impl<T> Try for Option<T> { | ||||
|     type Ok = T; | ||||
|     type Error = NoneError; | ||||
|  | ||||
|     #[inline] | ||||
|     fn into_result(self) -> Result<T, NoneError> { | ||||
|         self.ok_or(NoneError) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T, E> Try for Result<T, E> { | ||||
|     type Ok = T; | ||||
|     type Error = E; | ||||
|  | ||||
|     #[inline] | ||||
|     fn into_result(self) -> Self { | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(unused)] | ||||
| pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||||
|  | ||||
| impl<'a> Debug for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Display for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> LowerHex for Bytes<'a> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         write!(f, "{:#02x?}", self.0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "defmt")] | ||||
| impl<'a> defmt::Format for Bytes<'a> { | ||||
|     fn format(&self, fmt: defmt::Formatter) { | ||||
|         defmt::write!(fmt, "{:02x}", self.0) | ||||
|     } | ||||
| } | ||||
| @@ -1,317 +0,0 @@ | ||||
| #[cfg(feature = "stm32wl")] | ||||
| use embassy_stm32::interrupt; | ||||
| #[cfg(feature = "stm32wl")] | ||||
| use embassy_stm32::interrupt::InterruptExt; | ||||
| #[cfg(feature = "stm32wl")] | ||||
| use embassy_stm32::pac; | ||||
| #[cfg(feature = "stm32wl")] | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| #[cfg(feature = "stm32wl")] | ||||
| use embassy_sync::signal::Signal; | ||||
| use embedded_hal::digital::v2::OutputPin; | ||||
| use embedded_hal_async::delay::DelayUs; | ||||
| use embedded_hal_async::digital::Wait; | ||||
| use lora_phy::mod_params::RadioError::*; | ||||
| use lora_phy::mod_params::{BoardType, RadioError}; | ||||
| use lora_phy::mod_traits::InterfaceVariant; | ||||
|  | ||||
| /// Interrupt handler. | ||||
| #[cfg(feature = "stm32wl")] | ||||
| pub struct InterruptHandler {} | ||||
|  | ||||
| #[cfg(feature = "stm32wl")] | ||||
| impl interrupt::typelevel::Handler<interrupt::typelevel::SUBGHZ_RADIO> for InterruptHandler { | ||||
|     unsafe fn on_interrupt() { | ||||
|         interrupt::SUBGHZ_RADIO.disable(); | ||||
|         IRQ_SIGNAL.signal(()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "stm32wl")] | ||||
| static IRQ_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new(); | ||||
|  | ||||
| #[cfg(feature = "stm32wl")] | ||||
| /// Base for the InterfaceVariant implementation for an stm32wl/sx1262 combination | ||||
| pub struct Stm32wlInterfaceVariant<CTRL> { | ||||
|     board_type: BoardType, | ||||
|     rf_switch_rx: Option<CTRL>, | ||||
|     rf_switch_tx: Option<CTRL>, | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "stm32wl")] | ||||
| impl<'a, CTRL> Stm32wlInterfaceVariant<CTRL> | ||||
| where | ||||
|     CTRL: OutputPin, | ||||
| { | ||||
|     /// Create an InterfaceVariant instance for an stm32wl/sx1262 combination | ||||
|     pub fn new( | ||||
|         _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::SUBGHZ_RADIO, InterruptHandler>, | ||||
|         rf_switch_rx: Option<CTRL>, | ||||
|         rf_switch_tx: Option<CTRL>, | ||||
|     ) -> Result<Self, RadioError> { | ||||
|         interrupt::SUBGHZ_RADIO.disable(); | ||||
|         Ok(Self { | ||||
|             board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board | ||||
|             rf_switch_rx, | ||||
|             rf_switch_tx, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "stm32wl")] | ||||
| impl<CTRL> InterfaceVariant for Stm32wlInterfaceVariant<CTRL> | ||||
| where | ||||
|     CTRL: OutputPin, | ||||
| { | ||||
|     fn set_board_type(&mut self, board_type: BoardType) { | ||||
|         self.board_type = board_type; | ||||
|     } | ||||
|     async fn set_nss_low(&mut self) -> Result<(), RadioError> { | ||||
|         pac::PWR.subghzspicr().modify(|w| w.set_nss(false)); | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn set_nss_high(&mut self) -> Result<(), RadioError> { | ||||
|         pac::PWR.subghzspicr().modify(|w| w.set_nss(true)); | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> { | ||||
|         pac::RCC.csr().modify(|w| w.set_rfrst(true)); | ||||
|         pac::RCC.csr().modify(|w| w.set_rfrst(false)); | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn wait_on_busy(&mut self) -> Result<(), RadioError> { | ||||
|         while pac::PWR.sr2().read().rfbusys() {} | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     async fn await_irq(&mut self) -> Result<(), RadioError> { | ||||
|         unsafe { interrupt::SUBGHZ_RADIO.enable() }; | ||||
|         IRQ_SIGNAL.wait().await; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_high().map_err(|_| RfSwitchRx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_high().map_err(|_| RfSwitchTx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     async fn disable_rf_switch(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchTx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Base for the InterfaceVariant implementation for an stm32l0/sx1276 combination | ||||
| pub struct Stm32l0InterfaceVariant<CTRL, WAIT> { | ||||
|     board_type: BoardType, | ||||
|     nss: CTRL, | ||||
|     reset: CTRL, | ||||
|     irq: WAIT, | ||||
|     rf_switch_rx: Option<CTRL>, | ||||
|     rf_switch_tx: Option<CTRL>, | ||||
| } | ||||
|  | ||||
| impl<CTRL, WAIT> Stm32l0InterfaceVariant<CTRL, WAIT> | ||||
| where | ||||
|     CTRL: OutputPin, | ||||
|     WAIT: Wait, | ||||
| { | ||||
|     /// Create an InterfaceVariant instance for an stm32l0/sx1276 combination | ||||
|     pub fn new( | ||||
|         nss: CTRL, | ||||
|         reset: CTRL, | ||||
|         irq: WAIT, | ||||
|         rf_switch_rx: Option<CTRL>, | ||||
|         rf_switch_tx: Option<CTRL>, | ||||
|     ) -> Result<Self, RadioError> { | ||||
|         Ok(Self { | ||||
|             board_type: BoardType::Stm32l0Sx1276, // updated when associated with a specific LoRa board | ||||
|             nss, | ||||
|             reset, | ||||
|             irq, | ||||
|             rf_switch_rx, | ||||
|             rf_switch_tx, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<CTRL, WAIT> InterfaceVariant for Stm32l0InterfaceVariant<CTRL, WAIT> | ||||
| where | ||||
|     CTRL: OutputPin, | ||||
|     WAIT: Wait, | ||||
| { | ||||
|     fn set_board_type(&mut self, board_type: BoardType) { | ||||
|         self.board_type = board_type; | ||||
|     } | ||||
|     async fn set_nss_low(&mut self) -> Result<(), RadioError> { | ||||
|         self.nss.set_low().map_err(|_| NSS) | ||||
|     } | ||||
|     async fn set_nss_high(&mut self) -> Result<(), RadioError> { | ||||
|         self.nss.set_high().map_err(|_| NSS) | ||||
|     } | ||||
|     async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> { | ||||
|         delay.delay_ms(10).await; | ||||
|         self.reset.set_low().map_err(|_| Reset)?; | ||||
|         delay.delay_ms(10).await; | ||||
|         self.reset.set_high().map_err(|_| Reset)?; | ||||
|         delay.delay_ms(10).await; | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn wait_on_busy(&mut self) -> Result<(), RadioError> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn await_irq(&mut self) -> Result<(), RadioError> { | ||||
|         self.irq.wait_for_high().await.map_err(|_| Irq) | ||||
|     } | ||||
|  | ||||
|     async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_high().map_err(|_| RfSwitchRx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_high().map_err(|_| RfSwitchTx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     async fn disable_rf_switch(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchTx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Base for the InterfaceVariant implementation for a generic Sx126x LoRa board | ||||
| pub struct GenericSx126xInterfaceVariant<CTRL, WAIT> { | ||||
|     board_type: BoardType, | ||||
|     nss: CTRL, | ||||
|     reset: CTRL, | ||||
|     dio1: WAIT, | ||||
|     busy: WAIT, | ||||
|     rf_switch_rx: Option<CTRL>, | ||||
|     rf_switch_tx: Option<CTRL>, | ||||
| } | ||||
|  | ||||
| impl<CTRL, WAIT> GenericSx126xInterfaceVariant<CTRL, WAIT> | ||||
| where | ||||
|     CTRL: OutputPin, | ||||
|     WAIT: Wait, | ||||
| { | ||||
|     /// Create an InterfaceVariant instance for an nrf52840/sx1262 combination | ||||
|     pub fn new( | ||||
|         nss: CTRL, | ||||
|         reset: CTRL, | ||||
|         dio1: WAIT, | ||||
|         busy: WAIT, | ||||
|         rf_switch_rx: Option<CTRL>, | ||||
|         rf_switch_tx: Option<CTRL>, | ||||
|     ) -> Result<Self, RadioError> { | ||||
|         Ok(Self { | ||||
|             board_type: BoardType::Rak4631Sx1262, // updated when associated with a specific LoRa board | ||||
|             nss, | ||||
|             reset, | ||||
|             dio1, | ||||
|             busy, | ||||
|             rf_switch_rx, | ||||
|             rf_switch_tx, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<CTRL, WAIT> InterfaceVariant for GenericSx126xInterfaceVariant<CTRL, WAIT> | ||||
| where | ||||
|     CTRL: OutputPin, | ||||
|     WAIT: Wait, | ||||
| { | ||||
|     fn set_board_type(&mut self, board_type: BoardType) { | ||||
|         self.board_type = board_type; | ||||
|     } | ||||
|     async fn set_nss_low(&mut self) -> Result<(), RadioError> { | ||||
|         self.nss.set_low().map_err(|_| NSS) | ||||
|     } | ||||
|     async fn set_nss_high(&mut self) -> Result<(), RadioError> { | ||||
|         self.nss.set_high().map_err(|_| NSS) | ||||
|     } | ||||
|     async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> { | ||||
|         delay.delay_ms(10).await; | ||||
|         self.reset.set_low().map_err(|_| Reset)?; | ||||
|         delay.delay_ms(20).await; | ||||
|         self.reset.set_high().map_err(|_| Reset)?; | ||||
|         delay.delay_ms(10).await; | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn wait_on_busy(&mut self) -> Result<(), RadioError> { | ||||
|         self.busy.wait_for_low().await.map_err(|_| Busy) | ||||
|     } | ||||
|     async fn await_irq(&mut self) -> Result<(), RadioError> { | ||||
|         self.dio1.wait_for_high().await.map_err(|_| DIO1)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_high().map_err(|_| RfSwitchRx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_high().map_err(|_| RfSwitchTx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
|     async fn disable_rf_switch(&mut self) -> Result<(), RadioError> { | ||||
|         match &mut self.rf_switch_rx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, | ||||
|             None => (), | ||||
|         }; | ||||
|         match &mut self.rf_switch_tx { | ||||
|             Some(pin) => pin.set_low().map_err(|_| RfSwitchTx), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,40 +0,0 @@ | ||||
| #![no_std] | ||||
| #![feature(async_fn_in_trait, impl_trait_projections)] | ||||
| #![allow(stable_features, unknown_lints, async_fn_in_trait)] | ||||
| //! embassy-lora holds LoRa-specific functionality. | ||||
|  | ||||
| pub(crate) mod fmt; | ||||
|  | ||||
| /// interface variants required by the external lora physical layer crate (lora-phy) | ||||
| pub mod iv; | ||||
|  | ||||
| #[cfg(feature = "time")] | ||||
| use embassy_time::{Duration, Instant, Timer}; | ||||
|  | ||||
| /// A convenience timer to use with the LoRaWAN crate | ||||
| #[cfg(feature = "time")] | ||||
| pub struct LoraTimer { | ||||
|     start: Instant, | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "time")] | ||||
| impl LoraTimer { | ||||
|     pub fn new() -> Self { | ||||
|         Self { start: Instant::now() } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "time")] | ||||
| impl lorawan_device::async_device::radio::Timer for LoraTimer { | ||||
|     fn reset(&mut self) { | ||||
|         self.start = Instant::now(); | ||||
|     } | ||||
|  | ||||
|     async fn at(&mut self, millis: u64) { | ||||
|         Timer::at(self.start + Duration::from_millis(millis)).await | ||||
|     } | ||||
|  | ||||
|     async fn delay_ms(&mut self, millis: u64) { | ||||
|         Timer::after_millis(millis).await | ||||
|     } | ||||
| } | ||||
| @@ -13,16 +13,16 @@ edition = "2021" | ||||
| heapless = "0.8" | ||||
| defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4", default-features = false, optional = true } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2" } | ||||
| embedded-hal-bus = { version = "=0.1.0-rc.2", features = ["async"] } | ||||
| embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" } | ||||
| embassy-time = { version = "0.1.5", path = "../embassy-time" } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
| bitfield = "0.14.0" | ||||
|  | ||||
| [dev-dependencies] | ||||
| embedded-hal-mock = { version = "=0.10.0-rc.1", features = ["embedded-hal-async", "eh1"] } | ||||
| embedded-hal-mock = { git = "https://github.com/Dirbaio/embedded-hal-mock", rev = "c5c4dca18e043e6386aee02173f61a65fea3981e", features = ["embedded-hal-async", "eh1"] } | ||||
| crc = "3.0.1" | ||||
| env_logger = "0.10" | ||||
| critical-section = { version = "1.1.2", features = ["std"] } | ||||
|   | ||||
| @@ -729,7 +729,7 @@ mod tests { | ||||
|     use core::convert::Infallible; | ||||
|  | ||||
|     use embedded_hal_1::digital::{ErrorType, OutputPin}; | ||||
|     use embedded_hal_async::delay::DelayUs; | ||||
|     use embedded_hal_async::delay::DelayNs; | ||||
|     use embedded_hal_bus::spi::ExclusiveDevice; | ||||
|     use embedded_hal_mock::common::Generic; | ||||
|     use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction}; | ||||
| @@ -760,7 +760,11 @@ mod tests { | ||||
|     // see https://github.com/rust-embedded/embedded-hal/pull/462#issuecomment-1560014426 | ||||
|     struct MockDelay {} | ||||
|  | ||||
|     impl DelayUs for MockDelay { | ||||
|     impl DelayNs for MockDelay { | ||||
|         async fn delay_ns(&mut self, _ns: u32) { | ||||
|             todo!() | ||||
|         } | ||||
|  | ||||
|         async fn delay_us(&mut self, _us: u32) { | ||||
|             todo!() | ||||
|         } | ||||
|   | ||||
| @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" | ||||
| edition = "2021" | ||||
|  | ||||
| [dependencies] | ||||
| embedded-hal = { version = "1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-hal = { version = "1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2" } | ||||
| embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } | ||||
| embassy-time = { version = "0.1.5", path = "../embassy-time" } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
|   | ||||
| @@ -12,8 +12,8 @@ embassy-sync = { version = "0.4.0", path = "../embassy-sync"} | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | ||||
| embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"} | ||||
|  | ||||
| embedded-hal = { version = "1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-hal = { version = "1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2" } | ||||
|  | ||||
| noproto = { git="https://github.com/embassy-rs/noproto", rev = "f5e6d1f325b6ad4e344f60452b09576e24671f62", default-features = false, features = ["derive"] } | ||||
| #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } | ||||
|   | ||||
| @@ -15,7 +15,7 @@ log = ["dep:log", "ppproto/log"] | ||||
| defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4.14", optional = true } | ||||
|  | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
| embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
| ppproto = { version = "0.1.2"} | ||||
|   | ||||
| @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" | ||||
| edition = "2021" | ||||
|  | ||||
| [dependencies] | ||||
| embedded-hal = { version = "1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-hal = { version = "1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2" } | ||||
| embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" } | ||||
| embassy-time = { version = "0.1.5", path = "../embassy-time" } | ||||
| embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | ||||
|   | ||||
| @@ -54,7 +54,7 @@ smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp.git", rev = "b57e2f9e70 | ||||
| embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } | ||||
| embassy-time = { version = "0.1.5", path = "../embassy-time" } | ||||
| embassy-sync = { version = "0.4.0", path = "../embassy-sync" } | ||||
| embedded-io-async = { version = "0.6.0", optional = true } | ||||
| embedded-io-async = { version = "0.6.1", optional = true } | ||||
|  | ||||
| managed = { version = "0.8.0", default-features = false, features = [ "map" ] } | ||||
| heapless = { version = "0.8", default-features = false } | ||||
| @@ -63,4 +63,4 @@ generic-array = { version = "0.14.4", default-features = false } | ||||
| stable_deref_trait = { version = "1.2.0", default-features = false } | ||||
| futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } | ||||
| atomic-pool = "1.0" | ||||
| embedded-nal-async = { version = "0.7", optional = true } | ||||
| embedded-nal-async = { version = "0.7.1", optional = true } | ||||
|   | ||||
| @@ -101,10 +101,10 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } | ||||
| embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } | ||||
|  | ||||
| embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2", optional = true} | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2", optional = true} | ||||
| embedded-io = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.0", optional = true } | ||||
| embedded-io-async = { version = "0.6.1", optional = true } | ||||
|  | ||||
| defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4.14", optional = true } | ||||
|   | ||||
| @@ -76,7 +76,7 @@ critical-section = "1.1" | ||||
| futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||||
| chrono = { version = "0.4", default-features = false, optional = true } | ||||
| embedded-io = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.0", optional = true } | ||||
| embedded-io-async = { version = "0.6.1", optional = true } | ||||
| embedded-storage = { version = "0.3" } | ||||
| embedded-storage-async = { version = "0.4.0", optional = true } | ||||
| rand_core = "0.6.4" | ||||
| @@ -85,9 +85,9 @@ fixed = "1.23.1" | ||||
| rp-pac = { version = "6" } | ||||
|  | ||||
| embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2", optional = true} | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2", optional = true} | ||||
| embedded-hal-nb = { version = "=1.0.0-rc.2", optional = true} | ||||
|  | ||||
| pio-proc = {version= "0.2" } | ||||
| pio = {version= "0.2.1" } | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
|     any(feature = "ble", feature = "mac"), | ||||
|     allow(stable_features, unknown_lints, async_fn_in_trait) | ||||
| )] | ||||
| #![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))] | ||||
|  | ||||
| // This must go FIRST so that all the other modules see its macros. | ||||
| mod fmt; | ||||
|   | ||||
| @@ -42,9 +42,9 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona | ||||
| embassy-executor = { version = "0.3.3", path = "../embassy-executor", optional = true } | ||||
|  | ||||
| embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2", optional = true} | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2", optional = true} | ||||
| embedded-hal-nb = { version = "=1.0.0-rc.2", optional = true} | ||||
|  | ||||
| embedded-storage = "0.3.0" | ||||
| embedded-storage-async = { version = "0.4.0", optional = true } | ||||
| @@ -58,14 +58,14 @@ rand_core = "0.6.3" | ||||
| sdio-host = "0.5.0" | ||||
| embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } | ||||
| critical-section = "1.1" | ||||
| stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" } | ||||
| stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd" } | ||||
| vcell = "0.1.3" | ||||
| bxcan = "0.7.0" | ||||
| nb = "1.0.0" | ||||
| stm32-fmc = "0.3.0" | ||||
| cfg-if = "1.0.0" | ||||
| embedded-io = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.0", optional = true } | ||||
| embedded-io-async = { version = "0.6.1", optional = true } | ||||
| chrono = { version = "^0.4", default-features = false, optional = true} | ||||
| bit_field = "0.10.2" | ||||
| document-features = "0.2.7" | ||||
| @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } | ||||
| [build-dependencies] | ||||
| proc-macro2 = "1.0.36" | ||||
| quote = "1.0.15" | ||||
| stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]} | ||||
| stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd", default-features = false, features = ["metadata"]} | ||||
|  | ||||
|  | ||||
| [features] | ||||
|   | ||||
| @@ -65,7 +65,6 @@ fn main() { | ||||
|             match r.kind { | ||||
|                 // Generate singletons per pin, not per port | ||||
|                 "gpio" => { | ||||
|                     println!("{}", p.name); | ||||
|                     let port_letter = p.name.strip_prefix("GPIO").unwrap(); | ||||
|                     for pin_num in 0..16 { | ||||
|                         singletons.push(format!("P{}{}", port_letter, pin_num)); | ||||
| @@ -997,8 +996,8 @@ fn main() { | ||||
|         // SDMMCv1 uses the same channel for both directions, so just implement for RX | ||||
|         (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), | ||||
|         (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), | ||||
|         (("dac", "CH1"), quote!(crate::dac::DmaCh1)), | ||||
|         (("dac", "CH2"), quote!(crate::dac::DmaCh2)), | ||||
|         (("dac", "CH1"), quote!(crate::dac::DacDma1)), | ||||
|         (("dac", "CH2"), quote!(crate::dac::DacDma2)), | ||||
|     ] | ||||
|     .into(); | ||||
|  | ||||
| @@ -1138,6 +1137,23 @@ fn main() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // ======== | ||||
|     // Write peripheral_interrupts module. | ||||
|     let mut mt = TokenStream::new(); | ||||
|     for p in METADATA.peripherals { | ||||
|         let mut pt = TokenStream::new(); | ||||
|  | ||||
|         for irq in p.interrupts { | ||||
|             let iname = format_ident!("{}", irq.interrupt); | ||||
|             let sname = format_ident!("{}", irq.signal); | ||||
|             pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); | ||||
|         } | ||||
|  | ||||
|         let pname = format_ident!("{}", p.name); | ||||
|         mt.extend(quote!(pub mod #pname { #pt })); | ||||
|     } | ||||
|     g.extend(quote!(#[allow(non_camel_case_types)] pub mod peripheral_interrupts { #mt })); | ||||
|  | ||||
|     // ======== | ||||
|     // Write foreach_foo! macrotables | ||||
|  | ||||
| @@ -1296,6 +1312,9 @@ fn main() { | ||||
|  | ||||
|     let mut m = String::new(); | ||||
|  | ||||
|     // DO NOT ADD more macros like these. | ||||
|     // These turned to be a bad idea! | ||||
|     // Instead, make build.rs generate the final code. | ||||
|     make_table(&mut m, "foreach_flash_region", &flash_regions_table); | ||||
|     make_table(&mut m, "foreach_interrupt", &interrupts_table); | ||||
|     make_table(&mut m, "foreach_peripheral", &peripherals_table); | ||||
| @@ -1332,15 +1351,6 @@ fn main() { | ||||
|  | ||||
|     if let Some(core) = core_name { | ||||
|         println!("cargo:rustc-cfg={}_{}", &chip_name[..chip_name.len() - 2], core); | ||||
|     } else { | ||||
|         println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]); | ||||
|     } | ||||
|  | ||||
|     // ======== | ||||
|     // stm32f3 wildcard features used in RCC | ||||
|  | ||||
|     if chip_name.starts_with("stm32f3") { | ||||
|         println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]); | ||||
|     } | ||||
|  | ||||
|     // ======= | ||||
| @@ -1355,16 +1365,25 @@ fn main() { | ||||
|     if &chip_name[..8] == "stm32wba" { | ||||
|         println!("cargo:rustc-cfg={}", &chip_name[..8]); // stm32wba | ||||
|         println!("cargo:rustc-cfg={}", &chip_name[..10]); // stm32wba52 | ||||
|         println!("cargo:rustc-cfg=package_{}", &chip_name[10..11]); | ||||
|         println!("cargo:rustc-cfg=flashsize_{}", &chip_name[11..12]); | ||||
|     } else { | ||||
|         println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4 | ||||
|         println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429 | ||||
|         println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x | ||||
|         println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9 | ||||
|         println!("cargo:rustc-cfg=package_{}", &chip_name[9..10]); | ||||
|         println!("cargo:rustc-cfg=flashsize_{}", &chip_name[10..11]); | ||||
|     } | ||||
|  | ||||
|     // Handle time-driver-XXXX features. | ||||
|     if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {} | ||||
|     println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]); | ||||
|     // Mark the L4+ chips as they have many differences to regular L4. | ||||
|     if &chip_name[..7] == "stm32l4" { | ||||
|         if "pqrs".contains(&chip_name[7..8]) { | ||||
|             println!("cargo:rustc-cfg=stm32l4_plus"); | ||||
|         } else { | ||||
|             println!("cargo:rustc-cfg=stm32l4_nonplus"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     println!("cargo:rerun-if-changed=build.rs"); | ||||
| } | ||||
|   | ||||
| @@ -1,136 +1,66 @@ | ||||
| //! Provide access to the STM32 digital-to-analog converter (DAC). | ||||
| #![macro_use] | ||||
|  | ||||
| //! Provide access to the STM32 digital-to-analog converter (DAC). | ||||
| use core::marker::PhantomData; | ||||
|  | ||||
| use embassy_hal_internal::{into_ref, PeripheralRef}; | ||||
|  | ||||
| use crate::dma::NoDma; | ||||
| #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | ||||
| use crate::pac::dac; | ||||
| use crate::rcc::RccPeripheral; | ||||
| use crate::{peripherals, Peripheral}; | ||||
|  | ||||
| mod tsel; | ||||
| pub use tsel::TriggerSel; | ||||
|  | ||||
| /// Operating mode for DAC channel | ||||
| #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// Custom Errors | ||||
| pub enum Error { | ||||
|     UnconfiguredChannel, | ||||
|     InvalidValue, | ||||
| pub enum Mode { | ||||
|     /// Normal mode, channel is connected to external pin with buffer enabled. | ||||
|     NormalExternalBuffered, | ||||
|     /// Normal mode, channel is connected to external pin and internal peripherals | ||||
|     /// with buffer enabled. | ||||
|     NormalBothBuffered, | ||||
|     /// Normal mode, channel is connected to external pin with buffer disabled. | ||||
|     NormalExternalUnbuffered, | ||||
|     /// Normal mode, channel is connected to internal peripherals with buffer disabled. | ||||
|     NormalInternalUnbuffered, | ||||
|     /// Sample-and-hold mode, channel is connected to external pin with buffer enabled. | ||||
|     SampleHoldExternalBuffered, | ||||
|     /// Sample-and-hold mode, channel is connected to external pin and internal peripherals | ||||
|     /// with buffer enabled. | ||||
|     SampleHoldBothBuffered, | ||||
|     /// Sample-and-hold mode, channel is connected to external pin and internal peripherals | ||||
|     /// with buffer disabled. | ||||
|     SampleHoldBothUnbuffered, | ||||
|     /// Sample-and-hold mode, channel is connected to internal peripherals with buffer disabled. | ||||
|     SampleHoldInternalUnbuffered, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// DAC Channels | ||||
| pub enum Channel { | ||||
|     Ch1, | ||||
|     Ch2, | ||||
| } | ||||
|  | ||||
| impl Channel { | ||||
|     const fn index(&self) -> usize { | ||||
| #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | ||||
| impl Mode { | ||||
|     fn mode(&self) -> dac::vals::Mode { | ||||
|         match self { | ||||
|             Channel::Ch1 => 0, | ||||
|             Channel::Ch2 => 1, | ||||
|             Mode::NormalExternalBuffered => dac::vals::Mode::NORMAL_EXT_BUFEN, | ||||
|             Mode::NormalBothBuffered => dac::vals::Mode::NORMAL_EXT_INT_BUFEN, | ||||
|             Mode::NormalExternalUnbuffered => dac::vals::Mode::NORMAL_EXT_BUFDIS, | ||||
|             Mode::NormalInternalUnbuffered => dac::vals::Mode::NORMAL_INT_BUFDIS, | ||||
|             Mode::SampleHoldExternalBuffered => dac::vals::Mode::SAMPHOLD_EXT_BUFEN, | ||||
|             Mode::SampleHoldBothBuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFEN, | ||||
|             Mode::SampleHoldBothUnbuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFDIS, | ||||
|             Mode::SampleHoldInternalUnbuffered => dac::vals::Mode::SAMPHOLD_INT_BUFDIS, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// Trigger sources for CH1 | ||||
| pub enum Ch1Trigger { | ||||
|     #[cfg(dac_v3)] | ||||
|     Tim1, | ||||
|     Tim2, | ||||
|     #[cfg(not(dac_v3))] | ||||
|     Tim3, | ||||
|     #[cfg(dac_v3)] | ||||
|     Tim4, | ||||
|     #[cfg(dac_v3)] | ||||
|     Tim5, | ||||
|     Tim6, | ||||
|     Tim7, | ||||
|     #[cfg(dac_v3)] | ||||
|     Tim8, | ||||
|     Tim15, | ||||
|     #[cfg(dac_v3)] | ||||
|     Hrtim1Dactrg1, | ||||
|     #[cfg(dac_v3)] | ||||
|     Hrtim1Dactrg2, | ||||
|     #[cfg(dac_v3)] | ||||
|     Lptim1, | ||||
|     #[cfg(dac_v3)] | ||||
|     Lptim2, | ||||
|     #[cfg(dac_v3)] | ||||
|     Lptim3, | ||||
|     Exti9, | ||||
|     Software, | ||||
| } | ||||
|  | ||||
| impl Ch1Trigger { | ||||
|     fn tsel(&self) -> dac::vals::Tsel1 { | ||||
|         match self { | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Tim1 => dac::vals::Tsel1::TIM1_TRGO, | ||||
|             Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO, | ||||
|             #[cfg(not(dac_v3))] | ||||
|             Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Tim4 => dac::vals::Tsel1::TIM4_TRGO, | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Tim5 => dac::vals::Tsel1::TIM5_TRGO, | ||||
|             Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, | ||||
|             Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Tim8 => dac::vals::Tsel1::TIM8_TRGO, | ||||
|             Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Hrtim1Dactrg1 => dac::vals::Tsel1::HRTIM1_DACTRG1, | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Hrtim1Dactrg2 => dac::vals::Tsel1::HRTIM1_DACTRG2, | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Lptim1 => dac::vals::Tsel1::LPTIM1_OUT, | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Lptim2 => dac::vals::Tsel1::LPTIM2_OUT, | ||||
|             #[cfg(dac_v3)] | ||||
|             Ch1Trigger::Lptim3 => dac::vals::Tsel1::LPTIM3_OUT, | ||||
|             Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9, | ||||
|             Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// Trigger sources for CH2 | ||||
| pub enum Ch2Trigger { | ||||
|     Tim6, | ||||
|     Tim8, | ||||
|     Tim7, | ||||
|     Tim5, | ||||
|     Tim2, | ||||
|     Tim4, | ||||
|     Exti9, | ||||
|     Software, | ||||
| } | ||||
|  | ||||
| impl Ch2Trigger { | ||||
|     fn tsel(&self) -> dac::vals::Tsel2 { | ||||
|         match self { | ||||
|             Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO, | ||||
|             Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO, | ||||
|             Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO, | ||||
|             Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO, | ||||
|             Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO, | ||||
|             Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO, | ||||
|             Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9, | ||||
|             Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// Single 8 or 12 bit value that can be output by the DAC | ||||
| /// Single 8 or 12 bit value that can be output by the DAC. | ||||
| /// | ||||
| /// 12-bit values outside the permitted range are silently truncated. | ||||
| pub enum Value { | ||||
|     // 8 bit value | ||||
|     Bit8(u8), | ||||
| @@ -142,7 +72,21 @@ pub enum Value { | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// Array variant of [`Value`] | ||||
| /// Dual 8 or 12 bit values that can be output by the DAC channels 1 and 2 simultaneously. | ||||
| /// | ||||
| /// 12-bit values outside the permitted range are silently truncated. | ||||
| pub enum DualValue { | ||||
|     // 8 bit value | ||||
|     Bit8(u8, u8), | ||||
|     // 12 bit value stored in a u16, left-aligned | ||||
|     Bit12Left(u16, u16), | ||||
|     // 12 bit value stored in a u16, right-aligned | ||||
|     Bit12Right(u16, u16), | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// Array variant of [`Value`]. | ||||
| pub enum ValueArray<'a> { | ||||
|     // 8 bit values | ||||
|     Bit8(&'a [u8]), | ||||
| @@ -151,264 +95,206 @@ pub enum ValueArray<'a> { | ||||
|     // 12 bit values stored in a u16, right-aligned | ||||
|     Bit12Right(&'a [u16]), | ||||
| } | ||||
| /// Provide common functions for DAC channels | ||||
| pub trait DacChannel<T: Instance, Tx> { | ||||
|     const CHANNEL: Channel; | ||||
|  | ||||
|     /// Enable trigger of the given channel | ||||
|     fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> { | ||||
|         T::regs().cr().modify(|reg| { | ||||
|             reg.set_ten(Self::CHANNEL.index(), on); | ||||
|         }); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Set mode register of the given channel | ||||
|     #[cfg(any(dac_v2, dac_v3))] | ||||
|     fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { | ||||
|         T::regs().mcr().modify(|reg| { | ||||
|             reg.set_mode(Self::CHANNEL.index(), val); | ||||
|         }); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Set enable register of the given channel | ||||
|     fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> { | ||||
|         T::regs().cr().modify(|reg| { | ||||
|             reg.set_en(Self::CHANNEL.index(), on); | ||||
|         }); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Enable the DAC channel `ch` | ||||
|     fn enable_channel(&mut self) -> Result<(), Error> { | ||||
|         self.set_channel_enable(true) | ||||
|     } | ||||
|  | ||||
|     /// Disable the DAC channel `ch` | ||||
|     fn disable_channel(&mut self) -> Result<(), Error> { | ||||
|         self.set_channel_enable(false) | ||||
|     } | ||||
|  | ||||
|     /// Perform a software trigger on `ch` | ||||
|     fn trigger(&mut self) { | ||||
|         T::regs().swtrigr().write(|reg| { | ||||
|             reg.set_swtrig(Self::CHANNEL.index(), true); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /// Set a value to be output by the DAC on trigger. | ||||
|     /// | ||||
|     /// The `value` is written to the corresponding "data holding register". | ||||
|     fn set(&mut self, value: Value) -> Result<(), Error> { | ||||
|         match value { | ||||
|             Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), | ||||
|             Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), | ||||
|             Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Hold two DAC channels | ||||
| /// Driver for a single DAC channel. | ||||
| /// | ||||
| /// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously. | ||||
| /// | ||||
| /// # Example for obtaining both DAC channels | ||||
| /// | ||||
| /// ```ignore | ||||
| /// // DMA channels and pins may need to be changed for your controller | ||||
| /// let (dac_ch1, dac_ch2) = | ||||
| ///     embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); | ||||
| /// ``` | ||||
| pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { | ||||
|     ch1: DacCh1<'d, T, TxCh1>, | ||||
|     ch2: DacCh2<'d, T, TxCh2>, | ||||
| } | ||||
|  | ||||
| /// DAC CH1 | ||||
| /// | ||||
| /// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously. | ||||
| pub struct DacCh1<'d, T: Instance, Tx> { | ||||
|     /// To consume T | ||||
|     _peri: PeripheralRef<'d, T>, | ||||
|     #[allow(unused)] // For chips whose DMA is not (yet) supported | ||||
|     dma: PeripheralRef<'d, Tx>, | ||||
| } | ||||
|  | ||||
| /// DAC CH2 | ||||
| /// | ||||
| /// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously. | ||||
| pub struct DacCh2<'d, T: Instance, Tx> { | ||||
|     /// Instead of PeripheralRef to consume T | ||||
| /// If you want to use both channels, either together or independently, | ||||
| /// create a [`Dac`] first and use it to access each channel. | ||||
| pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> { | ||||
|     phantom: PhantomData<&'d mut T>, | ||||
|     #[allow(unused)] // For chips whose DMA is not (yet) supported | ||||
|     dma: PeripheralRef<'d, Tx>, | ||||
|     #[allow(unused)] | ||||
|     dma: PeripheralRef<'d, DMA>, | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { | ||||
|     /// Obtain DAC CH1 | ||||
|     pub fn new( | ||||
|         peri: impl Peripheral<P = T> + 'd, | ||||
|         dma: impl Peripheral<P = Tx> + 'd, | ||||
|         pin: impl Peripheral<P = impl DacPin<T, 1>> + crate::gpio::sealed::Pin + 'd, | ||||
|     ) -> Self { | ||||
|         pin.set_as_analog(); | ||||
|         into_ref!(peri, dma); | ||||
|         T::enable_and_reset(); | ||||
| pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>; | ||||
| pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>; | ||||
|  | ||||
|         let mut dac = Self { _peri: peri, dma }; | ||||
| impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | ||||
|     const IDX: usize = (N - 1) as usize; | ||||
|  | ||||
|         // Configure each activated channel. All results can be `unwrap`ed since they | ||||
|         // will only error if the channel is not configured (i.e. ch1, ch2 are false) | ||||
|         #[cfg(any(dac_v2, dac_v3))] | ||||
|         dac.set_channel_mode(0).unwrap(); | ||||
|         dac.enable_channel().unwrap(); | ||||
|         dac.set_trigger_enable(true).unwrap(); | ||||
|  | ||||
|         dac | ||||
|     } | ||||
|  | ||||
|     /// Select a new trigger for this channel | ||||
|     /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral. | ||||
|     /// | ||||
|     /// **Important**: This disables the channel! | ||||
|     pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { | ||||
|         unwrap!(self.disable_channel()); | ||||
|         T::regs().cr().modify(|reg| { | ||||
|             reg.set_tsel1(trigger.tsel()); | ||||
|         }); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Write `data` to the DAC CH1 via DMA. | ||||
|     /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument. | ||||
|     /// | ||||
|     /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||||
|     /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||||
|     /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||||
|     /// The channel is enabled on creation and begins to drive the output pin. | ||||
|     /// Note that some methods, such as `set_trigger()` and `set_mode()`, will | ||||
|     /// disable the channel; you must re-enable it with `enable()`. | ||||
|     /// | ||||
|     /// **Important:** Channel 1 has to be configured for the DAC instance! | ||||
|     pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||||
|     where | ||||
|         Tx: DmaCh1<T>, | ||||
|     { | ||||
|         let channel = Channel::Ch1.index(); | ||||
|         debug!("Writing to channel {}", channel); | ||||
|  | ||||
|         // Enable DAC and DMA | ||||
|         T::regs().cr().modify(|w| { | ||||
|             w.set_en(channel, true); | ||||
|             w.set_dmaen(channel, true); | ||||
|         }); | ||||
|  | ||||
|         let tx_request = self.dma.request(); | ||||
|         let dma_channel = &mut self.dma; | ||||
|  | ||||
|         let tx_options = crate::dma::TransferOptions { | ||||
|             circular, | ||||
|             half_transfer_ir: false, | ||||
|             complete_transfer_ir: !circular, | ||||
|             ..Default::default() | ||||
|         }; | ||||
|  | ||||
|         // Initiate the correct type of DMA transfer depending on what data is passed | ||||
|         let tx_f = match data { | ||||
|             ValueArray::Bit8(buf) => unsafe { | ||||
|                 crate::dma::Transfer::new_write( | ||||
|                     dma_channel, | ||||
|                     tx_request, | ||||
|                     buf, | ||||
|                     T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||||
|                     tx_options, | ||||
|                 ) | ||||
|             }, | ||||
|             ValueArray::Bit12Left(buf) => unsafe { | ||||
|                 crate::dma::Transfer::new_write( | ||||
|                     dma_channel, | ||||
|                     tx_request, | ||||
|                     buf, | ||||
|                     T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||||
|                     tx_options, | ||||
|                 ) | ||||
|             }, | ||||
|             ValueArray::Bit12Right(buf) => unsafe { | ||||
|                 crate::dma::Transfer::new_write( | ||||
|                     dma_channel, | ||||
|                     tx_request, | ||||
|                     buf, | ||||
|                     T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||||
|                     tx_options, | ||||
|                 ) | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         tx_f.await; | ||||
|  | ||||
|         // finish dma | ||||
|         // TODO: Do we need to check any status registers here? | ||||
|         T::regs().cr().modify(|w| { | ||||
|             // Disable the DAC peripheral | ||||
|             w.set_en(channel, false); | ||||
|             // Disable the DMA. TODO: Is this necessary? | ||||
|             w.set_dmaen(channel, false); | ||||
|         }); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | ||||
|     /// Obtain DAC CH2 | ||||
|     /// By default, triggering is disabled, but it can be enabled using | ||||
|     /// [`DacChannel::set_trigger()`]. | ||||
|     pub fn new( | ||||
|         _peri: impl Peripheral<P = T> + 'd, | ||||
|         dma: impl Peripheral<P = Tx> + 'd, | ||||
|         pin: impl Peripheral<P = impl DacPin<T, 2>> + crate::gpio::sealed::Pin + 'd, | ||||
|         dma: impl Peripheral<P = DMA> + 'd, | ||||
|         pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::sealed::Pin> + 'd, | ||||
|     ) -> Self { | ||||
|         into_ref!(dma, pin); | ||||
|         pin.set_as_analog(); | ||||
|         into_ref!(_peri, dma); | ||||
|         T::enable_and_reset(); | ||||
|  | ||||
|         let mut dac = Self { | ||||
|             phantom: PhantomData, | ||||
|             dma, | ||||
|         }; | ||||
|  | ||||
|         // Configure each activated channel. All results can be `unwrap`ed since they | ||||
|         // will only error if the channel is not configured (i.e. ch1, ch2 are false) | ||||
|         #[cfg(any(dac_v2, dac_v3))] | ||||
|         dac.set_channel_mode(0).unwrap(); | ||||
|         dac.enable_channel().unwrap(); | ||||
|         dac.set_trigger_enable(true).unwrap(); | ||||
|  | ||||
|         #[cfg(any(dac_v5, dac_v6, dac_v7))] | ||||
|         dac.set_hfsel(); | ||||
|         dac.enable(); | ||||
|         dac | ||||
|     } | ||||
|  | ||||
|     /// Select a new trigger for this channel | ||||
|     pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { | ||||
|         unwrap!(self.disable_channel()); | ||||
|         T::regs().cr().modify(|reg| { | ||||
|             reg.set_tsel2(trigger.tsel()); | ||||
|         }); | ||||
|         Ok(()) | ||||
|     /// Create a new `DacChannel` instance where the external output pin is not used, | ||||
|     /// so the DAC can only be used to generate internal signals. | ||||
|     /// The GPIO pin is therefore available to be used for other functions. | ||||
|     /// | ||||
|     /// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation. | ||||
|     /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the | ||||
|     /// channel; you must re-enable it with `enable()`. | ||||
|     /// | ||||
|     /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument. | ||||
|     /// | ||||
|     /// By default, triggering is disabled, but it can be enabled using | ||||
|     /// [`DacChannel::set_trigger()`]. | ||||
|     #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] | ||||
|     pub fn new_internal(_peri: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = DMA> + 'd) -> Self { | ||||
|         into_ref!(dma); | ||||
|         T::enable_and_reset(); | ||||
|         let mut dac = Self { | ||||
|             phantom: PhantomData, | ||||
|             dma, | ||||
|         }; | ||||
|         #[cfg(any(dac_v5, dac_v6, dac_v7))] | ||||
|         dac.set_hfsel(); | ||||
|         dac.set_mode(Mode::NormalInternalUnbuffered); | ||||
|         dac.enable(); | ||||
|         dac | ||||
|     } | ||||
|  | ||||
|     /// Write `data` to the DAC CH2 via DMA. | ||||
|     /// | ||||
|     /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||||
|     /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||||
|     /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||||
|     /// | ||||
|     /// **Important:** Channel 2 has to be configured for the DAC instance! | ||||
|     pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||||
|     where | ||||
|         Tx: DmaCh2<T>, | ||||
|     { | ||||
|         let channel = Channel::Ch2.index(); | ||||
|         debug!("Writing to channel {}", channel); | ||||
|     /// Enable or disable this channel. | ||||
|     pub fn set_enable(&mut self, on: bool) { | ||||
|         critical_section::with(|_| { | ||||
|             T::regs().cr().modify(|reg| { | ||||
|                 reg.set_en(Self::IDX, on); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /// Enable this channel. | ||||
|     pub fn enable(&mut self) { | ||||
|         self.set_enable(true) | ||||
|     } | ||||
|  | ||||
|     /// Disable this channel. | ||||
|     pub fn disable(&mut self) { | ||||
|         self.set_enable(false) | ||||
|     } | ||||
|  | ||||
|     /// Set the trigger source for this channel. | ||||
|     /// | ||||
|     /// This method disables the channel, so you may need to re-enable afterwards. | ||||
|     pub fn set_trigger(&mut self, source: TriggerSel) { | ||||
|         critical_section::with(|_| { | ||||
|             T::regs().cr().modify(|reg| { | ||||
|                 reg.set_en(Self::IDX, false); | ||||
|                 reg.set_tsel(Self::IDX, source as u8); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /// Enable or disable triggering for this channel. | ||||
|     pub fn set_triggering(&mut self, on: bool) { | ||||
|         critical_section::with(|_| { | ||||
|             T::regs().cr().modify(|reg| { | ||||
|                 reg.set_ten(Self::IDX, on); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /// Software trigger this channel. | ||||
|     pub fn trigger(&mut self) { | ||||
|         T::regs().swtrigr().write(|reg| { | ||||
|             reg.set_swtrig(Self::IDX, true); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /// Set mode of this channel. | ||||
|     /// | ||||
|     /// This method disables the channel, so you may need to re-enable afterwards. | ||||
|     #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | ||||
|     pub fn set_mode(&mut self, mode: Mode) { | ||||
|         critical_section::with(|_| { | ||||
|             T::regs().cr().modify(|reg| { | ||||
|                 reg.set_en(Self::IDX, false); | ||||
|             }); | ||||
|             T::regs().mcr().modify(|reg| { | ||||
|                 reg.set_mode(Self::IDX, mode.mode()); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /// Write a new value to this channel. | ||||
|     /// | ||||
|     /// If triggering is not enabled, the new value is immediately output; otherwise, | ||||
|     /// it will be output after the next trigger. | ||||
|     pub fn set(&mut self, value: Value) { | ||||
|         match value { | ||||
|             Value::Bit8(v) => T::regs().dhr8r(Self::IDX).write(|reg| reg.set_dhr(v)), | ||||
|             Value::Bit12Left(v) => T::regs().dhr12l(Self::IDX).write(|reg| reg.set_dhr(v)), | ||||
|             Value::Bit12Right(v) => T::regs().dhr12r(Self::IDX).write(|reg| reg.set_dhr(v)), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Read the current output value of the DAC. | ||||
|     pub fn read(&self) -> u16 { | ||||
|         T::regs().dor(Self::IDX).read().dor() | ||||
|     } | ||||
|  | ||||
|     /// Set HFSEL as appropriate for the current peripheral clock frequency. | ||||
|     #[cfg(dac_v5)] | ||||
|     fn set_hfsel(&mut self) { | ||||
|         if T::frequency() >= crate::time::mhz(80) { | ||||
|             critical_section::with(|_| { | ||||
|                 T::regs().cr().modify(|reg| { | ||||
|                     reg.set_hfsel(true); | ||||
|                 }); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Set HFSEL as appropriate for the current peripheral clock frequency. | ||||
|     #[cfg(any(dac_v6, dac_v7))] | ||||
|     fn set_hfsel(&mut self) { | ||||
|         if T::frequency() >= crate::time::mhz(160) { | ||||
|             critical_section::with(|_| { | ||||
|                 T::regs().mcr().modify(|reg| { | ||||
|                     reg.set_hfsel(0b10); | ||||
|                 }); | ||||
|             }); | ||||
|         } else if T::frequency() >= crate::time::mhz(80) { | ||||
|             critical_section::with(|_| { | ||||
|                 T::regs().mcr().modify(|reg| { | ||||
|                     reg.set_hfsel(0b01); | ||||
|                 }); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| macro_rules! impl_dma_methods { | ||||
|     ($n:literal, $trait:ident) => { | ||||
|         impl<'d, T: Instance, DMA> DacChannel<'d, T, $n, DMA> | ||||
|         where | ||||
|             DMA: $trait<T>, | ||||
|         { | ||||
|             /// Write `data` to this channel via DMA. | ||||
|             /// | ||||
|             /// To prevent delays or glitches when outputing a periodic waveform, the `circular` | ||||
|             /// flag can be set. This configures a circular DMA transfer that continually outputs | ||||
|             /// `data`. Note that for performance reasons in circular mode the transfer-complete | ||||
|             /// interrupt is disabled. | ||||
|             #[cfg(not(gpdma))] | ||||
|             pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) { | ||||
|                 // Enable DAC and DMA | ||||
|                 T::regs().cr().modify(|w| { | ||||
|             w.set_en(channel, true); | ||||
|             w.set_dmaen(channel, true); | ||||
|                     w.set_en(Self::IDX, true); | ||||
|                     w.set_dmaen(Self::IDX, true); | ||||
|                 }); | ||||
|  | ||||
|                 let tx_request = self.dma.request(); | ||||
| @@ -428,7 +314,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | ||||
|                             dma_channel, | ||||
|                             tx_request, | ||||
|                             buf, | ||||
|                     T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||||
|                             T::regs().dhr8r(Self::IDX).as_ptr() as *mut u8, | ||||
|                             tx_options, | ||||
|                         ) | ||||
|                     }, | ||||
| @@ -437,7 +323,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | ||||
|                             dma_channel, | ||||
|                             tx_request, | ||||
|                             buf, | ||||
|                     T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||||
|                             T::regs().dhr12l(Self::IDX).as_ptr() as *mut u16, | ||||
|                             tx_options, | ||||
|                         ) | ||||
|                     }, | ||||
| @@ -446,7 +332,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | ||||
|                             dma_channel, | ||||
|                             tx_request, | ||||
|                             buf, | ||||
|                     T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||||
|                             T::regs().dhr12r(Self::IDX).as_ptr() as *mut u16, | ||||
|                             tx_options, | ||||
|                         ) | ||||
|                     }, | ||||
| @@ -454,97 +340,152 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | ||||
|  | ||||
|                 tx_f.await; | ||||
|  | ||||
|         // finish dma | ||||
|         // TODO: Do we need to check any status registers here? | ||||
|                 T::regs().cr().modify(|w| { | ||||
|             // Disable the DAC peripheral | ||||
|             w.set_en(channel, false); | ||||
|             // Disable the DMA. TODO: Is this necessary? | ||||
|             w.set_dmaen(channel, false); | ||||
|                     w.set_en(Self::IDX, false); | ||||
|                     w.set_dmaen(Self::IDX, false); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
|         Ok(()) | ||||
| impl_dma_methods!(1, DacDma1); | ||||
| impl_dma_methods!(2, DacDma2); | ||||
|  | ||||
| impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> { | ||||
|     fn drop(&mut self) { | ||||
|         T::disable(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { | ||||
|     /// Create a new DAC instance with both channels. | ||||
| /// DAC driver. | ||||
| /// | ||||
| /// Use this struct when you want to use both channels, either together or independently. | ||||
| /// | ||||
| /// # Example | ||||
| /// | ||||
| /// ```ignore | ||||
| /// // Pins may need to be changed for your specific device. | ||||
| /// let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC, NoDma, NoDma, p.PA4, p.PA5).split(); | ||||
| /// ``` | ||||
| pub struct Dac<'d, T: Instance, DMACh1 = NoDma, DMACh2 = NoDma> { | ||||
|     ch1: DacChannel<'d, T, 1, DMACh1>, | ||||
|     ch2: DacChannel<'d, T, 2, DMACh2>, | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { | ||||
|     /// Create a new `Dac` instance, consuming the underlying DAC peripheral. | ||||
|     /// | ||||
|     /// This is used to obtain two independent channels via `split()` for use e.g. with DMA. | ||||
|     /// This struct allows you to access both channels of the DAC, where available. You can either | ||||
|     /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use | ||||
|     /// the two channels together. | ||||
|     /// | ||||
|     /// The channels are enabled on creation and begins to drive their output pins. | ||||
|     /// Note that some methods, such as `set_trigger()` and `set_mode()`, will | ||||
|     /// disable the channel; you must re-enable them with `enable()`. | ||||
|     /// | ||||
|     /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` | ||||
|     /// method on the underlying channels. | ||||
|     pub fn new( | ||||
|         peri: impl Peripheral<P = T> + 'd, | ||||
|         dma_ch1: impl Peripheral<P = TxCh1> + 'd, | ||||
|         dma_ch2: impl Peripheral<P = TxCh2> + 'd, | ||||
|         pin_ch1: impl Peripheral<P = impl DacPin<T, 1>> + crate::gpio::sealed::Pin + 'd, | ||||
|         pin_ch2: impl Peripheral<P = impl DacPin<T, 2>> + crate::gpio::sealed::Pin + 'd, | ||||
|         _peri: impl Peripheral<P = T> + 'd, | ||||
|         dma_ch1: impl Peripheral<P = DMACh1> + 'd, | ||||
|         dma_ch2: impl Peripheral<P = DMACh2> + 'd, | ||||
|         pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::sealed::Pin> + 'd, | ||||
|         pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::sealed::Pin> + 'd, | ||||
|     ) -> Self { | ||||
|         into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2); | ||||
|         pin_ch1.set_as_analog(); | ||||
|         pin_ch2.set_as_analog(); | ||||
|         into_ref!(peri, dma_ch1, dma_ch2); | ||||
|         // Enable twice to increment the DAC refcount for each channel. | ||||
|         T::enable_and_reset(); | ||||
|  | ||||
|         let mut dac_ch1 = DacCh1 { | ||||
|             _peri: peri, | ||||
|         T::enable_and_reset(); | ||||
|         Self { | ||||
|             ch1: DacCh1 { | ||||
|                 phantom: PhantomData, | ||||
|                 dma: dma_ch1, | ||||
|         }; | ||||
|  | ||||
|         let mut dac_ch2 = DacCh2 { | ||||
|             }, | ||||
|             ch2: DacCh2 { | ||||
|                 phantom: PhantomData, | ||||
|                 dma: dma_ch2, | ||||
|         }; | ||||
|  | ||||
|         // Configure each activated channel. All results can be `unwrap`ed since they | ||||
|         // will only error if the channel is not configured (i.e. ch1, ch2 are false) | ||||
|         #[cfg(any(dac_v2, dac_v3))] | ||||
|         dac_ch1.set_channel_mode(0).unwrap(); | ||||
|         dac_ch1.enable_channel().unwrap(); | ||||
|         dac_ch1.set_trigger_enable(true).unwrap(); | ||||
|  | ||||
|         #[cfg(any(dac_v2, dac_v3))] | ||||
|         dac_ch2.set_channel_mode(0).unwrap(); | ||||
|         dac_ch2.enable_channel().unwrap(); | ||||
|         dac_ch2.set_trigger_enable(true).unwrap(); | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Create a new `Dac` instance where the external output pins are not used, | ||||
|     /// so the DAC can only be used to generate internal signals but the GPIO | ||||
|     /// pins remain available for other functions. | ||||
|     /// | ||||
|     /// This struct allows you to access both channels of the DAC, where available. You can either | ||||
|     /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two | ||||
|     /// channels together. | ||||
|     /// | ||||
|     /// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation. | ||||
|     /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the | ||||
|     /// channel; you must re-enable them with `enable()`. | ||||
|     /// | ||||
|     /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` | ||||
|     /// method on the underlying channels. | ||||
|     #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] | ||||
|     pub fn new_internal( | ||||
|         _peri: impl Peripheral<P = T> + 'd, | ||||
|         dma_ch1: impl Peripheral<P = DMACh1> + 'd, | ||||
|         dma_ch2: impl Peripheral<P = DMACh2> + 'd, | ||||
|     ) -> Self { | ||||
|         into_ref!(dma_ch1, dma_ch2); | ||||
|         // Enable twice to increment the DAC refcount for each channel. | ||||
|         T::enable_and_reset(); | ||||
|         T::enable_and_reset(); | ||||
|         Self { | ||||
|             ch1: dac_ch1, | ||||
|             ch2: dac_ch2, | ||||
|             ch1: DacCh1 { | ||||
|                 phantom: PhantomData, | ||||
|                 dma: dma_ch1, | ||||
|             }, | ||||
|             ch2: DacCh2 { | ||||
|                 phantom: PhantomData, | ||||
|                 dma: dma_ch2, | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Split the DAC into CH1 and CH2 for independent use. | ||||
|     pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { | ||||
|     /// Split this `Dac` into separate channels. | ||||
|     /// | ||||
|     /// You can access and move the channels around separately after splitting. | ||||
|     pub fn split(self) -> (DacCh1<'d, T, DMACh1>, DacCh2<'d, T, DMACh2>) { | ||||
|         (self.ch1, self.ch2) | ||||
|     } | ||||
|  | ||||
|     /// Get mutable reference to CH1 | ||||
|     pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { | ||||
|     /// Temporarily access channel 1. | ||||
|     pub fn ch1(&mut self) -> &mut DacCh1<'d, T, DMACh1> { | ||||
|         &mut self.ch1 | ||||
|     } | ||||
|  | ||||
|     /// Get mutable reference to CH2 | ||||
|     pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { | ||||
|     /// Temporarily access channel 2. | ||||
|     pub fn ch2(&mut self) -> &mut DacCh2<'d, T, DMACh2> { | ||||
|         &mut self.ch2 | ||||
|     } | ||||
|  | ||||
|     /// Get reference to CH1 | ||||
|     pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { | ||||
|         &self.ch1 | ||||
|     /// Simultaneously update channels 1 and 2 with a new value. | ||||
|     /// | ||||
|     /// If triggering is not enabled, the new values are immediately output; | ||||
|     /// otherwise, they will be output after the next trigger. | ||||
|     pub fn set(&mut self, values: DualValue) { | ||||
|         match values { | ||||
|             DualValue::Bit8(v1, v2) => T::regs().dhr8rd().write(|reg| { | ||||
|                 reg.set_dhr(0, v1); | ||||
|                 reg.set_dhr(1, v2); | ||||
|             }), | ||||
|             DualValue::Bit12Left(v1, v2) => T::regs().dhr12ld().write(|reg| { | ||||
|                 reg.set_dhr(0, v1); | ||||
|                 reg.set_dhr(1, v2); | ||||
|             }), | ||||
|             DualValue::Bit12Right(v1, v2) => T::regs().dhr12rd().write(|reg| { | ||||
|                 reg.set_dhr(0, v1); | ||||
|                 reg.set_dhr(1, v2); | ||||
|             }), | ||||
|         } | ||||
|  | ||||
|     /// Get reference to CH2 | ||||
|     pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { | ||||
|         &self.ch2 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> { | ||||
|     const CHANNEL: Channel = Channel::Ch1; | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> { | ||||
|     const CHANNEL: Channel = Channel::Ch2; | ||||
| } | ||||
|  | ||||
| pub(crate) mod sealed { | ||||
|     pub trait Instance { | ||||
|         fn regs() -> &'static crate::pac::dac::Dac; | ||||
| @@ -552,8 +493,8 @@ pub(crate) mod sealed { | ||||
| } | ||||
|  | ||||
| pub trait Instance: sealed::Instance + RccPeripheral + 'static {} | ||||
| dma_trait!(DmaCh1, Instance); | ||||
| dma_trait!(DmaCh2, Instance); | ||||
| dma_trait!(DacDma1, Instance); | ||||
| dma_trait!(DacDma2, Instance); | ||||
|  | ||||
| /// Marks a pin that can be used with the DAC | ||||
| pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} | ||||
| @@ -568,12 +509,14 @@ foreach_peripheral!( | ||||
|             } | ||||
|  | ||||
|             fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) { | ||||
|                 // TODO: Increment refcount? | ||||
|                 crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true)); | ||||
|                 crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false)); | ||||
|                 crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); | ||||
|             } | ||||
|  | ||||
|             fn disable_with_cs(_cs: critical_section::CriticalSection) { | ||||
|                 // TODO: Decrement refcount? | ||||
|                 crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)) | ||||
|             } | ||||
|         } | ||||
|   | ||||
							
								
								
									
										282
									
								
								embassy-stm32/src/dac/tsel.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								embassy-stm32/src/dac/tsel.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,282 @@ | ||||
| /// Trigger selection for STM32F0. | ||||
| #[cfg(stm32f0)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     Tim3 = 1, | ||||
|     Tim7 = 2, | ||||
|     Tim15 = 3, | ||||
|     Tim2 = 4, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32F1. | ||||
| #[cfg(stm32f1)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     #[cfg(any(stm32f100, stm32f105, stm32f107))] | ||||
|     Tim3 = 1, | ||||
|     #[cfg(any(stm32f101, stm32f103))] | ||||
|     Tim8 = 1, | ||||
|     Tim7 = 2, | ||||
|     #[cfg(any(stm32f101, stm32f103, stm32f105, stm32f107))] | ||||
|     Tim5 = 3, | ||||
|     #[cfg(all(stm32f100, any(flashsize_4, flashsize_6, flashsize_8, flashsize_b)))] | ||||
|     Tim15 = 3, | ||||
|     #[cfg(all(stm32f100, any(flashsize_c, flashsize_d, flashsize_e)))] | ||||
|     /// Can be remapped to TIM15 with MISC_REMAP in AFIO_MAPR2. | ||||
|     Tim5Or15 = 3, | ||||
|     Tim2 = 4, | ||||
|     Tim4 = 5, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32F2/F4/F7/L4, except F410 or L4+. | ||||
| #[cfg(all(any(stm32f2, stm32f4, stm32f7, stm32l4_nonplus), not(stm32f410)))] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     Tim8 = 1, | ||||
|     #[cfg(not(any(stm32l45x, stm32l46x)))] | ||||
|     Tim7 = 2, | ||||
|     Tim5 = 3, | ||||
|     Tim2 = 4, | ||||
|     Tim4 = 5, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32F410. | ||||
| #[cfg(stm32f410)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim5 = 3, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32F301/2 and 318. | ||||
| #[cfg(any(stm32f301, stm32f302, stm32f318))] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     #[cfg(stm32f302)] | ||||
|     /// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1. | ||||
|     Tim3 = 1, | ||||
|     Tim15 = 3, | ||||
|     Tim2 = 4, | ||||
|     #[cfg(all(stm32f302, any(flashsize_6, flashsize_8)))] | ||||
|     Tim4 = 5, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32F303/3x8 (excluding 318 which is like 301, and 378 which is 37x). | ||||
| #[cfg(any(stm32f303, stm32f328, stm32f358, stm32f398))] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     /// * DAC1: defaults to TIM8 but can be remapped to TIM3 with DAC_TRIG_RMP in SYSCFG_CFGR1 | ||||
|     /// * DAC2: always TIM3 | ||||
|     Tim8Or3 = 1, | ||||
|     Tim7 = 2, | ||||
|     Tim15 = 3, | ||||
|     Tim2 = 4, | ||||
|     Tim4 = 5, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32F37x. | ||||
| #[cfg(any(stm32f373, stm32f378))] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     Tim3 = 1, | ||||
|     Tim7 = 2, | ||||
|     /// TIM5 on DAC1, TIM18 on DAC2 | ||||
|     Dac1Tim5Dac2Tim18 = 3, | ||||
|     Tim2 = 4, | ||||
|     Tim4 = 5, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32F334. | ||||
| #[cfg(stm32f334)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     /// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1. | ||||
|     Tim3 = 1, | ||||
|     Tim7 = 2, | ||||
|     /// Can be remapped to HRTIM_DACTRG1 using DAC1_TRIG3_RMP in SYSCFG_CFGR3. | ||||
|     Tim15OrHrtimDacTrg1 = 3, | ||||
|     Tim2 = 4, | ||||
|     /// Requires DAC_TRIG5_RMP set in SYSCFG_CFGR3. | ||||
|     HrtimDacTrg2 = 5, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32L0. | ||||
| #[cfg(stm32l0)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     Tim3 = 1, | ||||
|     Tim3Ch3 = 2, | ||||
|     Tim21 = 3, | ||||
|     Tim2 = 4, | ||||
|     Tim7 = 5, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for STM32L1. | ||||
| #[cfg(stm32l1)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Tim6 = 0, | ||||
|     Tim7 = 2, | ||||
|     Tim9 = 3, | ||||
|     Tim2 = 4, | ||||
|     Tim4 = 5, | ||||
|     Exti9 = 6, | ||||
|     Software = 7, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for L4+, L5, U5, H7. | ||||
| #[cfg(any(stm32l4_plus, stm32l5, stm32u5, stm32h7))] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Software = 0, | ||||
|     Tim1 = 1, | ||||
|     Tim2 = 2, | ||||
|     Tim4 = 3, | ||||
|     Tim5 = 4, | ||||
|     Tim6 = 5, | ||||
|     Tim7 = 6, | ||||
|     Tim8 = 7, | ||||
|     Tim15 = 8, | ||||
|     #[cfg(all(stm32h7, hrtim))] | ||||
|     Hrtim1DacTrg1 = 9, | ||||
|     #[cfg(all(stm32h7, hrtim))] | ||||
|     Hrtim1DacTrg2 = 10, | ||||
|     Lptim1 = 11, | ||||
|     #[cfg(not(stm32u5))] | ||||
|     Lptim2 = 12, | ||||
|     #[cfg(stm32u5)] | ||||
|     Lptim3 = 12, | ||||
|     Exti9 = 13, | ||||
|     #[cfg(any(stm32h7ax, stm32h7bx))] | ||||
|     /// RM0455 suggests this might be LPTIM2 on DAC1 and LPTIM3 on DAC2, | ||||
|     /// but it's probably wrong. Please let us know if you find out. | ||||
|     Lptim3 = 14, | ||||
|     #[cfg(any(stm32h72x, stm32h73x))] | ||||
|     Tim23 = 14, | ||||
|     #[cfg(any(stm32h72x, stm32h73x))] | ||||
|     Tim24 = 15, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for H5. | ||||
| #[cfg(stm32h5)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Software = 0, | ||||
|     Tim1 = 1, | ||||
|     Tim2 = 2, | ||||
|     #[cfg(any(stm32h56x, stm32h57x))] | ||||
|     Tim4 = 3, | ||||
|     #[cfg(stm32h503)] | ||||
|     Tim3 = 3, | ||||
|     #[cfg(any(stm32h56x, stm32h57x))] | ||||
|     Tim5 = 4, | ||||
|     Tim6 = 5, | ||||
|     Tim7 = 6, | ||||
|     #[cfg(any(stm32h56x, stm32h57x))] | ||||
|     Tim8 = 7, | ||||
|     #[cfg(any(stm32h56x, stm32h57x))] | ||||
|     Tim15 = 8, | ||||
|     Lptim1 = 11, | ||||
|     Lptim2 = 12, | ||||
|     Exti9 = 13, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for G0. | ||||
| #[cfg(stm32g0)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Software = 0, | ||||
|     Tim1 = 1, | ||||
|     Tim2 = 2, | ||||
|     Tim3 = 3, | ||||
|     Tim6 = 5, | ||||
|     Tim7 = 6, | ||||
|     Tim15 = 8, | ||||
|     Lptim1 = 11, | ||||
|     Lptim2 = 12, | ||||
|     Exti9 = 13, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for G4. | ||||
| #[cfg(stm32g4)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Software = 0, | ||||
|     /// * DAC1, DAC2, DAC4: TIM8 | ||||
|     /// * DAC3: TIM1 | ||||
|     Dac124Tim8Dac3Tim1 = 1, | ||||
|     Tim7 = 2, | ||||
|     Tim15 = 3, | ||||
|     Tim2 = 4, | ||||
|     Tim4 = 5, | ||||
|     Exti9 = 6, | ||||
|     Tim6 = 7, | ||||
|     Tim3 = 8, | ||||
|     HrtimDacRstTrg1 = 9, | ||||
|     HrtimDacRstTrg2 = 10, | ||||
|     HrtimDacRstTrg3 = 11, | ||||
|     HrtimDacRstTrg4 = 12, | ||||
|     HrtimDacRstTrg5 = 13, | ||||
|     HrtimDacRstTrg6 = 14, | ||||
|     /// * DAC1, DAC4: HRTIM_DAC_TRG1 | ||||
|     /// * DAC2: HRTIM_DAC_TRG2 | ||||
|     /// * DAC3: HRTIM_DAC_TRG3 | ||||
|     HrtimDacTrg123 = 15, | ||||
| } | ||||
|  | ||||
| /// Trigger selection for WL. | ||||
| #[cfg(stm32wl)] | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum TriggerSel { | ||||
|     Software = 0, | ||||
|     Tim1 = 1, | ||||
|     Tim2 = 2, | ||||
|     Lptim1 = 11, | ||||
|     Lptim2 = 12, | ||||
|     Lptim3 = 13, | ||||
|     Exti9 = 14, | ||||
| } | ||||
|  | ||||
| impl TriggerSel { | ||||
|     pub fn tsel(&self) -> u8 { | ||||
|         *self as u8 | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +1,14 @@ | ||||
| #![macro_use] | ||||
|  | ||||
| use core::marker::PhantomData; | ||||
|  | ||||
| use crate::interrupt; | ||||
|  | ||||
| #[cfg_attr(i2c_v1, path = "v1.rs")] | ||||
| #[cfg_attr(i2c_v2, path = "v2.rs")] | ||||
| mod _version; | ||||
| pub use _version::*; | ||||
| use embassy_sync::waitqueue::AtomicWaker; | ||||
|  | ||||
| use crate::peripherals; | ||||
|  | ||||
| @@ -23,6 +26,20 @@ pub enum Error { | ||||
|  | ||||
| pub(crate) mod sealed { | ||||
|     use super::*; | ||||
|  | ||||
|     pub struct State { | ||||
|         #[allow(unused)] | ||||
|         pub waker: AtomicWaker, | ||||
|     } | ||||
|  | ||||
|     impl State { | ||||
|         pub const fn new() -> Self { | ||||
|             Self { | ||||
|                 waker: AtomicWaker::new(), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub trait Instance: crate::rcc::RccPeripheral { | ||||
|         fn regs() -> crate::pac::i2c::I2c; | ||||
|         fn state() -> &'static State; | ||||
| @@ -30,7 +47,8 @@ pub(crate) mod sealed { | ||||
| } | ||||
|  | ||||
| pub trait Instance: sealed::Instance + 'static { | ||||
|     type Interrupt: interrupt::typelevel::Interrupt; | ||||
|     type EventInterrupt: interrupt::typelevel::Interrupt; | ||||
|     type ErrorInterrupt: interrupt::typelevel::Interrupt; | ||||
| } | ||||
|  | ||||
| pin_trait!(SclPin, Instance); | ||||
| @@ -38,21 +56,148 @@ pin_trait!(SdaPin, Instance); | ||||
| dma_trait!(RxDma, Instance); | ||||
| dma_trait!(TxDma, Instance); | ||||
|  | ||||
| foreach_interrupt!( | ||||
|     ($inst:ident, i2c, $block:ident, EV, $irq:ident) => { | ||||
| /// Interrupt handler. | ||||
| pub struct EventInterruptHandler<T: Instance> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInterruptHandler<T> { | ||||
|     unsafe fn on_interrupt() { | ||||
|         _version::on_interrupt::<T>() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct ErrorInterruptHandler<T: Instance> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Instance> interrupt::typelevel::Handler<T::ErrorInterrupt> for ErrorInterruptHandler<T> { | ||||
|     unsafe fn on_interrupt() { | ||||
|         _version::on_interrupt::<T>() | ||||
|     } | ||||
| } | ||||
|  | ||||
| foreach_peripheral!( | ||||
|     (i2c, $inst:ident) => { | ||||
|         impl sealed::Instance for peripherals::$inst { | ||||
|             fn regs() -> crate::pac::i2c::I2c { | ||||
|                 crate::pac::$inst | ||||
|             } | ||||
|  | ||||
|             fn state() -> &'static State { | ||||
|                 static STATE: State = State::new(); | ||||
|             fn state() -> &'static sealed::State { | ||||
|                 static STATE: sealed::State = sealed::State::new(); | ||||
|                 &STATE | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         impl Instance for peripherals::$inst { | ||||
|             type Interrupt = crate::interrupt::typelevel::$irq; | ||||
|             type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV; | ||||
|             type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER; | ||||
|         } | ||||
|     }; | ||||
| ); | ||||
|  | ||||
| mod eh02 { | ||||
|     use super::*; | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { | ||||
|         type Error = Error; | ||||
|  | ||||
|         fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_read(address, buffer) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { | ||||
|         type Error = Error; | ||||
|  | ||||
|         fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write(address, write) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { | ||||
|         type Error = Error; | ||||
|  | ||||
|         fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write_read(address, write, read) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "unstable-traits")] | ||||
| mod eh1 { | ||||
|     use super::*; | ||||
|     use crate::dma::NoDma; | ||||
|  | ||||
|     impl embedded_hal_1::i2c::Error for Error { | ||||
|         fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { | ||||
|             match *self { | ||||
|                 Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus, | ||||
|                 Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, | ||||
|                 Self::Nack => { | ||||
|                     embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) | ||||
|                 } | ||||
|                 Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, | ||||
|                 Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> { | ||||
|         type Error = Error; | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { | ||||
|         fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_read(address, read) | ||||
|         } | ||||
|  | ||||
|         fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write(address, write) | ||||
|         } | ||||
|  | ||||
|         fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write_read(address, write, read) | ||||
|         } | ||||
|  | ||||
|         fn transaction( | ||||
|             &mut self, | ||||
|             _address: u8, | ||||
|             _operations: &mut [embedded_hal_1::i2c::Operation<'_>], | ||||
|         ) -> Result<(), Self::Error> { | ||||
|             todo!(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(all(feature = "unstable-traits", feature = "nightly"))] | ||||
| mod eha { | ||||
|     use super::*; | ||||
|  | ||||
|     impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { | ||||
|         async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.read(address, read).await | ||||
|         } | ||||
|  | ||||
|         async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | ||||
|             self.write(address, write).await | ||||
|         } | ||||
|  | ||||
|         async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.write_read(address, write, read).await | ||||
|         } | ||||
|  | ||||
|         async fn transaction( | ||||
|             &mut self, | ||||
|             address: u8, | ||||
|             operations: &mut [embedded_hal_1::i2c::Operation<'_>], | ||||
|         ) -> Result<(), Self::Error> { | ||||
|             let _ = address; | ||||
|             let _ = operations; | ||||
|             todo!() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,23 +1,33 @@ | ||||
| use core::future::poll_fn; | ||||
| use core::marker::PhantomData; | ||||
| use core::task::Poll; | ||||
|  | ||||
| use embassy_embedded_hal::SetConfig; | ||||
| use embassy_futures::select::{select, Either}; | ||||
| use embassy_hal_internal::drop::OnDrop; | ||||
| use embassy_hal_internal::{into_ref, PeripheralRef}; | ||||
|  | ||||
| use crate::dma::NoDma; | ||||
| use super::*; | ||||
| use crate::dma::{NoDma, Transfer}; | ||||
| use crate::gpio::sealed::AFType; | ||||
| use crate::gpio::Pull; | ||||
| use crate::i2c::{Error, Instance, SclPin, SdaPin}; | ||||
| use crate::interrupt::typelevel::Interrupt; | ||||
| use crate::pac::i2c; | ||||
| use crate::time::Hertz; | ||||
| use crate::{interrupt, Peripheral}; | ||||
|  | ||||
| /// Interrupt handler. | ||||
| pub struct InterruptHandler<T: Instance> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||||
|     unsafe fn on_interrupt() {} | ||||
| pub unsafe fn on_interrupt<T: Instance>() { | ||||
|     let regs = T::regs(); | ||||
|     // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of | ||||
|     // other stuff, so we wake the task on every interrupt. | ||||
|     T::state().waker.wake(); | ||||
|     critical_section::with(|_| { | ||||
|         // Clear event interrupt flag. | ||||
|         regs.cr2().modify(|w| { | ||||
|             w.set_itevten(false); | ||||
|             w.set_iterren(false); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| #[non_exhaustive] | ||||
| @@ -27,14 +37,6 @@ pub struct Config { | ||||
|     pub scl_pullup: bool, | ||||
| } | ||||
|  | ||||
| pub struct State {} | ||||
|  | ||||
| impl State { | ||||
|     pub(crate) const fn new() -> Self { | ||||
|         Self {} | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { | ||||
|     phantom: PhantomData<&'d mut T>, | ||||
|     #[allow(dead_code)] | ||||
| @@ -48,7 +50,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|         _peri: impl Peripheral<P = T> + 'd, | ||||
|         scl: impl Peripheral<P = impl SclPin<T>> + 'd, | ||||
|         sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | ||||
|         _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||||
|         _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> | ||||
|             + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> | ||||
|             + 'd, | ||||
|         tx_dma: impl Peripheral<P = TXDMA> + 'd, | ||||
|         rx_dma: impl Peripheral<P = RXDMA> + 'd, | ||||
|         freq: Hertz, | ||||
| @@ -98,6 +102,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|             reg.set_pe(true); | ||||
|         }); | ||||
|  | ||||
|         unsafe { T::EventInterrupt::enable() }; | ||||
|         unsafe { T::ErrorInterrupt::enable() }; | ||||
|  | ||||
|         Self { | ||||
|             phantom: PhantomData, | ||||
|             tx_dma, | ||||
| @@ -105,40 +112,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> { | ||||
|     fn check_and_clear_error_flags() -> Result<i2c::regs::Sr1, Error> { | ||||
|         // Note that flags should only be cleared once they have been registered. If flags are | ||||
|         // cleared otherwise, there may be an inherent race condition and flags may be missed. | ||||
|         let sr1 = T::regs().sr1().read(); | ||||
|  | ||||
|         if sr1.timeout() { | ||||
|             T::regs().sr1().modify(|reg| reg.set_timeout(false)); | ||||
|             T::regs().sr1().write(|reg| { | ||||
|                 reg.0 = !0; | ||||
|                 reg.set_timeout(false); | ||||
|             }); | ||||
|             return Err(Error::Timeout); | ||||
|         } | ||||
|  | ||||
|         if sr1.pecerr() { | ||||
|             T::regs().sr1().modify(|reg| reg.set_pecerr(false)); | ||||
|             T::regs().sr1().write(|reg| { | ||||
|                 reg.0 = !0; | ||||
|                 reg.set_pecerr(false); | ||||
|             }); | ||||
|             return Err(Error::Crc); | ||||
|         } | ||||
|  | ||||
|         if sr1.ovr() { | ||||
|             T::regs().sr1().modify(|reg| reg.set_ovr(false)); | ||||
|             T::regs().sr1().write(|reg| { | ||||
|                 reg.0 = !0; | ||||
|                 reg.set_ovr(false); | ||||
|             }); | ||||
|             return Err(Error::Overrun); | ||||
|         } | ||||
|  | ||||
|         if sr1.af() { | ||||
|             T::regs().sr1().modify(|reg| reg.set_af(false)); | ||||
|             T::regs().sr1().write(|reg| { | ||||
|                 reg.0 = !0; | ||||
|                 reg.set_af(false); | ||||
|             }); | ||||
|             return Err(Error::Nack); | ||||
|         } | ||||
|  | ||||
|         if sr1.arlo() { | ||||
|             T::regs().sr1().modify(|reg| reg.set_arlo(false)); | ||||
|             T::regs().sr1().write(|reg| { | ||||
|                 reg.0 = !0; | ||||
|                 reg.set_arlo(false); | ||||
|             }); | ||||
|             return Err(Error::Arbitration); | ||||
|         } | ||||
|  | ||||
|         // The errata indicates that BERR may be incorrectly detected. It recommends ignoring and | ||||
|         // clearing the BERR bit instead. | ||||
|         if sr1.berr() { | ||||
|             T::regs().sr1().modify(|reg| reg.set_berr(false)); | ||||
|             T::regs().sr1().write(|reg| { | ||||
|                 reg.0 = !0; | ||||
|                 reg.set_berr(false); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         Ok(sr1) | ||||
| @@ -157,13 +182,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|         }); | ||||
|  | ||||
|         // Wait until START condition was generated | ||||
|         while !self.check_and_clear_error_flags()?.start() { | ||||
|         while !Self::check_and_clear_error_flags()?.start() { | ||||
|             check_timeout()?; | ||||
|         } | ||||
|  | ||||
|         // Also wait until signalled we're master and everything is waiting for us | ||||
|         while { | ||||
|             self.check_and_clear_error_flags()?; | ||||
|             Self::check_and_clear_error_flags()?; | ||||
|  | ||||
|             let sr2 = T::regs().sr2().read(); | ||||
|             !sr2.msl() && !sr2.busy() | ||||
| @@ -177,7 +202,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|         // Wait until address was sent | ||||
|         // Wait for the address to be acknowledged | ||||
|         // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. | ||||
|         while !self.check_and_clear_error_flags()?.addr() { | ||||
|         while !Self::check_and_clear_error_flags()?.addr() { | ||||
|             check_timeout()?; | ||||
|         } | ||||
|  | ||||
| @@ -197,7 +222,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|         // Wait until we're ready for sending | ||||
|         while { | ||||
|             // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. | ||||
|             !self.check_and_clear_error_flags()?.txe() | ||||
|             !Self::check_and_clear_error_flags()?.txe() | ||||
|         } { | ||||
|             check_timeout()?; | ||||
|         } | ||||
| @@ -208,7 +233,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|         // Wait until byte is transferred | ||||
|         while { | ||||
|             // Check for any potential error conditions. | ||||
|             !self.check_and_clear_error_flags()?.btf() | ||||
|             !Self::check_and_clear_error_flags()?.btf() | ||||
|         } { | ||||
|             check_timeout()?; | ||||
|         } | ||||
| @@ -219,7 +244,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|     fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> { | ||||
|         while { | ||||
|             // Check for any potential error conditions. | ||||
|             self.check_and_clear_error_flags()?; | ||||
|             Self::check_and_clear_error_flags()?; | ||||
|  | ||||
|             !T::regs().sr1().read().rxne() | ||||
|         } { | ||||
| @@ -244,7 +269,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|             }); | ||||
|  | ||||
|             // Wait until START condition was generated | ||||
|             while !self.check_and_clear_error_flags()?.start() { | ||||
|             while !Self::check_and_clear_error_flags()?.start() { | ||||
|                 check_timeout()?; | ||||
|             } | ||||
|  | ||||
| @@ -261,7 +286,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|  | ||||
|             // Wait until address was sent | ||||
|             // Wait for the address to be acknowledged | ||||
|             while !self.check_and_clear_error_flags()?.addr() { | ||||
|             while !Self::check_and_clear_error_flags()?.addr() { | ||||
|                 check_timeout()?; | ||||
|             } | ||||
|  | ||||
| @@ -336,6 +361,356 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|     pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | ||||
|         self.blocking_write_read_timeout(addr, write, read, || Ok(())) | ||||
|     } | ||||
|  | ||||
|     // Async | ||||
|  | ||||
|     #[inline] // pretty sure this should always be inlined | ||||
|     fn enable_interrupts() -> () { | ||||
|         T::regs().cr2().modify(|w| { | ||||
|             w.set_iterren(true); | ||||
|             w.set_itevten(true); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error> | ||||
|     where | ||||
|         TXDMA: crate::i2c::TxDma<T>, | ||||
|     { | ||||
|         let dma_transfer = unsafe { | ||||
|             let regs = T::regs(); | ||||
|             regs.cr2().modify(|w| { | ||||
|                 // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. | ||||
|                 w.set_dmaen(true); | ||||
|                 w.set_itbufen(false); | ||||
|             }); | ||||
|             // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. | ||||
|             let dst = regs.dr().as_ptr() as *mut u8; | ||||
|  | ||||
|             let ch = &mut self.tx_dma; | ||||
|             let request = ch.request(); | ||||
|             Transfer::new_write(ch, request, write, dst, Default::default()) | ||||
|         }; | ||||
|  | ||||
|         let on_drop = OnDrop::new(|| { | ||||
|             let regs = T::regs(); | ||||
|             regs.cr2().modify(|w| { | ||||
|                 w.set_dmaen(false); | ||||
|                 w.set_iterren(false); | ||||
|                 w.set_itevten(false); | ||||
|             }) | ||||
|         }); | ||||
|  | ||||
|         Self::enable_interrupts(); | ||||
|  | ||||
|         // Send a START condition | ||||
|         T::regs().cr1().modify(|reg| { | ||||
|             reg.set_start(true); | ||||
|         }); | ||||
|  | ||||
|         let state = T::state(); | ||||
|  | ||||
|         // Wait until START condition was generated | ||||
|         poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|  | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 Err(e) => Poll::Ready(Err(e)), | ||||
|                 Ok(sr1) => { | ||||
|                     if sr1.start() { | ||||
|                         Poll::Ready(Ok(())) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|  | ||||
|         // Also wait until signalled we're master and everything is waiting for us | ||||
|         Self::enable_interrupts(); | ||||
|         poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|  | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 Err(e) => Poll::Ready(Err(e)), | ||||
|                 Ok(_) => { | ||||
|                     let sr2 = T::regs().sr2().read(); | ||||
|                     if !sr2.msl() && !sr2.busy() { | ||||
|                         Poll::Pending | ||||
|                     } else { | ||||
|                         Poll::Ready(Ok(())) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|  | ||||
|         // Set up current address, we're trying to talk to | ||||
|         Self::enable_interrupts(); | ||||
|         T::regs().dr().write(|reg| reg.set_dr(address << 1)); | ||||
|  | ||||
|         poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 Err(e) => Poll::Ready(Err(e)), | ||||
|                 Ok(sr1) => { | ||||
|                     if sr1.addr() { | ||||
|                         // Clear the ADDR condition by reading SR2. | ||||
|                         T::regs().sr2().read(); | ||||
|                         Poll::Ready(Ok(())) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|         Self::enable_interrupts(); | ||||
|         let poll_error = poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|  | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 // Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other | ||||
|                 // identical poll_fn check_and_clear matches. | ||||
|                 Err(e) => Poll::Ready(Err::<T, Error>(e)), | ||||
|                 Ok(_) => Poll::Pending, | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         // Wait for either the DMA transfer to successfully finish, or an I2C error to occur. | ||||
|         match select(dma_transfer, poll_error).await { | ||||
|             Either::Second(Err(e)) => Err(e), | ||||
|             _ => Ok(()), | ||||
|         }?; | ||||
|  | ||||
|         // The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too. | ||||
|  | ||||
|         // 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA | ||||
|         // requests then wait for a BTF event before programming the Stop condition.” | ||||
|  | ||||
|         // TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it? | ||||
|         T::regs().cr2().modify(|w| { | ||||
|             w.set_dmaen(false); | ||||
|         }); | ||||
|  | ||||
|         Self::enable_interrupts(); | ||||
|         poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|  | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 Err(e) => Poll::Ready(Err(e)), | ||||
|                 Ok(sr1) => { | ||||
|                     if sr1.btf() { | ||||
|                         if send_stop { | ||||
|                             T::regs().cr1().modify(|w| { | ||||
|                                 w.set_stop(true); | ||||
|                             }); | ||||
|                         } | ||||
|  | ||||
|                         Poll::Ready(Ok(())) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|  | ||||
|         drop(on_drop); | ||||
|  | ||||
|         // Fallthrough is success | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> | ||||
|     where | ||||
|         TXDMA: crate::i2c::TxDma<T>, | ||||
|     { | ||||
|         self.write_with_stop(address, write, true).await?; | ||||
|  | ||||
|         // Wait for STOP condition to transmit. | ||||
|         Self::enable_interrupts(); | ||||
|         poll_fn(|cx| { | ||||
|             T::state().waker.register(cx.waker()); | ||||
|             // TODO: error interrupts are enabled here, should we additional check for and return errors? | ||||
|             if T::regs().cr1().read().stop() { | ||||
|                 Poll::Pending | ||||
|             } else { | ||||
|                 Poll::Ready(Ok(())) | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> | ||||
|     where | ||||
|         RXDMA: crate::i2c::RxDma<T>, | ||||
|     { | ||||
|         let state = T::state(); | ||||
|         let buffer_len = buffer.len(); | ||||
|  | ||||
|         let dma_transfer = unsafe { | ||||
|             let regs = T::regs(); | ||||
|             regs.cr2().modify(|w| { | ||||
|                 // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. | ||||
|                 w.set_itbufen(false); | ||||
|                 w.set_dmaen(true); | ||||
|             }); | ||||
|             // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. | ||||
|             let src = regs.dr().as_ptr() as *mut u8; | ||||
|  | ||||
|             let ch = &mut self.rx_dma; | ||||
|             let request = ch.request(); | ||||
|             Transfer::new_read(ch, request, src, buffer, Default::default()) | ||||
|         }; | ||||
|  | ||||
|         let on_drop = OnDrop::new(|| { | ||||
|             let regs = T::regs(); | ||||
|             regs.cr2().modify(|w| { | ||||
|                 w.set_dmaen(false); | ||||
|                 w.set_iterren(false); | ||||
|                 w.set_itevten(false); | ||||
|             }) | ||||
|         }); | ||||
|  | ||||
|         Self::enable_interrupts(); | ||||
|  | ||||
|         // Send a START condition and set ACK bit | ||||
|         T::regs().cr1().modify(|reg| { | ||||
|             reg.set_start(true); | ||||
|             reg.set_ack(true); | ||||
|         }); | ||||
|  | ||||
|         // Wait until START condition was generated | ||||
|         poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|  | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 Err(e) => Poll::Ready(Err(e)), | ||||
|                 Ok(sr1) => { | ||||
|                     if sr1.start() { | ||||
|                         Poll::Ready(Ok(())) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|  | ||||
|         // Also wait until signalled we're master and everything is waiting for us | ||||
|         Self::enable_interrupts(); | ||||
|         poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|  | ||||
|             // blocking read didn’t have a check_and_clear call here, but blocking write did so | ||||
|             // I’m adding it here in case that was an oversight. | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 Err(e) => Poll::Ready(Err(e)), | ||||
|                 Ok(_) => { | ||||
|                     let sr2 = T::regs().sr2().read(); | ||||
|                     if !sr2.msl() && !sr2.busy() { | ||||
|                         Poll::Pending | ||||
|                     } else { | ||||
|                         Poll::Ready(Ok(())) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|  | ||||
|         // Set up current address, we're trying to talk to | ||||
|         T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); | ||||
|  | ||||
|         // Wait for the address to be acknowledged | ||||
|  | ||||
|         Self::enable_interrupts(); | ||||
|         poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|  | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 Err(e) => Poll::Ready(Err(e)), | ||||
|                 Ok(sr1) => { | ||||
|                     if sr1.addr() { | ||||
|                         // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 | ||||
|                         // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. | ||||
|                         if buffer_len == 1 { | ||||
|                             T::regs().cr1().modify(|w| { | ||||
|                                 w.set_ack(false); | ||||
|                             }); | ||||
|                         } | ||||
|                         Poll::Ready(Ok(())) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|  | ||||
|         // Clear ADDR condition by reading SR2 | ||||
|         T::regs().sr2().read(); | ||||
|  | ||||
|         // 18.3.8: When a single byte must be received: [snip] Then the | ||||
|         // user can program the STOP condition either after clearing ADDR flag, or in the | ||||
|         // DMA Transfer Complete interrupt routine. | ||||
|         if buffer_len == 1 { | ||||
|             T::regs().cr1().modify(|w| { | ||||
|                 w.set_stop(true); | ||||
|             }); | ||||
|         } else { | ||||
|             // If, in the I2C_CR2 register, the LAST bit is set, I2C | ||||
|             // automatically sends a NACK after the next byte following EOT_1. The user can | ||||
|             // generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled. | ||||
|             T::regs().cr2().modify(|w| { | ||||
|                 w.set_last(true); | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         // Wait for bytes to be received, or an error to occur. | ||||
|         Self::enable_interrupts(); | ||||
|         let poll_error = poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|  | ||||
|             match Self::check_and_clear_error_flags() { | ||||
|                 Err(e) => Poll::Ready(Err::<T, Error>(e)), | ||||
|                 _ => Poll::Pending, | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         match select(dma_transfer, poll_error).await { | ||||
|             Either::Second(Err(e)) => Err(e), | ||||
|             _ => Ok(()), | ||||
|         }?; | ||||
|  | ||||
|         // Wait for the STOP to be sent (STOP bit cleared). | ||||
|         Self::enable_interrupts(); | ||||
|         poll_fn(|cx| { | ||||
|             state.waker.register(cx.waker()); | ||||
|             // TODO: error interrupts are enabled here, should we additional check for and return errors? | ||||
|             if T::regs().cr1().read().stop() { | ||||
|                 Poll::Pending | ||||
|             } else { | ||||
|                 Poll::Ready(Ok(())) | ||||
|             } | ||||
|         }) | ||||
|         .await?; | ||||
|         drop(on_drop); | ||||
|  | ||||
|         // Fallthrough is success | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> | ||||
|     where | ||||
|         RXDMA: crate::i2c::RxDma<T>, | ||||
|         TXDMA: crate::i2c::TxDma<T>, | ||||
|     { | ||||
|         self.write_with_stop(address, write, false).await?; | ||||
|         self.read(address, read).await | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { | ||||
| @@ -344,77 +719,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { | ||||
|     type Error = Error; | ||||
|  | ||||
|     fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|         self.blocking_read(addr, read) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { | ||||
|     type Error = Error; | ||||
|  | ||||
|     fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { | ||||
|         self.blocking_write(addr, write) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { | ||||
|     type Error = Error; | ||||
|  | ||||
|     fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|         self.blocking_write_read(addr, write, read) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "unstable-traits")] | ||||
| mod eh1 { | ||||
|     use super::*; | ||||
|  | ||||
|     impl embedded_hal_1::i2c::Error for Error { | ||||
|         fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { | ||||
|             match *self { | ||||
|                 Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus, | ||||
|                 Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, | ||||
|                 Self::Nack => { | ||||
|                     embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) | ||||
|                 } | ||||
|                 Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, | ||||
|                 Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for I2c<'d, T> { | ||||
|         type Error = Error; | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> { | ||||
|         fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_read(address, read) | ||||
|         } | ||||
|  | ||||
|         fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write(address, write) | ||||
|         } | ||||
|  | ||||
|         fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write_read(address, write, read) | ||||
|         } | ||||
|  | ||||
|         fn transaction( | ||||
|             &mut self, | ||||
|             _address: u8, | ||||
|             _operations: &mut [embedded_hal_1::i2c::Operation<'_>], | ||||
|         ) -> Result<(), Self::Error> { | ||||
|             todo!(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| enum Mode { | ||||
|     Fast, | ||||
|     Standard, | ||||
|   | ||||
| @@ -1,19 +1,17 @@ | ||||
| use core::cmp; | ||||
| use core::future::poll_fn; | ||||
| use core::marker::PhantomData; | ||||
| use core::task::Poll; | ||||
|  | ||||
| use embassy_embedded_hal::SetConfig; | ||||
| use embassy_hal_internal::drop::OnDrop; | ||||
| use embassy_hal_internal::{into_ref, PeripheralRef}; | ||||
| use embassy_sync::waitqueue::AtomicWaker; | ||||
| #[cfg(feature = "time")] | ||||
| use embassy_time::{Duration, Instant}; | ||||
|  | ||||
| use super::*; | ||||
| use crate::dma::{NoDma, Transfer}; | ||||
| use crate::gpio::sealed::AFType; | ||||
| use crate::gpio::Pull; | ||||
| use crate::i2c::{Error, Instance, SclPin, SdaPin}; | ||||
| use crate::interrupt::typelevel::Interrupt; | ||||
| use crate::pac::i2c; | ||||
| use crate::time::Hertz; | ||||
| @@ -36,13 +34,7 @@ pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> { | ||||
|     move || Ok(()) | ||||
| } | ||||
|  | ||||
| /// Interrupt handler. | ||||
| pub struct InterruptHandler<T: Instance> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||||
|     unsafe fn on_interrupt() { | ||||
| pub unsafe fn on_interrupt<T: Instance>() { | ||||
|     let regs = T::regs(); | ||||
|     let isr = regs.isr().read(); | ||||
|  | ||||
| @@ -54,7 +46,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | ||||
|     critical_section::with(|_| { | ||||
|         regs.cr1().modify(|w| w.set_tcie(false)); | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[non_exhaustive] | ||||
| @@ -77,18 +68,6 @@ impl Default for Config { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct State { | ||||
|     waker: AtomicWaker, | ||||
| } | ||||
|  | ||||
| impl State { | ||||
|     pub(crate) const fn new() -> Self { | ||||
|         Self { | ||||
|             waker: AtomicWaker::new(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { | ||||
|     _peri: PeripheralRef<'d, T>, | ||||
|     #[allow(dead_code)] | ||||
| @@ -104,7 +83,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|         peri: impl Peripheral<P = T> + 'd, | ||||
|         scl: impl Peripheral<P = impl SclPin<T>> + 'd, | ||||
|         sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | ||||
|         _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||||
|         _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> | ||||
|             + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> | ||||
|             + 'd, | ||||
|         tx_dma: impl Peripheral<P = TXDMA> + 'd, | ||||
|         rx_dma: impl Peripheral<P = RXDMA> + 'd, | ||||
|         freq: Hertz, | ||||
| @@ -150,8 +131,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | ||||
|             reg.set_pe(true); | ||||
|         }); | ||||
|  | ||||
|         T::Interrupt::unpend(); | ||||
|         unsafe { T::Interrupt::enable() }; | ||||
|         unsafe { T::EventInterrupt::enable() }; | ||||
|         unsafe { T::ErrorInterrupt::enable() }; | ||||
|  | ||||
|         Self { | ||||
|             _peri: peri, | ||||
| @@ -987,35 +968,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "time")] | ||||
| mod eh02 { | ||||
|     use super::*; | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { | ||||
|         type Error = Error; | ||||
|  | ||||
|         fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_read(address, buffer) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { | ||||
|         type Error = Error; | ||||
|  | ||||
|         fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write(address, write) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { | ||||
|         type Error = Error; | ||||
|  | ||||
|         fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write_read(address, write, read) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// I2C Stop Configuration | ||||
| /// | ||||
| /// Peripheral options for generating the STOP condition | ||||
| @@ -1140,83 +1092,6 @@ impl Timings { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "unstable-traits")] | ||||
| mod eh1 { | ||||
|     use super::*; | ||||
|  | ||||
|     impl embedded_hal_1::i2c::Error for Error { | ||||
|         fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { | ||||
|             match *self { | ||||
|                 Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus, | ||||
|                 Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, | ||||
|                 Self::Nack => { | ||||
|                     embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) | ||||
|                 } | ||||
|                 Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|                 Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, | ||||
|                 Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> { | ||||
|         type Error = Error; | ||||
|     } | ||||
|  | ||||
|     impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { | ||||
|         fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_read(address, read) | ||||
|         } | ||||
|  | ||||
|         fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write(address, write) | ||||
|         } | ||||
|  | ||||
|         fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.blocking_write_read(address, write, read) | ||||
|         } | ||||
|  | ||||
|         fn transaction( | ||||
|             &mut self, | ||||
|             _address: u8, | ||||
|             _operations: &mut [embedded_hal_1::i2c::Operation<'_>], | ||||
|         ) -> Result<(), Self::Error> { | ||||
|             todo!(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(all(feature = "unstable-traits", feature = "nightly"))] | ||||
| mod eha { | ||||
|     use super::super::{RxDma, TxDma}; | ||||
|     use super::*; | ||||
|  | ||||
|     impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { | ||||
|         async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.read(address, read).await | ||||
|         } | ||||
|  | ||||
|         async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | ||||
|             self.write(address, write).await | ||||
|         } | ||||
|  | ||||
|         async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | ||||
|             self.write_read(address, write, read).await | ||||
|         } | ||||
|  | ||||
|         async fn transaction( | ||||
|             &mut self, | ||||
|             address: u8, | ||||
|             operations: &mut [embedded_hal_1::i2c::Operation<'_>], | ||||
|         ) -> Result<(), Self::Error> { | ||||
|             let _ = address; | ||||
|             let _ = operations; | ||||
|             todo!() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> SetConfig for I2c<'d, T> { | ||||
|     type Config = Hertz; | ||||
|     type ConfigError = (); | ||||
|   | ||||
| @@ -346,10 +346,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { | ||||
|         None => { | ||||
|             cfg_if::cfg_if! { | ||||
|                 // For some chips PREDIV is always two, and cannot be changed | ||||
|                 if #[cfg(any( | ||||
|                         stm32f302xd, stm32f302xe, stm32f303xd, | ||||
|                         stm32f303xe, stm32f398xe | ||||
|                     ))] { | ||||
|                 if #[cfg(any(flashsize_d, flashsize_e))] { | ||||
|                     let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0); | ||||
|                     ( | ||||
|                         Hertz((HSI_FREQ.0 / divisor) * multiplier), | ||||
|   | ||||
| @@ -1457,8 +1457,8 @@ cfg_if::cfg_if! { | ||||
|         macro_rules! kernel_clk { | ||||
|             ($inst:ident) => { | ||||
|                 critical_section::with(|_| unsafe { | ||||
|                     crate::rcc::get_freqs().pll1_q | ||||
|                 }).expect("PLL48 is required for SDIO") | ||||
|                     unwrap!(crate::rcc::get_freqs().pll1_q) | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|     } else if #[cfg(stm32f7)] { | ||||
| @@ -1469,7 +1469,7 @@ cfg_if::cfg_if! { | ||||
|                     if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS { | ||||
|                         crate::rcc::get_freqs().sys | ||||
|                     } else { | ||||
|                         crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC") | ||||
|                         unwrap!(crate::rcc::get_freqs().pll1_q) | ||||
|                     } | ||||
|                 }) | ||||
|             }; | ||||
| @@ -1479,7 +1479,7 @@ cfg_if::cfg_if! { | ||||
|                     if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS { | ||||
|                         crate::rcc::get_freqs().sys | ||||
|                     } else { | ||||
|                         crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC") | ||||
|                         unwrap!(crate::rcc::get_freqs().pll1_q) | ||||
|                     } | ||||
|                 }) | ||||
|             }; | ||||
|   | ||||
| @@ -35,7 +35,7 @@ futures-util = { version = "0.3.17", default-features = false } | ||||
| critical-section = "1.1" | ||||
| heapless = "0.8" | ||||
| cfg-if = "1.0.0" | ||||
| embedded-io-async = { version = "0.6.0", optional = true } | ||||
| embedded-io-async = { version = "0.6.1", optional = true } | ||||
|  | ||||
| [dev-dependencies] | ||||
| futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } | ||||
|   | ||||
| @@ -22,8 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | ||||
|  | ||||
| ## 0.1.3 - 2023-08-28 | ||||
|  | ||||
| - Update `embedded-hal-async` to `1.0.0-rc.1` | ||||
| - Update `embedded-hal v1` to `1.0.0-rc.1` | ||||
| - Update `embedded-hal-async` to `1.0.0-rc.2` | ||||
| - Update `embedded-hal v1` to `1.0.0-rc.2` | ||||
|  | ||||
| ## 0.1.2 - 2023-07-05 | ||||
|  | ||||
|   | ||||
| @@ -242,8 +242,8 @@ defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4.14", optional = true } | ||||
|  | ||||
| embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2", optional = true} | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2", optional = true} | ||||
|  | ||||
| futures-util = { version = "0.3.17", default-features = false } | ||||
| critical-section = "1.1" | ||||
|   | ||||
| @@ -18,7 +18,11 @@ pub struct Delay; | ||||
| mod eh1 { | ||||
|     use super::*; | ||||
|  | ||||
|     impl embedded_hal_1::delay::DelayUs for Delay { | ||||
|     impl embedded_hal_1::delay::DelayNs for Delay { | ||||
|         fn delay_ns(&mut self, ns: u32) { | ||||
|             block_for(Duration::from_nanos(ns as u64)) | ||||
|         } | ||||
|  | ||||
|         fn delay_us(&mut self, us: u32) { | ||||
|             block_for(Duration::from_micros(us as u64)) | ||||
|         } | ||||
| @@ -34,13 +38,17 @@ mod eha { | ||||
|     use super::*; | ||||
|     use crate::Timer; | ||||
|  | ||||
|     impl embedded_hal_async::delay::DelayUs for Delay { | ||||
|         async fn delay_us(&mut self, micros: u32) { | ||||
|             Timer::after_micros(micros as _).await | ||||
|     impl embedded_hal_async::delay::DelayNs for Delay { | ||||
|         async fn delay_ns(&mut self, ns: u32) { | ||||
|             Timer::after_nanos(ns as _).await | ||||
|         } | ||||
|  | ||||
|         async fn delay_ms(&mut self, millis: u32) { | ||||
|             Timer::after_millis(millis as _).await | ||||
|         async fn delay_us(&mut self, us: u32) { | ||||
|             Timer::after_micros(us as _).await | ||||
|         } | ||||
|  | ||||
|         async fn delay_ms(&mut self, ms: u32) { | ||||
|             Timer::after_millis(ms as _).await | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ use core::fmt; | ||||
| use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; | ||||
|  | ||||
| use super::{GCD_1K, GCD_1M, TICK_HZ}; | ||||
| use crate::GCD_1G; | ||||
|  | ||||
| #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| @@ -61,6 +62,14 @@ impl Duration { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Creates a duration from the specified number of nanoseconds, rounding up. | ||||
|     /// NOTE: Delays this small may be inaccurate. | ||||
|     pub const fn from_nanos(micros: u64) -> Duration { | ||||
|         Duration { | ||||
|             ticks: div_ceil(micros * (TICK_HZ / GCD_1G), 1_000_000_000 / GCD_1G), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Creates a duration from the specified number of seconds, rounding down. | ||||
|     pub const fn from_secs_floor(secs: u64) -> Duration { | ||||
|         Duration { ticks: secs * TICK_HZ } | ||||
|   | ||||
| @@ -52,6 +52,7 @@ const fn gcd(a: u64, b: u64) -> u64 { | ||||
|  | ||||
| pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000); | ||||
| pub(crate) const GCD_1M: u64 = gcd(TICK_HZ, 1_000_000); | ||||
| pub(crate) const GCD_1G: u64 = gcd(TICK_HZ, 1_000_000_000); | ||||
|  | ||||
| #[cfg(feature = "defmt-timestamp-uptime")] | ||||
| defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() } | ||||
|   | ||||
| @@ -74,6 +74,15 @@ impl Timer { | ||||
|         Self::after(Duration::from_ticks(ticks)) | ||||
|     } | ||||
|  | ||||
|     /// Expire after the specified number of nanoseconds. | ||||
|     /// | ||||
|     /// This method is a convenience wrapper for calling `Timer::after(Duration::from_nanos())`. | ||||
|     /// For more details, refer to [`Timer::after()`] and [`Duration::from_nanos()`]. | ||||
|     #[inline] | ||||
|     pub fn after_nanos(nanos: u64) -> Self { | ||||
|         Self::after(Duration::from_nanos(nanos)) | ||||
|     } | ||||
|  | ||||
|     /// Expire after the specified number of microseconds. | ||||
|     /// | ||||
|     /// This method is a convenience wrapper for calling `Timer::after(Duration::from_micros())`. | ||||
|   | ||||
| @@ -4,7 +4,7 @@ This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can im | ||||
| traits to add support for using `embassy-usb` for a given chip/platform. | ||||
|  | ||||
| The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`] | ||||
| APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used | ||||
| APIs don't cause a semver-major bump of this crate. This allows existing HALs/BSPs to be used | ||||
| with the newer `embassy-usb` without needing updates. | ||||
|  | ||||
| If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate | ||||
|   | ||||
| @@ -21,10 +21,6 @@ nightly = [ | ||||
|     "embedded-io-async", | ||||
|     "embedded-hal-bus/async", | ||||
|     "embassy-net", | ||||
|     "embassy-lora", | ||||
|     "lora-phy", | ||||
|     "lorawan-device", | ||||
|     "lorawan", | ||||
| ] | ||||
|  | ||||
| [dependencies] | ||||
| @@ -36,11 +32,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm | ||||
| embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } | ||||
| embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } | ||||
| embedded-io = { version = "0.6.0", features = ["defmt-03"]  } | ||||
| embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] } | ||||
| embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } | ||||
| lora-phy = { version = "2", optional = true } | ||||
| lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } | ||||
| lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"], optional = true } | ||||
| embedded-io-async = { version = "0.6.1", optional = true, features = ["defmt-03"] } | ||||
| embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } | ||||
| embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true } | ||||
|  | ||||
| @@ -57,9 +49,9 @@ rand = { version = "0.8.4", default-features = false } | ||||
| embedded-storage = "0.3.0" | ||||
| usbd-hid = "0.6.0" | ||||
| serde = { version = "1.0.136", default-features = false } | ||||
| embedded-hal = { version = "1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "1.0.0-rc.1", optional = true } | ||||
| embedded-hal-bus = { version = "0.1.0-rc.1" } | ||||
| embedded-hal = { version = "1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "1.0.0-rc.2", optional = true } | ||||
| embedded-hal-bus = { version = "0.1.0-rc.2" } | ||||
| num-integer = { version = "0.1.45", default-features = false } | ||||
| microfft = "0.5.0" | ||||
|  | ||||
|   | ||||
| @@ -1,97 +0,0 @@ | ||||
| //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||||
| //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||||
| //! It demonstrates LORA CAD functionality. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||||
| use embassy_nrf::{bind_interrupts, peripherals, spim}; | ||||
| use embassy_time::{Delay, Timer}; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p = embassy_nrf::init(Default::default()); | ||||
|     let mut spi_config = spim::Config::default(); | ||||
|     spi_config.frequency = spim::Frequency::M16; | ||||
|  | ||||
|     let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||||
|     let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||||
|     let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||||
|     let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||||
|  | ||||
|     let iv = | ||||
|         GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||||
|     let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||||
|  | ||||
|     start_indicator.set_high(); | ||||
|     Timer::after_secs(5).await; | ||||
|     start_indicator.set_low(); | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.prepare_for_cad(&mdltn_params, true).await { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.cad().await { | ||||
|         Ok(cad_activity_detected) => { | ||||
|             if cad_activity_detected { | ||||
|                 info!("cad successful with activity detected") | ||||
|             } else { | ||||
|                 info!("cad successful without activity detected") | ||||
|             } | ||||
|             debug_indicator.set_high(); | ||||
|             Timer::after_secs(5).await; | ||||
|             debug_indicator.set_low(); | ||||
|         } | ||||
|         Err(err) => info!("cad unsuccessful = {}", err), | ||||
|     } | ||||
| } | ||||
| @@ -1,82 +0,0 @@ | ||||
| //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||||
| //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||||
| //! It demonstrates LoRaWAN join functionality. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_lora::LoraTimer; | ||||
| use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||||
| use embassy_nrf::rng::Rng; | ||||
| use embassy_nrf::{bind_interrupts, peripherals, rng, spim}; | ||||
| use embassy_time::Delay; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use lorawan::default_crypto::DefaultFactory as Crypto; | ||||
| use lorawan_device::async_device::lora_radio::LoRaRadio; | ||||
| use lorawan_device::async_device::{region, Device, JoinMode}; | ||||
| use lorawan_device::{AppEui, AppKey, DevEui}; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||||
|     RNG => rng::InterruptHandler<peripherals::RNG>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p = embassy_nrf::init(Default::default()); | ||||
|     let mut spi_config = spim::Config::default(); | ||||
|     spi_config.frequency = spim::Frequency::M16; | ||||
|  | ||||
|     let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||||
|     let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||||
|     let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||||
|     let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||||
|  | ||||
|     let iv = | ||||
|         GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||||
|  | ||||
|     let lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), true, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let radio = LoRaRadio::new(lora); | ||||
|     let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||||
|     let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); | ||||
|  | ||||
|     defmt::info!("Joining LoRaWAN network"); | ||||
|  | ||||
|     // TODO: Adjust the EUI and Keys according to your network credentials | ||||
|     match device | ||||
|         .join(&JoinMode::OTAA { | ||||
|             deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|             appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|             appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|         }) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => defmt::info!("LoRaWAN network joined"), | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| @@ -1,119 +0,0 @@ | ||||
| //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||||
| //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||||
| //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||||
| use embassy_nrf::{bind_interrupts, peripherals, spim}; | ||||
| use embassy_time::{Delay, Timer}; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p = embassy_nrf::init(Default::default()); | ||||
|     let mut spi_config = spim::Config::default(); | ||||
|     spi_config.frequency = spim::Frequency::M16; | ||||
|  | ||||
|     let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||||
|     let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||||
|     let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||||
|     let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||||
|  | ||||
|     let iv = | ||||
|         GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||||
|     let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||||
|  | ||||
|     start_indicator.set_high(); | ||||
|     Timer::after_secs(5).await; | ||||
|     start_indicator.set_low(); | ||||
|  | ||||
|     let mut receiving_buffer = [00u8; 100]; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let rx_pkt_params = { | ||||
|         match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora | ||||
|         .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     loop { | ||||
|         receiving_buffer = [00u8; 100]; | ||||
|         match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||||
|             Ok((received_len, _rx_pkt_status)) => { | ||||
|                 if (received_len == 3) | ||||
|                     && (receiving_buffer[0] == 0x01u8) | ||||
|                     && (receiving_buffer[1] == 0x02u8) | ||||
|                     && (receiving_buffer[2] == 0x03u8) | ||||
|                 { | ||||
|                     info!("rx successful"); | ||||
|                     debug_indicator.set_high(); | ||||
|                     Timer::after_secs(5).await; | ||||
|                     debug_indicator.set_low(); | ||||
|                 } else { | ||||
|                     info!("rx unknown packet"); | ||||
|                 } | ||||
|             } | ||||
|             Err(err) => info!("rx unsuccessful = {}", err), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,127 +0,0 @@ | ||||
| //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||||
| //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||||
| //! It demonstrates LoRa Rx duty cycle functionality in conjunction with the lora_p2p_send example. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||||
| use embassy_nrf::{bind_interrupts, peripherals, spim}; | ||||
| use embassy_time::{Delay, Timer}; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p = embassy_nrf::init(Default::default()); | ||||
|     let mut spi_config = spim::Config::default(); | ||||
|     spi_config.frequency = spim::Frequency::M16; | ||||
|  | ||||
|     let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||||
|     let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||||
|     let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||||
|     let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||||
|  | ||||
|     let iv = | ||||
|         GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||||
|     let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||||
|  | ||||
|     start_indicator.set_high(); | ||||
|     Timer::after_secs(5).await; | ||||
|     start_indicator.set_low(); | ||||
|  | ||||
|     let mut receiving_buffer = [00u8; 100]; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let rx_pkt_params = { | ||||
|         match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     // See "RM0453 Reference manual STM32WL5x advanced Arm®-based 32-bit MCUs with sub-GHz radio solution" for the best explanation of Rx duty cycle processing. | ||||
|     match lora | ||||
|         .prepare_for_rx( | ||||
|             &mdltn_params, | ||||
|             &rx_pkt_params, | ||||
|             None, | ||||
|             Some(&DutyCycleParams { | ||||
|                 rx_time: 300_000,    // 300_000 units * 15.625 us/unit = 4.69 s | ||||
|                 sleep_time: 200_000, // 200_000 units * 15.625 us/unit = 3.13 s | ||||
|             }), | ||||
|             false, | ||||
|         ) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     receiving_buffer = [00u8; 100]; | ||||
|     match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||||
|         Ok((received_len, _rx_pkt_status)) => { | ||||
|             if (received_len == 3) | ||||
|                 && (receiving_buffer[0] == 0x01u8) | ||||
|                 && (receiving_buffer[1] == 0x02u8) | ||||
|                 && (receiving_buffer[2] == 0x03u8) | ||||
|             { | ||||
|                 info!("rx successful"); | ||||
|                 debug_indicator.set_high(); | ||||
|                 Timer::after_secs(5).await; | ||||
|                 debug_indicator.set_low(); | ||||
|             } else { | ||||
|                 info!("rx unknown packet") | ||||
|             } | ||||
|         } | ||||
|         Err(err) => info!("rx unsuccessful = {}", err), | ||||
|     } | ||||
| } | ||||
| @@ -1,102 +0,0 @@ | ||||
| //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||||
| //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||||
| //! It demonstrates LORA P2P send functionality. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||||
| use embassy_nrf::{bind_interrupts, peripherals, spim}; | ||||
| use embassy_time::Delay; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p = embassy_nrf::init(Default::default()); | ||||
|     let mut spi_config = spim::Config::default(); | ||||
|     spi_config.frequency = spim::Frequency::M16; | ||||
|  | ||||
|     let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||||
|     let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||||
|     let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||||
|     let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||||
|     let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||||
|  | ||||
|     let iv = | ||||
|         GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut tx_pkt_params = { | ||||
|         match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||||
|     match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||||
|         Ok(()) => { | ||||
|             info!("TX DONE"); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.sleep(false).await { | ||||
|         Ok(()) => info!("Sleep successful"), | ||||
|         Err(err) => info!("Sleep unsuccessful = {}", err), | ||||
|     } | ||||
| } | ||||
| @@ -37,7 +37,7 @@ embassy-net = { version = "0.2.0", path = "../../embassy-net", features = [ | ||||
| embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ | ||||
|     "defmt", | ||||
| ] } | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
|  | ||||
| defmt = "0.3" | ||||
| defmt-rtt = "0.4" | ||||
|   | ||||
| @@ -16,10 +16,6 @@ embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defm | ||||
| embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | ||||
| embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||||
| embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } | ||||
| embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } | ||||
| lora-phy = { version = "2" } | ||||
| lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"] } | ||||
| lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"] } | ||||
| cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } | ||||
| cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } | ||||
|  | ||||
| @@ -42,10 +38,10 @@ smart-leds = "0.3.0" | ||||
| heapless = "0.8" | ||||
| usbd-hid = "0.6.1" | ||||
|  | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||
| embedded-hal-async = "1.0.0-rc.1" | ||||
| embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } | ||||
| embedded-io-async = { version = "0.6.0", features = ["defmt-03"] } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" } | ||||
| embedded-hal-async = "1.0.0-rc.2" | ||||
| embedded-hal-bus = { version = "0.1.0-rc.2", features = ["async"] } | ||||
| embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||||
| embedded-storage = { version = "0.3" } | ||||
| static_cell = { version = "2", features = ["nightly"]} | ||||
| portable-atomic = { version = "1.5", features = ["critical-section"] } | ||||
|   | ||||
| @@ -1,74 +0,0 @@ | ||||
| //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||||
| //! It demonstrates LoRaWAN join functionality. | ||||
|  | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_lora::LoraTimer; | ||||
| use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||||
| use embassy_rp::spi::{Config, Spi}; | ||||
| use embassy_time::Delay; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use lorawan::default_crypto::DefaultFactory as Crypto; | ||||
| use lorawan_device::async_device::lora_radio::LoRaRadio; | ||||
| use lorawan_device::async_device::{region, Device, JoinMode}; | ||||
| use lorawan_device::{AppEui, AppKey, DevEui}; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p = embassy_rp::init(Default::default()); | ||||
|  | ||||
|     let miso = p.PIN_12; | ||||
|     let mosi = p.PIN_11; | ||||
|     let clk = p.PIN_10; | ||||
|     let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||||
|  | ||||
|     let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||||
|     let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||||
|     let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||||
|     let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||||
|  | ||||
|     let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||||
|  | ||||
|     let lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), true, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let radio = LoRaRadio::new(lora); | ||||
|     let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||||
|     let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), embassy_rp::clocks::RoscRng); | ||||
|  | ||||
|     defmt::info!("Joining LoRaWAN network"); | ||||
|  | ||||
|     // TODO: Adjust the EUI and Keys according to your network credentials | ||||
|     match device | ||||
|         .join(&JoinMode::OTAA { | ||||
|             deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|             appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|             appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|         }) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => defmt::info!("LoRaWAN network joined"), | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| @@ -1,108 +0,0 @@ | ||||
| //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||||
| //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||||
|  | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||||
| use embassy_rp::spi::{Config, Spi}; | ||||
| use embassy_time::{Delay, Timer}; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p = embassy_rp::init(Default::default()); | ||||
|  | ||||
|     let miso = p.PIN_12; | ||||
|     let mosi = p.PIN_11; | ||||
|     let clk = p.PIN_10; | ||||
|     let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||||
|  | ||||
|     let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||||
|     let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||||
|     let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||||
|     let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||||
|  | ||||
|     let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut debug_indicator = Output::new(p.PIN_25, Level::Low); | ||||
|  | ||||
|     let mut receiving_buffer = [00u8; 100]; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let rx_pkt_params = { | ||||
|         match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora | ||||
|         .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     loop { | ||||
|         receiving_buffer = [00u8; 100]; | ||||
|         match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||||
|             Ok((received_len, _rx_pkt_status)) => { | ||||
|                 if (received_len == 3) | ||||
|                     && (receiving_buffer[0] == 0x01u8) | ||||
|                     && (receiving_buffer[1] == 0x02u8) | ||||
|                     && (receiving_buffer[2] == 0x03u8) | ||||
|                 { | ||||
|                     info!("rx successful"); | ||||
|                     debug_indicator.set_high(); | ||||
|                     Timer::after_secs(5).await; | ||||
|                     debug_indicator.set_low(); | ||||
|                 } else { | ||||
|                     info!("rx unknown packet"); | ||||
|                 } | ||||
|             } | ||||
|             Err(err) => info!("rx unsuccessful = {}", err), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,96 +0,0 @@ | ||||
| //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||||
| //! It demonstrates LORA P2P send functionality. | ||||
|  | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||||
| use embassy_rp::spi::{Config, Spi}; | ||||
| use embassy_time::Delay; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p = embassy_rp::init(Default::default()); | ||||
|  | ||||
|     let miso = p.PIN_12; | ||||
|     let mosi = p.PIN_11; | ||||
|     let clk = p.PIN_10; | ||||
|     let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||||
|  | ||||
|     let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||||
|     let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||||
|     let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||||
|     let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||||
|  | ||||
|     let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut tx_pkt_params = { | ||||
|         match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||||
|     match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||||
|         Ok(()) => { | ||||
|             info!("TX DONE"); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.sleep(false).await { | ||||
|         Ok(()) => info!("Sleep successful"), | ||||
|         Err(err) => info!("Sleep unsuccessful = {}", err), | ||||
|     } | ||||
| } | ||||
| @@ -1,133 +0,0 @@ | ||||
| //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||||
| //! It demonstrates LORA P2P send functionality using the second core, with data provided by the first core. | ||||
|  | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Executor; | ||||
| use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||||
| use embassy_rp::gpio::{AnyPin, Input, Level, Output, Pin, Pull}; | ||||
| use embassy_rp::multicore::{spawn_core1, Stack}; | ||||
| use embassy_rp::peripherals::SPI1; | ||||
| use embassy_rp::spi::{Async, Config, Spi}; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::channel::Channel; | ||||
| use embassy_time::{Delay, Timer}; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use static_cell::StaticCell; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| static mut CORE1_STACK: Stack<4096> = Stack::new(); | ||||
| static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | ||||
| static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | ||||
| static CHANNEL: Channel<CriticalSectionRawMutex, [u8; 3], 1> = Channel::new(); | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| #[cortex_m_rt::entry] | ||||
| fn main() -> ! { | ||||
|     let p = embassy_rp::init(Default::default()); | ||||
|  | ||||
|     let miso = p.PIN_12; | ||||
|     let mosi = p.PIN_11; | ||||
|     let clk = p.PIN_10; | ||||
|     let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||||
|  | ||||
|     let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||||
|     let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||||
|     let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||||
|     let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||||
|  | ||||
|     let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||||
|  | ||||
|     spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | ||||
|         let executor1 = EXECUTOR1.init(Executor::new()); | ||||
|         executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(spi, iv)))); | ||||
|     }); | ||||
|  | ||||
|     let executor0 = EXECUTOR0.init(Executor::new()); | ||||
|     executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); | ||||
| } | ||||
|  | ||||
| #[embassy_executor::task] | ||||
| async fn core0_task() { | ||||
|     info!("Hello from core 0"); | ||||
|     loop { | ||||
|         CHANNEL.send([0x01u8, 0x02u8, 0x03u8]).await; | ||||
|         Timer::after_millis(60 * 1000).await; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[embassy_executor::task] | ||||
| async fn core1_task( | ||||
|     spi: Spi<'static, SPI1, Async>, | ||||
|     iv: GenericSx126xInterfaceVariant<Output<'static, AnyPin>, Input<'static, AnyPin>>, | ||||
| ) { | ||||
|     info!("Hello from core 1"); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut tx_pkt_params = { | ||||
|         match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     loop { | ||||
|         let buffer: [u8; 3] = CHANNEL.receive().await; | ||||
|         match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||||
|             Ok(()) => {} | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||||
|             Ok(()) => { | ||||
|                 info!("TX DONE"); | ||||
|             } | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         match lora.sleep(false).await { | ||||
|             Ok(()) => info!("Sleep successful"), | ||||
|             Err(err) => info!("Sleep unsuccessful = {}", err), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -11,8 +11,8 @@ embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["lo | ||||
| embassy-net = { version = "0.2.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } | ||||
| embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } | ||||
| embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]} | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-adapters = { version = "0.6.0", features = ["futures-03"] } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
| embedded-io-adapters = { version = "0.6.1", features = ["futures-03"] } | ||||
| critical-section = { version = "1.1", features = ["std"] } | ||||
|  | ||||
| async-io = "1.6.0" | ||||
|   | ||||
| @@ -20,7 +20,7 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing | ||||
| cortex-m-rt = "0.7.0" | ||||
| embedded-hal = "0.2.6" | ||||
| embedded-io = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
| panic-probe = { version = "0.3", features = ["print-defmt"] } | ||||
| futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||||
| heapless = { version = "0.8", default-features = false } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::dac::{DacCh1, DacChannel, Value}; | ||||
| use embassy_stm32::dac::{DacCh1, Value}; | ||||
| use embassy_stm32::dma::NoDma; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| @@ -14,11 +14,10 @@ async fn main(_spawner: Spawner) -> ! { | ||||
|     info!("Hello World, dude!"); | ||||
|  | ||||
|     let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4); | ||||
|     unwrap!(dac.set_trigger_enable(false)); | ||||
|  | ||||
|     loop { | ||||
|         for v in 0..=255 { | ||||
|             unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); | ||||
|             dac.set(Value::Bit8(to_sine_wave(v))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F; | ||||
| const WHOAMI: u8 = 0x0F; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
|   | ||||
							
								
								
									
										62
									
								
								examples/stm32f4/src/bin/i2c_async.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								examples/stm32f4/src/bin/i2c_async.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| // Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1 | ||||
| // DMA peripherals changed to compile for stm32f429zi, for the CI. | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::i2c::I2c; | ||||
| use embassy_stm32::time::Hertz; | ||||
| use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const ADDRESS: u8 = 96; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||||
|     I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     info!("Hello world!"); | ||||
|     let p = embassy_stm32::init(Default::default()); | ||||
|  | ||||
|     let mut i2c = I2c::new( | ||||
|         p.I2C1, | ||||
|         p.PB8, | ||||
|         p.PB7, | ||||
|         Irqs, | ||||
|         p.DMA1_CH6, | ||||
|         p.DMA1_CH0, | ||||
|         Hertz(100_000), | ||||
|         Default::default(), | ||||
|     ); | ||||
|  | ||||
|     loop { | ||||
|         let a1454_read_sensor_command = [0x1F]; | ||||
|         let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0]; | ||||
|  | ||||
|         match i2c | ||||
|             .write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer) | ||||
|             .await | ||||
|         { | ||||
|             Ok(()) => { | ||||
|                 // Convert 12-bit signed integer into 16-bit signed integer. | ||||
|                 // Is the 12 bit number negative? | ||||
|                 if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 { | ||||
|                     sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000; | ||||
|                 } | ||||
|  | ||||
|                 let mut sensor_value_raw: u16 = sensor_data_buffer[3].into(); | ||||
|                 sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8; | ||||
|                 let sensor_value: u16 = sensor_value_raw.into(); | ||||
|                 let sensor_value = sensor_value as i16; | ||||
|                 info!("Data: {}", sensor_value); | ||||
|             } | ||||
|             Err(e) => error!("I2C Error during read: {:?}", e), | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										135
									
								
								examples/stm32f4/src/bin/i2c_comparison.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								examples/stm32f4/src/bin/i2c_comparison.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| // Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3 | ||||
| // on the pins referenced in the peripheral definitions. | ||||
| // Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI. | ||||
| // MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x | ||||
| // as long to complete as the blocking ones! | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::i2c::I2c; | ||||
| use embassy_stm32::time::Hertz; | ||||
| use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||||
| use embassy_time::Instant; | ||||
| use futures::future::try_join3; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const ADDRESS: u8 = 96; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||||
|     I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||||
|     I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||||
|     I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>; | ||||
|     I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>; | ||||
| }); | ||||
|  | ||||
| /// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer. | ||||
| fn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 { | ||||
|     let lower = buffer[3]; | ||||
|     let mut upper = buffer[2]; | ||||
|     // Fill in additional 1s if the 12 bit number is negative. | ||||
|     if (upper & 0b00001000) == 0b0001000 { | ||||
|         upper = upper | 0b11110000; | ||||
|     } | ||||
|  | ||||
|     let mut sensor_value_raw: u16 = lower.into(); | ||||
|     sensor_value_raw |= (upper as u16) << 8; | ||||
|     let sensor_value: u16 = sensor_value_raw.into(); | ||||
|     let sensor_value = sensor_value as i16; | ||||
|     sensor_value | ||||
| } | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     info!("Setting up peripherals."); | ||||
|     let p = embassy_stm32::init(Default::default()); | ||||
|  | ||||
|     let mut i2c1 = I2c::new( | ||||
|         p.I2C1, | ||||
|         p.PB8, | ||||
|         p.PB7, | ||||
|         Irqs, | ||||
|         p.DMA1_CH6, | ||||
|         p.DMA1_CH0, | ||||
|         Hertz(100_000), | ||||
|         Default::default(), | ||||
|     ); | ||||
|  | ||||
|     let mut i2c2 = I2c::new( | ||||
|         p.I2C2, | ||||
|         p.PB10, | ||||
|         p.PB11, | ||||
|         Irqs, | ||||
|         p.DMA1_CH7, | ||||
|         p.DMA1_CH3, | ||||
|         Hertz(100_000), | ||||
|         Default::default(), | ||||
|     ); | ||||
|  | ||||
|     let mut i2c3 = I2c::new( | ||||
|         p.I2C3, | ||||
|         p.PA8, | ||||
|         p.PC9, | ||||
|         Irqs, | ||||
|         p.DMA1_CH4, | ||||
|         p.DMA1_CH2, | ||||
|         Hertz(100_000), | ||||
|         Default::default(), | ||||
|     ); | ||||
|  | ||||
|     let a1454_read_sensor_command = [0x1F]; | ||||
|     let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0]; | ||||
|     let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0]; | ||||
|     let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0]; | ||||
|     loop { | ||||
|         // Blocking reads one after the other. Completes in about 2000us. | ||||
|         let blocking_read_start_us = Instant::now().as_micros(); | ||||
|         match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) { | ||||
|             Ok(()) => {} | ||||
|             Err(e) => error!("I2C Error: {:?}", e), | ||||
|         } | ||||
|         match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) { | ||||
|             Ok(()) => {} | ||||
|             Err(e) => error!("I2C Error: {:?}", e), | ||||
|         } | ||||
|         match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) { | ||||
|             Ok(()) => {} | ||||
|             Err(e) => error!("I2C Error: {:?}", e), | ||||
|         } | ||||
|         let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us; | ||||
|         info!( | ||||
|             "Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}", | ||||
|             blocking_read_total_us, | ||||
|             a1454_buf_to_i16(&i2c1_buffer), | ||||
|             a1454_buf_to_i16(&i2c2_buffer), | ||||
|             a1454_buf_to_i16(&i2c3_buffer) | ||||
|         ); | ||||
|  | ||||
|         // Async reads overlapping. Completes in about 1000us. | ||||
|         let async_read_start_us = Instant::now().as_micros(); | ||||
|  | ||||
|         let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer); | ||||
|         let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer); | ||||
|         let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer); | ||||
|  | ||||
|         // Wait for all three transactions to finish, or any one of them to fail. | ||||
|         match try_join3(i2c1_result, i2c2_result, i2c3_result).await { | ||||
|             Ok(_) => { | ||||
|                 let async_read_total_us = Instant::now().as_micros() - async_read_start_us; | ||||
|                 info!( | ||||
|                     "Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}", | ||||
|                     async_read_total_us, | ||||
|                     a1454_buf_to_i16(&i2c1_buffer), | ||||
|                     a1454_buf_to_i16(&i2c2_buffer), | ||||
|                     a1454_buf_to_i16(&i2c3_buffer) | ||||
|                 ); | ||||
|             } | ||||
|             Err(e) => error!("I2C Error during async write-read: {}", e), | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| @@ -11,7 +11,7 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||||
| embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||||
| embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
| embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||||
|  | ||||
| defmt = "0.3" | ||||
|   | ||||
| @@ -11,7 +11,7 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||||
| embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | ||||
| embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
| embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||||
|  | ||||
| defmt = "0.3" | ||||
| @@ -20,9 +20,9 @@ defmt-rtt = "0.4" | ||||
| cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||||
| cortex-m-rt = "0.7.0" | ||||
| embedded-hal = "0.2.6" | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-nal-async = { version = "0.7" } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2" } | ||||
| embedded-nal-async = { version = "0.7.1" } | ||||
| panic-probe = { version = "0.3", features = ["print-defmt"] } | ||||
| futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||||
| heapless = { version = "0.8", default-features = false } | ||||
|   | ||||
| @@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F; | ||||
| const WHOAMI: u8 = 0x0F; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
|   | ||||
| @@ -11,7 +11,7 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||||
| embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | ||||
| embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
| embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||||
|  | ||||
| defmt = "0.3" | ||||
| @@ -20,9 +20,9 @@ defmt-rtt = "0.4" | ||||
| cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||||
| cortex-m-rt = "0.7.0" | ||||
| embedded-hal = "0.2.6" | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-nal-async = { version = "0.7" } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2" } | ||||
| embedded-nal-async = { version = "0.7.1" } | ||||
| panic-probe = { version = "0.3", features = ["print-defmt"] } | ||||
| futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||||
| heapless = { version = "0.8", default-features = false } | ||||
|   | ||||
| @@ -19,7 +19,8 @@ const HEIGHT: usize = 100; | ||||
| static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2]; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C1_EV => i2c::InterruptHandler<peripherals::I2C1>; | ||||
|     I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||||
|     I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||||
|     DCMI => dcmi::InterruptHandler<peripherals::DCMI>; | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|  | ||||
| use cortex_m_rt::entry; | ||||
| use defmt::*; | ||||
| use embassy_stm32::dac::{DacCh1, DacChannel, Value}; | ||||
| use embassy_stm32::dac::{DacCh1, Value}; | ||||
| use embassy_stm32::dma::NoDma; | ||||
| use embassy_stm32::Config; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
| @@ -46,11 +46,10 @@ fn main() -> ! { | ||||
|     let p = embassy_stm32::init(config); | ||||
|  | ||||
|     let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); | ||||
|     unwrap!(dac.set_trigger_enable(false)); | ||||
|  | ||||
|     loop { | ||||
|         for v in 0..=255 { | ||||
|             unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); | ||||
|             dac.set(Value::Bit8(to_sine_wave(v))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,21 +4,15 @@ | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::dac::{DacChannel, ValueArray}; | ||||
| use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; | ||||
| use embassy_stm32::pac::timer::vals::{Mms, Opm}; | ||||
| use embassy_stm32::peripherals::{TIM6, TIM7}; | ||||
| use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; | ||||
| use embassy_stm32::rcc::low_level::RccPeripheral; | ||||
| use embassy_stm32::time::Hertz; | ||||
| use embassy_stm32::timer::low_level::Basic16bitInstance; | ||||
| use micromath::F32Ext; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| pub type Dac1Type = | ||||
|     embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; | ||||
|  | ||||
| pub type Dac2Type = | ||||
|     embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(spawner: Spawner) { | ||||
|     let mut config = embassy_stm32::Config::default(); | ||||
| @@ -63,7 +57,7 @@ async fn main(spawner: Spawner) { | ||||
| } | ||||
|  | ||||
| #[embassy_executor::task] | ||||
| async fn dac_task1(mut dac: Dac1Type) { | ||||
| async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { | ||||
|     let data: &[u8; 256] = &calculate_array::<256>(); | ||||
|  | ||||
|     info!("TIM6 frequency is {}", TIM6::frequency()); | ||||
| @@ -77,8 +71,9 @@ async fn dac_task1(mut dac: Dac1Type) { | ||||
|         error!("Reload value {} below threshold!", reload); | ||||
|     } | ||||
|  | ||||
|     dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); | ||||
|     dac.enable_channel().unwrap(); | ||||
|     dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6); | ||||
|     dac.set_triggering(true); | ||||
|     dac.enable(); | ||||
|  | ||||
|     TIM6::enable_and_reset(); | ||||
|     TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); | ||||
| @@ -100,14 +95,12 @@ async fn dac_task1(mut dac: Dac1Type) { | ||||
|     // Loop technically not necessary if DMA circular mode is enabled | ||||
|     loop { | ||||
|         info!("Loop DAC1"); | ||||
|         if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { | ||||
|             error!("Could not write to dac: {}", e); | ||||
|         } | ||||
|         dac.write(ValueArray::Bit8(data), true).await; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[embassy_executor::task] | ||||
| async fn dac_task2(mut dac: Dac2Type) { | ||||
| async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { | ||||
|     let data: &[u8; 256] = &calculate_array::<256>(); | ||||
|  | ||||
|     info!("TIM7 frequency is {}", TIM7::frequency()); | ||||
| @@ -127,7 +120,9 @@ async fn dac_task2(mut dac: Dac2Type) { | ||||
|         w.set_cen(true); | ||||
|     }); | ||||
|  | ||||
|     dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); | ||||
|     dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7); | ||||
|     dac.set_triggering(true); | ||||
|     dac.enable(); | ||||
|  | ||||
|     debug!( | ||||
|         "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", | ||||
| @@ -138,9 +133,7 @@ async fn dac_task2(mut dac: Dac2Type) { | ||||
|         data.len() | ||||
|     ); | ||||
|  | ||||
|     if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { | ||||
|         error!("Could not write to dac: {}", e); | ||||
|     } | ||||
|     dac.write(ValueArray::Bit8(data), true).await; | ||||
| } | ||||
|  | ||||
| fn to_sine_wave(v: u8) -> u8 { | ||||
|   | ||||
| @@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F; | ||||
| const WHOAMI: u8 = 0x0F; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
|   | ||||
| @@ -6,8 +6,7 @@ license = "MIT OR Apache-2.0" | ||||
|  | ||||
| [features] | ||||
| default = ["nightly"] | ||||
| nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", "embassy-executor/nightly", | ||||
|            "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "dep:embedded-io-async"] | ||||
| nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", "embassy-executor/nightly", "dep:embedded-io-async"] | ||||
|  | ||||
| [dependencies] | ||||
| # Change stm32l072cz to your chip name, if necessary. | ||||
| @@ -15,17 +14,13 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" | ||||
| embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] } | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||||
| embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||||
| embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } | ||||
| lora-phy = { version = "2", optional = true } | ||||
| lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } | ||||
| lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"], optional = true } | ||||
|  | ||||
| defmt = "0.3" | ||||
| defmt-rtt = "0.4" | ||||
|  | ||||
| embedded-storage = "0.3.0" | ||||
| embedded-io = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.0", optional = true } | ||||
| embedded-io-async = { version = "0.6.1", optional = true } | ||||
|  | ||||
| cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||||
| cortex-m-rt = "0.7.0" | ||||
|   | ||||
| @@ -1,97 +0,0 @@ | ||||
| //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. | ||||
| //! It demonstrates LORA P2P CAD functionality. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::Stm32l0InterfaceVariant; | ||||
| use embassy_stm32::exti::{Channel, ExtiInput}; | ||||
| use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | ||||
| use embassy_stm32::spi; | ||||
| use embassy_stm32::time::khz; | ||||
| use embassy_time::{Delay, Timer}; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1276_7_8_9::SX1276_7_8_9; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let mut config = embassy_stm32::Config::default(); | ||||
|     config.rcc.hsi = true; | ||||
|     config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; | ||||
|     let p = embassy_stm32::init(config); | ||||
|  | ||||
|     let mut spi_config = spi::Config::default(); | ||||
|     spi_config.frequency = khz(200); | ||||
|  | ||||
|     // SPI for sx1276 | ||||
|     let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); | ||||
|     let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); | ||||
|  | ||||
|     let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); | ||||
|     let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); | ||||
|  | ||||
|     let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low); | ||||
|     let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low); | ||||
|  | ||||
|     start_indicator.set_high(); | ||||
|     Timer::after_secs(5).await; | ||||
|     start_indicator.set_low(); | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.prepare_for_cad(&mdltn_params, true).await { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.cad().await { | ||||
|         Ok(cad_activity_detected) => { | ||||
|             if cad_activity_detected { | ||||
|                 info!("cad successful with activity detected") | ||||
|             } else { | ||||
|                 info!("cad successful without activity detected") | ||||
|             } | ||||
|             debug_indicator.set_high(); | ||||
|             Timer::after_secs(5).await; | ||||
|             debug_indicator.set_low(); | ||||
|         } | ||||
|         Err(err) => info!("cad unsuccessful = {}", err), | ||||
|     } | ||||
| } | ||||
| @@ -1,85 +0,0 @@ | ||||
| //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. | ||||
| //! It demonstrates LoRaWAN join functionality. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::Stm32l0InterfaceVariant; | ||||
| use embassy_lora::LoraTimer; | ||||
| use embassy_stm32::exti::{Channel, ExtiInput}; | ||||
| use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | ||||
| use embassy_stm32::rng::Rng; | ||||
| use embassy_stm32::time::khz; | ||||
| use embassy_stm32::{bind_interrupts, peripherals, rng, spi}; | ||||
| use embassy_time::Delay; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1276_7_8_9::SX1276_7_8_9; | ||||
| use lora_phy::LoRa; | ||||
| use lorawan::default_crypto::DefaultFactory as Crypto; | ||||
| use lorawan_device::async_device::lora_radio::LoRaRadio; | ||||
| use lorawan_device::async_device::{region, Device, JoinMode}; | ||||
| use lorawan_device::{AppEui, AppKey, DevEui}; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     RNG_LPUART1 => rng::InterruptHandler<peripherals::RNG>; | ||||
| }); | ||||
|  | ||||
| const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let mut config = embassy_stm32::Config::default(); | ||||
|     config.rcc.hsi = true; | ||||
|     config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; | ||||
|     let p = embassy_stm32::init(config); | ||||
|  | ||||
|     let mut spi_config = spi::Config::default(); | ||||
|     spi_config.frequency = khz(200); | ||||
|  | ||||
|     // SPI for sx1276 | ||||
|     let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); | ||||
|     let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); | ||||
|  | ||||
|     let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); | ||||
|     let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); | ||||
|  | ||||
|     let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); | ||||
|  | ||||
|     let lora = { | ||||
|         match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), true, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let radio = LoRaRadio::new(lora); | ||||
|     let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||||
|     let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); | ||||
|  | ||||
|     defmt::info!("Joining LoRaWAN network"); | ||||
|  | ||||
|     // TODO: Adjust the EUI and Keys according to your network credentials | ||||
|     match device | ||||
|         .join(&JoinMode::OTAA { | ||||
|             deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|             appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|             appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|         }) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => defmt::info!("LoRaWAN network joined"), | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| @@ -1,119 +0,0 @@ | ||||
| //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. | ||||
| //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::Stm32l0InterfaceVariant; | ||||
| use embassy_stm32::exti::{Channel, ExtiInput}; | ||||
| use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | ||||
| use embassy_stm32::spi; | ||||
| use embassy_stm32::time::khz; | ||||
| use embassy_time::{Delay, Timer}; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1276_7_8_9::SX1276_7_8_9; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let mut config = embassy_stm32::Config::default(); | ||||
|     config.rcc.hsi = true; | ||||
|     config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; | ||||
|     let p = embassy_stm32::init(config); | ||||
|  | ||||
|     let mut spi_config = spi::Config::default(); | ||||
|     spi_config.frequency = khz(200); | ||||
|  | ||||
|     // SPI for sx1276 | ||||
|     let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); | ||||
|     let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); | ||||
|  | ||||
|     let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); | ||||
|     let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); | ||||
|  | ||||
|     let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low); | ||||
|     let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low); | ||||
|  | ||||
|     start_indicator.set_high(); | ||||
|     Timer::after_secs(5).await; | ||||
|     start_indicator.set_low(); | ||||
|  | ||||
|     let mut receiving_buffer = [00u8; 100]; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let rx_pkt_params = { | ||||
|         match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora | ||||
|         .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     loop { | ||||
|         receiving_buffer = [00u8; 100]; | ||||
|         match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||||
|             Ok((received_len, _rx_pkt_status)) => { | ||||
|                 if (received_len == 3) | ||||
|                     && (receiving_buffer[0] == 0x01u8) | ||||
|                     && (receiving_buffer[1] == 0x02u8) | ||||
|                     && (receiving_buffer[2] == 0x03u8) | ||||
|                 { | ||||
|                     info!("rx successful"); | ||||
|                     debug_indicator.set_high(); | ||||
|                     Timer::after_secs(5).await; | ||||
|                     debug_indicator.set_low(); | ||||
|                 } else { | ||||
|                     info!("rx unknown packet"); | ||||
|                 } | ||||
|             } | ||||
|             Err(err) => info!("rx unsuccessful = {}", err), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,102 +0,0 @@ | ||||
| //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. | ||||
| //! It demonstrates LORA P2P send functionality. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::Stm32l0InterfaceVariant; | ||||
| use embassy_stm32::exti::{Channel, ExtiInput}; | ||||
| use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; | ||||
| use embassy_stm32::spi; | ||||
| use embassy_stm32::time::khz; | ||||
| use embassy_time::Delay; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1276_7_8_9::SX1276_7_8_9; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let mut config = embassy_stm32::Config::default(); | ||||
|     config.rcc.hsi = true; | ||||
|     config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; | ||||
|     let p = embassy_stm32::init(config); | ||||
|  | ||||
|     let mut spi_config = spi::Config::default(); | ||||
|     spi_config.frequency = khz(200); | ||||
|  | ||||
|     // SPI for sx1276 | ||||
|     let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); | ||||
|  | ||||
|     let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); | ||||
|     let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); | ||||
|  | ||||
|     let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); | ||||
|     let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); | ||||
|  | ||||
|     let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut tx_pkt_params = { | ||||
|         match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.prepare_for_tx(&mdltn_params, 17, true).await { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||||
|     match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||||
|         Ok(()) => { | ||||
|             info!("TX DONE"); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.sleep(false).await { | ||||
|         Ok(()) => info!("Sleep successful"), | ||||
|         Err(err) => info!("Sleep unsuccessful = {}", err), | ||||
|     } | ||||
| } | ||||
| @@ -15,7 +15,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm | ||||
| embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } | ||||
| embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "udp", "tcp", "dhcpv4", "medium-ethernet"] } | ||||
| embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||||
| embedded-io-async = { version = "0.6.0", features = ["defmt-03"] } | ||||
| embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||||
| embedded-io = { version = "0.6.0", features = ["defmt-03"] } | ||||
|  | ||||
| defmt = "0.3" | ||||
| @@ -24,9 +24,9 @@ defmt-rtt = "0.4" | ||||
| cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||||
| cortex-m-rt = "0.7.0" | ||||
| embedded-hal = "0.2.6" | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2" } | ||||
| embedded-hal-bus = { version = "=0.1.0-rc.2", features = ["async"] } | ||||
| panic-probe = { version = "0.3", features = ["print-defmt"] } | ||||
| futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||||
| heapless = { version = "0.8", default-features = false } | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_stm32::dac::{DacCh1, DacChannel, Value}; | ||||
| use embassy_stm32::dac::{DacCh1, Value}; | ||||
| use embassy_stm32::dma::NoDma; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| @@ -13,11 +13,10 @@ fn main() -> ! { | ||||
|     info!("Hello World!"); | ||||
|  | ||||
|     let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); | ||||
|     unwrap!(dac.set_trigger_enable(false)); | ||||
|  | ||||
|     loop { | ||||
|         for v in 0..=255 { | ||||
|             unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); | ||||
|             dac.set(Value::Bit8(to_sine_wave(v))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,21 +4,15 @@ | ||||
|  | ||||
| use defmt::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::dac::{DacChannel, ValueArray}; | ||||
| use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; | ||||
| use embassy_stm32::pac::timer::vals::{Mms, Opm}; | ||||
| use embassy_stm32::peripherals::{TIM6, TIM7}; | ||||
| use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; | ||||
| use embassy_stm32::rcc::low_level::RccPeripheral; | ||||
| use embassy_stm32::time::Hertz; | ||||
| use embassy_stm32::timer::low_level::Basic16bitInstance; | ||||
| use micromath::F32Ext; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| pub type Dac1Type = | ||||
|     embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; | ||||
|  | ||||
| pub type Dac2Type = | ||||
|     embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(spawner: Spawner) { | ||||
|     let config = embassy_stm32::Config::default(); | ||||
| @@ -34,7 +28,7 @@ async fn main(spawner: Spawner) { | ||||
| } | ||||
|  | ||||
| #[embassy_executor::task] | ||||
| async fn dac_task1(mut dac: Dac1Type) { | ||||
| async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { | ||||
|     let data: &[u8; 256] = &calculate_array::<256>(); | ||||
|  | ||||
|     info!("TIM6 frequency is {}", TIM6::frequency()); | ||||
| @@ -48,8 +42,9 @@ async fn dac_task1(mut dac: Dac1Type) { | ||||
|         error!("Reload value {} below threshold!", reload); | ||||
|     } | ||||
|  | ||||
|     dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); | ||||
|     dac.enable_channel().unwrap(); | ||||
|     dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6); | ||||
|     dac.set_triggering(true); | ||||
|     dac.enable(); | ||||
|  | ||||
|     TIM6::enable_and_reset(); | ||||
|     TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); | ||||
| @@ -71,14 +66,12 @@ async fn dac_task1(mut dac: Dac1Type) { | ||||
|     // Loop technically not necessary if DMA circular mode is enabled | ||||
|     loop { | ||||
|         info!("Loop DAC1"); | ||||
|         if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { | ||||
|             error!("Could not write to dac: {}", e); | ||||
|         } | ||||
|         dac.write(ValueArray::Bit8(data), true).await; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[embassy_executor::task] | ||||
| async fn dac_task2(mut dac: Dac2Type) { | ||||
| async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { | ||||
|     let data: &[u8; 256] = &calculate_array::<256>(); | ||||
|  | ||||
|     info!("TIM7 frequency is {}", TIM7::frequency()); | ||||
| @@ -98,7 +91,9 @@ async fn dac_task2(mut dac: Dac2Type) { | ||||
|         w.set_cen(true); | ||||
|     }); | ||||
|  | ||||
|     dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); | ||||
|     dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7); | ||||
|     dac.set_triggering(true); | ||||
|     dac.enable(); | ||||
|  | ||||
|     debug!( | ||||
|         "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", | ||||
| @@ -109,9 +104,7 @@ async fn dac_task2(mut dac: Dac2Type) { | ||||
|         data.len() | ||||
|     ); | ||||
|  | ||||
|     if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { | ||||
|         error!("Could not write to dac: {}", e); | ||||
|     } | ||||
|     dac.write(ValueArray::Bit8(data), true).await; | ||||
| } | ||||
|  | ||||
| fn to_sine_wave(v: u8) -> u8 { | ||||
|   | ||||
| @@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F; | ||||
| const WHOAMI: u8 = 0x0F; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
|   | ||||
| @@ -16,7 +16,8 @@ const ADDRESS: u8 = 0x5F; | ||||
| const WHOAMI: u8 = 0x0F; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
|   | ||||
| @@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F; | ||||
| const WHOAMI: u8 = 0x0F; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||||
|     I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
|   | ||||
| @@ -40,7 +40,8 @@ use static_cell::make_static; | ||||
| use {embassy_stm32 as hal, panic_probe as _}; | ||||
|  | ||||
| bind_interrupts!(struct Irqs { | ||||
|     I2C3_EV => i2c::InterruptHandler<peripherals::I2C3>; | ||||
|     I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>; | ||||
|     I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>; | ||||
|     RNG => rng::InterruptHandler<peripherals::RNG>; | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -25,7 +25,7 @@ embedded-hal = "0.2.6" | ||||
| futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||||
| heapless = { version = "0.8", default-features = false } | ||||
| rand_core = { version = "0.6.3", default-features = false } | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
| static_cell = { version = "2", features = ["nightly"]} | ||||
|  | ||||
| [profile.release] | ||||
|   | ||||
| @@ -11,10 +11,6 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||||
| embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||||
| embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } | ||||
| embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } | ||||
| lora-phy = { version = "2" } | ||||
| lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"] } | ||||
| lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"] } | ||||
|  | ||||
| defmt = "0.3" | ||||
| defmt-rtt = "0.4" | ||||
|   | ||||
| @@ -1,95 +0,0 @@ | ||||
| //! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio. | ||||
| //! It demonstrates LoRaWAN join functionality. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait, async_fn_in_trait)] | ||||
| #![allow(stable_features, unknown_lints, async_fn_in_trait)] | ||||
|  | ||||
| use defmt::info; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; | ||||
| use embassy_lora::LoraTimer; | ||||
| use embassy_stm32::gpio::{Level, Output, Pin, Speed}; | ||||
| use embassy_stm32::rng::{self, Rng}; | ||||
| use embassy_stm32::spi::Spi; | ||||
| use embassy_stm32::time::Hertz; | ||||
| use embassy_stm32::{bind_interrupts, peripherals}; | ||||
| use embassy_time::Delay; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use lorawan::default_crypto::DefaultFactory as Crypto; | ||||
| use lorawan_device::async_device::lora_radio::LoRaRadio; | ||||
| use lorawan_device::async_device::{region, Device, JoinMode}; | ||||
| use lorawan_device::{AppEui, AppKey, DevEui}; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||||
|  | ||||
| bind_interrupts!(struct Irqs{ | ||||
|     SUBGHZ_RADIO => InterruptHandler; | ||||
|     RNG => rng::InterruptHandler<peripherals::RNG>; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let mut config = embassy_stm32::Config::default(); | ||||
|     { | ||||
|         use embassy_stm32::rcc::*; | ||||
|         config.rcc.hse = Some(Hse { | ||||
|             freq: Hertz(32_000_000), | ||||
|             mode: HseMode::Bypass, | ||||
|             prescaler: HsePrescaler::DIV1, | ||||
|         }); | ||||
|         config.rcc.mux = ClockSrc::PLL1_R; | ||||
|         config.rcc.pll = Some(Pll { | ||||
|             source: PllSource::HSE, | ||||
|             prediv: PllPreDiv::DIV2, | ||||
|             mul: PllMul::MUL6, | ||||
|             divp: None, | ||||
|             divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG | ||||
|             divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2) | ||||
|         }); | ||||
|     } | ||||
|     let p = embassy_stm32::init(config); | ||||
|  | ||||
|     let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); | ||||
|  | ||||
|     // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx | ||||
|     let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); | ||||
|     let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | ||||
|     let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | ||||
|     let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); | ||||
|  | ||||
|     let lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), true, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     let radio = LoRaRadio::new(lora); | ||||
|     let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||||
|     let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); | ||||
|  | ||||
|     defmt::info!("Joining LoRaWAN network"); | ||||
|  | ||||
|     // TODO: Adjust the EUI and Keys according to your network credentials | ||||
|     match device | ||||
|         .join(&JoinMode::OTAA { | ||||
|             deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|             appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|             appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), | ||||
|         }) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => defmt::info!("LoRaWAN network joined"), | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| @@ -1,133 +0,0 @@ | ||||
| //! This example runs on the STM32WL board, which has a builtin Semtech Sx1262 radio. | ||||
| //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait, async_fn_in_trait)] | ||||
| #![allow(stable_features, unknown_lints, async_fn_in_trait)] | ||||
|  | ||||
| use defmt::info; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; | ||||
| use embassy_stm32::bind_interrupts; | ||||
| use embassy_stm32::gpio::{Level, Output, Pin, Speed}; | ||||
| use embassy_stm32::spi::Spi; | ||||
| use embassy_stm32::time::Hertz; | ||||
| use embassy_time::{Delay, Timer}; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| bind_interrupts!(struct Irqs{ | ||||
|     SUBGHZ_RADIO => InterruptHandler; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let mut config = embassy_stm32::Config::default(); | ||||
|     { | ||||
|         use embassy_stm32::rcc::*; | ||||
|         config.rcc.hse = Some(Hse { | ||||
|             freq: Hertz(32_000_000), | ||||
|             mode: HseMode::Bypass, | ||||
|             prescaler: HsePrescaler::DIV1, | ||||
|         }); | ||||
|         config.rcc.mux = ClockSrc::PLL1_R; | ||||
|         config.rcc.pll = Some(Pll { | ||||
|             source: PllSource::HSE, | ||||
|             prediv: PllPreDiv::DIV2, | ||||
|             mul: PllMul::MUL6, | ||||
|             divp: None, | ||||
|             divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG | ||||
|             divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2) | ||||
|         }); | ||||
|     } | ||||
|     let p = embassy_stm32::init(config); | ||||
|  | ||||
|     let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); | ||||
|  | ||||
|     // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx | ||||
|     let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); | ||||
|     let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | ||||
|     let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | ||||
|     let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut debug_indicator = Output::new(p.PB9, Level::Low, Speed::Low); | ||||
|     let mut start_indicator = Output::new(p.PB15, Level::Low, Speed::Low); | ||||
|  | ||||
|     start_indicator.set_high(); | ||||
|     Timer::after_secs(5).await; | ||||
|     start_indicator.set_low(); | ||||
|  | ||||
|     let mut receiving_buffer = [00u8; 100]; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let rx_pkt_params = { | ||||
|         match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora | ||||
|         .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false) | ||||
|         .await | ||||
|     { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     loop { | ||||
|         receiving_buffer = [00u8; 100]; | ||||
|         match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||||
|             Ok((received_len, _rx_pkt_status)) => { | ||||
|                 if (received_len == 3) | ||||
|                     && (receiving_buffer[0] == 0x01u8) | ||||
|                     && (receiving_buffer[1] == 0x02u8) | ||||
|                     && (receiving_buffer[2] == 0x03u8) | ||||
|                 { | ||||
|                     info!("rx successful"); | ||||
|                     debug_indicator.set_high(); | ||||
|                     Timer::after_secs(5).await; | ||||
|                     debug_indicator.set_low(); | ||||
|                 } else { | ||||
|                     info!("rx unknown packet"); | ||||
|                 } | ||||
|             } | ||||
|             Err(err) => info!("rx unsuccessful = {}", err), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,116 +0,0 @@ | ||||
| //! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio. | ||||
| //! It demonstrates LORA P2P send functionality. | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![macro_use] | ||||
| #![feature(type_alias_impl_trait, async_fn_in_trait)] | ||||
| #![allow(stable_features, unknown_lints, async_fn_in_trait)] | ||||
|  | ||||
| use defmt::info; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; | ||||
| use embassy_stm32::bind_interrupts; | ||||
| use embassy_stm32::gpio::{Level, Output, Pin, Speed}; | ||||
| use embassy_stm32::spi::Spi; | ||||
| use embassy_stm32::time::Hertz; | ||||
| use embassy_time::Delay; | ||||
| use lora_phy::mod_params::*; | ||||
| use lora_phy::sx1261_2::SX1261_2; | ||||
| use lora_phy::LoRa; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
|  | ||||
| const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||||
|  | ||||
| bind_interrupts!(struct Irqs{ | ||||
|     SUBGHZ_RADIO => InterruptHandler; | ||||
| }); | ||||
|  | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let mut config = embassy_stm32::Config::default(); | ||||
|     { | ||||
|         use embassy_stm32::rcc::*; | ||||
|         config.rcc.hse = Some(Hse { | ||||
|             freq: Hertz(32_000_000), | ||||
|             mode: HseMode::Bypass, | ||||
|             prescaler: HsePrescaler::DIV1, | ||||
|         }); | ||||
|         config.rcc.mux = ClockSrc::PLL1_R; | ||||
|         config.rcc.pll = Some(Pll { | ||||
|             source: PllSource::HSE, | ||||
|             prediv: PllPreDiv::DIV2, | ||||
|             mul: PllMul::MUL6, | ||||
|             divp: None, | ||||
|             divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG | ||||
|             divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2) | ||||
|         }); | ||||
|     } | ||||
|     let p = embassy_stm32::init(config); | ||||
|  | ||||
|     let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); | ||||
|  | ||||
|     // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx | ||||
|     let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); | ||||
|     let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | ||||
|     let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | ||||
|     let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); | ||||
|  | ||||
|     let mut lora = { | ||||
|         match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, Delay).await { | ||||
|             Ok(l) => l, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mdltn_params = { | ||||
|         match lora.create_modulation_params( | ||||
|             SpreadingFactor::_10, | ||||
|             Bandwidth::_250KHz, | ||||
|             CodingRate::_4_8, | ||||
|             LORA_FREQUENCY_IN_HZ, | ||||
|         ) { | ||||
|             Ok(mp) => mp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut tx_pkt_params = { | ||||
|         match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||||
|             Ok(pp) => pp, | ||||
|             Err(err) => { | ||||
|                 info!("Radio error = {}", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||||
|     match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||||
|         Ok(()) => { | ||||
|             info!("TX DONE"); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             info!("Radio error = {}", err); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     match lora.sleep(false).await { | ||||
|         Ok(()) => info!("Sleep successful"), | ||||
|         Err(err) => info!("Sleep unsuccessful = {}", err), | ||||
|     } | ||||
| } | ||||
| @@ -9,15 +9,15 @@ teleprobe-meta = "1" | ||||
|  | ||||
| embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||||
| embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } | ||||
| embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } | ||||
| embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | ||||
| embedded-io-async = { version = "0.6.0", features = ["defmt-03"] } | ||||
| embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||||
| embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } | ||||
| embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } | ||||
| embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] } | ||||
| embedded-hal-async = { version = "1.0.0-rc.1" } | ||||
| embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } | ||||
| embedded-hal-async = { version = "1.0.0-rc.2" } | ||||
| embedded-hal-bus = { version = "0.1.0-rc.2", features = ["async"] } | ||||
| static_cell = { version = "2", features = [ "nightly" ] } | ||||
| perf-client = { path = "../perf-client" } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"nrf52840-dk"); | ||||
|  | ||||
| use defmt::{assert_eq, *}; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"nrf52840-dk"); | ||||
|  | ||||
| use defmt::{assert_eq, *}; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"nrf52840-dk"); | ||||
|  | ||||
| use core::mem; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"nrf52840-dk"); | ||||
|  | ||||
| use defmt::{assert, info}; | ||||
|   | ||||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | ||||
| [dependencies] | ||||
| critical-section = { version = "1.1.1", features = ["restore-state-bool"] } | ||||
| embassy-sync = { version = "0.4.0", path = "../../embassy-sync" } | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] } | ||||
| embassy-time = { version = "0.1.5", path = "../../embassy-time" } | ||||
| embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
|  | ||||
| use embassy_executor::Spawner; | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" | ||||
| teleprobe-meta = "1.1" | ||||
|  | ||||
| embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] } | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||||
| embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||||
| embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] } | ||||
| embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"]  } | ||||
| embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||||
| @@ -24,12 +24,12 @@ defmt-rtt = "0.4" | ||||
| cortex-m = { version = "0.7.6" } | ||||
| cortex-m-rt = "0.7.0" | ||||
| embedded-hal = "0.2.6" | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.1" } | ||||
| embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] } | ||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" } | ||||
| embedded-hal-async = { version = "=1.0.0-rc.2" } | ||||
| embedded-hal-bus = { version = "=0.1.0-rc.2", features = ["async"] } | ||||
| panic-probe = { version = "0.3.0", features = ["print-defmt"] } | ||||
| futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||||
| embedded-io-async = { version = "0.6.0" } | ||||
| embedded-io-async = { version = "0.6.1" } | ||||
| embedded-storage = { version = "0.3" } | ||||
| static_cell = { version = "2", features = ["nightly"]} | ||||
| portable-atomic = { version = "1.5", features = ["critical-section"] } | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"rpi-pico"); | ||||
|  | ||||
| use defmt::*; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"rpi-pico"); | ||||
|  | ||||
| use defmt::{assert_eq, *}; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"rpi-pico"); | ||||
|  | ||||
| use defmt::{assert_eq, *}; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"rpi-pico"); | ||||
|  | ||||
| use defmt::*; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"rpi-pico"); | ||||
|  | ||||
| use defmt::*; | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| teleprobe_meta::target!(b"rpi-pico"); | ||||
|  | ||||
| use defmt::{assert, *}; | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user