#![cfg_attr(not(feature = "std"), no_std)]
#![doc(html_root_url = "https://docs.rs/ht16k33/0.4.0")]
#![deny(missing_docs)]
use embedded_hal as hal;
#[cfg(feature = "serde")]
use serde;
mod constants;
mod errors;
mod types;
pub mod i2c_mock;
pub use errors::ValidationError;
pub use types::{Dimming, Display, DisplayData, DisplayDataAddress, LedLocation, Oscillator};
pub use constants::{COMMONS_SIZE, ROWS_SIZE};
use hal::blocking::i2c::{Write, WriteRead};
pub struct HT16K33<I2C> {
i2c: I2C,
address: u8,
buffer: [DisplayData; ROWS_SIZE],
oscillator_state: Oscillator,
display_state: Display,
dimming_state: Dimming,
}
impl<I2C, E> HT16K33<I2C>
where
I2C: Write<Error = E> + WriteRead<Error = E>,
{
pub fn new(i2c: I2C, address: u8) -> Self {
HT16K33 {
address,
i2c,
buffer: [DisplayData::empty(); ROWS_SIZE],
oscillator_state: Oscillator::OFF,
display_state: Display::OFF,
dimming_state: Dimming::BRIGHTNESS_MAX,
}
}
pub fn initialize(&mut self) -> Result<(), E> {
self.set_oscillator(Oscillator::ON)?;
self.set_display(Display::OFF)?;
self.set_dimming(Dimming::BRIGHTNESS_MAX)?;
self.clear_display_buffer();
self.write_display_buffer()?;
Ok(())
}
pub fn destroy(self) -> I2C {
self.i2c
}
pub fn display_buffer(&self) -> &[DisplayData; ROWS_SIZE] {
&self.buffer
}
pub fn oscillator(&self) -> &Oscillator {
&self.oscillator_state
}
pub fn display(&self) -> &Display {
&self.display_state
}
pub fn dimming(&self) -> &Dimming {
&self.dimming_state
}
pub fn update_display_buffer(&mut self, location: LedLocation, enabled: bool) {
self.buffer[location.row_as_index()].set(location.common, enabled);
}
pub fn clear_display_buffer(&mut self) {
for row in self.buffer.iter_mut() {
*row = DisplayData::COMMON_NONE;
}
}
pub fn set_oscillator(&mut self, oscillator: Oscillator) -> Result<(), E> {
self.oscillator_state = oscillator;
self.i2c.write(
self.address,
&[(Oscillator::COMMAND | self.oscillator_state).bits()],
)?;
Ok(())
}
pub fn set_display(&mut self, display: Display) -> Result<(), E> {
self.display_state = display;
self.i2c.write(
self.address,
&[(Display::COMMAND | self.display_state).bits()],
)?;
Ok(())
}
pub fn set_dimming(&mut self, dimming: Dimming) -> Result<(), E> {
self.dimming_state = dimming;
self.i2c.write(
self.address,
&[(Dimming::COMMAND | self.dimming_state).bits()],
)?;
Ok(())
}
pub fn set_led(&mut self, location: LedLocation, enabled: bool) -> Result<(), E> {
self.update_display_buffer(location, enabled);
self.i2c.write(
self.address,
&[
location.row.bits(),
self.buffer[location.row_as_index()].bits(),
],
)?;
Ok(())
}
pub fn write_display_buffer(&mut self) -> Result<(), E> {
let mut write_buffer = [0u8; ROWS_SIZE + 1];
write_buffer[0] = DisplayDataAddress::ROW_0.bits();
for value in 0usize..self.buffer.len() {
write_buffer[value + 1] = self.buffer[value].bits();
}
self.i2c.write(self.address, &write_buffer)?;
Ok(())
}
pub fn read_display_buffer(&mut self) -> Result<(), E> {
let mut read_buffer = [0u8; ROWS_SIZE];
self.i2c.write_read(
self.address,
&[DisplayDataAddress::ROW_0.bits()],
&mut read_buffer,
)?;
for (index, value) in read_buffer.iter().enumerate() {
self.buffer[index] = DisplayData::from_bits_truncate(*value);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate std;
use embedded_hal_mock as hal;
use self::hal::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
use super::*;
use std::vec;
const ADDRESS: u8 = 0;
#[test]
fn new() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let ht16k33 = HT16K33::new(i2c, ADDRESS);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn initialize() {
let mut write_buffer = vec![super::DisplayDataAddress::ROW_0.bits()];
write_buffer.extend([0; super::ROWS_SIZE].iter().cloned());
let expectations = [
I2cTransaction::write(
ADDRESS,
vec![(super::Oscillator::COMMAND | super::Oscillator::ON).bits()],
),
I2cTransaction::write(
ADDRESS,
vec![(super::Display::COMMAND | super::Display::OFF).bits()],
),
I2cTransaction::write(
ADDRESS,
vec![(super::Dimming::COMMAND | Dimming::BRIGHTNESS_MAX).bits()],
),
I2cTransaction::write(ADDRESS, write_buffer),
];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.initialize().unwrap();
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn display_buffer() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let ht16k33 = HT16K33::new(i2c, ADDRESS);
let &buffer = ht16k33.display_buffer();
assert_eq!(buffer.len(), ROWS_SIZE);
for row in buffer.iter() {
assert_eq!(row.bits(), 0u8);
}
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn oscillator() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let ht16k33 = HT16K33::new(i2c, ADDRESS);
let &oscillator = ht16k33.oscillator();
assert_eq!(oscillator, Oscillator::OFF);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn display() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let ht16k33 = HT16K33::new(i2c, ADDRESS);
let &display = ht16k33.display();
assert_eq!(display, Display::OFF);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn dimming() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let ht16k33 = HT16K33::new(i2c, ADDRESS);
let &dimming = ht16k33.dimming();
assert_eq!(dimming, Dimming::BRIGHTNESS_MAX);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn update_display_buffer() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
let first_led = LedLocation::new(1, 4).unwrap();
let second_led = LedLocation::new(1, 5).unwrap();
ht16k33.update_display_buffer(first_led, true);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0001_0000);
ht16k33.update_display_buffer(second_led, true);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0011_0000);
ht16k33.update_display_buffer(first_led, false);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0010_0000);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn clear_display_buffer() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
let first_led = LedLocation::new(1, 4).unwrap();
let second_led = LedLocation::new(1, 5).unwrap();
ht16k33.update_display_buffer(first_led, true);
ht16k33.update_display_buffer(second_led, true);
ht16k33.clear_display_buffer();
let &buffer = ht16k33.display_buffer();
assert_eq!(buffer.len(), ROWS_SIZE);
for row in buffer.iter() {
assert_eq!(row.bits(), 0u8);
}
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn set_oscillator() {
let expectations = [I2cTransaction::write(
ADDRESS,
vec![(super::Oscillator::COMMAND | super::Oscillator::OFF).bits()],
)];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.set_oscillator(super::Oscillator::OFF).unwrap();
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn set_display() {
let expectations = [I2cTransaction::write(
ADDRESS,
vec![(super::Display::COMMAND | super::Display::OFF).bits()],
)];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.set_display(super::Display::OFF).unwrap();
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn set_dimming() {
let expectations = [I2cTransaction::write(
ADDRESS,
vec![(super::Dimming::COMMAND | Dimming::BRIGHTNESS_MAX).bits()],
)];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.set_dimming(Dimming::BRIGHTNESS_MAX).unwrap();
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn set_led() {
let expectations = [I2cTransaction::write(ADDRESS, vec![1u8, 0b1000_0000])];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33
.set_led(LedLocation::new(1, 7).unwrap(), true)
.unwrap();
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn write_display_buffer() {
let mut write_buffer = vec![super::DisplayDataAddress::ROW_0.bits()];
write_buffer.extend([0; super::ROWS_SIZE].iter().cloned());
let expectations = [I2cTransaction::write(ADDRESS, write_buffer)];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.write_display_buffer().unwrap();
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn read_display_buffer() {
let mut read_buffer = vec![0; super::ROWS_SIZE];
read_buffer[1] = 0b0000_0010;
read_buffer[15] = 0b0000_0010;
let expectations = [I2cTransaction::write_read(
ADDRESS,
vec![super::DisplayDataAddress::ROW_0.bits()],
read_buffer,
)];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.read_display_buffer().unwrap();
let &buffer = ht16k33.display_buffer();
for value in 0..buffer.len() {
match value {
1 | 15 => assert_eq!(buffer[value].bits(), 0b0000_0010),
_ => assert_eq!(buffer[value].bits(), 0),
}
}
i2c = ht16k33.destroy();
i2c.done();
}
}