Compare commits
122 Commits
embassy-ti
...
embassy-ex
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
b99533607c | |||
ea4d08b6cf | |||
05968bf0f3 | |||
fc086fd4ba | |||
ea702b3719 | |||
97d18c5ffb | |||
eed34f945c | |||
0b2d6996e8 | |||
e7fdd500d8 | |||
1f246d0e37 | |||
a7d5c87049 | |||
49e1091309 | |||
79b49c6fae | |||
f053bf742c | |||
9423987ac5 | |||
bc21b6efaf | |||
a7b90c7fb6 | |||
4e61d83555 | |||
c871fe0848 | |||
3c6c382465 | |||
171b764d82 | |||
08c8022583 | |||
4f2dcca34b | |||
9c30d565b9 | |||
f363f6ce92 | |||
6bf24b4d1a | |||
88bbc238b7 | |||
2cfe2439c9 | |||
7b38b95e10 | |||
5142674786 | |||
a5b1d2237f | |||
1669e39565 | |||
80e58426fc | |||
ad0eb3f4bd | |||
8d809c96ec | |||
7152031229 | |||
7ee7109508 | |||
18dc0dea63 | |||
9d674f0212 | |||
816778e3fa | |||
4f33cc5d1a | |||
2fed9f949a | |||
7412a859fd | |||
0db1332da8 | |||
334dfcdb65 | |||
54ba472540 | |||
4322293f63 | |||
c14527486d | |||
81298394b5 | |||
5d1576ea73 | |||
f46b838746 | |||
3f672c8a93 | |||
e5af4c4bce | |||
7bb9620b1a | |||
feb840c503 |
@ -21,7 +21,7 @@ Then, what follows are some declarations on how to deal with panics and faults.
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::example$basic/src/main.rs[lines="11..12"]
|
||||
include::example$basic/src/main.rs[lines="10"]
|
||||
----
|
||||
|
||||
=== Task declaration
|
||||
@ -30,7 +30,7 @@ After a bit of import declaration, the tasks run by the application should be de
|
||||
|
||||
[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.
|
||||
@ -45,23 +45,10 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera
|
||||
|
||||
[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:
|
||||
|
||||
```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:
|
||||
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:
|
||||
|
||||
. Creates an Embassy Executor
|
||||
. Initializes the microcontroller HAL to get the `Peripherals`
|
||||
@ -76,7 +63,7 @@ The project definition needs to contain the embassy dependencies:
|
||||
|
||||
[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).
|
||||
|
@ -8,7 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh
|
||||
|
||||
== 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.
|
||||
|
||||
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[]
|
||||
----
|
||||
|
||||
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.
|
||||
|
||||
== 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
|
||||
* 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[]
|
||||
----
|
||||
|
||||
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.
|
||||
|
||||
|
@ -4,9 +4,9 @@ The link:https://github.com/embassy-rs/embassy/tree/master/embassy-stm32[Embassy
|
||||
|
||||
== 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
|
||||
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.
|
||||
|
||||
=== The metapac
|
||||
|
@ -150,7 +150,7 @@ impl BootLoader {
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
/// | Active | 0 | 1 | 2 | 3 | - |
|
||||
/// | DFU | 0 | 3 | 2 | 1 | X |
|
||||
/// +-----------+-------+--------+--------+--------+--------+
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
///
|
||||
/// The algorithm starts by copying 'backwards', and after the first step, the layout is
|
||||
/// as follows:
|
||||
|
@ -17,9 +17,9 @@ pub struct BootLoader {
|
||||
page: AlignedBuffer<PAGE_SIZE>,
|
||||
}
|
||||
|
||||
impl BootLoader {
|
||||
impl Default for BootLoader {
|
||||
/// Create a new bootloader instance using parameters from linker script
|
||||
pub fn default() -> Self {
|
||||
fn default() -> Self {
|
||||
extern "C" {
|
||||
static __bootloader_state_start: u32;
|
||||
static __bootloader_state_end: u32;
|
||||
@ -54,7 +54,9 @@ impl BootLoader {
|
||||
|
||||
Self::new(active, dfu, state)
|
||||
}
|
||||
}
|
||||
|
||||
impl BootLoader {
|
||||
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
|
||||
pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
|
||||
Self {
|
||||
|
@ -14,43 +14,6 @@ pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize> {
|
||||
}
|
||||
|
||||
impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> BootLoader<PAGE_SIZE, WRITE_SIZE> {
|
||||
/// Create a new bootloader instance using parameters from linker script
|
||||
pub fn default() -> Self {
|
||||
extern "C" {
|
||||
static __bootloader_state_start: u32;
|
||||
static __bootloader_state_end: u32;
|
||||
static __bootloader_active_start: u32;
|
||||
static __bootloader_active_end: u32;
|
||||
static __bootloader_dfu_start: u32;
|
||||
static __bootloader_dfu_end: u32;
|
||||
}
|
||||
|
||||
let active = unsafe {
|
||||
Partition::new(
|
||||
&__bootloader_active_start as *const u32 as usize,
|
||||
&__bootloader_active_end as *const u32 as usize,
|
||||
)
|
||||
};
|
||||
let dfu = unsafe {
|
||||
Partition::new(
|
||||
&__bootloader_dfu_start as *const u32 as usize,
|
||||
&__bootloader_dfu_end as *const u32 as usize,
|
||||
)
|
||||
};
|
||||
let state = unsafe {
|
||||
Partition::new(
|
||||
&__bootloader_state_start as *const u32 as usize,
|
||||
&__bootloader_state_end as *const u32 as usize,
|
||||
)
|
||||
};
|
||||
|
||||
trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to);
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
|
||||
|
||||
Self::new(active, dfu, state)
|
||||
}
|
||||
|
||||
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
|
||||
pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
|
||||
Self {
|
||||
@ -85,3 +48,42 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> BootLoader<PAGE_SIZE, WRIT
|
||||
cortex_m::asm::bootload(start as *const u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> Default for BootLoader<PAGE_SIZE, WRITE_SIZE> {
|
||||
/// Create a new bootloader instance using parameters from linker script
|
||||
fn default() -> Self {
|
||||
extern "C" {
|
||||
static __bootloader_state_start: u32;
|
||||
static __bootloader_state_end: u32;
|
||||
static __bootloader_active_start: u32;
|
||||
static __bootloader_active_end: u32;
|
||||
static __bootloader_dfu_start: u32;
|
||||
static __bootloader_dfu_end: u32;
|
||||
}
|
||||
|
||||
let active = unsafe {
|
||||
Partition::new(
|
||||
&__bootloader_active_start as *const u32 as usize,
|
||||
&__bootloader_active_end as *const u32 as usize,
|
||||
)
|
||||
};
|
||||
let dfu = unsafe {
|
||||
Partition::new(
|
||||
&__bootloader_dfu_start as *const u32 as usize,
|
||||
&__bootloader_dfu_end as *const u32 as usize,
|
||||
)
|
||||
};
|
||||
let state = unsafe {
|
||||
Partition::new(
|
||||
&__bootloader_state_start as *const u32 as usize,
|
||||
&__bootloader_state_end as *const u32 as usize,
|
||||
)
|
||||
};
|
||||
|
||||
trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to);
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
|
||||
|
||||
Self::new(active, dfu, state)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,13 @@ name = "embassy-executor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
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]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
|
||||
@ -23,8 +29,8 @@ flavors = [
|
||||
|
||||
[features]
|
||||
default = []
|
||||
std = ["embassy-macros/std", "critical-section/std"]
|
||||
wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"]
|
||||
std = ["critical-section/std"]
|
||||
wasm = ["dep:wasm-bindgen", "dep:js-sys"]
|
||||
|
||||
# Enable nightly-only features
|
||||
nightly = []
|
||||
|
@ -8,18 +8,22 @@
|
||||
pub(crate) mod fmt;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use embassy_macros::{main, task};
|
||||
pub use embassy_macros::task;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(cortex_m)] {
|
||||
#[path="arch/cortex_m.rs"]
|
||||
mod arch;
|
||||
pub use arch::*;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use embassy_macros::main_cortex_m as main;
|
||||
}
|
||||
else if #[cfg(target_arch="riscv32")] {
|
||||
#[path="arch/riscv32.rs"]
|
||||
mod arch;
|
||||
pub use arch::*;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use embassy_macros::main_riscv as main;
|
||||
}
|
||||
else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] {
|
||||
#[path="arch/xtensa.rs"]
|
||||
@ -30,11 +34,15 @@ cfg_if::cfg_if! {
|
||||
#[path="arch/wasm.rs"]
|
||||
mod arch;
|
||||
pub use arch::*;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use embassy_macros::main_wasm as main;
|
||||
}
|
||||
else if #[cfg(feature="std")] {
|
||||
#[path="arch/std.rs"]
|
||||
mod 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
|
||||
/// the upcoming RX window start.
|
||||
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.radio
|
||||
@ -130,7 +130,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
|
||||
/// be able to hold a single LoRaWAN packet.
|
||||
async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> {
|
||||
assert!(buf.len() >= 255);
|
||||
trace!("RX request: {}", config);
|
||||
trace!("RX request: {:?}", config);
|
||||
self.switch.set_rx();
|
||||
|
||||
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.set_standby(StandbyClk::Rc)?;
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
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)));
|
||||
}
|
||||
|
||||
@ -193,7 +197,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
|
||||
.clear_irq_status(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 {
|
||||
Poll::Pending
|
||||
|
@ -87,7 +87,7 @@ where
|
||||
config.rf.spreading_factor.into(),
|
||||
config.rf.bandwidth.into(),
|
||||
config.rf.coding_rate.into(),
|
||||
4,
|
||||
8,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
@ -119,14 +119,14 @@ where
|
||||
config.spreading_factor.into(),
|
||||
config.bandwidth.into(),
|
||||
config.coding_rate.into(),
|
||||
4,
|
||||
8,
|
||||
4,
|
||||
false,
|
||||
0u8,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
@ -3,6 +3,13 @@ name = "embassy-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
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]
|
||||
syn = { version = "1.0.76", features = ["full", "extra-traits"] }
|
||||
@ -14,8 +21,5 @@ proc-macro2 = "1.0.29"
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
std = []
|
||||
wasm = []
|
||||
|
||||
# Enabling this cause interrupt::take! to require embassy-executor
|
||||
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;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
@ -6,6 +7,36 @@ mod macros;
|
||||
mod util;
|
||||
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]
|
||||
pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
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()
|
||||
}
|
||||
|
||||
/// 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]
|
||||
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 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]
|
||||
|
@ -7,7 +7,62 @@ use crate::util::ctxt::Ctxt;
|
||||
#[derive(Debug, FromMeta)]
|
||||
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)]
|
||||
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;
|
||||
|
||||
#[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! {
|
||||
#[::embassy_executor::task()]
|
||||
async fn __embassy_main(#fargs) {
|
||||
|
@ -131,8 +131,12 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
|
||||
|
||||
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
|
||||
|
||||
impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
|
||||
|
||||
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
|
||||
|
||||
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
|
||||
|
||||
impl_timer!(TIMER0, TIMER0, TIMER0);
|
||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||
|
@ -137,8 +137,12 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
|
||||
|
||||
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
|
||||
|
||||
impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
|
||||
|
||||
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
|
||||
|
||||
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
|
||||
|
||||
impl_pwm!(PWM0, PWM0, PWM0);
|
||||
|
||||
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!(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_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
|
||||
|
||||
impl_pwm!(PWM0, PWM0, PWM0);
|
||||
|
||||
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!(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!(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!(TIMER1, TIMER1, TIMER1);
|
||||
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!(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!(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!(PWM1, PWM1, PWM1);
|
||||
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!(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!(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!(PWM1, PWM1, PWM1);
|
||||
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!(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!(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!(PWM1, PWM1, PWM1);
|
||||
impl_pwm!(PWM2, PWM2, PWM2);
|
||||
|
@ -361,11 +361,21 @@ impl_spim!(UARTETWISPI1, SPIM1, SERIAL1);
|
||||
impl_spim!(UARTETWISPI2, SPIM2, SERIAL2);
|
||||
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!(UARTETWISPI1, TWIM1, SERIAL1);
|
||||
impl_twim!(UARTETWISPI2, TWIM2, SERIAL2);
|
||||
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!(PWM1, PWM1, PWM1);
|
||||
impl_pwm!(PWM2, PWM2, PWM2);
|
||||
|
@ -238,7 +238,9 @@ embassy_hal_common::peripherals! {
|
||||
|
||||
impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0);
|
||||
impl_spim!(UARTETWISPI0, SPIM0, SERIAL0);
|
||||
impl_spis!(UARTETWISPI0, SPIS0, SERIAL0);
|
||||
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
|
||||
impl_twis!(UARTETWISPI0, TWIS0, SERIAL0);
|
||||
|
||||
impl_timer!(TIMER0, TIMER0, TIMER0);
|
||||
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!(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!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
|
||||
impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
|
||||
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!(PWM1, PWM1, PWM1);
|
||||
impl_pwm!(PWM2, PWM2, PWM2);
|
||||
|
@ -2,7 +2,7 @@ use core::convert::Infallible;
|
||||
use core::future::{poll_fn, Future};
|
||||
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 crate::gpio::sealed::Pin as _;
|
||||
@ -148,7 +148,7 @@ impl Iterator for BitIter {
|
||||
|
||||
/// GPIOTE channel driver in input mode
|
||||
pub struct InputChannel<'d, C: Channel, T: GpioPin> {
|
||||
ch: C,
|
||||
ch: PeripheralRef<'d, C>,
|
||||
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> {
|
||||
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 num = ch.number();
|
||||
|
||||
@ -215,7 +217,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
|
||||
|
||||
/// GPIOTE channel driver in output mode
|
||||
pub struct OutputChannel<'d, C: Channel, T: GpioPin> {
|
||||
ch: C,
|
||||
ch: PeripheralRef<'d, C>,
|
||||
_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> {
|
||||
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 num = ch.number();
|
||||
|
||||
|
@ -96,10 +96,12 @@ pub mod rng;
|
||||
#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))]
|
||||
pub mod saadc;
|
||||
pub mod spim;
|
||||
pub mod spis;
|
||||
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
|
||||
pub mod temp;
|
||||
pub mod timer;
|
||||
pub mod twim;
|
||||
pub mod twis;
|
||||
pub mod uarte;
|
||||
#[cfg(any(
|
||||
feature = "_nrf5340-app",
|
||||
@ -267,5 +269,12 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
#[cfg(feature = "_time-driver")]
|
||||
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
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
};
|
||||
}
|
@ -54,6 +54,7 @@ critical-section = "1.1"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
chrono = { version = "0.4", default-features = false, optional = true }
|
||||
embedded-io = { version = "0.3.1", features = ["async"], optional = true }
|
||||
embedded-storage = { version = "0.3" }
|
||||
|
||||
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
|
||||
#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
|
||||
|
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())
|
||||
}
|
||||
}
|
@ -191,7 +191,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> {
|
||||
}
|
||||
}
|
||||
|
||||
const CHANNEL_COUNT: usize = 12;
|
||||
pub(crate) const CHANNEL_COUNT: usize = 12;
|
||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT];
|
||||
|
||||
|
463
embassy-rp/src/flash.rs
Normal file
463
embassy-rp/src/flash.rs
Normal file
@ -0,0 +1,463 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::Peripheral;
|
||||
use embedded_storage::nor_flash::{
|
||||
check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind,
|
||||
ReadNorFlash,
|
||||
};
|
||||
|
||||
use crate::peripherals::FLASH;
|
||||
|
||||
pub const FLASH_BASE: usize = 0x10000000;
|
||||
|
||||
// **NOTE**:
|
||||
//
|
||||
// These limitations are currently enforced because of using the
|
||||
// RP2040 boot-rom flash functions, that are optimized for flash compatibility
|
||||
// rather than performance.
|
||||
pub const PAGE_SIZE: usize = 256;
|
||||
pub const WRITE_SIZE: usize = 1;
|
||||
pub const READ_SIZE: usize = 1;
|
||||
pub const ERASE_SIZE: usize = 4096;
|
||||
|
||||
/// Error type for NVMC operations.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// Opration using a location not in flash.
|
||||
OutOfBounds,
|
||||
/// Unaligned operation or using unaligned buffers.
|
||||
Unaligned,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl From<NorFlashErrorKind> for Error {
|
||||
fn from(e: NorFlashErrorKind) -> Self {
|
||||
match e {
|
||||
NorFlashErrorKind::NotAligned => Self::Unaligned,
|
||||
NorFlashErrorKind::OutOfBounds => Self::OutOfBounds,
|
||||
_ => Self::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NorFlashError for Error {
|
||||
fn kind(&self) -> NorFlashErrorKind {
|
||||
match self {
|
||||
Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,
|
||||
Self::Unaligned => NorFlashErrorKind::NotAligned,
|
||||
Self::Other => NorFlashErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>);
|
||||
|
||||
impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
|
||||
pub fn new(_flash: impl Peripheral<P = T> + 'd) -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
|
||||
pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
||||
check_read(self, offset, bytes.len())?;
|
||||
|
||||
let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) };
|
||||
|
||||
bytes.copy_from_slice(flash_data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> usize {
|
||||
FLASH_SIZE
|
||||
}
|
||||
|
||||
pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
|
||||
check_erase(self, from, to)?;
|
||||
|
||||
trace!(
|
||||
"Erasing from 0x{:x} to 0x{:x}",
|
||||
FLASH_BASE as u32 + from,
|
||||
FLASH_BASE as u32 + to
|
||||
);
|
||||
|
||||
let len = to - from;
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
|
||||
check_write(self, offset, bytes.len())?;
|
||||
|
||||
trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset);
|
||||
|
||||
let end_offset = offset as usize + bytes.len();
|
||||
|
||||
let padded_offset = (offset as *const u8).align_offset(PAGE_SIZE);
|
||||
let start_padding = core::cmp::min(padded_offset, bytes.len());
|
||||
|
||||
// Pad in the beginning
|
||||
if start_padding > 0 {
|
||||
let start = PAGE_SIZE - padded_offset;
|
||||
let end = start + start_padding;
|
||||
|
||||
let mut pad_buf = [0xFF_u8; PAGE_SIZE];
|
||||
pad_buf[start..end].copy_from_slice(&bytes[..start_padding]);
|
||||
|
||||
let unaligned_offset = offset as usize - start;
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) }
|
||||
}
|
||||
|
||||
let remaining_len = bytes.len() - start_padding;
|
||||
let end_padding = start_padding + PAGE_SIZE * (remaining_len / PAGE_SIZE);
|
||||
|
||||
// Write aligned slice of length in multiples of 256 bytes
|
||||
// If the remaining bytes to be written is more than a full page.
|
||||
if remaining_len >= PAGE_SIZE {
|
||||
let mut aligned_offset = if start_padding > 0 {
|
||||
offset as usize + padded_offset
|
||||
} else {
|
||||
offset as usize
|
||||
};
|
||||
|
||||
if bytes.as_ptr() as usize >= 0x2000_0000 {
|
||||
let aligned_data = &bytes[start_padding..end_padding];
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) }
|
||||
} else {
|
||||
for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {
|
||||
let mut ram_buf = [0xFF_u8; PAGE_SIZE];
|
||||
ram_buf.copy_from_slice(chunk);
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true)) }
|
||||
aligned_offset += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pad in the end
|
||||
let rem_offset = (end_offset as *const u8).align_offset(PAGE_SIZE);
|
||||
let rem_padding = remaining_len % PAGE_SIZE;
|
||||
if rem_padding > 0 {
|
||||
let mut pad_buf = [0xFF_u8; PAGE_SIZE];
|
||||
pad_buf[..rem_padding].copy_from_slice(&bytes[end_padding..]);
|
||||
|
||||
let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Make sure to uphold the contract points with rp2040-flash.
|
||||
/// - interrupts must be disabled
|
||||
/// - DMA must not access flash memory
|
||||
unsafe fn in_ram(&mut self, operation: impl FnOnce()) {
|
||||
let dma_status = &mut [false; crate::dma::CHANNEL_COUNT];
|
||||
|
||||
// TODO: Make sure CORE1 is paused during the entire duration of the RAM function
|
||||
|
||||
critical_section::with(|_| {
|
||||
// Pause all DMA channels for the duration of the ram operation
|
||||
for (number, status) in dma_status.iter_mut().enumerate() {
|
||||
let ch = crate::pac::DMA.ch(number as _);
|
||||
*status = ch.ctrl_trig().read().en();
|
||||
if *status {
|
||||
ch.ctrl_trig().modify(|w| w.set_en(false));
|
||||
}
|
||||
}
|
||||
|
||||
// Run our flash operation in RAM
|
||||
operation();
|
||||
|
||||
// Re-enable previously enabled DMA channels
|
||||
for (number, status) in dma_status.iter().enumerate() {
|
||||
let ch = crate::pac::DMA.ch(number as _);
|
||||
if *status {
|
||||
ch.ctrl_trig().modify(|w| w.set_en(true));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> {
|
||||
const READ_SIZE: usize = READ_SIZE;
|
||||
|
||||
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read(offset, bytes)
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
self.capacity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {}
|
||||
|
||||
impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> {
|
||||
const WRITE_SIZE: usize = WRITE_SIZE;
|
||||
|
||||
const ERASE_SIZE: usize = ERASE_SIZE;
|
||||
|
||||
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||
self.erase(from, to)
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.write(offset, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod ram_helpers {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::rom_data;
|
||||
|
||||
#[repr(C)]
|
||||
struct FlashFunctionPointers<'a> {
|
||||
connect_internal_flash: unsafe extern "C" fn() -> (),
|
||||
flash_exit_xip: unsafe extern "C" fn() -> (),
|
||||
flash_range_erase: Option<unsafe extern "C" fn(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ()>,
|
||||
flash_range_program: Option<unsafe extern "C" fn(addr: u32, data: *const u8, count: usize) -> ()>,
|
||||
flash_flush_cache: unsafe extern "C" fn() -> (),
|
||||
flash_enter_cmd_xip: unsafe extern "C" fn() -> (),
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> {
|
||||
FlashFunctionPointers {
|
||||
connect_internal_flash: rom_data::connect_internal_flash::ptr(),
|
||||
flash_exit_xip: rom_data::flash_exit_xip::ptr(),
|
||||
flash_range_erase: if erase {
|
||||
Some(rom_data::flash_range_erase::ptr())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
flash_range_program: if write {
|
||||
Some(rom_data::flash_range_program::ptr())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
flash_flush_cache: rom_data::flash_flush_cache::ptr(),
|
||||
flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
/// # Safety
|
||||
///
|
||||
/// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode
|
||||
unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers {
|
||||
let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);
|
||||
let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr);
|
||||
FlashFunctionPointers {
|
||||
connect_internal_flash: rom_data::connect_internal_flash::ptr(),
|
||||
flash_exit_xip: rom_data::flash_exit_xip::ptr(),
|
||||
flash_range_erase: if erase {
|
||||
Some(rom_data::flash_range_erase::ptr())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
flash_range_program: if write {
|
||||
Some(rom_data::flash_range_program::ptr())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
flash_flush_cache: rom_data::flash_flush_cache::ptr(),
|
||||
flash_enter_cmd_xip: boot2_fn,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Erase a flash range starting at `addr` with length `len`.
|
||||
///
|
||||
/// `addr` and `len` must be multiples of 4096
|
||||
///
|
||||
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
|
||||
/// is used to re-initialize the XIP engine after flashing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Nothing must access flash while this is running.
|
||||
/// Usually this means:
|
||||
/// - interrupts must be disabled
|
||||
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
|
||||
/// - DMA must not access flash memory
|
||||
///
|
||||
/// `addr` and `len` parameters must be valid and are not checked.
|
||||
pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) {
|
||||
let mut boot2 = [0u32; 256 / 4];
|
||||
let ptrs = if use_boot2 {
|
||||
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
|
||||
flash_function_pointers_with_boot2(true, false, &boot2)
|
||||
} else {
|
||||
flash_function_pointers(true, false)
|
||||
};
|
||||
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers);
|
||||
}
|
||||
|
||||
/// Erase and rewrite a flash range starting at `addr` with data `data`.
|
||||
///
|
||||
/// `addr` and `data.len()` must be multiples of 4096
|
||||
///
|
||||
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
|
||||
/// is used to re-initialize the XIP engine after flashing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Nothing must access flash while this is running.
|
||||
/// Usually this means:
|
||||
/// - interrupts must be disabled
|
||||
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
|
||||
/// - DMA must not access flash memory
|
||||
///
|
||||
/// `addr` and `len` parameters must be valid and are not checked.
|
||||
pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) {
|
||||
let mut boot2 = [0u32; 256 / 4];
|
||||
let ptrs = if use_boot2 {
|
||||
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
|
||||
flash_function_pointers_with_boot2(true, true, &boot2)
|
||||
} else {
|
||||
flash_function_pointers(true, true)
|
||||
};
|
||||
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
write_flash_inner(
|
||||
addr,
|
||||
data.len() as u32,
|
||||
Some(data),
|
||||
&ptrs as *const FlashFunctionPointers,
|
||||
);
|
||||
}
|
||||
|
||||
/// Write a flash range starting at `addr` with data `data`.
|
||||
///
|
||||
/// `addr` and `data.len()` must be multiples of 256
|
||||
///
|
||||
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
|
||||
/// is used to re-initialize the XIP engine after flashing.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Nothing must access flash while this is running.
|
||||
/// Usually this means:
|
||||
/// - interrupts must be disabled
|
||||
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
|
||||
/// - DMA must not access flash memory
|
||||
///
|
||||
/// `addr` and `len` parameters must be valid and are not checked.
|
||||
pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) {
|
||||
let mut boot2 = [0u32; 256 / 4];
|
||||
let ptrs = if use_boot2 {
|
||||
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
|
||||
flash_function_pointers_with_boot2(false, true, &boot2)
|
||||
} else {
|
||||
flash_function_pointers(false, true)
|
||||
};
|
||||
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
write_flash_inner(
|
||||
addr,
|
||||
data.len() as u32,
|
||||
Some(data),
|
||||
&ptrs as *const FlashFunctionPointers,
|
||||
);
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Nothing must access flash while this is running.
|
||||
/// Usually this means:
|
||||
/// - interrupts must be disabled
|
||||
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
|
||||
/// - DMA must not access flash memory
|
||||
/// Length of data must be a multiple of 4096
|
||||
/// addr must be aligned to 4096
|
||||
#[inline(never)]
|
||||
#[link_section = ".data.ram_func"]
|
||||
unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
|
||||
/*
|
||||
Should be equivalent to:
|
||||
rom_data::connect_internal_flash();
|
||||
rom_data::flash_exit_xip();
|
||||
rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected
|
||||
rom_data::flash_range_program(addr, data as *const _, len); // if selected
|
||||
rom_data::flash_flush_cache();
|
||||
rom_data::flash_enter_cmd_xip();
|
||||
*/
|
||||
#[cfg(target_arch = "arm")]
|
||||
core::arch::asm!(
|
||||
"mov r8, r0",
|
||||
"mov r9, r2",
|
||||
"mov r10, r1",
|
||||
"ldr r4, [{ptrs}, #0]",
|
||||
"blx r4", // connect_internal_flash()
|
||||
|
||||
"ldr r4, [{ptrs}, #4]",
|
||||
"blx r4", // flash_exit_xip()
|
||||
|
||||
"mov r0, r8", // r0 = addr
|
||||
"mov r1, r10", // r1 = len
|
||||
"movs r2, #1",
|
||||
"lsls r2, r2, #31", // r2 = 1 << 31
|
||||
"movs r3, #0", // r3 = 0
|
||||
"ldr r4, [{ptrs}, #8]",
|
||||
"cmp r4, #0",
|
||||
"beq 1f",
|
||||
"blx r4", // flash_range_erase(addr, len, 1 << 31, 0)
|
||||
"1:",
|
||||
|
||||
"mov r0, r8", // r0 = addr
|
||||
"mov r1, r9", // r0 = data
|
||||
"mov r2, r10", // r2 = len
|
||||
"ldr r4, [{ptrs}, #12]",
|
||||
"cmp r4, #0",
|
||||
"beq 1f",
|
||||
"blx r4", // flash_range_program(addr, data, len);
|
||||
"1:",
|
||||
|
||||
"ldr r4, [{ptrs}, #16]",
|
||||
"blx r4", // flash_flush_cache();
|
||||
|
||||
"ldr r4, [{ptrs}, #20]",
|
||||
"blx r4", // flash_enter_cmd_xip();
|
||||
ptrs = in(reg) ptrs,
|
||||
// Registers r8-r15 are not allocated automatically,
|
||||
// so assign them manually. We need to use them as
|
||||
// otherwise there are not enough registers available.
|
||||
in("r0") addr,
|
||||
in("r2") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()),
|
||||
in("r1") len,
|
||||
out("r3") _,
|
||||
out("r4") _,
|
||||
lateout("r8") _,
|
||||
lateout("r9") _,
|
||||
lateout("r10") _,
|
||||
clobber_abi("C"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Instance {}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance {}
|
||||
|
||||
impl sealed::Instance for FLASH {}
|
||||
impl Instance for FLASH {}
|
@ -6,6 +6,7 @@ pub(crate) mod fmt;
|
||||
|
||||
mod intrinsics;
|
||||
|
||||
pub mod adc;
|
||||
pub mod dma;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
@ -20,6 +21,7 @@ pub mod uart;
|
||||
pub mod usb;
|
||||
|
||||
mod clocks;
|
||||
pub mod flash;
|
||||
mod reset;
|
||||
|
||||
// Reexports
|
||||
@ -95,6 +97,10 @@ embassy_hal_common::peripherals! {
|
||||
USB,
|
||||
|
||||
RTC,
|
||||
|
||||
FLASH,
|
||||
|
||||
ADC,
|
||||
}
|
||||
|
||||
#[link_section = ".boot2"]
|
||||
|
@ -1,9 +1,7 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use crate::adc::{AdcPin, Instance};
|
||||
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
|
||||
use crate::rcc::get_freqs;
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
@ -29,69 +27,9 @@ impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
}
|
||||
}
|
||||
|
||||
mod sample_time {
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 1.5 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
/// 1.5 ADC clock cycles
|
||||
Cycles1_5 = 0b000,
|
||||
|
||||
/// 7.5 ADC clock cycles
|
||||
Cycles7_5 = 0b001,
|
||||
|
||||
/// 13.5 ADC clock cycles
|
||||
Cycles13_5 = 0b010,
|
||||
|
||||
/// 28.5 ADC clock cycles
|
||||
Cycles28_5 = 0b011,
|
||||
|
||||
/// 41.5 ADC clock cycles
|
||||
Cycles41_5 = 0b100,
|
||||
|
||||
/// 55.5 ADC clock cycles
|
||||
Cycles55_5 = 0b101,
|
||||
|
||||
/// 71.5 ADC clock cycles
|
||||
Cycles71_5 = 0b110,
|
||||
|
||||
/// 239.5 ADC clock cycles
|
||||
Cycles239_5 = 0b111,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime {
|
||||
match self {
|
||||
SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5,
|
||||
SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5,
|
||||
SampleTime::Cycles13_5 => crate::pac::adc::vals::SampleTime::CYCLES13_5,
|
||||
SampleTime::Cycles28_5 => crate::pac::adc::vals::SampleTime::CYCLES28_5,
|
||||
SampleTime::Cycles41_5 => crate::pac::adc::vals::SampleTime::CYCLES41_5,
|
||||
SampleTime::Cycles55_5 => crate::pac::adc::vals::SampleTime::CYCLES55_5,
|
||||
SampleTime::Cycles71_5 => crate::pac::adc::vals::SampleTime::CYCLES71_5,
|
||||
SampleTime::Cycles239_5 => crate::pac::adc::vals::SampleTime::CYCLES239_5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles28_5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use sample_time::SampleTime;
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(_peri);
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(adc);
|
||||
T::enable();
|
||||
T::reset();
|
||||
unsafe {
|
||||
@ -120,8 +58,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
delay.delay_us((1_000_000) / Self::freq().0 + 1);
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,14 +139,11 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
.smpr2()
|
||||
.modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs()
|
||||
.smpr1()
|
||||
.modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,38 @@
|
||||
#![macro_use]
|
||||
|
||||
#[cfg_attr(adc_v4, path = "v4.rs")]
|
||||
#[cfg_attr(adc_v3, path = "v3.rs")]
|
||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||
#[cfg_attr(adc_g0, path = "v3.rs")]
|
||||
#[cfg_attr(adc_f1, path = "f1.rs")]
|
||||
#[cfg_attr(adc_v1, path = "v1.rs")]
|
||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
|
||||
#[cfg_attr(adc_v4, path = "v4.rs")]
|
||||
mod _version;
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
mod resolution;
|
||||
#[cfg(not(adc_v1))]
|
||||
mod sample_time;
|
||||
|
||||
#[allow(unused)]
|
||||
pub use _version::*;
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
pub use resolution::Resolution;
|
||||
#[cfg(not(adc_v1))]
|
||||
pub use sample_time::SampleTime;
|
||||
|
||||
use crate::peripherals;
|
||||
|
||||
#[cfg(not(adc_v1))]
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
#[allow(unused)]
|
||||
adc: crate::PeripheralRef<'d, T>,
|
||||
sample_time: SampleTime,
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> &'static crate::pac::adc::Adc;
|
||||
fn regs() -> crate::pac::adc::Adc;
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
fn common_regs() -> &'static crate::pac::adccommon::AdcCommon;
|
||||
}
|
||||
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
pub trait Common {
|
||||
fn regs() -> &'static crate::pac::adccommon::AdcCommon;
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||
}
|
||||
|
||||
pub trait AdcPin<T: Instance> {
|
||||
@ -34,12 +44,11 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v2)))]
|
||||
pub trait Instance: sealed::Instance + 'static {}
|
||||
#[cfg(any(adc_f1, adc_v2))]
|
||||
pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
pub trait Common: sealed::Common + 'static {}
|
||||
#[cfg(not(any(adc_f1, adc_v2, adc_v4)))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
||||
#[cfg(any(adc_f1, adc_v2, adc_v4))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
||||
|
||||
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
||||
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
|
||||
@ -47,14 +56,14 @@ pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
foreach_peripheral!(
|
||||
(adc, $inst:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::adc::Adc {
|
||||
&crate::pac::$inst
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
fn common_regs() -> &'static crate::pac::adccommon::AdcCommon {
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, $common_inst:ident) => {
|
||||
return &crate::pac::$common_inst
|
||||
return crate::pac::$common_inst
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -68,14 +77,14 @@ foreach_peripheral!(
|
||||
foreach_peripheral!(
|
||||
(adc, ADC3) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::ADC3 {
|
||||
fn regs() -> &'static crate::pac::adc::Adc {
|
||||
&crate::pac::ADC3
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::ADC3
|
||||
}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
fn common_regs() -> &'static crate::pac::adccommon::AdcCommon {
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC3_COMMON) => {
|
||||
return &crate::pac::ADC3_COMMON
|
||||
return crate::pac::ADC3_COMMON
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -85,14 +94,14 @@ foreach_peripheral!(
|
||||
};
|
||||
(adc, $inst:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::adc::Adc {
|
||||
&crate::pac::$inst
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
fn common_regs() -> &'static crate::pac::adccommon::AdcCommon {
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC_COMMON) => {
|
||||
return &crate::pac::ADC_COMMON
|
||||
return crate::pac::ADC_COMMON
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -102,19 +111,6 @@ foreach_peripheral!(
|
||||
};
|
||||
);
|
||||
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
foreach_peripheral!(
|
||||
(adccommon, $inst:ident) => {
|
||||
impl sealed::Common for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::adccommon::AdcCommon {
|
||||
&crate::pac::$inst
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::adc::Common for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
||||
macro_rules! impl_adc_pin {
|
||||
($inst:ident, $pin:ident, $ch:expr) => {
|
||||
impl crate::adc::AdcPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||
|
63
embassy-stm32/src/adc/resolution.rs
Normal file
63
embassy-stm32/src/adc/resolution.rs
Normal file
@ -0,0 +1,63 @@
|
||||
#[cfg(any(adc_v2, adc_v3, adc_g0))]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
SixBit,
|
||||
}
|
||||
|
||||
#[cfg(adc_v4)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Resolution {
|
||||
SixteenBit,
|
||||
FourteenBit,
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
#[cfg(any(adc_v2, adc_v3, adc_g0))]
|
||||
{
|
||||
Self::TwelveBit
|
||||
}
|
||||
#[cfg(adc_v4)]
|
||||
{
|
||||
Self::SixteenBit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Resolution> for crate::pac::adc::vals::Res {
|
||||
fn from(res: Resolution) -> crate::pac::adc::vals::Res {
|
||||
match res {
|
||||
#[cfg(adc_v4)]
|
||||
Resolution::SixteenBit => crate::pac::adc::vals::Res::SIXTEENBIT,
|
||||
#[cfg(adc_v4)]
|
||||
Resolution::FourteenBit => crate::pac::adc::vals::Res::FOURTEENBITV,
|
||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
#[cfg(any(adc_v2, adc_v3, adc_g0))]
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
pub fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
#[cfg(adc_v4)]
|
||||
Resolution::SixteenBit => (1 << 16) - 1,
|
||||
#[cfg(adc_v4)]
|
||||
Resolution::FourteenBit => (1 << 14) - 1,
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
#[cfg(any(adc_v2, adc_v3, adc_g0))]
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
}
|
111
embassy-stm32/src/adc/sample_time.rs
Normal file
111
embassy-stm32/src/adc/sample_time.rs
Normal file
@ -0,0 +1,111 @@
|
||||
macro_rules! impl_sample_time {
|
||||
($default_doc:expr, $default:ident, $pac:ty, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => {
|
||||
#[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
$(
|
||||
#[doc = concat!($doc, " ADC clock cycles.")]
|
||||
$variant,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<SampleTime> for $pac {
|
||||
fn from(sample_time: SampleTime) -> $pac {
|
||||
match sample_time {
|
||||
$(SampleTime::$variant => <$pac>::$pac_variant),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::$default
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(adc_f1)]
|
||||
impl_sample_time!(
|
||||
"1.5",
|
||||
Cycles1_5,
|
||||
crate::pac::adc::vals::SampleTime,
|
||||
(
|
||||
("1.5", Cycles1_5, CYCLES1_5),
|
||||
("7.5", Cycles7_5, CYCLES7_5),
|
||||
("13.5", Cycles13_5, CYCLES13_5),
|
||||
("28.5", Cycles28_5, CYCLES28_5),
|
||||
("41.5", Cycles41_5, CYCLES41_5),
|
||||
("55.5", Cycles55_5, CYCLES55_5),
|
||||
("71.5", Cycles71_5, CYCLES71_5),
|
||||
("239.5", Cycles239_5, CYCLES239_5)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_v2)]
|
||||
impl_sample_time!(
|
||||
"3",
|
||||
Cycles3,
|
||||
crate::pac::adc::vals::Smp,
|
||||
(
|
||||
("3", Cycles3, CYCLES3),
|
||||
("15", Cycles15, CYCLES15),
|
||||
("28", Cycles28, CYCLES28),
|
||||
("56", Cycles56, CYCLES56),
|
||||
("84", Cycles84, CYCLES84),
|
||||
("112", Cycles112, CYCLES112),
|
||||
("144", Cycles144, CYCLES144),
|
||||
("480", Cycles480, CYCLES480)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_v3)]
|
||||
impl_sample_time!(
|
||||
"2.5",
|
||||
Cycles2_5,
|
||||
crate::pac::adc::vals::SampleTime,
|
||||
(
|
||||
("2.5", Cycles2_5, CYCLES2_5),
|
||||
("6.5", Cycles6_5, CYCLES6_5),
|
||||
("12.5", Cycles12_5, CYCLES12_5),
|
||||
("24.5", Cycles24_5, CYCLES24_5),
|
||||
("47.5", Cycles47_5, CYCLES47_5),
|
||||
("92.5", Cycles92_5, CYCLES92_5),
|
||||
("247.5", Cycles247_5, CYCLES247_5),
|
||||
("640.5", Cycles640_5, CYCLES640_5)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_g0)]
|
||||
impl_sample_time!(
|
||||
"1.5",
|
||||
Cycles1_5,
|
||||
crate::pac::adc::vals::SampleTime,
|
||||
(
|
||||
("1.5", Cycles1_5, CYCLES1_5),
|
||||
("3.5", Cycles3_5, CYCLES3_5),
|
||||
("7.5", Cycles7_5, CYCLES7_5),
|
||||
("12.5", Cycles12_5, CYCLES12_5),
|
||||
("19.5", Cycles19_5, CYCLES19_5),
|
||||
("39.5", Cycles39_5, CYCLES39_5),
|
||||
("79.5", Cycles79_5, CYCLES79_5),
|
||||
("160.5", Cycles160_5, CYCLES160_5)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_v4)]
|
||||
impl_sample_time!(
|
||||
"1.5",
|
||||
Cycles1_5,
|
||||
crate::pac::adc::vals::Smp,
|
||||
(
|
||||
("1.5", Cycles1_5, CYCLES1_5),
|
||||
("2.5", Cycles2_5, CYCLES2_5),
|
||||
("8.5", Cycles8_5, CYCLES8_5),
|
||||
("16.5", Cycles16_5, CYCLES16_5),
|
||||
("32.5", Cycles32_5, CYCLES32_5),
|
||||
("64.5", Cycles64_5, CYCLES64_5),
|
||||
("387.5", Cycles387_5, CYCLES387_5),
|
||||
("810.5", Cycles810_5, CYCLES810_5)
|
||||
)
|
||||
);
|
@ -1,10 +1,8 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use super::InternalChannel;
|
||||
use crate::adc::{AdcPin, Instance};
|
||||
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
|
||||
use crate::peripherals::ADC1;
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
@ -17,39 +15,6 @@ pub const VREF_CALIB_MV: u32 = 3300;
|
||||
/// ADC turn-on time
|
||||
pub const ADC_POWERUP_TIME_US: u32 = 3;
|
||||
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
SixBit,
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
Self::TwelveBit
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
fn res(&self) -> crate::pac::adc::vals::Res {
|
||||
match self {
|
||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VrefInt;
|
||||
impl InternalChannel<ADC1> for VrefInt {}
|
||||
impl super::sealed::InternalChannel<ADC1> for VrefInt {
|
||||
@ -94,42 +59,6 @@ impl super::sealed::InternalChannel<ADC1> for Vbat {
|
||||
}
|
||||
}
|
||||
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 3 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
Cycles3 = 0b000,
|
||||
Cycles15 = 0b001,
|
||||
Cycles28 = 0b010,
|
||||
Cycles56 = 0b011,
|
||||
Cycles85 = 0b100,
|
||||
Cycles112 = 0b101,
|
||||
Cycles144 = 0b110,
|
||||
Cycles480 = 0b111,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp {
|
||||
match self {
|
||||
SampleTime::Cycles3 => crate::pac::adc::vals::Smp::CYCLES3,
|
||||
SampleTime::Cycles15 => crate::pac::adc::vals::Smp::CYCLES15,
|
||||
SampleTime::Cycles28 => crate::pac::adc::vals::Smp::CYCLES28,
|
||||
SampleTime::Cycles56 => crate::pac::adc::vals::Smp::CYCLES56,
|
||||
SampleTime::Cycles85 => crate::pac::adc::vals::Smp::CYCLES84,
|
||||
SampleTime::Cycles112 => crate::pac::adc::vals::Smp::CYCLES112,
|
||||
SampleTime::Cycles144 => crate::pac::adc::vals::Smp::CYCLES144,
|
||||
SampleTime::Cycles480 => crate::pac::adc::vals::Smp::CYCLES480,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles3
|
||||
}
|
||||
}
|
||||
|
||||
enum Prescaler {
|
||||
Div2,
|
||||
Div4,
|
||||
@ -161,18 +90,12 @@ impl Prescaler {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T> Adc<'d, T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(_peri);
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(adc);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -188,9 +111,8 @@ where
|
||||
delay.delay_us(ADC_POWERUP_TIME_US);
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
resolution: Resolution::default(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +121,9 @@ where
|
||||
}
|
||||
|
||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||
self.resolution = resolution;
|
||||
unsafe {
|
||||
T::regs().cr1().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables internal voltage reference and returns [VrefInt], which can be used in
|
||||
@ -283,7 +207,6 @@ where
|
||||
|
||||
unsafe fn read_channel(&mut self, channel: u8) -> u16 {
|
||||
// Configure ADC
|
||||
T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
|
||||
// Select channel
|
||||
T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
|
||||
@ -297,14 +220,11 @@ where
|
||||
}
|
||||
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
.smpr2()
|
||||
.modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs()
|
||||
.smpr1()
|
||||
.modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use crate::adc::{AdcPin, Instance};
|
||||
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
|
||||
use crate::Peripheral;
|
||||
|
||||
/// Default VREF voltage used for sample conversion to millivolts.
|
||||
@ -24,39 +22,6 @@ fn enable() {
|
||||
});
|
||||
}
|
||||
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
SixBit,
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
Self::TwelveBit
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
fn res(&self) -> crate::pac::adc::vals::Res {
|
||||
match self {
|
||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> AdcPin<T> for VrefInt {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for VrefInt {
|
||||
@ -93,125 +58,9 @@ impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(adc_g0))]
|
||||
mod sample_time {
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 2.5 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
/// 2.5 ADC clock cycles
|
||||
Cycles2_5 = 0b000,
|
||||
|
||||
/// 6.5 ADC clock cycles
|
||||
Cycles6_5 = 0b001,
|
||||
|
||||
/// 12.5 ADC clock cycles
|
||||
Cycles12_5 = 0b010,
|
||||
|
||||
/// 24.5 ADC clock cycles
|
||||
Cycles24_5 = 0b011,
|
||||
|
||||
/// 47.5 ADC clock cycles
|
||||
Cycles47_5 = 0b100,
|
||||
|
||||
/// 92.5 ADC clock cycles
|
||||
Cycles92_5 = 0b101,
|
||||
|
||||
/// 247.5 ADC clock cycles
|
||||
Cycles247_5 = 0b110,
|
||||
|
||||
/// 640.5 ADC clock cycles
|
||||
Cycles640_5 = 0b111,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime {
|
||||
match self {
|
||||
SampleTime::Cycles2_5 => crate::pac::adc::vals::SampleTime::CYCLES2_5,
|
||||
SampleTime::Cycles6_5 => crate::pac::adc::vals::SampleTime::CYCLES6_5,
|
||||
SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5,
|
||||
SampleTime::Cycles24_5 => crate::pac::adc::vals::SampleTime::CYCLES24_5,
|
||||
SampleTime::Cycles47_5 => crate::pac::adc::vals::SampleTime::CYCLES47_5,
|
||||
SampleTime::Cycles92_5 => crate::pac::adc::vals::SampleTime::CYCLES92_5,
|
||||
SampleTime::Cycles247_5 => crate::pac::adc::vals::SampleTime::CYCLES247_5,
|
||||
SampleTime::Cycles640_5 => crate::pac::adc::vals::SampleTime::CYCLES640_5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles2_5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(adc_g0)]
|
||||
mod sample_time {
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 1.5 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
/// 1.5 ADC clock cycles
|
||||
Cycles1_5 = 0b000,
|
||||
|
||||
/// 3.5 ADC clock cycles
|
||||
Cycles3_5 = 0b001,
|
||||
|
||||
/// 7.5 ADC clock cycles
|
||||
Cycles7_5 = 0b010,
|
||||
|
||||
/// 12.5 ADC clock cycles
|
||||
Cycles12_5 = 0b011,
|
||||
|
||||
/// 19.5 ADC clock cycles
|
||||
Cycles19_5 = 0b100,
|
||||
|
||||
/// 39.5 ADC clock cycles
|
||||
Cycles39_5 = 0b101,
|
||||
|
||||
/// 79.5 ADC clock cycles
|
||||
Cycles79_5 = 0b110,
|
||||
|
||||
/// 160.5 ADC clock cycles
|
||||
Cycles160_5 = 0b111,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime {
|
||||
match self {
|
||||
SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5,
|
||||
SampleTime::Cycles3_5 => crate::pac::adc::vals::SampleTime::CYCLES3_5,
|
||||
SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5,
|
||||
SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5,
|
||||
SampleTime::Cycles19_5 => crate::pac::adc::vals::SampleTime::CYCLES19_5,
|
||||
SampleTime::Cycles39_5 => crate::pac::adc::vals::SampleTime::CYCLES39_5,
|
||||
SampleTime::Cycles79_5 => crate::pac::adc::vals::SampleTime::CYCLES79_5,
|
||||
SampleTime::Cycles160_5 => crate::pac::adc::vals::SampleTime::CYCLES160_5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles1_5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use sample_time::SampleTime;
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(_peri);
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(adc);
|
||||
enable();
|
||||
unsafe {
|
||||
T::regs().cr().modify(|reg| {
|
||||
@ -222,7 +71,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
|
||||
#[cfg(adc_g0)]
|
||||
T::regs().cfgr1().modify(|reg| {
|
||||
reg.set_chselrmod(true);
|
||||
reg.set_chselrmod(false);
|
||||
});
|
||||
}
|
||||
|
||||
@ -241,9 +90,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
delay.delay_us(1);
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
resolution: Resolution::default(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +136,12 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||
self.resolution = resolution;
|
||||
unsafe {
|
||||
#[cfg(not(stm32g0))]
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
|
||||
#[cfg(stm32g0)]
|
||||
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -340,12 +193,6 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
// spin
|
||||
}
|
||||
|
||||
// Configure ADC
|
||||
#[cfg(not(stm32g0))]
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
#[cfg(stm32g0)]
|
||||
T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
|
||||
// Configure channel
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
|
||||
@ -353,7 +200,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
#[cfg(not(stm32g0))]
|
||||
T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
|
||||
#[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:
|
||||
// If we perform conversions slower than 1 kHz, the first read ADC value can be
|
||||
@ -374,19 +221,16 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
|
||||
#[cfg(stm32g0)]
|
||||
unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
|
||||
T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.sample_time()));
|
||||
T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
|
||||
}
|
||||
|
||||
#[cfg(not(stm32g0))]
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
.smpr1()
|
||||
.modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs()
|
||||
.smpr2()
|
||||
.modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use atomic_polyfill::{AtomicU8, Ordering};
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
|
||||
use pac::adccommon::vals::Presc;
|
||||
|
||||
use super::{AdcPin, Instance, InternalChannel};
|
||||
use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
|
||||
use crate::time::Hertz;
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
@ -14,42 +12,6 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
|
||||
/// VREF voltage used for factory calibration of VREFINTCAL register.
|
||||
pub const VREF_CALIB_MV: u32 = 3300;
|
||||
|
||||
pub enum Resolution {
|
||||
SixteenBit,
|
||||
FourteenBit,
|
||||
TwelveBit,
|
||||
TenBit,
|
||||
EightBit,
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
Self::SixteenBit
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
fn res(&self) -> pac::adc::vals::Res {
|
||||
match self {
|
||||
Resolution::SixteenBit => pac::adc::vals::Res::SIXTEENBIT,
|
||||
Resolution::FourteenBit => pac::adc::vals::Res::FOURTEENBITV,
|
||||
Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBITV,
|
||||
Resolution::TenBit => pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
Resolution::SixteenBit => (1 << 16) - 1,
|
||||
Resolution::FourteenBit => (1 << 14) - 1,
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> InternalChannel<T> for VrefInt {}
|
||||
@ -193,57 +155,6 @@ foreach_peripheral!(
|
||||
};
|
||||
);
|
||||
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 2.5 ADC clock cycles.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum SampleTime {
|
||||
/// 1.5 ADC clock cycles
|
||||
Cycles1_5,
|
||||
|
||||
/// 2.5 ADC clock cycles
|
||||
Cycles2_5,
|
||||
|
||||
/// 8.5 ADC clock cycles
|
||||
Cycles8_5,
|
||||
|
||||
/// 16.5 ADC clock cycles
|
||||
Cycles16_5,
|
||||
|
||||
/// 32.5 ADC clock cycles
|
||||
Cycles32_5,
|
||||
|
||||
/// 64.5 ADC clock cycles
|
||||
Cycles64_5,
|
||||
|
||||
/// 387.5 ADC clock cycles
|
||||
Cycles387_5,
|
||||
|
||||
/// 810.5 ADC clock cycles
|
||||
Cycles810_5,
|
||||
}
|
||||
|
||||
impl SampleTime {
|
||||
pub(crate) fn sample_time(&self) -> pac::adc::vals::Smp {
|
||||
match self {
|
||||
SampleTime::Cycles1_5 => pac::adc::vals::Smp::CYCLES1_5,
|
||||
SampleTime::Cycles2_5 => pac::adc::vals::Smp::CYCLES2_5,
|
||||
SampleTime::Cycles8_5 => pac::adc::vals::Smp::CYCLES8_5,
|
||||
SampleTime::Cycles16_5 => pac::adc::vals::Smp::CYCLES16_5,
|
||||
SampleTime::Cycles32_5 => pac::adc::vals::Smp::CYCLES32_5,
|
||||
SampleTime::Cycles64_5 => pac::adc::vals::Smp::CYCLES64_5,
|
||||
SampleTime::Cycles387_5 => pac::adc::vals::Smp::CYCLES387_5,
|
||||
SampleTime::Cycles810_5 => pac::adc::vals::Smp::CYCLES810_5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles1_5
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE (unused): The prescaler enum closely copies the hardware capabilities,
|
||||
// but high prescaling doesn't make a lot of sense in the current implementation and is ommited.
|
||||
#[allow(unused)]
|
||||
@ -312,15 +223,9 @@ impl Prescaler {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u16>) -> Self {
|
||||
embassy_hal_common::into_ref!(_peri);
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u16>) -> Self {
|
||||
embassy_hal_common::into_ref!(adc);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -350,9 +255,8 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
}
|
||||
|
||||
let mut s = Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
resolution: Resolution::default(),
|
||||
phantom: PhantomData,
|
||||
};
|
||||
s.power_up(delay);
|
||||
s.configure_differential_inputs();
|
||||
@ -454,7 +358,9 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
}
|
||||
|
||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||
self.resolution = resolution;
|
||||
unsafe {
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
@ -495,9 +401,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
}
|
||||
|
||||
unsafe fn read_channel(&mut self, channel: u8) -> u16 {
|
||||
// Configure ADC
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
|
||||
// Configure channel
|
||||
Self::set_channel_sample_time(channel, self.sample_time);
|
||||
|
||||
@ -514,14 +417,11 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
}
|
||||
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
.smpr(0)
|
||||
.modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
|
||||
T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs()
|
||||
.smpr(1)
|
||||
.modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
|
||||
T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
// 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.mtltx_qomr().modify(|w| w.set_tsf(true));
|
||||
|
||||
|
@ -45,6 +45,51 @@ impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {}
|
||||
|
||||
impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
||||
pub fn new(
|
||||
state: &'d mut State<'d, T>,
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> BufferedUart<'d, T> {
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
|
||||
}
|
||||
|
||||
pub fn new_with_rtscts(
|
||||
state: &'d mut State<'d, T>,
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
|
||||
cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(cts, rts);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
unsafe {
|
||||
rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
|
||||
cts.set_as_af(cts.af_num(), AFType::Input);
|
||||
T::regs().cr3().write(|w| {
|
||||
w.set_rtse(true);
|
||||
w.set_ctse(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
state: &'d mut State<'d, T>,
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
@ -56,34 +101,17 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(_peri, rx, tx, irq);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER);
|
||||
|
||||
unsafe {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
}
|
||||
|
||||
r.cr2().write(|_w| {});
|
||||
r.cr3().write(|_w| {});
|
||||
r.cr1().write(|w| {
|
||||
w.set_ue(true);
|
||||
w.set_te(true);
|
||||
w.set_re(true);
|
||||
w.set_m0(if config.parity != Parity::ParityNone {
|
||||
vals::M0::BIT9
|
||||
} else {
|
||||
vals::M0::BIT8
|
||||
});
|
||||
w.set_pce(config.parity != Parity::ParityNone);
|
||||
w.set_ps(match config.parity {
|
||||
Parity::ParityOdd => vals::Ps::ODD,
|
||||
Parity::ParityEven => vals::Ps::EVEN,
|
||||
_ => vals::Ps::EVEN,
|
||||
});
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER, true, true);
|
||||
|
||||
unsafe {
|
||||
r.cr1().modify(|w| {
|
||||
w.set_rxneie(true);
|
||||
w.set_idleie(true);
|
||||
});
|
||||
|
@ -6,6 +6,8 @@ use core::task::Poll;
|
||||
|
||||
use atomic_polyfill::{compiler_fence, Ordering};
|
||||
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 crate::dma::NoDma;
|
||||
@ -85,6 +87,13 @@ pub enum Error {
|
||||
BufferTooLong,
|
||||
}
|
||||
|
||||
enum ReadCompletionEvent {
|
||||
// DMA Read transfer completed first
|
||||
DmaCompleted,
|
||||
// Idle line detected first
|
||||
Idle,
|
||||
}
|
||||
|
||||
pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
|
||||
tx: UartTx<'d, T, TxDma>,
|
||||
rx: UartRx<'d, T, RxDma>,
|
||||
@ -102,7 +111,59 @@ pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> {
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
|
||||
fn new(tx_dma: PeripheralRef<'d, TxDma>) -> Self {
|
||||
/// usefull if you only want Uart Tx. It saves 1 pin and consumes a little less power
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self::new_inner(peri, tx, tx_dma, config)
|
||||
}
|
||||
|
||||
pub fn new_with_cts(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(cts);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
unsafe {
|
||||
cts.set_as_af(cts.af_num(), AFType::Input);
|
||||
T::regs().cr3().write(|w| {
|
||||
w.set_ctse(true);
|
||||
});
|
||||
}
|
||||
Self::new_inner(peri, tx, tx_dma, config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = TxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(_peri, tx, tx_dma);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
unsafe {
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
}
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER, false, true);
|
||||
|
||||
// create state once!
|
||||
let _s = T::state();
|
||||
|
||||
Self {
|
||||
tx_dma,
|
||||
phantom: PhantomData,
|
||||
@ -156,44 +217,52 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, irq, rx, rx_dma);
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self::new_inner(peri, irq, rx, rx_dma, config)
|
||||
}
|
||||
|
||||
pub fn new_with_rts(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rts);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
|
||||
T::regs().cr3().write(|w| {
|
||||
w.set_rtse(true);
|
||||
});
|
||||
}
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER);
|
||||
Self::new_inner(peri, irq, rx, rx_dma, config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, irq, rx, rx_dma);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
unsafe {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
|
||||
r.cr2().write(|_w| {});
|
||||
r.cr3().write(|w| {
|
||||
// enable Error Interrupt: (Frame error, Noise error, Overrun error)
|
||||
w.set_eie(true);
|
||||
});
|
||||
r.cr1().write(|w| {
|
||||
// enable uart
|
||||
w.set_ue(true);
|
||||
// enable receiver
|
||||
w.set_re(true);
|
||||
// configure word size
|
||||
w.set_m0(if config.parity != Parity::ParityNone {
|
||||
vals::M0::BIT9
|
||||
} else {
|
||||
vals::M0::BIT8
|
||||
});
|
||||
// configure parity
|
||||
w.set_pce(config.parity != Parity::ParityNone);
|
||||
w.set_ps(match config.parity {
|
||||
Parity::ParityOdd => vals::Ps::ODD,
|
||||
Parity::ParityEven => vals::Ps::EVEN,
|
||||
_ => vals::Ps::EVEN,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER, true, false);
|
||||
|
||||
irq.set_handler(Self::on_interrupt);
|
||||
irq.unpend();
|
||||
irq.enable();
|
||||
@ -325,30 +394,50 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
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
|
||||
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 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 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
|
||||
// here we only modify or read Rx related flags, interrupts and DMA channel
|
||||
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
|
||||
if !self.detect_previous_overrun {
|
||||
let sr = sr(r).read();
|
||||
@ -383,9 +472,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
// something went wrong
|
||||
// because the only way to get this flag cleared is to have an interrupt
|
||||
|
||||
// abort DMA transfer
|
||||
ch.request_stop();
|
||||
while ch.is_running() {}
|
||||
// DMA will be stopped when transfer is dropped
|
||||
|
||||
let sr = sr(r).read();
|
||||
// This read also clears the error and idle interrupt flags on v1.
|
||||
@ -408,26 +495,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
// clear idle flag
|
||||
if enable_idle_line_detection {
|
||||
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);
|
||||
if !enable_idle_line_detection {
|
||||
transfer.await;
|
||||
|
||||
// enable idle interrupt
|
||||
r.cr1().modify(|w| {
|
||||
w.set_idleie(true);
|
||||
});
|
||||
return Ok(ReadCompletionEvent::DmaCompleted);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
let res = poll_fn(move |cx| {
|
||||
// future which completes when idle line is detected
|
||||
let idle = poll_fn(move |cx| {
|
||||
let s = T::state();
|
||||
|
||||
ch.set_waker(cx.waker());
|
||||
s.rx_waker.register(cx.waker());
|
||||
|
||||
// SAFETY: read only and we only use Rx related flags
|
||||
@ -447,10 +538,6 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
if has_errors {
|
||||
// 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() {
|
||||
return Poll::Ready(Err(Error::Parity));
|
||||
}
|
||||
@ -466,44 +553,53 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
}
|
||||
|
||||
if sr.idle() {
|
||||
// Idle line
|
||||
|
||||
// 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));
|
||||
// Idle line detected
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
// wait for the first of DMA request or idle line detected to completes
|
||||
// select consumes its arguments
|
||||
// when transfer is dropped, it will stop the DMA request
|
||||
match select(transfer, idle).await {
|
||||
// DMA transfer completed first
|
||||
Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted),
|
||||
|
||||
// Idle line detected first
|
||||
Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle),
|
||||
|
||||
// error occurred
|
||||
Either::Second(Err(e)) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -563,31 +659,13 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER);
|
||||
|
||||
unsafe {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
|
||||
r.cr2().write(|_w| {});
|
||||
r.cr1().write(|w| {
|
||||
w.set_ue(true);
|
||||
w.set_te(true);
|
||||
w.set_re(true);
|
||||
w.set_m0(if config.parity != Parity::ParityNone {
|
||||
vals::M0::BIT9
|
||||
} else {
|
||||
vals::M0::BIT8
|
||||
});
|
||||
w.set_pce(config.parity != Parity::ParityNone);
|
||||
w.set_ps(match config.parity {
|
||||
Parity::ParityOdd => vals::Ps::ODD,
|
||||
Parity::ParityEven => vals::Ps::EVEN,
|
||||
_ => vals::Ps::EVEN,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
configure(r, &config, T::frequency(), T::MULTIPLIER, true, true);
|
||||
|
||||
irq.set_handler(UartRx::<T, RxDma>::on_interrupt);
|
||||
irq.unpend();
|
||||
irq.enable();
|
||||
@ -596,7 +674,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
let _s = T::state();
|
||||
|
||||
Self {
|
||||
tx: UartTx::new(tx_dma),
|
||||
tx: UartTx {
|
||||
tx_dma,
|
||||
phantom: PhantomData,
|
||||
},
|
||||
rx: UartRx {
|
||||
_peri: peri,
|
||||
rx_dma,
|
||||
@ -650,12 +731,38 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
}
|
||||
}
|
||||
|
||||
fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32) {
|
||||
fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable_rx: bool, enable_tx: bool) {
|
||||
if !enable_rx && !enable_tx {
|
||||
panic!("USART: At least one of RX or TX should be enabled");
|
||||
}
|
||||
|
||||
// TODO: better calculation, including error checking and OVER8 if possible.
|
||||
let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier;
|
||||
|
||||
unsafe {
|
||||
r.brr().write_value(regs::Brr(div));
|
||||
r.cr2().write(|_w| {});
|
||||
r.cr1().write(|w| {
|
||||
// enable uart
|
||||
w.set_ue(true);
|
||||
// enable transceiver
|
||||
w.set_te(enable_tx);
|
||||
// enable receiver
|
||||
w.set_re(enable_rx);
|
||||
// configure word size
|
||||
w.set_m0(if config.parity != Parity::ParityNone {
|
||||
vals::M0::BIT9
|
||||
} else {
|
||||
vals::M0::BIT8
|
||||
});
|
||||
// configure parity
|
||||
w.set_pce(config.parity != Parity::ParityNone);
|
||||
w.set_ps(match config.parity {
|
||||
Parity::ParityOdd => vals::Ps::ODD,
|
||||
Parity::ParityEven => vals::Ps::EVEN,
|
||||
_ => vals::Ps::EVEN,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,12 +13,12 @@ pub struct IndependentWatchdog<'d, T: Instance> {
|
||||
const MAX_RL: u16 = 0xFFF;
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
@ -33,12 +33,12 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
|
||||
// Find lowest prescaler value, which makes watchdog period longer or equal to timeout.
|
||||
// This iterates from 4 (2^2) to 256 (2^8).
|
||||
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)
|
||||
}));
|
||||
|
||||
// Prescaler value
|
||||
let psc = 2u8.pow(psc_power);
|
||||
let psc = 2u16.pow(psc_power);
|
||||
|
||||
// Convert prescaler power to PR register value
|
||||
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>
|
||||
where
|
||||
M: RawMutex,
|
||||
|
@ -6,7 +6,7 @@ use crate::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use crate::blocking_mutex::Mutex;
|
||||
|
||||
/// Utility struct to register and wake a waker.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct WakerRegistration {
|
||||
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-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-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.3"
|
||||
@ -30,7 +31,9 @@ byte-slice-cast = { version = "1.2.0", default-features = false }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
|
||||
embedded-hal-async = { version = "0.1.0-alpha.3" }
|
||||
embedded-io = { version = "0.3.1", features = ["async", "defmt"] }
|
||||
embedded-storage = { version = "0.3" }
|
||||
static_cell = "1.0.0"
|
||||
log = "0.4"
|
||||
|
||||
[profile.release]
|
||||
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;
|
||||
}
|
||||
}
|
89
examples/rp/src/bin/flash.rs
Normal file
89
examples/rp/src/bin/flash.rs
Normal file
@ -0,0 +1,89 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE};
|
||||
use embassy_rp::peripherals::FLASH;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const ADDR_OFFSET: u32 = 0x100000;
|
||||
const FLASH_SIZE: usize = 2 * 1024 * 1024;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
// add some delay to give an attached debug probe time to parse the
|
||||
// defmt RTT header. Reading that header might touch flash memory, which
|
||||
// interferes with flash write operations.
|
||||
// https://github.com/knurling-rs/defmt/pull/683
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
|
||||
let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH);
|
||||
erase_write_sector(&mut flash, 0x00);
|
||||
|
||||
multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) {
|
||||
info!(">>>> [multiwrite_bytes]");
|
||||
let mut read_buf = [0u8; ERASE_SIZE];
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf));
|
||||
|
||||
info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
|
||||
info!("Contents start with {=[u8]}", read_buf[0..4]);
|
||||
|
||||
defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
|
||||
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf));
|
||||
info!("Contents after erase starts with {=[u8]}", read_buf[0..4]);
|
||||
if read_buf.iter().any(|x| *x != 0xFF) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
|
||||
defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01]));
|
||||
defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02]));
|
||||
defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03]));
|
||||
defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04]));
|
||||
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf));
|
||||
info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
|
||||
if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
}
|
||||
|
||||
fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) {
|
||||
info!(">>>> [erase_write_sector]");
|
||||
let mut buf = [0u8; ERASE_SIZE];
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf));
|
||||
|
||||
info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
|
||||
info!("Contents start with {=[u8]}", buf[0..4]);
|
||||
|
||||
defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
|
||||
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf));
|
||||
info!("Contents after erase starts with {=[u8]}", buf[0..4]);
|
||||
if buf.iter().any(|x| *x != 0xFF) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
|
||||
for b in buf.iter_mut() {
|
||||
*b = 0xDA;
|
||||
}
|
||||
|
||||
defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf));
|
||||
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf));
|
||||
info!("Contents after write starts with {=[u8]}", buf[0..4]);
|
||||
if buf.iter().any(|x| *x != 0xDA) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
}
|
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() };
|
||||
}
|
||||
}
|
60
examples/stm32wl/src/bin/uart_async.rs
Normal file
60
examples/stm32wl/src/bin/uart_async.rs
Normal file
@ -0,0 +1,60 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::interrupt;
|
||||
use embassy_stm32::usart::{Config, Uart};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
/*
|
||||
Pass Incoming data from LPUART1 to USART1
|
||||
Example is written for the LoRa-E5 mini v1.0,
|
||||
but can be surely changed for your needs.
|
||||
*/
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32;
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
defmt::info!("Starting system");
|
||||
|
||||
let mut config1 = Config::default();
|
||||
config1.baudrate = 9600;
|
||||
|
||||
let mut config2 = Config::default();
|
||||
config2.baudrate = 9600;
|
||||
|
||||
//RX/TX connected to USB/UART Bridge on LoRa-E5 mini v1.0
|
||||
let irq = interrupt::take!(USART1);
|
||||
let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH3, p.DMA1_CH4, config1);
|
||||
|
||||
//RX1/TX1 (LPUART) on LoRa-E5 mini v1.0
|
||||
let irq = interrupt::take!(LPUART1);
|
||||
let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, irq, p.DMA1_CH5, p.DMA1_CH6, config2);
|
||||
|
||||
unwrap!(usart1.write(b"Hello Embassy World!\r\n").await);
|
||||
unwrap!(usart2.write(b"Hello Embassy World!\r\n").await);
|
||||
|
||||
let mut buf = [0u8; 300];
|
||||
loop {
|
||||
let result = usart2.read_until_idle(&mut buf).await;
|
||||
match result {
|
||||
Ok(size) => {
|
||||
match usart1.write(&buf[0..size]).await {
|
||||
Ok(()) => {
|
||||
//Write suc.
|
||||
}
|
||||
Err(..) => {
|
||||
//Wasnt able to write
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
//Ignore eg. framing errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,5 +9,7 @@ license = "MIT OR Apache-2.0"
|
||||
regex = "1.5.4"
|
||||
chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "28ffa8a19d84914089547f52900ffb5877a5dc23" }
|
||||
serde = { version = "1.0.130", features = [ "derive" ] }
|
||||
serde_json = "1.0.87"
|
||||
serde_yaml = "0.8.21"
|
||||
proc-macro2 = "1.0.29"
|
||||
|
||||
|
@ -223,7 +223,7 @@ impl Gen {
|
||||
fn load_chip(&mut self, name: &str) -> Chip {
|
||||
let chip_path = self.opts.data_dir.join("chips").join(&format!("{}.json", name));
|
||||
let chip = fs::read(chip_path).expect(&format!("Could not load chip {}", name));
|
||||
serde_yaml::from_slice(&chip).unwrap()
|
||||
serde_json::from_slice(&chip).unwrap()
|
||||
}
|
||||
|
||||
pub fn gen(&mut self) {
|
||||
|
@ -22,6 +22,7 @@ embedded-hal-async = { version = "=0.1.0-alpha.3" }
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
embedded-io = { version = "0.3.1", features = ["async"] }
|
||||
embedded-storage = { version = "0.3" }
|
||||
|
||||
[profile.dev]
|
||||
debug = 2
|
||||
|
54
tests/rp/src/bin/flash.rs
Normal file
54
tests/rp/src/bin/flash.rs
Normal file
@ -0,0 +1,54 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const ADDR_OFFSET: u32 = 0x4000;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
// add some delay to give an attached debug probe time to parse the
|
||||
// defmt RTT header. Reading that header might touch flash memory, which
|
||||
// interferes with flash write operations.
|
||||
// https://github.com/knurling-rs/defmt/pull/683
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
|
||||
let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH);
|
||||
|
||||
let mut buf = [0u8; ERASE_SIZE];
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf));
|
||||
|
||||
info!("Addr of flash block is {:x}", ADDR_OFFSET + FLASH_BASE as u32);
|
||||
info!("Contents start with {=[u8]}", buf[0..4]);
|
||||
|
||||
defmt::unwrap!(flash.erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32));
|
||||
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf));
|
||||
info!("Contents after erase starts with {=[u8]}", buf[0..4]);
|
||||
if buf.iter().any(|x| *x != 0xFF) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
|
||||
for b in buf.iter_mut() {
|
||||
*b = 0xDA;
|
||||
}
|
||||
|
||||
defmt::unwrap!(flash.write(ADDR_OFFSET, &mut buf));
|
||||
|
||||
defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf));
|
||||
info!("Contents after write starts with {=[u8]}", buf[0..4]);
|
||||
if buf.iter().any(|x| *x != 0xDA) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
Reference in New Issue
Block a user