display_driver/bus/mod.rs
1#[cfg(feature = "display-interface")]
2mod display_interface_impl;
3
4
5pub mod simple;
6pub use simple::SimpleDisplayBus;
7
8use crate::{Area, DisplayError, SolidColor};
9
10/// Error type trait.
11///
12/// This just defines the error type, to be used by the other traits.
13pub trait ErrorType {
14 /// Error type
15 type Error: core::fmt::Debug;
16}
17
18#[derive(Debug, Clone, Copy, Default)]
19pub struct FrameControl {
20 pub first: bool,
21 pub last: bool,
22}
23
24impl FrameControl {
25 pub fn new_standalone() -> Self {
26 Self {
27 first: true,
28 last: true,
29 }
30 }
31
32 pub fn new_first() -> Self {
33 Self {
34 first: true,
35 last: false,
36 }
37 }
38
39 pub fn new_last() -> Self {
40 Self {
41 first: false,
42 last: true,
43 }
44 }
45}
46
47/// Metadata about the pixel data transfer.
48///
49/// Advanced display buses (like MIPI DSI or QSPI with DMA) often require more context than just the
50/// raw pixel bytes.
51/// This struct carries that side-band information, allowing the bus implementation to orchestrate
52/// the transfer correctly.
53#[derive(Clone, Copy, Debug)]
54pub struct Metadata {
55 /// The rectangular area on the display this data corresponds to.
56 ///
57 /// If `Some`, the bus may use this to set the active window before sending data.
58 /// If `None`, the data is assumed to be a continuation of the previous stream.
59 pub area: Option<Area>,
60 /// Flags for frame synchronization (start/end of frame).
61 pub frame_control: FrameControl,
62}
63
64impl Metadata {
65 /// Creates metadata for a full screen update.
66 ///
67 /// This sets the area to the full display dimensions and marks the transfer as both the start
68 /// and end of a frame.
69 /// Use this for standard full-frame refreshing.
70 pub fn new_full_screen(w: u16, h: u16) -> Self {
71 Self {
72 area: Some(Area::from_origin(w, h)),
73 frame_control: FrameControl {
74 first: true,
75 last: true,
76 },
77 }
78 }
79
80 /// Creates metadata for continuing a stream of pixel data without resetting the area or frame
81 /// markers.
82 ///
83 /// Use this when splitting a large frame into multiple smaller chunks for transfer.
84 pub fn new_continue_stream() -> Self {
85 Self {
86 area: None,
87 frame_control: FrameControl {
88 first: false,
89 last: false,
90 },
91 }
92 }
93
94 /// Creates metadata with specific area and frame control settings.
95 ///
96 /// Use this for partial updates or specialized transfer patterns.
97 pub fn new_from_parts(area: Option<Area>, frame_control: FrameControl) -> Self {
98 Self {
99 area,
100 frame_control,
101 }
102 }
103}
104
105#[allow(async_fn_in_trait)]
106/// The core interface for all display bus implementations.
107///
108/// This trait serves as the abstraction layer between the high-level drawing logic and the
109/// low-level transport protocol. It accommodates a wide range of hardware, from simple 2-wire
110/// interfaces to complex high-speed buses.
111///
112/// The interface distinguishes between two types of traffic:
113/// - **Commands**: Small, latency-sensitive messages used for configuration (handled by `write_cmd`
114/// and `write_cmd_with_params`).
115/// - **Pixels**: Large, throughput-critical data streams used for changing the visual content
116/// (handled by `write_pixels`).
117///
118/// This separation allows for optimizations. For instance, `write_pixels` accepts [`Metadata`],
119/// enabling the underlying implementation to utilize hardware accelerators (like DMA or QSPI
120/// peripherals) that can handle address setting and bulk data transfer efficiently.
121pub trait DisplayBus: ErrorType {
122 /// Writes a command to the display.
123 ///
124 /// This is typically used for setting registers or sending configuration opcodes.
125 async fn write_cmd(&mut self, cmd: &[u8]) -> Result<(), Self::Error>;
126
127 // async fn write_cmds(&mut self, cmds: &[u8]) -> Result<(), Self::Error>;
128
129 /// Writes a command followed immediately by its parameters.
130 ///
131 /// This guarantees an atomic transaction where the command and parameters are sent without
132 /// interruption. This is critical for many display controllers that expect the parameter bytes
133 /// to immediately follow the command byte while the Chip Select (CS) line remains active.
134 async fn write_cmd_with_params(&mut self, cmd: &[u8], params: &[u8])
135 -> Result<(), Self::Error>;
136
137 /// Writes a stream of pixel data to the display.
138 ///
139 /// # Arguments
140 /// * `cmd` - The memory write command (e.g., `0x2C` for standard MIPI DCS).
141 /// * `data` - The raw pixel data bytes.
142 /// * `metadata` - Contextual information about this transfer, including the target area and
143 /// frame boundaries.
144 ///
145 /// Implementations should use the `metadata` to handle frame synchronization (VSYNC/TE) before
146 /// sending the pixel data.
147 async fn write_pixels(
148 &mut self,
149 cmd: &[u8],
150 data: &[u8],
151 metadata: Metadata,
152 ) -> Result<(), DisplayError<Self::Error>>;
153
154 /// Resets the screen via the bus (optional).
155 ///
156 /// Note: This method should only be implemented if the hardware has a physical Reset pin.
157 /// Avoid adding a Pin field to your `DisplayBus` wrapper for this purpose; use `LCDResetOption`
158 /// instead.
159 fn set_reset(&mut self, reset: bool) -> Result<(), DisplayError<Self::Error>> {
160 let _ = reset;
161 Err(DisplayError::Unsupported)
162 }
163}
164
165#[allow(async_fn_in_trait)]
166/// An optional trait for buses that support hardware-accelerated solid color filling.
167///
168/// Filling a large area with a single color is a common operation (e.g., clearing the screen).
169/// If the hardware supports it (e.g., via a 2D GPU or a DMA channel with a non-incrementing source
170/// address), this trait allows the driver to offload that work, significantly reducing CPU usage
171/// and bus traffic.
172pub trait BusHardwareFill: DisplayBus {
173 /// Fills a specific region of the display with a solid color.
174 ///
175 /// The implementation should leverage available hardware acceleration to perform this operation
176 /// efficiently.
177 async fn fill_solid(
178 &mut self,
179 cmd: &[u8],
180 color: SolidColor,
181 area: Area,
182 ) -> Result<(), DisplayError<Self::Error>>;
183}
184
185#[allow(async_fn_in_trait)]
186/// An optional trait for buses that support reading data back from the display.
187///
188/// While most display interactions are write-only, reading is sometimes necessary for:
189/// - Verifying the connection by reading the display ID.
190/// - Checking status registers.
191/// - Reading back frame memory (e.g., for screenshots), though this is less common.
192///
193/// Not all physical interfaces support bi-directional communication (e.g., SPI TFT is often
194/// write-only).
195pub trait BusRead: DisplayBus {
196 /// Reads data from the display.
197 ///
198 /// # Arguments
199 /// * `cmd` - The command to initiate the read operation.
200 /// * `params` - Optional parameters required before the read transaction begins.
201 /// * `buffer` - The destination buffer where the read data will be stored.
202 async fn read_data(
203 &mut self,
204 cmd: &[u8],
205 params: &[u8],
206 buffer: &mut [u8],
207 ) -> Result<(), DisplayError<Self::Error>> {
208 let (_, _, _) = (cmd, params, buffer);
209 Err(DisplayError::Unsupported)
210 }
211}
212
213/// An optional trait for buses that support non-atomic command and data writing.
214///
215/// Some buses, such as SPI, support sending commands and data in a single transaction, while others
216/// require separate transactions for commands and data.
217#[allow(async_fn_in_trait)]
218pub trait BusBytesIo: DisplayBus {
219 /// Writes a sequence of commands to the bus.
220 ///
221 /// This is typically used for sending register addresses or command opcodes.
222 async fn write_cmd_bytes(&mut self, cmd: &[u8]) -> Result<(), Self::Error>;
223
224 /// Writes a sequence of data bytes to the bus.
225 ///
226 /// This is used for sending command parameters or pixel data.
227 async fn write_data_bytes(&mut self, data: &[u8]) -> Result<(), Self::Error>;
228}