Compare commits

...

41 Commits

Author SHA1 Message Date
4634316749 Update embedded-(hal,io,nal). 2023-11-29 17:01:36 +01:00
1b9925e3da Move embassy-lora, lora examples to lora-phy repo. 2023-11-29 16:26:31 +01:00
b4bc9ac028 Merge pull request #2225 from bugadani/faq
(FAQ): How to fix issues with multiple crate versions
2023-11-28 11:41:49 +00:00
a3c99f0324 Add multiple dependency error faq 2023-11-28 08:12:23 +01:00
76f3a11f43 Merge pull request #2229 from jamesmunns/patch-3
(FAQ): Add time driver note
2023-11-28 06:31:35 +00:00
d63c052f0e Merge pull request #2228 from MaxiluxSystems/fix/shared-spi-no-time
embassy-embedded-hal: shared_bus/blocking/spi: fix build and behaviour when "time" feature disabled
2023-11-27 23:15:43 +00:00
2c9f4bce01 shared_bus/blocking/spi: fix build and behaviour when "time" disabled
new behaviour: check for DelayUs presence in operations instead of
shortcircuiting
2023-11-27 23:05:19 +00:00
74a6855f01 Update docs/modules/ROOT/pages/faq.adoc 2023-11-27 21:43:05 +01:00
f88aa9ce48 (FAQ): Add time driver note 2023-11-27 21:42:10 +01:00
3a8a950bb8 Merge pull request #2226 from AdinAck/faq-update-1
FAQ Update (1)
2023-11-27 18:59:53 +00:00
02305451b1 need be -> needed
Co-authored-by: Dániel Buga <bugadani@gmail.com>
2023-11-27 10:51:20 -08:00
a42aef7759 it's -> its
Co-authored-by: Dániel Buga <bugadani@gmail.com>
2023-11-27 10:51:05 -08:00
142f42fe90 ... 2023-11-27 10:44:47 -08:00
5844f5ce2d Update faq.adoc 2023-11-27 10:23:42 -08:00
e5fdd35bd6 Merge pull request #2223 from embassy-rs/use-executor-arena
tests: use executor task arena instead of TAIT.
2023-11-27 01:27:13 +00:00
0bd47c779b tests: use executor task arena instead of TAIT. 2023-11-27 01:23:49 +01:00
4481631326 Merge pull request #2222 from embassy-rs/f446-hil
f446 hil
2023-11-26 23:42:03 +00:00
cf13f70ea9 stm32/test: add stm32f446 (board not in HIL rig yet) 2023-11-27 00:38:57 +01:00
6bdacb4f69 stm32/sdmmc: use unwrap to ensure panics get printed to defmt. 2023-11-27 00:35:41 +01:00
ff3baf1e90 Merge pull request #2220 from jamesmunns/patch-2
Minor readme typo
2023-11-25 22:13:34 +00:00
d5dcbadbbe Minor readme typo 2023-11-25 22:31:03 +01:00
0ad0d27150 Merge pull request #2219 from embassy-rs/no-concat-bytes
cyw43: remove feature(concat_bytes).
2023-11-25 00:09:02 +00:00
03d500f548 cyw43: remove feature(concat_bytes). 2023-11-25 01:06:43 +01:00
8b46343b34 Merge pull request #2199 from adamgreig/stm32-dac
STM32 DAC: update to new PAC, combine ch1/ch2, fix trigger selection
2023-11-24 23:51:07 +00:00
a3ea01473a stm32: fix dac trait 2023-11-25 00:30:30 +01:00
cf84c8bfd1 WIP: use generated metapac from corresponding PR for CI 2023-11-25 00:30:29 +01:00
09d7950313 STM32 DAC: Rework DAC driver, support all families. 2023-11-25 00:29:45 +01:00
267cbaebe6 STM32 DAC: Disable circular writes with GPDMA as it doesn't yet support circular transfers 2023-11-25 00:29:45 +01:00
31fc337e2f STM32 DAC: Swap to new TSEL enum entirely in-HAL 2023-11-25 00:29:45 +01:00
135f350020 STM32 DAC: Use new Mode enum for setting channel mode 2023-11-25 00:29:45 +01:00
897663e023 STM32: Add cfg to differentiate L4 and L4+ families 2023-11-25 00:29:45 +01:00
2218d30c80 STM32: Remove vestigal build.rs cfgs, add new flashsize_X and package_X cfgs, use in F3 RCC 2023-11-25 00:29:45 +01:00
f5c9e3baa6 Merge pull request #2200 from barnabywalters/asynci2cv1
Implemented async I2C for v1
2023-11-24 23:08:47 +00:00
47bac9df70 Merge pull request #2216 from embassy-rs/stable
executor: add support for main/task macros in stable Rust
2023-11-24 22:57:46 +00:00
3efc3eee57 stm32/i2c: implement async i2c v1. 2023-11-24 23:55:46 +01:00
bc65b8f7ec stm32/i2c: add async, dual interrupt scaffolding. 2023-11-24 23:55:45 +01:00
78f8e6112a Merge pull request #2217 from embassy-rs/no-try
embassy-embedded-hal: don't use feature(try_blocks).
2023-11-24 22:55:02 +00:00
996c3c1f7e executor: make task arena size configurable. 2023-11-24 23:52:09 +01:00
171cdb94c7 executor: add support for main/task macros in stable (allocates tasks in an arena) 2023-11-24 23:52:09 +01:00
1fbc150fd6 executor: add some tests. 2023-11-24 23:52:09 +01:00
259cf6192b executor: Remove non-functional rtos-trace-interrupt. 2023-11-24 23:52:09 +01:00
144 changed files with 2527 additions and 3453 deletions

