Skip to main content

rp2040_hal/uart/
writer.rs

1//! Universal Asynchronous Receiver Transmitter - Transmitter Code
2//!
3//! This module is for transmitting data with a UART.
4
5use super::{FifoWatermark, UartDevice, ValidUartPinout};
6use crate::dma::{EndlessWriteTarget, WriteTarget};
7use crate::pac::uart0::RegisterBlock;
8use core::fmt;
9use core::{convert::Infallible, marker::PhantomData};
10use embedded_hal_0_2::serial::Write as Write02;
11use embedded_hal_nb::serial::{ErrorType, Write};
12use nb::Error::*;
13
14/// Set tx FIFO watermark
15///
16/// See DS: Table 423
17pub fn set_tx_watermark(rb: &RegisterBlock, watermark: FifoWatermark) {
18    let wm = match watermark {
19        FifoWatermark::Bytes4 => 4,
20        FifoWatermark::Bytes8 => 3,
21        FifoWatermark::Bytes16 => 2,
22        FifoWatermark::Bytes24 => 1,
23        FifoWatermark::Bytes28 => 0,
24    };
25    rb.uartifls()
26        .modify(|_r, w| unsafe { w.txiflsel().bits(wm) });
27}
28
29/// Returns `Err(WouldBlock)` if the UART is still busy transmitting data.
30/// It returns Ok(()) when the TX fifo and the transmit shift register are empty
31/// and the last stop bit is sent.
32pub(crate) fn transmit_flushed(rb: &RegisterBlock) -> nb::Result<(), Infallible> {
33    if rb.uartfr().read().busy().bit_is_set() {
34        Err(WouldBlock)
35    } else {
36        Ok(())
37    }
38}
39
40/// Returns `true` if the TX FIFO has space, or false if it is full
41pub(crate) fn uart_is_writable(rb: &RegisterBlock) -> bool {
42    rb.uartfr().read().txff().bit_is_clear()
43}
44
45/// Returns `true` if the UART is busy transmitting data, `false` after all
46/// bits (including stop bits) have been transmitted.
47pub(crate) fn uart_is_busy(rb: &RegisterBlock) -> bool {
48    rb.uartfr().read().busy().bit_is_set()
49}
50
51/// Writes bytes to the UART.
52///
53/// This function writes as long as it can. As soon that the FIFO is full,
54/// if:
55/// - 0 bytes were written, a WouldBlock Error is returned
56/// - some bytes were written, it is deemed to be a success
57///
58/// Upon success, the remaining (unwritten) slice is returned.
59pub(crate) fn write_raw<'d>(
60    rb: &RegisterBlock,
61    data: &'d [u8],
62) -> nb::Result<&'d [u8], Infallible> {
63    let mut bytes_written = 0;
64
65    for c in data {
66        if !uart_is_writable(rb) {
67            if bytes_written == 0 {
68                return Err(WouldBlock);
69            } else {
70                return Ok(&data[bytes_written..]);
71            }
72        }
73
74        rb.uartdr().write(|w| unsafe {
75            w.data().bits(*c);
76            w
77        });
78
79        bytes_written += 1;
80    }
81    Ok(&data[bytes_written..])
82}
83
84/// Writes bytes to the UART.
85///
86/// This function blocks until the full buffer has been sent.
87pub(crate) fn write_full_blocking(rb: &RegisterBlock, data: &[u8]) {
88    let mut temp = data;
89
90    while !temp.is_empty() {
91        temp = match write_raw(rb, temp) {
92            Ok(remaining) => remaining,
93            Err(WouldBlock) => continue,
94            Err(_) => unreachable!(),
95        }
96    }
97}
98
99/// Enables the Transmit Interrupt.
100///
101/// The relevant UARTx IRQ will fire when there is space in the transmit FIFO.
102pub(crate) fn enable_tx_interrupt(rb: &RegisterBlock) {
103    // Access the UART FIFO Level Select. We set the TX FIFO trip level
104    // to be when it's half-empty..
105
106    // 2 means '<= 1/2 full'.
107    rb.uartifls()
108        .modify(|_r, w| unsafe { w.txiflsel().bits(2) });
109
110    // Access the UART Interrupt Mask Set/Clear register. Setting a bit
111    // high enables the interrupt.
112
113    // We set the TX interrupt. This means we will get an interrupt when
114    // the TX FIFO level is triggered. This means we don't have to
115    // interrupt on every single byte, but can make use of the hardware
116    // FIFO.
117    rb.uartimsc().modify(|_r, w| {
118        w.txim().set_bit();
119        w
120    });
121}
122
123/// Disables the Transmit Interrupt.
124pub(crate) fn disable_tx_interrupt(rb: &RegisterBlock) {
125    // Access the UART Interrupt Mask Set/Clear register. Setting a bit
126    // low disables the interrupt.
127
128    rb.uartimsc().modify(|_r, w| {
129        w.txim().clear_bit();
130        w
131    });
132}
133
134/// Half of an [`UartPeripheral`] that is only capable of writing. Obtained by calling [`UartPeripheral::split()`]
135///
136/// [`UartPeripheral`]: struct.UartPeripheral.html
137/// [`UartPeripheral::split()`]: struct.UartPeripheral.html#method.split
138pub struct Writer<D: UartDevice, P: ValidUartPinout<D>> {
139    pub(super) device: D,
140    pub(super) device_marker: PhantomData<D>,
141    pub(super) pins: PhantomData<P>,
142}
143
144impl<D: UartDevice, P: ValidUartPinout<D>> Writer<D, P> {
145    /// Writes bytes to the UART.
146    ///
147    /// This function writes as long as it can. As soon that the FIFO is full,
148    /// if:
149    /// - 0 bytes were written, a WouldBlock Error is returned
150    /// - some bytes were written, it is deemed to be a success
151    ///
152    /// Upon success, the remaining (unwritten) slice is returned.
153    pub fn write_raw<'d>(&self, data: &'d [u8]) -> nb::Result<&'d [u8], Infallible> {
154        write_raw(&self.device, data)
155    }
156
157    /// Writes bytes to the UART.
158    ///
159    /// This function blocks until the full buffer has been sent.
160    pub fn write_full_blocking(&self, data: &[u8]) {
161        write_full_blocking(&self.device, data);
162    }
163
164    /// Enables the Transmit Interrupt.
165    ///
166    /// The relevant UARTx IRQ will fire when there is space in the transmit FIFO.
167    pub fn enable_tx_interrupt(&mut self) {
168        enable_tx_interrupt(&self.device)
169    }
170
171    /// Disables the Transmit Interrupt.
172    pub fn disable_tx_interrupt(&mut self) {
173        disable_tx_interrupt(&self.device)
174    }
175
176    /// Initiates a break
177    ///
178    /// If transmitting, this takes effect immediately after the current byte has completed.  
179    /// For proper execution of the break command, this must be held for at least 2 complete frames
180    /// worth of time.
181    ///
182    /// <div class="warning">The device won’t be able to send anything while breaking.</div>
183    ///
184    /// # Example
185    ///
186    /// ```no_run
187    /// # use rp2040_hal::uart::{Pins, ValidUartPinout, Enabled, UartPeripheral};
188    /// # use rp2040_hal::pac::UART0;
189    /// # use rp2040_hal::typelevel::OptionTNone;
190    /// # use embedded_hal_0_2::blocking::delay::DelayUs;
191    /// # type PINS = Pins<OptionTNone, OptionTNone, OptionTNone, OptionTNone>;
192    /// # let mut serial: UartPeripheral<Enabled, UART0, PINS> = unsafe { core::mem::zeroed() };
193    /// # let mut timer: rp2040_hal::Timer = unsafe { core::mem::zeroed() };
194    /// serial.lowlevel_break_start();
195    /// // at 115_200Bps on 8N1 configuration, 20bits takes (20*10⁶)/115200 = 173.611…μs.
196    /// timer.delay_us(175);
197    /// serial.lowlevel_break_stop();
198    /// ```
199    pub fn lowlevel_break_start(&mut self) {
200        self.device.uartlcr_h().modify(|_, w| w.brk().set_bit());
201    }
202
203    /// Terminates a break condition.
204    ///
205    /// See `lowlevel_break_start` for more details.
206    pub fn lowlevel_break_stop(&mut self) {
207        self.device.uartlcr_h().modify(|_, w| w.brk().clear_bit());
208    }
209}
210
211impl<D: UartDevice, P: ValidUartPinout<D>> embedded_io::ErrorType for Writer<D, P> {
212    type Error = Infallible;
213}
214
215impl<D: UartDevice, P: ValidUartPinout<D>> embedded_io::Write for Writer<D, P> {
216    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
217        // Blocks if and only if no bytes can be written.
218        let remaining = nb::block!(write_raw(&self.device, buf)).unwrap(); // Infallible
219        Ok(buf.len() - remaining.len())
220    }
221    fn flush(&mut self) -> Result<(), Self::Error> {
222        nb::block!(transmit_flushed(&self.device)).unwrap(); // Infallible
223        Ok(())
224    }
225}
226
227impl<D: UartDevice, P: ValidUartPinout<D>> embedded_io::WriteReady for Writer<D, P> {
228    fn write_ready(&mut self) -> Result<bool, Self::Error> {
229        Ok(uart_is_writable(&self.device))
230    }
231}
232
233impl<D: UartDevice, P: ValidUartPinout<D>> Write02<u8> for Writer<D, P> {
234    type Error = Infallible;
235
236    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
237        if self.write_raw(&[word]).is_err() {
238            Err(WouldBlock)
239        } else {
240            Ok(())
241        }
242    }
243
244    fn flush(&mut self) -> nb::Result<(), Self::Error> {
245        transmit_flushed(&self.device)
246    }
247}
248
249// Safety: This only writes to the TX fifo, so it doesn't
250// interact with rust-managed memory.
251unsafe impl<D: UartDevice, P: ValidUartPinout<D>> WriteTarget for Writer<D, P> {
252    type TransmittedWord = u8;
253
254    fn tx_treq() -> Option<u8> {
255        Some(D::tx_dreq())
256    }
257
258    fn tx_address_count(&mut self) -> (u32, u32) {
259        (self.device.uartdr().as_ptr() as u32, u32::MAX)
260    }
261
262    fn tx_increment(&self) -> bool {
263        false
264    }
265}
266
267impl<D: UartDevice, P: ValidUartPinout<D>> EndlessWriteTarget for Writer<D, P> {}
268
269impl<D: UartDevice, P: ValidUartPinout<D>> ErrorType for Writer<D, P> {
270    type Error = Infallible;
271}
272
273impl<D: UartDevice, P: ValidUartPinout<D>> Write<u8> for Writer<D, P> {
274    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
275        if self.write_raw(&[word]).is_err() {
276            Err(WouldBlock)
277        } else {
278            Ok(())
279        }
280    }
281
282    fn flush(&mut self) -> nb::Result<(), Self::Error> {
283        transmit_flushed(&self.device).map_err(|e| match e {
284            WouldBlock => WouldBlock,
285            Other(v) => match v {},
286        })
287    }
288}
289
290impl<D: UartDevice, P: ValidUartPinout<D>> fmt::Write for Writer<D, P> {
291    fn write_str(&mut self, s: &str) -> fmt::Result {
292        s.bytes()
293            .try_for_each(|c| nb::block!(Write::write(self, c)))
294            .map_err(|_| fmt::Error)
295    }
296}