Skip to main content

ht16k33_lite/
lib.rs

1#![allow(clippy::manual_range_contains)]
2#![allow(non_upper_case_globals)]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5//! ht16k33-lite is a low level library for communicating with ht16k33
6//! controllers.
7//!
8//! <https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf>
9
10pub use embedded_hal::blocking::i2c::{self, Read, Write};
11pub use enum_index::IndexEnum;
12use enum_index_derive::{EnumIndex, IndexEnum};
13
14pub const SEGMENTS_SIZE: usize = 16;
15pub const COMMONS_SIZE: usize  = 8;
16
17/// Trait for holding a command mask.
18///
19/// See <https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf#HT16K33v110_110516.indd%3A.134493>
20/// for more information.
21pub trait Command { const COMMAND_MASK: u8; }
22
23/// [System Setup Register](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf#HT16K33v110_110516.indd%3A.134352)
24#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
25#[repr(u8)]
26pub enum SystemSetupRegister {
27    /// Oscillator off
28    StandBy = Self::COMMAND_MASK | 0,
29    /// Oscillator on
30    Normal  = Self::COMMAND_MASK | 1,
31}
32
33impl Command for SystemSetupRegister {
34    /// Write only
35    const COMMAND_MASK: u8 = 0b0010_0000; 
36}
37
38/// Alias for System Setup Register.
39pub type System = SystemSetupRegister;
40
41/// [ROW/INT Set Register](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf#HT16K33v110_110516.indd%3A.134356)
42#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
43#[repr(u8)]
44pub enum RowIntSetupRegister {
45    /// Row driver output
46    Row     = Self::COMMAND_MASK | 0b0000,
47    /// Int output, active low
48    IntLow  = Self::COMMAND_MASK | 0b0001,
49    /// Int output, active high
50    IntHigh = Self::COMMAND_MASK | 0b0011,
51}
52
53impl Command for RowIntSetupRegister {
54    /// Write only
55    const COMMAND_MASK: u8 = 0b1010_0000;
56}
57
58/// Alias for ROW/INT Setup Register.
59pub type RowInt = RowIntSetupRegister;
60
61/// [Display Setup Register](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf#HT16K33v110_110516.indd%3A.134360)
62#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
63#[repr(u8)]
64pub enum DisplaySetupRegister {
65    /// Display off
66    Off     = Self::COMMAND_MASK | 0b0000,
67    /// Display on
68    On      = Self::COMMAND_MASK | 0b0001,
69    /// Display on + Blinking frequency = 2hz
70    TwoHz   = Self::COMMAND_MASK | 0b0011,
71    /// Display on + Blinking frequency = 1hz
72    OneHz   = Self::COMMAND_MASK | 0b0101,
73    /// Display on + Blinking frequency = 0.5hz
74    HalfHz  = Self::COMMAND_MASK | 0b0111,
75}
76
77impl Command for DisplaySetupRegister {
78    /// Write only
79    const COMMAND_MASK: u8 = 0b1000_0000; 
80}
81
82/// Alias for Display Setup Register.
83pub type Display = DisplaySetupRegister;
84
85/// [Digital Dimming Data Input](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf#HT16K33v110_110516.indd%3A.134397)
86#[derive(Copy, Clone, Eq, PartialEq, EnumIndex, IndexEnum, Hash, PartialOrd, Ord, Debug)]
87#[repr(u8)]
88pub enum DigitalDimmingDataInput { 
89    Duty1_16 =  Self::COMMAND_MASK | 0b0000,
90    Duty2_16 =  Self::COMMAND_MASK | 0b0001,
91    Duty3_16 =  Self::COMMAND_MASK | 0b0010,
92    Duty4_16 =  Self::COMMAND_MASK | 0b0011,
93    Duty5_16 =  Self::COMMAND_MASK | 0b0100,
94    Duty6_16 =  Self::COMMAND_MASK | 0b0101,
95    Duty7_16 =  Self::COMMAND_MASK | 0b0110,
96    Duty8_16 =  Self::COMMAND_MASK | 0b0111,
97    Duty9_16 =  Self::COMMAND_MASK | 0b1000,
98    Duty10_16 = Self::COMMAND_MASK | 0b1001,
99    Duty11_16 = Self::COMMAND_MASK | 0b1010,
100    Duty12_16 = Self::COMMAND_MASK | 0b1011,
101    Duty13_16 = Self::COMMAND_MASK | 0b1100,
102    Duty14_16 = Self::COMMAND_MASK | 0b1101,
103    Duty15_16 = Self::COMMAND_MASK | 0b1110,
104    Duty16_16 = Self::COMMAND_MASK | 0b1111,
105}
106
107impl Command for DigitalDimmingDataInput {
108    /// Write only
109    const COMMAND_MASK: u8 = 0b1110_0000;
110}
111
112/// Alias for Digital Dimming Data Input.
113pub type Dimming = DigitalDimmingDataInput;
114
115/// [Display Data Address Pointer](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf#HT16K33v110_110516.indd%3A.134369)
116#[derive(Copy, Clone, Eq, PartialEq, EnumIndex, IndexEnum, Hash, PartialOrd, Ord, Debug)]
117#[repr(u8)]
118pub enum DisplayDataAddressPointer {
119    Addr0 =  Self::COMMAND_MASK | 0b0000,
120    Addr1 =  Self::COMMAND_MASK | 0b0001,
121    Addr2 =  Self::COMMAND_MASK | 0b0010,
122    Addr3 =  Self::COMMAND_MASK | 0b0011,
123    Addr4 =  Self::COMMAND_MASK | 0b0100,
124    Addr5 =  Self::COMMAND_MASK | 0b0101,
125    Addr6 =  Self::COMMAND_MASK | 0b0110,
126    Addr7 =  Self::COMMAND_MASK | 0b0111,
127    Addr8 =  Self::COMMAND_MASK | 0b1000,
128    Addr9 =  Self::COMMAND_MASK | 0b1001,
129    Addr10 = Self::COMMAND_MASK | 0b1010,
130    Addr11 = Self::COMMAND_MASK | 0b1011,
131    Addr12 = Self::COMMAND_MASK | 0b1100,
132    Addr13 = Self::COMMAND_MASK | 0b1101,
133    Addr14 = Self::COMMAND_MASK | 0b1110,
134    Addr15 = Self::COMMAND_MASK | 0b1111,
135}
136
137impl Command for DisplayDataAddressPointer {
138    /// R/W;
139    const COMMAND_MASK: u8 = 0b0000_0000;
140}
141
142/// Alias for Display Data Address Pointer
143pub type DDAP = DisplayDataAddressPointer;
144
145/// [Key Data Address Pointers](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf#HT16K33v110_110516.indd%3A.134373)
146#[derive(Copy, Clone, Eq, PartialEq, EnumIndex, IndexEnum, Hash, PartialOrd, Ord, Debug)]
147#[repr(u8)]
148pub enum KeyDataAddressPointer {
149    Addr0 = Self::COMMAND_MASK | 0b000,
150    Addr1 = Self::COMMAND_MASK | 0b001,
151    Addr2 = Self::COMMAND_MASK | 0b010,
152    Addr3 = Self::COMMAND_MASK | 0b011,
153    Addr4 = Self::COMMAND_MASK | 0b100,
154    Addr5 = Self::COMMAND_MASK | 0b101,
155    Addr6 = Self::COMMAND_MASK | 0b110,
156    Addr7 = Self::COMMAND_MASK | 0b111,
157}
158
159impl Command for KeyDataAddressPointer {
160    /// Read only
161    const COMMAND_MASK: u8 = 0b0100_0000;
162}
163
164/// Alias for Key Data Address Pointer.
165pub type KDAP = KeyDataAddressPointer;
166
167/// Int Flag Address Pointer.
168/// 
169/// No implementation is planned. PRs welcomed.
170pub const INT_FLAG_ADDRESS_POINTER: u8 = 0b0110_0000;
171
172/// Alias for Int Flag Address Pointer.
173pub const IFAP: u8 = INT_FLAG_ADDRESS_POINTER;
174
175/// Test Mode.
176/// 
177/// No implementation is planned. PRs welcomed.
178pub const TEST_MODE_HOLTEK_ONLY: u8 = 0b1101_1001;
179
180pub type Result<Error> = core::result::Result<(), Error>;
181
182/// Driver for the HT16K33 RAM Mapping 16*8 LED controller Driver with key scan.
183/// 
184/// Has an internal state which it tries to reflect
185/// onto the connected controller.
186#[derive(Copy, Clone, Debug, Hash)]
187pub struct HT16K33<I2C> {
188    i2c:            I2C,
189    i2c_address:    u8,
190    // Contains both the `DisplayDataAddressPointer` as index 0 and the
191    // display buffer on the remaining 16 indexes.
192    dbuf:           [u8; SEGMENTS_SIZE + 1],
193    system:         SystemSetupRegister,
194    display:        DisplaySetupRegister,
195    rowint:         RowIntSetupRegister,
196    dimming:        DigitalDimmingDataInput,
197}
198
199impl<I2C> HT16K33<I2C> {
200    /// Creates a new instance of HT16K33 driver.
201    /// Nothing is known of the state of the controller so it is best to reset
202    /// it with `.power_on()` before use.
203    pub fn new(i2c: I2C, i2c_address: u8) -> Self {
204        let mut dbuf = [0; SEGMENTS_SIZE + 1];
205        dbuf[0] = DisplayDataAddressPointer::Addr0 as u8;
206
207        HT16K33 {
208            i2c,
209            i2c_address,
210            dbuf,
211            system:   SystemSetupRegister::StandBy,
212            display:  DisplaySetupRegister::Off,
213            rowint:   RowIntSetupRegister::Row,
214            dimming:  DigitalDimmingDataInput::Duty16_16,
215        }
216    }
217}
218
219pub trait HT16K33Trait<I2C, E>: Read + Write {
220    /// Destroys self and returns internal i2c interface.
221    fn i2c(self) -> I2C;
222
223    /// Returns a copy of the i2c address.
224    fn i2c_address(&self) -> u8;
225
226    /// Sets the i2c address.
227    fn set_i2c_address(&mut self, i2c_address: u8);
228    
229    /// Returns internal system setup register state. 
230    /// Might not reflect the state of the controller.
231    fn system(&self) -> SystemSetupRegister;
232
233    /// Writes system setup register state to controller
234    /// and if successful store it's new state internally.
235    fn write_system(&mut self, system: SystemSetupRegister) -> Result<E>;
236
237    /// Returns internal display setup register state. 
238    /// Might not reflect the state of the controller.
239    fn display(&self) -> DisplaySetupRegister;
240
241    /// Writes display setup register state to controller 
242    /// and if successful store it's new state internally.
243    fn write_display(&mut self, dsr: DisplaySetupRegister) -> Result<E>;
244
245    /// Returns internal ROW/INT setup register state. 
246    /// Might not reflect the state of the controller.
247    fn rowint(&self) -> RowIntSetupRegister;
248
249    /// Writes ROW/INT setup register state to controller
250    /// and if successful store it's new state internally.
251    fn write_rowint(&mut self, rowint: RowIntSetupRegister) -> Result<E>;
252
253    /// Returns internal digital dimming data input state. 
254    /// Might not reflect the state of the controller.
255    fn dimming(&self) -> DigitalDimmingDataInput;
256
257    /// Writes digital dimming data input state to controller
258    /// and if successful store it's new state internally.
259    fn write_dimming(&mut self, dim: DigitalDimmingDataInput) -> Result<E>;
260
261    /// Returns the internal display data address pointer state.
262    /// Might not reflect the state of the controller.
263    fn display_data_address_pointer(&self) -> DisplayDataAddressPointer;
264
265    /// Sets the display data address pointer internally.
266    fn set_display_data_address_pointer(
267        &mut self,
268        ddap: DisplayDataAddressPointer
269    );
270
271    /// Returns display buffer as a slice.
272    fn dbuf(&self) -> &[u8];
273
274    /// Returns display buffer as a mutable slice.
275    fn dbuf_mut(&mut self) -> &mut [u8];
276
277    /// Sets display buffer from a sized array.
278    fn set_dbuf(&mut self, src: &[u8; SEGMENTS_SIZE]);
279
280    /// Clears display buffer by setting all values to zero.
281    fn clear_dbuf(&mut self);
282
283    /// Writes display buffer to controller display ram.
284    fn write_dbuf(&mut self) -> Result<E>;
285
286    /// Reads display ram from controller into a provided buffer.
287    fn read_dram(&mut self, buf: &mut [u8]) -> Result<E>;
288
289    /// Reads display ram from controller into the internal display buffer.
290    fn read_dram_to_dbuf(&mut self) -> Result<E>;
291
292    /// Writes slice to controller display ram starting from the 
293    /// display data address pointer. Slice can be up to `SEGMENTS_SIZE` long.
294    /// No state in `self` is mutated except for `self.i2c` (internal driver).
295    /// 
296    /// # Panic
297    /// 
298    /// Panics if slice is longer than `SEGMENTS_SIZE`.
299    fn write_dram(&mut self, ddap: DisplayDataAddressPointer, slice: &[u8]) 
300    -> Result<E>;
301
302    /// Clears display buffer and writes to controller display ram.
303    fn clear_dram(&mut self) -> Result<E> {
304        self.clear_dbuf();
305        self.write_dbuf()
306    }
307
308    /// Writes `SystemSetupRegister::Normal`, 
309    /// `RowIntSetupRegister::Row`, 
310    /// `DisplaySetupRegister::On` and 
311    /// `DigitalDimmingDataInput::Duty16_16` to controller.
312    /// Then clears and writes display buffer.
313    fn power_on(&mut self) -> Result<E> {
314        self.write_system(SystemSetupRegister::Normal)?;
315        self.write_rowint(RowIntSetupRegister::Row)?;
316        self.write_display(DisplaySetupRegister::On)?;
317        self.write_dimming(DigitalDimmingDataInput::Duty16_16)?;
318        self.clear_dram()
319    }
320
321    /// Clears and writes a display buffer to controller,
322    /// writes `DigitalDimmingDataInput::Duty16_16`,
323    /// `DisplaySetupRegister::Off` and
324    /// `SystemSetupRegister::StandBy` to controller.
325    fn shutdown(&mut self) -> Result<E> {
326        self.clear_dram()?;
327        self.write_dimming(DigitalDimmingDataInput::Duty16_16)?;
328        self.write_display(DisplaySetupRegister::Off)?;
329        self.write_system(SystemSetupRegister::StandBy)
330    }
331}
332
333impl<I2C, E> Read for HT16K33<I2C>
334where
335    I2C: Read<Error = E>
336{
337    type Error = E;
338
339    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<Self::Error> {
340        self.i2c.read(address, buffer)
341    }
342}
343
344impl<I2C, E> Write for HT16K33<I2C>
345where
346    I2C: Write<Error = E>
347{
348    type Error = E;
349
350    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<Self::Error> {
351        self.i2c.write(address, bytes)
352    }
353}
354
355impl<I2C, E> HT16K33Trait<I2C, E> for HT16K33<I2C>
356where
357    I2C: Read<Error = E> + Write<Error = E>
358{
359    fn i2c(self) -> I2C {self.i2c}
360
361    fn i2c_address(&self) -> u8 {self.i2c_address}
362
363    fn set_i2c_address(&mut self, i2c_address: u8) {
364        self.i2c_address = i2c_address
365    }
366    
367    fn system(&self) -> System {self.system}
368
369    fn write_system(&mut self, sys: System) -> Result<E> {
370        self.write(self.i2c_address, &[sys as u8])?;
371        self.system = sys; Ok(())
372    }
373
374    fn display(&self) -> Display {self.display}
375
376    fn write_display(&mut self, dpy: Display) -> Result<E> {
377        self.write(self.i2c_address, &[dpy as u8])?;
378        self.display = dpy; Ok(())
379    }
380
381    fn rowint(&self) -> RowInt {self.rowint}
382
383    fn write_rowint(&mut self, rowint: RowInt) -> Result<E> {
384        self.write(self.i2c_address, &[rowint as u8])?;
385        self.rowint = rowint; Ok(())
386    }
387    
388    fn dimming(&self) -> Dimming {self.dimming}
389
390    fn write_dimming(&mut self, dim: Dimming) -> Result<E> {
391        self.write(self.i2c_address, &[dim as u8])?;
392        self.dimming = dim; Ok(())
393    }
394
395    fn display_data_address_pointer(&self) -> DDAP {
396        DDAP::index_enum(self.dbuf[0] as usize)
397            .expect("Internal Display Buffer has been corrupted.")
398    }
399
400    fn set_display_data_address_pointer(&mut self, ddap: DDAP) {
401        self.dbuf[0] = ddap as u8
402    }
403
404    fn dbuf(&self) -> &[u8] {&self.dbuf[1..]}
405
406    fn dbuf_mut(&mut self) -> &mut [u8] {&mut self.dbuf[1..]}
407
408    fn set_dbuf(&mut self, src: &[u8; SEGMENTS_SIZE]) {
409        self.dbuf[1..].clone_from_slice(src)
410    }
411
412    fn clear_dbuf(&mut self) {self.dbuf[1..].fill(0)}
413
414    fn write_dbuf(&mut self) -> Result<E> {
415        self.i2c.write(self.i2c_address, &self.dbuf)
416    }
417
418    fn read_dram(&mut self, buf: &mut [u8]) -> Result<E> {
419        self.i2c.read(self.i2c_address, buf)
420    }
421
422    fn read_dram_to_dbuf(&mut self) -> Result<E> {
423        self.i2c.read(self.i2c_address, &mut self.dbuf[1..])
424    }
425
426    fn write_dram(&mut self, ddap: DDAP, slice: &[u8]) -> Result<E> {
427        let mut buf = [0; SEGMENTS_SIZE + 1];
428        buf[0] = ddap as u8; 
429        buf[1..=slice.len()].clone_from_slice(slice);
430        self.i2c.write(self.i2c_address, &buf[..=slice.len()])
431    }
432}