3
.github/ci/test.sh vendored
View File

@ -15,6 +15,9 @@ export CARGO_NET_GIT_FETCH_WITH_CLI=true
hashtime restore /ci/cache/filetime.json || true
hashtime save /ci/cache/filetime.json
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly
cargo test --manifest-path ./embassy-sync/Cargo.toml
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml

3
ci.sh
View File

@ -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

View File

@ -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]

View File

@ -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)]

View File

@ -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";

View File

@ -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]

View File

@ -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!

View 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
]
----

View File

@ -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"

View File

@ -63,6 +63,10 @@ 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)?;
@ -74,12 +78,12 @@ where
Operation::Transfer(read, write) => bus.transfer(read, write).await,
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,
#[cfg(not(feature = "time"))]
Operation::DelayUs(us) => return Err(SpiDeviceError::DelayUsNotSupported),
Operation::DelayNs(_) => unreachable!(),
#[cfg(feature = "time")]
Operation::DelayUs(us) => match bus.flush().await {
Operation::DelayNs(ns) => match bus.flush().await {
Err(e) => Err(e),
Ok(()) => {
embassy_time::Timer::after_micros(*us as _).await;
embassy_time::Timer::after_nanos(*ns as _).await;
Ok(())
}
},
@ -137,6 +141,10 @@ 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)?;
@ -149,12 +157,12 @@ where
Operation::Transfer(read, write) => bus.transfer(read, write).await,
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,
#[cfg(not(feature = "time"))]
Operation::DelayUs(us) => return Err(SpiDeviceError::DelayUsNotSupported),
Operation::DelayNs(_) => unreachable!(),
#[cfg(feature = "time")]
Operation::DelayUs(us) => match bus.flush().await {
Operation::DelayNs(ns) => match bus.flush().await {
Err(e) => Err(e),
Ok(()) => {
embassy_time::Timer::after_micros(*us as _).await;
embassy_time::Timer::after_nanos(*ns as _).await;
Ok(())
}
},

View File

@ -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(())
}
});

View File

@ -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,
}
}

View File

