Skip to main content

display_driver/bus/
mod.rs

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