,
irq: Irq<'d, PIO, 0>,
cs: Output<'d>,
- dio: DIO,
- clk: CLK,
- dma: impl Peripheral + 'd,
- ) -> Self
- where
- DIO: PioPin,
- CLK: PioPin,
- {
+ dio: Peri<'d, impl PioPin>,
+ clk: Peri<'d, impl PioPin>,
+ dma: Peri<'d, DMA>,
+ ) -> Self {
let loaded_program = if clock_divider < DEFAULT_CLOCK_DIVIDER {
let overclock_program = pio_asm!(
".side_set 1"
@@ -146,7 +142,7 @@ where
cs,
sm,
irq,
- dma: dma.into_ref(),
+ dma: dma,
wrap_target: loaded_program.wrap.target,
}
}
diff --git a/docs/pages/overview.adoc b/docs/pages/overview.adoc
index abc7d25de..b169c686e 100644
--- a/docs/pages/overview.adoc
+++ b/docs/pages/overview.adoc
@@ -29,6 +29,7 @@ The Embassy project maintains HALs for select hardware, but you can still use HA
* link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
* link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
* link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 microcontroller.
+* link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers.
* link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips.
* link:https://github.com/ch32-rs/ch32-hal[ch32-hal], for the WCH 32-bit RISC-V(CH32V) series of chips.
* link:https://github.com/AlexCharlton/mpfs-hal[mpfs-hal], for the Microchip PolarFire SoC.
diff --git a/embassy-boot-nrf/src/lib.rs b/embassy-boot-nrf/src/lib.rs
index e5bc870b5..46c1994e2 100644
--- a/embassy-boot-nrf/src/lib.rs
+++ b/embassy-boot-nrf/src/lib.rs
@@ -9,7 +9,7 @@ pub use embassy_boot::{
};
use embassy_nrf::nvmc::PAGE_SIZE;
use embassy_nrf::peripherals::WDT;
-use embassy_nrf::wdt;
+use embassy_nrf::{wdt, Peri};
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
/// A bootloader for nRF devices.
@@ -113,7 +113,7 @@ pub struct WatchdogFlash {
impl WatchdogFlash {
/// Start a new watchdog with a given flash and WDT peripheral and a timeout
- pub fn start(flash: FLASH, wdt: WDT, config: wdt::Config) -> Self {
+ pub fn start(flash: FLASH, wdt: Peri<'static, WDT>, config: wdt::Config) -> Self {
let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) {
Ok(x) => x,
Err(_) => {
diff --git a/embassy-boot-rp/src/lib.rs b/embassy-boot-rp/src/lib.rs
index 6ec33a580..f704380ef 100644
--- a/embassy-boot-rp/src/lib.rs
+++ b/embassy-boot-rp/src/lib.rs
@@ -10,6 +10,7 @@ pub use embassy_boot::{
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
use embassy_rp::peripherals::{FLASH, WATCHDOG};
use embassy_rp::watchdog::Watchdog;
+use embassy_rp::Peri;
use embassy_time::Duration;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
@@ -68,7 +69,7 @@ pub struct WatchdogFlash<'d, const SIZE: usize> {
impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout
- pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self {
+ pub fn start(flash: Peri<'static, FLASH>, watchdog: Peri<'static, WATCHDOG>, timeout: Duration) -> Self {
let flash = Flash::<_, Blocking, SIZE>::new_blocking(flash);
let mut watchdog = Watchdog::new(watchdog);
watchdog.start(timeout);
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs
index e8134c6a9..91d6beee8 100644
--- a/embassy-executor-macros/src/macros/task.rs
+++ b/embassy-executor-macros/src/macros/task.rs
@@ -145,9 +145,20 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
};
#[cfg(not(feature = "nightly"))]
let mut task_outer_body = quote! {
+ const fn __task_pool_get(_: F) -> &'static #embassy_executor::raw::TaskPool
+ where
+ F: #embassy_executor::_export::TaskFn,
+ Fut: ::core::future::Future + 'static,
+ {
+ unsafe { &*POOL.get().cast() }
+ }
+
const POOL_SIZE: usize = #pool_size;
- static POOL: #embassy_executor::_export::TaskPoolRef = #embassy_executor::_export::TaskPoolRef::new();
- unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) }
+ static POOL: #embassy_executor::_export::TaskPoolHolder<
+ {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)},
+ {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)},
+ > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) };
+ unsafe { __task_pool_get(#task_inner_ident)._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) }
};
let task_outer_attrs = task_inner.attrs.clone();
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index d6f24ce84..79d899c61 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -108,98 +108,3 @@ timer-item-payload-size-2 = ["_timer-item-payload"]
timer-item-payload-size-4 = ["_timer-item-payload"]
## 8 bytes
timer-item-payload-size-8 = ["_timer-item-payload"]
-
-#! ### Task Arena Size
-#! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`.
-#!
-#!
-#! Preconfigured Task Arena Sizes:
-#!
-#!
-
-# BEGIN AUTOGENERATED CONFIG FEATURES
-# Generated by gen_config.py. DO NOT EDIT.
-## 64
-task-arena-size-64 = []
-## 128
-task-arena-size-128 = []
-## 192
-task-arena-size-192 = []
-## 256
-task-arena-size-256 = []
-## 320
-task-arena-size-320 = []
-## 384
-task-arena-size-384 = []
-## 512
-task-arena-size-512 = []
-## 640
-task-arena-size-640 = []
-## 768
-task-arena-size-768 = []
-## 1024
-task-arena-size-1024 = []
-## 1280
-task-arena-size-1280 = []
-## 1536
-task-arena-size-1536 = []
-## 2048
-task-arena-size-2048 = []
-## 2560
-task-arena-size-2560 = []
-## 3072
-task-arena-size-3072 = []
-## 4096 (default)
-task-arena-size-4096 = [] # Default
-## 5120
-task-arena-size-5120 = []
-## 6144
-task-arena-size-6144 = []
-## 8192
-task-arena-size-8192 = []
-## 10240
-task-arena-size-10240 = []
-## 12288
-task-arena-size-12288 = []
-## 16384
-task-arena-size-16384 = []
-## 20480
-task-arena-size-20480 = []
-## 24576
-task-arena-size-24576 = []
-## 32768
-task-arena-size-32768 = []
-## 40960
-task-arena-size-40960 = []
-## 49152
-task-arena-size-49152 = []
-## 65536
-task-arena-size-65536 = []
-## 81920
-task-arena-size-81920 = []
-## 98304
-task-arena-size-98304 = []
-## 131072
-task-arena-size-131072 = []
-## 163840
-task-arena-size-163840 = []
-## 196608
-task-arena-size-196608 = []
-## 262144
-task-arena-size-262144 = []
-## 327680
-task-arena-size-327680 = []
-## 393216
-task-arena-size-393216 = []
-## 524288
-task-arena-size-524288 = []
-## 655360
-task-arena-size-655360 = []
-## 786432
-task-arena-size-786432 = []
-## 1048576
-task-arena-size-1048576 = []
-
-# END AUTOGENERATED CONFIG FEATURES
-
-#!
diff --git a/embassy-executor/README.md b/embassy-executor/README.md
index 074c73555..85f15edbb 100644
--- a/embassy-executor/README.md
+++ b/embassy-executor/README.md
@@ -3,35 +3,10 @@
An async/await executor designed for embedded usage.
- No `alloc`, no heap needed.
-- With nightly Rust, task futures can be fully statically allocated.
+- Tasks are statically allocated. Each task gets its own `static`, with the exact size to hold the task (or multiple instances of it, if using `pool_size`) calculated automatically at compile time. If tasks don't fit in RAM, this is detected at compile time by the linker. Runtime panics due to running out of memory are not possible.
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
- Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
- Efficient polling: a wake will only poll the woken task, not all of them.
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
- Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
-
-## Task arena
-
-When the `nightly` Cargo feature is not enabled, `embassy-executor` allocates tasks out of an arena (a very simple bump allocator).
-
-If the task arena gets full, the program will panic at runtime. To guarantee this doesn't happen, you must set the size to the sum of sizes of all tasks.
-
-Tasks are allocated from the arena when spawned for the first time. If the task exits, the allocation is not released to the arena, but can be reused to spawn the task again. For multiple-instance tasks (like `#[embassy_executor::task(pool_size = 4)]`), the first spawn will allocate memory for all instances. This is done for performance and to increase predictability (for example, spawning at least 1 instance of every task at boot guarantees an immediate panic if the arena is too small, while allocating instances on-demand could delay the panic to only when the program is under load).
-
-The arena size can be configured in two ways:
-
-- Via Cargo features: enable a Cargo feature like `task-arena-size-8192`. Only a selection of values
- is available, see [Task Area Sizes](#task-arena-size) for reference.
-- Via environment variables at build time: set the variable named `EMBASSY_EXECUTOR_TASK_ARENA_SIZE`. For example
- `EMBASSY_EXECUTOR_TASK_ARENA_SIZE=4321 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
- Any value can be set, unlike with Cargo features.
-
-Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting
-with different values, compilation fails.
-
-## Statically allocating tasks
-
-When using nightly Rust, enable the `nightly` Cargo feature. This will make `embassy-executor` use the `type_alias_impl_trait` feature to allocate all tasks in `static`s. Each task gets its own `static`, with the exact size to hold the task (or multiple instances of it, if using `pool_size`) calculated automatically at compile time. If tasks don't fit in RAM, this is detected at compile time by the linker. Runtime panics due to running out of memory are not possible.
-
-The configured arena size is ignored, no arena is used at all.
diff --git a/embassy-executor/build.rs b/embassy-executor/build.rs
index 8a41d7503..37becde3e 100644
--- a/embassy-executor/build.rs
+++ b/embassy-executor/build.rs
@@ -1,99 +1,7 @@
-use std::collections::HashMap;
-use std::fmt::Write;
-use std::path::PathBuf;
-use std::{env, fs};
-
#[path = "./build_common.rs"]
mod common;
-static CONFIGS: &[(&str, usize)] = &[
- // BEGIN AUTOGENERATED CONFIG FEATURES
- // Generated by gen_config.py. DO NOT EDIT.
- ("TASK_ARENA_SIZE", 4096),
- // END AUTOGENERATED CONFIG FEATURES
-];
-
-struct ConfigState {
- value: usize,
- seen_feature: bool,
- seen_env: bool,
-}
-
fn main() {
- let crate_name = env::var("CARGO_PKG_NAME")
- .unwrap()
- .to_ascii_uppercase()
- .replace('-', "_");
-
- // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any
- // other file changed.
- println!("cargo:rerun-if-changed=build.rs");
-
- // Rebuild if config envvar changed.
- for (name, _) in CONFIGS {
- println!("cargo:rerun-if-env-changed={crate_name}_{name}");
- }
-
- let mut configs = HashMap::new();
- for (name, default) in CONFIGS {
- configs.insert(
- *name,
- ConfigState {
- value: *default,
- seen_env: false,
- seen_feature: false,
- },
- );
- }
-
- let prefix = format!("{crate_name}_");
- for (var, value) in env::vars() {
- if let Some(name) = var.strip_prefix(&prefix) {
- let Some(cfg) = configs.get_mut(name) else {
- panic!("Unknown env var {name}")
- };
-
- let Ok(value) = value.parse::() else {
- panic!("Invalid value for env var {name}: {value}")
- };
-
- cfg.value = value;
- cfg.seen_env = true;
- }
-
- if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") {
- if let Some(i) = feature.rfind('_') {
- let name = &feature[..i];
- let value = &feature[i + 1..];
- if let Some(cfg) = configs.get_mut(name) {
- let Ok(value) = value.parse::() else {
- panic!("Invalid value for feature {name}: {value}")
- };
-
- // envvars take priority.
- if !cfg.seen_env {
- if cfg.seen_feature {
- panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value);
- }
-
- cfg.value = value;
- cfg.seen_feature = true;
- }
- }
- }
- }
- }
-
- let mut data = String::new();
-
- for (name, cfg) in &configs {
- writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap();
- }
-
- let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
- let out_file = out_dir.join("config.rs").to_string_lossy().to_string();
- fs::write(out_file, data).unwrap();
-
let mut rustc_cfgs = common::CfgSet::new();
common::set_target_cfgs(&mut rustc_cfgs);
}
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs
index d816539ac..d6bd63665 100644
--- a/embassy-executor/src/lib.rs
+++ b/embassy-executor/src/lib.rs
@@ -50,101 +50,155 @@ pub mod raw;
mod spawner;
pub use spawner::*;
-mod config {
- #![allow(unused)]
- include!(concat!(env!("OUT_DIR"), "/config.rs"));
-}
-
/// Implementation details for embassy macros.
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
pub mod _export {
- use core::alloc::Layout;
- use core::cell::{Cell, UnsafeCell};
+ use core::cell::UnsafeCell;
use core::future::Future;
use core::mem::MaybeUninit;
- use core::ptr::null_mut;
-
- use critical_section::{CriticalSection, Mutex};
use crate::raw::TaskPool;
- struct Arena {
- buf: UnsafeCell>,
- ptr: Mutex>,
+ pub trait TaskFn: Copy {
+ type Fut: Future + 'static;
}
- unsafe impl Sync for Arena {}
- unsafe impl Send for Arena {}
-
- impl Arena {
- const fn new() -> Self {
- Self {
- buf: UnsafeCell::new(MaybeUninit::uninit()),
- ptr: Mutex::new(Cell::new(null_mut())),
+ macro_rules! task_fn_impl {
+ ($($Tn:ident),*) => {
+ impl TaskFn<($($Tn,)*)> for F
+ where
+ F: Copy + FnOnce($($Tn,)*) -> Fut,
+ Fut: Future + 'static,
+ {
+ type Fut = Fut;
}
- }
+ };
+ }
- fn alloc(&'static self, cs: CriticalSection) -> &'static mut MaybeUninit {
- let layout = Layout::new::();
+ task_fn_impl!();
+ task_fn_impl!(T0);
+ task_fn_impl!(T0, T1);
+ task_fn_impl!(T0, T1, T2);
+ task_fn_impl!(T0, T1, T2, T3);
+ task_fn_impl!(T0, T1, T2, T3, T4);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
+ task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
- let start = self.buf.get().cast::();
- let end = unsafe { start.add(N) };
+ #[allow(private_bounds)]
+ #[repr(C)]
+ pub struct TaskPoolHolder
+ where
+ Align: Alignment,
+ {
+ data: UnsafeCell<[MaybeUninit; SIZE]>,
+ align: Align,
+ }
- let mut ptr = self.ptr.borrow(cs).get();
- if ptr.is_null() {
- ptr = self.buf.get().cast::();
- }
+ unsafe impl Send for TaskPoolHolder where Align: Alignment {}
+ unsafe impl Sync for TaskPoolHolder where Align: Alignment {}
- let bytes_left = (end as usize) - (ptr as usize);
- let align_offset = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize);
-
- if align_offset + layout.size() > bytes_left {
- panic!("embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/");
- }
-
- let res = unsafe { ptr.add(align_offset) };
- let ptr = unsafe { ptr.add(align_offset + layout.size()) };
-
- self.ptr.borrow(cs).set(ptr);
-
- unsafe { &mut *(res as *mut MaybeUninit) }
+ #[allow(private_bounds)]
+ impl TaskPoolHolder
+ where
+ Align: Alignment,
+ {
+ pub const fn get(&self) -> *const u8 {
+ self.data.get().cast()
}
}
- static ARENA: Arena<{ crate::config::TASK_ARENA_SIZE }> = Arena::new();
-
- pub struct TaskPoolRef {
- // type-erased `&'static mut TaskPool`
- // Needed because statics can't have generics.
- ptr: Mutex| >,
+ pub const fn task_pool_size(_: F) -> usize
+ where
+ F: TaskFn,
+ Fut: Future + 'static,
+ {
+ size_of::>()
}
- unsafe impl Sync for TaskPoolRef {}
- unsafe impl Send for TaskPoolRef {}
- impl TaskPoolRef {
- pub const fn new() -> Self {
- Self {
- ptr: Mutex::new(Cell::new(null_mut())),
- }
- }
+ pub const fn task_pool_align(_: F) -> usize
+ where
+ F: TaskFn,
+ Fut: Future + 'static,
+ {
+ align_of::>()
+ }
- /// Get the pool for this ref, allocating it from the arena the first time.
- ///
- /// safety: for a given TaskPoolRef instance, must always call with the exact
- /// same generic params.
- pub unsafe fn get(&'static self) -> &'static TaskPool {
- critical_section::with(|cs| {
- let ptr = self.ptr.borrow(cs);
- if ptr.get().is_null() {
- let pool = ARENA.alloc::>(cs);
- pool.write(TaskPool::new());
- ptr.set(pool as *mut _ as _);
+ pub const fn task_pool_new(_: F) -> TaskPool
+ where
+ F: TaskFn,
+ Fut: Future + 'static,
+ {
+ TaskPool::new()
+ }
+
+ #[allow(private_bounds)]
+ #[repr(transparent)]
+ pub struct Align([::Archetype; 0])
+ where
+ Self: Alignment;
+
+ trait Alignment {
+ /// A zero-sized type of particular alignment.
+ type Archetype: Copy + Eq + PartialEq + Send + Sync + Unpin;
+ }
+
+ macro_rules! aligns {
+ ($($AlignX:ident: $n:literal,)*) => {
+ $(
+ #[derive(Copy, Clone, Eq, PartialEq)]
+ #[repr(align($n))]
+ struct $AlignX {}
+ impl Alignment for Align<$n> {
+ type Archetype = $AlignX;
}
-
- unsafe { &*(ptr.get() as *const _) }
- })
- }
+ )*
+ };
}
+
+ aligns!(
+ Align1: 1,
+ Align2: 2,
+ Align4: 4,
+ Align8: 8,
+ Align16: 16,
+ Align32: 32,
+ Align64: 64,
+ Align128: 128,
+ Align256: 256,
+ Align512: 512,
+ Align1024: 1024,
+ Align2048: 2048,
+ Align4096: 4096,
+ Align8192: 8192,
+ Align16384: 16384,
+ );
+ #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
+ aligns!(
+ Align32768: 32768,
+ Align65536: 65536,
+ Align131072: 131072,
+ Align262144: 262144,
+ Align524288: 524288,
+ Align1048576: 1048576,
+ Align2097152: 2097152,
+ Align4194304: 4194304,
+ Align8388608: 8388608,
+ Align16777216: 16777216,
+ Align33554432: 33554432,
+ Align67108864: 67108864,
+ Align134217728: 134217728,
+ Align268435456: 268435456,
+ Align536870912: 536870912,
+ );
}
diff --git a/embassy-executor/tests/ui.rs b/embassy-executor/tests/ui.rs
index be4679485..278a4b903 100644
--- a/embassy-executor/tests/ui.rs
+++ b/embassy-executor/tests/ui.rs
@@ -19,5 +19,6 @@ fn ui() {
t.compile_fail("tests/ui/not_async.rs");
t.compile_fail("tests/ui/self_ref.rs");
t.compile_fail("tests/ui/self.rs");
+ t.compile_fail("tests/ui/type_error.rs");
t.compile_fail("tests/ui/where_clause.rs");
}
diff --git a/embassy-executor/tests/ui/type_error.rs b/embassy-executor/tests/ui/type_error.rs
new file mode 100644
index 000000000..1734bc6c4
--- /dev/null
+++ b/embassy-executor/tests/ui/type_error.rs
@@ -0,0 +1,8 @@
+#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
+
+#[embassy_executor::task]
+async fn task() {
+ 5
+}
+
+fn main() {}
diff --git a/embassy-executor/tests/ui/type_error.stderr b/embassy-executor/tests/ui/type_error.stderr
new file mode 100644
index 000000000..bce315811
--- /dev/null
+++ b/embassy-executor/tests/ui/type_error.stderr
@@ -0,0 +1,7 @@
+error[E0308]: mismatched types
+ --> tests/ui/type_error.rs:5:5
+ |
+4 | async fn task() {
+ | - help: try adding a return type: `-> i32`
+5 | 5
+ | ^ expected `()`, found integer
diff --git a/embassy-hal-internal/src/lib.rs b/embassy-hal-internal/src/lib.rs
index 89f20e993..7addb71e2 100644
--- a/embassy-hal-internal/src/lib.rs
+++ b/embassy-hal-internal/src/lib.rs
@@ -11,7 +11,7 @@ pub mod drop;
mod macros;
mod peripheral;
pub mod ratio;
-pub use peripheral::{Peripheral, PeripheralRef};
+pub use peripheral::{Peri, PeripheralType};
#[cfg(feature = "cortex-m")]
pub mod interrupt;
diff --git a/embassy-hal-internal/src/macros.rs b/embassy-hal-internal/src/macros.rs
index 07cd89487..cd2bc3cab 100644
--- a/embassy-hal-internal/src/macros.rs
+++ b/embassy-hal-internal/src/macros.rs
@@ -18,8 +18,8 @@ macro_rules! peripherals_definition {
///
/// You must ensure that you're only using one instance of this type at a time.
#[inline]
- pub unsafe fn steal() -> Self {
- Self{ _private: ()}
+ pub unsafe fn steal() -> $crate::Peri<'static, Self> {
+ $crate::Peri::new_unchecked(Self{ _private: ()})
}
}
@@ -42,7 +42,7 @@ macro_rules! peripherals_struct {
$(
#[doc = concat!(stringify!($name), " peripheral")]
$(#[$cfg])?
- pub $name: peripherals::$name,
+ pub $name: $crate::Peri<'static, peripherals::$name>,
)*
}
@@ -108,28 +108,26 @@ macro_rules! peripherals {
};
}
-/// Convenience converting into reference.
-#[macro_export]
-macro_rules! into_ref {
- ($($name:ident),*) => {
- $(
- let mut $name = $name.into_ref();
- )*
- }
-}
-
/// Implement the peripheral trait.
#[macro_export]
macro_rules! impl_peripheral {
- ($type:ident) => {
- impl $crate::Peripheral for $type {
- type P = $type;
-
- #[inline]
- unsafe fn clone_unchecked(&self) -> Self::P {
- #[allow(clippy::needless_update)]
- $type { ..*self }
+ ($type:ident<$($T:ident $(: $bound:tt $(+ $others:tt )*)?),*>) => {
+ impl<$($T: $($bound $(+$others)*)?),*> Copy for $type <$($T),*> {}
+ impl<$($T: $($bound $(+$others)*)?),*> Clone for $type <$($T),*> {
+ fn clone(&self) -> Self {
+ *self
}
}
+ impl<$($T: $($bound $(+$others)*)?),*> PeripheralType for $type <$($T),*> {}
+ };
+
+ ($type:ident) => {
+ impl Copy for $type {}
+ impl Clone for $type {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+ impl $crate::PeripheralType for $type {}
};
}
diff --git a/embassy-hal-internal/src/peripheral.rs b/embassy-hal-internal/src/peripheral.rs
index 0b0f13338..803259bb8 100644
--- a/embassy-hal-internal/src/peripheral.rs
+++ b/embassy-hal-internal/src/peripheral.rs
@@ -1,5 +1,5 @@
use core::marker::PhantomData;
-use core::ops::{Deref, DerefMut};
+use core::ops::Deref;
/// An exclusive reference to a peripheral.
///
@@ -9,20 +9,26 @@ use core::ops::{Deref, DerefMut};
/// - Memory efficiency: Peripheral singletons are typically either zero-sized (for concrete
/// peripherals like `PA9` or `SPI4`) or very small (for example `AnyPin`, which is 1 byte).
/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized.
-/// PeripheralRef stores a copy of `T` instead, so it's the same size.
+/// Peripheral stores a copy of `T` instead, so it's the same size.
/// - Code size efficiency. If the user uses the same driver with both `SPI4` and `&mut SPI4`,
-/// the driver code would be monomorphized two times. With PeripheralRef, the driver is generic
-/// over a lifetime only. `SPI4` becomes `PeripheralRef<'static, SPI4>`, and `&mut SPI4` becomes
-/// `PeripheralRef<'a, SPI4>`. Lifetimes don't cause monomorphization.
-pub struct PeripheralRef<'a, T> {
+/// the driver code would be monomorphized two times. With Peri, the driver is generic
+/// over a lifetime only. `SPI4` becomes `Peri<'static, SPI4>`, and `&mut SPI4` becomes
+/// `Peri<'a, SPI4>`. Lifetimes don't cause monomorphization.
+pub struct Peri<'a, T: PeripheralType> {
inner: T,
_lifetime: PhantomData<&'a mut T>,
}
-impl<'a, T> PeripheralRef<'a, T> {
- /// Create a new reference to a peripheral.
+impl<'a, T: PeripheralType> Peri<'a, T> {
+ /// Create a new owned a peripheral.
+ ///
+ /// For use by HALs only.
+ ///
+ /// If you're an end user you shouldn't use this, you should use `steal()`
+ /// on the actual peripheral types instead.
#[inline]
- pub fn new(inner: T) -> Self {
+ #[doc(hidden)]
+ pub unsafe fn new_unchecked(inner: T) -> Self {
Self {
inner,
_lifetime: PhantomData,
@@ -38,46 +44,38 @@ impl<'a, T> PeripheralRef<'a, T> {
/// create two SPI drivers on `SPI1`, because they will "fight" each other.
///
/// You should strongly prefer using `reborrow()` instead. It returns a
- /// `PeripheralRef` that borrows `self`, which allows the borrow checker
+ /// `Peri` that borrows `self`, which allows the borrow checker
/// to enforce this at compile time.
- pub unsafe fn clone_unchecked(&self) -> PeripheralRef<'a, T>
- where
- T: Peripheral ,
- {
- PeripheralRef::new(self.inner.clone_unchecked())
+ pub unsafe fn clone_unchecked(&self) -> Peri<'a, T> {
+ Peri::new_unchecked(self.inner)
}
- /// Reborrow into a "child" PeripheralRef.
+ /// Reborrow into a "child" Peri.
///
- /// `self` will stay borrowed until the child PeripheralRef is dropped.
- pub fn reborrow(&mut self) -> PeripheralRef<'_, T>
- where
- T: Peripheral ,
- {
- // safety: we're returning the clone inside a new PeripheralRef that borrows
+ /// `self` will stay borrowed until the child Peripheral is dropped.
+ pub fn reborrow(&mut self) -> Peri<'_, T> {
+ // safety: we're returning the clone inside a new Peripheral that borrows
// self, so user code can't use both at the same time.
- PeripheralRef::new(unsafe { self.inner.clone_unchecked() })
+ unsafe { self.clone_unchecked() }
}
/// Map the inner peripheral using `Into`.
///
- /// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`, using an
+ /// This converts from `Peri<'a, T>` to `Peri<'a, U>`, using an
/// `Into` impl to convert from `T` to `U`.
///
- /// For example, this can be useful to degrade GPIO pins: converting from PeripheralRef<'a, PB11>` to `PeripheralRef<'a, AnyPin>`.
+ /// For example, this can be useful to.into() GPIO pins: converting from Peri<'a, PB11>` to `Peri<'a, AnyPin>`.
#[inline]
- pub fn map_into(self) -> PeripheralRef<'a, U>
+ pub fn into(self) -> Peri<'a, U>
where
T: Into,
+ U: PeripheralType,
{
- PeripheralRef {
- inner: self.inner.into(),
- _lifetime: PhantomData,
- }
+ unsafe { Peri::new_unchecked(self.inner.into()) }
}
}
-impl<'a, T> Deref for PeripheralRef<'a, T> {
+impl<'a, T: PeripheralType> Deref for Peri<'a, T> {
type Target = T;
#[inline]
@@ -86,92 +84,5 @@ impl<'a, T> Deref for PeripheralRef<'a, T> {
}
}
-/// Trait for any type that can be used as a peripheral of type `P`.
-///
-/// This is used in driver constructors, to allow passing either owned peripherals (e.g. `TWISPI0`),
-/// or borrowed peripherals (e.g. `&mut TWISPI0`).
-///
-/// For example, if you have a driver with a constructor like this:
-///
-/// ```ignore
-/// impl<'d, T: Instance> Twim<'d, T> {
-/// pub fn new(
-/// twim: impl Peripheral + 'd,
-/// irq: impl Peripheral + 'd,
-/// sda: impl Peripheral + 'd,
-/// scl: impl Peripheral + 'd,
-/// config: Config,
-/// ) -> Self { .. }
-/// }
-/// ```
-///
-/// You may call it with owned peripherals, which yields an instance that can live forever (`'static`):
-///
-/// ```ignore
-/// let mut twi: Twim<'static, ...> = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
-/// ```
-///
-/// Or you may call it with borrowed peripherals, which yields an instance that can only live for as long
-/// as the borrows last:
-///
-/// ```ignore
-/// let mut twi: Twim<'_, ...> = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config);
-/// ```
-///
-/// # Implementation details, for HAL authors
-///
-/// When writing a HAL, the intended way to use this trait is to take `impl Peripheral ` in
-/// the HAL's public API (such as driver constructors), calling `.into_ref()` to obtain a `PeripheralRef`,
-/// and storing that in the driver struct.
-///
-/// `.into_ref()` on an owned `T` yields a `PeripheralRef<'static, T>`.
-/// `.into_ref()` on an `&'a mut T` yields a `PeripheralRef<'a, T>`.
-pub trait Peripheral: Sized {
- /// Peripheral singleton type
- type P;
-
- /// Unsafely clone (duplicate) a peripheral singleton.
- ///
- /// # Safety
- ///
- /// This returns an owned clone of the peripheral. You must manually ensure
- /// only one copy of the peripheral is in use at a time. For example, don't
- /// create two SPI drivers on `SPI1`, because they will "fight" each other.
- ///
- /// You should strongly prefer using `into_ref()` instead. It returns a
- /// `PeripheralRef`, which allows the borrow checker to enforce this at compile time.
- unsafe fn clone_unchecked(&self) -> Self::P;
-
- /// Convert a value into a `PeripheralRef`.
- ///
- /// When called on an owned `T`, yields a `PeripheralRef<'static, T>`.
- /// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`.
- #[inline]
- fn into_ref<'a>(self) -> PeripheralRef<'a, Self::P>
- where
- Self: 'a,
- {
- PeripheralRef::new(unsafe { self.clone_unchecked() })
- }
-}
-
-impl<'b, T: DerefMut> Peripheral for T
-where
- T::Target: Peripheral,
-{
- type P = ::P;
-
- #[inline]
- unsafe fn clone_unchecked(&self) -> Self::P {
- T::Target::clone_unchecked(self)
- }
-}
-
-impl<'b, T: Peripheral> Peripheral for PeripheralRef<'_, T> {
- type P = T::P;
-
- #[inline]
- unsafe fn clone_unchecked(&self) -> Self::P {
- T::clone_unchecked(self)
- }
-}
+/// Marker trait for peripheral types.
+pub trait PeripheralType: Copy + Sized {}
diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml
new file mode 100644
index 000000000..28a8e7724
--- /dev/null
+++ b/embassy-mspm0/Cargo.toml
@@ -0,0 +1,132 @@
+[package]
+name = "embassy-mspm0"
+version = "0.1.0"
+edition = "2021"
+license = "MIT OR Apache-2.0"
+description = "Embassy Hardware Abstraction Layer (HAL) for Texas Instruments MSPM0 series microcontrollers"
+keywords = ["embedded", "async", "mspm0", "hal", "embedded-hal"]
+categories = ["embedded", "hardware-support", "no-std", "asynchronous"]
+repository = "https://github.com/embassy-rs/embassy"
+documentation = "https://docs.embassy.dev/embassy-mspm0"
+
+[package.metadata.embassy_docs]
+src_base = "https://github.com/embassy-rs/embassy/blob/embassy-mspm0-v$VERSION/embassy-mspm0/src/"
+src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-mspm0/src/"
+
+features = ["defmt", "unstable-pac", "time-driver-any"]
+flavors = [
+ { regex_feature = "mspm0c.*", target = "thumbv6m-none-eabi" },
+ { regex_feature = "mspm0l.*", target = "thumbv6m-none-eabi" },
+ { regex_feature = "mspm0g.*", target = "thumbv6m-none-eabi" },
+]
+
+[package.metadata.docs.rs]
+features = ["defmt", "unstable-pac", "time-driver-any", "time", "mspm0g3507"]
+rustdoc-args = ["--cfg", "docsrs"]
+
+[dependencies]
+embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
+embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
+# TODO: Support other tick rates
+embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true, features = ["tick-hz-32_768"] }
+embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
+embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
+embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
+embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false }
+embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true }
+
+embedded-hal = { version = "1.0" }
+embedded-hal-async = { version = "1.0" }
+
+defmt = { version = "0.3", optional = true }
+log = { version = "0.4.14", optional = true }
+cortex-m-rt = ">=0.6.15,<0.8"
+cortex-m = "0.7.6"
+critical-section = "1.2.0"
+
+# mspm0-metapac = { version = "" }
+mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-119240dd23ef5748d2a7bef219ca298d37ba604a" }
+
+[build-dependencies]
+proc-macro2 = "1.0.94"
+quote = "1.0.40"
+
+# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] }
+mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-119240dd23ef5748d2a7bef219ca298d37ba604a", default-features = false, features = ["metadata"] }
+
+[features]
+default = ["rt"]
+
+## Enable `mspm0-metapac`'s `rt` feature
+rt = ["mspm0-metapac/rt"]
+
+## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging
+defmt = [
+ "dep:defmt",
+ "embassy-sync/defmt",
+ "embassy-embedded-hal/defmt",
+ "embassy-hal-internal/defmt",
+ "embassy-time?/defmt",
+]
+
+## Re-export mspm0-metapac at `mspm0::pac`.
+## This is unstable because semver-minor (non-breaking) releases of embassy-mspm0 may major-bump (breaking) the mspm0-metapac version.
+## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.
+## There are no plans to make this stable.
+unstable-pac = []
+
+#! ## Time
+
+# Features starting with `_` are for internal use only. They're not intended
+# to be enabled by other crates, and are not covered by semver guarantees.
+_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"]
+
+# Use any time driver
+time-driver-any = ["_time-driver"]
+## Use TIMG0 as time driver
+time-driver-timg0 = ["_time-driver"]
+## Use TIMG1 as time driver
+time-driver-timg1 = ["_time-driver"]
+## Use TIMG2 as time driver
+time-driver-timg2 = ["_time-driver"]
+## Use TIMG3 as time driver
+time-driver-timg3 = ["_time-driver"]
+## Use TIMG4 as time driver
+time-driver-timg4 = ["_time-driver"]
+## Use TIMG5 as time driver
+time-driver-timg5 = ["_time-driver"]
+## Use TIMG6 as time driver
+time-driver-timg6 = ["_time-driver"]
+## Use TIMG7 as time driver
+time-driver-timg7 = ["_time-driver"]
+## Use TIMG8 as time driver
+time-driver-timg8 = ["_time-driver"]
+## Use TIMG9 as time driver
+time-driver-timg9 = ["_time-driver"]
+## Use TIMG10 as time driver
+time-driver-timg10 = ["_time-driver"]
+## Use TIMG11 as time driver
+time-driver-timg11 = ["_time-driver"]
+## Use TIMG12 as time driver
+time-driver-timg12 = ["_time-driver"]
+## Use TIMG13 as time driver
+time-driver-timg13 = ["_time-driver"]
+## Use TIMG14 as time driver
+time-driver-timg14 = ["_time-driver"]
+## Use TIMA0 as time driver
+time-driver-tima0 = ["_time-driver"]
+## Use TIMA1 as time driver
+time-driver-tima1 = ["_time-driver"]
+
+#! ## Chip-selection features
+#! Select your chip by specifying the model as a feature, e.g. `mspm0g350x`.
+#! Check the `Cargo.toml` for the latest list of supported chips.
+#!
+#! **Important:** Do not forget to adapt the target chip in your toolchain,
+#! e.g. in `.cargo/config.toml`.
+
+mspm0c110x = [ "mspm0-metapac/mspm0c110x" ]
+mspm0g350x = [ "mspm0-metapac/mspm0g350x" ]
+mspm0g351x = [ "mspm0-metapac/mspm0g351x" ]
+mspm0l130x = [ "mspm0-metapac/mspm0l130x" ]
+mspm0l222x = [ "mspm0-metapac/mspm0l222x" ]
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
new file mode 100644
index 000000000..39d8b2f8a
--- /dev/null
+++ b/embassy-mspm0/build.rs
@@ -0,0 +1,802 @@
+use std::cmp::Ordering;
+use std::collections::{BTreeSet, HashMap};
+use std::io::Write;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+use std::sync::LazyLock;
+use std::{env, fs};
+
+use common::CfgSet;
+use mspm0_metapac::metadata::METADATA;
+use proc_macro2::{Ident, Literal, Span, TokenStream};
+use quote::{format_ident, quote};
+
+#[path = "./build_common.rs"]
+mod common;
+
+fn main() {
+ generate_code();
+}
+
+fn generate_code() {
+ let mut cfgs = common::CfgSet::new();
+ common::set_target_cfgs(&mut cfgs);
+
+ cfgs.declare_all(&["gpio_pb", "gpio_pc", "int_group1"]);
+
+ let mut singletons = get_singletons(&mut cfgs);
+
+ time_driver(&mut singletons, &mut cfgs);
+
+ let mut g = TokenStream::new();
+
+ g.extend(generate_singletons(&singletons));
+ g.extend(generate_pincm_mapping());
+ g.extend(generate_pin());
+ g.extend(generate_timers());
+ g.extend(generate_interrupts());
+ g.extend(generate_peripheral_instances());
+ g.extend(generate_pin_trait_impls());
+
+ let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
+ fs::write(&out_file, g.to_string()).unwrap();
+ rustfmt(&out_file);
+}
+
+#[derive(Debug, Clone)]
+struct Singleton {
+ name: String,
+
+ cfg: Option,
+}
+
+impl PartialEq for Singleton {
+ fn eq(&self, other: &Self) -> bool {
+ self.name == other.name
+ }
+}
+
+impl Eq for Singleton {}
+
+impl PartialOrd for Singleton {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Singleton {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.name.cmp(&other.name)
+ }
+}
+
+fn get_singletons(cfgs: &mut common::CfgSet) -> Vec {
+ let mut singletons = Vec::::new();
+
+ for peripheral in METADATA.peripherals {
+ // Some peripherals do not generate a singleton, but generate a singleton for each pin.
+ let skip_peripheral_singleton = match peripheral.kind {
+ "gpio" => {
+ // Also enable ports that are present.
+ match peripheral.name {
+ "GPIOB" => cfgs.enable("gpio_pb"),
+ "GPIOC" => cfgs.enable("gpio_pc"),
+ _ => (),
+ }
+
+ true
+ }
+
+ // Each channel gets a singleton, handled separately.
+ "dma" => true,
+
+ // These peripherals do not exist as singletons, and have no signals but are managed
+ // by the HAL.
+ "iomux" | "cpuss" => true,
+
+ _ => false,
+ };
+
+ if !skip_peripheral_singleton {
+ singletons.push(Singleton {
+ name: peripheral.name.to_string(),
+ cfg: None,
+ });
+ }
+
+ let mut signals = BTreeSet::new();
+
+ // Pick out each unique signal. There may be multiple instances of each signal due to
+ // iomux mappings.
+ for pin in peripheral.pins {
+ let signal = if peripheral.name.starts_with("GPIO")
+ || peripheral.name.starts_with("VREF")
+ || peripheral.name.starts_with("RTC")
+ {
+ pin.signal.to_string()
+ } else {
+ format!("{}_{}", peripheral.name, pin.signal)
+ };
+
+ // We need to rename some signals to become valid Rust identifiers.
+ let signal = make_valid_identifier(&signal);
+ signals.insert(signal);
+ }
+
+ singletons.extend(signals);
+ }
+
+ // DMA channels get their own singletons
+ for dma_channel in METADATA.dma_channels.iter() {
+ singletons.push(Singleton {
+ name: format!("DMA_CH{}", dma_channel.number),
+ cfg: None,
+ });
+ }
+
+ singletons.sort_by(|a, b| a.name.cmp(&b.name));
+ singletons
+}
+
+fn make_valid_identifier(s: &str) -> Singleton {
+ let name = s.replace('+', "_P").replace("-", "_N");
+
+ Singleton { name, cfg: None }
+}
+
+fn generate_pincm_mapping() -> TokenStream {
+ let pincms = METADATA.pincm_mappings.iter().map(|mapping| {
+ let port_letter = mapping.pin.strip_prefix("P").unwrap();
+ let port_base = (port_letter.chars().next().unwrap() as u8 - b'A') * 32;
+ // This assumes all ports are single letter length.
+ // This is fine unless TI releases a part with 833+ GPIO pins.
+ let pin_number = mapping.pin[2..].parse::().unwrap();
+
+ let num = port_base + pin_number;
+
+ // But subtract 1 since pincm indices start from 0, not 1.
+ let pincm = Literal::u8_unsuffixed(mapping.pincm - 1);
+ quote! {
+ #num => #pincm
+ }
+ });
+
+ quote! {
+ #[doc = "Get the mapping from GPIO pin port to IOMUX PINCM index. This is required since the mapping from IO to PINCM index is not consistent across parts."]
+ pub(crate) fn gpio_pincm(pin_port: u8) -> u8 {
+ match pin_port {
+ #(#pincms),*,
+ _ => unreachable!(),
+ }
+ }
+ }
+}
+
+fn generate_pin() -> TokenStream {
+ let pin_impls = METADATA.pincm_mappings.iter().map(|pincm_mapping| {
+ let name = Ident::new(&pincm_mapping.pin, Span::call_site());
+ let port_letter = pincm_mapping.pin.strip_prefix("P").unwrap();
+ let port_letter = port_letter.chars().next().unwrap();
+ let pin_number = Literal::u8_unsuffixed(pincm_mapping.pin[2..].parse::().unwrap());
+
+ let port = Ident::new(&format!("Port{}", port_letter), Span::call_site());
+
+ // TODO: Feature gate pins that can be used as NRST
+
+ quote! {
+ impl_pin!(#name, crate::gpio::Port::#port, #pin_number);
+ }
+ });
+
+ quote! {
+ #(#pin_impls)*
+ }
+}
+
+fn time_driver(singletons: &mut Vec, cfgs: &mut CfgSet) {
+ // Timer features
+ for (timer, _) in TIMERS.iter() {
+ let name = timer.to_lowercase();
+ cfgs.declare(&format!("time_driver_{}", name));
+ }
+
+ let time_driver = match env::vars()
+ .map(|(a, _)| a)
+ .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
+ .get_one()
+ {
+ Ok(x) => Some(
+ x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
+ .unwrap()
+ .to_ascii_lowercase(),
+ ),
+ Err(GetOneError::None) => None,
+ Err(GetOneError::Multiple) => panic!("Multiple time-driver-xxx Cargo features enabled"),
+ };
+
+ // Verify the selected timer is available
+ let selected_timer = match time_driver.as_ref().map(|x| x.as_ref()) {
+ None => "",
+ Some("timg0") => "TIMG0",
+ Some("timg1") => "TIMG1",
+ Some("timg2") => "TIMG2",
+ Some("timg3") => "TIMG3",
+ Some("timg4") => "TIMG4",
+ Some("timg5") => "TIMG5",
+ Some("timg6") => "TIMG6",
+ Some("timg7") => "TIMG7",
+ Some("timg8") => "TIMG8",
+ Some("timg9") => "TIMG9",
+ Some("timg10") => "TIMG10",
+ Some("timg11") => "TIMG11",
+ Some("timg14") => "TIMG14",
+ Some("tima0") => "TIMA0",
+ Some("tima1") => "TIMA1",
+ Some("any") => {
+ // Order of timer candidates:
+ // 1. 16-bit, 2 channel
+ // 2. 16-bit, 2 channel with shadow registers
+ // 3. 16-bit, 4 channel
+ // 4. 16-bit with QEI
+ // 5. Advanced timers
+ //
+ // TODO: Select RTC first if available
+ // TODO: 32-bit timers are not considered yet
+ [
+ // 16-bit, 2 channel
+ "TIMG0", "TIMG1", "TIMG2", "TIMG3", // 16-bit, 2 channel with shadow registers
+ "TIMG4", "TIMG5", "TIMG6", "TIMG7", // 16-bit, 4 channel
+ "TIMG14", // 16-bit with QEI
+ "TIMG8", "TIMG9", "TIMG10", "TIMG11", // Advanced timers
+ "TIMA0", "TIMA1",
+ ]
+ .iter()
+ .find(|tim| singletons.iter().any(|s| s.name == **tim))
+ .expect("Could not find any timer")
+ }
+ _ => panic!("unknown time_driver {:?}", time_driver),
+ };
+
+ if !selected_timer.is_empty() {
+ cfgs.enable(format!("time_driver_{}", selected_timer.to_lowercase()));
+ }
+
+ // Apply cfgs to each timer and it's pins
+ for singleton in singletons.iter_mut() {
+ if singleton.name.starts_with("TIM") {
+ // Remove suffixes for pin singletons.
+ let name = if singleton.name.contains("_CCP") {
+ singleton.name.split_once("_CCP").unwrap().0
+ } else if singleton.name.contains("_FAULT") {
+ singleton.name.split_once("_FAULT").unwrap().0
+ } else if singleton.name.contains("_IDX") {
+ singleton.name.split_once("_IDX").unwrap().0
+ } else {
+ &singleton.name
+ };
+
+ let feature = format!("time-driver-{}", name.to_lowercase());
+
+ if singleton.name.contains(selected_timer) {
+ singleton.cfg = Some(quote! { #[cfg(not(all(feature = "time-driver-any", feature = #feature)))] });
+ } else {
+ singleton.cfg = Some(quote! { #[cfg(not(feature = #feature))] });
+ }
+ }
+ }
+}
+
+fn generate_singletons(singletons: &[Singleton]) -> TokenStream {
+ let singletons = singletons
+ .iter()
+ .map(|s| {
+ let cfg = s.cfg.clone().unwrap_or_default();
+
+ let ident = format_ident!("{}", s.name);
+
+ quote! {
+ #cfg
+ #ident
+ }
+ })
+ .collect::>();
+
+ quote! {
+ embassy_hal_internal::peripherals_definition!(#(#singletons),*);
+ embassy_hal_internal::peripherals_struct!(#(#singletons),*);
+ }
+}
+
+fn generate_timers() -> TokenStream {
+ // Generate timers
+ let timer_impls = METADATA
+ .peripherals
+ .iter()
+ .filter(|p| p.name.starts_with("TIM"))
+ .map(|peripheral| {
+ let name = Ident::new(&peripheral.name, Span::call_site());
+ let timers = &*TIMERS;
+
+ let timer = timers.get(peripheral.name).expect("Timer does not exist");
+ assert!(timer.bits == 16 || timer.bits == 32);
+ let bits = if timer.bits == 16 {
+ quote! { Bits16 }
+ } else {
+ quote! { Bits32 }
+ };
+
+ quote! {
+ impl_timer!(#name, #bits);
+ }
+ });
+
+ quote! {
+ #(#timer_impls)*
+ }
+}
+
+fn generate_interrupts() -> TokenStream {
+ // Generate interrupt module
+ let interrupts: Vec = METADATA
+ .interrupts
+ .iter()
+ .map(|interrupt| Ident::new(interrupt.name, Span::call_site()))
+ .collect();
+
+ let group_interrupt_enables = METADATA
+ .interrupts
+ .iter()
+ .filter(|interrupt| interrupt.name.contains("GROUP"))
+ .map(|interrupt| {
+ let name = Ident::new(interrupt.name, Span::call_site());
+
+ quote! {
+ crate::interrupt::typelevel::#name::enable();
+ }
+ });
+
+ // Generate interrupt enables for groups
+ quote! {
+ embassy_hal_internal::interrupt_mod! {
+ #(#interrupts),*
+ }
+
+ pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) {
+ use crate::interrupt::typelevel::Interrupt;
+
+ unsafe {
+ #(#group_interrupt_enables)*
+ }
+ }
+ }
+}
+
+fn generate_peripheral_instances() -> TokenStream {
+ let mut impls = Vec::::new();
+
+ for peripheral in METADATA.peripherals {
+ let peri = format_ident!("{}", peripheral.name);
+
+ // Will be filled in when uart implementation is finished
+ let _ = peri;
+ let tokens = match peripheral.kind {
+ // "uart" => Some(quote! { impl_uart_instance!(#peri); }),
+ _ => None,
+ };
+
+ if let Some(tokens) = tokens {
+ impls.push(tokens);
+ }
+ }
+
+ quote! {
+ #(#impls)*
+ }
+}
+
+fn generate_pin_trait_impls() -> TokenStream {
+ let mut impls = Vec::::new();
+
+ for peripheral in METADATA.peripherals {
+ for pin in peripheral.pins {
+ let key = (peripheral.kind, pin.signal);
+
+ let pin_name = format_ident!("{}", pin.pin);
+ let peri = format_ident!("{}", peripheral.name);
+ let pf = pin.pf;
+
+ // Will be filled in when uart implementation is finished
+ let _ = pin_name;
+ let _ = peri;
+ let _ = pf;
+
+ let tokens = match key {
+ // ("uart", "TX") => Some(quote! { impl_uart_tx_pin!(#peri, #pin_name, #pf); }),
+ // ("uart", "RX") => Some(quote! { impl_uart_rx_pin!(#peri, #pin_name, #pf); }),
+ // ("uart", "CTS") => Some(quote! { impl_uart_cts_pin!(#peri, #pin_name, #pf); }),
+ // ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }),
+ _ => None,
+ };
+
+ if let Some(tokens) = tokens {
+ impls.push(tokens);
+ }
+ }
+ }
+
+ quote! {
+ #(#impls)*
+ }
+}
+
+/// rustfmt a given path.
+/// Failures are logged to stderr and ignored.
+fn rustfmt(path: impl AsRef) {
+ let path = path.as_ref();
+ match Command::new("rustfmt").args([path]).output() {
+ Err(e) => {
+ eprintln!("failed to exec rustfmt {:?}: {:?}", path, e);
+ }
+ Ok(out) => {
+ if !out.status.success() {
+ eprintln!("rustfmt {:?} failed:", path);
+ eprintln!("=== STDOUT:");
+ std::io::stderr().write_all(&out.stdout).unwrap();
+ eprintln!("=== STDERR:");
+ std::io::stderr().write_all(&out.stderr).unwrap();
+ }
+ }
+ }
+}
+
+#[allow(dead_code)]
+struct TimerDesc {
+ bits: u8,
+ /// Is there an 8-bit prescaler
+ prescaler: bool,
+ /// Is there a repeat counter
+ repeat_counter: bool,
+ ccp_channels_internal: u8,
+ ccp_channels_external: u8,
+ external_pwm_channels: u8,
+ phase_load: bool,
+ shadow_load: bool,
+ shadow_ccs: bool,
+ deadband: bool,
+ fault_handler: bool,
+ qei_hall: bool,
+}
+
+/// Description of all timer instances.
+const TIMERS: LazyLock> = LazyLock::new(|| {
+ let mut map = HashMap::new();
+ map.insert(
+ "TIMG0".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG1".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG2".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG3".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG4".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: true,
+ shadow_ccs: true,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG5".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: true,
+ shadow_ccs: true,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG6".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: true,
+ shadow_ccs: true,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG7".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: true,
+ shadow_ccs: true,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG8".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: true,
+ },
+ );
+
+ map.insert(
+ "TIMG9".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: true,
+ },
+ );
+
+ map.insert(
+ "TIMG10".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: true,
+ },
+ );
+
+ map.insert(
+ "TIMG11".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: true,
+ },
+ );
+
+ map.insert(
+ "TIMG12".into(),
+ TimerDesc {
+ bits: 32,
+ prescaler: false,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: true,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG13".into(),
+ TimerDesc {
+ bits: 32,
+ prescaler: false,
+ repeat_counter: false,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 2,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: true,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMG14".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: false,
+ ccp_channels_internal: 4,
+ ccp_channels_external: 4,
+ external_pwm_channels: 4,
+ phase_load: false,
+ shadow_load: false,
+ shadow_ccs: false,
+ deadband: false,
+ fault_handler: false,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMA0".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: true,
+ ccp_channels_internal: 4,
+ ccp_channels_external: 2,
+ external_pwm_channels: 8,
+ phase_load: true,
+ shadow_load: true,
+ shadow_ccs: true,
+ deadband: true,
+ fault_handler: true,
+ qei_hall: false,
+ },
+ );
+
+ map.insert(
+ "TIMA1".into(),
+ TimerDesc {
+ bits: 16,
+ prescaler: true,
+ repeat_counter: true,
+ ccp_channels_internal: 2,
+ ccp_channels_external: 2,
+ external_pwm_channels: 4,
+ phase_load: true,
+ shadow_load: true,
+ shadow_ccs: true,
+ deadband: true,
+ fault_handler: true,
+ qei_hall: false,
+ },
+ );
+
+ map
+});
+
+enum GetOneError {
+ None,
+ Multiple,
+}
+
+trait IteratorExt: Iterator {
+ fn get_one(self) -> Result;
+}
+
+impl IteratorExt for T {
+ fn get_one(mut self) -> Result {
+ match self.next() {
+ None => Err(GetOneError::None),
+ Some(res) => match self.next() {
+ Some(_) => Err(GetOneError::Multiple),
+ None => Ok(res),
+ },
+ }
+ }
+}
diff --git a/embassy-mspm0/build_common.rs b/embassy-mspm0/build_common.rs
new file mode 100644
index 000000000..4f24e6d37
--- /dev/null
+++ b/embassy-mspm0/build_common.rs
@@ -0,0 +1,94 @@
+// NOTE: this file is copy-pasted between several Embassy crates, because there is no
+// straightforward way to share this code:
+// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =
+// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate
+// reside in the crate's directory,
+// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because
+// symlinks don't work on Windows.
+
+use std::collections::HashSet;
+use std::env;
+
+/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring
+/// them (`cargo:rust-check-cfg=cfg(X)`).
+#[derive(Debug)]
+pub struct CfgSet {
+ enabled: HashSet,
+ declared: HashSet,
+}
+
+impl CfgSet {
+ pub fn new() -> Self {
+ Self {
+ enabled: HashSet::new(),
+ declared: HashSet::new(),
+ }
+ }
+
+ /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.
+ ///
+ /// All configs that can potentially be enabled should be unconditionally declared using
+ /// [`Self::declare()`].
+ pub fn enable(&mut self, cfg: impl AsRef) {
+ if self.enabled.insert(cfg.as_ref().to_owned()) {
+ println!("cargo:rustc-cfg={}", cfg.as_ref());
+ }
+ }
+
+ pub fn enable_all(&mut self, cfgs: &[impl AsRef]) {
+ for cfg in cfgs.iter() {
+ self.enable(cfg.as_ref());
+ }
+ }
+
+ /// Declare a valid config for conditional compilation, without enabling it.
+ ///
+ /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.
+ pub fn declare(&mut self, cfg: impl AsRef) {
+ if self.declared.insert(cfg.as_ref().to_owned()) {
+ println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref());
+ }
+ }
+
+ pub fn declare_all(&mut self, cfgs: &[impl AsRef]) {
+ for cfg in cfgs.iter() {
+ self.declare(cfg.as_ref());
+ }
+ }
+
+ pub fn set(&mut self, cfg: impl Into, enable: bool) {
+ let cfg = cfg.into();
+ if enable {
+ self.enable(cfg.clone());
+ }
+ self.declare(cfg);
+ }
+}
+
+/// Sets configs that describe the target platform.
+pub fn set_target_cfgs(cfgs: &mut CfgSet) {
+ let target = env::var("TARGET").unwrap();
+
+ if target.starts_with("thumbv6m-") {
+ cfgs.enable_all(&["cortex_m", "armv6m"]);
+ } else if target.starts_with("thumbv7m-") {
+ cfgs.enable_all(&["cortex_m", "armv7m"]);
+ } else if target.starts_with("thumbv7em-") {
+ cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]);
+ } else if target.starts_with("thumbv8m.base") {
+ cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]);
+ } else if target.starts_with("thumbv8m.main") {
+ cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]);
+ }
+ cfgs.declare_all(&[
+ "cortex_m",
+ "armv6m",
+ "armv7m",
+ "armv7em",
+ "armv8m",
+ "armv8m_base",
+ "armv8m_main",
+ ]);
+
+ cfgs.set("has_fpu", target.ends_with("-eabihf"));
+}
diff --git a/embassy-mspm0/src/fmt.rs b/embassy-mspm0/src/fmt.rs
new file mode 100644
index 000000000..8ca61bc39
--- /dev/null
+++ b/embassy-mspm0/src/fmt.rs
@@ -0,0 +1,270 @@
+#![macro_use]
+#![allow(unused)]
+
+use core::fmt::{Debug, Display, LowerHex};
+
+#[cfg(all(feature = "defmt", feature = "log"))]
+compile_error!("You may not enable both `defmt` and `log` features.");
+
+#[collapse_debuginfo(yes)]
+macro_rules! assert {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! assert_eq {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert_eq!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert_eq!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! assert_ne {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert_ne!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert_ne!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! debug_assert {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! debug_assert_eq {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert_eq!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert_eq!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! debug_assert_ne {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert_ne!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert_ne!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! todo {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::todo!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::todo!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! unreachable {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::unreachable!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::unreachable!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! panic {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::panic!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::panic!($($x)*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! trace {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::trace!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::trace!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! debug {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::debug!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! info {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::info!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::info!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! warn {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::warn!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::warn!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+#[collapse_debuginfo(yes)]
+macro_rules! error {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::error!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::error!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+#[cfg(feature = "defmt")]
+#[collapse_debuginfo(yes)]
+macro_rules! unwrap {
+ ($($x:tt)*) => {
+ ::defmt::unwrap!($($x)*)
+ };
+}
+
+#[cfg(not(feature = "defmt"))]
+#[collapse_debuginfo(yes)]
+macro_rules! unwrap {
+ ($arg:expr) => {
+ match $crate::fmt::Try::into_result($arg) {
+ ::core::result::Result::Ok(t) => t,
+ ::core::result::Result::Err(e) => {
+ ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
+ }
+ }
+ };
+ ($arg:expr, $($msg:expr),+ $(,)? ) => {
+ match $crate::fmt::Try::into_result($arg) {
+ ::core::result::Result::Ok(t) => t,
+ ::core::result::Result::Err(e) => {
+ ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
+ }
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct NoneError;
+
+pub trait Try {
+ type Ok;
+ type Error;
+ fn into_result(self) -> Result;
+}
+
+impl Try for Option {
+ type Ok = T;
+ type Error = NoneError;
+
+ #[inline]
+ fn into_result(self) -> Result {
+ self.ok_or(NoneError)
+ }
+}
+
+impl Try for Result {
+ type Ok = T;
+ type Error = E;
+
+ #[inline]
+ fn into_result(self) -> Self {
+ self
+ }
+}
+
+pub(crate) struct Bytes<'a>(pub &'a [u8]);
+
+impl<'a> Debug for Bytes<'a> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(f, "{:#02x?}", self.0)
+ }
+}
+
+impl<'a> Display for Bytes<'a> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(f, "{:#02x?}", self.0)
+ }
+}
+
+impl<'a> LowerHex for Bytes<'a> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(f, "{:#02x?}", self.0)
+ }
+}
+
+#[cfg(feature = "defmt")]
+impl<'a> defmt::Format for Bytes<'a> {
+ fn format(&self, fmt: defmt::Formatter) {
+ defmt::write!(fmt, "{:02x}", self.0)
+ }
+}
diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs
new file mode 100644
index 000000000..2edadbc5a
--- /dev/null
+++ b/embassy-mspm0/src/gpio.rs
@@ -0,0 +1,1071 @@
+#![macro_use]
+
+use core::convert::Infallible;
+use core::future::Future;
+use core::pin::Pin as FuturePin;
+use core::task::{Context, Poll};
+
+use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
+use embassy_sync::waitqueue::AtomicWaker;
+
+use crate::pac::gpio::vals::*;
+use crate::pac::gpio::{self};
+#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
+use crate::pac::interrupt;
+use crate::pac::{self};
+
+/// Represents a digital input or output level.
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum Level {
+ /// Logical low.
+ Low,
+ /// Logical high.
+ High,
+}
+
+impl From for Level {
+ fn from(val: bool) -> Self {
+ match val {
+ true => Self::High,
+ false => Self::Low,
+ }
+ }
+}
+
+impl From for bool {
+ fn from(level: Level) -> bool {
+ match level {
+ Level::Low => false,
+ Level::High => true,
+ }
+ }
+}
+
+/// Represents a pull setting for an input.
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum Pull {
+ /// No pull.
+ None,
+ /// Internal pull-up resistor.
+ Up,
+ /// Internal pull-down resistor.
+ Down,
+}
+
+/// A GPIO bank with up to 32 pins.
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum Port {
+ /// Port A.
+ PortA = 0,
+
+ /// Port B.
+ #[cfg(gpio_pb)]
+ PortB = 1,
+
+ /// Port C.
+ #[cfg(gpio_pc)]
+ PortC = 2,
+}
+
+/// GPIO flexible pin.
+///
+/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain
+/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
+/// mode.
+pub struct Flex<'d> {
+ pin: Peri<'d, AnyPin>,
+}
+
+impl<'d> Flex<'d> {
+ /// Wrap the pin in a `Flex`.
+ ///
+ /// The pin remains disconnected. The initial output level is unspecified, but can be changed
+ /// before the pin is put into output mode.
+ #[inline]
+ pub fn new(pin: Peri<'d, impl Pin>) -> Self {
+ // Pin will be in disconnected state.
+ Self { pin: pin.into() }
+ }
+
+ /// Set the pin's pull.
+ #[inline]
+ pub fn set_pull(&mut self, pull: Pull) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pipd(matches!(pull, Pull::Down));
+ w.set_pipu(matches!(pull, Pull::Up));
+ });
+ }
+
+ /// Put the pin into input mode.
+ ///
+ /// The pull setting is left unchanged.
+ #[inline]
+ pub fn set_as_input(&mut self) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(GPIO_PF);
+ w.set_hiz1(false);
+ w.set_pc(true);
+ w.set_inena(true);
+ });
+
+ self.pin.block().doeclr31_0().write(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ });
+ }
+
+ /// Put the pin into output mode.
+ ///
+ /// The pin level will be whatever was set before (or low by default). If you want it to begin
+ /// at a specific level, call `set_high`/`set_low` on the pin first.
+ #[inline]
+ pub fn set_as_output(&mut self) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(GPIO_PF);
+ w.set_hiz1(false);
+ w.set_pc(true);
+ w.set_inena(false);
+ });
+
+ self.pin.block().doeset31_0().write(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ });
+ }
+
+ /// Put the pin into input + open-drain output mode.
+ ///
+ /// The hardware will drive the line low if you set it to low, and will leave it floating if you set
+ /// it to high, in which case you can read the input to figure out whether another device
+ /// is driving the line low.
+ ///
+ /// The pin level will be whatever was set before (or low by default). If you want it to begin
+ /// at a specific level, call `set_high`/`set_low` on the pin first.
+ ///
+ /// The internal weak pull-up and pull-down resistors will be disabled.
+ #[inline]
+ pub fn set_as_input_output(&mut self) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(GPIO_PF);
+ w.set_hiz1(true);
+ w.set_pc(true);
+ w.set_inena(false);
+ });
+
+ self.set_pull(Pull::None);
+ }
+
+ /// Set the pin as "disconnected", ie doing nothing and consuming the lowest
+ /// amount of power possible.
+ ///
+ /// This is currently the same as [`Self::set_as_analog()`] but is semantically different
+ /// really. Drivers should `set_as_disconnected()` pins when dropped.
+ ///
+ /// Note that this also disables the internal weak pull-up and pull-down resistors.
+ #[inline]
+ pub fn set_as_disconnected(&mut self) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(DISCONNECT_PF);
+ w.set_hiz1(false);
+ w.set_pc(false);
+ w.set_inena(false);
+ });
+
+ self.set_pull(Pull::None);
+ self.set_inversion(false);
+ }
+
+ /// Configure the logic inversion of this pin.
+ ///
+ /// Logic inversion applies to both the input and output path of this pin.
+ #[inline]
+ pub fn set_inversion(&mut self, invert: bool) {
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_inv(invert);
+ });
+ }
+
+ // TODO: drive strength, hysteresis, wakeup enable, wakeup compare
+
+ /// Put the pin into the PF mode, unchecked.
+ ///
+ /// This puts the pin into the PF mode, with the request number. This is completely unchecked,
+ /// it can attach the pin to literally any peripheral, so use with care. In addition the pin
+ /// peripheral is connected in the iomux.
+ ///
+ /// The peripheral attached to the pin depends on the part in use. Consult the datasheet
+ /// or technical reference manual for additional details.
+ #[inline]
+ pub fn set_pf_unchecked(&mut self, pf: u8) {
+ // Per SLAU893, PF is only 5 bits
+ assert!((pf & 0x3F) != 0, "PF is out of range");
+
+ let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
+
+ pincm.modify(|w| {
+ w.set_pf(pf);
+ // If the PF is manually set, connect the pin
+ w.set_pc(true);
+ });
+ }
+
+ /// Get whether the pin input level is high.
+ #[inline]
+ pub fn is_high(&self) -> bool {
+ !self.is_low()
+ }
+
+ /// Get whether the pin input level is low.
+ #[inline]
+ pub fn is_low(&self) -> bool {
+ self.pin.block().din31_0().read().dio(self.pin.bit_index())
+ }
+
+ /// Returns current pin level
+ #[inline]
+ pub fn get_level(&self) -> Level {
+ self.is_high().into()
+ }
+
+ /// Set the output as high.
+ #[inline]
+ pub fn set_high(&mut self) {
+ self.pin.block().doutset31_0().write(|w| {
+ w.set_dio(self.pin.bit_index() as usize, true);
+ });
+ }
+
+ /// Set the output as low.
+ #[inline]
+ pub fn set_low(&mut self) {
+ self.pin.block().doutclr31_0().write(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ });
+ }
+
+ /// Toggle pin output
+ #[inline]
+ pub fn toggle(&mut self) {
+ self.pin.block().douttgl31_0().write(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ })
+ }
+
+ /// Set the output level.
+ #[inline]
+ pub fn set_level(&mut self, level: Level) {
+ match level {
+ Level::Low => self.set_low(),
+ Level::High => self.set_high(),
+ }
+ }
+
+ /// Get the current pin input level.
+ #[inline]
+ pub fn get_output_level(&self) -> Level {
+ self.is_high().into()
+ }
+
+ /// Is the output level high?
+ #[inline]
+ pub fn is_set_high(&self) -> bool {
+ !self.is_set_low()
+ }
+
+ /// Is the output level low?
+ #[inline]
+ pub fn is_set_low(&self) -> bool {
+ (self.pin.block().dout31_0().read().0 & self.pin.bit_index() as u32) == 0
+ }
+
+ /// Wait until the pin is high. If it is already high, return immediately.
+ #[inline]
+ pub async fn wait_for_high(&mut self) {
+ if self.is_high() {
+ return;
+ }
+
+ self.wait_for_rising_edge().await
+ }
+
+ /// Wait until the pin is low. If it is already low, return immediately.
+ #[inline]
+ pub async fn wait_for_low(&mut self) {
+ if self.is_low() {
+ return;
+ }
+
+ self.wait_for_falling_edge().await
+ }
+
+ /// Wait for the pin to undergo a transition from low to high.
+ #[inline]
+ pub async fn wait_for_rising_edge(&mut self) {
+ InputFuture::new(self.pin.reborrow(), Polarity::RISE).await
+ }
+
+ /// Wait for the pin to undergo a transition from high to low.
+ #[inline]
+ pub async fn wait_for_falling_edge(&mut self) {
+ InputFuture::new(self.pin.reborrow(), Polarity::FALL).await
+ }
+
+ /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
+ #[inline]
+ pub async fn wait_for_any_edge(&mut self) {
+ InputFuture::new(self.pin.reborrow(), Polarity::RISE_FALL).await
+ }
+}
+
+impl<'d> Drop for Flex<'d> {
+ #[inline]
+ fn drop(&mut self) {
+ self.set_as_disconnected();
+ }
+}
+
+/// GPIO input driver.
+pub struct Input<'d> {
+ pin: Flex<'d>,
+}
+
+impl<'d> Input<'d> {
+ /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
+ #[inline]
+ pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {
+ let mut pin = Flex::new(pin);
+ pin.set_as_input();
+ pin.set_pull(pull);
+ Self { pin }
+ }
+
+ /// Get whether the pin input level is high.
+ #[inline]
+ pub fn is_high(&self) -> bool {
+ self.pin.is_high()
+ }
+
+ /// Get whether the pin input level is low.
+ #[inline]
+ pub fn is_low(&self) -> bool {
+ self.pin.is_low()
+ }
+
+ /// Get the current pin input level.
+ #[inline]
+ pub fn get_level(&self) -> Level {
+ self.pin.get_level()
+ }
+
+ /// Configure the logic inversion of this pin.
+ ///
+ /// Logic inversion applies to the input path of this pin.
+ #[inline]
+ pub fn set_inversion(&mut self, invert: bool) {
+ self.pin.set_inversion(invert)
+ }
+
+ /// Wait until the pin is high. If it is already high, return immediately.
+ #[inline]
+ pub async fn wait_for_high(&mut self) {
+ self.pin.wait_for_high().await
+ }
+
+ /// Wait until the pin is low. If it is already low, return immediately.
+ #[inline]
+ pub async fn wait_for_low(&mut self) {
+ self.pin.wait_for_low().await
+ }
+
+ /// Wait for the pin to undergo a transition from low to high.
+ #[inline]
+ pub async fn wait_for_rising_edge(&mut self) {
+ self.pin.wait_for_rising_edge().await
+ }
+
+ /// Wait for the pin to undergo a transition from high to low.
+ #[inline]
+ pub async fn wait_for_falling_edge(&mut self) {
+ self.pin.wait_for_falling_edge().await
+ }
+
+ /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
+ #[inline]
+ pub async fn wait_for_any_edge(&mut self) {
+ self.pin.wait_for_any_edge().await
+ }
+}
+
+/// GPIO output driver.
+///
+/// Note that pins will **return to their floating state** when `Output` is dropped.
+/// If pins should retain their state indefinitely, either keep ownership of the
+/// `Output`, or pass it to [`core::mem::forget`].
+pub struct Output<'d> {
+ pin: Flex<'d>,
+}
+
+impl<'d> Output<'d> {
+ /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.
+ #[inline]
+ pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
+ let mut pin = Flex::new(pin);
+ pin.set_as_output();
+ pin.set_level(initial_output);
+ Self { pin }
+ }
+
+ /// Set the output as high.
+ #[inline]
+ pub fn set_high(&mut self) {
+ self.pin.set_high();
+ }
+
+ /// Set the output as low.
+ #[inline]
+ pub fn set_low(&mut self) {
+ self.pin.set_low();
+ }
+
+ /// Set the output level.
+ #[inline]
+ pub fn set_level(&mut self, level: Level) {
+ self.pin.set_level(level)
+ }
+
+ /// Is the output pin set as high?
+ #[inline]
+ pub fn is_set_high(&self) -> bool {
+ self.pin.is_set_high()
+ }
+
+ /// Is the output pin set as low?
+ #[inline]
+ pub fn is_set_low(&self) -> bool {
+ self.pin.is_set_low()
+ }
+
+ /// What level output is set to
+ #[inline]
+ pub fn get_output_level(&self) -> Level {
+ self.pin.get_output_level()
+ }
+
+ /// Toggle pin output
+ #[inline]
+ pub fn toggle(&mut self) {
+ self.pin.toggle();
+ }
+
+ /// Configure the logic inversion of this pin.
+ ///
+ /// Logic inversion applies to the input path of this pin.
+ #[inline]
+ pub fn set_inversion(&mut self, invert: bool) {
+ self.pin.set_inversion(invert)
+ }
+}
+
+/// GPIO output open-drain driver.
+///
+/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.
+/// If pins should retain their state indefinitely, either keep ownership of the
+/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].
+pub struct OutputOpenDrain<'d> {
+ pin: Flex<'d>,
+}
+
+impl<'d> OutputOpenDrain<'d> {
+ /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].
+ #[inline]
+ pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
+ let mut pin = Flex::new(pin);
+ pin.set_level(initial_output);
+ pin.set_as_input_output();
+ Self { pin }
+ }
+
+ /// Get whether the pin input level is high.
+ #[inline]
+ pub fn is_high(&self) -> bool {
+ !self.pin.is_low()
+ }
+
+ /// Get whether the pin input level is low.
+ #[inline]
+ pub fn is_low(&self) -> bool {
+ self.pin.is_low()
+ }
+
+ /// Get the current pin input level.
+ #[inline]
+ pub fn get_level(&self) -> Level {
+ self.pin.get_level()
+ }
+
+ /// Set the output as high.
+ #[inline]
+ pub fn set_high(&mut self) {
+ self.pin.set_high();
+ }
+
+ /// Set the output as low.
+ #[inline]
+ pub fn set_low(&mut self) {
+ self.pin.set_low();
+ }
+
+ /// Set the output level.
+ #[inline]
+ pub fn set_level(&mut self, level: Level) {
+ self.pin.set_level(level);
+ }
+
+ /// Get whether the output level is set to high.
+ #[inline]
+ pub fn is_set_high(&self) -> bool {
+ self.pin.is_set_high()
+ }
+
+ /// Get whether the output level is set to low.
+ #[inline]
+ pub fn is_set_low(&self) -> bool {
+ self.pin.is_set_low()
+ }
+
+ /// Get the current output level.
+ #[inline]
+ pub fn get_output_level(&self) -> Level {
+ self.pin.get_output_level()
+ }
+
+ /// Toggle pin output
+ #[inline]
+ pub fn toggle(&mut self) {
+ self.pin.toggle()
+ }
+
+ /// Configure the logic inversion of this pin.
+ ///
+ /// Logic inversion applies to the input path of this pin.
+ #[inline]
+ pub fn set_inversion(&mut self, invert: bool) {
+ self.pin.set_inversion(invert)
+ }
+
+ /// Wait until the pin is high. If it is already high, return immediately.
+ #[inline]
+ pub async fn wait_for_high(&mut self) {
+ self.pin.wait_for_high().await
+ }
+
+ /// Wait until the pin is low. If it is already low, return immediately.
+ #[inline]
+ pub async fn wait_for_low(&mut self) {
+ self.pin.wait_for_low().await
+ }
+
+ /// Wait for the pin to undergo a transition from low to high.
+ #[inline]
+ pub async fn wait_for_rising_edge(&mut self) {
+ self.pin.wait_for_rising_edge().await
+ }
+
+ /// Wait for the pin to undergo a transition from high to low.
+ #[inline]
+ pub async fn wait_for_falling_edge(&mut self) {
+ self.pin.wait_for_falling_edge().await
+ }
+
+ /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
+ #[inline]
+ pub async fn wait_for_any_edge(&mut self) {
+ self.pin.wait_for_any_edge().await
+ }
+}
+
+/// Type-erased GPIO pin
+pub struct AnyPin {
+ pub(crate) pin_port: u8,
+}
+
+impl AnyPin {
+ /// Create an [AnyPin] for a specific pin.
+ ///
+ /// # Safety
+ /// - `pin_port` should not in use by another driver.
+ #[inline]
+ pub unsafe fn steal(pin_port: u8) -> Peri<'static, Self> {
+ Peri::new_unchecked(Self { pin_port })
+ }
+}
+
+impl_peripheral!(AnyPin);
+
+impl Pin for AnyPin {}
+impl SealedPin for AnyPin {
+ #[inline]
+ fn pin_port(&self) -> u8 {
+ self.pin_port
+ }
+}
+
+/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].
+#[allow(private_bounds)]
+pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static {
+ /// The index of this pin in PINCM (pin control management) registers.
+ #[inline]
+ fn pin_cm(&self) -> u8 {
+ self._pin_cm()
+ }
+}
+
+impl<'d> embedded_hal::digital::ErrorType for Flex<'d> {
+ type Error = Infallible;
+}
+
+impl<'d> embedded_hal::digital::InputPin for Flex<'d> {
+ #[inline]
+ fn is_high(&mut self) -> Result {
+ Ok((*self).is_high())
+ }
+
+ #[inline]
+ fn is_low(&mut self) -> Result {
+ Ok((*self).is_low())
+ }
+}
+
+impl<'d> embedded_hal::digital::OutputPin for Flex<'d> {
+ #[inline]
+ fn set_low(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_low())
+ }
+
+ #[inline]
+ fn set_high(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_high())
+ }
+}
+
+impl<'d> embedded_hal::digital::StatefulOutputPin for Flex<'d> {
+ #[inline]
+ fn is_set_high(&mut self) -> Result {
+ Ok((*self).is_set_high())
+ }
+
+ #[inline]
+ fn is_set_low(&mut self) -> Result {
+ Ok((*self).is_set_low())
+ }
+}
+
+impl<'d> embedded_hal_async::digital::Wait for Flex<'d> {
+ async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_high().await;
+ Ok(())
+ }
+
+ async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_low().await;
+ Ok(())
+ }
+
+ async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_rising_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_falling_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_any_edge().await;
+ Ok(())
+ }
+}
+
+impl<'d> embedded_hal::digital::ErrorType for Input<'d> {
+ type Error = Infallible;
+}
+
+impl<'d> embedded_hal::digital::InputPin for Input<'d> {
+ #[inline]
+ fn is_high(&mut self) -> Result {
+ Ok((*self).is_high())
+ }
+
+ #[inline]
+ fn is_low(&mut self) -> Result {
+ Ok((*self).is_low())
+ }
+}
+
+impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
+ async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_high().await;
+ Ok(())
+ }
+
+ async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_low().await;
+ Ok(())
+ }
+
+ async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_rising_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_falling_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_any_edge().await;
+ Ok(())
+ }
+}
+
+impl<'d> embedded_hal::digital::ErrorType for Output<'d> {
+ type Error = Infallible;
+}
+
+impl<'d> embedded_hal::digital::OutputPin for Output<'d> {
+ #[inline]
+ fn set_low(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_low())
+ }
+
+ #[inline]
+ fn set_high(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_high())
+ }
+}
+
+impl<'d> embedded_hal::digital::StatefulOutputPin for Output<'d> {
+ #[inline]
+ fn is_set_high(&mut self) -> Result {
+ Ok((*self).is_set_high())
+ }
+
+ #[inline]
+ fn is_set_low(&mut self) -> Result {
+ Ok((*self).is_set_low())
+ }
+}
+
+impl<'d> embedded_hal::digital::ErrorType for OutputOpenDrain<'d> {
+ type Error = Infallible;
+}
+
+impl<'d> embedded_hal::digital::InputPin for OutputOpenDrain<'d> {
+ #[inline]
+ fn is_high(&mut self) -> Result {
+ Ok((*self).is_high())
+ }
+
+ #[inline]
+ fn is_low(&mut self) -> Result {
+ Ok((*self).is_low())
+ }
+}
+
+impl<'d> embedded_hal::digital::OutputPin for OutputOpenDrain<'d> {
+ #[inline]
+ fn set_low(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_low())
+ }
+
+ #[inline]
+ fn set_high(&mut self) -> Result<(), Self::Error> {
+ Ok(self.set_high())
+ }
+}
+
+impl<'d> embedded_hal::digital::StatefulOutputPin for OutputOpenDrain<'d> {
+ #[inline]
+ fn is_set_high(&mut self) -> Result {
+ Ok((*self).is_set_high())
+ }
+
+ #[inline]
+ fn is_set_low(&mut self) -> Result {
+ Ok((*self).is_set_low())
+ }
+}
+
+impl<'d> embedded_hal_async::digital::Wait for OutputOpenDrain<'d> {
+ async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_high().await;
+ Ok(())
+ }
+
+ async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_low().await;
+ Ok(())
+ }
+
+ async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_rising_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_falling_edge().await;
+ Ok(())
+ }
+
+ async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
+ self.wait_for_any_edge().await;
+ Ok(())
+ }
+}
+
+/// The pin function to disconnect peripherals from the pin.
+///
+/// This is also the pin function used to connect to analog peripherals, such as an ADC.
+const DISCONNECT_PF: u8 = 0;
+
+/// The pin function for the GPIO peripheral.
+///
+/// This is fixed to `1` for every part.
+const GPIO_PF: u8 = 1;
+
+macro_rules! impl_pin {
+ ($name: ident, $port: expr, $pin_num: expr) => {
+ impl crate::gpio::Pin for crate::peripherals::$name {}
+ impl crate::gpio::SealedPin for crate::peripherals::$name {
+ #[inline]
+ fn pin_port(&self) -> u8 {
+ ($port as u8) * 32 + $pin_num
+ }
+ }
+
+ impl From for crate::gpio::AnyPin {
+ fn from(val: crate::peripherals::$name) -> Self {
+ Self {
+ pin_port: crate::gpio::SealedPin::pin_port(&val),
+ }
+ }
+ }
+ };
+}
+
+// TODO: Possible micro-op for C110X, not every pin is instantiated even on the 20 pin parts.
+// This would mean cfg guarding to just cfg guarding every pin instance.
+static PORTA_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
+#[cfg(gpio_pb)]
+static PORTB_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
+#[cfg(gpio_pc)]
+static PORTC_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
+
+pub(crate) trait SealedPin {
+ fn pin_port(&self) -> u8;
+
+ fn port(&self) -> Port {
+ match self.pin_port() / 32 {
+ 0 => Port::PortA,
+ #[cfg(gpio_pb)]
+ 1 => Port::PortB,
+ #[cfg(gpio_pc)]
+ 2 => Port::PortC,
+ _ => unreachable!(),
+ }
+ }
+
+ fn waker(&self) -> &AtomicWaker {
+ match self.port() {
+ Port::PortA => &PORTA_WAKERS[self.bit_index()],
+ #[cfg(gpio_pb)]
+ Port::PortB => &PORTB_WAKERS[self.bit_index()],
+ #[cfg(gpio_pc)]
+ Port::PortC => &PORTC_WAKERS[self.bit_index()],
+ }
+ }
+
+ fn _pin_cm(&self) -> u8 {
+ // Some parts like the MSPM0L222x have pincm mappings all over the place.
+ crate::gpio_pincm(self.pin_port())
+ }
+
+ fn bit_index(&self) -> usize {
+ (self.pin_port() % 32) as usize
+ }
+
+ #[inline]
+ fn block(&self) -> gpio::Gpio {
+ match self.pin_port() / 32 {
+ 0 => pac::GPIOA,
+ #[cfg(gpio_pb)]
+ 1 => pac::GPIOB,
+ #[cfg(gpio_pc)]
+ 2 => pac::GPIOC,
+ _ => unreachable!(),
+ }
+ }
+}
+
+#[must_use = "futures do nothing unless you `.await` or poll them"]
+struct InputFuture<'d> {
+ pin: Peri<'d, AnyPin>,
+}
+
+impl<'d> InputFuture<'d> {
+ fn new(pin: Peri<'d, AnyPin>, polarity: Polarity) -> Self {
+ let block = pin.block();
+
+ // Before clearing any previous edge events, we must disable events.
+ //
+ // If we don't do this, it is possible that after we clear the interrupt, the current event
+ // the hardware is listening for may not be the same event we will configure. This may result
+ // in RIS being set. Then when interrupts are unmasked and RIS is set, we may get the wrong event
+ // causing an interrupt.
+ //
+ // Selecting which polarity events happen is a RMW operation.
+ critical_section::with(|_cs| {
+ if pin.bit_index() >= 16 {
+ block.polarity31_16().modify(|w| {
+ w.set_dio(pin.bit_index() - 16, Polarity::DISABLE);
+ });
+ } else {
+ block.polarity15_0().modify(|w| {
+ w.set_dio(pin.bit_index(), Polarity::DISABLE);
+ });
+ };
+ });
+
+ // First clear the bit for this event. Otherwise previous edge events may be recorded.
+ block.cpu_int().iclr().write(|w| {
+ w.set_dio(pin.bit_index(), true);
+ });
+
+ // Selecting which polarity events happen is a RMW operation.
+ critical_section::with(|_cs| {
+ // Tell the hardware which pin event we want to receive.
+ if pin.bit_index() >= 16 {
+ block.polarity31_16().modify(|w| {
+ w.set_dio(pin.bit_index() - 16, polarity);
+ });
+ } else {
+ block.polarity15_0().modify(|w| {
+ w.set_dio(pin.bit_index(), polarity);
+ });
+ };
+ });
+
+ Self { pin }
+ }
+}
+
+impl<'d> Future for InputFuture<'d> {
+ type Output = ();
+
+ fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll {
+ // We need to register/re-register the waker for each poll because any
+ // calls to wake will deregister the waker.
+ let waker = self.pin.waker();
+ waker.register(cx.waker());
+
+ // The interrupt handler will mask the interrupt if the event has occurred.
+ if self.pin.block().cpu_int().ris().read().dio(self.pin.bit_index()) {
+ return Poll::Ready(());
+ }
+
+ // Unmasking the interrupt is a RMW operation.
+ //
+ // Guard with a critical section in case two different threads try to unmask at the same time.
+ critical_section::with(|_cs| {
+ self.pin.block().cpu_int().imask().modify(|w| {
+ w.set_dio(self.pin.bit_index(), true);
+ });
+ });
+
+ Poll::Pending
+ }
+}
+
+pub(crate) fn init(gpio: gpio::Gpio) {
+ gpio.gprcm().rstctl().write(|w| {
+ w.set_resetstkyclr(true);
+ w.set_resetassert(true);
+ w.set_key(ResetKey::KEY);
+ });
+
+ gpio.gprcm().pwren().write(|w| {
+ w.set_enable(true);
+ w.set_key(PwrenKey::KEY);
+ });
+
+ gpio.evt_mode().modify(|w| {
+ // The CPU will clear it's own interrupts
+ w.set_cpu_cfg(EvtCfg::SOFTWARE);
+ });
+}
+
+#[cfg(feature = "rt")]
+fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) {
+ // Only consider pins which have interrupts unmasked.
+ let bits = gpio.cpu_int().mis().read().0;
+
+ for i in BitIter(bits) {
+ wakers[i as usize].wake();
+
+ // Notify the future that an edge event has occurred by masking the interrupt for this pin.
+ gpio.cpu_int().imask().modify(|w| {
+ w.set_dio(i as usize, false);
+ });
+ }
+}
+
+struct BitIter(u32);
+
+impl Iterator for BitIter {
+ type Item = u32;
+
+ fn next(&mut self) -> Option {
+ match self.0.trailing_zeros() {
+ 32 => None,
+ b => {
+ self.0 &= !(1 << b);
+ Some(b)
+ }
+ }
+ }
+}
+
+// C110x has a dedicated interrupt just for GPIOA, as it does not have a GROUP1 interrupt.
+#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
+#[interrupt]
+fn GPIOA() {
+ gpioa_interrupt();
+}
+
+#[cfg(feature = "rt")]
+pub(crate) fn gpioa_interrupt() {
+ irq_handler(pac::GPIOA, &PORTA_WAKERS);
+}
+
+#[cfg(all(feature = "rt", gpio_pb))]
+pub(crate) fn gpiob_interrupt() {
+ irq_handler(pac::GPIOB, &PORTB_WAKERS);
+}
+
+#[cfg(all(feature = "rt", gpio_pc))]
+pub(crate) fn gpioc_interrupt() {
+ irq_handler(pac::GPIOC, &PORTC_WAKERS);
+}
diff --git a/embassy-mspm0/src/int_group/c110x.rs b/embassy-mspm0/src/int_group/c110x.rs
new file mode 100644
index 000000000..e6a9ddb99
--- /dev/null
+++ b/embassy-mspm0/src/int_group/c110x.rs
@@ -0,0 +1,25 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // TODO: Decompose to direct u8
+ let iidx = group.iidx().read().stat().to_bits();
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
diff --git a/embassy-mspm0/src/int_group/g350x.rs b/embassy-mspm0/src/int_group/g350x.rs
new file mode 100644
index 000000000..706ba2078
--- /dev/null
+++ b/embassy-mspm0/src/int_group/g350x.rs
@@ -0,0 +1,51 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::WWDT1 => todo!("implement WWDT1"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP1() {
+ use mspm0_metapac::Group1;
+
+ let group = pac::CPUSS.int_group(1);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group1::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 1: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group1::GPIOA => crate::gpio::gpioa_interrupt(),
+ Group1::GPIOB => crate::gpio::gpiob_interrupt(),
+ Group1::COMP0 => todo!("implement COMP0"),
+ Group1::COMP1 => todo!("implement COMP1"),
+ Group1::COMP2 => todo!("implement COMP2"),
+ Group1::TRNG => todo!("implement TRNG"),
+ }
+}
diff --git a/embassy-mspm0/src/int_group/g351x.rs b/embassy-mspm0/src/int_group/g351x.rs
new file mode 100644
index 000000000..e785018a7
--- /dev/null
+++ b/embassy-mspm0/src/int_group/g351x.rs
@@ -0,0 +1,52 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::WWDT1 => todo!("implement WWDT1"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP1() {
+ use mspm0_metapac::Group1;
+
+ let group = pac::CPUSS.int_group(1);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group1::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 1: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group1::GPIOA => crate::gpio::gpioa_interrupt(),
+ Group1::GPIOB => crate::gpio::gpiob_interrupt(),
+ Group1::COMP0 => todo!("implement COMP0"),
+ Group1::COMP1 => todo!("implement COMP1"),
+ Group1::COMP2 => todo!("implement COMP2"),
+ Group1::TRNG => todo!("implement TRNG"),
+ Group1::GPIOC => crate::gpio::gpioc_interrupt(),
+ }
+}
diff --git a/embassy-mspm0/src/int_group/l130x.rs b/embassy-mspm0/src/int_group/l130x.rs
new file mode 100644
index 000000000..8be5adcad
--- /dev/null
+++ b/embassy-mspm0/src/int_group/l130x.rs
@@ -0,0 +1,46 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP1() {
+ use mspm0_metapac::Group1;
+
+ let group = pac::CPUSS.int_group(1);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group1::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 1: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group1::GPIOA => crate::gpio::gpioa_interrupt(),
+ Group1::COMP0 => todo!("implement COMP0"),
+ }
+}
diff --git a/embassy-mspm0/src/int_group/l222x.rs b/embassy-mspm0/src/int_group/l222x.rs
new file mode 100644
index 000000000..eeb2ce70d
--- /dev/null
+++ b/embassy-mspm0/src/int_group/l222x.rs
@@ -0,0 +1,49 @@
+use crate::pac;
+use crate::pac::interrupt;
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP0() {
+ use mspm0_metapac::Group0;
+
+ let group = pac::CPUSS.int_group(0);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group0::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 0: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group0::WWDT0 => todo!("implement WWDT0"),
+ Group0::DEBUGSS => todo!("implement DEBUGSS"),
+ Group0::FLASHCTL => todo!("implement FLASHCTL"),
+ Group0::SYSCTL => todo!("implement SYSCTL"),
+ }
+}
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn GROUP1() {
+ use mspm0_metapac::Group1;
+
+ let group = pac::CPUSS.int_group(1);
+
+ // Must subtract by 1 since NO_INTR is value 0
+ let iidx = group.iidx().read().stat().to_bits() - 1;
+
+ let Ok(group) = pac::Group1::try_from(iidx as u8) else {
+ debug!("Invalid IIDX for group 1: {}", iidx);
+ return;
+ };
+
+ match group {
+ Group1::GPIOA => crate::gpio::gpioa_interrupt(),
+ Group1::GPIOB => crate::gpio::gpiob_interrupt(),
+ Group1::COMP0 => todo!("implement COMP0"),
+ Group1::TRNG => todo!("implement TRNG"),
+ Group1::GPIOC => crate::gpio::gpioc_interrupt(),
+ }
+}
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
new file mode 100644
index 000000000..99b7ed4a1
--- /dev/null
+++ b/embassy-mspm0/src/lib.rs
@@ -0,0 +1,119 @@
+#![no_std]
+// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
+#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
+
+// This mod MUST go first, so that the others see its macros.
+pub(crate) mod fmt;
+
+pub mod gpio;
+pub mod timer;
+
+/// Operating modes for peripherals.
+pub mod mode {
+ trait SealedMode {}
+
+ /// Operating mode for a peripheral.
+ #[allow(private_bounds)]
+ pub trait Mode: SealedMode {}
+
+ /// Blocking mode.
+ pub struct Blocking;
+ impl SealedMode for Blocking {}
+ impl Mode for Blocking {}
+
+ /// Async mode.
+ pub struct Async;
+ impl SealedMode for Async {}
+ impl Mode for Async {}
+}
+
+#[cfg(feature = "_time-driver")]
+mod time_driver;
+
+// Interrupt group handlers.
+#[cfg_attr(feature = "mspm0c110x", path = "int_group/c110x.rs")]
+#[cfg_attr(feature = "mspm0g350x", path = "int_group/g350x.rs")]
+#[cfg_attr(feature = "mspm0g351x", path = "int_group/g351x.rs")]
+#[cfg_attr(feature = "mspm0l130x", path = "int_group/l130x.rs")]
+#[cfg_attr(feature = "mspm0l222x", path = "int_group/l222x.rs")]
+mod int_group;
+
+pub(crate) mod _generated {
+ #![allow(dead_code)]
+ #![allow(unused_imports)]
+ #![allow(non_snake_case)]
+ #![allow(missing_docs)]
+
+ include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
+}
+
+// Reexports
+pub(crate) use _generated::gpio_pincm;
+pub use _generated::{peripherals, Peripherals};
+pub use embassy_hal_internal::Peri;
+#[cfg(feature = "unstable-pac")]
+pub use mspm0_metapac as pac;
+#[cfg(not(feature = "unstable-pac"))]
+pub(crate) use mspm0_metapac as pac;
+
+pub use crate::_generated::interrupt;
+
+/// `embassy-mspm0` global configuration.
+#[non_exhaustive]
+#[derive(Clone, Copy)]
+pub struct Config {
+ // TODO
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ // TODO
+ }
+ }
+}
+
+pub fn init(_config: Config) -> Peripherals {
+ critical_section::with(|cs| {
+ let peripherals = Peripherals::take_with_cs(cs);
+
+ // TODO: Further clock configuration
+
+ pac::SYSCTL.mclkcfg().modify(|w| {
+ // Enable MFCLK
+ w.set_usemftick(true);
+ // MDIV must be disabled if MFCLK is enabled.
+ w.set_mdiv(0);
+ });
+
+ // Enable MFCLK for peripheral use
+ //
+ // TODO: Optional?
+ pac::SYSCTL.genclken().modify(|w| {
+ w.set_mfpclken(true);
+ });
+
+ pac::SYSCTL.borthreshold().modify(|w| {
+ w.set_level(0);
+ });
+
+ gpio::init(pac::GPIOA);
+ #[cfg(gpio_pb)]
+ gpio::init(pac::GPIOB);
+ #[cfg(gpio_pc)]
+ gpio::init(pac::GPIOC);
+
+ _generated::enable_group_interrupts(cs);
+
+ #[cfg(feature = "mspm0c110x")]
+ unsafe {
+ use crate::_generated::interrupt::typelevel::Interrupt;
+ crate::interrupt::typelevel::GPIOA::enable();
+ }
+
+ #[cfg(feature = "_time-driver")]
+ time_driver::init(cs);
+
+ peripherals
+ })
+}
diff --git a/embassy-mspm0/src/time_driver.rs b/embassy-mspm0/src/time_driver.rs
new file mode 100644
index 000000000..e80e89e55
--- /dev/null
+++ b/embassy-mspm0/src/time_driver.rs
@@ -0,0 +1,426 @@
+use core::cell::{Cell, RefCell};
+use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
+use core::task::Waker;
+
+use critical_section::{CriticalSection, Mutex};
+use embassy_time_driver::Driver;
+use embassy_time_queue_utils::Queue;
+use mspm0_metapac::interrupt;
+use mspm0_metapac::tim::vals::{Cm, Cvae, CxC, EvtCfg, PwrenKey, Ratio, Repeat, ResetKey};
+use mspm0_metapac::tim::{Counterregs16, Tim};
+
+use crate::peripherals;
+use crate::timer::SealedTimer;
+
+#[cfg(any(time_driver_timg12, time_driver_timg13))]
+compile_error!("TIMG12 and TIMG13 are not supported by the time driver yet");
+
+// Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers.
+#[cfg(time_driver_timg0)]
+type T = peripherals::TIMG0;
+#[cfg(time_driver_timg1)]
+type T = peripherals::TIMG1;
+#[cfg(time_driver_timg2)]
+type T = peripherals::TIMG2;
+#[cfg(time_driver_timg3)]
+type T = peripherals::TIMG3;
+#[cfg(time_driver_timg4)]
+type T = peripherals::TIMG4;
+#[cfg(time_driver_timg5)]
+type T = peripherals::TIMG5;
+#[cfg(time_driver_timg6)]
+type T = peripherals::TIMG6;
+#[cfg(time_driver_timg7)]
+type T = peripherals::TIMG7;
+#[cfg(time_driver_timg8)]
+type T = peripherals::TIMG8;
+#[cfg(time_driver_timg9)]
+type T = peripherals::TIMG9;
+#[cfg(time_driver_timg10)]
+type T = peripherals::TIMG10;
+#[cfg(time_driver_timg11)]
+type T = peripherals::TIMG11;
+#[cfg(time_driver_timg14)]
+type T = peripherals::TIMG14;
+#[cfg(time_driver_tima0)]
+type T = peripherals::TIMA0;
+#[cfg(time_driver_tima1)]
+type T = peripherals::TIMA1;
+
+// TODO: RTC
+
+fn regs() -> Tim {
+ unsafe { Tim::from_ptr(T::regs()) }
+}
+
+fn regs_counter(tim: Tim) -> Counterregs16 {
+ unsafe { Counterregs16::from_ptr(tim.counterregs(0).as_ptr()) }
+}
+
+/// Clock timekeeping works with something we call "periods", which are time intervals
+/// of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods.
+fn calc_now(period: u32, counter: u16) -> u64 {
+ ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
+}
+
+/// The TIMx driver uses one of the `TIMG` or `TIMA` timer instances to implement a timer with a 32.768 kHz
+/// tick rate. (TODO: Allow setting the tick rate)
+///
+/// This driver defines a period to be 2^15 ticks. 16-bit timers of course count to 2^16 ticks.
+///
+/// To generate a period every 2^15 ticks, the CC0 value is set to 2^15 and the load value set to 2^16.
+/// Incrementing the period on a CCU0 and load results in the a period of 2^15 ticks.
+///
+/// For a specific timestamp, load the lower 16 bits into the CC1 value. When the period where the timestamp
+/// should be enabled is reached, then the CCU1 (CC1 up) interrupt runs to actually wake the timer.
+///
+/// TODO: Compensate for per part variance. This can supposedly be done with the FCC system.
+/// TODO: Allow using 32-bit timers (TIMG12 and TIMG13).
+struct TimxDriver {
+ /// Number of 2^15 periods elapsed since boot.
+ period: AtomicU32,
+ /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
+ alarm: Mutex| >,
+ queue: Mutex>,
+}
+
+impl TimxDriver {
+ #[inline(never)]
+ fn init(&'static self, _cs: CriticalSection) {
+ // Clock config
+ // TODO: Configurable tick rate up to 4 MHz (32 kHz for now)
+ let regs = regs();
+
+ // Reset timer
+ regs.gprcm(0).rstctl().write(|w| {
+ w.set_resetassert(true);
+ w.set_key(ResetKey::KEY);
+ w.set_resetstkyclr(true);
+ });
+
+ // Power up timer
+ regs.gprcm(0).pwren().write(|w| {
+ w.set_enable(true);
+ w.set_key(PwrenKey::KEY);
+ });
+
+ // Following the instructions according to SLAU847D 23.2.1: TIMCLK Configuration
+
+ // 1. Select TIMCLK source
+ regs.clksel().modify(|w| {
+ // Use LFCLK for a 32.768kHz tick rate
+ w.set_lfclk_sel(true);
+ // TODO: Allow MFCLK for configurable tick rate up to 4 MHz
+ // w.set_mfclk_sel(ClkSel::ENABLE);
+ });
+
+ // 2. Divide by TIMCLK, we don't need to divide further for the 32kHz tick rate
+ regs.clkdiv().modify(|w| {
+ w.set_ratio(Ratio::DIV_BY_1);
+ });
+
+ // 3. To be generic across timer instances, we do not use the prescaler.
+ // TODO: mspm0-sdk always sets this, regardless of timer width?
+ regs.commonregs(0).cps().modify(|w| {
+ w.set_pcnt(0);
+ });
+
+ regs.pdbgctl().modify(|w| {
+ w.set_free(true);
+ });
+
+ // 4. Enable the TIMCLK.
+ regs.commonregs(0).cclkctl().modify(|w| {
+ w.set_clken(true);
+ });
+
+ regs.counterregs(0).ctrctl().modify(|w| {
+ // allow counting during debug
+ w.set_repeat(Repeat::REPEAT_3);
+ w.set_cvae(Cvae::ZEROVAL);
+ w.set_cm(Cm::UP);
+
+ // Must explicitly set CZC, CAC and CLC to 0 in order for all the timers to count.
+ //
+ // The reset value of these registers is 0x07, which is a reserved value.
+ //
+ // Looking at a bit representation of the reset value, this appears to be an AND
+ // of 2-input QEI mode and CCCTL_3 ACOND. Given that TIMG14 and TIMA0 have no QEI
+ // and 4 capture and compare channels, this works by accident for those timer units.
+ w.set_czc(CxC::CCTL0);
+ w.set_cac(CxC::CCTL0);
+ w.set_clc(CxC::CCTL0);
+ });
+
+ // Setup the period
+ let ctr = regs_counter(regs);
+
+ // Middle
+ ctr.cc(0).modify(|w| {
+ w.set_ccval(0x7FFF);
+ });
+
+ ctr.load().modify(|w| {
+ w.set_ld(u16::MAX);
+ });
+
+ // Enable the period interrupts
+ //
+ // This does not appear to ever be set for CPU_INT in the TI SDK and is not technically needed.
+ regs.evt_mode().modify(|w| {
+ w.set_evt_cfg(0, EvtCfg::SOFTWARE);
+ });
+
+ regs.int_event(0).imask().modify(|w| {
+ w.set_l(true);
+ w.set_ccu0(true);
+ });
+
+ unsafe { T::enable_interrupt() };
+
+ // Allow the counter to start counting.
+ regs.counterregs(0).ctrctl().modify(|w| {
+ w.set_en(true);
+ });
+ }
+
+ #[inline(never)]
+ fn next_period(&self) {
+ let r = regs();
+
+ // We only modify the period from the timer interrupt, so we know this can't race.
+ let period = self.period.load(Ordering::Relaxed) + 1;
+ self.period.store(period, Ordering::Relaxed);
+ let t = (period as u64) << 15;
+
+ critical_section::with(move |cs| {
+ r.int_event(0).imask().modify(move |w| {
+ let alarm = self.alarm.borrow(cs);
+ let at = alarm.get();
+
+ if at < t + 0xC000 {
+ // just enable it. `set_alarm` has already set the correct CC1 val.
+ w.set_ccu1(true);
+ }
+ })
+ });
+ }
+
+ #[inline(never)]
+ fn on_interrupt(&self) {
+ let r = regs();
+
+ critical_section::with(|cs| {
+ let mis = r.int_event(0).mis().read();
+
+ // Advance to next period if overflowed
+ if mis.l() {
+ self.next_period();
+
+ r.int_event(0).iclr().write(|w| {
+ w.set_l(true);
+ });
+ }
+
+ if mis.ccu0() {
+ self.next_period();
+
+ r.int_event(0).iclr().write(|w| {
+ w.set_ccu0(true);
+ });
+ }
+
+ if mis.ccu1() {
+ r.int_event(0).iclr().write(|w| {
+ w.set_ccu1(true);
+ });
+
+ self.trigger_alarm(cs);
+ }
+ });
+ }
+
+ fn trigger_alarm(&self, cs: CriticalSection) {
+ let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
+
+ while !self.set_alarm(cs, next) {
+ next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
+ }
+ }
+
+ fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
+ let r = regs();
+ let ctr = regs_counter(r);
+
+ self.alarm.borrow(cs).set(timestamp);
+
+ let t = self.now();
+
+ if timestamp <= t {
+ // If alarm timestamp has passed the alarm will not fire.
+ // Disarm the alarm and return `false` to indicate that.
+ r.int_event(0).imask().modify(|w| w.set_ccu1(false));
+
+ self.alarm.borrow(cs).set(u64::MAX);
+
+ return false;
+ }
+
+ // Write the CC1 value regardless of whether we're going to enable it now or not.
+ // This way, when we enable it later, the right value is already set.
+ ctr.cc(1).write(|w| {
+ w.set_ccval(timestamp as u16);
+ });
+
+ // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
+ let diff = timestamp - t;
+ r.int_event(0).imask().modify(|w| w.set_ccu1(diff < 0xC000));
+
+ // Reevaluate if the alarm timestamp is still in the future
+ let t = self.now();
+ if timestamp <= t {
+ // If alarm timestamp has passed since we set it, we have a race condition and
+ // the alarm may or may not have fired.
+ // Disarm the alarm and return `false` to indicate that.
+ // It is the caller's responsibility to handle this ambiguity.
+ r.int_event(0).imask().modify(|w| w.set_ccu1(false));
+
+ self.alarm.borrow(cs).set(u64::MAX);
+
+ return false;
+ }
+
+ // We're confident the alarm will ring in the future.
+ true
+ }
+}
+
+impl Driver for TimxDriver {
+ fn now(&self) -> u64 {
+ let regs = regs();
+
+ let period = self.period.load(Ordering::Relaxed);
+ // Ensure the compiler does not read the counter before the period.
+ compiler_fence(Ordering::Acquire);
+
+ let counter = regs_counter(regs).ctr().read().cctr() as u16;
+
+ calc_now(period, counter)
+ }
+
+ fn schedule_wake(&self, at: u64, waker: &Waker) {
+ critical_section::with(|cs| {
+ let mut queue = self.queue.borrow(cs).borrow_mut();
+
+ if queue.schedule_wake(at, waker) {
+ let mut next = queue.next_expiration(self.now());
+
+ while !self.set_alarm(cs, next) {
+ next = queue.next_expiration(self.now());
+ }
+ }
+ });
+ }
+}
+
+embassy_time_driver::time_driver_impl!(static DRIVER: TimxDriver = TimxDriver {
+ period: AtomicU32::new(0),
+ alarm: Mutex::new(Cell::new(u64::MAX)),
+ queue: Mutex::new(RefCell::new(Queue::new()))
+});
+
+pub(crate) fn init(cs: CriticalSection) {
+ DRIVER.init(cs);
+}
+
+#[cfg(time_driver_timg0)]
+#[interrupt]
+fn TIMG0() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg1)]
+#[interrupt]
+fn TIMG1() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg2)]
+#[interrupt]
+fn TIMG2() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg3)]
+#[interrupt]
+fn TIMG3() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg4)]
+#[interrupt]
+fn TIMG4() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg5)]
+#[interrupt]
+fn TIMG5() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg6)]
+#[interrupt]
+fn TIMG6() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg7)]
+#[interrupt]
+fn TIMG7() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg8)]
+#[interrupt]
+fn TIMG8() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg9)]
+#[interrupt]
+fn TIMG9() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg10)]
+#[interrupt]
+fn TIMG10() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_timg11)]
+#[interrupt]
+fn TIMG11() {
+ DRIVER.on_interrupt();
+}
+
+// TODO: TIMG12 and TIMG13
+
+#[cfg(time_driver_timg14)]
+#[interrupt]
+fn TIMG14() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_tima0)]
+#[interrupt]
+fn TIMA0() {
+ DRIVER.on_interrupt();
+}
+
+#[cfg(time_driver_tima1)]
+#[interrupt]
+fn TIMA1() {
+ DRIVER.on_interrupt();
+}
diff --git a/embassy-mspm0/src/timer.rs b/embassy-mspm0/src/timer.rs
new file mode 100644
index 000000000..4441e5640
--- /dev/null
+++ b/embassy-mspm0/src/timer.rs
@@ -0,0 +1,48 @@
+#![macro_use]
+
+/// Amount of bits of a timer.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum TimerBits {
+ /// 16 bits.
+ Bits16,
+ /// 32 bits.
+ Bits32,
+}
+
+#[allow(private_bounds)]
+pub trait Timer: SealedTimer + 'static {
+ /// Amount of bits this timer has.
+ const BITS: TimerBits;
+}
+
+pub(crate) trait SealedTimer {
+ /// Registers for this timer.
+ ///
+ /// This is a raw pointer to the register block. The actual register block layout varies depending on the
+ /// timer type.
+ fn regs() -> *mut ();
+
+ /// Enable the interrupt corresponding to this timer.
+ unsafe fn enable_interrupt();
+}
+
+macro_rules! impl_timer {
+ ($name: ident, $bits: ident) => {
+ impl crate::timer::SealedTimer for crate::peripherals::$name {
+ fn regs() -> *mut () {
+ crate::pac::$name.as_ptr()
+ }
+
+ unsafe fn enable_interrupt() {
+ use embassy_hal_internal::interrupt::InterruptExt;
+ crate::interrupt::$name.unpend();
+ crate::interrupt::$name.enable();
+ }
+ }
+
+ impl crate::timer::Timer for crate::peripherals::$name {
+ const BITS: crate::timer::TimerBits = crate::timer::TimerBits::$bits;
+ }
+ };
+}
diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index c3fcfd06e..f939be004 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -16,7 +16,7 @@ use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, AtomicUsize, Orde
use core::task::Poll;
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
-use embassy_hal_internal::{into_ref, PeripheralRef};
+use embassy_hal_internal::Peri;
use pac::uarte::vals;
// Re-export SVD variants to allow user to directly set values
pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity};
@@ -28,7 +28,7 @@ use crate::ppi::{
};
use crate::timer::{Instance as TimerInstance, Timer};
use crate::uarte::{configure, configure_rx_pins, configure_tx_pins, drop_tx_rx, Config, Instance as UarteInstance};
-use crate::{interrupt, pac, Peripheral, EASY_DMA_SIZE};
+use crate::{interrupt, pac, EASY_DMA_SIZE};
pub(crate) struct State {
tx_buf: RingBuffer,
@@ -222,27 +222,26 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
/// Panics if `rx_buffer.len()` is odd.
#[allow(clippy::too_many_arguments)]
pub fn new(
- uarte: impl Peripheral + 'd,
- timer: impl Peripheral + 'd,
- ppi_ch1: impl Peripheral + 'd,
- ppi_ch2: impl Peripheral + 'd,
- ppi_group: impl Peripheral + 'd,
+ uarte: Peri<'d, U>,
+ timer: Peri<'d, T>,
+ ppi_ch1: Peri<'d, impl ConfigurableChannel>,
+ ppi_ch2: Peri<'d, impl ConfigurableChannel>,
+ ppi_group: Peri<'d, impl Group>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- rxd: impl Peripheral + 'd,
- txd: impl Peripheral + 'd,
+ rxd: Peri<'d, impl GpioPin>,
+ txd: Peri<'d, impl GpioPin>,
config: Config,
rx_buffer: &'d mut [u8],
tx_buffer: &'d mut [u8],
) -> Self {
- into_ref!(uarte, timer, rxd, txd, ppi_ch1, ppi_ch2, ppi_group);
Self::new_inner(
uarte,
timer,
- ppi_ch1.map_into(),
- ppi_ch2.map_into(),
- ppi_group.map_into(),
- rxd.map_into(),
- txd.map_into(),
+ ppi_ch1.into(),
+ ppi_ch2.into(),
+ ppi_group.into(),
+ rxd.into(),
+ txd.into(),
None,
None,
config,
@@ -258,31 +257,30 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
/// Panics if `rx_buffer.len()` is odd.
#[allow(clippy::too_many_arguments)]
pub fn new_with_rtscts(
- uarte: impl Peripheral + 'd,
- timer: impl Peripheral + 'd,
- ppi_ch1: impl Peripheral + 'd,
- ppi_ch2: impl Peripheral + 'd,
- ppi_group: impl Peripheral + 'd,
+ uarte: Peri<'d, U>,
+ timer: Peri<'d, T>,
+ ppi_ch1: Peri<'d, impl ConfigurableChannel>,
+ ppi_ch2: Peri<'d, impl ConfigurableChannel>,
+ ppi_group: Peri<'d, impl Group>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- rxd: impl Peripheral + 'd,
- txd: impl Peripheral + 'd,
- cts: impl Peripheral + 'd,
- rts: impl Peripheral + 'd,
+ rxd: Peri<'d, impl GpioPin>,
+ txd: Peri<'d, impl GpioPin>,
+ cts: Peri<'d, impl GpioPin>,
+ rts: Peri<'d, impl GpioPin>,
config: Config,
rx_buffer: &'d mut [u8],
tx_buffer: &'d mut [u8],
) -> Self {
- into_ref!(uarte, timer, rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group);
Self::new_inner(
uarte,
timer,
- ppi_ch1.map_into(),
- ppi_ch2.map_into(),
- ppi_group.map_into(),
- rxd.map_into(),
- txd.map_into(),
- Some(cts.map_into()),
- Some(rts.map_into()),
+ ppi_ch1.into(),
+ ppi_ch2.into(),
+ ppi_group.into(),
+ rxd.into(),
+ txd.into(),
+ Some(cts.into()),
+ Some(rts.into()),
config,
rx_buffer,
tx_buffer,
@@ -291,15 +289,15 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
#[allow(clippy::too_many_arguments)]
fn new_inner(
- peri: PeripheralRef<'d, U>,
- timer: PeripheralRef<'d, T>,
- ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>,
- ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>,
- ppi_group: PeripheralRef<'d, AnyGroup>,
- rxd: PeripheralRef<'d, AnyPin>,
- txd: PeripheralRef<'d, AnyPin>,
- cts: Option>,
- rts: Option>,
+ peri: Peri<'d, U>,
+ timer: Peri<'d, T>,
+ ppi_ch1: Peri<'d, AnyConfigurableChannel>,
+ ppi_ch2: Peri<'d, AnyConfigurableChannel>,
+ ppi_group: Peri<'d, AnyGroup>,
+ rxd: Peri<'d, AnyPin>,
+ txd: Peri<'d, AnyPin>,
+ cts: Option>,
+ rts: Option>,
config: Config,
rx_buffer: &'d mut [u8],
tx_buffer: &'d mut [u8],
@@ -372,20 +370,19 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
/// Reader part of the buffered UARTE driver.
pub struct BufferedUarteTx<'d, U: UarteInstance> {
- _peri: PeripheralRef<'d, U>,
+ _peri: Peri<'d, U>,
}
impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> {
/// Create a new BufferedUarteTx without hardware flow control.
pub fn new(
- uarte: impl Peripheral + 'd,
+ uarte: Peri<'d, U>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- txd: impl Peripheral + 'd,
+ txd: Peri<'d, impl GpioPin>,
config: Config,
tx_buffer: &'d mut [u8],
) -> Self {
- into_ref!(uarte, txd);
- Self::new_inner(uarte, txd.map_into(), None, config, tx_buffer)
+ Self::new_inner(uarte, txd.into(), None, config, tx_buffer)
}
/// Create a new BufferedUarte with hardware flow control (RTS/CTS)
@@ -394,21 +391,20 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> {
///
/// Panics if `rx_buffer.len()` is odd.
pub fn new_with_cts(
- uarte: impl Peripheral + 'd,
+ uarte: Peri<'d, U>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- txd: impl Peripheral + 'd,
- cts: impl Peripheral + 'd,
+ txd: Peri<'d, impl GpioPin>,
+ cts: Peri<'d, impl GpioPin>,
config: Config,
tx_buffer: &'d mut [u8],
) -> Self {
- into_ref!(uarte, txd, cts);
- Self::new_inner(uarte, txd.map_into(), Some(cts.map_into()), config, tx_buffer)
+ Self::new_inner(uarte, txd.into(), Some(cts.into()), config, tx_buffer)
}
fn new_inner(
- peri: PeripheralRef<'d, U>,
- txd: PeripheralRef<'d, AnyPin>,
- cts: Option>,
+ peri: Peri<'d, U>,
+ txd: Peri<'d, AnyPin>,
+ cts: Option>,
config: Config,
tx_buffer: &'d mut [u8],
) -> Self {
@@ -426,9 +422,9 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> {
}
fn new_innerer(
- peri: PeripheralRef<'d, U>,
- txd: PeripheralRef<'d, AnyPin>,
- cts: Option>,
+ peri: Peri<'d, U>,
+ txd: Peri<'d, AnyPin>,
+ cts: Option>,
tx_buffer: &'d mut [u8],
) -> Self {
let r = U::regs();
@@ -542,7 +538,7 @@ impl<'a, U: UarteInstance> Drop for BufferedUarteTx<'a, U> {
/// Reader part of the buffered UARTE driver.
pub struct BufferedUarteRx<'d, U: UarteInstance, T: TimerInstance> {
- _peri: PeripheralRef<'d, U>,
+ _peri: Peri<'d, U>,
timer: Timer<'d, T>,
_ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>,
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>,
@@ -557,24 +553,23 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
/// Panics if `rx_buffer.len()` is odd.
#[allow(clippy::too_many_arguments)]
pub fn new(
- uarte: impl Peripheral + 'd,
- timer: impl Peripheral + 'd,
- ppi_ch1: impl Peripheral + 'd,
- ppi_ch2: impl Peripheral + 'd,
- ppi_group: impl Peripheral + 'd,
+ uarte: Peri<'d, U>,
+ timer: Peri<'d, T>,
+ ppi_ch1: Peri<'d, impl ConfigurableChannel>,
+ ppi_ch2: Peri<'d, impl ConfigurableChannel>,
+ ppi_group: Peri<'d, impl Group>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- rxd: impl Peripheral + 'd,
+ rxd: Peri<'d, impl GpioPin>,
config: Config,
rx_buffer: &'d mut [u8],
) -> Self {
- into_ref!(uarte, timer, rxd, ppi_ch1, ppi_ch2, ppi_group);
Self::new_inner(
uarte,
timer,
- ppi_ch1.map_into(),
- ppi_ch2.map_into(),
- ppi_group.map_into(),
- rxd.map_into(),
+ ppi_ch1.into(),
+ ppi_ch2.into(),
+ ppi_group.into(),
+ rxd.into(),
None,
config,
rx_buffer,
@@ -588,26 +583,25 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
/// Panics if `rx_buffer.len()` is odd.
#[allow(clippy::too_many_arguments)]
pub fn new_with_rts(
- uarte: impl Peripheral + 'd,
- timer: impl Peripheral + 'd,
- ppi_ch1: impl Peripheral + 'd,
- ppi_ch2: impl Peripheral + 'd,
- ppi_group: impl Peripheral + 'd,
+ uarte: Peri<'d, U>,
+ timer: Peri<'d, T>,
+ ppi_ch1: Peri<'d, impl ConfigurableChannel>,
+ ppi_ch2: Peri<'d, impl ConfigurableChannel>,
+ ppi_group: Peri<'d, impl Group>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- rxd: impl Peripheral + 'd,
- rts: impl Peripheral + 'd,
+ rxd: Peri<'d, impl GpioPin>,
+ rts: Peri<'d, impl GpioPin>,
config: Config,
rx_buffer: &'d mut [u8],
) -> Self {
- into_ref!(uarte, timer, rxd, rts, ppi_ch1, ppi_ch2, ppi_group);
Self::new_inner(
uarte,
timer,
- ppi_ch1.map_into(),
- ppi_ch2.map_into(),
- ppi_group.map_into(),
- rxd.map_into(),
- Some(rts.map_into()),
+ ppi_ch1.into(),
+ ppi_ch2.into(),
+ ppi_group.into(),
+ rxd.into(),
+ Some(rts.into()),
config,
rx_buffer,
)
@@ -615,13 +609,13 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
#[allow(clippy::too_many_arguments)]
fn new_inner(
- peri: PeripheralRef<'d, U>,
- timer: PeripheralRef<'d, T>,
- ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>,
- ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>,
- ppi_group: PeripheralRef<'d, AnyGroup>,
- rxd: PeripheralRef<'d, AnyPin>,
- rts: Option>,
+ peri: Peri<'d, U>,
+ timer: Peri<'d, T>,
+ ppi_ch1: Peri<'d, AnyConfigurableChannel>,
+ ppi_ch2: Peri<'d, AnyConfigurableChannel>,
+ ppi_group: Peri<'d, AnyGroup>,
+ rxd: Peri<'d, AnyPin>,
+ rts: Option>,
config: Config,
rx_buffer: &'d mut [u8],
) -> Self {
@@ -640,13 +634,13 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
#[allow(clippy::too_many_arguments)]
fn new_innerer(
- peri: PeripheralRef<'d, U>,
- timer: PeripheralRef<'d, T>,
- ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>,
- ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>,
- ppi_group: PeripheralRef<'d, AnyGroup>,
- rxd: PeripheralRef<'d, AnyPin>,
- rts: Option>,
+ peri: Peri<'d, U>,
+ timer: Peri<'d, T>,
+ ppi_ch1: Peri<'d, AnyConfigurableChannel>,
+ ppi_ch2: Peri<'d, AnyConfigurableChannel>,
+ ppi_group: Peri<'d, AnyGroup>,
+ rxd: Peri<'d, AnyPin>,
+ rts: Option>,
rx_buffer: &'d mut [u8],
) -> Self {
assert!(rx_buffer.len() % 2 == 0);
diff --git a/embassy-nrf/src/egu.rs b/embassy-nrf/src/egu.rs
index 7f9abdac4..028396c7c 100644
--- a/embassy-nrf/src/egu.rs
+++ b/embassy-nrf/src/egu.rs
@@ -7,20 +7,19 @@
use core::marker::PhantomData;
-use embassy_hal_internal::into_ref;
+use embassy_hal_internal::PeripheralType;
use crate::ppi::{Event, Task};
-use crate::{interrupt, pac, Peripheral, PeripheralRef};
+use crate::{interrupt, pac, Peri};
/// An instance of the EGU.
pub struct Egu<'d, T: Instance> {
- _p: PeripheralRef<'d, T>,
+ _p: Peri<'d, T>,
}
impl<'d, T: Instance> Egu<'d, T> {
/// Create a new EGU instance.
- pub fn new(_p: impl Peripheral + 'd) -> Self {
- into_ref!(_p);
+ pub fn new(_p: Peri<'d, T>) -> Self {
Self { _p }
}
@@ -39,7 +38,7 @@ pub(crate) trait SealedInstance {
/// Basic Egu instance.
#[allow(private_bounds)]
-pub trait Instance: Peripheral + SealedInstance + 'static + Send {
+pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs
index c78fa4df5..d02da9ac5 100644
--- a/embassy-nrf/src/gpio.rs
+++ b/embassy-nrf/src/gpio.rs
@@ -5,14 +5,14 @@ use core::convert::Infallible;
use core::hint::unreachable_unchecked;
use cfg_if::cfg_if;
-use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
+use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
+use crate::pac;
use crate::pac::common::{Reg, RW};
use crate::pac::gpio;
use crate::pac::gpio::vals;
#[cfg(not(feature = "_nrf51"))]
use crate::pac::shared::{regs::Psel, vals::Connect};
-use crate::{pac, Peripheral};
/// A GPIO port with up to 32 pins.
#[derive(Debug, Eq, PartialEq)]
@@ -49,7 +49,7 @@ pub struct Input<'d> {
impl<'d> Input<'d> {
/// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
#[inline]
- pub fn new(pin: impl Peripheral + 'd, pull: Pull) -> Self {
+ pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {
let mut pin = Flex::new(pin);
pin.set_as_input(pull);
@@ -210,7 +210,7 @@ pub struct Output<'d> {
impl<'d> Output<'d> {
/// Create GPIO output driver for a [Pin] with the provided [Level] and [OutputDriver] configuration.
#[inline]
- pub fn new(pin: impl Peripheral + 'd, initial_output: Level, drive: OutputDrive) -> Self {
+ pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level, drive: OutputDrive) -> Self {
let mut pin = Flex::new(pin);
match initial_output {
Level::High => pin.set_high(),
@@ -310,7 +310,7 @@ fn convert_pull(pull: Pull) -> vals::Pull {
/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
/// mode.
pub struct Flex<'d> {
- pub(crate) pin: PeripheralRef<'d, AnyPin>,
+ pub(crate) pin: Peri<'d, AnyPin>,
}
impl<'d> Flex<'d> {
@@ -319,10 +319,9 @@ impl<'d> Flex<'d> {
/// The pin remains disconnected. The initial output level is unspecified, but can be changed
/// before the pin is put into output mode.
#[inline]
- pub fn new(pin: impl Peripheral + 'd) -> Self {
- into_ref!(pin);
+ pub fn new(pin: Peri<'d, impl Pin>) -> Self {
// Pin will be in disconnected state.
- Self { pin: pin.map_into() }
+ Self { pin: pin.into() }
}
/// Put the pin into input mode.
@@ -503,7 +502,7 @@ pub(crate) trait SealedPin {
/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].
#[allow(private_bounds)]
-pub trait Pin: Peripheral + Into + SealedPin + Sized + 'static {
+pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static {
/// Number of the pin within the port (0..31)
#[inline]
fn pin(&self) -> u8 {
@@ -529,19 +528,11 @@ pub trait Pin: Peripheral + Into + SealedPin + Sized + 'static
fn psel_bits(&self) -> pac::shared::regs::Psel {
pac::shared::regs::Psel(self.pin_port() as u32)
}
-
- /// Convert from concrete pin type PX_XX to type erased `AnyPin`.
- #[inline]
- fn degrade(self) -> AnyPin {
- AnyPin {
- pin_port: self.pin_port(),
- }
- }
}
/// Type-erased GPIO pin
pub struct AnyPin {
- pin_port: u8,
+ pub(crate) pin_port: u8,
}
impl AnyPin {
@@ -550,8 +541,8 @@ impl AnyPin {
/// # Safety
/// - `pin_port` should not in use by another driver.
#[inline]
- pub unsafe fn steal(pin_port: u8) -> Self {
- Self { pin_port }
+ pub unsafe fn steal(pin_port: u8) -> Peri<'static, Self> {
+ Peri::new_unchecked(Self { pin_port })
}
}
@@ -573,7 +564,7 @@ pub(crate) trait PselBits {
}
#[cfg(not(feature = "_nrf51"))]
-impl<'a, P: Pin> PselBits for Option> {
+impl<'a, P: Pin> PselBits for Option> {
#[inline]
fn psel_bits(&self) -> pac::shared::regs::Psel {
match self {
@@ -611,8 +602,10 @@ macro_rules! impl_pin {
}
impl From for crate::gpio::AnyPin {
- fn from(val: peripherals::$type) -> Self {
- crate::gpio::Pin::degrade(val)
+ fn from(_val: peripherals::$type) -> Self {
+ Self {
+ pin_port: $port_num * 32 + $pin_num,
+ }
}
}
};
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs
index 8771f9f08..d169b49f9 100644
--- a/embassy-nrf/src/gpiote.rs
+++ b/embassy-nrf/src/gpiote.rs
@@ -4,7 +4,7 @@ use core::convert::Infallible;
use core::future::{poll_fn, Future};
use core::task::{Context, Poll};
-use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
+use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin, SealedPin as _};
@@ -189,7 +189,7 @@ impl Iterator for BitIter {
/// GPIOTE channel driver in input mode
pub struct InputChannel<'d> {
- ch: PeripheralRef<'d, AnyChannel>,
+ ch: Peri<'d, AnyChannel>,
pin: Input<'d>,
}
@@ -204,9 +204,7 @@ impl<'d> Drop for InputChannel<'d> {
impl<'d> InputChannel<'d> {
/// Create a new GPIOTE input channel driver.
- pub fn new(ch: impl Peripheral + 'd, pin: Input<'d>, polarity: InputChannelPolarity) -> Self {
- into_ref!(ch);
-
+ pub fn new(ch: Peri<'d, impl Channel>, pin: Input<'d>, polarity: InputChannelPolarity) -> Self {
let g = regs();
let num = ch.number();
@@ -228,7 +226,7 @@ impl<'d> InputChannel<'d> {
g.events_in(num).write_value(0);
- InputChannel { ch: ch.map_into(), pin }
+ InputChannel { ch: ch.into(), pin }
}
/// Asynchronously wait for an event in this channel.
@@ -261,7 +259,7 @@ impl<'d> InputChannel<'d> {
/// GPIOTE channel driver in output mode
pub struct OutputChannel<'d> {
- ch: PeripheralRef<'d, AnyChannel>,
+ ch: Peri<'d, AnyChannel>,
_pin: Output<'d>,
}
@@ -276,8 +274,7 @@ impl<'d> Drop for OutputChannel<'d> {
impl<'d> OutputChannel<'d> {
/// Create a new GPIOTE output channel driver.
- pub fn new(ch: impl Peripheral + 'd, pin: Output<'d>, polarity: OutputChannelPolarity) -> Self {
- into_ref!(ch);
+ pub fn new(ch: Peri<'d, impl Channel>, pin: Output<'d>, polarity: OutputChannelPolarity) -> Self {
let g = regs();
let num = ch.number();
@@ -301,7 +298,7 @@ impl<'d> OutputChannel<'d> {
});
OutputChannel {
- ch: ch.map_into(),
+ ch: ch.into(),
_pin: pin,
}
}
@@ -351,14 +348,12 @@ impl<'d> OutputChannel<'d> {
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub(crate) struct PortInputFuture<'a> {
- pin: PeripheralRef<'a, AnyPin>,
+ pin: Peri<'a, AnyPin>,
}
impl<'a> PortInputFuture<'a> {
- fn new(pin: impl Peripheral + 'a) -> Self {
- Self {
- pin: pin.into_ref().map_into(),
- }
+ fn new(pin: Peri<'a, impl GpioPin>) -> Self {
+ Self { pin: pin.into() }
}
}
@@ -415,13 +410,13 @@ impl<'d> Flex<'d> {
/// Wait until the pin is high. If it is already high, return immediately.
pub async fn wait_for_high(&mut self) {
self.pin.conf().modify(|w| w.set_sense(Sense::HIGH));
- PortInputFuture::new(&mut self.pin).await
+ PortInputFuture::new(self.pin.reborrow()).await
}
/// Wait until the pin is low. If it is already low, return immediately.
pub async fn wait_for_low(&mut self) {
self.pin.conf().modify(|w| w.set_sense(Sense::LOW));
- PortInputFuture::new(&mut self.pin).await
+ PortInputFuture::new(self.pin.reborrow()).await
}
/// Wait for the pin to undergo a transition from low to high.
@@ -443,7 +438,7 @@ impl<'d> Flex<'d> {
} else {
self.pin.conf().modify(|w| w.set_sense(Sense::HIGH));
}
- PortInputFuture::new(&mut self.pin).await
+ PortInputFuture::new(self.pin.reborrow()).await
}
}
@@ -455,24 +450,14 @@ trait SealedChannel {}
///
/// Implemented by all GPIOTE channels.
#[allow(private_bounds)]
-pub trait Channel: SealedChannel + Into + Sized + 'static {
+pub trait Channel: PeripheralType + SealedChannel + Into + Sized + 'static {
/// Get the channel number.
fn number(&self) -> usize;
-
- /// Convert this channel to a type-erased `AnyChannel`.
- ///
- /// This allows using several channels in situations that might require
- /// them to be the same type, like putting them in an array.
- fn degrade(self) -> AnyChannel {
- AnyChannel {
- number: self.number() as u8,
- }
- }
}
/// Type-erased channel.
///
-/// Obtained by calling `Channel::degrade`.
+/// Obtained by calling `Channel::into()`.
///
/// This allows using several channels in situations that might require
/// them to be the same type, like putting them in an array.
@@ -498,7 +483,9 @@ macro_rules! impl_channel {
impl From for AnyChannel {
fn from(val: peripherals::$type) -> Self {
- Channel::degrade(val)
+ Self {
+ number: val.number() as u8,
+ }
}
}
};
diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs
index 384a1637b..a7dde8cd7 100644
--- a/embassy-nrf/src/i2s.rs
+++ b/embassy-nrf/src/i2s.rs
@@ -10,14 +10,14 @@ use core::sync::atomic::{compiler_fence, AtomicBool, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
-use embassy_hal_internal::{into_ref, PeripheralRef};
+use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::i2s::vals;
use crate::util::slice_in_ram_or;
-use crate::{interrupt, pac, Peripheral, EASY_DMA_SIZE};
+use crate::{interrupt, pac, EASY_DMA_SIZE};
/// Type alias for `MultiBuffering` with 2 buffers.
pub type DoubleBuffering = MultiBuffering;
@@ -406,12 +406,12 @@ impl interrupt::typelevel::Handler for InterruptHandl
/// I2S driver.
pub struct I2S<'d, T: Instance> {
- i2s: PeripheralRef<'d, T>,
- mck: Option>,
- sck: PeripheralRef<'d, AnyPin>,
- lrck: PeripheralRef<'d, AnyPin>,
- sdin: Option>,
- sdout: Option>,
+ i2s: Peri<'d, T>,
+ mck: Option>,
+ sck: Peri<'d, AnyPin>,
+ lrck: Peri<'d, AnyPin>,
+ sdin: Option>,
+ sdout: Option>,
master_clock: Option,
config: Config,
}
@@ -419,20 +419,19 @@ pub struct I2S<'d, T: Instance> {
impl<'d, T: Instance> I2S<'d, T> {
/// Create a new I2S in master mode
pub fn new_master(
- i2s: impl Peripheral + 'd,
+ i2s: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- mck: impl Peripheral + 'd,
- sck: impl Peripheral + 'd,
- lrck: impl Peripheral + 'd,
+ mck: Peri<'d, impl GpioPin>,
+ sck: Peri<'d, impl GpioPin>,
+ lrck: Peri<'d, impl GpioPin>,
master_clock: MasterClock,
config: Config,
) -> Self {
- into_ref!(i2s, mck, sck, lrck);
Self {
i2s,
- mck: Some(mck.map_into()),
- sck: sck.map_into(),
- lrck: lrck.map_into(),
+ mck: Some(mck.into()),
+ sck: sck.into(),
+ lrck: lrck.into(),
sdin: None,
sdout: None,
master_clock: Some(master_clock),
@@ -442,18 +441,17 @@ impl<'d, T: Instance> I2S<'d, T> {
/// Create a new I2S in slave mode
pub fn new_slave(
- i2s: impl Peripheral + 'd,
+ i2s: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- sck: impl Peripheral + 'd,
- lrck: impl Peripheral + 'd,
+ sck: Peri<'d, impl GpioPin>,
+ lrck: Peri<'d, impl GpioPin>,
config: Config,
) -> Self {
- into_ref!(i2s, sck, lrck);
Self {
i2s,
mck: None,
- sck: sck.map_into(),
- lrck: lrck.map_into(),
+ sck: sck.into(),
+ lrck: lrck.into(),
sdin: None,
sdout: None,
master_clock: None,
@@ -464,10 +462,10 @@ impl<'d, T: Instance> I2S<'d, T> {
/// I2S output only
pub fn output(
mut self,
- sdout: impl Peripheral + 'd,
+ sdout: Peri<'d, impl GpioPin>,
buffers: MultiBuffering,
) -> OutputStream<'d, T, S, NB, NS> {
- self.sdout = Some(sdout.into_ref().map_into());
+ self.sdout = Some(sdout.into());
OutputStream {
_p: self.build(),
buffers,
@@ -477,10 +475,10 @@ impl<'d, T: Instance> I2S<'d, T> {
/// I2S input only
pub fn input(
mut self,
- sdin: impl Peripheral + 'd,
+ sdin: Peri<'d, impl GpioPin>,
buffers: MultiBuffering,
) -> InputStream<'d, T, S, NB, NS> {
- self.sdin = Some(sdin.into_ref().map_into());
+ self.sdin = Some(sdin.into());
InputStream {
_p: self.build(),
buffers,
@@ -490,13 +488,13 @@ impl<'d, T: Instance> I2S<'d, T> {
/// I2S full duplex (input and output)
pub fn full_duplex(
mut self,
- sdin: impl Peripheral + 'd,
- sdout: impl Peripheral + 'd,
+ sdin: Peri<'d, impl GpioPin>,
+ sdout: Peri<'d, impl GpioPin>,
buffers_out: MultiBuffering,
buffers_in: MultiBuffering,
) -> FullDuplexStream<'d, T, S, NB, NS> {
- self.sdout = Some(sdout.into_ref().map_into());
- self.sdin = Some(sdin.into_ref().map_into());
+ self.sdout = Some(sdout.into());
+ self.sdin = Some(sdin.into());
FullDuplexStream {
_p: self.build(),
@@ -505,7 +503,7 @@ impl<'d, T: Instance> I2S<'d, T> {
}
}
- fn build(self) -> PeripheralRef<'d, T> {
+ fn build(self) -> Peri<'d, T> {
self.apply_config();
self.select_pins();
self.setup_interrupt();
@@ -702,7 +700,7 @@ impl<'d, T: Instance> I2S<'d, T> {
/// I2S output
pub struct OutputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> {
- _p: PeripheralRef<'d, T>,
+ _p: Peri<'d, T>,
buffers: MultiBuffering,
}
@@ -756,7 +754,7 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream<
/// I2S input
pub struct InputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> {
- _p: PeripheralRef<'d, T>,
+ _p: Peri<'d, T>,
buffers: MultiBuffering,
}
@@ -811,7 +809,7 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<'
/// I2S full duplex stream (input & output)
pub struct FullDuplexStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> {
- _p: PeripheralRef<'d, T>,
+ _p: Peri<'d, T>,
buffers_out: MultiBuffering,
buffers_in: MultiBuffering,
}
@@ -1148,7 +1146,7 @@ pub(crate) trait SealedInstance {
/// I2S peripheral instance.
#[allow(private_bounds)]
-pub trait Instance: Peripheral + SealedInstance + 'static + Send {
+pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 5cd0efa58..d2ff054f4 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -263,7 +263,7 @@ pub use chip::pac;
#[cfg(not(feature = "unstable-pac"))]
pub(crate) use chip::pac;
pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE};
-pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
+pub use embassy_hal_internal::{Peri, PeripheralType};
pub use crate::chip::interrupt;
#[cfg(feature = "rt")]
diff --git a/embassy-nrf/src/nfct.rs b/embassy-nrf/src/nfct.rs
index 8b4b6dfe0..8d70ec954 100644
--- a/embassy-nrf/src/nfct.rs
+++ b/embassy-nrf/src/nfct.rs
@@ -13,7 +13,6 @@ use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
-use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
pub use vals::{Bitframesdd as SddPat, Discardmode as DiscardMode};
@@ -22,7 +21,7 @@ use crate::pac::nfct::vals;
use crate::pac::NFCT;
use crate::peripherals::NFCT;
use crate::util::slice_in_ram;
-use crate::{interrupt, pac, Peripheral};
+use crate::{interrupt, pac, Peri};
/// NFCID1 (aka UID) of different sizes.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
@@ -96,7 +95,7 @@ pub enum Error {
/// NFC tag emulator driver.
pub struct NfcT<'d> {
- _p: PeripheralRef<'d, NFCT>,
+ _p: Peri<'d, NFCT>,
rx_buf: [u8; 256],
tx_buf: [u8; 256],
}
@@ -104,12 +103,10 @@ pub struct NfcT<'d> {
impl<'d> NfcT<'d> {
/// Create an Nfc Tag driver
pub fn new(
- _p: impl Peripheral + 'd,
+ _p: Peri<'d, NFCT>,
_irq: impl interrupt::typelevel::Binding + 'd,
config: &Config,
) -> Self {
- into_ref!(_p);
-
let r = pac::NFCT;
unsafe {
diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs
index 6973b4847..c46af0b34 100644
--- a/embassy-nrf/src/nvmc.rs
+++ b/embassy-nrf/src/nvmc.rs
@@ -2,14 +2,13 @@
use core::{ptr, slice};
-use embassy_hal_internal::{into_ref, PeripheralRef};
use embedded_storage::nor_flash::{
ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
};
use crate::pac::nvmc::vals;
use crate::peripherals::NVMC;
-use crate::{pac, Peripheral};
+use crate::{pac, Peri};
#[cfg(not(feature = "_nrf5340-net"))]
/// Erase size of NVMC flash in bytes.
@@ -42,13 +41,12 @@ impl NorFlashError for Error {
/// Non-Volatile Memory Controller (NVMC) that implements the `embedded-storage` traits.
pub struct Nvmc<'d> {
- _p: PeripheralRef<'d, NVMC>,
+ _p: Peri<'d, NVMC>,
}
impl<'d> Nvmc<'d> {
/// Create Nvmc driver.
- pub fn new(_p: impl Peripheral + 'd) -> Self {
- into_ref!(_p);
+ pub fn new(_p: Peri<'d, NVMC>) -> Self {
Self { _p }
}
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs
index 483d1a644..c2a4ba65f 100644
--- a/embassy-nrf/src/pdm.rs
+++ b/embassy-nrf/src/pdm.rs
@@ -8,7 +8,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
-use embassy_hal_internal::{into_ref, PeripheralRef};
+use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use fixed::types::I7F1;
@@ -25,7 +25,7 @@ pub use crate::pac::pdm::vals::Freq as Frequency;
feature = "_nrf91",
))]
pub use crate::pac::pdm::vals::Ratio;
-use crate::{interrupt, pac, Peripheral};
+use crate::{interrupt, pac};
/// Interrupt handler
pub struct InterruptHandler {
@@ -54,7 +54,7 @@ impl interrupt::typelevel::Handler for InterruptHandl
/// PDM microphone interface
pub struct Pdm<'d, T: Instance> {
- _peri: PeripheralRef<'d, T>,
+ _peri: Peri<'d, T>,
}
/// PDM error
@@ -89,24 +89,16 @@ pub enum SamplerState {
impl<'d, T: Instance> Pdm<'d, T> {
/// Create PDM driver
pub fn new(
- pdm: impl Peripheral + 'd,
+ pdm: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- clk: impl Peripheral + 'd,
- din: impl Peripheral + 'd,
+ clk: Peri<'d, impl GpioPin>,
+ din: Peri<'d, impl GpioPin>,
config: Config,
) -> Self {
- into_ref!(pdm, clk, din);
- Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
+ Self::new_inner(pdm, clk.into(), din.into(), config)
}
- fn new_inner(
- pdm: PeripheralRef<'d, T>,
- clk: PeripheralRef<'d, AnyPin>,
- din: PeripheralRef<'d, AnyPin>,
- config: Config,
- ) -> Self {
- into_ref!(pdm);
-
+ fn new_inner(pdm: Peri<'d, T>, clk: Peri<'d, AnyPin>, din: Peri<'d, AnyPin>, config: Config) -> Self {
let r = T::regs();
// setup gpio pins
@@ -452,7 +444,7 @@ pub(crate) trait SealedInstance {
/// PDM peripheral instance
#[allow(private_bounds)]
-pub trait Instance: Peripheral + SealedInstance + 'static + Send {
+pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
/// Interrupt for this peripheral
type Interrupt: interrupt::typelevel::Interrupt;
}
diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs
index 3c7b96df7..686f66987 100644
--- a/embassy-nrf/src/ppi/dppi.rs
+++ b/embassy-nrf/src/ppi/dppi.rs
@@ -1,7 +1,5 @@
-use embassy_hal_internal::into_ref;
-
use super::{Channel, ConfigurableChannel, Event, Ppi, Task};
-use crate::{pac, Peripheral};
+use crate::{pac, Peri};
const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
@@ -12,14 +10,14 @@ pub(crate) fn regs() -> pac::dppic::Dppic {
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
/// Configure PPI channel to trigger `task` on `event`.
- pub fn new_one_to_one(ch: impl Peripheral + 'd, event: Event<'d>, task: Task<'d>) -> Self {
+ pub fn new_one_to_one(ch: Peri<'d, C>, event: Event<'d>, task: Task<'d>) -> Self {
Ppi::new_many_to_many(ch, [event], [task])
}
}
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
- pub fn new_one_to_two(ch: impl Peripheral + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
+ pub fn new_one_to_two(ch: Peri<'d, C>, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
Ppi::new_many_to_many(ch, [event], [task1, task2])
}
}
@@ -28,13 +26,7 @@ impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usi
Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
{
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
- pub fn new_many_to_many(
- ch: impl Peripheral + 'd,
- events: [Event<'d>; EVENT_COUNT],
- tasks: [Task<'d>; TASK_COUNT],
- ) -> Self {
- into_ref!(ch);
-
+ pub fn new_many_to_many(ch: Peri<'d, C>, events: [Event<'d>; EVENT_COUNT], tasks: [Task<'d>; TASK_COUNT]) -> Self {
let val = DPPI_ENABLE_BIT | (ch.number() as u32 & DPPI_CHANNEL_MASK);
for task in tasks {
if unsafe { task.subscribe_reg().read_volatile() } != 0 {
diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs
index 325e4ce00..531777205 100644
--- a/embassy-nrf/src/ppi/mod.rs
+++ b/embassy-nrf/src/ppi/mod.rs
@@ -18,10 +18,10 @@
use core::marker::PhantomData;
use core::ptr::NonNull;
-use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
+use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
use crate::pac::common::{Reg, RW, W};
-use crate::{peripherals, Peripheral};
+use crate::peripherals;
#[cfg_attr(feature = "_dppi", path = "dppi.rs")]
#[cfg_attr(feature = "_ppi", path = "ppi.rs")]
@@ -30,7 +30,7 @@ pub(crate) use _version::*;
/// PPI channel driver.
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
- ch: PeripheralRef<'d, C>,
+ ch: Peri<'d, C>,
#[cfg(feature = "_dppi")]
events: [Event<'d>; EVENT_COUNT],
#[cfg(feature = "_dppi")]
@@ -39,16 +39,14 @@ pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize
/// PPI channel group driver.
pub struct PpiGroup<'d, G: Group> {
- g: PeripheralRef<'d, G>,
+ g: Peri<'d, G>,
}
impl<'d, G: Group> PpiGroup<'d, G> {
/// Create a new PPI group driver.
///
/// The group is initialized as containing no channels.
- pub fn new(g: impl Peripheral + 'd) -> Self {
- into_ref!(g);
-
+ pub fn new(g: Peri<'d, G>) -> Self {
let r = regs();
let n = g.number();
r.chg(n).write(|_| ());
@@ -210,34 +208,22 @@ pub(crate) trait SealedGroup {}
/// Interface for PPI channels.
#[allow(private_bounds)]
-pub trait Channel: SealedChannel + Peripheral + Sized + 'static {
+pub trait Channel: SealedChannel + PeripheralType + Sized + 'static {
/// Returns the number of the channel
fn number(&self) -> usize;
}
/// Interface for PPI channels that can be configured.
-pub trait ConfigurableChannel: Channel + Into {
- /// Convert into a type erased configurable channel.
- fn degrade(self) -> AnyConfigurableChannel;
-}
+pub trait ConfigurableChannel: Channel + Into {}
/// Interface for PPI channels that cannot be configured.
-pub trait StaticChannel: Channel + Into {
- /// Convert into a type erased static channel.
- fn degrade(self) -> AnyStaticChannel;
-}
+pub trait StaticChannel: Channel + Into {}
/// Interface for a group of PPI channels.
#[allow(private_bounds)]
-pub trait Group: SealedGroup + Peripheral + Into + Sized + 'static {
+pub trait Group: SealedGroup + PeripheralType + Into + Sized + 'static {
/// Returns the number of the group.
fn number(&self) -> usize;
- /// Convert into a type erased group.
- fn degrade(self) -> AnyGroup {
- AnyGroup {
- number: self.number() as u8,
- }
- }
}
// ======================
@@ -255,11 +241,7 @@ impl Channel for AnyStaticChannel {
self.number as usize
}
}
-impl StaticChannel for AnyStaticChannel {
- fn degrade(self) -> AnyStaticChannel {
- self
- }
-}
+impl StaticChannel for AnyStaticChannel {}
/// The any configurable channel can represent any configurable channel at runtime.
/// This can be used to have fewer generic parameters in some places.
@@ -273,11 +255,7 @@ impl Channel for AnyConfigurableChannel {
self.number as usize
}
}
-impl ConfigurableChannel for AnyConfigurableChannel {
- fn degrade(self) -> AnyConfigurableChannel {
- self
- }
-}
+impl ConfigurableChannel for AnyConfigurableChannel {}
#[cfg(not(feature = "_nrf51"))]
macro_rules! impl_ppi_channel {
@@ -291,35 +269,23 @@ macro_rules! impl_ppi_channel {
};
($type:ident, $number:expr => static) => {
impl_ppi_channel!($type, $number);
- impl crate::ppi::StaticChannel for peripherals::$type {
- fn degrade(self) -> crate::ppi::AnyStaticChannel {
- use crate::ppi::Channel;
- crate::ppi::AnyStaticChannel {
- number: self.number() as u8,
- }
- }
- }
-
+ impl crate::ppi::StaticChannel for peripherals::$type {}
impl From for crate::ppi::AnyStaticChannel {
fn from(val: peripherals::$type) -> Self {
- crate::ppi::StaticChannel::degrade(val)
+ Self {
+ number: crate::ppi::Channel::number(&val) as u8,
+ }
}
}
};
($type:ident, $number:expr => configurable) => {
impl_ppi_channel!($type, $number);
- impl crate::ppi::ConfigurableChannel for peripherals::$type {
- fn degrade(self) -> crate::ppi::AnyConfigurableChannel {
- use crate::ppi::Channel;
- crate::ppi::AnyConfigurableChannel {
- number: self.number() as u8,
- }
- }
- }
-
+ impl crate::ppi::ConfigurableChannel for peripherals::$type {}
impl From for crate::ppi::AnyConfigurableChannel {
fn from(val: peripherals::$type) -> Self {
- crate::ppi::ConfigurableChannel::degrade(val)
+ Self {
+ number: crate::ppi::Channel::number(&val) as u8,
+ }
}
}
};
@@ -351,7 +317,9 @@ macro_rules! impl_group {
impl From for crate::ppi::AnyGroup {
fn from(val: peripherals::$type) -> Self {
- crate::ppi::Group::degrade(val)
+ Self {
+ number: crate::ppi::Group::number(&val) as u8,
+ }
}
}
};
diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs
index a1beb9dcd..e04dacbc0 100644
--- a/embassy-nrf/src/ppi/ppi.rs
+++ b/embassy-nrf/src/ppi/ppi.rs
@@ -1,7 +1,5 @@
-use embassy_hal_internal::into_ref;
-
use super::{Channel, ConfigurableChannel, Event, Ppi, Task};
-use crate::{pac, Peripheral};
+use crate::{pac, Peri};
impl<'d> Task<'d> {
fn reg_val(&self) -> u32 {
@@ -21,9 +19,7 @@ pub(crate) fn regs() -> pac::ppi::Ppi {
#[cfg(not(feature = "_nrf51"))] // Not for nrf51 because of the fork task
impl<'d, C: super::StaticChannel> Ppi<'d, C, 0, 1> {
/// Configure PPI channel to trigger `task`.
- pub fn new_zero_to_one(ch: impl Peripheral + 'd, task: Task) -> Self {
- into_ref!(ch);
-
+ pub fn new_zero_to_one(ch: Peri<'d, C>, task: Task) -> Self {
let r = regs();
let n = ch.number();
r.fork(n).tep().write_value(task.reg_val());
@@ -34,9 +30,7 @@ impl<'d, C: super::StaticChannel> Ppi<'d, C, 0, 1> {
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
/// Configure PPI channel to trigger `task` on `event`.
- pub fn new_one_to_one(ch: impl Peripheral + 'd, event: Event<'d>, task: Task<'d>) -> Self {
- into_ref!(ch);
-
+ pub fn new_one_to_one(ch: Peri<'d, C>, event: Event<'d>, task: Task<'d>) -> Self {
let r = regs();
let n = ch.number();
r.ch(n).eep().write_value(event.reg_val());
@@ -49,9 +43,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
#[cfg(not(feature = "_nrf51"))] // Not for nrf51 because of the fork task
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
- pub fn new_one_to_two(ch: impl Peripheral + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
- into_ref!(ch);
-
+ pub fn new_one_to_two(ch: Peri<'d, C>, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
let r = regs();
let n = ch.number();
r.ch(n).eep().write_value(event.reg_val());
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs
index 6247ff6a5..a2e153e26 100644
--- a/embassy-nrf/src/pwm.rs
+++ b/embassy-nrf/src/pwm.rs
@@ -4,34 +4,34 @@
use core::sync::atomic::{compiler_fence, Ordering};
-use embassy_hal_internal::{into_ref, PeripheralRef};
+use embassy_hal_internal::{Peri, PeripheralType};
use crate::gpio::{convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, DISCONNECTED};
use crate::pac::gpio::vals as gpiovals;
use crate::pac::pwm::vals;
use crate::ppi::{Event, Task};
use crate::util::slice_in_ram_or;
-use crate::{interrupt, pac, Peripheral};
+use crate::{interrupt, pac};
/// SimplePwm is the traditional pwm interface you're probably used to, allowing
/// to simply set a duty cycle across up to four channels.
pub struct SimplePwm<'d, T: Instance> {
- _peri: PeripheralRef<'d, T>,
+ _peri: Peri<'d, T>,
duty: [u16; 4],
- ch0: Option>,
- ch1: Option>,
- ch2: Option>,
- ch3: Option>,
+ ch0: Option>,
+ ch1: Option>,
+ ch2: Option>,
+ ch3: Option>,
}
/// SequencePwm allows you to offload the updating of a sequence of duty cycles
/// to up to four channels, as well as repeat that sequence n times.
pub struct SequencePwm<'d, T: Instance> {
- _peri: PeripheralRef<'d, T>,
- ch0: Option>,
- ch1: Option>,
- ch2: Option>,
- ch3: Option>,
+ _peri: Peri<'d, T>,
+ ch0: Option>,
+ ch1: Option>,
+ ch2: Option>,
+ ch3: Option>,
}
/// PWM error
@@ -54,78 +54,61 @@ pub const PWM_CLK_HZ: u32 = 16_000_000;
impl<'d, T: Instance> SequencePwm<'d, T> {
/// Create a new 1-channel PWM
#[allow(unused_unsafe)]
- pub fn new_1ch(
- pwm: impl Peripheral + 'd,
- ch0: impl Peripheral + 'd,
- config: Config,
- ) -> Result {
- into_ref!(ch0);
- Self::new_inner(pwm, Some(ch0.map_into()), None, None, None, config)
+ pub fn new_1ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: Config) -> Result {
+ Self::new_inner(pwm, Some(ch0.into()), None, None, None, config)
}
/// Create a new 2-channel PWM
#[allow(unused_unsafe)]
pub fn new_2ch(
- pwm: impl Peripheral + 'd,
- ch0: impl Peripheral + 'd,
- ch1: impl Peripheral + 'd,
+ pwm: Peri<'d, T>,
+ ch0: Peri<'d, impl GpioPin>,
+ ch1: Peri<'d, impl GpioPin>,
config: Config,
) -> Result {
- into_ref!(ch0, ch1);
- Self::new_inner(pwm, Some(ch0.map_into()), Some(ch1.map_into()), None, None, config)
+ Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None, config)
}
/// Create a new 3-channel PWM
#[allow(unused_unsafe)]
pub fn new_3ch(
- pwm: impl Peripheral + 'd,
- ch0: impl Peripheral + 'd,
- ch1: impl Peripheral + 'd,
- ch2: impl Peripheral + 'd,
+ pwm: Peri<'d, T>,
+ ch0: Peri<'d, impl GpioPin>,
+ ch1: Peri<'d, impl GpioPin>,
+ ch2: Peri<'d, impl GpioPin>,
config: Config,
) -> Result {
- into_ref!(ch0, ch1, ch2);
- Self::new_inner(
- pwm,
- Some(ch0.map_into()),
- Some(ch1.map_into()),
- Some(ch2.map_into()),
- None,
- config,
- )
+ Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), Some(ch2.into()), None, config)
}
/// Create a new 4-channel PWM
#[allow(unused_unsafe)]
pub fn new_4ch(
- pwm: impl Peripheral + 'd,
- ch0: impl Peripheral + 'd,
- ch1: impl Peripheral + 'd,
- ch2: impl Peripheral + 'd,
- ch3: impl Peripheral + 'd,
+ pwm: Peri<'d, T>,
+ ch0: Peri<'d, impl GpioPin>,
+ ch1: Peri<'d, impl GpioPin>,
+ ch2: Peri<'d, impl GpioPin>,
+ ch3: Peri<'d, impl GpioPin>,
config: Config,
) -> Result {
- into_ref!(ch0, ch1, ch2, ch3);
Self::new_inner(
pwm,
- Some(ch0.map_into()),
- Some(ch1.map_into()),
- Some(ch2.map_into()),
- Some(ch3.map_into()),
+ Some(ch0.into()),
+ Some(ch1.into()),
+ Some(ch2.into()),
+ Some(ch3.into()),
config,
)
}
fn new_inner(
- _pwm: impl Peripheral + 'd,
- ch0: Option>,
- ch1: Option>,
- ch2: Option>,
- ch3: Option>,
+ _pwm: Peri<'d, T>,
+ ch0: Option>,
+ ch1: Option>,
+ ch2: Option>,
+ ch3: Option>,
config: Config,
) -> Result {
- into_ref!(_pwm);
-
let r = T::regs();
if let Some(pin) = &ch0 {
@@ -610,74 +593,54 @@ pub enum CounterMode {
impl<'d, T: Instance> SimplePwm<'d, T> {
/// Create a new 1-channel PWM
#[allow(unused_unsafe)]
- pub fn new_1ch(pwm: impl Peripheral + 'd, ch0: impl Peripheral + 'd) -> Self {
- unsafe {
- into_ref!(ch0);
- Self::new_inner(pwm, Some(ch0.map_into()), None, None, None)
- }
+ pub fn new_1ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>) -> Self {
+ unsafe { Self::new_inner(pwm, Some(ch0.into()), None, None, None) }
}
/// Create a new 2-channel PWM
#[allow(unused_unsafe)]
- pub fn new_2ch(
- pwm: impl Peripheral + 'd,
- ch0: impl Peripheral + 'd,
- ch1: impl Peripheral + 'd,
- ) -> Self {
- into_ref!(ch0, ch1);
- Self::new_inner(pwm, Some(ch0.map_into()), Some(ch1.map_into()), None, None)
+ pub fn new_2ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>) -> Self {
+ Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None)
}
/// Create a new 3-channel PWM
#[allow(unused_unsafe)]
pub fn new_3ch(
- pwm: impl Peripheral + 'd,
- ch0: impl Peripheral + 'd,
- ch1: impl Peripheral + 'd,
- ch2: impl Peripheral + 'd,
+ pwm: Peri<'d, T>,
+ ch0: Peri<'d, impl GpioPin>,
+ ch1: Peri<'d, impl GpioPin>,
+ ch2: Peri<'d, impl GpioPin>,
) -> Self {
- unsafe {
- into_ref!(ch0, ch1, ch2);
- Self::new_inner(
- pwm,
- Some(ch0.map_into()),
- Some(ch1.map_into()),
- Some(ch2.map_into()),
- None,
- )
- }
+ unsafe { Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), Some(ch2.into()), None) }
}
/// Create a new 4-channel PWM
#[allow(unused_unsafe)]
pub fn new_4ch(
- pwm: impl Peripheral + 'd,
- ch0: impl Peripheral + 'd,
- ch1: impl Peripheral + 'd,
- ch2: impl Peripheral + 'd,
- ch3: impl Peripheral + 'd,
+ pwm: Peri<'d, T>,
+ ch0: Peri<'d, impl GpioPin>,
+ ch1: Peri<'d, impl GpioPin>,
+ ch2: Peri<'d, impl GpioPin>,
+ ch3: Peri<'d, impl GpioPin>,
) -> Self {
unsafe {
- into_ref!(ch0, ch1, ch2, ch3);
Self::new_inner(
pwm,
- Some(ch0.map_into()),
- Some(ch1.map_into()),
- Some(ch2.map_into()),
- Some(ch3.map_into()),
+ Some(ch0.into()),
+ Some(ch1.into()),
+ Some(ch2.into()),
+ Some(ch3.into()),
)
}
}
fn new_inner(
- _pwm: impl Peripheral + 'd,
- ch0: Option>,
- ch1: Option>,
- ch2: Option>,
- ch3: Option>,
+ _pwm: Peri<'d, T>,
+ ch0: Option>,
+ ch1: Option>,
+ ch2: Option>,
+ ch3: Option>,
) -> Self {
- into_ref!(_pwm);
-
let r = T::regs();
for (i, ch) in [&ch0, &ch1, &ch2, &ch3].into_iter().enumerate() {
@@ -896,7 +859,7 @@ pub(crate) trait SealedInstance {
/// PWM peripheral instance.
#[allow(private_bounds)]
-pub trait Instance: Peripheral + SealedInstance + 'static {
+pub trait Instance: SealedInstance + PeripheralType + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs
index efd2a134c..69bfab0bb 100644
--- a/embassy-nrf/src/qdec.rs
+++ b/embassy-nrf/src/qdec.rs
@@ -6,18 +6,18 @@ use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
-use embassy_hal_internal::{into_ref, PeripheralRef};
+use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::gpio::vals as gpiovals;
use crate::pac::qdec::vals;
-use crate::{interrupt, pac, Peripheral};
+use crate::{interrupt, pac};
/// Quadrature decoder driver.
pub struct Qdec<'d, T: Instance> {
- _p: PeripheralRef<'d, T>,
+ _p: Peri<'d, T>,
}
/// QDEC config
@@ -62,34 +62,32 @@ impl interrupt::typelevel::Handler for InterruptHandl
impl<'d, T: Instance> Qdec<'d, T> {
/// Create a new QDEC.
pub fn new(
- qdec: impl Peripheral + 'd,
+ qdec: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- a: impl Peripheral + 'd,
- b: impl Peripheral + 'd,
+ a: Peri<'d, impl GpioPin>,
+ b: Peri<'d, impl GpioPin>,
config: Config,
) -> Self {
- into_ref!(qdec, a, b);
- Self::new_inner(qdec, a.map_into(), b.map_into(), None, config)
+ Self::new_inner(qdec, a.into(), b.into(), None, config)
}
/// Create a new QDEC, with a pin for LED output.
pub fn new_with_led(
- qdec: impl Peripheral + 'd,
+ qdec: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- a: impl Peripheral + 'd,
- b: impl Peripheral + 'd,
- led: impl Peripheral + 'd,
+ a: Peri<'d, impl GpioPin>,
+ b: Peri<'d, impl GpioPin>,
+ led: Peri<'d, impl GpioPin>,
config: Config,
) -> Self {
- into_ref!(qdec, a, b, led);
- Self::new_inner(qdec, a.map_into(), b.map_into(), Some(led.map_into()), config)
+ Self::new_inner(qdec, a.into(), b.into(), Some(led.into()), config)
}
fn new_inner(
- p: PeripheralRef<'d, T>,
- a: PeripheralRef<'d, AnyPin>,
- b: PeripheralRef<'d, AnyPin>,
- led: Option>,
+ p: Peri<'d, T>,
+ a: Peri<'d, AnyPin>,
+ b: Peri<'d, AnyPin>,
+ led: Option>,
config: Config,
) -> Self {
let r = T::regs();
@@ -272,7 +270,7 @@ pub(crate) trait SealedInstance {
/// qdec peripheral instance.
#[allow(private_bounds)]
-pub trait Instance: Peripheral + SealedInstance + 'static + Send {
+pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs
index 17e127700..e6e829f6e 100755
--- a/embassy-nrf/src/qspi.rs
+++ b/embassy-nrf/src/qspi.rs
@@ -8,7 +8,7 @@ use core::ptr;
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
-use embassy_hal_internal::{into_ref, PeripheralRef};
+use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
@@ -19,7 +19,7 @@ use crate::pac::qspi::vals;
pub use crate::pac::qspi::vals::{
Addrmode as AddressMode, Ppsize as WritePageSize, Readoc as ReadOpcode, Spimode as SpiMode, Writeoc as WriteOpcode,
};
-use crate::{interrupt, pac, Peripheral};
+use crate::{interrupt, pac};
/// Deep power-down config.
pub struct DeepPowerDownConfig {
@@ -139,7 +139,7 @@ impl interrupt::typelevel::Handler for InterruptHandl
/// QSPI flash driver.
pub struct Qspi<'d, T: Instance> {
- _peri: PeripheralRef<'d, T>,
+ _peri: Peri<'d, T>,
dpm_enabled: bool,
capacity: u32,
}
@@ -147,18 +147,16 @@ pub struct Qspi<'d, T: Instance> {
impl<'d, T: Instance> Qspi<'d, T> {
/// Create a new QSPI driver.
pub fn new(
- qspi: impl Peripheral + 'd,
+ qspi: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding> + 'd,
- sck: impl Peripheral + 'd,
- csn: impl Peripheral + 'd,
- io0: impl Peripheral + 'd,
- io1: impl Peripheral + 'd,
- io2: impl Peripheral + 'd,
- io3: impl Peripheral + 'd,
+ sck: Peri<'d, impl GpioPin>,
+ csn: Peri<'d, impl GpioPin>,
+ io0: Peri<'d, impl GpioPin>,
+ io1: Peri<'d, impl GpioPin>,
+ io2: Peri<'d, impl GpioPin>,
+ io3: Peri<'d, impl GpioPin>,
config: Config,
) -> Self {
- into_ref!(qspi, sck, csn, io0, io1, io2, io3);
-
let r = T::regs();
macro_rules! config_pin {
@@ -664,7 +662,7 @@ pub(crate) trait SealedInstance {
/// QSPI peripheral instance.
#[allow(private_bounds)]
-pub trait Instance: Peripheral + SealedInstance + 'static + Send {
+pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index 682ca1c79..d42bbe5f6 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -5,7 +5,6 @@ use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
-use embassy_hal_internal::{into_ref, PeripheralRef};
pub use pac::radio::vals::Mode;
#[cfg(not(feature = "_nrf51"))]
use pac::radio::vals::Plen as PreambleLength;
@@ -15,20 +14,19 @@ use crate::pac::radio::vals;
use crate::radio::*;
pub use crate::radio::{Error, TxPower};
use crate::util::slice_in_ram_or;
+use crate::Peri;
/// Radio driver.
pub struct Radio<'d, T: Instance> {
- _p: PeripheralRef<'d, T>,
+ _p: Peri<'d, T>,
}
impl<'d, T: Instance> Radio<'d, T> {
/// Create a new radio driver.
pub fn new(
- radio: impl Peripheral + 'd,
+ radio: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding> + 'd,
) -> Self {
- into_ref!(radio);
-
let r = T::regs();
r.pcnf1().write(|w| {
diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs
index 083842f4a..2f0bcbe04 100644
--- a/embassy-nrf/src/radio/ieee802154.rs
+++ b/embassy-nrf/src/radio/ieee802154.rs
@@ -4,13 +4,12 @@ use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
-use embassy_hal_internal::{into_ref, PeripheralRef};
use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower};
use crate::interrupt::typelevel::Interrupt;
use crate::interrupt::{self};
use crate::pac::radio::vals;
-use crate::Peripheral;
+use crate::Peri;
/// Default (IEEE compliant) Start of Frame Delimiter
pub const DEFAULT_SFD: u8 = 0xA7;
@@ -33,18 +32,16 @@ pub enum Cca {
/// IEEE 802.15.4 radio driver.
pub struct Radio<'d, T: Instance> {
- _p: PeripheralRef<'d, T>,
+ _p: Peri<'d, T>,
needs_enable: bool,
}
impl<'d, T: Instance> Radio<'d, T> {
/// Create a new IEEE 802.15.4 radio driver.
pub fn new(
- radio: impl Peripheral + 'd,
+ radio: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding> + 'd,
) -> Self {
- into_ref!(radio);
-
let r = T::regs();
// Disable and enable to reset peripheral
diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs
index 251f37d3d..982436266 100644
--- a/embassy-nrf/src/radio/mod.rs
+++ b/embassy-nrf/src/radio/mod.rs
@@ -19,11 +19,12 @@ pub mod ieee802154;
use core::marker::PhantomData;
+use embassy_hal_internal::PeripheralType;
use embassy_sync::waitqueue::AtomicWaker;
use pac::radio::vals::State as RadioState;
pub use pac::radio::vals::Txpower as TxPower;
-use crate::{interrupt, pac, Peripheral};
+use crate::{interrupt, pac};
/// RADIO error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -94,7 +95,7 @@ macro_rules! impl_radio {
/// Radio peripheral instance.
#[allow(private_bounds)]
-pub trait Instance: Peripheral + SealedInstance + 'static + Send {
+pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs
index 7a98ab2fb..e75ffda00 100644
--- a/embassy-nrf/src/rng.rs
+++ b/embassy-nrf/src/rng.rs
@@ -10,11 +10,11 @@ use core::task::Poll;
use critical_section::{CriticalSection, Mutex};
use embassy_hal_internal::drop::OnDrop;
-use embassy_hal_internal::{into_ref, PeripheralRef};
+use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::WakerRegistration;
use crate::interrupt::typelevel::Interrupt;
-use crate::{interrupt, pac, Peripheral};
+use crate::{interrupt, pac};
/// Interrupt handler.
pub struct InterruptHandler | | |