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