Compare commits
72 Commits
rp-remove-
...
embassy-bo
Author | SHA1 | Date | |
---|---|---|---|
c7be481190 | |||
89821846d7 | |||
758f5d7ea2 | |||
f0ba22fc17 | |||
db7e153fc0 | |||
a4f9e7cbcc | |||
de95ab264d | |||
04a7d97673 | |||
50c5cc5db6 | |||
b76631bebe | |||
eae67d0be8 | |||
2fa2c1a6fe | |||
83b199a874 | |||
cf900a8a3f | |||
4f2f375777 | |||
e7c876d744 | |||
64c2e1b9b6 | |||
61be0e75c8 | |||
a074cd0625 | |||
ca4f615b25 | |||
536b6a2de5 | |||
51233c0357 | |||
5c52d6c217 | |||
f474817872 | |||
97cb95bbf4 | |||
99c561a749 | |||
f09745dfe1 | |||
da9f82f507 | |||
f13639e78c | |||
908eef2775 | |||
633ffe46ae | |||
e6b9722a31 | |||
b8f51c6496 | |||
33ee48b9e8 | |||
a6d941fac3 | |||
15b4ed2c67 | |||
bbfb786139 | |||
81dc532d2d | |||
06fb3e4251 | |||
4943dec1a7 | |||
a444a65ebf | |||
9f870a5edf | |||
eb149a0bd4 | |||
551b54ddcb | |||
2528f45138 | |||
9505a6f752 | |||
ea61c19280 | |||
bcec55464f | |||
0b066b22d1 | |||
3a1ddd66c6 | |||
8d2d5a30a5 | |||
43c1afb6a6 | |||
eba42cb5f4 | |||
5cfad3f853 | |||
dca11095e2 | |||
e70ae71ecc | |||
d05979c708 | |||
6e1120e17e | |||
99682d313b | |||
cbc97758e3 | |||
059610a8de | |||
a3a58e8e4a | |||
bd5ef80bec | |||
c53614f057 | |||
1365ce6ab8 | |||
14a2d15240 | |||
af34fc4ccc | |||
aecfce1159 | |||
207fa19551 | |||
7da18e194a | |||
a3e8a6bc3a | |||
1920e90dcd |
@ -21,7 +21,7 @@ Then, what follows are some declarations on how to deal with panics and faults.
|
|||||||
|
|
||||||
[source,rust]
|
[source,rust]
|
||||||
----
|
----
|
||||||
include::example$basic/src/main.rs[lines="11..12"]
|
include::example$basic/src/main.rs[lines="10"]
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Task declaration
|
=== Task declaration
|
||||||
@ -30,7 +30,7 @@ After a bit of import declaration, the tasks run by the application should be de
|
|||||||
|
|
||||||
[source,rust]
|
[source,rust]
|
||||||
----
|
----
|
||||||
include::example$basic/src/main.rs[lines="13..22"]
|
include::example$basic/src/main.rs[lines="12..20"]
|
||||||
----
|
----
|
||||||
|
|
||||||
An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking.
|
An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking.
|
||||||
@ -45,23 +45,10 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera
|
|||||||
|
|
||||||
[source,rust]
|
[source,rust]
|
||||||
----
|
----
|
||||||
include::example$basic/src/main.rs[lines="23..-1"]
|
include::example$basic/src/main.rs[lines="22..-1"]
|
||||||
----
|
----
|
||||||
|
|
||||||
`#[embassy_executor::main]` takes an optional `config` parameter specifying a function that returns an instance of HAL's `Config` struct. For example:
|
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following:
|
||||||
|
|
||||||
```rust
|
|
||||||
fn embassy_config() -> embassy_nrf::config::Config {
|
|
||||||
embassy_nrf::config::Config::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::main(config = "embassy_config()")]
|
|
||||||
async fn main(_spawner: Spawner, p: embassy_nrf::Peripherals) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
What happens when the `blinker` task have been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following:
|
|
||||||
|
|
||||||
. Creates an Embassy Executor
|
. Creates an Embassy Executor
|
||||||
. Initializes the microcontroller HAL to get the `Peripherals`
|
. Initializes the microcontroller HAL to get the `Peripherals`
|
||||||
@ -76,7 +63,7 @@ The project definition needs to contain the embassy dependencies:
|
|||||||
|
|
||||||
[source,toml]
|
[source,toml]
|
||||||
----
|
----
|
||||||
include::example$basic/Cargo.toml[lines="8..9"]
|
include::example$basic/Cargo.toml[lines="9..11"]
|
||||||
----
|
----
|
||||||
|
|
||||||
Depending on your microcontroller, you may need to replace `embassy-nrf` with something else (`embassy-stm32` for STM32. Remember to update feature flags as well).
|
Depending on your microcontroller, you may need to replace `embassy-nrf` with something else (`embassy-stm32` for STM32. Remember to update feature flags as well).
|
||||||
|
@ -8,7 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh
|
|||||||
|
|
||||||
== PAC version
|
== PAC version
|
||||||
|
|
||||||
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provide distinct types
|
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types
|
||||||
to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
|
to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
|
||||||
|
|
||||||
Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
|
Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
|
||||||
@ -20,13 +20,13 @@ The blinky app using PAC is shown below:
|
|||||||
include::example$layer-by-layer/blinky-pac/src/main.rs[]
|
include::example$layer-by-layer/blinky-pac/src/main.rs[]
|
||||||
----
|
----
|
||||||
|
|
||||||
As you can see, there are a lot of code needed to enable the peripheral clocks, configuring the input pins and the output pins of the application.
|
As you can see, a lot of code is needed to enable the peripheral clocks and to configure the input pins and the output pins of the application.
|
||||||
|
|
||||||
Another downside of this application is that it is busy-looping while polling the button state. This prevents the microcontroller from utilizing any sleep mode to save power.
|
Another downside of this application is that it is busy-looping while polling the button state. This prevents the microcontroller from utilizing any sleep mode to save power.
|
||||||
|
|
||||||
== HAL version
|
== HAL version
|
||||||
|
|
||||||
To simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such
|
To simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such as:
|
||||||
|
|
||||||
* Automatically enabling the peripheral clock when you're using the peripheral
|
* Automatically enabling the peripheral clock when you're using the peripheral
|
||||||
* Deriving and applying register configuration from higher level types
|
* Deriving and applying register configuration from higher level types
|
||||||
@ -39,7 +39,7 @@ The HAL example is shown below:
|
|||||||
include::example$layer-by-layer/blinky-hal/src/main.rs[]
|
include::example$layer-by-layer/blinky-hal/src/main.rs[]
|
||||||
----
|
----
|
||||||
|
|
||||||
As you can see, the application becomes a lot simpler, even without using any async code. The `Input` and `Output` hides all the details accessing the GPIO registers, and allow you to use a much simpler API to query the state of the button and toggle the LED output accordingly.
|
As you can see, the application becomes a lot simpler, even without using any async code. The `Input` and `Output` types hide all the details of accessing the GPIO registers and allow you to use a much simpler API for querying the state of the button and toggling the LED output.
|
||||||
|
|
||||||
The same downside from the PAC example still applies though: the application is busy looping and consuming more power than necessary.
|
The same downside from the PAC example still applies though: the application is busy looping and consuming more power than necessary.
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ The link:https://github.com/embassy-rs/embassy/tree/master/embassy-stm32[Embassy
|
|||||||
|
|
||||||
== The infinite variant problem
|
== The infinite variant problem
|
||||||
|
|
||||||
STM32 microcontrollers comes in many families and flavors, and supporting all of them is a big undertaking. Embassy has taken advantage of the fact
|
STM32 microcontrollers come in many families, and flavors and supporting all of them is a big undertaking. Embassy has taken advantage of the fact
|
||||||
that the STM32 peripheral versions are shared across chip families. Instead of re-implementing the SPI
|
that the STM32 peripheral versions are shared across chip families. Instead of re-implementing the SPI
|
||||||
peripheral for every STM32 chip family, embassy have a single SPI implementation that depends on
|
peripheral for every STM32 chip family, embassy has a single SPI implementation that depends on
|
||||||
code-generated register types that are identical for STM32 families with the same version of a given peripheral.
|
code-generated register types that are identical for STM32 families with the same version of a given peripheral.
|
||||||
|
|
||||||
=== The metapac
|
=== The metapac
|
||||||
|
@ -2,13 +2,23 @@
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "embassy-boot"
|
name = "embassy-boot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Bootloader using Embassy"
|
description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks."
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/boot/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/boot/src/"
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
|
||||||
|
26
embassy-boot/nrf/README.md
Normal file
26
embassy-boot/nrf/README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# embassy-boot-nrf
|
||||||
|
|
||||||
|
An [Embassy](https://embassy.dev) project.
|
||||||
|
|
||||||
|
An adaptation of `embassy-boot` for nRF.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Load applications with our without the softdevice.
|
||||||
|
* Configure bootloader partitions based on linker script.
|
||||||
|
* Using watchdog timer to detect application failure.
|
||||||
|
|
||||||
|
|
||||||
|
## Minimum supported Rust version (MSRV)
|
||||||
|
|
||||||
|
`embassy-boot-nrf` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
@ -1,7 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig};
|
pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig};
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
# Bootloader for STM32
|
# embassy-boot-stm32
|
||||||
|
|
||||||
The bootloader uses `embassy-boot` to interact with the flash.
|
An [Embassy](https://embassy.dev) project.
|
||||||
|
|
||||||
# Usage
|
An adaptation of `embassy-boot` for STM32.
|
||||||
|
|
||||||
Flash the bootloader
|
## Features
|
||||||
|
|
||||||
```
|
* Configure bootloader partitions based on linker script.
|
||||||
cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx
|
* Load applications from active partition.
|
||||||
```
|
|
||||||
|
## Minimum supported Rust version (MSRV)
|
||||||
|
|
||||||
|
`embassy-boot-stm32` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State};
|
pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State};
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-executor"
|
name = "embassy-executor"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "async/await executor designed for embedded usage"
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
|
||||||
@ -21,10 +27,13 @@ flavors = [
|
|||||||
{ name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] },
|
{ name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["std", "nightly", "defmt"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
std = ["embassy-macros/std", "critical-section/std"]
|
std = ["critical-section/std"]
|
||||||
wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"]
|
wasm = ["dep:wasm-bindgen", "dep:js-sys"]
|
||||||
|
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = []
|
nightly = []
|
||||||
|
@ -8,18 +8,22 @@
|
|||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_macros::{main, task};
|
pub use embassy_macros::task;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(cortex_m)] {
|
if #[cfg(cortex_m)] {
|
||||||
#[path="arch/cortex_m.rs"]
|
#[path="arch/cortex_m.rs"]
|
||||||
mod arch;
|
mod arch;
|
||||||
pub use arch::*;
|
pub use arch::*;
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::main_cortex_m as main;
|
||||||
}
|
}
|
||||||
else if #[cfg(target_arch="riscv32")] {
|
else if #[cfg(target_arch="riscv32")] {
|
||||||
#[path="arch/riscv32.rs"]
|
#[path="arch/riscv32.rs"]
|
||||||
mod arch;
|
mod arch;
|
||||||
pub use arch::*;
|
pub use arch::*;
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::main_riscv as main;
|
||||||
}
|
}
|
||||||
else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] {
|
else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] {
|
||||||
#[path="arch/xtensa.rs"]
|
#[path="arch/xtensa.rs"]
|
||||||
@ -30,11 +34,15 @@ cfg_if::cfg_if! {
|
|||||||
#[path="arch/wasm.rs"]
|
#[path="arch/wasm.rs"]
|
||||||
mod arch;
|
mod arch;
|
||||||
pub use arch::*;
|
pub use arch::*;
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::main_wasm as main;
|
||||||
}
|
}
|
||||||
else if #[cfg(feature="std")] {
|
else if #[cfg(feature="std")] {
|
||||||
#[path="arch/std.rs"]
|
#[path="arch/std.rs"]
|
||||||
mod arch;
|
mod arch;
|
||||||
pub use arch::*;
|
pub use arch::*;
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::main_std as main;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
|
|||||||
/// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form
|
/// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form
|
||||||
/// the upcoming RX window start.
|
/// the upcoming RX window start.
|
||||||
async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> {
|
async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> {
|
||||||
trace!("TX request: {}", config);
|
trace!("TX request: {:?}", config);
|
||||||
self.switch.set_tx();
|
self.switch.set_tx();
|
||||||
|
|
||||||
self.radio
|
self.radio
|
||||||
@ -130,7 +130,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
|
|||||||
/// be able to hold a single LoRaWAN packet.
|
/// be able to hold a single LoRaWAN packet.
|
||||||
async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> {
|
async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> {
|
||||||
assert!(buf.len() >= 255);
|
assert!(buf.len() >= 255);
|
||||||
trace!("RX request: {}", config);
|
trace!("RX request: {:?}", config);
|
||||||
self.switch.set_rx();
|
self.switch.set_rx();
|
||||||
|
|
||||||
self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?;
|
self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?;
|
||||||
@ -172,7 +172,11 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
|
|||||||
self.radio.read_buffer(ptr, &mut buf[..len as usize])?;
|
self.radio.read_buffer(ptr, &mut buf[..len as usize])?;
|
||||||
self.radio.set_standby(StandbyClk::Rc)?;
|
self.radio.set_standby(StandbyClk::Rc)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]);
|
trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]);
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
trace!("RX done: {:02x?}", &mut buf[..len as usize]);
|
||||||
return Ok((len as usize, RxQuality::new(rssi, snr as i8)));
|
return Ok((len as usize, RxQuality::new(rssi, snr as i8)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +197,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
|
|||||||
.clear_irq_status(irq_status)
|
.clear_irq_status(irq_status)
|
||||||
.expect("error clearing irq status");
|
.expect("error clearing irq status");
|
||||||
|
|
||||||
trace!("SUGHZ IRQ 0b{=u16:b}, {:?}", irq_status, status);
|
trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status);
|
||||||
|
|
||||||
if irq_status == 0 {
|
if irq_status == 0 {
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
|
@ -87,7 +87,7 @@ where
|
|||||||
config.rf.spreading_factor.into(),
|
config.rf.spreading_factor.into(),
|
||||||
config.rf.bandwidth.into(),
|
config.rf.bandwidth.into(),
|
||||||
config.rf.coding_rate.into(),
|
config.rf.coding_rate.into(),
|
||||||
4,
|
8,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
@ -119,14 +119,14 @@ where
|
|||||||
config.spreading_factor.into(),
|
config.spreading_factor.into(),
|
||||||
config.bandwidth.into(),
|
config.bandwidth.into(),
|
||||||
config.coding_rate.into(),
|
config.coding_rate.into(),
|
||||||
4,
|
8,
|
||||||
4,
|
4,
|
||||||
false,
|
false,
|
||||||
0u8,
|
0u8,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
false,
|
true,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -3,6 +3,13 @@ name = "embassy-macros"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "macros for creating the entry point and tasks for embassy-executor"
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "1.0.76", features = ["full", "extra-traits"] }
|
syn = { version = "1.0.76", features = ["full", "extra-traits"] }
|
||||||
@ -14,8 +21,5 @@ proc-macro2 = "1.0.29"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = []
|
|
||||||
wasm = []
|
|
||||||
|
|
||||||
# Enabling this cause interrupt::take! to require embassy-executor
|
# Enabling this cause interrupt::take! to require embassy-executor
|
||||||
rtos-trace-interrupt = []
|
rtos-trace-interrupt = []
|
||||||
|
21
embassy-macros/README.md
Normal file
21
embassy-macros/README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# embassy-macros
|
||||||
|
|
||||||
|
An [Embassy](https://embassy.dev) project.
|
||||||
|
|
||||||
|
Macros for creating the main entry point and tasks that can be spawned by `embassy-executor`.
|
||||||
|
|
||||||
|
NOTE: The macros are re-exported by the `embassy-executor` crate which should be used instead of adding a direct dependency on the `embassy-macros` crate.
|
||||||
|
|
||||||
|
## Minimum supported Rust version (MSRV)
|
||||||
|
|
||||||
|
The `task` and `main` macros require the type alias impl trait (TAIT) nightly feature in order to compile.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
@ -1,3 +1,4 @@
|
|||||||
|
#![doc = include_str!("../README.md")]
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
@ -6,6 +7,36 @@ mod macros;
|
|||||||
mod util;
|
mod util;
|
||||||
use macros::*;
|
use macros::*;
|
||||||
|
|
||||||
|
/// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how
|
||||||
|
/// many concurrent tasks can be spawned (default is 1) for the function.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// The following restrictions apply:
|
||||||
|
///
|
||||||
|
/// * The function must be declared `async`.
|
||||||
|
/// * The function must not use generics.
|
||||||
|
/// * The optional `pool_size` attribute must be 1 or greater.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// Declaring a task taking no arguments:
|
||||||
|
///
|
||||||
|
/// ``` rust
|
||||||
|
/// #[embassy_executor::task]
|
||||||
|
/// async fn mytask() {
|
||||||
|
/// // Function body
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Declaring a task with a given pool size:
|
||||||
|
///
|
||||||
|
/// ``` rust
|
||||||
|
/// #[embassy_executor::task(pool_size = 4)]
|
||||||
|
/// async fn mytask() {
|
||||||
|
/// // Function body
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||||
@ -14,11 +45,104 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
task::run(args, f).unwrap_or_else(|x| x).into()
|
task::run(args, f).unwrap_or_else(|x| x).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task.
|
||||||
|
///
|
||||||
|
/// The following restrictions apply:
|
||||||
|
///
|
||||||
|
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
|
||||||
|
/// * The function must be declared `async`.
|
||||||
|
/// * The function must not use generics.
|
||||||
|
/// * Only a single `main` task may be declared.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
/// Spawning a task:
|
||||||
|
///
|
||||||
|
/// ``` rust
|
||||||
|
/// #[embassy_executor::main]
|
||||||
|
/// async fn main(_s: embassy_executor::Spawner) {
|
||||||
|
/// // Function body
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let f = syn::parse_macro_input!(item as syn::ItemFn);
|
let f = syn::parse_macro_input!(item as syn::ItemFn);
|
||||||
main::run(args, f).unwrap_or_else(|x| x).into()
|
main::run(args, f, main::cortex_m()).unwrap_or_else(|x| x).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task.
|
||||||
|
///
|
||||||
|
/// The following restrictions apply:
|
||||||
|
///
|
||||||
|
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
|
||||||
|
/// * The function must be declared `async`.
|
||||||
|
/// * The function must not use generics.
|
||||||
|
/// * Only a single `main` task may be declared.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
/// Spawning a task:
|
||||||
|
///
|
||||||
|
/// ``` rust
|
||||||
|
/// #[embassy_executor::main]
|
||||||
|
/// async fn main(_s: embassy_executor::Spawner) {
|
||||||
|
/// // Function body
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||||
|
let f = syn::parse_macro_input!(item as syn::ItemFn);
|
||||||
|
main::run(args, f, main::riscv()).unwrap_or_else(|x| x).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `executor` instance and declares an application entry point for STD spawning the corresponding function body as an async task.
|
||||||
|
///
|
||||||
|
/// The following restrictions apply:
|
||||||
|
///
|
||||||
|
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
|
||||||
|
/// * The function must be declared `async`.
|
||||||
|
/// * The function must not use generics.
|
||||||
|
/// * Only a single `main` task may be declared.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
/// Spawning a task:
|
||||||
|
///
|
||||||
|
/// ``` rust
|
||||||
|
/// #[embassy_executor::main]
|
||||||
|
/// async fn main(_s: embassy_executor::Spawner) {
|
||||||
|
/// // Function body
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||||
|
let f = syn::parse_macro_input!(item as syn::ItemFn);
|
||||||
|
main::run(args, f, main::std()).unwrap_or_else(|x| x).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task.
|
||||||
|
///
|
||||||
|
/// The following restrictions apply:
|
||||||
|
///
|
||||||
|
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
|
||||||
|
/// * The function must be declared `async`.
|
||||||
|
/// * The function must not use generics.
|
||||||
|
/// * Only a single `main` task may be declared.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
/// Spawning a task:
|
||||||
|
///
|
||||||
|
/// ``` rust
|
||||||
|
/// #[embassy_executor::main]
|
||||||
|
/// async fn main(_s: embassy_executor::Spawner) {
|
||||||
|
/// // Function body
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||||
|
let f = syn::parse_macro_input!(item as syn::ItemFn);
|
||||||
|
main::run(args, f, main::wasm()).unwrap_or_else(|x| x).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
|
@ -7,7 +7,62 @@ use crate::util::ctxt::Ctxt;
|
|||||||
#[derive(Debug, FromMeta)]
|
#[derive(Debug, FromMeta)]
|
||||||
struct Args {}
|
struct Args {}
|
||||||
|
|
||||||
pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, TokenStream> {
|
pub fn riscv() -> TokenStream {
|
||||||
|
quote! {
|
||||||
|
#[riscv_rt::entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut executor = ::embassy_executor::Executor::new();
|
||||||
|
let executor = unsafe { __make_static(&mut executor) };
|
||||||
|
executor.run(|spawner| {
|
||||||
|
spawner.must_spawn(__embassy_main(spawner));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cortex_m() -> TokenStream {
|
||||||
|
quote! {
|
||||||
|
#[cortex_m_rt::entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut executor = ::embassy_executor::Executor::new();
|
||||||
|
let executor = unsafe { __make_static(&mut executor) };
|
||||||
|
executor.run(|spawner| {
|
||||||
|
spawner.must_spawn(__embassy_main(spawner));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wasm() -> TokenStream {
|
||||||
|
quote! {
|
||||||
|
#[wasm_bindgen::prelude::wasm_bindgen(start)]
|
||||||
|
pub fn main() -> Result<(), wasm_bindgen::JsValue> {
|
||||||
|
static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new();
|
||||||
|
let executor = EXECUTOR.init(::embassy_executor::Executor::new());
|
||||||
|
|
||||||
|
executor.start(|spawner| {
|
||||||
|
spawner.spawn(__embassy_main(spawner)).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn std() -> TokenStream {
|
||||||
|
quote! {
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut executor = ::embassy_executor::Executor::new();
|
||||||
|
let executor = unsafe { __make_static(&mut executor) };
|
||||||
|
|
||||||
|
executor.run(|spawner| {
|
||||||
|
spawner.must_spawn(__embassy_main(spawner));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Result<TokenStream, TokenStream> {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
let args = Args::from_list(&args).map_err(|e| e.write_errors())?;
|
let args = Args::from_list(&args).map_err(|e| e.write_errors())?;
|
||||||
|
|
||||||
@ -30,46 +85,6 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, Toke
|
|||||||
|
|
||||||
let f_body = f.block;
|
let f_body = f.block;
|
||||||
|
|
||||||
#[cfg(feature = "wasm")]
|
|
||||||
let main = quote! {
|
|
||||||
#[wasm_bindgen::prelude::wasm_bindgen(start)]
|
|
||||||
pub fn main() -> Result<(), wasm_bindgen::JsValue> {
|
|
||||||
static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new();
|
|
||||||
let executor = EXECUTOR.init(::embassy_executor::Executor::new());
|
|
||||||
|
|
||||||
executor.start(|spawner| {
|
|
||||||
spawner.spawn(__embassy_main(spawner)).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(feature = "wasm")))]
|
|
||||||
let main = quote! {
|
|
||||||
fn main() -> ! {
|
|
||||||
let mut executor = ::embassy_executor::Executor::new();
|
|
||||||
let executor = unsafe { __make_static(&mut executor) };
|
|
||||||
|
|
||||||
executor.run(|spawner| {
|
|
||||||
spawner.must_spawn(__embassy_main(spawner));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(not(feature = "std"), not(feature = "wasm")))]
|
|
||||||
let main = quote! {
|
|
||||||
#[cortex_m_rt::entry]
|
|
||||||
fn main() -> ! {
|
|
||||||
let mut executor = ::embassy_executor::Executor::new();
|
|
||||||
let executor = unsafe { __make_static(&mut executor) };
|
|
||||||
|
|
||||||
executor.run(|spawner| {
|
|
||||||
spawner.must_spawn(__embassy_main(spawner));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = quote! {
|
let result = quote! {
|
||||||
#[::embassy_executor::task()]
|
#[::embassy_executor::task()]
|
||||||
async fn __embassy_main(#fargs) {
|
async fn __embassy_main(#fargs) {
|
||||||
|
@ -131,8 +131,12 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
|
|||||||
|
|
||||||
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
|
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
|
||||||
|
|
||||||
|
impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
|
||||||
|
|
||||||
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
|
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
|
||||||
|
|
||||||
|
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
|
||||||
|
|
||||||
impl_timer!(TIMER0, TIMER0, TIMER0);
|
impl_timer!(TIMER0, TIMER0, TIMER0);
|
||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
@ -137,8 +137,12 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
|
|||||||
|
|
||||||
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
|
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
|
||||||
|
|
||||||
|
impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
|
||||||
|
|
||||||
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
|
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
|
||||||
|
|
||||||
|
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
|
||||||
|
|
||||||
impl_pwm!(PWM0, PWM0, PWM0);
|
impl_pwm!(PWM0, PWM0, PWM0);
|
||||||
|
|
||||||
impl_timer!(TIMER0, TIMER0, TIMER0);
|
impl_timer!(TIMER0, TIMER0, TIMER0);
|
||||||
|
@ -138,8 +138,13 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
|
|||||||
impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
|
impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
|
||||||
impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1);
|
impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1);
|
||||||
|
|
||||||
|
impl_spis!(TWISPI0, SPIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
|
||||||
|
impl_spis!(SPI1, SPIS1, SPIM1_SPIS1_SPI1);
|
||||||
|
|
||||||
impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
|
impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
|
||||||
|
|
||||||
|
impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
|
||||||
|
|
||||||
impl_pwm!(PWM0, PWM0, PWM0);
|
impl_pwm!(PWM0, PWM0, PWM0);
|
||||||
|
|
||||||
impl_timer!(TIMER0, TIMER0, TIMER0);
|
impl_timer!(TIMER0, TIMER0, TIMER0);
|
||||||
|
@ -136,9 +136,15 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
|
|||||||
impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
|
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
|
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
impl_timer!(TIMER0, TIMER0, TIMER0);
|
impl_timer!(TIMER0, TIMER0, TIMER0);
|
||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
@ -146,9 +146,16 @@ impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
|||||||
impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
|
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
|
||||||
|
|
||||||
|
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
|
||||||
|
|
||||||
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
|
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
impl_pwm!(PWM0, PWM0, PWM0);
|
impl_pwm!(PWM0, PWM0, PWM0);
|
||||||
impl_pwm!(PWM1, PWM1, PWM1);
|
impl_pwm!(PWM1, PWM1, PWM1);
|
||||||
impl_pwm!(PWM2, PWM2, PWM2);
|
impl_pwm!(PWM2, PWM2, PWM2);
|
||||||
|
@ -174,9 +174,16 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
|||||||
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
|
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
|
||||||
impl_spim!(SPI3, SPIM3, SPIM3);
|
impl_spim!(SPI3, SPIM3, SPIM3);
|
||||||
|
|
||||||
|
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
|
||||||
|
|
||||||
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
|
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
impl_pwm!(PWM0, PWM0, PWM0);
|
impl_pwm!(PWM0, PWM0, PWM0);
|
||||||
impl_pwm!(PWM1, PWM1, PWM1);
|
impl_pwm!(PWM1, PWM1, PWM1);
|
||||||
impl_pwm!(PWM2, PWM2, PWM2);
|
impl_pwm!(PWM2, PWM2, PWM2);
|
||||||
|
@ -177,9 +177,16 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
|||||||
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
|
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
|
||||||
impl_spim!(SPI3, SPIM3, SPIM3);
|
impl_spim!(SPI3, SPIM3, SPIM3);
|
||||||
|
|
||||||
|
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
|
||||||
|
|
||||||
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
|
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
|
||||||
|
|
||||||
impl_pwm!(PWM0, PWM0, PWM0);
|
impl_pwm!(PWM0, PWM0, PWM0);
|
||||||
impl_pwm!(PWM1, PWM1, PWM1);
|
impl_pwm!(PWM1, PWM1, PWM1);
|
||||||
impl_pwm!(PWM2, PWM2, PWM2);
|
impl_pwm!(PWM2, PWM2, PWM2);
|
||||||
|
@ -361,11 +361,21 @@ impl_spim!(UARTETWISPI1, SPIM1, SERIAL1);
|
|||||||
impl_spim!(UARTETWISPI2, SPIM2, SERIAL2);
|
impl_spim!(UARTETWISPI2, SPIM2, SERIAL2);
|
||||||
impl_spim!(UARTETWISPI3, SPIM3, SERIAL3);
|
impl_spim!(UARTETWISPI3, SPIM3, SERIAL3);
|
||||||
|
|
||||||
|
impl_spis!(UARTETWISPI0, SPIS0, SERIAL0);
|
||||||
|
impl_spis!(UARTETWISPI1, SPIS1, SERIAL1);
|
||||||
|
impl_spis!(UARTETWISPI2, SPIS2, SERIAL2);
|
||||||
|
impl_spis!(UARTETWISPI3, SPIS3, SERIAL3);
|
||||||
|
|
||||||
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
|
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
|
||||||
impl_twim!(UARTETWISPI1, TWIM1, SERIAL1);
|
impl_twim!(UARTETWISPI1, TWIM1, SERIAL1);
|
||||||
impl_twim!(UARTETWISPI2, TWIM2, SERIAL2);
|
impl_twim!(UARTETWISPI2, TWIM2, SERIAL2);
|
||||||
impl_twim!(UARTETWISPI3, TWIM3, SERIAL3);
|
impl_twim!(UARTETWISPI3, TWIM3, SERIAL3);
|
||||||
|
|
||||||
|
impl_twis!(UARTETWISPI0, TWIS0, SERIAL0);
|
||||||
|
impl_twis!(UARTETWISPI1, TWIS1, SERIAL1);
|
||||||
|
impl_twis!(UARTETWISPI2, TWIS2, SERIAL2);
|
||||||
|
impl_twis!(UARTETWISPI3, TWIS3, SERIAL3);
|
||||||
|
|
||||||
impl_pwm!(PWM0, PWM0, PWM0);
|
impl_pwm!(PWM0, PWM0, PWM0);
|
||||||
impl_pwm!(PWM1, PWM1, PWM1);
|
impl_pwm!(PWM1, PWM1, PWM1);
|
||||||
impl_pwm!(PWM2, PWM2, PWM2);
|
impl_pwm!(PWM2, PWM2, PWM2);
|
||||||
|
@ -238,7 +238,9 @@ embassy_hal_common::peripherals! {
|
|||||||
|
|
||||||
impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0);
|
impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0);
|
||||||
impl_spim!(UARTETWISPI0, SPIM0, SERIAL0);
|
impl_spim!(UARTETWISPI0, SPIM0, SERIAL0);
|
||||||
|
impl_spis!(UARTETWISPI0, SPIS0, SERIAL0);
|
||||||
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
|
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
|
||||||
|
impl_twis!(UARTETWISPI0, TWIS0, SERIAL0);
|
||||||
|
|
||||||
impl_timer!(TIMER0, TIMER0, TIMER0);
|
impl_timer!(TIMER0, TIMER0, TIMER0);
|
||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
|
@ -275,11 +275,21 @@ impl_spim!(UARTETWISPI1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
|
|||||||
impl_spim!(UARTETWISPI2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
|
impl_spim!(UARTETWISPI2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
|
||||||
impl_spim!(UARTETWISPI3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
|
impl_spim!(UARTETWISPI3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
|
||||||
|
|
||||||
|
impl_spis!(UARTETWISPI0, SPIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
|
||||||
|
impl_spis!(UARTETWISPI1, SPIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
|
||||||
|
impl_spis!(UARTETWISPI2, SPIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
|
||||||
|
impl_spis!(UARTETWISPI3, SPIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
|
||||||
|
|
||||||
impl_twim!(UARTETWISPI0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
|
impl_twim!(UARTETWISPI0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
|
||||||
impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
|
impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
|
||||||
impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
|
impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
|
||||||
impl_twim!(UARTETWISPI3, TWIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
|
impl_twim!(UARTETWISPI3, TWIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
|
||||||
|
|
||||||
|
impl_twis!(UARTETWISPI0, TWIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
|
||||||
|
impl_twis!(UARTETWISPI1, TWIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
|
||||||
|
impl_twis!(UARTETWISPI2, TWIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
|
||||||
|
impl_twis!(UARTETWISPI3, TWIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
|
||||||
|
|
||||||
impl_pwm!(PWM0, PWM0, PWM0);
|
impl_pwm!(PWM0, PWM0, PWM0);
|
||||||
impl_pwm!(PWM1, PWM1, PWM1);
|
impl_pwm!(PWM1, PWM1, PWM1);
|
||||||
impl_pwm!(PWM2, PWM2, PWM2);
|
impl_pwm!(PWM2, PWM2, PWM2);
|
||||||
|
@ -2,7 +2,7 @@ use core::convert::Infallible;
|
|||||||
use core::future::{poll_fn, Future};
|
use core::future::{poll_fn, Future};
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef};
|
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use crate::gpio::sealed::Pin as _;
|
use crate::gpio::sealed::Pin as _;
|
||||||
@ -148,7 +148,7 @@ impl Iterator for BitIter {
|
|||||||
|
|
||||||
/// GPIOTE channel driver in input mode
|
/// GPIOTE channel driver in input mode
|
||||||
pub struct InputChannel<'d, C: Channel, T: GpioPin> {
|
pub struct InputChannel<'d, C: Channel, T: GpioPin> {
|
||||||
ch: C,
|
ch: PeripheralRef<'d, C>,
|
||||||
pin: Input<'d, T>,
|
pin: Input<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +162,9 @@ impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
|
impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
|
||||||
pub fn new(ch: C, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self {
|
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self {
|
||||||
|
into_ref!(ch);
|
||||||
|
|
||||||
let g = regs();
|
let g = regs();
|
||||||
let num = ch.number();
|
let num = ch.number();
|
||||||
|
|
||||||
@ -215,7 +217,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
|
|||||||
|
|
||||||
/// GPIOTE channel driver in output mode
|
/// GPIOTE channel driver in output mode
|
||||||
pub struct OutputChannel<'d, C: Channel, T: GpioPin> {
|
pub struct OutputChannel<'d, C: Channel, T: GpioPin> {
|
||||||
ch: C,
|
ch: PeripheralRef<'d, C>,
|
||||||
_pin: Output<'d, T>,
|
_pin: Output<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +231,8 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
|
impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
|
||||||
pub fn new(ch: C, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
|
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
|
||||||
|
into_ref!(ch);
|
||||||
let g = regs();
|
let g = regs();
|
||||||
let num = ch.number();
|
let num = ch.number();
|
||||||
|
|
||||||
|
@ -96,10 +96,12 @@ pub mod rng;
|
|||||||
#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))]
|
#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))]
|
||||||
pub mod saadc;
|
pub mod saadc;
|
||||||
pub mod spim;
|
pub mod spim;
|
||||||
|
pub mod spis;
|
||||||
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
|
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
|
||||||
pub mod temp;
|
pub mod temp;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod twim;
|
pub mod twim;
|
||||||
|
pub mod twis;
|
||||||
pub mod uarte;
|
pub mod uarte;
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "_nrf5340-app",
|
feature = "_nrf5340-app",
|
||||||
@ -267,5 +269,12 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
#[cfg(feature = "_time-driver")]
|
#[cfg(feature = "_time-driver")]
|
||||||
time_driver::init(config.time_interrupt_priority);
|
time_driver::init(config.time_interrupt_priority);
|
||||||
|
|
||||||
|
// Disable UARTE (enabled by default for some reason)
|
||||||
|
#[cfg(feature = "_nrf9160")]
|
||||||
|
unsafe {
|
||||||
|
(*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled());
|
||||||
|
(*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled());
|
||||||
|
}
|
||||||
|
|
||||||
peripherals
|
peripherals
|
||||||
}
|
}
|
||||||
|
539
embassy-nrf/src/spis.rs
Normal file
539
embassy-nrf/src/spis.rs
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
#![macro_use]
|
||||||
|
use core::future::poll_fn;
|
||||||
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use embassy_embedded_hal::SetConfig;
|
||||||
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
|
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||||
|
|
||||||
|
use crate::chip::FORCE_COPY_BUFFER_SIZE;
|
||||||
|
use crate::gpio::sealed::Pin as _;
|
||||||
|
use crate::gpio::{self, AnyPin, Pin as GpioPin};
|
||||||
|
use crate::interrupt::{Interrupt, InterruptExt};
|
||||||
|
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
|
||||||
|
use crate::{pac, Peripheral};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
TxBufferTooLong,
|
||||||
|
RxBufferTooLong,
|
||||||
|
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
||||||
|
DMABufferNotInDataMemory,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload.
|
||||||
|
///
|
||||||
|
/// For more details about EasyDMA, consult the module documentation.
|
||||||
|
pub struct Spis<'d, T: Instance> {
|
||||||
|
_p: PeripheralRef<'d, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Config {
|
||||||
|
pub mode: Mode,
|
||||||
|
pub orc: u8,
|
||||||
|
pub def: u8,
|
||||||
|
pub auto_acquire: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
mode: MODE_0,
|
||||||
|
orc: 0x00,
|
||||||
|
def: 0x00,
|
||||||
|
auto_acquire: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Spis<'d, T> {
|
||||||
|
pub fn new(
|
||||||
|
spis: impl Peripheral<P = T> + 'd,
|
||||||
|
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||||
|
cs: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
sck: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
miso: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
mosi: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(cs, sck, miso, mosi);
|
||||||
|
Self::new_inner(
|
||||||
|
spis,
|
||||||
|
irq,
|
||||||
|
cs.map_into(),
|
||||||
|
sck.map_into(),
|
||||||
|
Some(miso.map_into()),
|
||||||
|
Some(mosi.map_into()),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_txonly(
|
||||||
|
spis: impl Peripheral<P = T> + 'd,
|
||||||
|
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||||
|
cs: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
sck: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
miso: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(cs, sck, miso);
|
||||||
|
Self::new_inner(
|
||||||
|
spis,
|
||||||
|
irq,
|
||||||
|
cs.map_into(),
|
||||||
|
sck.map_into(),
|
||||||
|
Some(miso.map_into()),
|
||||||
|
None,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_rxonly(
|
||||||
|
spis: impl Peripheral<P = T> + 'd,
|
||||||
|
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||||
|
cs: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
sck: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
mosi: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(cs, sck, mosi);
|
||||||
|
Self::new_inner(
|
||||||
|
spis,
|
||||||
|
irq,
|
||||||
|
cs.map_into(),
|
||||||
|
sck.map_into(),
|
||||||
|
None,
|
||||||
|
Some(mosi.map_into()),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_inner(
|
||||||
|
spis: impl Peripheral<P = T> + 'd,
|
||||||
|
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||||
|
cs: PeripheralRef<'d, AnyPin>,
|
||||||
|
sck: PeripheralRef<'d, AnyPin>,
|
||||||
|
miso: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
|
mosi: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
into_ref!(spis, irq, cs, sck);
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
// Configure pins.
|
||||||
|
sck.conf().write(|w| w.input().connect().drive().h0h1());
|
||||||
|
r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
|
||||||
|
cs.conf().write(|w| w.input().connect().drive().h0h1());
|
||||||
|
r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) });
|
||||||
|
if let Some(mosi) = &mosi {
|
||||||
|
mosi.conf().write(|w| w.input().connect().drive().h0h1());
|
||||||
|
r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) });
|
||||||
|
}
|
||||||
|
if let Some(miso) = &miso {
|
||||||
|
miso.conf().write(|w| w.dir().output().drive().h0h1());
|
||||||
|
r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable SPIS instance.
|
||||||
|
r.enable.write(|w| w.enable().enabled());
|
||||||
|
|
||||||
|
// Configure mode.
|
||||||
|
let mode = config.mode;
|
||||||
|
r.config.write(|w| {
|
||||||
|
match mode {
|
||||||
|
MODE_0 => {
|
||||||
|
w.order().msb_first();
|
||||||
|
w.cpol().active_high();
|
||||||
|
w.cpha().leading();
|
||||||
|
}
|
||||||
|
MODE_1 => {
|
||||||
|
w.order().msb_first();
|
||||||
|
w.cpol().active_high();
|
||||||
|
w.cpha().trailing();
|
||||||
|
}
|
||||||
|
MODE_2 => {
|
||||||
|
w.order().msb_first();
|
||||||
|
w.cpol().active_low();
|
||||||
|
w.cpha().leading();
|
||||||
|
}
|
||||||
|
MODE_3 => {
|
||||||
|
w.order().msb_first();
|
||||||
|
w.cpol().active_low();
|
||||||
|
w.cpha().trailing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set over-read character.
|
||||||
|
let orc = config.orc;
|
||||||
|
r.orc.write(|w| unsafe { w.orc().bits(orc) });
|
||||||
|
|
||||||
|
// Set default character.
|
||||||
|
let def = config.def;
|
||||||
|
r.def.write(|w| unsafe { w.def().bits(def) });
|
||||||
|
|
||||||
|
// Configure auto-acquire on 'transfer end' event.
|
||||||
|
if config.auto_acquire {
|
||||||
|
r.shorts.write(|w| w.end_acquire().bit(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable all events interrupts.
|
||||||
|
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||||
|
|
||||||
|
irq.set_handler(Self::on_interrupt);
|
||||||
|
irq.unpend();
|
||||||
|
irq.enable();
|
||||||
|
|
||||||
|
Self { _p: spis }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt(_: *mut ()) {
|
||||||
|
let r = T::regs();
|
||||||
|
let s = T::state();
|
||||||
|
|
||||||
|
if r.events_end.read().bits() != 0 {
|
||||||
|
s.waker.wake();
|
||||||
|
r.intenclr.write(|w| w.end().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_acquired.read().bits() != 0 {
|
||||||
|
s.waker.wake();
|
||||||
|
r.intenclr.write(|w| w.acquired().clear());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||||
|
slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?;
|
||||||
|
// NOTE: RAM slice check for rx is not necessary, as a mutable
|
||||||
|
// slice can only be built from data located in RAM.
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
// Set up the DMA write.
|
||||||
|
let (ptr, len) = slice_ptr_parts(tx);
|
||||||
|
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||||
|
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||||
|
|
||||||
|
// Set up the DMA read.
|
||||||
|
let (ptr, len) = slice_ptr_parts_mut(rx);
|
||||||
|
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||||
|
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||||
|
|
||||||
|
// Reset end event.
|
||||||
|
r.events_end.reset();
|
||||||
|
|
||||||
|
// Release the semaphore.
|
||||||
|
r.tasks_release.write(|w| unsafe { w.bits(1) });
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
// Acquire semaphore.
|
||||||
|
if r.semstat.read().bits() != 1 {
|
||||||
|
r.events_acquired.reset();
|
||||||
|
r.tasks_acquire.write(|w| unsafe { w.bits(1) });
|
||||||
|
// Wait until CPU has acquired the semaphore.
|
||||||
|
while r.semstat.read().bits() != 1 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.prepare(rx, tx)?;
|
||||||
|
|
||||||
|
// Wait for 'end' event.
|
||||||
|
while r.events_end.read().bits() == 0 {}
|
||||||
|
|
||||||
|
let n_rx = r.rxd.amount.read().bits() as usize;
|
||||||
|
let n_tx = r.txd.amount.read().bits() as usize;
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
Ok((n_rx, n_tx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
|
||||||
|
match self.blocking_inner_from_ram(rx, tx) {
|
||||||
|
Ok(n) => Ok(n),
|
||||||
|
Err(Error::DMABufferNotInDataMemory) => {
|
||||||
|
trace!("Copying SPIS tx buffer into RAM for DMA");
|
||||||
|
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
|
||||||
|
tx_ram_buf.copy_from_slice(tx);
|
||||||
|
self.blocking_inner_from_ram(rx, tx_ram_buf)
|
||||||
|
}
|
||||||
|
Err(error) => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
let s = T::state();
|
||||||
|
|
||||||
|
// Clear status register.
|
||||||
|
r.status.write(|w| w.overflow().clear().overread().clear());
|
||||||
|
|
||||||
|
// Acquire semaphore.
|
||||||
|
if r.semstat.read().bits() != 1 {
|
||||||
|
// Reset and enable the acquire event.
|
||||||
|
r.events_acquired.reset();
|
||||||
|
r.intenset.write(|w| w.acquired().set());
|
||||||
|
|
||||||
|
// Request acquiring the SPIS semaphore.
|
||||||
|
r.tasks_acquire.write(|w| unsafe { w.bits(1) });
|
||||||
|
|
||||||
|
// Wait until CPU has acquired the semaphore.
|
||||||
|
poll_fn(|cx| {
|
||||||
|
s.waker.register(cx.waker());
|
||||||
|
if r.events_acquired.read().bits() == 1 {
|
||||||
|
r.events_acquired.reset();
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.prepare(rx, tx)?;
|
||||||
|
|
||||||
|
// Wait for 'end' event.
|
||||||
|
r.intenset.write(|w| w.end().set());
|
||||||
|
poll_fn(|cx| {
|
||||||
|
s.waker.register(cx.waker());
|
||||||
|
if r.events_end.read().bits() != 0 {
|
||||||
|
r.events_end.reset();
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let n_rx = r.rxd.amount.read().bits() as usize;
|
||||||
|
let n_tx = r.txd.amount.read().bits() as usize;
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
Ok((n_rx, n_tx))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
|
||||||
|
match self.async_inner_from_ram(rx, tx).await {
|
||||||
|
Ok(n) => Ok(n),
|
||||||
|
Err(Error::DMABufferNotInDataMemory) => {
|
||||||
|
trace!("Copying SPIS tx buffer into RAM for DMA");
|
||||||
|
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
|
||||||
|
tx_ram_buf.copy_from_slice(tx);
|
||||||
|
self.async_inner_from_ram(rx, tx_ram_buf).await
|
||||||
|
}
|
||||||
|
Err(error) => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads data from the SPI bus without sending anything. Blocks until `cs` is deasserted.
|
||||||
|
/// Returns number of bytes read.
|
||||||
|
pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
|
||||||
|
self.blocking_inner(data, &[]).map(|n| n.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simultaneously sends and receives data. Blocks until the transmission is completed.
|
||||||
|
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
|
||||||
|
/// Returns number of bytes transferred `(n_rx, n_tx)`.
|
||||||
|
pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
|
||||||
|
self.blocking_inner(read, write)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||||
|
/// Returns number of bytes transferred `(n_rx, n_tx)`.
|
||||||
|
pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
|
||||||
|
self.blocking_inner_from_ram(read, write)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simultaneously sends and receives data.
|
||||||
|
/// Places the received data into the same buffer and blocks until the transmission is completed.
|
||||||
|
/// Returns number of bytes transferred.
|
||||||
|
pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
|
||||||
|
self.blocking_inner_from_ram(data, data).map(|n| n.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends data, discarding any received data. Blocks until the transmission is completed.
|
||||||
|
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
|
||||||
|
/// Returns number of bytes written.
|
||||||
|
pub fn blocking_write(&mut self, data: &[u8]) -> Result<usize, Error> {
|
||||||
|
self.blocking_inner(&mut [], data).map(|n| n.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||||
|
/// Returns number of bytes written.
|
||||||
|
pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
|
||||||
|
self.blocking_inner_from_ram(&mut [], data).map(|n| n.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads data from the SPI bus without sending anything.
|
||||||
|
/// Returns number of bytes read.
|
||||||
|
pub async fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
|
||||||
|
self.async_inner(data, &[]).await.map(|n| n.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simultaneously sends and receives data.
|
||||||
|
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
|
||||||
|
/// Returns number of bytes transferred `(n_rx, n_tx)`.
|
||||||
|
pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
|
||||||
|
self.async_inner(read, write).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||||
|
/// Returns number of bytes transferred `(n_rx, n_tx)`.
|
||||||
|
pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
|
||||||
|
self.async_inner_from_ram(read, write).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simultaneously sends and receives data. Places the received data into the same buffer.
|
||||||
|
/// Returns number of bytes transferred.
|
||||||
|
pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
|
||||||
|
self.async_inner_from_ram(data, data).await.map(|n| n.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends data, discarding any received data.
|
||||||
|
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
|
||||||
|
/// Returns number of bytes written.
|
||||||
|
pub async fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
|
||||||
|
self.async_inner(&mut [], data).await.map(|n| n.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||||
|
/// Returns number of bytes written.
|
||||||
|
pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
|
||||||
|
self.async_inner_from_ram(&mut [], data).await.map(|n| n.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if last transaction overread.
|
||||||
|
pub fn is_overread(&mut self) -> bool {
|
||||||
|
T::regs().status.read().overread().is_present()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if last transaction overflowed.
|
||||||
|
pub fn is_overflow(&mut self) -> bool {
|
||||||
|
T::regs().status.read().overflow().is_present()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Drop for Spis<'d, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
trace!("spis drop");
|
||||||
|
|
||||||
|
// Disable
|
||||||
|
let r = T::regs();
|
||||||
|
r.enable.write(|w| w.enable().disabled());
|
||||||
|
|
||||||
|
gpio::deconfigure_pin(r.psel.sck.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.csn.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.miso.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.mosi.read().bits());
|
||||||
|
|
||||||
|
trace!("spis drop: done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
pub waker: AtomicWaker,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
waker: AtomicWaker::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance {
|
||||||
|
fn regs() -> &'static pac::spis0::RegisterBlock;
|
||||||
|
fn state() -> &'static State;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
|
||||||
|
type Interrupt: Interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_spis {
|
||||||
|
($type:ident, $pac_type:ident, $irq:ident) => {
|
||||||
|
impl crate::spis::sealed::Instance for peripherals::$type {
|
||||||
|
fn regs() -> &'static pac::spis0::RegisterBlock {
|
||||||
|
unsafe { &*pac::$pac_type::ptr() }
|
||||||
|
}
|
||||||
|
fn state() -> &'static crate::spis::sealed::State {
|
||||||
|
static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new();
|
||||||
|
&STATE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl crate::spis::Instance for peripherals::$type {
|
||||||
|
type Interrupt = crate::interrupt::$irq;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================
|
||||||
|
|
||||||
|
impl<'d, T: Instance> SetConfig for Spis<'d, T> {
|
||||||
|
type Config = Config;
|
||||||
|
fn set_config(&mut self, config: &Self::Config) {
|
||||||
|
let r = T::regs();
|
||||||
|
// Configure mode.
|
||||||
|
let mode = config.mode;
|
||||||
|
r.config.write(|w| {
|
||||||
|
match mode {
|
||||||
|
MODE_0 => {
|
||||||
|
w.order().msb_first();
|
||||||
|
w.cpol().active_high();
|
||||||
|
w.cpha().leading();
|
||||||
|
}
|
||||||
|
MODE_1 => {
|
||||||
|
w.order().msb_first();
|
||||||
|
w.cpol().active_high();
|
||||||
|
w.cpha().trailing();
|
||||||
|
}
|
||||||
|
MODE_2 => {
|
||||||
|
w.order().msb_first();
|
||||||
|
w.cpol().active_low();
|
||||||
|
w.cpha().leading();
|
||||||
|
}
|
||||||
|
MODE_3 => {
|
||||||
|
w.order().msb_first();
|
||||||
|
w.cpol().active_low();
|
||||||
|
w.cpha().trailing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set over-read character.
|
||||||
|
let orc = config.orc;
|
||||||
|
r.orc.write(|w| unsafe { w.orc().bits(orc) });
|
||||||
|
|
||||||
|
// Set default character.
|
||||||
|
let def = config.def;
|
||||||
|
r.def.write(|w| unsafe { w.def().bits(def) });
|
||||||
|
|
||||||
|
// Configure auto-acquire on 'transfer end' event.
|
||||||
|
let auto_acquire = config.auto_acquire;
|
||||||
|
r.shorts.write(|w| w.end_acquire().bit(auto_acquire));
|
||||||
|
}
|
||||||
|
}
|
759
embassy-nrf/src/twis.rs
Normal file
759
embassy-nrf/src/twis.rs
Normal file
@ -0,0 +1,759 @@
|
|||||||
|
#![macro_use]
|
||||||
|
|
||||||
|
//! HAL interface to the TWIS peripheral.
|
||||||
|
//!
|
||||||
|
//! See product specification:
|
||||||
|
//!
|
||||||
|
//! - nRF52832: Section 33
|
||||||
|
//! - nRF52840: Section 6.31
|
||||||
|
use core::future::{poll_fn, Future};
|
||||||
|
use core::sync::atomic::compiler_fence;
|
||||||
|
use core::sync::atomic::Ordering::SeqCst;
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
use embassy_time::{Duration, Instant};
|
||||||
|
|
||||||
|
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||||
|
use crate::gpio::Pin as GpioPin;
|
||||||
|
use crate::interrupt::{Interrupt, InterruptExt};
|
||||||
|
use crate::util::slice_in_ram_or;
|
||||||
|
use crate::{gpio, pac, Peripheral};
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Config {
|
||||||
|
pub address0: u8,
|
||||||
|
pub address1: Option<u8>,
|
||||||
|
pub orc: u8,
|
||||||
|
pub sda_high_drive: bool,
|
||||||
|
pub sda_pullup: bool,
|
||||||
|
pub scl_high_drive: bool,
|
||||||
|
pub scl_pullup: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
address0: 0x55,
|
||||||
|
address1: None,
|
||||||
|
orc: 0x00,
|
||||||
|
scl_high_drive: false,
|
||||||
|
sda_pullup: false,
|
||||||
|
sda_high_drive: false,
|
||||||
|
scl_pullup: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
enum Status {
|
||||||
|
Read,
|
||||||
|
Write,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
TxBufferTooLong,
|
||||||
|
RxBufferTooLong,
|
||||||
|
DataNack,
|
||||||
|
Bus,
|
||||||
|
DMABufferNotInDataMemory,
|
||||||
|
Overflow,
|
||||||
|
OverRead,
|
||||||
|
Timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Command {
|
||||||
|
Read,
|
||||||
|
WriteRead(usize),
|
||||||
|
Write(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interface to a TWIS instance using EasyDMA to offload the transmission and reception workload.
|
||||||
|
///
|
||||||
|
/// For more details about EasyDMA, consult the module documentation.
|
||||||
|
pub struct Twis<'d, T: Instance> {
|
||||||
|
_p: PeripheralRef<'d, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Twis<'d, T> {
|
||||||
|
pub fn new(
|
||||||
|
twis: impl Peripheral<P = T> + 'd,
|
||||||
|
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||||
|
sda: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
scl: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(twis, irq, sda, scl);
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
// Configure pins
|
||||||
|
sda.conf().write(|w| {
|
||||||
|
w.dir().input();
|
||||||
|
w.input().connect();
|
||||||
|
if config.sda_high_drive {
|
||||||
|
w.drive().h0d1();
|
||||||
|
} else {
|
||||||
|
w.drive().s0d1();
|
||||||
|
}
|
||||||
|
if config.sda_pullup {
|
||||||
|
w.pull().pullup();
|
||||||
|
}
|
||||||
|
w
|
||||||
|
});
|
||||||
|
scl.conf().write(|w| {
|
||||||
|
w.dir().input();
|
||||||
|
w.input().connect();
|
||||||
|
if config.scl_high_drive {
|
||||||
|
w.drive().h0d1();
|
||||||
|
} else {
|
||||||
|
w.drive().s0d1();
|
||||||
|
}
|
||||||
|
if config.scl_pullup {
|
||||||
|
w.pull().pullup();
|
||||||
|
}
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select pins.
|
||||||
|
r.psel.sda.write(|w| unsafe { w.bits(sda.psel_bits()) });
|
||||||
|
r.psel.scl.write(|w| unsafe { w.bits(scl.psel_bits()) });
|
||||||
|
|
||||||
|
// Enable TWIS instance.
|
||||||
|
r.enable.write(|w| w.enable().enabled());
|
||||||
|
|
||||||
|
// Disable all events interrupts
|
||||||
|
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||||
|
|
||||||
|
// Set address
|
||||||
|
r.address[0].write(|w| unsafe { w.address().bits(config.address0) });
|
||||||
|
r.config.write(|w| w.address0().enabled());
|
||||||
|
if let Some(address1) = config.address1 {
|
||||||
|
r.address[1].write(|w| unsafe { w.address().bits(address1) });
|
||||||
|
r.config.modify(|_r, w| w.address1().enabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set over-read character
|
||||||
|
r.orc.write(|w| unsafe { w.orc().bits(config.orc) });
|
||||||
|
|
||||||
|
// Generate suspend on read event
|
||||||
|
r.shorts.write(|w| w.read_suspend().enabled());
|
||||||
|
|
||||||
|
irq.set_handler(Self::on_interrupt);
|
||||||
|
irq.unpend();
|
||||||
|
irq.enable();
|
||||||
|
|
||||||
|
Self { _p: twis }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt(_: *mut ()) {
|
||||||
|
let r = T::regs();
|
||||||
|
let s = T::state();
|
||||||
|
|
||||||
|
if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 {
|
||||||
|
s.waker.wake();
|
||||||
|
r.intenclr.modify(|_r, w| w.read().clear().write().clear());
|
||||||
|
}
|
||||||
|
if r.events_stopped.read().bits() != 0 {
|
||||||
|
s.waker.wake();
|
||||||
|
r.intenclr.modify(|_r, w| w.stopped().clear());
|
||||||
|
}
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
s.waker.wake();
|
||||||
|
r.intenclr.modify(|_r, w| w.error().clear());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set TX buffer, checking that it is in RAM and has suitable length.
|
||||||
|
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||||
|
slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?;
|
||||||
|
|
||||||
|
if buffer.len() > EASY_DMA_SIZE {
|
||||||
|
return Err(Error::TxBufferTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
r.txd.ptr.write(|w|
|
||||||
|
// We're giving the register a pointer to the stack. Since we're
|
||||||
|
// waiting for the I2C transaction to end before this stack pointer
|
||||||
|
// becomes invalid, there's nothing wrong here.
|
||||||
|
//
|
||||||
|
// The PTR field is a full 32 bits wide and accepts the full range
|
||||||
|
// of values.
|
||||||
|
w.ptr().bits(buffer.as_ptr() as u32));
|
||||||
|
r.txd.maxcnt.write(|w|
|
||||||
|
// We're giving it the length of the buffer, so no danger of
|
||||||
|
// accessing invalid memory. We have verified that the length of the
|
||||||
|
// buffer fits in an `u8`, so the cast to `u8` is also fine.
|
||||||
|
//
|
||||||
|
// The MAXCNT field is 8 bits wide and accepts the full range of
|
||||||
|
// values.
|
||||||
|
w.maxcnt().bits(buffer.len() as _));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set RX buffer, checking that it has suitable length.
|
||||||
|
unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
// NOTE: RAM slice check is not necessary, as a mutable
|
||||||
|
// slice can only be built from data located in RAM.
|
||||||
|
|
||||||
|
if buffer.len() > EASY_DMA_SIZE {
|
||||||
|
return Err(Error::RxBufferTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
r.rxd.ptr.write(|w|
|
||||||
|
// We're giving the register a pointer to the stack. Since we're
|
||||||
|
// waiting for the I2C transaction to end before this stack pointer
|
||||||
|
// becomes invalid, there's nothing wrong here.
|
||||||
|
//
|
||||||
|
// The PTR field is a full 32 bits wide and accepts the full range
|
||||||
|
// of values.
|
||||||
|
w.ptr().bits(buffer.as_mut_ptr() as u32));
|
||||||
|
r.rxd.maxcnt.write(|w|
|
||||||
|
// We're giving it the length of the buffer, so no danger of
|
||||||
|
// accessing invalid memory. We have verified that the length of the
|
||||||
|
// buffer fits in an `u8`, so the cast to the type of maxcnt
|
||||||
|
// is also fine.
|
||||||
|
//
|
||||||
|
// Note that that nrf52840 maxcnt is a wider
|
||||||
|
// type than a u8, so we use a `_` cast rather than a `u8` cast.
|
||||||
|
// The MAXCNT field is thus at least 8 bits wide and accepts the
|
||||||
|
// full range of values that fit in a `u8`.
|
||||||
|
w.maxcnt().bits(buffer.len() as _));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_errorsrc(&mut self) {
|
||||||
|
let r = T::regs();
|
||||||
|
r.errorsrc
|
||||||
|
.write(|w| w.overflow().bit(true).overread().bit(true).dnack().bit(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns matched address for latest command.
|
||||||
|
pub fn address_match(&self) -> u8 {
|
||||||
|
let r = T::regs();
|
||||||
|
r.address[r.match_.read().bits() as usize].read().address().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the index of the address matched in the latest command.
|
||||||
|
pub fn address_match_index(&self) -> usize {
|
||||||
|
T::regs().match_.read().bits() as _
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for read, write, stop or error
|
||||||
|
fn blocking_listen_wait(&mut self) -> Result<Status, Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
loop {
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
while r.events_stopped.read().bits() == 0 {}
|
||||||
|
return Err(Error::Overflow);
|
||||||
|
}
|
||||||
|
if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
return Err(Error::Bus);
|
||||||
|
}
|
||||||
|
if r.events_read.read().bits() != 0 {
|
||||||
|
r.events_read.reset();
|
||||||
|
return Ok(Status::Read);
|
||||||
|
}
|
||||||
|
if r.events_write.read().bits() != 0 {
|
||||||
|
r.events_write.reset();
|
||||||
|
return Ok(Status::Write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for stop, repeated start or error
|
||||||
|
fn blocking_listen_wait_end(&mut self, status: Status) -> Result<Command, Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
loop {
|
||||||
|
// stop if an error occured
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
return Err(Error::Overflow);
|
||||||
|
} else if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
return match status {
|
||||||
|
Status::Read => Ok(Command::Read),
|
||||||
|
Status::Write => {
|
||||||
|
let n = r.rxd.amount.read().bits() as usize;
|
||||||
|
Ok(Command::Write(n))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if r.events_read.read().bits() != 0 {
|
||||||
|
r.events_read.reset();
|
||||||
|
let n = r.rxd.amount.read().bits() as usize;
|
||||||
|
return Ok(Command::WriteRead(n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for stop or error
|
||||||
|
fn blocking_wait(&mut self) -> Result<usize, Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
loop {
|
||||||
|
// stop if an error occured
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
let errorsrc = r.errorsrc.read();
|
||||||
|
if errorsrc.overread().is_detected() {
|
||||||
|
return Err(Error::OverRead);
|
||||||
|
} else if errorsrc.dnack().is_received() {
|
||||||
|
return Err(Error::DataNack);
|
||||||
|
} else {
|
||||||
|
return Err(Error::Bus);
|
||||||
|
}
|
||||||
|
} else if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
let n = r.txd.amount.read().bits() as usize;
|
||||||
|
return Ok(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for stop or error with timeout
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<usize, Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
let deadline = Instant::now() + timeout;
|
||||||
|
loop {
|
||||||
|
// stop if an error occured
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
let errorsrc = r.errorsrc.read();
|
||||||
|
if errorsrc.overread().is_detected() {
|
||||||
|
return Err(Error::OverRead);
|
||||||
|
} else if errorsrc.dnack().is_received() {
|
||||||
|
return Err(Error::DataNack);
|
||||||
|
} else {
|
||||||
|
return Err(Error::Bus);
|
||||||
|
}
|
||||||
|
} else if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
let n = r.txd.amount.read().bits() as usize;
|
||||||
|
return Ok(n);
|
||||||
|
} else if Instant::now() > deadline {
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
return Err(Error::Timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for read, write, stop or error with timeout
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result<Status, Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
let deadline = Instant::now() + timeout;
|
||||||
|
loop {
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
while r.events_stopped.read().bits() == 0 {}
|
||||||
|
return Err(Error::Overflow);
|
||||||
|
}
|
||||||
|
if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
return Err(Error::Bus);
|
||||||
|
}
|
||||||
|
if r.events_read.read().bits() != 0 {
|
||||||
|
r.events_read.reset();
|
||||||
|
return Ok(Status::Read);
|
||||||
|
}
|
||||||
|
if r.events_write.read().bits() != 0 {
|
||||||
|
r.events_write.reset();
|
||||||
|
return Ok(Status::Write);
|
||||||
|
}
|
||||||
|
if Instant::now() > deadline {
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
return Err(Error::Timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for stop, repeated start or error with timeout
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result<Command, Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
let deadline = Instant::now() + timeout;
|
||||||
|
loop {
|
||||||
|
// stop if an error occured
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
return Err(Error::Overflow);
|
||||||
|
} else if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
return match status {
|
||||||
|
Status::Read => Ok(Command::Read),
|
||||||
|
Status::Write => {
|
||||||
|
let n = r.rxd.amount.read().bits() as usize;
|
||||||
|
Ok(Command::Write(n))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if r.events_read.read().bits() != 0 {
|
||||||
|
r.events_read.reset();
|
||||||
|
let n = r.rxd.amount.read().bits() as usize;
|
||||||
|
return Ok(Command::WriteRead(n));
|
||||||
|
} else if Instant::now() > deadline {
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
return Err(Error::Timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for stop or error
|
||||||
|
fn async_wait(&mut self) -> impl Future<Output = Result<usize, Error>> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
let r = T::regs();
|
||||||
|
let s = T::state();
|
||||||
|
|
||||||
|
s.waker.register(cx.waker());
|
||||||
|
|
||||||
|
// stop if an error occured
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
let errorsrc = r.errorsrc.read();
|
||||||
|
if errorsrc.overread().is_detected() {
|
||||||
|
return Poll::Ready(Err(Error::OverRead));
|
||||||
|
} else if errorsrc.dnack().is_received() {
|
||||||
|
return Poll::Ready(Err(Error::DataNack));
|
||||||
|
} else {
|
||||||
|
return Poll::Ready(Err(Error::Bus));
|
||||||
|
}
|
||||||
|
} else if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
let n = r.txd.amount.read().bits() as usize;
|
||||||
|
return Poll::Ready(Ok(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for read or write
|
||||||
|
fn async_listen_wait(&mut self) -> impl Future<Output = Result<Status, Error>> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
let r = T::regs();
|
||||||
|
let s = T::state();
|
||||||
|
|
||||||
|
s.waker.register(cx.waker());
|
||||||
|
|
||||||
|
// stop if an error occured
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
return Poll::Ready(Err(Error::Overflow));
|
||||||
|
} else if r.events_read.read().bits() != 0 {
|
||||||
|
r.events_read.reset();
|
||||||
|
return Poll::Ready(Ok(Status::Read));
|
||||||
|
} else if r.events_write.read().bits() != 0 {
|
||||||
|
r.events_write.reset();
|
||||||
|
return Poll::Ready(Ok(Status::Write));
|
||||||
|
} else if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
return Poll::Ready(Err(Error::Bus));
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for stop, repeated start or error
|
||||||
|
fn async_listen_wait_end(&mut self, status: Status) -> impl Future<Output = Result<Command, Error>> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
let r = T::regs();
|
||||||
|
let s = T::state();
|
||||||
|
|
||||||
|
s.waker.register(cx.waker());
|
||||||
|
|
||||||
|
// stop if an error occured
|
||||||
|
if r.events_error.read().bits() != 0 {
|
||||||
|
r.events_error.reset();
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
return Poll::Ready(Err(Error::Overflow));
|
||||||
|
} else if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.events_stopped.reset();
|
||||||
|
return match status {
|
||||||
|
Status::Read => Poll::Ready(Ok(Command::Read)),
|
||||||
|
Status::Write => {
|
||||||
|
let n = r.rxd.amount.read().bits() as usize;
|
||||||
|
Poll::Ready(Ok(Command::Write(n)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if r.events_read.read().bits() != 0 {
|
||||||
|
r.events_read.reset();
|
||||||
|
let n = r.rxd.amount.read().bits() as usize;
|
||||||
|
return Poll::Ready(Ok(Command::WriteRead(n)));
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
compiler_fence(SeqCst);
|
||||||
|
|
||||||
|
// Set up the DMA write.
|
||||||
|
unsafe { self.set_tx_buffer(buffer)? };
|
||||||
|
|
||||||
|
// Clear events
|
||||||
|
r.events_stopped.reset();
|
||||||
|
r.events_error.reset();
|
||||||
|
self.clear_errorsrc();
|
||||||
|
|
||||||
|
if inten {
|
||||||
|
r.intenset.write(|w| w.stopped().set().error().set());
|
||||||
|
} else {
|
||||||
|
r.intenclr.write(|w| w.stopped().clear().error().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start write operation.
|
||||||
|
r.tasks_preparetx.write(|w| unsafe { w.bits(1) });
|
||||||
|
r.tasks_resume.write(|w| unsafe { w.bits(1) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> {
|
||||||
|
match self.setup_respond_from_ram(wr_buffer, inten) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(Error::DMABufferNotInDataMemory) => {
|
||||||
|
trace!("Copying TWIS tx buffer into RAM for DMA");
|
||||||
|
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
|
||||||
|
tx_ram_buf.copy_from_slice(wr_buffer);
|
||||||
|
self.setup_respond_from_ram(&tx_ram_buf, inten)
|
||||||
|
}
|
||||||
|
Err(error) => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_listen(&mut self, buffer: &mut [u8], inten: bool) -> Result<(), Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
compiler_fence(SeqCst);
|
||||||
|
|
||||||
|
// Set up the DMA read.
|
||||||
|
unsafe { self.set_rx_buffer(buffer)? };
|
||||||
|
|
||||||
|
// Clear events
|
||||||
|
r.events_read.reset();
|
||||||
|
r.events_write.reset();
|
||||||
|
r.events_stopped.reset();
|
||||||
|
r.events_error.reset();
|
||||||
|
self.clear_errorsrc();
|
||||||
|
|
||||||
|
if inten {
|
||||||
|
r.intenset
|
||||||
|
.write(|w| w.stopped().set().error().set().read().set().write().set());
|
||||||
|
} else {
|
||||||
|
r.intenclr
|
||||||
|
.write(|w| w.stopped().clear().error().clear().read().clear().write().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start read operation.
|
||||||
|
r.tasks_preparerx.write(|w| unsafe { w.bits(1) });
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_listen_end(&mut self, inten: bool) -> Result<(), Error> {
|
||||||
|
let r = T::regs();
|
||||||
|
compiler_fence(SeqCst);
|
||||||
|
|
||||||
|
// Clear events
|
||||||
|
r.events_read.reset();
|
||||||
|
r.events_write.reset();
|
||||||
|
r.events_stopped.reset();
|
||||||
|
r.events_error.reset();
|
||||||
|
self.clear_errorsrc();
|
||||||
|
|
||||||
|
if inten {
|
||||||
|
r.intenset.write(|w| w.stopped().set().error().set().read().set());
|
||||||
|
} else {
|
||||||
|
r.intenclr.write(|w| w.stopped().clear().error().clear().read().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for commands from an I2C master.
|
||||||
|
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
|
||||||
|
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||||
|
/// and at most 65535 bytes on the nRF52840.
|
||||||
|
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
|
||||||
|
pub fn blocking_listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
|
||||||
|
self.setup_listen(buffer, false)?;
|
||||||
|
let status = self.blocking_listen_wait()?;
|
||||||
|
if status == Status::Write {
|
||||||
|
self.setup_listen_end(false)?;
|
||||||
|
let command = self.blocking_listen_wait_end(status)?;
|
||||||
|
return Ok(command);
|
||||||
|
}
|
||||||
|
Ok(Command::Read)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Respond to an I2C master READ command.
|
||||||
|
/// Returns the number of bytes written.
|
||||||
|
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||||
|
/// and at most 65535 bytes on the nRF52840.
|
||||||
|
pub fn blocking_respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {
|
||||||
|
self.setup_respond(buffer, false)?;
|
||||||
|
self.blocking_wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [`blocking_respond_to_read`](Twis::blocking_respond_to_read) but will fail instead of copying data into RAM.
|
||||||
|
/// Consult the module level documentation to learn more.
|
||||||
|
pub fn blocking_respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {
|
||||||
|
self.setup_respond_from_ram(buffer, false)?;
|
||||||
|
self.blocking_wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================
|
||||||
|
|
||||||
|
/// Wait for commands from an I2C master, with timeout.
|
||||||
|
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
|
||||||
|
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||||
|
/// and at most 65535 bytes on the nRF52840.
|
||||||
|
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
pub fn blocking_listen_timeout(&mut self, buffer: &mut [u8], timeout: Duration) -> Result<Command, Error> {
|
||||||
|
self.setup_listen(buffer, false)?;
|
||||||
|
let status = self.blocking_listen_wait_timeout(timeout)?;
|
||||||
|
if status == Status::Write {
|
||||||
|
self.setup_listen_end(false)?;
|
||||||
|
let command = self.blocking_listen_wait_end_timeout(status, timeout)?;
|
||||||
|
return Ok(command);
|
||||||
|
}
|
||||||
|
Ok(Command::Read)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Respond to an I2C master READ command with timeout.
|
||||||
|
/// Returns the number of bytes written.
|
||||||
|
/// See [`blocking_respond_to_read`].
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
pub fn blocking_respond_to_read_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<usize, Error> {
|
||||||
|
self.setup_respond(buffer, false)?;
|
||||||
|
self.blocking_wait_timeout(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [`blocking_respond_to_read_timeout`](Twis::blocking_respond_to_read_timeout) but will fail instead of copying data into RAM.
|
||||||
|
/// Consult the module level documentation to learn more.
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
pub fn blocking_respond_to_read_from_ram_timeout(
|
||||||
|
&mut self,
|
||||||
|
buffer: &[u8],
|
||||||
|
timeout: Duration,
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
self.setup_respond_from_ram(buffer, false)?;
|
||||||
|
self.blocking_wait_timeout(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================
|
||||||
|
|
||||||
|
/// Wait asynchronously for commands from an I2C master.
|
||||||
|
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
|
||||||
|
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||||
|
/// and at most 65535 bytes on the nRF52840.
|
||||||
|
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
|
||||||
|
pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
|
||||||
|
self.setup_listen(buffer, true)?;
|
||||||
|
let status = self.async_listen_wait().await?;
|
||||||
|
if status == Status::Write {
|
||||||
|
self.setup_listen_end(true)?;
|
||||||
|
let command = self.async_listen_wait_end(status).await?;
|
||||||
|
return Ok(command);
|
||||||
|
}
|
||||||
|
Ok(Command::Read)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Respond to an I2C master READ command, asynchronously.
|
||||||
|
/// Returns the number of bytes written.
|
||||||
|
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||||
|
/// and at most 65535 bytes on the nRF52840.
|
||||||
|
pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {
|
||||||
|
self.setup_respond(buffer, true)?;
|
||||||
|
self.async_wait().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [`respond_to_read`](Twis::respond_to_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||||
|
pub async fn respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {
|
||||||
|
self.setup_respond_from_ram(buffer, true)?;
|
||||||
|
self.async_wait().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Instance> Drop for Twis<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
trace!("twis drop");
|
||||||
|
|
||||||
|
// TODO: check for abort
|
||||||
|
|
||||||
|
// disable!
|
||||||
|
let r = T::regs();
|
||||||
|
r.enable.write(|w| w.enable().disabled());
|
||||||
|
|
||||||
|
gpio::deconfigure_pin(r.psel.sda.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.scl.read().bits());
|
||||||
|
|
||||||
|
trace!("twis drop: done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
pub waker: AtomicWaker,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
waker: AtomicWaker::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance {
|
||||||
|
fn regs() -> &'static pac::twis0::RegisterBlock;
|
||||||
|
fn state() -> &'static State;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
|
||||||
|
type Interrupt: Interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_twis {
|
||||||
|
($type:ident, $pac_type:ident, $irq:ident) => {
|
||||||
|
impl crate::twis::sealed::Instance for peripherals::$type {
|
||||||
|
fn regs() -> &'static pac::twis0::RegisterBlock {
|
||||||
|
unsafe { &*pac::$pac_type::ptr() }
|
||||||
|
}
|
||||||
|
fn state() -> &'static crate::twis::sealed::State {
|
||||||
|
static STATE: crate::twis::sealed::State = crate::twis::sealed::State::new();
|
||||||
|
&STATE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl crate::twis::Instance for peripherals::$type {
|
||||||
|
type Interrupt = crate::interrupt::$irq;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
173
embassy-rp/src/adc.rs
Normal file
173
embassy-rp/src/adc.rs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
use core::future::poll_fn;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use embassy_hal_common::into_ref;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embedded_hal_02::adc::{Channel, OneShot};
|
||||||
|
|
||||||
|
use crate::interrupt::{self, InterruptExt};
|
||||||
|
use crate::peripherals::ADC;
|
||||||
|
use crate::{pac, peripherals, Peripheral};
|
||||||
|
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
// No errors for now
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Config {}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct Adc<'d> {
|
||||||
|
phantom: PhantomData<&'d ADC>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Adc<'d> {
|
||||||
|
#[inline]
|
||||||
|
fn regs() -> pac::adc::Adc {
|
||||||
|
pac::ADC
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn reset() -> pac::resets::regs::Peripherals {
|
||||||
|
let mut ret = pac::resets::regs::Peripherals::default();
|
||||||
|
ret.set_adc(true);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
_inner: impl Peripheral<P = ADC> + 'd,
|
||||||
|
irq: impl Peripheral<P = interrupt::ADC_IRQ_FIFO> + 'd,
|
||||||
|
_config: Config,
|
||||||
|
) -> Self {
|
||||||
|
into_ref!(irq);
|
||||||
|
unsafe {
|
||||||
|
let reset = Self::reset();
|
||||||
|
crate::reset::reset(reset);
|
||||||
|
crate::reset::unreset_wait(reset);
|
||||||
|
let r = Self::regs();
|
||||||
|
// Enable ADC
|
||||||
|
r.cs().write(|w| w.set_en(true));
|
||||||
|
// Wait for ADC ready
|
||||||
|
while !r.cs().read().ready() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup IRQ
|
||||||
|
irq.disable();
|
||||||
|
irq.set_handler(|_| unsafe {
|
||||||
|
let r = Self::regs();
|
||||||
|
r.inte().write(|w| w.set_fifo(false));
|
||||||
|
WAKER.wake();
|
||||||
|
});
|
||||||
|
irq.unpend();
|
||||||
|
irq.enable();
|
||||||
|
|
||||||
|
Self { phantom: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_ready() {
|
||||||
|
let r = Self::regs();
|
||||||
|
unsafe {
|
||||||
|
r.inte().write(|w| w.set_fifo(true));
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
poll_fn(|cx| {
|
||||||
|
WAKER.register(cx.waker());
|
||||||
|
if r.cs().read().ready() {
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read<PIN: Channel<Adc<'d>, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 {
|
||||||
|
let r = Self::regs();
|
||||||
|
unsafe {
|
||||||
|
r.cs().modify(|w| {
|
||||||
|
w.set_ainsel(PIN::channel());
|
||||||
|
w.set_start_once(true)
|
||||||
|
});
|
||||||
|
Self::wait_for_ready().await;
|
||||||
|
r.result().read().result().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_temperature(&mut self) -> u16 {
|
||||||
|
let r = Self::regs();
|
||||||
|
unsafe {
|
||||||
|
r.cs().modify(|w| w.set_ts_en(true));
|
||||||
|
if !r.cs().read().ready() {
|
||||||
|
Self::wait_for_ready().await;
|
||||||
|
}
|
||||||
|
r.cs().modify(|w| {
|
||||||
|
w.set_ainsel(4);
|
||||||
|
w.set_start_once(true)
|
||||||
|
});
|
||||||
|
Self::wait_for_ready().await;
|
||||||
|
r.result().read().result().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 {
|
||||||
|
let r = Self::regs();
|
||||||
|
unsafe {
|
||||||
|
r.cs().modify(|w| {
|
||||||
|
w.set_ainsel(PIN::channel());
|
||||||
|
w.set_start_once(true)
|
||||||
|
});
|
||||||
|
while !r.cs().read().ready() {}
|
||||||
|
r.result().read().result().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocking_read_temperature(&mut self) -> u16 {
|
||||||
|
let r = Self::regs();
|
||||||
|
unsafe {
|
||||||
|
r.cs().modify(|w| w.set_ts_en(true));
|
||||||
|
while !r.cs().read().ready() {}
|
||||||
|
r.cs().modify(|w| {
|
||||||
|
w.set_ainsel(4);
|
||||||
|
w.set_start_once(true)
|
||||||
|
});
|
||||||
|
while !r.cs().read().ready() {}
|
||||||
|
r.result().read().result().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_pin {
|
||||||
|
($pin:ident, $channel:expr) => {
|
||||||
|
impl Channel<Adc<'static>> for peripherals::$pin {
|
||||||
|
type ID = u8;
|
||||||
|
fn channel() -> u8 {
|
||||||
|
$channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_pin!(PIN_26, 0);
|
||||||
|
impl_pin!(PIN_27, 1);
|
||||||
|
impl_pin!(PIN_28, 2);
|
||||||
|
impl_pin!(PIN_29, 3);
|
||||||
|
|
||||||
|
impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static>
|
||||||
|
where
|
||||||
|
WORD: From<u16>,
|
||||||
|
PIN: Channel<Adc<'static>, ID = u8>,
|
||||||
|
{
|
||||||
|
type Error = ();
|
||||||
|
fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
|
||||||
|
Ok(self.blocking_read(pin).into())
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ pub(crate) mod fmt;
|
|||||||
|
|
||||||
mod intrinsics;
|
mod intrinsics;
|
||||||
|
|
||||||
|
pub mod adc;
|
||||||
pub mod dma;
|
pub mod dma;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
@ -98,6 +99,8 @@ embassy_hal_common::peripherals! {
|
|||||||
RTC,
|
RTC,
|
||||||
|
|
||||||
FLASH,
|
FLASH,
|
||||||
|
|
||||||
|
ADC,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link_section = ".boot2"]
|
#[link_section = ".boot2"]
|
||||||
|
@ -71,7 +71,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
|
|
||||||
#[cfg(adc_g0)]
|
#[cfg(adc_g0)]
|
||||||
T::regs().cfgr1().modify(|reg| {
|
T::regs().cfgr1().modify(|reg| {
|
||||||
reg.set_chselrmod(true);
|
reg.set_chselrmod(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +200,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
#[cfg(not(stm32g0))]
|
#[cfg(not(stm32g0))]
|
||||||
T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
|
T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
|
||||||
#[cfg(stm32g0)]
|
#[cfg(stm32g0)]
|
||||||
T::regs().chselr().write(|reg| reg.set_chsel(pin.channel() as u32));
|
T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel()));
|
||||||
|
|
||||||
// Some models are affected by an erratum:
|
// Some models are affected by an erratum:
|
||||||
// If we perform conversions slower than 1 kHz, the first read ADC value can be
|
// If we perform conversions slower than 1 kHz, the first read ADC value can be
|
||||||
|
@ -116,6 +116,24 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T,
|
|||||||
|
|
||||||
mac.macqtx_fcr().modify(|w| w.set_pt(0x100));
|
mac.macqtx_fcr().modify(|w| w.set_pt(0x100));
|
||||||
|
|
||||||
|
// disable all MMC RX interrupts
|
||||||
|
mac.mmc_rx_interrupt_mask().write(|w| {
|
||||||
|
w.set_rxcrcerpim(true);
|
||||||
|
w.set_rxalgnerpim(true);
|
||||||
|
w.set_rxucgpim(true);
|
||||||
|
w.set_rxlpiuscim(true);
|
||||||
|
w.set_rxlpitrcim(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
// disable all MMC TX interrupts
|
||||||
|
mac.mmc_tx_interrupt_mask().write(|w| {
|
||||||
|
w.set_txscolgpim(true);
|
||||||
|
w.set_txmcolgpim(true);
|
||||||
|
w.set_txgpktim(true);
|
||||||
|
w.set_txlpiuscim(true);
|
||||||
|
w.set_txlpitrcim(true);
|
||||||
|
});
|
||||||
|
|
||||||
mtl.mtlrx_qomr().modify(|w| w.set_rsf(true));
|
mtl.mtlrx_qomr().modify(|w| w.set_rsf(true));
|
||||||
mtl.mtltx_qomr().modify(|w| w.set_tsf(true));
|
mtl.mtltx_qomr().modify(|w| w.set_tsf(true));
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ use core::task::Poll;
|
|||||||
|
|
||||||
use atomic_polyfill::{compiler_fence, Ordering};
|
use atomic_polyfill::{compiler_fence, Ordering};
|
||||||
use embassy_cortex_m::interrupt::InterruptExt;
|
use embassy_cortex_m::interrupt::InterruptExt;
|
||||||
|
use embassy_futures::select::{select, Either};
|
||||||
|
use embassy_hal_common::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::dma::NoDma;
|
use crate::dma::NoDma;
|
||||||
@ -85,6 +87,13 @@ pub enum Error {
|
|||||||
BufferTooLong,
|
BufferTooLong,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ReadCompletionEvent {
|
||||||
|
// DMA Read transfer completed first
|
||||||
|
DmaCompleted,
|
||||||
|
// Idle line detected first
|
||||||
|
Idle,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
|
pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
|
||||||
tx: UartTx<'d, T, TxDma>,
|
tx: UartTx<'d, T, TxDma>,
|
||||||
rx: UartRx<'d, T, RxDma>,
|
rx: UartRx<'d, T, RxDma>,
|
||||||
@ -385,30 +394,50 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
self.inner_read(buffer, true).await
|
self.inner_read(buffer, true).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result<usize, Error>
|
async fn inner_read_run(
|
||||||
|
&mut self,
|
||||||
|
buffer: &mut [u8],
|
||||||
|
enable_idle_line_detection: bool,
|
||||||
|
) -> Result<ReadCompletionEvent, Error>
|
||||||
where
|
where
|
||||||
RxDma: crate::usart::RxDma<T>,
|
RxDma: crate::usart::RxDma<T>,
|
||||||
{
|
{
|
||||||
if buffer.is_empty() {
|
|
||||||
return Ok(0);
|
|
||||||
} else if buffer.len() > 0xFFFF {
|
|
||||||
return Err(Error::BufferTooLong);
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
let buffer_len = buffer.len();
|
// make sure USART state is restored to neutral state when this future is dropped
|
||||||
|
let _drop = OnDrop::new(move || {
|
||||||
|
// defmt::trace!("Clear all USART interrupts and DMA Read Request");
|
||||||
|
// clear all interrupts and DMA Rx Request
|
||||||
|
// SAFETY: only clears Rx related flags
|
||||||
|
unsafe {
|
||||||
|
r.cr1().modify(|w| {
|
||||||
|
// disable RXNE interrupt
|
||||||
|
w.set_rxneie(false);
|
||||||
|
// disable parity interrupt
|
||||||
|
w.set_peie(false);
|
||||||
|
// disable idle line interrupt
|
||||||
|
w.set_idleie(false);
|
||||||
|
});
|
||||||
|
r.cr3().modify(|w| {
|
||||||
|
// disable Error Interrupt: (Frame error, Noise error, Overrun error)
|
||||||
|
w.set_eie(false);
|
||||||
|
// disable DMA Rx Request
|
||||||
|
w.set_dmar(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let ch = &mut self.rx_dma;
|
let ch = &mut self.rx_dma;
|
||||||
let request = ch.request();
|
let request = ch.request();
|
||||||
|
|
||||||
|
// Start USART DMA
|
||||||
|
// will not do anything yet because DMAR is not yet set
|
||||||
|
// future which will complete when DMA Read request completes
|
||||||
|
let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer);
|
||||||
|
|
||||||
// SAFETY: The only way we might have a problem is using split rx and tx
|
// SAFETY: The only way we might have a problem is using split rx and tx
|
||||||
// here we only modify or read Rx related flags, interrupts and DMA channel
|
// here we only modify or read Rx related flags, interrupts and DMA channel
|
||||||
unsafe {
|
unsafe {
|
||||||
// Start USART DMA
|
|
||||||
// will not do anything yet because DMAR is not yet set
|
|
||||||
ch.start_read(request, rdr(r), buffer, Default::default());
|
|
||||||
|
|
||||||
// clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer
|
// clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer
|
||||||
if !self.detect_previous_overrun {
|
if !self.detect_previous_overrun {
|
||||||
let sr = sr(r).read();
|
let sr = sr(r).read();
|
||||||
@ -443,9 +472,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
// something went wrong
|
// something went wrong
|
||||||
// because the only way to get this flag cleared is to have an interrupt
|
// because the only way to get this flag cleared is to have an interrupt
|
||||||
|
|
||||||
// abort DMA transfer
|
// DMA will be stopped when transfer is dropped
|
||||||
ch.request_stop();
|
|
||||||
while ch.is_running() {}
|
|
||||||
|
|
||||||
let sr = sr(r).read();
|
let sr = sr(r).read();
|
||||||
// This read also clears the error and idle interrupt flags on v1.
|
// This read also clears the error and idle interrupt flags on v1.
|
||||||
@ -468,26 +495,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear idle flag
|
if !enable_idle_line_detection {
|
||||||
if enable_idle_line_detection {
|
transfer.await;
|
||||||
let sr = sr(r).read();
|
|
||||||
// This read also clears the error and idle interrupt flags on v1.
|
|
||||||
rdr(r).read_volatile();
|
|
||||||
clear_interrupt_flags(r, sr);
|
|
||||||
|
|
||||||
// enable idle interrupt
|
return Ok(ReadCompletionEvent::DmaCompleted);
|
||||||
r.cr1().modify(|w| {
|
|
||||||
w.set_idleie(true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear idle flag
|
||||||
|
let sr = sr(r).read();
|
||||||
|
// This read also clears the error and idle interrupt flags on v1.
|
||||||
|
rdr(r).read_volatile();
|
||||||
|
clear_interrupt_flags(r, sr);
|
||||||
|
|
||||||
|
// enable idle interrupt
|
||||||
|
r.cr1().modify(|w| {
|
||||||
|
w.set_idleie(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
let res = poll_fn(move |cx| {
|
// future which completes when idle line is detected
|
||||||
|
let idle = poll_fn(move |cx| {
|
||||||
let s = T::state();
|
let s = T::state();
|
||||||
|
|
||||||
ch.set_waker(cx.waker());
|
|
||||||
s.rx_waker.register(cx.waker());
|
s.rx_waker.register(cx.waker());
|
||||||
|
|
||||||
// SAFETY: read only and we only use Rx related flags
|
// SAFETY: read only and we only use Rx related flags
|
||||||
@ -507,10 +538,6 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
if has_errors {
|
if has_errors {
|
||||||
// all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler
|
// all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler
|
||||||
|
|
||||||
// stop dma transfer
|
|
||||||
ch.request_stop();
|
|
||||||
while ch.is_running() {}
|
|
||||||
|
|
||||||
if sr.pe() {
|
if sr.pe() {
|
||||||
return Poll::Ready(Err(Error::Parity));
|
return Poll::Ready(Err(Error::Parity));
|
||||||
}
|
}
|
||||||
@ -525,45 +552,54 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if enable_idle_line_detection && sr.idle() {
|
if sr.idle() {
|
||||||
// Idle line
|
// Idle line detected
|
||||||
|
return Poll::Ready(Ok(()));
|
||||||
// stop dma transfer
|
|
||||||
ch.request_stop();
|
|
||||||
while ch.is_running() {}
|
|
||||||
|
|
||||||
let n = buffer_len - (ch.remaining_transfers() as usize);
|
|
||||||
|
|
||||||
return Poll::Ready(Ok(n));
|
|
||||||
} else if !ch.is_running() {
|
|
||||||
// DMA complete
|
|
||||||
return Poll::Ready(Ok(buffer_len));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
})
|
});
|
||||||
.await;
|
|
||||||
|
|
||||||
// clear all interrupts and DMA Rx Request
|
// wait for the first of DMA request or idle line detected to completes
|
||||||
// SAFETY: only clears Rx related flags
|
// select consumes its arguments
|
||||||
unsafe {
|
// when transfer is dropped, it will stop the DMA request
|
||||||
r.cr1().modify(|w| {
|
match select(transfer, idle).await {
|
||||||
// disable RXNE interrupt
|
// DMA transfer completed first
|
||||||
w.set_rxneie(false);
|
Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted),
|
||||||
// disable parity interrupt
|
|
||||||
w.set_peie(false);
|
// Idle line detected first
|
||||||
// disable idle line interrupt
|
Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle),
|
||||||
w.set_idleie(false);
|
|
||||||
});
|
// error occurred
|
||||||
r.cr3().modify(|w| {
|
Either::Second(Err(e)) => Err(e),
|
||||||
// disable Error Interrupt: (Frame error, Noise error, Overrun error)
|
}
|
||||||
w.set_eie(false);
|
}
|
||||||
// disable DMA Rx Request
|
|
||||||
w.set_dmar(false);
|
async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result<usize, Error>
|
||||||
});
|
where
|
||||||
|
RxDma: crate::usart::RxDma<T>,
|
||||||
|
{
|
||||||
|
if buffer.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
} else if buffer.len() > 0xFFFF {
|
||||||
|
return Err(Error::BufferTooLong);
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
let buffer_len = buffer.len();
|
||||||
|
|
||||||
|
// wait for DMA to complete or IDLE line detection if requested
|
||||||
|
let res = self.inner_read_run(buffer, enable_idle_line_detection).await;
|
||||||
|
|
||||||
|
let ch = &mut self.rx_dma;
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len),
|
||||||
|
Ok(ReadCompletionEvent::Idle) => {
|
||||||
|
let n = buffer_len - (ch.remaining_transfers() as usize);
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,12 @@ pub struct IndependentWatchdog<'d, T: Instance> {
|
|||||||
const MAX_RL: u16 = 0xFFF;
|
const MAX_RL: u16 = 0xFFF;
|
||||||
|
|
||||||
/// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler
|
/// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler
|
||||||
const fn max_timeout(prescaler: u8) -> u32 {
|
const fn max_timeout(prescaler: u16) -> u32 {
|
||||||
1_000_000 * MAX_RL as u32 / (LSI_FREQ.0 / prescaler as u32)
|
1_000_000 * MAX_RL as u32 / (LSI_FREQ.0 / prescaler as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates watchdog reload value for the given prescaler and desired timeout
|
/// Calculates watchdog reload value for the given prescaler and desired timeout
|
||||||
const fn reload_value(prescaler: u8, timeout_us: u32) -> u16 {
|
const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 {
|
||||||
(timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16
|
(timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,12 +33,12 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
|
|||||||
// Find lowest prescaler value, which makes watchdog period longer or equal to timeout.
|
// Find lowest prescaler value, which makes watchdog period longer or equal to timeout.
|
||||||
// This iterates from 4 (2^2) to 256 (2^8).
|
// This iterates from 4 (2^2) to 256 (2^8).
|
||||||
let psc_power = unwrap!((2..=8).find(|psc_power| {
|
let psc_power = unwrap!((2..=8).find(|psc_power| {
|
||||||
let psc = 2u8.pow(*psc_power);
|
let psc = 2u16.pow(*psc_power);
|
||||||
timeout_us <= max_timeout(psc)
|
timeout_us <= max_timeout(psc)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Prescaler value
|
// Prescaler value
|
||||||
let psc = 2u8.pow(psc_power);
|
let psc = 2u16.pow(psc_power);
|
||||||
|
|
||||||
// Convert prescaler power to PR register value
|
// Convert prescaler power to PR register value
|
||||||
let pr = psc_power as u8 - 2;
|
let pr = psc_power as u8 - 2;
|
||||||
|
@ -56,6 +56,15 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<M, T> Default for Signal<M, T>
|
||||||
|
where
|
||||||
|
M: RawMutex,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<M, T: Send> Signal<M, T>
|
impl<M, T: Send> Signal<M, T>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
|
@ -6,7 +6,7 @@ use crate::blocking_mutex::raw::CriticalSectionRawMutex;
|
|||||||
use crate::blocking_mutex::Mutex;
|
use crate::blocking_mutex::Mutex;
|
||||||
|
|
||||||
/// Utility struct to register and wake a waker.
|
/// Utility struct to register and wake a waker.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct WakerRegistration {
|
pub struct WakerRegistration {
|
||||||
waker: Option<Waker>,
|
waker: Option<Waker>,
|
||||||
}
|
}
|
||||||
|
13
embassy-usb-logger/Cargo.toml
Normal file
13
embassy-usb-logger/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-usb-logger"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embassy-usb = { version = "0.1.0", path = "../embassy-usb" }
|
||||||
|
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||||
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
futures = { version = "0.3", default-features = false }
|
||||||
|
static_cell = "1"
|
||||||
|
usbd-hid = "0.6.0"
|
||||||
|
log = "0.4"
|
15
embassy-usb-logger/README.md
Normal file
15
embassy-usb-logger/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# embassy-usb-logger
|
||||||
|
|
||||||
|
USB implementation of the `log` crate. This logger can be used by any device that implements `embassy-usb`. When running,
|
||||||
|
it will output all logging done through the `log` facade to the USB serial peripheral.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Add the following embassy task to your application. The `Driver` type is different depending on which HAL you use.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn logger_task(driver: Driver<'static, USB>) {
|
||||||
|
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
||||||
|
}
|
||||||
|
```
|
146
embassy-usb-logger/src/lib.rs
Normal file
146
embassy-usb-logger/src/lib.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
use core::fmt::Write as _;
|
||||||
|
|
||||||
|
use embassy_futures::join::join;
|
||||||
|
use embassy_sync::pipe::Pipe;
|
||||||
|
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||||
|
use embassy_usb::driver::Driver;
|
||||||
|
use embassy_usb::{Builder, Config};
|
||||||
|
use log::{Metadata, Record};
|
||||||
|
|
||||||
|
type CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
|
||||||
|
/// The logger state containing buffers that must live as long as the USB peripheral.
|
||||||
|
pub struct LoggerState<'d> {
|
||||||
|
state: State<'d>,
|
||||||
|
device_descriptor: [u8; 32],
|
||||||
|
config_descriptor: [u8; 128],
|
||||||
|
bos_descriptor: [u8; 16],
|
||||||
|
control_buf: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> LoggerState<'d> {
|
||||||
|
/// Create a new instance of the logger state.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state: State::new(),
|
||||||
|
device_descriptor: [0; 32],
|
||||||
|
config_descriptor: [0; 128],
|
||||||
|
bos_descriptor: [0; 16],
|
||||||
|
control_buf: [0; 64],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The logger handle, which contains a pipe with configurable size for buffering log messages.
|
||||||
|
pub struct UsbLogger<const N: usize> {
|
||||||
|
buffer: Pipe<CS, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> UsbLogger<N> {
|
||||||
|
/// Create a new logger instance.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { buffer: Pipe::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the USB logger using the state and USB driver. Never returns.
|
||||||
|
pub async fn run<'d, D>(&'d self, state: &'d mut LoggerState<'d>, driver: D) -> !
|
||||||
|
where
|
||||||
|
D: Driver<'d>,
|
||||||
|
Self: 'd,
|
||||||
|
{
|
||||||
|
const MAX_PACKET_SIZE: u8 = 64;
|
||||||
|
let mut config = Config::new(0xc0de, 0xcafe);
|
||||||
|
config.manufacturer = Some("Embassy");
|
||||||
|
config.product = Some("USB-serial logger");
|
||||||
|
config.serial_number = None;
|
||||||
|
config.max_power = 100;
|
||||||
|
config.max_packet_size_0 = MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
// Required for windows compatiblity.
|
||||||
|
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
||||||
|
config.device_class = 0xEF;
|
||||||
|
config.device_sub_class = 0x02;
|
||||||
|
config.device_protocol = 0x01;
|
||||||
|
config.composite_with_iads = true;
|
||||||
|
|
||||||
|
let mut builder = Builder::new(
|
||||||
|
driver,
|
||||||
|
config,
|
||||||
|
&mut state.device_descriptor,
|
||||||
|
&mut state.config_descriptor,
|
||||||
|
&mut state.bos_descriptor,
|
||||||
|
&mut state.control_buf,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create classes on the builder.
|
||||||
|
let mut class = CdcAcmClass::new(&mut builder, &mut state.state, MAX_PACKET_SIZE as u16);
|
||||||
|
|
||||||
|
// Build the builder.
|
||||||
|
let mut device = builder.build();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let run_fut = device.run();
|
||||||
|
let log_fut = async {
|
||||||
|
let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
|
||||||
|
class.wait_connection().await;
|
||||||
|
loop {
|
||||||
|
let len = self.buffer.read(&mut rx[..]).await;
|
||||||
|
let _ = class.write_packet(&rx[..len]).await;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
join(run_fut, log_fut).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> log::Log for UsbLogger<N> {
|
||||||
|
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
if self.enabled(record.metadata()) {
|
||||||
|
let _ = write!(Writer(&self.buffer), "{}\r\n", record.args());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Writer<'d, const N: usize>(&'d Pipe<CS, N>);
|
||||||
|
|
||||||
|
impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||||
|
let _ = self.0.try_write(s.as_bytes());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize and run the USB serial logger, never returns.
|
||||||
|
///
|
||||||
|
/// Arguments specify the buffer size, log level and the USB driver, respectively.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This macro should only be invoked only once since it is setting the global logging state of the application.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! run {
|
||||||
|
( $x:expr, $l:expr, $p:ident ) => {
|
||||||
|
static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new();
|
||||||
|
unsafe {
|
||||||
|
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level($l));
|
||||||
|
}
|
||||||
|
let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;
|
||||||
|
};
|
||||||
|
}
|
27
examples/nrf/src/bin/spis.rs
Normal file
27
examples/nrf/src/bin/spis.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_nrf::interrupt;
|
||||||
|
use embassy_nrf::spis::{Config, Spis};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let p = embassy_nrf::init(Default::default());
|
||||||
|
info!("Running!");
|
||||||
|
|
||||||
|
let irq = interrupt::take!(SPIM2_SPIS2_SPI2);
|
||||||
|
let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut rx_buf = [0_u8; 64];
|
||||||
|
let tx_buf = [1_u8, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
if let Ok((n_rx, n_tx)) = spis.transfer(&mut rx_buf, &tx_buf).await {
|
||||||
|
info!("RX: {:?}", rx_buf[..n_rx]);
|
||||||
|
info!("TX: {:?}", tx_buf[..n_tx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
examples/nrf/src/bin/twis.rs
Normal file
46
examples/nrf/src/bin/twis.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//! TWIS example
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_nrf::interrupt;
|
||||||
|
use embassy_nrf::twis::{self, Command, Twis};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let p = embassy_nrf::init(Default::default());
|
||||||
|
|
||||||
|
let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
let mut config = twis::Config::default();
|
||||||
|
// Set i2c address
|
||||||
|
config.address0 = 0x55;
|
||||||
|
let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
|
||||||
|
|
||||||
|
info!("Listening...");
|
||||||
|
loop {
|
||||||
|
let response = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
// This buffer is used if the i2c master performs a Write or WriteRead
|
||||||
|
let mut buf = [0u8; 16];
|
||||||
|
match i2c.listen(&mut buf).await {
|
||||||
|
Ok(Command::Read) => {
|
||||||
|
info!("Got READ command. Respond with data:\n{:?}\n", response);
|
||||||
|
if let Err(e) = i2c.respond_to_read(&response).await {
|
||||||
|
error!("{:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Command::Write(n)) => info!("Got WRITE command with data:\n{:?}\n", buf[..n]),
|
||||||
|
Ok(Command::WriteRead(n)) => {
|
||||||
|
info!("Got WRITE/READ command with data:\n{:?}", buf[..n]);
|
||||||
|
info!("Respond with data:\n{:?}\n", response);
|
||||||
|
if let Err(e) = i2c.respond_to_read(&response).await {
|
||||||
|
error!("{:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => error!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt"
|
|||||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
|
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
|
||||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||||
|
embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
@ -32,6 +33,7 @@ embedded-hal-async = { version = "0.1.0-alpha.3" }
|
|||||||
embedded-io = { version = "0.3.1", features = ["async", "defmt"] }
|
embedded-io = { version = "0.3.1", features = ["async", "defmt"] }
|
||||||
embedded-storage = { version = "0.3" }
|
embedded-storage = { version = "0.3" }
|
||||||
static_cell = "1.0.0"
|
static_cell = "1.0.0"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
33
examples/rp/src/bin/adc.rs
Normal file
33
examples/rp/src/bin/adc.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::adc::{Adc, Config};
|
||||||
|
use embassy_rp::interrupt;
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
let irq = interrupt::take!(ADC_IRQ_FIFO);
|
||||||
|
let mut adc = Adc::new(p.ADC, irq, Config::default());
|
||||||
|
|
||||||
|
let mut p26 = p.PIN_26;
|
||||||
|
let mut p27 = p.PIN_27;
|
||||||
|
let mut p28 = p.PIN_28;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let level = adc.read(&mut p26).await;
|
||||||
|
info!("Pin 26 ADC: {}", level);
|
||||||
|
let level = adc.read(&mut p27).await;
|
||||||
|
info!("Pin 27 ADC: {}", level);
|
||||||
|
let level = adc.read(&mut p28).await;
|
||||||
|
info!("Pin 28 ADC: {}", level);
|
||||||
|
let temp = adc.read_temperature().await;
|
||||||
|
info!("Temp: {}", temp);
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
30
examples/rp/src/bin/usb_logger.rs
Normal file
30
examples/rp/src/bin/usb_logger.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::interrupt;
|
||||||
|
use embassy_rp::peripherals::USB;
|
||||||
|
use embassy_rp::usb::Driver;
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn logger_task(driver: Driver<'static, USB>) {
|
||||||
|
embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
let irq = interrupt::take!(USBCTRL_IRQ);
|
||||||
|
let driver = Driver::new(p.USB, irq);
|
||||||
|
spawner.spawn(logger_task(driver)).unwrap();
|
||||||
|
|
||||||
|
let mut counter = 0;
|
||||||
|
loop {
|
||||||
|
counter += 1;
|
||||||
|
log::info!("Tick {}", counter);
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
24
examples/stm32h7/src/bin/wdg.rs
Normal file
24
examples/stm32h7/src/bin/wdg.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::wdg::IndependentWatchdog;
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let mut wdg = IndependentWatchdog::new(p.IWDG1, 20_000_000);
|
||||||
|
|
||||||
|
unsafe { wdg.unleash() };
|
||||||
|
|
||||||
|
loop {
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
unsafe { wdg.pet() };
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user