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}