@ -27,31 +27,6 @@ default-target = "thumbv7em-none-eabi"
targets = ["thumbv7em-none-eabi"]
features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
[features]
# Architecture
_arch = [] # some arch was picked
arch-std = ["_arch", "critical-section/std"]
arch-cortex-m = ["_arch", "dep:cortex-m"]
arch-xtensa = ["_arch"]
arch-riscv32 = ["_arch", "dep:portable-atomic"]
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
executor-thread = []
# Enable the interrupt-mode executor (available in Cortex-M only)
executor-interrupt = []
# Enable nightly-only features
nightly = []
turbowakers = []
integrated-timers = ["dep:embassy-time"]
# Trace interrupt invocations with rtos-trace.
rtos-trace-interrupt = ["rtos-trace", "embassy-macros/rtos-trace-interrupt"]
[dependencies]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
@ -71,3 +46,74 @@ cortex-m = { version = "0.7.6", optional = true }
# arch-wasm dependencies
wasm-bindgen = { version = "0.2.82", optional = true }
js-sys = { version = "0.3", optional = true }
[dev-dependencies]
critical-section = { version = "1.1", features = ["std"] }
[features]
# Architecture
_arch = [] # some arch was picked
arch-std = ["_arch", "critical-section/std"]
arch-cortex-m = ["_arch", "dep:cortex-m"]
arch-xtensa = ["_arch"]
arch-riscv32 = ["_arch", "dep:portable-atomic"]
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
executor-thread = []
# Enable the interrupt-mode executor (available in Cortex-M only)
executor-interrupt = []
# Enable nightly-only features
nightly = ["embassy-macros/nightly"]
turbowakers = []
integrated-timers = ["dep:embassy-time"]
# BEGIN AUTOGENERATED CONFIG FEATURES
# Generated by gen_config.py. DO NOT EDIT.
task-arena-size-64 = []
task-arena-size-128 = []
task-arena-size-192 = []
task-arena-size-256 = []
task-arena-size-320 = []
task-arena-size-384 = []
task-arena-size-512 = []
task-arena-size-640 = []
task-arena-size-768 = []
task-arena-size-1024 = []
task-arena-size-1280 = []
task-arena-size-1536 = []
task-arena-size-2048 = []
task-arena-size-2560 = []
task-arena-size-3072 = []
task-arena-size-4096 = [] # Default
task-arena-size-5120 = []
task-arena-size-6144 = []
task-arena-size-8192 = []
task-arena-size-10240 = []
task-arena-size-12288 = []
task-arena-size-16384 = []
task-arena-size-20480 = []
task-arena-size-24576 = []
task-arena-size-32768 = []
task-arena-size-40960 = []
task-arena-size-49152 = []
task-arena-size-65536 = []
task-arena-size-81920 = []
task-arena-size-98304 = []
task-arena-size-131072 = []
task-arena-size-163840 = []
task-arena-size-196608 = []
task-arena-size-262144 = []
task-arena-size-327680 = []
task-arena-size-393216 = []
task-arena-size-524288 = []
task-arena-size-655360 = []
task-arena-size-786432 = []
task-arena-size-1048576 = []
# END AUTOGENERATED CONFIG FEATURES

View File

