ag_lcd/
display.rs

1use crate::Error;
2use embedded_hal::delay::DelayNs;
3use embedded_hal::digital::OutputPin;
4
5#[repr(u8)]
6#[allow(dead_code)]
7enum Command {
8    ClearDisplay = 0x01,   // LCD_CLEARDISPLAY
9    ReturnHome = 0x02,     // LCD_RETURNHOME
10    SetDisplayMode = 0x04, // LCD_ENTRYMODESET
11    SetDisplayCtrl = 0x08, // LCD_DISPLAYCONTROL
12    CursorShift = 0x10,    // LCD_CURSORSHIFT
13    SetDisplayFunc = 0x20, // LCD_FUNCTIONSET
14    SetCGramAddr = 0x40,   // LCD_SETCGRAMADDR
15    SetDDRAMAddr = 0x80,   // LCD_SETDDRAMADDR
16}
17
18#[repr(u8)]
19#[allow(dead_code)]
20enum Move {
21    Display = 0x08, // LCD_DISPLAYMOVE
22    Cursor = 0x00,  // LCD_CURSORMOVE
23}
24
25/// Flag that controls text direction
26#[repr(u8)]
27pub enum Layout {
28    /// Text runs from right to left
29    RightToLeft = 0x00, // LCD_ENTRYRIGHT
30
31    /// Text runs from left to right (default)
32    LeftToRight = 0x02, // LCD_ENTRYLEFT
33}
34
35/// Flag that sets the display to autoscroll
36#[repr(u8)]
37pub enum AutoScroll {
38    /// Turn AutoScroll on
39    On = 0x01, // LCD_ENTRYSHIFTINCREMENT
40
41    /// Turn AutoScroll off (default)
42    Off = 0x00, // LCD_ENTRYSHIFTDECREMENT
43}
44
45/// Flag that sets the display on/off
46#[repr(u8)]
47pub enum Display {
48    /// Turn Display on (default)
49    On = 0x04, // LCD_DISPLAYON
50
51    /// Turn Display off
52    Off = 0x00, // LCD_DISPLAYOFF
53}
54
55/// Flag that sets the cursor on/off
56#[repr(u8)]
57pub enum Cursor {
58    /// Turn Cursor on
59    On = 0x02, // LCD_CURSORON
60
61    /// Turn Cursor off
62    Off = 0x00, // LCD_CURSOROFF
63}
64
65/// Flag that sets cursor background to blink
66#[repr(u8)]
67pub enum Blink {
68    /// Turn Blink on
69    On = 0x01, // LCD_BLINKON
70
71    /// Turn Blink off (default)
72    Off = 0x00, // LCD_BLINKOFF
73}
74
75/// Flag that sets backlight state
76pub enum Backlight {
77    /// Turn Backlight on (default)
78    On,
79
80    /// Turn Backlight off
81    Off,
82}
83
84/// Flag used to indicate direction for display scrolling
85#[repr(u8)]
86pub enum Scroll {
87    /// Scroll display right
88    Right = 0x04, // LCD_MOVERIGHT
89
90    /// Scroll display left
91    Left = 0x00, // LCD_MOVELEFT
92}
93
94/// Flag for the bus mode of the display
95#[repr(u8)]
96pub enum Mode {
97    /// Use eight-bit bus (Set by [with_full_bus][LcdDisplay::with_full_bus])
98    EightBits = 0x10, // LCD_8BITMODE
99
100    /// Use four-bit bus (Set by [with_half_bus][LcdDisplay::with_half_bus])
101    FourBits = 0x00, // LCD_4BITMODE
102}
103
104/// Flag for the number of lines in the display
105#[repr(u8)]
106pub enum Lines {
107    /// Use four lines if available
108    ///
109    /// ## Notes
110    /// Since HD44780 doesn't support 4-line LCDs, 4-line display is used like a 2-line display,
111    /// but half of the characters were moved below the top part. Since the interface only allows
112    /// two states for amount of lines: two and one, a way to differentiate between four line and
113    /// two line mode is needed. According to HHD44780 documentation, when two-line display mode is
114    /// used, the bit that specifies font size is ignored. Because of that, we can use it to
115    /// differentiate between four line mode and two line mode.
116    FourLines = 0x0C,
117
118    /// Use two lines if available
119    TwoLines = 0x08, // LCD_2LINE
120
121    /// Use one line (default)
122    OneLine = 0x00, // LCD_1LINE
123}
124
125/// Flag for the character size of the display
126#[repr(u8)]
127pub enum Size {
128    /// Use display with 5x10 characters
129    Dots5x10 = 0x04, // LCD_5x10DOTS
130
131    /// Use display with 5x8 characters (default)
132    Dots5x8 = 0x00, // LCD_5x8DOTS
133}
134
135/// One of the most popular sizes for this kind of LCD is 16x2
136const DEFAULT_COLS: u8 = 16;
137
138const DEFAULT_DISPLAY_FUNC: u8 = Mode::FourBits as u8 | Lines::OneLine as u8 | Size::Dots5x8 as u8;
139const DEFAULT_DISPLAY_CTRL: u8 = Display::On as u8 | Cursor::Off as u8 | Blink::Off as u8;
140const DEFAULT_DISPLAY_MODE: u8 = Layout::LeftToRight as u8 | AutoScroll::Off as u8;
141
142const CMD_DELAY: u32 = 3500;
143const CHR_DELAY: u32 = 450;
144
145const RS: u8 = 0;
146const EN: u8 = 1;
147const RW: u8 = 2;
148const D0: u8 = 3;
149const D1: u8 = 4;
150const D2: u8 = 5;
151const D3: u8 = 6;
152const D4: u8 = 7;
153const D5: u8 = 8;
154const D6: u8 = 9;
155const D7: u8 = 10;
156const A: u8 = 11;
157
158/// The LCD display
159///
160/// Methods called on this struct will fail silently if the system or screen is
161/// misconfigured.
162pub struct LcdDisplay<T, D>
163where
164    T: OutputPin + Sized,
165    D: DelayNs + Sized,
166{
167    pins: [Option<T>; 12],
168    display_func: u8,
169    display_mode: u8,
170    display_ctrl: u8,
171    offsets: [u8; 4],
172    delay: D,
173    code: Error,
174}
175
176impl<T, D> LcdDisplay<T, D>
177where
178    T: OutputPin + Sized,
179    D: DelayNs + Sized,
180{
181    /// Create a new instance of the LcdDisplay
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// let peripherals = arduino_hal::Peripherals::take().unwrap();
187    /// let pins = arduino_hal::pins!(peripherals);
188    /// let delay = arduino_hal::Delay::new();
189    ///
190    /// let rs = pins.d12.into_output().downgrade();
191    /// let rw = pins.d11.into_output().downgrade();
192    /// let en = pins.d10.into_output().downgrade();
193    /// let d4 = pins.d5.into_output().downgrade();
194    /// let d5 = pins.d4.into_output().downgrade();
195    /// let d6 = pins.d3.into_output().downgrade();
196    /// let d7 = pins.d2.into_output().downgrade();
197    ///
198    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
199    ///     .with_half_bus(d4, d5, d6, d7)
200    ///     .with_blink(Blink::On)
201    ///     .with_cursor(Cursor::Off)
202    ///     .with_rw(d10) // optional (set lcd pin to GND if not provided)
203    ///     .build();
204    /// ```
205    pub fn new(rs: T, en: T, delay: D) -> Self {
206        Self {
207            pins: [
208                Some(rs),
209                Some(en),
210                None,
211                None,
212                None,
213                None,
214                None,
215                None,
216                None,
217                None,
218                None,
219                None,
220            ],
221            display_func: DEFAULT_DISPLAY_FUNC,
222            display_mode: DEFAULT_DISPLAY_MODE,
223            display_ctrl: DEFAULT_DISPLAY_CTRL,
224            offsets: [0x00, 0x40, 0x00 + DEFAULT_COLS, 0x40 + DEFAULT_COLS],
225            delay,
226            code: Error::None,
227        }
228    }
229
230    /// Set amount of columns this lcd has
231    pub fn with_cols(mut self, mut cols: u8) -> Self {
232        cols = cols.clamp(0, 31);
233        // First two bytes skipped because they are always the same
234        self.offsets[2] = 0x00 + cols;
235        self.offsets[3] = 0x40 + cols;
236        self
237    }
238
239    /// Set four pins that connect to the lcd screen and configure the display for four-pin mode.
240    ///
241    /// The parameters below (d4-d7) are labeled in the order that you should see on the LCD
242    /// itself. Regardless of how the display is connected to the arduino, 'D4' on the LCD should
243    /// map to 'd4' when calling this function.
244    ///
245    /// # Examples
246    ///
247    /// ```
248    /// ...
249    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
250    ///     .with_half_bus(d4, d5, d6, d7)
251    ///     .build();
252    /// ```
253    pub fn with_half_bus(mut self, d4: T, d5: T, d6: T, d7: T) -> Self {
254        // set to four-bit bus mode and assign pins
255        self.display_func &= !(Mode::EightBits as u8);
256        self.pins[D4 as usize] = Some(d4);
257        self.pins[D5 as usize] = Some(d5);
258        self.pins[D6 as usize] = Some(d6);
259        self.pins[D7 as usize] = Some(d7);
260        self
261    }
262
263    /// Set eight pins that connect to the lcd screen and configure the display for eight-pin mode.
264    ///
265    /// The parameters below (d0-d7) are labeled in the order that you should see on the LCD
266    /// itself. Regardless of how the display is connected to the arduino, 'D4' on the LCD should
267    /// map to 'd4' when calling this function.
268    ///
269    /// # Examples
270    ///
271    /// ```
272    /// ...
273    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
274    ///     .with_full_bus(d0, d1, d4, d5, d6, d7, d6, d7)
275    ///     .build();
276    /// ```
277    #[allow(clippy::too_many_arguments)]
278    pub fn with_full_bus(mut self, d0: T, d1: T, d2: T, d3: T, d4: T, d5: T, d6: T, d7: T) -> Self {
279        // set to eight-bit bus mode and assign pins
280        self.display_func |= Mode::EightBits as u8;
281        self.pins[D0 as usize] = Some(d0);
282        self.pins[D1 as usize] = Some(d1);
283        self.pins[D2 as usize] = Some(d2);
284        self.pins[D3 as usize] = Some(d3);
285        self.pins[D4 as usize] = Some(d4);
286        self.pins[D5 as usize] = Some(d5);
287        self.pins[D6 as usize] = Some(d6);
288        self.pins[D7 as usize] = Some(d7);
289        self
290    }
291
292    /// Set an RW (Read/Write) pin to use (This is optional and can normally be connected directly
293    /// to GND, leaving the display permanently in Write mode)
294    ///
295    /// # Examples
296    ///
297    /// ```
298    /// ...
299    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
300    ///     .with_half_bus(d4, d5, d6, d7)
301    ///     .with_rw(d10)
302    ///     .build();
303    /// ```
304    pub fn with_rw(mut self, rw: T) -> Self {
305        self.pins[RW as usize] = Some(rw);
306        self
307    }
308
309    /// Set the character size of the LCD display. (Defaults to Size::Dots5x8)
310    ///
311    /// # Examples
312    ///
313    /// ```
314    /// ...
315    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
316    ///     .with_half_bus(d4, d5, d6, d7)
317    ///     .with_size(Size::Dots5x8)
318    ///     .build();
319    /// ```
320    pub fn with_size(mut self, value: Size) -> Self {
321        match value {
322            Size::Dots5x10 => self.display_func |= Size::Dots5x10 as u8,
323            Size::Dots5x8 => self.display_func &= !(Size::Dots5x10 as u8),
324        }
325        self
326    }
327
328    /// Set the number of lines on the LCD display. (Default is Lines::OneLine)
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// ...
334    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
335    ///     .with_half_bus(d4, d5, d6, d7)
336    ///     .with_lines(Lines::OneLine)
337    ///     .build();
338    /// ```
339    pub fn with_lines(mut self, value: Lines) -> Self {
340        match value {
341            Lines::FourLines => self.display_func |= Lines::FourLines as u8,
342            Lines::TwoLines => self.display_func |= Lines::TwoLines as u8,
343            Lines::OneLine => self.display_func &= !(Lines::TwoLines as u8),
344        }
345        self
346    }
347
348    /// Set the text direction layout of the LCD display. (Default is Layout::LeftToRight)
349    ///
350    /// # Examples
351    ///
352    /// ```
353    /// ...
354    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
355    ///     .with_half_bus(d4, d5, d6, d7)
356    ///     .with_layout(Layout::LeftToRight)
357    ///     .build();
358    /// ```
359    pub fn with_layout(mut self, value: Layout) -> Self {
360        match value {
361            Layout::LeftToRight => self.display_mode |= Layout::LeftToRight as u8,
362            Layout::RightToLeft => self.display_mode &= !(Layout::LeftToRight as u8),
363        }
364        self
365    }
366
367    /// Set the LCD display on or off initially. (Default is Display::On)
368    ///
369    /// # Examples
370    ///
371    /// ```
372    /// ...
373    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
374    ///     .with_half_bus(d4, d5, d6, d7)
375    ///     .with_display(Display::On)
376    ///     .build();
377    /// ```
378    pub fn with_display(mut self, value: Display) -> Self {
379        match value {
380            Display::On => self.display_ctrl |= Display::On as u8,
381            Display::Off => self.display_ctrl &= !(Display::On as u8),
382        }
383        self
384    }
385
386    /// Set the cursor on or off initially. (Default is Cursor::Off)
387    ///
388    /// # Examples
389    ///
390    /// ```
391    /// ...
392    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
393    ///     .with_half_bus(d4, d5, d6, d7)
394    ///     .with_cursor(Cursor::Off)
395    ///     .build();
396    /// ```
397    pub fn with_cursor(mut self, value: Cursor) -> Self {
398        match value {
399            Cursor::On => self.display_ctrl |= Cursor::On as u8,
400            Cursor::Off => self.display_ctrl &= !(Cursor::On as u8),
401        }
402        self
403    }
404
405    /// Set the cursor background to blink on and off. (Default is Blink::Off)
406    ///
407    /// # Examples
408    ///
409    /// ```
410    /// ...
411    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
412    ///     .with_half_bus(d4, d5, d6, d7)
413    ///     .with_blink(Blink::Off)
414    ///     .build();
415    /// ```
416    pub fn with_blink(mut self, value: Blink) -> Self {
417        match value {
418            Blink::On => self.display_ctrl |= Blink::On as u8,
419            Blink::Off => self.display_ctrl &= !(Blink::On as u8),
420        }
421        self
422    }
423
424    /// Set a pin for controlling backlight state
425    pub fn with_backlight(mut self, backlight_pin: T) -> Self {
426        self.pins[A as usize] = Some(backlight_pin);
427        self
428    }
429
430    /// Set autoscroll on or off. (Default is AutoScroll::Off)
431    ///
432    /// # Examples
433    ///
434    /// ```
435    /// ...
436    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
437    ///     .with_half_bus(d4, d5, d6, d7)
438    ///     .with_autoscroll(AutoScroll::Off)
439    ///     .build();
440    /// ```
441    pub fn with_autoscroll(mut self, value: AutoScroll) -> Self {
442        match value {
443            AutoScroll::On => self.display_mode |= AutoScroll::On as u8,
444            AutoScroll::Off => self.display_mode &= !(AutoScroll::On as u8),
445        }
446        self
447    }
448
449    /// Increase reliability of initialization of LCD.
450    ///
451    /// Some users experience unreliable initialization of the LCD, where
452    /// the LCD sometimes is unable to display symbols after running
453    /// `.build()`. This method toggles the LCD off and on with some
454    /// delay in between, 3 times. A higher `delay_toggle` tends to make
455    /// this method more reliable, and a value of `10 000` is recommended.
456    /// Note that this method should be run as close as possible to
457    /// `.build()`.
458    ///
459    /// # Examples
460    ///
461    /// ```
462    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
463    ///     .with_half_bus(d4, d5, d6, d7)
464    ///     .with_reliable_init(10000)
465    ///     .build();
466    /// ```
467    pub fn with_reliable_init(mut self, delay_toggle: u32) -> Self {
468        if self.display_ctrl == Display::On as u8 {
469            for _ in 0..3 {
470                self.delay.delay_us(delay_toggle);
471                self.display_off();
472                self.delay.delay_us(delay_toggle);
473                self.display_on();
474            }
475        } else {
476            for _ in 0..3 {
477                self.delay.delay_us(delay_toggle);
478                self.display_on();
479                self.delay.delay_us(delay_toggle);
480                self.display_off();
481            }
482        }
483
484        self
485    }
486
487    /// Finish construction of the LcdDisplay and initialized the
488    /// display to the provided settings.
489    ///
490    /// # Examples
491    ///
492    /// ```
493    /// use ag_lcd::{Display, Blink, Cursor, LcdDisplay};
494    ///
495    /// let peripherals = arduino_hal::Peripherals::take().unwrap();
496    /// let pins = arduino_hal::pins!(peripherals);
497    /// let delay = arduino_hal::Delay::new();
498    ///
499    /// let rs = pins.d12.into_output().downgrade();
500    /// let rw = pins.d11.into_output().downgrade();
501    /// let en = pins.d10.into_output().downgrade();
502    ///
503    /// // left-side names refer to lcd pinout (e.g. 'd4' = D4 on lcd)
504    /// let d4 = pins.d5.into_output().downgrade();
505    /// let d5 = pins.d4.into_output().downgrade();
506    /// let d6 = pins.d3.into_output().downgrade();
507    /// let d7 = pins.d2.into_output().downgrade();
508    ///
509    /// let mut lcd: LcdDisplay<_,_> = LcdDisplay::new(rs, en, delay)
510    ///     .with_half_bus(d4, d5, d6, d7)
511    ///     .with_display(Display::On)
512    ///     .with_blink(Blink::On)
513    ///     .with_cursor(Cursor::On)
514    ///     .with_rw(rw) // optional (set lcd pin to GND if not provided)
515    ///     .build();
516    ///
517    /// lcd.print("Test message!");
518    /// ```
519    pub fn build(mut self) -> Self {
520        self.delay.delay_us(50000);
521
522        self.set(RS, false);
523        self.set(EN, false);
524
525        if self.exists(RW) {
526            self.set(RW, false);
527        }
528
529        match self.mode() {
530            Mode::FourBits => {
531                // display function is four bit
532                self.update(0x03);
533                self.delay.delay_us(4500);
534
535                self.update(0x03);
536                self.delay.delay_us(4500);
537
538                self.update(0x03);
539                self.delay.delay_us(150);
540
541                self.update(0x02);
542            }
543            Mode::EightBits => {
544                // display function is eight bit
545                self.command(Command::SetDisplayFunc as u8 | self.display_func);
546                self.delay.delay_us(4500);
547
548                self.command(Command::SetDisplayFunc as u8 | self.display_func);
549                self.delay.delay_us(150);
550
551                self.command(Command::SetDisplayFunc as u8 | self.display_func);
552            }
553        }
554
555        self.command(Command::SetDisplayFunc as u8 | self.display_func);
556        self.delay.delay_us(CMD_DELAY);
557
558        self.command(Command::SetDisplayCtrl as u8 | self.display_ctrl);
559        self.delay.delay_us(CMD_DELAY);
560
561        self.command(Command::SetDisplayMode as u8 | self.display_mode);
562        self.delay.delay_us(CMD_DELAY);
563
564        self.clear();
565        self.home();
566
567        // set an error code display is misconfigured
568        self.validate();
569        self
570    }
571
572    /// Set the position of the cursor.
573    ///
574    /// # Examples
575    ///
576    /// ```
577    /// let mut lcd: LcdDisplay<_,_> = ...;
578    ///
579    /// let row = 0;
580    /// let col = 2;
581    ///
582    /// lcd.set_position(col,row);
583    /// ```
584    pub fn set_position(&mut self, col: u8, mut row: u8) {
585        let max_lines = 4;
586
587        let num_lines = match self.lines() {
588            Lines::FourLines => 4,
589            Lines::TwoLines => 2,
590            Lines::OneLine => 1,
591        };
592
593        let mut pos = col;
594
595        if row >= max_lines {
596            row = max_lines.saturating_sub(1);
597        }
598
599        if row >= num_lines {
600            row = num_lines.saturating_sub(1);
601        }
602
603        pos += self.offsets[row as usize];
604        self.command(Command::SetDDRAMAddr as u8 | pos);
605        self.delay.delay_us(CMD_DELAY);
606    }
607
608    /// Scroll the display right or left.
609    ///
610    /// # Examples
611    ///
612    /// ```
613    /// let mut lcd: LcdDisplay<_,_> = ...;
614    ///
615    /// let direction = Scroll::Left;
616    /// let distance = 2;
617    ///
618    /// lcd.set_scroll(direction,distance);
619    /// ```
620    pub fn set_scroll(&mut self, direction: Scroll, distance: u8) {
621        let command = Command::CursorShift as u8 | Move::Display as u8 | direction as u8;
622        for _ in 0..distance {
623            self.command(command);
624            self.delay.delay_us(CMD_DELAY);
625        }
626    }
627
628    /// Set the text direction layout.
629    ///
630    /// # Examples
631    ///
632    /// ```
633    /// let mut lcd: LcdDisplay<_,_> = ...;
634    ///
635    /// lcd.set_layout(Layout::LeftToRight);
636    /// ```
637    pub fn set_layout(&mut self, layout: Layout) {
638        match layout {
639            Layout::LeftToRight => self.display_mode |= Layout::LeftToRight as u8,
640            Layout::RightToLeft => self.display_mode &= !(Layout::LeftToRight as u8),
641        }
642        self.command(Command::SetDisplayMode as u8 | self.display_mode);
643        self.delay.delay_us(CMD_DELAY);
644    }
645
646    /// Turn the display on or off.
647    ///
648    /// # Examples
649    ///
650    /// ```
651    /// let mut lcd: LcdDisplay<_,_> = ...;
652    ///
653    /// lcd.set_display(Display::Off);
654    /// ```
655    pub fn set_display(&mut self, display: Display) {
656        match display {
657            Display::On => self.display_ctrl |= Display::On as u8,
658            Display::Off => self.display_ctrl &= !(Display::On as u8),
659        }
660        self.command(Command::SetDisplayCtrl as u8 | self.display_ctrl);
661        self.delay.delay_us(CMD_DELAY);
662    }
663
664    /// Turn the cursor on or off.
665    ///
666    /// # Examples
667    ///
668    /// ```
669    /// let mut lcd: LcdDisplay<_,_> = ...;
670    ///
671    /// lcd.set_cursor(Cursor::On);
672    /// ```
673    pub fn set_cursor(&mut self, cursor: Cursor) {
674        match cursor {
675            Cursor::On => self.display_ctrl |= Cursor::On as u8,
676            Cursor::Off => self.display_ctrl &= !(Cursor::On as u8),
677        }
678        self.command(Command::SetDisplayCtrl as u8 | self.display_ctrl);
679        self.delay.delay_us(CMD_DELAY);
680    }
681
682    /// Make the background of the cursor blink or stop blinking.
683    ///
684    /// # Examples
685    ///
686    /// ```
687    /// let mut lcd: LcdDisplay<_,_> = ...;
688    ///
689    /// lcd.set_blink(Blink::On);
690    /// ```
691    pub fn set_blink(&mut self, blink: Blink) {
692        match blink {
693            Blink::On => self.display_ctrl |= Blink::On as u8,
694            Blink::Off => self.display_ctrl &= !(Blink::On as u8),
695        }
696        self.command(Command::SetDisplayCtrl as u8 | self.display_ctrl);
697        self.delay.delay_us(CMD_DELAY);
698    }
699
700    /// Enable or disable LCD backlight
701    pub fn set_backlight(&mut self, backlight: Backlight) {
702        match backlight {
703            Backlight::On => self.backlight_on(),
704            Backlight::Off => self.backlight_off(),
705        }
706    }
707
708    /// Turn auto scroll on or off.
709    ///
710    /// # Examples
711    ///
712    /// ```
713    /// let mut lcd: LcdDisplay<_,_> = ...;
714    ///
715    /// lcd.set_autoscroll(AutoScroll::On);
716    /// ```
717    pub fn set_autoscroll(&mut self, scroll: AutoScroll) {
718        match scroll {
719            AutoScroll::On => self.display_mode |= AutoScroll::On as u8,
720            AutoScroll::Off => self.display_mode &= !(AutoScroll::On as u8),
721        }
722        self.command(Command::SetDisplayMode as u8 | self.display_mode);
723        self.delay.delay_us(CMD_DELAY);
724    }
725
726    /// Add a new character map to the LCD memory (CGRAM) at a particular location.
727    /// There are eight locations available at positions 0-7, and location values
728    /// outside of this range will be bitwise masked to fall within the range, possibly
729    /// overwriting an existing custom character.
730    ///
731    /// # Examples
732    ///
733    /// ```
734    /// let mut lcd: LcdDisplay<_,_> = ...;
735    ///
736    /// // set a sideways smiley face in CGRAM at location 0.
737    /// lcd.set_character(0u8,[
738    ///     0b00110,
739    ///     0b00001,
740    ///     0b11001,
741    ///     0b00001,
742    ///     0b00001,
743    ///     0b11001,
744    ///     0b00001,
745    ///     0b00110
746    /// ]);
747    ///
748    /// // write the character code for the custom character.
749    /// lcd.home();
750    /// lcd.write(0u8);
751    /// ```
752    pub fn set_character(&mut self, mut location: u8, map: [u8; 8]) {
753        location &= 0x7; // limit to locations 0-7
754        self.command(Command::SetCGramAddr as u8 | (location << 3));
755        for ch in map.iter() {
756            self.write(*ch);
757        }
758    }
759
760    /// Clear the display.
761    ///
762    /// # Examples
763    ///
764    /// ```
765    /// let mut lcd: LcdDisplay<_,_> = ...;
766    /// lcd.clear();
767    /// ```
768    pub fn clear(&mut self) {
769        self.command(Command::ClearDisplay as u8);
770        self.delay.delay_us(CMD_DELAY);
771    }
772
773    /// Move the cursor to the home position.
774    ///
775    /// # Examples
776    ///
777    /// ```
778    /// let mut lcd: LcdDisplay<_,_> = ...;
779    /// lcd.home(); // cursor should be top-left
780    /// ```
781    pub fn home(&mut self) {
782        self.command(Command::ReturnHome as u8);
783        self.delay.delay_us(CMD_DELAY);
784    }
785
786    /// Scroll the display to the right. (See [set_scroll][LcdDisplay::set_scroll])
787    ///
788    /// # Examples
789    ///
790    /// ```
791    /// let mut lcd: LcdDisplay<_,_> = ...;
792    /// lcd.scroll_right(2); // display scrolls 2 positions to the right.
793    /// ```
794    pub fn scroll_right(&mut self, value: u8) {
795        self.set_scroll(Scroll::Right, value);
796    }
797
798    /// Scroll the display to the left. (See [set_scroll][LcdDisplay::set_scroll])
799    ///
800    /// # Examples
801    ///
802    /// ```
803    /// let mut lcd: LcdDisplay<_,_> = ...;
804    /// lcd.scroll_left(2); // display scrolls 2 positions to the left.
805    /// ```
806    pub fn scroll_left(&mut self, value: u8) {
807        self.set_scroll(Scroll::Left, value);
808    }
809
810    /// Set the text direction layout left-to-right. (See [set_layout][LcdDisplay::set_layout])
811    ///
812    /// # Examples
813    ///
814    /// ```
815    /// let mut lcd: LcdDisplay<_,_> = ...;
816    /// lcd.layout_left_to_right();
817    /// ```
818    pub fn layout_left_to_right(&mut self) {
819        self.set_layout(Layout::LeftToRight);
820    }
821
822    /// Set the text direction layout right-to-left. (See [set_layout][LcdDisplay::set_layout])
823    ///
824    /// # Examples
825    ///
826    /// ```
827    /// let mut lcd: LcdDisplay<_,_> = ...;
828    /// lcd.layout_right_to_left();
829    /// ```
830    pub fn layout_right_to_left(&mut self) {
831        self.set_layout(Layout::RightToLeft);
832    }
833
834    /// Turn the display on. (See [set_display][LcdDisplay::set_display])
835    ///
836    /// # Examples
837    ///
838    /// ```
839    /// let mut lcd: LcdDisplay<_,_> = ...;
840    /// lcd.display_on();
841    /// ```
842    pub fn display_on(&mut self) {
843        self.set_display(Display::On);
844    }
845
846    /// Turn the display off. (See [set_display][LcdDisplay::set_display])
847    ///
848    /// # Examples
849    ///
850    /// ```
851    /// let mut lcd: LcdDisplay<_,_> = ...;
852    /// lcd.display_off();
853    /// ```
854    pub fn display_off(&mut self) {
855        self.set_display(Display::Off);
856    }
857
858    /// Turn the cursor on. (See [set_cursor][LcdDisplay::set_cursor])
859    ///
860    /// # Examples
861    ///
862    /// ```
863    /// let mut lcd: LcdDisplay<_,_> = ...;
864    /// lcd.cursor_on();
865    /// ```
866    pub fn cursor_on(&mut self) {
867        self.set_cursor(Cursor::On);
868    }
869
870    /// Turn the cursor off. (See [set_cursor][LcdDisplay::set_cursor])
871    ///
872    /// # Examples
873    ///
874    /// ```
875    /// let mut lcd: LcdDisplay<_,_> = ...;
876    /// lcd.cursor_off();
877    /// ```
878    pub fn cursor_off(&mut self) {
879        self.set_cursor(Cursor::Off);
880    }
881
882    /// Set the background of the cursor to blink. (See [set_blink][LcdDisplay::set_blink])
883    ///
884    /// # Examples
885    ///
886    /// ```
887    /// let mut lcd: LcdDisplay<_,_> = ...;
888    /// lcd.blink_on();
889    /// ```
890    pub fn blink_on(&mut self) {
891        self.set_blink(Blink::On);
892    }
893
894    /// Set the background of the cursor to stop blinking. (See [set_blink][LcdDisplay::set_blink])
895    ///
896    /// # Examples
897    ///
898    /// ```
899    /// let mut lcd: LcdDisplay<_,_> = ...;
900    /// lcd.blink_off();
901    /// ```
902    pub fn blink_off(&mut self) {
903        self.set_blink(Blink::Off);
904    }
905
906    /// Turn backlight on
907    pub fn backlight_on(&mut self) {
908        if let Some(backlight_pin) = &mut self.pins[A as usize] {
909            let _ = backlight_pin.set_high();
910        }
911    }
912
913    /// Turn backlight off
914    pub fn backlight_off(&mut self) {
915        if let Some(backlight_pin) = &mut self.pins[A as usize] {
916            let _ = backlight_pin.set_low();
917        }
918    }
919
920    /// Turn autoscroll on. (See [set_autoscroll][LcdDisplay::set_autoscroll])
921    ///
922    /// # Examples
923    ///
924    /// ```
925    /// let mut lcd: LcdDisplay<_,_> = ...;
926    /// lcd.autoscroll_on();
927    /// ```
928    pub fn autoscroll_on(&mut self) {
929        self.set_autoscroll(AutoScroll::On);
930    }
931
932    /// Turn autoscroll off. (See [set_autoscroll][LcdDisplay::set_autoscroll])
933    ///
934    /// # Examples
935    ///
936    /// ```
937    /// let mut lcd: LcdDisplay<_,_> = ...;
938    /// lcd.autoscroll_off();
939    /// ```
940    pub fn autoscroll_off(&mut self) {
941        self.set_autoscroll(AutoScroll::Off);
942    }
943
944    /// Get the current bus mode. (See [with_half_bus][LcdDisplay::with_half_bus] and [with_full_bus][LcdDisplay::with_full_bus])
945    ///
946    /// # Examples
947    ///
948    /// ```
949    /// let mut lcd: LcdDisplay<_,_> = ...;
950    /// let mode = lcd.mode();
951    /// ```
952    pub fn mode(&self) -> Mode {
953        if (self.display_func & Mode::EightBits as u8) == 0 {
954            Mode::FourBits
955        } else {
956            Mode::EightBits
957        }
958    }
959
960    /// Get the current text direction layout. (See [set_layout][LcdDisplay::set_layout])
961    ///
962    /// # Examples
963    ///
964    /// ```
965    /// let mut lcd: LcdDisplay<_,_> = ...;
966    /// let layout = lcd.layout();
967    /// ```
968    pub fn layout(&self) -> Layout {
969        if (self.display_mode & Layout::LeftToRight as u8) == 0 {
970            Layout::RightToLeft
971        } else {
972            Layout::LeftToRight
973        }
974    }
975
976    /// Get the current state of the display (on or off). (See [set_display][LcdDisplay::set_display])
977    ///
978    /// # Examples
979    ///
980    /// ```
981    /// let mut lcd: LcdDisplay<_,_> = ...;
982    /// let display = lcd.display();
983    /// ```
984    pub fn display(&self) -> Display {
985        if (self.display_ctrl & Display::On as u8) == 0 {
986            Display::Off
987        } else {
988            Display::On
989        }
990    }
991
992    /// Get the current cursor state (on or off). (See [set_cursor][LcdDisplay::set_cursor])
993    ///
994    /// # Examples
995    ///
996    /// ```
997    /// let mut lcd: LcdDisplay<_,_> = ...;
998    /// let cursor = lcd.cursor();
999    /// ```
1000    pub fn cursor(&self) -> Cursor {
1001        if (self.display_ctrl & Cursor::On as u8) == 0 {
1002            Cursor::Off
1003        } else {
1004            Cursor::On
1005        }
1006    }
1007
1008    /// Get the current blink state (on or off). (See [set_blink][LcdDisplay::set_blink])
1009    ///
1010    /// # Examples
1011    ///
1012    /// ```
1013    /// let mut lcd: LcdDisplay<_,_> = ...;
1014    /// let blink = lcd.blink();
1015    /// ```
1016    pub fn blink(&self) -> Blink {
1017        if (self.display_ctrl & Blink::On as u8) == 0 {
1018            Blink::Off
1019        } else {
1020            Blink::On
1021        }
1022    }
1023
1024    /// Get the current autoscroll state (on or off). (See [set_autoscroll][LcdDisplay::set_autoscroll])
1025    ///
1026    /// # Examples
1027    ///
1028    /// ```
1029    /// let mut lcd: LcdDisplay<_,_> = ...;
1030    /// let autoscroll = lcd.autoscroll();
1031    /// ```
1032    pub fn autoscroll(&self) -> AutoScroll {
1033        if (self.display_mode & AutoScroll::On as u8) == 0 {
1034            AutoScroll::Off
1035        } else {
1036            AutoScroll::On
1037        }
1038    }
1039
1040    /// Get the number of lines. (See [with_lines][LcdDisplay::with_lines])
1041    ///
1042    /// # Examples
1043    ///
1044    /// ```
1045    /// let mut lcd: LcdDisplay<_,_> = ...;
1046    /// let lines = lcd.lines();
1047    /// ```
1048    pub fn lines(&self) -> Lines {
1049        let flag_bits: u8 = self.display_func & 0x0C;
1050        if flag_bits == Lines::FourLines as u8 {
1051            Lines::FourLines
1052        } else if flag_bits == Lines::TwoLines as u8 {
1053            Lines::TwoLines
1054        } else {
1055            Lines::OneLine
1056        }
1057    }
1058
1059    /// Get the current error code. If an error occurs, the internal code will be
1060    /// set to a value other than [Error::None][Error::None] (11u8).
1061    ///
1062    /// # Examples
1063    ///
1064    /// ```
1065    /// let mut lcd: LcdDisplay<_,_> = ...;
1066    /// let code = lcd.error();
1067    /// ```
1068    pub fn error(&self) -> Error {
1069        self.code.clone()
1070    }
1071
1072    /// Print a message to the LCD display.
1073    ///
1074    /// # Examples
1075    ///
1076    /// ```
1077    /// let mut lcd: LcdDisplay<_,_> = ...;
1078    /// lcd.print("TEST MESSAGE");
1079    /// ```
1080    pub fn print(&mut self, text: &str) {
1081        for ch in text.chars() {
1082            self.write(ch as u8);
1083        }
1084    }
1085
1086    /// Write a single character to the LCD display.
1087    ///
1088    /// # Examples
1089    ///
1090    /// ```
1091    /// let mut lcd: LcdDisplay<_,_> = ...;
1092    /// lcd.write('A' as u8);
1093    /// ```
1094    pub fn write(&mut self, value: u8) {
1095        self.delay.delay_us(CHR_DELAY);
1096        self.send(value, true);
1097    }
1098
1099    /// Execute a command on the LCD display, usually by using bitwise OR to combine
1100    /// flags in various ways.
1101    ///
1102    /// # Examples
1103    ///
1104    /// ```
1105    /// self.command(Command::SetDisplayCtrl as u8 | self.display_ctrl);
1106    /// ```
1107    fn command(&mut self, value: u8) {
1108        self.send(value, false);
1109    }
1110
1111    /// Send bytes to the LCD display with the RS pin set either high (for commands)
1112    /// or low (to write to memory)
1113    ///
1114    /// # Examples
1115    ///
1116    /// ```
1117    /// self.send(value, true);
1118    /// ```
1119    fn send(&mut self, byte: u8, mode: bool) {
1120        self.set(RS, mode);
1121
1122        if self.exists(RW) {
1123            self.set(RW, false);
1124        }
1125
1126        match self.mode() {
1127            Mode::FourBits => {
1128                self.update(byte >> 4);
1129                self.update(byte);
1130            }
1131            Mode::EightBits => {
1132                self.update(byte);
1133            }
1134        }
1135    }
1136
1137    /// Update the on-device memory by sending either the bottom nibble (in
1138    /// four-bit mode) or a whole byte (in eight-bit) and then pulsing the enable pin.
1139    ///
1140    /// # Examples
1141    ///
1142    /// ```
1143    /// self.update(byte);
1144    /// ```
1145    fn update(&mut self, byte: u8) {
1146        self.set(EN, false);
1147        match self.mode() {
1148            Mode::FourBits => {
1149                self.set(D7, (byte >> 3) & 1 > 0);
1150                self.set(D6, (byte >> 2) & 1 > 0);
1151                self.set(D5, (byte >> 1) & 1 > 0);
1152                self.set(D4, (byte >> 0) & 1 > 0);
1153            }
1154            Mode::EightBits => {
1155                self.set(D7, (byte >> 7) & 1 > 0);
1156                self.set(D6, (byte >> 6) & 1 > 0);
1157                self.set(D5, (byte >> 5) & 1 > 0);
1158                self.set(D4, (byte >> 4) & 1 > 0);
1159                self.set(D3, (byte >> 3) & 1 > 0);
1160                self.set(D2, (byte >> 2) & 1 > 0);
1161                self.set(D1, (byte >> 1) & 1 > 0);
1162                self.set(D0, (byte >> 0) & 1 > 0);
1163            }
1164        };
1165        self.pulse();
1166    }
1167
1168    /// Set the enable pin high and then low to make the LCD accept the most
1169    /// recently transmitted data.
1170    ///
1171    /// # Examples
1172    ///
1173    /// ```
1174    /// self.pulse();
1175    /// ```
1176    fn pulse(&mut self) {
1177        self.set(EN, true);
1178        self.set(EN, false);
1179    }
1180
1181    /// Set a pin at position `index` to a particular value
1182    ///
1183    /// # Examples
1184    ///
1185    /// ```
1186    /// self.set(RS, true);
1187    /// ```
1188    fn set(&mut self, index: u8, value: bool) {
1189        if self.pins[index as usize]
1190            .as_mut()
1191            .and_then(|p| match value {
1192                true => p.set_high().ok(),
1193                false => p.set_low().ok(),
1194            })
1195            .is_none()
1196        {
1197            self.code = index.into();
1198        }
1199    }
1200
1201    /// Check that a pin exists
1202    ///
1203    /// # Examples
1204    ///
1205    /// ```
1206    /// if self.exists(RS) {
1207    ///     ...
1208    /// }
1209    /// ```
1210    fn exists(&self, index: u8) -> bool {
1211        self.pins[index as usize].is_some()
1212    }
1213
1214    /// Set an error code if display is misconfigured. Currently
1215    /// only validates the number of pins for the given bus width.
1216    fn validate(&mut self) {
1217        if match self.mode() {
1218            Mode::FourBits => {
1219                self.exists(D4) || self.exists(D5) || self.exists(D6) || self.exists(D7)
1220            }
1221            Mode::EightBits => {
1222                self.exists(D0)
1223                    || self.exists(D1)
1224                    || self.exists(D2)
1225                    || self.exists(D3)
1226                    || self.exists(D4)
1227                    || self.exists(D5)
1228                    || self.exists(D6)
1229                    || self.exists(D7)
1230            }
1231        } {
1232            self.code = Error::InvalidMode;
1233        }
1234    }
1235}
1236
1237/// Implementation of ufmt::uWrite
1238///
1239/// This trait allows us to use the uwrite/uwriteln macros from ufmt
1240/// to format arbitrary arguments (that have the appropriate uDisplay or uDebug traits
1241/// implemented) into a string to display on the lcd screen.
1242///
1243/// # Examples
1244///
1245/// ```
1246/// let mut lcd: LcdDisplay<_,_> = ...;
1247///
1248/// let count = 3;
1249/// uwriteln!(&mut lcd, "COUNT IS: {}",count);
1250/// ```
1251///
1252#[cfg(feature = "ufmt")]
1253impl<T, D> ufmt::uWrite for LcdDisplay<T, D>
1254where
1255    T: OutputPin<Error = core::convert::Infallible> + Sized,
1256    D: DelayNs + Sized,
1257{
1258    type Error = core::convert::Infallible;
1259
1260    fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
1261        self.print(s);
1262        Ok(())
1263    }
1264
1265    fn write_char(&mut self, c: char) -> Result<(), Self::Error> {
1266        self.write(c as u8);
1267        Ok(())
1268    }
1269}