aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-11-14 18:43:27 +0100
committerJames Munns <[email protected]>2025-11-14 18:43:27 +0100
commit8cdccae3c6c4a805cf5003b1a859734c105d76e8 (patch)
tree7c605a58aa7e124bbed658dfc5f6822a25a83e98 /src
parente799d6c8956ed3ea5ced65d58c3065a22927ad10 (diff)
Continue working on examples
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs2
-rw-r--r--src/lpuart/mod.rs20
-rw-r--r--src/ostimer.rs10
-rw-r--r--src/rtc.rs32
-rw-r--r--src/uart.rs316
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;
13pub mod lpuart; 13pub mod lpuart;
14pub mod ostimer; 14pub mod ostimer;
15pub mod rtc; 15pub mod rtc;
16pub mod uart;
17 16
18embassy_hal_internal::peripherals!(PORT1, PORT2, PORT3, LPUART2, OSTIMER0, GPIO, PIO2_2, PIO2_3, GPIO3, RTC0, ADC1,); 17embassy_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;
45pub(crate) use mcxa_pac as pac; 44pub(crate) use mcxa_pac as pac;
46pub use ostimer::Ostimer0 as Ostimer0Token; 45pub use ostimer::Ostimer0 as Ostimer0Token;
47pub use rtc::Rtc0 as Rtc0Token; 46pub use rtc::Rtc0 as Rtc0Token;
48pub 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.
2use core::sync::atomic::{AtomicBool, Ordering}; 2use core::sync::atomic::{AtomicBool, Ordering};
3 3
4use embassy_hal_internal::{Peri, PeripheralType};
5
6use crate::clocks::with_clocks;
4use crate::pac; 7use crate::pac;
5use crate::pac::rtc0::cr::Um; 8use crate::pac::rtc0::cr::Um;
6 9
@@ -9,7 +12,7 @@ type Regs = pac::rtc0::RegisterBlock;
9static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false); 12static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false);
10 13
11// Token-based instance pattern like embassy-imxrt 14// Token-based instance pattern like embassy-imxrt
12pub trait Instance { 15pub 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
26impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::RTC0> {
27 #[inline(always)]
28 fn ptr() -> *const Regs {
29 pac::Rtc0::ptr()
30 }
31}
32
33const DAYS_IN_A_YEAR: u32 = 365; 28const DAYS_IN_A_YEAR: u32 = 365;
34const SECONDS_IN_A_DAY: u32 = 86400; 29const SECONDS_IN_A_DAY: u32 = 86400;
35const SECONDS_IN_A_HOUR: u32 = 3600; 30const 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)
160pub struct Rtc<I: Instance> { 155pub struct Rtc<'a, I: Instance> {
161 _inst: core::marker::PhantomData<I>, 156 _inst: core::marker::PhantomData<&'a mut I>,
162} 157}
163 158
164impl<I: Instance> Rtc<I> { 159impl<'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
8use core::cell::RefCell;
9
10use cortex_m::interrupt::Mutex;
11use embassy_sync::signal::Signal;
12
13use crate::pac;
14
15// svd2rust defines the shared LPUART RegisterBlock under lpuart0; all instances reuse it.
16type Regs = pac::lpuart0::RegisterBlock;
17
18// Token-based instance pattern like embassy-imxrt
19pub trait Instance {
20 fn ptr() -> *const Regs;
21}
22
23/// Token for LPUART2 provided by embassy-hal-internal peripherals macro.
24pub type Lpuart2 = crate::peripherals::LPUART2;
25impl 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
33impl 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)]
42pub 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)]
50pub enum Parity {
51 None,
52 Even,
53 Odd,
54}
55#[derive(Copy, Clone)]
56pub enum StopBits {
57 One,
58 Two,
59}
60
61impl 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.
75fn 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)
104pub struct Uart<I: Instance> {
105 _inst: core::marker::PhantomData<I>,
106}
107
108impl<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
194const RX_BUFFER_SIZE: usize = 256;
195pub struct RingBuffer {
196 buffer: [u8; RX_BUFFER_SIZE],
197 read_idx: usize,
198 write_idx: usize,
199 count: usize,
200}
201
202impl Default for RingBuffer {
203 fn default() -> Self {
204 Self::new()
205 }
206}
207
208impl 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
248static RX_BUFFER: Mutex<RefCell<RingBuffer>> = Mutex::new(RefCell::new(RingBuffer::new()));
249static RX_SIGNAL: Signal<embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex, ()> = Signal::new();
250
251// Debug counter for interrupt handler calls
252static mut INTERRUPT_COUNT: u32 = 0;
253
254impl<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!.
291pub struct UartInterruptHandler;
292
293impl 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}