lcd_async/models/
ili9225.rs

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
14/// ILI9225 display in Rgb565 color mode.
15pub 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; // Driver Output Control
24const ILI9225_LCD_AC_DRIVING_CTRL: u8 = 0x02; // LCD AC Driving Control
25const ILI9225_ENTRY_MODE: u8 = 0x03; // Entry Mode
26const ILI9225_DISP_CTRL1: u8 = 0x07; // Display Control 1
27const ILI9225_BLANK_PERIOD_CTRL1: u8 = 0x08; // Blank Period Control
28const ILI9225_FRAME_CYCLE_CTRL: u8 = 0x0B; // Frame Cycle Control
29const ILI9225_INTERFACE_CTRL: u8 = 0x0C; // Interface Control
30const ILI9225_OSC_CTRL: u8 = 0x0F; // Osc Control
31const ILI9225_VCI_RECYCLING: u8 = 0x15; // Osc Control
32const ILI9225_RAM_ADDR_SET1: u8 = 0x20; // Osc Control
33const ILI9225_RAM_ADDR_SET2: u8 = 0x21; // Osc Control
34
35const ILI9225_GATE_SCAN_CTRL: u8 = 0x30; // Gate Scan Control Register
36const ILI9225_VERTICAL_SCROLL_CTRL1: u8 = 0x31; // Vertical Scroll Control 1 Register
37const ILI9225_VERTICAL_SCROLL_CTRL2: u8 = 0x32; // Vertical Scroll Control 2 Register
38const ILI9225_VERTICAL_SCROLL_CTRL3: u8 = 0x33; // Vertical Scroll Control 3 Register
39const ILI9225_PARTIAL_DRIVING_POS1: u8 = 0x34; // Partial Driving Position 1 Register
40const ILI9225_PARTIAL_DRIVING_POS2: u8 = 0x35; // Partial Driving Position 2 Register
41const ILI9225_HORIZONTAL_WINDOW_ADDR1: u8 = 0x36; // Horizontal Address Start Position
42const ILI9225_HORIZONTAL_WINDOW_ADDR2: u8 = 0x37; // Horizontal Address End Position
43const ILI9225_VERTICAL_WINDOW_ADDR1: u8 = 0x38; // Vertical Address Start Position
44const ILI9225_VERTICAL_WINDOW_ADDR2: u8 = 0x39; // Vertical Address End Position
45
46const ILI9225_GAMMA_CTRL1: u8 = 0x50; // Gamma Control 1
47const ILI9225_GAMMA_CTRL2: u8 = 0x51; // Gamma Control 2
48const ILI9225_GAMMA_CTRL3: u8 = 0x52; // Gamma Control 3
49const ILI9225_GAMMA_CTRL4: u8 = 0x53; // Gamma Control 4
50const ILI9225_GAMMA_CTRL5: u8 = 0x54; // Gamma Control 5
51const ILI9225_GAMMA_CTRL6: u8 = 0x55; // Gamma Control 6
52const ILI9225_GAMMA_CTRL7: u8 = 0x56; // Gamma Control 7
53const ILI9225_GAMMA_CTRL8: u8 = 0x57; // Gamma Control 8
54const ILI9225_GAMMA_CTRL9: u8 = 0x58; // Gamma Control 9
55const ILI9225_GAMMA_CTRL10: u8 = 0x59; // Gamma Control 10
56
57async fn options_write_cmd<DI>(di: &mut DI, options: &ModelOptions) -> Result<(), DI::Error>
58where
59    DI: Interface,
60{
61    // Command 1: DRIVER_OUTPUT_CTRL (0x01)
62    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    // Command 2: ENTRY_MODE (0x03)
74    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        /* Set SS bit and direction output from S528 to S1 */
116        di.write_raw(ILI9225_POWER_CTRL1, &[0x00, 0x00]).await?; // Set SAP,DSTB,STB
117        di.write_raw(ILI9225_POWER_CTRL2, &[0x00, 0x00]).await?; // Set APON,PON,AON,VCI1EN,VC
118        di.write_raw(ILI9225_POWER_CTRL3, &[0x00, 0x00]).await?; // Set BT,DC1,DC2,DC3
119        di.write_raw(ILI9225_POWER_CTRL4, &[0x00, 0x00]).await?; // Set GVDD
120        di.write_raw(ILI9225_POWER_CTRL5, &[0x00, 0x00]).await?; // Set VCOMH/VCOML voltage
121
122        delay.delay_us(40_000).await;
123
124        di.write_raw(ILI9225_POWER_CTRL1, &[0x00, 0x18]).await?; // Set APON,PON,AON,VCI1EN,VC
125        di.write_raw(ILI9225_POWER_CTRL2, &[0x61, 0x21]).await?; // Set BT,DC1,DC2,DC3
126        di.write_raw(ILI9225_POWER_CTRL3, &[0x00, 0x6F]).await?; // Set GVDD   /*007F 0088 */
127        di.write_raw(ILI9225_POWER_CTRL4, &[0x49, 0x5F]).await?; // Set VCOMH/VCOML voltage
128        di.write_raw(ILI9225_POWER_CTRL5, &[0x08, 0x00]).await?; // Set SAP,DSTB,STB
129        delay.delay_us(10_000).await;
130        di.write_raw(ILI9225_POWER_CTRL2, &[0x10, 0x3B]).await?; // Set APON,PON,AON,VCI1EN,VC
131        delay.delay_us(30_000).await;
132
133        di.write_raw(ILI9225_LCD_AC_DRIVING_CTRL, &[0x01, 0x00])
134            .await?; // set 1 line inversion
135
136        options_write_cmd(di, options).await?;
137        di.write_raw(ILI9225_DISP_CTRL1, &[0x00, 0x00]).await?; // Display off
138        di.write_raw(ILI9225_BLANK_PERIOD_CTRL1, &[0x08, 0x08])
139            .await?; // set the back porch and front porch
140        di.write_raw(ILI9225_FRAME_CYCLE_CTRL, &[0x11, 0x00])
141            .await?; // set the clocks number per line
142        di.write_raw(ILI9225_INTERFACE_CTRL, &[0x00, 0x00]).await?; // CPU  interface
143        di.write_raw(ILI9225_OSC_CTRL, &[0x0F, 0x01]).await?; // Set Osc  /*0e01*/
144        di.write_raw(ILI9225_VCI_RECYCLING, &[0x00, 0x20]).await?; // Set VCI recycling
145        di.write_raw(ILI9225_RAM_ADDR_SET1, &[0x00, 0x00]).await?; // RAM Address
146        di.write_raw(ILI9225_RAM_ADDR_SET2, &[0x00, 0x00]).await?; // RAM Address
147
148        /* Set GRAM area */
149        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        /* Set GAMMA curve */
170        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        // Acroding the datasheet, TEMON only one bit,
269        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        // Not support, ignore it
286        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        // Not support, ignore it
293        Ok(())
294    }
295}
296
297crate::dcs::macros::dcs_basic_command!(
298    /// Initiate Framebuffer Memory Write
299    WriteMemoryStartILI9225,
300    0x22
301);
302
303crate::dcs::macros::dcs_basic_command!(
304    /// Software Reset
305    SoftResetILI9225,
306    0x28
307);