Skip to main content

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}