lcd1602_driver/lcd/
impls.rs

1use embedded_hal::delay::DelayNs;
2
3use crate::command::{Font, LineMode, MoveDirection, RAMType, ShiftType};
4use crate::sender::SendCommand;
5use crate::{command::CommandSet, lcd::State};
6
7use super::{Anim, Basic, BasicRead, CGRAMGraph, Ext, ExtRead, Lcd};
8
9impl<'a, 'b, Sender, Delayer, const READABLE: bool> Basic for Lcd<'a, 'b, Sender, Delayer, READABLE>
10where
11    Sender: SendCommand<Delayer, READABLE>,
12    Delayer: DelayNs,
13{
14    fn set_backlight(&mut self, backlight: State) {
15        self.sender.set_actual_backlight(backlight);
16        self.state.set_backlight(backlight);
17    }
18
19    fn get_backlight(&self) -> State {
20        self.state.get_backlight()
21    }
22
23    fn write_byte_to_cur(&mut self, byte: u8) {
24        assert!(
25            self.get_ram_type() == RAMType::DDRam,
26            "Current in CGRAM, use .set_cursor_pos() to change to DDRAM"
27        );
28
29        self.sender.wait_and_send(
30            CommandSet::WriteDataToRAM(byte),
31            self.delayer,
32            self.poll_interval_us,
33        );
34
35        // since AC of UT7066U will automatically increase, we only need to update LCD struct
36        // since RAM of UT7066U is looped, we need to mimic it
37        let last_pos = self.get_cursor_pos();
38        let line_capacity = self.get_line_capacity();
39
40        let raw_pos = match self.get_direction() {
41            MoveDirection::RightToLeft => match self.get_line_mode() {
42                LineMode::OneLine => {
43                    if last_pos.0 == 0 {
44                        (line_capacity - 1, 0)
45                    } else {
46                        (last_pos.0 - 1, 0)
47                    }
48                }
49                LineMode::TwoLine => {
50                    if last_pos.0 == 0 {
51                        if last_pos.1 == 1 {
52                            (line_capacity - 1, 0)
53                        } else {
54                            (line_capacity - 1, 1)
55                        }
56                    } else {
57                        (last_pos.0 - 1, last_pos.1)
58                    }
59                }
60            },
61            MoveDirection::LeftToRight => match self.get_line_mode() {
62                LineMode::OneLine => {
63                    if last_pos.0 == line_capacity - 1 {
64                        (0, 0)
65                    } else {
66                        (last_pos.0 + 1, 0)
67                    }
68                }
69                LineMode::TwoLine => {
70                    if last_pos.0 == line_capacity - 1 {
71                        if last_pos.1 == 0 {
72                            (0, 1)
73                        } else {
74                            (0, 0)
75                        }
76                    } else {
77                        (last_pos.0 + 1, last_pos.1)
78                    }
79                }
80            },
81        };
82        self.state.set_cursor_pos(raw_pos);
83    }
84
85    fn write_graph_to_cgram(&mut self, index: u8, graph_data: &CGRAMGraph) {
86        if graph_data.lower.is_some() {
87            assert!(index < 4, "Only 4 graphs allowed in CGRAM for 5x11 Font")
88        } else {
89            assert!(index < 8, "Only 8 graphs allowed in CGRAM for 5x8 Font")
90        }
91
92        assert!(
93            graph_data.upper.iter().all(|&line| line < 2u8.pow(5)),
94            "Only lower 5 bits use to construct the graph"
95        );
96
97        if let Some(graph_data_lower) = graph_data.lower {
98            assert!(
99                graph_data_lower.iter().all(|&line| line < 2u8.pow(5)),
100                "Only lower 5 bits use to construct the graph"
101            );
102        }
103
104        // if DDRAM is write from right to left, then when we change to CGRAM, graph will write from lower to upper
105        // we will change it to left to right, to make writing correct
106        let direction_flipped = if self.get_direction() == MoveDirection::RightToLeft {
107            self.set_direction(MoveDirection::LeftToRight);
108            true
109        } else {
110            false
111        };
112
113        // index is convert to high 3 bits of CGRAM address
114        self.set_cgram_addr(
115            index
116                .checked_shl(match graph_data.lower {
117                    None => 3,
118                    Some(_) => 4,
119                })
120                .unwrap(),
121        );
122
123        graph_data.upper.iter().for_each(|&line_data| {
124            self.sender.wait_and_send(
125                CommandSet::WriteDataToRAM(line_data),
126                self.delayer,
127                self.poll_interval_us,
128            );
129        });
130
131        if let Some(graph_data_lower) = graph_data.lower {
132            graph_data_lower.iter().for_each(|&line_data| {
133                self.sender.wait_and_send(
134                    CommandSet::WriteDataToRAM(line_data),
135                    self.delayer,
136                    self.poll_interval_us,
137                );
138            });
139        }
140
141        // if writing direction is changed, then restore it
142        if direction_flipped {
143            self.set_direction(MoveDirection::RightToLeft)
144        }
145    }
146
147    fn clean_display(&mut self) {
148        self.sender.wait_and_send(
149            CommandSet::ClearDisplay,
150            self.delayer,
151            self.poll_interval_us,
152        );
153    }
154
155    fn return_home(&mut self) {
156        self.state.set_cursor_pos((0, 0));
157        self.state.set_display_offset(0);
158
159        self.sender
160            .wait_and_send(CommandSet::ReturnHome, self.delayer, self.poll_interval_us);
161    }
162
163    fn set_line_mode(&mut self, line: LineMode) {
164        self.state.set_line_mode(line);
165
166        self.sender.wait_and_send(
167            CommandSet::FunctionSet(
168                self.state.get_data_width(),
169                self.get_line_mode(),
170                self.get_font(),
171            ),
172            self.delayer,
173            self.poll_interval_us,
174        );
175    }
176
177    fn get_line_mode(&self) -> LineMode {
178        self.state.get_line_mode()
179    }
180
181    fn set_font(&mut self, font: Font) {
182        self.state.set_font(font);
183
184        self.sender.wait_and_send(
185            CommandSet::FunctionSet(
186                self.state.get_data_width(),
187                self.get_line_mode(),
188                self.get_font(),
189            ),
190            self.delayer,
191            self.poll_interval_us,
192        );
193    }
194    fn get_font(&self) -> Font {
195        self.state.get_font()
196    }
197    fn set_display_state(&mut self, display: State) {
198        self.state.set_display_state(display);
199
200        self.sender.wait_and_send(
201            CommandSet::DisplayOnOff {
202                display: self.get_display_state(),
203                cursor: self.get_cursor_state(),
204                cursor_blink: self.get_cursor_blink_state(),
205            },
206            self.delayer,
207            self.poll_interval_us,
208        );
209    }
210    fn get_display_state(&self) -> State {
211        self.state.get_display_state()
212    }
213    fn set_cursor_state(&mut self, cursor: State) {
214        self.state.set_cursor_state(cursor);
215
216        self.sender.wait_and_send(
217            CommandSet::DisplayOnOff {
218                display: self.get_display_state(),
219                cursor: self.get_cursor_state(),
220                cursor_blink: self.get_cursor_blink_state(),
221            },
222            self.delayer,
223            self.poll_interval_us,
224        );
225    }
226    fn get_cursor_state(&self) -> State {
227        self.state.get_cursor_state()
228    }
229    fn get_ram_type(&self) -> RAMType {
230        self.state.get_ram_type()
231    }
232    fn set_cursor_blink_state(&mut self, blink: State) {
233        self.state.set_cursor_blink(blink);
234
235        self.sender.wait_and_send(
236            CommandSet::DisplayOnOff {
237                display: self.get_display_state(),
238                cursor: self.get_cursor_state(),
239                cursor_blink: self.get_cursor_blink_state(),
240            },
241            self.delayer,
242            self.poll_interval_us,
243        );
244    }
245    fn get_cursor_blink_state(&self) -> State {
246        self.state.get_cursor_blink()
247    }
248    fn set_direction(&mut self, dir: MoveDirection) {
249        self.state.set_direction(dir);
250
251        self.sender.wait_and_send(
252            CommandSet::EntryModeSet(self.get_direction(), self.get_shift_type()),
253            self.delayer,
254            self.poll_interval_us,
255        );
256    }
257    fn get_direction(&self) -> MoveDirection {
258        self.state.get_direction()
259    }
260    fn set_shift_type(&mut self, shift: ShiftType) {
261        self.state.set_shift_type(shift);
262
263        self.sender.wait_and_send(
264            CommandSet::EntryModeSet(self.get_direction(), self.get_shift_type()),
265            self.delayer,
266            self.poll_interval_us,
267        );
268    }
269    fn get_shift_type(&self) -> ShiftType {
270        self.state.get_shift_type()
271    }
272    fn set_cursor_pos(&mut self, pos: (u8, u8)) {
273        self.state.set_ram_type(RAMType::DDRam);
274        self.state.set_cursor_pos(pos);
275
276        // in one line mode, pos.1 will always keep at 0
277        // in two line mode, the second line start at 0x40
278        let raw_pos: u8 = pos.1 * 0x40 + pos.0;
279
280        self.sender.wait_and_send(
281            CommandSet::SetDDRAM(raw_pos),
282            self.delayer,
283            self.poll_interval_us,
284        );
285    }
286    fn set_cgram_addr(&mut self, addr: u8) {
287        assert!(addr < 2u8.pow(6), "CGRAM Address overflow");
288
289        self.state.set_ram_type(RAMType::CGRam);
290
291        self.sender.wait_and_send(
292            CommandSet::SetCGRAM(addr),
293            self.delayer,
294            self.poll_interval_us,
295        );
296    }
297    fn get_cursor_pos(&self) -> (u8, u8) {
298        self.state.get_cursor_pos()
299    }
300    fn shift_cursor_or_display(&mut self, shift_type: ShiftType, dir: MoveDirection) {
301        self.state.shift_cursor_or_display(shift_type, dir);
302
303        self.sender.wait_and_send(
304            CommandSet::CursorOrDisplayShift(shift_type, dir),
305            self.delayer,
306            self.poll_interval_us,
307        );
308    }
309    fn get_display_offset(&self) -> u8 {
310        self.state.get_display_offset()
311    }
312
313    fn set_poll_interval(&mut self, interval_us: u32) {
314        self.poll_interval_us = interval_us;
315    }
316
317    fn get_poll_interval_us(&self) -> u32 {
318        self.poll_interval_us
319    }
320
321    fn get_line_capacity(&self) -> u8 {
322        self.state.get_line_capacity()
323    }
324
325    fn calculate_pos_by_offset(&self, start: (u8, u8), offset: (i8, i8)) -> (u8, u8) {
326        self.state.calculate_pos_by_offset(start, offset)
327    }
328
329    fn delay_ms(&mut self, ms: u32) {
330        self.delayer.delay_ms(ms);
331    }
332
333    fn delay_us(&mut self, us: u32) {
334        self.delayer.delay_us(us)
335    }
336}
337
338impl<'a, 'b, Sender, Delayer> BasicRead for Lcd<'a, 'b, Sender, Delayer, true>
339where
340    Sender: SendCommand<Delayer, true>,
341    Delayer: DelayNs,
342{
343    fn read_u8_from_cur(&mut self) -> u8 {
344        self.sender
345            .wait_and_send(
346                CommandSet::ReadDataFromRAM,
347                self.delayer,
348                self.poll_interval_us,
349            )
350            .unwrap()
351    }
352}
353
354impl<'a, 'b, Sender, Delayer, const READABLE: bool> Ext for Lcd<'a, 'b, Sender, Delayer, READABLE>
355where
356    Delayer: DelayNs,
357    Sender: SendCommand<Delayer, READABLE>,
358{
359}
360
361impl<'a, 'b, Sender, Delayer> ExtRead for Lcd<'a, 'b, Sender, Delayer, true>
362where
363    Delayer: DelayNs,
364    Sender: SendCommand<Delayer, true>,
365{
366}
367
368impl<'a, 'b, Sender, Delayer, const READABLE: bool> Anim for Lcd<'a, 'b, Sender, Delayer, READABLE>
369where
370    Delayer: DelayNs,
371    Sender: SendCommand<Delayer, READABLE>,
372{
373}