Skip to main content

display_driver/
lib.rs

1#![no_std]
2
3pub mod area;
4pub mod bus;
5pub mod color;
6pub mod panel;
7
8#[cfg(feature = "embedded-graphics")]
9pub mod eg;
10
11pub use crate::area::Area;
12pub use crate::bus::{
13    BusBytesIo, BusHardwareFill, DisplayBus, FrameControl, Metadata, SimpleDisplayBus,
14};
15pub use color::{ColorFormat, ColorType, SolidColor};
16pub use panel::{reset::LCDResetOption, Orientation, Panel, PanelSetBrightness};
17
18use embedded_hal_async::delay::DelayNs;
19
20/// Error type for display operations.
21#[derive(Debug)]
22pub enum DisplayError<E> {
23    /// Error propagated from the underlying bus.
24    BusError(E),
25    /// The requested operation is not supported by the display or driver.
26    Unsupported,
27    /// Parameter is out of valid range.
28    OutOfRange,
29    /// Invalid arguments.
30    InvalidArgs,
31    /// The area is unaligned.
32    UnalignedArea,
33}
34
35impl<E> From<E> for DisplayError<E> {
36    fn from(error: E) -> Self {
37        Self::BusError(error)
38    }
39}
40
41/// A builder for configuring and initializing a [`DisplayDriver`].
42///
43/// Use [`DisplayDriver::builder`] to create a builder, then chain configuration methods
44/// and call [`init`](DisplayDriverBuilder::init) to complete initialization.
45///
46/// # Example
47/// ```ignore
48/// let mut display = DisplayDriver::builder(bus, panel)
49///     .with_color_format(ColorFormat::RGB565)
50///     .with_orientation(Orientation::Deg270)
51///     .init(&mut delay).await.unwrap();
52/// ```
53pub struct DisplayDriverBuilder<B: DisplayBus, P: Panel<B>> {
54    bus: B,
55    panel: P,
56    color_format: Option<ColorFormat>,
57    orientation: Option<Orientation>,
58}
59
60impl<B: DisplayBus, P: Panel<B>> DisplayDriverBuilder<B, P> {
61    /// Creates a new builder with the given bus and panel.
62    fn new(bus: B, panel: P) -> Self {
63        Self {
64            bus,
65            panel,
66            color_format: None,
67            orientation: None,
68        }
69    }
70
71    /// Sets the color format to be applied during initialization.
72    pub fn with_color_format(mut self, color_format: ColorFormat) -> Self {
73        self.color_format = Some(color_format);
74        self
75    }
76
77    /// Sets the orientation to be applied during initialization.
78    pub fn with_orientation(mut self, orientation: Orientation) -> Self {
79        self.orientation = Some(orientation);
80        self
81    }
82
83    /// Initializes the display and returns the configured [`DisplayDriver`].
84    ///
85    /// This method:
86    /// 1. Calls the panel's initialization sequence
87    /// 2. Applies the color format if configured
88    /// 3. Applies the orientation if configured
89    pub async fn init<D: DelayNs>(
90        mut self,
91        delay: &mut D,
92    ) -> Result<DisplayDriver<B, P>, DisplayError<B::Error>> {
93        self.panel
94            .init(&mut self.bus, delay)
95            .await
96            .map_err(DisplayError::BusError)?;
97
98        if let Some(color_format) = self.color_format {
99            self.panel
100                .set_color_format(&mut self.bus, color_format)
101                .await?;
102        }
103
104        if let Some(orientation) = self.orientation {
105            self.panel
106                .set_orientation(&mut self.bus, orientation)
107                .await?;
108        }
109
110        Ok(DisplayDriver {
111            bus: self.bus,
112            panel: self.panel,
113        })
114    }
115}
116
117/// The high-level driver that orchestrates drawing operations.
118///
119/// This struct acts as the "glue" between the logical [`Panel`] implementation (which knows the command set)
120/// and the [`DisplayBus`] (which handles the physical transport). It exposes user-friendly methods
121/// for drawing pixels, filling rectangles, and managing the display state.
122pub struct DisplayDriver<B: DisplayBus, P: Panel<B>> {
123    /// The underlying bus interface used for communication.
124    pub bus: B,
125    /// The panel.
126    pub panel: P,
127}
128
129impl<B: DisplayBus, P: Panel<B>> DisplayDriver<B, P> {
130    /// Creates a builder for configuring and initializing a display driver.
131    ///
132    /// # Example
133    /// ```ignore
134    /// let mut display = DisplayDriver::builder(bus, panel)
135    ///     .with_color_format(ColorFormat::RGB565)
136    ///     .with_orientation(Orientation::Deg270)
137    ///     .init(&mut delay).await.unwrap();
138    /// ```
139    pub fn builder(bus: B, panel: P) -> DisplayDriverBuilder<B, P> {
140        DisplayDriverBuilder::new(bus, panel)
141    }
142
143    /// Creates a new display driver directly (without builder).
144    ///
145    /// Use [`builder`](Self::builder) for a fluent initialization API.
146    pub fn new(bus: B, panel: P) -> Self {
147        Self { bus, panel }
148    }
149
150    /// Initializes the display.
151    pub async fn init(&mut self, delay: &mut impl DelayNs) -> Result<(), DisplayError<B::Error>> {
152        self.panel
153            .init(&mut self.bus, delay)
154            .await
155            .map_err(DisplayError::BusError)
156    }
157
158    /// Sets the window.
159    ///
160    /// Use `write_pixels` or `write_frame` if you just want to draw a buffer.
161    /// Use `fill_solid_xxx` if you just want to fill an Area.
162    pub async fn set_window(&mut self, area: Area) -> Result<(), DisplayError<B::Error>> {
163        if (self.panel.x_alignment() > 1 || self.panel.y_alignment() > 1)
164            && (!area.x.is_multiple_of(self.panel.x_alignment())
165                || !area.y.is_multiple_of(self.panel.y_alignment())
166                || !area.w.is_multiple_of(self.panel.x_alignment())
167                || !area.h.is_multiple_of(self.panel.y_alignment()))
168            {
169                return Err(DisplayError::UnalignedArea);
170            }
171
172        if area.w == 0 || area.h == 0 {
173            return Err(DisplayError::InvalidArgs);
174        }
175
176        let (x1, y1) = area.bottom_right();
177        self.panel
178            .set_window(&mut self.bus, area.x, area.y, x1, y1)
179            .await
180    }
181
182    /// Sets the pixel color format.
183    pub async fn set_color_format(
184        &mut self,
185        color_format: ColorFormat,
186    ) -> Result<(), DisplayError<B::Error>> {
187        self.panel
188            .set_color_format(&mut self.bus, color_format)
189            .await
190    }
191
192    /// Sets the display orientation.
193    pub async fn set_orientation(
194        &mut self,
195        orientation: Orientation,
196    ) -> Result<(), DisplayError<B::Error>> {
197        self.panel.set_orientation(&mut self.bus, orientation).await
198    }
199
200    /// Writes pixels to the specified area.
201    pub async fn write_pixels(
202        &mut self,
203        area: Area,
204        frame_control: FrameControl,
205        buffer: &[u8],
206    ) -> Result<(), DisplayError<B::Error>> {
207        self.set_window(area).await?;
208        let cmd = &P::PIXEL_WRITE_CMD[0..P::CMD_LEN];
209        let metadata = Metadata {
210            area: Some(area),
211            frame_control,
212        };
213        self.bus.write_pixels(cmd, buffer, metadata).await
214    }
215
216    /// Writes the entire buffer to the display.
217    pub async fn write_frame(&mut self, buffer: &[u8]) -> Result<(), DisplayError<B::Error>> {
218        self.write_pixels(
219            Area::from_origin_size(self.panel.size()),
220            FrameControl::new_standalone(),
221            buffer,
222        )
223        .await
224    }
225}
226
227impl<B: DisplayBus, P: Panel<B> + PanelSetBrightness<B>> DisplayDriver<B, P> {
228    /// Sets the display brightness (if supported by the panel).
229    pub async fn set_brightness(&mut self, brightness: u8) -> Result<(), DisplayError<B::Error>> {
230        self.panel.set_brightness(&mut self.bus, brightness).await
231    }
232}
233
234impl<B: DisplayBus + BusHardwareFill, P: Panel<B>> DisplayDriver<B, P> {
235    /// Fills the area with a solid color using bus auto-fill.
236    pub async fn fill_solid_via_bus(
237        &mut self,
238        color: SolidColor,
239        area: Area,
240    ) -> Result<(), DisplayError<B::Error>> {
241        self.set_window(area).await?;
242        let cmd = &P::PIXEL_WRITE_CMD[0..P::CMD_LEN];
243        self.bus.fill_solid(cmd, color, area).await
244    }
245
246    /// Fills the entire screen with a solid color using bus auto-fill.
247    pub async fn fill_screen_via_bus(
248        &mut self,
249        color: SolidColor,
250    ) -> Result<(), DisplayError<B::Error>> {
251        self.fill_solid_via_bus(color, Area::from_origin_size(self.panel.size()))
252            .await
253    }
254}
255
256impl<B, P> DisplayDriver<B, P>
257where
258    B: DisplayBus + BusBytesIo,
259    P: Panel<B>,
260{
261    /// Fills the area with a solid color.
262    pub async fn fill_solid_batch<const N: usize>(
263        &mut self,
264        color: SolidColor,
265        area: Area,
266    ) -> Result<(), DisplayError<B::Error>> {
267        self.set_window(area).await?;
268        let cmd = &P::PIXEL_WRITE_CMD[0..P::CMD_LEN];
269
270        self.bus
271            .write_cmd_bytes(cmd)
272            .await
273            .map_err(DisplayError::BusError)?;
274
275        let pixel_size = color.format.size_bytes() as usize;
276        let total_pixels = area.total_pixels();
277        let mut remaining_pixels = total_pixels;
278
279        let mut buffer = [0u8; N];
280
281        // Calculate how many full pixels fit in the buffer
282        let pixels_per_chunk = buffer.len() / pixel_size;
283
284        // Extract the raw bytes for the color based on its size
285        let color_bytes = &color.raw[..pixel_size];
286
287        // Pre-fill the buffer with the color pattern
288        for i in 0..pixels_per_chunk {
289            buffer[i * pixel_size..(i + 1) * pixel_size].copy_from_slice(color_bytes);
290        }
291
292        while remaining_pixels > 0 {
293            let current_pixels = remaining_pixels.min(pixels_per_chunk);
294            let byte_count = current_pixels * pixel_size;
295            self.bus
296                .write_data_bytes(&buffer[0..byte_count])
297                .await
298                .map_err(DisplayError::BusError)?;
299            remaining_pixels -= current_pixels;
300        }
301
302        Ok(())
303    }
304
305    /// Fills the entire screen with a solid color.
306    pub async fn fill_screen_batch<const N: usize>(
307        &mut self,
308        color: SolidColor,
309    ) -> Result<(), DisplayError<B::Error>> {
310        self.fill_solid_batch::<N>(color, Area::from_origin_size(self.panel.size()))
311            .await
312    }
313}