1#![no_std]
2
3use embedded_hal_0_2_x::blocking::i2c::{AddressMode, Write, WriteIter};
4use fugit::{Duration, NanosDurationU32};
5
6pub struct One {}
7pub struct Two {}
8
9pub trait DisplayRows {
10 fn value() -> u8;
11}
12
13impl DisplayRows for One {
14 fn value() -> u8 {
15 0b0000
16 }
17}
18
19impl DisplayRows for Two {
20 fn value() -> u8 {
21 0b1000
22 }
23}
24
25pub struct Single {}
26pub struct Double {}
27
28pub trait CharacterHeight {
29 fn value() -> u8;
30}
31
32impl CharacterHeight for Single {
33 fn value() -> u8 {
34 0b0000
35 }
36}
37
38impl CharacterHeight for Double {
39 fn value() -> u8 {
40 0b0100
41 }
42}
43
44pub struct Normal {}
45pub struct Extention {}
46pub struct Unknown {}
47
48pub trait InstructionSet {
49 fn value() -> u8;
50}
51
52impl InstructionSet for Normal {
53 fn value() -> u8 {
54 0b0000_0000
55 }
56}
57
58impl InstructionSet for Extention {
59 fn value() -> u8 {
60 0b0000_0001
61 }
62}
63
64#[derive(Copy, Clone, Eq, PartialEq, Debug)]
65pub enum Character {
66 Char(char),
67 Cgram(u8),
68}
69
70pub const DDRAM_ADDRESS_LINE1: u8 = 0x00;
71pub const DDRAM_ADDRESS_LINE2: u8 = 0x40;
72
73pub struct ExecutionTime(NanosDurationU32);
74
75impl<const NOM: u32, const DENOM: u32> From<ExecutionTime> for Duration<u32, NOM, DENOM> {
76 fn from(v: ExecutionTime) -> Duration<u32, NOM, DENOM> {
77 v.0.convert()
78 }
79}
80
81impl<const NOM: u32, const DENOM: u32> From<ExecutionTime> for Duration<u64, NOM, DENOM> {
82 fn from(v: ExecutionTime) -> Duration<u64, NOM, DENOM> {
83 v.0.convert().into()
84 }
85}
86
87pub const EXECUTION_TIME_SHORT: ExecutionTime = ExecutionTime(NanosDurationU32::nanos(26_300));
88pub const EXECUTION_TIME_LONG: ExecutionTime = ExecutionTime(NanosDurationU32::micros(1_080));
89
90pub struct St7032i<'a, I, W, A, D, N = Two, DH = Single>
91where
92 W: Write<A> + WriteIter<A>,
93 A: AddressMode + Copy,
94 D: From<ExecutionTime>,
95 N: DisplayRows,
96 DH: CharacterHeight,
97{
98 dev: &'a mut W,
99 addr: A,
100 phantom: core::marker::PhantomData<(I, D, N, DH)>,
101}
102
103impl<'a, W, A, D, N, DH> St7032i<'a, Unknown, W, A, D, N, DH>
104where
105 W: Write<A> + WriteIter<A>,
106 A: AddressMode + Copy,
107 D: From<ExecutionTime>,
108 N: DisplayRows,
109 DH: CharacterHeight,
110{
111 pub fn new(dev: &'a mut W, addr: A) -> Self {
112 Self {
113 dev,
114 addr,
115 phantom: core::marker::PhantomData,
116 }
117 }
118}
119
120impl<'a, I, W, A, D, N, DH> St7032i<'a, I, W, A, D, N, DH>
121where
122 W: Write<A> + WriteIter<A>,
123 A: AddressMode + Copy,
124 D: From<ExecutionTime>,
125 N: DisplayRows,
126 DH: CharacterHeight,
127{
128 #[allow(clippy::type_complexity)]
129 pub fn set_instruction_set<ITarget: InstructionSet>(
130 mut self,
131 ) -> Result<(St7032i<'a, ITarget, W, A, D, N, DH>, D), <W as Write<A>>::Error> {
132 let value = 0b0011_0000 | N::value() | DH::value() | ITarget::value();
133 self.write_raw(&[0b00_000000, value]).map(|_| {
134 (
135 St7032i::<ITarget, W, A, D, N, DH> {
136 dev: self.dev,
137 addr: self.addr,
138 phantom: core::marker::PhantomData,
139 },
140 EXECUTION_TIME_SHORT.into(),
141 )
142 })
143 }
144
145 pub fn write_raw(&mut self, data: &[u8]) -> Result<(), <W as Write<A>>::Error> {
146 Write::write(self.dev, self.addr, data)
147 }
148
149 pub fn write_iter_raw<B>(&mut self, data: B) -> Result<(), <W as WriteIter<A>>::Error>
150 where
151 B: IntoIterator<Item = u8>,
152 {
153 WriteIter::write(self.dev, self.addr, data)
154 }
155}
156
157impl<I, W, A, D, N, DH> St7032i<'_, I, W, A, D, N, DH>
158where
159 I: InstructionSet,
160 W: Write<A> + WriteIter<A>,
161 A: AddressMode + Copy,
162 D: From<ExecutionTime>,
163 N: DisplayRows,
164 DH: CharacterHeight,
165{
166 pub fn cmd_clear_display(&mut self) -> Result<D, <W as Write<A>>::Error> {
167 self.write_raw(&[0b00_000000, 0b0000_0001])
168 .map(|_| EXECUTION_TIME_LONG.into())
169 }
170
171 pub fn cmd_display_on_off(
172 &mut self,
173 display: bool,
174 cursor: bool,
175 cursor_blink: bool,
176 ) -> Result<D, <W as Write<A>>::Error> {
177 let mut value = 0b0000_1000;
178 if display {
179 value |= 0b100;
180 }
181 if cursor {
182 value |= 0b010;
183 }
184 if cursor_blink {
185 value |= 0b001;
186 }
187 self.write_raw(&[0b00_000000, value])
188 .map(|_| EXECUTION_TIME_SHORT.into())
189 }
190
191 pub fn cmd_set_ddram_address(&mut self, addr: u8) -> Result<D, <W as Write<A>>::Error> {
192 let value = 0b1000_0000 | (addr & 0b0111_1111);
193 self.write_raw(&[0b00_000000, value])
194 .map(|_| EXECUTION_TIME_SHORT.into())
195 }
196
197 pub fn cmd_write_chars<C>(&mut self, chars: C) -> Result<D, <W as WriteIter<A>>::Error>
198 where
199 C: IntoIterator<Item = Character>,
200 C::IntoIter: Iterator<Item = C::Item> + Clone,
201 {
202 self.write_iter_raw(stuff_control_bytes(
203 chars
204 .into_iter()
205 .map(|c| to_character_code(c).unwrap_or(0b0010_0000)),
206 ))
207 .map(|_| EXECUTION_TIME_SHORT.into())
208 }
209
210 pub fn cmd_write_bytes<C>(&mut self, bytes: C) -> Result<D, <W as WriteIter<A>>::Error>
211 where
212 C: IntoIterator<Item = u8>,
213 C::IntoIter: Iterator<Item = C::Item> + Clone,
214 {
215 self.write_iter_raw(stuff_control_bytes(bytes))
216 .map(|_| EXECUTION_TIME_SHORT.into())
217 }
218}
219
220impl<W, A, D, N, DH> St7032i<'_, Normal, W, A, D, N, DH>
221where
222 W: Write<A> + WriteIter<A>,
223 A: AddressMode + Copy,
224 D: From<ExecutionTime>,
225 N: DisplayRows,
226 DH: CharacterHeight,
227{
228 pub fn cmd_set_cgram_address(&mut self, addr: u8) -> Result<D, <W as Write<A>>::Error> {
229 let value = 0b0100_0000 | (addr & 0b011_1111);
230 self.write_raw(&[0b00_000000, value])
231 .map(|_| EXECUTION_TIME_SHORT.into())
232 }
233}
234
235impl<W, A, D, N, DH> St7032i<'_, Extention, W, A, D, N, DH>
236where
237 W: Write<A> + WriteIter<A>,
238 A: AddressMode + Copy,
239 D: From<ExecutionTime>,
240 N: DisplayRows,
241 DH: CharacterHeight,
242{
243 pub fn cmd_internal_osc_frequency(&mut self) -> Result<D, <W as Write<A>>::Error> {
244 self.write_raw(&[0b00_000000, 0b0001_0100])
245 .map(|_| EXECUTION_TIME_SHORT.into())
246 }
247
248 pub fn cmd_power_icon_contrast_set(
249 &mut self,
250 icon: bool,
251 booster: bool,
252 contrast_hi2: u8,
253 ) -> Result<D, <W as Write<A>>::Error> {
254 let mut value = 0b0101_0000 | (contrast_hi2 & 0b0011);
255 if icon {
256 value |= 0b1000;
257 }
258 if booster {
259 value |= 0b0100;
260 }
261 self.write_raw(&[0b00_000000, value])
262 .map(|_| EXECUTION_TIME_SHORT.into())
263 }
264
265 pub fn cmd_follower_control(
266 &mut self,
267 enable: bool,
268 ratio: u8,
269 ) -> Result<D, <W as Write<A>>::Error> {
270 let mut value = 0b0110_0000 | (ratio & 0b0111);
271 if enable {
272 value |= 0b1000;
273 }
274 self.write_raw(&[0b00_000000, value])
275 .map(|_| EXECUTION_TIME_SHORT.into())
276 }
277
278 pub fn cmd_contrast_set(&mut self, contrast_low4: u8) -> Result<D, <W as Write<A>>::Error> {
279 let value = 0b0111_0000 | (contrast_low4 & 0b0000_1111);
280 self.write_raw(&[0b00_000000, value])
281 .map(|_| EXECUTION_TIME_SHORT.into())
282 }
283}
284
285impl<W, A, D, N, DH> core::fmt::Write for St7032i<'_, Normal, W, A, D, N, DH>
286where
287 W: Write<A> + WriteIter<A>,
288 A: AddressMode + Copy,
289 D: From<ExecutionTime>,
290 N: DisplayRows,
291 DH: CharacterHeight,
292{
293 fn write_str(&mut self, s: &str) -> core::fmt::Result {
294 match self.cmd_write_chars(s.chars().map(Character::from)) {
295 Ok(_) => Ok(()),
296 Err(_) => Err(core::fmt::Error),
297 }
298 }
299}
300
301impl From<char> for Character {
302 fn from(c: char) -> Self {
303 Character::Char(c)
304 }
305}
306
307fn to_character_code(c: Character) -> Option<u8> {
308 match c {
309 Character::Char(c @ ' '..='}') => Some(c as u8),
311
312 Character::Char(c @ '。'..='゚') => Some((c as u16 - 0xfec0) as u8),
314
315 Character::Cgram(c @ 0..=7) => Some(c),
316
317 _ => None,
318 }
319}
320
321fn stuff_control_bytes<C>(chars: C) -> impl Iterator<Item = u8>
322where
323 C: IntoIterator<Item = u8>,
324 C::IntoIter: Iterator<Item = C::Item> + Clone,
325{
326 let chars = chars.into_iter();
327 chars
328 .clone()
332 .map(|_| 0b11_000000)
333 .skip(1)
335 .chain(core::iter::once(0b01_000000))
336 .zip(chars)
339 .flat_map(|c| [c.0, c.1])
341}
342
343#[cfg(test)]
344mod tests {
345 extern crate std;
346 use crate::*;
347
348 #[test]
349 fn test_to_character_code() {
350 assert_eq!(to_character_code(' '.into()), Some(0b0010_0000));
351 assert_eq!(to_character_code('!'.into()), Some(0b0010_0001));
352 assert_eq!(to_character_code('0'.into()), Some(0b0011_0000));
353 assert_eq!(to_character_code('@'.into()), Some(0b0100_0000));
354 assert_eq!(to_character_code('A'.into()), Some(0b0100_0001));
355 assert_eq!(to_character_code('`'.into()), Some(0b0110_0000));
356 assert_eq!(to_character_code('a'.into()), Some(0b0110_0001));
357 assert_eq!(to_character_code('}'.into()), Some(0b0111_1101));
358 assert_eq!(to_character_code('~'.into()), None);
359 assert_eq!(to_character_code('。'.into()), Some(0b1010_0001));
360 assert_eq!(to_character_code('ア'.into()), Some(0b1011_0001));
361 assert_eq!(to_character_code('゚'.into()), Some(0b1101_1111));
362
363 assert_eq!(to_character_code(Character::Cgram(0)), Some(0b0000_0000));
364 assert_eq!(to_character_code(Character::Cgram(7)), Some(0b0000_0111));
365 assert_eq!(to_character_code(Character::Cgram(8)), None);
366 }
367
368 #[test]
369 fn test_to_write_command() {
370 {
371 let mut iter = stuff_control_bytes([] as [u8; 0]);
372 assert_eq!(iter.next(), None);
373 }
374
375 {
376 let mut iter = stuff_control_bytes([0xa5]);
377 assert_eq!(iter.next(), Some(0b01_000000));
378 assert_eq!(iter.next(), Some(0xa5));
379 assert_eq!(iter.next(), None);
380 }
381
382 {
383 let mut iter = stuff_control_bytes([0xa5, 0x5a, 0xff]);
384 assert_eq!(iter.next(), Some(0b11_000000));
385 assert_eq!(iter.next(), Some(0xa5));
386 assert_eq!(iter.next(), Some(0b11_000000));
387 assert_eq!(iter.next(), Some(0x5a));
388 assert_eq!(iter.next(), Some(0b01_000000));
389 assert_eq!(iter.next(), Some(0xff));
390 assert_eq!(iter.next(), None);
391 }
392 }
393}