1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/*!
# LCD 1602 Driver

Basic Usage:

1. Initialize pins:
   * Initialize three push-pull pins for **RS** / **RW** / **E** of LCD1602
   * Initialize 8 or 4 **open-drain** pins for DB0~DB7 or DB4~DB7 of LCD1602
   * Initialize a delay timer(which implement [embedded_hal::blocking::delay])
2. Use [8 pin Pins::new()] or [4 pin Pins::new()] to create a [Pins] struct containing all initialized pins
3. Use the [Builder::new()] to create a [Builder] struct with [Pins] and the delay timer
4. Use the functions provided by [Builder] to configure the initial state of the LCD1602
5. Use the [.build_and_init()] to convert the [Builder] to an [LCD] struct, and initialize the LCD1602
6. Use [LCD] struct:
   * [LCDBasic] trait provides functions close to LCD1602 instructions
   * [LCDExt] trait provides commonly used **non-animation** functions
   * [LCDAnimation] trait provides simple **animation** functions


[8 pin Pins::new()]: crate::pins::EightPinsAPI::new()
[4 pin Pins::new()]: crate::pins::FourPinsAPI::new()
[Builder::new()]: crate::builder::BuilderAPI::new()
[Builder]: crate::builder::Builder
[.build_and_init()]: crate::builder::BuilderAPI::build_and_init()
*/

#![no_std]

use embedded_hal::{
    blocking::delay::{DelayMs, DelayUs},
    digital::v2::{InputPin, OutputPin},
};
use enums::{
    animation::{FlipStyle, MoveStyle},
    basic_command::RAMType,
};

use self::{
    enums::basic_command::{Font, LineMode, MoveDirection, ShiftType, State},
    full_command::FullCommand,
    pins::Pins,
};

pub mod builder;
pub mod command_set;
pub mod enums;
mod full_command;
mod impl_animation;
mod impl_ext;
mod impl_lcd_api;
mod impl_pin_interaction;
mod impl_struct_api;
pub mod pins;
pub mod utils;

/// The main struct for operating the LCD1602
pub struct LCD<ControlPin, DBPin, const PIN_CNT: usize, Delayer>
where
    ControlPin: OutputPin,
    DBPin: OutputPin + InputPin,
    Delayer: DelayMs<u32> + DelayUs<u32>,
{
    pins: Pins<ControlPin, DBPin, PIN_CNT>,
    delayer: Delayer,
    line: LineMode,
    font: Font,
    display_on: State,
    cursor_on: State,
    cursor_blink: State,
    direction: MoveDirection,
    shift_type: ShiftType,
    cursor_pos: (u8, u8),
    display_offset: u8,
    wait_interval_us: u32,
    ram_type: RAMType,
}

/// The [LCDAnimation] trait provides methods for animating the display
pub trait LCDAnimation {
    /// Make the entire screen blink
    ///
    /// # Arguments
    ///
    /// * `count` - the number of times to blink the screen. If the value is `0`, the screen will blink endless.
    /// * `interval_us` - The interval (in microseconds) at which the screen state changes
    fn full_display_blink(&mut self, count: u32, interval_us: u32);

    /// Typewriter-style display
    ///
    /// # Arguments
    ///
    /// * `str` - string to display
    /// * `delay_us` - The interval (in microseconds) of each character show up
    fn typewriter_write(&mut self, str: &str, delay_us: u32);

    /// Split-Flap-style display
    ///
    /// # Arguments
    ///
    /// * `str` - string to display
    /// * `fs` - flip style, see [FlipStyle]
    /// * `max_flip_cnt` - The maximum number of times to flip the display before reaching the target character
    /// * `per_flip_delay_us` - The delay (in microseconds) between each flip. It is recommended to set this value to at least `100_000`.
    /// * `per_char_flip_delay_us` - Used in [FlipStyle::Sequential] mode, this is the time (in microseconds) to wait between flipping each character
    fn split_flap_write(
        &mut self,
        str: &str,
        fs: FlipStyle,
        max_flip_cnt: u8,
        per_flip_delay_us: u32,
        per_char_flip_delay_us: Option<u32>,
    );

    /// Move the display window to the specified position (measured from the upper-left corner of the display)
    ///
    /// # Arguments
    ///
    /// * `target_pos` - The target position of the display window
    /// * `ms` - The style of movement, see [MoveStyle]
    /// * `display_state_when_shift` - Whether to turn off the screen during the move
    /// * `delay_us_per_step` - The delay (in microseconds) between each step of the move
    fn shift_display_to_pos(
        &mut self,
        target_pos: u8,
        ms: MoveStyle,
        display_state_when_shift: State,
        delay_us_per_step: u32,
    );

