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}