1use crate::dcs::DcsCommand;
2use crate::dcs::InterfaceExt;
3use crate::dcs::SetAddressMode;
4use crate::options;
5use crate::options::{ColorOrder, Rotation};
6use crate::{
7 interface::Interface,
8 models::{Model, ModelInitError},
9 options::ModelOptions,
10};
11use embedded_graphics_core::pixelcolor::Rgb565;
12use embedded_hal_async::delay::DelayNs;
13
14pub struct ILI9225Rgb565;
16
17const ILI9225_POWER_CTRL1: u8 = 0x10;
18const ILI9225_POWER_CTRL2: u8 = 0x11;
19const ILI9225_POWER_CTRL3: u8 = 0x12;
20const ILI9225_POWER_CTRL4: u8 = 0x13;
21const ILI9225_POWER_CTRL5: u8 = 0x14;
22
23const ILI9225_DRIVER_OUTPUT_CTRL: u8 = 0x01; const ILI9225_LCD_AC_DRIVING_CTRL: u8 = 0x02; const ILI9225_ENTRY_MODE: u8 = 0x03; const ILI9225_DISP_CTRL1: u8 = 0x07; const ILI9225_BLANK_PERIOD_CTRL1: u8 = 0x08; const ILI9225_FRAME_CYCLE_CTRL: u8 = 0x0B; const ILI9225_INTERFACE_CTRL: u8 = 0x0C; const ILI9225_OSC_CTRL: u8 = 0x0F; const ILI9225_VCI_RECYCLING: u8 = 0x15; const ILI9225_RAM_ADDR_SET1: u8 = 0x20; const ILI9225_RAM_ADDR_SET2: u8 = 0x21; const ILI9225_GATE_SCAN_CTRL: u8 = 0x30; const ILI9225_VERTICAL_SCROLL_CTRL1: u8 = 0x31; const ILI9225_VERTICAL_SCROLL_CTRL2: u8 = 0x32; const ILI9225_VERTICAL_SCROLL_CTRL3: u8 = 0x33; const ILI9225_PARTIAL_DRIVING_POS1: u8 = 0x34; const ILI9225_PARTIAL_DRIVING_POS2: u8 = 0x35; const ILI9225_HORIZONTAL_WINDOW_ADDR1: u8 = 0x36; const ILI9225_HORIZONTAL_WINDOW_ADDR2: u8 = 0x37; const ILI9225_VERTICAL_WINDOW_ADDR1: u8 = 0x38; const ILI9225_VERTICAL_WINDOW_ADDR2: u8 = 0x39; const ILI9225_GAMMA_CTRL1: u8 = 0x50; const ILI9225_GAMMA_CTRL2: u8 = 0x51; const ILI9225_GAMMA_CTRL3: u8 = 0x52; const ILI9225_GAMMA_CTRL4: u8 = 0x53; const ILI9225_GAMMA_CTRL5: u8 = 0x54; const ILI9225_GAMMA_CTRL6: u8 = 0x55; const ILI9225_GAMMA_CTRL7: u8 = 0x56; const ILI9225_GAMMA_CTRL8: u8 = 0x57; const ILI9225_GAMMA_CTRL9: u8 = 0x58; const ILI9225_GAMMA_CTRL10: u8 = 0x59; async fn options_write_cmd<DI>(di: &mut DI, options: &ModelOptions) -> Result<(), DI::Error>
58where
59 DI: Interface,
60{
61 let driver_high_byte = match options.orientation.rotation {
63 Rotation::Deg0 => 0x01,
64 Rotation::Deg90 => 0x00,
65 Rotation::Deg180 => 0x02,
66 Rotation::Deg270 => 0x03,
67 };
68
69 let driver_params = [driver_high_byte, 0x1C];
70 di.write_raw(ILI9225_DRIVER_OUTPUT_CTRL, &driver_params)
71 .await?;
72
73 let color_order_byte = match options.color_order {
75 ColorOrder::Rgb => 0x00,
76 ColorOrder::Bgr => 0x10,
77 };
78
79 let entry_low_byte = if options.orientation.rotation.is_vertical() {
80 0x38
81 } else {
82 0x30
83 };
84 let entry_params = [color_order_byte, entry_low_byte];
85 di.write_raw(ILI9225_ENTRY_MODE, &entry_params).await?;
86
87 Ok(())
88}
89
90fn options2ctrl_low(options: &ModelOptions) -> u8 {
91 0b10011
92 | match options.invert_colors {
93 options::ColorInversion::Normal => 0,
94 options::ColorInversion::Inverted => 0b100,
95 }
96}
97
98impl Model for ILI9225Rgb565 {
99 type ColorFormat = Rgb565;
100 const FRAMEBUFFER_SIZE: (u16, u16) = (176, 220);
101 const RESET_DURATION: u32 = 1000;
102
103 async fn init<DELAY, DI>(
104 &mut self,
105 di: &mut DI,
106 delay: &mut DELAY,
107 options: &ModelOptions,
108 ) -> Result<SetAddressMode, ModelInitError<DI::Error>>
109 where
110 DELAY: DelayNs,
111 DI: Interface,
112 {
113 let madctl = SetAddressMode::from(options);
114
115 di.write_raw(ILI9225_POWER_CTRL1, &[0x00, 0x00]).await?; di.write_raw(ILI9225_POWER_CTRL2, &[0x00, 0x00]).await?; di.write_raw(ILI9225_POWER_CTRL3, &[0x00, 0x00]).await?; di.write_raw(ILI9225_POWER_CTRL4, &[0x00, 0x00]).await?; di.write_raw(ILI9225_POWER_CTRL5, &[0x00, 0x00]).await?; delay.delay_us(40_000).await;
123
124 di.write_raw(ILI9225_POWER_CTRL1, &[0x00, 0x18]).await?; di.write_raw(ILI9225_POWER_CTRL2, &[0x61, 0x21]).await?; di.write_raw(ILI9225_POWER_CTRL3, &[0x00, 0x6F]).await?; di.write_raw(ILI9225_POWER_CTRL4, &[0x49, 0x5F]).await?; di.write_raw(ILI9225_POWER_CTRL5, &[0x08, 0x00]).await?; delay.delay_us(10_000).await;
130 di.write_raw(ILI9225_POWER_CTRL2, &[0x10, 0x3B]).await?; delay.delay_us(30_000).await;
132
133 di.write_raw(ILI9225_LCD_AC_DRIVING_CTRL, &[0x01, 0x00])
134 .await?; options_write_cmd(di, options).await?;
137 di.write_raw(ILI9225_DISP_CTRL1, &[0x00, 0x00]).await?; di.write_raw(ILI9225_BLANK_PERIOD_CTRL1, &[0x08, 0x08])
139 .await?; di.write_raw(ILI9225_FRAME_CYCLE_CTRL, &[0x11, 0x00])
141 .await?; di.write_raw(ILI9225_INTERFACE_CTRL, &[0x00, 0x00]).await?; di.write_raw(ILI9225_OSC_CTRL, &[0x0F, 0x01]).await?; di.write_raw(ILI9225_VCI_RECYCLING, &[0x00, 0x20]).await?; di.write_raw(ILI9225_RAM_ADDR_SET1, &[0x00, 0x00]).await?; di.write_raw(ILI9225_RAM_ADDR_SET2, &[0x00, 0x00]).await?; di.write_raw(ILI9225_GATE_SCAN_CTRL, &[0x00, 0x00]).await?;
150 di.write_raw(ILI9225_VERTICAL_SCROLL_CTRL1, &[0x00, 0xDB])
151 .await?;
152 di.write_raw(ILI9225_VERTICAL_SCROLL_CTRL2, &[0x00, 0x00])
153 .await?;
154 di.write_raw(ILI9225_VERTICAL_SCROLL_CTRL3, &[0x00, 0x00])
155 .await?;
156 di.write_raw(ILI9225_PARTIAL_DRIVING_POS1, &[0x00, 0xDB])
157 .await?;
158 di.write_raw(ILI9225_PARTIAL_DRIVING_POS2, &[0x00, 0x00])
159 .await?;
160 di.write_raw(ILI9225_HORIZONTAL_WINDOW_ADDR1, &[0x00, 0xAF])
161 .await?;
162 di.write_raw(ILI9225_HORIZONTAL_WINDOW_ADDR2, &[0x00, 0x00])
163 .await?;
164 di.write_raw(ILI9225_VERTICAL_WINDOW_ADDR1, &[0x00, 0xDB])
165 .await?;
166 di.write_raw(ILI9225_VERTICAL_WINDOW_ADDR2, &[0x00, 0x00])
167 .await?;
168
169 di.write_raw(ILI9225_GAMMA_CTRL1, &[0x00, 0x00]).await?;
171 di.write_raw(ILI9225_GAMMA_CTRL2, &[0x08, 0x08]).await?;
172 di.write_raw(ILI9225_GAMMA_CTRL3, &[0x08, 0x0A]).await?;
173 di.write_raw(ILI9225_GAMMA_CTRL4, &[0x00, 0x0A]).await?;
174 di.write_raw(ILI9225_GAMMA_CTRL5, &[0x0A, 0x08]).await?;
175 di.write_raw(ILI9225_GAMMA_CTRL6, &[0x08, 0x08]).await?;
176 di.write_raw(ILI9225_GAMMA_CTRL7, &[0x00, 0x00]).await?;
177 di.write_raw(ILI9225_GAMMA_CTRL8, &[0x0A, 0x00]).await?;
178 di.write_raw(ILI9225_GAMMA_CTRL9, &[0x07, 0x10]).await?;
179 di.write_raw(ILI9225_GAMMA_CTRL10, &[0x07, 0x10]).await?;
180
181 di.write_raw(ILI9225_DISP_CTRL1, &[0x00, 0x12]).await?;
182 delay.delay_us(50_000).await;
183
184 let low = options2ctrl_low(options);
185
186 di.write_raw(ILI9225_DISP_CTRL1, &[0x10, low]).await?;
187 delay.delay_us(50_000).await;
188
189 Ok(madctl)
190 }
191
192 async fn update_address_window<DI>(
193 di: &mut DI,
194 rotation: Rotation,
195 sx: u16,
196 sy: u16,
197 ex: u16,
198 ey: u16,
199 ) -> Result<(), DI::Error>
200 where
201 DI: Interface,
202 {
203 match rotation {
204 Rotation::Deg0 | Rotation::Deg180 => {
205 di.write_raw(0x37, &sx.to_be_bytes()).await?;
206 di.write_raw(0x36, &ex.to_be_bytes()).await?;
207 di.write_raw(0x39, &sy.to_be_bytes()).await?;
208 di.write_raw(0x38, &ey.to_be_bytes()).await?;
209 di.write_raw(0x20, &sx.to_be_bytes()).await?;
210 di.write_raw(0x21, &sy.to_be_bytes()).await
211 }
212 Rotation::Deg90 | Rotation::Deg270 => {
213 di.write_raw(0x39, &sx.to_be_bytes()).await?;
214 di.write_raw(0x38, &ex.to_be_bytes()).await?;
215 di.write_raw(0x37, &sy.to_be_bytes()).await?;
216 di.write_raw(0x36, &ey.to_be_bytes()).await?;
217 di.write_raw(0x21, &sx.to_be_bytes()).await?;
218 di.write_raw(0x20, &sy.to_be_bytes()).await
219 }
220 }
221 }
222
223 async fn sleep<DI, DELAY>(di: &mut DI, delay: &mut DELAY) -> Result<(), DI::Error>
224 where
225 DI: Interface,
226 DELAY: DelayNs,
227 {
228 di.write_raw(ILI9225_DISP_CTRL1, &[0x00, 0x00]).await?;
229 delay.delay_us(50_000).await;
230 di.write_raw(ILI9225_POWER_CTRL2, &[0x00, 0x07]).await?;
231 delay.delay_us(50_000).await;
232 di.write_raw(ILI9225_POWER_CTRL1, &[0x0A, 0x01]).await
233 }
234
235 async fn wake<DI, DELAY>(di: &mut DI, delay: &mut DELAY) -> Result<(), DI::Error>
236 where
237 DI: Interface,
238 DELAY: DelayNs,
239 {
240 di.write_raw(ILI9225_POWER_CTRL1, &[0x0A, 0x00]).await?;
241 di.write_raw(ILI9225_POWER_CTRL2, &[0x10, 0x3B]).await?;
242 delay.delay_us(50_000).await;
243 di.write_raw(ILI9225_DISP_CTRL1, &[0x10, 0x17]).await
244 }
245
246 async fn write_memory_start<DI>(di: &mut DI) -> Result<(), DI::Error>
247 where
248 DI: Interface,
249 {
250 di.write_command(WriteMemoryStartILI9225).await
251 }
252
253 async fn update_options<DI>(&self, di: &mut DI, options: &ModelOptions) -> Result<(), DI::Error>
254 where
255 DI: Interface,
256 {
257 options_write_cmd(di, options).await
258 }
259 async fn set_tearing_effect<DI>(
260 di: &mut DI,
261 tearing_effect: options::TearingEffect,
262 options: &ModelOptions,
263 ) -> Result<(), DI::Error>
264 where
265 DI: Interface,
266 {
267 let low = options2ctrl_low(options);
268 let high = match tearing_effect {
270 options::TearingEffect::Off => 0,
271 options::TearingEffect::Vertical => 0x10,
272 options::TearingEffect::HorizontalAndVertical => 0x10,
273 };
274
275 di.write_raw(ILI9225_DISP_CTRL1, &[high, low]).await
276 }
277 async fn set_vertical_scroll_region<DI>(
278 _di: &mut DI,
279 _top_fixed_area: u16,
280 _bottom_fixed_area: u16,
281 ) -> Result<(), DI::Error>
282 where
283 DI: Interface,
284 {
285 Ok(())
287 }
288 async fn set_vertical_scroll_offset<DI>(_di: &mut DI, _offset: u16) -> Result<(), DI::Error>
289 where
290 DI: Interface,
291 {
292 Ok(())
294 }
295}
296
297crate::dcs::macros::dcs_basic_command!(
298 WriteMemoryStartILI9225,
300 0x22
301);
302
303crate::dcs::macros::dcs_basic_command!(
304 SoftResetILI9225,
306 0x28
307);