    /// Wait for specified milliseconds
    fn delay_ms(&mut self, ms: u32);

    /// Wait for specified microseconds
    fn delay_us(&mut self, us: u32);
}

/// [LCDExt] traits provide common non-animation display methods
pub trait LCDExt {
    /// toggle entire display on and off (it doesn't toggle backlight)
    fn toggle_display(&mut self);
    /// write [char] to current position
    fn write_char_to_cur(&mut self, char: char);
    /// write string to current position
    fn write_str_to_cur(&mut self, str: &str);
    /// write a byte to specific position
    fn write_byte_to_pos(&mut self, byte: impl Into<u8>, pos: (u8, u8));
    /// read a byte from specific position
    fn read_byte_from_pos(&mut self, pos: (u8, u8)) -> u8;
    /// write a char to specific position
    fn write_char_to_pos(&mut self, char: char, pos: (u8, u8));
    /// write string to specific position
    fn write_str_to_pos(&mut self, str: &str, pos: (u8, u8));
    /// write custom graph to specific position
    fn write_graph_to_pos(&mut self, index: u8, pos: (u8, u8));
    // read custom graph data from CGRAM
    fn read_graph_from_cgram(&mut self, index: u8) -> [u8; 8];
    // change cursor position with relative offset
    fn offset_cursor_pos(&mut self, offset: (i8, i8));
}

/// [LCDBasic] traits provide methods that close to LCD1602 instructions
pub trait LCDBasic {
    fn write_u8_to_cur(&mut self, byte: impl Into<u8>);
    fn read_u8_from_cur(&mut self) -> u8;
    fn write_graph_to_cgram(&mut self, index: u8, graph: &[u8; 8]);
    fn write_graph_to_cur(&mut self, index: u8);
    fn clean_display(&mut self);
    fn return_home(&mut self);
    fn set_line_mode(&mut self, line: LineMode);
    fn get_line_mode(&self) -> LineMode;
    fn set_font(&mut self, font: Font);
    fn get_font(&self) -> Font;
    fn set_display_state(&mut self, display: State);
    fn get_display_state(&self) -> State;
    fn set_cursor_state(&mut self, cursor: State);
    fn get_cursor_state(&self) -> State;
    fn get_ram_type(&self) -> RAMType;
    fn set_cursor_blink_state(&mut self, blink: State);
    fn get_cursor_blink_state(&self) -> State;
    fn set_default_direction(&mut self, dir: MoveDirection);
    fn get_default_direction(&self) -> MoveDirection;
    fn set_default_shift_type(&mut self, shift: ShiftType);
    fn get_default_shift_type(&self) -> ShiftType;
    fn set_cursor_pos(&mut self, pos: (u8, u8));
    fn set_cgram_addr(&mut self, addr: u8);
    fn get_cursor_pos(&self) -> (u8, u8);
    fn shift_cursor_or_display(&mut self, shift_type: ShiftType, dir: MoveDirection);
    fn get_display_offset(&self) -> u8;
    fn set_wait_interval_us(&mut self, interval: u32);
    fn get_wait_interval_us(&self) -> u32;
    fn get_line_capacity(&self) -> u8;
}

trait StructAPI {
    fn internal_init_lcd(&mut self);
    fn internal_set_line_mode(&mut self, line: LineMode);
    fn internal_set_font(&mut self, font: Font);
    fn internal_set_display_state(&mut self, display: State);
    fn internal_set_cursor_state(&mut self, cursor: State);
    fn internal_set_cursor_pos(&mut self, pos: (u8, u8));
    fn internal_set_ram_type(&mut self, ram_type: RAMType);
    fn internal_set_cursor_blink(&mut self, blink: State);
    fn internal_set_direction(&mut self, dir: MoveDirection);
    fn internal_set_shift(&mut self, shift: ShiftType);
    fn internal_set_display_offset(&mut self, offset: u8);
    fn internal_shift_cursor_or_display(&mut self, st: ShiftType, dir: MoveDirection);
    fn internal_calculate_pos_by_offset(&self, offset: (i8, i8)) -> (u8, u8);
}

trait StructUtils {
    fn calculate_pos_by_offset(&self, original_pos: (u8, u8), offset: (i8, i8)) -> (u8, u8);
}

trait PinsInteraction {
    fn delay_and_send(&mut self, command: impl Into<FullCommand>, wait_ms: u32) -> Option<u8>;
    fn wait_and_send(&mut self, command: impl Into<FullCommand>) -> Option<u8>;
    fn wait_for_idle(&mut self);
    fn check_busy(&mut self) -> bool;
}