omnitiles/hw/
usart.rs

1// SPDX-License-Identifier: MIT
2// © 2025–2026 Christopher Liu
3
4//! USART abstraction layer.
5//!
6//! Provides several printing helpers for hex, decimal, and ASCII strings to print to an attached
7//! debug terminal.
8//!
9//! Note: When using `writeln!`, be sure to include `\r` (CR) in the format string to ensure correct
10//! line endings on the terminal.
11//!
12//! To access the terminal on the host machine, connect to the debug USB port and use
13//! ```
14//! $ screen /dev/tty.usbmodem* <baud_rate>
15//! ```
16//!
17//! To close the debug terminal, press `Ctrl+A` then `Ctrl+\` then `y`.
18
19use core::fmt;
20use nb::block;
21
22use stm32f7xx_hal::{
23    prelude::*,
24    serial::{Instance, Pins, Rx, Serial, Tx},
25};
26
27pub struct Usart<U: Instance> {
28    tx: Tx<U>,
29    rx: Rx<U>,
30}
31
32impl<U: Instance> Usart<U> {
33    pub fn new<PINS: Pins<U>>(serial: Serial<U, PINS>) -> Self {
34        let (tx, rx) = serial.split();
35        Self { tx, rx }
36    }
37
38    /// Read a single byte from the RX buffer, if available (non-blocking).
39    pub fn read_byte(&mut self) -> Option<u8> {
40        match self.rx.read() {
41            Ok(b) => Some(b),
42            Err(nb::Error::WouldBlock) => None,
43            Err(_) => None,
44        }
45    }
46
47    #[inline]
48    pub fn write_byte(&mut self, b: u8) {
49        let _ = block!(self.tx.write(b));
50    }
51
52    pub fn write_str(&mut self, s: &str) {
53        for &b in s.as_bytes() {
54            self.write_byte(b);
55        }
56    }
57
58    /// Write string and CRLF terminator.
59    #[inline]
60    pub fn println(&mut self, s: &str) {
61        self.write_str(s);
62        self.write_str("\r\n");
63    }
64
65    /// Block until the hardware TX FIFO/drain is flushed.
66    #[inline]
67    pub fn flush(&mut self) {
68        let _ = block!(self.tx.flush());
69    }
70
71    pub fn print_hex_u8(&mut self, n: u8) {
72        const HEX: &[u8; 16] = b"0123456789ABCDEF";
73        self.write_str("0x");
74        self.write_byte(HEX[((n >> 4) & 0xF) as usize]);
75        self.write_byte(HEX[(n & 0xF) as usize]);
76    }
77
78    pub fn print_hex_u16(&mut self, n: u16) {
79        const HEX: &[u8; 16] = b"0123456789ABCDEF";
80        self.write_str("0x");
81        for shift in (0..=12).rev().step_by(4) {
82            self.write_byte(HEX[((n >> shift) & 0xF) as usize]);
83        }
84    }
85
86    pub fn print_hex_u32(&mut self, n: u32) {
87        const HEX: &[u8; 16] = b"0123456789ABCDEF";
88        self.write_str("0x");
89        for (i, shift) in (0..=28).rev().step_by(4).enumerate() {
90            if i == 4 {
91                self.write_byte(b'_');
92            }
93            self.write_byte(HEX[((n >> shift) & 0xF) as usize]);
94        }
95    }
96
97    pub fn print_u32(&mut self, mut n: u32) {
98        let mut buf = [0u8; 10];
99        let mut i = buf.len();
100        if n == 0 {
101            self.write_byte(b'0');
102            return;
103        }
104        while n > 0 {
105            i -= 1;
106            buf[i] = b'0' + (n % 10) as u8;
107            n /= 10;
108        }
109        for &b in &buf[i..] {
110            self.write_byte(b);
111        }
112    }
113}
114
115// Implement `core::fmt::Write` so we can use `write!` / `writeln!` on `Usart`.
116impl<U: Instance> fmt::Write for Usart<U> {
117    fn write_str(&mut self, s: &str) -> fmt::Result {
118        Usart::write_str(self, s);
119        Ok(())
120    }
121}