1
1
//! Serial traits
2
+ //!
3
+ //! # Buffered vs Unbuffered
4
+ //!
5
+ //! There is two main ways a serial receiver can operate: buffered and unbuffered.
6
+ //!
7
+ //! - **Buffered**: The serial driver has a reasonably-sized buffer in RAM. Incoming bytes
8
+ //! are placed into the buffer automatically without user code having to actively do
9
+ //! anything (using either DMA, or a high-priority interrupt). User code reads bytes
10
+ //! out of the buffer. Bytes are only lost if received while buffer is full.
11
+ //! - **Unbuffered**: The serial driver has no buffer (or a buffer so small that it's negligible,
12
+ //! for example nRF chips have a 4-byte buffer in hardware, and some STM32 chips have a 1-byte
13
+ //! buffer). User code does "read" operations that pop bytes directly from the hardware
14
+ //! into the user's buffer. Bytes received while the user code is not actively doing a read
15
+ //! *at that very instant* are lost.
16
+ //!
17
+ //! For example:
18
+ //! - Linux's /dev/ttyX is buffered.
19
+ //! - Most Rust HALs offer unbuffered serial drivers at the time of writing.
20
+ //! - Some HALs (such as `embassy`) offer both buffered and unbuffered.
21
+ //!
22
+ //! There are tradeoffs when deciding which one to use. Unbuffered is the simplest, and allows for
23
+ //! the lowest memory usage since data can be transferred directly from hardware to the user's buffer, avoiding
24
+ //! the need for intermediary drivers. However, with unbuffered it's very easy to **lose data** if the code
25
+ //! spends too much time between read calls. This can be solved either by using buffered serial at the cost of
26
+ //! more RAM usage, or using hardware flow control (RTS/CTS) at the cost of using more MCU pins.
27
+ //!
28
+ //! The read traits in this API ([`ReadExact`] and [`ReadUntilIdle`]) are intended to **model unbuffered serial interfaces**.
29
+ //! Data that arrives when your code is not running a `read_*()` call **is lost**.
30
+ //!
31
+ //! Drivers should only use these traits when the use case allows for it. For example, `ReadUntilIdle` can be used
32
+ //! for packet-wise communications, but you have to ensure the protocol guarantees enough idle time between
33
+ //! packets so that you won't lose data for the next packet while processing the previous one.
34
+ //!
35
+ //! Drivers that require **buffered** serial ports should use [`embedded-io`](https://docs.rs/embedded-io) instead. These
36
+ //! traits allow for a much more `std::io`-like usage, and implementations guarantee data is not lost until the
37
+ //! (much bigger) buffer overflows.
2
38
3
39
/// Serial error
4
40
pub trait Error : core:: fmt:: Debug {
@@ -73,7 +109,7 @@ impl<T: ErrorType> ErrorType for &mut T {
73
109
type Error = T :: Error ;
74
110
}
75
111
76
- /// Read an exact amount of words from a serial interface
112
+ /// Read an exact amount of words from an *unbuffered* serial interface
77
113
///
78
114
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
79
115
/// This can be encoded in this trait via the `Word` type parameter.
@@ -90,7 +126,7 @@ impl<T: ReadExact<Word>, Word: 'static + Copy> ReadExact<Word> for &mut T {
90
126
}
91
127
}
92
128
93
- /// Read words from a serial interface, until the line becomes idle.
129
+ /// Read words from an *unbuffered* serial interface, until the line becomes idle.
94
130
///
95
131
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
96
132
/// This can be encoded in this trait via the `Word` type parameter.
0 commit comments