tm1637_embedded_hal/
device.rs

1//! Device definition and implementation.
2
3use duplicate::duplicate_item;
4
5/// Identity trait.
6///
7/// Used to trick the compiler while using [`duplicate_item`] to implement `async` and `blocking` versions of the same module.
8/// Using this trait, we can write normal rust code that can also be formatted by `rustfmt`.
9#[cfg(any(feature = "async", feature = "blocking"))]
10trait Identity: Sized {
11    fn identity(self) -> Self {
12        self
13    }
14}
15
16#[cfg(any(feature = "async", feature = "blocking"))]
17impl<T: Sized> Identity for T {}
18
19#[duplicate_item(
20    feature_        module        async     await               delay_trait;
21    ["async"]       [asynch]      [async]   [await.identity()]  [embedded_hal_async::delay::DelayNs];
22    ["blocking"]    [blocking]    []        [identity()]        [embedded_hal::delay::DelayNs];
23)]
24pub mod module {
25    //! Device definition and implementation.
26
27    #[cfg(feature=feature_)]
28    mod inner {
29        use super::super::Identity;
30        use crate::brightness::{Brightness, DisplayState};
31        use embedded_hal::digital::OutputPin;
32
33        /// `TM1637` 7-segment display builder.
34        #[derive(Clone)]
35        #[cfg_attr(feature = "impl-defmt-format", derive(defmt::Format))]
36        #[cfg_attr(feature = "impl-debug", derive(core::fmt::Debug))]
37        pub struct TM1637Builder<CLK, DIO, DELAY> {
38            /// The inner [`TM1637`] instance.
39            inner: TM1637<CLK, DIO, DELAY>,
40        }
41
42        impl<CLK, DIO, DELAY> TM1637Builder<CLK, DIO, DELAY> {
43            /// Create a new [`TM1637Builder`] instance.
44            pub fn new(clk: CLK, dio: DIO, delay: DELAY) -> Self {
45                Self {
46                    inner: TM1637 {
47                        clk,
48                        dio,
49                        delay,
50                        brightness: Brightness::L0,
51                        delay_us: 10,
52                        num_positions: 4,
53                    },
54                }
55            }
56
57            /// Set the brightness level.
58            pub fn brightness(mut self, brightness: Brightness) -> Self {
59                self.inner.brightness = brightness;
60                self
61            }
62
63            /// Set the delay in microseconds.
64            pub fn delay_us(mut self, delay_us: u32) -> Self {
65                self.inner.delay_us = delay_us;
66                self
67            }
68
69            /// Set the number of positions on the display.
70            pub fn num_positions(mut self, num_positions: u8) -> Self {
71                self.inner.num_positions = num_positions;
72                self
73            }
74
75            /// Build the [`TM1637`] instance.
76            pub fn build(self) -> TM1637<CLK, DIO, DELAY> {
77                self.inner
78            }
79        }
80
81        /// `TM1637` 7-segment display driver.
82        #[derive(Clone)]
83        #[cfg_attr(feature = "impl-defmt-format", derive(defmt::Format))]
84        #[cfg_attr(feature = "impl-debug", derive(core::fmt::Debug))]
85        pub struct TM1637<CLK, DIO, DELAY> {
86            /// Clock.
87            clk: CLK,
88            /// Data input/output.
89            dio: DIO,
90            /// Delay provider.
91            delay: DELAY,
92            /// Brightness level.
93            brightness: Brightness,
94            /// The delay in microseconds.
95            ///
96            /// Experiment with this value to find the best value for your display.
97            delay_us: u32,
98            /// The number of positions on the display.
99            num_positions: u8,
100        }
101
102        impl<CLK, DIO, DELAY, ERR> TM1637<CLK, DIO, DELAY>
103        where
104            CLK: OutputPin<Error = ERR>,
105            DIO: OutputPin<Error = ERR>,
106            DELAY: delay_trait,
107        {
108            /// Create a new [`TM1637`] instance.
109            pub fn new(
110                clk: CLK,
111                dio: DIO,
112                delay: DELAY,
113                brightness: Brightness,
114                delay_us: u32,
115                num_positions: u8,
116            ) -> Self {
117                Self {
118                    clk,
119                    dio,
120                    delay,
121                    brightness,
122                    delay_us,
123                    num_positions,
124                }
125            }
126
127            /// Create a new [`TM1637Builder`] instance.
128            pub fn builder(clk: CLK, dio: DIO, delay: DELAY) -> TM1637Builder<CLK, DIO, DELAY> {
129                TM1637Builder::new(clk, dio, delay)
130            }
131
132            /// Send a byte to the display and wait for the ACK.
133            async fn write_byte(&mut self, byte: u8) -> Result<(), ERR> {
134                let mut rest = byte;
135
136                for _ in 0..8 {
137                    self.bit_delay().await;
138                    tri!(self.clk.set_low());
139                    self.bit_delay().await;
140
141                    match rest & 0x01 {
142                        1 => tri!(self.dio.set_high()),
143                        _ => tri!(self.dio.set_low()),
144                    }
145
146                    self.bit_delay().await;
147                    tri!(self.clk.set_high());
148                    self.bit_delay().await;
149
150                    rest >>= 1;
151                }
152
153                tri!(self.clk.set_low());
154                tri!(self.dio.set_high());
155                self.bit_delay().await;
156
157                tri!(self.clk.set_high());
158                self.bit_delay().await;
159
160                tri!(self.clk.set_low());
161                self.bit_delay().await;
162
163                Ok(())
164            }
165
166            /// Write the `cmd` to the display.
167            async fn write_cmd_raw(&mut self, cmd: u8) -> Result<(), ERR> {
168                tri!(self.start().await);
169                tri!(self.write_byte(cmd).await);
170                tri!(self.stop().await);
171
172                Ok(())
173            }
174
175            /// Start the communication with the display.
176            async fn start(&mut self) -> Result<(), ERR> {
177                tri!(self.dio.set_low());
178                self.bit_delay().await;
179                tri!(self.clk.set_low());
180                self.bit_delay().await;
181
182                Ok(())
183            }
184
185            /// Stop the communication with the display.
186            async fn stop(&mut self) -> Result<(), ERR> {
187                tri!(self.dio.set_low());
188                self.bit_delay().await;
189                tri!(self.clk.set_high());
190                self.bit_delay().await;
191                tri!(self.dio.set_high());
192                self.bit_delay().await;
193
194                Ok(())
195            }
196
197            /// Delay for [`TM1637::delay_us`] microseconds using [`TM1637::delay`] provider.
198            async fn bit_delay(&mut self) {
199                self.delay.delay_us(self.delay_us).await;
200            }
201
202            /// Initialize the display.
203            ///
204            /// Clear the display and set the brightness level.
205            pub async fn init(&mut self) -> Result<(), ERR> {
206                tri!(self.clear().await);
207                self.write_cmd_raw(self.brightness as u8).await
208            }
209
210            /// Turn the display on.
211            pub async fn on(&mut self) -> Result<(), ERR> {
212                self.write_cmd_raw(self.brightness as u8).await
213            }
214
215            /// Turn the display off.
216            pub async fn off(&mut self) -> Result<(), ERR> {
217                self.write_cmd_raw(DisplayState::Off as u8).await
218            }
219
220            /// Clear the display.
221            pub async fn clear(&mut self) -> Result<(), ERR> {
222                self.write_segments_raw_iter(
223                    0,
224                    core::iter::repeat(0).take(self.num_positions as usize),
225                )
226                .await
227            }
228
229            /// Write the given bytes to the display starting from the given position.
230            ///
231            /// See [`TM1637::write_segments_raw_iter`].
232            pub async fn write_segments_raw(
233                &mut self,
234                position: u8,
235                bytes: &[u8],
236            ) -> Result<(), ERR> {
237                self.write_segments_raw_iter(position, bytes.iter().copied())
238                    .await
239            }
240
241            /// Write the given bytes to the display starting from the given position.
242            ///
243            /// # Notes:
244            /// - Positions greater than [`TM1637::num_positions`] will be ignored.
245            /// - Bytes with index greater than [`TM1637::num_positions`] will be ignored.
246            ///
247            /// Brightness level will not be written to the device on each call. Make sure to call [`TM1637::write_brightness`] or [`TM1637::init`] to set the brightness level.
248            pub async fn write_segments_raw_iter<ITER: Iterator<Item = u8>>(
249                &mut self,
250                position: u8,
251                bytes: ITER,
252            ) -> Result<(), ERR> {
253                #[cfg(not(feature = "disable-checks"))]
254                if position >= self.num_positions {
255                    return Ok(());
256                }
257
258                // COMM1
259                tri!(self.write_cmd_raw(0x40).await);
260
261                // COMM2
262                tri!(self.start().await);
263                tri!(self.write_byte(0xc0 | (position & 0x03)).await);
264
265                #[cfg(not(feature = "disable-checks"))]
266                let bytes = bytes.take(self.num_positions as usize - position as usize);
267
268                for byte in bytes {
269                    tri!(self.write_byte(byte).await);
270                }
271
272                tri!(self.stop().await);
273
274                Ok(())
275            }
276
277            /// Set [`TM1637::brightness`] and write the brightness level to the display.
278            pub async fn write_brightness(&mut self, brightness: Brightness) -> Result<(), ERR> {
279                self.brightness = brightness;
280                self.write_cmd_raw(brightness as u8).await
281            }
282
283            /// Move all segments across the display starting and ending at `position`.
284            ///
285            /// If the length of the bytes is less than or equal to [`TM1637::num_positions`] - `position`, the bytes will only be written to the display.
286            ///
287            /// `N` is the size of the internal window used to move the segments. Please make sure that `N` is equal to [`TM1637::num_positions`].
288            /// [`TM1637::num_positions`] will be removed in the future in favor of a constant generic parameter representing the number of positions.
289            pub async fn move_segments_raw<const N: usize>(
290                &mut self,
291                position: u8,
292                bytes: &[u8],
293                delay_ms: u32,
294            ) -> Result<(), ERR> {
295                let num_positions = self.num_positions as usize;
296
297                if bytes.len() <= num_positions - position as usize {
298                    return self.write_segments_raw(position, bytes).await;
299                }
300
301                for i in 0..=bytes.len() {
302                    let mut window = [0u8; N];
303                    for j in 0..num_positions {
304                        window[j] = bytes[(i + j) % bytes.len()];
305                    }
306
307                    tri!(self.write_segments_raw(position, &window).await);
308
309                    self.delay.delay_ms(delay_ms).await;
310                }
311
312                Ok(())
313            }
314
315            /// Convert an `ASCII` string to a byte iterator using [`from_ascii_byte`](crate::mappings::from_ascii_byte) and write the segments to the display using [`TM1637::write_segments_raw_iter`].
316            ///
317            /// Only available when the `mappings` feature of this library is activated.
318            ///
319            /// # Example
320            ///
321            /// Write the string `"Err"` to the display:
322            ///
323            /// ```rust, ignore
324            /// let mut tm = TM1637::builder(clk_pin, dio_pin, delay)
325            ///    .brightness(Brightness::L3)
326            ///    .build();
327            ///
328            /// tm.init().ok();
329            ///
330            /// tm.write_ascii_str(0, "Err").ok();
331            /// ```
332            ///
333            /// On a `4-digit display`, this will look like this:
334            ///
335            /// ```text
336            /// +---+ +---+ +---+ +---+
337            /// | E | | r | | r | |   |
338            /// +---+ +---+ +---+ +---+
339            /// ```
340            #[cfg(feature = "mappings")]
341            pub async fn write_ascii_str(
342                &mut self,
343                position: u8,
344                ascii_str: &str,
345            ) -> Result<(), ERR> {
346                let iter = ascii_str
347                    .as_bytes()
348                    .iter()
349                    .take(self.num_positions as usize - position as usize)
350                    .copied()
351                    .map(crate::mappings::from_ascii_byte);
352
353                self.write_segments_raw_iter(position, iter).await
354            }
355
356            /// Convert an `ASCII` string to a byte array using [`from_ascii_byte`](crate::mappings::from_ascii_byte) and move the segments across the display using [`TM1637::move_segments_raw`].
357            ///
358            /// - `N` is the size of the internal window used to move the segments. See [`TM1637::move_segments_raw`] for more information.
359            /// - `M` is the maximum number of bytes that can be converted from the `ASCII` string.
360            ///
361            /// Only available when the `mappings` feature of this library is activated.
362            ///
363            /// # Example
364            ///
365            /// Move the string `"HELLO "` across a `4-digit display`:
366            ///
367            /// ```rust, ignore
368            /// let mut tm = TM1637::builder(clk_pin, dio_pin, delay)
369            ///    .brightness(Brightness::L3)
370            ///    .build();
371            ///
372            /// tm.init().ok();
373            ///
374            /// // 4 is the actual number of positions on the display.
375            /// // 6 is the maximum number of bytes that can be converted from the ASCII string.
376            /// // In case of "HELLO ", all six bytes will be converted.
377            /// tm.move_ascii_str::<4, 6>(0, "HELLO ", 500).ok();
378            /// ```
379            ///
380            /// On a `4-digit display`, this will look like this:
381            ///
382            /// ```text
383            /// +---+ +---+ +---+ +---+
384            /// | H | | E | | L | | L |
385            /// +---+ +---+ +---+ +---+
386            ///
387            /// +---+ +---+ +---+ +---+
388            /// | E | | L | | L | | O |
389            /// +---+ +---+ +---+ +---+
390            ///
391            /// +---+ +---+ +---+ +---+
392            /// | L | | L | | O | |   |
393            /// +---+ +---+ +---+ +---+
394            ///
395            /// +---+ +---+ +---+ +---+
396            /// | L | | O | |   | | H |
397            /// +---+ +---+ +---+ +---+
398            ///
399            /// +---+ +---+ +---+ +---+
400            /// | O | |   | | H | | E |
401            /// +---+ +---+ +---+ +---+
402            ///
403            /// +---+ +---+ +---+ +---+
404            /// |   | | H | | E | | L |
405            /// +---+ +---+ +---+ +---+
406            ///
407            /// +---+ +---+ +---+ +---+
408            /// | H | | E | | L | | L |
409            /// +---+ +---+ +---+ +---+
410            /// ```
411            #[cfg(feature = "mappings")]
412            pub async fn move_ascii_str<const N: usize, const M: usize>(
413                &mut self,
414                position: u8,
415                ascii_str: &str,
416                delay_ms: u32,
417            ) -> Result<(), ERR> {
418                let mut bytes = [0u8; M];
419                ascii_str
420                    .as_bytes()
421                    .iter()
422                    .take(M)
423                    .enumerate()
424                    .for_each(|(i, &byte)| bytes[i] = crate::mappings::from_ascii_byte(byte));
425
426                self.move_segments_raw::<N>(position, &bytes, delay_ms)
427                    .await
428            }
429        }
430    }
431
432    #[cfg(feature=feature_)]
433    pub use inner::*;
434}