From 8cdccae3c6c4a805cf5003b1a859734c105d76e8 Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 14 Nov 2025 18:43:27 +0100 Subject: Continue working on examples --- Cargo.toml | 3 - examples/adc_interrupt.rs | 29 ++-- examples/adc_polling.rs | 30 +++- examples/blink.rs | 4 - examples/hello.rs | 33 +++-- examples/lpuart_buffered.rs | 9 +- examples/lpuart_polling.rs | 11 +- examples/ostimer_alarm.rs | 32 +++-- examples/ostimer_async.rs | 29 ++-- examples/ostimer_counter.rs | 26 +++- examples/ostimer_race_test.rs | 71 ++++++---- examples/rtc_alarm.rs | 28 ++-- examples/uart_interrupt.rs | 69 --------- src/lib.rs | 2 - src/lpuart/mod.rs | 20 +++ src/ostimer.rs | 10 +- src/rtc.rs | 32 +++-- src/uart.rs | 316 ------------------------------------------ 18 files changed, 248 insertions(+), 506 deletions(-) delete mode 100644 examples/uart_interrupt.rs delete mode 100644 src/uart.rs diff --git a/Cargo.toml b/Cargo.toml index 19d1c7174..259becfe8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,9 +57,6 @@ name = "hello" [[example]] name = "blink" -[[example]] -name = "uart_interrupt" - [[example]] name = "ostimer_alarm" diff --git a/examples/adc_interrupt.rs b/examples/adc_interrupt.rs index 3be85ac75..536152539 100644 --- a/examples/adc_interrupt.rs +++ b/examples/adc_interrupt.rs @@ -4,13 +4,13 @@ use embassy_executor::Spawner; use embassy_mcxa276::clocks::periph_helpers::{AdcClockSel, Div4}; use embassy_mcxa276::clocks::PoweredClock; +use embassy_mcxa276::lpuart::{Config, Lpuart}; use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; -use hal::uart; use mcxa_pac::adc1::cfg::{Pwrsel, Refsel}; use mcxa_pac::adc1::cmdl1::{Adch, Mode}; use mcxa_pac::adc1::ctrl::CalAvgs; use mcxa_pac::adc1::tctrl::Tcmd; -use {cortex_m, embassy_mcxa276 as hal}; +use {embassy_mcxa276 as hal}; mod common; use hal::{bind_interrupts, InterruptExt}; @@ -28,14 +28,25 @@ static KEEP_ADC: unsafe extern "C" fn() = ADC1; async fn main(_spawner: Spawner) { let p = hal::init(hal::config::Config::default()); + // 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 unsafe { common::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"); + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin + config, + ) + .unwrap(); unsafe { common::init_adc(hal::pac()); @@ -71,7 +82,7 @@ async fn main(_spawner: Spawner) { 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"); + uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n"); adc.enable_interrupt(0x1); @@ -88,7 +99,7 @@ async fn main(_spawner: Spawner) { while !adc.is_interrupt_triggered() { // Wait until the interrupt is triggered } - // uart.write_str_blocking("\r\n*** ADC interrupt TRIGGERED! ***\r\n"); + uart.write_str_blocking("\r\n*** ADC interrupt TRIGGERED! ***\r\n"); //TBD need to print the value } } diff --git a/examples/adc_polling.rs b/examples/adc_polling.rs index 4b5f9422d..2fe4153db 100644 --- a/examples/adc_polling.rs +++ b/examples/adc_polling.rs @@ -2,9 +2,11 @@ #![no_main] use embassy_executor::Spawner; +use embassy_mcxa276::clocks::periph_helpers::{AdcClockSel, Div4}; +use embassy_mcxa276::clocks::PoweredClock; +use embassy_mcxa276::lpuart::{Config, Lpuart}; use embassy_mcxa276 as hal; use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; -use hal::uart; use mcxa_pac::adc1::cfg::{Pwrsel, Refsel}; use mcxa_pac::adc1::cmdl1::{Adch, Mode}; use mcxa_pac::adc1::ctrl::CalAvgs; @@ -27,10 +29,27 @@ async fn main(_spawner: Spawner) { common::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)); + // 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 + unsafe { + common::init_uart2(hal::pac()); + } + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin + config, + ) + .unwrap(); - uart.write_str_blocking("\r\n=== ADC polling Example ===\r\n"); + uart.blocking_write(b"\r\n=== ADC polling Example ===\r\n").unwrap(); unsafe { common::init_adc(hal::pac()); @@ -47,6 +66,9 @@ async fn main(_spawner: Spawner) { enable_conv_pause: false, conv_pause_delay: 0, fifo_watermark: 0, + power: PoweredClock::NormalEnabledDeepSleepDisabled, + source: AdcClockSel::FroLfDiv, + div: Div4::no_div(), }; let adc = hal::adc::Adc::::new(p.ADC1, adc_config); diff --git a/examples/blink.rs b/examples/blink.rs index 564353d5c..0f489abb9 100644 --- a/examples/blink.rs +++ b/examples/blink.rs @@ -28,10 +28,6 @@ async fn main(_spawner: Spawner) { unsafe { common::init_led(hal::pac()); } - // Initialize OSTIMER for async timing - unsafe { - common::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); diff --git a/examples/hello.rs b/examples/hello.rs index e39adaced..dbb53fdcf 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -2,15 +2,14 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa276 as hal; -use hal::uart; +use embassy_mcxa276::{self as hal, lpuart::{Blocking, Config, Lpuart}}; mod common; use {defmt_rtt as _, panic_probe as _}; /// Simple helper to write a byte as hex to UART -fn write_hex_byte(uart: &hal::uart::Uart, byte: u8) { +fn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, 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]); @@ -22,15 +21,25 @@ async fn main(_spawner: Spawner) { defmt::info!("boot"); - // Board-level init for UART2 clocks and pins. + // 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 unsafe { common::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)); + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin + config, + ) + .unwrap(); // Print welcome message before any async delays to guarantee early console output uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); @@ -69,12 +78,12 @@ async fn main(_spawner: Spawner) { let num_str = &command[4..]; if let Ok(num) = parse_u8(num_str) { uart.write_str_blocking("Hex: 0x"); - write_hex_byte(&uart, num); + write_hex_byte(&mut uart, num); uart.write_str_blocking("\r\n"); } else { uart.write_str_blocking("Invalid number for hex command\r\n"); } - } else if command.len() > 0 { + } 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"); @@ -103,7 +112,7 @@ async fn main(_spawner: Spawner) { fn parse_u8(bytes: &[u8]) -> Result { let mut result = 0u8; for &b in bytes { - if b >= b'0' && b <= b'9' { + if b.is_ascii_digit() { result = result.checked_mul(10).ok_or(())?; result = result.checked_add(b - b'0').ok_or(())?; } else { diff --git a/examples/lpuart_buffered.rs b/examples/lpuart_buffered.rs index 6ae690c56..9e297ca67 100644 --- a/examples/lpuart_buffered.rs +++ b/examples/lpuart_buffered.rs @@ -22,7 +22,7 @@ unsafe extern "C" fn lpuart2_handler() { #[embassy_executor::main] async fn main(_spawner: Spawner) { - let _p = hal::init(hal::config::Config::default()); + let p = hal::init(hal::config::Config::default()); unsafe { hal::interrupt::install_irq_handler(mcxa_pac::Interrupt::LPUART2, lpuart2_handler); @@ -33,7 +33,6 @@ async fn main(_spawner: Spawner) { unsafe { common::init_uart2(hal::pac()); - common::init_ostimer0(hal::pac()); } // UART configuration (enable both TX and RX) @@ -51,9 +50,9 @@ async fn main(_spawner: Spawner) { // 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 + p.LPUART2, + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin Irqs, &mut tx_buf, &mut rx_buf, diff --git a/examples/lpuart_polling.rs b/examples/lpuart_polling.rs index 067c7eb53..c9630dca5 100644 --- a/examples/lpuart_polling.rs +++ b/examples/lpuart_polling.rs @@ -4,14 +4,13 @@ use embassy_executor::Spawner; use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _}; -use crate::hal::lpuart::{lib, Config, Lpuart}; +use crate::hal::lpuart::{Config, Lpuart}; mod common; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let _p = hal::init(hal::config::Config::default()); - let p2 = lib::init(); + let p = hal::init(hal::config::Config::default()); defmt::info!("boot"); @@ -30,9 +29,9 @@ async fn main(_spawner: Spawner) { // 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 + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin config, ) .unwrap(); diff --git a/examples/ostimer_alarm.rs b/examples/ostimer_alarm.rs index 4f29a2c7c..f3a84d312 100644 --- a/examples/ostimer_alarm.rs +++ b/examples/ostimer_alarm.rs @@ -4,12 +4,15 @@ use core::sync::atomic::{AtomicBool, Ordering}; use embassy_executor::Spawner; -use hal::uart; -use {cortex_m, embassy_mcxa276 as hal}; +use embassy_mcxa276 as hal; mod common; -use embassy_mcxa276::{bind_interrupts, clocks::{periph_helpers::OstimerClockSel, PoweredClock}}; +use embassy_mcxa276::{ + bind_interrupts, + clocks::{periph_helpers::OstimerClockSel, PoweredClock}, + lpuart::{Config, Lpuart}, +}; use {defmt_rtt as _, panic_probe as _}; // Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed. @@ -33,15 +36,26 @@ fn alarm_callback() { async fn main(_spawner: Spawner) { let p = hal::init(hal::config::Config::default()); - // Enable/clock OSTIMER0 and UART2 before touching their registers - unsafe { - common::init_ostimer0(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 unsafe { common::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)); + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin + config, + ) + .unwrap(); + uart.write_str_blocking("OSTIMER Alarm Example\n"); // Initialize embassy-time global driver backed by OSTIMER0 diff --git a/examples/ostimer_async.rs b/examples/ostimer_async.rs index 27e14e022..2642a633d 100644 --- a/examples/ostimer_async.rs +++ b/examples/ostimer_async.rs @@ -2,8 +2,7 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa276 as hal; -use hal::uart; +use embassy_mcxa276::{self as hal, lpuart::{Config, Lpuart}}; mod common; @@ -22,18 +21,28 @@ 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()); + let p = hal::init(hal::config::Config::default()); - // Enable/clock OSTIMER0 and UART2 before touching their registers - unsafe { - common::init_ostimer0(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 unsafe { common::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"); + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin + config, + ) + .unwrap(); + uart.blocking_write(b"boot\n").unwrap(); // Avoid mass NVIC writes here; DefaultHandler now safely returns. diff --git a/examples/ostimer_counter.rs b/examples/ostimer_counter.rs index 069e879d8..590c5a14b 100644 --- a/examples/ostimer_counter.rs +++ b/examples/ostimer_counter.rs @@ -7,7 +7,10 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa276::clocks::{periph_helpers::OstimerClockSel, PoweredClock}; +use embassy_mcxa276::{ + clocks::{periph_helpers::OstimerClockSel, PoweredClock}, + lpuart::{Blocking, Config, Lpuart}, +}; use embassy_time::{Duration, Timer}; use hal::bind_interrupts; use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _}; @@ -22,12 +25,25 @@ bind_interrupts!(struct Irqs { async fn main(_spawner: Spawner) { let p = hal::init(Default::default()); - // Enable/clock OSTIMER0 and UART2 before touching their registers + // 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 unsafe { common::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)); + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin + config, + ) + .unwrap(); uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n"); @@ -90,7 +106,7 @@ async fn main(_spawner: Spawner) { } // Helper function to write a u64 value as decimal string -fn write_u64(uart: &mut hal::uart::Uart, value: u64) { +fn write_u64(uart: &mut Lpuart<'_, Blocking>, value: u64) { if value == 0 { uart.write_str_blocking("0"); return; diff --git a/examples/ostimer_race_test.rs b/examples/ostimer_race_test.rs index 6e3d4ac21..131d10f64 100644 --- a/examples/ostimer_race_test.rs +++ b/examples/ostimer_race_test.rs @@ -12,7 +12,10 @@ use core::sync::atomic::{AtomicU32, Ordering}; use embassy_executor::Spawner; -use embassy_mcxa276::clocks::{periph_helpers::OstimerClockSel, PoweredClock}; +use embassy_mcxa276::{ + clocks::{periph_helpers::OstimerClockSel, PoweredClock}, + lpuart::{Blocking, Config, Lpuart}, +}; use embassy_time::{Duration, Timer}; use hal::bind_interrupts; use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _}; @@ -43,7 +46,7 @@ fn alarm_callback() { } } -fn report_default_handler(uart: &mut hal::uart::Uart) { +fn report_default_handler(uart: &mut Lpuart<'_, Blocking>) { let snapshot = hal::interrupt::default_handler_snapshot(); if snapshot.count == 0 { return; @@ -72,15 +75,25 @@ fn report_default_handler(uart: &mut hal::uart::Uart) { async fn main(_spawner: Spawner) { let p = hal::init(Default::default()); - // Enable/clock OSTIMER0 and UART2 before touching their registers - unsafe { - common::init_ostimer0(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 unsafe { common::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)); + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin + config, + ) + .unwrap(); uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n"); @@ -140,7 +153,7 @@ async fn main(_spawner: Spawner) { // 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, + uart: &mut Lpuart<'_, Blocking>, ) { let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); @@ -177,7 +190,7 @@ async fn test_rapid_alarms( // 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, + uart: &mut Lpuart<'_, Blocking>, ) { let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); @@ -238,7 +251,7 @@ async fn test_counter_reading_during_interrupts( // Test concurrent timer operations (embassy-time + alarms) async fn test_concurrent_operations( ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, - uart: &mut hal::uart::Uart, + uart: &mut Lpuart<'_, Blocking>, ) { let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); @@ -267,7 +280,7 @@ async fn test_concurrent_operations( // Test timer reset during active operations async fn test_reset_during_operation( ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, - uart: &mut hal::uart::Uart, + uart: &mut Lpuart<'_, Blocking>, peripherals: &mcxa_pac::Peripherals, ) { let initial_counter = ostimer.now(); @@ -308,7 +321,7 @@ async fn test_reset_during_operation( } // Helper function to write a u32 value as decimal string -fn write_u32(uart: &mut hal::uart::Uart, value: u32) { +fn write_u32(uart: &mut Lpuart<'_, Blocking>, value: u32) { if value == 0 { uart.write_str_blocking("0"); return; @@ -343,7 +356,7 @@ fn write_u32(uart: &mut hal::uart::Uart, value: u32) { } } -fn write_hex32(uart: &mut hal::uart::Uart, value: u32) { +fn write_hex32(uart: &mut Lpuart<'_, Blocking>, value: u32) { let mut buf = [b'0'; 8]; let mut tmp = value; for i in (0..8).rev() { @@ -355,15 +368,13 @@ fn write_hex32(uart: &mut hal::uart::Uart, value: u32) { }; tmp >>= 4; } - for b in &buf { - uart.write_byte(*b); - } + uart.blocking_write(&buf).unwrap(); } // Helper function to write a u64 value as decimal string -fn write_u64(uart: &mut hal::uart::Uart, value: u64) { +fn write_u64(uart: &mut Lpuart<'_, Blocking>, value: u64) { if value == 0 { - uart.write_str_blocking("0"); + uart.blocking_write(b"0").unwrap(); return; } @@ -381,17 +392,17 @@ fn write_u64(uart: &mut hal::uart::Uart, value: u64) { 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("?"), + b'0' => uart.blocking_write(b"0").unwrap(), + b'1' => uart.blocking_write(b"1").unwrap(), + b'2' => uart.blocking_write(b"2").unwrap(), + b'3' => uart.blocking_write(b"3").unwrap(), + b'4' => uart.blocking_write(b"4").unwrap(), + b'5' => uart.blocking_write(b"5").unwrap(), + b'6' => uart.blocking_write(b"6").unwrap(), + b'7' => uart.blocking_write(b"7").unwrap(), + b'8' => uart.blocking_write(b"8").unwrap(), + b'9' => uart.blocking_write(b"9").unwrap(), + _ => uart.blocking_write(b"?").unwrap(), } } } diff --git a/examples/rtc_alarm.rs b/examples/rtc_alarm.rs index c27fd4c55..1cda37054 100644 --- a/examples/rtc_alarm.rs +++ b/examples/rtc_alarm.rs @@ -2,13 +2,14 @@ #![no_main] use embassy_executor::Spawner; +use embassy_mcxa276::lpuart::{Config, Lpuart}; use hal::rtc::{RtcDateTime, RtcInterruptEnable}; -use hal::{uart, InterruptExt}; -use {cortex_m, embassy_mcxa276 as hal}; +use hal::InterruptExt; +use {embassy_mcxa276 as hal}; mod common; -type MyRtc = hal::rtc::Rtc; +type MyRtc = hal::rtc::Rtc<'static, hal::rtc::Rtc0>; use embassy_mcxa276::bind_interrupts; use {defmt_rtt as _, panic_probe as _}; @@ -25,17 +26,28 @@ static KEEP_RTC: unsafe extern "C" fn() = RTC; async fn main(_spawner: Spawner) { let p = hal::init(hal::config::Config::default()); + // 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 unsafe { common::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)); + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.PIO2_2, // TX pin + p.PIO2_3, // RX pin + config, + ) + .unwrap(); 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); diff --git a/examples/uart_interrupt.rs b/examples/uart_interrupt.rs deleted file mode 100644 index 190a4d850..000000000 --- a/examples/uart_interrupt.rs +++ /dev/null @@ -1,69 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -// use embassy_mcxa276 as hal; -// use hal::interrupt::typelevel::Handler; -// use hal::uart; - -// mod common; - -// use embassy_mcxa276::bind_interrupts; -// use {defmt_rtt as _, 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 { -// common::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/src/lib.rs b/src/lib.rs index 4120c1e84..ec2cb31e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ pub mod interrupt; pub mod lpuart; pub mod ostimer; pub mod rtc; -pub mod uart; embassy_hal_internal::peripherals!(PORT1, PORT2, PORT3, LPUART2, OSTIMER0, GPIO, PIO2_2, PIO2_3, GPIO3, RTC0, ADC1,); @@ -45,7 +44,6 @@ pub use mcxa_pac as pac; pub(crate) use mcxa_pac as pac; pub use ostimer::Ostimer0 as Ostimer0Token; pub use rtc::Rtc0 as Rtc0Token; -pub use uart::Lpuart2 as Uart2Token; /// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals. /// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling). diff --git a/src/lpuart/mod.rs b/src/lpuart/mod.rs index 9e8b25ff6..b3d7c4885 100644 --- a/src/lpuart/mod.rs +++ b/src/lpuart/mod.rs @@ -772,6 +772,10 @@ impl<'a> LpuartTx<'a, Blocking> { Ok(()) } + pub fn write_str_blocking(&mut self, buf: &str) { + let _ = self.blocking_write(buf.as_bytes()); + } + /// Write data to LPUART TX without blocking. pub fn write(&mut self, buf: &[u8]) -> Result<()> { for x in buf { @@ -901,6 +905,22 @@ impl<'a> Lpuart<'a, Blocking> { self.tx.blocking_write(buf) } + pub fn write_byte(&mut self, byte: u8) { + _ = self.tx.write_byte(byte); + } + + pub fn read_byte_blocking(&mut self) -> u8 { + loop { + if let Ok(b) = self.rx.read_byte() { + return b; + } + } + } + + pub fn write_str_blocking(&mut self, buf: &str) { + self.tx.write_str_blocking(buf); + } + /// Write data to LPUART TX without blocking pub fn write(&mut self, buf: &[u8]) -> Result<()> { self.tx.write(buf) diff --git a/src/ostimer.rs b/src/ostimer.rs index efa534194..ebdf7d45d 100644 --- a/src/ostimer.rs +++ b/src/ostimer.rs @@ -534,7 +534,7 @@ pub mod time_driver { bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK, }; - use crate::pac; + use crate::{clocks::{enable_and_reset, periph_helpers::{OsTimerConfig, OstimerClockSel}, PoweredClock}, pac, peripherals::OSTIMER0}; pub struct Driver; static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); @@ -621,6 +621,14 @@ pub mod time_driver { /// Note: The frequency parameter is currently accepted for API compatibility. /// The embassy_time_driver macro handles driver registration automatically. pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { + let _clock_freq = unsafe { + enable_and_reset::(&OsTimerConfig { + power: PoweredClock::AlwaysEnabled, + source: OstimerClockSel::Clk1M, + }) + .expect("Enabling OsTimer clock should not fail") + }; + // Mask/clear at peripheral and set default MATCH let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; super::prime_match_registers(r); diff --git a/src/rtc.rs b/src/rtc.rs index facb9cf8c..f526e82ac 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -1,6 +1,9 @@ //! RTC DateTime driver. use core::sync::atomic::{AtomicBool, Ordering}; +use embassy_hal_internal::{Peri, PeripheralType}; + +use crate::clocks::with_clocks; use crate::pac; use crate::pac::rtc0::cr::Um; @@ -9,7 +12,7 @@ type Regs = pac::rtc0::RegisterBlock; static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false); // Token-based instance pattern like embassy-imxrt -pub trait Instance { +pub trait Instance: PeripheralType { fn ptr() -> *const Regs; } @@ -22,14 +25,6 @@ impl Instance for crate::peripherals::RTC0 { } } -// Also implement Instance for the Peri wrapper type -impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::RTC0> { - #[inline(always)] - fn ptr() -> *const Regs { - pac::Rtc0::ptr() - } -} - const DAYS_IN_A_YEAR: u32 = 365; const SECONDS_IN_A_DAY: u32 = 86400; const SECONDS_IN_A_HOUR: u32 = 3600; @@ -157,15 +152,26 @@ pub fn get_default_config() -> RtcConfig { } } /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) -pub struct Rtc { - _inst: core::marker::PhantomData, +pub struct Rtc<'a, I: Instance> { + _inst: core::marker::PhantomData<&'a mut I>, } -impl Rtc { +impl<'a, I: Instance> Rtc<'a, I> { /// initialize RTC - pub fn new(_inst: impl Instance, config: RtcConfig) -> Self { + pub fn new(_inst: Peri<'a, I>, config: RtcConfig) -> Self { let rtc = unsafe { &*I::ptr() }; + // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock + // on the vsys domain is active + let clocks = with_clocks(|c| { + c.clk_16k_vsys.clone() + }); + match clocks { + None => panic!("Clocks have not been initialized"), + Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"), + Some(Some(_)) => {} + } + /* RTC reset */ rtc.cr().modify(|_, w| w.swr().set_bit()); rtc.cr().modify(|_, w| w.swr().clear_bit()); diff --git a/src/uart.rs b/src/uart.rs deleted file mode 100644 index 3705959d3..000000000 --- a/src/uart.rs +++ /dev/null @@ -1,316 +0,0 @@ -//! Minimal polling UART2 bring-up replicating MCUXpresso hello_world ordering. -//! WARNING: This is a narrow implementation only for debug console (115200 8N1). - -// TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs -// are complete prior to release. -#![allow(clippy::missing_safety_doc)] - -use core::cell::RefCell; - -use cortex_m::interrupt::Mutex; -use embassy_sync::signal::Signal; - -use crate::pac; - -// svd2rust defines the shared LPUART RegisterBlock under lpuart0; all instances reuse it. -type Regs = pac::lpuart0::RegisterBlock; - -// Token-based instance pattern like embassy-imxrt -pub trait Instance { - fn ptr() -> *const Regs; -} - -/// Token for LPUART2 provided by embassy-hal-internal peripherals macro. -pub type Lpuart2 = crate::peripherals::LPUART2; -impl Instance for crate::peripherals::LPUART2 { - #[inline(always)] - fn ptr() -> *const Regs { - pac::Lpuart2::ptr() - } -} - -// Also implement Instance for the Peri wrapper type -impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::LPUART2> { - #[inline(always)] - fn ptr() -> *const Regs { - pac::Lpuart2::ptr() - } -} - -/// UART configuration (explicit src_hz; no hardcoded frequencies) -#[derive(Copy, Clone)] -pub struct Config { - pub src_hz: u32, - pub baud: u32, - pub parity: Parity, - pub stop_bits: StopBits, -} - -#[derive(Copy, Clone)] -pub enum Parity { - None, - Even, - Odd, -} -#[derive(Copy, Clone)] -pub enum StopBits { - One, - Two, -} - -impl Config { - pub fn new(src_hz: u32) -> Self { - Self { - src_hz, - baud: 115_200, - parity: Parity::None, - stop_bits: StopBits::One, - } - } -} - -/// Compute a valid (OSR, SBR) tuple for given source clock and baud. -/// Uses a functional fold approach to find the best OSR/SBR combination -/// with minimal baud rate error. -fn compute_osr_sbr(src_hz: u32, baud: u32) -> (u8, u16) { - let (best_osr, best_sbr, _best_err) = (8u32..=32).fold( - (16u8, 4u16, u32::MAX), // (best_osr, best_sbr, best_err) - |(best_osr, best_sbr, best_err), osr| { - let denom = baud.saturating_mul(osr); - if denom == 0 { - return (best_osr, best_sbr, best_err); - } - - let sbr = (src_hz + denom / 2) / denom; // round - if sbr == 0 || sbr > 0x1FFF { - return (best_osr, best_sbr, best_err); - } - - let actual = src_hz / (osr * sbr); - let err = actual.abs_diff(baud); - - // Update best if this is better, or same error but higher OSR - if err < best_err || (err == best_err && osr as u8 > best_osr) { - (osr as u8, sbr as u16, err) - } else { - (best_osr, best_sbr, best_err) - } - }, - ); - (best_osr, best_sbr) -} - -/// Minimal UART handle for a specific instance I (store the zero-sized token like embassy) -pub struct Uart { - _inst: core::marker::PhantomData, -} - -impl Uart { - /// Create and initialize LPUART (reset + config). Clocks and pins must be prepared by the caller. - pub fn new(_inst: impl Instance, cfg: Config) -> Self { - let l = unsafe { &*I::ptr() }; - // 1) software reset pulse - l.global().write(|w| w.rst().reset()); - cortex_m::asm::delay(3); // Short delay for reset to take effect - l.global().write(|w| w.rst().no_effect()); - cortex_m::asm::delay(10); // Allow peripheral to stabilize after reset - // 2) BAUD - let (osr, sbr) = compute_osr_sbr(cfg.src_hz, cfg.baud); - l.baud().modify(|_, w| { - let w = match cfg.stop_bits { - StopBits::One => w.sbns().one(), - StopBits::Two => w.sbns().two(), - }; - // OSR field encodes (osr-1); use raw bits to avoid a long match on all variants - let raw_osr = osr.saturating_sub(1); - unsafe { w.osr().bits(raw_osr).sbr().bits(sbr) } - }); - // 3) CTRL baseline and parity - l.ctrl().write(|w| { - let w = w.ilt().from_stop().idlecfg().idle_2(); - let w = match cfg.parity { - Parity::None => w.pe().disabled(), - Parity::Even => w.pe().enabled().pt().even(), - Parity::Odd => w.pe().enabled().pt().odd(), - }; - w.re().enabled().te().enabled().rie().disabled() - }); - // 4) FIFOs and WATER: keep it simple for polling; disable FIFOs and set RX watermark to 0 - l.fifo().modify(|_, w| { - w.txfe() - .disabled() - .rxfe() - .disabled() - .txflush() - .txfifo_rst() - .rxflush() - .rxfifo_rst() - }); - l.water() - .modify(|_, w| unsafe { w.txwater().bits(0).rxwater().bits(0) }); - Self { - _inst: core::marker::PhantomData, - } - } - - /// Enable RX interrupts. The caller must ensure an appropriate IRQ handler is installed. - pub unsafe fn enable_rx_interrupts(&self) { - let l = &*I::ptr(); - l.ctrl().modify(|_, w| w.rie().enabled()); - } - - #[inline(never)] - pub fn write_byte(&self, b: u8) { - let l = unsafe { &*I::ptr() }; - // Timeout after ~10ms at 12MHz (assuming 115200 baud, should be plenty) - const DATA_OFFSET: usize = 0x1C; // DATA register offset inside LPUART block - let data_ptr = unsafe { (I::ptr() as *mut u8).add(DATA_OFFSET) }; - for _ in 0..120000 { - if l.water().read().txcount().bits() == 0 { - unsafe { core::ptr::write_volatile(data_ptr, b) }; - return; - } - } - // If timeout, skip the write to avoid hanging - } - - #[inline(never)] - pub fn write_str_blocking(&self, s: &str) { - for &b in s.as_bytes() { - if b == b'\n' { - self.write_byte(b'\r'); - } - self.write_byte(b); - } - } - pub fn read_byte_blocking(&self) -> u8 { - let l = unsafe { &*I::ptr() }; - while !l.stat().read().rdrf().is_rxdata() {} - (l.data().read().bits() & 0xFF) as u8 - } -} - -// Simple ring buffer for UART RX data -const RX_BUFFER_SIZE: usize = 256; -pub struct RingBuffer { - buffer: [u8; RX_BUFFER_SIZE], - read_idx: usize, - write_idx: usize, - count: usize, -} - -impl Default for RingBuffer { - fn default() -> Self { - Self::new() - } -} - -impl RingBuffer { - pub const fn new() -> Self { - Self { - buffer: [0; RX_BUFFER_SIZE], - read_idx: 0, - write_idx: 0, - count: 0, - } - } - - pub fn push(&mut self, data: u8) -> bool { - if self.count >= RX_BUFFER_SIZE { - return false; // Buffer full - } - self.buffer[self.write_idx] = data; - self.write_idx = (self.write_idx + 1) % RX_BUFFER_SIZE; - self.count += 1; - true - } - - pub fn pop(&mut self) -> Option { - if self.count == 0 { - return None; - } - let data = self.buffer[self.read_idx]; - self.read_idx = (self.read_idx + 1) % RX_BUFFER_SIZE; - self.count -= 1; - Some(data) - } - - pub fn is_empty(&self) -> bool { - self.count == 0 - } - - pub fn len(&self) -> usize { - self.count - } -} - -// Global RX buffer shared between interrupt handler and UART instance -static RX_BUFFER: Mutex> = Mutex::new(RefCell::new(RingBuffer::new())); -static RX_SIGNAL: Signal = Signal::new(); - -// Debug counter for interrupt handler calls -static mut INTERRUPT_COUNT: u32 = 0; - -impl Uart { - /// Read a byte asynchronously using interrupts - pub async fn read_byte_async(&self) -> u8 { - loop { - // Check if we have data in the buffer - let byte = cortex_m::interrupt::free(|cs| { - let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); - buffer.pop() - }); - - if let Some(byte) = byte { - return byte; - } - - // Wait for the interrupt signal - RX_SIGNAL.wait().await; - } - } - - /// Check if there's data available in the RX buffer - pub fn rx_data_available(&self) -> bool { - cortex_m::interrupt::free(|cs| { - let buffer = RX_BUFFER.borrow(cs).borrow(); - !buffer.is_empty() - }) - } - - /// Try to read a byte from RX buffer (non-blocking) - pub fn try_read_byte(&self) -> Option { - cortex_m::interrupt::free(|cs| { - let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); - buffer.pop() - }) - } -} - -/// Type-level handler for LPUART2 interrupts, compatible with bind_interrupts!. -pub struct UartInterruptHandler; - -impl crate::interrupt::typelevel::Handler for UartInterruptHandler { - unsafe fn on_interrupt() { - INTERRUPT_COUNT += 1; - - let lpuart = &*pac::Lpuart2::ptr(); - - // Check if we have RX data - if lpuart.stat().read().rdrf().is_rxdata() { - // Read the data byte - let data = (lpuart.data().read().bits() & 0xFF) as u8; - - // Store in ring buffer - cortex_m::interrupt::free(|cs| { - let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); - if buffer.push(data) { - // Data added successfully, signal waiting tasks - RX_SIGNAL.signal(()); - } - }); - } - // Always clear any error flags that might cause spurious interrupts - let _ = lpuart.stat().read(); - } -} -- cgit