st7032i/
lib.rs

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        // 0b0010_0000 ~ 0b0111_1101
310        Character::Char(c @ ' '..='}') => Some(c as u8),
311
312        // 0b1010_0001 ~ 0b1101_1111
313        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        // chars と要素数が同じで 0b11_000000 だけの Iterator を作る
329        //                          │└──── C0: 最終データのとき0、それ以外のとき1
330        //                          └───── Rs: Data register の読み書きのとき1
331        .clone()
332        .map(|_| 0b11_000000)
333        // 最後だけ 0b01_000000 にする
334        .skip(1)
335        .chain(core::iter::once(0b01_000000))
336        // ↑ で作った control byte の Iterator と chars を zip
337        // chars は ST7032 のコードに変換 未対応の文字はスペースに置換
338        .zip(chars)
339        // control byte, data byte の順で交互に出力
340        .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}