1use crate::{
4 dcs::{self, InterfaceExt, SetAddressMode},
5 interface::Interface,
6 options::{self, ModelOptions, Rotation},
7 ConfigurationError,
8};
9use embedded_graphics_core::prelude::RgbColor;
10use embedded_hal_async::delay::DelayNs;
11
12mod gc9107;
14mod gc9a01;
15mod ili9225;
16mod ili9341;
17mod ili9342c;
18mod ili934x;
19mod ili9486;
20mod ili9488;
21mod ili948x;
22mod rm67162;
23mod st7735s;
24mod st7789;
25mod st7796;
26
27pub use gc9107::*;
28pub use gc9a01::*;
29pub use ili9225::*;
30pub use ili9341::*;
31pub use ili9342c::*;
32pub use ili9486::*;
33pub use ili9488::*;
34pub use rm67162::*;
35pub use st7735s::*;
36pub use st7789::*;
37pub use st7796::*;
38
39pub trait Model {
41 type ColorFormat: RgbColor;
43
44 const FRAMEBUFFER_SIZE: (u16, u16);
46
47 const RESET_DURATION: u32 = 10;
49
50 fn init<DELAY, DI>(
53 &mut self,
54 di: &mut DI,
55 delay: &mut DELAY,
56 options: &ModelOptions,
57 ) -> impl core::future::Future<Output = Result<SetAddressMode, ModelInitError<DI::Error>>>
58 where
59 DELAY: DelayNs,
60 DI: Interface;
61
62 fn update_address_window<DI>(
64 di: &mut DI,
65 _rotation: Rotation,
66 sx: u16,
67 sy: u16,
68 ex: u16,
69 ey: u16,
70 ) -> impl core::future::Future<Output = Result<(), DI::Error>>
71 where
72 DI: Interface,
73 {
74 async move {
75 di.write_command(dcs::SetColumnAddress::new(sx, ex)).await?;
76 di.write_command(dcs::SetPageAddress::new(sy, ey)).await
77 }
78 }
79
80 fn sleep<DI, DELAY>(
84 di: &mut DI,
85 delay: &mut DELAY,
86 ) -> impl core::future::Future<Output = Result<(), DI::Error>>
87 where
88 DI: Interface,
89 DELAY: DelayNs,
90 {
91 async move {
92 di.write_command(dcs::EnterSleepMode).await?;
93 delay.delay_us(120_000).await;
95 Ok(())
96 }
97 }
98 fn wake<DI, DELAY>(
102 di: &mut DI,
103 delay: &mut DELAY,
104 ) -> impl core::future::Future<Output = Result<(), DI::Error>>
105 where
106 DI: Interface,
107 DELAY: DelayNs,
108 {
109 async move {
110 di.write_command(dcs::ExitSleepMode).await?;
111 delay.delay_us(120_000).await;
113 Ok(())
114 }
115 }
116 fn write_memory_start<DI>(
120 di: &mut DI,
121 ) -> impl core::future::Future<Output = Result<(), DI::Error>>
122 where
123 DI: Interface,
124 {
125 async move { di.write_command(dcs::WriteMemoryStart).await }
126 }
127 fn software_reset<DI>(di: &mut DI) -> impl core::future::Future<Output = Result<(), DI::Error>>
131 where
132 DI: Interface,
133 {
134 async move { di.write_command(dcs::SoftReset).await }
135 }
136 fn update_options<DI>(
140 &self,
141 di: &mut DI,
142 options: &ModelOptions,
143 ) -> impl core::future::Future<Output = Result<(), DI::Error>>
144 where
145 DI: Interface,
146 {
147 async move {
148 let madctl = SetAddressMode::from(options);
149 di.write_command(madctl).await
150 }
151 }
152
153 fn set_tearing_effect<DI>(
157 di: &mut DI,
158 tearing_effect: options::TearingEffect,
159 _options: &ModelOptions,
160 ) -> impl core::future::Future<Output = Result<(), DI::Error>>
161 where
162 DI: Interface,
163 {
164 async move {
165 di.write_command(dcs::SetTearingEffect::new(tearing_effect))
166 .await
167 }
168 }
169
170 fn set_vertical_scroll_region<DI>(
186 di: &mut DI,
187 top_fixed_area: u16,
188 bottom_fixed_area: u16,
189 ) -> impl core::future::Future<Output = Result<(), DI::Error>>
190 where
191 DI: Interface,
192 {
193 async move {
194 let rows = Self::FRAMEBUFFER_SIZE.1;
195
196 let vscrdef = if top_fixed_area + bottom_fixed_area > rows {
197 dcs::SetScrollArea::new(rows, 0, 0)
198 } else {
199 dcs::SetScrollArea::new(
200 top_fixed_area,
201 rows - top_fixed_area - bottom_fixed_area,
202 bottom_fixed_area,
203 )
204 };
205
206 di.write_command(vscrdef).await
207 }
208 }
209
210 fn set_vertical_scroll_offset<DI>(
218 di: &mut DI,
219 offset: u16,
220 ) -> impl core::future::Future<Output = Result<(), DI::Error>>
221 where
222 DI: Interface,
223 {
224 async move {
225 let vscad = dcs::SetScrollStart::new(offset);
226 di.write_command(vscad).await
227 }
228 }
229}
230
231pub enum ModelInitError<DiError> {
236 Interface(DiError),
238
239 InvalidConfiguration(ConfigurationError),
245}
246
247impl<DiError> From<DiError> for ModelInitError<DiError> {
248 fn from(value: DiError) -> Self {
249 Self::Interface(value)
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use embedded_graphics::pixelcolor::Rgb565;
256
257 use crate::{
258 Builder,
259 _mock::{MockDelay, MockDisplayInterface},
260 dcs::SetAddressMode,
261 interface::InterfaceKind,
262 ConfigurationError, InitError,
263 };
264
265 use super::*;
266
267 struct OnlyOneKindModel(InterfaceKind);
268
269 impl Model for OnlyOneKindModel {
270 type ColorFormat = Rgb565;
271
272 const FRAMEBUFFER_SIZE: (u16, u16) = (16, 16);
273
274 async fn init<DELAY, DI>(
275 &mut self,
276 _di: &mut DI,
277 _delay: &mut DELAY,
278 _options: &ModelOptions,
279 ) -> Result<SetAddressMode, ModelInitError<DI::Error>>
280 where
281 DELAY: DelayNs,
282 DI: Interface,
283 {
284 if DI::KIND != self.0 {
285 return Err(ModelInitError::InvalidConfiguration(
286 ConfigurationError::UnsupportedInterface,
287 ));
288 }
289
290 Ok(SetAddressMode::default())
291 }
292 }
293
294 #[test]
295 fn test_assert_interface_kind_serial() {
296 tokio_test::block_on(async {
297 Builder::new(
298 OnlyOneKindModel(InterfaceKind::Serial4Line),
299 MockDisplayInterface,
300 )
301 .init(&mut MockDelay)
302 .await
303 .unwrap();
304 });
305 }
306
307 #[test]
308 fn test_assert_interface_kind_parallel() {
309 tokio_test::block_on(async {
310 assert!(matches!(
311 Builder::new(
312 OnlyOneKindModel(InterfaceKind::Parallel8Bit),
313 MockDisplayInterface,
314 )
315 .init(&mut MockDelay)
316 .await,
317 Err(InitError::InvalidConfiguration(
318 ConfigurationError::UnsupportedInterface
319 ))
320 ));
321 });
322 }
323}