liquid_crystal/lcd_trait/
mod.rs

1use core::marker::PhantomData;
2
3pub use embedded_hal::delay::DelayNs;
4#[cfg(feature="async")]
5pub use embedded_hal_async::delay::DelayNs as ADelay;
6
7pub mod commands;
8pub mod interfaces;
9pub mod layout;
10
11pub use interfaces::*;
12pub use layout::*;
13
14pub use commands::Commands::*;
15pub use commands::*;
16
17///enum of possible values ​​that can be written with the "write" function
18pub enum SendType<'s> {
19    Command(Commands),
20    Text(&'s str),
21    CustomChar(u8),
22}
23
24pub enum BusBits {
25    Bus4Bits,
26    Bus8Bits,
27}
28
29enum LCDEntryMode {
30    LCDShiftMode = 0x01,
31    LCDDirection = 0x02,
32}
33
34enum LCDDisplayControl {
35    LCDBlink = 0x01,
36    LCDCursor = 0x02,
37    LCDDisplay = 0x04,
38}
39
40pub struct Blocking;
41#[cfg(feature="async")]
42pub struct Async;
43
44pub struct LiquidCrystal<'interface, T: Interface, const COLS: u8, const LINES: usize, MODE = Blocking> {
45    interface: &'interface mut T,
46    corrent_enable: u8,
47    bus: BusBits,
48    layout: Layout<COLS, LINES>,
49    entry_mode: u8,
50    display_control: u8,
51    _mode: PhantomData<MODE>,
52}
53
54impl<'interface, T: Interface, const COLS: u8, const LINES: usize>
55    LiquidCrystal<'interface, T, COLS, LINES>
56{
57    pub fn new(
58        interface: &'interface mut T,
59        bus: BusBits,
60        layout: Layout<COLS, LINES>,
61    ) -> LiquidCrystal<'interface, T, COLS, LINES> {
62        LiquidCrystal {
63            interface,
64            bus,
65            layout,
66            corrent_enable: 0b11,
67            entry_mode: 0x06,      //shift Off, written from left to right
68            display_control: 0x0C, //display on, cursor off, cursor blinking off
69            _mode: PhantomData,
70        }
71    }
72
73    #[cfg(feature="async")]
74    pub fn asynch(self) -> LiquidCrystal<'interface, T, COLS, LINES, Async> {
75        LiquidCrystal {
76            interface: self.interface,
77            bus: self.bus,
78            layout: self.layout,
79            corrent_enable: self.corrent_enable,
80            entry_mode: self.entry_mode,
81            display_control: self.display_control,
82            _mode: PhantomData,
83        }
84    }
85
86    fn send8bits(&mut self, delay: &mut impl DelayNs, data: u8, rs_state: u8) {
87        self.interface.send(rs_state, data);
88        self.interface
89            .send(rs_state | (self.corrent_enable << 2), data);
90        delay.delay_us(1);
91        self.interface.send(rs_state, data);
92    }
93
94    fn send4bits(&mut self, delay: &mut impl DelayNs, data: u8, rs_state: u8) {
95        let high_nibble = data & 0xF0;
96        let low_nibble = data << 4;
97        self.send8bits(delay, high_nibble, rs_state);
98        delay.delay_us(1);
99        self.send8bits(delay, low_nibble, rs_state);
100    }
101
102    /// ### low level function to send data.
103    /// processes the data before sending it to send4bits.
104    /// `rs_state` represents the state of the RS pin of the display
105    /// (0x01 write)
106    /// (0x00 command)
107    pub fn send(&mut self, delay: &mut impl DelayNs, data: u8, rs_state: u8) {
108        match self.bus {
109            BusBits::Bus8Bits => self.send8bits(delay, data, rs_state),
110            BusBits::Bus4Bits => self.send4bits(delay, data, rs_state),
111        };
112
113        if rs_state == 1 {
114            delay.delay_us(2);
115        } else {
116            delay.delay_us(40);
117        }
118    }
119
120    pub fn begin(&mut self, delay: &mut impl DelayNs) {
121        delay.delay_ms(50);
122        self.send8bits(delay, 0x30, 0);
123        delay.delay_us(4100);
124        self.send8bits(delay, 0x30, 0);
125        delay.delay_us(100);
126        self.send8bits(delay, 0x30, 0);
127        delay.delay_us(100);
128        match self.bus {
129            BusBits::Bus8Bits => self.send8bits(delay, 0x38, 0),
130            BusBits::Bus4Bits => {
131                self.send8bits(delay, 0x20, 0);
132                self.send(delay, 0x28, 0);
133            }
134        };
135        self.write(delay, SendType::Command(Clear));
136        self.write(delay, SendType::Command(Reset));
137        self.update_config(delay);
138    }
139
140    /// ### write on the display
141    /// # Exemple
142    /// to send Text
143    /// ```ignore
144    /// write(&mut delay,Text("Text"))
145    /// ```
146    /// to send Command
147    ///
148    /// ```ignore
149    ///  write(&mut delay,Command(Command))
150    /// ```
151    ///
152    /// to send custom char
153    ///
154    /// ```ignore
155    ///  write(&mut delay, CustomChar(slot))
156    /// ```
157    ///
158    pub fn write<'s>(&mut self, delay: &mut impl DelayNs, data: SendType<'s>) -> &mut Self {
159        match data {
160            SendType::Command(x) => {
161                self.send(delay, x as u8, 0x00);
162                delay.delay_us(2000);
163            }
164            SendType::Text(x) => {
165                for text in x.chars() {
166                    self.send(delay, text as u8, RS);
167                }
168            }
169            SendType::CustomChar(slot) => {
170                if slot < 8 {
171                    self.send(delay, slot, RS);
172                }
173            }
174        };
175        self
176    }
177
178    /// ### moves the cursor to the indicated location.
179    /// receives the line and column position and moves the cursor
180    pub fn set_cursor(&mut self, delay: &mut impl DelayNs, line: usize, colum: u8) -> &mut Self {
181        if (line < LINES) || (colum < COLS) {
182            self.send(delay, self.layout.addrs[line] + colum, 0);
183        }
184        self
185    }
186
187    /// ### create custom characters
188    /// attention: this function resets the internal variables of the display.
189    pub fn custom_char(
190        &mut self,
191        delay: &mut impl DelayNs,
192        char_array: &[u8; 8],
193        slot: u8,
194    ) -> &mut Self {
195        if slot < 8 {
196            self.send(delay, 0x40 | (slot << 3), 0x00);
197            for c in 0..8 {
198                self.send(delay, char_array[c], RS);
199            }
200        }
201        self.write(delay, SendType::Command(Reset));
202        self
203    }
204
205    /// ### send the configs to the display
206    pub fn update_config(&mut self, delay: &mut impl DelayNs) -> &mut Self {
207        self.send(delay, self.display_control, 0);
208        self.send(delay, self.entry_mode, 0);
209        self
210    }
211}
212
213#[cfg(feature="async")]
214impl<'interface, T: Interface, const COLS: u8, const LINES: usize>
215    LiquidCrystal<'interface, T, COLS, LINES, Async>
216{
217    pub fn blocking(self) -> LiquidCrystal<'interface, T, COLS, LINES, Blocking> {
218        LiquidCrystal {
219            interface: self.interface,
220            bus: self.bus,
221            layout: self.layout,
222            corrent_enable: self.corrent_enable,
223            entry_mode: self.entry_mode,
224            display_control: self.display_control,
225            _mode: PhantomData,
226        }
227    }
228
229    async fn send8bits(&mut self, delay: &mut impl ADelay, data: u8, rs_state: u8) {
230        self.interface.send(rs_state, data);
231        self.interface
232            .send(rs_state | (self.corrent_enable << 2), data);
233        delay.delay_us(1).await;
234        self.interface.send(rs_state, data);
235    }
236
237    async fn send4bits(&mut self, delay: &mut impl ADelay, data: u8, rs_state: u8) {
238        let high_nibble = data & 0xF0;
239        let low_nibble = data << 4;
240        self.send8bits(delay, high_nibble, rs_state).await;
241        delay.delay_us(1).await;
242        self.send8bits(delay, low_nibble, rs_state).await;
243    }
244
245    /// ### low level function to send data.
246    /// processes the data before sending it to send4bits.
247    /// `rs_state` represents the state of the RS pin of the display
248    /// (0x01 write)
249    /// (0x00 command)
250    pub async fn send(&mut self, delay: &mut impl ADelay, data: u8, rs_state: u8) {
251        match self.bus {
252            BusBits::Bus8Bits => self.send8bits(delay, data, rs_state).await,
253            BusBits::Bus4Bits => self.send4bits(delay, data, rs_state).await,
254        };
255
256        if rs_state == 1 {
257            delay.delay_us(2).await;
258        } else {
259            delay.delay_us(40).await;
260        }
261    }
262
263    pub async fn begin(&mut self, delay: &mut impl ADelay) {
264        delay.delay_ms(50).await;
265        self.send8bits(delay, 0x30, 0).await;
266        delay.delay_us(4100).await;
267        self.send8bits(delay, 0x30, 0).await;
268        delay.delay_us(100).await;
269        self.send8bits(delay, 0x30, 0).await;
270        delay.delay_us(100).await;
271        match self.bus {
272            BusBits::Bus8Bits => self.send8bits(delay, 0x38, 0).await,
273            BusBits::Bus4Bits => {
274                self.send8bits(delay, 0x20, 0).await;
275                self.send(delay, 0x28, 0).await;
276            }
277        };
278        self.write(delay, SendType::Command(Clear)).await;
279        self.write(delay, SendType::Command(Reset)).await;
280        self.update_config(delay).await;
281    }
282
283    /// ### write on the display
284    /// # Exemple
285    /// to send Text
286    /// ```ignore
287    /// write(&mut delay,Text("Text"))
288    /// ```
289    /// to send Command
290    ///
291    /// ```ignore
292    ///  write(&mut delay,Command(Command))
293    /// ```
294    ///
295    /// to send custom char
296    ///
297    /// ```ignore
298    ///  write(&mut delay, CustomChar(slot))
299    /// ```
300    ///
301    pub async fn write<'s>(&mut self, delay: &mut impl ADelay, data: SendType<'s>) -> &mut Self {
302        match data {
303            SendType::Command(x) => {
304                self.send(delay, x as u8, 0x00).await;
305                delay.delay_us(2000).await;
306            }
307            SendType::Text(x) => {
308                for text in x.chars() {
309                    self.send(delay, text as u8, RS).await;
310                }
311            }
312            SendType::CustomChar(slot) => {
313                if slot < 8 {
314                    self.send(delay, slot, RS).await;
315                }
316            }
317        };
318        self
319    }
320
321    /// ### moves the cursor to the indicated location.
322    /// receives the line and column position and moves the cursor
323    pub async fn set_cursor(&mut self, delay: &mut impl ADelay, line: usize, colum: u8) -> &mut Self {
324        if (line < LINES) || (colum < COLS) {
325            self.send(delay, self.layout.addrs[line] + colum, 0).await;
326        }
327        self
328    }
329
330    /// ### create custom characters
331    /// attention: this function resets the internal variables of the display.
332    pub async fn custom_char(
333        &mut self,
334        delay: &mut impl ADelay,
335        char_array: &[u8; 8],
336        slot: u8,
337    ) -> &mut Self {
338        if slot < 8 {
339            self.send(delay, 0x40 | (slot << 3), 0x00).await;
340            for c in 0..8 {
341                self.send(delay, char_array[c], RS).await;
342            }
343        }
344        self.write(delay, SendType::Command(Reset)).await;
345        self
346    }
347
348    /// ### send the configs to the display
349    pub async fn update_config(&mut self, delay: &mut impl ADelay) -> &mut Self {
350        self.send(delay, self.display_control, 0).await;
351        self.send(delay, self.entry_mode, 0).await;
352        self
353    }
354}
355
356impl<'interface, T: Interface, const COLS: u8, const LINES: usize, MODE>
357    LiquidCrystal<'interface, T, COLS, LINES, MODE>
358{
359    /// ### enable all displays
360    pub fn echo(&mut self) -> &mut Self {
361        self.corrent_enable = 0b11;
362        self
363    }
364
365    /// ### select a display
366    ///0 = EN1 | 1 = EN2
367    pub fn select_lcd(&mut self, en: u8) -> &mut Self {
368        if en < 2 {
369            self.corrent_enable = 1 << en;
370        }
371        self
372    }
373
374    /// ### enable blinking cursor
375    /// use update_config after configuration to apply changes!
376    #[inline]
377    pub fn enable_blink(&mut self) -> &mut Self {
378        self.display_control |= LCDDisplayControl::LCDBlink as u8;
379        self
380    }
381
382    /// ### enable cursor
383    /// use update_config after configuration to apply changes!
384    #[inline]
385    pub fn enable_cursor(&mut self) -> &mut Self {
386        self.display_control |= LCDDisplayControl::LCDCursor as u8;
387        self
388    }
389
390    /// ### enable display
391    /// use update_config after configuration to apply changes!
392    #[inline]
393    pub fn enable_display(&mut self) -> &mut Self {
394        self.display_control |= LCDDisplayControl::LCDDisplay as u8;
395        self
396    }
397
398    /// ### enable autoscroll
399    /// use update_config after configuration to apply changes!
400    #[inline]
401    pub fn enable_autoscroll(&mut self) -> &mut Self {
402        self.entry_mode |= LCDEntryMode::LCDShiftMode as u8;
403        self
404    }
405
406    /// ### disable blinking cursor
407    /// use update_config after configuration to apply changes!
408    #[inline]
409    pub fn disable_blink(&mut self) -> &mut Self {
410        self.display_control &= !(LCDDisplayControl::LCDBlink as u8);
411        self
412    }
413
414    /// ### disable cursor
415    /// use update_config after configuration to apply changes!
416    #[inline]
417    pub fn disable_cursor(&mut self) -> &mut Self {
418        self.display_control &= !(LCDDisplayControl::LCDCursor as u8);
419        self
420    }
421
422    /// ### disable display
423    /// use update_config after configuration to apply changes!
424    #[inline]
425    pub fn disable_display(&mut self) -> &mut Self {
426        self.display_control &= !(LCDDisplayControl::LCDDisplay as u8);
427        self
428    }
429
430    /// ### disable autoscroll
431    /// use update_config after configuration to apply changes!
432    #[inline]
433    pub fn disable_autoscroll(&mut self) -> &mut Self {
434        self.entry_mode &= !(LCDEntryMode::LCDShiftMode as u8);
435        self
436    }
437
438    /// ### autoscroll increments position
439    /// use update_config after configuration to apply changes!
440    #[inline]
441    pub fn set_autoscroll_increment(&mut self) -> &mut Self {
442        self.entry_mode |= LCDEntryMode::LCDDirection as u8;
443        self
444    }
445
446    /// ### autoscroll decrements position
447    /// use update_config after configuration to apply changes!
448    #[inline]
449    pub fn set_autoscroll_decrement(&mut self) -> &mut Self {
450        self.entry_mode &= !(LCDEntryMode::LCDDirection as u8);
451        self
452    }
453}