aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorFelipe Balbi <[email protected]>2025-11-07 11:00:15 -0800
committerGitHub <[email protected]>2025-11-07 11:00:15 -0800
commit5632acec18cc5906b1625a8facf530db56c73300 (patch)
treeabf2897f4b2f9814069c64611896be2d42cf2ce8 /examples
parent47e383545f4aac3bfaec0563429cc721540e665a (diff)
parent9590d94ee9ba016f65a13100c429fc56ffe58e40 (diff)
Merge pull request #1 from bogdan-petru/import/mcxa276-initial
feat(mcxa276): initial HAL import
Diffstat (limited to 'examples')
-rw-r--r--examples/adc_interrupt.rs89
-rw-r--r--examples/adc_polling.rs79
-rw-r--r--examples/blink.rs84
-rw-r--r--examples/common/mod.rs45
-rw-r--r--examples/hello.rs114
-rw-r--r--examples/lpuart_buffered.rs77
-rw-r--r--examples/lpuart_polling.rs53
-rw-r--r--examples/ostimer_alarm.rs111
-rw-r--r--examples/ostimer_async.rs57
-rw-r--r--examples/ostimer_counter.rs128
-rw-r--r--examples/ostimer_race_test.rs396
-rw-r--r--examples/rtc_alarm.rs87
-rw-r--r--examples/uart_interrupt.rs69
13 files changed, 1389 insertions, 0 deletions
diff --git a/examples/adc_interrupt.rs b/examples/adc_interrupt.rs
new file mode 100644
index 000000000..f0df3196c
--- /dev/null
+++ b/examples/adc_interrupt.rs
@@ -0,0 +1,89 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use hal::adc::{LpadcConfig, TriggerPriorityPolicy};
6use hal::pac::adc1::cfg::{Pwrsel, Refsel};
7use hal::pac::adc1::cmdl1::{Adch, Mode};
8use hal::pac::adc1::ctrl::CalAvgs;
9use hal::pac::adc1::tctrl::Tcmd;
10use hal::uart;
11use {cortex_m, embassy_mcxa276 as hal};
12mod common;
13
14use hal::{bind_interrupts, InterruptExt};
15use {defmt_rtt as _, panic_probe as _};
16
17bind_interrupts!(struct Irqs {
18 ADC1 => hal::adc::AdcHandler;
19});
20
21#[used]
22#[no_mangle]
23static KEEP_ADC: unsafe extern "C" fn() = ADC1;
24
25#[embassy_executor::main]
26async fn main(_spawner: Spawner) {
27 let p = hal::init(hal::config::Config::default());
28
29 unsafe {
30 common::init_uart2(hal::pac());
31 }
32
33 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
34 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
35
36 uart.write_str_blocking("\r\n=== ADC interrupt Example ===\r\n");
37
38 unsafe {
39 common::init_adc(hal::pac());
40 }
41
42 let adc_config = LpadcConfig {
43 enable_in_doze_mode: true,
44 conversion_average_mode: CalAvgs::Average128,
45 enable_analog_preliminary: true,
46 power_up_delay: 0x80,
47 reference_voltage_source: Refsel::Option3,
48 power_level_mode: Pwrsel::Lowest,
49 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
50 enable_conv_pause: false,
51 conv_pause_delay: 0,
52 fifo_watermark: 0,
53 };
54 let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config);
55
56 adc.do_offset_calibration();
57 adc.do_auto_calibration();
58
59 let mut conv_command_config = adc.get_default_conv_command_config();
60 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
61 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
62 adc.set_conv_command_config(1, &conv_command_config);
63
64 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
65 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
66 conv_trigger_config.enable_hardware_trigger = false;
67 adc.set_conv_trigger_config(0, &conv_trigger_config);
68
69 uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n");
70
71 adc.enable_interrupt(0x1);
72
73 unsafe {
74 hal::interrupt::ADC1.enable();
75 }
76
77 unsafe {
78 cortex_m::interrupt::enable();
79 }
80
81 loop {
82 adc.do_software_trigger(1);
83 while !adc.is_interrupt_triggered() {
84 // Wait until the interrupt is triggered
85 }
86 uart.write_str_blocking("\r\n*** ADC interrupt TRIGGERED! ***\r\n");
87 //TBD need to print the value
88 }
89}
diff --git a/examples/adc_polling.rs b/examples/adc_polling.rs
new file mode 100644
index 000000000..561500d2d
--- /dev/null
+++ b/examples/adc_polling.rs
@@ -0,0 +1,79 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy};
7use hal::pac::adc1::cfg::{Pwrsel, Refsel};
8use hal::pac::adc1::cmdl1::{Adch, Mode};
9use hal::pac::adc1::ctrl::CalAvgs;
10use hal::pac::adc1::tctrl::Tcmd;
11use hal::uart;
12
13mod common;
14
15use core::fmt::Write;
16
17use heapless::String;
18use {defmt_rtt as _, panic_probe as _};
19
20const G_LPADC_RESULT_SHIFT: u32 = 0;
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 let p = hal::init(hal::config::Config::default());
25
26 unsafe {
27 common::init_uart2(hal::pac());
28 }
29
30 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
31 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
32
33 uart.write_str_blocking("\r\n=== ADC polling Example ===\r\n");
34
35 unsafe {
36 common::init_adc(hal::pac());
37 }
38
39 let adc_config = LpadcConfig {
40 enable_in_doze_mode: true,
41 conversion_average_mode: CalAvgs::Average128,
42 enable_analog_preliminary: true,
43 power_up_delay: 0x80,
44 reference_voltage_source: Refsel::Option3,
45 power_level_mode: Pwrsel::Lowest,
46 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
47 enable_conv_pause: false,
48 conv_pause_delay: 0,
49 fifo_watermark: 0,
50 };
51 let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config);
52
53 adc.do_offset_calibration();
54 adc.do_auto_calibration();
55
56 let mut conv_command_config = adc.get_default_conv_command_config();
57 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
58 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
59 adc.set_conv_command_config(1, &conv_command_config);
60
61 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
62 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
63 conv_trigger_config.enable_hardware_trigger = false;
64 adc.set_conv_trigger_config(0, &conv_trigger_config);
65
66 uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n");
67
68 loop {
69 adc.do_software_trigger(1);
70 let mut result: Option<ConvResult> = None;
71 while result.is_none() {
72 result = hal::adc::get_conv_result();
73 }
74 let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT;
75 let mut buf: String<16> = String::new(); // adjust size as needed
76 write!(buf, "\r\nvalue: {}\r\n", value).unwrap();
77 uart.write_str_blocking(&buf);
78 }
79}
diff --git a/examples/blink.rs b/examples/blink.rs
new file mode 100644
index 000000000..564353d5c
--- /dev/null
+++ b/examples/blink.rs
@@ -0,0 +1,84 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use embassy_time::{Duration, Timer};
7use hal::gpio::pins::PIO3_18;
8use hal::gpio::{Level, Output};
9
10mod common;
11
12use embassy_mcxa276::bind_interrupts;
13
14// Bind only OS_EVENT for timer interrupts
15bind_interrupts!(struct Irqs {
16 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
17});
18
19#[used]
20#[no_mangle]
21static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let _p = hal::init(hal::config::Config::default());
26
27 // Board-style init: enable LED GPIO/PORT clocks used by blink
28 unsafe {
29 common::init_led(hal::pac());
30 }
31 // Initialize OSTIMER for async timing
32 unsafe {
33 common::init_ostimer0(hal::pac());
34 }
35
36 // Initialize embassy-time global driver backed by OSTIMER0
37 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
38
39 // Configure LED pin for GPIO mode
40 PIO3_18::set_mux_gpio();
41
42 let mut led = Output::new(PIO3_18::degrade(), Level::High);
43
44 // Complex blinking pattern: SOS in Morse code
45 // S: ... (3 short)
46 // O: --- (3 long)
47 // S: ... (3 short)
48 // With pauses between letters and words
49
50 loop {
51 // S: three short blinks
52 for _ in 0..3 {
53 led.set_low();
54 Timer::after(Duration::from_millis(150)).await;
55 led.set_high();
56 Timer::after(Duration::from_millis(150)).await;
57 }
58
59 // Pause between letters
60 Timer::after(Duration::from_millis(300)).await;
61
62 // O: three long blinks
63 for _ in 0..3 {
64 led.set_low();
65 Timer::after(Duration::from_millis(450)).await;
66 led.set_high();
67 Timer::after(Duration::from_millis(150)).await;
68 }
69
70 // Pause between letters
71 Timer::after(Duration::from_millis(300)).await;
72
73 // S: three short blinks
74 for _ in 0..3 {
75 led.set_low();
76 Timer::after(Duration::from_millis(150)).await;
77 led.set_high();
78 Timer::after(Duration::from_millis(150)).await;
79 }
80
81 // Long pause between words (SOS repeats)
82 Timer::after(Duration::from_millis(1000)).await;
83 }
84}
diff --git a/examples/common/mod.rs b/examples/common/mod.rs
new file mode 100644
index 000000000..7ada4c456
--- /dev/null
+++ b/examples/common/mod.rs
@@ -0,0 +1,45 @@
1//! Shared board-specific helpers for the FRDM-MCXA276 examples.
2//! These live with the examples so the HAL stays generic.
3
4use embassy_mcxa276 as hal;
5use hal::{clocks, pins, reset};
6
7/// Initialize clocks and pin muxing for UART2 debug console.
8/// Safe to call multiple times; writes are idempotent for our use.
9#[allow(dead_code)]
10pub unsafe fn init_uart2(p: &hal::pac::Peripherals) {
11 clocks::ensure_frolf_running(p);
12 clocks::enable_uart2_port2(p);
13 reset::release_reset_port2(p);
14 reset::release_reset_lpuart2(p);
15 pins::configure_uart2_pins_port2();
16 clocks::select_uart2_clock(p);
17}
18
19/// Initialize clocks for the LED GPIO/PORT used by the blink example.
20#[allow(dead_code)]
21pub unsafe fn init_led(p: &hal::pac::Peripherals) {
22 clocks::enable_led_port(p);
23 reset::release_reset_gpio3(p);
24 reset::release_reset_port3(p);
25}
26
27/// Initialize clocks for OSTIMER0 (1 MHz source).
28#[allow(dead_code)]
29pub unsafe fn init_ostimer0(p: &hal::pac::Peripherals) {
30 clocks::ensure_frolf_running(p);
31 clocks::enable_ostimer0(p);
32 reset::release_reset_ostimer0(p);
33 clocks::select_ostimer0_clock_1m(p);
34}
35
36/// Initialize clocks and pin muxing for ADC.
37#[allow(dead_code)]
38pub unsafe fn init_adc(p: &hal::pac::Peripherals) {
39 clocks::ensure_frolf_running(p);
40 clocks::enable_adc(p);
41 reset::release_reset_port1(p);
42 reset::release_reset_adc1(p);
43 pins::configure_adc_pins();
44 clocks::select_adc_clock(p);
45}
diff --git a/examples/hello.rs b/examples/hello.rs
new file mode 100644
index 000000000..e39adaced
--- /dev/null
+++ b/examples/hello.rs
@@ -0,0 +1,114 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use hal::uart;
7
8mod common;
9
10use {defmt_rtt as _, panic_probe as _};
11
12/// Simple helper to write a byte as hex to UART
13fn write_hex_byte(uart: &hal::uart::Uart<hal::uart::Lpuart2>, byte: u8) {
14 const HEX_DIGITS: &[u8] = b"0123456789ABCDEF";
15 uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]);
16 uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]);
17}
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 let p = hal::init(hal::config::Config::default());
22
23 defmt::info!("boot");
24
25 // Board-level init for UART2 clocks and pins.
26 unsafe {
27 common::init_uart2(hal::pac());
28 }
29
30 // Get UART source frequency from clock configuration
31 // Using hardcoded frequency for now - dynamic detection may have issues
32 let src = 12_000_000; // FRO_LF_DIV at 12MHz with DIV=0
33 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
34
35 // Print welcome message before any async delays to guarantee early console output
36 uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n");
37 uart.write_str_blocking("Available commands:\r\n");
38 uart.write_str_blocking(" help - Show this help\r\n");
39 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
40 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
41 uart.write_str_blocking("Type a command: ");
42
43 let mut buffer = [0u8; 64];
44 let mut buf_idx = 0;
45
46 loop {
47 // Read a byte from UART
48 let byte = uart.read_byte_blocking();
49
50 // Echo the character back
51 if byte == b'\r' || byte == b'\n' {
52 // Enter pressed - process command
53 uart.write_str_blocking("\r\n");
54
55 if buf_idx > 0 {
56 let command = &buffer[0..buf_idx];
57
58 if command == b"help" {
59 uart.write_str_blocking("Available commands:\r\n");
60 uart.write_str_blocking(" help - Show this help\r\n");
61 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
62 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
63 } else if command.starts_with(b"echo ") && command.len() > 5 {
64 uart.write_str_blocking("Echo: ");
65 uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or(""));
66 uart.write_str_blocking("\r\n");
67 } else if command.starts_with(b"hex ") && command.len() > 4 {
68 // Parse the byte value
69 let num_str = &command[4..];
70 if let Ok(num) = parse_u8(num_str) {
71 uart.write_str_blocking("Hex: 0x");
72 write_hex_byte(&uart, num);
73 uart.write_str_blocking("\r\n");
74 } else {
75 uart.write_str_blocking("Invalid number for hex command\r\n");
76 }
77 } else if command.len() > 0 {
78 uart.write_str_blocking("Unknown command: ");
79 uart.write_str_blocking(core::str::from_utf8(command).unwrap_or(""));
80 uart.write_str_blocking("\r\n");
81 }
82 }
83
84 // Reset buffer and prompt
85 buf_idx = 0;
86 uart.write_str_blocking("Type a command: ");
87 } else if byte == 8 || byte == 127 {
88 // Backspace
89 if buf_idx > 0 {
90 buf_idx -= 1;
91 uart.write_str_blocking("\x08 \x08"); // Erase character
92 }
93 } else if buf_idx < buffer.len() - 1 {
94 // Regular character
95 buffer[buf_idx] = byte;
96 buf_idx += 1;
97 uart.write_byte(byte);
98 }
99 }
100}
101
102/// Simple parser for u8 from ASCII bytes
103fn parse_u8(bytes: &[u8]) -> Result<u8, ()> {
104 let mut result = 0u8;
105 for &b in bytes {
106 if b >= b'0' && b <= b'9' {
107 result = result.checked_mul(10).ok_or(())?;
108 result = result.checked_add(b - b'0').ok_or(())?;
109 } else {
110 return Err(());
111 }
112 }
113 Ok(result)
114}
diff --git a/examples/lpuart_buffered.rs b/examples/lpuart_buffered.rs
new file mode 100644
index 000000000..30ba3f333
--- /dev/null
+++ b/examples/lpuart_buffered.rs
@@ -0,0 +1,77 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use embassy_mcxa276::interrupt::typelevel::Handler;
7use embassy_mcxa276::lpuart::buffered::BufferedLpuart;
8use embassy_mcxa276::{bind_interrupts, lpuart};
9use embedded_io_async::{Read, Write};
10
11mod common;
12
13// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver
14bind_interrupts!(struct Irqs {
15 LPUART2 => lpuart::buffered::BufferedInterruptHandler::<lpuart::lib::peripherals::LPUART2>;
16});
17
18// Wrapper function for the interrupt handler
19unsafe extern "C" fn lpuart2_handler() {
20 lpuart::buffered::BufferedInterruptHandler::<lpuart::lib::peripherals::LPUART2>::on_interrupt();
21}
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let _p = hal::init(hal::config::Config::default());
26 let p2 = lpuart::lib::init();
27
28 unsafe {
29 hal::interrupt::install_irq_handler(mcxa276_pac::Interrupt::LPUART2, lpuart2_handler);
30 }
31
32 // Configure NVIC for LPUART2
33 hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3);
34
35 unsafe {
36 common::init_uart2(hal::pac());
37 common::init_ostimer0(hal::pac());
38 }
39
40 // UART configuration (enable both TX and RX)
41 let config = lpuart::Config {
42 baudrate_bps: 115_200,
43 enable_tx: true,
44 enable_rx: true,
45 rx_fifo_watermark: 0,
46 tx_fifo_watermark: 0,
47 ..Default::default()
48 };
49
50 let mut tx_buf = [0u8; 256];
51 let mut rx_buf = [0u8; 256];
52
53 // Create a buffered LPUART2 instance with both TX and RX
54 let mut uart = BufferedLpuart::new(
55 p2.LPUART2,
56 p2.PIO2_2, // TX pin
57 p2.PIO2_3, // RX pin
58 Irqs,
59 &mut tx_buf,
60 &mut rx_buf,
61 config,
62 )
63 .unwrap();
64
65 // Split into TX and RX parts
66 let (tx, rx) = uart.split_ref();
67
68 tx.write(b"Hello buffered LPUART.\r\n").await.unwrap();
69 tx.write(b"Type characters to echo them back.\r\n").await.unwrap();
70
71 // Echo loop
72 let mut buf = [0u8; 4];
73 loop {
74 rx.read_exact(&mut buf[..]).await.unwrap();
75 tx.write_all(&buf[..]).await.unwrap();
76 }
77}
diff --git a/examples/lpuart_polling.rs b/examples/lpuart_polling.rs
new file mode 100644
index 000000000..067c7eb53
--- /dev/null
+++ b/examples/lpuart_polling.rs
@@ -0,0 +1,53 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _};
6
7use crate::hal::lpuart::{lib, Config, Lpuart};
8
9mod common;
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let _p = hal::init(hal::config::Config::default());
14 let p2 = lib::init();
15
16 defmt::info!("boot");
17
18 // Board-level init for UART2 clocks and pins.
19 unsafe {
20 common::init_uart2(hal::pac());
21 }
22
23 // Create UART configuration
24 let config = Config {
25 baudrate_bps: 115_200,
26 enable_tx: true,
27 enable_rx: true,
28 ..Default::default()
29 };
30
31 // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX
32 let lpuart = Lpuart::new_blocking(
33 p2.LPUART2, // Peripheral
34 p2.PIO2_2, // TX pin
35 p2.PIO2_3, // RX pin
36 config,
37 )
38 .unwrap();
39
40 // Split into separate TX and RX parts
41 let (mut tx, mut rx) = lpuart.split();
42
43 // Write hello messages
44 tx.blocking_write(b"Hello world.\r\n").unwrap();
45 tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap();
46
47 // Echo loop
48 loop {
49 let mut buf = [0u8; 1];
50 rx.blocking_read(&mut buf).unwrap();
51 tx.blocking_write(&buf).unwrap();
52 }
53}
diff --git a/examples/ostimer_alarm.rs b/examples/ostimer_alarm.rs
new file mode 100644
index 000000000..78ca4bbc5
--- /dev/null
+++ b/examples/ostimer_alarm.rs
@@ -0,0 +1,111 @@
1#![no_std]
2#![no_main]
3
4use core::sync::atomic::{AtomicBool, Ordering};
5
6use embassy_executor::Spawner;
7use hal::uart;
8use {cortex_m, embassy_mcxa276 as hal};
9
10mod common;
11
12use embassy_mcxa276::bind_interrupts;
13use {defmt_rtt as _, panic_probe as _};
14
15// Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed.
16bind_interrupts!(struct Irqs {
17 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
18});
19
20#[used]
21#[no_mangle]
22static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
23
24// Global flag for alarm callback
25static ALARM_FLAG: AtomicBool = AtomicBool::new(false);
26
27// Alarm callback function
28fn alarm_callback() {
29 ALARM_FLAG.store(true, Ordering::Release);
30}
31
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = hal::init(hal::config::Config::default());
35
36 // Enable/clock OSTIMER0 and UART2 before touching their registers
37 unsafe {
38 common::init_ostimer0(hal::pac());
39 }
40 unsafe {
41 common::init_uart2(hal::pac());
42 }
43 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
44 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
45 uart.write_str_blocking("OSTIMER Alarm Example\n");
46
47 // Initialize embassy-time global driver backed by OSTIMER0
48 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
49
50 // Create OSTIMER instance
51 let config = hal::ostimer::Config {
52 init_match_max: true,
53 clock_frequency_hz: 1_000_000, // 1MHz
54 };
55 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(p.OSTIMER0, config, hal::pac());
56
57 // Create alarm with callback
58 let alarm = hal::ostimer::Alarm::new()
59 .with_callback(alarm_callback)
60 .with_flag(&ALARM_FLAG);
61
62 uart.write_str_blocking("Scheduling alarm for 2 seconds...\n");
63
64 // Schedule alarm to expire in 2 seconds (2,000,000 microseconds)
65 let scheduled = ostimer.schedule_alarm_delay(&alarm, 2_000_000);
66 if scheduled {
67 uart.write_str_blocking("Alarm scheduled successfully\n");
68 } else {
69 uart.write_str_blocking("Failed to schedule alarm (would exceed timer capacity)\n");
70 return;
71 }
72
73 // Wait for alarm to expire
74 loop {
75 // Check if alarm has expired
76 if ALARM_FLAG.load(Ordering::Acquire) {
77 uart.write_str_blocking("Alarm expired! Callback executed.\n");
78 break;
79 }
80
81 // Busy wait - don't use Timer::after_millis as it interferes with alarm MATCH
82 for _ in 0..100000 {
83 cortex_m::asm::nop();
84 }
85 }
86
87 // Demonstrate canceling an alarm
88 uart.write_str_blocking("Scheduling another alarm for 3 seconds...\n");
89 ALARM_FLAG.store(false, Ordering::Release); // Reset flag
90
91 let scheduled = ostimer.schedule_alarm_delay(&alarm, 3_000_000);
92 if scheduled {
93 uart.write_str_blocking("Alarm scheduled. Waiting 1 second then canceling...\n");
94
95 // Wait 1 second
96 embassy_time::Timer::after_millis(1000).await;
97
98 // Cancel the alarm
99 ostimer.cancel_alarm(&alarm);
100 uart.write_str_blocking("Alarm canceled\n");
101
102 // Check immediately if alarm flag is set
103 if !ALARM_FLAG.load(Ordering::Acquire) {
104 uart.write_str_blocking("Alarm was successfully canceled\n");
105 } else {
106 uart.write_str_blocking("Alarm fired despite cancellation\n");
107 }
108 }
109
110 uart.write_str_blocking("Example complete\n");
111}
diff --git a/examples/ostimer_async.rs b/examples/ostimer_async.rs
new file mode 100644
index 000000000..27e14e022
--- /dev/null
+++ b/examples/ostimer_async.rs
@@ -0,0 +1,57 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use hal::uart;
7
8mod common;
9
10use embassy_mcxa276::bind_interrupts;
11use embassy_time::{Duration, Timer};
12use {defmt_rtt as _, panic_probe as _};
13
14// Bind only OS_EVENT, and retain the symbol explicitly so it can’t be GC’ed.
15bind_interrupts!(struct Irqs {
16 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
17});
18
19#[used]
20#[no_mangle]
21static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let _p = hal::init(hal::config::Config::default());
26
27 // Enable/clock OSTIMER0 and UART2 before touching their registers
28 unsafe {
29 common::init_ostimer0(hal::pac());
30 }
31 unsafe {
32 common::init_uart2(hal::pac());
33 }
34 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
35 let uart = uart::Uart::<uart::Lpuart2>::new(_p.LPUART2, uart::Config::new(src));
36 uart.write_str_blocking("boot\n");
37
38 // Avoid mass NVIC writes here; DefaultHandler now safely returns.
39
40 // Initialize embassy-time global driver backed by OSTIMER0 (re-enables OS_EVENT with priority)
41 // The bind_interrupts! macro handles handler binding automatically
42
43 // Initialize OSTIMER with default 1MHz frequency
44 // Adjust this value to match your actual OSTIMER clock frequency
45 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
46
47 // Removed force-pend; rely on real hardware match to trigger OS_EVENT.
48
49 // Log using defmt if enabled
50 defmt::info!("OSTIMER async example starting...");
51
52 loop {
53 defmt::info!("tick");
54 uart.write_str_blocking("tick\n");
55 Timer::after(Duration::from_millis(1000)).await;
56 }
57}
diff --git a/examples/ostimer_counter.rs b/examples/ostimer_counter.rs
new file mode 100644
index 000000000..e95140a88
--- /dev/null
+++ b/examples/ostimer_counter.rs
@@ -0,0 +1,128 @@
1//! # OSTIMER Counter Reading and Reset Example
2//!
3//! This example demonstrates the new timer counter reading and reset functionality
4//! of the OSTIMER driver.
5
6#![no_std]
7#![no_main]
8
9use embassy_executor::Spawner;
10use embassy_time::{Duration, Timer};
11use hal::bind_interrupts;
12use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _};
13
14mod common;
15
16bind_interrupts!(struct Irqs {
17 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
18});
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let p = hal::init(Default::default());
23
24 // Enable/clock OSTIMER0 and UART2 before touching their registers
25 unsafe {
26 common::init_ostimer0(hal::pac());
27 }
28 unsafe {
29 common::init_uart2(hal::pac());
30 }
31 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
32 let mut uart = hal::uart::Uart::<hal::uart::Lpuart2>::new(p.LPUART2, hal::uart::Config::new(src));
33
34 uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n");
35
36 // Initialize the OSTIMER time driver
37 hal::ostimer::time_driver::init(
38 hal::interrupt::Priority::from(3),
39 1_000_000, // 1MHz clock
40 );
41
42 // Create OSTIMER instance
43 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(
44 p.OSTIMER0,
45 hal::ostimer::Config {
46 init_match_max: true,
47 clock_frequency_hz: 1_000_000,
48 },
49 hal::pac(),
50 );
51
52 // Read initial counter value
53 let initial_counter = ostimer.now();
54 uart.write_str_blocking("Initial counter value: ");
55 write_u64(&mut uart, initial_counter);
56 uart.write_str_blocking("\n");
57
58 // Wait a bit to let counter increment
59 Timer::after(Duration::from_millis(100)).await;
60
61 // Read counter again
62 let counter_after_wait = ostimer.now();
63 uart.write_str_blocking("Counter after 100ms wait: ");
64 write_u64(&mut uart, counter_after_wait);
65 uart.write_str_blocking("\n");
66 uart.write_str_blocking("Difference: ");
67 write_u64(&mut uart, counter_after_wait - initial_counter);
68 uart.write_str_blocking(" ticks\n");
69
70 // Reset the timer
71 uart.write_str_blocking("Resetting timer...\n");
72 ostimer.reset(hal::pac());
73
74 // Read counter after reset
75 let counter_after_reset = ostimer.now();
76 uart.write_str_blocking("Counter after reset: ");
77 write_u64(&mut uart, counter_after_reset);
78 uart.write_str_blocking("\n");
79
80 // Wait again to verify timer is working
81 Timer::after(Duration::from_millis(50)).await;
82
83 let final_counter = ostimer.now();
84 uart.write_str_blocking("Counter after another 50ms: ");
85 write_u64(&mut uart, final_counter);
86 uart.write_str_blocking("\n");
87 uart.write_str_blocking("Difference after reset: ");
88 write_u64(&mut uart, final_counter - counter_after_reset);
89 uart.write_str_blocking(" ticks\n");
90
91 uart.write_str_blocking("Example complete\n");
92}
93
94// Helper function to write a u64 value as decimal string
95fn write_u64(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u64) {
96 if value == 0 {
97 uart.write_str_blocking("0");
98 return;
99 }
100
101 let mut buffer = [0u8; 20]; // Enough for max u64
102 let mut i = 0;
103 let mut v = value;
104
105 while v > 0 {
106 buffer[i] = b'0' + (v % 10) as u8;
107 v /= 10;
108 i += 1;
109 }
110
111 // Write digits in reverse order
112 while i > 0 {
113 i -= 1;
114 match buffer[i] {
115 b'0' => uart.write_str_blocking("0"),
116 b'1' => uart.write_str_blocking("1"),
117 b'2' => uart.write_str_blocking("2"),
118 b'3' => uart.write_str_blocking("3"),
119 b'4' => uart.write_str_blocking("4"),
120 b'5' => uart.write_str_blocking("5"),
121 b'6' => uart.write_str_blocking("6"),
122 b'7' => uart.write_str_blocking("7"),
123 b'8' => uart.write_str_blocking("8"),
124 b'9' => uart.write_str_blocking("9"),
125 _ => uart.write_str_blocking("?"),
126 }
127 }
128}
diff --git a/examples/ostimer_race_test.rs b/examples/ostimer_race_test.rs
new file mode 100644
index 000000000..a637b6353
--- /dev/null
+++ b/examples/ostimer_race_test.rs
@@ -0,0 +1,396 @@
1//! # OSTIMER Race Condition Test
2//!
3//! This example tests for race conditions in the OSTIMER driver by:
4//! - Scheduling alarms sequentially (hardware limitation: only one at a time)
5//! - Reading the counter during interrupt-heavy periods
6//! - Testing concurrent timer operations
7//! - Stress testing interrupt handling
8
9#![no_std]
10#![no_main]
11
12use core::sync::atomic::{AtomicU32, Ordering};
13
14use embassy_executor::Spawner;
15use embassy_time::{Duration, Timer};
16use hal::bind_interrupts;
17use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _};
18
19mod common;
20
21bind_interrupts!(struct Irqs {
22 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
23});
24
25#[used]
26#[no_mangle]
27static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
28
29// Global counters for race condition detection
30static ALARM_CALLBACK_COUNT: AtomicU32 = AtomicU32::new(0);
31static INTERRUPT_COUNT: AtomicU32 = AtomicU32::new(0);
32static RACE_DETECTED: AtomicU32 = AtomicU32::new(0);
33
34// Alarm callback function
35fn alarm_callback() {
36 let _count = ALARM_CALLBACK_COUNT.fetch_add(1, Ordering::SeqCst);
37 INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst);
38
39 // Simulate some work in the callback to increase chance of races
40 for _ in 0..10 {
41 cortex_m::asm::nop();
42 }
43}
44
45fn report_default_handler(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>) {
46 let snapshot = hal::interrupt::default_handler_snapshot();
47 if snapshot.count == 0 {
48 return;
49 }
50
51 uart.write_str_blocking("WARNING: DefaultHandler executed ");
52 write_u32(uart, snapshot.count);
53 uart.write_str_blocking(" time(s). Vector=");
54 write_u32(uart, snapshot.vector as u32);
55 uart.write_str_blocking(" CFSR=0x");
56 write_hex32(uart, snapshot.cfsr);
57 uart.write_str_blocking(" HFSR=0x");
58 write_hex32(uart, snapshot.hfsr);
59 uart.write_str_blocking(" PC=0x");
60 write_hex32(uart, snapshot.stacked_pc);
61 uart.write_str_blocking(" LR=0x");
62 write_hex32(uart, snapshot.stacked_lr);
63 uart.write_str_blocking(" SP=0x");
64 write_hex32(uart, snapshot.stacked_sp);
65 uart.write_str_blocking("\n");
66
67 hal::interrupt::clear_default_handler_snapshot();
68}
69
70#[embassy_executor::main]
71async fn main(_spawner: Spawner) {
72 let p = hal::init(Default::default());
73
74 // Enable/clock OSTIMER0 and UART2 before touching their registers
75 unsafe {
76 common::init_ostimer0(hal::pac());
77 }
78 unsafe {
79 common::init_uart2(hal::pac());
80 }
81 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
82 let mut uart = hal::uart::Uart::<hal::uart::Lpuart2>::new(p.LPUART2, hal::uart::Config::new(src));
83
84 uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n");
85
86 // The bind_interrupts! macro handles handler binding automatically
87
88 // Initialize the OSTIMER time driver FIRST
89 hal::ostimer::time_driver::init(
90 hal::interrupt::Priority::from(3),
91 1_000_000, // 1MHz clock
92 );
93
94 uart.write_str_blocking("Time driver initialized\n");
95
96 // Create OSTIMER instance
97 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(
98 p.OSTIMER0,
99 hal::ostimer::Config {
100 init_match_max: true,
101 clock_frequency_hz: 1_000_000,
102 },
103 hal::pac(),
104 );
105
106 uart.write_str_blocking("OSTIMER instance created\n");
107
108 // Test 1: Sequential alarm scheduling (OSTIMER only supports one alarm at a time)
109 uart.write_str_blocking("Test 1: Sequential alarm scheduling...\n");
110 test_rapid_alarms(&ostimer, &mut uart).await;
111 report_default_handler(&mut uart);
112
113 // Test 2: Counter reading during interrupts
114 uart.write_str_blocking("Test 2: Counter reading during interrupts...\n");
115 test_counter_reading_during_interrupts(&ostimer, &mut uart).await;
116 report_default_handler(&mut uart);
117
118 // Test 3: Concurrent timer operations
119 uart.write_str_blocking("Test 3: Concurrent timer operations...\n");
120 test_concurrent_operations(&ostimer, &mut uart).await;
121 report_default_handler(&mut uart);
122
123 // Test 4: Timer reset during operation
124 uart.write_str_blocking("Test 4: Timer reset during operation...\n");
125 test_reset_during_operation(&ostimer, &mut uart, hal::pac()).await;
126 report_default_handler(&mut uart);
127
128 // Report results
129 uart.write_str_blocking("Race condition test complete\n");
130 uart.write_str_blocking("Callback count: ");
131 write_u32(&mut uart, ALARM_CALLBACK_COUNT.load(Ordering::SeqCst));
132 uart.write_str_blocking("\nInterrupt count: ");
133 write_u32(&mut uart, INTERRUPT_COUNT.load(Ordering::SeqCst));
134 uart.write_str_blocking("\nRaces detected: ");
135 write_u32(&mut uart, RACE_DETECTED.load(Ordering::SeqCst));
136 uart.write_str_blocking("\n");
137}
138
139// Test rapid alarm scheduling to stress interrupt handling
140async fn test_rapid_alarms(
141 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
142 uart: &mut hal::uart::Uart<hal::uart::Lpuart2>,
143) {
144 let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst);
145
146 // Schedule 10 alarms sequentially (OSTIMER only supports one alarm at a time)
147 for _i in 0..10 {
148 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
149 let delay_us = 1000; // 1ms delay for each alarm
150 if ostimer.schedule_alarm_delay(&alarm, delay_us) {
151 // Wait for this alarm to complete before scheduling the next
152 Timer::after(Duration::from_micros(delay_us + 100)).await;
153 report_default_handler(uart);
154 } else {
155 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
156 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm (match not ready)\n");
157 }
158 }
159
160 // All alarms should have completed by now
161 let final_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst);
162 let expected_count = initial_count + 10;
163
164 if final_count != expected_count {
165 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
166 uart.write_str_blocking("ERROR: Expected ");
167 write_u32(uart, expected_count);
168 uart.write_str_blocking(" callbacks, got ");
169 write_u32(uart, final_count);
170 uart.write_str_blocking("\n");
171 } else {
172 uart.write_str_blocking("PASS: All rapid alarms executed\n");
173 }
174}
175
176// Test reading counter while interrupts are firing
177async fn test_counter_reading_during_interrupts(
178 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
179 uart: &mut hal::uart::Uart<hal::uart::Lpuart2>,
180) {
181 let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
182
183 // Schedule an alarm that will fire soon
184 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
185 if !ostimer.schedule_alarm_delay(&alarm, 500) {
186 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
187 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before counter stress\n");
188 }
189
190 // While alarm is pending, read the counter many times rapidly
191 // This tests if counter reading is atomic and doesn't get corrupted by interrupts
192 let mut last_counter = ostimer.now();
193 let mut consistent_reads = 0;
194 let mut total_reads = 0;
195
196 for _ in 0..1000 {
197 let current_counter = ostimer.now();
198 total_reads += 1;
199
200 // Check if counter is monotonically increasing (basic sanity check)
201 if current_counter >= last_counter {
202 consistent_reads += 1;
203 }
204 last_counter = current_counter;
205
206 // Small delay between reads
207 for _ in 0..10 {
208 cortex_m::asm::nop();
209 }
210
211 report_default_handler(uart);
212 }
213
214 // Wait for alarm to complete
215 Timer::after(Duration::from_millis(1)).await;
216
217 let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
218
219 if consistent_reads == total_reads {
220 uart.write_str_blocking("PASS: Counter reading consistent during interrupts\n");
221 } else {
222 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
223 uart.write_str_blocking("ERROR: Counter reading inconsistent: ");
224 write_u32(uart, consistent_reads);
225 uart.write_str_blocking("/");
226 write_u32(uart, total_reads);
227 uart.write_str_blocking(" consistent\n");
228 }
229
230 if final_interrupt_count > initial_interrupt_count {
231 uart.write_str_blocking("PASS: Interrupt fired during counter reading test\n");
232 } else {
233 uart.write_str_blocking("WARNING: No interrupt fired during counter reading test\n");
234 }
235}
236
237// Test concurrent timer operations (embassy-time + alarms)
238async fn test_concurrent_operations(
239 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
240 uart: &mut hal::uart::Uart<hal::uart::Lpuart2>,
241) {
242 let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
243
244 // Start an embassy-time timer
245 let timer_future = Timer::after(Duration::from_millis(2));
246
247 // Schedule an alarm that should fire before the timer
248 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
249 if !ostimer.schedule_alarm_delay(&alarm, 1000) {
250 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
251 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before concurrent operations\n");
252 }
253
254 // Wait for both to complete
255 timer_future.await;
256
257 let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
258
259 if final_interrupt_count > initial_interrupt_count {
260 uart.write_str_blocking("PASS: Concurrent operations completed\n");
261 } else {
262 uart.write_str_blocking("WARNING: No interrupts during concurrent operations\n");
263 }
264}
265
266// Test timer reset during active operations
267async fn test_reset_during_operation(
268 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
269 uart: &mut hal::uart::Uart<hal::uart::Lpuart2>,
270 peripherals: &hal::pac::Peripherals,
271) {
272 let initial_counter = ostimer.now();
273
274 // Schedule an alarm
275 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
276 if !ostimer.schedule_alarm_delay(&alarm, 2000) {
277 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
278 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before reset test\n");
279 }
280
281 // Wait a bit then reset the timer
282 Timer::after(Duration::from_millis(1)).await;
283 ostimer.reset(peripherals);
284
285 // Check counter after reset
286 let counter_after_reset = ostimer.now();
287
288 // Wait to see if the alarm still fires (it shouldn't after reset)
289 Timer::after(Duration::from_millis(2)).await;
290
291 let final_counter = ostimer.now();
292
293 if counter_after_reset < initial_counter {
294 uart.write_str_blocking("PASS: Timer reset successful\n");
295 } else {
296 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
297 uart.write_str_blocking("ERROR: Timer reset may have failed\n");
298 }
299
300 uart.write_str_blocking("Counter progression after reset: ");
301 write_u64(uart, initial_counter);
302 uart.write_str_blocking(" -> ");
303 write_u64(uart, counter_after_reset);
304 uart.write_str_blocking(" -> ");
305 write_u64(uart, final_counter);
306 uart.write_str_blocking("\n");
307}
308
309// Helper function to write a u32 value as decimal string
310fn write_u32(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u32) {
311 if value == 0 {
312 uart.write_str_blocking("0");
313 return;
314 }
315
316 let mut buffer = [0u8; 10]; // Enough for max u32
317 let mut i = 0;
318 let mut v = value;
319
320 while v > 0 {
321 buffer[i] = b'0' + (v % 10) as u8;
322 v /= 10;
323 i += 1;
324 }
325
326 // Write digits in reverse order
327 while i > 0 {
328 i -= 1;
329 match buffer[i] {
330 b'0' => uart.write_str_blocking("0"),
331 b'1' => uart.write_str_blocking("1"),
332 b'2' => uart.write_str_blocking("2"),
333 b'3' => uart.write_str_blocking("3"),
334 b'4' => uart.write_str_blocking("4"),
335 b'5' => uart.write_str_blocking("5"),
336 b'6' => uart.write_str_blocking("6"),
337 b'7' => uart.write_str_blocking("7"),
338 b'8' => uart.write_str_blocking("8"),
339 b'9' => uart.write_str_blocking("9"),
340 _ => uart.write_str_blocking("?"),
341 }
342 }
343}
344
345fn write_hex32(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u32) {
346 let mut buf = [b'0'; 8];
347 let mut tmp = value;
348 for i in (0..8).rev() {
349 let digit = (tmp & 0xF) as u8;
350 buf[i] = match digit {
351 0..=9 => b'0' + digit,
352 10..=15 => b'A' + (digit - 10),
353 _ => b'?',
354 };
355 tmp >>= 4;
356 }
357 for b in &buf {
358 uart.write_byte(*b);
359 }
360}
361
362// Helper function to write a u64 value as decimal string
363fn write_u64(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u64) {
364 if value == 0 {
365 uart.write_str_blocking("0");
366 return;
367 }
368
369 let mut buffer = [0u8; 20]; // Enough for max u64
370 let mut i = 0;
371 let mut v = value;
372
373 while v > 0 {
374 buffer[i] = b'0' + (v % 10) as u8;
375 v /= 10;
376 i += 1;
377 }
378
379 // Write digits in reverse order
380 while i > 0 {
381 i -= 1;
382 match buffer[i] {
383 b'0' => uart.write_str_blocking("0"),
384 b'1' => uart.write_str_blocking("1"),
385 b'2' => uart.write_str_blocking("2"),
386 b'3' => uart.write_str_blocking("3"),
387 b'4' => uart.write_str_blocking("4"),
388 b'5' => uart.write_str_blocking("5"),
389 b'6' => uart.write_str_blocking("6"),
390 b'7' => uart.write_str_blocking("7"),
391 b'8' => uart.write_str_blocking("8"),
392 b'9' => uart.write_str_blocking("9"),
393 _ => uart.write_str_blocking("?"),
394 }
395 }
396}
diff --git a/examples/rtc_alarm.rs b/examples/rtc_alarm.rs
new file mode 100644
index 000000000..c27fd4c55
--- /dev/null
+++ b/examples/rtc_alarm.rs
@@ -0,0 +1,87 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use hal::rtc::{RtcDateTime, RtcInterruptEnable};
6use hal::{uart, InterruptExt};
7use {cortex_m, embassy_mcxa276 as hal};
8
9mod common;
10
11type MyRtc = hal::rtc::Rtc<hal::rtc::Rtc0>;
12
13use embassy_mcxa276::bind_interrupts;
14use {defmt_rtt as _, panic_probe as _};
15
16bind_interrupts!(struct Irqs {
17 RTC => hal::rtc::RtcHandler;
18});
19
20#[used]
21#[no_mangle]
22static KEEP_RTC: unsafe extern "C" fn() = RTC;
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let p = hal::init(hal::config::Config::default());
27
28 unsafe {
29 common::init_uart2(hal::pac());
30 }
31
32 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
33 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
34
35 uart.write_str_blocking("\r\n=== RTC Alarm Example ===\r\n");
36
37 unsafe { hal::clocks::init_fro16k(hal::pac()) };
38
39 let rtc_config = hal::rtc::get_default_config();
40
41 let rtc = MyRtc::new(p.RTC0, rtc_config);
42
43 let now = RtcDateTime {
44 year: 2025,
45 month: 10,
46 day: 15,
47 hour: 14,
48 minute: 30,
49 second: 0,
50 };
51
52 rtc.stop();
53
54 uart.write_str_blocking("Time set to: 2025-10-15 14:30:00\r\n");
55 rtc.set_datetime(now);
56
57 let mut alarm = now;
58 alarm.second += 10;
59
60 rtc.set_alarm(alarm);
61 uart.write_str_blocking("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)\r\n");
62
63 rtc.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
64
65 unsafe {
66 hal::interrupt::RTC.enable();
67 }
68
69 unsafe {
70 cortex_m::interrupt::enable();
71 }
72
73 rtc.start();
74
75 uart.write_str_blocking("RTC started, waiting for alarm...\r\n");
76
77 loop {
78 if rtc.is_alarm_triggered() {
79 uart.write_str_blocking("\r\n*** ALARM TRIGGERED! ***\r\n");
80 break;
81 }
82 }
83
84 uart.write_str_blocking("Example complete - Test PASSED!\r\n");
85
86 loop {}
87}
diff --git a/examples/uart_interrupt.rs b/examples/uart_interrupt.rs
new file mode 100644
index 000000000..bd734f859
--- /dev/null
+++ b/examples/uart_interrupt.rs
@@ -0,0 +1,69 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use hal::interrupt::typelevel::Handler;
7use hal::uart;
8
9mod common;
10
11use embassy_mcxa276::bind_interrupts;
12use {defmt_rtt as _, panic_probe as _};
13
14// Bind LPUART2 interrupt to our handler
15bind_interrupts!(struct Irqs {
16 LPUART2 => hal::uart::UartInterruptHandler;
17});
18
19#[used]
20#[no_mangle]
21static KEEP_LPUART2: unsafe extern "C" fn() = LPUART2;
22
23// Wrapper function for the interrupt handler
24unsafe extern "C" fn lpuart2_handler() {
25 hal::uart::UartInterruptHandler::on_interrupt();
26}
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let _p = hal::init(hal::config::Config::default());
31
32 // Enable/clock UART2 before touching its registers
33 unsafe {
34 common::init_uart2(hal::pac());
35 }
36 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
37 let uart = uart::Uart::<uart::Lpuart2>::new(_p.LPUART2, uart::Config::new(src));
38
39 // Configure LPUART2 interrupt for UART operation BEFORE any UART usage
40 hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::from(3));
41
42 // Manually install the interrupt handler and enable RX IRQs in the peripheral
43 unsafe {
44 hal::interrupt::LPUART2.install_handler(lpuart2_handler);
45 // Enable RX interrupts so the handler actually fires on incoming bytes
46 uart.enable_rx_interrupts();
47 }
48
49 // Print welcome message
50 uart.write_str_blocking("UART interrupt echo demo starting...\r\n");
51 uart.write_str_blocking("Type characters to echo them back.\r\n");
52
53 // Log using defmt if enabled
54 defmt::info!("UART interrupt echo demo starting...");
55
56 loop {
57 // Check if we have received any data
58 if uart.rx_data_available() {
59 if let Some(byte) = uart.try_read_byte() {
60 // Echo it back
61 uart.write_byte(byte);
62 uart.write_str_blocking(" (received)\r\n");
63 }
64 } else {
65 // No data available, wait a bit before checking again
66 cortex_m::asm::delay(12_000_000); // ~1 second at 12MHz
67 }
68 }
69}