@ -2,10 +2,36 @@
An async/await executor designed for embedded usage.
- No `alloc`, no heap needed. Task futures are statically allocated.
- No `alloc`, no heap needed.
- With nightly Rust, task futures can be fully statically allocated.
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
- Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
- Efficient polling: a wake will only poll the woken task, not all of them.
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
- Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
## Task arena
When the `nightly` Cargo feature is not enabled, `embassy-executor` allocates tasks out of an arena (a very simple bump allocator).
If the task arena gets full, the program will panic at runtime. To guarantee this doesn't happen, you must set the size to the sum of sizes of all tasks.
Tasks are allocated from the arena when spawned for the first time. If the task exists, the allocation is not released to the arena, but can be reused to spawn the task again. For multiple-instance tasks (like `#[embassy_executor::task(pool_size = 4)]`), the first spawn will allocate memory for all instances. This is done for performance and to increase predictability (for example, spawning at least 1 instance of every task at boot guarantees an immediate panic if the arena is too small, while allocating instances on-demand could delay the panic to only when the program is under load).
The arena size can be configured in two ways:
- Via Cargo features: enable a Cargo feature like `task-arena-size-8192`. Only a selection of values
is available, check `Cargo.toml` for the list.
- Via environment variables at build time: set the variable named `EMBASSY_EXECUTOR_TASK_ARENA_SIZE`. For example
`EMBASSY_EXECUTOR_TASK_ARENA_SIZE=4321 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
Any value can be set, unlike with Cargo features.
Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting
with different values, compilation fails.
## Statically allocating tasks
When using nightly Rust, enable the `nightly` Cargo feature. This will make `embassy-executor` use the `type_alias_impl_trait` feature to allocate all tasks in `static`s. Each task gets its own `static`, with the exact size to hold the task (or multiple instances of it, if using `pool_size`) calculated automatically at compile time. If tasks don't fit in RAM, this is detected at compile time by the linker. Runtime panics due to running out of memory are not possible.
The configured arena size is ignored, no arena is used at all.

View File

@ -1,6 +1,97 @@
use std::env;
use std::collections::HashMap;
use std::fmt::Write;
use std::path::PathBuf;
use std::{env, fs};
static CONFIGS: &[(&str, usize)] = &[
// BEGIN AUTOGENERATED CONFIG FEATURES
// Generated by gen_config.py. DO NOT EDIT.
("TASK_ARENA_SIZE", 4096),
// END AUTOGENERATED CONFIG FEATURES
];
struct ConfigState {
value: usize,
seen_feature: bool,
seen_env: bool,
}
fn main() {
let crate_name = env::var("CARGO_PKG_NAME")
.unwrap()
.to_ascii_uppercase()
.replace('-', "_");
// only rebuild if build.rs changed. Otherwise Cargo will rebuild if any
// other file changed.
println!("cargo:rerun-if-changed=build.rs");
// Rebuild if config envvar changed.
for (name, _) in CONFIGS {
println!("cargo:rerun-if-env-changed={crate_name}_{name}");
}
let mut configs = HashMap::new();
for (name, default) in CONFIGS {
configs.insert(
*name,
ConfigState {
value: *default,
seen_env: false,
seen_feature: false,
},
);
}
let prefix = format!("{crate_name}_");
for (var, value) in env::vars() {
if let Some(name) = var.strip_prefix(&prefix) {
let Some(cfg) = configs.get_mut(name) else {
panic!("Unknown env var {name}")
};
let Ok(value) = value.parse::<usize>() else {
panic!("Invalid value for env var {name}: {value}")
};
cfg.value = value;
cfg.seen_env = true;
}
if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") {
if let Some(i) = feature.rfind('_') {
let name = &feature[..i];
let value = &feature[i + 1..];
if let Some(cfg) = configs.get_mut(name) {
let Ok(value) = value.parse::<usize>() else {
panic!("Invalid value for feature {name}: {value}")
};
// envvars take priority.
if !cfg.seen_env {
if cfg.seen_feature {
panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value);
}
cfg.value = value;
cfg.seen_feature = true;
}
}
}
}
}
let mut data = String::new();
for (name, cfg) in &configs {
writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap();
}
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let out_file = out_dir.join("config.rs").to_string_lossy().to_string();
fs::write(out_file, data).unwrap();
// cortex-m targets
let target = env::var("TARGET").unwrap();
if target.starts_with("thumbv6m-") {

View File

@ -0,0 +1,82 @@
import os
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
features = []
def feature(name, default, min=None, max=None, pow2=None, vals=None, factors=[]):
if vals is None:
assert min is not None
assert max is not None
vals = set()
val = min
while val <= max:
vals.add(val)
for f in factors:
if val * f <= max:
vals.add(val * f)
if (pow2 == True or (isinstance(pow2, int) and val >= pow2)) and val > 0:
val *= 2
else:
val += 1
vals.add(default)
vals = sorted(list(vals))
features.append(
{
"name": name,
"default": default,
"vals": vals,
}
)
feature(
"task_arena_size", default=4096, min=64, max=1024 * 1024, pow2=True, factors=[3, 5]
)
# ========= Update Cargo.toml
things = ""
for f in features:
name = f["name"].replace("_", "-")
for val in f["vals"]:
things += f"{name}-{val} = []"
if val == f["default"]:
things += " # Default"
things += "\n"
things += "\n"
SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n"
SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n"
HELP = "# Generated by gen_config.py. DO NOT EDIT.\n"
with open("Cargo.toml", "r") as f:
data = f.read()
before, data = data.split(SEPARATOR_START, maxsplit=1)
_, after = data.split(SEPARATOR_END, maxsplit=1)
data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after
with open("Cargo.toml", "w") as f:
f.write(data)
# ========= Update build.rs
things = ""
for f in features:
name = f["name"].upper()
things += f' ("{name}", {f["default"]}),\n'
SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n"
SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n"
HELP = " // Generated by gen_config.py. DO NOT EDIT.\n"
with open("build.rs", "r") as f:
data = f.read()
before, data = data.split(SEPARATOR_START, maxsplit=1)
_, after = data.split(SEPARATOR_END, maxsplit=1)
data = before + SEPARATOR_START + HELP + things + " " + SEPARATOR_END + after
with open("build.rs", "w") as f:
f.write(data)

View File

@ -51,7 +51,6 @@ mod thread {
use core::arch::asm;
use core::marker::PhantomData;
#[cfg(feature = "nightly")]
pub use embassy_macros::main_cortex_m as main;
use crate::{raw, Spawner};

View File

@ -7,7 +7,6 @@ pub use thread::*;
mod thread {
use core::marker::PhantomData;
#[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main;
use portable_atomic::{AtomicBool, Ordering};

View File

@ -8,7 +8,6 @@ mod thread {
use std::marker::PhantomData;
use std::sync::{Condvar, Mutex};
#[cfg(feature = "nightly")]
pub use embassy_macros::main_std as main;
use crate::{raw, Spawner};

View File

@ -8,7 +8,6 @@ mod thread {
use core::marker::PhantomData;
#[cfg(feature = "nightly")]
pub use embassy_macros::main_wasm as main;
use js_sys::Promise;
use wasm_bindgen::prelude::*;

View File

@ -8,7 +8,6 @@ mod thread {
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main;
use crate::{raw, Spawner};

View File

@ -1,5 +1,5 @@
#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
#![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))]
#![cfg_attr(feature = "arch-xtensa", feature(asm_experimental_arch))]
#![allow(clippy::new_without_default)]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
@ -7,7 +7,6 @@
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
#[cfg(feature = "nightly")]
pub use embassy_macros::task;
macro_rules! check_at_most_one {
@ -41,28 +40,101 @@ pub mod raw;
mod spawner;
pub use spawner::*;
mod config {
#![allow(unused)]
include!(concat!(env!("OUT_DIR"), "/config.rs"));
}
/// Implementation details for embassy macros.
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
pub mod _export {
#[cfg(feature = "rtos-trace")]
pub use rtos_trace::trace;
use core::alloc::Layout;
use core::cell::{Cell, UnsafeCell};
use core::future::Future;
use core::mem::MaybeUninit;
use core::ptr::null_mut;
/// Expands the given block of code when `embassy-executor` is compiled with
/// the `rtos-trace-interrupt` feature.
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "rtos-trace-interrupt")]
macro_rules! rtos_trace_interrupt {
($($tt:tt)*) => { $($tt)* };
use critical_section::{CriticalSection, Mutex};
use crate::raw::TaskPool;
struct Arena<const N: usize> {
buf: UnsafeCell<MaybeUninit<[u8; N]>>,
ptr: Mutex<Cell<*mut u8>>,
}
/// Does not expand the given block of code when `embassy-executor` is
/// compiled without the `rtos-trace-interrupt` feature.
#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "rtos-trace-interrupt"))]
macro_rules! rtos_trace_interrupt {
($($tt:tt)*) => {};
unsafe impl<const N: usize> Sync for Arena<N> {}
unsafe impl<const N: usize> Send for Arena<N> {}
impl<const N: usize> Arena<N> {
const fn new() -> Self {
Self {
buf: UnsafeCell::new(MaybeUninit::uninit()),
ptr: Mutex::new(Cell::new(null_mut())),
}
}
fn alloc<T>(&'static self, cs: CriticalSection) -> &'static mut MaybeUninit<T> {
let layout = Layout::new::<T>();
let start = self.buf.get().cast::<u8>();
let end = unsafe { start.add(N) };
let mut ptr = self.ptr.borrow(cs).get();
if ptr.is_null() {
ptr = self.buf.get().cast::<u8>();
}
let bytes_left = (end as usize) - (ptr as usize);
let align_offset = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize);
if align_offset + layout.size() > bytes_left {
panic!("embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/");
}
let res = unsafe { ptr.add(align_offset) };
let ptr = unsafe { ptr.add(align_offset + layout.size()) };
self.ptr.borrow(cs).set(ptr);
unsafe { &mut *(res as *mut MaybeUninit<T>) }
}
}
static ARENA: Arena<{ crate::config::TASK_ARENA_SIZE }> = Arena::new();
pub struct TaskPoolRef {
// type-erased `&'static mut TaskPool<F, N>`
// Needed because statics can't have generics.
ptr: Mutex<Cell<*mut ()>>,
}
unsafe impl Sync for TaskPoolRef {}
unsafe impl Send for TaskPoolRef {}
impl TaskPoolRef {
pub const fn new() -> Self {
Self {
ptr: Mutex::new(Cell::new(null_mut())),
}
}
/// Get the pool for this ref, allocating it from the arena the first time.
///
/// safety: for a given TaskPoolRef instance, must always call with the exact
/// same generic params.
pub unsafe fn get<F: Future, const N: usize>(&'static self) -> &'static TaskPool<F, N> {
critical_section::with(|cs| {
let ptr = self.ptr.borrow(cs);
if ptr.get().is_null() {
let pool = ARENA.alloc::<TaskPool<F, N>>(cs);
pool.write(TaskPool::new());
ptr.set(pool as *mut _ as _);
}
unsafe { &*(ptr.get() as *const _) }
})
}
}
}

