hd44780_controller/controller/
async_controller.rs

1use core::marker::PhantomData;
2
3use crate::{
4    charset::{InfallibleCharset, MinimalCharset},
5    command::{Commands, *},
6    device::{AsyncDevice, CommandExtAsync},
7};
8
9use super::{config::*, state::*, Controller};
10
11impl<D: AsyncDevice> Controller<D, Uninit> {
12    pub async fn init(mut self) -> Result<Controller<D, Init>, super::Error> {
13        self.device
14            .execute_commands_async::<Commands, _>(&[
15                Enter4Bit.into(),
16                FunctionSet {
17                    data_length: self.initial_config.data_length,
18                    number_of_lines: self.initial_config.lines,
19                    character_font: self.initial_config.font,
20                }
21                .into(),
22            ])
23            .await?;
24
25        let mut controller: Controller<_, Init> = Controller {
26            device: self.device,
27            initial_config: self.initial_config,
28            runtime_config: self.runtime_config,
29            _state: PhantomData,
30        };
31
32        controller.set_runtime_config(self.runtime_config).await?;
33        controller.clear().await?;
34
35        Ok(controller)
36    }
37
38    pub fn release(self) -> D {
39        self.device
40    }
41}
42
43impl<D: AsyncDevice> Controller<D, Init> {
44    pub async fn clear(&mut self) -> Result<(), super::Error> {
45        Ok(self.device.execute_command_async(&ClearDisplay).await?)
46    }
47
48    pub async fn reset(&mut self) -> Result<(), super::Error> {
49        self.clear().await?;
50        self.set_runtime_config(RuntimeConfig::default()).await?;
51        self.set_cursor_position(0, 0).await
52    }
53
54    pub async fn return_home(&mut self) -> Result<(), super::Error> {
55        Ok(self.device.execute_command_async(&ReturnHome).await?)
56    }
57
58    pub fn initial_config(&self) -> InitialConfig {
59        self.initial_config
60    }
61
62    pub fn runtime_config(&self) -> RuntimeConfig {
63        self.runtime_config
64    }
65
66    pub async fn set_runtime_config(&mut self, cfg: RuntimeConfig) -> Result<(), super::Error> {
67        self.device
68            .execute_commands_async::<Commands, _>(&[
69                DisplayOnOff {
70                    display: cfg.display,
71                    cursor: cfg.cursor,
72                    blinking: cfg.blink,
73                }
74                .into(),
75                EntryModeSet {
76                    cursor_move_direction: cfg.cursor_direction,
77                    shift: cfg.shift,
78                }
79                .into(),
80                SetBacklight(cfg.backlight).into(),
81            ])
82            .await?;
83
84        self.runtime_config = cfg;
85
86        Ok(())
87    }
88
89    pub fn backlight(&self) -> bool {
90        self.runtime_config.backlight
91    }
92
93    pub async fn set_backlight(&mut self, state: bool) -> Result<(), super::Error> {
94        let new_config = RuntimeConfig {
95            backlight: state,
96            ..self.runtime_config()
97        };
98        self.set_runtime_config(new_config).await
99    }
100
101    pub fn cursor_blinking(&self) -> bool {
102        self.runtime_config.blink
103    }
104
105    pub async fn set_cursor_blinking(&mut self, state: bool) -> Result<(), super::Error> {
106        let new_config = RuntimeConfig {
107            blink: state,
108            ..self.runtime_config()
109        };
110        self.set_runtime_config(new_config).await
111    }
112
113    pub fn cursor_visible(&self) -> bool {
114        self.runtime_config.cursor
115    }
116
117    pub async fn set_cursor_visible(&mut self, state: bool) -> Result<(), super::Error> {
118        let new_config = RuntimeConfig {
119            cursor: state,
120            ..self.runtime_config()
121        };
122        self.set_runtime_config(new_config).await
123    }
124
125    pub async fn set_cursor_position(&mut self, row: u8, col: u8) -> Result<(), super::Error> {
126        if row >= 4 || col >= 0x40 {
127            return Err(super::Error::OutOfBounds);
128        }
129
130        let addr = match (row, col) {
131            (0, c) => c,
132            (1, c) => 0x40 + c,
133            (2, c) => 0x14 + c,
134            (3, c) => 0x54 + c,
135            _ => unreachable!(),
136        };
137
138        self.device
139            .execute_command_async(&SetDDRamAddress(addr))
140            .await?;
141
142        Ok(())
143    }
144
145    pub async fn write_raw_char(&mut self, code: u8) -> Result<(), super::Error> {
146        Ok(self.device.execute_command_async(&WriteChar(code)).await?)
147    }
148
149    pub async fn write_raw_str(
150        &mut self,
151        raw_code: impl Iterator<Item = u8>,
152    ) -> Result<(), super::Error> {
153        for c in raw_code {
154            self.write_raw_char(c).await?;
155        }
156        Ok(())
157    }
158
159    pub async fn write_raw_line(
160        &mut self,
161        row: u8,
162        raw_code: impl Iterator<Item = u8>,
163    ) -> Result<(), super::Error> {
164        self.set_cursor_position(row, 0).await?;
165        for (_, c) in (0..40).zip(raw_code.chain(core::iter::repeat(b' '))) {
166            self.write_raw_char(c).await?;
167        }
168        Ok(())
169    }
170
171    pub async fn write_char_with_charset<C: InfallibleCharset>(
172        &mut self,
173        c: char,
174        charset: C,
175    ) -> Result<(), super::Error> {
176        self.write_raw_char(charset.char_to_code(c)).await
177    }
178
179    pub async fn write_str_with_charset<C: InfallibleCharset>(
180        &mut self,
181        s: impl Iterator<Item = char>,
182        charset: C,
183    ) -> Result<(), super::Error> {
184        self.write_raw_str(s.map(|c| charset.char_to_code(c))).await
185    }
186
187    pub async fn write_line_with_charset<C: InfallibleCharset>(
188        &mut self,
189        row: u8,
190        s: impl Iterator<Item = char>,
191        charset: C,
192    ) -> Result<(), super::Error> {
193        self.set_cursor_position(row, 0).await?;
194        self.write_raw_line(row, s.map(|c| charset.char_to_code(c)))
195            .await
196    }
197
198    pub async fn write_char(&mut self, c: char) -> Result<(), super::Error> {
199        self.write_char_with_charset(c, MinimalCharset::BLANK_FALLBACK)
200            .await
201    }
202
203    pub async fn write_str(&mut self, s: impl Iterator<Item = char>) -> Result<(), super::Error> {
204        self.write_str_with_charset(s, MinimalCharset::BLANK_FALLBACK)
205            .await
206    }
207
208    pub async fn write_line(
209        &mut self,
210        row: u8,
211        s: impl Iterator<Item = char>,
212    ) -> Result<(), super::Error> {
213        self.write_line_with_charset(row, s, MinimalCharset::BLANK_FALLBACK)
214            .await
215    }
216
217    #[cfg(feature = "fmt")]
218    #[cfg_attr(docsrs, doc(cfg(feature = "fmt")))]
219    pub async fn write_line_fmt_with_charset<C: InfallibleCharset, const BUFFER_SIZE: usize>(
220        &mut self,
221        row: u8,
222        args: core::fmt::Arguments<'_>,
223        charset: C,
224    ) -> Result<(), super::Error> {
225        let mut buffer = heapless::String::<BUFFER_SIZE>::new();
226        // string formatting should be infallible
227        let _ = core::fmt::write(&mut buffer, args);
228        self.write_line_with_charset(row, buffer.chars(), charset)
229            .await
230    }
231
232    #[cfg(feature = "fmt")]
233    #[cfg_attr(docsrs, doc(cfg(feature = "fmt")))]
234    pub async fn write_line_fmt<const BUFFER_SIZE: usize>(
235        &mut self,
236        row: u8,
237        args: core::fmt::Arguments<'_>,
238    ) -> Result<(), super::Error> {
239        self.write_line_fmt_with_charset::<_, BUFFER_SIZE>(
240            row,
241            args,
242            MinimalCharset::BLANK_FALLBACK,
243        )
244        .await
245    }
246
247    #[cfg(feature = "fmt")]
248    #[cfg_attr(docsrs, doc(cfg(feature = "fmt")))]
249    pub async fn write_fmt_with_charset<C: InfallibleCharset, const BUFFER_SIZE: usize>(
250        &mut self,
251        args: core::fmt::Arguments<'_>,
252        charset: C,
253    ) -> Result<(), super::Error> {
254        let mut buffer = heapless::String::<BUFFER_SIZE>::new();
255        // string formatting should be infallible
256        let _ = core::fmt::write(&mut buffer, args);
257        self.write_str_with_charset(buffer.chars(), charset).await
258    }
259
260    #[cfg(feature = "fmt")]
261    #[cfg_attr(docsrs, doc(cfg(feature = "fmt")))]
262    pub async fn write_fmt<const BUFFER_SIZE: usize>(
263        &mut self,
264        args: core::fmt::Arguments<'_>,
265    ) -> Result<(), super::Error> {
266        // placeholders for const generic is not yet supported.
267        self.write_fmt_with_charset::<_, BUFFER_SIZE>(args, MinimalCharset::BLANK_FALLBACK)
268            .await
269    }
270
271    pub fn device(&mut self) -> &mut D {
272        &mut self.device
273    }
274
275    pub fn release(self) -> D {
276        self.device
277    }
278}