aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adc.rs388
-rw-r--r--src/baremetal/mod.rs6
-rw-r--r--src/board.rs14
-rw-r--r--src/clocks.rs134
-rw-r--r--src/config.rs20
-rw-r--r--src/gpio.rs244
-rw-r--r--src/interrupt.rs349
-rw-r--r--src/lib.rs142
-rw-r--r--src/lpuart/buffered.rs675
-rw-r--r--src/lpuart/mod.rs1201
-rw-r--r--src/main.rs18
-rw-r--r--src/ostimer.rs678
-rw-r--r--src/pins.rs123
-rw-r--r--src/reset.rs112
-rw-r--r--src/rtc.rs284
-rw-r--r--src/uart.rs306
16 files changed, 4670 insertions, 24 deletions
diff --git a/src/adc.rs b/src/adc.rs
new file mode 100644
index 000000000..d456971f7
--- /dev/null
+++ b/src/adc.rs
@@ -0,0 +1,388 @@
1//! ADC driver
2use core::sync::atomic::{AtomicBool, Ordering};
3
4use crate::pac;
5use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres};
6use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts};
7use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode};
8use crate::pac::adc1::ctrl::CalAvgs;
9use crate::pac::adc1::tctrl::{Tcmd, Tpri};
10
11type Regs = pac::adc1::RegisterBlock;
12
13static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false);
14// Token-based instance pattern like embassy-imxrt
15pub trait Instance {
16 fn ptr() -> *const Regs;
17}
18
19/// Token for ADC1
20pub type Adc1 = crate::peripherals::ADC1;
21impl Instance for crate::peripherals::ADC1 {
22 #[inline(always)]
23 fn ptr() -> *const Regs {
24 pac::Adc1::ptr()
25 }
26}
27
28// Also implement Instance for the Peri wrapper type
29impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> {
30 #[inline(always)]
31 fn ptr() -> *const Regs {
32 pac::Adc1::ptr()
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[repr(u8)]
38pub enum TriggerPriorityPolicy {
39 ConvPreemptImmediatelyNotAutoResumed = 0,
40 ConvPreemptSoftlyNotAutoResumed = 1,
41 ConvPreemptImmediatelyAutoRestarted = 4,
42 ConvPreemptSoftlyAutoRestarted = 5,
43 ConvPreemptImmediatelyAutoResumed = 12,
44 ConvPreemptSoftlyAutoResumed = 13,
45 ConvPreemptSubsequentlyNotAutoResumed = 2,
46 ConvPreemptSubsequentlyAutoRestarted = 6,
47 ConvPreemptSubsequentlyAutoResumed = 14,
48 TriggerPriorityExceptionDisabled = 16,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct LpadcConfig {
53 pub enable_in_doze_mode: bool,
54 pub conversion_average_mode: CalAvgs,
55 pub enable_analog_preliminary: bool,
56 pub power_up_delay: u8,
57 pub reference_voltage_source: Refsel,
58 pub power_level_mode: Pwrsel,
59 pub trigger_priority_policy: TriggerPriorityPolicy,
60 pub enable_conv_pause: bool,
61 pub conv_pause_delay: u16,
62 pub fifo_watermark: u8,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct ConvCommandConfig {
67 pub sample_channel_mode: Ctype,
68 pub channel_number: Adch,
69 pub chained_next_command_number: Next,
70 pub enable_auto_channel_increment: bool,
71 pub loop_count: u8,
72 pub hardware_average_mode: Avgs,
73 pub sample_time_mode: Sts,
74 pub hardware_compare_mode: Cmpen,
75 pub hardware_compare_value_high: u32,
76 pub hardware_compare_value_low: u32,
77 pub conversion_resolution_mode: Mode,
78 pub enable_wait_trigger: bool,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct ConvTriggerConfig {
83 pub target_command_id: Tcmd,
84 pub delay_power: u8,
85 pub priority: Tpri,
86 pub enable_hardware_trigger: bool,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub struct ConvResult {
91 pub command_id_source: u32,
92 pub loop_count_index: u32,
93 pub trigger_id_source: u32,
94 pub conv_value: u16,
95}
96
97pub struct Adc<I: Instance> {
98 _inst: core::marker::PhantomData<I>,
99}
100
101impl<I: Instance> Adc<I> {
102 /// initialize ADC
103 pub fn new(_inst: impl Instance, config: LpadcConfig) -> Self {
104 let adc = unsafe { &*I::ptr() };
105
106 /* Reset the module. */
107 adc.ctrl().modify(|_, w| w.rst().held_in_reset());
108 adc.ctrl().modify(|_, w| w.rst().released_from_reset());
109
110 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
111
112 /* Disable the module before setting configuration. */
113 adc.ctrl().modify(|_, w| w.adcen().disabled());
114
115 /* Configure the module generally. */
116 if config.enable_in_doze_mode {
117 adc.ctrl().modify(|_, w| w.dozen().enabled());
118 } else {
119 adc.ctrl().modify(|_, w| w.dozen().disabled());
120 }
121
122 /* Set calibration average mode. */
123 adc.ctrl()
124 .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode));
125
126 adc.cfg().write(|w| unsafe {
127 let w = if config.enable_analog_preliminary {
128 w.pwren().pre_enabled()
129 } else {
130 w
131 };
132
133 w.pudly()
134 .bits(config.power_up_delay)
135 .refsel()
136 .variant(config.reference_voltage_source)
137 .pwrsel()
138 .variant(config.power_level_mode)
139 .tprictrl()
140 .variant(match config.trigger_priority_policy {
141 TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed
142 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
143 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority,
144 TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed
145 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
146 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority,
147 _ => Tprictrl::AbortCurrentOnPriority,
148 })
149 .tres()
150 .variant(match config.trigger_priority_policy {
151 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted
152 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
153 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
154 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
155 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
156 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled,
157 _ => Tres::Disabled,
158 })
159 .tcmdres()
160 .variant(match config.trigger_priority_policy {
161 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
162 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
163 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed
164 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled,
165 _ => Tcmdres::Disabled,
166 })
167 .hpt_exdi()
168 .variant(match config.trigger_priority_policy {
169 TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled,
170 _ => HptExdi::Enabled,
171 })
172 });
173
174 if config.enable_conv_pause {
175 adc.pause()
176 .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) });
177 } else {
178 adc.pause().write(|w| unsafe { w.bits(0) });
179 }
180
181 adc.fctrl0()
182 .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) });
183
184 // Enable ADC
185 adc.ctrl().modify(|_, w| w.adcen().enabled());
186
187 Self {
188 _inst: core::marker::PhantomData,
189 }
190 }
191
192 pub fn deinit(&self) {
193 let adc = unsafe { &*I::ptr() };
194 adc.ctrl().modify(|_, w| w.adcen().disabled());
195 }
196
197 pub fn get_default_config() -> LpadcConfig {
198 LpadcConfig {
199 enable_in_doze_mode: true,
200 conversion_average_mode: CalAvgs::NoAverage,
201 enable_analog_preliminary: false,
202 power_up_delay: 0x80,
203 reference_voltage_source: Refsel::Option1,
204 power_level_mode: Pwrsel::Lowest,
205 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
206 enable_conv_pause: false,
207 conv_pause_delay: 0,
208 fifo_watermark: 0,
209 }
210 }
211
212 pub fn do_offset_calibration(&self) {
213 let adc = unsafe { &*I::ptr() };
214 // Enable calibration mode
215 adc.ctrl()
216 .modify(|_, w| w.calofs().offset_calibration_request_pending());
217
218 // Wait for calibration to complete (polling status register)
219 while adc.stat().read().cal_rdy().is_not_set() {}
220 }
221
222 pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 {
223 let mut gcra_array = [0u32; 17];
224 let mut gcalr: u32 = 0;
225
226 for i in (1..=17).rev() {
227 let shift = 16 - (i - 1);
228 let step = 1.0 / (1u32 << shift) as f32;
229 let tmp = (gain_adjustment / step) as u32;
230 gcra_array[i - 1] = tmp;
231 gain_adjustment = gain_adjustment - tmp as f32 * step;
232 }
233
234 for i in (1..=17).rev() {
235 gcalr += gcra_array[i - 1] << (i - 1);
236 }
237 gcalr
238 }
239
240 pub fn do_auto_calibration(&self) {
241 let adc = unsafe { &*I::ptr() };
242 adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending());
243
244 while adc.gcc0().read().rdy().is_gain_cal_not_valid() {}
245
246 let mut gcca = adc.gcc0().read().gain_cal().bits() as u32;
247 if gcca & (((0xFFFF >> 0) + 1) >> 1) != 0 {
248 gcca |= !0xFFFF;
249 }
250
251 let gcra = 131072.0 / (131072.0 - gcca as f32);
252
253 // Write to GCR0
254 adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) });
255
256 adc.gcr0().modify(|_, w| w.rdy().set_bit());
257
258 // Wait for calibration to complete (polling status register)
259 while adc.stat().read().cal_rdy().is_not_set() {}
260 }
261
262 pub fn do_software_trigger(&self, trigger_id_mask: u32) {
263 let adc = unsafe { &*I::ptr() };
264 adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) });
265 }
266
267 pub fn get_default_conv_command_config(&self) -> ConvCommandConfig {
268 ConvCommandConfig {
269 sample_channel_mode: Ctype::SingleEndedASideChannel,
270 channel_number: Adch::SelectCh0,
271 chained_next_command_number: Next::NoNextCmdTerminateOnFinish,
272 enable_auto_channel_increment: false,
273 loop_count: 0,
274 hardware_average_mode: Avgs::NoAverage,
275 sample_time_mode: Sts::Sample3p5,
276 hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult,
277 hardware_compare_value_high: 0,
278 hardware_compare_value_low: 0,
279 conversion_resolution_mode: Mode::Data12Bits,
280 enable_wait_trigger: false,
281 }
282 }
283
284 //TBD Need to add cmdlx and cmdhx with x {2..7}
285 pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) {
286 let adc = unsafe { &*I::ptr() };
287
288 match index {
289 1 => {
290 adc.cmdl1().write(|w| {
291 w.adch()
292 .variant(config.channel_number)
293 .mode()
294 .variant(config.conversion_resolution_mode)
295 });
296 adc.cmdh1().write(|w| unsafe {
297 w.next()
298 .variant(config.chained_next_command_number)
299 .loop_()
300 .bits(config.loop_count)
301 .avgs()
302 .variant(config.hardware_average_mode)
303 .sts()
304 .variant(config.sample_time_mode)
305 .cmpen()
306 .variant(config.hardware_compare_mode);
307 if config.enable_wait_trigger {
308 w.wait_trig().enabled();
309 }
310 if config.enable_auto_channel_increment {
311 w.lwi().enabled();
312 }
313 w
314 });
315 }
316 _ => panic!("Invalid command index: must be between 1 and 7"),
317 }
318 }
319
320 pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig {
321 ConvTriggerConfig {
322 target_command_id: Tcmd::NotValid,
323 delay_power: 0,
324 priority: Tpri::HighestPriority,
325 enable_hardware_trigger: false,
326 }
327 }
328
329 pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) {
330 let adc = unsafe { &*I::ptr() };
331 let tctrl = &adc.tctrl(trigger_id);
332
333 tctrl.write(|w| unsafe {
334 let w = w.tcmd().variant(config.target_command_id);
335 let w = w.tdly().bits(config.delay_power);
336 w.tpri().variant(config.priority);
337 if config.enable_hardware_trigger {
338 w.hten().enabled()
339 } else {
340 w
341 }
342 });
343 }
344
345 pub fn do_reset_fifo(&self) {
346 let adc = unsafe { &*I::ptr() };
347 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
348 }
349
350 pub fn enable_interrupt(&self, mask: u32) {
351 let adc = unsafe { &*I::ptr() };
352 adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
353 INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst);
354 }
355
356 pub fn is_interrupt_triggered(&self) -> bool {
357 INTERRUPT_TRIGGERED.load(Ordering::Relaxed)
358 }
359}
360
361pub fn get_conv_result() -> Option<ConvResult> {
362 let adc = unsafe { &*pac::Adc1::ptr() };
363 let fifo = adc.resfifo0().read().bits();
364 const VALID_MASK: u32 = 1 << 31;
365 if fifo & VALID_MASK == 0 {
366 return None;
367 }
368
369 Some(ConvResult {
370 command_id_source: (fifo >> 24) & 0x0F,
371 loop_count_index: (fifo >> 20) & 0x0F,
372 trigger_id_source: (fifo >> 16) & 0x0F,
373 conv_value: (fifo & 0xFFFF) as u16,
374 })
375}
376
377pub fn on_interrupt() {
378 if get_conv_result().is_some() {
379 INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst);
380 }
381}
382
383pub struct AdcHandler;
384impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::ADC1> for AdcHandler {
385 unsafe fn on_interrupt() {
386 on_interrupt();
387 }
388}
diff --git a/src/baremetal/mod.rs b/src/baremetal/mod.rs
deleted file mode 100644
index c03b9538b..000000000
--- a/src/baremetal/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
1use core::panic::PanicInfo;
2
3#[panic_handler]
4fn panic(_info: &PanicInfo) -> ! {
5 loop {}
6}
diff --git a/src/board.rs b/src/board.rs
new file mode 100644
index 000000000..fa679e82c
--- /dev/null
+++ b/src/board.rs
@@ -0,0 +1,14 @@
1use crate::{clocks, pac, pins};
2
3/// Initialize clocks and pin muxing for UART2 debug console.
4pub unsafe fn init_uart2(p: &pac::Peripherals) {
5 clocks::ensure_frolf_running(p);
6 clocks::enable_uart2_port2(p);
7 pins::configure_uart2_pins_port2();
8 clocks::select_uart2_clock(p);
9}
10
11/// Initialize clocks for the LED GPIO/PORT used by the blink example.
12pub unsafe fn init_led(p: &pac::Peripherals) {
13 clocks::enable_led_port(p);
14}
diff --git a/src/clocks.rs b/src/clocks.rs
new file mode 100644
index 000000000..65a17cef6
--- /dev/null
+++ b/src/clocks.rs
@@ -0,0 +1,134 @@
1//! Clock control helpers (no magic numbers, PAC field access only).
2//! Provides reusable gate abstractions for peripherals used by the examples.
3use crate::pac;
4
5/// Trait describing an AHB clock gate that can be toggled through MRCC.
6pub trait Gate {
7 /// Enable the clock gate.
8 unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock);
9
10 /// Return whether the clock gate is currently enabled.
11 fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool;
12}
13
14/// Enable a clock gate for the given peripheral set.
15#[inline]
16pub unsafe fn enable<G: Gate>(peripherals: &pac::Peripherals) {
17 let mrcc = &peripherals.mrcc0;
18 G::enable(mrcc);
19 while !G::is_enabled(mrcc) {}
20 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
21}
22
23/// Check whether a gate is currently enabled.
24#[inline]
25pub fn is_enabled<G: Gate>(peripherals: &pac::Peripherals) -> bool {
26 G::is_enabled(&peripherals.mrcc0)
27}
28
29macro_rules! impl_cc_gate {
30 ($name:ident, $reg:ident, $field:ident) => {
31 pub struct $name;
32
33 impl Gate for $name {
34 #[inline]
35 unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock) {
36 mrcc.$reg().modify(|_, w| w.$field().enabled());
37 }
38
39 #[inline]
40 fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool {
41 mrcc.$reg().read().$field().is_enabled()
42 }
43 }
44 };
45}
46
47pub mod gate {
48 use super::*;
49
50 impl_cc_gate!(Port2, mrcc_glb_cc1, port2);
51 impl_cc_gate!(Port3, mrcc_glb_cc1, port3);
52 impl_cc_gate!(Ostimer0, mrcc_glb_cc1, ostimer0);
53 impl_cc_gate!(Lpuart2, mrcc_glb_cc0, lpuart2);
54 impl_cc_gate!(Gpio3, mrcc_glb_cc2, gpio3);
55 impl_cc_gate!(Port1, mrcc_glb_cc1, port1);
56 impl_cc_gate!(Adc1, mrcc_glb_cc1, adc1);
57}
58
59/// Convenience helper enabling the PORT2 and LPUART2 gates required for the debug UART.
60pub unsafe fn enable_uart2_port2(peripherals: &pac::Peripherals) {
61 enable::<gate::Port2>(peripherals);
62 enable::<gate::Lpuart2>(peripherals);
63}
64
65/// Convenience helper enabling the PORT3 and GPIO3 gates used by the LED in the examples.
66pub unsafe fn enable_led_port(peripherals: &pac::Peripherals) {
67 enable::<gate::Port3>(peripherals);
68 enable::<gate::Gpio3>(peripherals);
69}
70
71/// Convenience helper enabling the OSTIMER0 clock gate.
72pub unsafe fn enable_ostimer0(peripherals: &pac::Peripherals) {
73 enable::<gate::Ostimer0>(peripherals);
74}
75
76pub unsafe fn select_uart2_clock(peripherals: &pac::Peripherals) {
77 // Use FRO_LF_DIV (already running) MUX=0 DIV=0
78 let mrcc = &peripherals.mrcc0;
79 mrcc.mrcc_lpuart2_clksel().write(|w| w.mux().clkroot_func_0());
80 mrcc.mrcc_lpuart2_clkdiv().write(|w| unsafe { w.bits(0) });
81}
82
83pub unsafe fn ensure_frolf_running(peripherals: &pac::Peripherals) {
84 // Ensure FRO_LF divider clock is running (reset default HALT=1 stops it)
85 let sys = &peripherals.syscon;
86 sys.frolfdiv().modify(|_, w| {
87 // DIV defaults to 0; keep it explicit and clear HALT
88 unsafe { w.div().bits(0) }.halt().run()
89 });
90}
91
92/// Compute the FRO_LF_DIV output frequency currently selected for LPUART2.
93/// Assumes select_uart2_clock() has chosen MUX=0 (FRO_LF_DIV) and DIV is set in SYSCON.FRO_LF_DIV.
94pub unsafe fn uart2_src_hz(peripherals: &pac::Peripherals) -> u32 {
95 // SYSCON.FRO_LF_DIV: DIV field is simple divider: freq_out = 12_000_000 / (DIV+1) for many NXP parts.
96 // On MCXA276 FRO_LF base is 12 MHz; our init keeps DIV=0, so result=12_000_000.
97 // Read it anyway for future generality.
98 let div = peripherals.syscon.frolfdiv().read().div().bits() as u32;
99 let base = 12_000_000u32;
100 base / (div + 1)
101}
102
103/// Enable clock gate and release reset for OSTIMER0.
104/// Select OSTIMER0 clock source = 1 MHz root (working bring-up configuration).
105pub unsafe fn select_ostimer0_clock_1m(peripherals: &pac::Peripherals) {
106 let mrcc = &peripherals.mrcc0;
107 mrcc.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
108}
109
110pub unsafe fn init_fro16k(peripherals: &pac::Peripherals) {
111 let vbat = &peripherals.vbat0;
112 // Enable FRO16K oscillator
113 vbat.froctla().modify(|_, w| w.fro_en().set_bit());
114
115 // Lock the control register
116 vbat.frolcka().modify(|_, w| w.lock().set_bit());
117
118 // Enable clock outputs to both VSYS and VDD_CORE domains
119 // Bit 0: clk_16k0 to VSYS domain
120 // Bit 1: clk_16k1 to VDD_CORE domain
121 vbat.froclke().modify(|_, w| unsafe { w.clke().bits(0x3) });
122}
123
124pub unsafe fn enable_adc(peripherals: &pac::Peripherals) {
125 enable::<gate::Port1>(peripherals);
126 enable::<gate::Adc1>(peripherals);
127}
128
129pub unsafe fn select_adc_clock(peripherals: &pac::Peripherals) {
130 // Use FRO_LF_DIV (already running) MUX=0 DIV=0
131 let mrcc = &peripherals.mrcc0;
132 mrcc.mrcc_adc_clksel().write(|w| w.mux().clkroot_func_0());
133 mrcc.mrcc_adc_clkdiv().write(|w| unsafe { w.bits(0) });
134}
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 000000000..93aed5a99
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,20 @@
1// HAL configuration (minimal), mirroring embassy-imxrt style
2
3use crate::interrupt::Priority;
4
5#[non_exhaustive]
6pub struct Config {
7 pub time_interrupt_priority: Priority,
8 pub rtc_interrupt_priority: Priority,
9 pub adc_interrupt_priority: Priority,
10}
11
12impl Default for Config {
13 fn default() -> Self {
14 Self {
15 time_interrupt_priority: Priority::from(0),
16 rtc_interrupt_priority: Priority::from(0),
17 adc_interrupt_priority: Priority::from(0),
18 }
19 }
20}
diff --git a/src/gpio.rs b/src/gpio.rs
new file mode 100644
index 000000000..1e7214b28
--- /dev/null
+++ b/src/gpio.rs
@@ -0,0 +1,244 @@
1//! GPIO driver built around a type-erased `Flex` pin, similar to other Embassy HALs.
2//! The exported `Output`/`Input` drivers own a `Flex` so they no longer depend on the
3//! concrete pin type.
4
5use core::marker::PhantomData;
6
7use crate::{pac, pins as pin_config};
8
9/// Logical level for GPIO pins.
10#[derive(Copy, Clone, Eq, PartialEq, Debug)]
11pub enum Level {
12 Low,
13 High,
14}
15
16pub type Gpio = crate::peripherals::GPIO;
17
18/// Type-erased representation of a GPIO pin.
19#[derive(Copy, Clone)]
20pub struct AnyPin {
21 port: u8,
22 pin: u8,
23 gpio: *const pac::gpio0::RegisterBlock,
24}
25
26impl AnyPin {
27 /// Create an `AnyPin` from raw components.
28 pub fn new(port: u8, pin: u8, gpio: *const pac::gpio0::RegisterBlock) -> Self {
29 Self { port, pin, gpio }
30 }
31
32 #[inline(always)]
33 fn mask(&self) -> u32 {
34 1u32 << self.pin
35 }
36
37 #[inline(always)]
38 fn gpio(&self) -> &'static pac::gpio0::RegisterBlock {
39 unsafe { &*self.gpio }
40 }
41
42 #[inline(always)]
43 pub fn port_index(&self) -> u8 {
44 self.port
45 }
46
47 #[inline(always)]
48 pub fn pin_index(&self) -> u8 {
49 self.pin
50 }
51}
52
53/// Type-level trait implemented by concrete pin ZSTs.
54pub trait PinId {
55 fn port_index() -> u8;
56 fn pin_index() -> u8;
57 fn gpio_ptr() -> *const pac::gpio0::RegisterBlock;
58
59 fn set_mux_gpio() {
60 unsafe { pin_config::set_pin_mux_gpio(Self::port_index(), Self::pin_index()) }
61 }
62
63 fn degrade() -> AnyPin {
64 AnyPin::new(Self::port_index(), Self::pin_index(), Self::gpio_ptr())
65 }
66}
67
68pub mod pins {
69 use super::{pac, AnyPin, PinId};
70
71 macro_rules! define_pin {
72 ($Name:ident, $port:literal, $pin:literal, $GpioBlk:ident) => {
73 pub struct $Name;
74 impl super::PinId for $Name {
75 #[inline(always)]
76 fn port_index() -> u8 {
77 $port
78 }
79 #[inline(always)]
80 fn pin_index() -> u8 {
81 $pin
82 }
83 #[inline(always)]
84 fn gpio_ptr() -> *const pac::gpio0::RegisterBlock {
85 pac::$GpioBlk::ptr()
86 }
87 }
88
89 impl $Name {
90 /// Convenience helper to obtain a type-erased handle to this pin.
91 pub fn degrade() -> AnyPin {
92 <Self as PinId>::degrade()
93 }
94
95 pub fn set_mux_gpio() {
96 <Self as PinId>::set_mux_gpio()
97 }
98 }
99 };
100 }
101
102 // Extend this list as more pins are needed.
103 define_pin!(PIO3_18, 3, 18, Gpio3);
104}
105
106/// A flexible pin that can be configured as input or output.
107pub struct Flex<'d> {
108 pin: AnyPin,
109 _marker: PhantomData<&'d mut ()>,
110}
111
112impl<'d> Flex<'d> {
113 pub fn new(pin: AnyPin) -> Self {
114 Self {
115 pin,
116 _marker: PhantomData,
117 }
118 }
119
120 #[inline(always)]
121 fn gpio(&self) -> &'static pac::gpio0::RegisterBlock {
122 self.pin.gpio()
123 }
124
125 #[inline(always)]
126 fn mask(&self) -> u32 {
127 self.pin.mask()
128 }
129
130 pub fn set_as_input(&mut self) {
131 let mask = self.mask();
132 let gpio = self.gpio();
133 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
134 }
135
136 pub fn set_as_output(&mut self) {
137 let mask = self.mask();
138 let gpio = self.gpio();
139 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
140 }
141
142 pub fn set_high(&mut self) {
143 self.gpio().psor().write(|w| unsafe { w.bits(self.mask()) });
144 }
145
146 pub fn set_low(&mut self) {
147 self.gpio().pcor().write(|w| unsafe { w.bits(self.mask()) });
148 }
149
150 pub fn set_level(&mut self, level: Level) {
151 match level {
152 Level::High => self.set_high(),
153 Level::Low => self.set_low(),
154 }
155 }
156
157 pub fn toggle(&mut self) {
158 self.gpio().ptor().write(|w| unsafe { w.bits(self.mask()) });
159 }
160
161 pub fn is_high(&self) -> bool {
162 (self.gpio().pdir().read().bits() & self.mask()) != 0
163 }
164
165 pub fn is_low(&self) -> bool {
166 !self.is_high()
167 }
168}
169
170/// GPIO output driver that owns a `Flex` pin.
171pub struct Output<'d> {
172 flex: Flex<'d>,
173}
174
175impl<'d> Output<'d> {
176 pub fn new(pin: AnyPin, initial: Level) -> Self {
177 let mut flex = Flex::new(pin);
178 flex.set_level(initial);
179 flex.set_as_output();
180 Self { flex }
181 }
182
183 #[inline]
184 pub fn set_high(&mut self) {
185 self.flex.set_high();
186 }
187
188 #[inline]
189 pub fn set_low(&mut self) {
190 self.flex.set_low();
191 }
192
193 #[inline]
194 pub fn set_level(&mut self, level: Level) {
195 self.flex.set_level(level);
196 }
197
198 #[inline]
199 pub fn toggle(&mut self) {
200 self.flex.toggle();
201 }
202
203 #[inline]
204 pub fn is_set_high(&self) -> bool {
205 self.flex.is_high()
206 }
207
208 #[inline]
209 pub fn is_set_low(&self) -> bool {
210 !self.is_set_high()
211 }
212
213 /// Expose the inner `Flex` if callers need to reconfigure the pin.
214 pub fn into_flex(self) -> Flex<'d> {
215 self.flex
216 }
217}
218
219/// GPIO input driver that owns a `Flex` pin.
220pub struct Input<'d> {
221 flex: Flex<'d>,
222}
223
224impl<'d> Input<'d> {
225 pub fn new(pin: AnyPin) -> Self {
226 let mut flex = Flex::new(pin);
227 flex.set_as_input();
228 Self { flex }
229 }
230
231 #[inline]
232 pub fn is_high(&self) -> bool {
233 self.flex.is_high()
234 }
235
236 #[inline]
237 pub fn is_low(&self) -> bool {
238 self.flex.is_low()
239 }
240
241 pub fn into_flex(self) -> Flex<'d> {
242 self.flex
243 }
244}
diff --git a/src/interrupt.rs b/src/interrupt.rs
new file mode 100644
index 000000000..09d7acbef
--- /dev/null
+++ b/src/interrupt.rs
@@ -0,0 +1,349 @@
1//! Minimal interrupt helpers mirroring embassy-imxrt style for OS_EVENT and LPUART2.
2//! Type-level interrupt traits and bindings are provided by the
3//! `embassy_hal_internal::interrupt_mod!` macro via the generated module below.
4
5mod generated {
6 embassy_hal_internal::interrupt_mod!(OS_EVENT, LPUART2, RTC, ADC1);
7}
8
9use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
10
11pub use generated::interrupt::{typelevel, Priority};
12
13use crate::pac::Interrupt;
14
15/// Trait for configuring and controlling interrupts.
16///
17/// This trait provides a consistent interface for interrupt management across
18/// different interrupt sources, similar to embassy-imxrt's InterruptExt.
19pub trait InterruptExt {
20 /// Clear any pending interrupt in NVIC.
21 fn unpend(&self);
22
23 /// Set NVIC priority for this interrupt.
24 fn set_priority(&self, priority: Priority);
25
26 /// Enable this interrupt in NVIC.
27 ///
28 /// # Safety
29 /// This function is unsafe because it can enable interrupts that may not be
30 /// properly configured, potentially leading to undefined behavior.
31 unsafe fn enable(&self);
32
33 /// Disable this interrupt in NVIC.
34 ///
35 /// # Safety
36 /// This function is unsafe because disabling interrupts may leave the system
37 /// in an inconsistent state if the interrupt was expected to fire.
38 unsafe fn disable(&self);
39
40 /// Check if the interrupt is pending in NVIC.
41 fn is_pending(&self) -> bool;
42}
43
44#[derive(Clone, Copy, Debug, Default)]
45pub struct DefaultHandlerSnapshot {
46 pub vector: u16,
47 pub count: u32,
48 pub cfsr: u32,
49 pub hfsr: u32,
50 pub stacked_pc: u32,
51 pub stacked_lr: u32,
52 pub stacked_sp: u32,
53}
54
55static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0);
56static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0);
57static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0);
58static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0);
59static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0);
60static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0);
61static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0);
62
63#[inline]
64pub fn default_handler_snapshot() -> DefaultHandlerSnapshot {
65 DefaultHandlerSnapshot {
66 vector: LAST_DEFAULT_VECTOR.load(Ordering::Relaxed),
67 count: LAST_DEFAULT_COUNT.load(Ordering::Relaxed),
68 cfsr: LAST_DEFAULT_CFSR.load(Ordering::Relaxed),
69 hfsr: LAST_DEFAULT_HFSR.load(Ordering::Relaxed),
70 stacked_pc: LAST_DEFAULT_PC.load(Ordering::Relaxed),
71 stacked_lr: LAST_DEFAULT_LR.load(Ordering::Relaxed),
72 stacked_sp: LAST_DEFAULT_SP.load(Ordering::Relaxed),
73 }
74}
75
76#[inline]
77pub fn clear_default_handler_snapshot() {
78 LAST_DEFAULT_VECTOR.store(0, Ordering::Relaxed);
79 LAST_DEFAULT_COUNT.store(0, Ordering::Relaxed);
80 LAST_DEFAULT_CFSR.store(0, Ordering::Relaxed);
81 LAST_DEFAULT_HFSR.store(0, Ordering::Relaxed);
82 LAST_DEFAULT_PC.store(0, Ordering::Relaxed);
83 LAST_DEFAULT_LR.store(0, Ordering::Relaxed);
84 LAST_DEFAULT_SP.store(0, Ordering::Relaxed);
85}
86
87/// OS_EVENT interrupt helper with methods similar to embassy-imxrt's InterruptExt.
88pub struct OsEvent;
89pub const OS_EVENT: OsEvent = OsEvent;
90
91impl InterruptExt for OsEvent {
92 /// Clear any pending OS_EVENT in NVIC.
93 #[inline]
94 fn unpend(&self) {
95 cortex_m::peripheral::NVIC::unpend(Interrupt::OS_EVENT);
96 }
97
98 /// Set NVIC priority for OS_EVENT.
99 #[inline]
100 fn set_priority(&self, priority: Priority) {
101 unsafe {
102 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
103 nvic.set_priority(Interrupt::OS_EVENT, u8::from(priority));
104 }
105 }
106
107 /// Enable OS_EVENT in NVIC.
108 #[inline]
109 unsafe fn enable(&self) {
110 cortex_m::peripheral::NVIC::unmask(Interrupt::OS_EVENT);
111 }
112
113 /// Disable OS_EVENT in NVIC.
114 #[inline]
115 unsafe fn disable(&self) {
116 cortex_m::peripheral::NVIC::mask(Interrupt::OS_EVENT);
117 }
118
119 /// Check if OS_EVENT is pending in NVIC.
120 #[inline]
121 fn is_pending(&self) -> bool {
122 cortex_m::peripheral::NVIC::is_pending(Interrupt::OS_EVENT)
123 }
124}
125
126impl OsEvent {
127 /// Configure OS_EVENT interrupt for timer operation.
128 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
129 /// Also performs a software event to wake any pending WFE.
130 pub fn configure_for_timer(&self, priority: Priority) {
131 // Configure NVIC
132 self.unpend();
133 self.set_priority(priority);
134 unsafe {
135 self.enable();
136 }
137
138 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
139 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
140 unsafe {
141 cortex_m::interrupt::enable();
142 }
143
144 // Wake any executor WFE that might be sleeping when we armed the first deadline
145 cortex_m::asm::sev();
146 }
147}
148
149/// LPUART2 interrupt helper with methods similar to embassy-imxrt's InterruptExt.
150pub struct Lpuart2;
151pub const LPUART2: Lpuart2 = Lpuart2;
152
153impl InterruptExt for Lpuart2 {
154 /// Clear any pending LPUART2 in NVIC.
155 #[inline]
156 fn unpend(&self) {
157 cortex_m::peripheral::NVIC::unpend(Interrupt::LPUART2);
158 }
159
160 /// Set NVIC priority for LPUART2.
161 #[inline]
162 fn set_priority(&self, priority: Priority) {
163 unsafe {
164 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
165 nvic.set_priority(Interrupt::LPUART2, u8::from(priority));
166 }
167 }
168
169 /// Enable LPUART2 in NVIC.
170 #[inline]
171 unsafe fn enable(&self) {
172 cortex_m::peripheral::NVIC::unmask(Interrupt::LPUART2);
173 }
174
175 /// Disable LPUART2 in NVIC.
176 #[inline]
177 unsafe fn disable(&self) {
178 cortex_m::peripheral::NVIC::mask(Interrupt::LPUART2);
179 }
180
181 /// Check if LPUART2 is pending in NVIC.
182 #[inline]
183 fn is_pending(&self) -> bool {
184 cortex_m::peripheral::NVIC::is_pending(Interrupt::LPUART2)
185 }
186}
187
188impl Lpuart2 {
189 /// Configure LPUART2 interrupt for UART operation.
190 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
191 pub fn configure_for_uart(&self, priority: Priority) {
192 // Configure NVIC
193 self.unpend();
194 self.set_priority(priority);
195 unsafe {
196 self.enable();
197 }
198
199 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
200 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
201 unsafe {
202 cortex_m::interrupt::enable();
203 }
204 }
205
206 /// Install LPUART2 handler into the RAM vector table.
207 /// Safety: See `install_irq_handler`.
208 pub unsafe fn install_handler(&self, handler: unsafe extern "C" fn()) {
209 install_irq_handler(Interrupt::LPUART2, handler);
210 }
211}
212
213pub struct Rtc;
214pub const RTC: Rtc = Rtc;
215
216impl InterruptExt for Rtc {
217 /// Clear any pending RTC in NVIC.
218 #[inline]
219 fn unpend(&self) {
220 cortex_m::peripheral::NVIC::unpend(Interrupt::RTC);
221 }
222
223 /// Set NVIC priority for RTC.
224 #[inline]
225 fn set_priority(&self, priority: Priority) {
226 unsafe {
227 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
228 nvic.set_priority(Interrupt::RTC, u8::from(priority));
229 }
230 }
231
232 /// Enable RTC in NVIC.
233 #[inline]
234 unsafe fn enable(&self) {
235 cortex_m::peripheral::NVIC::unmask(Interrupt::RTC);
236 }
237
238 /// Disable RTC in NVIC.
239 #[inline]
240 unsafe fn disable(&self) {
241 cortex_m::peripheral::NVIC::mask(Interrupt::RTC);
242 }
243
244 /// Check if RTC is pending in NVIC.
245 #[inline]
246 fn is_pending(&self) -> bool {
247 cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC)
248 }
249}
250
251pub struct Adc;
252pub const ADC1: Adc = Adc;
253
254impl InterruptExt for Adc {
255 /// Clear any pending ADC1 in NVIC.
256 #[inline]
257 fn unpend(&self) {
258 cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1);
259 }
260
261 /// Set NVIC priority for ADC1.
262 #[inline]
263 fn set_priority(&self, priority: Priority) {
264 unsafe {
265 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
266 nvic.set_priority(Interrupt::ADC1, u8::from(priority));
267 }
268 }
269
270 /// Enable ADC1 in NVIC.
271 #[inline]
272 unsafe fn enable(&self) {
273 cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1);
274 }
275
276 /// Disable ADC1 in NVIC.
277 #[inline]
278 unsafe fn disable(&self) {
279 cortex_m::peripheral::NVIC::mask(Interrupt::ADC1);
280 }
281
282 /// Check if ADC1 is pending in NVIC.
283 #[inline]
284 fn is_pending(&self) -> bool {
285 cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1)
286 }
287}
288
289/// Set VTOR (Vector Table Offset) to a RAM-based vector table.
290/// Pass a pointer to the first word in the RAM table (stack pointer slot 0).
291/// Safety: Caller must ensure the RAM table is valid and aligned as required by the core.
292pub unsafe fn vtor_set_ram_vector_base(base: *const u32) {
293 core::ptr::write_volatile(0xE000_ED08 as *mut u32, base as u32);
294}
295
296/// Install an interrupt handler into the current VTOR-based vector table.
297/// This writes the function pointer at index 16 + irq number.
298/// Safety: Caller must ensure VTOR points at a writable RAM table and that `handler`
299/// has the correct ABI and lifetime.
300pub unsafe fn install_irq_handler(irq: Interrupt, handler: unsafe extern "C" fn()) {
301 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
302 let idx = 16 + (irq as isize as usize);
303 core::ptr::write_volatile(vtor_base.add(idx), handler as usize as u32);
304}
305
306impl OsEvent {
307 /// Convenience to install the OS_EVENT handler into the RAM vector table.
308 /// Safety: See `install_irq_handler`.
309 pub unsafe fn install_handler(&self, handler: extern "C" fn()) {
310 install_irq_handler(Interrupt::OS_EVENT, handler);
311 }
312}
313
314/// Install OS_EVENT handler by raw address. Useful to avoid fn pointer type mismatches.
315/// Safety: Caller must ensure the address is a valid `extern "C" fn()` handler.
316pub unsafe fn os_event_install_handler_raw(handler_addr: usize) {
317 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
318 let idx = 16 + (Interrupt::OS_EVENT as isize as usize);
319 core::ptr::write_volatile(vtor_base.add(idx), handler_addr as u32);
320}
321
322/// Provide a conservative default IRQ handler that avoids wedging the system.
323/// It clears all NVIC pending bits and returns, so spurious or reserved IRQs
324/// don’t trap the core in an infinite WFI loop during bring-up.
325#[no_mangle]
326pub unsafe extern "C" fn DefaultHandler() {
327 let active = core::ptr::read_volatile(0xE000_ED04 as *const u32) & 0x1FF;
328 let cfsr = core::ptr::read_volatile(0xE000_ED28 as *const u32);
329 let hfsr = core::ptr::read_volatile(0xE000_ED2C as *const u32);
330
331 let sp = cortex_m::register::msp::read();
332 let stacked = sp as *const u32;
333 // Stacked registers follow ARMv8-M procedure call standard order
334 let stacked_pc = unsafe { stacked.add(6).read() };
335 let stacked_lr = unsafe { stacked.add(5).read() };
336
337 LAST_DEFAULT_VECTOR.store(active as u16, Ordering::Relaxed);
338 LAST_DEFAULT_CFSR.store(cfsr, Ordering::Relaxed);
339 LAST_DEFAULT_HFSR.store(hfsr, Ordering::Relaxed);
340 LAST_DEFAULT_COUNT.fetch_add(1, Ordering::Relaxed);
341 LAST_DEFAULT_PC.store(stacked_pc, Ordering::Relaxed);
342 LAST_DEFAULT_LR.store(stacked_lr, Ordering::Relaxed);
343 LAST_DEFAULT_SP.store(sp, Ordering::Relaxed);
344
345 // Do nothing here: on some MCUs/TrustZone setups, writing NVIC from a spurious
346 // handler can fault if targeting the Secure bank. Just return.
347 cortex_m::asm::dsb();
348 cortex_m::asm::isb();
349}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 000000000..fe27aadba
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,142 @@
1#![no_std]
2
3pub mod clocks; // still provide clock helpers
4pub mod gpio;
5pub mod pins; // pin mux helpers
6pub mod reset; // reset control helpers
7
8pub mod adc;
9pub mod config;
10pub mod interrupt;
11pub mod lpuart;
12pub mod ostimer;
13pub mod rtc;
14pub mod uart;
15
16embassy_hal_internal::peripherals!(LPUART2, OSTIMER0, GPIO, RTC0, ADC1,);
17
18/// Get access to the PAC Peripherals for low-level register access.
19/// This is a lazy-initialized singleton that can be called after init().
20#[allow(static_mut_refs)]
21pub fn pac() -> &'static pac::Peripherals {
22 // SAFETY: We only call this after init(), and the PAC is a singleton.
23 // The embassy peripheral tokens ensure we don't have multiple mutable accesses.
24 unsafe {
25 static mut PAC_INSTANCE: Option<pac::Peripherals> = None;
26 if PAC_INSTANCE.is_none() {
27 PAC_INSTANCE = Some(pac::Peripherals::steal());
28 }
29 PAC_INSTANCE.as_ref().unwrap()
30 }
31}
32
33// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it.
34
35// Re-export interrupt traits and types
36pub use adc::Adc1 as Adc1Token;
37pub use gpio::pins::*;
38pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output};
39pub use interrupt::InterruptExt;
40#[cfg(feature = "unstable-pac")]
41pub use mcxa_pac as pac;
42#[cfg(not(feature = "unstable-pac"))]
43pub(crate) use mcxa_pac as pac;
44pub use ostimer::Ostimer0 as Ostimer0Token;
45pub use rtc::Rtc0 as Rtc0Token;
46pub use uart::Lpuart2 as Uart2Token;
47
48/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
49/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
50#[allow(unused_variables)]
51pub fn init(cfg: crate::config::Config) -> Peripherals {
52 let peripherals = Peripherals::take();
53 // Apply user-configured priority early; enabling is left to examples/apps
54 crate::interrupt::OS_EVENT.set_priority(cfg.time_interrupt_priority);
55 // Apply user-configured priority early; enabling is left to examples/apps
56 crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);
57 // Apply user-configured priority early; enabling is left to examples/apps
58 crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority);
59 peripherals
60}
61
62/// Optional hook called by cortex-m-rt before RAM init.
63/// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state
64/// left by soft resets/debug sessions.
65///
66/// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor'
67/// feature is incompatible with our setup because it expects __vector_table to be
68/// defined differently than how our RAM-based linker script arranges it.
69#[no_mangle]
70pub unsafe extern "C" fn __pre_init() {
71 // Set the VTOR to point to the interrupt vector table in RAM
72 // This is required since code runs from RAM on this MCU
73 crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32);
74
75 // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs.
76 let nvic = &*cortex_m::peripheral::NVIC::PTR;
77 for i in 0..4 {
78 // 4 words x 32 = 128 IRQs
79 nvic.icer[i].write(0xFFFF_FFFF);
80 nvic.icpr[i].write(0xFFFF_FFFF);
81 }
82 // Do NOT touch peripheral registers here: clocks may be off and accesses can fault.
83 crate::interrupt::clear_default_handler_snapshot();
84}
85
86/// Internal helper to dispatch a type-level interrupt handler.
87#[inline(always)]
88#[doc(hidden)]
89pub unsafe fn __handle_interrupt<T, H>()
90where
91 T: crate::interrupt::typelevel::Interrupt,
92 H: crate::interrupt::typelevel::Handler<T>,
93{
94 H::on_interrupt();
95}
96
97/// Macro to bind interrupts to handlers, similar to embassy-imxrt.
98///
99/// Example:
100/// - Bind OS_EVENT to the OSTIMER time-driver handler
101/// bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; });
102#[macro_export]
103macro_rules! bind_interrupts {
104 ($(#[$attr:meta])* $vis:vis struct $name:ident {
105 $(
106 $(#[cfg($cond_irq:meta)])?
107 $irq:ident => $(
108 $(#[cfg($cond_handler:meta)])?
109 $handler:ty
110 ),*;
111 )*
112 }) => {
113 #[derive(Copy, Clone)]
114 $(#[$attr])*
115 $vis struct $name;
116
117 $(
118 #[allow(non_snake_case)]
119 #[no_mangle]
120 $(#[cfg($cond_irq)])?
121 unsafe extern "C" fn $irq() {
122 unsafe {
123 $(
124 $(#[cfg($cond_handler)])?
125 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
126 )*
127 }
128 }
129
130 $(#[cfg($cond_irq)])?
131 $crate::bind_interrupts!(@inner
132 $(
133 $(#[cfg($cond_handler)])?
134 unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
135 )*
136 );
137 )*
138 };
139 (@inner $($t:tt)*) => {
140 $($t)*
141 }
142}
diff --git a/src/lpuart/buffered.rs b/src/lpuart/buffered.rs
new file mode 100644
index 000000000..0413fed8e
--- /dev/null
+++ b/src/lpuart/buffered.rs
@@ -0,0 +1,675 @@
1use core::future::poll_fn;
2use core::marker::PhantomData;
3use core::sync::atomic::{AtomicBool, Ordering};
4use core::task::Poll;
5
6use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
7use embassy_hal_internal::Peri;
8use embassy_sync::waitqueue::AtomicWaker;
9
10use super::*;
11use crate::interrupt;
12
13// ============================================================================
14// STATIC STATE MANAGEMENT
15// ============================================================================
16
17/// State for buffered LPUART operations
18pub struct State {
19 rx_waker: AtomicWaker,
20 rx_buf: RingBuffer,
21 tx_waker: AtomicWaker,
22 tx_buf: RingBuffer,
23 tx_done: AtomicBool,
24 initialized: AtomicBool,
25}
26
27impl State {
28 /// Create a new state instance
29 pub const fn new() -> Self {
30 Self {
31 rx_waker: AtomicWaker::new(),
32 rx_buf: RingBuffer::new(),
33 tx_waker: AtomicWaker::new(),
34 tx_buf: RingBuffer::new(),
35 tx_done: AtomicBool::new(true),
36 initialized: AtomicBool::new(false),
37 }
38 }
39}
40// ============================================================================
41// BUFFERED DRIVER STRUCTURES
42// ============================================================================
43
44/// Buffered LPUART driver
45pub struct BufferedLpuart<'a> {
46 tx: BufferedLpuartTx<'a>,
47 rx: BufferedLpuartRx<'a>,
48}
49
50/// Buffered LPUART TX driver
51pub struct BufferedLpuartTx<'a> {
52 info: Info,
53 state: &'static State,
54 _tx_pin: Peri<'a, AnyPin>,
55}
56
57/// Buffered LPUART RX driver
58pub struct BufferedLpuartRx<'a> {
59 info: Info,
60 state: &'static State,
61 _rx_pin: Peri<'a, AnyPin>,
62}
63
64// ============================================================================
65// BUFFERED LPUART IMPLEMENTATION
66// ============================================================================
67
68impl<'a> BufferedLpuart<'a> {
69 /// Create a new buffered LPUART instance
70 pub fn new<T: Instance>(
71 _inner: Peri<'a, T>,
72 tx_pin: Peri<'a, impl TxPin<T>>,
73 rx_pin: Peri<'a, impl RxPin<T>>,
74 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
75 tx_buffer: &'a mut [u8],
76 rx_buffer: &'a mut [u8],
77 config: Config,
78 ) -> Result<Self> {
79 // Configure pins
80 tx_pin.as_tx();
81 rx_pin.as_rx();
82
83 // Convert pins to AnyPin
84 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
85 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
86
87 let state = T::buffered_state();
88
89 // Initialize the peripheral
90 Self::init::<T>(Some(&tx_pin), Some(&rx_pin), None, None, tx_buffer, rx_buffer, config)?;
91
92 Ok(Self {
93 tx: BufferedLpuartTx {
94 info: T::info(),
95 state,
96 _tx_pin: tx_pin,
97 },
98 rx: BufferedLpuartRx {
99 info: T::info(),
100 state,
101 _rx_pin: rx_pin,
102 },
103 })
104 }
105
106 /// Create a new buffered LPUART with flexible pin configuration
107 pub fn new_with_pins<T: Instance>(
108 _inner: Peri<'a, T>,
109 tx_pin: Option<Peri<'a, impl TxPin<T>>>,
110 rx_pin: Option<Peri<'a, impl RxPin<T>>>,
111 rts_pin: Option<Peri<'a, impl RtsPin<T>>>,
112 cts_pin: Option<Peri<'a, impl CtsPin<T>>>,
113 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
114 tx_buffer: &'a mut [u8],
115 rx_buffer: &'a mut [u8],
116 config: Config,
117 ) -> Result<Self> {
118 // Configure pins if provided
119 let tx_pin = tx_pin.map(|pin| {
120 pin.as_tx();
121 let converted: Peri<'a, AnyPin> = pin.into();
122 converted
123 });
124
125 let rx_pin = rx_pin.map(|pin| {
126 pin.as_rx();
127 let converted: Peri<'a, AnyPin> = pin.into();
128 converted
129 });
130
131 let rts_pin = rts_pin.map(|pin| {
132 pin.as_rts();
133 let converted: Peri<'a, AnyPin> = pin.into();
134 converted
135 });
136
137 let cts_pin = cts_pin.map(|pin| {
138 pin.as_cts();
139 let converted: Peri<'a, AnyPin> = pin.into();
140 converted
141 });
142
143 let state = T::buffered_state();
144
145 // Initialize the peripheral
146 Self::init::<T>(
147 tx_pin.as_ref(),
148 rx_pin.as_ref(),
149 rts_pin.as_ref(),
150 cts_pin.as_ref(),
151 tx_buffer,
152 rx_buffer,
153 config,
154 )?;
155
156 // Create TX and RX instances
157 let (tx, rx) = if let (Some(tx_pin), Some(rx_pin)) = (tx_pin, rx_pin) {
158 (
159 BufferedLpuartTx {
160 info: T::info(),
161 state,
162 _tx_pin: tx_pin,
163 },
164 BufferedLpuartRx {
165 info: T::info(),
166 state,
167 _rx_pin: rx_pin,
168 },
169 )
170 } else {
171 return Err(Error::InvalidArgument);
172 };
173
174 Ok(Self { tx, rx })
175 }
176
177 fn init<T: Instance>(
178 _tx: Option<&Peri<'a, AnyPin>>,
179 _rx: Option<&Peri<'a, AnyPin>>,
180 _rts: Option<&Peri<'a, AnyPin>>,
181 _cts: Option<&Peri<'a, AnyPin>>,
182 tx_buffer: &'a mut [u8],
183 rx_buffer: &'a mut [u8],
184 mut config: Config,
185 ) -> Result<()> {
186 let regs = T::info().regs;
187 let state = T::buffered_state();
188
189 // Check if already initialized
190 if state.initialized.load(Ordering::Relaxed) {
191 return Err(Error::InvalidArgument);
192 }
193
194 // Initialize ring buffers
195 assert!(!tx_buffer.is_empty());
196 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), tx_buffer.len()) }
197
198 assert!(!rx_buffer.is_empty());
199 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_buffer.len()) }
200
201 // Mark as initialized
202 state.initialized.store(true, Ordering::Relaxed);
203
204 // Enable TX and RX for buffered operation
205 config.enable_tx = true;
206 config.enable_rx = true;
207
208 // Perform standard initialization
209 perform_software_reset(regs);
210 disable_transceiver(regs);
211 configure_baudrate(regs, config.baudrate_bps, config.clock)?;
212 configure_frame_format(regs, &config);
213 configure_control_settings(regs, &config);
214 configure_fifo(regs, &config);
215 clear_all_status_flags(regs);
216 configure_flow_control(regs, &config);
217 configure_bit_order(regs, config.msb_firs);
218
219 // Enable interrupts for buffered operation
220 cortex_m::interrupt::free(|_| {
221 regs.ctrl().modify(|_, w| {
222 w.rie()
223 .enabled() // RX interrupt
224 .orie()
225 .enabled() // Overrun interrupt
226 .peie()
227 .enabled() // Parity error interrupt
228 .feie()
229 .enabled() // Framing error interrupt
230 .neie()
231 .enabled() // Noise error interrupt
232 });
233 });
234
235 // Enable the transceiver
236 enable_transceiver(regs, config.enable_tx, config.enable_rx);
237
238 // Enable the interrupt
239 // unsafe {
240 // // TODO: Used the propper interrupt enable method for the specific LPUART instance
241 // // T::Interrupt::enable();
242 // }
243
244 Ok(())
245 }
246
247 /// Split the buffered LPUART into separate TX and RX parts
248 pub fn split(self) -> (BufferedLpuartTx<'a>, BufferedLpuartRx<'a>) {
249 (self.tx, self.rx)
250 }
251
252 /// Get mutable references to TX and RX parts
253 pub fn split_ref(&mut self) -> (&mut BufferedLpuartTx<'a>, &mut BufferedLpuartRx<'a>) {
254 (&mut self.tx, &mut self.rx)
255 }
256}
257
258// ============================================================================
259// BUFFERED TX IMPLEMENTATION
260// ============================================================================
261
262impl<'a> BufferedLpuartTx<'a> {
263 /// Create a new TX-only buffered LPUART
264 pub fn new<T: Instance>(
265 _inner: Peri<'a, T>,
266 tx_pin: Peri<'a, impl TxPin<T>>,
267 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
268 tx_buffer: &'a mut [u8],
269 config: Config,
270 ) -> Result<Self> {
271 tx_pin.as_tx();
272 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
273
274 let info = T::info();
275 let state = T::buffered_state();
276
277 // Check if already initialized
278 if state.initialized.load(Ordering::Relaxed) {
279 return Err(Error::InvalidArgument);
280 }
281
282 // Initialize TX ring buffer only
283 unsafe {
284 let tx_buf = &state.tx_buf as *const _ as *mut RingBuffer;
285 (*tx_buf).init(tx_buffer.as_mut_ptr(), tx_buffer.len());
286 }
287
288 state.initialized.store(true, Ordering::Relaxed);
289
290 // Initialize with TX only
291 BufferedLpuart::init::<T>(
292 Some(&tx_pin),
293 None,
294 None,
295 None,
296 tx_buffer,
297 &mut [], // Empty RX buffer
298 config,
299 )?;
300
301 Ok(Self {
302 info,
303 state,
304 _tx_pin: tx_pin,
305 })
306 }
307
308 /// Write data asynchronously
309 pub async fn write(&mut self, buf: &[u8]) -> Result<usize> {
310 let mut written = 0;
311
312 for &byte in buf {
313 // Wait for space in the buffer
314 poll_fn(|cx| {
315 self.state.tx_waker.register(cx.waker());
316
317 let mut writer = unsafe { self.state.tx_buf.writer() };
318 if writer.push_one(byte) {
319 // Enable TX interrupt to start transmission
320 cortex_m::interrupt::free(|_| {
321 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
322 });
323 Poll::Ready(Ok(()))
324 } else {
325 Poll::Pending
326 }
327 })
328 .await?;
329
330 written += 1;
331 }
332
333 Ok(written)
334 }
335
336 /// Flush the TX buffer and wait for transmission to complete
337 pub async fn flush(&mut self) -> Result<()> {
338 // Wait for TX buffer to empty and transmission to complete
339 poll_fn(|cx| {
340 self.state.tx_waker.register(cx.waker());
341
342 let tx_empty = self.state.tx_buf.is_empty();
343 let fifo_empty = self.info.regs.water().read().txcount().bits() == 0;
344 let tc_complete = self.info.regs.stat().read().tc().is_complete();
345
346 if tx_empty && fifo_empty && tc_complete {
347 Poll::Ready(Ok(()))
348 } else {
349 // Enable appropriate interrupt
350 cortex_m::interrupt::free(|_| {
351 if !tx_empty {
352 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
353 } else {
354 self.info.regs.ctrl().modify(|_, w| w.tcie().enabled());
355 }
356 });
357 Poll::Pending
358 }
359 })
360 .await
361 }
362
363 /// Try to write without blocking
364 pub fn try_write(&mut self, buf: &[u8]) -> Result<usize> {
365 let mut writer = unsafe { self.state.tx_buf.writer() };
366 let mut written = 0;
367
368 for &byte in buf {
369 if writer.push_one(byte) {
370 written += 1;
371 } else {
372 break;
373 }
374 }
375
376 if written > 0 {
377 // Enable TX interrupt to start transmission
378 cortex_m::interrupt::free(|_| {
379 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
380 });
381 }
382
383 Ok(written)
384 }
385}
386
387// ============================================================================
388// BUFFERED RX IMPLEMENTATION
389// ============================================================================
390
391impl<'a> BufferedLpuartRx<'a> {
392 /// Create a new RX-only buffered LPUART
393 pub fn new<T: Instance>(
394 _inner: Peri<'a, T>,
395 rx_pin: Peri<'a, impl RxPin<T>>,
396 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
397 rx_buffer: &'a mut [u8],
398 config: Config,
399 ) -> Result<Self> {
400 rx_pin.as_rx();
401 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
402
403 let info = T::info();
404 let state = T::buffered_state();
405
406 // Check if already initialized
407 if state.initialized.load(Ordering::Relaxed) {
408 return Err(Error::InvalidArgument);
409 }
410
411 // Initialize RX ring buffer only
412 unsafe {
413 let rx_buf = &state.rx_buf as *const _ as *mut RingBuffer;
414 (*rx_buf).init(rx_buffer.as_mut_ptr(), rx_buffer.len());
415 }
416
417 state.initialized.store(true, Ordering::Relaxed);
418
419 // Initialize with RX only
420 BufferedLpuart::init::<T>(
421 None,
422 Some(&rx_pin),
423 None,
424 None,
425 &mut [], // Empty TX buffer
426 rx_buffer,
427 config,
428 )?;
429
430 Ok(Self {
431 info,
432 state,
433 _rx_pin: rx_pin,
434 })
435 }
436
437 /// Read data asynchronously
438 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
439 if buf.is_empty() {
440 return Ok(0);
441 }
442
443 let mut read = 0;
444
445 // Try to read available data
446 poll_fn(|cx| {
447 self.state.rx_waker.register(cx.waker());
448
449 // Disable RX interrupt while reading from buffer
450 cortex_m::interrupt::free(|_| {
451 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
452 });
453
454 let mut reader = unsafe { self.state.rx_buf.reader() };
455 let available = reader.pop(|data| {
456 let to_copy = core::cmp::min(data.len(), buf.len() - read);
457 if to_copy > 0 {
458 buf[read..read + to_copy].copy_from_slice(&data[..to_copy]);
459 read += to_copy;
460 }
461 to_copy
462 });
463
464 // Re-enable RX interrupt
465 cortex_m::interrupt::free(|_| {
466 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
467 });
468
469 if read > 0 {
470 Poll::Ready(Ok(read))
471 } else if available == 0 {
472 Poll::Pending
473 } else {
474 Poll::Ready(Ok(0))
475 }
476 })
477 .await
478 }
479
480 /// Try to read without blocking
481 pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> {
482 if buf.is_empty() {
483 return Ok(0);
484 }
485
486 // Disable RX interrupt while reading from buffer
487 cortex_m::interrupt::free(|_| {
488 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
489 });
490
491 let mut reader = unsafe { self.state.rx_buf.reader() };
492 let read = reader.pop(|data| {
493 let to_copy = core::cmp::min(data.len(), buf.len());
494 if to_copy > 0 {
495 buf[..to_copy].copy_from_slice(&data[..to_copy]);
496 }
497 to_copy
498 });
499
500 // Re-enable RX interrupt
501 cortex_m::interrupt::free(|_| {
502 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
503 });
504
505 Ok(read)
506 }
507}
508
509// ============================================================================
510// INTERRUPT HANDLER
511// ============================================================================
512
513/// Buffered UART interrupt handler
514pub struct BufferedInterruptHandler<T: Instance> {
515 _phantom: PhantomData<T>,
516}
517
518impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {
519 unsafe fn on_interrupt() {
520 let regs = T::info().regs;
521 let state = T::buffered_state();
522
523 // Check if this instance is initialized
524 if !state.initialized.load(Ordering::Relaxed) {
525 return;
526 }
527
528 let ctrl = regs.ctrl().read();
529 let stat = regs.stat().read();
530 let has_fifo = regs.param().read().rxfifo().bits() > 0;
531
532 // Handle overrun error
533 if stat.or().is_overrun() {
534 regs.stat().write(|w| w.or().clear_bit_by_one());
535 state.rx_waker.wake();
536 return;
537 }
538
539 // Clear other error flags
540 if stat.pf().is_parity() {
541 regs.stat().write(|w| w.pf().clear_bit_by_one());
542 }
543 if stat.fe().is_error() {
544 regs.stat().write(|w| w.fe().clear_bit_by_one());
545 }
546 if stat.nf().is_noise() {
547 regs.stat().write(|w| w.nf().clear_bit_by_one());
548 }
549
550 // Handle RX data
551 if ctrl.rie().is_enabled() && (has_data(regs) || stat.idle().is_idle()) {
552 let mut pushed_any = false;
553 let mut writer = state.rx_buf.writer();
554
555 if has_fifo {
556 // Read from FIFO
557 while regs.water().read().rxcount().bits() > 0 {
558 let byte = (regs.data().read().bits() & 0xFF) as u8;
559 if writer.push_one(byte) {
560 pushed_any = true;
561 } else {
562 // Buffer full, stop reading
563 break;
564 }
565 }
566 } else {
567 // Read single byte
568 if regs.stat().read().rdrf().is_rxdata() {
569 let byte = (regs.data().read().bits() & 0xFF) as u8;
570 if writer.push_one(byte) {
571 pushed_any = true;
572 }
573 }
574 }
575
576 if pushed_any {
577 state.rx_waker.wake();
578 }
579
580 // Clear idle flag if set
581 if stat.idle().is_idle() {
582 regs.stat().write(|w| w.idle().clear_bit_by_one());
583 }
584 }
585
586 // Handle TX data
587 if ctrl.tie().is_enabled() {
588 let mut sent_any = false;
589 let mut reader = state.tx_buf.reader();
590
591 // Send data while TX buffer is ready and we have data
592 while regs.stat().read().tdre().is_no_txdata() {
593 if let Some(byte) = reader.pop_one() {
594 regs.data().write(|w| w.bits(u32::from(byte)));
595 sent_any = true;
596 } else {
597 // No more data to send
598 break;
599 }
600 }
601
602 if sent_any {
603 state.tx_waker.wake();
604 }
605
606 // If buffer is empty, switch to TC interrupt or disable
607 if state.tx_buf.is_empty() {
608 cortex_m::interrupt::free(|_| {
609 regs.ctrl().modify(|_, w| w.tie().disabled().tcie().enabled());
610 });
611 }
612 }
613
614 // Handle transmission complete
615 if ctrl.tcie().is_enabled() {
616 if regs.stat().read().tc().is_complete() {
617 state.tx_done.store(true, Ordering::Release);
618 state.tx_waker.wake();
619
620 // Disable TC interrupt
621 cortex_m::interrupt::free(|_| {
622 regs.ctrl().modify(|_, w| w.tcie().disabled());
623 });
624 }
625 }
626 }
627}
628
629// ============================================================================
630// EMBEDDED-IO ASYNC TRAIT IMPLEMENTATIONS
631// ============================================================================
632
633impl embedded_io_async::ErrorType for BufferedLpuartTx<'_> {
634 type Error = Error;
635}
636
637impl embedded_io_async::ErrorType for BufferedLpuartRx<'_> {
638 type Error = Error;
639}
640
641impl embedded_io_async::ErrorType for BufferedLpuart<'_> {
642 type Error = Error;
643}
644
645impl embedded_io_async::Write for BufferedLpuartTx<'_> {
646 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
647 self.write(buf).await
648 }
649
650 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
651 self.flush().await
652 }
653}
654
655impl embedded_io_async::Read for BufferedLpuartRx<'_> {
656 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
657 self.read(buf).await
658 }
659}
660
661impl embedded_io_async::Write for BufferedLpuart<'_> {
662 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
663 self.tx.write(buf).await
664 }
665
666 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
667 self.tx.flush().await
668 }
669}
670
671impl embedded_io_async::Read for BufferedLpuart<'_> {
672 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
673 self.rx.read(buf).await
674 }
675}
diff --git a/src/lpuart/mod.rs b/src/lpuart/mod.rs
new file mode 100644
index 000000000..bed10bdb0
--- /dev/null
+++ b/src/lpuart/mod.rs
@@ -0,0 +1,1201 @@
1use core::marker::PhantomData;
2
3use embassy_hal_internal::{Peri, PeripheralType};
4use paste::paste;
5
6use crate::pac::lpuart0::baud::Sbns as StopBits;
7use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits};
8use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource};
9use crate::pac::lpuart0::stat::Msbf as MsbFirst;
10use crate::{interrupt, pac};
11
12pub mod buffered;
13
14// ============================================================================
15// STUB IMPLEMENTATION
16// ============================================================================
17
18// Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API
19// Pin and Clock initialization is currently done at the examples level.
20
21// --- START LIB ---
22
23// Use our own instance of Peripherals, until we align `lib.rs` with the EMBASSY-IMXRT approach
24// Inlined peripherals_definition! to bypass the `Peripherals::take_with_cs()` check
25// SHOULD NOT BE USED IN THE FINAL VERSION
26pub mod lib {
27 // embassy_hal_internal::peripherals!(LPUART2, PIO2_2, PIO2_3)
28
29 embassy_hal_internal::peripherals_definition!(LPUART2, PIO2_2, PIO2_3,);
30 #[doc = r" Struct containing all the peripheral singletons."]
31 #[doc = r""]
32 #[doc = r" To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]."]
33 #[allow(non_snake_case)]
34 pub struct Peripherals {
35 #[doc = concat!(stringify!(LPUART2)," peripheral")]
36 pub LPUART2: embassy_hal_internal::Peri<'static, peripherals::LPUART2>,
37 #[doc = concat!(stringify!(PIO2_2)," peripheral")]
38 pub PIO2_2: embassy_hal_internal::Peri<'static, peripherals::PIO2_2>,
39 #[doc = concat!(stringify!(PIO2_3)," peripheral")]
40 pub PIO2_3: embassy_hal_internal::Peri<'static, peripherals::PIO2_3>,
41 }
42 impl Peripherals {
43 #[doc = r"Returns all the peripherals *once*"]
44 #[inline]
45 pub(crate) fn take() -> Self {
46 critical_section::with(Self::take_with_cs)
47 }
48 #[doc = r"Returns all the peripherals *once*"]
49 #[inline]
50 pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self {
51 #[no_mangle]
52 static mut _EMBASSY_DEVICE_PERIPHERALS2: bool = false; // ALIGN: Temporary fix to use stub Peripherals
53 unsafe {
54 if _EMBASSY_DEVICE_PERIPHERALS2 {
55 panic!("init called more than once!")
56 }
57 _EMBASSY_DEVICE_PERIPHERALS2 = true;
58 Self::steal()
59 }
60 }
61 }
62 impl Peripherals {
63 #[doc = r" Unsafely create an instance of this peripheral out of thin air."]
64 #[doc = r""]
65 #[doc = r" # Safety"]
66 #[doc = r""]
67 #[doc = r" You must ensure that you're only using one instance of this type at a time."]
68 #[inline]
69 pub unsafe fn steal() -> Self {
70 Self {
71 LPUART2: peripherals::LPUART2::steal(),
72 PIO2_2: peripherals::PIO2_2::steal(),
73 PIO2_3: peripherals::PIO2_3::steal(),
74 }
75 }
76 }
77
78 /// Initialize HAL
79 pub fn init() -> Peripherals {
80 Peripherals::take()
81 }
82}
83
84// --- END LIB ---
85
86// --- START GPIO ---
87
88mod gpio {
89 use embassy_hal_internal::PeripheralType;
90 trait SealedPin {}
91
92 #[allow(private_bounds)]
93 pub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static {
94 /// Type-erase the pin.
95 fn degrade(self) -> AnyPin {
96 todo!()
97 }
98 }
99
100 // Add this macro to implement GpioPin for all pins
101 macro_rules! impl_gpio_pin {
102 ($($pin:ident),*) => {
103 $(
104 impl SealedPin for super::lib::peripherals::$pin {}
105
106 impl GpioPin for super::lib::peripherals::$pin {}
107
108 impl Into<AnyPin> for super::lib::peripherals::$pin {
109 fn into(self) -> AnyPin {
110 AnyPin
111 }
112 }
113 )*
114 };
115 }
116
117 // Implement GpioPin for all pins from lib.rs
118 impl_gpio_pin!(PIO2_2, PIO2_3);
119
120 #[derive(Debug, Clone, Copy)]
121 pub struct AnyPin;
122
123 impl PeripheralType for AnyPin {}
124
125 pub enum Alt {
126 ALT3,
127 }
128}
129
130use gpio::{AnyPin, GpioPin as Pin};
131
132// --- END GPIO ---
133
134// --- START DMA ---
135mod dma {
136 pub struct Channel<'d> {
137 pub(super) _lifetime: core::marker::PhantomData<&'d ()>,
138 }
139}
140
141use dma::Channel;
142
143// --- END DMA ---
144
145// --- START CLOCK ---
146mod clock {
147 #[derive(Debug, Clone, Copy)]
148 pub enum Clock {
149 FroLf, // Low-Frequency Free-Running Oscillator
150 }
151}
152
153use clock::Clock;
154
155// --- END CLOCK ---
156
157// ============================================================================
158// MISC
159// ============================================================================
160
161mod sealed {
162 /// Simply seal a trait to prevent external implementations
163 pub trait Sealed {}
164}
165
166// ============================================================================
167// INSTANCE TRAIT
168// ============================================================================
169
170pub type Regs = &'static crate::pac::lpuart0::RegisterBlock;
171
172pub trait SealedInstance {
173 fn info() -> Info;
174 fn index() -> usize;
175 fn buffered_state() -> &'static buffered::State;
176}
177
178pub struct Info {
179 pub regs: Regs,
180}
181
182/// Trait for LPUART peripheral instances
183#[allow(private_bounds)]
184pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
185 type Interrupt: interrupt::typelevel::Interrupt;
186}
187
188macro_rules! impl_instance {
189 ($($n:expr),*) => {
190 $(
191 paste!{
192 impl SealedInstance for lib::peripherals::[<LPUART $n>] {
193 fn info() -> Info {
194 Info {
195 regs: unsafe { &*pac::[<Lpuart $n>]::ptr() },
196 }
197 }
198
199 #[inline]
200 fn index() -> usize {
201 $n
202 }
203
204 fn buffered_state() -> &'static buffered::State {
205 static BUFFERED_STATE: buffered::State = buffered::State::new();
206 &BUFFERED_STATE
207 }
208 }
209
210 impl Instance for lib::peripherals::[<LPUART $n>] {
211 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];
212 }
213 }
214 )*
215 };
216}
217
218// impl_instance!(0, 1, 2, 3, 4);
219impl_instance!(2);
220
221// ============================================================================
222// INSTANCE HELPER FUNCTIONS
223// ============================================================================
224
225/// Perform software reset on the LPUART peripheral
226pub fn perform_software_reset(regs: Regs) {
227 // Software reset - set and clear RST bit (Global register)
228 regs.global().write(|w| w.rst().reset());
229 regs.global().write(|w| w.rst().no_effect());
230}
231
232/// Disable both transmitter and receiver
233pub fn disable_transceiver(regs: Regs) {
234 regs.ctrl().modify(|_, w| w.te().disabled().re().disabled());
235}
236
237/// Calculate and configure baudrate settings
238pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock: Clock) -> Result<()> {
239 let clock_freq = get_fc_freq(clock)?;
240 let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?;
241
242 // Configure BAUD register
243 regs.baud().modify(|_, w| unsafe {
244 // Clear and set OSR
245 w.osr().bits((osr - 1) as u8);
246 // Clear and set SBR
247 w.sbr().bits(sbr);
248 // Set BOTHEDGE if OSR is between 4 and 7
249 if osr > 3 && osr < 8 {
250 w.bothedge().enabled()
251 } else {
252 w.bothedge().disabled()
253 }
254 });
255
256 Ok(())
257}
258
259/// Configure frame format (stop bits, data bits)
260pub fn configure_frame_format(regs: Regs, config: &Config) {
261 // Configure stop bits
262 regs.baud().modify(|_, w| w.sbns().variant(config.stop_bits_count));
263
264 // Clear M10 for now (10-bit mode)
265 regs.baud().modify(|_, w| w.m10().disabled());
266}
267
268/// Configure control settings (parity, data bits, idle config, pin swap)
269pub fn configure_control_settings(regs: Regs, config: &Config) {
270 regs.ctrl().modify(|_, w| {
271 // Parity configuration
272 let mut w = if let Some(parity) = config.parity_mode {
273 w.pe().enabled().pt().variant(parity)
274 } else {
275 w.pe().disabled()
276 };
277
278 // Data bits configuration
279 w = match config.data_bits_count {
280 DataBits::Data8 => {
281 if config.parity_mode.is_some() {
282 w.m().data9() // 8 data + 1 parity = 9 bits
283 } else {
284 w.m().data8() // 8 data bits only
285 }
286 }
287 DataBits::Data9 => w.m().data9(),
288 };
289
290 // Idle configuration
291 w = w.idlecfg().variant(config.rx_idle_config);
292 w = w.ilt().variant(config.rx_idle_type);
293
294 // Swap TXD/RXD if configured
295 if config.swap_txd_rxd {
296 w.swap().swap()
297 } else {
298 w.swap().standard()
299 }
300 });
301}
302
303/// Configure FIFO settings and watermarks
304pub fn configure_fifo(regs: Regs, config: &Config) {
305 // Configure WATER register for FIFO watermarks
306 regs.water().write(|w| unsafe {
307 w.rxwater()
308 .bits(config.rx_fifo_watermark as u8)
309 .txwater()
310 .bits(config.tx_fifo_watermark as u8)
311 });
312
313 // Enable TX/RX FIFOs
314 regs.fifo().modify(|_, w| w.txfe().enabled().rxfe().enabled());
315
316 // Flush FIFOs
317 regs.fifo()
318 .modify(|_, w| w.txflush().txfifo_rst().rxflush().rxfifo_rst());
319}
320
321/// Clear all status flags
322pub fn clear_all_status_flags(regs: Regs) {
323 regs.stat().reset();
324}
325
326/// Configure hardware flow control if enabled
327pub fn configure_flow_control(regs: Regs, config: &Config) {
328 if config.enable_rx_rts || config.enable_tx_cts {
329 regs.modir().modify(|_, w| {
330 let mut w = w;
331
332 // Configure TX CTS
333 w = w.txctsc().variant(config.tx_cts_config);
334 w = w.txctssrc().variant(config.tx_cts_source);
335
336 if config.enable_rx_rts {
337 w = w.rxrtse().enabled();
338 } else {
339 w = w.rxrtse().disabled();
340 }
341
342 if config.enable_tx_cts {
343 w = w.txctse().enabled();
344 } else {
345 w = w.txctse().disabled();
346 }
347
348 w
349 });
350 }
351}
352
353/// Configure bit order (MSB first or LSB first)
354pub fn configure_bit_order(regs: Regs, msb_first: MsbFirst) {
355 regs.stat().modify(|_, w| w.msbf().variant(msb_first));
356}
357
358/// Enable transmitter and/or receiver based on configuration
359pub fn enable_transceiver(regs: Regs, enable_tx: bool, enable_rx: bool) {
360 regs.ctrl().modify(|_, w| {
361 let mut w = w;
362 if enable_tx {
363 w = w.te().enabled();
364 }
365 if enable_rx {
366 w = w.re().enabled();
367 }
368 w
369 });
370}
371
372pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> {
373 let mut baud_diff = baudrate;
374 let mut osr = 0u8;
375 let mut sbr = 0u16;
376
377 // Try OSR values from 4 to 32
378 for osr_temp in 4u8..=32u8 {
379 // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2
380 let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32) + 1) / 2;
381
382 let sbr_temp = if sbr_calc == 0 {
383 1
384 } else if sbr_calc > 0x1FFF {
385 0x1FFF
386 } else {
387 sbr_calc as u16
388 };
389
390 // Calculate actual baud rate
391 let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32);
392
393 let temp_diff = if calculated_baud > baudrate {
394 calculated_baud - baudrate
395 } else {
396 baudrate - calculated_baud
397 };
398
399 if temp_diff <= baud_diff {
400 baud_diff = temp_diff;
401 osr = osr_temp;
402 sbr = sbr_temp;
403 }
404 }
405
406 // Check if baud rate difference is within 3%
407 if baud_diff > (baudrate / 100) * 3 {
408 return Err(Error::UnsupportedBaudrate);
409 }
410
411 Ok((osr, sbr))
412}
413
414pub fn get_fc_freq(clock: Clock) -> Result<u32> {
415 // This is a placeholder - actual implementation would query the clock system
416 // In real implementation, this would get the LPUART clock frequency
417 match clock {
418 Clock::FroLf => Ok(12_000_000), // Low frequency oscillator
419 #[allow(unreachable_patterns)]
420 _ => Err(Error::InvalidArgument),
421 }
422}
423
424/// Wait for all transmit operations to complete
425pub fn wait_for_tx_complete(regs: Regs) {
426 // Wait for TX FIFO to empty
427 while regs.water().read().txcount().bits() != 0 {
428 // Wait for TX FIFO to drain
429 }
430
431 // Wait for last character to shift out (TC = Transmission Complete)
432 while regs.stat().read().tc().is_active() {
433 // Wait for transmission to complete
434 }
435}
436
437pub fn check_and_clear_rx_errors(regs: Regs) -> Result<()> {
438 let stat = regs.stat().read();
439 let mut status = Ok(());
440
441 // Check for overrun first - other error flags are prevented when OR is set
442 if stat.or().is_overrun() {
443 regs.stat().write(|w| w.or().clear_bit_by_one());
444
445 return Err(Error::Overrun);
446 }
447
448 if stat.pf().is_parity() {
449 regs.stat().write(|w| w.pf().clear_bit_by_one());
450 status = Err(Error::Parity);
451 }
452
453 if stat.fe().is_error() {
454 regs.stat().write(|w| w.fe().clear_bit_by_one());
455 status = Err(Error::Framing);
456 }
457
458 if stat.nf().is_noise() {
459 regs.stat().write(|w| w.nf().clear_bit_by_one());
460 status = Err(Error::Noise);
461 }
462
463 status
464}
465
466pub fn has_data(regs: Regs) -> bool {
467 if regs.param().read().rxfifo().bits() > 0 {
468 // FIFO is available - check RXCOUNT in WATER register
469 regs.water().read().rxcount().bits() > 0
470 } else {
471 // No FIFO - check RDRF flag in STAT register
472 regs.stat().read().rdrf().is_rxdata()
473 }
474}
475
476// ============================================================================
477// PIN TRAITS FOR LPUART FUNCTIONALITY
478// ============================================================================
479
480impl<T: Pin> sealed::Sealed for T {}
481
482/// io configuration trait for Lpuart Tx configuration
483pub trait TxPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
484 /// convert the pin to appropriate function for Lpuart Tx usage
485 fn as_tx(&self);
486}
487
488/// io configuration trait for Lpuart Rx configuration
489pub trait RxPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
490 /// convert the pin to appropriate function for Lpuart Rx usage
491 fn as_rx(&self);
492}
493
494/// io configuration trait for Lpuart Cts
495pub trait CtsPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
496 /// convert the pin to appropriate function for Lpuart Cts usage
497 fn as_cts(&self);
498}
499
500/// io configuration trait for Lpuart Rts
501pub trait RtsPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
502 /// convert the pin to appropriate function for Lpuart Rts usage
503 fn as_rts(&self);
504}
505
506macro_rules! impl_pin_trait {
507 ($fcn:ident, $mode:ident, $($pin:ident, $alt:ident),*) => {
508 paste! {
509 $(
510 impl [<$mode:camel Pin>]<lib::peripherals::$fcn> for lib::peripherals::$pin {
511 fn [<as_ $mode>](&self) {
512 let _alt = gpio::Alt::$alt;
513 // todo!("Configure pin for LPUART function")
514 }
515 }
516 )*
517 }
518 };
519}
520
521// Document identifier: MCXA343/344 Rev. 1DraftB ReleaseCandidate, 2025-07-10 - 6.1 MCX A173, A174 Signal Multiplexing and Pin Assignments
522// impl_pin_trait!(LPUART0, rx, PIO2_0, ALT2, PIO0_2, ALT2, PIO0_20, ALT3);
523// impl_pin_trait!(LPUART0, tx, PIO2_1, ALT2, PIO0_3, ALT2, PIO0_21, ALT3);
524// impl_pin_trait!(LPUART0, rts, PIO2_2, ALT2, PIO0_0, ALT2, PIO0_22, ALT3);
525// impl_pin_trait!(LPUART0, cts, PIO2_3, ALT2, PIO0_1, ALT2, PIO0_23, ALT3);
526impl_pin_trait!(LPUART2, rx, PIO2_3, ALT3);
527impl_pin_trait!(LPUART2, tx, PIO2_2, ALT3);
528
529// ============================================================================
530// ERROR TYPES AND RESULTS
531// ============================================================================
532
533/// LPUART error types
534#[derive(Debug, Copy, Clone, Eq, PartialEq)]
535#[cfg_attr(feature = "defmt", derive(defmt::Format))]
536pub enum Error {
537 /// Read error
538 Read,
539 /// Buffer overflow
540 Overrun,
541 /// Noise error
542 Noise,
543 /// Framing error
544 Framing,
545 /// Parity error
546 Parity,
547 /// Failure
548 Fail,
549 /// Invalid argument
550 InvalidArgument,
551 /// Lpuart baud rate cannot be supported with the given clock
552 UnsupportedBaudrate,
553 /// RX FIFO Empty
554 RxFifoEmpty,
555 /// TX FIFO Full
556 TxFifoFull,
557 /// TX Busy
558 TxBusy,
559}
560
561/// A specialized Result type for LPUART operations
562pub type Result<T> = core::result::Result<T, Error>;
563
564// ============================================================================
565// CONFIGURATION STRUCTURES
566// ============================================================================
567
568/// Lpuart config
569#[derive(Debug, Clone, Copy)]
570pub struct Config {
571 /// Baud rate in bits per second
572 pub baudrate_bps: u32,
573 /// Clock
574 pub clock: Clock,
575 /// Parity configuration
576 pub parity_mode: Option<Parity>,
577 /// Number of data bits
578 pub data_bits_count: DataBits,
579 /// MSB First or LSB First configuration
580 pub msb_firs: MsbFirst,
581 /// Number of stop bits
582 pub stop_bits_count: StopBits,
583 /// TX FIFO watermark
584 pub tx_fifo_watermark: u8,
585 /// RX FIFO watermark
586 pub rx_fifo_watermark: u8,
587 /// RX RTS enable
588 pub enable_rx_rts: bool,
589 /// TX CTS enable
590 pub enable_tx_cts: bool,
591 /// TX CTS source
592 pub tx_cts_source: TxCtsSource,
593 /// TX CTS configure
594 pub tx_cts_config: TxCtsConfig,
595 /// RX IDLE type
596 pub rx_idle_type: IdleType,
597 /// RX IDLE configuration
598 pub rx_idle_config: IdleConfig,
599 /// Enable transmitter
600 pub enable_tx: bool,
601 /// Enable receiver
602 pub enable_rx: bool,
603 /// Swap TXD and RXD pins
604 pub swap_txd_rxd: bool,
605}
606
607impl Default for Config {
608 fn default() -> Self {
609 Self {
610 baudrate_bps: 115_200u32,
611 clock: Clock::FroLf,
612 parity_mode: None,
613 data_bits_count: DataBits::Data8,
614 msb_firs: MsbFirst::LsbFirst,
615 stop_bits_count: StopBits::One,
616 tx_fifo_watermark: 0,
617 rx_fifo_watermark: 1,
618 enable_rx_rts: false,
619 enable_tx_cts: false,
620 tx_cts_source: TxCtsSource::Cts,
621 tx_cts_config: TxCtsConfig::Start,
622 rx_idle_type: IdleType::FromStart,
623 rx_idle_config: IdleConfig::Idle1,
624 enable_tx: false,
625 enable_rx: false,
626 swap_txd_rxd: false,
627 }
628 }
629}
630
631/// LPUART status flags
632#[derive(Debug, Clone, Copy)]
633#[cfg_attr(feature = "defmt", derive(defmt::Format))]
634pub struct Status {
635 /// Transmit data register empty
636 pub tx_empty: bool,
637 /// Transmission complete
638 pub tx_complete: bool,
639 /// Receive data register full
640 pub rx_full: bool,
641 /// Idle line detected
642 pub idle: bool,
643 /// Receiver overrun
644 pub overrun: bool,
645 /// Noise error
646 pub noise: bool,
647 /// Framing error
648 pub framing: bool,
649 /// Parity error
650 pub parity: bool,
651}
652
653// ============================================================================
654// MODE TRAITS (BLOCKING/ASYNC)
655// ============================================================================
656
657/// Driver move trait.
658#[allow(private_bounds)]
659pub trait Mode: sealed::Sealed {}
660
661/// Blocking mode.
662pub struct Blocking;
663impl sealed::Sealed for Blocking {}
664impl Mode for Blocking {}
665
666/// Async mode.
667pub struct Async;
668impl sealed::Sealed for Async {}
669impl Mode for Async {}
670
671// ============================================================================
672// CORE DRIVER STRUCTURES
673// ============================================================================
674
675/// Lpuart driver.
676pub struct Lpuart<'a, M: Mode> {
677 info: Info,
678 tx: LpuartTx<'a, M>,
679 rx: LpuartRx<'a, M>,
680}
681
682/// Lpuart TX driver.
683pub struct LpuartTx<'a, M: Mode> {
684 info: Info,
685 _tx_pin: Peri<'a, AnyPin>,
686 _tx_dma: Option<Channel<'a>>,
687 mode: PhantomData<(&'a (), M)>,
688}
689
690/// Lpuart Rx driver.
691pub struct LpuartRx<'a, M: Mode> {
692 info: Info,
693 _rx_pin: Peri<'a, AnyPin>,
694 _rx_dma: Option<Channel<'a>>,
695 mode: PhantomData<(&'a (), M)>,
696}
697
698// ============================================================================
699// LPUART CORE IMPLEMENTATION
700// ============================================================================
701
702impl<'a, M: Mode> Lpuart<'a, M> {
703 fn init<T: Instance>(
704 _tx: Option<&Peri<'a, AnyPin>>,
705 _rx: Option<&Peri<'a, AnyPin>>,
706 _rts: Option<&Peri<'a, AnyPin>>,
707 _cts: Option<&Peri<'a, AnyPin>>,
708 config: Config,
709 ) -> Result<()> {
710 let regs = T::info().regs;
711
712 // Perform initialization sequence
713 perform_software_reset(regs);
714 disable_transceiver(regs);
715 configure_baudrate(regs, config.baudrate_bps, config.clock)?;
716 configure_frame_format(regs, &config);
717 configure_control_settings(regs, &config);
718 configure_fifo(regs, &config);
719 clear_all_status_flags(regs);
720 configure_flow_control(regs, &config);
721 configure_bit_order(regs, config.msb_firs);
722 enable_transceiver(regs, config.enable_tx, config.enable_rx);
723
724 Ok(())
725 }
726
727 /// Deinitialize the LPUART peripheral
728 pub fn deinit(&self) -> Result<()> {
729 let regs = self.info.regs;
730
731 // Wait for TX operations to complete
732 wait_for_tx_complete(regs);
733
734 // Clear all status flags
735 clear_all_status_flags(regs);
736
737 // Disable the module - clear all CTRL register bits
738 regs.ctrl().reset();
739
740 Ok(())
741 }
742
743 /// Split the Lpuart into a transmitter and receiver
744 pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) {
745 (self.tx, self.rx)
746 }
747
748 /// Split the Lpuart into a transmitter and receiver by mutable reference
749 pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) {
750 (&mut self.tx, &mut self.rx)
751 }
752}
753
754// ============================================================================
755// BLOCKING MODE IMPLEMENTATIONS
756// ============================================================================
757
758impl<'a> Lpuart<'a, Blocking> {
759 /// Create a new blocking LPUART instance with TX and RX pins.
760 pub fn new_blocking<T: Instance>(
761 _inner: Peri<'a, T>,
762 tx_pin: Peri<'a, impl TxPin<T>>,
763 rx_pin: Peri<'a, impl RxPin<T>>,
764 config: Config,
765 ) -> Result<Self> {
766 // Configure the pins for LPUART usage
767 tx_pin.as_tx();
768 rx_pin.as_rx();
769
770 // Convert pins to AnyPin
771 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
772 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
773
774 // Initialize the peripheral
775 Self::init::<T>(Some(&tx_pin), Some(&rx_pin), None, None, config)?;
776
777 Ok(Self {
778 info: T::info(),
779 tx: LpuartTx::new_inner(T::info(), tx_pin, None),
780 rx: LpuartRx::new_inner(T::info(), rx_pin, None),
781 })
782 }
783}
784
785// ----------------------------------------------------------------------------
786// Blocking TX Implementation
787// ----------------------------------------------------------------------------
788
789impl<'a, M: Mode> LpuartTx<'a, M> {
790 fn new_inner(info: Info, tx_pin: Peri<'a, AnyPin>, tx_dma: Option<Channel<'a>>) -> Self {
791 Self {
792 info,
793 _tx_pin: tx_pin,
794 _tx_dma: tx_dma,
795 mode: PhantomData,
796 }
797 }
798}
799
800impl<'a> LpuartTx<'a, Blocking> {
801 /// Create a new blocking LPUART which can only send data.
802 pub fn new_blocking<T: Instance>(
803 _inner: Peri<'a, T>,
804 tx_pin: Peri<'a, impl TxPin<T>>,
805 config: Config,
806 ) -> Result<Self> {
807 tx_pin.as_tx();
808
809 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
810
811 Lpuart::<Blocking>::init::<T>(Some(&tx_pin), None, None, None, config)?;
812
813 Ok(Self::new_inner(T::info(), tx_pin, None))
814 }
815
816 fn write_byte_internal(&mut self, byte: u8) -> Result<()> {
817 self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) });
818
819 Ok(())
820 }
821
822 fn blocking_write_byte(&mut self, byte: u8) -> Result<()> {
823 while self.info.regs.stat().read().tdre().is_txdata() {}
824 self.write_byte_internal(byte)
825 }
826
827 fn write_byte(&mut self, byte: u8) -> Result<()> {
828 if self.info.regs.stat().read().tdre().is_txdata() {
829 Err(Error::TxFifoFull)
830 } else {
831 self.write_byte_internal(byte)
832 }
833 }
834
835 /// Write data to LPUART TX blocking execution until all data is sent.
836 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
837 for x in buf {
838 self.blocking_write_byte(*x)?;
839 }
840
841 Ok(())
842 }
843
844 /// Write data to LPUART TX without blocking.
845 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
846 for x in buf {
847 self.write_byte(*x)?;
848 }
849
850 Ok(())
851 }
852
853 /// Flush LPUART TX blocking execution until all data has been transmitted.
854 pub fn blocking_flush(&mut self) -> Result<()> {
855 while self.info.regs.water().read().txcount().bits() != 0 {
856 // Wait for TX FIFO to drain
857 }
858
859 // Wait for last character to shift out
860 while self.info.regs.stat().read().tc().is_active() {
861 // Wait for transmission to complete
862 }
863
864 Ok(())
865 }
866
867 /// Flush LPUART TX.
868 pub fn flush(&mut self) -> Result<()> {
869 // Check if TX FIFO is empty
870 if self.info.regs.water().read().txcount().bits() != 0 {
871 return Err(Error::TxBusy);
872 }
873
874 // Check if transmission is complete
875 if self.info.regs.stat().read().tc().is_active() {
876 return Err(Error::TxBusy);
877 }
878
879 Ok(())
880 }
881}
882
883// ----------------------------------------------------------------------------
884// Blocking RX Implementation
885// ----------------------------------------------------------------------------
886
887impl<'a, M: Mode> LpuartRx<'a, M> {
888 fn new_inner(info: Info, rx_pin: Peri<'a, AnyPin>, rx_dma: Option<Channel<'a>>) -> Self {
889 Self {
890 info,
891 _rx_pin: rx_pin,
892 _rx_dma: rx_dma,
893 mode: PhantomData,
894 }
895 }
896}
897
898impl<'a> LpuartRx<'a, Blocking> {
899 /// Create a new blocking LPUART which can only receive data.
900 pub fn new_blocking<T: Instance>(
901 _inner: Peri<'a, T>,
902 rx_pin: Peri<'a, impl RxPin<T>>,
903 config: Config,
904 ) -> Result<Self> {
905 rx_pin.as_rx();
906
907 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
908
909 Lpuart::<Blocking>::init::<T>(None, Some(&rx_pin), None, None, config)?;
910
911 Ok(Self::new_inner(T::info(), rx_pin, None))
912 }
913
914 fn read_byte_internal(&mut self) -> Result<u8> {
915 let data = self.info.regs.data().read();
916
917 Ok((data.bits() & 0xFF) as u8)
918 }
919
920 fn read_byte(&mut self) -> Result<u8> {
921 check_and_clear_rx_errors(self.info.regs)?;
922
923 if !has_data(self.info.regs) {
924 return Err(Error::RxFifoEmpty);
925 }
926
927 self.read_byte_internal()
928 }
929
930 fn blocking_read_byte(&mut self) -> Result<u8> {
931 loop {
932 if has_data(self.info.regs) {
933 return self.read_byte_internal();
934 }
935
936 check_and_clear_rx_errors(self.info.regs)?;
937 }
938 }
939
940 /// Read data from LPUART RX without blocking.
941 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
942 for byte in buf.iter_mut() {
943 *byte = self.read_byte()?;
944 }
945 Ok(())
946 }
947
948 /// Read data from LPUART RX blocking execution until the buffer is filled.
949 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
950 for byte in buf.iter_mut() {
951 *byte = self.blocking_read_byte()?;
952 }
953 Ok(())
954 }
955}
956
957impl<'a> Lpuart<'a, Blocking> {
958 /// Read data from LPUART RX blocking execution until the buffer is filled
959 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
960 self.rx.blocking_read(buf)
961 }
962
963 /// Read data from LPUART RX without blocking
964 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
965 self.rx.read(buf)
966 }
967
968 /// Write data to LPUART TX blocking execution until all data is sent
969 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
970 self.tx.blocking_write(buf)
971 }
972
973 /// Write data to LPUART TX without blocking
974 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
975 self.tx.write(buf)
976 }
977
978 /// Flush LPUART TX blocking execution until all data has been transmitted
979 pub fn blocking_flush(&mut self) -> Result<()> {
980 self.tx.blocking_flush()
981 }
982
983 /// Flush LPUART TX without blocking
984 pub fn flush(&mut self) -> Result<()> {
985 self.tx.flush()
986 }
987}
988
989// ============================================================================
990// ASYNC MODE IMPLEMENTATIONS
991// ============================================================================
992
993// TODO: Implement async mode for LPUART
994
995// ============================================================================
996// EMBEDDED-HAL 0.2 TRAIT IMPLEMENTATIONS
997// ============================================================================
998
999impl embedded_hal_02::serial::Read<u8> for LpuartRx<'_, Blocking> {
1000 type Error = Error;
1001
1002 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1003 let mut buf = [0; 1];
1004 match self.read(&mut buf) {
1005 Ok(_) => Ok(buf[0]),
1006 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1007 Err(e) => Err(nb::Error::Other(e)),
1008 }
1009 }
1010}
1011
1012impl embedded_hal_02::serial::Write<u8> for LpuartTx<'_, Blocking> {
1013 type Error = Error;
1014
1015 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1016 match self.write(&[word]) {
1017 Ok(_) => Ok(()),
1018 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1019 Err(e) => Err(nb::Error::Other(e)),
1020 }
1021 }
1022
1023 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1024 match self.flush() {
1025 Ok(_) => Ok(()),
1026 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1027 Err(e) => Err(nb::Error::Other(e)),
1028 }
1029 }
1030}
1031
1032impl embedded_hal_02::blocking::serial::Write<u8> for LpuartTx<'_, Blocking> {
1033 type Error = Error;
1034
1035 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1036 self.blocking_write(buffer)
1037 }
1038
1039 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1040 self.blocking_flush()
1041 }
1042}
1043
1044impl embedded_hal_02::serial::Read<u8> for Lpuart<'_, Blocking> {
1045 type Error = Error;
1046
1047 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1048 embedded_hal_02::serial::Read::read(&mut self.rx)
1049 }
1050}
1051
1052impl embedded_hal_02::serial::Write<u8> for Lpuart<'_, Blocking> {
1053 type Error = Error;
1054
1055 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1056 embedded_hal_02::serial::Write::write(&mut self.tx, word)
1057 }
1058
1059 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1060 embedded_hal_02::serial::Write::flush(&mut self.tx)
1061 }
1062}
1063
1064impl embedded_hal_02::blocking::serial::Write<u8> for Lpuart<'_, Blocking> {
1065 type Error = Error;
1066
1067 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1068 self.blocking_write(buffer)
1069 }
1070
1071 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1072 self.blocking_flush()
1073 }
1074}
1075
1076// ============================================================================
1077// EMBEDDED-HAL-NB TRAIT IMPLEMENTATIONS
1078// ============================================================================
1079
1080impl embedded_hal_nb::serial::Error for Error {
1081 fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
1082 match *self {
1083 Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
1084 Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
1085 Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
1086 Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
1087 _ => embedded_hal_nb::serial::ErrorKind::Other,
1088 }
1089 }
1090}
1091
1092impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> {
1093 type Error = Error;
1094}
1095
1096impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> {
1097 type Error = Error;
1098}
1099
1100impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> {
1101 type Error = Error;
1102}
1103
1104impl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> {
1105 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1106 let mut buf = [0; 1];
1107 match self.read(&mut buf) {
1108 Ok(_) => Ok(buf[0]),
1109 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1110 Err(e) => Err(nb::Error::Other(e)),
1111 }
1112 }
1113}
1114
1115impl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> {
1116 fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
1117 match self.write(&[word]) {
1118 Ok(_) => Ok(()),
1119 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1120 Err(e) => Err(nb::Error::Other(e)),
1121 }
1122 }
1123
1124 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1125 match self.flush() {
1126 Ok(_) => Ok(()),
1127 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1128 Err(e) => Err(nb::Error::Other(e)),
1129 }
1130 }
1131}
1132
1133impl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> {
1134 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1135 embedded_hal_nb::serial::Read::read(&mut self.rx)
1136 }
1137}
1138
1139impl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> {
1140 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
1141 embedded_hal_nb::serial::Write::write(&mut self.tx, char)
1142 }
1143
1144 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1145 embedded_hal_nb::serial::Write::flush(&mut self.tx)
1146 }
1147}
1148
1149// ============================================================================
1150// EMBEDDED-IO TRAIT IMPLEMENTATIONS
1151// ============================================================================
1152
1153impl embedded_io::Error for Error {
1154 fn kind(&self) -> embedded_io::ErrorKind {
1155 embedded_io::ErrorKind::Other
1156 }
1157}
1158
1159impl embedded_io::ErrorType for LpuartRx<'_, Blocking> {
1160 type Error = Error;
1161}
1162
1163impl embedded_io::ErrorType for LpuartTx<'_, Blocking> {
1164 type Error = Error;
1165}
1166
1167impl embedded_io::ErrorType for Lpuart<'_, Blocking> {
1168 type Error = Error;
1169}
1170
1171impl embedded_io::Read for LpuartRx<'_, Blocking> {
1172 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1173 self.blocking_read(buf).map(|_| buf.len())
1174 }
1175}
1176
1177impl embedded_io::Write for LpuartTx<'_, Blocking> {
1178 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1179 self.blocking_write(buf).map(|_| buf.len())
1180 }
1181
1182 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1183 self.blocking_flush()
1184 }
1185}
1186
1187impl embedded_io::Read for Lpuart<'_, Blocking> {
1188 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1189 embedded_io::Read::read(&mut self.rx, buf)
1190 }
1191}
1192
1193impl embedded_io::Write for Lpuart<'_, Blocking> {
1194 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1195 embedded_io::Write::write(&mut self.tx, buf)
1196 }
1197
1198 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1199 embedded_io::Write::flush(&mut self.tx)
1200 }
1201}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 7111536ff..000000000
--- a/src/main.rs
+++ /dev/null
@@ -1,18 +0,0 @@
1#![cfg_attr(target_os = "none", no_std)]
2#![cfg_attr(target_os = "none", no_main)]
3
4#[cfg(target_os = "none")]
5mod baremetal;
6
7#[cfg(not(target_os = "none"))]
8fn main() {
9 println!("Hello, world!");
10}
11
12#[cfg(test)]
13mod tests {
14 #[test]
15 fn it_works() {
16 assert_eq!(2 + 2, 4);
17 }
18}
diff --git a/src/ostimer.rs b/src/ostimer.rs
new file mode 100644
index 000000000..a4cab6970
--- /dev/null
+++ b/src/ostimer.rs
@@ -0,0 +1,678 @@
1//! # OSTIMER Driver with Robustness Features
2//!
3//! This module provides an async timer driver for the NXP MCXA276 OSTIMER peripheral
4//! with protection against race conditions and timer rollover issues.
5//!
6//! ## Features
7//!
8//! - Async timing with embassy-time integration
9//! - Gray code counter handling (42-bit counter)
10//! - Interrupt-driven wakeups
11//! - Configurable interrupt priority
12//! - **Race condition protection**: Critical sections and atomic operations
13//! - **Timer rollover handling**: Bounds checking and rollover prevention
14//!
15//! ## Clock Frequency Configuration
16//!
17//! The OSTIMER frequency depends on your system's clock configuration. You must provide
18//! the actual frequency when calling `time_driver::init()`.
19//!
20//! ## Race Condition Protection
21//! - Critical sections in interrupt handlers prevent concurrent access
22//! - Atomic register operations with memory barriers
23//! - Proper interrupt flag clearing and validation
24//!
25//! ## Timer Rollover Handling
26//! - Bounds checking prevents scheduling beyond timer capacity
27//! - Immediate wake for timestamps that would cause rollover issues
28#![allow(dead_code)]
29
30use core::sync::atomic::{AtomicBool, Ordering};
31
32use crate::interrupt::InterruptExt;
33use crate::pac;
34
35// PAC defines the shared RegisterBlock under `ostimer0`.
36type Regs = pac::ostimer0::RegisterBlock;
37
38// OSTIMER EVTIMER register layout constants
39/// Total width of the EVTIMER counter in bits (42 bits total)
40const EVTIMER_TOTAL_BITS: u32 = 42;
41/// Width of the low part of EVTIMER (bits 31:0)
42const EVTIMER_LO_BITS: u32 = 32;
43/// Width of the high part of EVTIMER (bits 41:32)
44const EVTIMER_HI_BITS: u32 = 10;
45/// Bit position where high part starts in the combined 64-bit value
46const EVTIMER_HI_SHIFT: u32 = 32;
47
48/// Bit mask for the high part of EVTIMER
49const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1;
50
51/// Maximum value for MATCH_L register (32-bit)
52const MATCH_L_MAX: u32 = u32::MAX;
53/// Maximum value for MATCH_H register (10-bit)
54const MATCH_H_MAX: u16 = EVTIMER_HI_MASK;
55
56/// Bit mask for extracting the low 32 bits from a 64-bit value
57const LOW_32_BIT_MASK: u64 = u32::MAX as u64;
58
59/// Gray code conversion bit shifts (most significant to least)
60const GRAY_CONVERSION_SHIFTS: [u32; 6] = [32, 16, 8, 4, 2, 1];
61
62/// Maximum timer value before rollover (2^42 - 1 ticks)
63/// Actual rollover time depends on the configured clock frequency
64const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1;
65
66/// Threshold for detecting timer rollover in comparisons (1 second at 1MHz)
67const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000;
68
69/// Common default interrupt priority for OSTIMER
70const DEFAULT_INTERRUPT_PRIORITY: u8 = 3;
71
72// Global alarm state for interrupt handling
73static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false);
74static mut ALARM_CALLBACK: Option<fn()> = None;
75static mut ALARM_FLAG: Option<*const AtomicBool> = None;
76static mut ALARM_TARGET_TIME: u64 = 0;
77
78/// Number of tight spin iterations between elapsed time checks while waiting for MATCH writes to return to the idle (0) state.
79const MATCH_WRITE_READY_SPINS: usize = 512;
80/// Maximum time (in OSTIMER ticks) to wait for MATCH registers to become writable (~5 ms at 1 MHz).
81const MATCH_WRITE_READY_TIMEOUT_TICKS: u64 = 5_000;
82/// Short stabilization delay executed after toggling the MRCC reset line to let the OSTIMER bus interface settle.
83const RESET_STABILIZE_SPINS: usize = 512;
84
85pub(super) fn wait_for_match_write_ready(r: &Regs) -> bool {
86 let start = now_ticks_read();
87 let mut spin_budget = 0usize;
88
89 loop {
90 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
91 return true;
92 }
93
94 cortex_m::asm::nop();
95 spin_budget += 1;
96
97 if spin_budget >= MATCH_WRITE_READY_SPINS {
98 spin_budget = 0;
99
100 let elapsed = now_ticks_read().wrapping_sub(start);
101 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
102 return false;
103 }
104 }
105 }
106}
107
108pub(super) fn wait_for_match_write_complete(r: &Regs) -> bool {
109 let start = now_ticks_read();
110 let mut spin_budget = 0usize;
111
112 loop {
113 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
114 return true;
115 }
116
117 cortex_m::asm::nop();
118 spin_budget += 1;
119
120 if spin_budget >= MATCH_WRITE_READY_SPINS {
121 spin_budget = 0;
122
123 let elapsed = now_ticks_read().wrapping_sub(start);
124 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
125 return false;
126 }
127 }
128 }
129}
130
131fn prime_match_registers(r: &Regs) {
132 // Disable the interrupt, clear any pending flag, then wait until the MATCH registers are writable.
133 r.osevent_ctrl()
134 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
135
136 if wait_for_match_write_ready(r) {
137 r.match_l().write(|w| unsafe { w.match_value().bits(MATCH_L_MAX) });
138 r.match_h().write(|w| unsafe { w.match_value().bits(MATCH_H_MAX) });
139 let _ = wait_for_match_write_complete(r);
140 }
141}
142
143/// Single-shot alarm functionality for OSTIMER
144pub struct Alarm<'d> {
145 /// Whether the alarm is currently active
146 active: AtomicBool,
147 /// Callback to execute when alarm expires (optional)
148 callback: Option<fn()>,
149 /// Flag that gets set when alarm expires (optional)
150 flag: Option<&'d AtomicBool>,
151 _phantom: core::marker::PhantomData<&'d mut ()>,
152}
153
154impl<'d> Alarm<'d> {
155 /// Create a new alarm instance
156 pub fn new() -> Self {
157 Self {
158 active: AtomicBool::new(false),
159 callback: None,
160 flag: None,
161 _phantom: core::marker::PhantomData,
162 }
163 }
164
165 /// Set a callback that will be executed when the alarm expires
166 /// Note: Due to interrupt handler constraints, callbacks must be static function pointers
167 pub fn with_callback(mut self, callback: fn()) -> Self {
168 self.callback = Some(callback);
169 self
170 }
171
172 /// Set a flag that will be set to true when the alarm expires
173 pub fn with_flag(mut self, flag: &'d AtomicBool) -> Self {
174 self.flag = Some(flag);
175 self
176 }
177
178 /// Check if the alarm is currently active
179 pub fn is_active(&self) -> bool {
180 self.active.load(Ordering::Acquire)
181 }
182
183 /// Cancel the alarm if it's active
184 pub fn cancel(&self) {
185 self.active.store(false, Ordering::Release);
186 }
187}
188
189/// Configuration for Ostimer::new()
190#[derive(Copy, Clone)]
191pub struct Config {
192 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag.
193 pub init_match_max: bool,
194 /// OSTIMER clock frequency in Hz (must match the actual hardware clock)
195 pub clock_frequency_hz: u64,
196}
197
198impl Default for Config {
199 fn default() -> Self {
200 Self {
201 init_match_max: true,
202 // Default to 1MHz - user should override this with actual frequency
203 clock_frequency_hz: 1_000_000,
204 }
205 }
206}
207
208/// OSTIMER peripheral instance
209pub struct Ostimer<'d, I: Instance> {
210 _inst: core::marker::PhantomData<I>,
211 clock_frequency_hz: u64,
212 _phantom: core::marker::PhantomData<&'d mut ()>,
213}
214
215impl<'d, I: Instance> Ostimer<'d, I> {
216 /// Construct OSTIMER handle.
217 /// Requires clocks for the instance to be enabled by the board before calling.
218 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation.
219 pub fn new(_inst: impl Instance, cfg: Config, _p: &'d crate::pac::Peripherals) -> Self {
220 assert!(cfg.clock_frequency_hz > 0, "OSTIMER frequency must be greater than 0");
221
222 if cfg.init_match_max {
223 let r: &Regs = unsafe { &*I::ptr() };
224 // Mask INTENA, clear pending flag, and set MATCH to max so no spurious IRQ fires.
225 prime_match_registers(r);
226 }
227
228 Self {
229 _inst: core::marker::PhantomData,
230 clock_frequency_hz: cfg.clock_frequency_hz,
231 _phantom: core::marker::PhantomData,
232 }
233 }
234
235 /// Get the configured clock frequency in Hz
236 pub fn clock_frequency_hz(&self) -> u64 {
237 self.clock_frequency_hz
238 }
239
240 /// Read the current timer counter value in timer ticks
241 ///
242 /// # Returns
243 /// Current timer counter value as a 64-bit unsigned integer
244 pub fn now(&self) -> u64 {
245 now_ticks_read()
246 }
247
248 /// Reset the timer counter to zero
249 ///
250 /// This performs a hardware reset of the OSTIMER peripheral, which will reset
251 /// the counter to zero and clear any pending interrupts. Note that this will
252 /// affect all timer operations including embassy-time.
253 ///
254 /// # Safety
255 /// This operation will reset the entire OSTIMER peripheral. Any active alarms
256 /// or time_driver operations will be disrupted. Use with caution.
257 pub fn reset(&self, peripherals: &crate::pac::Peripherals) {
258 critical_section::with(|_| {
259 let r: &Regs = unsafe { &*I::ptr() };
260
261 // Mask the peripheral interrupt flag before we toggle the reset line so that
262 // no new NVIC activity races with the reset sequence.
263 r.osevent_ctrl()
264 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
265
266 unsafe {
267 crate::reset::assert::<crate::reset::line::Ostimer0>(peripherals);
268 }
269
270 for _ in 0..RESET_STABILIZE_SPINS {
271 cortex_m::asm::nop();
272 }
273
274 unsafe {
275 crate::reset::release::<crate::reset::line::Ostimer0>(peripherals);
276 }
277
278 while !<crate::reset::line::Ostimer0 as crate::reset::ResetLine>::is_released(&peripherals.mrcc0) {
279 cortex_m::asm::nop();
280 }
281
282 for _ in 0..RESET_STABILIZE_SPINS {
283 cortex_m::asm::nop();
284 }
285
286 // Clear alarm bookkeeping before re-arming MATCH registers.
287 ALARM_ACTIVE.store(false, Ordering::Release);
288 unsafe {
289 ALARM_TARGET_TIME = 0;
290 ALARM_CALLBACK = None;
291 ALARM_FLAG = None;
292 }
293
294 prime_match_registers(r);
295 });
296
297 // Ensure no stale OS_EVENT request remains pending after the reset sequence.
298 crate::interrupt::OS_EVENT.unpend();
299 }
300
301 /// Schedule a single-shot alarm to expire after the specified delay in microseconds
302 ///
303 /// # Parameters
304 /// * `alarm` - The alarm instance to schedule
305 /// * `delay_us` - Delay in microseconds from now
306 ///
307 /// # Returns
308 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
309 pub fn schedule_alarm_delay(&self, alarm: &Alarm, delay_us: u64) -> bool {
310 let delay_ticks = (delay_us * self.clock_frequency_hz) / 1_000_000;
311 let target_time = now_ticks_read() + delay_ticks;
312 self.schedule_alarm_at(alarm, target_time)
313 }
314
315 /// Schedule a single-shot alarm to expire at the specified absolute time in timer ticks
316 ///
317 /// # Parameters
318 /// * `alarm` - The alarm instance to schedule
319 /// * `target_ticks` - Absolute time in timer ticks when the alarm should expire
320 ///
321 /// # Returns
322 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
323 pub fn schedule_alarm_at(&self, alarm: &Alarm, target_ticks: u64) -> bool {
324 let now = now_ticks_read();
325
326 // Check if target time is in the past
327 if target_ticks <= now {
328 // Execute callback immediately if alarm was supposed to be active
329 if alarm.active.load(Ordering::Acquire) {
330 alarm.active.store(false, Ordering::Release);
331 if let Some(callback) = alarm.callback {
332 callback();
333 }
334 if let Some(flag) = &alarm.flag {
335 flag.store(true, Ordering::Release);
336 }
337 }
338 return true;
339 }
340
341 // Check for timer rollover
342 let max_future = now + TIMER_MAX_VALUE;
343 if target_ticks > max_future {
344 return false; // Would exceed timer capacity
345 }
346
347 // Program the timer
348 let r: &Regs = unsafe { &*I::ptr() };
349
350 critical_section::with(|_| {
351 // Disable interrupt and clear flag
352 r.osevent_ctrl()
353 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
354
355 if !wait_for_match_write_ready(r) {
356 prime_match_registers(r);
357
358 if !wait_for_match_write_ready(r) {
359 alarm.active.store(false, Ordering::Release);
360 ALARM_ACTIVE.store(false, Ordering::Release);
361 unsafe {
362 ALARM_TARGET_TIME = 0;
363 ALARM_CALLBACK = None;
364 ALARM_FLAG = None;
365 }
366 return false;
367 }
368 }
369
370 // Mark alarm as active now that we know the MATCH registers are writable
371 alarm.active.store(true, Ordering::Release);
372
373 // Set global alarm state for interrupt handler
374 ALARM_ACTIVE.store(true, Ordering::Release);
375 unsafe {
376 ALARM_TARGET_TIME = target_ticks;
377 ALARM_CALLBACK = alarm.callback;
378 ALARM_FLAG = alarm.flag.map(|f| f as *const AtomicBool);
379 }
380
381 // Program MATCH registers (Gray-coded)
382 let gray = bin_to_gray(target_ticks);
383 let l = (gray & LOW_32_BIT_MASK) as u32;
384 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
385
386 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
387 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
388
389 if !wait_for_match_write_complete(r) {
390 alarm.active.store(false, Ordering::Release);
391 ALARM_ACTIVE.store(false, Ordering::Release);
392 unsafe {
393 ALARM_TARGET_TIME = 0;
394 ALARM_CALLBACK = None;
395 ALARM_FLAG = None;
396 }
397 return false;
398 }
399
400 let now_after_program = now_ticks_read();
401 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
402 if now_after_program >= target_ticks && !intrflag_set {
403 alarm.active.store(false, Ordering::Release);
404 ALARM_ACTIVE.store(false, Ordering::Release);
405 unsafe {
406 ALARM_TARGET_TIME = 0;
407 ALARM_CALLBACK = None;
408 ALARM_FLAG = None;
409 }
410 return false;
411 }
412
413 // Enable interrupt
414 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
415
416 true
417 })
418 }
419
420 /// Cancel any active alarm
421 pub fn cancel_alarm(&self, alarm: &Alarm) {
422 critical_section::with(|_| {
423 alarm.cancel();
424
425 // Clear global alarm state
426 ALARM_ACTIVE.store(false, Ordering::Release);
427 unsafe { ALARM_TARGET_TIME = 0 };
428
429 // Reset MATCH registers to maximum values to prevent spurious interrupts
430 let r: &Regs = unsafe { &*I::ptr() };
431 prime_match_registers(r);
432 });
433 }
434
435 /// Check if an alarm has expired (call this from your interrupt handler)
436 /// Returns true if the alarm was active and has now expired
437 pub fn check_alarm_expired(&self, alarm: &Alarm) -> bool {
438 if alarm.active.load(Ordering::Acquire) {
439 alarm.active.store(false, Ordering::Release);
440
441 // Execute callback
442 if let Some(callback) = alarm.callback {
443 callback();
444 }
445
446 // Set flag
447 if let Some(flag) = &alarm.flag {
448 flag.store(true, Ordering::Release);
449 }
450
451 true
452 } else {
453 false
454 }
455 }
456}
457
458/// Read current EVTIMER (Gray-coded) and convert to binary ticks.
459#[inline(always)]
460fn now_ticks_read() -> u64 {
461 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
462
463 // Read high then low to minimize incoherent snapshots
464 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64);
465 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64;
466
467 // Combine and convert from Gray code to binary
468 let gray = lo | (hi << EVTIMER_HI_SHIFT);
469 gray_to_bin(gray)
470}
471
472// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance
473pub trait Instance {
474 fn ptr() -> *const Regs;
475}
476
477// Token for OSTIMER0 provided by embassy-hal-internal peripherals macro.
478pub type Ostimer0 = crate::peripherals::OSTIMER0;
479
480impl Instance for crate::peripherals::OSTIMER0 {
481 #[inline(always)]
482 fn ptr() -> *const Regs {
483 pac::Ostimer0::ptr()
484 }
485}
486
487// Also implement Instance for the Peri wrapper type
488impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> {
489 #[inline(always)]
490 fn ptr() -> *const Regs {
491 pac::Ostimer0::ptr()
492 }
493}
494
495#[inline(always)]
496fn bin_to_gray(x: u64) -> u64 {
497 x ^ (x >> 1)
498}
499
500#[inline(always)]
501fn gray_to_bin(gray: u64) -> u64 {
502 // More efficient iterative conversion using predefined shifts
503 let mut bin = gray;
504 for &shift in &GRAY_CONVERSION_SHIFTS {
505 bin ^= bin >> shift;
506 }
507 bin
508}
509
510pub mod time_driver {
511 use core::sync::atomic::Ordering;
512 use core::task::Waker;
513
514 use embassy_sync::waitqueue::AtomicWaker;
515 use embassy_time_driver as etd;
516
517 use super::{
518 bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME,
519 EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK,
520 };
521 use crate::pac;
522 pub struct Driver;
523 static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
524
525 impl etd::Driver for Driver {
526 fn now(&self) -> u64 {
527 // Use the hardware counter (frequency configured in init)
528 super::now_ticks_read()
529 }
530
531 fn schedule_wake(&self, timestamp: u64, waker: &Waker) {
532 let now = self.now();
533
534 // If timestamp is in the past or very close to now, wake immediately
535 if timestamp <= now {
536 waker.wake_by_ref();
537 return;
538 }
539
540 // Prevent scheduling too far in the future (beyond timer rollover)
541 // This prevents wraparound issues
542 let max_future = now + super::TIMER_MAX_VALUE;
543 if timestamp > max_future {
544 // For very long timeouts, wake immediately to avoid rollover issues
545 waker.wake_by_ref();
546 return;
547 }
548
549 // Register the waker first so any immediate wake below is observed by the executor.
550 TIMER_WAKER.register(waker);
551
552 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
553
554 critical_section::with(|_| {
555 // Mask INTENA and clear flag
556 r.osevent_ctrl()
557 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
558
559 // Read back to ensure W1C took effect on hardware
560 let _ = r.osevent_ctrl().read().ostimer_intrflag().bit();
561
562 if !super::wait_for_match_write_ready(r) {
563 super::prime_match_registers(r);
564
565 if !super::wait_for_match_write_ready(r) {
566 // If we can't safely program MATCH, wake immediately and leave INTENA masked.
567 waker.wake_by_ref();
568 return;
569 }
570 }
571
572 // Program MATCH (Gray-coded). Write low then high, then fence.
573 let gray = bin_to_gray(timestamp);
574 let l = (gray & LOW_32_BIT_MASK) as u32;
575
576 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
577
578 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
579 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
580
581 if !super::wait_for_match_write_complete(r) {
582 waker.wake_by_ref();
583 return;
584 }
585
586 let now_after_program = super::now_ticks_read();
587 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
588 if now_after_program >= timestamp && !intrflag_set {
589 waker.wake_by_ref();
590 return;
591 }
592
593 // Enable peripheral interrupt
594 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
595 });
596 }
597 }
598
599 /// Install the global embassy-time driver and configure NVIC priority for OS_EVENT.
600 ///
601 /// # Parameters
602 /// * `priority` - Interrupt priority for the OSTIMER interrupt
603 /// * `frequency_hz` - Actual OSTIMER clock frequency in Hz (stored for future use)
604 ///
605 /// Note: The frequency parameter is currently accepted for API compatibility.
606 /// The embassy_time_driver macro handles driver registration automatically.
607 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) {
608 // Mask/clear at peripheral and set default MATCH
609 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
610 super::prime_match_registers(r);
611
612 // Configure NVIC for timer operation
613 crate::interrupt::OS_EVENT.configure_for_timer(priority);
614
615 // Note: The embassy_time_driver macro automatically registers the driver
616 // The frequency parameter is accepted for future compatibility
617 let _ = frequency_hz; // Suppress unused parameter warning
618 }
619
620 // Export the global time driver expected by embassy-time
621 embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver);
622
623 /// To be called from the OS_EVENT IRQ.
624 pub fn on_interrupt() {
625 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
626
627 // Critical section to prevent races with schedule_wake
628 critical_section::with(|_| {
629 // Check if interrupt is actually pending and handle it atomically
630 if r.osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
631 // Clear flag and disable interrupt atomically
632 r.osevent_ctrl().write(|w| {
633 w.ostimer_intrflag()
634 .clear_bit_by_one() // Write-1-to-clear using safe helper
635 .ostimer_intena()
636 .clear_bit()
637 });
638
639 // Wake the waiting task
640 TIMER_WAKER.wake();
641
642 // Handle alarm callback if active and this interrupt is for the alarm
643 if ALARM_ACTIVE.load(Ordering::SeqCst) {
644 let current_time = now_ticks_read();
645 let target_time = unsafe { ALARM_TARGET_TIME };
646
647 // Check if current time is close to alarm target time (within 1000 ticks for timing variations)
648 if current_time >= target_time && current_time <= target_time + 1000 {
649 ALARM_ACTIVE.store(false, Ordering::SeqCst);
650 unsafe { ALARM_TARGET_TIME = 0 };
651
652 // Execute callback if set
653 unsafe {
654 if let Some(callback) = ALARM_CALLBACK {
655 callback();
656 }
657 }
658
659 // Set flag if provided
660 unsafe {
661 if let Some(flag) = ALARM_FLAG {
662 (*flag).store(true, Ordering::SeqCst);
663 }
664 }
665 }
666 }
667 }
668 });
669 }
670
671 /// Type-level handler to be used with bind_interrupts! for OS_EVENT.
672 pub struct OsEventHandler;
673 impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::OS_EVENT> for OsEventHandler {
674 unsafe fn on_interrupt() {
675 on_interrupt();
676 }
677 }
678}
diff --git a/src/pins.rs b/src/pins.rs
new file mode 100644
index 000000000..f802568f3
--- /dev/null
+++ b/src/pins.rs
@@ -0,0 +1,123 @@
1//! Pin configuration helpers (separate from peripheral drivers).
2use crate::pac;
3
4pub unsafe fn configure_uart2_pins_port2() {
5 // P2_2 = LPUART2_TX ALT3, P2_3 = LPUART2_RX ALT3 with pull-up, input enable, high drive, slow slew.
6 let port2 = &*pac::Port2::ptr();
7 port2.pcr2().write(|w| {
8 w.ps()
9 .ps1()
10 .pe()
11 .pe1()
12 .sre()
13 .sre1()
14 .dse()
15 .dse1()
16 .mux()
17 .mux11()
18 .ibe()
19 .ibe1()
20 });
21 port2.pcr3().write(|w| {
22 w.ps()
23 .ps1()
24 .pe()
25 .pe1()
26 .sre()
27 .sre1()
28 .dse()
29 .dse1()
30 .mux()
31 .mux11()
32 .ibe()
33 .ibe1()
34 });
35 core::arch::asm!("dsb sy; isb sy");
36}
37
38pub unsafe fn configure_adc_pins() {
39 // P1_10 = ADC1_A8
40 let port1 = &*pac::Port1::ptr();
41 port1.pcr10().write(|w| {
42 w.ps()
43 .ps0()
44 .pe()
45 .pe0()
46 .sre()
47 .sre0()
48 .ode()
49 .ode0()
50 .dse()
51 .dse0()
52 .mux()
53 .mux00()
54 .ibe()
55 .ibe0()
56 .inv()
57 .inv0()
58 .lk()
59 .lk0()
60 });
61 core::arch::asm!("dsb sy; isb sy");
62}
63
64/// Configure a pin for a specific mux alternative.
65///
66/// # Arguments
67/// * `port` - Port number (0-4)
68/// * `pin` - Pin number (varies by port: PORT0=0-7, PORT1=0-19, PORT2=0-26, PORT3=0-31, PORT4=0-7)
69/// * `mux` - Mux alternative (0-15, where 0 = GPIO, 1-15 = other functions)
70pub unsafe fn set_pin_mux(port: u8, pin: u8, mux: u8) {
71 // Validate mux value (0-15)
72 if mux > 15 {
73 panic!("Invalid mux value: {}, must be 0-15", mux);
74 }
75
76 // Validate pin number based on port
77 let max_pin = match port {
78 0 => 7, // PORT0: pins 0-7
79 1 => 19, // PORT1: pins 0-19
80 2 => 26, // PORT2: pins 0-26
81 3 => 31, // PORT3: pins 0-31
82 4 => 7, // PORT4: pins 0-7
83 _ => panic!("Unsupported GPIO port: {}", port),
84 };
85
86 if pin > max_pin {
87 panic!("Invalid pin {} for PORT{}, max pin is {}", pin, port, max_pin);
88 }
89
90 // Get the base address for the port
91 let port_base: *mut u32 = match port {
92 0 => pac::Port0::ptr() as *mut u32,
93 1 => pac::Port1::ptr() as *mut u32,
94 2 => pac::Port2::ptr() as *mut u32,
95 3 => pac::Port3::ptr() as *mut u32,
96 4 => pac::Port4::ptr() as *mut u32,
97 _ => panic!("Unsupported GPIO port: {}", port),
98 };
99
100 // PCR registers are 4 bytes apart, starting at offset 0 for PCR0
101 let pcr_addr = port_base.add(pin as usize);
102
103 // Read current PCR value
104 let current_val = pcr_addr.read_volatile();
105
106 // Clear mux bits (bits 8-11) and set new mux value
107 let new_val = (current_val & !(0xF << 8)) | ((mux as u32) << 8);
108
109 // Write back the new value
110 pcr_addr.write_volatile(new_val);
111
112 core::arch::asm!("dsb sy; isb sy");
113}
114
115/// Configure a pin for GPIO mode (ALT0).
116/// This is a convenience function that calls set_pin_mux with mux=0.
117///
118/// # Arguments
119/// * `port` - Port number (0-4)
120/// * `pin` - Pin number (varies by port: PORT0=0-7, PORT1=0-19, PORT2=0-26, PORT3=0-31, PORT4=0-7)
121pub unsafe fn set_pin_mux_gpio(port: u8, pin: u8) {
122 set_pin_mux(port, pin, 0);
123}
diff --git a/src/reset.rs b/src/reset.rs
new file mode 100644
index 000000000..1c131d1cc
--- /dev/null
+++ b/src/reset.rs
@@ -0,0 +1,112 @@
1//! Reset control helpers built on PAC field writers.
2use crate::pac;
3
4/// Trait describing a reset line that can be asserted/deasserted.
5pub trait ResetLine {
6 /// Drive the peripheral out of reset.
7 unsafe fn release(mrcc: &pac::mrcc0::RegisterBlock);
8
9 /// Drive the peripheral into reset.
10 unsafe fn assert(mrcc: &pac::mrcc0::RegisterBlock);
11
12 /// Check whether the peripheral is currently released.
13 fn is_released(mrcc: &pac::mrcc0::RegisterBlock) -> bool;
14}
15
16/// Release a reset line for the given peripheral set.
17#[inline]
18pub unsafe fn release<R: ResetLine>(peripherals: &pac::Peripherals) {
19 R::release(&peripherals.mrcc0);
20}
21
22/// Assert a reset line for the given peripheral set.
23#[inline]
24pub unsafe fn assert<R: ResetLine>(peripherals: &pac::Peripherals) {
25 R::assert(&peripherals.mrcc0);
26}
27
28/// Pulse a reset line (assert then release) with a short delay.
29#[inline]
30pub unsafe fn pulse<R: ResetLine>(peripherals: &pac::Peripherals) {
31 let mrcc = &peripherals.mrcc0;
32 R::assert(mrcc);
33 cortex_m::asm::nop();
34 cortex_m::asm::nop();
35 R::release(mrcc);
36}
37
38macro_rules! impl_reset_line {
39 ($name:ident, $reg:ident, $field:ident) => {
40 pub struct $name;
41
42 impl ResetLine for $name {
43 #[inline]
44 unsafe fn release(mrcc: &pac::mrcc0::RegisterBlock) {
45 mrcc.$reg().modify(|_, w| w.$field().enabled());
46 }
47
48 #[inline]
49 unsafe fn assert(mrcc: &pac::mrcc0::RegisterBlock) {
50 mrcc.$reg().modify(|_, w| w.$field().disabled());
51 }
52
53 #[inline]
54 fn is_released(mrcc: &pac::mrcc0::RegisterBlock) -> bool {
55 mrcc.$reg().read().$field().is_enabled()
56 }
57 }
58 };
59}
60
61pub mod line {
62 use super::*;
63
64 impl_reset_line!(Port2, mrcc_glb_rst1, port2);
65 impl_reset_line!(Port3, mrcc_glb_rst1, port3);
66 impl_reset_line!(Gpio3, mrcc_glb_rst2, gpio3);
67 impl_reset_line!(Lpuart2, mrcc_glb_rst0, lpuart2);
68 impl_reset_line!(Ostimer0, mrcc_glb_rst1, ostimer0);
69 impl_reset_line!(Port1, mrcc_glb_rst1, port1);
70 impl_reset_line!(Adc1, mrcc_glb_rst1, adc1);
71}
72
73#[inline]
74pub unsafe fn release_reset_port2(peripherals: &pac::Peripherals) {
75 release::<line::Port2>(peripherals);
76}
77
78#[inline]
79pub unsafe fn release_reset_port3(peripherals: &pac::Peripherals) {
80 release::<line::Port3>(peripherals);
81}
82
83#[inline]
84pub unsafe fn release_reset_gpio3(peripherals: &pac::Peripherals) {
85 release::<line::Gpio3>(peripherals);
86}
87
88#[inline]
89pub unsafe fn release_reset_lpuart2(peripherals: &pac::Peripherals) {
90 release::<line::Lpuart2>(peripherals);
91}
92
93#[inline]
94pub unsafe fn release_reset_ostimer0(peripherals: &pac::Peripherals) {
95 release::<line::Ostimer0>(peripherals);
96}
97
98/// Convenience shim retained for existing call sites.
99#[inline]
100pub unsafe fn reset_ostimer0(peripherals: &pac::Peripherals) {
101 pulse::<line::Ostimer0>(peripherals);
102}
103
104#[inline]
105pub unsafe fn release_reset_port1(peripherals: &pac::Peripherals) {
106 release::<line::Port1>(peripherals);
107}
108
109#[inline]
110pub unsafe fn release_reset_adc1(peripherals: &pac::Peripherals) {
111 release::<line::Adc1>(peripherals);
112}
diff --git a/src/rtc.rs b/src/rtc.rs
new file mode 100644
index 000000000..d62da1f0a
--- /dev/null
+++ b/src/rtc.rs
@@ -0,0 +1,284 @@
1//! RTC DateTime driver.
2use core::sync::atomic::{AtomicBool, Ordering};
3
4use crate::pac;
5use crate::pac::rtc0::cr::Um;
6
7type Regs = pac::rtc0::RegisterBlock;
8
9static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false);
10
11// Token-based instance pattern like embassy-imxrt
12pub trait Instance {
13 fn ptr() -> *const Regs;
14}
15
16/// Token for RTC0
17pub type Rtc0 = crate::peripherals::RTC0;
18impl Instance for crate::peripherals::RTC0 {
19 #[inline(always)]
20 fn ptr() -> *const Regs {
21 pac::Rtc0::ptr()
22 }
23}
24
25// Also implement Instance for the Peri wrapper type
26impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::RTC0> {
27 #[inline(always)]
28 fn ptr() -> *const Regs {
29 pac::Rtc0::ptr()
30 }
31}
32
33const DAYS_IN_A_YEAR: u32 = 365;
34const SECONDS_IN_A_DAY: u32 = 86400;
35const SECONDS_IN_A_HOUR: u32 = 3600;
36const SECONDS_IN_A_MINUTE: u32 = 60;
37const YEAR_RANGE_START: u16 = 1970;
38
39#[derive(Debug, Clone, Copy)]
40pub struct RtcDateTime {
41 pub year: u16,
42 pub month: u8,
43 pub day: u8,
44 pub hour: u8,
45 pub minute: u8,
46 pub second: u8,
47}
48#[derive(Copy, Clone)]
49pub struct RtcConfig {
50 #[allow(dead_code)]
51 wakeup_select: bool,
52 update_mode: Um,
53 #[allow(dead_code)]
54 supervisor_access: bool,
55 compensation_interval: u8,
56 compensation_time: u8,
57}
58
59#[derive(Copy, Clone)]
60pub struct RtcInterruptEnable;
61impl RtcInterruptEnable {
62 pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;
63 pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;
64 pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;
65 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
66}
67
68pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
69 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
70
71 let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;
72 seconds += (datetime.year as u32 / 4) - (1970 / 4);
73 seconds += month_days[datetime.month as usize] as u32;
74 seconds += datetime.day as u32 - 1;
75
76 if (datetime.year & 3 == 0) && (datetime.month <= 2) {
77 seconds -= 1;
78 }
79
80 seconds = seconds * SECONDS_IN_A_DAY
81 + (datetime.hour as u32 * SECONDS_IN_A_HOUR)
82 + (datetime.minute as u32 * SECONDS_IN_A_MINUTE)
83 + datetime.second as u32;
84
85 seconds
86}
87
88pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
89 let mut seconds_remaining = seconds;
90 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
91 seconds_remaining %= SECONDS_IN_A_DAY;
92
93 let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;
94 seconds_remaining %= SECONDS_IN_A_HOUR;
95 let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;
96 let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;
97
98 let mut year = YEAR_RANGE_START;
99 let mut days_in_year = DAYS_IN_A_YEAR;
100
101 while days > days_in_year {
102 days -= days_in_year;
103 year += 1;
104
105 days_in_year = if year % 4 == 0 {
106 DAYS_IN_A_YEAR + 1
107 } else {
108 DAYS_IN_A_YEAR
109 };
110 }
111
112 let mut days_per_month = [0u8, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
113 if year % 4 == 0 {
114 days_per_month[2] = 29;
115 }
116
117 let mut month = 1;
118 for m in 1..=12 {
119 if days <= days_per_month[m] as u32 {
120 month = m;
121 break;
122 } else {
123 days -= days_per_month[m] as u32;
124 }
125 }
126
127 let day = days as u8;
128
129 RtcDateTime {
130 year,
131 month: month as u8,
132 day,
133 hour,
134 minute,
135 second,
136 }
137}
138
139pub fn get_default_config() -> RtcConfig {
140 RtcConfig {
141 wakeup_select: false,
142 update_mode: Um::Um0,
143 supervisor_access: false,
144 compensation_interval: 0,
145 compensation_time: 0,
146 }
147}
148/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
149pub struct Rtc<I: Instance> {
150 _inst: core::marker::PhantomData<I>,
151}
152
153impl<I: Instance> Rtc<I> {
154 /// initialize RTC
155 pub fn new(_inst: impl Instance, config: RtcConfig) -> Self {
156 let rtc = unsafe { &*I::ptr() };
157
158 /* RTC reset */
159 rtc.cr().modify(|_, w| w.swr().set_bit());
160 rtc.cr().modify(|_, w| w.swr().clear_bit());
161 rtc.tsr().write(|w| unsafe { w.bits(1) });
162
163 rtc.cr().modify(|_, w| w.um().variant(config.update_mode));
164
165 rtc.tcr().modify(|_, w| unsafe {
166 w.cir()
167 .bits(config.compensation_interval)
168 .tcr()
169 .bits(config.compensation_time)
170 });
171
172 Self {
173 _inst: core::marker::PhantomData,
174 }
175 }
176
177 pub fn set_datetime(&self, datetime: RtcDateTime) {
178 let rtc = unsafe { &*I::ptr() };
179 let seconds = convert_datetime_to_seconds(&datetime);
180 rtc.tsr().write(|w| unsafe { w.bits(seconds) });
181 }
182
183 pub fn get_datetime(&self) -> RtcDateTime {
184 let rtc = unsafe { &*I::ptr() };
185 let seconds = rtc.tsr().read().bits();
186 convert_seconds_to_datetime(seconds)
187 }
188
189 pub fn set_alarm(&self, alarm: RtcDateTime) {
190 let rtc = unsafe { &*I::ptr() };
191 let seconds = convert_datetime_to_seconds(&alarm);
192
193 rtc.tar().write(|w| unsafe { w.bits(0) });
194 let mut timeout = 10000;
195 while rtc.tar().read().bits() != 0 && timeout > 0 {
196 timeout -= 1;
197 }
198
199 rtc.tar().write(|w| unsafe { w.bits(seconds) });
200
201 let mut timeout = 10000;
202 while rtc.tar().read().bits() != seconds && timeout > 0 {
203 timeout -= 1;
204 }
205 }
206
207 pub fn get_alarm(&self) -> RtcDateTime {
208 let rtc = unsafe { &*I::ptr() };
209 let alarm_seconds = rtc.tar().read().bits();
210 convert_seconds_to_datetime(alarm_seconds)
211 }
212
213 pub fn start(&self) {
214 let rtc = unsafe { &*I::ptr() };
215 rtc.sr().modify(|_, w| w.tce().set_bit());
216 }
217
218 pub fn stop(&self) {
219 let rtc = unsafe { &*I::ptr() };
220 rtc.sr().modify(|_, w| w.tce().clear_bit());
221 }
222
223 pub fn set_interrupt(&self, mask: u32) {
224 let rtc = unsafe { &*I::ptr() };
225
226 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
227 rtc.ier().modify(|_, w| w.tiie().tiie_1());
228 }
229 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
230 rtc.ier().modify(|_, w| w.toie().toie_1());
231 }
232 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
233 rtc.ier().modify(|_, w| w.taie().taie_1());
234 }
235 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
236 rtc.ier().modify(|_, w| w.tsie().tsie_1());
237 }
238
239 ALARM_TRIGGERED.store(false, Ordering::SeqCst);
240 }
241
242 pub fn disable_interrupt(&self, mask: u32) {
243 let rtc = unsafe { &*I::ptr() };
244
245 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
246 rtc.ier().modify(|_, w| w.tiie().tiie_0());
247 }
248 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
249 rtc.ier().modify(|_, w| w.toie().toie_0());
250 }
251 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
252 rtc.ier().modify(|_, w| w.taie().taie_0());
253 }
254 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
255 rtc.ier().modify(|_, w| w.tsie().tsie_0());
256 }
257 }
258
259 pub fn clear_alarm_flag(&self) {
260 let rtc = unsafe { &*I::ptr() };
261 rtc.ier().modify(|_, w| w.taie().clear_bit());
262 }
263
264 pub fn is_alarm_triggered(&self) -> bool {
265 ALARM_TRIGGERED.load(Ordering::Relaxed)
266 }
267}
268
269pub fn on_interrupt() {
270 let rtc = unsafe { &*pac::Rtc0::ptr() };
271 // Check if this is actually a time alarm interrupt
272 let sr = rtc.sr().read();
273 if sr.taf().bit_is_set() {
274 rtc.ier().modify(|_, w| w.taie().clear_bit());
275 ALARM_TRIGGERED.store(true, Ordering::SeqCst);
276 }
277}
278
279pub struct RtcHandler;
280impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC> for RtcHandler {
281 unsafe fn on_interrupt() {
282 on_interrupt();
283 }
284}
diff --git a/src/uart.rs b/src/uart.rs
new file mode 100644
index 000000000..3209a318d
--- /dev/null
+++ b/src/uart.rs
@@ -0,0 +1,306 @@
1//! Minimal polling UART2 bring-up replicating MCUXpresso hello_world ordering.
2//! WARNING: This is a narrow implementation only for debug console (115200 8N1).
3
4use core::cell::RefCell;
5
6use cortex_m::interrupt::Mutex;
7use embassy_sync::signal::Signal;
8
9use crate::pac;
10
11// svd2rust defines the shared LPUART RegisterBlock under lpuart0; all instances reuse it.
12type Regs = pac::lpuart0::RegisterBlock;
13
14// Token-based instance pattern like embassy-imxrt
15pub trait Instance {
16 fn ptr() -> *const Regs;
17}
18
19/// Token for LPUART2 provided by embassy-hal-internal peripherals macro.
20pub type Lpuart2 = crate::peripherals::LPUART2;
21impl Instance for crate::peripherals::LPUART2 {
22 #[inline(always)]
23 fn ptr() -> *const Regs {
24 pac::Lpuart2::ptr()
25 }
26}
27
28// Also implement Instance for the Peri wrapper type
29impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::LPUART2> {
30 #[inline(always)]
31 fn ptr() -> *const Regs {
32 pac::Lpuart2::ptr()
33 }
34}
35
36/// UART configuration (explicit src_hz; no hardcoded frequencies)
37#[derive(Copy, Clone)]
38pub struct Config {
39 pub src_hz: u32,
40 pub baud: u32,
41 pub parity: Parity,
42 pub stop_bits: StopBits,
43}
44
45#[derive(Copy, Clone)]
46pub enum Parity {
47 None,
48 Even,
49 Odd,
50}
51#[derive(Copy, Clone)]
52pub enum StopBits {
53 One,
54 Two,
55}
56
57impl Config {
58 pub fn new(src_hz: u32) -> Self {
59 Self {
60 src_hz,
61 baud: 115_200,
62 parity: Parity::None,
63 stop_bits: StopBits::One,
64 }
65 }
66}
67
68/// Compute a valid (OSR, SBR) tuple for given source clock and baud.
69/// Uses a functional fold approach to find the best OSR/SBR combination
70/// with minimal baud rate error.
71fn compute_osr_sbr(src_hz: u32, baud: u32) -> (u8, u16) {
72 let (best_osr, best_sbr, _best_err) = (8u32..=32).fold(
73 (16u8, 4u16, u32::MAX), // (best_osr, best_sbr, best_err)
74 |(best_osr, best_sbr, best_err), osr| {
75 let denom = baud.saturating_mul(osr);
76 if denom == 0 {
77 return (best_osr, best_sbr, best_err);
78 }
79
80 let sbr = (src_hz + denom / 2) / denom; // round
81 if sbr == 0 || sbr > 0x1FFF {
82 return (best_osr, best_sbr, best_err);
83 }
84
85 let actual = src_hz / (osr * sbr);
86 let err = actual.abs_diff(baud);
87
88 // Update best if this is better, or same error but higher OSR
89 if err < best_err || (err == best_err && osr as u8 > best_osr) {
90 (osr as u8, sbr as u16, err)
91 } else {
92 (best_osr, best_sbr, best_err)
93 }
94 },
95 );
96 (best_osr, best_sbr)
97}
98
99/// Minimal UART handle for a specific instance I (store the zero-sized token like embassy)
100pub struct Uart<I: Instance> {
101 _inst: core::marker::PhantomData<I>,
102}
103
104impl<I: Instance> Uart<I> {
105 /// Create and initialize LPUART (reset + config). Clocks and pins must be prepared by the caller.
106 pub fn new(_inst: impl Instance, cfg: Config) -> Self {
107 let l = unsafe { &*I::ptr() };
108 // 1) software reset pulse
109 l.global().write(|w| w.rst().reset());
110 cortex_m::asm::delay(3); // Short delay for reset to take effect
111 l.global().write(|w| w.rst().no_effect());
112 cortex_m::asm::delay(10); // Allow peripheral to stabilize after reset
113 // 2) BAUD
114 let (osr, sbr) = compute_osr_sbr(cfg.src_hz, cfg.baud);
115 l.baud().modify(|_, w| {
116 let w = match cfg.stop_bits {
117 StopBits::One => w.sbns().one(),
118 StopBits::Two => w.sbns().two(),
119 };
120 // OSR field encodes (osr-1); use raw bits to avoid a long match on all variants
121 let raw_osr = osr.saturating_sub(1) as u8;
122 unsafe { w.osr().bits(raw_osr).sbr().bits(sbr) }
123 });
124 // 3) CTRL baseline and parity
125 l.ctrl().write(|w| {
126 let w = w.ilt().from_stop().idlecfg().idle_2();
127 let w = match cfg.parity {
128 Parity::None => w.pe().disabled(),
129 Parity::Even => w.pe().enabled().pt().even(),
130 Parity::Odd => w.pe().enabled().pt().odd(),
131 };
132 w.re().enabled().te().enabled().rie().disabled()
133 });
134 // 4) FIFOs and WATER: keep it simple for polling; disable FIFOs and set RX watermark to 0
135 l.fifo().modify(|_, w| {
136 w.txfe()
137 .disabled()
138 .rxfe()
139 .disabled()
140 .txflush()
141 .txfifo_rst()
142 .rxflush()
143 .rxfifo_rst()
144 });
145 l.water()
146 .modify(|_, w| unsafe { w.txwater().bits(0).rxwater().bits(0) });
147 Self {
148 _inst: core::marker::PhantomData,
149 }
150 }
151
152 /// Enable RX interrupts. The caller must ensure an appropriate IRQ handler is installed.
153 pub unsafe fn enable_rx_interrupts(&self) {
154 let l = &*I::ptr();
155 l.ctrl().modify(|_, w| w.rie().enabled());
156 }
157
158 #[inline(never)]
159 pub fn write_byte(&self, b: u8) {
160 let l = unsafe { &*I::ptr() };
161 // Timeout after ~10ms at 12MHz (assuming 115200 baud, should be plenty)
162 const DATA_OFFSET: usize = 0x1C; // DATA register offset inside LPUART block
163 let data_ptr = unsafe { (I::ptr() as *mut u8).add(DATA_OFFSET) };
164 for _ in 0..120000 {
165 if l.water().read().txcount().bits() == 0 {
166 unsafe { core::ptr::write_volatile(data_ptr, b) };
167 return;
168 }
169 }
170 // If timeout, skip the write to avoid hanging
171 }
172
173 #[inline(never)]
174 pub fn write_str_blocking(&self, s: &str) {
175 for &b in s.as_bytes() {
176 if b == b'\n' {
177 self.write_byte(b'\r');
178 }
179 self.write_byte(b);
180 }
181 }
182 pub fn read_byte_blocking(&self) -> u8 {
183 let l = unsafe { &*I::ptr() };
184 while !l.stat().read().rdrf().is_rxdata() {}
185 (l.data().read().bits() & 0xFF) as u8
186 }
187}
188
189// Simple ring buffer for UART RX data
190const RX_BUFFER_SIZE: usize = 256;
191pub struct RingBuffer {
192 buffer: [u8; RX_BUFFER_SIZE],
193 read_idx: usize,
194 write_idx: usize,
195 count: usize,
196}
197
198impl RingBuffer {
199 pub const fn new() -> Self {
200 Self {
201 buffer: [0; RX_BUFFER_SIZE],
202 read_idx: 0,
203 write_idx: 0,
204 count: 0,
205 }
206 }
207
208 pub fn push(&mut self, data: u8) -> bool {
209 if self.count >= RX_BUFFER_SIZE {
210 return false; // Buffer full
211 }
212 self.buffer[self.write_idx] = data;
213 self.write_idx = (self.write_idx + 1) % RX_BUFFER_SIZE;
214 self.count += 1;
215 true
216 }
217
218 pub fn pop(&mut self) -> Option<u8> {
219 if self.count == 0 {
220 return None;
221 }
222 let data = self.buffer[self.read_idx];
223 self.read_idx = (self.read_idx + 1) % RX_BUFFER_SIZE;
224 self.count -= 1;
225 Some(data)
226 }
227
228 pub fn is_empty(&self) -> bool {
229 self.count == 0
230 }
231
232 pub fn len(&self) -> usize {
233 self.count
234 }
235}
236
237// Global RX buffer shared between interrupt handler and UART instance
238static RX_BUFFER: Mutex<RefCell<RingBuffer>> = Mutex::new(RefCell::new(RingBuffer::new()));
239static RX_SIGNAL: Signal<embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex, ()> = Signal::new();
240
241// Debug counter for interrupt handler calls
242static mut INTERRUPT_COUNT: u32 = 0;
243
244impl<I: Instance> Uart<I> {
245 /// Read a byte asynchronously using interrupts
246 pub async fn read_byte_async(&self) -> u8 {
247 loop {
248 // Check if we have data in the buffer
249 let byte = cortex_m::interrupt::free(|cs| {
250 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
251 buffer.pop()
252 });
253
254 if let Some(byte) = byte {
255 return byte;
256 }
257
258 // Wait for the interrupt signal
259 RX_SIGNAL.wait().await;
260 }
261 }
262
263 /// Check if there's data available in the RX buffer
264 pub fn rx_data_available(&self) -> bool {
265 cortex_m::interrupt::free(|cs| {
266 let buffer = RX_BUFFER.borrow(cs).borrow();
267 !buffer.is_empty()
268 })
269 }
270
271 /// Try to read a byte from RX buffer (non-blocking)
272 pub fn try_read_byte(&self) -> Option<u8> {
273 cortex_m::interrupt::free(|cs| {
274 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
275 buffer.pop()
276 })
277 }
278}
279
280/// Type-level handler for LPUART2 interrupts, compatible with bind_interrupts!.
281pub struct UartInterruptHandler;
282
283impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::LPUART2> for UartInterruptHandler {
284 unsafe fn on_interrupt() {
285 INTERRUPT_COUNT += 1;
286
287 let lpuart = &*pac::Lpuart2::ptr();
288
289 // Check if we have RX data
290 if lpuart.stat().read().rdrf().is_rxdata() {
291 // Read the data byte
292 let data = (lpuart.data().read().bits() & 0xFF) as u8;
293
294 // Store in ring buffer
295 cortex_m::interrupt::free(|cs| {
296 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
297 if buffer.push(data) {
298 // Data added successfully, signal waiting tasks
299 RX_SIGNAL.signal(());
300 }
301 });
302 }
303 // Always clear any error flags that might cause spurious interrupts
304 let _ = lpuart.stat().read();
305 }
306}