1use embedded_hal::digital::{self, OutputPin};
4use embedded_hal_async::delay::DelayNs;
5
6use crate::{
7 dcs::InterfaceExt,
8 interface::Interface,
9 models::{Model, ModelInitError},
10 options::{ColorInversion, ColorOrder, ModelOptions, Orientation, RefreshOrder},
11 Display,
12};
13
14pub struct Builder<DI, MODEL, RST>
35where
36 DI: Interface,
37 MODEL: Model,
38{
39 di: DI,
40 model: MODEL,
41 rst: Option<RST>,
42 options: ModelOptions,
43}
44
45impl<DI, MODEL> Builder<DI, MODEL, NoResetPin>
46where
47 DI: Interface,
48 MODEL: Model,
49{
50 #[must_use]
54 pub fn new(model: MODEL, di: DI) -> Self {
55 Self {
56 di,
57 model,
58 rst: None,
59 options: ModelOptions::full_size::<MODEL>(),
60 }
61 }
62}
63
64impl<DI, MODEL, RST> Builder<DI, MODEL, RST>
65where
66 DI: Interface,
67 MODEL: Model,
68 RST: OutputPin,
69{
70 #[must_use]
74 pub fn invert_colors(mut self, color_inversion: ColorInversion) -> Self {
75 self.options.invert_colors = color_inversion;
76 self
77 }
78
79 #[must_use]
83 pub fn color_order(mut self, color_order: ColorOrder) -> Self {
84 self.options.color_order = color_order;
85 self
86 }
87
88 #[must_use]
92 pub fn orientation(mut self, orientation: Orientation) -> Self {
93 self.options.orientation = orientation;
94 self
95 }
96
97 #[must_use]
101 pub fn refresh_order(mut self, refresh_order: RefreshOrder) -> Self {
102 self.options.refresh_order = refresh_order;
103 self
104 }
105
106 #[must_use]
110 pub fn display_size(mut self, width: u16, height: u16) -> Self {
111 self.options.display_size = (width, height);
112 self
113 }
114
115 #[must_use]
119 pub fn display_offset(mut self, x: u16, y: u16) -> Self {
120 self.options.display_offset = (x, y);
121 self
122 }
123
124 #[must_use]
131 pub fn reset_pin<RST2: OutputPin>(self, rst: RST2) -> Builder<DI, MODEL, RST2> {
132 Builder {
133 di: self.di,
134 model: self.model,
135 rst: Some(rst),
136 options: self.options,
137 }
138 }
139
140 pub async fn init(
148 mut self,
149 delay_source: &mut impl DelayNs,
150 ) -> Result<Display<DI, MODEL, RST>, InitError<DI::Error, RST::Error>> {
151 let to_u32 = |(a, b)| (u32::from(a), u32::from(b));
152 let (width, height) = to_u32(self.options.display_size);
153 let (offset_x, offset_y) = to_u32(self.options.display_offset);
154 let (max_width, max_height) = to_u32(MODEL::FRAMEBUFFER_SIZE);
155
156 if width == 0 || height == 0 || width > max_width || height > max_height {
157 return Err(InitError::InvalidConfiguration(
158 ConfigurationError::InvalidDisplaySize,
159 ));
160 }
161
162 if width + offset_x > max_width {
163 return Err(InitError::InvalidConfiguration(
164 ConfigurationError::InvalidDisplayOffset,
165 ));
166 }
167
168 if height + offset_y > max_height {
169 return Err(InitError::InvalidConfiguration(
170 ConfigurationError::InvalidDisplayOffset,
171 ));
172 }
173
174 match self.rst {
175 Some(ref mut rst) => {
176 rst.set_low().map_err(InitError::ResetPin)?;
177 delay_source.delay_us(MODEL::RESET_DURATION).await;
178 rst.set_high().map_err(InitError::ResetPin)?;
179 }
180 None => self
181 .di
182 .write_command(crate::dcs::SoftReset)
183 .await
184 .map_err(InitError::Interface)?,
185 }
186
187 let madctl = self
188 .model
189 .init(&mut self.di, delay_source, &self.options)
190 .await?;
191
192 let display = Display {
193 di: self.di,
194 model: self.model,
195 rst: self.rst,
196 options: self.options,
197 madctl,
198 sleeping: false, };
200
201 Ok(display)
202 }
203}
204
205#[derive(Debug)]
207pub enum InitError<DI, P> {
208 Interface(DI),
210
211 ResetPin(P),
213
214 InvalidConfiguration(ConfigurationError),
220}
221
222#[non_exhaustive]
224#[derive(Debug)]
225pub enum ConfigurationError {
226 UnsupportedInterface,
234 InvalidDisplaySize,
238 InvalidDisplayOffset,
245}
246
247impl<DiError, P> From<ModelInitError<DiError>> for InitError<DiError, P> {
248 fn from(value: ModelInitError<DiError>) -> Self {
249 match value {
250 ModelInitError::Interface(e) => Self::Interface(e),
251 ModelInitError::InvalidConfiguration(ce) => Self::InvalidConfiguration(ce),
252 }
253 }
254}
255
256pub enum NoResetPin {}
258
259impl digital::OutputPin for NoResetPin {
260 fn set_low(&mut self) -> Result<(), Self::Error> {
261 Ok(())
262 }
263
264 fn set_high(&mut self) -> Result<(), Self::Error> {
265 Ok(())
266 }
267}
268
269impl digital::ErrorType for NoResetPin {
270 type Error = core::convert::Infallible;
271}
272
273#[cfg(test)]
274mod tests {
275 use crate::{
276 _mock::{MockDelay, MockDisplayInterface, MockOutputPin},
277 models::ILI9341Rgb565,
278 };
279
280 use super::*;
281
282 #[test]
283 fn init_without_reset_pin() {
284 tokio_test::block_on(async {
285 let _: Display<_, _, NoResetPin> = Builder::new(ILI9341Rgb565, MockDisplayInterface)
286 .init(&mut MockDelay)
287 .await
288 .unwrap();
289 });
290 }
291
292 #[test]
293 fn init_reset_pin() {
294 tokio_test::block_on(async {
295 let _: Display<_, _, MockOutputPin> = Builder::new(ILI9341Rgb565, MockDisplayInterface)
296 .reset_pin(MockOutputPin)
297 .init(&mut MockDelay)
298 .await
299 .unwrap();
300 });
301 }
302
303 #[test]
304 fn error_too_wide() {
305 tokio_test::block_on(async {
306 assert!(matches!(
307 Builder::new(ILI9341Rgb565, MockDisplayInterface)
308 .reset_pin(MockOutputPin)
309 .display_size(241, 320)
310 .init(&mut MockDelay)
311 .await,
312 Err(InitError::InvalidConfiguration(
313 ConfigurationError::InvalidDisplaySize
314 ))
315 ));
316 });
317 }
318
319 #[test]
320 fn error_too_tall() {
321 tokio_test::block_on(async {
322 assert!(matches!(
323 Builder::new(ILI9341Rgb565, MockDisplayInterface)
324 .reset_pin(MockOutputPin)
325 .display_size(240, 321)
326 .init(&mut MockDelay)
327 .await,
328 Err(InitError::InvalidConfiguration(
329 ConfigurationError::InvalidDisplaySize
330 )),
331 ));
332 });
333 }
334
335 #[test]
336 fn error_offset_invalid_x() {
337 tokio_test::block_on(async {
338 assert!(matches!(
339 Builder::new(ILI9341Rgb565, MockDisplayInterface)
340 .reset_pin(MockOutputPin)
341 .display_size(240, 320)
342 .display_offset(1, 0)
343 .init(&mut MockDelay)
344 .await,
345 Err(InitError::InvalidConfiguration(
346 ConfigurationError::InvalidDisplayOffset
347 )),
348 ));
349 });
350 }
351
352 #[test]
353 fn error_offset_invalid_y() {
354 tokio_test::block_on(async {
355 assert!(matches!(
356 Builder::new(ILI9341Rgb565, MockDisplayInterface)
357 .reset_pin(MockOutputPin)
358 .display_size(240, 310)
359 .display_offset(0, 11)
360 .init(&mut MockDelay)
361 .await,
362 Err(InitError::InvalidConfiguration(
363 ConfigurationError::InvalidDisplayOffset
364 )),
365 ));
366 });
367 }
368
369 #[test]
370 fn error_zero_size() {
371 tokio_test::block_on(async {
372 assert!(matches!(
373 Builder::new(ILI9341Rgb565, MockDisplayInterface)
374 .reset_pin(MockOutputPin)
375 .display_size(0, 0)
376 .init(&mut MockDelay)
377 .await,
378 Err(InitError::InvalidConfiguration(
379 ConfigurationError::InvalidDisplaySize
380 )),
381 ));
382 });
383 }
384}