1#![no_std]
2
3use core::fmt::{self, Write as FmtWrite};
4use core::hint::spin_loop;
5
6use cortex_m::asm;
7use heapless::spsc::Queue;
8#[cfg(feature = "rp2040")]
9use pio_proc::pio_asm;
10
11#[cfg(feature = "rp2040")]
12use rp2040_hal::pio::{
13 PIOBuilder, PIOExt, PinDir, Running, Rx, ShiftDirection, StateMachine, StateMachineIndex, Tx,
14 UninitStateMachine, PIO,
15};
16
17#[cfg(feature = "rp2350")]
18use rp235x_hal::pio::{
19 PIOBuilder, PIOExt, PinDir, Running, Rx, ShiftDirection, StateMachine, StateMachineIndex, Tx,
20 UninitStateMachine, PIO,
21};
22
23#[cfg(all(feature = "rp2350", target_arch = "arm", target_os = "none"))]
24use pio::pio_asm;
25
26#[cfg(all(feature = "rp2040", feature = "rp2350"))]
27compile_error!("features `rp2040` and `rp2350` cannot be enabled at the same time");
28
29#[cfg(not(any(feature = "rp2040", feature = "rp2350")))]
30compile_error!("either feature `rp2040` or `rp2350` must be enabled");
31
32#[cfg(all(feature = "rp2040", not(target_arch = "arm")))]
33compile_error!("`rp2040` only supports ARM targets");
34
35const DEFAULT_TX_BUF_SIZE: usize = 512;
36const DEFAULT_RX_BUF_SIZE: usize = 512;
37
38const DEFAULT_TX_PIPELINE_CHARS: u32 = 5;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum InitError {
43 InvalidBaud,
44 UnsupportedConfig,
45 InstallTxProgram,
46 InstallRxProgram,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum DataBits {
51 Five = 5,
52 Six = 6,
53 Seven = 7,
54 Eight = 8,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum StopBits {
59 One,
60 Two,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum Parity {
65 None,
66 Even,
67 Odd,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum ServiceMode {
72 Polling,
73 InterruptDriven,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub struct UartConfig {
78 pub baud: u32,
79 pub data_bits: DataBits,
80 pub parity: Parity,
81 pub stop_bits: StopBits,
82 pub auto_echo: bool,
83 pub service_mode: ServiceMode,
84 pub tx_pipeline_chars: u32,
85}
86
87impl Default for UartConfig {
88 fn default() -> Self {
89 Self {
90 baud: 115_200,
91 data_bits: DataBits::Eight,
92 parity: Parity::None,
93 stop_bits: StopBits::One,
94 auto_echo: false,
95 service_mode: ServiceMode::Polling,
96 tx_pipeline_chars: DEFAULT_TX_PIPELINE_CHARS,
97 }
98 }
99}
100
101impl UartConfig {
102 #[inline]
103 pub fn frame_bits(&self) -> u32 {
104 let data_bits = self.data_bits as u32;
105 let parity_bits = match self.parity {
106 Parity::None => 0,
107 Parity::Even | Parity::Odd => 1,
108 };
109 let stop_bits = match self.stop_bits {
110 StopBits::One => 1,
111 StopBits::Two => 2,
112 };
113
114 1 + data_bits + parity_bits + stop_bits
115 }
116
117 #[inline]
118 pub fn is_supported_by_current_backend(&self) -> bool {
119 self.baud != 0
120 && self.data_bits == DataBits::Eight
121 && self.parity == Parity::None
122 && self.stop_bits == StopBits::One
123 }
124}
125
126pub struct RpPioSerial<
127 P: PIOExt,
128 SMTX: StateMachineIndex,
129 SMRX: StateMachineIndex,
130 const TX_BUF: usize = DEFAULT_TX_BUF_SIZE,
131 const RX_BUF: usize = DEFAULT_RX_BUF_SIZE,
132> {
133 _sm_tx: StateMachine<(P, SMTX), Running>,
134 _sm_rx: StateMachine<(P, SMRX), Running>,
135 tx: Tx<(P, SMTX)>,
136 rx: Rx<(P, SMRX)>,
137
138 tx_buf: Queue<u8, TX_BUF>,
139 rx_buf: Queue<u8, RX_BUF>,
140
141 config: UartConfig,
142 clock_hz: u32,
143 char_delay_cycles: u32,
144
145 dropped_tx: u32,
146 dropped_rx: u32,
147
148 hw_drain_pending: bool,
151}
152
153impl<
154 P: PIOExt,
155 SMTX: StateMachineIndex,
156 SMRX: StateMachineIndex,
157 const TX_BUF: usize,
158 const RX_BUF: usize,
159 > RpPioSerial<P, SMTX, SMRX, TX_BUF, RX_BUF>
160{
161 pub fn new(
169 pio: &mut PIO<P>,
170 sm_tx: UninitStateMachine<(P, SMTX)>,
171 sm_rx: UninitStateMachine<(P, SMRX)>,
172 tx_pin: u8,
173 rx_pin: u8,
174 clock_hz: u32,
175 config: UartConfig,
176 ) -> Result<Self, InitError> {
177 if clock_hz == 0 || config.baud == 0 {
178 return Err(InitError::InvalidBaud);
179 }
180
181 if !config.is_supported_by_current_backend() {
182 return Err(InitError::UnsupportedConfig);
183 }
184
185 let (div_int, div_frac) = calc_clkdiv(clock_hz, config.baud)?;
186 let char_delay_cycles = calc_char_delay_cycles(clock_hz, config.frame_bits(), config.baud);
187
188 let tx_program = pio_asm!(
195 ".side_set 1 opt"
196 "pull side 1 [7]"
197 "set x, 7 side 0 [7]"
198 "bitloop:"
199 "out pins, 1"
200 "jmp x-- bitloop [6]"
201 );
202
203 let installed_tx = pio
204 .install(&tx_program.program)
205 .map_err(|_| InitError::InstallTxProgram)?;
206
207 let (mut sm_tx, _, tx) = PIOBuilder::from_installed_program(installed_tx)
208 .out_pins(tx_pin, 1)
209 .side_set_pin_base(tx_pin)
210 .clock_divisor_fixed_point(div_int, div_frac)
211 .out_shift_direction(ShiftDirection::Right)
212 .autopull(false)
213 .build(sm_tx);
215
216 sm_tx.set_pindirs([(tx_pin, PinDir::Output)]);
217 let sm_tx = sm_tx.start();
218
219 let rx_program = pio_asm!(
224 "idle_wait:"
225 "wait 1 pin 0" "start_wait:"
228 "wait 0 pin 0" "set x, 7 [10]" "bitloop:"
233 "in pins, 1"
234 "jmp x-- bitloop [6]"
235
236 "push"
237 "jmp idle_wait"
238 );
239 let installed_rx = pio
240 .install(&rx_program.program)
241 .map_err(|_| InitError::InstallRxProgram)?;
242
243 let (mut sm_rx, rx, _) = PIOBuilder::from_installed_program(installed_rx)
244 .in_pin_base(rx_pin)
245 .clock_divisor_fixed_point(div_int, div_frac)
246 .in_shift_direction(ShiftDirection::Left)
247 .autopush(false)
248 .build(sm_rx);
249
250 sm_rx.set_pindirs([(rx_pin, PinDir::Input)]);
251 let sm_rx = sm_rx.start();
252
253 Ok(Self {
254 _sm_tx: sm_tx,
255 _sm_rx: sm_rx,
256 tx,
257 rx,
258 tx_buf: Queue::new(),
259 rx_buf: Queue::new(),
260 config,
261 clock_hz,
262 char_delay_cycles,
263 dropped_tx: 0,
264 dropped_rx: 0,
265 hw_drain_pending: false,
266 })
267 }
268
269 pub fn config(&self) -> UartConfig {
274 self.config
275 }
276
277 pub fn baud(&self) -> u32 {
278 self.config.baud
279 }
280
281 pub fn clock_hz(&self) -> u32 {
282 self.clock_hz
283 }
284
285 pub fn data_bits(&self) -> DataBits {
286 self.config.data_bits
287 }
288
289 pub fn parity(&self) -> Parity {
290 self.config.parity
291 }
292
293 pub fn stop_bits(&self) -> StopBits {
294 self.config.stop_bits
295 }
296
297 pub fn auto_echo(&self) -> bool {
298 self.config.auto_echo
299 }
300
301 pub fn service_mode(&self) -> ServiceMode {
302 self.config.service_mode
303 }
304
305 pub fn set_auto_echo(&mut self, enable: bool) {
306 self.config.auto_echo = enable;
307 }
308
309 pub fn set_service_mode(&mut self, mode: ServiceMode) {
310 self.config.service_mode = mode;
311 }
312
313 pub fn dropped_tx(&self) -> u32 {
314 self.dropped_tx
315 }
316
317 pub fn dropped_rx(&self) -> u32 {
318 self.dropped_rx
319 }
320
321 pub fn available(&self) -> usize {
322 self.rx_buf.len()
323 }
324
325 pub fn tx_pending(&self) -> usize {
326 self.tx_buf.len()
327 }
328
329 pub fn clear_rx(&mut self) {
330 while self.rx_buf.dequeue().is_some() {}
331 }
332
333 pub fn clear_tx_buffer(&mut self) {
334 while self.tx_buf.dequeue().is_some() {}
335 }
336
337 pub fn poll(&mut self) {
343 self.pump_rx();
344 self.flush_tx_nonblocking();
345 }
346
347 pub fn on_interrupt(&mut self) {
351 self.pump_rx();
352 self.flush_tx_nonblocking();
353 }
354
355 pub fn kick_tx(&mut self) {
357 self.flush_tx_nonblocking();
358 }
359
360 pub fn write(&mut self, data: &[u8]) -> usize {
368 let n = self.enqueue_tx(data);
369 self.flush_tx_nonblocking();
370 n
371 }
372
373 pub fn write_byte(&mut self, b: u8) -> bool {
374 self.write(&[b]) == 1
375 }
376
377 pub fn print(&mut self, s: &str) -> usize {
378 self.write(s.as_bytes())
379 }
380
381 pub fn println(&mut self, s: &str) -> usize {
382 let mut n = 0;
383 n += self.write(s.as_bytes());
384 n += self.write(b"\r\n");
385 n
386 }
387
388 pub fn flush_blocking(&mut self) {
394 while let Some(b) = self.tx_buf.dequeue() {
395 while self.tx.is_full() {
396 spin_loop();
397 }
398 self.tx.write(u32::from(b));
399 self.hw_drain_pending = true;
400 }
401
402 if self.hw_drain_pending {
403 self.wait_chars(self.config.tx_pipeline_chars.max(1));
404 self.hw_drain_pending = false;
405 }
406 }
407
408 pub fn write_all_blocking(&mut self, data: &[u8]) {
413 self.flush_blocking();
414
415 for &b in data {
416 while self.tx.is_full() {
417 spin_loop();
418 }
419 self.tx.write(u32::from(b));
420 self.hw_drain_pending = true;
421 }
422
423 if self.hw_drain_pending {
424 self.wait_chars(self.config.tx_pipeline_chars.max(1));
425 self.hw_drain_pending = false;
426 }
427 }
428
429 pub fn write_byte_blocking(&mut self, b: u8) {
430 self.write_all_blocking(&[b]);
431 }
432
433 pub fn print_blocking(&mut self, s: &str) {
434 self.write_all_blocking(s.as_bytes());
435 }
436
437 pub fn println_blocking(&mut self, s: &str) {
438 self.write_all_blocking(s.as_bytes());
439 self.write_all_blocking(b"\r\n");
440 }
441
442 pub fn write_repeated_blocking(&mut self, byte: u8, count: usize) {
444 self.flush_blocking();
445
446 for _ in 0..count {
447 while self.tx.is_full() {
448 spin_loop();
449 }
450 self.tx.write(u32::from(byte));
451 self.hw_drain_pending = true;
452 }
453
454 if self.hw_drain_pending {
455 self.wait_chars(self.config.tx_pipeline_chars.max(1));
456 self.hw_drain_pending = false;
457 }
458 }
459
460 pub fn fmt_write_blocking(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
462 struct BlockingFmt<
463 'a,
464 P: PIOExt,
465 SMTX: StateMachineIndex,
466 SMRX: StateMachineIndex,
467 const TX_BUF: usize,
468 const RX_BUF: usize,
469 > {
470 serial: &'a mut RpPioSerial<P, SMTX, SMRX, TX_BUF, RX_BUF>,
471 }
472
473 impl<
474 'a,
475 P: PIOExt,
476 SMTX: StateMachineIndex,
477 SMRX: StateMachineIndex,
478 const TX_BUF: usize,
479 const RX_BUF: usize,
480 > FmtWrite for BlockingFmt<'a, P, SMTX, SMRX, TX_BUF, RX_BUF>
481 {
482 fn write_str(&mut self, s: &str) -> fmt::Result {
483 self.serial.write_all_blocking(s.as_bytes());
484 Ok(())
485 }
486 }
487
488 let mut w = BlockingFmt { serial: self };
489 w.write_fmt(args)
490 }
491
492 pub fn read(&mut self, buf: &mut [u8]) -> usize {
498 self.pump_rx();
499
500 let mut n = 0;
501 while n < buf.len() {
502 match self.rx_buf.dequeue() {
503 Some(b) => {
504 buf[n] = b;
505 n += 1;
506 }
507 None => break,
508 }
509 }
510 n
511 }
512
513 pub fn read_byte(&mut self) -> Option<u8> {
514 self.pump_rx();
515 self.rx_buf.dequeue()
516 }
517
518 pub fn read_byte_blocking(&mut self) -> u8 {
520 loop {
521 self.pump_rx();
522 if let Some(b) = self.rx_buf.dequeue() {
523 return b;
524 }
525 spin_loop();
526 }
527 }
528
529 pub fn read_exact_blocking(&mut self, buf: &mut [u8]) {
531 for b in buf {
532 *b = self.read_byte_blocking();
533 }
534 }
535
536 fn enqueue_tx(&mut self, data: &[u8]) -> usize {
541 let mut n = 0;
542 for &b in data {
543 if self.tx_buf.enqueue(b).is_ok() {
544 n += 1;
545 } else {
546 self.dropped_tx = self.dropped_tx.wrapping_add(1);
547 break;
548 }
549 }
550 n
551 }
552
553 fn pump_rx(&mut self) {
554 while let Some(word) = self.rx.read() {
555 let b = ((word & 0xFF) as u8).reverse_bits();
556 if self.rx_buf.enqueue(b).is_err() {
559 self.dropped_rx = self.dropped_rx.wrapping_add(1);
560 }
561
562 if self.config.auto_echo {
563 if self.tx_buf.enqueue(b).is_err() {
564 self.dropped_tx = self.dropped_tx.wrapping_add(1);
565 }
566 }
567 }
568 }
569
570 fn flush_tx_nonblocking(&mut self) {
571 while !self.tx.is_full() {
572 let Some(b) = self.tx_buf.dequeue() else {
573 break;
574 };
575 self.tx.write(u32::from(b));
576 self.hw_drain_pending = true;
577 }
578 }
579
580 #[inline]
581 fn wait_chars(&self, chars: u32) {
582 let cycles = self.char_delay_cycles.saturating_mul(chars.max(1));
583 asm::delay(cycles);
584 }
585}
586
587impl<
592 P: PIOExt,
593 SMTX: StateMachineIndex,
594 SMRX: StateMachineIndex,
595 const TX_BUF: usize,
596 const RX_BUF: usize,
597 > FmtWrite for RpPioSerial<P, SMTX, SMRX, TX_BUF, RX_BUF>
598{
599 fn write_str(&mut self, s: &str) -> fmt::Result {
600 let _ = self.write(s.as_bytes());
601 Ok(())
602 }
603}
604
605#[macro_export]
610macro_rules! pio_print {
611 ($serial:expr, $($tt:tt)*) => {{
612 let _ = core::fmt::Write::write_fmt(&mut $serial, format_args!($($tt)*));
613 }};
614}
615
616#[macro_export]
617macro_rules! pio_println {
618 ($serial:expr) => {{
619 let _ = $serial.write(b"\r\n");
620 }};
621 ($serial:expr, $($tt:tt)*) => {{
622 let _ = core::fmt::Write::write_fmt(&mut $serial, format_args!($($tt)*));
623 let _ = $serial.write(b"\r\n");
624 }};
625}
626
627#[macro_export]
629macro_rules! pio_bprint {
630 ($serial:expr, $($tt:tt)*) => {{
631 let _ = $serial.fmt_write_blocking(format_args!($($tt)*));
632 }};
633}
634
635#[macro_export]
636macro_rules! pio_bprintln {
637 ($serial:expr) => {{
638 $serial.write_all_blocking(b"\r\n");
639 }};
640 ($serial:expr, $($tt:tt)*) => {{
641 let _ = $serial.fmt_write_blocking(format_args!($($tt)*));
642 $serial.write_all_blocking(b"\r\n");
643 }};
644}
645
646fn calc_clkdiv(clock_hz: u32, baud: u32) -> Result<(u16, u8), InitError> {
651 if baud == 0 {
652 return Err(InitError::InvalidBaud);
653 }
654
655 let denom = (baud as u64) * 8;
657 if denom == 0 {
658 return Err(InitError::InvalidBaud);
659 }
660
661 let div = ((clock_hz as u64) << 8) / denom;
662 let int = (div >> 8) as u16;
663 let frac = (div & 0xFF) as u8;
664
665 if int == 0 && frac == 0 {
666 return Err(InitError::InvalidBaud);
667 }
668
669 Ok((int, frac))
670}
671
672fn calc_char_delay_cycles(clock_hz: u32, frame_bits: u32, baud: u32) -> u32 {
673 let cycles = (clock_hz as u64).saturating_mul(frame_bits.max(1) as u64) / (baud.max(1) as u64);
674
675 cycles.max(1).min(u32::MAX as u64) as u32
676}