View File

@ -0,0 +1,137 @@
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
use std::boxed::Box;
use std::future::poll_fn;
use std::sync::{Arc, Mutex};
use std::task::Poll;
use embassy_executor::raw::Executor;
use embassy_executor::task;
#[export_name = "__pender"]
fn __pender(context: *mut ()) {
unsafe {
let trace = &*(context as *const Trace);
trace.push("pend");
}
}
#[derive(Clone)]
struct Trace {
trace: Arc<Mutex<Vec<&'static str>>>,
}
impl Trace {
fn new() -> Self {
Self {
trace: Arc::new(Mutex::new(Vec::new())),
}
}
fn push(&self, value: &'static str) {
self.trace.lock().unwrap().push(value)
}
fn get(&self) -> Vec<&'static str> {
self.trace.lock().unwrap().clone()
}
}
fn setup() -> (&'static Executor, Trace) {
let trace = Trace::new();
let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut ();
let executor = &*Box::leak(Box::new(Executor::new(context)));
(executor, trace)
}
#[test]
fn executor_noop() {
let (executor, trace) = setup();
unsafe { executor.poll() };
assert!(trace.get().is_empty())
}
#[test]
fn executor_task() {
#[task]
async fn task1(trace: Trace) {
trace.push("poll task1")
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone())).unwrap();
unsafe { executor.poll() };
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", // spawning a task pends the executor
"poll task1", // poll only once.
]
)
}
#[test]
fn executor_task_self_wake() {
#[task]
async fn task1(trace: Trace) {
poll_fn(|cx| {
trace.push("poll task1");
cx.waker().wake_by_ref();
Poll::Pending
})
.await
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone())).unwrap();
unsafe { executor.poll() };
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", // spawning a task pends the executor
"poll task1", //
"pend", // task self-wakes
"poll task1", //
"pend", // task self-wakes
]
)
}
#[test]
fn executor_task_self_wake_twice() {
#[task]
async fn task1(trace: Trace) {
poll_fn(|cx| {
trace.push("poll task1");
cx.waker().wake_by_ref();
trace.push("poll task1 wake 2");
cx.waker().wake_by_ref();
Poll::Pending
})
.await
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone())).unwrap();
unsafe { executor.poll() };
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", // spawning a task pends the executor
"poll task1", //
"pend", // task self-wakes
"poll task1 wake 2", // task self-wakes again, shouldn't pend
"poll task1", //
"pend", // task self-wakes
"poll task1 wake 2", // task self-wakes again, shouldn't pend
]
)
}

