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#[derive(Debug)]
22pub enum DisplayError<E> {
23 BusError(E),
25 Unsupported,
27 OutOfRange,
29 InvalidArgs,
31 UnalignedArea,
33}
34
35impl<E> From<E> for DisplayError<E> {
36 fn from(error: E) -> Self {
37 Self::BusError(error)
38 }
39}
40
41pub 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 fn new(bus: B, panel: P) -> Self {
63 Self {
64 bus,
65 panel,
66 color_format: None,
67 orientation: None,
68 }
69 }
70
71 pub fn with_color_format(mut self, color_format: ColorFormat) -> Self {
73 self.color_format = Some(color_format);
74 self
75 }
76
77 pub fn with_orientation(mut self, orientation: Orientation) -> Self {
79 self.orientation = Some(orientation);
80 self
81 }
82
83 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
117pub struct DisplayDriver<B: DisplayBus, P: Panel<B>> {
123 pub bus: B,
125 pub panel: P,
127}
128
129impl<B: DisplayBus, P: Panel<B>> DisplayDriver<B, P> {
130 pub fn builder(bus: B, panel: P) -> DisplayDriverBuilder<B, P> {
140 DisplayDriverBuilder::new(bus, panel)
141 }
142
143 pub fn new(bus: B, panel: P) -> Self {
147 Self { bus, panel }
148 }
149
150 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 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 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 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 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 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 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 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 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 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 let pixels_per_chunk = buffer.len() / pixel_size;
283
284 let color_bytes = &color.raw[..pixel_size];
286
287 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 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}