lcd_ili9341_spi/
device.rs

1use embedded_hal as hal;
2
3use hal::delay::DelayNs;
4use hal::digital::OutputPin;
5use hal::pwm::SetDutyCycle;
6use hal::spi::SpiBus;
7
8use crate::commands::*;
9use crate::utils::{color_buffer, u16_to_bytes};
10
11const COLUMNS: u16 = 240;
12const PAGES: u16 = 320;
13
14#[derive(Debug)]
15pub enum LcdError {
16    PinError,
17    SpiError,
18}
19
20/// Display rotation, where Rotate0 is the default vertical orientation.
21/// Enum variants represent clock-wise rotation angles.
22#[derive(Debug)]
23pub enum LcdOrientation {
24    Rotate0,
25    Rotate90,
26    Rotate180,
27    Rotate270,
28}
29
30/// Main LCD struct
31///
32/// Required hardware connections:
33/// - spi: the Spi interface
34/// - dc_pin: Data / Command selector
35/// - rst_pin: Device reset pin
36/// - bl_pin: backlight level pin (PWM)
37pub struct Lcd<T, U, V, W> {
38    spi: T,
39    dc_pin: U,  // Data / Command - 0=WriteCommand, 1=WriteData
40    rst_pin: V, // Reset
41    bl_pin: W,  // Backlight PWM
42    orientation: LcdOrientation,
43    max_buffer_size: usize,
44}
45
46impl<T, U, V, W> Lcd<T, U, V, W>
47where
48    T: SpiBus,
49    U: OutputPin,
50    V: OutputPin,
51    W: SetDutyCycle,
52{
53    pub fn new(spi: T, dc_pin: U, rst_pin: V, bl_pin: W) -> Self {
54        Self {
55            spi,
56            dc_pin,
57            rst_pin,
58            bl_pin,
59            orientation: LcdOrientation::Rotate0,
60            max_buffer_size: 32,
61        }
62    }
63    /// Sets display's rotation
64    ///
65    /// Should be called before LCD initialization:
66    /// ```
67    /// let mut lcd = Lcd::new(
68    ///     spi,
69    ///     dc_pin,
70    ///     rst_pin,
71    ///     bl_pin
72    ///    );
73    ///    .with_orientation(lcd_2inch4::LcdOrientation::Rotate90);
74    /// let _ = lcd.init(&mut delay);
75    /// ```
76
77    pub fn with_orientation(mut self, orientation: LcdOrientation) -> Self {
78        self.orientation = orientation;
79        self
80    }
81
82    /// Max SPI chunk size. In bytes
83    pub fn with_max_buffer_size(mut self, size: usize) -> Self {
84        self.max_buffer_size = size;
85        self
86    }
87
88    fn size(&self) -> (u16, u16) {
89        match self.orientation {
90            LcdOrientation::Rotate0 | LcdOrientation::Rotate180 => (COLUMNS, PAGES),
91            LcdOrientation::Rotate90 | LcdOrientation::Rotate270 => (PAGES, COLUMNS),
92        }
93    }
94
95    fn memory_access_control_value(&self) -> u8 {
96        let orientation = match self.orientation {
97            LcdOrientation::Rotate0 => 0b00000000,
98            LcdOrientation::Rotate90 => 0b01100000,
99            LcdOrientation::Rotate180 => 0b11000000,
100            LcdOrientation::Rotate270 => 0b10100000,
101        };
102        orientation | 0b00001000
103    }
104
105    pub fn reset<D>(&mut self, delay: &mut D) -> Result<(), LcdError>
106    where
107        D: DelayNs,
108    {
109        // delay values taken from the Arduino C driver
110        delay.delay_ms(200);
111        self.rst_pin.set_low().map_err(|_| LcdError::PinError)?;
112        delay.delay_ms(200);
113        self.rst_pin.set_high().map_err(|_| LcdError::PinError)?;
114        delay.delay_ms(200);
115        Ok(())
116    }
117
118    pub fn set_backlight(&mut self, value: u16) -> Result<(), LcdError> {
119        self.bl_pin
120            .set_duty_cycle(value)
121            .map_err(|_| LcdError::PinError)?;
122        Ok(())
123    }
124
125    fn write_command(&mut self, cmd: u8) -> Result<(), LcdError> {
126        self.dc_pin.set_low().map_err(|_| LcdError::PinError)?;
127        self.spi.write(&[cmd]).map_err(|_| LcdError::SpiError)?;
128        Ok(())
129    }
130
131    /// Sets the data pin
132    #[inline(always)]
133    pub(crate) fn enable_write_data(&mut self) -> Result<(), LcdError> {
134        self.dc_pin.set_high().map_err(|_| LcdError::PinError)?;
135        Ok(())
136    }
137
138    /// Sets the data pin and sends the payload
139    #[inline(always)]
140    pub(crate) fn write_data(&mut self, data: &[u8]) -> Result<(), LcdError> {
141        let mut offset = 0;
142        self.enable_write_data()?;
143        while offset < data.len() {
144            let end = data.len().min(offset + self.max_buffer_size);
145            self.write_data_continue(&data[offset..end])?;
146            offset += self.max_buffer_size;
147        }
148        Ok(())
149    }
150
151    /// Sends the payload via SPI.
152    /// Expects to be called only after `enable_write_data` or `write_data`
153    #[inline(always)]
154    pub(crate) fn write_data_continue(&mut self, data: &[u8]) -> Result<(), LcdError> {
155        self.spi.write(data).map_err(|_| LcdError::SpiError)?;
156        Ok(())
157    }
158
159    pub fn init<D>(&mut self, delay: &mut D) -> Result<(), LcdError>
160    where
161        D: DelayNs,
162    {
163        // command sequence taken from Arduino C driver
164        self.reset(delay)?;
165
166        self.write_command(SLEEP_OUT)?;
167
168        self.write_command(POWER_CONTROL_B)?;
169        self.write_data(&[0x00])?;
170        self.write_data(&[0xC1])?;
171        self.write_data(&[0x30])?;
172        self.write_command(POWER_ON_SEQ_CONTROL)?;
173        self.write_data(&[0x64])?;
174        self.write_data(&[0x03])?;
175        self.write_data(&[0x12])?;
176        self.write_data(&[0x81])?;
177        self.write_command(DRIVER_TIMING_CONTROL_A)?;
178        self.write_data(&[0x85])?;
179        self.write_data(&[0x00])?;
180        self.write_data(&[0x79])?;
181        self.write_command(POWER_CONTROL_A)?;
182        self.write_data(&[0x39])?;
183        self.write_data(&[0x2C])?;
184        self.write_data(&[0x00])?;
185        self.write_data(&[0x34])?;
186        self.write_data(&[0x02])?;
187        self.write_command(PUMP_RATIO_CONTROL)?;
188        self.write_data(&[0x20])?;
189        self.write_command(DRIVER_TIMING_CONTROL_B)?;
190        self.write_data(&[0x00])?;
191        self.write_data(&[0x00])?;
192        self.write_command(POWER_CONTROL_1)?;
193        self.write_data(&[0x1D])?;
194        self.write_command(POWER_CONTROL_2)?;
195        self.write_data(&[0x12])?;
196        self.write_command(VCOM_CONTROL_1)?;
197        self.write_data(&[0x33])?;
198        self.write_data(&[0x3F])?;
199        self.write_command(VCOM_CONTROL_2)?;
200        self.write_data(&[0x92])?;
201        self.write_command(PIXEL_FORMAT_SET)?;
202        self.write_data(&[0x55])?;
203        self.write_command(MEMORY_ACCESS_CONTROL)?;
204        self.write_data(&[self.memory_access_control_value()])?;
205        self.write_command(FRAME_CONTROL_NORMAL_MODE)?;
206        self.write_data(&[0x00])?;
207        self.write_data(&[0x12])?;
208        self.write_command(DISPLAY_FUNCTION_CONTROL)?;
209        self.write_data(&[0x0A])?;
210        self.write_data(&[0xA2])?;
211
212        self.write_command(SET_TEAR_SCANLINE)?;
213        self.write_data(&[0x02])?;
214
215        self.write_command(DISPLAY_ON)?;
216        self.set_gamma()?;
217
218        self.set_backlight(255)?;
219
220        Ok(())
221    }
222
223    fn set_gamma(&mut self) -> Result<(), LcdError> {
224        self.write_command(ENABLE_3G)?;
225        self.write_data(&[0x00])?;
226        self.write_command(GAMMA_SET)?;
227        self.write_data(&[0x01])?;
228        self.write_command(POSITIVE_GAMMA_CORRECTION)?;
229        self.write_data(&[0x0F])?;
230        self.write_data(&[0x22])?;
231        self.write_data(&[0x1C])?;
232        self.write_data(&[0x1B])?;
233        self.write_data(&[0x08])?;
234        self.write_data(&[0x0F])?;
235        self.write_data(&[0x48])?;
236        self.write_data(&[0xB8])?;
237        self.write_data(&[0x34])?;
238        self.write_data(&[0x05])?;
239        self.write_data(&[0x0C])?;
240        self.write_data(&[0x09])?;
241        self.write_data(&[0x0F])?;
242        self.write_data(&[0x07])?;
243        self.write_data(&[0x00])?;
244        self.write_command(NEGATIVE_GAMMA_CORRECTION)?;
245        self.write_data(&[0x00])?;
246        self.write_data(&[0x23])?;
247        self.write_data(&[0x24])?;
248        self.write_data(&[0x07])?;
249        self.write_data(&[0x10])?;
250        self.write_data(&[0x07])?;
251        self.write_data(&[0x38])?;
252        self.write_data(&[0x47])?;
253        self.write_data(&[0x4B])?;
254        self.write_data(&[0x0A])?;
255        self.write_data(&[0x13])?;
256        self.write_data(&[0x06])?;
257        self.write_data(&[0x30])?;
258        self.write_data(&[0x38])?;
259        self.write_data(&[0x0F])?;
260        Ok(())
261    }
262
263    /// Leave off state
264    pub fn display_on(&mut self) -> Result<(), LcdError> {
265        self.write_command(DISPLAY_ON)
266    }
267
268    /// Enter off state
269    pub fn display_off(&mut self) -> Result<(), LcdError> {
270        self.write_command(DISPLAY_OFF)
271    }
272
273    /// Enter sleep mode
274    pub fn enter_sleep_mode(&mut self) -> Result<(), LcdError> {
275        self.write_command(ENTER_SLEEP_MODE)
276    }
277
278    /// Disable sleep mode
279    pub fn leave_sleep_mode(&mut self) -> Result<(), LcdError> {
280        self.write_command(SLEEP_OUT)
281    }
282
283    pub(crate) fn set_window(
284        &mut self,
285        x0: u16,
286        y0: u16,
287        x1: u16,
288        y1: u16,
289    ) -> Result<(), LcdError> {
290        let c1 = x1.saturating_sub(1).max(x0);
291        let p1 = y1.saturating_sub(1).max(y0);
292        let (c0h, c0l) = u16_to_bytes(x0);
293        let (c1h, c1l) = u16_to_bytes(c1);
294        let (p0h, p0l) = u16_to_bytes(y0);
295        let (p1h, p1l) = u16_to_bytes(p1);
296
297        self.write_command(COLUMN_ADDRESS_SET)?;
298        self.write_data(&[c0h, c0l, c1h, c1l])?;
299
300        self.write_command(PAGE_ADDRESS_SET)?;
301        self.write_data(&[p0h, p0l, p1h, p1l])?;
302
303        self.write_command(MEMORY_WRITE)?;
304        Ok(())
305    }
306
307    /// Clear the entire screen with the given color
308    pub fn clear(&mut self, color: u16) -> Result<(), LcdError> {
309        let (w, h) = self.size();
310        self.fill_rect(0, 0, w, h, color)?;
311
312        Ok(())
313    }
314
315    /// Draw filled rect or line (when width or height set to 1)
316    pub fn fill_rect(
317        &mut self,
318        x: u16,
319        y: u16,
320        w: u16,
321        h: u16,
322        color: u16,
323    ) -> Result<(), LcdError> {
324        self.set_window(x, y, x + w, y + h)?;
325        self.enable_write_data()?;
326
327        // spi send optimization
328        // slight buffer overflow seems ok
329        let chunk = color_buffer::<32>(color);
330        for _ in 0..(w as u32 * h as u32).div_ceil(16) {
331            self.write_data_continue(&chunk)?;
332        }
333        Ok(())
334    }
335
336    /// Draw raw sprite data on the screen.
337    ///
338    /// The input buffer should contain color information in high_byte_u8, low_byte_u8 format.
339    /// Buffer length should match the rect specified by the (x, y, w, h) although it's currently
340    /// not checked.
341    pub fn draw_sprite(
342        &mut self,
343        x: u16,
344        y: u16,
345        w: u16,
346        h: u16,
347        data: &[u8],
348    ) -> Result<(), LcdError> {
349        self.set_window(x, y, x + w, y + h)?;
350        self.write_data(&data)?;
351        Ok(())
352    }
353}