1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
#![no_std]
// TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs
// are complete prior to release.
#![allow(clippy::missing_safety_doc)]
pub mod clocks; // still provide clock helpers
pub mod gpio;
pub mod pins; // pin mux helpers
pub mod adc;
pub mod config;
pub mod interrupt;
pub mod lpuart;
pub mod ostimer;
pub mod rtc;
embassy_hal_internal::peripherals!(PORT1, PORT2, PORT3, LPUART2, OSTIMER0, GPIO, PIO2_2, PIO2_3, GPIO3, RTC0, ADC1,);
/// Get access to the PAC Peripherals for low-level register access.
/// This is a lazy-initialized singleton that can be called after init().
#[allow(static_mut_refs)]
pub fn pac() -> &'static pac::Peripherals {
// SAFETY: We only call this after init(), and the PAC is a singleton.
// The embassy peripheral tokens ensure we don't have multiple mutable accesses.
unsafe {
static mut PAC_INSTANCE: Option<pac::Peripherals> = None;
if PAC_INSTANCE.is_none() {
PAC_INSTANCE = Some(pac::Peripherals::steal());
}
PAC_INSTANCE.as_ref().unwrap()
}
}
// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it.
// Re-export interrupt traits and types
pub use adc::Adc1 as Adc1Token;
pub use gpio::pins::*;
pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output};
pub use interrupt::InterruptExt;
#[cfg(feature = "unstable-pac")]
pub use mcxa_pac as pac;
#[cfg(not(feature = "unstable-pac"))]
pub(crate) use mcxa_pac as pac;
pub use ostimer::Ostimer0 as Ostimer0Token;
pub use rtc::Rtc0 as Rtc0Token;
/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
#[allow(unused_variables)]
pub fn init(cfg: crate::config::Config) -> Peripherals {
let peripherals = Peripherals::take();
// Apply user-configured priority early; enabling is left to examples/apps
crate::interrupt::OS_EVENT.set_priority(cfg.time_interrupt_priority);
// Apply user-configured priority early; enabling is left to examples/apps
crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);
// Apply user-configured priority early; enabling is left to examples/apps
crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority);
peripherals
}
// /// Optional hook called by cortex-m-rt before RAM init.
// /// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state
// /// left by soft resets/debug sessions.
// ///
// /// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor'
// /// feature is incompatible with our setup because it expects __vector_table to be
// /// defined differently than how our RAM-based linker script arranges it.
// #[no_mangle]
// pub unsafe extern "C" fn __pre_init() {
// // Set the VTOR to point to the interrupt vector table in RAM
// // This is required since code runs from RAM on this MCU
// crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32);
// // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs.
// let nvic = &*cortex_m::peripheral::NVIC::PTR;
// for i in 0..4 {
// // 4 words x 32 = 128 IRQs
// nvic.icer[i].write(0xFFFF_FFFF);
// nvic.icpr[i].write(0xFFFF_FFFF);
// }
// // Do NOT touch peripheral registers here: clocks may be off and accesses can fault.
// crate::interrupt::clear_default_handler_snapshot();
// }
/// Internal helper to dispatch a type-level interrupt handler.
#[inline(always)]
#[doc(hidden)]
pub unsafe fn __handle_interrupt<T, H>()
where
T: crate::interrupt::typelevel::Interrupt,
H: crate::interrupt::typelevel::Handler<T>,
{
H::on_interrupt();
}
/// Macro to bind interrupts to handlers, similar to embassy-imxrt.
///
/// Example:
/// - Bind OS_EVENT to the OSTIMER time-driver handler
/// bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; });
#[macro_export]
macro_rules! bind_interrupts {
($(#[$attr:meta])* $vis:vis struct $name:ident {
$(
$(#[cfg($cond_irq:meta)])?
$irq:ident => $(
$(#[cfg($cond_handler:meta)])?
$handler:ty
),*;
)*
}) => {
#[derive(Copy, Clone)]
$(#[$attr])*
$vis struct $name;
$(
#[allow(non_snake_case)]
#[no_mangle]
$(#[cfg($cond_irq)])?
unsafe extern "C" fn $irq() {
unsafe {
$(
$(#[cfg($cond_handler)])?
<$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
)*
}
}
$(#[cfg($cond_irq)])?
$crate::bind_interrupts!(@inner
$(
$(#[cfg($cond_handler)])?
unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
)*
);
)*
};
(@inner $($t:tt)*) => {
$($t)*
}
}
|