Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
4634316749 | |||
1b9925e3da | |||
b4bc9ac028 | |||
a3c99f0324 | |||
76f3a11f43 | |||
d63c052f0e | |||
2c9f4bce01 | |||
74a6855f01 | |||
f88aa9ce48 | |||
3a8a950bb8 | |||
02305451b1 | |||
a42aef7759 | |||
142f42fe90 | |||
5844f5ce2d | |||
e5fdd35bd6 | |||
0bd47c779b | |||
4481631326 | |||
cf13f70ea9 | |||
6bdacb4f69 | |||
ff3baf1e90 | |||
d5dcbadbbe | |||
0ad0d27150 | |||
03d500f548 | |||
8b46343b34 | |||
a3ea01473a | |||
cf84c8bfd1 | |||
09d7950313 | |||
267cbaebe6 | |||
31fc337e2f | |||
135f350020 | |||
897663e023 | |||
2218d30c80 | |||
f5c9e3baa6 | |||
47bac9df70 | |||
3efc3eee57 | |||
bc65b8f7ec | |||
78f8e6112a | |||
5528c33649 |
3
ci.sh
3
ci.sh
@ -190,6 +190,7 @@ cargo batch \
|
||||
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --out-dir out/tests/stm32f446re \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
||||
@ -220,6 +221,8 @@ cargo batch \
|
||||
rm out/tests/stm32wb55rg/wpan_mac
|
||||
rm out/tests/stm32wb55rg/wpan_ble
|
||||
|
||||
# not in CI yet.
|
||||
rm -rf out/tests/stm32f446re
|
||||
|
||||
# unstable, I think it's running out of RAM?
|
||||
rm out/tests/stm32f207zg/eth
|
||||
|
@ -23,7 +23,7 @@ cortex-m = "0.7.6"
|
||||
cortex-m-rt = "0.7.0"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
||||
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.2" }
|
||||
num_enum = { version = "0.5.7", default-features = false }
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)]
|
||||
#![feature(async_fn_in_trait, type_alias_impl_trait)]
|
||||
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||
#![deny(unused_must_use)]
|
||||
|
||||
|
@ -1,54 +1,48 @@
|
||||
macro_rules! nvram {
|
||||
($($s:literal,)*) => {
|
||||
concat_bytes!($($s, b"\x00",)* b"\x00\x00")
|
||||
};
|
||||
}
|
||||
|
||||
pub static NVRAM: &'static [u8] = &*nvram!(
|
||||
b"NVRAMRev=$Rev$",
|
||||
b"manfid=0x2d0",
|
||||
b"prodid=0x0727",
|
||||
b"vendid=0x14e4",
|
||||
b"devid=0x43e2",
|
||||
b"boardtype=0x0887",
|
||||
b"boardrev=0x1100",
|
||||
b"boardnum=22",
|
||||
b"macaddr=00:A0:50:b5:59:5e",
|
||||
b"sromrev=11",
|
||||
b"boardflags=0x00404001",
|
||||
b"boardflags3=0x04000000",
|
||||
b"xtalfreq=37400",
|
||||
b"nocrc=1",
|
||||
b"ag0=255",
|
||||
b"aa2g=1",
|
||||
b"ccode=ALL",
|
||||
b"pa0itssit=0x20",
|
||||
b"extpagain2g=0",
|
||||
b"pa2ga0=-168,6649,-778",
|
||||
b"AvVmid_c0=0x0,0xc8",
|
||||
b"cckpwroffset0=5",
|
||||
b"maxp2ga0=84",
|
||||
b"txpwrbckof=6",
|
||||
b"cckbw202gpo=0",
|
||||
b"legofdmbw202gpo=0x66111111",
|
||||
b"mcsbw202gpo=0x77711111",
|
||||
b"propbw202gpo=0xdd",
|
||||
b"ofdmdigfilttype=18",
|
||||
b"ofdmdigfilttypebe=18",
|
||||
b"papdmode=1",
|
||||
b"papdvalidtest=1",
|
||||
b"pacalidx2g=45",
|
||||
b"papdepsoffset=-30",
|
||||
b"papdendidx=58",
|
||||
b"ltecxmux=0",
|
||||
b"ltecxpadnum=0x0102",
|
||||
b"ltecxfnsel=0x44",
|
||||
b"ltecxgcigpio=0x01",
|
||||
b"il0macaddr=00:90:4c:c5:12:38",
|
||||
b"wl0id=0x431b",
|
||||
b"deadman_to=0xffffffff",
|
||||
b"muxenab=0x100",
|
||||
b"spurconfig=0x3",
|
||||
b"glitch_based_crsmin=1",
|
||||
b"btc_mode=1",
|
||||
);
|
||||
pub static NVRAM: &'static [u8] = b"
|
||||
NVRAMRev=$Rev$\x00\
|
||||
manfid=0x2d0\x00\
|
||||
prodid=0x0727\x00\
|
||||
vendid=0x14e4\x00\
|
||||
devid=0x43e2\x00\
|
||||
boardtype=0x0887\x00\
|
||||
boardrev=0x1100\x00\
|
||||
boardnum=22\x00\
|
||||
macaddr=00:A0:50:b5:59:5e\x00\
|
||||
sromrev=11\x00\
|
||||
boardflags=0x00404001\x00\
|
||||
boardflags3=0x04000000\x00\
|
||||
xtalfreq=37400\x00\
|
||||
nocrc=1\x00\
|
||||
ag0=255\x00\
|
||||
aa2g=1\x00\
|
||||
ccode=ALL\x00\
|
||||
pa0itssit=0x20\x00\
|
||||
extpagain2g=0\x00\
|
||||
pa2ga0=-168,6649,-778\x00\
|
||||
AvVmid_c0=0x0,0xc8\x00\
|
||||
cckpwroffset0=5\x00\
|
||||
maxp2ga0=84\x00\
|
||||
txpwrbckof=6\x00\
|
||||
cckbw202gpo=0\x00\
|
||||
legofdmbw202gpo=0x66111111\x00\
|
||||
mcsbw202gpo=0x77711111\x00\
|
||||
propbw202gpo=0xdd\x00\
|
||||
ofdmdigfilttype=18\x00\
|
||||
ofdmdigfilttypebe=18\x00\
|
||||
papdmode=1\x00\
|
||||
papdvalidtest=1\x00\
|
||||
pacalidx2g=45\x00\
|
||||
papdepsoffset=-30\x00\
|
||||
papdendidx=58\x00\
|
||||
ltecxmux=0\x00\
|
||||
ltecxpadnum=0x0102\x00\
|
||||
ltecxfnsel=0x44\x00\
|
||||
ltecxgcigpio=0x01\x00\
|
||||
il0macaddr=00:90:4c:c5:12:38\x00\
|
||||
wl0id=0x431b\x00\
|
||||
deadman_to=0xffffffff\x00\
|
||||
muxenab=0x100\x00\
|
||||
spurconfig=0x3\x00\
|
||||
glitch_based_crsmin=1\x00\
|
||||
btc_mode=1\x00\
|
||||
\x00";
|
||||
|
@ -1,5 +1,6 @@
|
||||
* xref:getting_started.adoc[Getting started]
|
||||
** xref:basic_application.adoc[Basic application]
|
||||
** xref:project_structure.adoc[Project Structure]
|
||||
* xref:layer_by_layer.adoc[Bare metal to async]
|
||||
* xref:runtime.adoc[Executor]
|
||||
* xref:hal.adoc[HAL]
|
||||
|
@ -36,3 +36,102 @@ For Cortex-M targets, consider making sure that ALL of the following features ar
|
||||
* `nightly`
|
||||
|
||||
For Xtensa ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate].
|
||||
|
||||
== Why is my binary so big?
|
||||
|
||||
The first step to managing your binary size is to set up your link:https://doc.rust-lang.org/cargo/reference/profiles.html[profiles].
|
||||
|
||||
[source,toml]
|
||||
----
|
||||
[profile.release]
|
||||
debug = false
|
||||
lto = true
|
||||
opt-level = "s"
|
||||
incremental = true
|
||||
----
|
||||
|
||||
All of these flags are elaborated on in the Rust Book page linked above.
|
||||
|
||||
=== My binary is still big... filled with `std::fmt` stuff!
|
||||
|
||||
This means your code is sufficiently complex that `panic!` invocation's formatting requirements could not be optimized out, despite your usage of `panic-halt` or `panic-reset`.
|
||||
|
||||
You can remedy this by adding the following to your `.cargo/config.toml`:
|
||||
|
||||
[source,toml]
|
||||
----
|
||||
[unstable]
|
||||
build-std = ["core"]
|
||||
build-std-features = ["panic_immediate_abort"]
|
||||
----
|
||||
|
||||
This replaces all panics with a `UDF` (undefined) instruction.
|
||||
|
||||
Depending on your chipset, this will exhibit different behavior.
|
||||
|
||||
Refer to the spec for your chipset, but for `thumbv6m`, it results in a hardfault. Which can be configured like so:
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
#[exception]
|
||||
unsafe fn HardFault(_frame: &ExceptionFrame) -> ! {
|
||||
SCB::sys_reset() // <- you could do something other than reset
|
||||
}
|
||||
----
|
||||
|
||||
Refer to cortex-m's link:https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.exception.html[exception handling] for more info.
|
||||
|
||||
== `embassy-time` throws linker errors
|
||||
|
||||
If you see linker error like this:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
= note: rust-lld: error: undefined symbol: _embassy_time_now
|
||||
>>> referenced by driver.rs:127 (src/driver.rs:127)
|
||||
>>> embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::now::hefb1f99d6e069842) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib
|
||||
|
||||
rust-lld: error: undefined symbol: _embassy_time_allocate_alarm
|
||||
>>> referenced by driver.rs:134 (src/driver.rs:134)
|
||||
>>> embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::allocate_alarm::hf5145b6bd46706b2) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib
|
||||
|
||||
rust-lld: error: undefined symbol: _embassy_time_set_alarm_callback
|
||||
>>> referenced by driver.rs:139 (src/driver.rs:139)
|
||||
>>> embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::set_alarm_callback::h24f92388d96eafd2) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib
|
||||
|
||||
rust-lld: error: undefined symbol: _embassy_time_set_alarm
|
||||
>>> referenced by driver.rs:144 (src/driver.rs:144)
|
||||
>>> embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::set_alarm::h530a5b1f444a6d5b) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib
|
||||
----
|
||||
|
||||
You probably need to enable a time driver for your HAL (not in `embassy-time`!). For example with `embassy-stm32`, you might need to enable `time-driver-any`:
|
||||
|
||||
[source,toml]
|
||||
----
|
||||
[dependencies.embassy-stm32]
|
||||
version = "0.1.0"
|
||||
features = [
|
||||
# ...
|
||||
"time-driver-any", # Add this line!
|
||||
# ...
|
||||
]
|
||||
----
|
||||
|
||||
== Error: `Only one package in the dependency graph may specify the same links value.`
|
||||
|
||||
You have multiple versions of the same crate in your dependency tree. This means that some of your
|
||||
embassy crates are coming from crates.io, and some from git, each of them pulling in a different set
|
||||
of dependencies.
|
||||
|
||||
To resolve this issue, make sure to only use a single source for all your embassy crates! To do this,
|
||||
you should patch your dependencies to use git sources using `[patch.crates.io]` and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.
|
||||
|
||||
Example:
|
||||
|
||||
[source,toml]
|
||||
----
|
||||
[patch.crates-io]
|
||||
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
----
|
||||
|
||||
Note that the git revision should match any other embassy patches or git dependencies that you are using!
|
||||
|
80
docs/modules/ROOT/pages/project_structure.adoc
Normal file
80
docs/modules/ROOT/pages/project_structure.adoc
Normal file
@ -0,0 +1,80 @@
|
||||
= Project Structure
|
||||
|
||||
There are many ways to configure embassy and its components for your exact application. The link:https://github.com/embassy-rs/embassy/tree/main/examples[examples] directory for each chipset demonstrates how your project structure should look. Let's break it down:
|
||||
|
||||
The toplevel file structure of your project should look like this:
|
||||
[source,plain]
|
||||
----
|
||||
{} = Maybe
|
||||
|
||||
my-project
|
||||
|- .cargo
|
||||
| |- config.toml
|
||||
|- src
|
||||
| |- main.rs
|
||||
|- build.rs
|
||||
|- Cargo.toml
|
||||
|- {memory.x}
|
||||
|- rust-toolchain.toml
|
||||
----
|
||||
|
||||
=== .cargo/config.toml
|
||||
|
||||
This directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device.
|
||||
|
||||
Here is a minimal example:
|
||||
|
||||
[source,toml]
|
||||
----
|
||||
[target.thumbv6m-none-eabi] # <-change for your platform
|
||||
runner = 'probe-rs run --chip STM32F031K6Tx' # <- change for your chip
|
||||
|
||||
[build]
|
||||
target = "thumbv6m-none-eabi" # <-change for your platform
|
||||
|
||||
[env]
|
||||
DEFMT_LOG = "trace" # <- can change to info, warn, or error
|
||||
----
|
||||
|
||||
=== build.rs
|
||||
|
||||
This is the build script for your project. It links defmt (what is defmt?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].
|
||||
|
||||
=== Cargo.toml
|
||||
|
||||
This is your manifest file, where you can configure all of the embassy components to use the features you need.
|
||||
|
||||
TODO: someone should exhaustively describe every feature for every component!
|
||||
|
||||
=== memory.x
|
||||
|
||||
This file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x.
|
||||
|
||||
Here is an example for using S140 with an nRF52840:
|
||||
|
||||
[source,x]
|
||||
----
|
||||
MEMORY
|
||||
{
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
/* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
|
||||
FLASH : ORIGIN = 0x00027000, LENGTH = 868K
|
||||
RAM : ORIGIN = 0x20020000, LENGTH = 128K
|
||||
}
|
||||
----
|
||||
|
||||
=== rust-toolchain.toml
|
||||
|
||||
This file configures the rust version and configuration to use.
|
||||
|
||||
A minimal example:
|
||||
|
||||
[source,toml]
|
||||
----
|
||||
[toolchain]
|
||||
channel = "nightly-2023-08-19" # <- as of writing, this is the exact rust version embassy uses
|
||||
components = [ "rust-src", "rustfmt" ] # <- optionally add "llvm-tools-preview" for some extra features like "cargo size"
|
||||
targets = [
|
||||
"thumbv6m-none-eabi" # <-change for your platform
|
||||
]
|
||||
----
|
@ -25,8 +25,8 @@ embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||
"unproven",
|
||||
] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2", optional = true }
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||
nb = "1.0.0"
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
|
@ -63,22 +63,36 @@ where
|
||||
CS: OutputPin,
|
||||
{
|
||||
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||
if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {
|
||||
return Err(SpiDeviceError::DelayNotSupported);
|
||||
}
|
||||
|
||||
let mut bus = self.bus.lock().await;
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res: Result<(), BUS::Error> = try {
|
||||
let op_res = 'ops: {
|
||||
for op in operations {
|
||||
match op {
|
||||
Operation::Read(buf) => bus.read(buf).await?,
|
||||
Operation::Write(buf) => bus.write(buf).await?,
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
||||
let res = match op {
|
||||
Operation::Read(buf) => bus.read(buf).await,
|
||||
Operation::Write(buf) => bus.write(buf).await,
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write).await,
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,
|
||||
#[cfg(not(feature = "time"))]
|
||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||
Operation::DelayNs(_) => unreachable!(),
|
||||
#[cfg(feature = "time")]
|
||||
Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await,
|
||||
Operation::DelayNs(ns) => match bus.flush().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(()) => {
|
||||
embassy_time::Timer::after_nanos(*ns as _).await;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
};
|
||||
if let Err(e) = res {
|
||||
break 'ops Err(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
@ -127,23 +141,37 @@ where
|
||||
CS: OutputPin,
|
||||
{
|
||||
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||
if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {
|
||||
return Err(SpiDeviceError::DelayNotSupported);
|
||||
}
|
||||
|
||||
let mut bus = self.bus.lock().await;
|
||||
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res: Result<(), BUS::Error> = try {
|
||||
let op_res = 'ops: {
|
||||
for op in operations {
|
||||
match op {
|
||||
Operation::Read(buf) => bus.read(buf).await?,
|
||||
Operation::Write(buf) => bus.write(buf).await?,
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
||||
let res = match op {
|
||||
Operation::Read(buf) => bus.read(buf).await,
|
||||
Operation::Write(buf) => bus.write(buf).await,
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write).await,
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,
|
||||
#[cfg(not(feature = "time"))]
|
||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||
Operation::DelayNs(_) => unreachable!(),
|
||||
#[cfg(feature = "time")]
|
||||
Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await,
|
||||
Operation::DelayNs(ns) => match bus.flush().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(()) => {
|
||||
embassy_time::Timer::after_nanos(*ns as _).await;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
};
|
||||
if let Err(e) = res {
|
||||
break 'ops Err(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
|
@ -55,6 +55,10 @@ where
|
||||
CS: OutputPin,
|
||||
{
|
||||
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||
if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {
|
||||
return Err(SpiDeviceError::DelayNotSupported);
|
||||
}
|
||||
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
@ -65,10 +69,10 @@ where
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||
#[cfg(not(feature = "time"))]
|
||||
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
|
||||
Operation::DelayNs(_) => unreachable!(),
|
||||
#[cfg(feature = "time")]
|
||||
Operation::DelayUs(us) => {
|
||||
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
|
||||
Operation::DelayNs(ns) => {
|
||||
embassy_time::block_for(embassy_time::Duration::from_nanos(*ns as _));
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
@ -161,6 +165,10 @@ where
|
||||
CS: OutputPin,
|
||||
{
|
||||
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||
if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {
|
||||
return Err(SpiDeviceError::DelayNotSupported);
|
||||
}
|
||||
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||
@ -172,10 +180,10 @@ where
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||
#[cfg(not(feature = "time"))]
|
||||
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
|
||||
Operation::DelayNs(_) => unreachable!(),
|
||||
#[cfg(feature = "time")]
|
||||
Operation::DelayUs(us) => {
|
||||
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
|
||||
Operation::DelayNs(ns) => {
|
||||
embassy_time::block_for(embassy_time::Duration::from_nanos(*ns as _));
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
@ -39,8 +39,8 @@ pub enum SpiDeviceError<BUS, CS> {
|
||||
Spi(BUS),
|
||||
/// Setting the value of the Chip Select (CS) pin failed.
|
||||
Cs(CS),
|
||||
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
|
||||
DelayUsNotSupported,
|
||||
/// Delay operations are not supported when the `time` Cargo feature is not enabled.
|
||||
DelayNotSupported,
|
||||
/// The SPI bus could not be configured.
|
||||
Config,
|
||||
}
|
||||
@ -54,7 +54,7 @@ where
|
||||
match self {
|
||||
Self::Spi(e) => e.kind(),
|
||||
Self::Cs(_) => spi::ErrorKind::Other,
|
||||
Self::DelayUsNotSupported => spi::ErrorKind::Other,
|
||||
Self::DelayNotSupported => spi::ErrorKind::Other,
|
||||
Self::Config => spi::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
[package]
|
||||
name = "embassy-lora"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
|
||||
features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/unstable-pac", "time", "defmt"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[features]
|
||||
stm32wl = ["dep:embassy-stm32"]
|
||||
time = ["embassy-time", "lorawan-device"]
|
||||
defmt = ["dep:defmt", "lorawan-device/defmt"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||
|
||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||
lora-phy = { version = "2" }
|
||||
lorawan-device = { version = "0.11.0", default-features = false, features = ["async"], optional = true }
|
@ -1,258 +0,0 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
::core::unreachable!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unreachable!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:#02x?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Bytes<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:#02x?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LowerHex for Bytes<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:#02x?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl<'a> defmt::Format for Bytes<'a> {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(fmt, "{:02x}", self.0)
|
||||
}
|
||||
}
|
@ -1,317 +0,0 @@
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_stm32::interrupt;
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_stm32::interrupt::InterruptExt;
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_stm32::pac;
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_sync::signal::Signal;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal_async::delay::DelayUs;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use lora_phy::mod_params::RadioError::*;
|
||||
use lora_phy::mod_params::{BoardType, RadioError};
|
||||
use lora_phy::mod_traits::InterfaceVariant;
|
||||
|
||||
/// Interrupt handler.
|
||||
#[cfg(feature = "stm32wl")]
|
||||
pub struct InterruptHandler {}
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
impl interrupt::typelevel::Handler<interrupt::typelevel::SUBGHZ_RADIO> for InterruptHandler {
|
||||
unsafe fn on_interrupt() {
|
||||
interrupt::SUBGHZ_RADIO.disable();
|
||||
IRQ_SIGNAL.signal(());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
static IRQ_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
/// Base for the InterfaceVariant implementation for an stm32wl/sx1262 combination
|
||||
pub struct Stm32wlInterfaceVariant<CTRL> {
|
||||
board_type: BoardType,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
impl<'a, CTRL> Stm32wlInterfaceVariant<CTRL>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
{
|
||||
/// Create an InterfaceVariant instance for an stm32wl/sx1262 combination
|
||||
pub fn new(
|
||||
_irq: impl interrupt::typelevel::Binding<interrupt::typelevel::SUBGHZ_RADIO, InterruptHandler>,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
) -> Result<Self, RadioError> {
|
||||
interrupt::SUBGHZ_RADIO.disable();
|
||||
Ok(Self {
|
||||
board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board
|
||||
rf_switch_rx,
|
||||
rf_switch_tx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
impl<CTRL> InterfaceVariant for Stm32wlInterfaceVariant<CTRL>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
{
|
||||
fn set_board_type(&mut self, board_type: BoardType) {
|
||||
self.board_type = board_type;
|
||||
}
|
||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||
pac::PWR.subghzspicr().modify(|w| w.set_nss(false));
|
||||
Ok(())
|
||||
}
|
||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||
pac::PWR.subghzspicr().modify(|w| w.set_nss(true));
|
||||
Ok(())
|
||||
}
|
||||
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||
pac::RCC.csr().modify(|w| w.set_rfrst(true));
|
||||
pac::RCC.csr().modify(|w| w.set_rfrst(false));
|
||||
Ok(())
|
||||
}
|
||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||
while pac::PWR.sr2().read().rfbusys() {}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||
unsafe { interrupt::SUBGHZ_RADIO.enable() };
|
||||
IRQ_SIGNAL.wait().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Base for the InterfaceVariant implementation for an stm32l0/sx1276 combination
|
||||
pub struct Stm32l0InterfaceVariant<CTRL, WAIT> {
|
||||
board_type: BoardType,
|
||||
nss: CTRL,
|
||||
reset: CTRL,
|
||||
irq: WAIT,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
}
|
||||
|
||||
impl<CTRL, WAIT> Stm32l0InterfaceVariant<CTRL, WAIT>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
/// Create an InterfaceVariant instance for an stm32l0/sx1276 combination
|
||||
pub fn new(
|
||||
nss: CTRL,
|
||||
reset: CTRL,
|
||||
irq: WAIT,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
) -> Result<Self, RadioError> {
|
||||
Ok(Self {
|
||||
board_type: BoardType::Stm32l0Sx1276, // updated when associated with a specific LoRa board
|
||||
nss,
|
||||
reset,
|
||||
irq,
|
||||
rf_switch_rx,
|
||||
rf_switch_tx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTRL, WAIT> InterfaceVariant for Stm32l0InterfaceVariant<CTRL, WAIT>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
fn set_board_type(&mut self, board_type: BoardType) {
|
||||
self.board_type = board_type;
|
||||
}
|
||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||
self.nss.set_low().map_err(|_| NSS)
|
||||
}
|
||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||
self.nss.set_high().map_err(|_| NSS)
|
||||
}
|
||||
async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||
delay.delay_ms(10).await;
|
||||
self.reset.set_low().map_err(|_| Reset)?;
|
||||
delay.delay_ms(10).await;
|
||||
self.reset.set_high().map_err(|_| Reset)?;
|
||||
delay.delay_ms(10).await;
|
||||
Ok(())
|
||||
}
|
||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||
Ok(())
|
||||
}
|
||||
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||
self.irq.wait_for_high().await.map_err(|_| Irq)
|
||||
}
|
||||
|
||||
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Base for the InterfaceVariant implementation for a generic Sx126x LoRa board
|
||||
pub struct GenericSx126xInterfaceVariant<CTRL, WAIT> {
|
||||
board_type: BoardType,
|
||||
nss: CTRL,
|
||||
reset: CTRL,
|
||||
dio1: WAIT,
|
||||
busy: WAIT,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
}
|
||||
|
||||
impl<CTRL, WAIT> GenericSx126xInterfaceVariant<CTRL, WAIT>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
/// Create an InterfaceVariant instance for an nrf52840/sx1262 combination
|
||||
pub fn new(
|
||||
nss: CTRL,
|
||||
reset: CTRL,
|
||||
dio1: WAIT,
|
||||
busy: WAIT,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
) -> Result<Self, RadioError> {
|
||||
Ok(Self {
|
||||
board_type: BoardType::Rak4631Sx1262, // updated when associated with a specific LoRa board
|
||||
nss,
|
||||
reset,
|
||||
dio1,
|
||||
busy,
|
||||
rf_switch_rx,
|
||||
rf_switch_tx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTRL, WAIT> InterfaceVariant for GenericSx126xInterfaceVariant<CTRL, WAIT>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
fn set_board_type(&mut self, board_type: BoardType) {
|
||||
self.board_type = board_type;
|
||||
}
|
||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||
self.nss.set_low().map_err(|_| NSS)
|
||||
}
|
||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||
self.nss.set_high().map_err(|_| NSS)
|
||||
}
|
||||
async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||
delay.delay_ms(10).await;
|
||||
self.reset.set_low().map_err(|_| Reset)?;
|
||||
delay.delay_ms(20).await;
|
||||
self.reset.set_high().map_err(|_| Reset)?;
|
||||
delay.delay_ms(10).await;
|
||||
Ok(())
|
||||
}
|
||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||
self.busy.wait_for_low().await.map_err(|_| Busy)
|
||||
}
|
||||
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(async_fn_in_trait, impl_trait_projections)]
|
||||
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||
//! embassy-lora holds LoRa-specific functionality.
|
||||
|
||||
pub(crate) mod fmt;
|
||||
|
||||
/// interface variants required by the external lora physical layer crate (lora-phy)
|
||||
pub mod iv;
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
|
||||
/// A convenience timer to use with the LoRaWAN crate
|
||||
#[cfg(feature = "time")]
|
||||
pub struct LoraTimer {
|
||||
start: Instant,
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl LoraTimer {
|
||||
pub fn new() -> Self {
|
||||
Self { start: Instant::now() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl lorawan_device::async_device::radio::Timer for LoraTimer {
|
||||
fn reset(&mut self) {
|
||||
self.start = Instant::now();
|
||||
}
|
||||
|
||||
async fn at(&mut self, millis: u64) {
|
||||
Timer::at(self.start + Duration::from_millis(millis)).await
|
||||
}
|
||||
|
||||
async fn delay_ms(&mut self, millis: u64) {
|
||||
Timer::after_millis(millis).await
|
||||
}
|
||||
}
|
@ -13,16 +13,16 @@ edition = "2021"
|
||||
heapless = "0.8"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4", default-features = false, optional = true }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
embedded-hal-bus = { version = "=0.1.0-rc.2", features = ["async"] }
|
||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
||||
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
bitfield = "0.14.0"
|
||||
|
||||
[dev-dependencies]
|
||||
embedded-hal-mock = { version = "=0.10.0-rc.1", features = ["embedded-hal-async", "eh1"] }
|
||||
embedded-hal-mock = { git = "https://github.com/Dirbaio/embedded-hal-mock", rev = "c5c4dca18e043e6386aee02173f61a65fea3981e", features = ["embedded-hal-async", "eh1"] }
|
||||
crc = "3.0.1"
|
||||
env_logger = "0.10"
|
||||
critical-section = { version = "1.1.2", features = ["std"] }
|
||||
|
@ -729,7 +729,7 @@ mod tests {
|
||||
use core::convert::Infallible;
|
||||
|
||||
use embedded_hal_1::digital::{ErrorType, OutputPin};
|
||||
use embedded_hal_async::delay::DelayUs;
|
||||
use embedded_hal_async::delay::DelayNs;
|
||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||
use embedded_hal_mock::common::Generic;
|
||||
use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction};
|
||||
@ -760,7 +760,11 @@ mod tests {
|
||||
// see https://github.com/rust-embedded/embedded-hal/pull/462#issuecomment-1560014426
|
||||
struct MockDelay {}
|
||||
|
||||
impl DelayUs for MockDelay {
|
||||
impl DelayNs for MockDelay {
|
||||
async fn delay_ns(&mut self, _ns: u32) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delay_us(&mut self, _us: u32) {
|
||||
todo!()
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = { version = "1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-hal = { version = "1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
|
@ -12,8 +12,8 @@ embassy-sync = { version = "0.4.0", path = "../embassy-sync"}
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
|
||||
|
||||
embedded-hal = { version = "1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-hal = { version = "1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
|
||||
noproto = { git="https://github.com/embassy-rs/noproto", rev = "f5e6d1f325b6ad4e344f60452b09576e24671f62", default-features = false, features = ["derive"] }
|
||||
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
||||
|
@ -15,7 +15,7 @@ log = ["dep:log", "ppproto/log"]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
ppproto = { version = "0.1.2"}
|
||||
|
@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = { version = "1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-hal = { version = "1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
||||
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
|
@ -54,7 +54,7 @@ smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp.git", rev = "b57e2f9e70
|
||||
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
embedded-io-async = { version = "0.6.1", optional = true }
|
||||
|
||||
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
@ -63,4 +63,4 @@ generic-array = { version = "0.14.4", default-features = false }
|
||||
stable_deref_trait = { version = "1.2.0", default-features = false }
|
||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||
atomic-pool = "1.0"
|
||||
embedded-nal-async = { version = "0.7", optional = true }
|
||||
embedded-nal-async = { version = "0.7.1", optional = true }
|
||||
|
@ -101,10 +101,10 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2", optional = true}
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2", optional = true}
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
embedded-io-async = { version = "0.6.1", optional = true }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
@ -76,7 +76,7 @@ critical-section = "1.1"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
chrono = { version = "0.4", default-features = false, optional = true }
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
embedded-io-async = { version = "0.6.1", optional = true }
|
||||
embedded-storage = { version = "0.3" }
|
||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||
rand_core = "0.6.4"
|
||||
@ -85,9 +85,9 @@ fixed = "1.23.1"
|
||||
rp-pac = { version = "6" }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2", optional = true}
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-rc.2", optional = true}
|
||||
|
||||
pio-proc = {version= "0.2" }
|
||||
pio = {version= "0.2.1" }
|
||||
|
@ -4,7 +4,6 @@
|
||||
any(feature = "ble", feature = "mac"),
|
||||
allow(stable_features, unknown_lints, async_fn_in_trait)
|
||||
)]
|
||||
#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))]
|
||||
|
||||
// This must go FIRST so that all the other modules see its macros.
|
||||
mod fmt;
|
||||
|
@ -42,9 +42,9 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona
|
||||
embassy-executor = { version = "0.3.3", path = "../embassy-executor", optional = true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2", optional = true}
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-rc.2", optional = true}
|
||||
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||
@ -58,14 +58,14 @@ rand_core = "0.6.3"
|
||||
sdio-host = "0.5.0"
|
||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||
critical-section = "1.1"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd" }
|
||||
vcell = "0.1.3"
|
||||
bxcan = "0.7.0"
|
||||
nb = "1.0.0"
|
||||
stm32-fmc = "0.3.0"
|
||||
cfg-if = "1.0.0"
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
embedded-io-async = { version = "0.6.1", optional = true }
|
||||
chrono = { version = "^0.4", default-features = false, optional = true}
|
||||
bit_field = "0.10.2"
|
||||
document-features = "0.2.7"
|
||||
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
||||
[build-dependencies]
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
|
@ -65,7 +65,6 @@ fn main() {
|
||||
match r.kind {
|
||||
// Generate singletons per pin, not per port
|
||||
"gpio" => {
|
||||
println!("{}", p.name);
|
||||
let port_letter = p.name.strip_prefix("GPIO").unwrap();
|
||||
for pin_num in 0..16 {
|
||||
singletons.push(format!("P{}{}", port_letter, pin_num));
|
||||
@ -997,8 +996,8 @@ fn main() {
|
||||
// SDMMCv1 uses the same channel for both directions, so just implement for RX
|
||||
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
|
||||
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
|
||||
(("dac", "CH1"), quote!(crate::dac::DmaCh1)),
|
||||
(("dac", "CH2"), quote!(crate::dac::DmaCh2)),
|
||||
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
|
||||
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
|
||||
]
|
||||
.into();
|
||||
|
||||
@ -1138,6 +1137,23 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// ========
|
||||
// Write peripheral_interrupts module.
|
||||
let mut mt = TokenStream::new();
|
||||
for p in METADATA.peripherals {
|
||||
let mut pt = TokenStream::new();
|
||||
|
||||
for irq in p.interrupts {
|
||||
let iname = format_ident!("{}", irq.interrupt);
|
||||
let sname = format_ident!("{}", irq.signal);
|
||||
pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;));
|
||||
}
|
||||
|
||||
let pname = format_ident!("{}", p.name);
|
||||
mt.extend(quote!(pub mod #pname { #pt }));
|
||||
}
|
||||
g.extend(quote!(#[allow(non_camel_case_types)] pub mod peripheral_interrupts { #mt }));
|
||||
|
||||
// ========
|
||||
// Write foreach_foo! macrotables
|
||||
|
||||
@ -1296,6 +1312,9 @@ fn main() {
|
||||
|
||||
let mut m = String::new();
|
||||
|
||||
// DO NOT ADD more macros like these.
|
||||
// These turned to be a bad idea!
|
||||
// Instead, make build.rs generate the final code.
|
||||
make_table(&mut m, "foreach_flash_region", &flash_regions_table);
|
||||
make_table(&mut m, "foreach_interrupt", &interrupts_table);
|
||||
make_table(&mut m, "foreach_peripheral", &peripherals_table);
|
||||
@ -1332,15 +1351,6 @@ fn main() {
|
||||
|
||||
if let Some(core) = core_name {
|
||||
println!("cargo:rustc-cfg={}_{}", &chip_name[..chip_name.len() - 2], core);
|
||||
} else {
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);
|
||||
}
|
||||
|
||||
// ========
|
||||
// stm32f3 wildcard features used in RCC
|
||||
|
||||
if chip_name.starts_with("stm32f3") {
|
||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]);
|
||||
}
|
||||
|
||||
// =======
|
||||
@ -1355,16 +1365,25 @@ fn main() {
|
||||
if &chip_name[..8] == "stm32wba" {
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..8]); // stm32wba
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..10]); // stm32wba52
|
||||
println!("cargo:rustc-cfg=package_{}", &chip_name[10..11]);
|
||||
println!("cargo:rustc-cfg=flashsize_{}", &chip_name[11..12]);
|
||||
} else {
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429
|
||||
println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
|
||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
|
||||
println!("cargo:rustc-cfg=package_{}", &chip_name[9..10]);
|
||||
println!("cargo:rustc-cfg=flashsize_{}", &chip_name[10..11]);
|
||||
}
|
||||
|
||||
// Handle time-driver-XXXX features.
|
||||
if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {}
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);
|
||||
// Mark the L4+ chips as they have many differences to regular L4.
|
||||
if &chip_name[..7] == "stm32l4" {
|
||||
if "pqrs".contains(&chip_name[7..8]) {
|
||||
println!("cargo:rustc-cfg=stm32l4_plus");
|
||||
} else {
|
||||
println!("cargo:rustc-cfg=stm32l4_nonplus");
|
||||
}
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
|
@ -1,136 +1,66 @@
|
||||
//! Provide access to the STM32 digital-to-analog converter (DAC).
|
||||
#![macro_use]
|
||||
|
||||
//! Provide access to the STM32 digital-to-analog converter (DAC).
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::dma::NoDma;
|
||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||
use crate::pac::dac;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
mod tsel;
|
||||
pub use tsel::TriggerSel;
|
||||
|
||||
/// Operating mode for DAC channel
|
||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Custom Errors
|
||||
pub enum Error {
|
||||
UnconfiguredChannel,
|
||||
InvalidValue,
|
||||
pub enum Mode {
|
||||
/// Normal mode, channel is connected to external pin with buffer enabled.
|
||||
NormalExternalBuffered,
|
||||
/// Normal mode, channel is connected to external pin and internal peripherals
|
||||
/// with buffer enabled.
|
||||
NormalBothBuffered,
|
||||
/// Normal mode, channel is connected to external pin with buffer disabled.
|
||||
NormalExternalUnbuffered,
|
||||
/// Normal mode, channel is connected to internal peripherals with buffer disabled.
|
||||
NormalInternalUnbuffered,
|
||||
/// Sample-and-hold mode, channel is connected to external pin with buffer enabled.
|
||||
SampleHoldExternalBuffered,
|
||||
/// Sample-and-hold mode, channel is connected to external pin and internal peripherals
|
||||
/// with buffer enabled.
|
||||
SampleHoldBothBuffered,
|
||||
/// Sample-and-hold mode, channel is connected to external pin and internal peripherals
|
||||
/// with buffer disabled.
|
||||
SampleHoldBothUnbuffered,
|
||||
/// Sample-and-hold mode, channel is connected to internal peripherals with buffer disabled.
|
||||
SampleHoldInternalUnbuffered,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// DAC Channels
|
||||
pub enum Channel {
|
||||
Ch1,
|
||||
Ch2,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
const fn index(&self) -> usize {
|
||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
|
||||
impl Mode {
|
||||
fn mode(&self) -> dac::vals::Mode {
|
||||
match self {
|
||||
Channel::Ch1 => 0,
|
||||
Channel::Ch2 => 1,
|
||||
Mode::NormalExternalBuffered => dac::vals::Mode::NORMAL_EXT_BUFEN,
|
||||
Mode::NormalBothBuffered => dac::vals::Mode::NORMAL_EXT_INT_BUFEN,
|
||||
Mode::NormalExternalUnbuffered => dac::vals::Mode::NORMAL_EXT_BUFDIS,
|
||||
Mode::NormalInternalUnbuffered => dac::vals::Mode::NORMAL_INT_BUFDIS,
|
||||
Mode::SampleHoldExternalBuffered => dac::vals::Mode::SAMPHOLD_EXT_BUFEN,
|
||||
Mode::SampleHoldBothBuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFEN,
|
||||
Mode::SampleHoldBothUnbuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFDIS,
|
||||
Mode::SampleHoldInternalUnbuffered => dac::vals::Mode::SAMPHOLD_INT_BUFDIS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Trigger sources for CH1
|
||||
pub enum Ch1Trigger {
|
||||
#[cfg(dac_v3)]
|
||||
Tim1,
|
||||
Tim2,
|
||||
#[cfg(not(dac_v3))]
|
||||
Tim3,
|
||||
#[cfg(dac_v3)]
|
||||
Tim4,
|
||||
#[cfg(dac_v3)]
|
||||
Tim5,
|
||||
Tim6,
|
||||
Tim7,
|
||||
#[cfg(dac_v3)]
|
||||
Tim8,
|
||||
Tim15,
|
||||
#[cfg(dac_v3)]
|
||||
Hrtim1Dactrg1,
|
||||
#[cfg(dac_v3)]
|
||||
Hrtim1Dactrg2,
|
||||
#[cfg(dac_v3)]
|
||||
Lptim1,
|
||||
#[cfg(dac_v3)]
|
||||
Lptim2,
|
||||
#[cfg(dac_v3)]
|
||||
Lptim3,
|
||||
Exti9,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl Ch1Trigger {
|
||||
fn tsel(&self) -> dac::vals::Tsel1 {
|
||||
match self {
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Tim1 => dac::vals::Tsel1::TIM1_TRGO,
|
||||
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
|
||||
#[cfg(not(dac_v3))]
|
||||
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Tim4 => dac::vals::Tsel1::TIM4_TRGO,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Tim5 => dac::vals::Tsel1::TIM5_TRGO,
|
||||
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
||||
Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Tim8 => dac::vals::Tsel1::TIM8_TRGO,
|
||||
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Hrtim1Dactrg1 => dac::vals::Tsel1::HRTIM1_DACTRG1,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Hrtim1Dactrg2 => dac::vals::Tsel1::HRTIM1_DACTRG2,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Lptim1 => dac::vals::Tsel1::LPTIM1_OUT,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Lptim2 => dac::vals::Tsel1::LPTIM2_OUT,
|
||||
#[cfg(dac_v3)]
|
||||
Ch1Trigger::Lptim3 => dac::vals::Tsel1::LPTIM3_OUT,
|
||||
Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
|
||||
Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Trigger sources for CH2
|
||||
pub enum Ch2Trigger {
|
||||
Tim6,
|
||||
Tim8,
|
||||
Tim7,
|
||||
Tim5,
|
||||
Tim2,
|
||||
Tim4,
|
||||
Exti9,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl Ch2Trigger {
|
||||
fn tsel(&self) -> dac::vals::Tsel2 {
|
||||
match self {
|
||||
Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
|
||||
Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
|
||||
Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
|
||||
Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
|
||||
Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
|
||||
Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
|
||||
Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
|
||||
Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Single 8 or 12 bit value that can be output by the DAC
|
||||
/// Single 8 or 12 bit value that can be output by the DAC.
|
||||
///
|
||||
/// 12-bit values outside the permitted range are silently truncated.
|
||||
pub enum Value {
|
||||
// 8 bit value
|
||||
Bit8(u8),
|
||||
@ -142,7 +72,21 @@ pub enum Value {
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Array variant of [`Value`]
|
||||
/// Dual 8 or 12 bit values that can be output by the DAC channels 1 and 2 simultaneously.
|
||||
///
|
||||
/// 12-bit values outside the permitted range are silently truncated.
|
||||
pub enum DualValue {
|
||||
// 8 bit value
|
||||
Bit8(u8, u8),
|
||||
// 12 bit value stored in a u16, left-aligned
|
||||
Bit12Left(u16, u16),
|
||||
// 12 bit value stored in a u16, right-aligned
|
||||
Bit12Right(u16, u16),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Array variant of [`Value`].
|
||||
pub enum ValueArray<'a> {
|
||||
// 8 bit values
|
||||
Bit8(&'a [u8]),
|
||||
@ -151,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 {
|
||||
|
282
embassy-stm32/src/dac/tsel.rs
Normal file
282
embassy-stm32/src/dac/tsel.rs
Normal file
@ -0,0 +1,282 @@
|
||||
/// Trigger selection for STM32F0.
|
||||
#[cfg(stm32f0)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim3 = 1,
|
||||
Tim7 = 2,
|
||||
Tim15 = 3,
|
||||
Tim2 = 4,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F1.
|
||||
#[cfg(stm32f1)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
#[cfg(any(stm32f100, stm32f105, stm32f107))]
|
||||
Tim3 = 1,
|
||||
#[cfg(any(stm32f101, stm32f103))]
|
||||
Tim8 = 1,
|
||||
Tim7 = 2,
|
||||
#[cfg(any(stm32f101, stm32f103, stm32f105, stm32f107))]
|
||||
Tim5 = 3,
|
||||
#[cfg(all(stm32f100, any(flashsize_4, flashsize_6, flashsize_8, flashsize_b)))]
|
||||
Tim15 = 3,
|
||||
#[cfg(all(stm32f100, any(flashsize_c, flashsize_d, flashsize_e)))]
|
||||
/// Can be remapped to TIM15 with MISC_REMAP in AFIO_MAPR2.
|
||||
Tim5Or15 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F2/F4/F7/L4, except F410 or L4+.
|
||||
#[cfg(all(any(stm32f2, stm32f4, stm32f7, stm32l4_nonplus), not(stm32f410)))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim8 = 1,
|
||||
#[cfg(not(any(stm32l45x, stm32l46x)))]
|
||||
Tim7 = 2,
|
||||
Tim5 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F410.
|
||||
#[cfg(stm32f410)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim5 = 3,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F301/2 and 318.
|
||||
#[cfg(any(stm32f301, stm32f302, stm32f318))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
#[cfg(stm32f302)]
|
||||
/// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1.
|
||||
Tim3 = 1,
|
||||
Tim15 = 3,
|
||||
Tim2 = 4,
|
||||
#[cfg(all(stm32f302, any(flashsize_6, flashsize_8)))]
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F303/3x8 (excluding 318 which is like 301, and 378 which is 37x).
|
||||
#[cfg(any(stm32f303, stm32f328, stm32f358, stm32f398))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
/// * DAC1: defaults to TIM8 but can be remapped to TIM3 with DAC_TRIG_RMP in SYSCFG_CFGR1
|
||||
/// * DAC2: always TIM3
|
||||
Tim8Or3 = 1,
|
||||
Tim7 = 2,
|
||||
Tim15 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F37x.
|
||||
#[cfg(any(stm32f373, stm32f378))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim3 = 1,
|
||||
Tim7 = 2,
|
||||
/// TIM5 on DAC1, TIM18 on DAC2
|
||||
Dac1Tim5Dac2Tim18 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32F334.
|
||||
#[cfg(stm32f334)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
/// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1.
|
||||
Tim3 = 1,
|
||||
Tim7 = 2,
|
||||
/// Can be remapped to HRTIM_DACTRG1 using DAC1_TRIG3_RMP in SYSCFG_CFGR3.
|
||||
Tim15OrHrtimDacTrg1 = 3,
|
||||
Tim2 = 4,
|
||||
/// Requires DAC_TRIG5_RMP set in SYSCFG_CFGR3.
|
||||
HrtimDacTrg2 = 5,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32L0.
|
||||
#[cfg(stm32l0)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim3 = 1,
|
||||
Tim3Ch3 = 2,
|
||||
Tim21 = 3,
|
||||
Tim2 = 4,
|
||||
Tim7 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for STM32L1.
|
||||
#[cfg(stm32l1)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Tim6 = 0,
|
||||
Tim7 = 2,
|
||||
Tim9 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Software = 7,
|
||||
}
|
||||
|
||||
/// Trigger selection for L4+, L5, U5, H7.
|
||||
#[cfg(any(stm32l4_plus, stm32l5, stm32u5, stm32h7))]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
Tim1 = 1,
|
||||
Tim2 = 2,
|
||||
Tim4 = 3,
|
||||
Tim5 = 4,
|
||||
Tim6 = 5,
|
||||
Tim7 = 6,
|
||||
Tim8 = 7,
|
||||
Tim15 = 8,
|
||||
#[cfg(all(stm32h7, hrtim))]
|
||||
Hrtim1DacTrg1 = 9,
|
||||
#[cfg(all(stm32h7, hrtim))]
|
||||
Hrtim1DacTrg2 = 10,
|
||||
Lptim1 = 11,
|
||||
#[cfg(not(stm32u5))]
|
||||
Lptim2 = 12,
|
||||
#[cfg(stm32u5)]
|
||||
Lptim3 = 12,
|
||||
Exti9 = 13,
|
||||
#[cfg(any(stm32h7ax, stm32h7bx))]
|
||||
/// RM0455 suggests this might be LPTIM2 on DAC1 and LPTIM3 on DAC2,
|
||||
/// but it's probably wrong. Please let us know if you find out.
|
||||
Lptim3 = 14,
|
||||
#[cfg(any(stm32h72x, stm32h73x))]
|
||||
Tim23 = 14,
|
||||
#[cfg(any(stm32h72x, stm32h73x))]
|
||||
Tim24 = 15,
|
||||
}
|
||||
|
||||
/// Trigger selection for H5.
|
||||
#[cfg(stm32h5)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
Tim1 = 1,
|
||||
Tim2 = 2,
|
||||
#[cfg(any(stm32h56x, stm32h57x))]
|
||||
Tim4 = 3,
|
||||
#[cfg(stm32h503)]
|
||||
Tim3 = 3,
|
||||
#[cfg(any(stm32h56x, stm32h57x))]
|
||||
Tim5 = 4,
|
||||
Tim6 = 5,
|
||||
Tim7 = 6,
|
||||
#[cfg(any(stm32h56x, stm32h57x))]
|
||||
Tim8 = 7,
|
||||
#[cfg(any(stm32h56x, stm32h57x))]
|
||||
Tim15 = 8,
|
||||
Lptim1 = 11,
|
||||
Lptim2 = 12,
|
||||
Exti9 = 13,
|
||||
}
|
||||
|
||||
/// Trigger selection for G0.
|
||||
#[cfg(stm32g0)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
Tim1 = 1,
|
||||
Tim2 = 2,
|
||||
Tim3 = 3,
|
||||
Tim6 = 5,
|
||||
Tim7 = 6,
|
||||
Tim15 = 8,
|
||||
Lptim1 = 11,
|
||||
Lptim2 = 12,
|
||||
Exti9 = 13,
|
||||
}
|
||||
|
||||
/// Trigger selection for G4.
|
||||
#[cfg(stm32g4)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
/// * DAC1, DAC2, DAC4: TIM8
|
||||
/// * DAC3: TIM1
|
||||
Dac124Tim8Dac3Tim1 = 1,
|
||||
Tim7 = 2,
|
||||
Tim15 = 3,
|
||||
Tim2 = 4,
|
||||
Tim4 = 5,
|
||||
Exti9 = 6,
|
||||
Tim6 = 7,
|
||||
Tim3 = 8,
|
||||
HrtimDacRstTrg1 = 9,
|
||||
HrtimDacRstTrg2 = 10,
|
||||
HrtimDacRstTrg3 = 11,
|
||||
HrtimDacRstTrg4 = 12,
|
||||
HrtimDacRstTrg5 = 13,
|
||||
HrtimDacRstTrg6 = 14,
|
||||
/// * DAC1, DAC4: HRTIM_DAC_TRG1
|
||||
/// * DAC2: HRTIM_DAC_TRG2
|
||||
/// * DAC3: HRTIM_DAC_TRG3
|
||||
HrtimDacTrg123 = 15,
|
||||
}
|
||||
|
||||
/// Trigger selection for WL.
|
||||
#[cfg(stm32wl)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TriggerSel {
|
||||
Software = 0,
|
||||
Tim1 = 1,
|
||||
Tim2 = 2,
|
||||
Lptim1 = 11,
|
||||
Lptim2 = 12,
|
||||
Lptim3 = 13,
|
||||
Exti9 = 14,
|
||||
}
|
||||
|
||||
impl TriggerSel {
|
||||
pub fn tsel(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::interrupt;
|
||||
|
||||
#[cfg_attr(i2c_v1, path = "v1.rs")]
|
||||
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::peripherals;
|
||||
|
||||
@ -23,6 +26,20 @@ pub enum Error {
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub struct State {
|
||||
#[allow(unused)]
|
||||
pub waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: crate::rcc::RccPeripheral {
|
||||
fn regs() -> crate::pac::i2c::I2c;
|
||||
fn state() -> &'static State;
|
||||
@ -30,7 +47,8 @@ pub(crate) mod sealed {
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + 'static {
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
type EventInterrupt: interrupt::typelevel::Interrupt;
|
||||
type ErrorInterrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
pin_trait!(SclPin, Instance);
|
||||
@ -38,21 +56,148 @@ pin_trait!(SdaPin, Instance);
|
||||
dma_trait!(RxDma, Instance);
|
||||
dma_trait!(TxDma, Instance);
|
||||
|
||||
foreach_interrupt!(
|
||||
($inst:ident, i2c, $block:ident, EV, $irq:ident) => {
|
||||
/// Interrupt handler.
|
||||
pub struct EventInterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
_version::on_interrupt::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ErrorInterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::ErrorInterrupt> for ErrorInterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
_version::on_interrupt::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
foreach_peripheral!(
|
||||
(i2c, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::i2c::I2c {
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
||||
fn state() -> &'static State {
|
||||
static STATE: State = State::new();
|
||||
fn state() -> &'static sealed::State {
|
||||
static STATE: sealed::State = sealed::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV;
|
||||
type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
mod eh02 {
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(address, write)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(address, write, read)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-traits")]
|
||||
mod eh1 {
|
||||
use super::*;
|
||||
use crate::dma::NoDma;
|
||||
|
||||
impl embedded_hal_1::i2c::Error for Error {
|
||||
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
|
||||
match *self {
|
||||
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
|
||||
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
|
||||
Self::Nack => {
|
||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
|
||||
}
|
||||
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
|
||||
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
|
||||
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, read)
|
||||
}
|
||||
|
||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(address, write)
|
||||
}
|
||||
|
||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(address, write, read)
|
||||
}
|
||||
|
||||
fn transaction(
|
||||
&mut self,
|
||||
_address: u8,
|
||||
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> {
|
||||
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read(address, read).await
|
||||
}
|
||||
|
||||
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.write(address, write).await
|
||||
}
|
||||
|
||||
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.write_read(address, write, read).await
|
||||
}
|
||||
|
||||
async fn transaction(
|
||||
&mut self,
|
||||
address: u8,
|
||||
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
let _ = address;
|
||||
let _ = operations;
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,33 @@
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_futures::select::{select, Either};
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::dma::NoDma;
|
||||
use super::*;
|
||||
use crate::dma::{NoDma, Transfer};
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Pull;
|
||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::pac::i2c;
|
||||
use crate::time::Hertz;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {}
|
||||
pub unsafe fn on_interrupt<T: Instance>() {
|
||||
let regs = T::regs();
|
||||
// i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of
|
||||
// other stuff, so we wake the task on every interrupt.
|
||||
T::state().waker.wake();
|
||||
critical_section::with(|_| {
|
||||
// Clear event interrupt flag.
|
||||
regs.cr2().modify(|w| {
|
||||
w.set_itevten(false);
|
||||
w.set_iterren(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
@ -27,14 +37,6 @@ pub struct Config {
|
||||
pub scl_pullup: bool,
|
||||
}
|
||||
|
||||
pub struct State {}
|
||||
|
||||
impl State {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
#[allow(dead_code)]
|
||||
@ -48,7 +50,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
|
||||
+ 'd,
|
||||
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||||
freq: Hertz,
|
||||
@ -98,6 +102,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
reg.set_pe(true);
|
||||
});
|
||||
|
||||
unsafe { T::EventInterrupt::enable() };
|
||||
unsafe { T::ErrorInterrupt::enable() };
|
||||
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
tx_dma,
|
||||
@ -105,40 +112,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
|
||||
fn check_and_clear_error_flags() -> Result<i2c::regs::Sr1, Error> {
|
||||
// Note that flags should only be cleared once they have been registered. If flags are
|
||||
// cleared otherwise, there may be an inherent race condition and flags may be missed.
|
||||
let sr1 = T::regs().sr1().read();
|
||||
|
||||
if sr1.timeout() {
|
||||
T::regs().sr1().modify(|reg| reg.set_timeout(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_timeout(false);
|
||||
});
|
||||
return Err(Error::Timeout);
|
||||
}
|
||||
|
||||
if sr1.pecerr() {
|
||||
T::regs().sr1().modify(|reg| reg.set_pecerr(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_pecerr(false);
|
||||
});
|
||||
return Err(Error::Crc);
|
||||
}
|
||||
|
||||
if sr1.ovr() {
|
||||
T::regs().sr1().modify(|reg| reg.set_ovr(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_ovr(false);
|
||||
});
|
||||
return Err(Error::Overrun);
|
||||
}
|
||||
|
||||
if sr1.af() {
|
||||
T::regs().sr1().modify(|reg| reg.set_af(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_af(false);
|
||||
});
|
||||
return Err(Error::Nack);
|
||||
}
|
||||
|
||||
if sr1.arlo() {
|
||||
T::regs().sr1().modify(|reg| reg.set_arlo(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_arlo(false);
|
||||
});
|
||||
return Err(Error::Arbitration);
|
||||
}
|
||||
|
||||
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
||||
// clearing the BERR bit instead.
|
||||
if sr1.berr() {
|
||||
T::regs().sr1().modify(|reg| reg.set_berr(false));
|
||||
T::regs().sr1().write(|reg| {
|
||||
reg.0 = !0;
|
||||
reg.set_berr(false);
|
||||
});
|
||||
}
|
||||
|
||||
Ok(sr1)
|
||||
@ -157,13 +182,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
});
|
||||
|
||||
// Wait until START condition was generated
|
||||
while !self.check_and_clear_error_flags()?.start() {
|
||||
while !Self::check_and_clear_error_flags()?.start() {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
// Also wait until signalled we're master and everything is waiting for us
|
||||
while {
|
||||
self.check_and_clear_error_flags()?;
|
||||
Self::check_and_clear_error_flags()?;
|
||||
|
||||
let sr2 = T::regs().sr2().read();
|
||||
!sr2.msl() && !sr2.busy()
|
||||
@ -177,7 +202,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
// Wait until address was sent
|
||||
// Wait for the address to be acknowledged
|
||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||||
while !self.check_and_clear_error_flags()?.addr() {
|
||||
while !Self::check_and_clear_error_flags()?.addr() {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
@ -197,7 +222,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
// Wait until we're ready for sending
|
||||
while {
|
||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||||
!self.check_and_clear_error_flags()?.txe()
|
||||
!Self::check_and_clear_error_flags()?.txe()
|
||||
} {
|
||||
check_timeout()?;
|
||||
}
|
||||
@ -208,7 +233,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
// Wait until byte is transferred
|
||||
while {
|
||||
// Check for any potential error conditions.
|
||||
!self.check_and_clear_error_flags()?.btf()
|
||||
!Self::check_and_clear_error_flags()?.btf()
|
||||
} {
|
||||
check_timeout()?;
|
||||
}
|
||||
@ -219,7 +244,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
|
||||
while {
|
||||
// Check for any potential error conditions.
|
||||
self.check_and_clear_error_flags()?;
|
||||
Self::check_and_clear_error_flags()?;
|
||||
|
||||
!T::regs().sr1().read().rxne()
|
||||
} {
|
||||
@ -244,7 +269,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
});
|
||||
|
||||
// Wait until START condition was generated
|
||||
while !self.check_and_clear_error_flags()?.start() {
|
||||
while !Self::check_and_clear_error_flags()?.start() {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
@ -261,7 +286,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
|
||||
// Wait until address was sent
|
||||
// Wait for the address to be acknowledged
|
||||
while !self.check_and_clear_error_flags()?.addr() {
|
||||
while !Self::check_and_clear_error_flags()?.addr() {
|
||||
check_timeout()?;
|
||||
}
|
||||
|
||||
@ -336,6 +361,356 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
||||
self.blocking_write_read_timeout(addr, write, read, || Ok(()))
|
||||
}
|
||||
|
||||
// Async
|
||||
|
||||
#[inline] // pretty sure this should always be inlined
|
||||
fn enable_interrupts() -> () {
|
||||
T::regs().cr2().modify(|w| {
|
||||
w.set_iterren(true);
|
||||
w.set_itevten(true);
|
||||
});
|
||||
}
|
||||
|
||||
async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error>
|
||||
where
|
||||
TXDMA: crate::i2c::TxDma<T>,
|
||||
{
|
||||
let dma_transfer = unsafe {
|
||||
let regs = T::regs();
|
||||
regs.cr2().modify(|w| {
|
||||
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
|
||||
w.set_dmaen(true);
|
||||
w.set_itbufen(false);
|
||||
});
|
||||
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
|
||||
let dst = regs.dr().as_ptr() as *mut u8;
|
||||
|
||||
let ch = &mut self.tx_dma;
|
||||
let request = ch.request();
|
||||
Transfer::new_write(ch, request, write, dst, Default::default())
|
||||
};
|
||||
|
||||
let on_drop = OnDrop::new(|| {
|
||||
let regs = T::regs();
|
||||
regs.cr2().modify(|w| {
|
||||
w.set_dmaen(false);
|
||||
w.set_iterren(false);
|
||||
w.set_itevten(false);
|
||||
})
|
||||
});
|
||||
|
||||
Self::enable_interrupts();
|
||||
|
||||
// Send a START condition
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_start(true);
|
||||
});
|
||||
|
||||
let state = T::state();
|
||||
|
||||
// Wait until START condition was generated
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
match Self::check_and_clear_error_flags() {
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
Ok(sr1) => {
|
||||
if sr1.start() {
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Also wait until signalled we're master and everything is waiting for us
|
||||
Self::enable_interrupts();
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
match Self::check_and_clear_error_flags() {
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
Ok(_) => {
|
||||
let sr2 = T::regs().sr2().read();
|
||||
if !sr2.msl() && !sr2.busy() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Set up current address, we're trying to talk to
|
||||
Self::enable_interrupts();
|
||||
T::regs().dr().write(|reg| reg.set_dr(address << 1));
|
||||
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
match Self::check_and_clear_error_flags() {
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
Ok(sr1) => {
|
||||
if sr1.addr() {
|
||||
// Clear the ADDR condition by reading SR2.
|
||||
T::regs().sr2().read();
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
Self::enable_interrupts();
|
||||
let poll_error = poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
match Self::check_and_clear_error_flags() {
|
||||
// Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other
|
||||
// identical poll_fn check_and_clear matches.
|
||||
Err(e) => Poll::Ready(Err::<T, Error>(e)),
|
||||
Ok(_) => Poll::Pending,
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for either the DMA transfer to successfully finish, or an I2C error to occur.
|
||||
match select(dma_transfer, poll_error).await {
|
||||
Either::Second(Err(e)) => Err(e),
|
||||
_ => Ok(()),
|
||||
}?;
|
||||
|
||||
// The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too.
|
||||
|
||||
// 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA
|
||||
// requests then wait for a BTF event before programming the Stop condition.”
|
||||
|
||||
// TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it?
|
||||
T::regs().cr2().modify(|w| {
|
||||
w.set_dmaen(false);
|
||||
});
|
||||
|
||||
Self::enable_interrupts();
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
match Self::check_and_clear_error_flags() {
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
Ok(sr1) => {
|
||||
if sr1.btf() {
|
||||
if send_stop {
|
||||
T::regs().cr1().modify(|w| {
|
||||
w.set_stop(true);
|
||||
});
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
drop(on_drop);
|
||||
|
||||
// Fallthrough is success
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
|
||||
where
|
||||
TXDMA: crate::i2c::TxDma<T>,
|
||||
{
|
||||
self.write_with_stop(address, write, true).await?;
|
||||
|
||||
// Wait for STOP condition to transmit.
|
||||
Self::enable_interrupts();
|
||||
poll_fn(|cx| {
|
||||
T::state().waker.register(cx.waker());
|
||||
// TODO: error interrupts are enabled here, should we additional check for and return errors?
|
||||
if T::regs().cr1().read().stop() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
||||
where
|
||||
RXDMA: crate::i2c::RxDma<T>,
|
||||
{
|
||||
let state = T::state();
|
||||
let buffer_len = buffer.len();
|
||||
|
||||
let dma_transfer = unsafe {
|
||||
let regs = T::regs();
|
||||
regs.cr2().modify(|w| {
|
||||
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
|
||||
w.set_itbufen(false);
|
||||
w.set_dmaen(true);
|
||||
});
|
||||
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
|
||||
let src = regs.dr().as_ptr() as *mut u8;
|
||||
|
||||
let ch = &mut self.rx_dma;
|
||||
let request = ch.request();
|
||||
Transfer::new_read(ch, request, src, buffer, Default::default())
|
||||
};
|
||||
|
||||
let on_drop = OnDrop::new(|| {
|
||||
let regs = T::regs();
|
||||
regs.cr2().modify(|w| {
|
||||
w.set_dmaen(false);
|
||||
w.set_iterren(false);
|
||||
w.set_itevten(false);
|
||||
})
|
||||
});
|
||||
|
||||
Self::enable_interrupts();
|
||||
|
||||
// Send a START condition and set ACK bit
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_start(true);
|
||||
reg.set_ack(true);
|
||||
});
|
||||
|
||||
// Wait until START condition was generated
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
match Self::check_and_clear_error_flags() {
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
Ok(sr1) => {
|
||||
if sr1.start() {
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Also wait until signalled we're master and everything is waiting for us
|
||||
Self::enable_interrupts();
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
// blocking read didn’t have a check_and_clear call here, but blocking write did so
|
||||
// I’m adding it here in case that was an oversight.
|
||||
match Self::check_and_clear_error_flags() {
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
Ok(_) => {
|
||||
let sr2 = T::regs().sr2().read();
|
||||
if !sr2.msl() && !sr2.busy() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Set up current address, we're trying to talk to
|
||||
T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1));
|
||||
|
||||
// Wait for the address to be acknowledged
|
||||
|
||||
Self::enable_interrupts();
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
match Self::check_and_clear_error_flags() {
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
Ok(sr1) => {
|
||||
if sr1.addr() {
|
||||
// 18.3.8: When a single byte must be received: the NACK must be programmed during EV6
|
||||
// event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag.
|
||||
if buffer_len == 1 {
|
||||
T::regs().cr1().modify(|w| {
|
||||
w.set_ack(false);
|
||||
});
|
||||
}
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Clear ADDR condition by reading SR2
|
||||
T::regs().sr2().read();
|
||||
|
||||
// 18.3.8: When a single byte must be received: [snip] Then the
|
||||
// user can program the STOP condition either after clearing ADDR flag, or in the
|
||||
// DMA Transfer Complete interrupt routine.
|
||||
if buffer_len == 1 {
|
||||
T::regs().cr1().modify(|w| {
|
||||
w.set_stop(true);
|
||||
});
|
||||
} else {
|
||||
// If, in the I2C_CR2 register, the LAST bit is set, I2C
|
||||
// automatically sends a NACK after the next byte following EOT_1. The user can
|
||||
// generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled.
|
||||
T::regs().cr2().modify(|w| {
|
||||
w.set_last(true);
|
||||
})
|
||||
}
|
||||
|
||||
// Wait for bytes to be received, or an error to occur.
|
||||
Self::enable_interrupts();
|
||||
let poll_error = poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
match Self::check_and_clear_error_flags() {
|
||||
Err(e) => Poll::Ready(Err::<T, Error>(e)),
|
||||
_ => Poll::Pending,
|
||||
}
|
||||
});
|
||||
|
||||
match select(dma_transfer, poll_error).await {
|
||||
Either::Second(Err(e)) => Err(e),
|
||||
_ => Ok(()),
|
||||
}?;
|
||||
|
||||
// Wait for the STOP to be sent (STOP bit cleared).
|
||||
Self::enable_interrupts();
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
// TODO: error interrupts are enabled here, should we additional check for and return errors?
|
||||
if T::regs().cr1().read().stop() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
drop(on_drop);
|
||||
|
||||
// Fallthrough is success
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
|
||||
where
|
||||
RXDMA: crate::i2c::RxDma<T>,
|
||||
TXDMA: crate::i2c::TxDma<T>,
|
||||
{
|
||||
self.write_with_stop(address, write, false).await?;
|
||||
self.read(address, read).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
||||
@ -344,77 +719,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(addr, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(addr, write)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(addr, write, read)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-traits")]
|
||||
mod eh1 {
|
||||
use super::*;
|
||||
|
||||
impl embedded_hal_1::i2c::Error for Error {
|
||||
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
|
||||
match *self {
|
||||
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
|
||||
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
|
||||
Self::Nack => {
|
||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
|
||||
}
|
||||
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
|
||||
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> {
|
||||
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, read)
|
||||
}
|
||||
|
||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(address, write)
|
||||
}
|
||||
|
||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(address, write, read)
|
||||
}
|
||||
|
||||
fn transaction(
|
||||
&mut self,
|
||||
_address: u8,
|
||||
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
Fast,
|
||||
Standard,
|
||||
|
@ -1,19 +1,17 @@
|
||||
use core::cmp;
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Instant};
|
||||
|
||||
use super::*;
|
||||
use crate::dma::{NoDma, Transfer};
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Pull;
|
||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::pac::i2c;
|
||||
use crate::time::Hertz;
|
||||
@ -36,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 = ();
|
||||
|
@ -346,10 +346,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
|
||||
None => {
|
||||
cfg_if::cfg_if! {
|
||||
// For some chips PREDIV is always two, and cannot be changed
|
||||
if #[cfg(any(
|
||||
stm32f302xd, stm32f302xe, stm32f303xd,
|
||||
stm32f303xe, stm32f398xe
|
||||
))] {
|
||||
if #[cfg(any(flashsize_d, flashsize_e))] {
|
||||
let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0);
|
||||
(
|
||||
Hertz((HSI_FREQ.0 / divisor) * multiplier),
|
||||
|
@ -1457,8 +1457,8 @@ cfg_if::cfg_if! {
|
||||
macro_rules! kernel_clk {
|
||||
($inst:ident) => {
|
||||
critical_section::with(|_| unsafe {
|
||||
crate::rcc::get_freqs().pll1_q
|
||||
}).expect("PLL48 is required for SDIO")
|
||||
unwrap!(crate::rcc::get_freqs().pll1_q)
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if #[cfg(stm32f7)] {
|
||||
@ -1469,7 +1469,7 @@ cfg_if::cfg_if! {
|
||||
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
|
||||
crate::rcc::get_freqs().sys
|
||||
} else {
|
||||
crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC")
|
||||
unwrap!(crate::rcc::get_freqs().pll1_q)
|
||||
}
|
||||
})
|
||||
};
|
||||
@ -1479,7 +1479,7 @@ cfg_if::cfg_if! {
|
||||
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
|
||||
crate::rcc::get_freqs().sys
|
||||
} else {
|
||||
crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC")
|
||||
unwrap!(crate::rcc::get_freqs().pll1_q)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
@ -35,7 +35,7 @@ futures-util = { version = "0.3.17", default-features = false }
|
||||
critical-section = "1.1"
|
||||
heapless = "0.8"
|
||||
cfg-if = "1.0.0"
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
embedded-io-async = { version = "0.6.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
futures-executor = { version = "0.3.17", features = [ "thread-pool" ] }
|
||||
|
@ -22,8 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## 0.1.3 - 2023-08-28
|
||||
|
||||
- Update `embedded-hal-async` to `1.0.0-rc.1`
|
||||
- Update `embedded-hal v1` to `1.0.0-rc.1`
|
||||
- Update `embedded-hal-async` to `1.0.0-rc.2`
|
||||
- Update `embedded-hal v1` to `1.0.0-rc.2`
|
||||
|
||||
## 0.1.2 - 2023-07-05
|
||||
|
||||
|
@ -242,8 +242,8 @@ defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2", optional = true}
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2", optional = true}
|
||||
|
||||
futures-util = { version = "0.3.17", default-features = false }
|
||||
critical-section = "1.1"
|
||||
|
@ -18,7 +18,11 @@ pub struct Delay;
|
||||
mod eh1 {
|
||||
use super::*;
|
||||
|
||||
impl embedded_hal_1::delay::DelayUs for Delay {
|
||||
impl embedded_hal_1::delay::DelayNs for Delay {
|
||||
fn delay_ns(&mut self, ns: u32) {
|
||||
block_for(Duration::from_nanos(ns as u64))
|
||||
}
|
||||
|
||||
fn delay_us(&mut self, us: u32) {
|
||||
block_for(Duration::from_micros(us as u64))
|
||||
}
|
||||
@ -34,13 +38,17 @@ mod eha {
|
||||
use super::*;
|
||||
use crate::Timer;
|
||||
|
||||
impl embedded_hal_async::delay::DelayUs for Delay {
|
||||
async fn delay_us(&mut self, micros: u32) {
|
||||
Timer::after_micros(micros as _).await
|
||||
impl embedded_hal_async::delay::DelayNs for Delay {
|
||||
async fn delay_ns(&mut self, ns: u32) {
|
||||
Timer::after_nanos(ns as _).await
|
||||
}
|
||||
|
||||
async fn delay_ms(&mut self, millis: u32) {
|
||||
Timer::after_millis(millis as _).await
|
||||
async fn delay_us(&mut self, us: u32) {
|
||||
Timer::after_micros(us as _).await
|
||||
}
|
||||
|
||||
async fn delay_ms(&mut self, ms: u32) {
|
||||
Timer::after_millis(ms as _).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
use super::{GCD_1K, GCD_1M, TICK_HZ};
|
||||
use crate::GCD_1G;
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
@ -61,6 +62,14 @@ impl Duration {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a duration from the specified number of nanoseconds, rounding up.
|
||||
/// NOTE: Delays this small may be inaccurate.
|
||||
pub const fn from_nanos(micros: u64) -> Duration {
|
||||
Duration {
|
||||
ticks: div_ceil(micros * (TICK_HZ / GCD_1G), 1_000_000_000 / GCD_1G),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a duration from the specified number of seconds, rounding down.
|
||||
pub const fn from_secs_floor(secs: u64) -> Duration {
|
||||
Duration { ticks: secs * TICK_HZ }
|
||||
|
@ -52,6 +52,7 @@ const fn gcd(a: u64, b: u64) -> u64 {
|
||||
|
||||
pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000);
|
||||
pub(crate) const GCD_1M: u64 = gcd(TICK_HZ, 1_000_000);
|
||||
pub(crate) const GCD_1G: u64 = gcd(TICK_HZ, 1_000_000_000);
|
||||
|
||||
#[cfg(feature = "defmt-timestamp-uptime")]
|
||||
defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() }
|
||||
|
@ -74,6 +74,15 @@ impl Timer {
|
||||
Self::after(Duration::from_ticks(ticks))
|
||||
}
|
||||
|
||||
/// Expire after the specified number of nanoseconds.
|
||||
///
|
||||
/// This method is a convenience wrapper for calling `Timer::after(Duration::from_nanos())`.
|
||||
/// For more details, refer to [`Timer::after()`] and [`Duration::from_nanos()`].
|
||||
#[inline]
|
||||
pub fn after_nanos(nanos: u64) -> Self {
|
||||
Self::after(Duration::from_nanos(nanos))
|
||||
}
|
||||
|
||||
/// Expire after the specified number of microseconds.
|
||||
///
|
||||
/// This method is a convenience wrapper for calling `Timer::after(Duration::from_micros())`.
|
||||
|
@ -4,7 +4,7 @@ This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can im
|
||||
traits to add support for using `embassy-usb` for a given chip/platform.
|
||||
|
||||
The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`]
|
||||
APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used
|
||||
APIs don't cause a semver-major bump of this crate. This allows existing HALs/BSPs to be used
|
||||
with the newer `embassy-usb` without needing updates.
|
||||
|
||||
If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate
|
||||
|
@ -21,10 +21,6 @@ nightly = [
|
||||
"embedded-io-async",
|
||||
"embedded-hal-bus/async",
|
||||
"embassy-net",
|
||||
"embassy-lora",
|
||||
"lora-phy",
|
||||
"lorawan-device",
|
||||
"lorawan",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
@ -36,11 +32,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm
|
||||
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
|
||||
embedded-io = { version = "0.6.0", features = ["defmt-03"] }
|
||||
embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] }
|
||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true }
|
||||
lora-phy = { version = "2", optional = true }
|
||||
lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
|
||||
lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"], optional = true }
|
||||
embedded-io-async = { version = "0.6.1", optional = true, features = ["defmt-03"] }
|
||||
embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true }
|
||||
embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true }
|
||||
|
||||
@ -57,9 +49,9 @@ rand = { version = "0.8.4", default-features = false }
|
||||
embedded-storage = "0.3.0"
|
||||
usbd-hid = "0.6.0"
|
||||
serde = { version = "1.0.136", default-features = false }
|
||||
embedded-hal = { version = "1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "1.0.0-rc.1", optional = true }
|
||||
embedded-hal-bus = { version = "0.1.0-rc.1" }
|
||||
embedded-hal = { version = "1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "1.0.0-rc.2", optional = true }
|
||||
embedded-hal-bus = { version = "0.1.0-rc.2" }
|
||||
num-integer = { version = "0.1.45", default-features = false }
|
||||
microfft = "0.5.0"
|
||||
|
||||
|
@ -1,97 +0,0 @@
|
||||
//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
|
||||
//! Other nrf/sx126x combinations may work with appropriate pin modifications.
|
||||
//! It demonstrates LORA CAD functionality.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
|
||||
use embassy_nrf::{bind_interrupts, peripherals, spim};
|
||||
use embassy_time::{Delay, Timer};
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_nrf::init(Default::default());
|
||||
let mut spi_config = spim::Config::default();
|
||||
spi_config.frequency = spim::Frequency::M16;
|
||||
|
||||
let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
|
||||
|
||||
let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
|
||||
let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
|
||||
let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
|
||||
let busy = Input::new(p.P1_14.degrade(), Pull::Down);
|
||||
let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
|
||||
let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
|
||||
|
||||
let iv =
|
||||
GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
|
||||
let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
|
||||
|
||||
start_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
start_indicator.set_low();
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora.prepare_for_cad(&mdltn_params, true).await {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match lora.cad().await {
|
||||
Ok(cad_activity_detected) => {
|
||||
if cad_activity_detected {
|
||||
info!("cad successful with activity detected")
|
||||
} else {
|
||||
info!("cad successful without activity detected")
|
||||
}
|
||||
debug_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
debug_indicator.set_low();
|
||||
}
|
||||
Err(err) => info!("cad unsuccessful = {}", err),
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
|
||||
//! Other nrf/sx126x combinations may work with appropriate pin modifications.
|
||||
//! It demonstrates LoRaWAN join functionality.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_lora::LoraTimer;
|
||||
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
|
||||
use embassy_nrf::rng::Rng;
|
||||
use embassy_nrf::{bind_interrupts, peripherals, rng, spim};
|
||||
use embassy_time::Delay;
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use lorawan::default_crypto::DefaultFactory as Crypto;
|
||||
use lorawan_device::async_device::lora_radio::LoRaRadio;
|
||||
use lorawan_device::async_device::{region, Device, JoinMode};
|
||||
use lorawan_device::{AppEui, AppKey, DevEui};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
|
||||
RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_nrf::init(Default::default());
|
||||
let mut spi_config = spim::Config::default();
|
||||
spi_config.frequency = spim::Frequency::M16;
|
||||
|
||||
let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
|
||||
|
||||
let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
|
||||
let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
|
||||
let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
|
||||
let busy = Input::new(p.P1_14.degrade(), Pull::Down);
|
||||
let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
|
||||
let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
|
||||
|
||||
let iv =
|
||||
GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
|
||||
|
||||
let lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), true, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let radio = LoRaRadio::new(lora);
|
||||
let region: region::Configuration = region::Configuration::new(LORAWAN_REGION);
|
||||
let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs));
|
||||
|
||||
defmt::info!("Joining LoRaWAN network");
|
||||
|
||||
// TODO: Adjust the EUI and Keys according to your network credentials
|
||||
match device
|
||||
.join(&JoinMode::OTAA {
|
||||
deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => defmt::info!("LoRaWAN network joined"),
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
|
||||
//! Other nrf/sx126x combinations may work with appropriate pin modifications.
|
||||
//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
|
||||
use embassy_nrf::{bind_interrupts, peripherals, spim};
|
||||
use embassy_time::{Delay, Timer};
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_nrf::init(Default::default());
|
||||
let mut spi_config = spim::Config::default();
|
||||
spi_config.frequency = spim::Frequency::M16;
|
||||
|
||||
let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
|
||||
|
||||
let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
|
||||
let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
|
||||
let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
|
||||
let busy = Input::new(p.P1_14.degrade(), Pull::Down);
|
||||
let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
|
||||
let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
|
||||
|
||||
let iv =
|
||||
GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
|
||||
let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
|
||||
|
||||
start_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
start_indicator.set_low();
|
||||
|
||||
let mut receiving_buffer = [00u8; 100];
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let rx_pkt_params = {
|
||||
match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora
|
||||
.prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
receiving_buffer = [00u8; 100];
|
||||
match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
|
||||
Ok((received_len, _rx_pkt_status)) => {
|
||||
if (received_len == 3)
|
||||
&& (receiving_buffer[0] == 0x01u8)
|
||||
&& (receiving_buffer[1] == 0x02u8)
|
||||
&& (receiving_buffer[2] == 0x03u8)
|
||||
{
|
||||
info!("rx successful");
|
||||
debug_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
debug_indicator.set_low();
|
||||
} else {
|
||||
info!("rx unknown packet");
|
||||
}
|
||||
}
|
||||
Err(err) => info!("rx unsuccessful = {}", err),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
|
||||
//! Other nrf/sx126x combinations may work with appropriate pin modifications.
|
||||
//! It demonstrates LoRa Rx duty cycle functionality in conjunction with the lora_p2p_send example.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
|
||||
use embassy_nrf::{bind_interrupts, peripherals, spim};
|
||||
use embassy_time::{Delay, Timer};
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_nrf::init(Default::default());
|
||||
let mut spi_config = spim::Config::default();
|
||||
spi_config.frequency = spim::Frequency::M16;
|
||||
|
||||
let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
|
||||
|
||||
let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
|
||||
let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
|
||||
let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
|
||||
let busy = Input::new(p.P1_14.degrade(), Pull::Down);
|
||||
let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
|
||||
let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
|
||||
|
||||
let iv =
|
||||
GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
|
||||
let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
|
||||
|
||||
start_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
start_indicator.set_low();
|
||||
|
||||
let mut receiving_buffer = [00u8; 100];
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let rx_pkt_params = {
|
||||
match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// See "RM0453 Reference manual STM32WL5x advanced Arm®-based 32-bit MCUs with sub-GHz radio solution" for the best explanation of Rx duty cycle processing.
|
||||
match lora
|
||||
.prepare_for_rx(
|
||||
&mdltn_params,
|
||||
&rx_pkt_params,
|
||||
None,
|
||||
Some(&DutyCycleParams {
|
||||
rx_time: 300_000, // 300_000 units * 15.625 us/unit = 4.69 s
|
||||
sleep_time: 200_000, // 200_000 units * 15.625 us/unit = 3.13 s
|
||||
}),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
receiving_buffer = [00u8; 100];
|
||||
match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
|
||||
Ok((received_len, _rx_pkt_status)) => {
|
||||
if (received_len == 3)
|
||||
&& (receiving_buffer[0] == 0x01u8)
|
||||
&& (receiving_buffer[1] == 0x02u8)
|
||||
&& (receiving_buffer[2] == 0x03u8)
|
||||
{
|
||||
info!("rx successful");
|
||||
debug_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
debug_indicator.set_low();
|
||||
} else {
|
||||
info!("rx unknown packet")
|
||||
}
|
||||
}
|
||||
Err(err) => info!("rx unsuccessful = {}", err),
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
|
||||
//! Other nrf/sx126x combinations may work with appropriate pin modifications.
|
||||
//! It demonstrates LORA P2P send functionality.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
|
||||
use embassy_nrf::{bind_interrupts, peripherals, spim};
|
||||
use embassy_time::Delay;
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler<peripherals::TWISPI1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_nrf::init(Default::default());
|
||||
let mut spi_config = spim::Config::default();
|
||||
spi_config.frequency = spim::Frequency::M16;
|
||||
|
||||
let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config);
|
||||
|
||||
let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
|
||||
let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
|
||||
let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
|
||||
let busy = Input::new(p.P1_14.degrade(), Pull::Down);
|
||||
let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
|
||||
let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
|
||||
|
||||
let iv =
|
||||
GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut tx_pkt_params = {
|
||||
match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora.prepare_for_tx(&mdltn_params, 20, false).await {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let buffer = [0x01u8, 0x02u8, 0x03u8];
|
||||
match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
|
||||
Ok(()) => {
|
||||
info!("TX DONE");
|
||||
}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match lora.sleep(false).await {
|
||||
Ok(()) => info!("Sleep successful"),
|
||||
Err(err) => info!("Sleep unsuccessful = {}", err),
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ embassy-net = { version = "0.2.0", path = "../../embassy-net", features = [
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [
|
||||
"defmt",
|
||||
] }
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
|
@ -16,10 +16,6 @@ embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defm
|
||||
embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
|
||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] }
|
||||
lora-phy = { version = "2" }
|
||||
lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"] }
|
||||
lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"] }
|
||||
cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] }
|
||||
cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] }
|
||||
|
||||
@ -42,10 +38,10 @@ smart-leds = "0.3.0"
|
||||
heapless = "0.8"
|
||||
usbd-hid = "0.6.1"
|
||||
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||
embedded-hal-async = "1.0.0-rc.1"
|
||||
embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] }
|
||||
embedded-io-async = { version = "0.6.0", features = ["defmt-03"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
||||
embedded-hal-async = "1.0.0-rc.2"
|
||||
embedded-hal-bus = { version = "0.1.0-rc.2", features = ["async"] }
|
||||
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
||||
embedded-storage = { version = "0.3" }
|
||||
static_cell = { version = "2", features = ["nightly"]}
|
||||
portable-atomic = { version = "1.5", features = ["critical-section"] }
|
||||
|
@ -1,74 +0,0 @@
|
||||
//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio.
|
||||
//! It demonstrates LoRaWAN join functionality.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_lora::LoraTimer;
|
||||
use embassy_rp::gpio::{Input, Level, Output, Pin, Pull};
|
||||
use embassy_rp::spi::{Config, Spi};
|
||||
use embassy_time::Delay;
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use lorawan::default_crypto::DefaultFactory as Crypto;
|
||||
use lorawan_device::async_device::lora_radio::LoRaRadio;
|
||||
use lorawan_device::async_device::{region, Device, JoinMode};
|
||||
use lorawan_device::{AppEui, AppKey, DevEui};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let miso = p.PIN_12;
|
||||
let mosi = p.PIN_11;
|
||||
let clk = p.PIN_10;
|
||||
let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
|
||||
|
||||
let nss = Output::new(p.PIN_3.degrade(), Level::High);
|
||||
let reset = Output::new(p.PIN_15.degrade(), Level::High);
|
||||
let dio1 = Input::new(p.PIN_20.degrade(), Pull::None);
|
||||
let busy = Input::new(p.PIN_2.degrade(), Pull::None);
|
||||
|
||||
let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap();
|
||||
|
||||
let lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), true, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let radio = LoRaRadio::new(lora);
|
||||
let region: region::Configuration = region::Configuration::new(LORAWAN_REGION);
|
||||
let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), embassy_rp::clocks::RoscRng);
|
||||
|
||||
defmt::info!("Joining LoRaWAN network");
|
||||
|
||||
// TODO: Adjust the EUI and Keys according to your network credentials
|
||||
match device
|
||||
.join(&JoinMode::OTAA {
|
||||
deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => defmt::info!("LoRaWAN network joined"),
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio.
|
||||
//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_rp::gpio::{Input, Level, Output, Pin, Pull};
|
||||
use embassy_rp::spi::{Config, Spi};
|
||||
use embassy_time::{Delay, Timer};
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let miso = p.PIN_12;
|
||||
let mosi = p.PIN_11;
|
||||
let clk = p.PIN_10;
|
||||
let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
|
||||
|
||||
let nss = Output::new(p.PIN_3.degrade(), Level::High);
|
||||
let reset = Output::new(p.PIN_15.degrade(), Level::High);
|
||||
let dio1 = Input::new(p.PIN_20.degrade(), Pull::None);
|
||||
let busy = Input::new(p.PIN_2.degrade(), Pull::None);
|
||||
|
||||
let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut debug_indicator = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
let mut receiving_buffer = [00u8; 100];
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let rx_pkt_params = {
|
||||
match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora
|
||||
.prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
receiving_buffer = [00u8; 100];
|
||||
match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
|
||||
Ok((received_len, _rx_pkt_status)) => {
|
||||
if (received_len == 3)
|
||||
&& (receiving_buffer[0] == 0x01u8)
|
||||
&& (receiving_buffer[1] == 0x02u8)
|
||||
&& (receiving_buffer[2] == 0x03u8)
|
||||
{
|
||||
info!("rx successful");
|
||||
debug_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
debug_indicator.set_low();
|
||||
} else {
|
||||
info!("rx unknown packet");
|
||||
}
|
||||
}
|
||||
Err(err) => info!("rx unsuccessful = {}", err),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio.
|
||||
//! It demonstrates LORA P2P send functionality.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_rp::gpio::{Input, Level, Output, Pin, Pull};
|
||||
use embassy_rp::spi::{Config, Spi};
|
||||
use embassy_time::Delay;
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let miso = p.PIN_12;
|
||||
let mosi = p.PIN_11;
|
||||
let clk = p.PIN_10;
|
||||
let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
|
||||
|
||||
let nss = Output::new(p.PIN_3.degrade(), Level::High);
|
||||
let reset = Output::new(p.PIN_15.degrade(), Level::High);
|
||||
let dio1 = Input::new(p.PIN_20.degrade(), Pull::None);
|
||||
let busy = Input::new(p.PIN_2.degrade(), Pull::None);
|
||||
|
||||
let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut tx_pkt_params = {
|
||||
match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora.prepare_for_tx(&mdltn_params, 20, false).await {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let buffer = [0x01u8, 0x02u8, 0x03u8];
|
||||
match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
|
||||
Ok(()) => {
|
||||
info!("TX DONE");
|
||||
}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match lora.sleep(false).await {
|
||||
Ok(()) => info!("Sleep successful"),
|
||||
Err(err) => info!("Sleep unsuccessful = {}", err),
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio.
|
||||
//! It demonstrates LORA P2P send functionality using the second core, with data provided by the first core.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Executor;
|
||||
use embassy_lora::iv::GenericSx126xInterfaceVariant;
|
||||
use embassy_rp::gpio::{AnyPin, Input, Level, Output, Pin, Pull};
|
||||
use embassy_rp::multicore::{spawn_core1, Stack};
|
||||
use embassy_rp::peripherals::SPI1;
|
||||
use embassy_rp::spi::{Async, Config, Spi};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_time::{Delay, Timer};
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
static mut CORE1_STACK: Stack<4096> = Stack::new();
|
||||
static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
|
||||
static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
|
||||
static CHANNEL: Channel<CriticalSectionRawMutex, [u8; 3], 1> = Channel::new();
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let miso = p.PIN_12;
|
||||
let mosi = p.PIN_11;
|
||||
let clk = p.PIN_10;
|
||||
let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
|
||||
|
||||
let nss = Output::new(p.PIN_3.degrade(), Level::High);
|
||||
let reset = Output::new(p.PIN_15.degrade(), Level::High);
|
||||
let dio1 = Input::new(p.PIN_20.degrade(), Pull::None);
|
||||
let busy = Input::new(p.PIN_2.degrade(), Pull::None);
|
||||
|
||||
let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap();
|
||||
|
||||
spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
|
||||
let executor1 = EXECUTOR1.init(Executor::new());
|
||||
executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(spi, iv))));
|
||||
});
|
||||
|
||||
let executor0 = EXECUTOR0.init(Executor::new());
|
||||
executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn core0_task() {
|
||||
info!("Hello from core 0");
|
||||
loop {
|
||||
CHANNEL.send([0x01u8, 0x02u8, 0x03u8]).await;
|
||||
Timer::after_millis(60 * 1000).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn core1_task(
|
||||
spi: Spi<'static, SPI1, Async>,
|
||||
iv: GenericSx126xInterfaceVariant<Output<'static, AnyPin>, Input<'static, AnyPin>>,
|
||||
) {
|
||||
info!("Hello from core 1");
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut tx_pkt_params = {
|
||||
match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let buffer: [u8; 3] = CHANNEL.receive().await;
|
||||
match lora.prepare_for_tx(&mdltn_params, 20, false).await {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
|
||||
Ok(()) => {
|
||||
info!("TX DONE");
|
||||
}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match lora.sleep(false).await {
|
||||
Ok(()) => info!("Sleep successful"),
|
||||
Err(err) => info!("Sleep unsuccessful = {}", err),
|
||||
}
|
||||
}
|
||||
}
|
@ -11,8 +11,8 @@ embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["lo
|
||||
embassy-net = { version = "0.2.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] }
|
||||
embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" }
|
||||
embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]}
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-adapters = { version = "0.6.0", features = ["futures-03"] }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
embedded-io-adapters = { version = "0.6.1", features = ["futures-03"] }
|
||||
critical-section = { version = "1.1", features = ["std"] }
|
||||
|
||||
async-io = "1.6.0"
|
||||
|
@ -20,7 +20,7 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing
|
||||
cortex-m-rt = "0.7.0"
|
||||
embedded-hal = "0.2.6"
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||
use embassy_stm32::dac::{DacCh1, Value};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -14,11 +14,10 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
info!("Hello World, dude!");
|
||||
|
||||
let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4);
|
||||
unwrap!(dac.set_trigger_enable(false));
|
||||
|
||||
loop {
|
||||
for v in 0..=255 {
|
||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
||||
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
62
examples/stm32f4/src/bin/i2c_async.rs
Normal file
62
examples/stm32f4/src/bin/i2c_async.rs
Normal file
@ -0,0 +1,62 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
// Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1
|
||||
// DMA peripherals changed to compile for stm32f429zi, for the CI.
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::i2c::I2c;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const ADDRESS: u8 = 96;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
info!("Hello world!");
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
|
||||
let mut i2c = I2c::new(
|
||||
p.I2C1,
|
||||
p.PB8,
|
||||
p.PB7,
|
||||
Irqs,
|
||||
p.DMA1_CH6,
|
||||
p.DMA1_CH0,
|
||||
Hertz(100_000),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
loop {
|
||||
let a1454_read_sensor_command = [0x1F];
|
||||
let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||
|
||||
match i2c
|
||||
.write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
// Convert 12-bit signed integer into 16-bit signed integer.
|
||||
// Is the 12 bit number negative?
|
||||
if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 {
|
||||
sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000;
|
||||
}
|
||||
|
||||
let mut sensor_value_raw: u16 = sensor_data_buffer[3].into();
|
||||
sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8;
|
||||
let sensor_value: u16 = sensor_value_raw.into();
|
||||
let sensor_value = sensor_value as i16;
|
||||
info!("Data: {}", sensor_value);
|
||||
}
|
||||
Err(e) => error!("I2C Error during read: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
135
examples/stm32f4/src/bin/i2c_comparison.rs
Normal file
135
examples/stm32f4/src/bin/i2c_comparison.rs
Normal file
@ -0,0 +1,135 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
// Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3
|
||||
// on the pins referenced in the peripheral definitions.
|
||||
// Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI.
|
||||
// MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x
|
||||
// as long to complete as the blocking ones!
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::i2c::I2c;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||
use embassy_time::Instant;
|
||||
use futures::future::try_join3;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const ADDRESS: u8 = 96;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
|
||||
I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
|
||||
});
|
||||
|
||||
/// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer.
|
||||
fn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 {
|
||||
let lower = buffer[3];
|
||||
let mut upper = buffer[2];
|
||||
// Fill in additional 1s if the 12 bit number is negative.
|
||||
if (upper & 0b00001000) == 0b0001000 {
|
||||
upper = upper | 0b11110000;
|
||||
}
|
||||
|
||||
let mut sensor_value_raw: u16 = lower.into();
|
||||
sensor_value_raw |= (upper as u16) << 8;
|
||||
let sensor_value: u16 = sensor_value_raw.into();
|
||||
let sensor_value = sensor_value as i16;
|
||||
sensor_value
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
info!("Setting up peripherals.");
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
|
||||
let mut i2c1 = I2c::new(
|
||||
p.I2C1,
|
||||
p.PB8,
|
||||
p.PB7,
|
||||
Irqs,
|
||||
p.DMA1_CH6,
|
||||
p.DMA1_CH0,
|
||||
Hertz(100_000),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let mut i2c2 = I2c::new(
|
||||
p.I2C2,
|
||||
p.PB10,
|
||||
p.PB11,
|
||||
Irqs,
|
||||
p.DMA1_CH7,
|
||||
p.DMA1_CH3,
|
||||
Hertz(100_000),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let mut i2c3 = I2c::new(
|
||||
p.I2C3,
|
||||
p.PA8,
|
||||
p.PC9,
|
||||
Irqs,
|
||||
p.DMA1_CH4,
|
||||
p.DMA1_CH2,
|
||||
Hertz(100_000),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let a1454_read_sensor_command = [0x1F];
|
||||
let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||
let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||
let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0];
|
||||
loop {
|
||||
// Blocking reads one after the other. Completes in about 2000us.
|
||||
let blocking_read_start_us = Instant::now().as_micros();
|
||||
match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) {
|
||||
Ok(()) => {}
|
||||
Err(e) => error!("I2C Error: {:?}", e),
|
||||
}
|
||||
match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) {
|
||||
Ok(()) => {}
|
||||
Err(e) => error!("I2C Error: {:?}", e),
|
||||
}
|
||||
match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) {
|
||||
Ok(()) => {}
|
||||
Err(e) => error!("I2C Error: {:?}", e),
|
||||
}
|
||||
let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us;
|
||||
info!(
|
||||
"Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}",
|
||||
blocking_read_total_us,
|
||||
a1454_buf_to_i16(&i2c1_buffer),
|
||||
a1454_buf_to_i16(&i2c2_buffer),
|
||||
a1454_buf_to_i16(&i2c3_buffer)
|
||||
);
|
||||
|
||||
// Async reads overlapping. Completes in about 1000us.
|
||||
let async_read_start_us = Instant::now().as_micros();
|
||||
|
||||
let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer);
|
||||
let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer);
|
||||
let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer);
|
||||
|
||||
// Wait for all three transactions to finish, or any one of them to fail.
|
||||
match try_join3(i2c1_result, i2c2_result, i2c3_result).await {
|
||||
Ok(_) => {
|
||||
let async_read_total_us = Instant::now().as_micros() - async_read_start_us;
|
||||
info!(
|
||||
"Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}",
|
||||
async_read_total_us,
|
||||
a1454_buf_to_i16(&i2c1_buffer),
|
||||
a1454_buf_to_i16(&i2c2_buffer),
|
||||
a1454_buf_to_i16(&i2c3_buffer)
|
||||
);
|
||||
}
|
||||
Err(e) => error!("I2C Error during async write-read: {}", e),
|
||||
};
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -11,7 +11,7 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] }
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
||||
defmt = "0.3"
|
||||
@ -20,9 +20,9 @@ defmt-rtt = "0.4"
|
||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
embedded-hal = "0.2.6"
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-nal-async = { version = "0.7" }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
embedded-nal-async = { version = "0.7.1" }
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -11,7 +11,7 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
||||
defmt = "0.3"
|
||||
@ -20,9 +20,9 @@ defmt-rtt = "0.4"
|
||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
embedded-hal = "0.2.6"
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-nal-async = { version = "0.7" }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
embedded-nal-async = { version = "0.7.1" }
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
|
@ -19,7 +19,8 @@ const HEIGHT: usize = 100;
|
||||
static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_EV => i2c::InterruptHandler<peripherals::I2C1>;
|
||||
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||
DCMI => dcmi::InterruptHandler<peripherals::DCMI>;
|
||||
});
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use defmt::*;
|
||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||
use embassy_stm32::dac::{DacCh1, Value};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use embassy_stm32::Config;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
@ -46,11 +46,10 @@ fn main() -> ! {
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
||||
unwrap!(dac.set_trigger_enable(false));
|
||||
|
||||
loop {
|
||||
for v in 0..=255 {
|
||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
||||
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,21 +4,15 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::dac::{DacChannel, ValueArray};
|
||||
use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
|
||||
use embassy_stm32::pac::timer::vals::{Mms, Opm};
|
||||
use embassy_stm32::peripherals::{TIM6, TIM7};
|
||||
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
||||
use micromath::F32Ext;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
pub type Dac1Type =
|
||||
embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
|
||||
|
||||
pub type Dac2Type =
|
||||
embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
@ -63,7 +57,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn dac_task1(mut dac: Dac1Type) {
|
||||
async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
||||
let data: &[u8; 256] = &calculate_array::<256>();
|
||||
|
||||
info!("TIM6 frequency is {}", TIM6::frequency());
|
||||
@ -77,8 +71,9 @@ async fn dac_task1(mut dac: Dac1Type) {
|
||||
error!("Reload value {} below threshold!", reload);
|
||||
}
|
||||
|
||||
dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap();
|
||||
dac.enable_channel().unwrap();
|
||||
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6);
|
||||
dac.set_triggering(true);
|
||||
dac.enable();
|
||||
|
||||
TIM6::enable_and_reset();
|
||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
@ -100,14 +95,12 @@ async fn dac_task1(mut dac: Dac1Type) {
|
||||
// Loop technically not necessary if DMA circular mode is enabled
|
||||
loop {
|
||||
info!("Loop DAC1");
|
||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
||||
error!("Could not write to dac: {}", e);
|
||||
}
|
||||
dac.write(ValueArray::Bit8(data), true).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn dac_task2(mut dac: Dac2Type) {
|
||||
async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
||||
let data: &[u8; 256] = &calculate_array::<256>();
|
||||
|
||||
info!("TIM7 frequency is {}", TIM7::frequency());
|
||||
@ -127,7 +120,9 @@ async fn dac_task2(mut dac: Dac2Type) {
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap();
|
||||
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7);
|
||||
dac.set_triggering(true);
|
||||
dac.enable();
|
||||
|
||||
debug!(
|
||||
"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
|
||||
@ -138,9 +133,7 @@ async fn dac_task2(mut dac: Dac2Type) {
|
||||
data.len()
|
||||
);
|
||||
|
||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
||||
error!("Could not write to dac: {}", e);
|
||||
}
|
||||
dac.write(ValueArray::Bit8(data), true).await;
|
||||
}
|
||||
|
||||
fn to_sine_wave(v: u8) -> u8 {
|
||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -6,8 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = ["nightly"]
|
||||
nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", "embassy-executor/nightly",
|
||||
"embassy-lora", "lora-phy", "lorawan-device", "lorawan", "dep:embedded-io-async"]
|
||||
nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", "embassy-executor/nightly", "dep:embedded-io-async"]
|
||||
|
||||
[dependencies]
|
||||
# Change stm32l072cz to your chip name, if necessary.
|
||||
@ -15,17 +14,13 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
|
||||
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true }
|
||||
lora-phy = { version = "2", optional = true }
|
||||
lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
|
||||
lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"], optional = true }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
embedded-io-async = { version = "0.6.1", optional = true }
|
||||
|
||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
|
@ -1,97 +0,0 @@
|
||||
//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
|
||||
//! It demonstrates LORA P2P CAD functionality.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::Stm32l0InterfaceVariant;
|
||||
use embassy_stm32::exti::{Channel, ExtiInput};
|
||||
use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed};
|
||||
use embassy_stm32::spi;
|
||||
use embassy_stm32::time::khz;
|
||||
use embassy_time::{Delay, Timer};
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1276_7_8_9::SX1276_7_8_9;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
config.rcc.hsi = true;
|
||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let mut spi_config = spi::Config::default();
|
||||
spi_config.frequency = khz(200);
|
||||
|
||||
// SPI for sx1276
|
||||
let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config);
|
||||
|
||||
let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
|
||||
let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
|
||||
|
||||
let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
|
||||
let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
|
||||
|
||||
let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low);
|
||||
let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low);
|
||||
|
||||
start_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
start_indicator.set_low();
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora.prepare_for_cad(&mdltn_params, true).await {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match lora.cad().await {
|
||||
Ok(cad_activity_detected) => {
|
||||
if cad_activity_detected {
|
||||
info!("cad successful with activity detected")
|
||||
} else {
|
||||
info!("cad successful without activity detected")
|
||||
}
|
||||
debug_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
debug_indicator.set_low();
|
||||
}
|
||||
Err(err) => info!("cad unsuccessful = {}", err),
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
|
||||
//! It demonstrates LoRaWAN join functionality.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::Stm32l0InterfaceVariant;
|
||||
use embassy_lora::LoraTimer;
|
||||
use embassy_stm32::exti::{Channel, ExtiInput};
|
||||
use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed};
|
||||
use embassy_stm32::rng::Rng;
|
||||
use embassy_stm32::time::khz;
|
||||
use embassy_stm32::{bind_interrupts, peripherals, rng, spi};
|
||||
use embassy_time::Delay;
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1276_7_8_9::SX1276_7_8_9;
|
||||
use lora_phy::LoRa;
|
||||
use lorawan::default_crypto::DefaultFactory as Crypto;
|
||||
use lorawan_device::async_device::lora_radio::LoRaRadio;
|
||||
use lorawan_device::async_device::{region, Device, JoinMode};
|
||||
use lorawan_device::{AppEui, AppKey, DevEui};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
RNG_LPUART1 => rng::InterruptHandler<peripherals::RNG>;
|
||||
});
|
||||
|
||||
const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
config.rcc.hsi = true;
|
||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let mut spi_config = spi::Config::default();
|
||||
spi_config.frequency = khz(200);
|
||||
|
||||
// SPI for sx1276
|
||||
let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config);
|
||||
|
||||
let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
|
||||
let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
|
||||
|
||||
let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
|
||||
let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
|
||||
|
||||
let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
|
||||
|
||||
let lora = {
|
||||
match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), true, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let radio = LoRaRadio::new(lora);
|
||||
let region: region::Configuration = region::Configuration::new(LORAWAN_REGION);
|
||||
let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs));
|
||||
|
||||
defmt::info!("Joining LoRaWAN network");
|
||||
|
||||
// TODO: Adjust the EUI and Keys according to your network credentials
|
||||
match device
|
||||
.join(&JoinMode::OTAA {
|
||||
deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => defmt::info!("LoRaWAN network joined"),
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
|
||||
//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::Stm32l0InterfaceVariant;
|
||||
use embassy_stm32::exti::{Channel, ExtiInput};
|
||||
use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed};
|
||||
use embassy_stm32::spi;
|
||||
use embassy_stm32::time::khz;
|
||||
use embassy_time::{Delay, Timer};
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1276_7_8_9::SX1276_7_8_9;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
config.rcc.hsi = true;
|
||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let mut spi_config = spi::Config::default();
|
||||
spi_config.frequency = khz(200);
|
||||
|
||||
// SPI for sx1276
|
||||
let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config);
|
||||
|
||||
let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
|
||||
let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
|
||||
|
||||
let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
|
||||
let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
|
||||
|
||||
let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low);
|
||||
let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low);
|
||||
|
||||
start_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
start_indicator.set_low();
|
||||
|
||||
let mut receiving_buffer = [00u8; 100];
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let rx_pkt_params = {
|
||||
match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora
|
||||
.prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
receiving_buffer = [00u8; 100];
|
||||
match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
|
||||
Ok((received_len, _rx_pkt_status)) => {
|
||||
if (received_len == 3)
|
||||
&& (receiving_buffer[0] == 0x01u8)
|
||||
&& (receiving_buffer[1] == 0x02u8)
|
||||
&& (receiving_buffer[2] == 0x03u8)
|
||||
{
|
||||
info!("rx successful");
|
||||
debug_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
debug_indicator.set_low();
|
||||
} else {
|
||||
info!("rx unknown packet");
|
||||
}
|
||||
}
|
||||
Err(err) => info!("rx unsuccessful = {}", err),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio.
|
||||
//! It demonstrates LORA P2P send functionality.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::Stm32l0InterfaceVariant;
|
||||
use embassy_stm32::exti::{Channel, ExtiInput};
|
||||
use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed};
|
||||
use embassy_stm32::spi;
|
||||
use embassy_stm32::time::khz;
|
||||
use embassy_time::Delay;
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1276_7_8_9::SX1276_7_8_9;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
config.rcc.hsi = true;
|
||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let mut spi_config = spi::Config::default();
|
||||
spi_config.frequency = khz(200);
|
||||
|
||||
// SPI for sx1276
|
||||
let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config);
|
||||
|
||||
let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low);
|
||||
let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low);
|
||||
|
||||
let irq_pin = Input::new(p.PB4.degrade(), Pull::Up);
|
||||
let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade());
|
||||
|
||||
let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut tx_pkt_params = {
|
||||
match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora.prepare_for_tx(&mdltn_params, 17, true).await {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let buffer = [0x01u8, 0x02u8, 0x03u8];
|
||||
match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
|
||||
Ok(()) => {
|
||||
info!("TX DONE");
|
||||
}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match lora.sleep(false).await {
|
||||
Ok(()) => info!("Sleep successful"),
|
||||
Err(err) => info!("Sleep unsuccessful = {}", err),
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
|
||||
embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" }
|
||||
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "udp", "tcp", "dhcpv4", "medium-ethernet"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embedded-io-async = { version = "0.6.0", features = ["defmt-03"] }
|
||||
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
||||
embedded-io = { version = "0.6.0", features = ["defmt-03"] }
|
||||
|
||||
defmt = "0.3"
|
||||
@ -24,9 +24,9 @@ defmt-rtt = "0.4"
|
||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
embedded-hal = "0.2.6"
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
embedded-hal-bus = { version = "=0.1.0-rc.2", features = ["async"] }
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
|
@ -3,7 +3,7 @@
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
|
||||
use embassy_stm32::dac::{DacCh1, Value};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -13,11 +13,10 @@ fn main() -> ! {
|
||||
info!("Hello World!");
|
||||
|
||||
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
|
||||
unwrap!(dac.set_trigger_enable(false));
|
||||
|
||||
loop {
|
||||
for v in 0..=255 {
|
||||
unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
|
||||
dac.set(Value::Bit8(to_sine_wave(v)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,21 +4,15 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::dac::{DacChannel, ValueArray};
|
||||
use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
|
||||
use embassy_stm32::pac::timer::vals::{Mms, Opm};
|
||||
use embassy_stm32::peripherals::{TIM6, TIM7};
|
||||
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
||||
use micromath::F32Ext;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
pub type Dac1Type =
|
||||
embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
|
||||
|
||||
pub type Dac2Type =
|
||||
embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let config = embassy_stm32::Config::default();
|
||||
@ -34,7 +28,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn dac_task1(mut dac: Dac1Type) {
|
||||
async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
||||
let data: &[u8; 256] = &calculate_array::<256>();
|
||||
|
||||
info!("TIM6 frequency is {}", TIM6::frequency());
|
||||
@ -48,8 +42,9 @@ async fn dac_task1(mut dac: Dac1Type) {
|
||||
error!("Reload value {} below threshold!", reload);
|
||||
}
|
||||
|
||||
dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap();
|
||||
dac.enable_channel().unwrap();
|
||||
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6);
|
||||
dac.set_triggering(true);
|
||||
dac.enable();
|
||||
|
||||
TIM6::enable_and_reset();
|
||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
@ -71,14 +66,12 @@ async fn dac_task1(mut dac: Dac1Type) {
|
||||
// Loop technically not necessary if DMA circular mode is enabled
|
||||
loop {
|
||||
info!("Loop DAC1");
|
||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
||||
error!("Could not write to dac: {}", e);
|
||||
}
|
||||
dac.write(ValueArray::Bit8(data), true).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn dac_task2(mut dac: Dac2Type) {
|
||||
async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
||||
let data: &[u8; 256] = &calculate_array::<256>();
|
||||
|
||||
info!("TIM7 frequency is {}", TIM7::frequency());
|
||||
@ -98,7 +91,9 @@ async fn dac_task2(mut dac: Dac2Type) {
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap();
|
||||
dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7);
|
||||
dac.set_triggering(true);
|
||||
dac.enable();
|
||||
|
||||
debug!(
|
||||
"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
|
||||
@ -109,9 +104,7 @@ async fn dac_task2(mut dac: Dac2Type) {
|
||||
data.len()
|
||||
);
|
||||
|
||||
if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
|
||||
error!("Could not write to dac: {}", e);
|
||||
}
|
||||
dac.write(ValueArray::Bit8(data), true).await;
|
||||
}
|
||||
|
||||
fn to_sine_wave(v: u8) -> u8 {
|
||||
|
@ -14,7 +14,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -16,7 +16,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -13,7 +13,8 @@ const ADDRESS: u8 = 0x5F;
|
||||
const WHOAMI: u8 = 0x0F;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C2_EV => i2c::InterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -40,7 +40,8 @@ use static_cell::make_static;
|
||||
use {embassy_stm32 as hal, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C3_EV => i2c::InterruptHandler<peripherals::I2C3>;
|
||||
I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
|
||||
I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
|
||||
RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||
});
|
||||
|
||||
|
@ -25,7 +25,7 @@ embedded-hal = "0.2.6"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
rand_core = { version = "0.6.3", default-features = false }
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
static_cell = { version = "2", features = ["nightly"]}
|
||||
|
||||
[profile.release]
|
||||
|
@ -11,10 +11,6 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
|
||||
lora-phy = { version = "2" }
|
||||
lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"] }
|
||||
lorawan = { version = "0.7.4", default-features = false, features = ["default-crypto"] }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
|
@ -1,95 +0,0 @@
|
||||
//! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio.
|
||||
//! It demonstrates LoRaWAN join functionality.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait, async_fn_in_trait)]
|
||||
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant};
|
||||
use embassy_lora::LoraTimer;
|
||||
use embassy_stm32::gpio::{Level, Output, Pin, Speed};
|
||||
use embassy_stm32::rng::{self, Rng};
|
||||
use embassy_stm32::spi::Spi;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::{bind_interrupts, peripherals};
|
||||
use embassy_time::Delay;
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use lorawan::default_crypto::DefaultFactory as Crypto;
|
||||
use lorawan_device::async_device::lora_radio::LoRaRadio;
|
||||
use lorawan_device::async_device::{region, Device, JoinMode};
|
||||
use lorawan_device::{AppEui, AppKey, DevEui};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region
|
||||
|
||||
bind_interrupts!(struct Irqs{
|
||||
SUBGHZ_RADIO => InterruptHandler;
|
||||
RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(32_000_000),
|
||||
mode: HseMode::Bypass,
|
||||
prescaler: HsePrescaler::DIV1,
|
||||
});
|
||||
config.rcc.mux = ClockSrc::PLL1_R;
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV2,
|
||||
mul: PllMul::MUL6,
|
||||
divp: None,
|
||||
divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG
|
||||
divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2)
|
||||
});
|
||||
}
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2);
|
||||
|
||||
// Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx
|
||||
let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);
|
||||
let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
|
||||
let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
|
||||
let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap();
|
||||
|
||||
let lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), true, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
let radio = LoRaRadio::new(lora);
|
||||
let region: region::Configuration = region::Configuration::new(LORAWAN_REGION);
|
||||
let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs));
|
||||
|
||||
defmt::info!("Joining LoRaWAN network");
|
||||
|
||||
// TODO: Adjust the EUI and Keys according to your network credentials
|
||||
match device
|
||||
.join(&JoinMode::OTAA {
|
||||
deveui: DevEui::from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
appeui: AppEui::from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
appkey: AppKey::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => defmt::info!("LoRaWAN network joined"),
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
//! This example runs on the STM32WL board, which has a builtin Semtech Sx1262 radio.
|
||||
//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait, async_fn_in_trait)]
|
||||
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant};
|
||||
use embassy_stm32::bind_interrupts;
|
||||
use embassy_stm32::gpio::{Level, Output, Pin, Speed};
|
||||
use embassy_stm32::spi::Spi;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_time::{Delay, Timer};
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
bind_interrupts!(struct Irqs{
|
||||
SUBGHZ_RADIO => InterruptHandler;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(32_000_000),
|
||||
mode: HseMode::Bypass,
|
||||
prescaler: HsePrescaler::DIV1,
|
||||
});
|
||||
config.rcc.mux = ClockSrc::PLL1_R;
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV2,
|
||||
mul: PllMul::MUL6,
|
||||
divp: None,
|
||||
divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG
|
||||
divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2)
|
||||
});
|
||||
}
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2);
|
||||
|
||||
// Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx
|
||||
let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);
|
||||
let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
|
||||
let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
|
||||
let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut debug_indicator = Output::new(p.PB9, Level::Low, Speed::Low);
|
||||
let mut start_indicator = Output::new(p.PB15, Level::Low, Speed::Low);
|
||||
|
||||
start_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
start_indicator.set_low();
|
||||
|
||||
let mut receiving_buffer = [00u8; 100];
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let rx_pkt_params = {
|
||||
match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora
|
||||
.prepare_for_rx(&mdltn_params, &rx_pkt_params, None, None, false)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
receiving_buffer = [00u8; 100];
|
||||
match lora.rx(&rx_pkt_params, &mut receiving_buffer).await {
|
||||
Ok((received_len, _rx_pkt_status)) => {
|
||||
if (received_len == 3)
|
||||
&& (receiving_buffer[0] == 0x01u8)
|
||||
&& (receiving_buffer[1] == 0x02u8)
|
||||
&& (receiving_buffer[2] == 0x03u8)
|
||||
{
|
||||
info!("rx successful");
|
||||
debug_indicator.set_high();
|
||||
Timer::after_secs(5).await;
|
||||
debug_indicator.set_low();
|
||||
} else {
|
||||
info!("rx unknown packet");
|
||||
}
|
||||
}
|
||||
Err(err) => info!("rx unsuccessful = {}", err),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
//! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio.
|
||||
//! It demonstrates LORA P2P send functionality.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(type_alias_impl_trait, async_fn_in_trait)]
|
||||
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant};
|
||||
use embassy_stm32::bind_interrupts;
|
||||
use embassy_stm32::gpio::{Level, Output, Pin, Speed};
|
||||
use embassy_stm32::spi::Spi;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_time::Delay;
|
||||
use lora_phy::mod_params::*;
|
||||
use lora_phy::sx1261_2::SX1261_2;
|
||||
use lora_phy::LoRa;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region
|
||||
|
||||
bind_interrupts!(struct Irqs{
|
||||
SUBGHZ_RADIO => InterruptHandler;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(32_000_000),
|
||||
mode: HseMode::Bypass,
|
||||
prescaler: HsePrescaler::DIV1,
|
||||
});
|
||||
config.rcc.mux = ClockSrc::PLL1_R;
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSource::HSE,
|
||||
prediv: PllPreDiv::DIV2,
|
||||
mul: PllMul::MUL6,
|
||||
divp: None,
|
||||
divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG
|
||||
divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2)
|
||||
});
|
||||
}
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2);
|
||||
|
||||
// Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx
|
||||
let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);
|
||||
let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High);
|
||||
let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
|
||||
let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap();
|
||||
|
||||
let mut lora = {
|
||||
match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, Delay).await {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mdltn_params = {
|
||||
match lora.create_modulation_params(
|
||||
SpreadingFactor::_10,
|
||||
Bandwidth::_250KHz,
|
||||
CodingRate::_4_8,
|
||||
LORA_FREQUENCY_IN_HZ,
|
||||
) {
|
||||
Ok(mp) => mp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut tx_pkt_params = {
|
||||
match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) {
|
||||
Ok(pp) => pp,
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match lora.prepare_for_tx(&mdltn_params, 20, false).await {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let buffer = [0x01u8, 0x02u8, 0x03u8];
|
||||
match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await {
|
||||
Ok(()) => {
|
||||
info!("TX DONE");
|
||||
}
|
||||
Err(err) => {
|
||||
info!("Radio error = {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match lora.sleep(false).await {
|
||||
Ok(()) => info!("Sleep successful"),
|
||||
Err(err) => info!("Sleep unsuccessful = {}", err),
|
||||
}
|
||||
}
|
@ -9,15 +9,15 @@ teleprobe-meta = "1"
|
||||
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt", "nightly"] }
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
|
||||
embedded-io-async = { version = "0.6.0", features = ["defmt-03"] }
|
||||
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
||||
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
|
||||
embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] }
|
||||
embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] }
|
||||
embedded-hal-async = { version = "1.0.0-rc.1" }
|
||||
embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] }
|
||||
embedded-hal-async = { version = "1.0.0-rc.2" }
|
||||
embedded-hal-bus = { version = "0.1.0-rc.2", features = ["async"] }
|
||||
static_cell = { version = "2", features = [ "nightly" ] }
|
||||
perf-client = { path = "../perf-client" }
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"nrf52840-dk");
|
||||
|
||||
use defmt::{assert_eq, *};
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"nrf52840-dk");
|
||||
|
||||
use defmt::{assert_eq, *};
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"nrf52840-dk");
|
||||
|
||||
use core::mem;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"nrf52840-dk");
|
||||
|
||||
use defmt::{assert, info};
|
||||
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
critical-section = { version = "1.1.1", features = ["restore-state-bool"] }
|
||||
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] }
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] }
|
||||
embassy-time = { version = "0.1.5", path = "../../embassy-time" }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
teleprobe-meta = "1.1"
|
||||
|
||||
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.3.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] }
|
||||
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
@ -24,12 +24,12 @@ defmt-rtt = "0.4"
|
||||
cortex-m = { version = "0.7.6" }
|
||||
cortex-m-rt = "0.7.0"
|
||||
embedded-hal = "0.2.6"
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||
embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
embedded-hal-bus = { version = "=0.1.0-rc.2", features = ["async"] }
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
embedded-io-async = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
embedded-storage = { version = "0.3" }
|
||||
static_cell = { version = "2", features = ["nightly"]}
|
||||
portable-atomic = { version = "1.5", features = ["critical-section"] }
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"rpi-pico");
|
||||
|
||||
use defmt::*;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"rpi-pico");
|
||||
|
||||
use defmt::{assert_eq, *};
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"rpi-pico");
|
||||
|
||||
use defmt::{assert_eq, *};
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"rpi-pico");
|
||||
|
||||
use defmt::*;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"rpi-pico");
|
||||
|
||||
use defmt::*;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"rpi-pico");
|
||||
|
||||
use defmt::{assert, *};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user