lcd1602_driver/
sender.rs

1//! Built-in sender
2//!
3//! If you want to create a new sender, you will need to implement [`SendCommand`] trait
4
5use embedded_hal::{
6    delay::DelayNs,
7    digital::{InputPin, OutputPin},
8};
9
10use crate::{
11    command::{Command, CommandSet, State},
12    utils::{BitOps, BitState},
13};
14
15mod i2c_sender;
16mod parallel_sender;
17
18pub use i2c_sender::I2cSender;
19pub use parallel_sender::ParallelSender;
20
21/// [`IfReadable`] trait is a simple const bound trait
22///
23/// Note:
24///
25/// If your [`SendCommand`] implementation has same code on both Read-Write and Write-Only mode,
26/// you can replace trait bound `Output + Input` with `Output + IfReadable<READABLE>`.
27pub trait IfReadable<const READABLE: bool> {}
28
29impl<T> IfReadable<false> for T where T: OutputPin {}
30impl<T> IfReadable<true> for T where T: OutputPin + InputPin {}
31
32/// [`SendCommand`] is the trait a sender should implement to communicate with the hardware
33pub trait SendCommand<Delayer: DelayNs, const READABLE: bool> {
34    /// Parse a [`Command`] and sending data to hardware,
35    /// and return the result value when [`Command`] is a [`ReadWriteOp::Read`](crate::command::ReadWriteOp::Read) command
36    fn send(&mut self, command: Command) -> Option<u8>;
37
38    /// Wait specific duration, and send command
39    fn delay_and_send(
40        &mut self,
41        command_set: CommandSet,
42        delayer: &mut Delayer,
43        delay_us: u32,
44    ) -> Option<u8> {
45        delayer.delay_us(delay_us);
46        self.send(command_set.into())
47    }
48
49    /// Check LCD busy state, when LCD is idle, send the command
50    fn wait_and_send(
51        &mut self,
52        command_set: CommandSet,
53        delayer: &mut Delayer,
54        poll_interval_us: u32,
55    ) -> Option<u8> {
56        self.wait_for_idle(delayer, poll_interval_us);
57
58        let res = self.send(command_set.into());
59
60        // According to ST6077U datasheet, after CommandSet::ClearDisplay or CommandSet::ReturnHome,
61        // we will need to manually wait at least 1.52 ms before sending other command.
62        // This only needs to happen if we don't know when the command finishes (aka not readable)
63        if !READABLE
64            && (command_set == CommandSet::ClearDisplay || command_set == CommandSet::ReturnHome)
65        {
66            self.wait_for_idle(delayer, poll_interval_us.max(1520));
67        }
68
69        res
70    }
71
72    /// Wait in a busy loop, until LCD is idle
73    ///
74    /// If LCD driver is Write-Only, this method will wait one `poll_interval_us` and return.
75    fn wait_for_idle(&mut self, delayer: &mut Delayer, poll_interval_us: u32) {
76        if READABLE {
77            while self.check_busy() {
78                delayer.delay_us(poll_interval_us);
79            }
80        } else {
81            delayer.delay_us(poll_interval_us);
82        }
83    }
84
85    /// Check LCD busy state
86    ///
87    /// If LCD driver is Write-Only, this method will return false instantly.
88    fn check_busy(&mut self) -> bool {
89        if READABLE {
90            let busy_state = self
91                .send(CommandSet::ReadBusyFlagAndAddress.into())
92                .unwrap();
93            matches!(busy_state.check_bit(7), BitState::Set)
94        } else {
95            false
96        }
97    }
98
99    /// Get the current backlight
100    ///
101    /// Note:
102    /// If a driver doesn't support read backlight state, just silently bypass it
103    fn get_actual_backlight(&mut self) -> State {
104        State::default()
105    }
106
107    /// Set the backlight
108    ///
109    /// Note:
110    /// If a driver doesn't support change backlight, just silently bypass it
111    #[allow(unused_variables)]
112    fn set_actual_backlight(&mut self, backlight: State) {}
113}