1use core::marker::PhantomData;
2
3use super::address::Address;
4use super::{DisplayControlBuilder, EntryModeBuilder, FunctionSetBuilder, Home};
5use hal::{Init, ReadMode, Receive, Send, WriteMode};
6
7const LCD_WIDTH: usize = 16;
8
9bitflags! {
10 struct Instructions: u8 {
11 const CLEAR_DISPLAY = 0b0000_0001;
12 const RETURN_HOME = 0b0000_0010;
13 const SHIFT = 0b0001_0000;
14 }
15}
16
17bitflags! {
18 struct ShiftTarget: u8 {
19 const CURSOR = 0b0000_0000;
20 const DISPLAY = 0b0000_1000;
21 }
22}
23
24bitflags! {
25 struct ShiftDirection: u8 {
26 const RIGHT = 0b0000_0100;
27 const LEFT = 0b0000_0000;
28 }
29}
30
31enum RamType {
32 DisplayData,
33 CharacterGenerator,
34}
35
36impl From<RamType> for u8 {
37 fn from(ram_type: RamType) -> Self {
38 match ram_type {
39 RamType::DisplayData => 0b1000_0000,
40 RamType::CharacterGenerator => 0b0100_0000,
41 }
42 }
43}
44
45pub enum ShiftTo {
47 Right(u8),
49 Left(u8),
51}
52
53impl ShiftTo {
54 fn as_offset_and_raw_direction(&self) -> (u8, ShiftDirection) {
55 match *self {
56 ShiftTo::Right(offset) => (offset, ShiftDirection::RIGHT),
57 ShiftTo::Left(offset) => (offset, ShiftDirection::LEFT),
58 }
59 }
60}
61
62pub enum SeekFrom<T: Into<Address>> {
64 Home(u8),
66 Current(u8),
68 Line { line: T, bytes: u8 },
70}
71
72pub struct Display<P, U>
77where
78 U: Into<Address> + Home,
79{
80 connection: P,
81 cursor_address: Address,
82 _line_marker: PhantomData<U>,
83}
84
85impl<P, U> Display<P, U>
86where
87 P: Init + Send + Receive,
88 U: Into<Address> + Home,
89{
90 const FIRST_4BIT_INIT_INSTRUCTION: WriteMode = WriteMode::Command(0x33);
91 const SECOND_4BIT_INIT_INSTRUCTION: WriteMode = WriteMode::Command(0x32);
92
93 pub fn new(connection: P) -> Self {
95 Display {
96 connection: connection,
97 cursor_address: Address::from(0),
98 _line_marker: PhantomData,
99 }
100 }
101
102 pub fn init(&self, builder: &FunctionSetBuilder) {
103 self.connection.init();
104
105 let cmd = builder.build_command();
106 let cmd = WriteMode::Command(cmd);
107
108 self.init_by_instruction(cmd);
109 }
110
111 fn init_by_instruction(&self, function_set: WriteMode) {
112 self.connection.send(Self::FIRST_4BIT_INIT_INSTRUCTION);
113 self.connection.send(Self::SECOND_4BIT_INIT_INSTRUCTION);
114
115 self.connection.send(function_set);
116
117 self.clear();
118 }
119
120 pub fn set_entry_mode(&self, builder: &EntryModeBuilder) {
122 let cmd = WriteMode::Command(builder.build_command());
123 self.connection.send(cmd);
124 }
125
126 pub fn set_display_control(&self, builder: &DisplayControlBuilder) {
128 let cmd = WriteMode::Command(builder.build_command());
129 self.connection.send(cmd);
130 }
131
132 pub fn shift_cursor(&mut self, direction: ShiftTo) {
136 let (offset, raw_direction) = direction.as_offset_and_raw_direction();
137
138 if offset == 0 {
139 return;
140 }
141
142 match direction {
143 ShiftTo::Right(offset) => self.cursor_address += offset.into(),
144 ShiftTo::Left(offset) => self.cursor_address -= offset.into(),
145 }
146
147 self.raw_shift(ShiftTarget::CURSOR, offset, raw_direction);
148 }
149
150 pub fn shift(&self, direction: ShiftTo) {
157 let (offset, raw_direction) = direction.as_offset_and_raw_direction();
158
159 self.raw_shift(ShiftTarget::DISPLAY, offset, raw_direction);
160 }
161
162 fn raw_shift(&self, shift_type: ShiftTarget, offset: u8, raw_direction: ShiftDirection) {
163 let mut cmd = Instructions::SHIFT.bits();
164
165 cmd |= shift_type.bits();
166 cmd |= raw_direction.bits();
167
168 for _ in 0..offset {
169 self.connection.send(WriteMode::Command(cmd));
170 }
171 }
172
173 pub fn clear(&self) {
178 let cmd = Instructions::CLEAR_DISPLAY.bits();
179 self.connection.send(WriteMode::Command(cmd));
180 }
181
182 fn generic_seek(&mut self, ram_type: RamType, pos: SeekFrom<U>) {
183 let mut cmd = ram_type.into();
184
185 let (start, addr) = match pos {
186 SeekFrom::Home(bytes) => (U::FIRST_LINE_ADDRESS.into(), bytes.into()),
187 SeekFrom::Current(bytes) => (self.cursor_address, bytes.into()),
188 SeekFrom::Line { line, bytes } => (line.into(), bytes.into()),
189 };
190
191 self.cursor_address = start + addr;
192
193 cmd |= u8::from(self.cursor_address);
194
195 self.connection.send(WriteMode::Command(cmd));
196 }
197
198 pub fn seek(&mut self, pos: SeekFrom<U>) {
200 self.generic_seek(RamType::DisplayData, pos);
201 }
202
203 pub fn seek_cgram(&mut self, pos: SeekFrom<U>) {
205 self.generic_seek(RamType::CharacterGenerator, pos);
206 }
207
208 pub fn write(&mut self, c: u8) {
211 self.cursor_address += Address::from(1);
212 self.connection.send(WriteMode::Data(c));
213 }
214
215 pub fn read_byte(&mut self) -> u8 {
217 self.cursor_address += Address::from(1);
218 self.connection.receive(ReadMode::Data)
219 }
220
221 pub fn read_busy_flag(&self) -> (bool, u8) {
223 let byte = self.connection.receive(ReadMode::BusyFlag);
224
225 let busy_flag = (byte & 0b1000_0000) != 0;
226
227 let address = byte & 0b0111_1111;
228
229 (busy_flag, address)
230 }
231
232 pub fn write_message(&mut self, msg: &str) {
235 for c in msg.as_bytes().iter().take(LCD_WIDTH) {
236 self.write(*c);
237 }
238 }
239
240 pub fn get_connection(self) -> P {
241 self.connection
242 }
243}