View File

@ -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 }

View File

@ -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)
}
}

View File

@ -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(()),
}
}
}

View File

@ -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
}
}

View File

@ -21,5 +21,4 @@ proc-macro2 = "1.0.29"
proc-macro = true
[features]
# Enabling this cause interrupt::take! to require embassy-executor
rtos-trace-interrupt = []
nightly = []

View File

@ -79,6 +79,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
task_inner.vis = syn::Visibility::Inherited;
task_inner.sig.ident = task_inner_ident.clone();
#[cfg(feature = "nightly")]
let mut task_outer: ItemFn = parse_quote! {
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
type Fut = impl ::core::future::Future + 'static;
@ -87,6 +88,14 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
}
};
#[cfg(not(feature = "nightly"))]
let mut task_outer: ItemFn = parse_quote! {
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
const POOL_SIZE: usize = #pool_size;
static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new();
unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
}
};
task_outer.attrs.append(&mut task_inner.attrs.clone());

View File

@ -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"] }

View File

@ -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!()
}

View File

@ -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" }

View File

@ -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"] }

View File

@ -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"}

View File

@ -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" }

View File

@ -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 }

View File

@ -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 }

View File

@ -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" }

View File

@ -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;

View File

@ -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]

View File

@ -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");
}

View File

@ -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,398 +95,395 @@ 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(())
}
/// Write `data` to the DAC CH2 via DMA.
/// 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.
///
/// 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 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()`.
///
/// **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 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, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> {
/// Create a new DAC instance with both channels.
/// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
///
/// This is used to obtain two independent channels via `split()` for use e.g. with DMA.
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,
) -> Self {
pin_ch1.set_as_analog();
pin_ch2.set_as_analog();
into_ref!(peri, dma_ch1, dma_ch2);
/// 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_ch1 = DacCh1 {
_peri: peri,
dma: dma_ch1,
};
let mut dac_ch2 = DacCh2 {
let mut dac = Self {
phantom: PhantomData,
dma: dma_ch2,
dma,
};
#[cfg(any(dac_v5, dac_v6, dac_v7))]
dac.set_hfsel();
dac.set_mode(Mode::NormalInternalUnbuffered);
dac.enable();
dac
}
// 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();
/// 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);
});
});
}
#[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();
/// Enable this channel.
pub fn enable(&mut self) {
self.set_enable(true)
}
Self {
ch1: dac_ch1,
ch2: dac_ch2,
/// 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)),
}
}
/// Split the DAC into CH1 and CH2 for independent use.
pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) {
/// 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(Self::IDX, true);
w.set_dmaen(Self::IDX, 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(Self::IDX).as_ptr() as *mut u8,
tx_options,
)
},
ValueArray::Bit12Left(buf) => unsafe {
crate::dma::Transfer::new_write(
dma_channel,
tx_request,
buf,
T::regs().dhr12l(Self::IDX).as_ptr() as *mut u16,
tx_options,
)
},
ValueArray::Bit12Right(buf) => unsafe {
crate::dma::Transfer::new_write(
dma_channel,
tx_request,
buf,
T::regs().dhr12r(Self::IDX).as_ptr() as *mut u16,
tx_options,
)
},
};
tx_f.await;
T::regs().cr().modify(|w| {
w.set_en(Self::IDX, false);
w.set_dmaen(Self::IDX, false);
});
}
}
};
}
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();
}
}
/// 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 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 = 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();
// Enable twice to increment the DAC refcount for each channel.
T::enable_and_reset();
T::enable_and_reset();
Self {
ch1: DacCh1 {
phantom: PhantomData,
dma: dma_ch1,
},
ch2: DacCh2 {
phantom: PhantomData,
dma: dma_ch2,
},
}
}
/// 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: DacCh1 {
phantom: PhantomData,
dma: dma_ch1,
},
ch2: DacCh2 {
phantom: PhantomData,
dma: dma_ch2,
},
}
}
/// 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 {
@ -552,34 +493,36 @@ 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 {}
foreach_peripheral!(
(dac, $inst:ident) => {
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
#[cfg(any(rcc_h7, rcc_h7rm0433))]
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
fn frequency() -> crate::time::Hertz {
critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 })
}
// H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
#[cfg(any(rcc_h7, rcc_h7rm0433))]
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
fn frequency() -> crate::time::Hertz {
critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 })
}
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
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 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) {
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
}
}
fn disable_with_cs(_cs: critical_section::CriticalSection) {
// TODO: Decrement refcount?
crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
}
}
#[cfg(any(rcc_h7, rcc_h7rm0433))]
impl crate::rcc::RccPeripheral for peripherals::$inst {}
#[cfg(any(rcc_h7, rcc_h7rm0433))]
impl crate::rcc::RccPeripheral for peripherals::$inst {}
impl crate::dac::sealed::Instance for peripherals::$inst {
fn regs() -> &'static crate::pac::dac::Dac {

View 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
}
}

