lcd1602_driver/lcd/
init.rs

1use embedded_hal::delay::DelayNs;
2
3use crate::{
4    command::{CommandSet, DataWidth, Font, LineMode, MoveDirection, RAMType, ShiftType, State},
5    lcd::Lcd,
6    sender::SendCommand,
7    state::LcdState,
8};
9
10/// [`Config`] is the init config of a [`Lcd`]
11#[derive(Default)]
12pub struct Config {
13    state: LcdState,
14
15    // This two state is for automatic correction
16    font_set_by_user: Option<Font>,
17    line_mode_set_by_user: Option<LineMode>,
18}
19
20#[allow(missing_docs)]
21impl Config {
22    pub fn get_backlight(&self) -> State {
23        self.state.get_backlight()
24    }
25
26    pub fn set_backlight(mut self, backlight: State) -> Self {
27        self.state.set_backlight(backlight);
28        self
29    }
30
31    pub fn get_data_width(&self) -> DataWidth {
32        self.state.get_data_width()
33    }
34
35    pub fn set_data_width(mut self, data_width: DataWidth) -> Self {
36        self.state.set_data_width(data_width);
37        self
38    }
39
40    pub fn get_line_mode(&self) -> LineMode {
41        self.state.get_line_mode()
42    }
43
44    pub fn set_line_mode(mut self, line_mode: LineMode) -> Self {
45        self.line_mode_set_by_user = Some(line_mode);
46
47        // automatically Font & LineMode compatible check
48        self.state.set_font(match line_mode {
49            LineMode::OneLine => self.font_set_by_user.unwrap_or_default(),
50            LineMode::TwoLine => Font::Font5x8,
51        });
52
53        self.state.set_line_mode(line_mode);
54        self
55    }
56
57    pub fn get_line_capacity(&self) -> u8 {
58        self.state.get_line_capacity()
59    }
60
61    pub fn get_font(&self) -> Font {
62        self.state.get_font()
63    }
64
65    pub fn set_font(mut self, font: Font) -> Self {
66        self.font_set_by_user = Some(font);
67
68        // automatically Font & LineMode compatible check
69        self.state.set_line_mode(match font {
70            Font::Font5x8 => self.line_mode_set_by_user.unwrap_or_default(),
71            Font::Font5x11 => LineMode::OneLine,
72        });
73
74        self.state.set_font(font);
75        self
76    }
77
78    pub fn get_display_state(&self) -> State {
79        self.state.get_display_state()
80    }
81
82    pub fn set_display_state(mut self, display: State) -> Self {
83        self.state.set_display_state(display);
84        self
85    }
86
87    pub fn get_cursor_state(&self) -> State {
88        self.state.get_cursor_state()
89    }
90
91    pub fn set_cursor_state(mut self, cursor: State) -> Self {
92        self.state.set_cursor_state(cursor);
93        self
94    }
95
96    pub fn get_cursor_blink(&self) -> State {
97        self.state.get_cursor_blink()
98    }
99
100    pub fn set_cursor_blink(mut self, blink: State) -> Self {
101        self.state.set_cursor_blink(blink);
102        self
103    }
104
105    pub fn get_direction(&self) -> MoveDirection {
106        self.state.get_direction()
107    }
108
109    pub fn set_direction(mut self, dir: MoveDirection) -> Self {
110        self.state.set_direction(dir);
111        self
112    }
113
114    pub fn get_shift_type(&self) -> ShiftType {
115        self.state.get_shift_type()
116    }
117
118    pub fn set_shift_type(mut self, shift: ShiftType) -> Self {
119        self.state.set_shift_type(shift);
120        self
121    }
122
123    pub fn get_cursor_pos(&self) -> (u8, u8) {
124        self.state.get_cursor_pos()
125    }
126
127    pub fn set_cursor_pos(mut self, pos: (u8, u8)) -> Self {
128        self.state.set_cursor_pos(pos);
129        self
130    }
131
132    pub fn get_display_offset(&self) -> u8 {
133        self.state.get_display_offset()
134    }
135
136    pub fn set_display_offset(mut self, offset: u8) -> Self {
137        self.state.set_display_offset(offset);
138        self
139    }
140
141    pub fn get_ram_type(&self) -> RAMType {
142        self.state.get_ram_type()
143    }
144
145    pub fn set_ram_type(mut self, ram_type: RAMType) -> Self {
146        self.state.set_ram_type(ram_type);
147        self
148    }
149}
150
151impl<'a, 'b, Sender, Delayer, const READABLE: bool> Lcd<'a, 'b, Sender, Delayer, READABLE>
152where
153    Sender: SendCommand<Delayer, READABLE>,
154    Delayer: DelayNs,
155{
156    /// Create a [`Lcd`] driver, and init LCD hardware
157    ///
158    /// If we don't specify `poll_interval_us`,  
159    /// then default value will be 40 us for Write-Only mode, and 10 us for Read-Write mode
160    pub fn new(
161        sender: &'a mut Sender,
162        delayer: &'b mut Delayer,
163        config: Config,
164        poll_interval_us: Option<u32>,
165    ) -> Self {
166        let poll_interval_us = if !READABLE {
167            poll_interval_us.unwrap_or_default().max(40)
168        } else {
169            poll_interval_us.unwrap_or_default().max(10)
170        };
171
172        let state = config.state;
173
174        // in initialization process, we'd better use "raw command", to strictly follow datasheet
175
176        // only first 2 or 3 commands are different between 4 pin and 8 pin mode
177        match state.get_data_width() {
178            DataWidth::Bit4 => {
179                sender.delay_and_send(
180                    CommandSet::HalfFunctionSet,
181                    delayer,
182                    poll_interval_us.max(40_000),
183                );
184
185                sender.delay_and_send(
186                    CommandSet::FunctionSet(
187                        DataWidth::Bit4,
188                        state.get_line_mode(),
189                        state.get_font(),
190                    ),
191                    delayer,
192                    poll_interval_us,
193                );
194
195                sender.delay_and_send(
196                    CommandSet::FunctionSet(
197                        DataWidth::Bit4,
198                        state.get_line_mode(),
199                        state.get_font(),
200                    ),
201                    delayer,
202                    poll_interval_us,
203                );
204            }
205
206            DataWidth::Bit8 => {
207                sender.delay_and_send(
208                    CommandSet::FunctionSet(
209                        DataWidth::Bit8,
210                        state.get_line_mode(),
211                        state.get_font(),
212                    ),
213                    delayer,
214                    poll_interval_us.max(40_000),
215                );
216
217                sender.delay_and_send(
218                    CommandSet::FunctionSet(
219                        DataWidth::Bit8,
220                        state.get_line_mode(),
221                        state.get_font(),
222                    ),
223                    delayer,
224                    poll_interval_us,
225                );
226            }
227        }
228
229        sender.wait_and_send(
230            CommandSet::DisplayOnOff {
231                display: state.get_display_state(),
232                cursor: state.get_cursor_state(),
233                cursor_blink: state.get_cursor_blink(),
234            },
235            delayer,
236            poll_interval_us,
237        );
238
239        sender.wait_and_send(CommandSet::ClearDisplay, delayer, poll_interval_us);
240
241        sender.wait_and_send(
242            CommandSet::EntryModeSet(state.get_direction(), state.get_shift_type()),
243            delayer,
244            poll_interval_us,
245        );
246
247        // set backlight after LCD init
248        sender.set_actual_backlight(state.get_backlight());
249
250        Lcd {
251            sender,
252            delayer,
253            state,
254            poll_interval_us,
255        }
256    }
257}