From 77b2c602a60e41c7c977003a6d40367ac285930e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Nov 2025 13:17:44 -0800 Subject: 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 --- examples/src/bin/adc_interrupt.rs | 86 ++++++++ examples/src/bin/adc_polling.rs | 76 +++++++ examples/src/bin/blink.rs | 78 +++++++ examples/src/bin/hello.rs | 111 ++++++++++ examples/src/bin/lpuart_buffered.rs | 76 +++++++ examples/src/bin/lpuart_polling.rs | 51 +++++ examples/src/bin/ostimer_alarm.rs | 106 +++++++++ examples/src/bin/ostimer_async.rs | 52 +++++ examples/src/bin/ostimer_counter.rs | 125 +++++++++++ examples/src/bin/ostimer_race_test.rs | 393 ++++++++++++++++++++++++++++++++++ examples/src/bin/rtc_alarm.rs | 84 ++++++++ examples/src/bin/uart_interrupt.rs | 66 ++++++ examples/src/common/mod.rs | 45 ++++ examples/src/lib.rs | 63 ++++++ 14 files changed, 1412 insertions(+) create mode 100644 examples/src/bin/adc_interrupt.rs create mode 100644 examples/src/bin/adc_polling.rs create mode 100644 examples/src/bin/blink.rs create mode 100644 examples/src/bin/hello.rs create mode 100644 examples/src/bin/lpuart_buffered.rs create mode 100644 examples/src/bin/lpuart_polling.rs create mode 100644 examples/src/bin/ostimer_alarm.rs create mode 100644 examples/src/bin/ostimer_async.rs create mode 100644 examples/src/bin/ostimer_counter.rs create mode 100644 examples/src/bin/ostimer_race_test.rs create mode 100644 examples/src/bin/rtc_alarm.rs create mode 100644 examples/src/bin/uart_interrupt.rs create mode 100644 examples/src/common/mod.rs create mode 100644 examples/src/lib.rs (limited to 'examples/src') 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 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::{init_adc, init_uart2}; +use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; +use hal::pac::adc1::cfg::{Pwrsel, Refsel}; +use hal::pac::adc1::cmdl1::{Adch, Mode}; +use hal::pac::adc1::ctrl::CalAvgs; +use hal::pac::adc1::tctrl::Tcmd; +use hal::{bind_interrupts, uart, InterruptExt}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC1 => hal::adc::AdcHandler; +}); + +#[used] +#[no_mangle] +static KEEP_ADC: unsafe extern "C" fn() = ADC1; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + unsafe { + init_uart2(hal::pac()); + } + + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + + uart.write_str_blocking("\r\n=== ADC interrupt Example ===\r\n"); + + unsafe { + init_adc(hal::pac()); + } + + let adc_config = LpadcConfig { + enable_in_doze_mode: true, + conversion_average_mode: CalAvgs::Average128, + enable_analog_preliminary: true, + power_up_delay: 0x80, + reference_voltage_source: Refsel::Option3, + power_level_mode: Pwrsel::Lowest, + trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, + enable_conv_pause: false, + conv_pause_delay: 0, + fifo_watermark: 0, + }; + let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + + adc.do_offset_calibration(); + adc.do_auto_calibration(); + + let mut conv_command_config = adc.get_default_conv_command_config(); + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + adc.set_conv_command_config(1, &conv_command_config); + + let mut conv_trigger_config = adc.get_default_conv_trigger_config(); + conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; + conv_trigger_config.enable_hardware_trigger = false; + adc.set_conv_trigger_config(0, &conv_trigger_config); + + uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n"); + + adc.enable_interrupt(0x1); + + unsafe { + hal::interrupt::ADC1.enable(); + } + + unsafe { + cortex_m::interrupt::enable(); + } + + loop { + adc.do_software_trigger(1); + while !adc.is_interrupt_triggered() { + // Wait until the interrupt is triggered + } + uart.write_str_blocking("\r\n*** ADC interrupt TRIGGERED! ***\r\n"); + //TBD need to print the value + } +} 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 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; + +use embassy_executor::Spawner; +use embassy_mcxa_examples::{init_adc, init_uart2}; +use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; +use hal::pac::adc1::cfg::{Pwrsel, Refsel}; +use hal::pac::adc1::cmdl1::{Adch, Mode}; +use hal::pac::adc1::ctrl::CalAvgs; +use hal::pac::adc1::tctrl::Tcmd; +use hal::uart; +use heapless::String; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +const G_LPADC_RESULT_SHIFT: u32 = 0; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + unsafe { + init_uart2(hal::pac()); + } + + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + + uart.write_str_blocking("\r\n=== ADC polling Example ===\r\n"); + + unsafe { + init_adc(hal::pac()); + } + + let adc_config = LpadcConfig { + enable_in_doze_mode: true, + conversion_average_mode: CalAvgs::Average128, + enable_analog_preliminary: true, + power_up_delay: 0x80, + reference_voltage_source: Refsel::Option3, + power_level_mode: Pwrsel::Lowest, + trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, + enable_conv_pause: false, + conv_pause_delay: 0, + fifo_watermark: 0, + }; + let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + + adc.do_offset_calibration(); + adc.do_auto_calibration(); + + let mut conv_command_config = adc.get_default_conv_command_config(); + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + adc.set_conv_command_config(1, &conv_command_config); + + let mut conv_trigger_config = adc.get_default_conv_trigger_config(); + conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; + conv_trigger_config.enable_hardware_trigger = false; + adc.set_conv_trigger_config(0, &conv_trigger_config); + + uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n"); + + loop { + adc.do_software_trigger(1); + let mut result: Option = None; + while result.is_none() { + result = hal::adc::get_conv_result(); + } + let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; + let mut buf: String<16> = String::new(); // adjust size as needed + write!(buf, "\r\nvalue: {}\r\n", value).unwrap(); + uart.write_str_blocking(&buf); + } +} 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 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa as hal; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa_examples::{init_led, init_ostimer0}; +use embassy_time::{Duration, Timer}; +use hal::gpio::pins::PIO3_18; +use hal::gpio::{Level, Output}; + +// Bind only OS_EVENT for timer interrupts +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[used] +#[no_mangle] +static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + + unsafe { + init_led(hal::pac()); + init_ostimer0(hal::pac()); + } + + // Initialize embassy-time global driver backed by OSTIMER0 + hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); + + // Configure LED pin for GPIO mode + PIO3_18::set_mux_gpio(); + + let mut led = Output::new(PIO3_18::degrade(), Level::High); + + // Complex blinking pattern: SOS in Morse code + // S: ... (3 short) + // O: --- (3 long) + // S: ... (3 short) + // With pauses between letters and words + + loop { + // S: three short blinks + for _ in 0..3 { + led.set_low(); + Timer::after(Duration::from_millis(150)).await; + led.set_high(); + Timer::after(Duration::from_millis(150)).await; + } + + // Pause between letters + Timer::after(Duration::from_millis(300)).await; + + // O: three long blinks + for _ in 0..3 { + led.set_low(); + Timer::after(Duration::from_millis(450)).await; + led.set_high(); + Timer::after(Duration::from_millis(150)).await; + } + + // Pause between letters + Timer::after(Duration::from_millis(300)).await; + + // S: three short blinks + for _ in 0..3 { + led.set_low(); + Timer::after(Duration::from_millis(150)).await; + led.set_high(); + Timer::after(Duration::from_millis(150)).await; + } + + // Long pause between words (SOS repeats) + Timer::after(Duration::from_millis(1000)).await; + } +} 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 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::init_uart2; +use hal::uart; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +/// Simple helper to write a byte as hex to UART +fn write_hex_byte(uart: &hal::uart::Uart, byte: u8) { + const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; + uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]); + uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + defmt::info!("boot"); + + // Board-level init for UART2 clocks and pins. + unsafe { + init_uart2(hal::pac()); + } + + // Get UART source frequency from clock configuration + // Using hardcoded frequency for now - dynamic detection may have issues + let src = 12_000_000; // FRO_LF_DIV at 12MHz with DIV=0 + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + + // Print welcome message before any async delays to guarantee early console output + uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); + uart.write_str_blocking("Available commands:\r\n"); + uart.write_str_blocking(" help - Show this help\r\n"); + uart.write_str_blocking(" echo - Echo back the text\r\n"); + uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); + uart.write_str_blocking("Type a command: "); + + let mut buffer = [0u8; 64]; + let mut buf_idx = 0; + + loop { + // Read a byte from UART + let byte = uart.read_byte_blocking(); + + // Echo the character back + if byte == b'\r' || byte == b'\n' { + // Enter pressed - process command + uart.write_str_blocking("\r\n"); + + if buf_idx > 0 { + let command = &buffer[0..buf_idx]; + + if command == b"help" { + uart.write_str_blocking("Available commands:\r\n"); + uart.write_str_blocking(" help - Show this help\r\n"); + uart.write_str_blocking(" echo - Echo back the text\r\n"); + uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); + } else if command.starts_with(b"echo ") && command.len() > 5 { + uart.write_str_blocking("Echo: "); + uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or("")); + uart.write_str_blocking("\r\n"); + } else if command.starts_with(b"hex ") && command.len() > 4 { + // Parse the byte value + let num_str = &command[4..]; + if let Ok(num) = parse_u8(num_str) { + uart.write_str_blocking("Hex: 0x"); + write_hex_byte(&uart, num); + uart.write_str_blocking("\r\n"); + } else { + uart.write_str_blocking("Invalid number for hex command\r\n"); + } + } else if !command.is_empty() { + uart.write_str_blocking("Unknown command: "); + uart.write_str_blocking(core::str::from_utf8(command).unwrap_or("")); + uart.write_str_blocking("\r\n"); + } + } + + // Reset buffer and prompt + buf_idx = 0; + uart.write_str_blocking("Type a command: "); + } else if byte == 8 || byte == 127 { + // Backspace + if buf_idx > 0 { + buf_idx -= 1; + uart.write_str_blocking("\x08 \x08"); // Erase character + } + } else if buf_idx < buffer.len() - 1 { + // Regular character + buffer[buf_idx] = byte; + buf_idx += 1; + uart.write_byte(byte); + } + } +} + +/// Simple parser for u8 from ASCII bytes +fn parse_u8(bytes: &[u8]) -> Result { + let mut result = 0u8; + for &b in bytes { + if b.is_ascii_digit() { + result = result.checked_mul(10).ok_or(())?; + result = result.checked_add(b - b'0').ok_or(())?; + } else { + return Err(()); + } + } + Ok(result) +} 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 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa as hal; +use embassy_mcxa::interrupt::typelevel::Handler; +use embassy_mcxa::lpuart::buffered::BufferedLpuart; +use embassy_mcxa::{bind_interrupts, lpuart}; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use embedded_io_async::{Read, Write}; + +// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver +bind_interrupts!(struct Irqs { + LPUART2 => lpuart::buffered::BufferedInterruptHandler::; +}); + +// Wrapper function for the interrupt handler +unsafe extern "C" fn lpuart2_handler() { + lpuart::buffered::BufferedInterruptHandler::::on_interrupt(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + let p2 = lpuart::lib::init(); + + unsafe { + hal::interrupt::install_irq_handler(hal::pac::Interrupt::LPUART2, lpuart2_handler); + } + + // Configure NVIC for LPUART2 + hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3); + + unsafe { + init_uart2(hal::pac()); + init_ostimer0(hal::pac()); + } + + // UART configuration (enable both TX and RX) + let config = lpuart::Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: true, + rx_fifo_watermark: 0, + tx_fifo_watermark: 0, + ..Default::default() + }; + + let mut tx_buf = [0u8; 256]; + let mut rx_buf = [0u8; 256]; + + // Create a buffered LPUART2 instance with both TX and RX + let mut uart = BufferedLpuart::new( + p2.LPUART2, + p2.PIO2_2, // TX pin + p2.PIO2_3, // RX pin + Irqs, + &mut tx_buf, + &mut rx_buf, + config, + ) + .unwrap(); + + // Split into TX and RX parts + let (tx, rx) = uart.split_ref(); + + tx.write(b"Hello buffered LPUART.\r\n").await.unwrap(); + tx.write(b"Type characters to echo them back.\r\n").await.unwrap(); + + // Echo loop + let mut buf = [0u8; 4]; + loop { + rx.read_exact(&mut buf[..]).await.unwrap(); + tx.write_all(&buf[..]).await.unwrap(); + } +} 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 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::init_uart2; +use hal::lpuart::{lib, Config, Lpuart}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + let p2 = lib::init(); + + defmt::info!("boot"); + + // Board-level init for UART2 clocks and pins. + unsafe { + init_uart2(hal::pac()); + } + + // Create UART configuration + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: true, + ..Default::default() + }; + + // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX + let lpuart = Lpuart::new_blocking( + p2.LPUART2, // Peripheral + p2.PIO2_2, // TX pin + p2.PIO2_3, // RX pin + config, + ) + .unwrap(); + + // Split into separate TX and RX parts + let (mut tx, mut rx) = lpuart.split(); + + // Write hello messages + tx.blocking_write(b"Hello world.\r\n").unwrap(); + tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap(); + + // Echo loop + loop { + let mut buf = [0u8; 1]; + rx.blocking_read(&mut buf).unwrap(); + tx.blocking_write(&buf).unwrap(); + } +} 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 @@ +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use hal::uart; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed. +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[used] +#[no_mangle] +static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; + +// Global flag for alarm callback +static ALARM_FLAG: AtomicBool = AtomicBool::new(false); + +// Alarm callback function +fn alarm_callback() { + ALARM_FLAG.store(true, Ordering::Release); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + // Enable/clock OSTIMER0 and UART2 before touching their registers + unsafe { + init_ostimer0(hal::pac()); + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + uart.write_str_blocking("OSTIMER Alarm Example\n"); + + // Initialize embassy-time global driver backed by OSTIMER0 + hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); + + // Create OSTIMER instance + let config = hal::ostimer::Config { + init_match_max: true, + clock_frequency_hz: 1_000_000, // 1MHz + }; + let ostimer = hal::ostimer::Ostimer::::new(p.OSTIMER0, config, hal::pac()); + + // Create alarm with callback + let alarm = hal::ostimer::Alarm::new() + .with_callback(alarm_callback) + .with_flag(&ALARM_FLAG); + + uart.write_str_blocking("Scheduling alarm for 2 seconds...\n"); + + // Schedule alarm to expire in 2 seconds (2,000,000 microseconds) + let scheduled = ostimer.schedule_alarm_delay(&alarm, 2_000_000); + if scheduled { + uart.write_str_blocking("Alarm scheduled successfully\n"); + } else { + uart.write_str_blocking("Failed to schedule alarm (would exceed timer capacity)\n"); + return; + } + + // Wait for alarm to expire + loop { + // Check if alarm has expired + if ALARM_FLAG.load(Ordering::Acquire) { + uart.write_str_blocking("Alarm expired! Callback executed.\n"); + break; + } + + // Busy wait - don't use Timer::after_millis as it interferes with alarm MATCH + for _ in 0..100000 { + cortex_m::asm::nop(); + } + } + + // Demonstrate canceling an alarm + uart.write_str_blocking("Scheduling another alarm for 3 seconds...\n"); + ALARM_FLAG.store(false, Ordering::Release); // Reset flag + + let scheduled = ostimer.schedule_alarm_delay(&alarm, 3_000_000); + if scheduled { + uart.write_str_blocking("Alarm scheduled. Waiting 1 second then canceling...\n"); + + // Wait 1 second + embassy_time::Timer::after_millis(1000).await; + + // Cancel the alarm + ostimer.cancel_alarm(&alarm); + uart.write_str_blocking("Alarm canceled\n"); + + // Check immediately if alarm flag is set + if !ALARM_FLAG.load(Ordering::Acquire) { + uart.write_str_blocking("Alarm was successfully canceled\n"); + } else { + uart.write_str_blocking("Alarm fired despite cancellation\n"); + } + } + + uart.write_str_blocking("Example complete\n"); +} 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 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use embassy_time::{Duration, Timer}; +use hal::uart; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind only OS_EVENT, and retain the symbol explicitly so it can’t be GC’ed. +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[used] +#[no_mangle] +static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + + // Enable/clock OSTIMER0 and UART2 before touching their registers + unsafe { + init_ostimer0(hal::pac()); + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(_p.LPUART2, uart::Config::new(src)); + uart.write_str_blocking("boot\n"); + + // Avoid mass NVIC writes here; DefaultHandler now safely returns. + + // Initialize embassy-time global driver backed by OSTIMER0 (re-enables OS_EVENT with priority) + // The bind_interrupts! macro handles handler binding automatically + + // Initialize OSTIMER with default 1MHz frequency + // Adjust this value to match your actual OSTIMER clock frequency + hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); + + // Removed force-pend; rely on real hardware match to trigger OS_EVENT. + + // Log using defmt if enabled + defmt::info!("OSTIMER async example starting..."); + + loop { + defmt::info!("tick"); + uart.write_str_blocking("tick\n"); + Timer::after(Duration::from_millis(1000)).await; + } +} 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 @@ +//! # OSTIMER Counter Reading and Reset Example +//! +//! This example demonstrates the new timer counter reading and reset functionality +//! of the OSTIMER driver. + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use embassy_time::{Duration, Timer}; +use hal::bind_interrupts; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(Default::default()); + + // Enable/clock OSTIMER0 and UART2 before touching their registers + unsafe { + init_ostimer0(hal::pac()); + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let mut uart = hal::uart::Uart::::new(p.LPUART2, hal::uart::Config::new(src)); + + uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n"); + + // Initialize the OSTIMER time driver + hal::ostimer::time_driver::init( + hal::interrupt::Priority::from(3), + 1_000_000, // 1MHz clock + ); + + // Create OSTIMER instance + let ostimer = hal::ostimer::Ostimer::::new( + p.OSTIMER0, + hal::ostimer::Config { + init_match_max: true, + clock_frequency_hz: 1_000_000, + }, + hal::pac(), + ); + + // Read initial counter value + let initial_counter = ostimer.now(); + uart.write_str_blocking("Initial counter value: "); + write_u64(&mut uart, initial_counter); + uart.write_str_blocking("\n"); + + // Wait a bit to let counter increment + Timer::after(Duration::from_millis(100)).await; + + // Read counter again + let counter_after_wait = ostimer.now(); + uart.write_str_blocking("Counter after 100ms wait: "); + write_u64(&mut uart, counter_after_wait); + uart.write_str_blocking("\n"); + uart.write_str_blocking("Difference: "); + write_u64(&mut uart, counter_after_wait - initial_counter); + uart.write_str_blocking(" ticks\n"); + + // Reset the timer + uart.write_str_blocking("Resetting timer...\n"); + ostimer.reset(hal::pac()); + + // Read counter after reset + let counter_after_reset = ostimer.now(); + uart.write_str_blocking("Counter after reset: "); + write_u64(&mut uart, counter_after_reset); + uart.write_str_blocking("\n"); + + // Wait again to verify timer is working + Timer::after(Duration::from_millis(50)).await; + + let final_counter = ostimer.now(); + uart.write_str_blocking("Counter after another 50ms: "); + write_u64(&mut uart, final_counter); + uart.write_str_blocking("\n"); + uart.write_str_blocking("Difference after reset: "); + write_u64(&mut uart, final_counter - counter_after_reset); + uart.write_str_blocking(" ticks\n"); + + uart.write_str_blocking("Example complete\n"); +} + +// Helper function to write a u64 value as decimal string +fn write_u64(uart: &mut hal::uart::Uart, value: u64) { + if value == 0 { + uart.write_str_blocking("0"); + return; + } + + let mut buffer = [0u8; 20]; // Enough for max u64 + let mut i = 0; + let mut v = value; + + while v > 0 { + buffer[i] = b'0' + (v % 10) as u8; + v /= 10; + i += 1; + } + + // Write digits in reverse order + while i > 0 { + i -= 1; + match buffer[i] { + b'0' => uart.write_str_blocking("0"), + b'1' => uart.write_str_blocking("1"), + b'2' => uart.write_str_blocking("2"), + b'3' => uart.write_str_blocking("3"), + b'4' => uart.write_str_blocking("4"), + b'5' => uart.write_str_blocking("5"), + b'6' => uart.write_str_blocking("6"), + b'7' => uart.write_str_blocking("7"), + b'8' => uart.write_str_blocking("8"), + b'9' => uart.write_str_blocking("9"), + _ => uart.write_str_blocking("?"), + } + } +} 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 @@ +//! # OSTIMER Race Condition Test +//! +//! This example tests for race conditions in the OSTIMER driver by: +//! - Scheduling alarms sequentially (hardware limitation: only one at a time) +//! - Reading the counter during interrupt-heavy periods +//! - Testing concurrent timer operations +//! - Stress testing interrupt handling + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use embassy_executor::Spawner; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use embassy_time::{Duration, Timer}; +use hal::bind_interrupts; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[used] +#[no_mangle] +static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; + +// Global counters for race condition detection +static ALARM_CALLBACK_COUNT: AtomicU32 = AtomicU32::new(0); +static INTERRUPT_COUNT: AtomicU32 = AtomicU32::new(0); +static RACE_DETECTED: AtomicU32 = AtomicU32::new(0); + +// Alarm callback function +fn alarm_callback() { + let _count = ALARM_CALLBACK_COUNT.fetch_add(1, Ordering::SeqCst); + INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst); + + // Simulate some work in the callback to increase chance of races + for _ in 0..10 { + cortex_m::asm::nop(); + } +} + +fn report_default_handler(uart: &mut hal::uart::Uart) { + let snapshot = hal::interrupt::default_handler_snapshot(); + if snapshot.count == 0 { + return; + } + + uart.write_str_blocking("WARNING: DefaultHandler executed "); + write_u32(uart, snapshot.count); + uart.write_str_blocking(" time(s). Vector="); + write_u32(uart, snapshot.vector as u32); + uart.write_str_blocking(" CFSR=0x"); + write_hex32(uart, snapshot.cfsr); + uart.write_str_blocking(" HFSR=0x"); + write_hex32(uart, snapshot.hfsr); + uart.write_str_blocking(" PC=0x"); + write_hex32(uart, snapshot.stacked_pc); + uart.write_str_blocking(" LR=0x"); + write_hex32(uart, snapshot.stacked_lr); + uart.write_str_blocking(" SP=0x"); + write_hex32(uart, snapshot.stacked_sp); + uart.write_str_blocking("\n"); + + hal::interrupt::clear_default_handler_snapshot(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(Default::default()); + + // Enable/clock OSTIMER0 and UART2 before touching their registers + unsafe { + init_ostimer0(hal::pac()); + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let mut uart = hal::uart::Uart::::new(p.LPUART2, hal::uart::Config::new(src)); + + uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n"); + + // The bind_interrupts! macro handles handler binding automatically + + // Initialize the OSTIMER time driver FIRST + hal::ostimer::time_driver::init( + hal::interrupt::Priority::from(3), + 1_000_000, // 1MHz clock + ); + + uart.write_str_blocking("Time driver initialized\n"); + + // Create OSTIMER instance + let ostimer = hal::ostimer::Ostimer::::new( + p.OSTIMER0, + hal::ostimer::Config { + init_match_max: true, + clock_frequency_hz: 1_000_000, + }, + hal::pac(), + ); + + uart.write_str_blocking("OSTIMER instance created\n"); + + // Test 1: Sequential alarm scheduling (OSTIMER only supports one alarm at a time) + uart.write_str_blocking("Test 1: Sequential alarm scheduling...\n"); + test_rapid_alarms(&ostimer, &mut uart).await; + report_default_handler(&mut uart); + + // Test 2: Counter reading during interrupts + uart.write_str_blocking("Test 2: Counter reading during interrupts...\n"); + test_counter_reading_during_interrupts(&ostimer, &mut uart).await; + report_default_handler(&mut uart); + + // Test 3: Concurrent timer operations + uart.write_str_blocking("Test 3: Concurrent timer operations...\n"); + test_concurrent_operations(&ostimer, &mut uart).await; + report_default_handler(&mut uart); + + // Test 4: Timer reset during operation + uart.write_str_blocking("Test 4: Timer reset during operation...\n"); + test_reset_during_operation(&ostimer, &mut uart, hal::pac()).await; + report_default_handler(&mut uart); + + // Report results + uart.write_str_blocking("Race condition test complete\n"); + uart.write_str_blocking("Callback count: "); + write_u32(&mut uart, ALARM_CALLBACK_COUNT.load(Ordering::SeqCst)); + uart.write_str_blocking("\nInterrupt count: "); + write_u32(&mut uart, INTERRUPT_COUNT.load(Ordering::SeqCst)); + uart.write_str_blocking("\nRaces detected: "); + write_u32(&mut uart, RACE_DETECTED.load(Ordering::SeqCst)); + uart.write_str_blocking("\n"); +} + +// Test rapid alarm scheduling to stress interrupt handling +async fn test_rapid_alarms( + ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, + uart: &mut hal::uart::Uart, +) { + let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); + + // Schedule 10 alarms sequentially (OSTIMER only supports one alarm at a time) + for _i in 0..10 { + let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); + let delay_us = 1000; // 1ms delay for each alarm + if ostimer.schedule_alarm_delay(&alarm, delay_us) { + // Wait for this alarm to complete before scheduling the next + Timer::after(Duration::from_micros(delay_us + 100)).await; + report_default_handler(uart); + } else { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm (match not ready)\n"); + } + } + + // All alarms should have completed by now + let final_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); + let expected_count = initial_count + 10; + + if final_count != expected_count { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Expected "); + write_u32(uart, expected_count); + uart.write_str_blocking(" callbacks, got "); + write_u32(uart, final_count); + uart.write_str_blocking("\n"); + } else { + uart.write_str_blocking("PASS: All rapid alarms executed\n"); + } +} + +// Test reading counter while interrupts are firing +async fn test_counter_reading_during_interrupts( + ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, + uart: &mut hal::uart::Uart, +) { + let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); + + // Schedule an alarm that will fire soon + let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); + if !ostimer.schedule_alarm_delay(&alarm, 500) { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before counter stress\n"); + } + + // While alarm is pending, read the counter many times rapidly + // This tests if counter reading is atomic and doesn't get corrupted by interrupts + let mut last_counter = ostimer.now(); + let mut consistent_reads = 0; + let mut total_reads = 0; + + for _ in 0..1000 { + let current_counter = ostimer.now(); + total_reads += 1; + + // Check if counter is monotonically increasing (basic sanity check) + if current_counter >= last_counter { + consistent_reads += 1; + } + last_counter = current_counter; + + // Small delay between reads + for _ in 0..10 { + cortex_m::asm::nop(); + } + + report_default_handler(uart); + } + + // Wait for alarm to complete + Timer::after(Duration::from_millis(1)).await; + + let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); + + if consistent_reads == total_reads { + uart.write_str_blocking("PASS: Counter reading consistent during interrupts\n"); + } else { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Counter reading inconsistent: "); + write_u32(uart, consistent_reads); + uart.write_str_blocking("/"); + write_u32(uart, total_reads); + uart.write_str_blocking(" consistent\n"); + } + + if final_interrupt_count > initial_interrupt_count { + uart.write_str_blocking("PASS: Interrupt fired during counter reading test\n"); + } else { + uart.write_str_blocking("WARNING: No interrupt fired during counter reading test\n"); + } +} + +// Test concurrent timer operations (embassy-time + alarms) +async fn test_concurrent_operations( + ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, + uart: &mut hal::uart::Uart, +) { + let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); + + // Start an embassy-time timer + let timer_future = Timer::after(Duration::from_millis(2)); + + // Schedule an alarm that should fire before the timer + let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); + if !ostimer.schedule_alarm_delay(&alarm, 1000) { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before concurrent operations\n"); + } + + // Wait for both to complete + timer_future.await; + + let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); + + if final_interrupt_count > initial_interrupt_count { + uart.write_str_blocking("PASS: Concurrent operations completed\n"); + } else { + uart.write_str_blocking("WARNING: No interrupts during concurrent operations\n"); + } +} + +// Test timer reset during active operations +async fn test_reset_during_operation( + ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, + uart: &mut hal::uart::Uart, + peripherals: &hal::pac::Peripherals, +) { + let initial_counter = ostimer.now(); + + // Schedule an alarm + let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); + if !ostimer.schedule_alarm_delay(&alarm, 2000) { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before reset test\n"); + } + + // Wait a bit then reset the timer + Timer::after(Duration::from_millis(1)).await; + ostimer.reset(peripherals); + + // Check counter after reset + let counter_after_reset = ostimer.now(); + + // Wait to see if the alarm still fires (it shouldn't after reset) + Timer::after(Duration::from_millis(2)).await; + + let final_counter = ostimer.now(); + + if counter_after_reset < initial_counter { + uart.write_str_blocking("PASS: Timer reset successful\n"); + } else { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Timer reset may have failed\n"); + } + + uart.write_str_blocking("Counter progression after reset: "); + write_u64(uart, initial_counter); + uart.write_str_blocking(" -> "); + write_u64(uart, counter_after_reset); + uart.write_str_blocking(" -> "); + write_u64(uart, final_counter); + uart.write_str_blocking("\n"); +} + +// Helper function to write a u32 value as decimal string +fn write_u32(uart: &mut hal::uart::Uart, value: u32) { + if value == 0 { + uart.write_str_blocking("0"); + return; + } + + let mut buffer = [0u8; 10]; // Enough for max u32 + let mut i = 0; + let mut v = value; + + while v > 0 { + buffer[i] = b'0' + (v % 10) as u8; + v /= 10; + i += 1; + } + + // Write digits in reverse order + while i > 0 { + i -= 1; + match buffer[i] { + b'0' => uart.write_str_blocking("0"), + b'1' => uart.write_str_blocking("1"), + b'2' => uart.write_str_blocking("2"), + b'3' => uart.write_str_blocking("3"), + b'4' => uart.write_str_blocking("4"), + b'5' => uart.write_str_blocking("5"), + b'6' => uart.write_str_blocking("6"), + b'7' => uart.write_str_blocking("7"), + b'8' => uart.write_str_blocking("8"), + b'9' => uart.write_str_blocking("9"), + _ => uart.write_str_blocking("?"), + } + } +} + +fn write_hex32(uart: &mut hal::uart::Uart, value: u32) { + let mut buf = [b'0'; 8]; + let mut tmp = value; + for i in (0..8).rev() { + let digit = (tmp & 0xF) as u8; + buf[i] = match digit { + 0..=9 => b'0' + digit, + 10..=15 => b'A' + (digit - 10), + _ => b'?', + }; + tmp >>= 4; + } + for b in &buf { + uart.write_byte(*b); + } +} + +// Helper function to write a u64 value as decimal string +fn write_u64(uart: &mut hal::uart::Uart, value: u64) { + if value == 0 { + uart.write_str_blocking("0"); + return; + } + + let mut buffer = [0u8; 20]; // Enough for max u64 + let mut i = 0; + let mut v = value; + + while v > 0 { + buffer[i] = b'0' + (v % 10) as u8; + v /= 10; + i += 1; + } + + // Write digits in reverse order + while i > 0 { + i -= 1; + match buffer[i] { + b'0' => uart.write_str_blocking("0"), + b'1' => uart.write_str_blocking("1"), + b'2' => uart.write_str_blocking("2"), + b'3' => uart.write_str_blocking("3"), + b'4' => uart.write_str_blocking("4"), + b'5' => uart.write_str_blocking("5"), + b'6' => uart.write_str_blocking("6"), + b'7' => uart.write_str_blocking("7"), + b'8' => uart.write_str_blocking("8"), + b'9' => uart.write_str_blocking("9"), + _ => uart.write_str_blocking("?"), + } + } +} 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 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa as hal; +use embassy_mcxa_examples::init_uart2; +use hal::rtc::{RtcDateTime, RtcInterruptEnable}; +use hal::{uart, InterruptExt}; + +type MyRtc = hal::rtc::Rtc; + +use embassy_mcxa::bind_interrupts; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RTC => hal::rtc::RtcHandler; +}); + +#[used] +#[no_mangle] +static KEEP_RTC: unsafe extern "C" fn() = RTC; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + unsafe { + init_uart2(hal::pac()); + } + + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + + uart.write_str_blocking("\r\n=== RTC Alarm Example ===\r\n"); + + unsafe { hal::clocks::init_fro16k(hal::pac()) }; + + let rtc_config = hal::rtc::get_default_config(); + + let rtc = MyRtc::new(p.RTC0, rtc_config); + + let now = RtcDateTime { + year: 2025, + month: 10, + day: 15, + hour: 14, + minute: 30, + second: 0, + }; + + rtc.stop(); + + uart.write_str_blocking("Time set to: 2025-10-15 14:30:00\r\n"); + rtc.set_datetime(now); + + let mut alarm = now; + alarm.second += 10; + + rtc.set_alarm(alarm); + uart.write_str_blocking("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)\r\n"); + + rtc.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); + + unsafe { + hal::interrupt::RTC.enable(); + } + + unsafe { + cortex_m::interrupt::enable(); + } + + rtc.start(); + + uart.write_str_blocking("RTC started, waiting for alarm...\r\n"); + + loop { + if rtc.is_alarm_triggered() { + uart.write_str_blocking("\r\n*** ALARM TRIGGERED! ***\r\n"); + break; + } + } + + uart.write_str_blocking("Example complete - Test PASSED!\r\n"); +} 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 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa_examples::init_uart2; +use hal::interrupt::typelevel::Handler; +use hal::uart; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind LPUART2 interrupt to our handler +bind_interrupts!(struct Irqs { + LPUART2 => hal::uart::UartInterruptHandler; +}); + +#[used] +#[no_mangle] +static KEEP_LPUART2: unsafe extern "C" fn() = LPUART2; + +// Wrapper function for the interrupt handler +unsafe extern "C" fn lpuart2_handler() { + hal::uart::UartInterruptHandler::on_interrupt(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + + // Enable/clock UART2 before touching its registers + unsafe { + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(_p.LPUART2, uart::Config::new(src)); + + // Configure LPUART2 interrupt for UART operation BEFORE any UART usage + hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::from(3)); + + // Manually install the interrupt handler and enable RX IRQs in the peripheral + unsafe { + hal::interrupt::LPUART2.install_handler(lpuart2_handler); + // Enable RX interrupts so the handler actually fires on incoming bytes + uart.enable_rx_interrupts(); + } + + // Print welcome message + uart.write_str_blocking("UART interrupt echo demo starting...\r\n"); + uart.write_str_blocking("Type characters to echo them back.\r\n"); + + // Log using defmt if enabled + defmt::info!("UART interrupt echo demo starting..."); + + loop { + // Check if we have received any data + if uart.rx_data_available() { + if let Some(byte) = uart.try_read_byte() { + // Echo it back + uart.write_byte(byte); + uart.write_str_blocking(" (received)\r\n"); + } + } else { + // No data available, wait a bit before checking again + cortex_m::asm::delay(12_000_000); // ~1 second at 12MHz + } + } +} diff --git a/examples/src/common/mod.rs b/examples/src/common/mod.rs new file mode 100644 index 000000000..8cb4590f8 --- /dev/null +++ b/examples/src/common/mod.rs @@ -0,0 +1,45 @@ +//! Shared board-specific helpers for the FRDM-MCXA276 examples. +//! These live with the examples so the HAL stays generic. + +use hal::{clocks, pins, reset}; +use {embassy_mcxa as hal, panic_probe as _}; + +/// Initialize clocks and pin muxing for UART2 debug console. +/// Safe to call multiple times; writes are idempotent for our use. +#[allow(dead_code)] +pub unsafe fn init_uart2(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_uart2_port2(p); + reset::release_reset_port2(p); + reset::release_reset_lpuart2(p); + pins::configure_uart2_pins_port2(); + clocks::select_uart2_clock(p); +} + +/// Initialize clocks for the LED GPIO/PORT used by the blink example. +#[allow(dead_code)] +pub unsafe fn init_led(p: &hal::pac::Peripherals) { + clocks::enable_led_port(p); + reset::release_reset_gpio3(p); + reset::release_reset_port3(p); +} + +/// Initialize clocks for OSTIMER0 (1 MHz source). +#[allow(dead_code)] +pub unsafe fn init_ostimer0(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_ostimer0(p); + reset::release_reset_ostimer0(p); + clocks::select_ostimer0_clock_1m(p); +} + +/// Initialize clocks and pin muxing for ADC. +#[allow(dead_code)] +pub unsafe fn init_adc(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_adc(p); + reset::release_reset_port1(p); + reset::release_reset_adc1(p); + pins::configure_adc_pins(); + clocks::select_adc_clock(p); +} diff --git a/examples/src/lib.rs b/examples/src/lib.rs new file mode 100644 index 000000000..cf4194559 --- /dev/null +++ b/examples/src/lib.rs @@ -0,0 +1,63 @@ +#![no_std] + +//! Shared board-specific helpers for the FRDM-MCXA276 examples. +//! These live with the examples so the HAL stays generic. + +use hal::{clocks, pins, reset}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +/// Initialize clocks and pin muxing for UART2 debug console. +/// Safe to call multiple times; writes are idempotent for our use. +/// +/// # Safety +/// +/// Called only once to initialize the peripheral +#[allow(dead_code)] +pub unsafe fn init_uart2(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_uart2_port2(p); + reset::release_reset_port2(p); + reset::release_reset_lpuart2(p); + pins::configure_uart2_pins_port2(); + clocks::select_uart2_clock(p); +} + +/// Initialize clocks for the LED GPIO/PORT used by the blink example. +/// +/// # Safety +/// +/// Called only once to initialize the peripheral +#[allow(dead_code)] +pub unsafe fn init_led(p: &hal::pac::Peripherals) { + clocks::enable_led_port(p); + reset::release_reset_gpio3(p); + reset::release_reset_port3(p); +} + +/// Initialize clocks for OSTIMER0 (1 MHz source). +/// +/// # Safety +/// +/// Called only once to initialize the peripheral +#[allow(dead_code)] +pub unsafe fn init_ostimer0(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_ostimer0(p); + reset::release_reset_ostimer0(p); + clocks::select_ostimer0_clock_1m(p); +} + +/// Initialize clocks and pin muxing for ADC. +/// +/// # Safety +/// +/// Called only once to initialize the peripheral +#[allow(dead_code)] +pub unsafe fn init_adc(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_adc(p); + reset::release_reset_port1(p); + reset::release_reset_adc1(p); + pins::configure_adc_pins(); + clocks::select_adc_clock(p); +} -- cgit