Skip to content

Commit 296c303

Browse files
committed
serial: document buffered vs unbuffered traits.
1 parent c392745 commit 296c303

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

embedded-hal-async/src/serial.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//! Serial interface
2+
//!
3+
//! See the documentation on [`embedded_hal::serial`] for details.
24
35
pub use embedded_hal::serial::{Error, ErrorKind, ErrorType};
46

5-
/// Read an exact amount of words from a serial interface
7+
/// Read an exact amount of words from an *unbuffered* serial interface
68
///
79
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
810
/// This can be encoded in this trait via the `Word` type parameter.
@@ -19,7 +21,7 @@ impl<T: ReadExact<Word>, Word: 'static + Copy> ReadExact<Word> for &mut T {
1921
}
2022
}
2123

22-
/// Read words from a serial interface, until the line becomes idle.
24+
/// Read words from an *unbuffered* serial interface, until the line becomes idle.
2325
///
2426
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
2527
/// This can be encoded in this trait via the `Word` type parameter.

embedded-hal/src/serial.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,40 @@
11
//! 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.
238
339
/// Serial error
440
pub trait Error: core::fmt::Debug {
@@ -73,7 +109,7 @@ impl<T: ErrorType> ErrorType for &mut T {
73109
type Error = T::Error;
74110
}
75111

76-
/// Read an exact amount of words from a serial interface
112+
/// Read an exact amount of words from an *unbuffered* serial interface
77113
///
78114
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
79115
/// 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 {
90126
}
91127
}
92128

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.
94130
///
95131
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
96132
/// This can be encoded in this trait via the `Word` type parameter.

0 commit comments

Comments
 (0)