tm1637_embedded_hal/
device.rs

1use ::core::marker::PhantomData;
2
3use ::embedded_hal::digital::OutputPin;
4use ::futures::Stream;
5
6use crate::{
7    tokens::{Async, Blocking},
8    Brightness, ConditionalInputPin, Error, TM1637Builder,
9};
10
11/// `TM1637` 7-segment display driver.
12///
13/// # Type parameters
14///
15/// - `N`: Number of positions on the display. 4 or 6.
16/// - `T`: Operating mode. [`Async`](crate::tokens::Async) or [`Blocking`](crate::tokens::Blocking).
17/// - `CLK`: Clock.
18/// - `DIO`: Data input/output.
19/// - `DELAY`: Delay provider.
20#[derive(Debug, Clone)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub struct TM1637<const N: usize, T, CLK, DIO, DELAY> {
23    /// Clock.
24    clk: CLK,
25    /// Data input/output.
26    dio: DIO,
27    /// Delay provider.
28    delay: DELAY,
29    /// Brightness level.
30    brightness: Brightness,
31    /// The delay in microseconds.
32    ///
33    /// Experiment with this value to find the best value for your display.
34    delay_us: u32,
35    _token: PhantomData<T>,
36}
37
38impl<const N: usize, T, CLK, DIO, DELAY> TM1637<N, T, CLK, DIO, DELAY> {
39    /// Create a new [`TM1637`] instance.
40    pub const fn new(
41        clk: CLK,
42        dio: DIO,
43        delay: DELAY,
44        brightness: Brightness,
45        delay_us: u32,
46    ) -> Self {
47        Self {
48            clk,
49            dio,
50            delay,
51            brightness,
52            delay_us,
53            _token: PhantomData,
54        }
55    }
56
57    /// Create a new [`TM1637Builder`] instance.
58    ///
59    /// See [`TM1637Builder::new`] for default values.
60    pub const fn builder(clk: CLK, dio: DIO, delay: DELAY) -> TM1637Builder<CLK, DIO, DELAY> {
61        TM1637Builder::new(clk, dio, delay)
62    }
63
64    /// Get the number of positions on the display.
65    pub const fn num_positions(&self) -> usize {
66        N
67    }
68
69    /// Get the brightness level.
70    pub const fn brightness(&self) -> Brightness {
71        self.brightness
72    }
73
74    /// Get the delay in microseconds.
75    pub const fn delay_us(&self) -> u32 {
76        self.delay_us
77    }
78
79    /// Get a reference to the clock pin.
80    pub const fn clk(&self) -> &CLK {
81        &self.clk
82    }
83
84    /// Get a mutable reference to the clock pin.
85    pub const fn clk_mut(&mut self) -> &mut CLK {
86        &mut self.clk
87    }
88
89    /// Get a reference to the data input/output pin.
90    pub const fn dio(&self) -> &DIO {
91        &self.dio
92    }
93
94    /// Get a mutable reference to the data input/output pin.
95    pub const fn dio_mut(&mut self) -> &mut DIO {
96        &mut self.dio
97    }
98
99    /// Get a reference to the delay provider.
100    pub const fn delay(&self) -> &DELAY {
101        &self.delay
102    }
103
104    /// Get a mutable reference to the delay provider.
105    pub const fn delay_mut(&mut self) -> &mut DELAY {
106        &mut self.delay
107    }
108
109    /// Split the [`TM1637`] instance into its parts.
110    pub fn into_parts(self) -> (CLK, DIO, DELAY) {
111        (self.clk, self.dio, self.delay)
112    }
113}
114
115impl<const N: usize, CLK, DIO, DELAY, ERR> TM1637<N, Async, CLK, DIO, DELAY>
116where
117    CLK: OutputPin<Error = ERR>,
118    DIO: OutputPin<Error = ERR> + ConditionalInputPin<ERR>,
119    DELAY: ::embedded_hal_async::delay::DelayNs,
120{
121    /// Scroll the given `iter` of bytes on the display starting from `position` with a delay of `delay_ms` milliseconds.
122    ///
123    /// ## Note
124    ///
125    /// - The stream does not stop on error.
126    pub(crate) fn scroll<'a>(
127        &'a mut self,
128        position: usize,
129        delay_ms: u32,
130        iter: impl Iterator<Item = impl Iterator<Item = u8>> + 'a,
131    ) -> impl Stream<Item = Result<(), Error<ERR>>> + 'a {
132        futures::stream::unfold((self, iter), move |(this, mut bytes)| async move {
133            match bytes.next() {
134                Some(window) => match this.display(position, window).await {
135                    Ok(_) => {
136                        this.delay.delay_ms(delay_ms).await;
137
138                        Some((Ok(()), (this, bytes)))
139                    }
140                    Err(e) => Some((Err(e), (this, bytes))),
141                },
142                None => None,
143            }
144        })
145    }
146}
147
148impl<const N: usize, CLK, DIO, DELAY, ERR> TM1637<N, Blocking, CLK, DIO, DELAY>
149where
150    CLK: OutputPin<Error = ERR>,
151    DIO: OutputPin<Error = ERR> + ConditionalInputPin<ERR>,
152    DELAY: ::embedded_hal::delay::DelayNs,
153{
154    /// Scroll the given `iter` of bytes on the display starting from `position` with a delay of `delay_ms` milliseconds.
155    ///
156    /// ## Note
157    ///
158    /// - The iterator does not stop on error.
159    pub(crate) fn scroll<'a>(
160        &'a mut self,
161        position: usize,
162        delay_ms: u32,
163        iter: impl Iterator<Item = impl Iterator<Item = u8>> + 'a,
164    ) -> impl Iterator<Item = Result<(), Error<ERR>>> + 'a {
165        iter.map(move |bytes| match self.display(position, bytes) {
166            Ok(_) => {
167                self.delay.delay_ms(delay_ms);
168
169                Ok(())
170            }
171            Err(e) => Err(e),
172        })
173    }
174}
175
176#[::duplicate::duplicate_item(
177    module        async     await               Token                         DelayTrait;
178    [asynch]      [async]   [await.identity()]  [crate::tokens::Async]        [::embedded_hal_async::delay::DelayNs];
179    [blocking]    []        [identity()]        [crate::tokens::Blocking]     [::embedded_hal::delay::DelayNs];
180)]
181pub mod module {
182    use crate::{
183        options::{circles::CirclesDisplayOptions, DisplayOptions},
184        tokens::NotFlipped,
185        Brightness, ConditionalInputPin, Error, Identity, TM1637,
186    };
187    use ::embedded_hal::digital::OutputPin;
188
189    impl<const N: usize, CLK, DIO, DELAY, ERR> TM1637<N, Token, CLK, DIO, DELAY>
190    where
191        CLK: OutputPin<Error = ERR>,
192        DIO: OutputPin<Error = ERR> + ConditionalInputPin<ERR>,
193        DELAY: DelayTrait,
194    {
195        /// Send a byte to the display and wait for the ACK.
196        async fn write_byte(&mut self, byte: u8) -> Result<(), Error<ERR>> {
197            let mut rest = byte;
198
199            for _ in 0..8 {
200                self.clk.set_low()?;
201
202                match rest & 0x01 {
203                    1 => self.dio.set_high()?,
204                    _ => self.dio.set_low()?,
205                }
206
207                self.bit_delay().await;
208
209                self.clk.set_high()?;
210                self.bit_delay().await;
211
212                rest >>= 1;
213            }
214
215            self.clk.set_low()?;
216            self.dio.set_low()?;
217            self.bit_delay().await;
218
219            self.clk.set_high()?;
220            self.bit_delay().await;
221
222            // Ack
223            #[cfg(feature = "ack")]
224            let ack = self.wait_for_ack().await?;
225
226            self.clk.set_low()?;
227            self.dio.set_low()?;
228            self.bit_delay().await;
229
230            #[cfg(feature = "ack")]
231            ack.then_some(()).ok_or(Error::Ack)?;
232
233            Ok(())
234        }
235
236        /// Wait for 255 cycles for the acknowledgment signal from the display.
237        #[cfg(feature = "ack")]
238        async fn wait_for_ack(&mut self) -> Result<bool, Error<ERR>> {
239            for _ in 0..255 {
240                if self.dio.is_low()? {
241                    return Ok(true);
242                }
243
244                self.bit_delay().await;
245            }
246
247            Ok(false)
248        }
249
250        /// Start the communication with the display.
251        async fn start(&mut self) -> Result<(), Error<ERR>> {
252            self.dio.set_high()?;
253            self.clk.set_high()?;
254            self.bit_delay().await;
255            self.dio.set_low()?;
256            self.bit_delay().await;
257
258            Ok(())
259        }
260
261        /// Stop the communication with the display.
262        async fn stop(&mut self) -> Result<(), Error<ERR>> {
263            self.dio.set_low()?;
264            self.clk.set_high()?;
265            self.bit_delay().await;
266            self.dio.set_high()?;
267            self.bit_delay().await;
268
269            Ok(())
270        }
271
272        /// Write the `cmd` to the display.
273        async fn write_cmd(&mut self, cmd: u8) -> Result<(), Error<ERR>> {
274            self.start().await?;
275            self.write_byte(cmd).await?;
276            self.stop().await?;
277
278            Ok(())
279        }
280
281        /// Perform command 1.
282        async fn write_start_display_cmd(&mut self) -> Result<(), Error<ERR>> {
283            self.write_cmd(0x40).await?;
284
285            Ok(())
286        }
287
288        /// Perform command 2.
289        async fn write_display_cmd(
290            &mut self,
291            position: usize,
292            bytes: impl Iterator<Item = u8>,
293        ) -> Result<(), Error<ERR>> {
294            self.start().await?;
295
296            self.write_byte(0xc0 | (position as u8 & 0x03)).await?;
297
298            for byte in bytes {
299                self.write_byte(byte).await?;
300            }
301
302            self.stop().await?;
303
304            Ok(())
305        }
306
307        /// Perform command 3.
308        async fn write_brightness_cmd(&mut self, brightness: Brightness) -> Result<(), Error<ERR>> {
309            self.write_cmd(brightness as u8).await
310        }
311
312        /// Delay for [`TM1637::delay_us`] microseconds using [`TM1637::delay`] provider.
313        async fn bit_delay(&mut self) {
314            self.delay.delay_us(self.delay_us).await;
315        }
316
317        /// Initialize the display.
318        ///
319        /// Clear the display and set the brightness level.
320        pub async fn init(&mut self) -> Result<(), Error<ERR>> {
321            self.clear().await?;
322            self.write_cmd(self.brightness as u8).await
323        }
324
325        /// Turn the display on.
326        pub async fn on(&mut self) -> Result<(), Error<ERR>> {
327            self.write_cmd(self.brightness as u8).await
328        }
329
330        /// Turn the display off.
331        pub async fn off(&mut self) -> Result<(), Error<ERR>> {
332            self.write_cmd(Brightness::Off as u8).await
333        }
334
335        /// Clear the display.
336        pub async fn clear(&mut self) -> Result<(), Error<ERR>> {
337            self.display(0, ::core::iter::repeat_n(0, self.num_positions()))
338                .await
339        }
340
341        /// Set [`TM1637::brightness`] and write the brightness level to the display.
342        pub async fn set_brightness(&mut self, brightness: Brightness) -> Result<(), Error<ERR>> {
343            self.brightness = brightness;
344
345            self.write_brightness_cmd(brightness).await
346        }
347
348        /// Write the given `bytes` to the display starting from `position`.
349        ///
350        /// Brightness level will not be written to the device on each call. Make sure to call [`TM1637::set_brightness`] or [`TM1637::init`] to set the brightness level.
351        pub async fn display(
352            &mut self,
353            position: usize,
354            bytes: impl Iterator<Item = u8>,
355        ) -> Result<(), Error<ERR>> {
356            // Comm 1
357            self.write_start_display_cmd().await?;
358
359            // Comm 2
360            self.write_display_cmd(position, bytes).await?;
361
362            Ok(())
363        }
364
365        /// Write the given `bytes` slice to the display starting from `position`.
366        ///
367        /// See [`TM1637::display`].
368        pub async fn display_slice(
369            &mut self,
370            position: usize,
371            bytes: &[u8],
372        ) -> Result<(), Error<ERR>> {
373            self.display(position, bytes.iter().copied()).await
374        }
375
376        /// High-level API for static or animated display operations.
377        ///
378        /// # Example
379        ///
380        /// Scroll the text "Error" with a dot on the first position from right to left with a delay of 700ms.
381        ///
382        /// ```rust
383        /// use tm1637_embedded_hal::{mock::Noop, options::{ScrollDirection, ScrollStyle}, TM1637Builder};
384        ///
385        /// let mut tm = TM1637Builder::new(Noop, Noop, Noop).build_blocking::<4>();
386        ///
387        /// tm.options()
388        ///     .str("Error")
389        ///     .dot(1)
390        ///     .scroll()
391        ///     .delay_ms(700)
392        ///     .direction(ScrollDirection::RightToLeft)
393        ///     .style(ScrollStyle::Circular)
394        ///     .finish()
395        ///     .run();
396        /// ```
397        pub const fn options(
398            &mut self,
399        ) -> DisplayOptions<'_, N, Token, CLK, DIO, DELAY, ::core::iter::Empty<u8>, NotFlipped>
400        {
401            DisplayOptions::empty(self)
402        }
403
404        /// High-level API for animated circles (loading spinner).
405        pub const fn circles(&mut self) -> CirclesDisplayOptions<'_, N, Token, CLK, DIO, DELAY> {
406            CirclesDisplayOptions::new(self)
407        }
408    }
409}