diff options
| author | James Munns <[email protected]> | 2025-11-14 18:43:27 +0100 |
|---|---|---|
| committer | James Munns <[email protected]> | 2025-11-14 18:43:27 +0100 |
| commit | 8cdccae3c6c4a805cf5003b1a859734c105d76e8 (patch) | |
| tree | 7c605a58aa7e124bbed658dfc5f6822a25a83e98 /src | |
| parent | e799d6c8956ed3ea5ced65d58c3065a22927ad10 (diff) | |
Continue working on examples
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/lpuart/mod.rs | 20 | ||||
| -rw-r--r-- | src/ostimer.rs | 10 | ||||
| -rw-r--r-- | src/rtc.rs | 32 | ||||
| -rw-r--r-- | src/uart.rs | 316 |
5 files changed, 48 insertions, 332 deletions
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; | |||
| 13 | pub mod lpuart; | 13 | pub mod lpuart; |
| 14 | pub mod ostimer; | 14 | pub mod ostimer; |
| 15 | pub mod rtc; | 15 | pub mod rtc; |
| 16 | pub mod uart; | ||
| 17 | 16 | ||
| 18 | embassy_hal_internal::peripherals!(PORT1, PORT2, PORT3, LPUART2, OSTIMER0, GPIO, PIO2_2, PIO2_3, GPIO3, RTC0, ADC1,); | 17 | embassy_hal_internal::peripherals!(PORT1, PORT2, PORT3, LPUART2, OSTIMER0, GPIO, PIO2_2, PIO2_3, GPIO3, RTC0, ADC1,); |
| 19 | 18 | ||
| @@ -45,7 +44,6 @@ pub use mcxa_pac as pac; | |||
| 45 | pub(crate) use mcxa_pac as pac; | 44 | pub(crate) use mcxa_pac as pac; |
| 46 | pub use ostimer::Ostimer0 as Ostimer0Token; | 45 | pub use ostimer::Ostimer0 as Ostimer0Token; |
| 47 | pub use rtc::Rtc0 as Rtc0Token; | 46 | pub use rtc::Rtc0 as Rtc0Token; |
| 48 | pub use uart::Lpuart2 as Uart2Token; | ||
| 49 | 47 | ||
| 50 | /// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals. | 48 | /// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals. |
| 51 | /// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling). | 49 | /// 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> { | |||
| 772 | Ok(()) | 772 | Ok(()) |
| 773 | } | 773 | } |
| 774 | 774 | ||
| 775 | pub fn write_str_blocking(&mut self, buf: &str) { | ||
| 776 | let _ = self.blocking_write(buf.as_bytes()); | ||
| 777 | } | ||
| 778 | |||
| 775 | /// Write data to LPUART TX without blocking. | 779 | /// Write data to LPUART TX without blocking. |
| 776 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { | 780 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { |
| 777 | for x in buf { | 781 | for x in buf { |
| @@ -901,6 +905,22 @@ impl<'a> Lpuart<'a, Blocking> { | |||
| 901 | self.tx.blocking_write(buf) | 905 | self.tx.blocking_write(buf) |
| 902 | } | 906 | } |
| 903 | 907 | ||
| 908 | pub fn write_byte(&mut self, byte: u8) { | ||
| 909 | _ = self.tx.write_byte(byte); | ||
| 910 | } | ||
| 911 | |||
| 912 | pub fn read_byte_blocking(&mut self) -> u8 { | ||
| 913 | loop { | ||
| 914 | if let Ok(b) = self.rx.read_byte() { | ||
| 915 | return b; | ||
| 916 | } | ||
| 917 | } | ||
| 918 | } | ||
| 919 | |||
| 920 | pub fn write_str_blocking(&mut self, buf: &str) { | ||
| 921 | self.tx.write_str_blocking(buf); | ||
| 922 | } | ||
| 923 | |||
| 904 | /// Write data to LPUART TX without blocking | 924 | /// Write data to LPUART TX without blocking |
| 905 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { | 925 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { |
| 906 | self.tx.write(buf) | 926 | 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 { | |||
| 534 | bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, | 534 | bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, |
| 535 | EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK, | 535 | EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK, |
| 536 | }; | 536 | }; |
| 537 | use crate::pac; | 537 | use crate::{clocks::{enable_and_reset, periph_helpers::{OsTimerConfig, OstimerClockSel}, PoweredClock}, pac, peripherals::OSTIMER0}; |
| 538 | pub struct Driver; | 538 | pub struct Driver; |
| 539 | static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); | 539 | static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); |
| 540 | 540 | ||
| @@ -621,6 +621,14 @@ pub mod time_driver { | |||
| 621 | /// Note: The frequency parameter is currently accepted for API compatibility. | 621 | /// Note: The frequency parameter is currently accepted for API compatibility. |
| 622 | /// The embassy_time_driver macro handles driver registration automatically. | 622 | /// The embassy_time_driver macro handles driver registration automatically. |
| 623 | pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { | 623 | pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { |
| 624 | let _clock_freq = unsafe { | ||
| 625 | enable_and_reset::<OSTIMER0>(&OsTimerConfig { | ||
| 626 | power: PoweredClock::AlwaysEnabled, | ||
| 627 | source: OstimerClockSel::Clk1M, | ||
| 628 | }) | ||
| 629 | .expect("Enabling OsTimer clock should not fail") | ||
| 630 | }; | ||
| 631 | |||
| 624 | // Mask/clear at peripheral and set default MATCH | 632 | // Mask/clear at peripheral and set default MATCH |
| 625 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; | 633 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; |
| 626 | super::prime_match_registers(r); | 634 | 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 @@ | |||
| 1 | //! RTC DateTime driver. | 1 | //! RTC DateTime driver. |
| 2 | use core::sync::atomic::{AtomicBool, Ordering}; | 2 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | |||
| 6 | use crate::clocks::with_clocks; | ||
| 4 | use crate::pac; | 7 | use crate::pac; |
| 5 | use crate::pac::rtc0::cr::Um; | 8 | use crate::pac::rtc0::cr::Um; |
| 6 | 9 | ||
| @@ -9,7 +12,7 @@ type Regs = pac::rtc0::RegisterBlock; | |||
| 9 | static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false); | 12 | static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false); |
| 10 | 13 | ||
| 11 | // Token-based instance pattern like embassy-imxrt | 14 | // Token-based instance pattern like embassy-imxrt |
| 12 | pub trait Instance { | 15 | pub trait Instance: PeripheralType { |
| 13 | fn ptr() -> *const Regs; | 16 | fn ptr() -> *const Regs; |
| 14 | } | 17 | } |
| 15 | 18 | ||
| @@ -22,14 +25,6 @@ impl Instance for crate::peripherals::RTC0 { | |||
| 22 | } | 25 | } |
| 23 | } | 26 | } |
| 24 | 27 | ||
| 25 | // Also implement Instance for the Peri wrapper type | ||
| 26 | impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::RTC0> { | ||
| 27 | #[inline(always)] | ||
| 28 | fn ptr() -> *const Regs { | ||
| 29 | pac::Rtc0::ptr() | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | const DAYS_IN_A_YEAR: u32 = 365; | 28 | const DAYS_IN_A_YEAR: u32 = 365; |
| 34 | const SECONDS_IN_A_DAY: u32 = 86400; | 29 | const SECONDS_IN_A_DAY: u32 = 86400; |
| 35 | const SECONDS_IN_A_HOUR: u32 = 3600; | 30 | const SECONDS_IN_A_HOUR: u32 = 3600; |
| @@ -157,15 +152,26 @@ pub fn get_default_config() -> RtcConfig { | |||
| 157 | } | 152 | } |
| 158 | } | 153 | } |
| 159 | /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) | 154 | /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) |
| 160 | pub struct Rtc<I: Instance> { | 155 | pub struct Rtc<'a, I: Instance> { |
| 161 | _inst: core::marker::PhantomData<I>, | 156 | _inst: core::marker::PhantomData<&'a mut I>, |
| 162 | } | 157 | } |
| 163 | 158 | ||
| 164 | impl<I: Instance> Rtc<I> { | 159 | impl<'a, I: Instance> Rtc<'a, I> { |
| 165 | /// initialize RTC | 160 | /// initialize RTC |
| 166 | pub fn new(_inst: impl Instance, config: RtcConfig) -> Self { | 161 | pub fn new(_inst: Peri<'a, I>, config: RtcConfig) -> Self { |
| 167 | let rtc = unsafe { &*I::ptr() }; | 162 | let rtc = unsafe { &*I::ptr() }; |
| 168 | 163 | ||
| 164 | // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock | ||
| 165 | // on the vsys domain is active | ||
| 166 | let clocks = with_clocks(|c| { | ||
| 167 | c.clk_16k_vsys.clone() | ||
| 168 | }); | ||
| 169 | match clocks { | ||
| 170 | None => panic!("Clocks have not been initialized"), | ||
| 171 | Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"), | ||
| 172 | Some(Some(_)) => {} | ||
| 173 | } | ||
| 174 | |||
| 169 | /* RTC reset */ | 175 | /* RTC reset */ |
| 170 | rtc.cr().modify(|_, w| w.swr().set_bit()); | 176 | rtc.cr().modify(|_, w| w.swr().set_bit()); |
| 171 | rtc.cr().modify(|_, w| w.swr().clear_bit()); | 177 | 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 @@ | |||
| 1 | //! Minimal polling UART2 bring-up replicating MCUXpresso hello_world ordering. | ||
| 2 | //! WARNING: This is a narrow implementation only for debug console (115200 8N1). | ||
| 3 | |||
| 4 | // TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs | ||
| 5 | // are complete prior to release. | ||
| 6 | #![allow(clippy::missing_safety_doc)] | ||
| 7 | |||
| 8 | use core::cell::RefCell; | ||
| 9 | |||
| 10 | use cortex_m::interrupt::Mutex; | ||
| 11 | use embassy_sync::signal::Signal; | ||
| 12 | |||
| 13 | use crate::pac; | ||
| 14 | |||
| 15 | // svd2rust defines the shared LPUART RegisterBlock under lpuart0; all instances reuse it. | ||
| 16 | type Regs = pac::lpuart0::RegisterBlock; | ||
| 17 | |||
| 18 | // Token-based instance pattern like embassy-imxrt | ||
| 19 | pub trait Instance { | ||
| 20 | fn ptr() -> *const Regs; | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Token for LPUART2 provided by embassy-hal-internal peripherals macro. | ||
| 24 | pub type Lpuart2 = crate::peripherals::LPUART2; | ||
| 25 | impl Instance for crate::peripherals::LPUART2 { | ||
| 26 | #[inline(always)] | ||
| 27 | fn ptr() -> *const Regs { | ||
| 28 | pac::Lpuart2::ptr() | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | // Also implement Instance for the Peri wrapper type | ||
| 33 | impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::LPUART2> { | ||
| 34 | #[inline(always)] | ||
| 35 | fn ptr() -> *const Regs { | ||
| 36 | pac::Lpuart2::ptr() | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /// UART configuration (explicit src_hz; no hardcoded frequencies) | ||
| 41 | #[derive(Copy, Clone)] | ||
| 42 | pub struct Config { | ||
| 43 | pub src_hz: u32, | ||
| 44 | pub baud: u32, | ||
| 45 | pub parity: Parity, | ||
| 46 | pub stop_bits: StopBits, | ||
| 47 | } | ||
| 48 | |||
| 49 | #[derive(Copy, Clone)] | ||
| 50 | pub enum Parity { | ||
| 51 | None, | ||
| 52 | Even, | ||
| 53 | Odd, | ||
| 54 | } | ||
| 55 | #[derive(Copy, Clone)] | ||
| 56 | pub enum StopBits { | ||
| 57 | One, | ||
| 58 | Two, | ||
| 59 | } | ||
| 60 | |||
| 61 | impl Config { | ||
| 62 | pub fn new(src_hz: u32) -> Self { | ||
| 63 | Self { | ||
| 64 | src_hz, | ||
| 65 | baud: 115_200, | ||
| 66 | parity: Parity::None, | ||
| 67 | stop_bits: StopBits::One, | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Compute a valid (OSR, SBR) tuple for given source clock and baud. | ||
| 73 | /// Uses a functional fold approach to find the best OSR/SBR combination | ||
| 74 | /// with minimal baud rate error. | ||
| 75 | fn compute_osr_sbr(src_hz: u32, baud: u32) -> (u8, u16) { | ||
| 76 | let (best_osr, best_sbr, _best_err) = (8u32..=32).fold( | ||
| 77 | (16u8, 4u16, u32::MAX), // (best_osr, best_sbr, best_err) | ||
| 78 | |(best_osr, best_sbr, best_err), osr| { | ||
| 79 | let denom = baud.saturating_mul(osr); | ||
| 80 | if denom == 0 { | ||
| 81 | return (best_osr, best_sbr, best_err); | ||
| 82 | } | ||
| 83 | |||
| 84 | let sbr = (src_hz + denom / 2) / denom; // round | ||
| 85 | if sbr == 0 || sbr > 0x1FFF { | ||
| 86 | return (best_osr, best_sbr, best_err); | ||
| 87 | } | ||
| 88 | |||
| 89 | let actual = src_hz / (osr * sbr); | ||
| 90 | let err = actual.abs_diff(baud); | ||
| 91 | |||
| 92 | // Update best if this is better, or same error but higher OSR | ||
| 93 | if err < best_err || (err == best_err && osr as u8 > best_osr) { | ||
| 94 | (osr as u8, sbr as u16, err) | ||
| 95 | } else { | ||
| 96 | (best_osr, best_sbr, best_err) | ||
| 97 | } | ||
| 98 | }, | ||
| 99 | ); | ||
| 100 | (best_osr, best_sbr) | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Minimal UART handle for a specific instance I (store the zero-sized token like embassy) | ||
| 104 | pub struct Uart<I: Instance> { | ||
| 105 | _inst: core::marker::PhantomData<I>, | ||
| 106 | } | ||
| 107 | |||
| 108 | impl<I: Instance> Uart<I> { | ||
| 109 | /// Create and initialize LPUART (reset + config). Clocks and pins must be prepared by the caller. | ||
| 110 | pub fn new(_inst: impl Instance, cfg: Config) -> Self { | ||
| 111 | let l = unsafe { &*I::ptr() }; | ||
| 112 | // 1) software reset pulse | ||
| 113 | l.global().write(|w| w.rst().reset()); | ||
| 114 | cortex_m::asm::delay(3); // Short delay for reset to take effect | ||
| 115 | l.global().write(|w| w.rst().no_effect()); | ||
| 116 | cortex_m::asm::delay(10); // Allow peripheral to stabilize after reset | ||
| 117 | // 2) BAUD | ||
| 118 | let (osr, sbr) = compute_osr_sbr(cfg.src_hz, cfg.baud); | ||
| 119 | l.baud().modify(|_, w| { | ||
| 120 | let w = match cfg.stop_bits { | ||
| 121 | StopBits::One => w.sbns().one(), | ||
| 122 | StopBits::Two => w.sbns().two(), | ||
| 123 | }; | ||
| 124 | // OSR field encodes (osr-1); use raw bits to avoid a long match on all variants | ||
| 125 | let raw_osr = osr.saturating_sub(1); | ||
| 126 | unsafe { w.osr().bits(raw_osr).sbr().bits(sbr) } | ||
| 127 | }); | ||
| 128 | // 3) CTRL baseline and parity | ||
| 129 | l.ctrl().write(|w| { | ||
| 130 | let w = w.ilt().from_stop().idlecfg().idle_2(); | ||
| 131 | let w = match cfg.parity { | ||
| 132 | Parity::None => w.pe().disabled(), | ||
| 133 | Parity::Even => w.pe().enabled().pt().even(), | ||
| 134 | Parity::Odd => w.pe().enabled().pt().odd(), | ||
| 135 | }; | ||
| 136 | w.re().enabled().te().enabled().rie().disabled() | ||
| 137 | }); | ||
| 138 | // 4) FIFOs and WATER: keep it simple for polling; disable FIFOs and set RX watermark to 0 | ||
| 139 | l.fifo().modify(|_, w| { | ||
| 140 | w.txfe() | ||
| 141 | .disabled() | ||
| 142 | .rxfe() | ||
| 143 | .disabled() | ||
| 144 | .txflush() | ||
| 145 | .txfifo_rst() | ||
| 146 | .rxflush() | ||
| 147 | .rxfifo_rst() | ||
| 148 | }); | ||
| 149 | l.water() | ||
| 150 | .modify(|_, w| unsafe { w.txwater().bits(0).rxwater().bits(0) }); | ||
| 151 | Self { | ||
| 152 | _inst: core::marker::PhantomData, | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Enable RX interrupts. The caller must ensure an appropriate IRQ handler is installed. | ||
| 157 | pub unsafe fn enable_rx_interrupts(&self) { | ||
| 158 | let l = &*I::ptr(); | ||
| 159 | l.ctrl().modify(|_, w| w.rie().enabled()); | ||
| 160 | } | ||
| 161 | |||
| 162 | #[inline(never)] | ||
| 163 | pub fn write_byte(&self, b: u8) { | ||
| 164 | let l = unsafe { &*I::ptr() }; | ||
| 165 | // Timeout after ~10ms at 12MHz (assuming 115200 baud, should be plenty) | ||
| 166 | const DATA_OFFSET: usize = 0x1C; // DATA register offset inside LPUART block | ||
| 167 | let data_ptr = unsafe { (I::ptr() as *mut u8).add(DATA_OFFSET) }; | ||
| 168 | for _ in 0..120000 { | ||
| 169 | if l.water().read().txcount().bits() == 0 { | ||
| 170 | unsafe { core::ptr::write_volatile(data_ptr, b) }; | ||
| 171 | return; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | // If timeout, skip the write to avoid hanging | ||
| 175 | } | ||
| 176 | |||
| 177 | #[inline(never)] | ||
| 178 | pub fn write_str_blocking(&self, s: &str) { | ||
| 179 | for &b in s.as_bytes() { | ||
| 180 | if b == b'\n' { | ||
| 181 | self.write_byte(b'\r'); | ||
| 182 | } | ||
| 183 | self.write_byte(b); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | pub fn read_byte_blocking(&self) -> u8 { | ||
| 187 | let l = unsafe { &*I::ptr() }; | ||
| 188 | while !l.stat().read().rdrf().is_rxdata() {} | ||
| 189 | (l.data().read().bits() & 0xFF) as u8 | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | // Simple ring buffer for UART RX data | ||
| 194 | const RX_BUFFER_SIZE: usize = 256; | ||
| 195 | pub struct RingBuffer { | ||
| 196 | buffer: [u8; RX_BUFFER_SIZE], | ||
| 197 | read_idx: usize, | ||
| 198 | write_idx: usize, | ||
| 199 | count: usize, | ||
| 200 | } | ||
| 201 | |||
| 202 | impl Default for RingBuffer { | ||
| 203 | fn default() -> Self { | ||
| 204 | Self::new() | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | impl RingBuffer { | ||
| 209 | pub const fn new() -> Self { | ||
| 210 | Self { | ||
| 211 | buffer: [0; RX_BUFFER_SIZE], | ||
| 212 | read_idx: 0, | ||
| 213 | write_idx: 0, | ||
| 214 | count: 0, | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | pub fn push(&mut self, data: u8) -> bool { | ||
| 219 | if self.count >= RX_BUFFER_SIZE { | ||
| 220 | return false; // Buffer full | ||
| 221 | } | ||
| 222 | self.buffer[self.write_idx] = data; | ||
| 223 | self.write_idx = (self.write_idx + 1) % RX_BUFFER_SIZE; | ||
| 224 | self.count += 1; | ||
| 225 | true | ||
| 226 | } | ||
| 227 | |||
| 228 | pub fn pop(&mut self) -> Option<u8> { | ||
| 229 | if self.count == 0 { | ||
| 230 | return None; | ||
| 231 | } | ||
| 232 | let data = self.buffer[self.read_idx]; | ||
| 233 | self.read_idx = (self.read_idx + 1) % RX_BUFFER_SIZE; | ||
| 234 | self.count -= 1; | ||
| 235 | Some(data) | ||
| 236 | } | ||
| 237 | |||
| 238 | pub fn is_empty(&self) -> bool { | ||
| 239 | self.count == 0 | ||
| 240 | } | ||
| 241 | |||
| 242 | pub fn len(&self) -> usize { | ||
| 243 | self.count | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | // Global RX buffer shared between interrupt handler and UART instance | ||
| 248 | static RX_BUFFER: Mutex<RefCell<RingBuffer>> = Mutex::new(RefCell::new(RingBuffer::new())); | ||
| 249 | static RX_SIGNAL: Signal<embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex, ()> = Signal::new(); | ||
| 250 | |||
| 251 | // Debug counter for interrupt handler calls | ||
| 252 | static mut INTERRUPT_COUNT: u32 = 0; | ||
| 253 | |||
| 254 | impl<I: Instance> Uart<I> { | ||
| 255 | /// Read a byte asynchronously using interrupts | ||
| 256 | pub async fn read_byte_async(&self) -> u8 { | ||
| 257 | loop { | ||
| 258 | // Check if we have data in the buffer | ||
| 259 | let byte = cortex_m::interrupt::free(|cs| { | ||
| 260 | let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); | ||
| 261 | buffer.pop() | ||
| 262 | }); | ||
| 263 | |||
| 264 | if let Some(byte) = byte { | ||
| 265 | return byte; | ||
| 266 | } | ||
| 267 | |||
| 268 | // Wait for the interrupt signal | ||
| 269 | RX_SIGNAL.wait().await; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | /// Check if there's data available in the RX buffer | ||
| 274 | pub fn rx_data_available(&self) -> bool { | ||
| 275 | cortex_m::interrupt::free(|cs| { | ||
| 276 | let buffer = RX_BUFFER.borrow(cs).borrow(); | ||
| 277 | !buffer.is_empty() | ||
| 278 | }) | ||
| 279 | } | ||
| 280 | |||
| 281 | /// Try to read a byte from RX buffer (non-blocking) | ||
| 282 | pub fn try_read_byte(&self) -> Option<u8> { | ||
| 283 | cortex_m::interrupt::free(|cs| { | ||
| 284 | let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); | ||
| 285 | buffer.pop() | ||
| 286 | }) | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Type-level handler for LPUART2 interrupts, compatible with bind_interrupts!. | ||
| 291 | pub struct UartInterruptHandler; | ||
| 292 | |||
| 293 | impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::LPUART2> for UartInterruptHandler { | ||
| 294 | unsafe fn on_interrupt() { | ||
| 295 | INTERRUPT_COUNT += 1; | ||
| 296 | |||
| 297 | let lpuart = &*pac::Lpuart2::ptr(); | ||
| 298 | |||
| 299 | // Check if we have RX data | ||
| 300 | if lpuart.stat().read().rdrf().is_rxdata() { | ||
| 301 | // Read the data byte | ||
| 302 | let data = (lpuart.data().read().bits() & 0xFF) as u8; | ||
| 303 | |||
| 304 | // Store in ring buffer | ||
| 305 | cortex_m::interrupt::free(|cs| { | ||
| 306 | let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); | ||
| 307 | if buffer.push(data) { | ||
| 308 | // Data added successfully, signal waiting tasks | ||
| 309 | RX_SIGNAL.signal(()); | ||
| 310 | } | ||
| 311 | }); | ||
| 312 | } | ||
| 313 | // Always clear any error flags that might cause spurious interrupts | ||
| 314 | let _ = lpuart.stat().read(); | ||
| 315 | } | ||
| 316 | } | ||