View File

@ -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!()
}
}
}

View File

@ -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 didnt 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 didnt have a check_and_clear call here, but blocking write did so
// Im 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,

View File

@ -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,25 +34,18 @@ pub fn no_timeout_fn() -> impl Fn() -> Result<(), Error> {
move || Ok(())
}
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
pub unsafe fn on_interrupt<T: Instance>() {
let regs = T::regs();
let isr = regs.isr().read();
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let regs = T::regs();
let isr = regs.isr().read();
if isr.tcr() || isr.tc() {
T::state().waker.wake();
}
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
// the interrupt
critical_section::with(|_| {
regs.cr1().modify(|w| w.set_tcie(false));
});
if isr.tcr() || isr.tc() {
T::state().waker.wake();
}
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
// the interrupt
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 = ();

View File

@ -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),

View File

@ -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)
}
})
};

View File

@ -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" ] }

View File

@ -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

View File

@ -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"

View File

@ -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
}
}
}

View File

@ -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 }

View File

@ -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() }

View File

@ -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())`.

View File

@ -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

View File

@ -17,7 +17,7 @@ log = [
[dependencies]
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] }
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time" }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }

View File

@ -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"

View File

@ -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),
}
}

View File

@ -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;
}
};
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}

View File

@ -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),
}
}

View File

@ -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"

View File

@ -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"] }

View File

@ -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;
}
};
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}

View File

@ -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),
}
}
}

View File

@ -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"

View File

@ -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 }

View File

@ -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)));
}
}
}

View File

@ -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]

View 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),
}
}
}

View 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),
};
}
}

View File

@ -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"

View File

@ -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 }

View File

@ -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]

View File

@ -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 }

View File

@ -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>;
});

View File

@ -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)));
}
}
}

View File

@ -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 {

View File

@ -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]

View File

@ -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"

View File

@ -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),
}
}

View File

@ -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;
}
};
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}

View File

@ -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 }

View File

@ -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)));
}
}
}

View File

@ -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 {

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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>;
});

View File

@ -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]

View File

@ -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"

View File

@ -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;
}
};
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}

Some files were not shown because too many files have changed in this diff Show More