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