Skip to main content

hcms_29xx/
lib.rs

1#![no_std]
2
3mod control_word;
4mod font5x7;
5
6pub use control_word::PeakCurrent;
7use control_word::*;
8use core::cell::RefCell;
9use embedded_hal::digital::{ErrorType, OutputPin};
10pub use font5x7::FONT5X7;
11use num_traits::{ToPrimitive, Zero};
12
13pub const CHAR_HEIGHT: usize = 7;
14pub const CHAR_WIDTH: usize = 5;
15const DEVICE_CHARS: u8 = 4;
16
17pub struct UnconfiguredPin;
18
19impl OutputPin for UnconfiguredPin {
20    fn set_low(&mut self) -> Result<(), Self::Error> {
21        Ok(())
22    }
23
24    fn set_high(&mut self) -> Result<(), Self::Error> {
25        Ok(())
26    }
27}
28
29impl ErrorType for UnconfiguredPin {
30    type Error = core::convert::Infallible;
31}
32
33#[derive(Debug)]
34pub enum Hcms29xxError<PinErr> {
35    PinNotConfigured,
36    ValueTooLong,
37    DataPinError(PinErr),
38    RsPinError(PinErr),
39    ClkPinError(PinErr),
40    CePinError(PinErr),
41    BlankPinError(PinErr),
42    OscSelPinError(PinErr),
43    ResetPinError(PinErr),
44}
45
46pub struct Hcms29xx<
47    const NUM_CHARS: usize,
48    DataPin,
49    RsPin,
50    ClkPin,
51    CePin,
52    BlankPin = UnconfiguredPin,
53    OscSelPin = UnconfiguredPin,
54    ResetPin = UnconfiguredPin,
55> where
56    DataPin: OutputPin,
57    RsPin: OutputPin,
58    ClkPin: OutputPin,
59    CePin: OutputPin,
60    BlankPin: OutputPin,
61    OscSelPin: OutputPin,
62    ResetPin: OutputPin,
63{
64    data: RefCell<DataPin>,
65    rs: RefCell<RsPin>,
66    clk: RefCell<ClkPin>,
67    ce: RefCell<CePin>,
68    blank: RefCell<BlankPin>,
69    osc_sel: RefCell<OscSelPin>,
70    reset: RefCell<ResetPin>,
71    control_word_0: ControlWord0,
72    control_word_1: ControlWord1,
73    // state kept locally to simplify/reduce overall code size
74    data_out_mode: DataOutMode,
75    font_ascii_start_index: u8,
76}
77
78impl<
79        const NUM_CHARS: usize,
80        DataPin,
81        RsPin,
82        ClkPin,
83        CePin,
84        BlankPin,
85        OscSelPin,
86        ResetPin,
87        PinErr,
88    > Hcms29xx<NUM_CHARS, DataPin, RsPin, ClkPin, CePin, BlankPin, OscSelPin, ResetPin>
89where
90    DataPin: OutputPin + ErrorType<Error = PinErr>,
91    RsPin: OutputPin + ErrorType<Error = PinErr>,
92    ClkPin: OutputPin + ErrorType<Error = PinErr>,
93    CePin: OutputPin + ErrorType<Error = PinErr>,
94    BlankPin: OutputPin + ErrorType<Error = PinErr>,
95    OscSelPin: OutputPin + ErrorType<Error = PinErr>,
96    ResetPin: OutputPin + ErrorType<Error = PinErr>,
97{
98    const _ASSERT_MIN_CHARS: () = assert!(NUM_CHARS >= 4, "NUM_CHARS must be at least 4");
99
100    pub fn new(
101        data: DataPin,
102        rs: RsPin,
103        clk: ClkPin,
104        ce: CePin,
105        blank: BlankPin,
106        osc_sel: OscSelPin,
107        reset: ResetPin,
108    ) -> Result<Self, Hcms29xxError<PinErr>> {
109        let data_ref_cell = RefCell::new(data);
110        let rs_ref_cell = RefCell::new(rs);
111        let clk_ref_cell = RefCell::new(clk);
112        let ce_ref_cell = RefCell::new(ce);
113        let blank_ref_cell = RefCell::new(blank);
114        let osc_sel_ref_cell = RefCell::new(osc_sel);
115        let reset_ref_cell = RefCell::new(reset);
116
117        data_ref_cell
118            .borrow_mut()
119            .set_low()
120            .map_err(Hcms29xxError::DataPinError)?;
121        ce_ref_cell
122            .borrow_mut()
123            .set_high()
124            .map_err(Hcms29xxError::CePinError)?;
125        blank_ref_cell
126            .borrow_mut()
127            .set_high()
128            .map_err(Hcms29xxError::BlankPinError)?;
129        // default to internal oscillator, user can set ext osc if needed
130        osc_sel_ref_cell
131            .borrow_mut()
132            .set_high()
133            .map_err(Hcms29xxError::OscSelPinError)?;
134        reset_ref_cell
135            .borrow_mut()
136            .set_high()
137            .map_err(Hcms29xxError::ResetPinError)?;
138
139        Ok(Hcms29xx {
140            data: data_ref_cell,
141            rs: rs_ref_cell,
142            clk: clk_ref_cell,
143            ce: ce_ref_cell,
144            blank: blank_ref_cell,
145            osc_sel: osc_sel_ref_cell,
146            reset: reset_ref_cell,
147            control_word_0: ControlWord0::default(),
148            control_word_1: ControlWord1::default(),
149            data_out_mode: DataOutMode::Serial,
150            #[cfg(feature = "avr-progmem")]
151            font_ascii_start_index: FONT5X7.load_at(0) - 1,
152            #[cfg(not(feature = "avr-progmem"))]
153            font_ascii_start_index: FONT5X7[0] - 1,
154        })
155    }
156
157    pub fn destroy(self) -> (DataPin, RsPin, ClkPin, CePin, BlankPin, OscSelPin, ResetPin) {
158        (
159            self.data.into_inner(),
160            self.rs.into_inner(),
161            self.clk.into_inner(),
162            self.ce.into_inner(),
163            self.blank.into_inner(),
164            self.osc_sel.into_inner(),
165            self.reset.into_inner(),
166        )
167    }
168
169    pub fn begin(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
170        self.clear()?;
171
172        self.update_control_word(self.control_word_0.bits())?;
173        self.update_control_word(self.control_word_1.bits())?;
174
175        Ok(())
176    }
177
178    pub fn clear(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
179        self.set_dot_data()?;
180        for _ in 0..NUM_CHARS * CHAR_WIDTH {
181            self.send_byte(0x00)?;
182        }
183        self.end_transfer()?;
184        Ok(())
185    }
186
187    pub fn print_ascii_bytes(&mut self, bytes: &[u8]) -> Result<(), Hcms29xxError<PinErr>> {
188        self.set_dot_data()?;
189        for i in 0..NUM_CHARS {
190            if i >= bytes.len() || bytes[i] < self.font_ascii_start_index {
191                break;
192            }
193            let char_index: usize = (bytes[i] - self.font_ascii_start_index) as usize * CHAR_WIDTH;
194            for col in 0..CHAR_WIDTH {
195                #[cfg(feature = "avr-progmem")]
196                self.send_byte(FONT5X7.load_at(char_index + col))?;
197                #[cfg(not(feature = "avr-progmem"))]
198                self.send_byte(FONT5X7[char_index + col])?;
199            }
200        }
201        self.end_transfer()?;
202        Ok(())
203    }
204
205    pub fn print_cols(&mut self, cols: &[u8]) -> Result<(), Hcms29xxError<PinErr>> {
206        self.set_dot_data()?;
207        for &byte in cols {
208            self.send_byte(byte)?;
209        }
210        self.end_transfer()?;
211        Ok(())
212    }
213
214    pub fn print_int<T>(&mut self, value: T) -> Result<(), Hcms29xxError<PinErr>>
215    where
216        T: Copy + Zero + ToPrimitive,
217    {
218        // avoid using 64-bit arithmetic for smaller displays
219        if NUM_CHARS <= 10 {
220            self.print_int_32bit(value)
221        } else {
222            self.print_int_64bit(value)
223        }
224    }
225
226    fn print_int_32bit<T>(&mut self, value: T) -> Result<(), Hcms29xxError<PinErr>>
227    where
228        T: Copy + Zero + ToPrimitive,
229    {
230        const fn max_unsigned_for_chars(chars: usize) -> u32 {
231            let mut power_of_10 = 1u32;
232            let mut i = 0;
233            while i < chars {
234                power_of_10 = power_of_10.saturating_mul(10);
235                i += 1;
236            }
237            power_of_10.saturating_sub(1)
238        }
239
240        const fn max_signed_for_chars(chars: usize) -> i32 {
241            max_unsigned_for_chars(chars - 1) as i32
242        }
243
244        let (mut has_sign, mut value) = if let Some(signed_val) = value.to_i32() {
245            let max_signed = max_signed_for_chars(NUM_CHARS);
246            if signed_val < -max_signed || signed_val > max_signed {
247                return Err(Hcms29xxError::ValueTooLong);
248            }
249            if signed_val < 0 {
250                (true, (-signed_val) as u32)
251            } else {
252                (false, signed_val as u32)
253            }
254        } else if let Some(unsigned_val) = value.to_u32() {
255            let max_unsigned = max_unsigned_for_chars(NUM_CHARS);
256            if unsigned_val > max_unsigned {
257                return Err(Hcms29xxError::ValueTooLong);
258            }
259            (false, unsigned_val)
260        } else {
261            return Err(Hcms29xxError::ValueTooLong);
262        };
263
264        let mut buf = [b' '; NUM_CHARS];
265        buf[NUM_CHARS - 1] = b'0';
266
267        for index in (0..NUM_CHARS).rev() {
268            buf[index] = if value > 0 {
269                let digit = b'0' + (value % 10) as u8;
270                value /= 10;
271                digit
272            } else if has_sign {
273                has_sign = false;
274                b'-'
275            } else {
276                break;
277            };
278        }
279
280        self.print_ascii_bytes(&buf)
281    }
282
283    fn print_int_64bit<T>(&mut self, value: T) -> Result<(), Hcms29xxError<PinErr>>
284    where
285        T: Copy + Zero + ToPrimitive,
286    {
287        const fn max_unsigned_64_for_chars(chars: usize) -> u64 {
288            let mut power_of_10 = 1u64;
289            let mut i = 0;
290            while i < chars {
291                power_of_10 = power_of_10.saturating_mul(10);
292                i += 1;
293            }
294            power_of_10.saturating_sub(1)
295        }
296
297        const fn max_signed_64_for_chars(chars: usize) -> i64 {
298            max_unsigned_64_for_chars(chars - 1) as i64
299        }
300
301        let (mut has_sign, mut value) = if let Some(signed_val) = value.to_i64() {
302            let max_signed = max_signed_64_for_chars(NUM_CHARS);
303            if signed_val < -max_signed || signed_val > max_signed {
304                return Err(Hcms29xxError::ValueTooLong);
305            }
306            if signed_val < 0 {
307                (true, (-signed_val) as u64)
308            } else {
309                (false, signed_val as u64)
310            }
311        } else if let Some(unsigned_val) = value.to_u64() {
312            let max_unsigned = max_unsigned_64_for_chars(NUM_CHARS);
313            if unsigned_val > max_unsigned {
314                return Err(Hcms29xxError::ValueTooLong);
315            }
316            (false, unsigned_val)
317        } else {
318            return Err(Hcms29xxError::ValueTooLong);
319        };
320
321        let mut buf = [b' '; NUM_CHARS];
322        buf[NUM_CHARS - 1] = b'0';
323
324        for index in (0..NUM_CHARS).rev() {
325            buf[index] = if value > 0 {
326                let digit = b'0' + (value % 10) as u8;
327                value /= 10;
328                digit
329            } else if has_sign {
330                has_sign = false;
331                b'-'
332            } else {
333                break;
334            };
335        }
336
337        self.print_ascii_bytes(&buf)
338    }
339
340    #[cfg(feature = "print_float")]
341    pub fn print_float<T>(&mut self, value: T, precision: u8) -> Result<(), Hcms29xxError<PinErr>>
342    where
343        T: Copy + ToPrimitive,
344    {
345        let float_val = value.to_f32().ok_or(Hcms29xxError::ValueTooLong)?;
346        let mut buf = [b' '; NUM_CHARS];
347
348        // special values
349        if float_val.is_nan() {
350            buf[(NUM_CHARS - 3)..].copy_from_slice(b"NaN");
351            return self.print_ascii_bytes(&buf);
352        }
353        if float_val.is_infinite() {
354            let inf_str = if float_val.is_sign_positive() {
355                b"+Inf"
356            } else {
357                b"-Inf"
358            };
359            buf[(NUM_CHARS - inf_str.len())..].copy_from_slice(inf_str);
360            return self.print_ascii_bytes(&buf);
361        }
362
363        let is_negative = float_val.is_sign_negative();
364        let abs_val = float_val.abs();
365
366        // check value bounds
367        let integer_digits = if abs_val < 1.0 {
368            1
369        } else {
370            let mut temp = abs_val as u32;
371            let mut digits = 0;
372            while temp > 0 {
373                digits += 1;
374                temp /= 10;
375            }
376            digits
377        };
378        let total_chars = (if is_negative { 1 } else { 0 })
379            + integer_digits
380            + (if precision > 0 { 1 } else { 0 })
381            + precision as usize;
382        if total_chars > NUM_CHARS {
383            return Err(Hcms29xxError::ValueTooLong);
384        }
385
386        // scale number to integer value for formatting
387        let mut pos = NUM_CHARS;
388        let mut scale_factor = 1.0f32;
389        for _ in 0..precision {
390            scale_factor *= 10.0;
391        }
392        let rounded_val = abs_val * scale_factor + 0.5;
393        let mut digits = rounded_val as u32;
394
395        for _ in 0..precision {
396            pos -= 1;
397            buf[pos] = b'0' + (digits % 10) as u8;
398            digits /= 10;
399        }
400
401        if precision > 0 {
402            pos -= 1;
403            buf[pos] = b'.';
404        }
405
406        if digits == 0 {
407            pos -= 1;
408            buf[pos] = b'0';
409        } else {
410            while digits > 0 {
411                pos -= 1;
412                buf[pos] = b'0' + (digits % 10) as u8;
413                digits /= 10;
414            }
415        }
416
417        if is_negative {
418            pos -= 1;
419            buf[pos] = b'-';
420        }
421
422        self.print_ascii_bytes(&buf)
423    }
424
425    #[deprecated(since = "0.2.0", note = "Use print_int instead")]
426    pub fn print_i32(&mut self, value: i32) -> Result<(), Hcms29xxError<PinErr>> {
427        self.print_int_32bit(value)
428    }
429
430    #[deprecated(since = "0.2.0", note = "Use print_int instead")]
431    pub fn print_u32(&mut self, value: u32) -> Result<(), Hcms29xxError<PinErr>> {
432        self.print_int_32bit(value)
433    }
434
435    pub fn display_blank(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
436        self.blank
437            .borrow_mut()
438            .set_high()
439            .map_err(Hcms29xxError::BlankPinError)?;
440        Ok(())
441    }
442
443    pub fn display_sleep(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
444        self.control_word_0.set_wake_bit(SleepMode::Sleep);
445        self.update_control_word(self.control_word_0.bits())?;
446        Ok(())
447    }
448
449    pub fn display_wake(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
450        self.control_word_0.set_wake_bit(SleepMode::Normal);
451        self.update_control_word(self.control_word_0.bits())?;
452        Ok(())
453    }
454
455    pub fn display_unblank(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
456        self.blank
457            .borrow_mut()
458            .set_low()
459            .map_err(Hcms29xxError::BlankPinError)?;
460        Ok(())
461    }
462
463    pub fn reset(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
464        self.reset
465            .borrow_mut()
466            .set_low()
467            .map_err(Hcms29xxError::ResetPinError)?;
468        self.reset
469            .borrow_mut()
470            .set_high()
471            .map_err(Hcms29xxError::ResetPinError)?;
472        Ok(())
473    }
474
475    pub fn set_brightness(&mut self, brightness: u8) -> Result<(), Hcms29xxError<PinErr>> {
476        self.control_word_0.set_brightness_bits(brightness);
477        self.update_control_word(self.control_word_0.bits())?;
478        Ok(())
479    }
480
481    pub fn set_peak_current(&mut self, current: PeakCurrent) -> Result<(), Hcms29xxError<PinErr>> {
482        self.control_word_0.set_peak_current_bits(current);
483        self.update_control_word(self.control_word_0.bits())?;
484        Ok(())
485    }
486
487    pub fn set_ext_osc_prescale_direct(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
488        self.control_word_1
489            .set_ext_osc_prescaler_bit(ExtOscPrescaler::Direct);
490        self.update_control_word(self.control_word_1.bits())?;
491        Ok(())
492    }
493
494    pub fn set_ext_osc_prescale_div8(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
495        self.control_word_1
496            .set_ext_osc_prescaler_bit(ExtOscPrescaler::Div8);
497        self.update_control_word(self.control_word_1.bits())?;
498        Ok(())
499    }
500
501    pub fn set_ext_osc(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
502        self.osc_sel
503            .borrow_mut()
504            .set_low()
505            .map_err(Hcms29xxError::OscSelPinError)?;
506        Ok(())
507    }
508
509    pub fn set_int_osc(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
510        self.osc_sel
511            .borrow_mut()
512            .set_high()
513            .map_err(Hcms29xxError::OscSelPinError)?;
514        Ok(())
515    }
516
517    pub fn set_serial_data_out(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
518        self.control_word_1
519            .set_data_out_mode_bit(DataOutMode::Serial);
520        self.update_control_word(self.control_word_1.bits())?;
521
522        // update local state once change is sent to device
523        self.data_out_mode = DataOutMode::Serial;
524
525        Ok(())
526    }
527
528    pub fn set_simultaneous_data_out(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
529        self.control_word_1
530            .set_data_out_mode_bit(DataOutMode::Simultaneous);
531        self.update_control_word(self.control_word_1.bits())?;
532
533        // update local state once change is sent to device
534        self.data_out_mode = DataOutMode::Simultaneous;
535
536        Ok(())
537    }
538
539    fn update_control_word(&mut self, control_word: u8) -> Result<(), Hcms29xxError<PinErr>> {
540        let times_to_send = if self.data_out_mode == DataOutMode::Serial {
541            NUM_CHARS as u8 / DEVICE_CHARS
542        } else {
543            1
544        };
545
546        self.set_control_data()?;
547        for _ in 0..times_to_send {
548            self.send_byte(control_word)?;
549        }
550        self.end_transfer()?;
551
552        Ok(())
553    }
554
555    fn set_dot_data(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
556        self.clk
557            .borrow_mut()
558            .set_high()
559            .map_err(Hcms29xxError::ClkPinError)?;
560        self.rs
561            .borrow_mut()
562            .set_low()
563            .map_err(Hcms29xxError::RsPinError)?;
564        self.ce
565            .borrow_mut()
566            .set_low()
567            .map_err(Hcms29xxError::CePinError)?;
568        Ok(())
569    }
570
571    fn set_control_data(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
572        self.clk
573            .borrow_mut()
574            .set_high()
575            .map_err(Hcms29xxError::ClkPinError)?;
576        self.rs
577            .borrow_mut()
578            .set_high()
579            .map_err(Hcms29xxError::RsPinError)?;
580        self.ce
581            .borrow_mut()
582            .set_low()
583            .map_err(Hcms29xxError::CePinError)?;
584        Ok(())
585    }
586
587    fn send_byte(&mut self, byte: u8) -> Result<(), Hcms29xxError<PinErr>> {
588        for i in 0..8 {
589            self.clk
590                .borrow_mut()
591                .set_low()
592                .map_err(Hcms29xxError::ClkPinError)?;
593            if (byte & (1 << (7 - i))) != 0 {
594                self.data
595                    .borrow_mut()
596                    .set_high()
597                    .map_err(Hcms29xxError::DataPinError)?;
598            } else {
599                self.data
600                    .borrow_mut()
601                    .set_low()
602                    .map_err(Hcms29xxError::DataPinError)?;
603            }
604            self.clk
605                .borrow_mut()
606                .set_high()
607                .map_err(Hcms29xxError::ClkPinError)?;
608        }
609        Ok(())
610    }
611
612    fn end_transfer(&mut self) -> Result<(), Hcms29xxError<PinErr>> {
613        self.ce
614            .borrow_mut()
615            .set_high()
616            .map_err(Hcms29xxError::CePinError)?;
617        self.clk
618            .borrow_mut()
619            .set_low()
620            .map_err(Hcms29xxError::ClkPinError)?;
621        Ok(())
622    }
623}