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