aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-12-05 12:03:00 +0000
committerGitHub <[email protected]>2025-12-05 12:03:00 +0000
commit4053e5233fab875c4607cd2fed7a7cf5659ce3e1 (patch)
tree9fd7d45b6c3f65c7eb62604ba3be625de3b4d3a5
parent5572b884a166b4012a250a12537c4cdd63b377c3 (diff)
parent2b607b8aae8cab98e41159f657ecd2c3bf790592 (diff)
Merge pull request #4989 from jamesmunns/james/upstream-mcxa
Upstream `embassy-mcxa`
-rw-r--r--docs/pages/mcxa.adoc18
-rw-r--r--embassy-mcxa/.gitignore19
-rw-r--r--embassy-mcxa/Cargo.toml61
-rw-r--r--embassy-mcxa/README.md5
-rw-r--r--embassy-mcxa/src/adc.rs409
-rw-r--r--embassy-mcxa/src/clkout.rs169
-rw-r--r--embassy-mcxa/src/clocks/config.rs204
-rw-r--r--embassy-mcxa/src/clocks/mod.rs943
-rw-r--r--embassy-mcxa/src/clocks/periph_helpers.rs498
-rw-r--r--embassy-mcxa/src/config.rs27
-rw-r--r--embassy-mcxa/src/gpio.rs1062
-rw-r--r--embassy-mcxa/src/i2c/controller.rs742
-rw-r--r--embassy-mcxa/src/i2c/mod.rs194
-rw-r--r--embassy-mcxa/src/interrupt.rs563
-rw-r--r--embassy-mcxa/src/lib.rs471
-rw-r--r--embassy-mcxa/src/lpuart/buffered.rs780
-rw-r--r--embassy-mcxa/src/lpuart/mod.rs1292
-rw-r--r--embassy-mcxa/src/ostimer.rs745
-rw-r--r--embassy-mcxa/src/pins.rs28
-rw-r--r--embassy-mcxa/src/rtc.rs453
-rw-r--r--examples/mcxa/.cargo/config.toml17
-rw-r--r--examples/mcxa/.gitignore1
-rw-r--r--examples/mcxa/Cargo.toml32
-rw-r--r--examples/mcxa/build.rs20
-rw-r--r--examples/mcxa/memory.x5
-rw-r--r--examples/mcxa/src/bin/adc_interrupt.rs84
-rw-r--r--examples/mcxa/src/bin/adc_polling.rs68
-rw-r--r--examples/mcxa/src/bin/blinky.rs36
-rw-r--r--examples/mcxa/src/bin/button.rs23
-rw-r--r--examples/mcxa/src/bin/button_async.rs29
-rw-r--r--examples/mcxa/src/bin/clkout.rs69
-rw-r--r--examples/mcxa/src/bin/hello.rs119
-rw-r--r--examples/mcxa/src/bin/i2c-async.rs39
-rw-r--r--examples/mcxa/src/bin/i2c-blocking.rs31
-rw-r--r--examples/mcxa/src/bin/i2c-scan-blocking.rs41
-rw-r--r--examples/mcxa/src/bin/lpuart_buffered.rs62
-rw-r--r--examples/mcxa/src/bin/lpuart_polling.rs47
-rw-r--r--examples/mcxa/src/bin/rtc_alarm.rs47
-rw-r--r--examples/mcxa/src/lib.rs16
39 files changed, 9469 insertions, 0 deletions
diff --git a/docs/pages/mcxa.adoc b/docs/pages/mcxa.adoc
new file mode 100644
index 000000000..e2284f45f
--- /dev/null
+++ b/docs/pages/mcxa.adoc
@@ -0,0 +1,18 @@
1= Embassy MCX-A HAL
2
3The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-mcxa[Embassy MCX-A HAL] is based on the following PAC (Peripheral Access Crate):
4
5* link:https://github.com/OpenDevicePartnership/mcxa-pac[mcxa-pac]
6
7== Peripherals
8
9The following peripherals have a HAL implementation at present
10
11* Clocks
12* GPIO
13* ADC
14* CLKOUT
15* I2C
16* LPUart
17* OSTimer
18* RTC
diff --git a/embassy-mcxa/.gitignore b/embassy-mcxa/.gitignore
new file mode 100644
index 000000000..d128a49cb
--- /dev/null
+++ b/embassy-mcxa/.gitignore
@@ -0,0 +1,19 @@
1# Rust
2/target/
3
4# IDE
5.vscode/
6.idea/
7
8# OS
9.DS_Store
10Thumbs.db
11
12# Embedded
13*.bin
14*.hex
15*.elf
16*.map
17
18# Debug
19*.log
diff --git a/embassy-mcxa/Cargo.toml b/embassy-mcxa/Cargo.toml
new file mode 100644
index 000000000..65328c751
--- /dev/null
+++ b/embassy-mcxa/Cargo.toml
@@ -0,0 +1,61 @@
1[package]
2name = "embassy-mcxa"
3version = "0.1.0"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6description = "Embassy Hardware Abstraction Layer (HAL) for NXP MCXA series of MCUs"
7repository = "https://github.com/embassy-rs/embassy"
8keywords = ["embedded", "hal", "nxp", "mcxa", "embassy"]
9categories = ["embedded", "hardware-support", "no-std"]
10documentation = "https://docs.embassy.dev/embassy-mcxa"
11
12[package.metadata.embassy]
13build = [
14 {target = "thumbv8m.main-none-eabihf", features = ["defmt", "time", "unstable-pac"]},
15 {target = "thumbv8m.main-none-eabihf", features = ["defmt", "time", "unstable-pac"]},
16]
17
18[dependencies]
19cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
20# If you would like "device" to be an optional feature, please open an issue.
21cortex-m-rt = { version = "0.7", features = ["device"] }
22critical-section = "1.2.0"
23defmt = { version = "1.0", optional = true }
24embassy-embedded-hal = "0.5.0"
25embassy-hal-internal = { version = "0.3.0", features = ["cortex-m", "prio-bits-3"] }
26embassy-sync = "0.7.2"
27embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
28embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
29embedded-hal-async = { version = "1.0" }
30embedded-hal-nb = { version = "1.0" }
31embedded-io = "0.6"
32embedded-io-async = { version = "0.6.1" }
33heapless = "0.8"
34mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "e7dfed8740b449b6ac646bab8ac6776a3c099267" }
35nb = "1.1.0"
36paste = "1.0.15"
37maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] }
38
39# `time` dependencies
40embassy-time = { version = "0.5.0", optional = true }
41embassy-time-driver = { version = "0.2.1", optional = true }
42
43[features]
44default = ["rt"]
45
46# Base defmt feature enables core + panic handler
47# Use with one logger feature: defmt-rtt (preferred) or defmt-uart (fallback)
48defmt = ["dep:defmt", "mcxa-pac/defmt"]
49
50unstable-pac = []
51
52# dummy feature to silence embassy-hal-internal lint
53#
54# This feature makes no change to embassy-mcxa's operation.
55rt = []
56
57# Embassy time
58time = [
59 "dep:embassy-time",
60 "dep:embassy-time-driver",
61]
diff --git a/embassy-mcxa/README.md b/embassy-mcxa/README.md
new file mode 100644
index 000000000..079eeb487
--- /dev/null
+++ b/embassy-mcxa/README.md
@@ -0,0 +1,5 @@
1# Embassy MCXA256 HAL
2
3A Hardware Abstraction Layer (HAL) for the NXP MCXA256 microcontroller
4using the Embassy async framework. This HAL provides safe, idiomatic
5Rust interfaces for GPIO, UART, and OSTIMER peripherals.
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs
new file mode 100644
index 000000000..7475299ba
--- /dev/null
+++ b/embassy-mcxa/src/adc.rs
@@ -0,0 +1,409 @@
1//! ADC driver
2use core::sync::atomic::{AtomicBool, Ordering};
3
4use embassy_hal_internal::{Peri, PeripheralType};
5
6use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4};
7use crate::clocks::{Gate, PoweredClock, enable_and_reset};
8use crate::pac;
9use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres};
10use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts};
11use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode};
12use crate::pac::adc1::ctrl::CalAvgs;
13use crate::pac::adc1::tctrl::{Tcmd, Tpri};
14
15type Regs = pac::adc1::RegisterBlock;
16
17static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false);
18// Token-based instance pattern like embassy-imxrt
19pub trait Instance: Gate<MrccPeriphConfig = AdcConfig> + PeripheralType {
20 fn ptr() -> *const Regs;
21}
22
23/// Token for ADC1
24pub type Adc1 = crate::peripherals::ADC1;
25impl Instance for crate::peripherals::ADC1 {
26 #[inline(always)]
27 fn ptr() -> *const Regs {
28 pac::Adc1::ptr()
29 }
30}
31
32// Also implement Instance for the Peri wrapper type
33// impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> {
34// #[inline(always)]
35// fn ptr() -> *const Regs {
36// pac::Adc1::ptr()
37// }
38// }
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[repr(u8)]
42pub enum TriggerPriorityPolicy {
43 ConvPreemptImmediatelyNotAutoResumed = 0,
44 ConvPreemptSoftlyNotAutoResumed = 1,
45 ConvPreemptImmediatelyAutoRestarted = 4,
46 ConvPreemptSoftlyAutoRestarted = 5,
47 ConvPreemptImmediatelyAutoResumed = 12,
48 ConvPreemptSoftlyAutoResumed = 13,
49 ConvPreemptSubsequentlyNotAutoResumed = 2,
50 ConvPreemptSubsequentlyAutoRestarted = 6,
51 ConvPreemptSubsequentlyAutoResumed = 14,
52 TriggerPriorityExceptionDisabled = 16,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct LpadcConfig {
57 pub enable_in_doze_mode: bool,
58 pub conversion_average_mode: CalAvgs,
59 pub enable_analog_preliminary: bool,
60 pub power_up_delay: u8,
61 pub reference_voltage_source: Refsel,
62 pub power_level_mode: Pwrsel,
63 pub trigger_priority_policy: TriggerPriorityPolicy,
64 pub enable_conv_pause: bool,
65 pub conv_pause_delay: u16,
66 pub fifo_watermark: u8,
67 pub power: PoweredClock,
68 pub source: AdcClockSel,
69 pub div: Div4,
70}
71
72impl Default for LpadcConfig {
73 fn default() -> Self {
74 LpadcConfig {
75 enable_in_doze_mode: true,
76 conversion_average_mode: CalAvgs::NoAverage,
77 enable_analog_preliminary: false,
78 power_up_delay: 0x80,
79 reference_voltage_source: Refsel::Option1,
80 power_level_mode: Pwrsel::Lowest,
81 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
82 enable_conv_pause: false,
83 conv_pause_delay: 0,
84 fifo_watermark: 0,
85 power: PoweredClock::NormalEnabledDeepSleepDisabled,
86 source: AdcClockSel::FroLfDiv,
87 div: Div4::no_div(),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub struct ConvCommandConfig {
94 pub sample_channel_mode: Ctype,
95 pub channel_number: Adch,
96 pub chained_next_command_number: Next,
97 pub enable_auto_channel_increment: bool,
98 pub loop_count: u8,
99 pub hardware_average_mode: Avgs,
100 pub sample_time_mode: Sts,
101 pub hardware_compare_mode: Cmpen,
102 pub hardware_compare_value_high: u32,
103 pub hardware_compare_value_low: u32,
104 pub conversion_resolution_mode: Mode,
105 pub enable_wait_trigger: bool,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct ConvTriggerConfig {
110 pub target_command_id: Tcmd,
111 pub delay_power: u8,
112 pub priority: Tpri,
113 pub enable_hardware_trigger: bool,
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub struct ConvResult {
118 pub command_id_source: u32,
119 pub loop_count_index: u32,
120 pub trigger_id_source: u32,
121 pub conv_value: u16,
122}
123
124pub struct Adc<'a, I: Instance> {
125 _inst: core::marker::PhantomData<&'a mut I>,
126}
127
128impl<'a, I: Instance> Adc<'a, I> {
129 /// initialize ADC
130 pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self {
131 let adc = unsafe { &*I::ptr() };
132
133 let _clock_freq = unsafe {
134 enable_and_reset::<I>(&AdcConfig {
135 power: config.power,
136 source: config.source,
137 div: config.div,
138 })
139 .expect("Adc Init should not fail")
140 };
141
142 /* Reset the module. */
143 adc.ctrl().modify(|_, w| w.rst().held_in_reset());
144 adc.ctrl().modify(|_, w| w.rst().released_from_reset());
145
146 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
147
148 /* Disable the module before setting configuration. */
149 adc.ctrl().modify(|_, w| w.adcen().disabled());
150
151 /* Configure the module generally. */
152 if config.enable_in_doze_mode {
153 adc.ctrl().modify(|_, w| w.dozen().enabled());
154 } else {
155 adc.ctrl().modify(|_, w| w.dozen().disabled());
156 }
157
158 /* Set calibration average mode. */
159 adc.ctrl()
160 .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode));
161
162 adc.cfg().write(|w| unsafe {
163 let w = if config.enable_analog_preliminary {
164 w.pwren().pre_enabled()
165 } else {
166 w
167 };
168
169 w.pudly()
170 .bits(config.power_up_delay)
171 .refsel()
172 .variant(config.reference_voltage_source)
173 .pwrsel()
174 .variant(config.power_level_mode)
175 .tprictrl()
176 .variant(match config.trigger_priority_policy {
177 TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed
178 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
179 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority,
180 TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed
181 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
182 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority,
183 _ => Tprictrl::AbortCurrentOnPriority,
184 })
185 .tres()
186 .variant(match config.trigger_priority_policy {
187 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted
188 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
189 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
190 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
191 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
192 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled,
193 _ => Tres::Disabled,
194 })
195 .tcmdres()
196 .variant(match config.trigger_priority_policy {
197 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
198 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
199 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed
200 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled,
201 _ => Tcmdres::Disabled,
202 })
203 .hpt_exdi()
204 .variant(match config.trigger_priority_policy {
205 TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled,
206 _ => HptExdi::Enabled,
207 })
208 });
209
210 if config.enable_conv_pause {
211 adc.pause()
212 .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) });
213 } else {
214 adc.pause().write(|w| unsafe { w.bits(0) });
215 }
216
217 adc.fctrl0()
218 .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) });
219
220 // Enable ADC
221 adc.ctrl().modify(|_, w| w.adcen().enabled());
222
223 Self {
224 _inst: core::marker::PhantomData,
225 }
226 }
227
228 pub fn deinit(&self) {
229 let adc = unsafe { &*I::ptr() };
230 adc.ctrl().modify(|_, w| w.adcen().disabled());
231 }
232
233 pub fn do_offset_calibration(&self) {
234 let adc = unsafe { &*I::ptr() };
235 // Enable calibration mode
236 adc.ctrl()
237 .modify(|_, w| w.calofs().offset_calibration_request_pending());
238
239 // Wait for calibration to complete (polling status register)
240 while adc.stat().read().cal_rdy().is_not_set() {}
241 }
242
243 pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 {
244 let mut gcra_array = [0u32; 17];
245 let mut gcalr: u32 = 0;
246
247 for i in (1..=17).rev() {
248 let shift = 16 - (i - 1);
249 let step = 1.0 / (1u32 << shift) as f32;
250 let tmp = (gain_adjustment / step) as u32;
251 gcra_array[i - 1] = tmp;
252 gain_adjustment -= tmp as f32 * step;
253 }
254
255 for i in (1..=17).rev() {
256 gcalr += gcra_array[i - 1] << (i - 1);
257 }
258 gcalr
259 }
260
261 pub fn do_auto_calibration(&self) {
262 let adc = unsafe { &*I::ptr() };
263 adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending());
264
265 while adc.gcc0().read().rdy().is_gain_cal_not_valid() {}
266
267 let mut gcca = adc.gcc0().read().gain_cal().bits() as u32;
268 if gcca & ((0xFFFF + 1) >> 1) != 0 {
269 gcca |= !0xFFFF;
270 }
271
272 let gcra = 131072.0 / (131072.0 - gcca as f32);
273
274 // Write to GCR0
275 adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) });
276
277 adc.gcr0().modify(|_, w| w.rdy().set_bit());
278
279 // Wait for calibration to complete (polling status register)
280 while adc.stat().read().cal_rdy().is_not_set() {}
281 }
282
283 pub fn do_software_trigger(&self, trigger_id_mask: u32) {
284 let adc = unsafe { &*I::ptr() };
285 adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) });
286 }
287
288 pub fn get_default_conv_command_config(&self) -> ConvCommandConfig {
289 ConvCommandConfig {
290 sample_channel_mode: Ctype::SingleEndedASideChannel,
291 channel_number: Adch::SelectCh0,
292 chained_next_command_number: Next::NoNextCmdTerminateOnFinish,
293 enable_auto_channel_increment: false,
294 loop_count: 0,
295 hardware_average_mode: Avgs::NoAverage,
296 sample_time_mode: Sts::Sample3p5,
297 hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult,
298 hardware_compare_value_high: 0,
299 hardware_compare_value_low: 0,
300 conversion_resolution_mode: Mode::Data12Bits,
301 enable_wait_trigger: false,
302 }
303 }
304
305 //TBD Need to add cmdlx and cmdhx with x {2..7}
306 pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) {
307 let adc = unsafe { &*I::ptr() };
308
309 match index {
310 1 => {
311 adc.cmdl1().write(|w| {
312 w.adch()
313 .variant(config.channel_number)
314 .mode()
315 .variant(config.conversion_resolution_mode)
316 });
317 adc.cmdh1().write(|w| unsafe {
318 w.next()
319 .variant(config.chained_next_command_number)
320 .loop_()
321 .bits(config.loop_count)
322 .avgs()
323 .variant(config.hardware_average_mode)
324 .sts()
325 .variant(config.sample_time_mode)
326 .cmpen()
327 .variant(config.hardware_compare_mode);
328 if config.enable_wait_trigger {
329 w.wait_trig().enabled();
330 }
331 if config.enable_auto_channel_increment {
332 w.lwi().enabled();
333 }
334 w
335 });
336 }
337 _ => panic!("Invalid command index: must be between 1 and 7"),
338 }
339 }
340
341 pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig {
342 ConvTriggerConfig {
343 target_command_id: Tcmd::NotValid,
344 delay_power: 0,
345 priority: Tpri::HighestPriority,
346 enable_hardware_trigger: false,
347 }
348 }
349
350 pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) {
351 let adc = unsafe { &*I::ptr() };
352 let tctrl = &adc.tctrl(trigger_id);
353
354 tctrl.write(|w| unsafe {
355 let w = w.tcmd().variant(config.target_command_id);
356 let w = w.tdly().bits(config.delay_power);
357 w.tpri().variant(config.priority);
358 if config.enable_hardware_trigger {
359 w.hten().enabled()
360 } else {
361 w
362 }
363 });
364 }
365
366 pub fn do_reset_fifo(&self) {
367 let adc = unsafe { &*I::ptr() };
368 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
369 }
370
371 pub fn enable_interrupt(&self, mask: u32) {
372 let adc = unsafe { &*I::ptr() };
373 adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
374 INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst);
375 }
376
377 pub fn is_interrupt_triggered(&self) -> bool {
378 INTERRUPT_TRIGGERED.load(Ordering::Relaxed)
379 }
380}
381
382pub fn get_conv_result() -> Option<ConvResult> {
383 let adc = unsafe { &*pac::Adc1::ptr() };
384 let fifo = adc.resfifo0().read().bits();
385 const VALID_MASK: u32 = 1 << 31;
386 if fifo & VALID_MASK == 0 {
387 return None;
388 }
389
390 Some(ConvResult {
391 command_id_source: (fifo >> 24) & 0x0F,
392 loop_count_index: (fifo >> 20) & 0x0F,
393 trigger_id_source: (fifo >> 16) & 0x0F,
394 conv_value: (fifo & 0xFFFF) as u16,
395 })
396}
397
398pub fn on_interrupt() {
399 if get_conv_result().is_some() {
400 INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst);
401 }
402}
403
404pub struct AdcHandler;
405impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::ADC1> for AdcHandler {
406 unsafe fn on_interrupt() {
407 on_interrupt();
408 }
409}
diff --git a/embassy-mcxa/src/clkout.rs b/embassy-mcxa/src/clkout.rs
new file mode 100644
index 000000000..5b21f24b0
--- /dev/null
+++ b/embassy-mcxa/src/clkout.rs
@@ -0,0 +1,169 @@
1//! CLKOUT pseudo-peripheral
2//!
3//! CLKOUT is a part of the clock generation subsystem, and can be used
4//! either to generate arbitrary waveforms, or to debug the state of
5//! internal oscillators.
6
7use core::marker::PhantomData;
8
9use embassy_hal_internal::Peri;
10
11pub use crate::clocks::periph_helpers::Div4;
12use crate::clocks::{ClockError, PoweredClock, with_clocks};
13use crate::pac::mrcc0::mrcc_clkout_clksel::Mux;
14use crate::peripherals::CLKOUT;
15
16/// A peripheral representing the CLKOUT pseudo-peripheral
17pub struct ClockOut<'a> {
18 _p: PhantomData<&'a mut CLKOUT>,
19 freq: u32,
20}
21
22/// Selected clock source to output
23pub enum ClockOutSel {
24 /// 12MHz Internal Oscillator
25 Fro12M,
26 /// FRO180M Internal Oscillator, via divisor
27 FroHfDiv,
28 /// External Oscillator
29 ClkIn,
30 /// 16KHz oscillator
31 Clk16K,
32 /// Output of PLL1
33 Pll1Clk,
34 /// Main System CPU clock, divided by 6
35 SlowClk,
36}
37
38/// Configuration for the ClockOut
39pub struct Config {
40 /// Selected Source Clock
41 pub sel: ClockOutSel,
42 /// Selected division level
43 pub div: Div4,
44 /// Selected power level
45 pub level: PoweredClock,
46}
47
48impl<'a> ClockOut<'a> {
49 /// Create a new ClockOut pin. On success, the clock signal will begin immediately
50 /// on the given pin.
51 pub fn new(
52 _peri: Peri<'a, CLKOUT>,
53 pin: Peri<'a, impl sealed::ClockOutPin>,
54 cfg: Config,
55 ) -> Result<Self, ClockError> {
56 // There's no MRCC enable bit, so we check the validity of the clocks here
57 //
58 // TODO: Should we check that the frequency is suitably low?
59 let (freq, mux) = check_sel(cfg.sel, cfg.level)?;
60
61 // All good! Apply requested config, starting with the pin.
62 pin.mux();
63
64 setup_clkout(mux, cfg.div);
65
66 Ok(Self {
67 _p: PhantomData,
68 freq: freq / cfg.div.into_divisor(),
69 })
70 }
71
72 /// Frequency of the clkout pin
73 #[inline]
74 pub fn frequency(&self) -> u32 {
75 self.freq
76 }
77}
78
79impl Drop for ClockOut<'_> {
80 fn drop(&mut self) {
81 disable_clkout();
82 }
83}
84
85/// Check whether the given clock selection is valid
86fn check_sel(sel: ClockOutSel, level: PoweredClock) -> Result<(u32, Mux), ClockError> {
87 let res = with_clocks(|c| {
88 Ok(match sel {
89 ClockOutSel::Fro12M => (c.ensure_fro_hf_active(&level)?, Mux::Clkroot12m),
90 ClockOutSel::FroHfDiv => (c.ensure_fro_hf_div_active(&level)?, Mux::ClkrootFircDiv),
91 ClockOutSel::ClkIn => (c.ensure_clk_in_active(&level)?, Mux::ClkrootSosc),
92 ClockOutSel::Clk16K => (c.ensure_clk_16k_vdd_core_active(&level)?, Mux::Clkroot16k),
93 ClockOutSel::Pll1Clk => (c.ensure_pll1_clk_active(&level)?, Mux::ClkrootSpll),
94 ClockOutSel::SlowClk => (c.ensure_slow_clk_active(&level)?, Mux::ClkrootSlow),
95 })
96 });
97 let Some(res) = res else {
98 return Err(ClockError::NeverInitialized);
99 };
100 res
101}
102
103/// Set up the clkout pin using the given mux and div settings
104fn setup_clkout(mux: Mux, div: Div4) {
105 let mrcc = unsafe { crate::pac::Mrcc0::steal() };
106
107 mrcc.mrcc_clkout_clksel().write(|w| w.mux().variant(mux));
108
109 // Set up clkdiv
110 mrcc.mrcc_clkout_clkdiv().write(|w| {
111 w.halt().set_bit();
112 w.reset().set_bit();
113 unsafe { w.div().bits(div.into_bits()) };
114 w
115 });
116 mrcc.mrcc_clkout_clkdiv().write(|w| {
117 w.halt().clear_bit();
118 w.reset().clear_bit();
119 unsafe { w.div().bits(div.into_bits()) };
120 w
121 });
122
123 while mrcc.mrcc_clkout_clkdiv().read().unstab().bit_is_set() {}
124}
125
126/// Stop the clkout
127fn disable_clkout() {
128 // Stop the output by selecting the "none" clock
129 //
130 // TODO: restore the pin to hi-z or something?
131 let mrcc = unsafe { crate::pac::Mrcc0::steal() };
132 mrcc.mrcc_clkout_clkdiv().write(|w| {
133 w.reset().set_bit();
134 w.halt().set_bit();
135 unsafe {
136 w.div().bits(0);
137 }
138 w
139 });
140 mrcc.mrcc_clkout_clksel().write(|w| unsafe { w.bits(0b111) });
141}
142
143mod sealed {
144 use embassy_hal_internal::PeripheralType;
145
146 use crate::gpio::{Pull, SealedPin};
147
148 /// Sealed marker trait for clockout pins
149 pub trait ClockOutPin: PeripheralType {
150 /// Set the given pin to the correct muxing state
151 fn mux(&self);
152 }
153
154 macro_rules! impl_pin {
155 ($pin:ident, $func:ident) => {
156 impl ClockOutPin for crate::peripherals::$pin {
157 fn mux(&self) {
158 self.set_function(crate::pac::port0::pcr0::Mux::$func);
159 self.set_pull(Pull::Disabled);
160 }
161 }
162 };
163 }
164
165 impl_pin!(P0_6, Mux12);
166 impl_pin!(P3_6, Mux1);
167 impl_pin!(P3_8, Mux12);
168 impl_pin!(P4_2, Mux1);
169}
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs
new file mode 100644
index 000000000..0563b8917
--- /dev/null
+++ b/embassy-mcxa/src/clocks/config.rs
@@ -0,0 +1,204 @@
1//! Clock Configuration
2//!
3//! This module holds configuration types used for the system clocks. For
4//! configuration of individual peripherals, see [`super::periph_helpers`].
5
6use super::PoweredClock;
7
8/// This type represents a divider in the range 1..=256.
9///
10/// At a hardware level, this is an 8-bit register from 0..=255,
11/// which adds one.
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13pub struct Div8(pub(super) u8);
14
15impl Div8 {
16 /// Store a "raw" divisor value that will divide the source by
17 /// `(n + 1)`, e.g. `Div8::from_raw(0)` will divide the source
18 /// by 1, and `Div8::from_raw(255)` will divide the source by
19 /// 256.
20 pub const fn from_raw(n: u8) -> Self {
21 Self(n)
22 }
23
24 /// Divide by one, or no division
25 pub const fn no_div() -> Self {
26 Self(0)
27 }
28
29 /// Store a specific divisor value that will divide the source
30 /// by `n`. e.g. `Div8::from_divisor(1)` will divide the source
31 /// by 1, and `Div8::from_divisor(256)` will divide the source
32 /// by 256.
33 ///
34 /// Will return `None` if `n` is not in the range `1..=256`.
35 /// Consider [`Self::from_raw`] for an infallible version.
36 pub const fn from_divisor(n: u16) -> Option<Self> {
37 let Some(n) = n.checked_sub(1) else {
38 return None;
39 };
40 if n > (u8::MAX as u16) {
41 return None;
42 }
43 Some(Self(n as u8))
44 }
45
46 /// Convert into "raw" bits form
47 #[inline(always)]
48 pub const fn into_bits(self) -> u8 {
49 self.0
50 }
51
52 /// Convert into "divisor" form, as a u32 for convenient frequency math
53 #[inline(always)]
54 pub const fn into_divisor(self) -> u32 {
55 self.0 as u32 + 1
56 }
57}
58
59/// ```text
60/// ┌─────────────────────────────────────────────────────────┐
61/// │ │
62/// │ ┌───────────┐ clk_out ┌─────────┐ │
63/// XTAL ──────┼──▷│ System │───────────▷│ │ clk_in │
64/// │ │ OSC │ clkout_byp │ MUX │──────────────────┼──────▷
65/// EXTAL ──────┼──▷│ │───────────▷│ │ │
66/// │ └───────────┘ └─────────┘ │
67/// │ │
68/// │ ┌───────────┐ fro_hf_root ┌────┐ fro_hf │
69/// │ │ FRO180 ├───────┬─────▷│ CG │─────────────────────┼──────▷
70/// │ │ │ │ ├────┤ clk_45m │
71/// │ │ │ └─────▷│ CG │─────────────────────┼──────▷
72/// │ └───────────┘ └────┘ │
73/// │ ┌───────────┐ fro_12m_root ┌────┐ fro_12m │
74/// │ │ FRO12M │────────┬─────▷│ CG │────────────────────┼──────▷
75/// │ │ │ │ ├────┤ clk_1m │
76/// │ │ │ └─────▷│1/12│────────────────────┼──────▷
77/// │ └───────────┘ └────┘ │
78/// │ │
79/// │ ┌──────────┐ │
80/// │ │000 │ │
81/// │ clk_in │ │ │
82/// │ ───────────────▷│001 │ │
83/// │ fro_12m │ │ │
84/// │ ───────────────▷│010 │ │
85/// │ fro_hf_root │ │ │
86/// │ ───────────────▷│011 │ main_clk │
87/// │ │ │───────────────────────────┼──────▷
88/// clk_16k ──────┼─────────────────▷│100 │ │
89/// │ none │ │ │
90/// │ ───────────────▷│101 │ │
91/// │ pll1_clk │ │ │
92/// │ ───────────────▷│110 │ │
93/// │ none │ │ │
94/// │ ───────────────▷│111 │ │
95/// │ └──────────┘ │
96/// │ ▲ │
97/// │ │ │
98/// │ SCG SCS │
99/// │ SCG-Lite │
100/// └─────────────────────────────────────────────────────────┘
101///
102///
103/// clk_in ┌─────┐
104/// ───────────────▷│00 │
105/// clk_45m │ │
106/// ───────────────▷│01 │ ┌───────────┐ pll1_clk
107/// none │ │─────▷│ SPLL │───────────────▷
108/// ───────────────▷│10 │ └───────────┘
109/// fro_12m │ │
110/// ───────────────▷│11 │
111/// └─────┘
112/// ```
113#[non_exhaustive]
114pub struct ClocksConfig {
115 /// FIRC, FRO180, 45/60/90/180M clock source
116 pub firc: Option<FircConfig>,
117 /// SIRC, FRO12M, clk_12m clock source
118 // NOTE: I don't think we *can* disable the SIRC?
119 pub sirc: SircConfig,
120 /// FRO16K clock source
121 pub fro16k: Option<Fro16KConfig>,
122}
123
124// FIRC/FRO180M
125
126/// ```text
127/// ┌───────────┐ fro_hf_root ┌────┐ fro_hf
128/// │ FRO180M ├───────┬─────▷│GATE│──────────▷
129/// │ │ │ ├────┤ clk_45m
130/// │ │ └─────▷│GATE│──────────▷
131/// └───────────┘ └────┘
132/// ```
133#[non_exhaustive]
134pub struct FircConfig {
135 /// Selected clock frequency
136 pub frequency: FircFreqSel,
137 /// Selected power state of the clock
138 pub power: PoweredClock,
139 /// Is the "fro_hf" gated clock enabled?
140 pub fro_hf_enabled: bool,
141 /// Is the "clk_45m" gated clock enabled?
142 pub clk_45m_enabled: bool,
143 /// Is the "fro_hf_div" clock enabled? Requires `fro_hf`!
144 pub fro_hf_div: Option<Div8>,
145}
146
147/// Selected FIRC frequency
148pub enum FircFreqSel {
149 /// 45MHz Output
150 Mhz45,
151 /// 60MHz Output
152 Mhz60,
153 /// 90MHz Output
154 Mhz90,
155 /// 180MHz Output
156 Mhz180,
157}
158
159// SIRC/FRO12M
160
161/// ```text
162/// ┌───────────┐ fro_12m_root ┌────┐ fro_12m
163/// │ FRO12M │────────┬─────▷│ CG │──────────▷
164/// │ │ │ ├────┤ clk_1m
165/// │ │ └─────▷│1/12│──────────▷
166/// └───────────┘ └────┘
167/// ```
168#[non_exhaustive]
169pub struct SircConfig {
170 pub power: PoweredClock,
171 // peripheral output, aka sirc_12mhz
172 pub fro_12m_enabled: bool,
173 /// Is the "fro_lf_div" clock enabled? Requires `fro_12m`!
174 pub fro_lf_div: Option<Div8>,
175}
176
177#[non_exhaustive]
178pub struct Fro16KConfig {
179 pub vsys_domain_active: bool,
180 pub vdd_core_domain_active: bool,
181}
182
183impl Default for ClocksConfig {
184 fn default() -> Self {
185 Self {
186 firc: Some(FircConfig {
187 frequency: FircFreqSel::Mhz45,
188 power: PoweredClock::NormalEnabledDeepSleepDisabled,
189 fro_hf_enabled: true,
190 clk_45m_enabled: true,
191 fro_hf_div: None,
192 }),
193 sirc: SircConfig {
194 power: PoweredClock::AlwaysEnabled,
195 fro_12m_enabled: true,
196 fro_lf_div: None,
197 },
198 fro16k: Some(Fro16KConfig {
199 vsys_domain_active: true,
200 vdd_core_domain_active: true,
201 }),
202 }
203 }
204}
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs
new file mode 100644
index 000000000..9c9e6ef3d
--- /dev/null
+++ b/embassy-mcxa/src/clocks/mod.rs
@@ -0,0 +1,943 @@
1//! # Clock Module
2//!
3//! For the MCX-A, we separate clock and peripheral control into two main stages:
4//!
5//! 1. At startup, e.g. when `embassy_mcxa::init()` is called, we configure the
6//! core system clocks, including external and internal oscillators. This
7//! configuration is then largely static for the duration of the program.
8//! 2. When HAL drivers are created, e.g. `Lpuart::new()` is called, the driver
9//! is responsible for two main things:
10//! * Ensuring that any required "upstream" core system clocks necessary for
11//! clocking the peripheral is active and configured to a reasonable value
12//! * Enabling the clock gates for that peripheral, and resetting the peripheral
13//!
14//! From a user perspective, only step 1 is visible. Step 2 is automatically handled
15//! by HAL drivers, using interfaces defined in this module.
16//!
17//! It is also possible to *view* the state of the clock configuration after [`init()`]
18//! has been called, using the [`with_clocks()`] function, which provides a view of the
19//! [`Clocks`] structure.
20//!
21//! ## For HAL driver implementors
22//!
23//! The majority of peripherals in the MCXA chip are fed from either a "hard-coded" or
24//! configurable clock source, e.g. selecting the FROM12M or `clk_1m` as a source. This
25//! selection, as well as often any pre-scaler division from that source clock, is made
26//! through MRCC registers.
27//!
28//! Any peripheral that is controlled through the MRCC register can automatically implement
29//! the necessary APIs using the `impl_cc_gate!` macro in this module. You will also need
30//! to define the configuration surface and steps necessary to fully configure that peripheral
31//! from a clocks perspective by:
32//!
33//! 1. Defining a configuration type in the [`periph_helpers`] module that contains any selects
34//! or divisions available to the HAL driver
35//! 2. Implementing the [`periph_helpers::SPConfHelper`] trait, which should check that the
36//! necessary input clocks are reasonable
37
38use core::cell::RefCell;
39
40use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig};
41use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten};
42use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten};
43use periph_helpers::SPConfHelper;
44
45use crate::pac;
46pub mod config;
47pub mod periph_helpers;
48
49//
50// Statics/Consts
51//
52
53/// The state of system core clocks.
54///
55/// Initialized by [`init()`], and then unchanged for the remainder of the program.
56static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None));
57
58//
59// Free functions
60//
61
62/// Initialize the core system clocks with the given [`ClocksConfig`].
63///
64/// This function should be called EXACTLY once at start-up, usually via a
65/// call to [`embassy_mcxa::init()`](crate::init()). Subsequent calls will
66/// return an error.
67pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
68 critical_section::with(|cs| {
69 if CLOCKS.borrow_ref(cs).is_some() {
70 Err(ClockError::AlreadyInitialized)
71 } else {
72 Ok(())
73 }
74 })?;
75
76 let mut clocks = Clocks::default();
77 let mut operator = ClockOperator {
78 clocks: &mut clocks,
79 config: &settings,
80
81 _mrcc0: unsafe { pac::Mrcc0::steal() },
82 scg0: unsafe { pac::Scg0::steal() },
83 syscon: unsafe { pac::Syscon::steal() },
84 vbat0: unsafe { pac::Vbat0::steal() },
85 };
86
87 operator.configure_firc_clocks()?;
88 operator.configure_sirc_clocks()?;
89 operator.configure_fro16k_clocks()?;
90
91 // For now, just use FIRC as the main/cpu clock, which should already be
92 // the case on reset
93 assert!(operator.scg0.rccr().read().scs().is_firc());
94 let input = operator.clocks.fro_hf_root.clone().unwrap();
95 operator.clocks.main_clk = Some(input.clone());
96 // We can also assume cpu/system clk == fro_hf because div is /1.
97 assert_eq!(operator.syscon.ahbclkdiv().read().div().bits(), 0);
98 operator.clocks.cpu_system_clk = Some(input);
99
100 critical_section::with(|cs| {
101 let mut clks = CLOCKS.borrow_ref_mut(cs);
102 assert!(clks.is_none(), "Clock setup race!");
103 *clks = Some(clocks);
104 });
105
106 Ok(())
107}
108
109/// Obtain the full clocks structure, calling the given closure in a critical section.
110///
111/// The given closure will be called with read-only access to the state of the system
112/// clocks. This can be used to query and return the state of a given clock.
113///
114/// As the caller's closure will be called in a critical section, care must be taken
115/// not to block or cause any other undue delays while accessing.
116///
117/// Calls to this function will not succeed until after a successful call to `init()`,
118/// and will always return None.
119pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
120 critical_section::with(|cs| {
121 let c = CLOCKS.borrow_ref(cs);
122 let c = c.as_ref()?;
123 Some(f(c))
124 })
125}
126
127//
128// Structs/Enums
129//
130
131/// The `Clocks` structure contains the initialized state of the core system clocks
132///
133/// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function
134/// at boot time.
135#[derive(Default, Debug, Clone)]
136#[non_exhaustive]
137pub struct Clocks {
138 /// The `clk_in` is a clock provided by an external oscillator
139 pub clk_in: Option<Clock>,
140
141 // FRO180M stuff
142 //
143 /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator
144 ///
145 /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`,
146 /// and `fro_hf_div`.
147 pub fro_hf_root: Option<Clock>,
148
149 /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate.
150 pub fro_hf: Option<Clock>,
151
152 /// `clk_45` is a 45MHz clock, sourced from `fro_hf`.
153 pub clk_45m: Option<Clock>,
154
155 /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`.
156 pub fro_hf_div: Option<Clock>,
157
158 //
159 // End FRO180M
160
161 // FRO12M stuff
162 //
163 /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator
164 ///
165 /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`,
166 /// `and `fro_lf_div`.
167 pub fro_12m_root: Option<Clock>,
168
169 /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate.
170 pub fro_12m: Option<Clock>,
171
172 /// `clk_1m` is a 1MHz clock, sourced from `fro_12m`
173 pub clk_1m: Option<Clock>,
174
175 /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m`
176 pub fro_lf_div: Option<Clock>,
177 //
178 // End FRO12M stuff
179 /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator.
180 ///
181 /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in
182 /// the system domain, such as the CMP and RTC.
183 pub clk_16k_vsys: Option<Clock>,
184
185 /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator.
186 ///
187 /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in
188 /// the VDD Core domain, such as the OSTimer or LPUarts.
189 pub clk_16k_vdd_core: Option<Clock>,
190
191 /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some
192 /// peripherals.
193 pub main_clk: Option<Clock>,
194
195 /// `CPU_CLK` or `SYSTEM_CLK` is the output of `main_clk`, run through the `AHBCLKDIV`
196 pub cpu_system_clk: Option<Clock>,
197
198 /// `pll1_clk` is the output of the main system PLL, `pll1`.
199 pub pll1_clk: Option<Clock>,
200}
201
202/// `ClockError` is the main error returned when configuring or checking clock state
203#[derive(Debug, Copy, Clone, Eq, PartialEq)]
204#[cfg_attr(feature = "defmt", derive(defmt::Format))]
205#[non_exhaustive]
206pub enum ClockError {
207 /// The system clocks were never initialized by calling [`init()`]
208 NeverInitialized,
209 /// The [`init()`] function was called more than once
210 AlreadyInitialized,
211 /// The requested configuration was not possible to fulfill, as the system clocks
212 /// were not configured in a compatible way
213 BadConfig { clock: &'static str, reason: &'static str },
214 /// The requested configuration was not possible to fulfill, as the required system
215 /// clocks have not yet been implemented.
216 NotImplemented { clock: &'static str },
217 /// The requested peripheral could not be configured, as the steps necessary to
218 /// enable it have not yet been implemented.
219 UnimplementedConfig,
220}
221
222/// Information regarding a system clock
223#[derive(Debug, Clone)]
224pub struct Clock {
225 /// The frequency, in Hz, of the given clock
226 pub frequency: u32,
227 /// The power state of the clock, e.g. whether it is active in deep sleep mode
228 /// or not.
229 pub power: PoweredClock,
230}
231
232/// The power state of a given clock.
233///
234/// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep
235/// mode will be stopped. This means that any downstream usage, e.g. by peripherals,
236/// will also stop.
237///
238/// In the future, we will provide an API for entering Deep Sleep, and if there are
239/// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into
240/// Deep Sleep will be prevented, in order to avoid misbehaving peripherals.
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum PoweredClock {
243 /// The given clock will NOT continue running in Deep Sleep mode
244 NormalEnabledDeepSleepDisabled,
245 /// The given clock WILL continue running in Deep Sleep mode
246 AlwaysEnabled,
247}
248
249/// The ClockOperator is a private helper type that contains the methods used
250/// during system clock initialization.
251///
252/// # SAFETY
253///
254/// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`,
255/// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function.
256struct ClockOperator<'a> {
257 /// A mutable reference to the current state of system clocks
258 clocks: &'a mut Clocks,
259 /// A reference to the requested configuration provided by the caller of [`init()`]
260 config: &'a ClocksConfig,
261
262 // We hold on to stolen peripherals
263 _mrcc0: pac::Mrcc0,
264 scg0: pac::Scg0,
265 syscon: pac::Syscon,
266 vbat0: pac::Vbat0,
267}
268
269/// Trait describing an AHB clock gate that can be toggled through MRCC.
270pub trait Gate {
271 type MrccPeriphConfig: SPConfHelper;
272
273 /// Enable the clock gate.
274 ///
275 /// # SAFETY
276 ///
277 /// The current peripheral must be disabled prior to calling this method
278 unsafe fn enable_clock();
279
280 /// Disable the clock gate.
281 ///
282 /// # SAFETY
283 ///
284 /// There must be no active user of this peripheral when calling this method
285 unsafe fn disable_clock();
286
287 /// Drive the peripheral into reset.
288 ///
289 /// # SAFETY
290 ///
291 /// There must be no active user of this peripheral when calling this method
292 unsafe fn assert_reset();
293
294 /// Drive the peripheral out of reset.
295 ///
296 /// # SAFETY
297 ///
298 /// There must be no active user of this peripheral when calling this method
299 unsafe fn release_reset();
300
301 /// Return whether the clock gate for this peripheral is currently enabled.
302 fn is_clock_enabled() -> bool;
303
304 /// Return whether the peripheral is currently held in reset.
305 fn is_reset_released() -> bool;
306}
307
308/// This is the primary helper method HAL drivers are expected to call when creating
309/// an instance of the peripheral.
310///
311/// This method:
312///
313/// 1. Enables the MRCC clock gate for this peripheral
314/// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error
315/// and re-disabling the peripheral if this fails.
316/// 3. Pulses the MRCC reset line, to reset the peripheral to the default state
317/// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account
318/// the selected upstream clock, as well as any division specified by `cfg`.
319///
320/// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method
321/// may return `Ok(0)`. In the future, this might be updated to return the correct
322/// "ambient" clock, e.g. the AHB/APB frequency.
323///
324/// # SAFETY
325///
326/// This peripheral must not yet be in use prior to calling `enable_and_reset`.
327#[inline]
328pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
329 let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?;
330 pulse_reset::<G>();
331 Ok(freq)
332}
333
334/// Enable the clock gate for the given peripheral.
335///
336/// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need
337/// to control the duration of the pulse more directly.
338///
339/// # SAFETY
340///
341/// This peripheral must not yet be in use prior to calling `enable`.
342#[inline]
343pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
344 G::enable_clock();
345 while !G::is_clock_enabled() {}
346 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
347
348 let freq = critical_section::with(|cs| {
349 let clocks = CLOCKS.borrow_ref(cs);
350 let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?;
351 cfg.post_enable_config(clocks)
352 });
353
354 freq.inspect_err(|_e| {
355 G::disable_clock();
356 })
357}
358
359/// Disable the clock gate for the given peripheral.
360///
361/// # SAFETY
362///
363/// This peripheral must no longer be in use prior to calling `enable`.
364#[allow(dead_code)]
365#[inline]
366pub unsafe fn disable<G: Gate>() {
367 G::disable_clock();
368}
369
370/// Check whether a gate is currently enabled.
371#[allow(dead_code)]
372#[inline]
373pub fn is_clock_enabled<G: Gate>() -> bool {
374 G::is_clock_enabled()
375}
376
377/// Release a reset line for the given peripheral set.
378///
379/// Prefer [`enable_and_reset`].
380///
381/// # SAFETY
382///
383/// This peripheral must not yet be in use prior to calling `release_reset`.
384#[inline]
385pub unsafe fn release_reset<G: Gate>() {
386 G::release_reset();
387}
388
389/// Assert a reset line for the given peripheral set.
390///
391/// Prefer [`enable_and_reset`].
392///
393/// # SAFETY
394///
395/// This peripheral must not yet be in use prior to calling `assert_reset`.
396#[inline]
397pub unsafe fn assert_reset<G: Gate>() {
398 G::assert_reset();
399}
400
401/// Check whether the peripheral is held in reset.
402#[inline]
403pub unsafe fn is_reset_released<G: Gate>() -> bool {
404 G::is_reset_released()
405}
406
407/// Pulse a reset line (assert then release) with a short delay.
408///
409/// Prefer [`enable_and_reset`].
410///
411/// # SAFETY
412///
413/// This peripheral must not yet be in use prior to calling `release_reset`.
414#[inline]
415pub unsafe fn pulse_reset<G: Gate>() {
416 G::assert_reset();
417 cortex_m::asm::nop();
418 cortex_m::asm::nop();
419 G::release_reset();
420}
421
422//
423// `impl`s for structs/enums
424//
425
426/// The [`Clocks`] type's methods generally take the form of "ensure X clock is active".
427///
428/// These methods are intended to be used by HAL peripheral implementors to ensure that their
429/// selected clocks are active at a suitable level at time of construction. These methods
430/// return the frequency of the requested clock, in Hertz, or a [`ClockError`].
431impl Clocks {
432 /// Ensure the `fro_lf_div` clock is active and valid at the given power state.
433 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
434 let Some(clk) = self.fro_lf_div.as_ref() else {
435 return Err(ClockError::BadConfig {
436 clock: "fro_lf_div",
437 reason: "required but not active",
438 });
439 };
440 if !clk.power.meets_requirement_of(at_level) {
441 return Err(ClockError::BadConfig {
442 clock: "fro_lf_div",
443 reason: "not low power active",
444 });
445 }
446 Ok(clk.frequency)
447 }
448
449 /// Ensure the `fro_hf` clock is active and valid at the given power state.
450 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
451 let Some(clk) = self.fro_hf.as_ref() else {
452 return Err(ClockError::BadConfig {
453 clock: "fro_hf",
454 reason: "required but not active",
455 });
456 };
457 if !clk.power.meets_requirement_of(at_level) {
458 return Err(ClockError::BadConfig {
459 clock: "fro_hf",
460 reason: "not low power active",
461 });
462 }
463 Ok(clk.frequency)
464 }
465
466 /// Ensure the `fro_hf_div` clock is active and valid at the given power state.
467 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
468 let Some(clk) = self.fro_hf_div.as_ref() else {
469 return Err(ClockError::BadConfig {
470 clock: "fro_hf_div",
471 reason: "required but not active",
472 });
473 };
474 if !clk.power.meets_requirement_of(at_level) {
475 return Err(ClockError::BadConfig {
476 clock: "fro_hf_div",
477 reason: "not low power active",
478 });
479 }
480 Ok(clk.frequency)
481 }
482
483 /// Ensure the `clk_in` clock is active and valid at the given power state.
484 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
485 Err(ClockError::NotImplemented { clock: "clk_in" })
486 }
487
488 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
489 pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
490 // NOTE: clk_16k is always active in low power mode
491 Ok(self
492 .clk_16k_vsys
493 .as_ref()
494 .ok_or(ClockError::BadConfig {
495 clock: "clk_16k_vsys",
496 reason: "required but not active",
497 })?
498 .frequency)
499 }
500
501 /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state.
502 pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
503 // NOTE: clk_16k is always active in low power mode
504 Ok(self
505 .clk_16k_vdd_core
506 .as_ref()
507 .ok_or(ClockError::BadConfig {
508 clock: "clk_16k_vdd_core",
509 reason: "required but not active",
510 })?
511 .frequency)
512 }
513
514 /// Ensure the `clk_1m` clock is active and valid at the given power state.
515 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
516 let Some(clk) = self.clk_1m.as_ref() else {
517 return Err(ClockError::BadConfig {
518 clock: "clk_1m",
519 reason: "required but not active",
520 });
521 };
522 if !clk.power.meets_requirement_of(at_level) {
523 return Err(ClockError::BadConfig {
524 clock: "clk_1m",
525 reason: "not low power active",
526 });
527 }
528 Ok(clk.frequency)
529 }
530
531 /// Ensure the `pll1_clk` clock is active and valid at the given power state.
532 pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
533 Err(ClockError::NotImplemented { clock: "pll1_clk" })
534 }
535
536 /// Ensure the `pll1_clk_div` clock is active and valid at the given power state.
537 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
538 Err(ClockError::NotImplemented { clock: "pll1_clk_div" })
539 }
540
541 /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active
542 pub fn ensure_cpu_system_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
543 let Some(clk) = self.cpu_system_clk.as_ref() else {
544 return Err(ClockError::BadConfig {
545 clock: "cpu_system_clk",
546 reason: "required but not active",
547 });
548 };
549 // Can the main_clk ever be active in deep sleep? I think it is gated?
550 match at_level {
551 PoweredClock::NormalEnabledDeepSleepDisabled => {}
552 PoweredClock::AlwaysEnabled => {
553 return Err(ClockError::BadConfig {
554 clock: "main_clk",
555 reason: "not low power active",
556 });
557 }
558 }
559
560 Ok(clk.frequency)
561 }
562
563 pub fn ensure_slow_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
564 let freq = self.ensure_cpu_system_clk_active(at_level)?;
565
566 Ok(freq / 6)
567 }
568}
569
570impl PoweredClock {
571 /// Does THIS clock meet the power requirements of the OTHER clock?
572 pub fn meets_requirement_of(&self, other: &Self) -> bool {
573 match (self, other) {
574 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false,
575 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
576 (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
577 (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true,
578 }
579 }
580}
581
582impl ClockOperator<'_> {
583 /// Configure the FIRC/FRO180M clock family
584 ///
585 /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used
586 /// as the main clock used for the CPU, AHB, APB, etc.
587 fn configure_firc_clocks(&mut self) -> Result<(), ClockError> {
588 const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig {
589 clock: "firc",
590 reason: "For now, FIRC must be enabled and in default state!",
591 });
592
593 // Did the user give us a FIRC config?
594 let Some(firc) = self.config.firc.as_ref() else {
595 return HARDCODED_ERR;
596 };
597 // Is the FIRC set to 45MHz (should be reset default)
598 if !matches!(firc.frequency, FircFreqSel::Mhz45) {
599 return HARDCODED_ERR;
600 }
601 let base_freq = 45_000_000;
602
603 // Now, check if the FIRC as expected for our hardcoded value
604 let mut firc_ok = true;
605
606 // Is the hardware currently set to the default 45MHz?
607 //
608 // NOTE: the SVD currently has the wrong(?) values for these:
609 // 45 -> 48
610 // 60 -> 64
611 // 90 -> 96
612 // 180 -> 192
613 // Probably correct-ish, but for a different trim value?
614 firc_ok &= self.scg0.firccfg().read().freq_sel().is_firc_48mhz_192s();
615
616 // Check some values in the CSR
617 let csr = self.scg0.firccsr().read();
618 // Is it enabled?
619 firc_ok &= csr.fircen().is_enabled();
620 // Is it accurate?
621 firc_ok &= csr.fircacc().is_enabled_and_valid();
622 // Is there no error?
623 firc_ok &= csr.fircerr().is_error_not_detected();
624 // Is the FIRC the system clock?
625 firc_ok &= csr.fircsel().is_firc();
626 // Is it valid?
627 firc_ok &= csr.fircvld().is_enabled_and_valid();
628
629 // Are we happy with the current (hardcoded) state?
630 if !firc_ok {
631 return HARDCODED_ERR;
632 }
633
634 // Note that the fro_hf_root is active
635 self.clocks.fro_hf_root = Some(Clock {
636 frequency: base_freq,
637 power: firc.power,
638 });
639
640 // Okay! Now we're past that, let's enable all the downstream clocks.
641 let FircConfig {
642 frequency: _,
643 power,
644 fro_hf_enabled,
645 clk_45m_enabled,
646 fro_hf_div,
647 } = firc;
648
649 // When is the FRO enabled?
650 let pow_set = match power {
651 PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes,
652 PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes,
653 };
654
655 // Do we enable the `fro_hf` output?
656 let fro_hf_set = if *fro_hf_enabled {
657 self.clocks.fro_hf = Some(Clock {
658 frequency: base_freq,
659 power: *power,
660 });
661 FircFclkPeriphEn::Enabled
662 } else {
663 FircFclkPeriphEn::Disabled
664 };
665
666 // Do we enable the `clk_45m` output?
667 let clk_45m_set = if *clk_45m_enabled {
668 self.clocks.clk_45m = Some(Clock {
669 frequency: 45_000_000,
670 power: *power,
671 });
672 FircSclkPeriphEn::Enabled
673 } else {
674 FircSclkPeriphEn::Disabled
675 };
676
677 self.scg0.firccsr().modify(|_r, w| {
678 w.fircsten().variant(pow_set);
679 w.firc_fclk_periph_en().variant(fro_hf_set);
680 w.firc_sclk_periph_en().variant(clk_45m_set);
681 w
682 });
683
684 // Do we enable the `fro_hf_div` output?
685 if let Some(d) = fro_hf_div.as_ref() {
686 // We need `fro_hf` to be enabled
687 if !*fro_hf_enabled {
688 return Err(ClockError::BadConfig {
689 clock: "fro_hf_div",
690 reason: "fro_hf not enabled",
691 });
692 }
693
694 // Halt and reset the div; then set our desired div.
695 self.syscon.frohfdiv().write(|w| {
696 w.halt().halt();
697 w.reset().asserted();
698 unsafe { w.div().bits(d.into_bits()) };
699 w
700 });
701 // Then unhalt it, and reset it
702 self.syscon.frohfdiv().write(|w| {
703 w.halt().run();
704 w.reset().released();
705 w
706 });
707
708 // Wait for clock to stabilize
709 while self.syscon.frohfdiv().read().unstab().is_ongoing() {}
710
711 // Store off the clock info
712 self.clocks.fro_hf_div = Some(Clock {
713 frequency: base_freq / d.into_divisor(),
714 power: *power,
715 });
716 }
717
718 Ok(())
719 }
720
721 /// Configure the SIRC/FRO12M clock family
722 fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> {
723 let SircConfig {
724 power,
725 fro_12m_enabled,
726 fro_lf_div,
727 } = &self.config.sirc;
728 let base_freq = 12_000_000;
729
730 // Allow writes
731 self.scg0.sirccsr().modify(|_r, w| w.lk().write_enabled());
732 self.clocks.fro_12m_root = Some(Clock {
733 frequency: base_freq,
734 power: *power,
735 });
736
737 let deep = match power {
738 PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled,
739 PoweredClock::AlwaysEnabled => Sircsten::Enabled,
740 };
741 let pclk = if *fro_12m_enabled {
742 self.clocks.fro_12m = Some(Clock {
743 frequency: base_freq,
744 power: *power,
745 });
746 self.clocks.clk_1m = Some(Clock {
747 frequency: base_freq / 12,
748 power: *power,
749 });
750 SircClkPeriphEn::Enabled
751 } else {
752 SircClkPeriphEn::Disabled
753 };
754
755 // Set sleep/peripheral usage
756 self.scg0.sirccsr().modify(|_r, w| {
757 w.sircsten().variant(deep);
758 w.sirc_clk_periph_en().variant(pclk);
759 w
760 });
761
762 while self.scg0.sirccsr().read().sircvld().is_disabled_or_not_valid() {}
763 if self.scg0.sirccsr().read().sircerr().is_error_detected() {
764 return Err(ClockError::BadConfig {
765 clock: "sirc",
766 reason: "error set",
767 });
768 }
769
770 // reset lock
771 self.scg0.sirccsr().modify(|_r, w| w.lk().write_disabled());
772
773 // Do we enable the `fro_lf_div` output?
774 if let Some(d) = fro_lf_div.as_ref() {
775 // We need `fro_lf` to be enabled
776 if !*fro_12m_enabled {
777 return Err(ClockError::BadConfig {
778 clock: "fro_lf_div",
779 reason: "fro_12m not enabled",
780 });
781 }
782
783 // Halt and reset the div; then set our desired div.
784 self.syscon.frolfdiv().write(|w| {
785 w.halt().halt();
786 w.reset().asserted();
787 unsafe { w.div().bits(d.into_bits()) };
788 w
789 });
790 // Then unhalt it, and reset it
791 self.syscon.frolfdiv().modify(|_r, w| {
792 w.halt().run();
793 w.reset().released();
794 w
795 });
796
797 // Wait for clock to stabilize
798 while self.syscon.frolfdiv().read().unstab().is_ongoing() {}
799
800 // Store off the clock info
801 self.clocks.fro_lf_div = Some(Clock {
802 frequency: base_freq / d.into_divisor(),
803 power: *power,
804 });
805 }
806
807 Ok(())
808 }
809
810 /// Configure the FRO16K/clk_16k clock family
811 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {
812 let Some(fro16k) = self.config.fro16k.as_ref() else {
813 return Ok(());
814 };
815 // Enable FRO16K oscillator
816 self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit());
817
818 // Lock the control register
819 self.vbat0.frolcka().modify(|_, w| w.lock().set_bit());
820
821 let Fro16KConfig {
822 vsys_domain_active,
823 vdd_core_domain_active,
824 } = fro16k;
825
826 // Enable clock outputs to both VSYS and VDD_CORE domains
827 // Bit 0: clk_16k0 to VSYS domain
828 // Bit 1: clk_16k1 to VDD_CORE domain
829 //
830 // TODO: Define sub-fields for this register with a PAC patch?
831 let mut bits = 0;
832 if *vsys_domain_active {
833 bits |= 0b01;
834 self.clocks.clk_16k_vsys = Some(Clock {
835 frequency: 16_384,
836 power: PoweredClock::AlwaysEnabled,
837 });
838 }
839 if *vdd_core_domain_active {
840 bits |= 0b10;
841 self.clocks.clk_16k_vdd_core = Some(Clock {
842 frequency: 16_384,
843 power: PoweredClock::AlwaysEnabled,
844 });
845 }
846 self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) });
847
848 Ok(())
849 }
850}
851
852//
853// Macros/macro impls
854//
855
856/// This macro is used to implement the [`Gate`] trait for a given peripheral
857/// that is controlled by the MRCC peripheral.
858macro_rules! impl_cc_gate {
859 ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => {
860 impl Gate for crate::peripherals::$name {
861 type MrccPeriphConfig = $config;
862
863 #[inline]
864 unsafe fn enable_clock() {
865 let mrcc = unsafe { pac::Mrcc0::steal() };
866 mrcc.$clk_reg().modify(|_, w| w.$field().enabled());
867 }
868
869 #[inline]
870 unsafe fn disable_clock() {
871 let mrcc = unsafe { pac::Mrcc0::steal() };
872 mrcc.$clk_reg().modify(|_r, w| w.$field().disabled());
873 }
874
875 #[inline]
876 fn is_clock_enabled() -> bool {
877 let mrcc = unsafe { pac::Mrcc0::steal() };
878 mrcc.$clk_reg().read().$field().is_enabled()
879 }
880
881 #[inline]
882 unsafe fn release_reset() {
883 let mrcc = unsafe { pac::Mrcc0::steal() };
884 mrcc.$rst_reg().modify(|_, w| w.$field().enabled());
885 }
886
887 #[inline]
888 unsafe fn assert_reset() {
889 let mrcc = unsafe { pac::Mrcc0::steal() };
890 mrcc.$rst_reg().modify(|_, w| w.$field().disabled());
891 }
892
893 #[inline]
894 fn is_reset_released() -> bool {
895 let mrcc = unsafe { pac::Mrcc0::steal() };
896 mrcc.$rst_reg().read().$field().is_enabled()
897 }
898 }
899 };
900}
901
902/// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait,
903/// for various low level peripherals.
904pub(crate) mod gate {
905 #[cfg(not(feature = "time"))]
906 use super::periph_helpers::OsTimerConfig;
907 use super::periph_helpers::{AdcConfig, Lpi2cConfig, LpuartConfig, NoConfig};
908 use super::*;
909
910 // These peripherals have no additional upstream clocks or configuration required
911 // other than enabling through the MRCC gate. Currently, these peripherals will
912 // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or
913 // [`SPConfHelper::post_enable_config()`].
914 impl_cc_gate!(PORT0, mrcc_glb_cc1, mrcc_glb_rst1, port0, NoConfig);
915 impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig);
916 impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig);
917 impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig);
918 impl_cc_gate!(PORT4, mrcc_glb_cc1, mrcc_glb_rst1, port4, NoConfig);
919
920 impl_cc_gate!(GPIO0, mrcc_glb_cc2, mrcc_glb_rst2, gpio0, NoConfig);
921 impl_cc_gate!(GPIO1, mrcc_glb_cc2, mrcc_glb_rst2, gpio1, NoConfig);
922 impl_cc_gate!(GPIO2, mrcc_glb_cc2, mrcc_glb_rst2, gpio2, NoConfig);
923 impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig);
924 impl_cc_gate!(GPIO4, mrcc_glb_cc2, mrcc_glb_rst2, gpio4, NoConfig);
925
926 // These peripherals DO have meaningful configuration, and could fail if the system
927 // clocks do not match their needs.
928 #[cfg(not(feature = "time"))]
929 impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
930
931 impl_cc_gate!(LPI2C0, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig);
932 impl_cc_gate!(LPI2C1, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig);
933 impl_cc_gate!(LPI2C2, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c2, Lpi2cConfig);
934 impl_cc_gate!(LPI2C3, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c3, Lpi2cConfig);
935
936 impl_cc_gate!(LPUART0, mrcc_glb_cc0, mrcc_glb_rst0, lpuart0, LpuartConfig);
937 impl_cc_gate!(LPUART1, mrcc_glb_cc0, mrcc_glb_rst0, lpuart1, LpuartConfig);
938 impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig);
939 impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig);
940 impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig);
941 impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig);
942 impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig);
943}
diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs
new file mode 100644
index 000000000..fed5e558e
--- /dev/null
+++ b/embassy-mcxa/src/clocks/periph_helpers.rs
@@ -0,0 +1,498 @@
1//! Peripheral Helpers
2//!
3//! The purpose of this module is to define the per-peripheral special handling
4//! required from a clocking perspective. Different peripherals have different
5//! selectable source clocks, and some peripherals have additional pre-dividers
6//! that can be used.
7//!
8//! See the docs of [`SPConfHelper`] for more details.
9
10use super::{ClockError, Clocks, PoweredClock};
11use crate::pac;
12
13/// Sealed Peripheral Configuration Helper
14///
15/// NOTE: the name "sealed" doesn't *totally* make sense because its not sealed yet in the
16/// embassy-mcxa project, but it derives from embassy-imxrt where it is. We should
17/// fix the name, or actually do the sealing of peripherals.
18///
19/// This trait serves to act as a per-peripheral customization for clocking behavior.
20///
21/// This trait should be implemented on a configuration type for a given peripheral, and
22/// provide the methods that will be called by the higher level operations like
23/// `embassy_mcxa::clocks::enable_and_reset()`.
24pub trait SPConfHelper {
25 /// This method is called AFTER a given MRCC peripheral has been enabled (e.g. un-gated),
26 /// but BEFORE the peripheral reset line is reset.
27 ///
28 /// This function should check that any relevant upstream clocks are enabled, are in a
29 /// reasonable power state, and that the requested configuration can be made. If any of
30 /// these checks fail, an `Err(ClockError)` should be returned, likely `ClockError::BadConfig`.
31 ///
32 /// This function SHOULD NOT make any changes to the system clock configuration, even
33 /// unsafely, as this should remain static for the duration of the program.
34 ///
35 /// This function WILL be called in a critical section, care should be taken not to delay
36 /// for an unreasonable amount of time.
37 ///
38 /// On success, this function MUST return an `Ok(freq)`, where `freq` is the frequency
39 /// fed into the peripheral, taking into account the selected source clock, as well as
40 /// any pre-divisors.
41 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>;
42}
43
44/// Copy and paste macro that:
45///
46/// * Sets the clocksel mux to `$selvar`
47/// * Resets and halts the div, and applies the calculated div4 bits
48/// * Releases reset + halt
49/// * Waits for the div to stabilize
50/// * Returns `Ok($freq / $conf.div.into_divisor())`
51///
52/// Assumes:
53///
54/// * self is a configuration struct that has a field called `div`, which
55/// is a `Div4`
56///
57/// usage:
58///
59/// ```rust
60/// apply_div4!(self, clksel, clkdiv, variant, freq)
61/// ```
62///
63/// In the future if we make all the clksel+clkdiv pairs into commonly derivedFrom
64/// registers, or if we put some kind of simple trait around those regs, we could
65/// do this with something other than a macro, but for now, this is harm-reduction
66/// to avoid incorrect copy + paste
67macro_rules! apply_div4 {
68 ($conf:ident, $selreg:ident, $divreg:ident, $selvar:ident, $freq:ident) => {{
69 // set clksel
70 $selreg.modify(|_r, w| w.mux().variant($selvar));
71
72 // Set up clkdiv
73 $divreg.modify(|_r, w| {
74 unsafe { w.div().bits($conf.div.into_bits()) }
75 .halt()
76 .asserted()
77 .reset()
78 .asserted()
79 });
80 $divreg.modify(|_r, w| w.halt().deasserted().reset().deasserted());
81
82 while $divreg.read().unstab().is_unstable() {}
83
84 Ok($freq / $conf.div.into_divisor())
85 }};
86}
87
88// config types
89
90/// This type represents a divider in the range 1..=16.
91///
92/// At a hardware level, this is an 8-bit register from 0..=15,
93/// which adds one.
94///
95/// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain
96/// seems to use 4 bit dividers!
97#[derive(Copy, Clone, Debug, PartialEq, Eq)]
98pub struct Div4(pub(super) u8);
99
100impl Div4 {
101 /// Divide by one, or no division
102 pub const fn no_div() -> Self {
103 Self(0)
104 }
105
106 /// Store a "raw" divisor value that will divide the source by
107 /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source
108 /// by 1, and `Div4::from_raw(15)` will divide the source by
109 /// 16.
110 pub const fn from_raw(n: u8) -> Option<Self> {
111 if n > 0b1111 { None } else { Some(Self(n)) }
112 }
113
114 /// Store a specific divisor value that will divide the source
115 /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source
116 /// by 1, and `Div4::from_divisor(16)` will divide the source
117 /// by 16.
118 ///
119 /// Will return `None` if `n` is not in the range `1..=16`.
120 /// Consider [`Self::from_raw`] for an infallible version.
121 pub const fn from_divisor(n: u8) -> Option<Self> {
122 let Some(n) = n.checked_sub(1) else {
123 return None;
124 };
125 if n > 0b1111 {
126 return None;
127 }
128 Some(Self(n))
129 }
130
131 /// Convert into "raw" bits form
132 #[inline(always)]
133 pub const fn into_bits(self) -> u8 {
134 self.0
135 }
136
137 /// Convert into "divisor" form, as a u32 for convenient frequency math
138 #[inline(always)]
139 pub const fn into_divisor(self) -> u32 {
140 self.0 as u32 + 1
141 }
142}
143
144/// A basic type that always returns an error when `post_enable_config` is called.
145///
146/// Should only be used as a placeholder.
147pub struct UnimplementedConfig;
148
149impl SPConfHelper for UnimplementedConfig {
150 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
151 Err(ClockError::UnimplementedConfig)
152 }
153}
154
155/// A basic type that always returns `Ok(0)` when `post_enable_config` is called.
156///
157/// This should only be used for peripherals that are "ambiently" clocked, like `PORTn`
158/// peripherals, which have no selectable/configurable source clock.
159pub struct NoConfig;
160impl SPConfHelper for NoConfig {
161 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
162 Ok(0)
163 }
164}
165
166//
167// LPI2c
168//
169
170/// Selectable clocks for `Lpi2c` peripherals
171#[derive(Debug, Clone, Copy)]
172pub enum Lpi2cClockSel {
173 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
174 /// "fro_lf_div"
175 FroLfDiv,
176 /// FRO180M/FRO_HF/FIRC clock source, passed through divider
177 /// "fro_hf_div"
178 FroHfDiv,
179 /// SOSC/XTAL/EXTAL clock source
180 ClkIn,
181 /// clk_1m/FRO_LF divided by 12
182 Clk1M,
183 /// Output of PLL1, passed through clock divider,
184 /// "pll1_clk_div", maybe "pll1_lf_div"?
185 Pll1ClkDiv,
186 /// Disabled
187 None,
188}
189
190/// Which instance of the `Lpi2c` is this?
191///
192/// Should not be directly selectable by end-users.
193#[derive(Copy, Clone, Debug, PartialEq, Eq)]
194pub enum Lpi2cInstance {
195 /// Instance 0
196 Lpi2c0,
197 /// Instance 1
198 Lpi2c1,
199 /// Instance 2
200 Lpi2c2,
201 /// Instance 3
202 Lpi2c3,
203}
204
205/// Top level configuration for `Lpi2c` instances.
206pub struct Lpi2cConfig {
207 /// Power state required for this peripheral
208 pub power: PoweredClock,
209 /// Clock source
210 pub source: Lpi2cClockSel,
211 /// Clock divisor
212 pub div: Div4,
213 /// Which instance is this?
214 // NOTE: should not be user settable
215 pub(crate) instance: Lpi2cInstance,
216}
217
218impl SPConfHelper for Lpi2cConfig {
219 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
220 // check that source is suitable
221 let mrcc0 = unsafe { pac::Mrcc0::steal() };
222 use mcxa_pac::mrcc0::mrcc_lpi2c0_clksel::Mux;
223
224 let (clkdiv, clksel) = match self.instance {
225 Lpi2cInstance::Lpi2c0 => (mrcc0.mrcc_lpi2c0_clkdiv(), mrcc0.mrcc_lpi2c0_clksel()),
226 Lpi2cInstance::Lpi2c1 => (mrcc0.mrcc_lpi2c1_clkdiv(), mrcc0.mrcc_lpi2c1_clksel()),
227 Lpi2cInstance::Lpi2c2 => (mrcc0.mrcc_lpi2c2_clkdiv(), mrcc0.mrcc_lpi2c2_clksel()),
228 Lpi2cInstance::Lpi2c3 => (mrcc0.mrcc_lpi2c3_clkdiv(), mrcc0.mrcc_lpi2c3_clksel()),
229 };
230
231 let (freq, variant) = match self.source {
232 Lpi2cClockSel::FroLfDiv => {
233 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
234 (freq, Mux::ClkrootFunc0)
235 }
236 Lpi2cClockSel::FroHfDiv => {
237 let freq = clocks.ensure_fro_hf_div_active(&self.power)?;
238 (freq, Mux::ClkrootFunc2)
239 }
240 Lpi2cClockSel::ClkIn => {
241 let freq = clocks.ensure_clk_in_active(&self.power)?;
242 (freq, Mux::ClkrootFunc3)
243 }
244 Lpi2cClockSel::Clk1M => {
245 let freq = clocks.ensure_clk_1m_active(&self.power)?;
246 (freq, Mux::ClkrootFunc5)
247 }
248 Lpi2cClockSel::Pll1ClkDiv => {
249 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
250 (freq, Mux::ClkrootFunc6)
251 }
252 Lpi2cClockSel::None => unsafe {
253 // no ClkrootFunc7, just write manually for now
254 clksel.write(|w| w.bits(0b111));
255 clkdiv.modify(|_r, w| w.reset().asserted().halt().asserted());
256 return Ok(0);
257 },
258 };
259
260 apply_div4!(self, clksel, clkdiv, variant, freq)
261 }
262}
263
264//
265// LPUart
266//
267
268/// Selectable clocks for Lpuart peripherals
269#[derive(Debug, Clone, Copy)]
270pub enum LpuartClockSel {
271 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
272 /// "fro_lf_div"
273 FroLfDiv,
274 /// FRO180M/FRO_HF/FIRC clock source, passed through divider
275 /// "fro_hf_div"
276 FroHfDiv,
277 /// SOSC/XTAL/EXTAL clock source
278 ClkIn,
279 /// FRO16K/clk_16k source
280 Clk16K,
281 /// clk_1m/FRO_LF divided by 12
282 Clk1M,
283 /// Output of PLL1, passed through clock divider,
284 /// "pll1_clk_div", maybe "pll1_lf_div"?
285 Pll1ClkDiv,
286 /// Disabled
287 None,
288}
289
290/// Which instance of the Lpuart is this?
291///
292/// Should not be directly selectable by end-users.
293#[derive(Copy, Clone, Debug, PartialEq, Eq)]
294pub enum LpuartInstance {
295 /// Instance 0
296 Lpuart0,
297 /// Instance 1
298 Lpuart1,
299 /// Instance 2
300 Lpuart2,
301 /// Instance 3
302 Lpuart3,
303 /// Instance 4
304 Lpuart4,
305 /// Instance 5
306 Lpuart5,
307}
308
309/// Top level configuration for `Lpuart` instances.
310pub struct LpuartConfig {
311 /// Power state required for this peripheral
312 pub power: PoweredClock,
313 /// Clock source
314 pub source: LpuartClockSel,
315 /// Clock divisor
316 pub div: Div4,
317 /// Which instance is this?
318 // NOTE: should not be user settable
319 pub(crate) instance: LpuartInstance,
320}
321
322impl SPConfHelper for LpuartConfig {
323 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
324 // check that source is suitable
325 let mrcc0 = unsafe { pac::Mrcc0::steal() };
326 use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux;
327
328 let (clkdiv, clksel) = match self.instance {
329 LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()),
330 LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()),
331 LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()),
332 LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()),
333 LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()),
334 LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()),
335 };
336
337 let (freq, variant) = match self.source {
338 LpuartClockSel::FroLfDiv => {
339 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
340 (freq, Mux::ClkrootFunc0)
341 }
342 LpuartClockSel::FroHfDiv => {
343 let freq = clocks.ensure_fro_hf_div_active(&self.power)?;
344 (freq, Mux::ClkrootFunc2)
345 }
346 LpuartClockSel::ClkIn => {
347 let freq = clocks.ensure_clk_in_active(&self.power)?;
348 (freq, Mux::ClkrootFunc3)
349 }
350 LpuartClockSel::Clk16K => {
351 let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;
352 (freq, Mux::ClkrootFunc4)
353 }
354 LpuartClockSel::Clk1M => {
355 let freq = clocks.ensure_clk_1m_active(&self.power)?;
356 (freq, Mux::ClkrootFunc5)
357 }
358 LpuartClockSel::Pll1ClkDiv => {
359 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
360 (freq, Mux::ClkrootFunc6)
361 }
362 LpuartClockSel::None => unsafe {
363 // no ClkrootFunc7, just write manually for now
364 clksel.write(|w| w.bits(0b111));
365 clkdiv.modify(|_r, w| {
366 w.reset().asserted();
367 w.halt().asserted();
368 w
369 });
370 return Ok(0);
371 },
372 };
373
374 // set clksel
375 apply_div4!(self, clksel, clkdiv, variant, freq)
376 }
377}
378
379//
380// OSTimer
381//
382
383/// Selectable clocks for the OSTimer peripheral
384#[derive(Copy, Clone, Debug, PartialEq, Eq)]
385pub enum OstimerClockSel {
386 /// 16k clock, sourced from FRO16K (Vdd Core)
387 Clk16kVddCore,
388 /// 1 MHz Clock sourced from FRO12M
389 Clk1M,
390 /// Disabled
391 None,
392}
393
394/// Top level configuration for the `OSTimer` peripheral
395pub struct OsTimerConfig {
396 /// Power state required for this peripheral
397 pub power: PoweredClock,
398 /// Selected clock source for this peripheral
399 pub source: OstimerClockSel,
400}
401
402impl SPConfHelper for OsTimerConfig {
403 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
404 let mrcc0 = unsafe { pac::Mrcc0::steal() };
405 Ok(match self.source {
406 OstimerClockSel::Clk16kVddCore => {
407 let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;
408 mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_16k());
409 freq
410 }
411 OstimerClockSel::Clk1M => {
412 let freq = clocks.ensure_clk_1m_active(&self.power)?;
413 mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
414 freq
415 }
416 OstimerClockSel::None => {
417 mrcc0.mrcc_ostimer0_clksel().write(|w| unsafe { w.mux().bits(0b11) });
418 0
419 }
420 })
421 }
422}
423
424//
425// Adc
426//
427
428/// Selectable clocks for the ADC peripheral
429#[derive(Copy, Clone, Debug, PartialEq, Eq)]
430pub enum AdcClockSel {
431 /// Divided `fro_lf`/`clk_12m`/FRO12M source
432 FroLfDiv,
433 /// Gated `fro_hf`/`FRO180M` source
434 FroHf,
435 /// External Clock Source
436 ClkIn,
437 /// 1MHz clock sourced by a divided `fro_lf`/`clk_12m`
438 Clk1M,
439 /// Internal PLL output, with configurable divisor
440 Pll1ClkDiv,
441 /// No clock/disabled
442 None,
443}
444
445/// Top level configuration for the ADC peripheral
446pub struct AdcConfig {
447 /// Power state required for this peripheral
448 pub power: PoweredClock,
449 /// Selected clock-source for this peripheral
450 pub source: AdcClockSel,
451 /// Pre-divisor, applied to the upstream clock output
452 pub div: Div4,
453}
454
455impl SPConfHelper for AdcConfig {
456 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
457 use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux;
458 let mrcc0 = unsafe { pac::Mrcc0::steal() };
459 let (freq, variant) = match self.source {
460 AdcClockSel::FroLfDiv => {
461 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
462 (freq, Mux::ClkrootFunc0)
463 }
464 AdcClockSel::FroHf => {
465 let freq = clocks.ensure_fro_hf_active(&self.power)?;
466 (freq, Mux::ClkrootFunc1)
467 }
468 AdcClockSel::ClkIn => {
469 let freq = clocks.ensure_clk_in_active(&self.power)?;
470 (freq, Mux::ClkrootFunc3)
471 }
472 AdcClockSel::Clk1M => {
473 let freq = clocks.ensure_clk_1m_active(&self.power)?;
474 (freq, Mux::ClkrootFunc5)
475 }
476 AdcClockSel::Pll1ClkDiv => {
477 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
478 (freq, Mux::ClkrootFunc6)
479 }
480 AdcClockSel::None => {
481 mrcc0.mrcc_adc_clksel().write(|w| unsafe {
482 // no ClkrootFunc7, just write manually for now
483 w.mux().bits(0b111)
484 });
485 mrcc0.mrcc_adc_clkdiv().modify(|_r, w| {
486 w.reset().asserted();
487 w.halt().asserted();
488 w
489 });
490 return Ok(0);
491 }
492 };
493 let clksel = mrcc0.mrcc_adc_clksel();
494 let clkdiv = mrcc0.mrcc_adc_clkdiv();
495
496 apply_div4!(self, clksel, clkdiv, variant, freq)
497 }
498}
diff --git a/embassy-mcxa/src/config.rs b/embassy-mcxa/src/config.rs
new file mode 100644
index 000000000..8d52a1d44
--- /dev/null
+++ b/embassy-mcxa/src/config.rs
@@ -0,0 +1,27 @@
1// HAL configuration (minimal), mirroring embassy-imxrt style
2
3use crate::clocks::config::ClocksConfig;
4use crate::interrupt::Priority;
5
6#[non_exhaustive]
7pub struct Config {
8 #[cfg(feature = "time")]
9 pub time_interrupt_priority: Priority,
10 pub rtc_interrupt_priority: Priority,
11 pub adc_interrupt_priority: Priority,
12 pub gpio_interrupt_priority: Priority,
13 pub clock_cfg: ClocksConfig,
14}
15
16impl Default for Config {
17 fn default() -> Self {
18 Self {
19 #[cfg(feature = "time")]
20 time_interrupt_priority: Priority::from(0),
21 rtc_interrupt_priority: Priority::from(0),
22 adc_interrupt_priority: Priority::from(0),
23 gpio_interrupt_priority: Priority::from(0),
24 clock_cfg: ClocksConfig::default(),
25 }
26 }
27}
diff --git a/embassy-mcxa/src/gpio.rs b/embassy-mcxa/src/gpio.rs
new file mode 100644
index 000000000..65f8df985
--- /dev/null
+++ b/embassy-mcxa/src/gpio.rs
@@ -0,0 +1,1062 @@
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::convert::Infallible;
6use core::future::Future;
7use core::marker::PhantomData;
8use core::pin::pin;
9
10use embassy_hal_internal::{Peri, PeripheralType};
11use maitake_sync::WaitMap;
12use paste::paste;
13
14use crate::pac::interrupt;
15use crate::pac::port0::pcr0::{Dse, Inv, Mux, Pe, Ps, Sre};
16
17struct BitIter(u32);
18
19impl Iterator for BitIter {
20 type Item = usize;
21
22 fn next(&mut self) -> Option<Self::Item> {
23 match self.0.trailing_zeros() {
24 32 => None,
25 b => {
26 self.0 &= !(1 << b);
27 Some(b as usize)
28 }
29 }
30 }
31}
32
33const PORT_COUNT: usize = 5;
34
35static PORT_WAIT_MAPS: [WaitMap<usize, ()>; PORT_COUNT] = [
36 WaitMap::new(),
37 WaitMap::new(),
38 WaitMap::new(),
39 WaitMap::new(),
40 WaitMap::new(),
41];
42
43fn irq_handler(port_index: usize, gpio_base: *const crate::pac::gpio0::RegisterBlock) {
44 let gpio = unsafe { &*gpio_base };
45 let isfr = gpio.isfr0().read().bits();
46
47 for pin in BitIter(isfr) {
48 // Clear all pending interrupts
49 gpio.isfr0().write(|w| unsafe { w.bits(1 << pin) });
50 gpio.icr(pin).modify(|_, w| w.irqc().irqc0()); // Disable interrupt
51
52 // Wake the corresponding port waker
53 if let Some(w) = PORT_WAIT_MAPS.get(port_index) {
54 w.wake(&pin, ());
55 }
56 }
57}
58
59#[interrupt]
60fn GPIO0() {
61 irq_handler(0, crate::pac::Gpio0::ptr());
62}
63
64#[interrupt]
65fn GPIO1() {
66 irq_handler(1, crate::pac::Gpio1::ptr());
67}
68
69#[interrupt]
70fn GPIO2() {
71 irq_handler(2, crate::pac::Gpio2::ptr());
72}
73
74#[interrupt]
75fn GPIO3() {
76 irq_handler(3, crate::pac::Gpio3::ptr());
77}
78
79#[interrupt]
80fn GPIO4() {
81 irq_handler(4, crate::pac::Gpio4::ptr());
82}
83
84pub(crate) unsafe fn init() {
85 use embassy_hal_internal::interrupt::InterruptExt;
86
87 crate::pac::interrupt::GPIO0.enable();
88 crate::pac::interrupt::GPIO1.enable();
89 crate::pac::interrupt::GPIO2.enable();
90 crate::pac::interrupt::GPIO3.enable();
91 crate::pac::interrupt::GPIO4.enable();
92
93 cortex_m::interrupt::enable();
94}
95
96/// Logical level for GPIO pins.
97#[derive(Copy, Clone, Eq, PartialEq, Debug)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub enum Level {
100 Low,
101 High,
102}
103
104impl From<bool> for Level {
105 fn from(val: bool) -> Self {
106 match val {
107 true => Self::High,
108 false => Self::Low,
109 }
110 }
111}
112
113#[derive(Copy, Clone, Eq, PartialEq, Debug)]
114pub enum Pull {
115 Disabled,
116 Up,
117 Down,
118}
119
120impl From<Pull> for (Pe, Ps) {
121 fn from(pull: Pull) -> Self {
122 match pull {
123 Pull::Disabled => (Pe::Pe0, Ps::Ps0),
124 Pull::Up => (Pe::Pe1, Ps::Ps1),
125 Pull::Down => (Pe::Pe1, Ps::Ps0),
126 }
127 }
128}
129
130#[derive(Copy, Clone, Eq, PartialEq, Debug)]
131pub enum SlewRate {
132 Fast,
133 Slow,
134}
135
136impl From<SlewRate> for Sre {
137 fn from(slew_rate: SlewRate) -> Self {
138 match slew_rate {
139 SlewRate::Fast => Sre::Sre0,
140 SlewRate::Slow => Sre::Sre1,
141 }
142 }
143}
144
145#[derive(Copy, Clone, Eq, PartialEq, Debug)]
146pub enum DriveStrength {
147 Normal,
148 Double,
149}
150
151impl From<DriveStrength> for Dse {
152 fn from(strength: DriveStrength) -> Self {
153 match strength {
154 DriveStrength::Normal => Dse::Dse0,
155 DriveStrength::Double => Dse::Dse1,
156 }
157 }
158}
159
160#[derive(Copy, Clone, Eq, PartialEq, Debug)]
161pub enum Inverter {
162 Disabled,
163 Enabled,
164}
165
166impl From<Inverter> for Inv {
167 fn from(strength: Inverter) -> Self {
168 match strength {
169 Inverter::Disabled => Inv::Inv0,
170 Inverter::Enabled => Inv::Inv1,
171 }
172 }
173}
174
175pub type Gpio = crate::peripherals::GPIO0;
176
177/// Type-erased representation of a GPIO pin.
178pub struct AnyPin {
179 port: usize,
180 pin: usize,
181 gpio: &'static crate::pac::gpio0::RegisterBlock,
182 port_reg: &'static crate::pac::port0::RegisterBlock,
183 pcr_reg: &'static crate::pac::port0::Pcr0,
184}
185
186impl AnyPin {
187 /// Create an `AnyPin` from raw components.
188 fn new(
189 port: usize,
190 pin: usize,
191 gpio: &'static crate::pac::gpio0::RegisterBlock,
192 port_reg: &'static crate::pac::port0::RegisterBlock,
193 pcr_reg: &'static crate::pac::port0::Pcr0,
194 ) -> Self {
195 Self {
196 port,
197 pin,
198 gpio,
199 port_reg,
200 pcr_reg,
201 }
202 }
203
204 #[inline(always)]
205 fn mask(&self) -> u32 {
206 1 << self.pin
207 }
208
209 #[inline(always)]
210 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
211 self.gpio
212 }
213
214 #[inline(always)]
215 pub fn port_index(&self) -> usize {
216 self.port
217 }
218
219 #[inline(always)]
220 pub fn pin_index(&self) -> usize {
221 self.pin
222 }
223
224 #[inline(always)]
225 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
226 self.port_reg
227 }
228
229 #[inline(always)]
230 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
231 self.pcr_reg
232 }
233}
234
235embassy_hal_internal::impl_peripheral!(AnyPin);
236
237pub(crate) trait SealedPin {
238 fn pin_port(&self) -> usize;
239
240 fn port(&self) -> usize {
241 self.pin_port() / 32
242 }
243
244 fn pin(&self) -> usize {
245 self.pin_port() % 32
246 }
247
248 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock;
249
250 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock;
251
252 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0;
253
254 fn set_function(&self, function: Mux);
255
256 fn set_pull(&self, pull: Pull);
257
258 fn set_drive_strength(&self, strength: Dse);
259
260 fn set_slew_rate(&self, slew_rate: Sre);
261
262 fn set_enable_input_buffer(&self);
263}
264
265/// GPIO pin trait.
266#[allow(private_bounds)]
267pub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static {
268 /// Type-erase the pin.
269 fn degrade(self) -> AnyPin {
270 // SAFETY: This is only called within the GpioPin trait, which is only
271 // implemented within this module on valid pin peripherals and thus
272 // has been verified to be correct.
273 AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg())
274 }
275}
276
277impl SealedPin for AnyPin {
278 fn pin_port(&self) -> usize {
279 self.port * 32 + self.pin
280 }
281
282 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
283 self.gpio()
284 }
285
286 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
287 self.port_reg()
288 }
289
290 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
291 self.pcr_reg()
292 }
293
294 fn set_function(&self, function: Mux) {
295 self.pcr_reg().modify(|_, w| w.mux().variant(function));
296 }
297
298 fn set_pull(&self, pull: Pull) {
299 let (pull_enable, pull_select) = pull.into();
300 self.pcr_reg().modify(|_, w| {
301 w.pe().variant(pull_enable);
302 w.ps().variant(pull_select)
303 });
304 }
305
306 fn set_drive_strength(&self, strength: Dse) {
307 self.pcr_reg().modify(|_, w| w.dse().variant(strength));
308 }
309
310 fn set_slew_rate(&self, slew_rate: Sre) {
311 self.pcr_reg().modify(|_, w| w.sre().variant(slew_rate));
312 }
313
314 fn set_enable_input_buffer(&self) {
315 self.pcr_reg().modify(|_, w| w.ibe().ibe1());
316 }
317}
318
319impl GpioPin for AnyPin {}
320
321macro_rules! impl_pin {
322 ($peri:ident, $port:expr, $pin:expr, $block:ident) => {
323 paste! {
324 impl SealedPin for crate::peripherals::$peri {
325 fn pin_port(&self) -> usize {
326 $port * 32 + $pin
327 }
328
329 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
330 unsafe { &*crate::pac::$block::ptr() }
331 }
332
333 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
334 unsafe { &*crate::pac::[<Port $port>]::ptr() }
335 }
336
337 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
338 self.port_reg().[<pcr $pin>]()
339 }
340
341 fn set_function(&self, function: Mux) {
342 unsafe {
343 let port_reg = &*crate::pac::[<Port $port>]::ptr();
344 port_reg.[<pcr $pin>]().modify(|_, w| {
345 w.mux().variant(function)
346 });
347 }
348 }
349
350 fn set_pull(&self, pull: Pull) {
351 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
352 let (pull_enable, pull_select) = pull.into();
353 port_reg.[<pcr $pin>]().modify(|_, w| {
354 w.pe().variant(pull_enable);
355 w.ps().variant(pull_select)
356 });
357 }
358
359 fn set_drive_strength(&self, strength: Dse) {
360 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
361 port_reg.[<pcr $pin>]().modify(|_, w| w.dse().variant(strength));
362 }
363
364 fn set_slew_rate(&self, slew_rate: Sre) {
365 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
366 port_reg.[<pcr $pin>]().modify(|_, w| w.sre().variant(slew_rate));
367 }
368
369 fn set_enable_input_buffer(&self) {
370 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
371 port_reg.[<pcr $pin>]().modify(|_, w| w.ibe().ibe1());
372 }
373 }
374
375 impl GpioPin for crate::peripherals::$peri {}
376
377 impl From<crate::peripherals::$peri> for AnyPin {
378 fn from(value: crate::peripherals::$peri) -> Self {
379 value.degrade()
380 }
381 }
382
383 impl crate::peripherals::$peri {
384 /// Convenience helper to obtain a type-erased handle to this pin.
385 pub fn degrade(&self) -> AnyPin {
386 AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg())
387 }
388 }
389 }
390 };
391}
392
393impl_pin!(P0_0, 0, 0, Gpio0);
394impl_pin!(P0_1, 0, 1, Gpio0);
395impl_pin!(P0_2, 0, 2, Gpio0);
396impl_pin!(P0_3, 0, 3, Gpio0);
397impl_pin!(P0_4, 0, 4, Gpio0);
398impl_pin!(P0_5, 0, 5, Gpio0);
399impl_pin!(P0_6, 0, 6, Gpio0);
400impl_pin!(P0_7, 0, 7, Gpio0);
401impl_pin!(P0_8, 0, 8, Gpio0);
402impl_pin!(P0_9, 0, 9, Gpio0);
403impl_pin!(P0_10, 0, 10, Gpio0);
404impl_pin!(P0_11, 0, 11, Gpio0);
405impl_pin!(P0_12, 0, 12, Gpio0);
406impl_pin!(P0_13, 0, 13, Gpio0);
407impl_pin!(P0_14, 0, 14, Gpio0);
408impl_pin!(P0_15, 0, 15, Gpio0);
409impl_pin!(P0_16, 0, 16, Gpio0);
410impl_pin!(P0_17, 0, 17, Gpio0);
411impl_pin!(P0_18, 0, 18, Gpio0);
412impl_pin!(P0_19, 0, 19, Gpio0);
413impl_pin!(P0_20, 0, 20, Gpio0);
414impl_pin!(P0_21, 0, 21, Gpio0);
415impl_pin!(P0_22, 0, 22, Gpio0);
416impl_pin!(P0_23, 0, 23, Gpio0);
417impl_pin!(P0_24, 0, 24, Gpio0);
418impl_pin!(P0_25, 0, 25, Gpio0);
419impl_pin!(P0_26, 0, 26, Gpio0);
420impl_pin!(P0_27, 0, 27, Gpio0);
421impl_pin!(P0_28, 0, 28, Gpio0);
422impl_pin!(P0_29, 0, 29, Gpio0);
423impl_pin!(P0_30, 0, 30, Gpio0);
424impl_pin!(P0_31, 0, 31, Gpio0);
425
426impl_pin!(P1_0, 1, 0, Gpio1);
427impl_pin!(P1_1, 1, 1, Gpio1);
428impl_pin!(P1_2, 1, 2, Gpio1);
429impl_pin!(P1_3, 1, 3, Gpio1);
430impl_pin!(P1_4, 1, 4, Gpio1);
431impl_pin!(P1_5, 1, 5, Gpio1);
432impl_pin!(P1_6, 1, 6, Gpio1);
433impl_pin!(P1_7, 1, 7, Gpio1);
434impl_pin!(P1_8, 1, 8, Gpio1);
435impl_pin!(P1_9, 1, 9, Gpio1);
436impl_pin!(P1_10, 1, 10, Gpio1);
437impl_pin!(P1_11, 1, 11, Gpio1);
438impl_pin!(P1_12, 1, 12, Gpio1);
439impl_pin!(P1_13, 1, 13, Gpio1);
440impl_pin!(P1_14, 1, 14, Gpio1);
441impl_pin!(P1_15, 1, 15, Gpio1);
442impl_pin!(P1_16, 1, 16, Gpio1);
443impl_pin!(P1_17, 1, 17, Gpio1);
444impl_pin!(P1_18, 1, 18, Gpio1);
445impl_pin!(P1_19, 1, 19, Gpio1);
446impl_pin!(P1_20, 1, 20, Gpio1);
447impl_pin!(P1_21, 1, 21, Gpio1);
448impl_pin!(P1_22, 1, 22, Gpio1);
449impl_pin!(P1_23, 1, 23, Gpio1);
450impl_pin!(P1_24, 1, 24, Gpio1);
451impl_pin!(P1_25, 1, 25, Gpio1);
452impl_pin!(P1_26, 1, 26, Gpio1);
453impl_pin!(P1_27, 1, 27, Gpio1);
454impl_pin!(P1_28, 1, 28, Gpio1);
455impl_pin!(P1_29, 1, 29, Gpio1);
456impl_pin!(P1_30, 1, 30, Gpio1);
457impl_pin!(P1_31, 1, 31, Gpio1);
458
459impl_pin!(P2_0, 2, 0, Gpio2);
460impl_pin!(P2_1, 2, 1, Gpio2);
461impl_pin!(P2_2, 2, 2, Gpio2);
462impl_pin!(P2_3, 2, 3, Gpio2);
463impl_pin!(P2_4, 2, 4, Gpio2);
464impl_pin!(P2_5, 2, 5, Gpio2);
465impl_pin!(P2_6, 2, 6, Gpio2);
466impl_pin!(P2_7, 2, 7, Gpio2);
467impl_pin!(P2_8, 2, 8, Gpio2);
468impl_pin!(P2_9, 2, 9, Gpio2);
469impl_pin!(P2_10, 2, 10, Gpio2);
470impl_pin!(P2_11, 2, 11, Gpio2);
471impl_pin!(P2_12, 2, 12, Gpio2);
472impl_pin!(P2_13, 2, 13, Gpio2);
473impl_pin!(P2_14, 2, 14, Gpio2);
474impl_pin!(P2_15, 2, 15, Gpio2);
475impl_pin!(P2_16, 2, 16, Gpio2);
476impl_pin!(P2_17, 2, 17, Gpio2);
477impl_pin!(P2_18, 2, 18, Gpio2);
478impl_pin!(P2_19, 2, 19, Gpio2);
479impl_pin!(P2_20, 2, 20, Gpio2);
480impl_pin!(P2_21, 2, 21, Gpio2);
481impl_pin!(P2_22, 2, 22, Gpio2);
482impl_pin!(P2_23, 2, 23, Gpio2);
483impl_pin!(P2_24, 2, 24, Gpio2);
484impl_pin!(P2_25, 2, 25, Gpio2);
485impl_pin!(P2_26, 2, 26, Gpio2);
486impl_pin!(P2_27, 2, 27, Gpio2);
487impl_pin!(P2_28, 2, 28, Gpio2);
488impl_pin!(P2_29, 2, 29, Gpio2);
489impl_pin!(P2_30, 2, 30, Gpio2);
490impl_pin!(P2_31, 2, 31, Gpio2);
491
492impl_pin!(P3_0, 3, 0, Gpio3);
493impl_pin!(P3_1, 3, 1, Gpio3);
494impl_pin!(P3_2, 3, 2, Gpio3);
495impl_pin!(P3_3, 3, 3, Gpio3);
496impl_pin!(P3_4, 3, 4, Gpio3);
497impl_pin!(P3_5, 3, 5, Gpio3);
498impl_pin!(P3_6, 3, 6, Gpio3);
499impl_pin!(P3_7, 3, 7, Gpio3);
500impl_pin!(P3_8, 3, 8, Gpio3);
501impl_pin!(P3_9, 3, 9, Gpio3);
502impl_pin!(P3_10, 3, 10, Gpio3);
503impl_pin!(P3_11, 3, 11, Gpio3);
504impl_pin!(P3_12, 3, 12, Gpio3);
505impl_pin!(P3_13, 3, 13, Gpio3);
506impl_pin!(P3_14, 3, 14, Gpio3);
507impl_pin!(P3_15, 3, 15, Gpio3);
508impl_pin!(P3_16, 3, 16, Gpio3);
509impl_pin!(P3_17, 3, 17, Gpio3);
510impl_pin!(P3_18, 3, 18, Gpio3);
511impl_pin!(P3_19, 3, 19, Gpio3);
512impl_pin!(P3_20, 3, 20, Gpio3);
513impl_pin!(P3_21, 3, 21, Gpio3);
514impl_pin!(P3_22, 3, 22, Gpio3);
515impl_pin!(P3_23, 3, 23, Gpio3);
516impl_pin!(P3_24, 3, 24, Gpio3);
517impl_pin!(P3_25, 3, 25, Gpio3);
518impl_pin!(P3_26, 3, 26, Gpio3);
519impl_pin!(P3_27, 3, 27, Gpio3);
520impl_pin!(P3_28, 3, 28, Gpio3);
521impl_pin!(P3_29, 3, 29, Gpio3);
522impl_pin!(P3_30, 3, 30, Gpio3);
523impl_pin!(P3_31, 3, 31, Gpio3);
524
525impl_pin!(P4_0, 4, 0, Gpio4);
526impl_pin!(P4_1, 4, 1, Gpio4);
527impl_pin!(P4_2, 4, 2, Gpio4);
528impl_pin!(P4_3, 4, 3, Gpio4);
529impl_pin!(P4_4, 4, 4, Gpio4);
530impl_pin!(P4_5, 4, 5, Gpio4);
531impl_pin!(P4_6, 4, 6, Gpio4);
532impl_pin!(P4_7, 4, 7, Gpio4);
533impl_pin!(P4_8, 4, 8, Gpio4);
534impl_pin!(P4_9, 4, 9, Gpio4);
535impl_pin!(P4_10, 4, 10, Gpio4);
536impl_pin!(P4_11, 4, 11, Gpio4);
537impl_pin!(P4_12, 4, 12, Gpio4);
538impl_pin!(P4_13, 4, 13, Gpio4);
539impl_pin!(P4_14, 4, 14, Gpio4);
540impl_pin!(P4_15, 4, 15, Gpio4);
541impl_pin!(P4_16, 4, 16, Gpio4);
542impl_pin!(P4_17, 4, 17, Gpio4);
543impl_pin!(P4_18, 4, 18, Gpio4);
544impl_pin!(P4_19, 4, 19, Gpio4);
545impl_pin!(P4_20, 4, 20, Gpio4);
546impl_pin!(P4_21, 4, 21, Gpio4);
547impl_pin!(P4_22, 4, 22, Gpio4);
548impl_pin!(P4_23, 4, 23, Gpio4);
549impl_pin!(P4_24, 4, 24, Gpio4);
550impl_pin!(P4_25, 4, 25, Gpio4);
551impl_pin!(P4_26, 4, 26, Gpio4);
552impl_pin!(P4_27, 4, 27, Gpio4);
553impl_pin!(P4_28, 4, 28, Gpio4);
554impl_pin!(P4_29, 4, 29, Gpio4);
555impl_pin!(P4_30, 4, 30, Gpio4);
556impl_pin!(P4_31, 4, 31, Gpio4);
557
558/// A flexible pin that can be configured as input or output.
559pub struct Flex<'d> {
560 pin: Peri<'d, AnyPin>,
561 _marker: PhantomData<&'d mut ()>,
562}
563
564impl<'d> Flex<'d> {
565 /// Wrap the pin in a `Flex`.
566 ///
567 /// The pin remains unmodified. The initial output level is unspecified, but
568 /// can be changed before the pin is put into output mode.
569 pub fn new(pin: Peri<'d, impl GpioPin>) -> Self {
570 pin.set_function(Mux::Mux0);
571 Self {
572 pin: pin.into(),
573 _marker: PhantomData,
574 }
575 }
576
577 #[inline]
578 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
579 self.pin.gpio()
580 }
581
582 #[inline]
583 fn mask(&self) -> u32 {
584 self.pin.mask()
585 }
586
587 /// Put the pin into input mode.
588 pub fn set_as_input(&mut self) {
589 let mask = self.mask();
590 let gpio = self.gpio();
591
592 self.set_enable_input_buffer();
593
594 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
595 }
596
597 /// Put the pin into output mode.
598 pub fn set_as_output(&mut self) {
599 let mask = self.mask();
600 let gpio = self.gpio();
601
602 self.set_pull(Pull::Disabled);
603
604 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
605 }
606
607 /// Set output level to High.
608 #[inline]
609 pub fn set_high(&mut self) {
610 self.gpio().psor().write(|w| unsafe { w.bits(self.mask()) });
611 }
612
613 /// Set output level to Low.
614 #[inline]
615 pub fn set_low(&mut self) {
616 self.gpio().pcor().write(|w| unsafe { w.bits(self.mask()) });
617 }
618
619 /// Set output level to the given `Level`.
620 #[inline]
621 pub fn set_level(&mut self, level: Level) {
622 match level {
623 Level::High => self.set_high(),
624 Level::Low => self.set_low(),
625 }
626 }
627
628 /// Toggle output level.
629 #[inline]
630 pub fn toggle(&mut self) {
631 self.gpio().ptor().write(|w| unsafe { w.bits(self.mask()) });
632 }
633
634 /// Get whether the pin input level is high.
635 #[inline]
636 pub fn is_high(&self) -> bool {
637 (self.gpio().pdir().read().bits() & self.mask()) != 0
638 }
639
640 /// Get whether the pin input level is low.
641 #[inline]
642 pub fn is_low(&self) -> bool {
643 !self.is_high()
644 }
645
646 /// Is the output pin set as high?
647 #[inline]
648 pub fn is_set_high(&self) -> bool {
649 self.is_high()
650 }
651
652 /// Is the output pin set as low?
653 #[inline]
654 pub fn is_set_low(&self) -> bool {
655 !self.is_set_high()
656 }
657
658 /// Configure the pin pull up/down level.
659 pub fn set_pull(&mut self, pull_select: Pull) {
660 self.pin.set_pull(pull_select);
661 }
662
663 /// Configure the pin drive strength.
664 pub fn set_drive_strength(&mut self, strength: DriveStrength) {
665 self.pin.set_drive_strength(strength.into());
666 }
667
668 /// Configure the pin slew rate.
669 pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
670 self.pin.set_slew_rate(slew_rate.into());
671 }
672
673 /// Enable input buffer for the pin.
674 pub fn set_enable_input_buffer(&mut self) {
675 self.pin.set_enable_input_buffer();
676 }
677
678 /// Get pin level.
679 pub fn get_level(&self) -> Level {
680 self.is_high().into()
681 }
682}
683
684/// Async methods
685impl<'d> Flex<'d> {
686 /// Helper function that waits for a given interrupt trigger
687 async fn wait_for_inner(&mut self, level: crate::pac::gpio0::icr::Irqc) {
688 // First, ensure that we have a waker that is ready for this port+pin
689 let w = PORT_WAIT_MAPS[self.pin.port].wait(self.pin.pin);
690 let mut w = pin!(w);
691 // Wait for the subscription to occur, which requires polling at least once
692 //
693 // This function returns a result, but can only be an Err if:
694 //
695 // * We call `.close()` on a WaitMap, which we never do
696 // * We have a duplicate key, which can't happen because `wait_for_*` methods
697 // take an &mut ref of their unique port+pin combo
698 //
699 // So we wait for it to complete, but ignore the result.
700 _ = w.as_mut().subscribe().await;
701
702 // Now that our waker is in the map, we can enable the appropriate interrupt
703 //
704 // Clear any existing pending interrupt on this pin
705 self.pin
706 .gpio()
707 .isfr0()
708 .write(|w| unsafe { w.bits(1 << self.pin.pin()) });
709 self.pin.gpio().icr(self.pin.pin()).write(|w| w.isf().isf1());
710
711 // Pin interrupt configuration
712 self.pin
713 .gpio()
714 .icr(self.pin.pin())
715 .modify(|_, w| w.irqc().variant(level));
716
717 // Finally, we can await the matching call to `.wake()` from the interrupt.
718 //
719 // Again, technically, this could return a result, but for the same reasons
720 // as above, this can't be an error in our case, so just wait for it to complete
721 _ = w.await;
722 }
723
724 /// Wait until the pin is high. If it is already high, return immediately.
725 #[inline]
726 pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
727 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc12)
728 }
729
730 /// Wait until the pin is low. If it is already low, return immediately.
731 #[inline]
732 pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
733 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc8)
734 }
735
736 /// Wait for the pin to undergo a transition from low to high.
737 #[inline]
738 pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
739 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc9)
740 }
741
742 /// Wait for the pin to undergo a transition from high to low.
743 #[inline]
744 pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
745 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc10)
746 }
747
748 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
749 #[inline]
750 pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
751 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc11)
752 }
753}
754
755/// GPIO output driver that owns a `Flex` pin.
756pub struct Output<'d> {
757 flex: Flex<'d>,
758}
759
760impl<'d> Output<'d> {
761 /// Create a GPIO output driver for a [GpioPin] with the provided [Level].
762 pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level, strength: DriveStrength, slew_rate: SlewRate) -> Self {
763 let mut flex = Flex::new(pin);
764 flex.set_level(initial);
765 flex.set_as_output();
766 flex.set_drive_strength(strength);
767 flex.set_slew_rate(slew_rate);
768 Self { flex }
769 }
770
771 /// Set the output as high.
772 #[inline]
773 pub fn set_high(&mut self) {
774 self.flex.set_high();
775 }
776
777 /// Set the output as low.
778 #[inline]
779 pub fn set_low(&mut self) {
780 self.flex.set_low();
781 }
782
783 /// Set the output level.
784 #[inline]
785 pub fn set_level(&mut self, level: Level) {
786 self.flex.set_level(level);
787 }
788
789 /// Toggle the output level.
790 #[inline]
791 pub fn toggle(&mut self) {
792 self.flex.toggle();
793 }
794
795 /// Is the output pin set as high?
796 #[inline]
797 pub fn is_set_high(&self) -> bool {
798 self.flex.is_high()
799 }
800
801 /// Is the output pin set as low?
802 #[inline]
803 pub fn is_set_low(&self) -> bool {
804 !self.is_set_high()
805 }
806
807 /// Expose the inner `Flex` if callers need to reconfigure the pin.
808 #[inline]
809 pub fn into_flex(self) -> Flex<'d> {
810 self.flex
811 }
812}
813
814/// GPIO input driver that owns a `Flex` pin.
815pub struct Input<'d> {
816 flex: Flex<'d>,
817}
818
819impl<'d> Input<'d> {
820 /// Create a GPIO input driver for a [GpioPin].
821 ///
822 pub fn new(pin: Peri<'d, impl GpioPin>, pull_select: Pull) -> Self {
823 let mut flex = Flex::new(pin);
824 flex.set_as_input();
825 flex.set_pull(pull_select);
826 Self { flex }
827 }
828
829 /// Get whether the pin input level is high.
830 #[inline]
831 pub fn is_high(&self) -> bool {
832 self.flex.is_high()
833 }
834
835 /// Get whether the pin input level is low.
836 #[inline]
837 pub fn is_low(&self) -> bool {
838 self.flex.is_low()
839 }
840
841 /// Expose the inner `Flex` if callers need to reconfigure the pin.
842 ///
843 /// Since Drive Strength and Slew Rate are not set when creating the Input
844 /// pin, they need to be set when converting
845 #[inline]
846 pub fn into_flex(mut self, strength: DriveStrength, slew_rate: SlewRate) -> Flex<'d> {
847 self.flex.set_drive_strength(strength);
848 self.flex.set_slew_rate(slew_rate);
849 self.flex
850 }
851
852 /// Get the pin level.
853 pub fn get_level(&self) -> Level {
854 self.flex.get_level()
855 }
856}
857
858/// Async methods
859impl<'d> Input<'d> {
860 /// Wait until the pin is high. If it is already high, return immediately.
861 #[inline]
862 pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
863 self.flex.wait_for_high()
864 }
865
866 /// Wait until the pin is low. If it is already low, return immediately.
867 #[inline]
868 pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
869 self.flex.wait_for_low()
870 }
871
872 /// Wait for the pin to undergo a transition from low to high.
873 #[inline]
874 pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
875 self.flex.wait_for_rising_edge()
876 }
877
878 /// Wait for the pin to undergo a transition from high to low.
879 #[inline]
880 pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
881 self.flex.wait_for_falling_edge()
882 }
883
884 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
885 #[inline]
886 pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
887 self.flex.wait_for_any_edge()
888 }
889}
890
891impl embedded_hal_async::digital::Wait for Input<'_> {
892 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
893 self.wait_for_high().await;
894 Ok(())
895 }
896
897 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
898 self.wait_for_low().await;
899 Ok(())
900 }
901
902 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
903 self.wait_for_rising_edge().await;
904 Ok(())
905 }
906
907 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
908 self.wait_for_falling_edge().await;
909 Ok(())
910 }
911
912 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
913 self.wait_for_any_edge().await;
914 Ok(())
915 }
916}
917
918impl embedded_hal_async::digital::Wait for Flex<'_> {
919 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
920 self.wait_for_high().await;
921 Ok(())
922 }
923
924 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
925 self.wait_for_low().await;
926 Ok(())
927 }
928
929 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
930 self.wait_for_rising_edge().await;
931 Ok(())
932 }
933
934 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
935 self.wait_for_falling_edge().await;
936 Ok(())
937 }
938
939 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
940 self.wait_for_any_edge().await;
941 Ok(())
942 }
943}
944
945// Both embedded_hal 0.2 and 1.0 must be supported by embassy HALs.
946impl embedded_hal_02::digital::v2::InputPin for Flex<'_> {
947 // GPIO operations on this block cannot fail, therefor we set the error type
948 // to Infallible to guarantee that we can only produce Ok variants.
949 type Error = Infallible;
950
951 #[inline]
952 fn is_high(&self) -> Result<bool, Self::Error> {
953 Ok(self.is_high())
954 }
955
956 #[inline]
957 fn is_low(&self) -> Result<bool, Self::Error> {
958 Ok(self.is_low())
959 }
960}
961
962impl embedded_hal_02::digital::v2::InputPin for Input<'_> {
963 type Error = Infallible;
964
965 #[inline]
966 fn is_high(&self) -> Result<bool, Self::Error> {
967 Ok(self.is_high())
968 }
969
970 #[inline]
971 fn is_low(&self) -> Result<bool, Self::Error> {
972 Ok(self.is_low())
973 }
974}
975
976impl embedded_hal_02::digital::v2::OutputPin for Flex<'_> {
977 type Error = Infallible;
978
979 #[inline]
980 fn set_high(&mut self) -> Result<(), Self::Error> {
981 self.set_high();
982 Ok(())
983 }
984
985 #[inline]
986 fn set_low(&mut self) -> Result<(), Self::Error> {
987 self.set_low();
988 Ok(())
989 }
990}
991
992impl embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'_> {
993 #[inline]
994 fn is_set_high(&self) -> Result<bool, Self::Error> {
995 Ok(self.is_set_high())
996 }
997
998 #[inline]
999 fn is_set_low(&self) -> Result<bool, Self::Error> {
1000 Ok(self.is_set_low())
1001 }
1002}
1003
1004impl embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'_> {
1005 type Error = Infallible;
1006
1007 #[inline]
1008 fn toggle(&mut self) -> Result<(), Self::Error> {
1009 self.toggle();
1010 Ok(())
1011 }
1012}
1013
1014impl embedded_hal_1::digital::ErrorType for Flex<'_> {
1015 type Error = Infallible;
1016}
1017
1018impl embedded_hal_1::digital::ErrorType for Input<'_> {
1019 type Error = Infallible;
1020}
1021
1022impl embedded_hal_1::digital::ErrorType for Output<'_> {
1023 type Error = Infallible;
1024}
1025
1026impl embedded_hal_1::digital::InputPin for Input<'_> {
1027 #[inline]
1028 fn is_high(&mut self) -> Result<bool, Self::Error> {
1029 Ok((*self).is_high())
1030 }
1031
1032 #[inline]
1033 fn is_low(&mut self) -> Result<bool, Self::Error> {
1034 Ok((*self).is_low())
1035 }
1036}
1037
1038impl embedded_hal_1::digital::OutputPin for Flex<'_> {
1039 #[inline]
1040 fn set_high(&mut self) -> Result<(), Self::Error> {
1041 self.set_high();
1042 Ok(())
1043 }
1044
1045 #[inline]
1046 fn set_low(&mut self) -> Result<(), Self::Error> {
1047 self.set_low();
1048 Ok(())
1049 }
1050}
1051
1052impl embedded_hal_1::digital::StatefulOutputPin for Flex<'_> {
1053 #[inline]
1054 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
1055 Ok((*self).is_set_high())
1056 }
1057
1058 #[inline]
1059 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
1060 Ok((*self).is_set_low())
1061 }
1062}
diff --git a/embassy-mcxa/src/i2c/controller.rs b/embassy-mcxa/src/i2c/controller.rs
new file mode 100644
index 000000000..c27d508b0
--- /dev/null
+++ b/embassy-mcxa/src/i2c/controller.rs
@@ -0,0 +1,742 @@
1//! LPI2C controller driver
2
3use core::future::Future;
4use core::marker::PhantomData;
5
6use embassy_hal_internal::Peri;
7use embassy_hal_internal::drop::OnDrop;
8use mcxa_pac::lpi2c0::mtdr::Cmd;
9
10use super::{Async, Blocking, Error, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin};
11use crate::AnyPin;
12use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig};
13use crate::clocks::{PoweredClock, enable_and_reset};
14use crate::interrupt::typelevel::Interrupt;
15
16/// Bus speed (nominal SCL, no clock stretching)
17#[derive(Clone, Copy, Default, PartialEq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub enum Speed {
20 #[default]
21 /// 100 kbit/sec
22 Standard,
23 /// 400 kbit/sec
24 Fast,
25 /// 1 Mbit/sec
26 FastPlus,
27 /// 3.4 Mbit/sec
28 UltraFast,
29}
30
31impl From<Speed> for (u8, u8, u8, u8) {
32 fn from(value: Speed) -> (u8, u8, u8, u8) {
33 match value {
34 Speed::Standard => (0x3d, 0x37, 0x3b, 0x1d),
35 Speed::Fast => (0x0e, 0x0c, 0x0d, 0x06),
36 Speed::FastPlus => (0x04, 0x03, 0x03, 0x02),
37
38 // UltraFast is "special". Leaving it unimplemented until
39 // the driver and the clock API is further stabilized.
40 Speed::UltraFast => todo!(),
41 }
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47enum SendStop {
48 No,
49 Yes,
50}
51
52/// I2C controller configuration
53#[derive(Clone, Copy, Default)]
54#[non_exhaustive]
55pub struct Config {
56 /// Bus speed
57 pub speed: Speed,
58}
59
60/// I2C Controller Driver.
61pub struct I2c<'d, T: Instance, M: Mode> {
62 _peri: Peri<'d, T>,
63 _scl: Peri<'d, AnyPin>,
64 _sda: Peri<'d, AnyPin>,
65 _phantom: PhantomData<M>,
66 is_hs: bool,
67}
68
69impl<'d, T: Instance> I2c<'d, T, Blocking> {
70 /// Create a new blocking instance of the I2C Controller bus driver.
71 pub fn new_blocking(
72 peri: Peri<'d, T>,
73 scl: Peri<'d, impl SclPin<T>>,
74 sda: Peri<'d, impl SdaPin<T>>,
75 config: Config,
76 ) -> Result<Self> {
77 Self::new_inner(peri, scl, sda, config)
78 }
79}
80
81impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
82 fn new_inner(
83 _peri: Peri<'d, T>,
84 scl: Peri<'d, impl SclPin<T>>,
85 sda: Peri<'d, impl SdaPin<T>>,
86 config: Config,
87 ) -> Result<Self> {
88 let (power, source, div) = Self::clock_config(config.speed);
89
90 // Enable clocks
91 let conf = Lpi2cConfig {
92 power,
93 source,
94 div,
95 instance: T::CLOCK_INSTANCE,
96 };
97
98 _ = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
99
100 scl.mux();
101 sda.mux();
102
103 let _scl = scl.into();
104 let _sda = sda.into();
105
106 Self::set_config(&config)?;
107
108 Ok(Self {
109 _peri,
110 _scl,
111 _sda,
112 _phantom: PhantomData,
113 is_hs: config.speed == Speed::UltraFast,
114 })
115 }
116
117 fn set_config(config: &Config) -> Result<()> {
118 // Disable the controller.
119 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().disabled()));
120
121 // Soft-reset the controller, read and write FIFOs.
122 Self::reset_fifos();
123 critical_section::with(|_| {
124 T::regs().mcr().modify(|_, w| w.rst().reset());
125 // According to Reference Manual section 40.7.1.4, "There
126 // is no minimum delay required before clearing the
127 // software reset", therefore we clear it immediately.
128 T::regs().mcr().modify(|_, w| w.rst().not_reset());
129
130 T::regs().mcr().modify(|_, w| w.dozen().clear_bit().dbgen().clear_bit());
131 });
132
133 let (clklo, clkhi, sethold, datavd) = config.speed.into();
134
135 critical_section::with(|_| {
136 T::regs().mccr0().modify(|_, w| unsafe {
137 w.clklo()
138 .bits(clklo)
139 .clkhi()
140 .bits(clkhi)
141 .sethold()
142 .bits(sethold)
143 .datavd()
144 .bits(datavd)
145 })
146 });
147
148 // Enable the controller.
149 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().enabled()));
150
151 // Clear all flags
152 T::regs().msr().write(|w| {
153 w.epf()
154 .clear_bit_by_one()
155 .sdf()
156 .clear_bit_by_one()
157 .ndf()
158 .clear_bit_by_one()
159 .alf()
160 .clear_bit_by_one()
161 .fef()
162 .clear_bit_by_one()
163 .pltf()
164 .clear_bit_by_one()
165 .dmf()
166 .clear_bit_by_one()
167 .stf()
168 .clear_bit_by_one()
169 });
170
171 Ok(())
172 }
173
174 // REVISIT: turn this into a function of the speed parameter
175 fn clock_config(speed: Speed) -> (PoweredClock, Lpi2cClockSel, Div4) {
176 match speed {
177 Speed::Standard | Speed::Fast | Speed::FastPlus => (
178 PoweredClock::NormalEnabledDeepSleepDisabled,
179 Lpi2cClockSel::FroLfDiv,
180 const { Div4::no_div() },
181 ),
182 Speed::UltraFast => (
183 PoweredClock::NormalEnabledDeepSleepDisabled,
184 Lpi2cClockSel::FroHfDiv,
185 const { Div4::no_div() },
186 ),
187 }
188 }
189
190 /// Resets both TX and RX FIFOs dropping their contents.
191 fn reset_fifos() {
192 critical_section::with(|_| {
193 T::regs().mcr().modify(|_, w| w.rtf().reset().rrf().reset());
194 });
195 }
196
197 /// Checks whether the TX FIFO is full
198 fn is_tx_fifo_full() -> bool {
199 let txfifo_size = 1 << T::regs().param().read().mtxfifo().bits();
200 T::regs().mfsr().read().txcount().bits() == txfifo_size
201 }
202
203 /// Checks whether the TX FIFO is empty
204 fn is_tx_fifo_empty() -> bool {
205 T::regs().mfsr().read().txcount() == 0
206 }
207
208 /// Checks whether the RX FIFO is empty.
209 fn is_rx_fifo_empty() -> bool {
210 T::regs().mfsr().read().rxcount() == 0
211 }
212
213 /// Reads and parses the controller status producing an
214 /// appropriate `Result<(), Error>` variant.
215 fn status() -> Result<()> {
216 let msr = T::regs().msr().read();
217 T::regs().msr().write(|w| {
218 w.epf()
219 .clear_bit_by_one()
220 .sdf()
221 .clear_bit_by_one()
222 .ndf()
223 .clear_bit_by_one()
224 .alf()
225 .clear_bit_by_one()
226 .fef()
227 .clear_bit_by_one()
228 .fef()
229 .clear_bit_by_one()
230 .pltf()
231 .clear_bit_by_one()
232 .dmf()
233 .clear_bit_by_one()
234 .stf()
235 .clear_bit_by_one()
236 });
237
238 if msr.ndf().bit_is_set() {
239 Err(Error::AddressNack)
240 } else if msr.alf().bit_is_set() {
241 Err(Error::ArbitrationLoss)
242 } else if msr.fef().bit_is_set() {
243 Err(Error::FifoError)
244 } else {
245 Ok(())
246 }
247 }
248
249 /// Inserts the given command into the outgoing FIFO.
250 ///
251 /// Caller must ensure there is space in the FIFO for the new
252 /// command.
253 fn send_cmd(cmd: Cmd, data: u8) {
254 #[cfg(feature = "defmt")]
255 defmt::trace!(
256 "Sending cmd '{}' ({}) with data '{:08x}' MSR: {:08x}",
257 cmd,
258 cmd as u8,
259 data,
260 T::regs().msr().read().bits()
261 );
262
263 T::regs()
264 .mtdr()
265 .write(|w| unsafe { w.data().bits(data) }.cmd().variant(cmd));
266 }
267
268 /// Prepares an appropriate Start condition on bus by issuing a
269 /// `Start` command together with the device address and R/w bit.
270 ///
271 /// Blocks waiting for space in the FIFO to become available, then
272 /// sends the command and blocks waiting for the FIFO to become
273 /// empty ensuring the command was sent.
274 fn start(&mut self, address: u8, read: bool) -> Result<()> {
275 if address >= 0x80 {
276 return Err(Error::AddressOutOfRange(address));
277 }
278
279 // Wait until we have space in the TxFIFO
280 while Self::is_tx_fifo_full() {}
281
282 let addr_rw = address << 1 | if read { 1 } else { 0 };
283 Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
284
285 // Wait for TxFIFO to be drained
286 while !Self::is_tx_fifo_empty() {}
287
288 // Check controller status
289 Self::status()
290 }
291
292 /// Prepares a Stop condition on the bus.
293 ///
294 /// Analogous to `start`, this blocks waiting for space in the
295 /// FIFO to become available, then sends the command and blocks
296 /// waiting for the FIFO to become empty ensuring the command was
297 /// sent.
298 fn stop() -> Result<()> {
299 // Wait until we have space in the TxFIFO
300 while Self::is_tx_fifo_full() {}
301
302 Self::send_cmd(Cmd::Stop, 0);
303
304 // Wait for TxFIFO to be drained
305 while !Self::is_tx_fifo_empty() {}
306
307 Self::status()
308 }
309
310 fn blocking_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
311 if read.is_empty() {
312 return Err(Error::InvalidReadBufferLength);
313 }
314
315 for chunk in read.chunks_mut(256) {
316 self.start(address, true)?;
317
318 // Wait until we have space in the TxFIFO
319 while Self::is_tx_fifo_full() {}
320
321 Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
322
323 for byte in chunk.iter_mut() {
324 // Wait until there's data in the RxFIFO
325 while Self::is_rx_fifo_empty() {}
326
327 *byte = T::regs().mrdr().read().data().bits();
328 }
329 }
330
331 if send_stop == SendStop::Yes {
332 Self::stop()?;
333 }
334
335 Ok(())
336 }
337
338 fn blocking_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
339 self.start(address, false)?;
340
341 // Usually, embassy HALs error out with an empty write,
342 // however empty writes are useful for writing I2C scanning
343 // logic through write probing. That is, we send a start with
344 // R/w bit cleared, but instead of writing any data, just send
345 // the stop onto the bus. This has the effect of checking if
346 // the resulting address got an ACK but causing no
347 // side-effects to the device on the other end.
348 //
349 // Because of this, we are not going to error out in case of
350 // empty writes.
351 #[cfg(feature = "defmt")]
352 if write.is_empty() {
353 defmt::trace!("Empty write, write probing?");
354 }
355
356 for byte in write {
357 // Wait until we have space in the TxFIFO
358 while Self::is_tx_fifo_full() {}
359
360 Self::send_cmd(Cmd::Transmit, *byte);
361 }
362
363 if send_stop == SendStop::Yes {
364 Self::stop()?;
365 }
366
367 Ok(())
368 }
369
370 // Public API: Blocking
371
372 /// Read from address into buffer blocking caller until done.
373 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
374 self.blocking_read_internal(address, read, SendStop::Yes)
375 }
376
377 /// Write to address from buffer blocking caller until done.
378 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<()> {
379 self.blocking_write_internal(address, write, SendStop::Yes)
380 }
381
382 /// Write to address from bytes and read from address into buffer blocking caller until done.
383 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
384 self.blocking_write_internal(address, write, SendStop::No)?;
385 self.blocking_read_internal(address, read, SendStop::Yes)
386 }
387}
388
389impl<'d, T: Instance> I2c<'d, T, Async> {
390 /// Create a new async instance of the I2C Controller bus driver.
391 pub fn new_async(
392 peri: Peri<'d, T>,
393 scl: Peri<'d, impl SclPin<T>>,
394 sda: Peri<'d, impl SdaPin<T>>,
395 _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
396 config: Config,
397 ) -> Result<Self> {
398 T::Interrupt::unpend();
399
400 // Safety: `_irq` ensures an Interrupt Handler exists.
401 unsafe { T::Interrupt::enable() };
402
403 Self::new_inner(peri, scl, sda, config)
404 }
405
406 fn remediation() {
407 #[cfg(feature = "defmt")]
408 defmt::trace!("Future dropped, issuing stop",);
409
410 // if the FIFO is not empty, drop its contents.
411 if !Self::is_tx_fifo_empty() {
412 Self::reset_fifos();
413 }
414
415 // send a stop command
416 let _ = Self::stop();
417 }
418
419 fn enable_rx_ints(&mut self) {
420 T::regs().mier().write(|w| {
421 w.rdie()
422 .enabled()
423 .ndie()
424 .enabled()
425 .alie()
426 .enabled()
427 .feie()
428 .enabled()
429 .pltie()
430 .enabled()
431 });
432 }
433
434 fn enable_tx_ints(&mut self) {
435 T::regs().mier().write(|w| {
436 w.tdie()
437 .enabled()
438 .ndie()
439 .enabled()
440 .alie()
441 .enabled()
442 .feie()
443 .enabled()
444 .pltie()
445 .enabled()
446 });
447 }
448
449 async fn async_start(&mut self, address: u8, read: bool) -> Result<()> {
450 if address >= 0x80 {
451 return Err(Error::AddressOutOfRange(address));
452 }
453
454 // send the start command
455 let addr_rw = address << 1 | if read { 1 } else { 0 };
456 Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
457
458 T::wait_cell()
459 .wait_for(|| {
460 // enable interrupts
461 self.enable_tx_ints();
462 // if the command FIFO is empty, we're done sending start
463 Self::is_tx_fifo_empty()
464 })
465 .await
466 .map_err(|_| Error::Other)?;
467
468 Self::status()
469 }
470
471 async fn async_stop(&mut self) -> Result<()> {
472 // send the stop command
473 Self::send_cmd(Cmd::Stop, 0);
474
475 T::wait_cell()
476 .wait_for(|| {
477 // enable interrupts
478 self.enable_tx_ints();
479 // if the command FIFO is empty, we're done sending stop
480 Self::is_tx_fifo_empty()
481 })
482 .await
483 .map_err(|_| Error::Other)?;
484
485 Self::status()
486 }
487
488 async fn async_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
489 if read.is_empty() {
490 return Err(Error::InvalidReadBufferLength);
491 }
492
493 // perform corrective action if the future is dropped
494 let on_drop = OnDrop::new(|| Self::remediation());
495
496 for chunk in read.chunks_mut(256) {
497 self.async_start(address, true).await?;
498
499 // send receive command
500 Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
501
502 T::wait_cell()
503 .wait_for(|| {
504 // enable interrupts
505 self.enable_tx_ints();
506 // if the command FIFO is empty, we're done sending start
507 Self::is_tx_fifo_empty()
508 })
509 .await
510 .map_err(|_| Error::Other)?;
511
512 for byte in chunk.iter_mut() {
513 T::wait_cell()
514 .wait_for(|| {
515 // enable interrupts
516 self.enable_rx_ints();
517 // if the rx FIFO is not empty, we need to read a byte
518 !Self::is_rx_fifo_empty()
519 })
520 .await
521 .map_err(|_| Error::ReadFail)?;
522
523 *byte = T::regs().mrdr().read().data().bits();
524 }
525 }
526
527 if send_stop == SendStop::Yes {
528 self.async_stop().await?;
529 }
530
531 // defuse it if the future is not dropped
532 on_drop.defuse();
533
534 Ok(())
535 }
536
537 async fn async_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
538 self.async_start(address, false).await?;
539
540 // perform corrective action if the future is dropped
541 let on_drop = OnDrop::new(|| Self::remediation());
542
543 // Usually, embassy HALs error out with an empty write,
544 // however empty writes are useful for writing I2C scanning
545 // logic through write probing. That is, we send a start with
546 // R/w bit cleared, but instead of writing any data, just send
547 // the stop onto the bus. This has the effect of checking if
548 // the resulting address got an ACK but causing no
549 // side-effects to the device on the other end.
550 //
551 // Because of this, we are not going to error out in case of
552 // empty writes.
553 #[cfg(feature = "defmt")]
554 if write.is_empty() {
555 defmt::trace!("Empty write, write probing?");
556 }
557
558 for byte in write {
559 T::wait_cell()
560 .wait_for(|| {
561 // enable interrupts
562 self.enable_tx_ints();
563 // initiate transmit
564 Self::send_cmd(Cmd::Transmit, *byte);
565 // if the tx FIFO is empty, we're done transmiting
566 Self::is_tx_fifo_empty()
567 })
568 .await
569 .map_err(|_| Error::WriteFail)?;
570 }
571
572 if send_stop == SendStop::Yes {
573 self.async_stop().await?;
574 }
575
576 // defuse it if the future is not dropped
577 on_drop.defuse();
578
579 Ok(())
580 }
581
582 // Public API: Async
583
584 /// Read from address into buffer asynchronously.
585 pub fn async_read<'a>(
586 &mut self,
587 address: u8,
588 read: &'a mut [u8],
589 ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> {
590 self.async_read_internal(address, read, SendStop::Yes)
591 }
592
593 /// Write to address from buffer asynchronously.
594 pub fn async_write<'a>(
595 &mut self,
596 address: u8,
597 write: &'a [u8],
598 ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> {
599 self.async_write_internal(address, write, SendStop::Yes)
600 }
601
602 /// Write to address from bytes and read from address into buffer asynchronously.
603 pub async fn async_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
604 self.async_write_internal(address, write, SendStop::No).await?;
605 self.async_read_internal(address, read, SendStop::Yes).await
606 }
607}
608
609impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
610 type Error = Error;
611
612 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<()> {
613 self.blocking_read(address, buffer)
614 }
615}
616
617impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
618 type Error = Error;
619
620 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<()> {
621 self.blocking_write(address, bytes)
622 }
623}
624
625impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
626 type Error = Error;
627
628 fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<()> {
629 self.blocking_write_read(address, bytes, buffer)
630 }
631}
632
633impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> {
634 type Error = Error;
635
636 fn exec(&mut self, address: u8, operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>]) -> Result<()> {
637 if let Some((last, rest)) = operations.split_last_mut() {
638 for op in rest {
639 match op {
640 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
641 self.blocking_read_internal(address, buf, SendStop::No)?
642 }
643 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
644 self.blocking_write_internal(address, buf, SendStop::No)?
645 }
646 }
647 }
648
649 match last {
650 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
651 self.blocking_read_internal(address, buf, SendStop::Yes)
652 }
653 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
654 self.blocking_write_internal(address, buf, SendStop::Yes)
655 }
656 }
657 } else {
658 Ok(())
659 }
660 }
661}
662
663impl embedded_hal_1::i2c::Error for Error {
664 fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
665 match *self {
666 Self::ArbitrationLoss => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
667 Self::AddressNack => {
668 embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
669 }
670 _ => embedded_hal_1::i2c::ErrorKind::Other,
671 }
672 }
673}
674
675impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
676 type Error = Error;
677}
678
679impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
680 fn transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
681 if let Some((last, rest)) = operations.split_last_mut() {
682 for op in rest {
683 match op {
684 embedded_hal_1::i2c::Operation::Read(buf) => {
685 self.blocking_read_internal(address, buf, SendStop::No)?
686 }
687 embedded_hal_1::i2c::Operation::Write(buf) => {
688 self.blocking_write_internal(address, buf, SendStop::No)?
689 }
690 }
691 }
692
693 match last {
694 embedded_hal_1::i2c::Operation::Read(buf) => self.blocking_read_internal(address, buf, SendStop::Yes),
695 embedded_hal_1::i2c::Operation::Write(buf) => self.blocking_write_internal(address, buf, SendStop::Yes),
696 }
697 } else {
698 Ok(())
699 }
700 }
701}
702
703impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> {
704 async fn transaction(
705 &mut self,
706 address: u8,
707 operations: &mut [embedded_hal_async::i2c::Operation<'_>],
708 ) -> Result<()> {
709 if let Some((last, rest)) = operations.split_last_mut() {
710 for op in rest {
711 match op {
712 embedded_hal_async::i2c::Operation::Read(buf) => {
713 self.async_read_internal(address, buf, SendStop::No).await?
714 }
715 embedded_hal_async::i2c::Operation::Write(buf) => {
716 self.async_write_internal(address, buf, SendStop::No).await?
717 }
718 }
719 }
720
721 match last {
722 embedded_hal_async::i2c::Operation::Read(buf) => {
723 self.async_read_internal(address, buf, SendStop::Yes).await
724 }
725 embedded_hal_async::i2c::Operation::Write(buf) => {
726 self.async_write_internal(address, buf, SendStop::Yes).await
727 }
728 }
729 } else {
730 Ok(())
731 }
732 }
733}
734
735impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> {
736 type Config = Config;
737 type ConfigError = Error;
738
739 fn set_config(&mut self, config: &Self::Config) -> Result<()> {
740 Self::set_config(config)
741 }
742}
diff --git a/embassy-mcxa/src/i2c/mod.rs b/embassy-mcxa/src/i2c/mod.rs
new file mode 100644
index 000000000..9a014224a
--- /dev/null
+++ b/embassy-mcxa/src/i2c/mod.rs
@@ -0,0 +1,194 @@
1//! I2C Support
2
3use core::marker::PhantomData;
4
5use embassy_hal_internal::PeripheralType;
6use maitake_sync::WaitCell;
7use paste::paste;
8
9use crate::clocks::periph_helpers::Lpi2cConfig;
10use crate::clocks::{ClockError, Gate};
11use crate::gpio::{GpioPin, SealedPin};
12use crate::{interrupt, pac};
13
14/// Shorthand for `Result<T>`.
15pub type Result<T> = core::result::Result<T, Error>;
16
17pub mod controller;
18
19/// Error information type
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub enum Error {
23 /// Clock configuration error.
24 ClockSetup(ClockError),
25 /// FIFO Error
26 FifoError,
27 /// Reading for I2C failed.
28 ReadFail,
29 /// Writing to I2C failed.
30 WriteFail,
31 /// I2C address NAK condition.
32 AddressNack,
33 /// Bus level arbitration loss.
34 ArbitrationLoss,
35 /// Address out of range.
36 AddressOutOfRange(u8),
37 /// Invalid write buffer length.
38 InvalidWriteBufferLength,
39 /// Invalid read buffer length.
40 InvalidReadBufferLength,
41 /// Other internal errors or unexpected state.
42 Other,
43}
44
45/// I2C interrupt handler.
46pub struct InterruptHandler<T: Instance> {
47 _phantom: PhantomData<T>,
48}
49
50impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
51 unsafe fn on_interrupt() {
52 if T::regs().mier().read().bits() != 0 {
53 T::regs().mier().write(|w| {
54 w.tdie()
55 .disabled()
56 .rdie()
57 .disabled()
58 .epie()
59 .disabled()
60 .sdie()
61 .disabled()
62 .ndie()
63 .disabled()
64 .alie()
65 .disabled()
66 .feie()
67 .disabled()
68 .pltie()
69 .disabled()
70 .dmie()
71 .disabled()
72 .stie()
73 .disabled()
74 });
75
76 T::wait_cell().wake();
77 }
78 }
79}
80
81mod sealed {
82 /// Seal a trait
83 pub trait Sealed {}
84}
85
86impl<T: GpioPin> sealed::Sealed for T {}
87
88trait SealedInstance {
89 fn regs() -> &'static pac::lpi2c0::RegisterBlock;
90 fn wait_cell() -> &'static WaitCell;
91}
92
93/// I2C Instance
94#[allow(private_bounds)]
95pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = Lpi2cConfig> {
96 /// Interrupt for this I2C instance.
97 type Interrupt: interrupt::typelevel::Interrupt;
98 /// Clock instance
99 const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance;
100}
101
102macro_rules! impl_instance {
103 ($($n:expr),*) => {
104 $(
105 paste!{
106 impl SealedInstance for crate::peripherals::[<LPI2C $n>] {
107 fn regs() -> &'static pac::lpi2c0::RegisterBlock {
108 unsafe { &*pac::[<Lpi2c $n>]::ptr() }
109 }
110
111 fn wait_cell() -> &'static WaitCell {
112 static WAIT_CELL: WaitCell = WaitCell::new();
113 &WAIT_CELL
114 }
115 }
116
117 impl Instance for crate::peripherals::[<LPI2C $n>] {
118 type Interrupt = crate::interrupt::typelevel::[<LPI2C $n>];
119 const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance
120 = crate::clocks::periph_helpers::Lpi2cInstance::[<Lpi2c $n>];
121 }
122 }
123 )*
124 };
125}
126
127impl_instance!(0, 1, 2, 3);
128
129/// SCL pin trait.
130pub trait SclPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
131 fn mux(&self);
132}
133
134/// SDA pin trait.
135pub trait SdaPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
136 fn mux(&self);
137}
138
139/// Driver mode.
140#[allow(private_bounds)]
141pub trait Mode: sealed::Sealed {}
142
143/// Blocking mode.
144pub struct Blocking;
145impl sealed::Sealed for Blocking {}
146impl Mode for Blocking {}
147
148/// Async mode.
149pub struct Async;
150impl sealed::Sealed for Async {}
151impl Mode for Async {}
152
153macro_rules! impl_pin {
154 ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => {
155 impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin {
156 fn mux(&self) {
157 self.set_pull(crate::gpio::Pull::Disabled);
158 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
159 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
160 self.set_function(crate::pac::port0::pcr0::Mux::$fn);
161 self.set_enable_input_buffer();
162 }
163 }
164 };
165}
166
167impl_pin!(P0_16, LPI2C0, Mux2, SdaPin);
168impl_pin!(P0_17, LPI2C0, Mux2, SclPin);
169impl_pin!(P0_18, LPI2C0, Mux2, SclPin);
170impl_pin!(P0_19, LPI2C0, Mux2, SdaPin);
171impl_pin!(P1_0, LPI2C1, Mux3, SdaPin);
172impl_pin!(P1_1, LPI2C1, Mux3, SclPin);
173impl_pin!(P1_2, LPI2C1, Mux3, SdaPin);
174impl_pin!(P1_3, LPI2C1, Mux3, SclPin);
175impl_pin!(P1_8, LPI2C2, Mux3, SdaPin);
176impl_pin!(P1_9, LPI2C2, Mux3, SclPin);
177impl_pin!(P1_10, LPI2C2, Mux3, SdaPin);
178impl_pin!(P1_11, LPI2C2, Mux3, SclPin);
179impl_pin!(P1_12, LPI2C1, Mux2, SdaPin);
180impl_pin!(P1_13, LPI2C1, Mux2, SclPin);
181impl_pin!(P1_14, LPI2C1, Mux2, SclPin);
182impl_pin!(P1_15, LPI2C1, Mux2, SdaPin);
183impl_pin!(P1_30, LPI2C0, Mux3, SdaPin);
184impl_pin!(P1_31, LPI2C0, Mux3, SclPin);
185impl_pin!(P3_27, LPI2C3, Mux2, SclPin);
186impl_pin!(P3_28, LPI2C3, Mux2, SdaPin);
187// impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin?
188impl_pin!(P3_30, LPI2C3, Mux2, SclPin);
189impl_pin!(P3_31, LPI2C3, Mux2, SdaPin);
190impl_pin!(P4_2, LPI2C2, Mux2, SdaPin);
191impl_pin!(P4_3, LPI2C0, Mux2, SclPin);
192impl_pin!(P4_4, LPI2C2, Mux2, SdaPin);
193impl_pin!(P4_5, LPI2C0, Mux2, SclPin);
194// impl_pin!(P4_6, LPI2C0, Mux2, HreqPin); What is this HREQ pin?
diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs
new file mode 100644
index 000000000..c1f7e55a0
--- /dev/null
+++ b/embassy-mcxa/src/interrupt.rs
@@ -0,0 +1,563 @@
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
5// TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs
6// are complete prior to release.
7#![allow(clippy::missing_safety_doc)]
8
9mod generated {
10 #[rustfmt::skip]
11 embassy_hal_internal::interrupt_mod!(
12 ADC1,
13 GPIO0,
14 GPIO1,
15 GPIO2,
16 GPIO3,
17 GPIO4,
18 LPI2C0,
19 LPI2C1,
20 LPI2C2,
21 LPI2C3,
22 LPUART0,
23 LPUART1,
24 LPUART2,
25 LPUART3,
26 LPUART4,
27 LPUART5,
28 OS_EVENT,
29 RTC,
30 );
31}
32
33use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
34
35pub use generated::interrupt::{Priority, typelevel};
36
37use crate::pac::Interrupt;
38
39/// Trait for configuring and controlling interrupts.
40///
41/// This trait provides a consistent interface for interrupt management across
42/// different interrupt sources, similar to embassy-imxrt's InterruptExt.
43pub trait InterruptExt {
44 /// Clear any pending interrupt in NVIC.
45 fn unpend(&self);
46
47 /// Set NVIC priority for this interrupt.
48 fn set_priority(&self, priority: Priority);
49
50 /// Enable this interrupt in NVIC.
51 ///
52 /// # Safety
53 /// This function is unsafe because it can enable interrupts that may not be
54 /// properly configured, potentially leading to undefined behavior.
55 unsafe fn enable(&self);
56
57 /// Disable this interrupt in NVIC.
58 ///
59 /// # Safety
60 /// This function is unsafe because disabling interrupts may leave the system
61 /// in an inconsistent state if the interrupt was expected to fire.
62 unsafe fn disable(&self);
63
64 /// Check if the interrupt is pending in NVIC.
65 fn is_pending(&self) -> bool;
66}
67
68#[derive(Clone, Copy, Debug, Default)]
69pub struct DefaultHandlerSnapshot {
70 pub vector: u16,
71 pub count: u32,
72 pub cfsr: u32,
73 pub hfsr: u32,
74 pub stacked_pc: u32,
75 pub stacked_lr: u32,
76 pub stacked_sp: u32,
77}
78
79static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0);
80static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0);
81static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0);
82static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0);
83static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0);
84static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0);
85static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0);
86
87#[inline]
88pub fn default_handler_snapshot() -> DefaultHandlerSnapshot {
89 DefaultHandlerSnapshot {
90 vector: LAST_DEFAULT_VECTOR.load(Ordering::Relaxed),
91 count: LAST_DEFAULT_COUNT.load(Ordering::Relaxed),
92 cfsr: LAST_DEFAULT_CFSR.load(Ordering::Relaxed),
93 hfsr: LAST_DEFAULT_HFSR.load(Ordering::Relaxed),
94 stacked_pc: LAST_DEFAULT_PC.load(Ordering::Relaxed),
95 stacked_lr: LAST_DEFAULT_LR.load(Ordering::Relaxed),
96 stacked_sp: LAST_DEFAULT_SP.load(Ordering::Relaxed),
97 }
98}
99
100#[inline]
101pub fn clear_default_handler_snapshot() {
102 LAST_DEFAULT_VECTOR.store(0, Ordering::Relaxed);
103 LAST_DEFAULT_COUNT.store(0, Ordering::Relaxed);
104 LAST_DEFAULT_CFSR.store(0, Ordering::Relaxed);
105 LAST_DEFAULT_HFSR.store(0, Ordering::Relaxed);
106 LAST_DEFAULT_PC.store(0, Ordering::Relaxed);
107 LAST_DEFAULT_LR.store(0, Ordering::Relaxed);
108 LAST_DEFAULT_SP.store(0, Ordering::Relaxed);
109}
110
111/// OS_EVENT interrupt helper with methods similar to embassy-imxrt's InterruptExt.
112pub struct OsEvent;
113pub const OS_EVENT: OsEvent = OsEvent;
114
115impl InterruptExt for OsEvent {
116 /// Clear any pending OS_EVENT in NVIC.
117 #[inline]
118 fn unpend(&self) {
119 cortex_m::peripheral::NVIC::unpend(Interrupt::OS_EVENT);
120 }
121
122 /// Set NVIC priority for OS_EVENT.
123 #[inline]
124 fn set_priority(&self, priority: Priority) {
125 unsafe {
126 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
127 nvic.set_priority(Interrupt::OS_EVENT, u8::from(priority));
128 }
129 }
130
131 /// Enable OS_EVENT in NVIC.
132 #[inline]
133 unsafe fn enable(&self) {
134 cortex_m::peripheral::NVIC::unmask(Interrupt::OS_EVENT);
135 }
136
137 /// Disable OS_EVENT in NVIC.
138 #[inline]
139 unsafe fn disable(&self) {
140 cortex_m::peripheral::NVIC::mask(Interrupt::OS_EVENT);
141 }
142
143 /// Check if OS_EVENT is pending in NVIC.
144 #[inline]
145 fn is_pending(&self) -> bool {
146 cortex_m::peripheral::NVIC::is_pending(Interrupt::OS_EVENT)
147 }
148}
149
150impl OsEvent {
151 /// Configure OS_EVENT interrupt for timer operation.
152 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
153 /// Also performs a software event to wake any pending WFE.
154 pub fn configure_for_timer(&self, priority: Priority) {
155 // Configure NVIC
156 self.unpend();
157 self.set_priority(priority);
158 unsafe {
159 self.enable();
160 }
161
162 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
163 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
164 unsafe {
165 cortex_m::interrupt::enable();
166 }
167
168 // Wake any executor WFE that might be sleeping when we armed the first deadline
169 cortex_m::asm::sev();
170 }
171}
172
173/// LPUART2 interrupt helper with methods similar to embassy-imxrt's InterruptExt.
174pub struct Lpuart2;
175pub const LPUART2: Lpuart2 = Lpuart2;
176
177impl InterruptExt for Lpuart2 {
178 /// Clear any pending LPUART2 in NVIC.
179 #[inline]
180 fn unpend(&self) {
181 cortex_m::peripheral::NVIC::unpend(Interrupt::LPUART2);
182 }
183
184 /// Set NVIC priority for LPUART2.
185 #[inline]
186 fn set_priority(&self, priority: Priority) {
187 unsafe {
188 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
189 nvic.set_priority(Interrupt::LPUART2, u8::from(priority));
190 }
191 }
192
193 /// Enable LPUART2 in NVIC.
194 #[inline]
195 unsafe fn enable(&self) {
196 cortex_m::peripheral::NVIC::unmask(Interrupt::LPUART2);
197 }
198
199 /// Disable LPUART2 in NVIC.
200 #[inline]
201 unsafe fn disable(&self) {
202 cortex_m::peripheral::NVIC::mask(Interrupt::LPUART2);
203 }
204
205 /// Check if LPUART2 is pending in NVIC.
206 #[inline]
207 fn is_pending(&self) -> bool {
208 cortex_m::peripheral::NVIC::is_pending(Interrupt::LPUART2)
209 }
210}
211
212impl Lpuart2 {
213 /// Configure LPUART2 interrupt for UART operation.
214 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
215 pub fn configure_for_uart(&self, priority: Priority) {
216 // Configure NVIC
217 self.unpend();
218 self.set_priority(priority);
219 unsafe {
220 self.enable();
221 }
222
223 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
224 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
225 unsafe {
226 cortex_m::interrupt::enable();
227 }
228 }
229
230 /// Install LPUART2 handler into the RAM vector table.
231 /// Safety: See `install_irq_handler`.
232 pub unsafe fn install_handler(&self, handler: unsafe extern "C" fn()) {
233 install_irq_handler(Interrupt::LPUART2, handler);
234 }
235}
236
237pub struct Rtc;
238pub const RTC: Rtc = Rtc;
239
240impl InterruptExt for Rtc {
241 /// Clear any pending RTC in NVIC.
242 #[inline]
243 fn unpend(&self) {
244 cortex_m::peripheral::NVIC::unpend(Interrupt::RTC);
245 }
246
247 /// Set NVIC priority for RTC.
248 #[inline]
249 fn set_priority(&self, priority: Priority) {
250 unsafe {
251 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
252 nvic.set_priority(Interrupt::RTC, u8::from(priority));
253 }
254 }
255
256 /// Enable RTC in NVIC.
257 #[inline]
258 unsafe fn enable(&self) {
259 cortex_m::peripheral::NVIC::unmask(Interrupt::RTC);
260 }
261
262 /// Disable RTC in NVIC.
263 #[inline]
264 unsafe fn disable(&self) {
265 cortex_m::peripheral::NVIC::mask(Interrupt::RTC);
266 }
267
268 /// Check if RTC is pending in NVIC.
269 #[inline]
270 fn is_pending(&self) -> bool {
271 cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC)
272 }
273}
274
275pub struct Adc;
276pub const ADC1: Adc = Adc;
277
278impl InterruptExt for Adc {
279 /// Clear any pending ADC1 in NVIC.
280 #[inline]
281 fn unpend(&self) {
282 cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1);
283 }
284
285 /// Set NVIC priority for ADC1.
286 #[inline]
287 fn set_priority(&self, priority: Priority) {
288 unsafe {
289 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
290 nvic.set_priority(Interrupt::ADC1, u8::from(priority));
291 }
292 }
293
294 /// Enable ADC1 in NVIC.
295 #[inline]
296 unsafe fn enable(&self) {
297 cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1);
298 }
299
300 /// Disable ADC1 in NVIC.
301 #[inline]
302 unsafe fn disable(&self) {
303 cortex_m::peripheral::NVIC::mask(Interrupt::ADC1);
304 }
305
306 /// Check if ADC1 is pending in NVIC.
307 #[inline]
308 fn is_pending(&self) -> bool {
309 cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1)
310 }
311}
312
313pub struct Gpio0;
314pub const GPIO0: Gpio0 = Gpio0;
315
316impl InterruptExt for Gpio0 {
317 /// Clear any pending GPIO0 in NVIC.
318 #[inline]
319 fn unpend(&self) {
320 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO0);
321 }
322
323 /// Set NVIC priority for GPIO0.
324 #[inline]
325 fn set_priority(&self, priority: Priority) {
326 unsafe {
327 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
328 nvic.set_priority(Interrupt::GPIO0, u8::from(priority));
329 }
330 }
331
332 /// Enable GPIO0 in NVIC.
333 #[inline]
334 unsafe fn enable(&self) {
335 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO0);
336 }
337
338 /// Disable GPIO0 in NVIC.
339 #[inline]
340 unsafe fn disable(&self) {
341 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO0);
342 }
343
344 /// Check if GPIO0 is pending in NVIC.
345 #[inline]
346 fn is_pending(&self) -> bool {
347 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO0)
348 }
349}
350
351pub struct Gpio1;
352pub const GPIO1: Gpio1 = Gpio1;
353
354impl InterruptExt for Gpio1 {
355 /// Clear any pending GPIO1 in NVIC.
356 #[inline]
357 fn unpend(&self) {
358 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO1);
359 }
360
361 /// Set NVIC priority for GPIO1.
362 #[inline]
363 fn set_priority(&self, priority: Priority) {
364 unsafe {
365 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
366 nvic.set_priority(Interrupt::GPIO1, u8::from(priority));
367 }
368 }
369
370 /// Enable GPIO1 in NVIC.
371 #[inline]
372 unsafe fn enable(&self) {
373 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO1);
374 }
375
376 /// Disable GPIO1 in NVIC.
377 #[inline]
378 unsafe fn disable(&self) {
379 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO1);
380 }
381
382 /// Check if GPIO1 is pending in NVIC.
383 #[inline]
384 fn is_pending(&self) -> bool {
385 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO1)
386 }
387}
388
389pub struct Gpio2;
390pub const GPIO2: Gpio2 = Gpio2;
391
392impl InterruptExt for Gpio2 {
393 /// Clear any pending GPIO2 in NVIC.
394 #[inline]
395 fn unpend(&self) {
396 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO2);
397 }
398
399 /// Set NVIC priority for GPIO2.
400 #[inline]
401 fn set_priority(&self, priority: Priority) {
402 unsafe {
403 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
404 nvic.set_priority(Interrupt::GPIO2, u8::from(priority));
405 }
406 }
407
408 /// Enable GPIO2 in NVIC.
409 #[inline]
410 unsafe fn enable(&self) {
411 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO2);
412 }
413
414 /// Disable GPIO2 in NVIC.
415 #[inline]
416 unsafe fn disable(&self) {
417 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO2);
418 }
419
420 /// Check if GPIO2 is pending in NVIC.
421 #[inline]
422 fn is_pending(&self) -> bool {
423 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO2)
424 }
425}
426
427pub struct Gpio3;
428pub const GPIO3: Gpio3 = Gpio3;
429
430impl InterruptExt for Gpio3 {
431 /// Clear any pending GPIO3 in NVIC.
432 #[inline]
433 fn unpend(&self) {
434 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO3);
435 }
436
437 /// Set NVIC priority for GPIO3.
438 #[inline]
439 fn set_priority(&self, priority: Priority) {
440 unsafe {
441 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
442 nvic.set_priority(Interrupt::GPIO3, u8::from(priority));
443 }
444 }
445
446 /// Enable GPIO3 in NVIC.
447 #[inline]
448 unsafe fn enable(&self) {
449 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO3);
450 }
451
452 /// Disable GPIO3 in NVIC.
453 #[inline]
454 unsafe fn disable(&self) {
455 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO3);
456 }
457
458 /// Check if GPIO3 is pending in NVIC.
459 #[inline]
460 fn is_pending(&self) -> bool {
461 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO3)
462 }
463}
464
465pub struct Gpio4;
466pub const GPIO4: Gpio4 = Gpio4;
467
468impl InterruptExt for Gpio4 {
469 /// Clear any pending GPIO4 in NVIC.
470 #[inline]
471 fn unpend(&self) {
472 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO4);
473 }
474
475 /// Set NVIC priority for GPIO4.
476 #[inline]
477 fn set_priority(&self, priority: Priority) {
478 unsafe {
479 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
480 nvic.set_priority(Interrupt::GPIO4, u8::from(priority));
481 }
482 }
483
484 /// Enable GPIO4 in NVIC.
485 #[inline]
486 unsafe fn enable(&self) {
487 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO4);
488 }
489
490 /// Disable GPIO4 in NVIC.
491 #[inline]
492 unsafe fn disable(&self) {
493 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO4);
494 }
495
496 /// Check if GPIO4 is pending in NVIC.
497 #[inline]
498 fn is_pending(&self) -> bool {
499 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO4)
500 }
501}
502
503/// Set VTOR (Vector Table Offset) to a RAM-based vector table.
504/// Pass a pointer to the first word in the RAM table (stack pointer slot 0).
505/// Safety: Caller must ensure the RAM table is valid and aligned as required by the core.
506pub unsafe fn vtor_set_ram_vector_base(base: *const u32) {
507 core::ptr::write_volatile(0xE000_ED08 as *mut u32, base as u32);
508}
509
510/// Install an interrupt handler into the current VTOR-based vector table.
511/// This writes the function pointer at index 16 + irq number.
512/// Safety: Caller must ensure VTOR points at a writable RAM table and that `handler`
513/// has the correct ABI and lifetime.
514pub unsafe fn install_irq_handler(irq: Interrupt, handler: unsafe extern "C" fn()) {
515 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
516 let idx = 16 + (irq as isize as usize);
517 core::ptr::write_volatile(vtor_base.add(idx), handler as usize as u32);
518}
519
520impl OsEvent {
521 /// Convenience to install the OS_EVENT handler into the RAM vector table.
522 /// Safety: See `install_irq_handler`.
523 pub unsafe fn install_handler(&self, handler: extern "C" fn()) {
524 install_irq_handler(Interrupt::OS_EVENT, handler);
525 }
526}
527
528/// Install OS_EVENT handler by raw address. Useful to avoid fn pointer type mismatches.
529/// Safety: Caller must ensure the address is a valid `extern "C" fn()` handler.
530pub unsafe fn os_event_install_handler_raw(handler_addr: usize) {
531 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
532 let idx = 16 + (Interrupt::OS_EVENT as isize as usize);
533 core::ptr::write_volatile(vtor_base.add(idx), handler_addr as u32);
534}
535
536/// Provide a conservative default IRQ handler that avoids wedging the system.
537/// It clears all NVIC pending bits and returns, so spurious or reserved IRQs
538/// don’t trap the core in an infinite WFI loop during bring-up.
539#[no_mangle]
540pub unsafe extern "C" fn DefaultHandler() {
541 let active = core::ptr::read_volatile(0xE000_ED04 as *const u32) & 0x1FF;
542 let cfsr = core::ptr::read_volatile(0xE000_ED28 as *const u32);
543 let hfsr = core::ptr::read_volatile(0xE000_ED2C as *const u32);
544
545 let sp = cortex_m::register::msp::read();
546 let stacked = sp as *const u32;
547 // Stacked registers follow ARMv8-M procedure call standard order
548 let stacked_pc = unsafe { stacked.add(6).read() };
549 let stacked_lr = unsafe { stacked.add(5).read() };
550
551 LAST_DEFAULT_VECTOR.store(active as u16, Ordering::Relaxed);
552 LAST_DEFAULT_CFSR.store(cfsr, Ordering::Relaxed);
553 LAST_DEFAULT_HFSR.store(hfsr, Ordering::Relaxed);
554 LAST_DEFAULT_COUNT.fetch_add(1, Ordering::Relaxed);
555 LAST_DEFAULT_PC.store(stacked_pc, Ordering::Relaxed);
556 LAST_DEFAULT_LR.store(stacked_lr, Ordering::Relaxed);
557 LAST_DEFAULT_SP.store(sp, Ordering::Relaxed);
558
559 // Do nothing here: on some MCUs/TrustZone setups, writing NVIC from a spurious
560 // handler can fault if targeting the Secure bank. Just return.
561 cortex_m::asm::dsb();
562 cortex_m::asm::isb();
563}
diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs
new file mode 100644
index 000000000..c6d8adc8f
--- /dev/null
+++ b/embassy-mcxa/src/lib.rs
@@ -0,0 +1,471 @@
1#![no_std]
2#![allow(async_fn_in_trait)]
3#![doc = include_str!("../README.md")]
4
5// //! ## Feature flags
6// #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
7
8pub mod clocks; // still provide clock helpers
9pub mod gpio;
10pub mod pins; // pin mux helpers
11
12pub mod adc;
13pub mod clkout;
14pub mod config;
15pub mod i2c;
16pub mod interrupt;
17pub mod lpuart;
18pub mod ostimer;
19pub mod rtc;
20
21pub use crate::pac::NVIC_PRIO_BITS;
22
23#[rustfmt::skip]
24embassy_hal_internal::peripherals!(
25 ADC0,
26 ADC1,
27
28 AOI0,
29 AOI1,
30
31 CAN0,
32 CAN1,
33
34 CDOG0,
35 CDOG1,
36
37 // CLKOUT is not specifically a peripheral (it's part of SYSCON),
38 // but we still want it to be a singleton.
39 CLKOUT,
40
41 CMC,
42 CMP0,
43 CMP1,
44 CRC0,
45
46 CTIMER0,
47 CTIMER1,
48 CTIMER2,
49 CTIMER3,
50 CTIMER4,
51
52 DBGMAILBOX,
53 DMA0,
54 EDMA0_TCD0,
55 EIM0,
56 EQDC0,
57 EQDC1,
58 ERM0,
59 FLEXIO0,
60 FLEXPWM0,
61 FLEXPWM1,
62 FMC0,
63 FMU0,
64 FREQME0,
65 GLIKEY0,
66
67 GPIO0,
68 GPIO1,
69 GPIO2,
70 GPIO3,
71 GPIO4,
72
73 I3C0,
74 INPUTMUX0,
75
76 LPI2C0,
77 LPI2C1,
78 LPI2C2,
79 LPI2C3,
80
81 LPSPI0,
82 LPSPI1,
83
84 LPTMR0,
85
86 LPUART0,
87 LPUART1,
88 LPUART2,
89 LPUART3,
90 LPUART4,
91 LPUART5,
92
93 MAU0,
94 MBC0,
95 MRCC0,
96 OPAMP0,
97
98 #[cfg(not(feature = "time"))]
99 OSTIMER0,
100
101 P0_0,
102 P0_1,
103 P0_2,
104 P0_3,
105 P0_4,
106 P0_5,
107 P0_6,
108 P0_7,
109 P0_8,
110 P0_9,
111 P0_10,
112 P0_11,
113 P0_12,
114 P0_13,
115 P0_14,
116 P0_15,
117 P0_16,
118 P0_17,
119 P0_18,
120 P0_19,
121 P0_20,
122 P0_21,
123 P0_22,
124 P0_23,
125 P0_24,
126 P0_25,
127 P0_26,
128 P0_27,
129 P0_28,
130 P0_29,
131 P0_30,
132 P0_31,
133
134 P1_0,
135 P1_1,
136 P1_2,
137 P1_3,
138 P1_4,
139 P1_5,
140 P1_6,
141 P1_7,
142 P1_8,
143 P1_9,
144 P1_10,
145 P1_11,
146 P1_12,
147 P1_13,
148 P1_14,
149 P1_15,
150 P1_16,
151 P1_17,
152 P1_18,
153 P1_19,
154 P1_20,
155 P1_21,
156 P1_22,
157 P1_23,
158 P1_24,
159 P1_25,
160 P1_26,
161 P1_27,
162 P1_28,
163 P1_29,
164 P1_30,
165 P1_31,
166
167 P2_0,
168 P2_1,
169 P2_2,
170 P2_3,
171 P2_4,
172 P2_5,
173 P2_6,
174 P2_7,
175 P2_8,
176 P2_9,
177 P2_10,
178 P2_11,
179 P2_12,
180 P2_13,
181 P2_14,
182 P2_15,
183 P2_16,
184 P2_17,
185 P2_18,
186 P2_19,
187 P2_20,
188 P2_21,
189 P2_22,
190 P2_23,
191 P2_24,
192 P2_25,
193 P2_26,
194 P2_27,
195 P2_28,
196 P2_29,
197 P2_30,
198 P2_31,
199
200 P3_0,
201 P3_1,
202 P3_2,
203 P3_3,
204 P3_4,
205 P3_5,
206 P3_6,
207 P3_7,
208 P3_8,
209 P3_9,
210 P3_10,
211 P3_11,
212 P3_12,
213 P3_13,
214 P3_14,
215 P3_15,
216 P3_16,
217 P3_17,
218 P3_18,
219 P3_19,
220 P3_20,
221 P3_21,
222 P3_22,
223 P3_23,
224 P3_24,
225 P3_25,
226 P3_26,
227 P3_27,
228 P3_28,
229 P3_29,
230 P3_30,
231 P3_31,
232
233 P4_0,
234 P4_1,
235 P4_2,
236 P4_3,
237 P4_4,
238 P4_5,
239 P4_6,
240 P4_7,
241 P4_8,
242 P4_9,
243 P4_10,
244 P4_11,
245 P4_12,
246 P4_13,
247 P4_14,
248 P4_15,
249 P4_16,
250 P4_17,
251 P4_18,
252 P4_19,
253 P4_20,
254 P4_21,
255 P4_22,
256 P4_23,
257 P4_24,
258 P4_25,
259 P4_26,
260 P4_27,
261 P4_28,
262 P4_29,
263 P4_30,
264 P4_31,
265
266 P5_0,
267 P5_1,
268 P5_2,
269 P5_3,
270 P5_4,
271 P5_5,
272 P5_6,
273 P5_7,
274 P5_8,
275 P5_9,
276 P5_10,
277 P5_11,
278 P5_12,
279 P5_13,
280 P5_14,
281 P5_15,
282 P5_16,
283 P5_17,
284 P5_18,
285 P5_19,
286 P5_20,
287 P5_21,
288 P5_22,
289 P5_23,
290 P5_24,
291 P5_25,
292 P5_26,
293 P5_27,
294 P5_28,
295 P5_29,
296 P5_30,
297 P5_31,
298
299 PKC0,
300
301 PORT0,
302 PORT1,
303 PORT2,
304 PORT3,
305 PORT4,
306
307 RTC0,
308 SAU,
309 SCG0,
310 SCN_SCB,
311 SGI0,
312 SMARTDMA0,
313 SPC0,
314 SYSCON,
315 TDET0,
316 TRNG0,
317 UDF0,
318 USB0,
319 UTICK0,
320 VBAT0,
321 WAKETIMER0,
322 WUU0,
323 WWDT0,
324);
325
326// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it.
327
328// Re-export interrupt traits and types
329pub use adc::Adc1 as Adc1Token;
330pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output};
331pub use interrupt::InterruptExt;
332#[cfg(feature = "unstable-pac")]
333pub use mcxa_pac as pac;
334#[cfg(not(feature = "unstable-pac"))]
335pub(crate) use mcxa_pac as pac;
336
337/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
338/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
339pub fn init(cfg: crate::config::Config) -> Peripherals {
340 let peripherals = Peripherals::take();
341 // Apply user-configured priority early; enabling is left to examples/apps
342 #[cfg(feature = "time")]
343 crate::interrupt::OS_EVENT.set_priority(cfg.time_interrupt_priority);
344 // Apply user-configured priority early; enabling is left to examples/apps
345 crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);
346 // Apply user-configured priority early; enabling is left to examples/apps
347 crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority);
348 // Apply user-configured priority early; enabling is left to examples/apps
349 crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority);
350 // Apply user-configured priority early; enabling is left to examples/apps
351 crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority);
352 // Apply user-configured priority early; enabling is left to examples/apps
353 crate::interrupt::GPIO2.set_priority(cfg.gpio_interrupt_priority);
354 // Apply user-configured priority early; enabling is left to examples/apps
355 crate::interrupt::GPIO3.set_priority(cfg.gpio_interrupt_priority);
356 // Apply user-configured priority early; enabling is left to examples/apps
357 crate::interrupt::GPIO4.set_priority(cfg.gpio_interrupt_priority);
358
359 // Configure clocks
360 crate::clocks::init(cfg.clock_cfg).unwrap();
361
362 unsafe {
363 crate::gpio::init();
364 }
365
366 // Initialize embassy-time global driver backed by OSTIMER0
367 #[cfg(feature = "time")]
368 crate::ostimer::time_driver::init(crate::config::Config::default().time_interrupt_priority, 1_000_000);
369
370 // Enable GPIO clocks
371 unsafe {
372 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT0>(&crate::clocks::periph_helpers::NoConfig);
373 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO0>(&crate::clocks::periph_helpers::NoConfig);
374
375 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT1>(&crate::clocks::periph_helpers::NoConfig);
376 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO1>(&crate::clocks::periph_helpers::NoConfig);
377
378 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT2>(&crate::clocks::periph_helpers::NoConfig);
379 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO2>(&crate::clocks::periph_helpers::NoConfig);
380
381 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT3>(&crate::clocks::periph_helpers::NoConfig);
382 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO3>(&crate::clocks::periph_helpers::NoConfig);
383
384 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT4>(&crate::clocks::periph_helpers::NoConfig);
385 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO4>(&crate::clocks::periph_helpers::NoConfig);
386 }
387
388 peripherals
389}
390
391// /// Optional hook called by cortex-m-rt before RAM init.
392// /// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state
393// /// left by soft resets/debug sessions.
394// ///
395// /// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor'
396// /// feature is incompatible with our setup because it expects __vector_table to be
397// /// defined differently than how our RAM-based linker script arranges it.
398// #[no_mangle]
399// pub unsafe extern "C" fn __pre_init() {
400// // Set the VTOR to point to the interrupt vector table in RAM
401// // This is required since code runs from RAM on this MCU
402// crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32);
403
404// // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs.
405// let nvic = &*cortex_m::peripheral::NVIC::PTR;
406// for i in 0..4 {
407// // 4 words x 32 = 128 IRQs
408// nvic.icer[i].write(0xFFFF_FFFF);
409// nvic.icpr[i].write(0xFFFF_FFFF);
410// }
411// // Do NOT touch peripheral registers here: clocks may be off and accesses can fault.
412// crate::interrupt::clear_default_handler_snapshot();
413// }
414
415/// Internal helper to dispatch a type-level interrupt handler.
416#[inline(always)]
417#[doc(hidden)]
418pub unsafe fn __handle_interrupt<T, H>()
419where
420 T: crate::interrupt::typelevel::Interrupt,
421 H: crate::interrupt::typelevel::Handler<T>,
422{
423 H::on_interrupt();
424}
425
426/// Macro to bind interrupts to handlers, similar to embassy-imxrt.
427///
428/// Example:
429/// - Bind OS_EVENT to the OSTIMER time-driver handler
430/// bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; });
431#[macro_export]
432macro_rules! bind_interrupts {
433 ($(#[$attr:meta])* $vis:vis struct $name:ident {
434 $(
435 $(#[cfg($cond_irq:meta)])?
436 $irq:ident => $(
437 $(#[cfg($cond_handler:meta)])?
438 $handler:ty
439 ),*;
440 )*
441 }) => {
442 #[derive(Copy, Clone)]
443 $(#[$attr])*
444 $vis struct $name;
445
446 $(
447 #[allow(non_snake_case)]
448 #[no_mangle]
449 $(#[cfg($cond_irq)])?
450 unsafe extern "C" fn $irq() {
451 unsafe {
452 $(
453 $(#[cfg($cond_handler)])?
454 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
455 )*
456 }
457 }
458
459 $(#[cfg($cond_irq)])?
460 $crate::bind_interrupts!(@inner
461 $(
462 $(#[cfg($cond_handler)])?
463 unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
464 )*
465 );
466 )*
467 };
468 (@inner $($t:tt)*) => {
469 $($t)*
470 }
471}
diff --git a/embassy-mcxa/src/lpuart/buffered.rs b/embassy-mcxa/src/lpuart/buffered.rs
new file mode 100644
index 000000000..34fdbb76c
--- /dev/null
+++ b/embassy-mcxa/src/lpuart/buffered.rs
@@ -0,0 +1,780 @@
1use core::future::poll_fn;
2use core::marker::PhantomData;
3use core::sync::atomic::{AtomicBool, Ordering};
4use core::task::Poll;
5
6use embassy_hal_internal::Peri;
7use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
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 tx_waker: AtomicWaker,
20 tx_buf: RingBuffer,
21 tx_done: AtomicBool,
22 rx_waker: AtomicWaker,
23 rx_buf: RingBuffer,
24 initialized: AtomicBool,
25}
26
27impl Default for State {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl State {
34 /// Create a new state instance
35 pub const fn new() -> Self {
36 Self {
37 tx_waker: AtomicWaker::new(),
38 tx_buf: RingBuffer::new(),
39 tx_done: AtomicBool::new(true),
40 rx_waker: AtomicWaker::new(),
41 rx_buf: RingBuffer::new(),
42 initialized: AtomicBool::new(false),
43 }
44 }
45}
46// ============================================================================
47// BUFFERED DRIVER STRUCTURES
48// ============================================================================
49
50/// Buffered LPUART driver
51pub struct BufferedLpuart<'a> {
52 tx: BufferedLpuartTx<'a>,
53 rx: BufferedLpuartRx<'a>,
54}
55
56/// Buffered LPUART TX driver
57pub struct BufferedLpuartTx<'a> {
58 info: Info,
59 state: &'static State,
60 _tx_pin: Peri<'a, AnyPin>,
61 _cts_pin: Option<Peri<'a, AnyPin>>,
62}
63
64/// Buffered LPUART RX driver
65pub struct BufferedLpuartRx<'a> {
66 info: Info,
67 state: &'static State,
68 _rx_pin: Peri<'a, AnyPin>,
69 _rts_pin: Option<Peri<'a, AnyPin>>,
70}
71
72// ============================================================================
73// BUFFERED LPUART IMPLEMENTATION
74// ============================================================================
75
76impl<'a> BufferedLpuart<'a> {
77 /// Common initialization logic
78 fn init_common<T: Instance>(
79 _inner: &Peri<'a, T>,
80 tx_buffer: Option<&'a mut [u8]>,
81 rx_buffer: Option<&'a mut [u8]>,
82 config: &Config,
83 enable_tx: bool,
84 enable_rx: bool,
85 enable_rts: bool,
86 enable_cts: bool,
87 ) -> Result<&'static State> {
88 let state = T::buffered_state();
89
90 if state.initialized.load(Ordering::Relaxed) {
91 return Err(Error::InvalidArgument);
92 }
93
94 // Initialize buffers
95 if let Some(tx_buffer) = tx_buffer {
96 if tx_buffer.is_empty() {
97 return Err(Error::InvalidArgument);
98 }
99 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), tx_buffer.len()) };
100 }
101
102 if let Some(rx_buffer) = rx_buffer {
103 if rx_buffer.is_empty() {
104 return Err(Error::InvalidArgument);
105 }
106 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_buffer.len()) };
107 }
108
109 state.initialized.store(true, Ordering::Relaxed);
110
111 // Enable clocks and initialize hardware
112 let conf = LpuartConfig {
113 power: config.power,
114 source: config.source,
115 div: config.div,
116 instance: T::CLOCK_INSTANCE,
117 };
118 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
119
120 Self::init_hardware(
121 T::info().regs,
122 *config,
123 clock_freq,
124 enable_tx,
125 enable_rx,
126 enable_rts,
127 enable_cts,
128 )?;
129
130 Ok(state)
131 }
132
133 /// Helper for full-duplex initialization
134 fn new_inner<T: Instance>(
135 inner: Peri<'a, T>,
136 tx_pin: Peri<'a, AnyPin>,
137 rx_pin: Peri<'a, AnyPin>,
138 rts_pin: Option<Peri<'a, AnyPin>>,
139 cts_pin: Option<Peri<'a, AnyPin>>,
140 tx_buffer: &'a mut [u8],
141 rx_buffer: &'a mut [u8],
142 config: Config,
143 ) -> Result<(BufferedLpuartTx<'a>, BufferedLpuartRx<'a>)> {
144 let state = Self::init_common::<T>(
145 &inner,
146 Some(tx_buffer),
147 Some(rx_buffer),
148 &config,
149 true,
150 true,
151 rts_pin.is_some(),
152 cts_pin.is_some(),
153 )?;
154
155 let tx = BufferedLpuartTx {
156 info: T::info(),
157 state,
158 _tx_pin: tx_pin,
159 _cts_pin: cts_pin,
160 };
161
162 let rx = BufferedLpuartRx {
163 info: T::info(),
164 state,
165 _rx_pin: rx_pin,
166 _rts_pin: rts_pin,
167 };
168
169 Ok((tx, rx))
170 }
171
172 /// Common hardware initialization logic
173 fn init_hardware(
174 regs: &'static mcxa_pac::lpuart0::RegisterBlock,
175 config: Config,
176 clock_freq: u32,
177 enable_tx: bool,
178 enable_rx: bool,
179 enable_rts: bool,
180 enable_cts: bool,
181 ) -> Result<()> {
182 // Perform standard initialization
183 perform_software_reset(regs);
184 disable_transceiver(regs);
185 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
186 configure_frame_format(regs, &config);
187 configure_control_settings(regs, &config);
188 configure_fifo(regs, &config);
189 clear_all_status_flags(regs);
190 configure_flow_control(regs, enable_rts, enable_cts, &config);
191 configure_bit_order(regs, config.msb_first);
192
193 // Enable interrupts for buffered operation
194 cortex_m::interrupt::free(|_| {
195 regs.ctrl().modify(|_, w| {
196 w.rie()
197 .enabled() // RX interrupt
198 .orie()
199 .enabled() // Overrun interrupt
200 .peie()
201 .enabled() // Parity error interrupt
202 .feie()
203 .enabled() // Framing error interrupt
204 .neie()
205 .enabled() // Noise error interrupt
206 });
207 });
208
209 // Enable the transceiver
210 enable_transceiver(regs, enable_rx, enable_tx);
211
212 Ok(())
213 }
214
215 /// Create a new full duplex buffered LPUART
216 pub fn new<T: Instance>(
217 inner: Peri<'a, T>,
218 tx_pin: Peri<'a, impl TxPin<T>>,
219 rx_pin: Peri<'a, impl RxPin<T>>,
220 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
221 tx_buffer: &'a mut [u8],
222 rx_buffer: &'a mut [u8],
223 config: Config,
224 ) -> Result<Self> {
225 tx_pin.as_tx();
226 rx_pin.as_rx();
227
228 let (tx, rx) = Self::new_inner::<T>(
229 inner,
230 tx_pin.into(),
231 rx_pin.into(),
232 None,
233 None,
234 tx_buffer,
235 rx_buffer,
236 config,
237 )?;
238
239 Ok(Self { tx, rx })
240 }
241
242 /// Create a new buffered LPUART instance with RTS/CTS flow control
243 pub fn new_with_rtscts<T: Instance>(
244 inner: Peri<'a, T>,
245 tx_pin: Peri<'a, impl TxPin<T>>,
246 rx_pin: Peri<'a, impl RxPin<T>>,
247 rts_pin: Peri<'a, impl RtsPin<T>>,
248 cts_pin: Peri<'a, impl CtsPin<T>>,
249 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
250 tx_buffer: &'a mut [u8],
251 rx_buffer: &'a mut [u8],
252 config: Config,
253 ) -> Result<Self> {
254 tx_pin.as_tx();
255 rx_pin.as_rx();
256 rts_pin.as_rts();
257 cts_pin.as_cts();
258
259 let (tx, rx) = Self::new_inner::<T>(
260 inner,
261 tx_pin.into(),
262 rx_pin.into(),
263 Some(rts_pin.into()),
264 Some(cts_pin.into()),
265 tx_buffer,
266 rx_buffer,
267 config,
268 )?;
269
270 Ok(Self { tx, rx })
271 }
272
273 /// Create a new buffered LPUART with only RTS flow control (RX flow control)
274 pub fn new_with_rts<T: Instance>(
275 inner: Peri<'a, T>,
276 tx_pin: Peri<'a, impl TxPin<T>>,
277 rx_pin: Peri<'a, impl RxPin<T>>,
278 rts_pin: Peri<'a, impl RtsPin<T>>,
279 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
280 tx_buffer: &'a mut [u8],
281 rx_buffer: &'a mut [u8],
282 config: Config,
283 ) -> Result<Self> {
284 tx_pin.as_tx();
285 rx_pin.as_rx();
286 rts_pin.as_rts();
287
288 let (tx, rx) = Self::new_inner::<T>(
289 inner,
290 tx_pin.into(),
291 rx_pin.into(),
292 Some(rts_pin.into()),
293 None,
294 tx_buffer,
295 rx_buffer,
296 config,
297 )?;
298
299 Ok(Self { tx, rx })
300 }
301
302 /// Create a new buffered LPUART with only CTS flow control (TX flow control)
303 pub fn new_with_cts<T: Instance>(
304 inner: Peri<'a, T>,
305 tx_pin: Peri<'a, impl TxPin<T>>,
306 rx_pin: Peri<'a, impl RxPin<T>>,
307 cts_pin: Peri<'a, impl CtsPin<T>>,
308 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
309 tx_buffer: &'a mut [u8],
310 rx_buffer: &'a mut [u8],
311 config: Config,
312 ) -> Result<Self> {
313 tx_pin.as_tx();
314 rx_pin.as_rx();
315 cts_pin.as_cts();
316
317 let (tx, rx) = Self::new_inner::<T>(
318 inner,
319 tx_pin.into(),
320 rx_pin.into(),
321 None,
322 Some(cts_pin.into()),
323 tx_buffer,
324 rx_buffer,
325 config,
326 )?;
327
328 Ok(Self { tx, rx })
329 }
330
331 /// Split the buffered LPUART into separate TX and RX parts
332 pub fn split(self) -> (BufferedLpuartTx<'a>, BufferedLpuartRx<'a>) {
333 (self.tx, self.rx)
334 }
335
336 /// Get mutable references to TX and RX parts
337 pub fn split_ref(&mut self) -> (&mut BufferedLpuartTx<'a>, &mut BufferedLpuartRx<'a>) {
338 (&mut self.tx, &mut self.rx)
339 }
340}
341
342// ============================================================================
343// BUFFERED TX IMPLEMENTATION
344// ============================================================================
345
346impl<'a> BufferedLpuartTx<'a> {
347 /// Helper for TX-only initialization
348 fn new_inner<T: Instance>(
349 inner: Peri<'a, T>,
350 tx_pin: Peri<'a, AnyPin>,
351 cts_pin: Option<Peri<'a, AnyPin>>,
352 tx_buffer: &'a mut [u8],
353 config: Config,
354 ) -> Result<BufferedLpuartTx<'a>> {
355 let state = BufferedLpuart::init_common::<T>(
356 &inner,
357 Some(tx_buffer),
358 None,
359 &config,
360 true,
361 false,
362 false,
363 cts_pin.is_some(),
364 )?;
365
366 Ok(BufferedLpuartTx {
367 info: T::info(),
368 state,
369 _tx_pin: tx_pin,
370 _cts_pin: cts_pin,
371 })
372 }
373
374 pub fn new<T: Instance>(
375 inner: Peri<'a, T>,
376 tx_pin: Peri<'a, impl TxPin<T>>,
377 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
378 tx_buffer: &'a mut [u8],
379 config: Config,
380 ) -> Result<Self> {
381 tx_pin.as_tx();
382
383 Self::new_inner::<T>(inner, tx_pin.into(), None, tx_buffer, config)
384 }
385
386 /// Create a new TX-only buffered LPUART with CTS flow control
387 pub fn new_with_cts<T: Instance>(
388 inner: Peri<'a, T>,
389 tx_pin: Peri<'a, impl TxPin<T>>,
390 cts_pin: Peri<'a, impl CtsPin<T>>,
391 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
392 tx_buffer: &'a mut [u8],
393 config: Config,
394 ) -> Result<Self> {
395 tx_pin.as_tx();
396 cts_pin.as_cts();
397
398 Self::new_inner::<T>(inner, tx_pin.into(), Some(cts_pin.into()), tx_buffer, config)
399 }
400}
401
402impl<'a> BufferedLpuartTx<'a> {
403 /// Write data asynchronously
404 pub async fn write(&mut self, buf: &[u8]) -> Result<usize> {
405 let mut written = 0;
406
407 for &byte in buf {
408 // Wait for space in the buffer
409 poll_fn(|cx| {
410 self.state.tx_waker.register(cx.waker());
411
412 let mut writer = unsafe { self.state.tx_buf.writer() };
413 if writer.push_one(byte) {
414 // Enable TX interrupt to start transmission
415 cortex_m::interrupt::free(|_| {
416 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
417 });
418 Poll::Ready(Ok(()))
419 } else {
420 Poll::Pending
421 }
422 })
423 .await?;
424
425 written += 1;
426 }
427
428 Ok(written)
429 }
430
431 /// Flush the TX buffer and wait for transmission to complete
432 pub async fn flush(&mut self) -> Result<()> {
433 // Wait for TX buffer to empty and transmission to complete
434 poll_fn(|cx| {
435 self.state.tx_waker.register(cx.waker());
436
437 let tx_empty = self.state.tx_buf.is_empty();
438 let fifo_empty = self.info.regs.water().read().txcount().bits() == 0;
439 let tc_complete = self.info.regs.stat().read().tc().is_complete();
440
441 if tx_empty && fifo_empty && tc_complete {
442 Poll::Ready(Ok(()))
443 } else {
444 // Enable appropriate interrupt
445 cortex_m::interrupt::free(|_| {
446 if !tx_empty {
447 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
448 } else {
449 self.info.regs.ctrl().modify(|_, w| w.tcie().enabled());
450 }
451 });
452 Poll::Pending
453 }
454 })
455 .await
456 }
457
458 /// Try to write without blocking
459 pub fn try_write(&mut self, buf: &[u8]) -> Result<usize> {
460 let mut writer = unsafe { self.state.tx_buf.writer() };
461 let mut written = 0;
462
463 for &byte in buf {
464 if writer.push_one(byte) {
465 written += 1;
466 } else {
467 break;
468 }
469 }
470
471 if written > 0 {
472 // Enable TX interrupt to start transmission
473 cortex_m::interrupt::free(|_| {
474 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
475 });
476 }
477
478 Ok(written)
479 }
480}
481
482// ============================================================================
483// BUFFERED RX IMPLEMENTATION
484// ============================================================================
485
486impl<'a> BufferedLpuartRx<'a> {
487 /// Helper for RX-only initialization
488 fn new_inner<T: Instance>(
489 inner: Peri<'a, T>,
490 rx_pin: Peri<'a, AnyPin>,
491 rts_pin: Option<Peri<'a, AnyPin>>,
492 rx_buffer: &'a mut [u8],
493 config: Config,
494 ) -> Result<BufferedLpuartRx<'a>> {
495 let state = BufferedLpuart::init_common::<T>(
496 &inner,
497 None,
498 Some(rx_buffer),
499 &config,
500 false,
501 true,
502 rts_pin.is_some(),
503 false,
504 )?;
505
506 Ok(BufferedLpuartRx {
507 info: T::info(),
508 state,
509 _rx_pin: rx_pin,
510 _rts_pin: rts_pin,
511 })
512 }
513
514 /// Create a new RX-only buffered LPUART
515 pub fn new<T: Instance>(
516 inner: Peri<'a, T>,
517 rx_pin: Peri<'a, impl RxPin<T>>,
518 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
519 rx_buffer: &'a mut [u8],
520 config: Config,
521 ) -> Result<Self> {
522 rx_pin.as_rx();
523
524 Self::new_inner::<T>(inner, rx_pin.into(), None, rx_buffer, config)
525 }
526
527 /// Create a new RX-only buffered LPUART with RTS flow control
528 pub fn new_with_rts<T: Instance>(
529 inner: Peri<'a, T>,
530 rx_pin: Peri<'a, impl RxPin<T>>,
531 rts_pin: Peri<'a, impl RtsPin<T>>,
532 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
533 rx_buffer: &'a mut [u8],
534 config: Config,
535 ) -> Result<Self> {
536 rx_pin.as_rx();
537 rts_pin.as_rts();
538
539 Self::new_inner::<T>(inner, rx_pin.into(), Some(rts_pin.into()), rx_buffer, config)
540 }
541}
542
543impl<'a> BufferedLpuartRx<'a> {
544 /// Read data asynchronously
545 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
546 if buf.is_empty() {
547 return Ok(0);
548 }
549
550 let mut read = 0;
551
552 // Try to read available data
553 poll_fn(|cx| {
554 self.state.rx_waker.register(cx.waker());
555
556 // Disable RX interrupt while reading from buffer
557 cortex_m::interrupt::free(|_| {
558 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
559 });
560
561 let mut reader = unsafe { self.state.rx_buf.reader() };
562 let available = reader.pop(|data| {
563 let to_copy = core::cmp::min(data.len(), buf.len() - read);
564 if to_copy > 0 {
565 buf[read..read + to_copy].copy_from_slice(&data[..to_copy]);
566 read += to_copy;
567 }
568 to_copy
569 });
570
571 // Re-enable RX interrupt
572 cortex_m::interrupt::free(|_| {
573 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
574 });
575
576 if read > 0 {
577 Poll::Ready(Ok(read))
578 } else if available == 0 {
579 Poll::Pending
580 } else {
581 Poll::Ready(Ok(0))
582 }
583 })
584 .await
585 }
586
587 /// Try to read without blocking
588 pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> {
589 if buf.is_empty() {
590 return Ok(0);
591 }
592
593 // Disable RX interrupt while reading from buffer
594 cortex_m::interrupt::free(|_| {
595 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
596 });
597
598 let mut reader = unsafe { self.state.rx_buf.reader() };
599 let read = reader.pop(|data| {
600 let to_copy = core::cmp::min(data.len(), buf.len());
601 if to_copy > 0 {
602 buf[..to_copy].copy_from_slice(&data[..to_copy]);
603 }
604 to_copy
605 });
606
607 // Re-enable RX interrupt
608 cortex_m::interrupt::free(|_| {
609 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
610 });
611
612 Ok(read)
613 }
614}
615
616// ============================================================================
617// INTERRUPT HANDLER
618// ============================================================================
619
620/// Buffered UART interrupt handler
621pub struct BufferedInterruptHandler<T: Instance> {
622 _phantom: PhantomData<T>,
623}
624
625impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {
626 unsafe fn on_interrupt() {
627 let regs = T::info().regs;
628 let state = T::buffered_state();
629
630 // Check if this instance is initialized
631 if !state.initialized.load(Ordering::Relaxed) {
632 return;
633 }
634
635 let ctrl = regs.ctrl().read();
636 let stat = regs.stat().read();
637 let has_fifo = regs.param().read().rxfifo().bits() > 0;
638
639 // Handle overrun error
640 if stat.or().is_overrun() {
641 regs.stat().write(|w| w.or().clear_bit_by_one());
642 state.rx_waker.wake();
643 return;
644 }
645
646 // Clear other error flags
647 if stat.pf().is_parity() {
648 regs.stat().write(|w| w.pf().clear_bit_by_one());
649 }
650 if stat.fe().is_error() {
651 regs.stat().write(|w| w.fe().clear_bit_by_one());
652 }
653 if stat.nf().is_noise() {
654 regs.stat().write(|w| w.nf().clear_bit_by_one());
655 }
656
657 // Handle RX data
658 if ctrl.rie().is_enabled() && (has_data(regs) || stat.idle().is_idle()) {
659 let mut pushed_any = false;
660 let mut writer = state.rx_buf.writer();
661
662 if has_fifo {
663 // Read from FIFO
664 while regs.water().read().rxcount().bits() > 0 {
665 let byte = (regs.data().read().bits() & 0xFF) as u8;
666 if writer.push_one(byte) {
667 pushed_any = true;
668 } else {
669 // Buffer full, stop reading
670 break;
671 }
672 }
673 } else {
674 // Read single byte
675 if regs.stat().read().rdrf().is_rxdata() {
676 let byte = (regs.data().read().bits() & 0xFF) as u8;
677 if writer.push_one(byte) {
678 pushed_any = true;
679 }
680 }
681 }
682
683 if pushed_any {
684 state.rx_waker.wake();
685 }
686
687 // Clear idle flag if set
688 if stat.idle().is_idle() {
689 regs.stat().write(|w| w.idle().clear_bit_by_one());
690 }
691 }
692
693 // Handle TX data
694 if ctrl.tie().is_enabled() {
695 let mut sent_any = false;
696 let mut reader = state.tx_buf.reader();
697
698 // Send data while TX buffer is ready and we have data
699 while regs.stat().read().tdre().is_no_txdata() {
700 if let Some(byte) = reader.pop_one() {
701 regs.data().write(|w| w.bits(u32::from(byte)));
702 sent_any = true;
703 } else {
704 // No more data to send
705 break;
706 }
707 }
708
709 if sent_any {
710 state.tx_waker.wake();
711 }
712
713 // If buffer is empty, switch to TC interrupt or disable
714 if state.tx_buf.is_empty() {
715 cortex_m::interrupt::free(|_| {
716 regs.ctrl().modify(|_, w| w.tie().disabled().tcie().enabled());
717 });
718 }
719 }
720
721 // Handle transmission complete
722 if ctrl.tcie().is_enabled() && regs.stat().read().tc().is_complete() {
723 state.tx_done.store(true, Ordering::Release);
724 state.tx_waker.wake();
725
726 // Disable TC interrupt
727 cortex_m::interrupt::free(|_| {
728 regs.ctrl().modify(|_, w| w.tcie().disabled());
729 });
730 }
731 }
732}
733
734// ============================================================================
735// EMBEDDED-IO ASYNC TRAIT IMPLEMENTATIONS
736// ============================================================================
737
738impl embedded_io_async::ErrorType for BufferedLpuartTx<'_> {
739 type Error = Error;
740}
741
742impl embedded_io_async::ErrorType for BufferedLpuartRx<'_> {
743 type Error = Error;
744}
745
746impl embedded_io_async::ErrorType for BufferedLpuart<'_> {
747 type Error = Error;
748}
749
750impl embedded_io_async::Write for BufferedLpuartTx<'_> {
751 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
752 self.write(buf).await
753 }
754
755 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
756 self.flush().await
757 }
758}
759
760impl embedded_io_async::Read for BufferedLpuartRx<'_> {
761 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
762 self.read(buf).await
763 }
764}
765
766impl embedded_io_async::Write for BufferedLpuart<'_> {
767 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
768 self.tx.write(buf).await
769 }
770
771 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
772 self.tx.flush().await
773 }
774}
775
776impl embedded_io_async::Read for BufferedLpuart<'_> {
777 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
778 self.rx.read(buf).await
779 }
780}
diff --git a/embassy-mcxa/src/lpuart/mod.rs b/embassy-mcxa/src/lpuart/mod.rs
new file mode 100644
index 000000000..b8a2d5172
--- /dev/null
+++ b/embassy-mcxa/src/lpuart/mod.rs
@@ -0,0 +1,1292 @@
1use core::marker::PhantomData;
2
3use embassy_hal_internal::{Peri, PeripheralType};
4use paste::paste;
5
6use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig};
7use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset};
8use crate::gpio::SealedPin;
9use crate::pac::lpuart0::baud::Sbns as StopBits;
10use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Pt as Parity};
11use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource};
12use crate::pac::lpuart0::stat::Msbf as MsbFirst;
13use crate::{AnyPin, interrupt, pac};
14
15pub mod buffered;
16
17// ============================================================================
18// STUB IMPLEMENTATION
19// ============================================================================
20
21// Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API
22// Pin and Clock initialization is currently done at the examples level.
23
24// --- START DMA ---
25mod dma {
26 pub struct Channel<'d> {
27 pub(super) _lifetime: core::marker::PhantomData<&'d ()>,
28 }
29}
30
31use dma::Channel;
32
33// --- END DMA ---
34
35// ============================================================================
36// MISC
37// ============================================================================
38
39mod sealed {
40 /// Simply seal a trait to prevent external implementations
41 pub trait Sealed {}
42}
43
44// ============================================================================
45// INSTANCE TRAIT
46// ============================================================================
47
48pub type Regs = &'static crate::pac::lpuart0::RegisterBlock;
49
50pub trait SealedInstance {
51 fn info() -> Info;
52 fn index() -> usize;
53 fn buffered_state() -> &'static buffered::State;
54}
55
56pub struct Info {
57 pub regs: Regs,
58}
59
60/// Trait for LPUART peripheral instances
61#[allow(private_bounds)]
62pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> {
63 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance;
64 type Interrupt: interrupt::typelevel::Interrupt;
65}
66
67macro_rules! impl_instance {
68 ($($n:expr),*) => {
69 $(
70 paste!{
71 impl SealedInstance for crate::peripherals::[<LPUART $n>] {
72 fn info() -> Info {
73 Info {
74 regs: unsafe { &*pac::[<Lpuart $n>]::ptr() },
75 }
76 }
77
78 #[inline]
79 fn index() -> usize {
80 $n
81 }
82
83 fn buffered_state() -> &'static buffered::State {
84 static BUFFERED_STATE: buffered::State = buffered::State::new();
85 &BUFFERED_STATE
86 }
87 }
88
89 impl Instance for crate::peripherals::[<LPUART $n>] {
90 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance
91 = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>];
92 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];
93 }
94 }
95 )*
96 };
97}
98
99impl_instance!(0, 1, 2, 3, 4, 5);
100
101// ============================================================================
102// INSTANCE HELPER FUNCTIONS
103// ============================================================================
104
105/// Perform software reset on the LPUART peripheral
106pub fn perform_software_reset(regs: Regs) {
107 // Software reset - set and clear RST bit (Global register)
108 regs.global().write(|w| w.rst().reset());
109 regs.global().write(|w| w.rst().no_effect());
110}
111
112/// Disable both transmitter and receiver
113pub fn disable_transceiver(regs: Regs) {
114 regs.ctrl().modify(|_, w| w.te().disabled().re().disabled());
115}
116
117/// Calculate and configure baudrate settings
118pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock_freq: u32) -> Result<()> {
119 let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?;
120
121 // Configure BAUD register
122 regs.baud().modify(|_, w| unsafe {
123 // Clear and set OSR
124 w.osr().bits(osr - 1);
125 // Clear and set SBR
126 w.sbr().bits(sbr);
127 // Set BOTHEDGE if OSR is between 4 and 7
128 if osr > 3 && osr < 8 {
129 w.bothedge().enabled()
130 } else {
131 w.bothedge().disabled()
132 }
133 });
134
135 Ok(())
136}
137
138/// Configure frame format (stop bits, data bits)
139pub fn configure_frame_format(regs: Regs, config: &Config) {
140 // Configure stop bits
141 regs.baud().modify(|_, w| w.sbns().variant(config.stop_bits_count));
142
143 // Clear M10 for now (10-bit mode)
144 regs.baud().modify(|_, w| w.m10().disabled());
145}
146
147/// Configure control settings (parity, data bits, idle config, pin swap)
148pub fn configure_control_settings(regs: Regs, config: &Config) {
149 regs.ctrl().modify(|_, w| {
150 // Parity configuration
151 let mut w = if let Some(parity) = config.parity_mode {
152 w.pe().enabled().pt().variant(parity)
153 } else {
154 w.pe().disabled()
155 };
156
157 // Data bits configuration
158 w = match config.data_bits_count {
159 DataBits::Data8 => {
160 if config.parity_mode.is_some() {
161 w.m().data9() // 8 data + 1 parity = 9 bits
162 } else {
163 w.m().data8() // 8 data bits only
164 }
165 }
166 DataBits::Data9 => w.m().data9(),
167 };
168
169 // Idle configuration
170 w = w.idlecfg().variant(config.rx_idle_config);
171 w = w.ilt().variant(config.rx_idle_type);
172
173 // Swap TXD/RXD if configured
174 if config.swap_txd_rxd {
175 w.swap().swap()
176 } else {
177 w.swap().standard()
178 }
179 });
180}
181
182/// Configure FIFO settings and watermarks
183pub fn configure_fifo(regs: Regs, config: &Config) {
184 // Configure WATER register for FIFO watermarks
185 regs.water().write(|w| unsafe {
186 w.rxwater()
187 .bits(config.rx_fifo_watermark)
188 .txwater()
189 .bits(config.tx_fifo_watermark)
190 });
191
192 // Enable TX/RX FIFOs
193 regs.fifo().modify(|_, w| w.txfe().enabled().rxfe().enabled());
194
195 // Flush FIFOs
196 regs.fifo()
197 .modify(|_, w| w.txflush().txfifo_rst().rxflush().rxfifo_rst());
198}
199
200/// Clear all status flags
201pub fn clear_all_status_flags(regs: Regs) {
202 regs.stat().reset();
203}
204
205/// Configure hardware flow control if enabled
206pub fn configure_flow_control(regs: Regs, enable_tx_cts: bool, enable_rx_rts: bool, config: &Config) {
207 if enable_rx_rts || enable_tx_cts {
208 regs.modir().modify(|_, w| {
209 let mut w = w;
210
211 // Configure TX CTS
212 w = w.txctsc().variant(config.tx_cts_config);
213 w = w.txctssrc().variant(config.tx_cts_source);
214
215 if enable_rx_rts {
216 w = w.rxrtse().enabled();
217 } else {
218 w = w.rxrtse().disabled();
219 }
220
221 if enable_tx_cts {
222 w = w.txctse().enabled();
223 } else {
224 w = w.txctse().disabled();
225 }
226
227 w
228 });
229 }
230}
231
232/// Configure bit order (MSB first or LSB first)
233pub fn configure_bit_order(regs: Regs, msb_first: MsbFirst) {
234 regs.stat().modify(|_, w| w.msbf().variant(msb_first));
235}
236
237/// Enable transmitter and/or receiver based on configuration
238pub fn enable_transceiver(regs: Regs, enable_tx: bool, enable_rx: bool) {
239 regs.ctrl().modify(|_, w| {
240 let mut w = w;
241 if enable_tx {
242 w = w.te().enabled();
243 }
244 if enable_rx {
245 w = w.re().enabled();
246 }
247 w
248 });
249}
250
251pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> {
252 let mut baud_diff = baudrate;
253 let mut osr = 0u8;
254 let mut sbr = 0u16;
255
256 // Try OSR values from 4 to 32
257 for osr_temp in 4u8..=32u8 {
258 // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2
259 let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32)).div_ceil(2);
260
261 let sbr_temp = if sbr_calc == 0 {
262 1
263 } else if sbr_calc > 0x1FFF {
264 0x1FFF
265 } else {
266 sbr_calc as u16
267 };
268
269 // Calculate actual baud rate
270 let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32);
271
272 let temp_diff = calculated_baud.abs_diff(baudrate);
273
274 if temp_diff <= baud_diff {
275 baud_diff = temp_diff;
276 osr = osr_temp;
277 sbr = sbr_temp;
278 }
279 }
280
281 // Check if baud rate difference is within 3%
282 if baud_diff > (baudrate / 100) * 3 {
283 return Err(Error::UnsupportedBaudrate);
284 }
285
286 Ok((osr, sbr))
287}
288
289/// Wait for all transmit operations to complete
290pub fn wait_for_tx_complete(regs: Regs) {
291 // Wait for TX FIFO to empty
292 while regs.water().read().txcount().bits() != 0 {
293 // Wait for TX FIFO to drain
294 }
295
296 // Wait for last character to shift out (TC = Transmission Complete)
297 while regs.stat().read().tc().is_active() {
298 // Wait for transmission to complete
299 }
300}
301
302pub fn check_and_clear_rx_errors(regs: Regs) -> Result<()> {
303 let stat = regs.stat().read();
304 let mut status = Ok(());
305
306 // Check for overrun first - other error flags are prevented when OR is set
307 if stat.or().is_overrun() {
308 regs.stat().write(|w| w.or().clear_bit_by_one());
309
310 return Err(Error::Overrun);
311 }
312
313 if stat.pf().is_parity() {
314 regs.stat().write(|w| w.pf().clear_bit_by_one());
315 status = Err(Error::Parity);
316 }
317
318 if stat.fe().is_error() {
319 regs.stat().write(|w| w.fe().clear_bit_by_one());
320 status = Err(Error::Framing);
321 }
322
323 if stat.nf().is_noise() {
324 regs.stat().write(|w| w.nf().clear_bit_by_one());
325 status = Err(Error::Noise);
326 }
327
328 status
329}
330
331pub fn has_data(regs: Regs) -> bool {
332 if regs.param().read().rxfifo().bits() > 0 {
333 // FIFO is available - check RXCOUNT in WATER register
334 regs.water().read().rxcount().bits() > 0
335 } else {
336 // No FIFO - check RDRF flag in STAT register
337 regs.stat().read().rdrf().is_rxdata()
338 }
339}
340
341// ============================================================================
342// PIN TRAITS FOR LPUART FUNCTIONALITY
343// ============================================================================
344
345impl<T: SealedPin> sealed::Sealed for T {}
346
347/// io configuration trait for Lpuart Tx configuration
348pub trait TxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
349 /// convert the pin to appropriate function for Lpuart Tx usage
350 fn as_tx(&self);
351}
352
353/// io configuration trait for Lpuart Rx configuration
354pub trait RxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
355 /// convert the pin to appropriate function for Lpuart Rx usage
356 fn as_rx(&self);
357}
358
359/// io configuration trait for Lpuart Cts
360pub trait CtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
361 /// convert the pin to appropriate function for Lpuart Cts usage
362 fn as_cts(&self);
363}
364
365/// io configuration trait for Lpuart Rts
366pub trait RtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
367 /// convert the pin to appropriate function for Lpuart Rts usage
368 fn as_rts(&self);
369}
370
371macro_rules! impl_tx_pin {
372 ($inst:ident, $pin:ident, $alt:ident) => {
373 impl TxPin<crate::peripherals::$inst> for crate::peripherals::$pin {
374 fn as_tx(&self) {
375 // TODO: Check these are right
376 self.set_pull(crate::gpio::Pull::Up);
377 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
378 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
379 self.set_function(crate::pac::port0::pcr0::Mux::$alt);
380 self.set_enable_input_buffer();
381 }
382 }
383 };
384}
385
386macro_rules! impl_rx_pin {
387 ($inst:ident, $pin:ident, $alt:ident) => {
388 impl RxPin<crate::peripherals::$inst> for crate::peripherals::$pin {
389 fn as_rx(&self) {
390 // TODO: Check these are right
391 self.set_pull(crate::gpio::Pull::Up);
392 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
393 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
394 self.set_function(crate::pac::port0::pcr0::Mux::$alt);
395 self.set_enable_input_buffer();
396 }
397 }
398 };
399}
400
401// TODO: Macro and impls for CTS/RTS pins
402macro_rules! impl_cts_pin {
403 ($inst:ident, $pin:ident, $alt:ident) => {
404 impl CtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {
405 fn as_cts(&self) {
406 todo!()
407 }
408 }
409 };
410}
411
412macro_rules! impl_rts_pin {
413 ($inst:ident, $pin:ident, $alt:ident) => {
414 impl RtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {
415 fn as_rts(&self) {
416 todo!()
417 }
418 }
419 };
420}
421
422// LPUART 0
423impl_tx_pin!(LPUART0, P0_3, Mux2);
424impl_tx_pin!(LPUART0, P0_21, Mux3);
425impl_tx_pin!(LPUART0, P2_1, Mux2);
426
427impl_rx_pin!(LPUART0, P0_2, Mux2);
428impl_rx_pin!(LPUART0, P0_20, Mux3);
429impl_rx_pin!(LPUART0, P2_0, Mux2);
430
431impl_cts_pin!(LPUART0, P0_1, Mux2);
432impl_cts_pin!(LPUART0, P0_23, Mux3);
433impl_cts_pin!(LPUART0, P2_3, Mux2);
434
435impl_rts_pin!(LPUART0, P0_0, Mux2);
436impl_rts_pin!(LPUART0, P0_22, Mux3);
437impl_rts_pin!(LPUART0, P2_2, Mux2);
438
439// LPUART 1
440impl_tx_pin!(LPUART1, P1_9, Mux2);
441impl_tx_pin!(LPUART1, P2_13, Mux3);
442impl_tx_pin!(LPUART1, P3_9, Mux3);
443impl_tx_pin!(LPUART1, P3_21, Mux3);
444
445impl_rx_pin!(LPUART1, P1_8, Mux2);
446impl_rx_pin!(LPUART1, P2_12, Mux3);
447impl_rx_pin!(LPUART1, P3_8, Mux3);
448impl_rx_pin!(LPUART1, P3_20, Mux3);
449
450impl_cts_pin!(LPUART1, P1_11, Mux2);
451impl_cts_pin!(LPUART1, P2_17, Mux3);
452impl_cts_pin!(LPUART1, P3_11, Mux3);
453impl_cts_pin!(LPUART1, P3_23, Mux3);
454
455impl_rts_pin!(LPUART1, P1_10, Mux2);
456impl_rts_pin!(LPUART1, P2_15, Mux3);
457impl_rts_pin!(LPUART1, P2_16, Mux3);
458impl_rts_pin!(LPUART1, P3_10, Mux3);
459
460// LPUART 2
461impl_tx_pin!(LPUART2, P1_5, Mux3);
462impl_tx_pin!(LPUART2, P1_13, Mux3);
463impl_tx_pin!(LPUART2, P2_2, Mux3);
464impl_tx_pin!(LPUART2, P2_10, Mux3);
465impl_tx_pin!(LPUART2, P3_15, Mux2);
466
467impl_rx_pin!(LPUART2, P1_4, Mux3);
468impl_rx_pin!(LPUART2, P1_12, Mux3);
469impl_rx_pin!(LPUART2, P2_3, Mux3);
470impl_rx_pin!(LPUART2, P2_11, Mux3);
471impl_rx_pin!(LPUART2, P3_14, Mux2);
472
473impl_cts_pin!(LPUART2, P1_7, Mux3);
474impl_cts_pin!(LPUART2, P1_15, Mux3);
475impl_cts_pin!(LPUART2, P2_4, Mux3);
476impl_cts_pin!(LPUART2, P3_13, Mux2);
477
478impl_rts_pin!(LPUART2, P1_6, Mux3);
479impl_rts_pin!(LPUART2, P1_14, Mux3);
480impl_rts_pin!(LPUART2, P2_5, Mux3);
481impl_rts_pin!(LPUART2, P3_12, Mux2);
482
483// LPUART 3
484impl_tx_pin!(LPUART3, P3_1, Mux3);
485impl_tx_pin!(LPUART3, P3_12, Mux3);
486impl_tx_pin!(LPUART3, P4_5, Mux3);
487
488impl_rx_pin!(LPUART3, P3_0, Mux3);
489impl_rx_pin!(LPUART3, P3_13, Mux3);
490impl_rx_pin!(LPUART3, P4_2, Mux3);
491
492impl_cts_pin!(LPUART3, P3_7, Mux3);
493impl_cts_pin!(LPUART3, P3_14, Mux3);
494impl_cts_pin!(LPUART3, P4_6, Mux3);
495
496impl_rts_pin!(LPUART3, P3_6, Mux3);
497impl_rts_pin!(LPUART3, P3_15, Mux3);
498impl_rts_pin!(LPUART3, P4_7, Mux3);
499
500// LPUART 4
501impl_tx_pin!(LPUART4, P2_7, Mux3);
502impl_tx_pin!(LPUART4, P3_19, Mux2);
503impl_tx_pin!(LPUART4, P3_27, Mux3);
504impl_tx_pin!(LPUART4, P4_3, Mux3);
505
506impl_rx_pin!(LPUART4, P2_6, Mux3);
507impl_rx_pin!(LPUART4, P3_18, Mux2);
508impl_rx_pin!(LPUART4, P3_28, Mux3);
509impl_rx_pin!(LPUART4, P4_4, Mux3);
510
511impl_cts_pin!(LPUART4, P2_0, Mux3);
512impl_cts_pin!(LPUART4, P3_17, Mux2);
513impl_cts_pin!(LPUART4, P3_31, Mux3);
514
515impl_rts_pin!(LPUART4, P2_1, Mux3);
516impl_rts_pin!(LPUART4, P3_16, Mux2);
517impl_rts_pin!(LPUART4, P3_30, Mux3);
518
519// LPUART 5
520impl_tx_pin!(LPUART5, P1_10, Mux8);
521impl_tx_pin!(LPUART5, P1_17, Mux8);
522
523impl_rx_pin!(LPUART5, P1_11, Mux8);
524impl_rx_pin!(LPUART5, P1_16, Mux8);
525
526impl_cts_pin!(LPUART5, P1_12, Mux8);
527impl_cts_pin!(LPUART5, P1_19, Mux8);
528
529impl_rts_pin!(LPUART5, P1_13, Mux8);
530impl_rts_pin!(LPUART5, P1_18, Mux8);
531
532// ============================================================================
533// ERROR TYPES AND RESULTS
534// ============================================================================
535
536/// LPUART error types
537#[derive(Debug, Copy, Clone, Eq, PartialEq)]
538#[cfg_attr(feature = "defmt", derive(defmt::Format))]
539pub enum Error {
540 /// Read error
541 Read,
542 /// Buffer overflow
543 Overrun,
544 /// Noise error
545 Noise,
546 /// Framing error
547 Framing,
548 /// Parity error
549 Parity,
550 /// Failure
551 Fail,
552 /// Invalid argument
553 InvalidArgument,
554 /// Lpuart baud rate cannot be supported with the given clock
555 UnsupportedBaudrate,
556 /// RX FIFO Empty
557 RxFifoEmpty,
558 /// TX FIFO Full
559 TxFifoFull,
560 /// TX Busy
561 TxBusy,
562 /// Clock Error
563 ClockSetup(ClockError),
564}
565
566/// A specialized Result type for LPUART operations
567pub type Result<T> = core::result::Result<T, Error>;
568
569// ============================================================================
570// CONFIGURATION STRUCTURES
571// ============================================================================
572
573/// Lpuart config
574#[derive(Debug, Clone, Copy)]
575pub struct Config {
576 /// Power state required for this peripheral
577 pub power: PoweredClock,
578 /// Clock source
579 pub source: LpuartClockSel,
580 /// Clock divisor
581 pub div: Div4,
582 /// Baud rate in bits per second
583 pub baudrate_bps: u32,
584 /// Parity configuration
585 pub parity_mode: Option<Parity>,
586 /// Number of data bits
587 pub data_bits_count: DataBits,
588 /// MSB First or LSB First configuration
589 pub msb_first: MsbFirst,
590 /// Number of stop bits
591 pub stop_bits_count: StopBits,
592 /// TX FIFO watermark
593 pub tx_fifo_watermark: u8,
594 /// RX FIFO watermark
595 pub rx_fifo_watermark: u8,
596 /// TX CTS source
597 pub tx_cts_source: TxCtsSource,
598 /// TX CTS configure
599 pub tx_cts_config: TxCtsConfig,
600 /// RX IDLE type
601 pub rx_idle_type: IdleType,
602 /// RX IDLE configuration
603 pub rx_idle_config: IdleConfig,
604 /// Swap TXD and RXD pins
605 pub swap_txd_rxd: bool,
606}
607
608impl Default for Config {
609 fn default() -> Self {
610 Self {
611 baudrate_bps: 115_200u32,
612 parity_mode: None,
613 data_bits_count: DataBits::Data8,
614 msb_first: MsbFirst::LsbFirst,
615 stop_bits_count: StopBits::One,
616 tx_fifo_watermark: 0,
617 rx_fifo_watermark: 1,
618 tx_cts_source: TxCtsSource::Cts,
619 tx_cts_config: TxCtsConfig::Start,
620 rx_idle_type: IdleType::FromStart,
621 rx_idle_config: IdleConfig::Idle1,
622 swap_txd_rxd: false,
623 power: PoweredClock::NormalEnabledDeepSleepDisabled,
624 source: LpuartClockSel::FroLfDiv,
625 div: Div4::no_div(),
626 }
627 }
628}
629
630/// LPUART status flags
631#[derive(Debug, Clone, Copy)]
632#[cfg_attr(feature = "defmt", derive(defmt::Format))]
633pub struct Status {
634 /// Transmit data register empty
635 pub tx_empty: bool,
636 /// Transmission complete
637 pub tx_complete: bool,
638 /// Receive data register full
639 pub rx_full: bool,
640 /// Idle line detected
641 pub idle: bool,
642 /// Receiver overrun
643 pub overrun: bool,
644 /// Noise error
645 pub noise: bool,
646 /// Framing error
647 pub framing: bool,
648 /// Parity error
649 pub parity: bool,
650}
651
652// ============================================================================
653// MODE TRAITS (BLOCKING/ASYNC)
654// ============================================================================
655
656/// Driver move trait.
657#[allow(private_bounds)]
658pub trait Mode: sealed::Sealed {}
659
660/// Blocking mode.
661pub struct Blocking;
662impl sealed::Sealed for Blocking {}
663impl Mode for Blocking {}
664
665/// Async mode.
666pub struct Async;
667impl sealed::Sealed for Async {}
668impl Mode for Async {}
669
670// ============================================================================
671// CORE DRIVER STRUCTURES
672// ============================================================================
673
674/// Lpuart driver.
675pub struct Lpuart<'a, M: Mode> {
676 info: Info,
677 tx: LpuartTx<'a, M>,
678 rx: LpuartRx<'a, M>,
679}
680
681/// Lpuart TX driver.
682pub struct LpuartTx<'a, M: Mode> {
683 info: Info,
684 _tx_pin: Peri<'a, AnyPin>,
685 _cts_pin: Option<Peri<'a, AnyPin>>,
686 _tx_dma: Option<Channel<'a>>,
687 mode: PhantomData<(&'a (), M)>,
688}
689
690/// Lpuart Rx driver.
691pub struct LpuartRx<'a, M: Mode> {
692 info: Info,
693 _rx_pin: Peri<'a, AnyPin>,
694 _rts_pin: Option<Peri<'a, AnyPin>>,
695 _rx_dma: Option<Channel<'a>>,
696 mode: PhantomData<(&'a (), M)>,
697}
698
699// ============================================================================
700// LPUART CORE IMPLEMENTATION
701// ============================================================================
702
703impl<'a, M: Mode> Lpuart<'a, M> {
704 fn init<T: Instance>(
705 enable_tx: bool,
706 enable_rx: bool,
707 enable_tx_cts: bool,
708 enable_rx_rts: bool,
709 config: Config,
710 ) -> Result<()> {
711 let regs = T::info().regs;
712
713 // Enable clocks
714 let conf = LpuartConfig {
715 power: config.power,
716 source: config.source,
717 div: config.div,
718 instance: T::CLOCK_INSTANCE,
719 };
720 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
721
722 // Perform initialization sequence
723 perform_software_reset(regs);
724 disable_transceiver(regs);
725 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
726 configure_frame_format(regs, &config);
727 configure_control_settings(regs, &config);
728 configure_fifo(regs, &config);
729 clear_all_status_flags(regs);
730 configure_flow_control(regs, enable_tx_cts, enable_rx_rts, &config);
731 configure_bit_order(regs, config.msb_first);
732 enable_transceiver(regs, enable_rx, enable_tx);
733
734 Ok(())
735 }
736
737 /// Deinitialize the LPUART peripheral
738 pub fn deinit(&self) -> Result<()> {
739 let regs = self.info.regs;
740
741 // Wait for TX operations to complete
742 wait_for_tx_complete(regs);
743
744 // Clear all status flags
745 clear_all_status_flags(regs);
746
747 // Disable the module - clear all CTRL register bits
748 regs.ctrl().reset();
749
750 Ok(())
751 }
752
753 /// Split the Lpuart into a transmitter and receiver
754 pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) {
755 (self.tx, self.rx)
756 }
757
758 /// Split the Lpuart into a transmitter and receiver by mutable reference
759 pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) {
760 (&mut self.tx, &mut self.rx)
761 }
762}
763
764// ============================================================================
765// BLOCKING MODE IMPLEMENTATIONS
766// ============================================================================
767
768impl<'a> Lpuart<'a, Blocking> {
769 /// Create a new blocking LPUART instance with RX/TX pins.
770 pub fn new_blocking<T: Instance>(
771 _inner: Peri<'a, T>,
772 tx_pin: Peri<'a, impl TxPin<T>>,
773 rx_pin: Peri<'a, impl RxPin<T>>,
774 config: Config,
775 ) -> Result<Self> {
776 // Configure the pins for LPUART usage
777 tx_pin.as_tx();
778 rx_pin.as_rx();
779
780 // Initialize the peripheral
781 Self::init::<T>(true, true, false, false, config)?;
782
783 Ok(Self {
784 info: T::info(),
785 tx: LpuartTx::new_inner(T::info(), tx_pin.into(), None, None),
786 rx: LpuartRx::new_inner(T::info(), rx_pin.into(), None, None),
787 })
788 }
789
790 /// Create a new blocking LPUART instance with RX, TX and RTS/CTS flow control pins
791 pub fn new_blocking_with_rtscts<T: Instance>(
792 _inner: Peri<'a, T>,
793 tx_pin: Peri<'a, impl TxPin<T>>,
794 rx_pin: Peri<'a, impl RxPin<T>>,
795 cts_pin: Peri<'a, impl CtsPin<T>>,
796 rts_pin: Peri<'a, impl RtsPin<T>>,
797 config: Config,
798 ) -> Result<Self> {
799 // Configure the pins for LPUART usage
800 rx_pin.as_rx();
801 tx_pin.as_tx();
802 rts_pin.as_rts();
803 cts_pin.as_cts();
804
805 // Initialize the peripheral with flow control
806 Self::init::<T>(true, true, true, true, config)?;
807
808 Ok(Self {
809 info: T::info(),
810 rx: LpuartRx::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into()), None),
811 tx: LpuartTx::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into()), None),
812 })
813 }
814}
815
816// ----------------------------------------------------------------------------
817// Blocking TX Implementation
818// ----------------------------------------------------------------------------
819
820impl<'a, M: Mode> LpuartTx<'a, M> {
821 fn new_inner(
822 info: Info,
823 tx_pin: Peri<'a, AnyPin>,
824 cts_pin: Option<Peri<'a, AnyPin>>,
825 tx_dma: Option<Channel<'a>>,
826 ) -> Self {
827 Self {
828 info,
829 _tx_pin: tx_pin,
830 _cts_pin: cts_pin,
831 _tx_dma: tx_dma,
832 mode: PhantomData,
833 }
834 }
835}
836
837impl<'a> LpuartTx<'a, Blocking> {
838 /// Create a new blocking LPUART transmitter instance
839 pub fn new_blocking<T: Instance>(
840 _inner: Peri<'a, T>,
841 tx_pin: Peri<'a, impl TxPin<T>>,
842 config: Config,
843 ) -> Result<Self> {
844 // Configure the pins for LPUART usage
845 tx_pin.as_tx();
846
847 // Initialize the peripheral
848 Lpuart::<Blocking>::init::<T>(true, false, false, false, config)?;
849
850 Ok(Self::new_inner(T::info(), tx_pin.into(), None, None))
851 }
852
853 /// Create a new blocking LPUART transmitter instance with CTS flow control
854 pub fn new_blocking_with_cts<T: Instance>(
855 _inner: Peri<'a, T>,
856 tx_pin: Peri<'a, impl TxPin<T>>,
857 cts_pin: Peri<'a, impl CtsPin<T>>,
858 config: Config,
859 ) -> Result<Self> {
860 tx_pin.as_tx();
861 cts_pin.as_cts();
862
863 Lpuart::<Blocking>::init::<T>(true, false, true, false, config)?;
864
865 Ok(Self::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into()), None))
866 }
867
868 fn write_byte_internal(&mut self, byte: u8) -> Result<()> {
869 self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) });
870
871 Ok(())
872 }
873
874 fn blocking_write_byte(&mut self, byte: u8) -> Result<()> {
875 while self.info.regs.stat().read().tdre().is_txdata() {}
876 self.write_byte_internal(byte)
877 }
878
879 fn write_byte(&mut self, byte: u8) -> Result<()> {
880 if self.info.regs.stat().read().tdre().is_txdata() {
881 Err(Error::TxFifoFull)
882 } else {
883 self.write_byte_internal(byte)
884 }
885 }
886
887 /// Write data to LPUART TX blocking execution until all data is sent.
888 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
889 for x in buf {
890 self.blocking_write_byte(*x)?;
891 }
892
893 Ok(())
894 }
895
896 pub fn write_str_blocking(&mut self, buf: &str) {
897 let _ = self.blocking_write(buf.as_bytes());
898 }
899
900 /// Write data to LPUART TX without blocking.
901 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
902 for x in buf {
903 self.write_byte(*x)?;
904 }
905
906 Ok(())
907 }
908
909 /// Flush LPUART TX blocking execution until all data has been transmitted.
910 pub fn blocking_flush(&mut self) -> Result<()> {
911 while self.info.regs.water().read().txcount().bits() != 0 {
912 // Wait for TX FIFO to drain
913 }
914
915 // Wait for last character to shift out
916 while self.info.regs.stat().read().tc().is_active() {
917 // Wait for transmission to complete
918 }
919
920 Ok(())
921 }
922
923 /// Flush LPUART TX.
924 pub fn flush(&mut self) -> Result<()> {
925 // Check if TX FIFO is empty
926 if self.info.regs.water().read().txcount().bits() != 0 {
927 return Err(Error::TxBusy);
928 }
929
930 // Check if transmission is complete
931 if self.info.regs.stat().read().tc().is_active() {
932 return Err(Error::TxBusy);
933 }
934
935 Ok(())
936 }
937}
938
939// ----------------------------------------------------------------------------
940// Blocking RX Implementation
941// ----------------------------------------------------------------------------
942
943impl<'a, M: Mode> LpuartRx<'a, M> {
944 fn new_inner(
945 info: Info,
946 rx_pin: Peri<'a, AnyPin>,
947 rts_pin: Option<Peri<'a, AnyPin>>,
948 rx_dma: Option<Channel<'a>>,
949 ) -> Self {
950 Self {
951 info,
952 _rx_pin: rx_pin,
953 _rts_pin: rts_pin,
954 _rx_dma: rx_dma,
955 mode: PhantomData,
956 }
957 }
958}
959
960impl<'a> LpuartRx<'a, Blocking> {
961 /// Create a new blocking LPUART Receiver instance
962 pub fn new_blocking<T: Instance>(
963 _inner: Peri<'a, T>,
964 rx_pin: Peri<'a, impl RxPin<T>>,
965 config: Config,
966 ) -> Result<Self> {
967 rx_pin.as_rx();
968
969 Lpuart::<Blocking>::init::<T>(false, true, false, false, config)?;
970
971 Ok(Self::new_inner(T::info(), rx_pin.into(), None, None))
972 }
973
974 /// Create a new blocking LPUART Receiver instance with RTS flow control
975 pub fn new_blocking_with_rts<T: Instance>(
976 _inner: Peri<'a, T>,
977 rx_pin: Peri<'a, impl RxPin<T>>,
978 rts_pin: Peri<'a, impl RtsPin<T>>,
979 config: Config,
980 ) -> Result<Self> {
981 rx_pin.as_rx();
982 rts_pin.as_rts();
983
984 Lpuart::<Blocking>::init::<T>(false, true, false, true, config)?;
985
986 Ok(Self::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into()), None))
987 }
988
989 fn read_byte_internal(&mut self) -> Result<u8> {
990 let data = self.info.regs.data().read();
991
992 Ok((data.bits() & 0xFF) as u8)
993 }
994
995 fn read_byte(&mut self) -> Result<u8> {
996 check_and_clear_rx_errors(self.info.regs)?;
997
998 if !has_data(self.info.regs) {
999 return Err(Error::RxFifoEmpty);
1000 }
1001
1002 self.read_byte_internal()
1003 }
1004
1005 fn blocking_read_byte(&mut self) -> Result<u8> {
1006 loop {
1007 if has_data(self.info.regs) {
1008 return self.read_byte_internal();
1009 }
1010
1011 check_and_clear_rx_errors(self.info.regs)?;
1012 }
1013 }
1014
1015 /// Read data from LPUART RX without blocking.
1016 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
1017 for byte in buf.iter_mut() {
1018 *byte = self.read_byte()?;
1019 }
1020 Ok(())
1021 }
1022
1023 /// Read data from LPUART RX blocking execution until the buffer is filled.
1024 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1025 for byte in buf.iter_mut() {
1026 *byte = self.blocking_read_byte()?;
1027 }
1028 Ok(())
1029 }
1030}
1031
1032impl<'a> Lpuart<'a, Blocking> {
1033 /// Read data from LPUART RX blocking execution until the buffer is filled
1034 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1035 self.rx.blocking_read(buf)
1036 }
1037
1038 /// Read data from LPUART RX without blocking
1039 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
1040 self.rx.read(buf)
1041 }
1042
1043 /// Write data to LPUART TX blocking execution until all data is sent
1044 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
1045 self.tx.blocking_write(buf)
1046 }
1047
1048 pub fn write_byte(&mut self, byte: u8) -> Result<()> {
1049 self.tx.write_byte(byte)
1050 }
1051
1052 pub fn read_byte_blocking(&mut self) -> u8 {
1053 loop {
1054 if let Ok(b) = self.rx.read_byte() {
1055 return b;
1056 }
1057 }
1058 }
1059
1060 pub fn write_str_blocking(&mut self, buf: &str) {
1061 self.tx.write_str_blocking(buf);
1062 }
1063
1064 /// Write data to LPUART TX without blocking
1065 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
1066 self.tx.write(buf)
1067 }
1068
1069 /// Flush LPUART TX blocking execution until all data has been transmitted
1070 pub fn blocking_flush(&mut self) -> Result<()> {
1071 self.tx.blocking_flush()
1072 }
1073
1074 /// Flush LPUART TX without blocking
1075 pub fn flush(&mut self) -> Result<()> {
1076 self.tx.flush()
1077 }
1078}
1079
1080// ============================================================================
1081// ASYNC MODE IMPLEMENTATIONS
1082// ============================================================================
1083
1084// TODO: Implement async mode for LPUART
1085
1086// ============================================================================
1087// EMBEDDED-HAL 0.2 TRAIT IMPLEMENTATIONS
1088// ============================================================================
1089
1090impl embedded_hal_02::serial::Read<u8> for LpuartRx<'_, Blocking> {
1091 type Error = Error;
1092
1093 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1094 let mut buf = [0; 1];
1095 match self.read(&mut buf) {
1096 Ok(_) => Ok(buf[0]),
1097 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1098 Err(e) => Err(nb::Error::Other(e)),
1099 }
1100 }
1101}
1102
1103impl embedded_hal_02::serial::Write<u8> for LpuartTx<'_, Blocking> {
1104 type Error = Error;
1105
1106 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1107 match self.write(&[word]) {
1108 Ok(_) => Ok(()),
1109 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1110 Err(e) => Err(nb::Error::Other(e)),
1111 }
1112 }
1113
1114 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1115 match self.flush() {
1116 Ok(_) => Ok(()),
1117 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1118 Err(e) => Err(nb::Error::Other(e)),
1119 }
1120 }
1121}
1122
1123impl embedded_hal_02::blocking::serial::Write<u8> for LpuartTx<'_, Blocking> {
1124 type Error = Error;
1125
1126 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1127 self.blocking_write(buffer)
1128 }
1129
1130 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1131 self.blocking_flush()
1132 }
1133}
1134
1135impl embedded_hal_02::serial::Read<u8> for Lpuart<'_, Blocking> {
1136 type Error = Error;
1137
1138 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1139 embedded_hal_02::serial::Read::read(&mut self.rx)
1140 }
1141}
1142
1143impl embedded_hal_02::serial::Write<u8> for Lpuart<'_, Blocking> {
1144 type Error = Error;
1145
1146 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1147 embedded_hal_02::serial::Write::write(&mut self.tx, word)
1148 }
1149
1150 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1151 embedded_hal_02::serial::Write::flush(&mut self.tx)
1152 }
1153}
1154
1155impl embedded_hal_02::blocking::serial::Write<u8> for Lpuart<'_, Blocking> {
1156 type Error = Error;
1157
1158 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1159 self.blocking_write(buffer)
1160 }
1161
1162 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1163 self.blocking_flush()
1164 }
1165}
1166
1167// ============================================================================
1168// EMBEDDED-HAL-NB TRAIT IMPLEMENTATIONS
1169// ============================================================================
1170
1171impl embedded_hal_nb::serial::Error for Error {
1172 fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
1173 match *self {
1174 Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
1175 Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
1176 Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
1177 Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
1178 _ => embedded_hal_nb::serial::ErrorKind::Other,
1179 }
1180 }
1181}
1182
1183impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> {
1184 type Error = Error;
1185}
1186
1187impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> {
1188 type Error = Error;
1189}
1190
1191impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> {
1192 type Error = Error;
1193}
1194
1195impl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> {
1196 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1197 let mut buf = [0; 1];
1198 match self.read(&mut buf) {
1199 Ok(_) => Ok(buf[0]),
1200 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1201 Err(e) => Err(nb::Error::Other(e)),
1202 }
1203 }
1204}
1205
1206impl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> {
1207 fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
1208 match self.write(&[word]) {
1209 Ok(_) => Ok(()),
1210 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1211 Err(e) => Err(nb::Error::Other(e)),
1212 }
1213 }
1214
1215 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1216 match self.flush() {
1217 Ok(_) => Ok(()),
1218 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1219 Err(e) => Err(nb::Error::Other(e)),
1220 }
1221 }
1222}
1223
1224impl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> {
1225 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1226 embedded_hal_nb::serial::Read::read(&mut self.rx)
1227 }
1228}
1229
1230impl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> {
1231 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
1232 embedded_hal_nb::serial::Write::write(&mut self.tx, char)
1233 }
1234
1235 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1236 embedded_hal_nb::serial::Write::flush(&mut self.tx)
1237 }
1238}
1239
1240// ============================================================================
1241// EMBEDDED-IO TRAIT IMPLEMENTATIONS
1242// ============================================================================
1243
1244impl embedded_io::Error for Error {
1245 fn kind(&self) -> embedded_io::ErrorKind {
1246 embedded_io::ErrorKind::Other
1247 }
1248}
1249
1250impl embedded_io::ErrorType for LpuartRx<'_, Blocking> {
1251 type Error = Error;
1252}
1253
1254impl embedded_io::ErrorType for LpuartTx<'_, Blocking> {
1255 type Error = Error;
1256}
1257
1258impl embedded_io::ErrorType for Lpuart<'_, Blocking> {
1259 type Error = Error;
1260}
1261
1262impl embedded_io::Read for LpuartRx<'_, Blocking> {
1263 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1264 self.blocking_read(buf).map(|_| buf.len())
1265 }
1266}
1267
1268impl embedded_io::Write for LpuartTx<'_, Blocking> {
1269 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1270 self.blocking_write(buf).map(|_| buf.len())
1271 }
1272
1273 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1274 self.blocking_flush()
1275 }
1276}
1277
1278impl embedded_io::Read for Lpuart<'_, Blocking> {
1279 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1280 embedded_io::Read::read(&mut self.rx, buf)
1281 }
1282}
1283
1284impl embedded_io::Write for Lpuart<'_, Blocking> {
1285 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1286 embedded_io::Write::write(&mut self.tx, buf)
1287 }
1288
1289 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1290 embedded_io::Write::flush(&mut self.tx)
1291 }
1292}
diff --git a/embassy-mcxa/src/ostimer.rs b/embassy-mcxa/src/ostimer.rs
new file mode 100644
index 000000000..9e66e82d8
--- /dev/null
+++ b/embassy-mcxa/src/ostimer.rs
@@ -0,0 +1,745 @@
1//! # OSTIMER Driver with Robustness Features
2//!
3//! This module provides an async timer driver for the NXP MCXA276 OSTIMER peripheral
4//! with protection against race conditions and timer rollover issues.
5//!
6//! ## Features
7//!
8//! - Async timing with embassy-time integration
9//! - Gray code counter handling (42-bit counter)
10//! - Interrupt-driven wakeups
11//! - Configurable interrupt priority
12//! - **Race condition protection**: Critical sections and atomic operations
13//! - **Timer rollover handling**: Bounds checking and rollover prevention
14//!
15//! ## Clock Frequency Configuration
16//!
17//! The OSTIMER frequency depends on your system's clock configuration. You must provide
18//! the actual frequency when calling `time_driver::init()`.
19//!
20//! ## Race Condition Protection
21//! - Critical sections in interrupt handlers prevent concurrent access
22//! - Atomic register operations with memory barriers
23//! - Proper interrupt flag clearing and validation
24//!
25//! ## Timer Rollover Handling
26//! - Bounds checking prevents scheduling beyond timer capacity
27//! - Immediate wake for timestamps that would cause rollover issues
28#![allow(dead_code)]
29
30use core::sync::atomic::{AtomicBool, Ordering};
31
32use embassy_hal_internal::{Peri, PeripheralType};
33
34use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
35use crate::clocks::{Gate, PoweredClock, assert_reset, enable_and_reset, is_reset_released, release_reset};
36use crate::interrupt::InterruptExt;
37use crate::pac;
38
39// PAC defines the shared RegisterBlock under `ostimer0`.
40type Regs = pac::ostimer0::RegisterBlock;
41
42// OSTIMER EVTIMER register layout constants
43/// Total width of the EVTIMER counter in bits (42 bits total)
44const EVTIMER_TOTAL_BITS: u32 = 42;
45/// Width of the low part of EVTIMER (bits 31:0)
46const EVTIMER_LO_BITS: u32 = 32;
47/// Width of the high part of EVTIMER (bits 41:32)
48const EVTIMER_HI_BITS: u32 = 10;
49/// Bit position where high part starts in the combined 64-bit value
50const EVTIMER_HI_SHIFT: u32 = 32;
51
52/// Bit mask for the high part of EVTIMER
53const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1;
54
55/// Maximum value for MATCH_L register (32-bit)
56const MATCH_L_MAX: u32 = u32::MAX;
57/// Maximum value for MATCH_H register (10-bit)
58const MATCH_H_MAX: u16 = EVTIMER_HI_MASK;
59
60/// Bit mask for extracting the low 32 bits from a 64-bit value
61const LOW_32_BIT_MASK: u64 = u32::MAX as u64;
62
63/// Gray code conversion bit shifts (most significant to least)
64const GRAY_CONVERSION_SHIFTS: [u32; 6] = [32, 16, 8, 4, 2, 1];
65
66/// Maximum timer value before rollover (2^42 - 1 ticks)
67/// Actual rollover time depends on the configured clock frequency
68const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1;
69
70/// Threshold for detecting timer rollover in comparisons (1 second at 1MHz)
71const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000;
72
73/// Common default interrupt priority for OSTIMER
74const DEFAULT_INTERRUPT_PRIORITY: u8 = 3;
75
76// Global alarm state for interrupt handling
77static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false);
78static mut ALARM_CALLBACK: Option<fn()> = None;
79static mut ALARM_FLAG: Option<*const AtomicBool> = None;
80static mut ALARM_TARGET_TIME: u64 = 0;
81
82/// Number of tight spin iterations between elapsed time checks while waiting for MATCH writes to return to the idle (0) state.
83const MATCH_WRITE_READY_SPINS: usize = 512;
84/// Maximum time (in OSTIMER ticks) to wait for MATCH registers to become writable (~5 ms at 1 MHz).
85const MATCH_WRITE_READY_TIMEOUT_TICKS: u64 = 5_000;
86/// Short stabilization delay executed after toggling the MRCC reset line to let the OSTIMER bus interface settle.
87const RESET_STABILIZE_SPINS: usize = 512;
88
89pub(super) fn wait_for_match_write_ready(r: &Regs) -> bool {
90 let start = now_ticks_read();
91 let mut spin_budget = 0usize;
92
93 loop {
94 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
95 return true;
96 }
97
98 cortex_m::asm::nop();
99 spin_budget += 1;
100
101 if spin_budget >= MATCH_WRITE_READY_SPINS {
102 spin_budget = 0;
103
104 let elapsed = now_ticks_read().wrapping_sub(start);
105 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
106 return false;
107 }
108 }
109 }
110}
111
112pub(super) fn wait_for_match_write_complete(r: &Regs) -> bool {
113 let start = now_ticks_read();
114 let mut spin_budget = 0usize;
115
116 loop {
117 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
118 return true;
119 }
120
121 cortex_m::asm::nop();
122 spin_budget += 1;
123
124 if spin_budget >= MATCH_WRITE_READY_SPINS {
125 spin_budget = 0;
126
127 let elapsed = now_ticks_read().wrapping_sub(start);
128 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
129 return false;
130 }
131 }
132 }
133}
134
135fn prime_match_registers(r: &Regs) {
136 // Disable the interrupt, clear any pending flag, then wait until the MATCH registers are writable.
137 r.osevent_ctrl()
138 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
139
140 if wait_for_match_write_ready(r) {
141 r.match_l().write(|w| unsafe { w.match_value().bits(MATCH_L_MAX) });
142 r.match_h().write(|w| unsafe { w.match_value().bits(MATCH_H_MAX) });
143 let _ = wait_for_match_write_complete(r);
144 }
145}
146
147/// Single-shot alarm functionality for OSTIMER
148pub struct Alarm<'d> {
149 /// Whether the alarm is currently active
150 active: AtomicBool,
151 /// Callback to execute when alarm expires (optional)
152 callback: Option<fn()>,
153 /// Flag that gets set when alarm expires (optional)
154 flag: Option<&'d AtomicBool>,
155 _phantom: core::marker::PhantomData<&'d mut ()>,
156}
157
158impl<'d> Default for Alarm<'d> {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl<'d> Alarm<'d> {
165 /// Create a new alarm instance
166 pub fn new() -> Self {
167 Self {
168 active: AtomicBool::new(false),
169 callback: None,
170 flag: None,
171 _phantom: core::marker::PhantomData,
172 }
173 }
174
175 /// Set a callback that will be executed when the alarm expires
176 /// Note: Due to interrupt handler constraints, callbacks must be static function pointers
177 pub fn with_callback(mut self, callback: fn()) -> Self {
178 self.callback = Some(callback);
179 self
180 }
181
182 /// Set a flag that will be set to true when the alarm expires
183 pub fn with_flag(mut self, flag: &'d AtomicBool) -> Self {
184 self.flag = Some(flag);
185 self
186 }
187
188 /// Check if the alarm is currently active
189 pub fn is_active(&self) -> bool {
190 self.active.load(Ordering::Acquire)
191 }
192
193 /// Cancel the alarm if it's active
194 pub fn cancel(&self) {
195 self.active.store(false, Ordering::Release);
196 }
197}
198
199/// Configuration for Ostimer::new()
200#[derive(Copy, Clone)]
201pub struct Config {
202 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag.
203 pub init_match_max: bool,
204 pub power: PoweredClock,
205 pub source: OstimerClockSel,
206}
207
208impl Default for Config {
209 fn default() -> Self {
210 Self {
211 init_match_max: true,
212 power: PoweredClock::NormalEnabledDeepSleepDisabled,
213 source: OstimerClockSel::Clk1M,
214 }
215 }
216}
217
218/// OSTIMER peripheral instance
219pub struct Ostimer<'d, I: Instance> {
220 _inst: core::marker::PhantomData<I>,
221 clock_frequency_hz: u64,
222 _phantom: core::marker::PhantomData<&'d mut ()>,
223}
224
225impl<'d, I: Instance> Ostimer<'d, I> {
226 /// Construct OSTIMER handle.
227 /// Requires clocks for the instance to be enabled by the board before calling.
228 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation.
229 pub fn new(_inst: Peri<'d, I>, cfg: Config) -> Self {
230 let clock_freq = unsafe {
231 enable_and_reset::<I>(&OsTimerConfig {
232 power: cfg.power,
233 source: cfg.source,
234 })
235 .expect("Enabling OsTimer clock should not fail")
236 };
237
238 assert!(clock_freq > 0, "OSTIMER frequency must be greater than 0");
239
240 if cfg.init_match_max {
241 let r: &Regs = unsafe { &*I::ptr() };
242 // Mask INTENA, clear pending flag, and set MATCH to max so no spurious IRQ fires.
243 prime_match_registers(r);
244 }
245
246 Self {
247 _inst: core::marker::PhantomData,
248 clock_frequency_hz: clock_freq as u64,
249 _phantom: core::marker::PhantomData,
250 }
251 }
252
253 /// Get the configured clock frequency in Hz
254 pub fn clock_frequency_hz(&self) -> u64 {
255 self.clock_frequency_hz
256 }
257
258 /// Read the current timer counter value in timer ticks
259 ///
260 /// # Returns
261 /// Current timer counter value as a 64-bit unsigned integer
262 pub fn now(&self) -> u64 {
263 now_ticks_read()
264 }
265
266 /// Reset the timer counter to zero
267 ///
268 /// This performs a hardware reset of the OSTIMER peripheral, which will reset
269 /// the counter to zero and clear any pending interrupts. Note that this will
270 /// affect all timer operations including embassy-time.
271 ///
272 /// # Safety
273 /// This operation will reset the entire OSTIMER peripheral. Any active alarms
274 /// or time_driver operations will be disrupted. Use with caution.
275 pub fn reset(&self, _peripherals: &crate::pac::Peripherals) {
276 critical_section::with(|_| {
277 let r: &Regs = unsafe { &*I::ptr() };
278
279 // Mask the peripheral interrupt flag before we toggle the reset line so that
280 // no new NVIC activity races with the reset sequence.
281 r.osevent_ctrl()
282 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
283
284 unsafe {
285 assert_reset::<I>();
286
287 for _ in 0..RESET_STABILIZE_SPINS {
288 cortex_m::asm::nop();
289 }
290
291 release_reset::<I>();
292
293 while !is_reset_released::<I>() {
294 cortex_m::asm::nop();
295 }
296 }
297
298 for _ in 0..RESET_STABILIZE_SPINS {
299 cortex_m::asm::nop();
300 }
301
302 // Clear alarm bookkeeping before re-arming MATCH registers.
303 ALARM_ACTIVE.store(false, Ordering::Release);
304 unsafe {
305 ALARM_TARGET_TIME = 0;
306 ALARM_CALLBACK = None;
307 ALARM_FLAG = None;
308 }
309
310 prime_match_registers(r);
311 });
312
313 // Ensure no stale OS_EVENT request remains pending after the reset sequence.
314 crate::interrupt::OS_EVENT.unpend();
315 }
316
317 /// Schedule a single-shot alarm to expire after the specified delay in microseconds
318 ///
319 /// # Parameters
320 /// * `alarm` - The alarm instance to schedule
321 /// * `delay_us` - Delay in microseconds from now
322 ///
323 /// # Returns
324 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
325 pub fn schedule_alarm_delay(&self, alarm: &Alarm, delay_us: u64) -> bool {
326 let delay_ticks = (delay_us * self.clock_frequency_hz) / 1_000_000;
327 let target_time = now_ticks_read() + delay_ticks;
328 self.schedule_alarm_at(alarm, target_time)
329 }
330
331 /// Schedule a single-shot alarm to expire at the specified absolute time in timer ticks
332 ///
333 /// # Parameters
334 /// * `alarm` - The alarm instance to schedule
335 /// * `target_ticks` - Absolute time in timer ticks when the alarm should expire
336 ///
337 /// # Returns
338 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
339 pub fn schedule_alarm_at(&self, alarm: &Alarm, target_ticks: u64) -> bool {
340 let now = now_ticks_read();
341
342 // Check if target time is in the past
343 if target_ticks <= now {
344 // Execute callback immediately if alarm was supposed to be active
345 if alarm.active.load(Ordering::Acquire) {
346 alarm.active.store(false, Ordering::Release);
347 if let Some(callback) = alarm.callback {
348 callback();
349 }
350 if let Some(flag) = &alarm.flag {
351 flag.store(true, Ordering::Release);
352 }
353 }
354 return true;
355 }
356
357 // Check for timer rollover
358 let max_future = now + TIMER_MAX_VALUE;
359 if target_ticks > max_future {
360 return false; // Would exceed timer capacity
361 }
362
363 // Program the timer
364 let r: &Regs = unsafe { &*I::ptr() };
365
366 critical_section::with(|_| {
367 // Disable interrupt and clear flag
368 r.osevent_ctrl()
369 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
370
371 if !wait_for_match_write_ready(r) {
372 prime_match_registers(r);
373
374 if !wait_for_match_write_ready(r) {
375 alarm.active.store(false, Ordering::Release);
376 ALARM_ACTIVE.store(false, Ordering::Release);
377 unsafe {
378 ALARM_TARGET_TIME = 0;
379 ALARM_CALLBACK = None;
380 ALARM_FLAG = None;
381 }
382 return false;
383 }
384 }
385
386 // Mark alarm as active now that we know the MATCH registers are writable
387 alarm.active.store(true, Ordering::Release);
388
389 // Set global alarm state for interrupt handler
390 ALARM_ACTIVE.store(true, Ordering::Release);
391 unsafe {
392 ALARM_TARGET_TIME = target_ticks;
393 ALARM_CALLBACK = alarm.callback;
394 ALARM_FLAG = alarm.flag.map(|f| f as *const AtomicBool);
395 }
396
397 // Program MATCH registers (Gray-coded)
398 let gray = bin_to_gray(target_ticks);
399 let l = (gray & LOW_32_BIT_MASK) as u32;
400 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
401
402 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
403 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
404
405 if !wait_for_match_write_complete(r) {
406 alarm.active.store(false, Ordering::Release);
407 ALARM_ACTIVE.store(false, Ordering::Release);
408 unsafe {
409 ALARM_TARGET_TIME = 0;
410 ALARM_CALLBACK = None;
411 ALARM_FLAG = None;
412 }
413 return false;
414 }
415
416 let now_after_program = now_ticks_read();
417 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
418 if now_after_program >= target_ticks && !intrflag_set {
419 alarm.active.store(false, Ordering::Release);
420 ALARM_ACTIVE.store(false, Ordering::Release);
421 unsafe {
422 ALARM_TARGET_TIME = 0;
423 ALARM_CALLBACK = None;
424 ALARM_FLAG = None;
425 }
426 return false;
427 }
428
429 // Enable interrupt
430 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
431
432 true
433 })
434 }
435
436 /// Cancel any active alarm
437 pub fn cancel_alarm(&self, alarm: &Alarm) {
438 critical_section::with(|_| {
439 alarm.cancel();
440
441 // Clear global alarm state
442 ALARM_ACTIVE.store(false, Ordering::Release);
443 unsafe { ALARM_TARGET_TIME = 0 };
444
445 // Reset MATCH registers to maximum values to prevent spurious interrupts
446 let r: &Regs = unsafe { &*I::ptr() };
447 prime_match_registers(r);
448 });
449 }
450
451 /// Check if an alarm has expired (call this from your interrupt handler)
452 /// Returns true if the alarm was active and has now expired
453 pub fn check_alarm_expired(&self, alarm: &Alarm) -> bool {
454 if alarm.active.load(Ordering::Acquire) {
455 alarm.active.store(false, Ordering::Release);
456
457 // Execute callback
458 if let Some(callback) = alarm.callback {
459 callback();
460 }
461
462 // Set flag
463 if let Some(flag) = &alarm.flag {
464 flag.store(true, Ordering::Release);
465 }
466
467 true
468 } else {
469 false
470 }
471 }
472}
473
474/// Read current EVTIMER (Gray-coded) and convert to binary ticks.
475#[inline(always)]
476fn now_ticks_read() -> u64 {
477 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
478
479 // Read high then low to minimize incoherent snapshots
480 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64);
481 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64;
482 // Combine and convert from Gray code to binary
483 let gray = lo | (hi << EVTIMER_HI_SHIFT);
484 gray_to_bin(gray)
485}
486
487// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance
488pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType {
489 fn ptr() -> *const Regs;
490}
491
492#[cfg(not(feature = "time"))]
493impl Instance for crate::peripherals::OSTIMER0 {
494 #[inline(always)]
495 fn ptr() -> *const Regs {
496 pac::Ostimer0::ptr()
497 }
498}
499
500#[inline(always)]
501fn bin_to_gray(x: u64) -> u64 {
502 x ^ (x >> 1)
503}
504
505#[inline(always)]
506fn gray_to_bin(gray: u64) -> u64 {
507 // More efficient iterative conversion using predefined shifts
508 let mut bin = gray;
509 for &shift in &GRAY_CONVERSION_SHIFTS {
510 bin ^= bin >> shift;
511 }
512 bin
513}
514
515#[cfg(feature = "time")]
516pub mod time_driver {
517 use core::sync::atomic::Ordering;
518 use core::task::Waker;
519
520 use embassy_sync::waitqueue::AtomicWaker;
521 use embassy_time_driver as etd;
522
523 use super::{
524 ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, EVTIMER_HI_MASK, EVTIMER_HI_SHIFT,
525 LOW_32_BIT_MASK, Regs, bin_to_gray, now_ticks_read,
526 };
527 use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
528 use crate::clocks::{PoweredClock, enable_and_reset};
529 use crate::pac;
530
531 #[allow(non_camel_case_types)]
532 pub(crate) struct _OSTIMER0_TIME_DRIVER {
533 _x: (),
534 }
535
536 // #[cfg(feature = "time")]
537 // impl_cc_gate!(_OSTIMER0_TIME_DRIVER, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
538
539 impl crate::clocks::Gate for _OSTIMER0_TIME_DRIVER {
540 type MrccPeriphConfig = crate::clocks::periph_helpers::OsTimerConfig;
541
542 #[inline]
543 unsafe fn enable_clock() {
544 let mrcc = unsafe { pac::Mrcc0::steal() };
545 mrcc.mrcc_glb_cc1().modify(|_, w| w.ostimer0().enabled());
546 }
547
548 #[inline]
549 unsafe fn disable_clock() {
550 let mrcc = unsafe { pac::Mrcc0::steal() };
551 mrcc.mrcc_glb_cc1().modify(|_r, w| w.ostimer0().disabled());
552 }
553
554 #[inline]
555 fn is_clock_enabled() -> bool {
556 let mrcc = unsafe { pac::Mrcc0::steal() };
557 mrcc.mrcc_glb_cc1().read().ostimer0().is_enabled()
558 }
559
560 #[inline]
561 unsafe fn release_reset() {
562 let mrcc = unsafe { pac::Mrcc0::steal() };
563 mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().enabled());
564 }
565
566 #[inline]
567 unsafe fn assert_reset() {
568 let mrcc = unsafe { pac::Mrcc0::steal() };
569 mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().disabled());
570 }
571
572 #[inline]
573 fn is_reset_released() -> bool {
574 let mrcc = unsafe { pac::Mrcc0::steal() };
575 mrcc.mrcc_glb_rst1().read().ostimer0().is_enabled()
576 }
577 }
578
579 pub struct Driver;
580 static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
581
582 impl etd::Driver for Driver {
583 fn now(&self) -> u64 {
584 // Use the hardware counter (frequency configured in init)
585 super::now_ticks_read()
586 }
587
588 fn schedule_wake(&self, timestamp: u64, waker: &Waker) {
589 let now = self.now();
590
591 // If timestamp is in the past or very close to now, wake immediately
592 if timestamp <= now {
593 waker.wake_by_ref();
594 return;
595 }
596
597 // Prevent scheduling too far in the future (beyond timer rollover)
598 // This prevents wraparound issues
599 let max_future = now + super::TIMER_MAX_VALUE;
600 if timestamp > max_future {
601 // For very long timeouts, wake immediately to avoid rollover issues
602 waker.wake_by_ref();
603 return;
604 }
605
606 // Register the waker first so any immediate wake below is observed by the executor.
607 TIMER_WAKER.register(waker);
608
609 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
610
611 critical_section::with(|_| {
612 // Mask INTENA and clear flag
613 r.osevent_ctrl()
614 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
615
616 // Read back to ensure W1C took effect on hardware
617 let _ = r.osevent_ctrl().read().ostimer_intrflag().bit();
618
619 if !super::wait_for_match_write_ready(r) {
620 super::prime_match_registers(r);
621
622 if !super::wait_for_match_write_ready(r) {
623 // If we can't safely program MATCH, wake immediately and leave INTENA masked.
624 waker.wake_by_ref();
625 return;
626 }
627 }
628
629 // Program MATCH (Gray-coded). Write low then high, then fence.
630 let gray = bin_to_gray(timestamp);
631 let l = (gray & LOW_32_BIT_MASK) as u32;
632
633 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
634
635 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
636 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
637
638 if !super::wait_for_match_write_complete(r) {
639 waker.wake_by_ref();
640 return;
641 }
642
643 let now_after_program = super::now_ticks_read();
644 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
645 if now_after_program >= timestamp && !intrflag_set {
646 waker.wake_by_ref();
647 return;
648 }
649
650 // Enable peripheral interrupt
651 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
652 });
653 }
654 }
655
656 /// Install the global embassy-time driver and configure NVIC priority for OS_EVENT.
657 ///
658 /// # Parameters
659 /// * `priority` - Interrupt priority for the OSTIMER interrupt
660 /// * `frequency_hz` - Actual OSTIMER clock frequency in Hz (stored for future use)
661 ///
662 /// Note: The frequency parameter is currently accepted for API compatibility.
663 /// The embassy_time_driver macro handles driver registration automatically.
664 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) {
665 let _clock_freq = unsafe {
666 enable_and_reset::<_OSTIMER0_TIME_DRIVER>(&OsTimerConfig {
667 power: PoweredClock::AlwaysEnabled,
668 source: OstimerClockSel::Clk1M,
669 })
670 .expect("Enabling OsTimer clock should not fail")
671 };
672
673 // Mask/clear at peripheral and set default MATCH
674 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
675 super::prime_match_registers(r);
676
677 // Configure NVIC for timer operation
678 crate::interrupt::OS_EVENT.configure_for_timer(priority);
679
680 // Note: The embassy_time_driver macro automatically registers the driver
681 // The frequency parameter is accepted for future compatibility
682 let _ = frequency_hz; // Suppress unused parameter warning
683 }
684
685 // Export the global time driver expected by embassy-time
686 embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver);
687
688 /// To be called from the OS_EVENT IRQ.
689 pub fn on_interrupt() {
690 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
691
692 // Critical section to prevent races with schedule_wake
693 critical_section::with(|_| {
694 // Check if interrupt is actually pending and handle it atomically
695 if r.osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
696 // Clear flag and disable interrupt atomically
697 r.osevent_ctrl().write(|w| {
698 w.ostimer_intrflag()
699 .clear_bit_by_one() // Write-1-to-clear using safe helper
700 .ostimer_intena()
701 .clear_bit()
702 });
703
704 // Wake the waiting task
705 TIMER_WAKER.wake();
706
707 // Handle alarm callback if active and this interrupt is for the alarm
708 if ALARM_ACTIVE.load(Ordering::SeqCst) {
709 let current_time = now_ticks_read();
710 let target_time = unsafe { ALARM_TARGET_TIME };
711
712 // Check if current time is close to alarm target time (within 1000 ticks for timing variations)
713 if current_time >= target_time && current_time <= target_time + 1000 {
714 ALARM_ACTIVE.store(false, Ordering::SeqCst);
715 unsafe { ALARM_TARGET_TIME = 0 };
716
717 // Execute callback if set
718 unsafe {
719 if let Some(callback) = ALARM_CALLBACK {
720 callback();
721 }
722 }
723
724 // Set flag if provided
725 unsafe {
726 if let Some(flag) = ALARM_FLAG {
727 (*flag).store(true, Ordering::SeqCst);
728 }
729 }
730 }
731 }
732 }
733 });
734 }
735}
736
737#[cfg(feature = "time")]
738use crate::pac::interrupt;
739
740#[cfg(feature = "time")]
741#[allow(non_snake_case)]
742#[interrupt]
743fn OS_EVENT() {
744 time_driver::on_interrupt()
745}
diff --git a/embassy-mcxa/src/pins.rs b/embassy-mcxa/src/pins.rs
new file mode 100644
index 000000000..fdf1b0a86
--- /dev/null
+++ b/embassy-mcxa/src/pins.rs
@@ -0,0 +1,28 @@
1//! Pin configuration helpers (separate from peripheral drivers).
2use crate::pac;
3
4pub unsafe fn configure_adc_pins() {
5 // P1_10 = ADC1_A8
6 let port1 = &*pac::Port1::ptr();
7 port1.pcr10().write(|w| {
8 w.ps()
9 .ps0()
10 .pe()
11 .pe0()
12 .sre()
13 .sre0()
14 .ode()
15 .ode0()
16 .dse()
17 .dse0()
18 .mux()
19 .mux0()
20 .ibe()
21 .ibe0()
22 .inv()
23 .inv0()
24 .lk()
25 .lk0()
26 });
27 core::arch::asm!("dsb sy; isb sy");
28}
diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs
new file mode 100644
index 000000000..f975d9c9f
--- /dev/null
+++ b/embassy-mcxa/src/rtc.rs
@@ -0,0 +1,453 @@
1//! RTC DateTime driver.
2use core::marker::PhantomData;
3
4use embassy_hal_internal::{Peri, PeripheralType};
5use maitake_sync::WaitCell;
6
7use crate::clocks::with_clocks;
8use crate::interrupt::typelevel::{Handler, Interrupt};
9use crate::pac;
10use crate::pac::rtc0::cr::Um;
11
12type Regs = pac::rtc0::RegisterBlock;
13
14/// Global wait cell for alarm notifications
15static WAKER: WaitCell = WaitCell::new();
16
17/// RTC interrupt handler.
18pub struct InterruptHandler<I: Instance> {
19 _phantom: PhantomData<I>,
20}
21
22/// Trait for RTC peripheral instances
23pub trait Instance: PeripheralType {
24 type Interrupt: Interrupt;
25 fn ptr() -> *const Regs;
26}
27
28/// Token for RTC0
29pub type Rtc0 = crate::peripherals::RTC0;
30impl Instance for crate::peripherals::RTC0 {
31 type Interrupt = crate::interrupt::typelevel::RTC;
32 #[inline(always)]
33 fn ptr() -> *const Regs {
34 pac::Rtc0::ptr()
35 }
36}
37
38/// Number of days in a standard year
39const DAYS_IN_A_YEAR: u32 = 365;
40/// Number of seconds in a day
41const SECONDS_IN_A_DAY: u32 = 86400;
42/// Number of seconds in an hour
43const SECONDS_IN_A_HOUR: u32 = 3600;
44/// Number of seconds in a minute
45const SECONDS_IN_A_MINUTE: u32 = 60;
46/// Unix epoch start year
47const YEAR_RANGE_START: u16 = 1970;
48
49/// Date and time structure for RTC operations
50#[derive(Debug, Clone, Copy)]
51pub struct RtcDateTime {
52 pub year: u16,
53 pub month: u8,
54 pub day: u8,
55 pub hour: u8,
56 pub minute: u8,
57 pub second: u8,
58}
59#[derive(Copy, Clone)]
60pub struct RtcConfig {
61 #[allow(dead_code)]
62 wakeup_select: bool,
63 update_mode: Um,
64 #[allow(dead_code)]
65 supervisor_access: bool,
66 compensation_interval: u8,
67 compensation_time: u8,
68}
69
70/// RTC interrupt enable flags
71#[derive(Copy, Clone)]
72pub struct RtcInterruptEnable;
73impl RtcInterruptEnable {
74 pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;
75 pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;
76 pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;
77 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
78}
79
80/// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01)
81///
82/// # Arguments
83///
84/// * `datetime` - The date and time to convert
85///
86/// # Returns
87///
88/// Unix timestamp as u32
89///
90/// # Note
91///
92/// This function handles leap years correctly.
93pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
94 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
95
96 let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;
97 seconds += (datetime.year as u32 / 4) - (1970 / 4);
98 seconds += month_days[datetime.month as usize] as u32;
99 seconds += datetime.day as u32 - 1;
100
101 if (datetime.year & 3 == 0) && (datetime.month <= 2) {
102 seconds -= 1;
103 }
104
105 seconds = seconds * SECONDS_IN_A_DAY
106 + (datetime.hour as u32 * SECONDS_IN_A_HOUR)
107 + (datetime.minute as u32 * SECONDS_IN_A_MINUTE)
108 + datetime.second as u32;
109
110 seconds
111}
112
113/// Converts Unix timestamp to DateTime structure
114///
115/// # Arguments
116///
117/// * `seconds` - Unix timestamp (seconds since 1970-01-01)
118///
119/// # Returns
120///
121/// RtcDateTime structure with the converted date and time
122///
123/// # Note
124///
125/// This function handles leap years correctly.
126pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
127 let mut seconds_remaining = seconds;
128 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
129 seconds_remaining %= SECONDS_IN_A_DAY;
130
131 let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;
132 seconds_remaining %= SECONDS_IN_A_HOUR;
133 let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;
134 let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;
135
136 let mut year = YEAR_RANGE_START;
137 let mut days_in_year = DAYS_IN_A_YEAR;
138
139 while days > days_in_year {
140 days -= days_in_year;
141 year += 1;
142
143 days_in_year = if year.is_multiple_of(4) {
144 DAYS_IN_A_YEAR + 1
145 } else {
146 DAYS_IN_A_YEAR
147 };
148 }
149
150 let days_per_month = [
151 31,
152 if year.is_multiple_of(4) { 29 } else { 28 },
153 31,
154 30,
155 31,
156 30,
157 31,
158 31,
159 30,
160 31,
161 30,
162 31,
163 ];
164
165 let mut month = 1;
166 for (m, month_days) in days_per_month.iter().enumerate() {
167 let m = m + 1;
168 if days <= *month_days as u32 {
169 month = m;
170 break;
171 } else {
172 days -= *month_days as u32;
173 }
174 }
175
176 let day = days as u8;
177
178 RtcDateTime {
179 year,
180 month: month as u8,
181 day,
182 hour,
183 minute,
184 second,
185 }
186}
187
188/// Returns default RTC configuration
189///
190/// # Returns
191///
192/// RtcConfig with sensible default values:
193/// - No wakeup selection
194/// - Update mode 0 (immediate updates)
195/// - No supervisor access restriction
196/// - No compensation
197pub fn get_default_config() -> RtcConfig {
198 RtcConfig {
199 wakeup_select: false,
200 update_mode: Um::Um0,
201 supervisor_access: false,
202 compensation_interval: 0,
203 compensation_time: 0,
204 }
205}
206/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
207pub struct Rtc<'a, I: Instance> {
208 _inst: core::marker::PhantomData<&'a mut I>,
209}
210
211impl<'a, I: Instance> Rtc<'a, I> {
212 /// Create a new instance of the real time clock.
213 pub fn new(
214 _inst: Peri<'a, I>,
215 _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a,
216 config: RtcConfig,
217 ) -> Self {
218 let rtc = unsafe { &*I::ptr() };
219
220 // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock
221 // on the vsys domain is active
222 let clocks = with_clocks(|c| c.clk_16k_vsys.clone());
223 match clocks {
224 None => panic!("Clocks have not been initialized"),
225 Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"),
226 Some(Some(_)) => {}
227 }
228
229 // RTC reset
230 rtc.cr().modify(|_, w| w.swr().set_bit());
231 rtc.cr().modify(|_, w| w.swr().clear_bit());
232 rtc.tsr().write(|w| unsafe { w.bits(1) });
233
234 rtc.cr().modify(|_, w| w.um().variant(config.update_mode));
235
236 rtc.tcr().modify(|_, w| unsafe {
237 w.cir()
238 .bits(config.compensation_interval)
239 .tcr()
240 .bits(config.compensation_time)
241 });
242
243 // Enable RTC interrupt
244 I::Interrupt::unpend();
245 unsafe { I::Interrupt::enable() };
246
247 Self {
248 _inst: core::marker::PhantomData,
249 }
250 }
251
252 /// Set the current date and time
253 ///
254 /// # Arguments
255 ///
256 /// * `datetime` - The date and time to set
257 ///
258 /// # Note
259 ///
260 /// The datetime is converted to Unix timestamp and written to the time seconds register.
261 pub fn set_datetime(&self, datetime: RtcDateTime) {
262 let rtc = unsafe { &*I::ptr() };
263 let seconds = convert_datetime_to_seconds(&datetime);
264 rtc.tsr().write(|w| unsafe { w.bits(seconds) });
265 }
266
267 /// Get the current date and time
268 ///
269 /// # Returns
270 ///
271 /// Current date and time as RtcDateTime
272 ///
273 /// # Note
274 ///
275 /// Reads the current Unix timestamp from the time seconds register and converts it.
276 pub fn get_datetime(&self) -> RtcDateTime {
277 let rtc = unsafe { &*I::ptr() };
278 let seconds = rtc.tsr().read().bits();
279 convert_seconds_to_datetime(seconds)
280 }
281
282 /// Set the alarm date and time
283 ///
284 /// # Arguments
285 ///
286 /// * `alarm` - The date and time when the alarm should trigger
287 ///
288 /// # Note
289 ///
290 /// This function:
291 /// - Clears any existing alarm by writing 0 to the alarm register
292 /// - Waits for the clear operation to complete
293 /// - Sets the new alarm time
294 /// - Waits for the write operation to complete
295 /// - Uses timeouts to prevent infinite loops
296 /// - Enables the alarm interrupt after setting
297 pub fn set_alarm(&self, alarm: RtcDateTime) {
298 let rtc = unsafe { &*I::ptr() };
299 let seconds = convert_datetime_to_seconds(&alarm);
300
301 rtc.tar().write(|w| unsafe { w.bits(0) });
302 let mut timeout = 10000;
303 while rtc.tar().read().bits() != 0 && timeout > 0 {
304 timeout -= 1;
305 }
306
307 rtc.tar().write(|w| unsafe { w.bits(seconds) });
308
309 let mut timeout = 10000;
310 while rtc.tar().read().bits() != seconds && timeout > 0 {
311 timeout -= 1;
312 }
313
314 self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
315 }
316
317 /// Get the current alarm date and time
318 ///
319 /// # Returns
320 ///
321 /// Alarm date and time as RtcDateTime
322 ///
323 /// # Note
324 ///
325 /// Reads the alarm timestamp from the time alarm register and converts it.
326 pub fn get_alarm(&self) -> RtcDateTime {
327 let rtc = unsafe { &*I::ptr() };
328 let alarm_seconds = rtc.tar().read().bits();
329 convert_seconds_to_datetime(alarm_seconds)
330 }
331
332 /// Start the RTC time counter
333 ///
334 /// # Note
335 ///
336 /// Sets the Time Counter Enable (TCE) bit in the status register.
337 pub fn start(&self) {
338 let rtc = unsafe { &*I::ptr() };
339 rtc.sr().modify(|_, w| w.tce().set_bit());
340 }
341
342 /// Stop the RTC time counter
343 ///
344 /// # Note
345 ///
346 /// Clears the Time Counter Enable (TCE) bit in the status register.
347 pub fn stop(&self) {
348 let rtc = unsafe { &*I::ptr() };
349 rtc.sr().modify(|_, w| w.tce().clear_bit());
350 }
351
352 /// Enable specific RTC interrupts
353 ///
354 /// # Arguments
355 ///
356 /// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants)
357 ///
358 /// # Note
359 ///
360 /// This function enables the specified interrupt types and resets the alarm occurred flag.
361 /// Available interrupts:
362 /// - Time Invalid Interrupt
363 /// - Time Overflow Interrupt
364 /// - Alarm Interrupt
365 /// - Seconds Interrupt
366 pub fn set_interrupt(&self, mask: u32) {
367 let rtc = unsafe { &*I::ptr() };
368
369 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
370 rtc.ier().modify(|_, w| w.tiie().tiie_1());
371 }
372 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
373 rtc.ier().modify(|_, w| w.toie().toie_1());
374 }
375 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
376 rtc.ier().modify(|_, w| w.taie().taie_1());
377 }
378 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
379 rtc.ier().modify(|_, w| w.tsie().tsie_1());
380 }
381 }
382
383 /// Disable specific RTC interrupts
384 ///
385 /// # Arguments
386 ///
387 /// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants)
388 ///
389 /// # Note
390 ///
391 /// This function disables the specified interrupt types.
392 pub fn disable_interrupt(&self, mask: u32) {
393 let rtc = unsafe { &*I::ptr() };
394
395 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
396 rtc.ier().modify(|_, w| w.tiie().tiie_0());
397 }
398 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
399 rtc.ier().modify(|_, w| w.toie().toie_0());
400 }
401 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
402 rtc.ier().modify(|_, w| w.taie().taie_0());
403 }
404 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
405 rtc.ier().modify(|_, w| w.tsie().tsie_0());
406 }
407 }
408
409 /// Clear the alarm interrupt flag
410 ///
411 /// # Note
412 ///
413 /// This function clears the Time Alarm Interrupt Enable bit.
414 pub fn clear_alarm_flag(&self) {
415 let rtc = unsafe { &*I::ptr() };
416 rtc.ier().modify(|_, w| w.taie().clear_bit());
417 }
418
419 /// Wait for an RTC alarm to trigger.
420 ///
421 /// # Arguments
422 ///
423 /// * `alarm` - The date and time when the alarm should trigger
424 /// This function will wait until the RTC alarm is triggered.
425 /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.
426 pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) {
427 let wait = WAKER.subscribe().await;
428
429 self.set_alarm(alarm);
430 self.start();
431
432 // REVISIT: propagate error?
433 let _ = wait.await;
434
435 // Clear the interrupt and disable the alarm after waking up
436 self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
437 }
438}
439
440/// RTC interrupt handler
441///
442/// This struct implements the interrupt handler for RTC events.
443impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
444 unsafe fn on_interrupt() {
445 let rtc = &*pac::Rtc0::ptr();
446 // Check if this is actually a time alarm interrupt
447 let sr = rtc.sr().read();
448 if sr.taf().bit_is_set() {
449 rtc.ier().modify(|_, w| w.taie().clear_bit());
450 WAKER.wake();
451 }
452 }
453}
diff --git a/examples/mcxa/.cargo/config.toml b/examples/mcxa/.cargo/config.toml
new file mode 100644
index 000000000..aedc55b06
--- /dev/null
+++ b/examples/mcxa/.cargo/config.toml
@@ -0,0 +1,17 @@
1[target.thumbv8m.main-none-eabihf]
2runner = 'probe-rs run --chip MCXA276 --preverify --verify --protocol swd --speed 12000'
3
4rustflags = [
5 "-C", "linker=flip-link",
6 "-C", "link-arg=-Tlink.x",
7 "-C", "link-arg=-Tdefmt.x",
8 # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
9 # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
10 "-C", "link-arg=--nmagic",
11]
12
13[build]
14target = "thumbv8m.main-none-eabihf" # Cortex-M33
15
16[env]
17DEFMT_LOG = "trace"
diff --git a/examples/mcxa/.gitignore b/examples/mcxa/.gitignore
new file mode 100644
index 000000000..2f7896d1d
--- /dev/null
+++ b/examples/mcxa/.gitignore
@@ -0,0 +1 @@
target/
diff --git a/examples/mcxa/Cargo.toml b/examples/mcxa/Cargo.toml
new file mode 100644
index 000000000..4d0459f41
--- /dev/null
+++ b/examples/mcxa/Cargo.toml
@@ -0,0 +1,32 @@
1[package]
2name = "embassy-mcxa-examples"
3version = "0.1.0"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6publish = false
7
8[dependencies]
9cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
10cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] }
11critical-section = "1.2.0"
12defmt = "1.0"
13defmt-rtt = "1.0"
14embassy-embedded-hal = "0.5.0"
15embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false }
16embassy-mcxa = { path = "../../embassy-mcxa", features = ["defmt", "unstable-pac", "time"] }
17embassy-sync = "0.7.2"
18embassy-time = "0.5.0"
19embassy-time-driver = "0.2.1"
20embedded-io-async = "0.6.1"
21heapless = "0.9.2"
22panic-probe = { version = "1.0", features = ["print-defmt"] }
23tmp108 = "0.4.0"
24
25[profile.release]
26lto = true # better optimizations
27debug = 2 # enough information for defmt/rtt locations
28
29[package.metadata.embassy]
30build = [
31 { target = "thumbv8m.main-none-eabihf", artifact-dir = "out/examples/mcxa" }
32]
diff --git a/examples/mcxa/build.rs b/examples/mcxa/build.rs
new file mode 100644
index 000000000..f076bba9f
--- /dev/null
+++ b/examples/mcxa/build.rs
@@ -0,0 +1,20 @@
1use std::env;
2use std::fs::File;
3use std::io::Write;
4use std::path::PathBuf;
5
6fn main() {
7 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
8
9 // Generate memory.x - put "FLASH" at start of RAM, RAM after "FLASH"
10 // cortex-m-rt expects FLASH for code, RAM for data/bss/stack
11 // Both are in RAM, but separated to satisfy cortex-m-rt's expectations
12 // MCXA256 has 128KB RAM total
13 File::create(out.join("memory.x"))
14 .unwrap()
15 .write_all(include_bytes!("memory.x"))
16 .unwrap();
17
18 println!("cargo:rustc-link-search={}", out.display());
19 println!("cargo:rerun-if-changed=memory.x");
20}
diff --git a/examples/mcxa/memory.x b/examples/mcxa/memory.x
new file mode 100644
index 000000000..315ced58a
--- /dev/null
+++ b/examples/mcxa/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x00000000, LENGTH = 1M
4 RAM : ORIGIN = 0x20000000, LENGTH = 128K
5}
diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs
new file mode 100644
index 000000000..c88b1fe8d
--- /dev/null
+++ b/examples/mcxa/src/bin/adc_interrupt.rs
@@ -0,0 +1,84 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa_examples::init_adc_pins;
6use hal::adc::{LpadcConfig, TriggerPriorityPolicy};
7use hal::clocks::PoweredClock;
8use hal::clocks::periph_helpers::{AdcClockSel, Div4};
9use hal::pac::adc1::cfg::{Pwrsel, Refsel};
10use hal::pac::adc1::cmdl1::{Adch, Mode};
11use hal::pac::adc1::ctrl::CalAvgs;
12use hal::pac::adc1::tctrl::Tcmd;
13use hal::{InterruptExt, bind_interrupts};
14use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
15
16bind_interrupts!(struct Irqs {
17 ADC1 => hal::adc::AdcHandler;
18});
19
20#[used]
21#[no_mangle]
22static KEEP_ADC: unsafe extern "C" fn() = ADC1;
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let p = hal::init(hal::config::Config::default());
27
28 defmt::info!("ADC interrupt Example");
29
30 unsafe {
31 init_adc_pins();
32 }
33
34 let adc_config = LpadcConfig {
35 enable_in_doze_mode: true,
36 conversion_average_mode: CalAvgs::Average128,
37 enable_analog_preliminary: true,
38 power_up_delay: 0x80,
39 reference_voltage_source: Refsel::Option3,
40 power_level_mode: Pwrsel::Lowest,
41 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
42 enable_conv_pause: false,
43 conv_pause_delay: 0,
44 fifo_watermark: 0,
45 power: PoweredClock::NormalEnabledDeepSleepDisabled,
46 source: AdcClockSel::FroLfDiv,
47 div: Div4::no_div(),
48 };
49 let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config);
50
51 adc.do_offset_calibration();
52 adc.do_auto_calibration();
53
54 let mut conv_command_config = adc.get_default_conv_command_config();
55 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
56 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
57 adc.set_conv_command_config(1, &conv_command_config);
58
59 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
60 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
61 conv_trigger_config.enable_hardware_trigger = false;
62 adc.set_conv_trigger_config(0, &conv_trigger_config);
63
64 defmt::info!("ADC configuration done...");
65
66 adc.enable_interrupt(0x1);
67
68 unsafe {
69 hal::interrupt::ADC1.enable();
70 }
71
72 unsafe {
73 cortex_m::interrupt::enable();
74 }
75
76 loop {
77 adc.do_software_trigger(1);
78 while !adc.is_interrupt_triggered() {
79 // Wait until the interrupt is triggered
80 }
81 defmt::info!("*** ADC interrupt TRIGGERED! ***");
82 //TBD need to print the value
83 }
84}
diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs
new file mode 100644
index 000000000..07c50f224
--- /dev/null
+++ b/examples/mcxa/src/bin/adc_polling.rs
@@ -0,0 +1,68 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa_examples::init_adc_pins;
6use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy};
7use hal::clocks::PoweredClock;
8use hal::clocks::periph_helpers::{AdcClockSel, Div4};
9use hal::pac::adc1::cfg::{Pwrsel, Refsel};
10use hal::pac::adc1::cmdl1::{Adch, Mode};
11use hal::pac::adc1::ctrl::CalAvgs;
12use hal::pac::adc1::tctrl::Tcmd;
13use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
14
15const G_LPADC_RESULT_SHIFT: u32 = 0;
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let p = hal::init(hal::config::Config::default());
20
21 unsafe {
22 init_adc_pins();
23 }
24
25 defmt::info!("=== ADC polling Example ===");
26
27 let adc_config = LpadcConfig {
28 enable_in_doze_mode: true,
29 conversion_average_mode: CalAvgs::Average128,
30 enable_analog_preliminary: true,
31 power_up_delay: 0x80,
32 reference_voltage_source: Refsel::Option3,
33 power_level_mode: Pwrsel::Lowest,
34 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
35 enable_conv_pause: false,
36 conv_pause_delay: 0,
37 fifo_watermark: 0,
38 power: PoweredClock::NormalEnabledDeepSleepDisabled,
39 source: AdcClockSel::FroLfDiv,
40 div: Div4::no_div(),
41 };
42 let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config);
43
44 adc.do_offset_calibration();
45 adc.do_auto_calibration();
46
47 let mut conv_command_config = adc.get_default_conv_command_config();
48 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
49 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
50 adc.set_conv_command_config(1, &conv_command_config);
51
52 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
53 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
54 conv_trigger_config.enable_hardware_trigger = false;
55 adc.set_conv_trigger_config(0, &conv_trigger_config);
56
57 defmt::info!("=== ADC configuration done... ===");
58
59 loop {
60 adc.do_software_trigger(1);
61 let mut result: Option<ConvResult> = None;
62 while result.is_none() {
63 result = hal::adc::get_conv_result();
64 }
65 let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT;
66 defmt::info!("value: {=u16}", value);
67 }
68}
diff --git a/examples/mcxa/src/bin/blinky.rs b/examples/mcxa/src/bin/blinky.rs
new file mode 100644
index 000000000..dd08ec0d9
--- /dev/null
+++ b/examples/mcxa/src/bin/blinky.rs
@@ -0,0 +1,36 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{DriveStrength, Level, Output, SlewRate};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("Blink example");
14
15 let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);
16 let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast);
17 let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast);
18
19 loop {
20 defmt::info!("Toggle LEDs");
21
22 red.toggle();
23 Timer::after_millis(250).await;
24
25 red.toggle();
26 green.toggle();
27 Timer::after_millis(250).await;
28
29 green.toggle();
30 blue.toggle();
31 Timer::after_millis(250).await;
32 blue.toggle();
33
34 Timer::after_millis(250).await;
35 }
36}
diff --git a/examples/mcxa/src/bin/button.rs b/examples/mcxa/src/bin/button.rs
new file mode 100644
index 000000000..943edbb15
--- /dev/null
+++ b/examples/mcxa/src/bin/button.rs
@@ -0,0 +1,23 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{Input, Pull};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("Button example");
14
15 // This button is labeled "WAKEUP" on the FRDM-MCXA276
16 // The board already has a 10K pullup
17 let monitor = Input::new(p.P1_7, Pull::Disabled);
18
19 loop {
20 defmt::info!("Pin level is {:?}", monitor.get_level());
21 Timer::after_millis(1000).await;
22 }
23}
diff --git a/examples/mcxa/src/bin/button_async.rs b/examples/mcxa/src/bin/button_async.rs
new file mode 100644
index 000000000..6cc7b62cd
--- /dev/null
+++ b/examples/mcxa/src/bin/button_async.rs
@@ -0,0 +1,29 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{Input, Pull};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("GPIO interrupt example");
14
15 // This button is labeled "WAKEUP" on the FRDM-MCXA276
16 // The board already has a 10K pullup
17 let mut pin = Input::new(p.P1_7, Pull::Disabled);
18
19 let mut press_count = 0u32;
20
21 loop {
22 pin.wait_for_falling_edge().await;
23
24 press_count += 1;
25
26 defmt::info!("Button pressed! Count: {}", press_count);
27 Timer::after_millis(50).await;
28 }
29}
diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs
new file mode 100644
index 000000000..bfd963540
--- /dev/null
+++ b/examples/mcxa/src/bin/clkout.rs
@@ -0,0 +1,69 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4};
6use embassy_mcxa::clocks::PoweredClock;
7use embassy_mcxa::gpio::{DriveStrength, SlewRate};
8use embassy_mcxa::{Level, Output};
9use embassy_time::Timer;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12/// Demonstrate CLKOUT, using Pin P4.2
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = hal::init(hal::config::Config::default());
16 let mut pin = p.P4_2;
17 let mut clkout = p.CLKOUT;
18
19 loop {
20 defmt::info!("Set Low...");
21 let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);
22 Timer::after_millis(500).await;
23
24 defmt::info!("Set High...");
25 output.set_high();
26 Timer::after_millis(400).await;
27
28 defmt::info!("Set Low...");
29 output.set_low();
30 Timer::after_millis(500).await;
31
32 defmt::info!("16k...");
33 // Run Clock Out with the 16K clock
34 let _clock_out = ClockOut::new(
35 clkout.reborrow(),
36 pin.reborrow(),
37 Config {
38 sel: ClockOutSel::Clk16K,
39 div: Div4::no_div(),
40 level: PoweredClock::NormalEnabledDeepSleepDisabled,
41 },
42 )
43 .unwrap();
44
45 Timer::after_millis(3000).await;
46
47 defmt::info!("Set Low...");
48 drop(_clock_out);
49
50 let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);
51 Timer::after_millis(500).await;
52
53 // Run Clock Out with the 12M clock, divided by 3
54 defmt::info!("4M...");
55 let _clock_out = ClockOut::new(
56 clkout.reborrow(),
57 pin.reborrow(),
58 Config {
59 sel: ClockOutSel::Fro12M,
60 div: const { Div4::from_divisor(3).unwrap() },
61 level: PoweredClock::NormalEnabledDeepSleepDisabled,
62 },
63 )
64 .unwrap();
65
66 // Let it run for 3 seconds...
67 Timer::after_millis(3000).await;
68 }
69}
diff --git a/examples/mcxa/src/bin/hello.rs b/examples/mcxa/src/bin/hello.rs
new file mode 100644
index 000000000..e371d9413
--- /dev/null
+++ b/examples/mcxa/src/bin/hello.rs
@@ -0,0 +1,119 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use hal::lpuart::{Blocking, Config, Lpuart};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9/// Simple helper to write a byte as hex to UART
10fn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, byte: u8) {
11 const HEX_DIGITS: &[u8] = b"0123456789ABCDEF";
12 let _ = uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]);
13 let _ = uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]);
14}
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 let mut cfg = hal::config::Config::default();
19 cfg.clock_cfg.sirc.fro_12m_enabled = true;
20 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
21 let p = hal::init(cfg);
22
23 defmt::info!("boot");
24
25 // Create UART configuration
26 let config = Config {
27 baudrate_bps: 115_200,
28 ..Default::default()
29 };
30
31 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
32 let mut uart = Lpuart::new_blocking(
33 p.LPUART2, // Peripheral
34 p.P2_2, // TX pin
35 p.P2_3, // RX pin
36 config,
37 )
38 .unwrap();
39
40 // Print welcome message before any async delays to guarantee early console output
41 uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n");
42 uart.write_str_blocking("Available commands:\r\n");
43 uart.write_str_blocking(" help - Show this help\r\n");
44 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
45 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
46 uart.write_str_blocking("Type a command: ");
47
48 let mut buffer = [0u8; 64];
49 let mut buf_idx = 0;
50
51 loop {
52 // Read a byte from UART
53 let byte = uart.read_byte_blocking();
54
55 // Echo the character back
56 if byte == b'\r' || byte == b'\n' {
57 // Enter pressed - process command
58 uart.write_str_blocking("\r\n");
59
60 if buf_idx > 0 {
61 let command = &buffer[0..buf_idx];
62
63 if command == b"help" {
64 uart.write_str_blocking("Available commands:\r\n");
65 uart.write_str_blocking(" help - Show this help\r\n");
66 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
67 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
68 } else if command.starts_with(b"echo ") && command.len() > 5 {
69 uart.write_str_blocking("Echo: ");
70 uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or(""));
71 uart.write_str_blocking("\r\n");
72 } else if command.starts_with(b"hex ") && command.len() > 4 {
73 // Parse the byte value
74 let num_str = &command[4..];
75 if let Ok(num) = parse_u8(num_str) {
76 uart.write_str_blocking("Hex: 0x");
77 write_hex_byte(&mut uart, num);
78 uart.write_str_blocking("\r\n");
79 } else {
80 uart.write_str_blocking("Invalid number for hex command\r\n");
81 }
82 } else if !command.is_empty() {
83 uart.write_str_blocking("Unknown command: ");
84 uart.write_str_blocking(core::str::from_utf8(command).unwrap_or(""));
85 uart.write_str_blocking("\r\n");
86 }
87 }
88
89 // Reset buffer and prompt
90 buf_idx = 0;
91 uart.write_str_blocking("Type a command: ");
92 } else if byte == 8 || byte == 127 {
93 // Backspace
94 if buf_idx > 0 {
95 buf_idx -= 1;
96 uart.write_str_blocking("\x08 \x08"); // Erase character
97 }
98 } else if buf_idx < buffer.len() - 1 {
99 // Regular character
100 buffer[buf_idx] = byte;
101 buf_idx += 1;
102 let _ = uart.write_byte(byte);
103 }
104 }
105}
106
107/// Simple parser for u8 from ASCII bytes
108fn parse_u8(bytes: &[u8]) -> Result<u8, ()> {
109 let mut result = 0u8;
110 for &b in bytes {
111 if b.is_ascii_digit() {
112 result = result.checked_mul(10).ok_or(())?;
113 result = result.checked_add(b - b'0').ok_or(())?;
114 } else {
115 return Err(());
116 }
117 }
118 Ok(result)
119}
diff --git a/examples/mcxa/src/bin/i2c-async.rs b/examples/mcxa/src/bin/i2c-async.rs
new file mode 100644
index 000000000..edcfd5f22
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-async.rs
@@ -0,0 +1,39 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::bind_interrupts;
7use hal::clocks::config::Div8;
8use hal::config::Config;
9use hal::i2c::InterruptHandler;
10use hal::i2c::controller::{self, I2c, Speed};
11use hal::peripherals::LPI2C3;
12use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
13
14bind_interrupts!(
15 struct Irqs {
16 LPI2C3 => InterruptHandler<LPI2C3>;
17 }
18);
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let mut config = Config::default();
23 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
24
25 let p = hal::init(config);
26
27 defmt::info!("I2C example");
28
29 let mut config = controller::Config::default();
30 config.speed = Speed::Standard;
31 let mut i2c = I2c::new_async(p.LPI2C3, p.P3_27, p.P3_28, Irqs, config).unwrap();
32 let mut buf = [0u8; 2];
33
34 loop {
35 i2c.async_write_read(0x48, &[0x00], &mut buf).await.unwrap();
36 defmt::info!("Buffer: {:02x}", buf);
37 Timer::after_secs(1).await;
38 }
39}
diff --git a/examples/mcxa/src/bin/i2c-blocking.rs b/examples/mcxa/src/bin/i2c-blocking.rs
new file mode 100644
index 000000000..0f6c8cbae
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-blocking.rs
@@ -0,0 +1,31 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::clocks::config::Div8;
7use hal::config::Config;
8use hal::i2c::controller::{self, I2c, Speed};
9use tmp108::Tmp108;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let mut config = Config::default();
15 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
16
17 let p = hal::init(config);
18
19 defmt::info!("I2C example");
20
21 let mut config = controller::Config::default();
22 config.speed = Speed::Standard;
23 let i2c = I2c::new_blocking(p.LPI2C3, p.P3_27, p.P3_28, config).unwrap();
24 let mut tmp = Tmp108::new_with_a0_gnd(i2c);
25
26 loop {
27 let temperature = tmp.temperature().unwrap();
28 defmt::info!("Temperature: {}C", temperature);
29 Timer::after_secs(1).await;
30 }
31}
diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs
new file mode 100644
index 000000000..0197f9b1d
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs
@@ -0,0 +1,41 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::Input;
6use embassy_mcxa::gpio::Pull;
7use embassy_time::Timer;
8use hal::clocks::config::Div8;
9use hal::config::Config;
10use hal::i2c::controller::{self, I2c, Speed};
11use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let mut config = Config::default();
16 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
17
18 let p = hal::init(config);
19
20 defmt::info!("I2C example");
21
22 let mut config = controller::Config::default();
23 config.speed = Speed::Standard;
24
25 // Note: P0_2 is connected to P1_8 on the FRDM_MCXA276 via a resistor, and
26 // defaults to SWO on the debug peripheral. Explicitly make it a high-z
27 // input.
28 let _pin = Input::new(p.P0_2, Pull::Disabled);
29 let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap();
30
31 for addr in 0x01..=0x7f {
32 let result = i2c.blocking_write(addr, &[]);
33 if result.is_ok() {
34 defmt::info!("Device found at addr {:02x}", addr);
35 }
36 }
37
38 loop {
39 Timer::after_secs(10).await;
40 }
41}
diff --git a/examples/mcxa/src/bin/lpuart_buffered.rs b/examples/mcxa/src/bin/lpuart_buffered.rs
new file mode 100644
index 000000000..47b56b7c7
--- /dev/null
+++ b/examples/mcxa/src/bin/lpuart_buffered.rs
@@ -0,0 +1,62 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use embassy_mcxa::lpuart::Config;
7use embassy_mcxa::lpuart::buffered::BufferedLpuart;
8use embassy_mcxa::{bind_interrupts, lpuart};
9use embedded_io_async::Write;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver
13bind_interrupts!(struct Irqs {
14 LPUART2 => lpuart::buffered::BufferedInterruptHandler::<hal::peripherals::LPUART2>;
15});
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let mut cfg = hal::config::Config::default();
20 cfg.clock_cfg.sirc.fro_12m_enabled = true;
21 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
22 let p = hal::init(cfg);
23
24 // Configure NVIC for LPUART2
25 hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3);
26
27 // UART configuration (enable both TX and RX)
28 let config = Config {
29 baudrate_bps: 115_200,
30 rx_fifo_watermark: 0,
31 tx_fifo_watermark: 0,
32 ..Default::default()
33 };
34
35 let mut tx_buf = [0u8; 256];
36 let mut rx_buf = [0u8; 256];
37
38 // Create a buffered LPUART2 instance with both TX and RX
39 let mut uart = BufferedLpuart::new(
40 p.LPUART2,
41 p.P2_2, // TX pin
42 p.P2_3, // RX pin
43 Irqs,
44 &mut tx_buf,
45 &mut rx_buf,
46 config,
47 )
48 .unwrap();
49
50 // Split into TX and RX parts
51 let (tx, rx) = uart.split_ref();
52
53 tx.write(b"Hello buffered LPUART.\r\n").await.unwrap();
54 tx.write(b"Type characters to echo them back.\r\n").await.unwrap();
55
56 // Echo loop
57 let mut buf = [0u8; 4];
58 loop {
59 let used = rx.read(&mut buf).await.unwrap();
60 tx.write_all(&buf[..used]).await.unwrap();
61 }
62}
diff --git a/examples/mcxa/src/bin/lpuart_polling.rs b/examples/mcxa/src/bin/lpuart_polling.rs
new file mode 100644
index 000000000..b80668834
--- /dev/null
+++ b/examples/mcxa/src/bin/lpuart_polling.rs
@@ -0,0 +1,47 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
7
8use crate::hal::lpuart::{Config, Lpuart};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let mut cfg = hal::config::Config::default();
13 cfg.clock_cfg.sirc.fro_12m_enabled = true;
14 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
15 let p = hal::init(cfg);
16
17 defmt::info!("boot");
18
19 // Create UART configuration
20 let config = Config {
21 baudrate_bps: 115_200,
22 ..Default::default()
23 };
24
25 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
26 let lpuart = Lpuart::new_blocking(
27 p.LPUART2, // Peripheral
28 p.P2_2, // TX pin
29 p.P2_3, // RX pin
30 config,
31 )
32 .unwrap();
33
34 // Split into separate TX and RX parts
35 let (mut tx, mut rx) = lpuart.split();
36
37 // Write hello messages
38 tx.blocking_write(b"Hello world.\r\n").unwrap();
39 tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap();
40
41 // Echo loop
42 loop {
43 let mut buf = [0u8; 1];
44 rx.blocking_read(&mut buf).unwrap();
45 tx.blocking_write(&buf).unwrap();
46 }
47}
diff --git a/examples/mcxa/src/bin/rtc_alarm.rs b/examples/mcxa/src/bin/rtc_alarm.rs
new file mode 100644
index 000000000..fe355888b
--- /dev/null
+++ b/examples/mcxa/src/bin/rtc_alarm.rs
@@ -0,0 +1,47 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::bind_interrupts;
6use hal::rtc::{InterruptHandler, Rtc, RtcDateTime};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9bind_interrupts!(struct Irqs {
10 RTC => InterruptHandler<hal::rtc::Rtc0>;
11});
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = hal::init(hal::config::Config::default());
16
17 defmt::info!("=== RTC Alarm Example ===");
18
19 let rtc_config = hal::rtc::get_default_config();
20
21 let mut rtc = Rtc::new(p.RTC0, Irqs, rtc_config);
22
23 let now = RtcDateTime {
24 year: 2025,
25 month: 10,
26 day: 15,
27 hour: 14,
28 minute: 30,
29 second: 0,
30 };
31
32 rtc.stop();
33
34 defmt::info!("Time set to: 2025-10-15 14:30:00");
35 rtc.set_datetime(now);
36
37 let mut alarm = now;
38 alarm.second += 10;
39
40 defmt::info!("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)");
41 defmt::info!("RTC started, waiting for alarm...");
42
43 rtc.wait_for_alarm(alarm).await;
44 defmt::info!("*** ALARM TRIGGERED! ***");
45
46 defmt::info!("Example complete - Test PASSED!");
47}
diff --git a/examples/mcxa/src/lib.rs b/examples/mcxa/src/lib.rs
new file mode 100644
index 000000000..2573a6adc
--- /dev/null
+++ b/examples/mcxa/src/lib.rs
@@ -0,0 +1,16 @@
1#![no_std]
2#![allow(clippy::missing_safety_doc)]
3
4//! Shared board-specific helpers for the FRDM-MCXA276 examples.
5//! These live with the examples so the HAL stays generic.
6
7use hal::{clocks, pins};
8use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
9
10/// Initialize clocks and pin muxing for ADC.
11pub unsafe fn init_adc_pins() {
12 // NOTE: Lpuart has been updated to properly enable + reset its own clocks.
13 // GPIO has not.
14 _ = clocks::enable_and_reset::<hal::peripherals::PORT1>(&clocks::periph_helpers::NoConfig);
15 pins::configure_adc_pins();
16}