#![warn(missing_docs)]
#![warn(missing_doc_code_examples)]
#![doc(html_root_url = "https://docs.rs/adafruit-7segment/0.1.0")]
#![cfg_attr(not(feature = "std"), no_std)]
mod fonts;
use fonts::*;
pub use ascii::{AsciiChar, ToAsciiChar};
use embedded_hal::blocking::i2c::{Write, WriteRead};
use ht16k33::{DisplayData, DisplayDataAddress, LedLocation, COMMONS_SIZE, HT16K33};
#[derive(Debug)]
pub enum Error {
InsufficientDigits,
NotValidChar,
}
pub trait SevenSegment<E> {
fn update_buffer_with_digit(&mut self, index: Index, value: u8);
fn update_buffer_with_dot(&mut self, index: Index, dot_on: bool);
fn update_buffer_with_colon(&mut self, colon_on: bool);
fn update_buffer_with_char(&mut self, index: Index, value: AsciiChar) -> Result<(), Error>;
fn update_buffer_with_float(
&mut self,
index: Index,
value: f32,
fractional_digits: u8,
base: u8,
) -> Result<(), Error>;
}
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub enum Index {
One,
Two,
Three,
Four,
}
impl From<Index> for u8 {
fn from(i: Index) -> u8 {
match i {
Index::One => 0,
Index::Two => 1,
Index::Three => 2,
Index::Four => 3,
}
}
}
impl From<u8> for Index {
fn from(v: u8) -> Index {
match v {
0 => Index::One,
1 => Index::Two,
2 => Index::Three,
3 => Index::Four,
_ => panic!("Invalid index > 3"),
}
}
}
const MINUS_SIGN: u8 = 0x40;
const DOT_BIT: u8 = 7;
const COLON_BIT: u8 = 1;
fn set_bit<I2C, E>(display: &mut HT16K33<I2C>, index: u8, bit: u8, on: bool)
where
I2C: Write<Error = E> + WriteRead<Error = E>,
{
debug_assert!((bit as usize) < (COMMONS_SIZE * 2));
let index = index * 2;
let row = DisplayDataAddress::from_bits_truncate(if bit < 8 { index } else { index + 1 });
let common = DisplayData::from_bits_truncate(1 << (bit % 8));
display.update_display_buffer(LedLocation { row, common }, on);
}
fn update_bits<I2C, E>(display: &mut HT16K33<I2C>, index: Index, bits: u8)
where
I2C: Write<Error = E> + WriteRead<Error = E>,
{
let pos: u8;
if index > Index::Two {
pos = u8::from(index) + 1u8;
} else {
pos = index.into();
}
for i in 0..8 {
let on = ((bits >> i) & 1) == 1;
set_bit(display, pos, i, on);
}
}
impl<I2C, E> SevenSegment<E> for HT16K33<I2C>
where
I2C: Write<Error = E> + WriteRead<Error = E>,
{
fn update_buffer_with_digit(&mut self, index: Index, value: u8) {
let value = value as usize;
assert!(value < HEX_NUMBER_FONT_TABLE.len());
let bits = HEX_NUMBER_FONT_TABLE[value];
update_bits(self, index, bits);
}
fn update_buffer_with_dot(&mut self, index: Index, dot_on: bool) {
let pos: u8;
if index > Index::Two {
pos = u8::from(index) + 1u8;
} else {
pos = index.into();
}
set_bit(self, pos, DOT_BIT, dot_on);
}
fn update_buffer_with_colon(&mut self, colon_on: bool) {
set_bit(self, 2u8, COLON_BIT, colon_on);
}
fn update_buffer_with_char(&mut self, index: Index, value: AsciiChar) -> Result<(), Error> {
if value.is_ascii_hexdigit() {
let val: u8;
if value.is_ascii_digit() {
val = value.as_byte() - b'0';
} else {
val = 0x0A + (value.to_ascii_uppercase().as_byte() - b'A');
}
let val = val as usize;
assert!(val < HEX_NUMBER_FONT_TABLE.len());
let bits = HEX_NUMBER_FONT_TABLE[val];
update_bits(self, index, bits);
} else if value == '-' {
update_bits(self, index, MINUS_SIGN);
} else {
return Err(Error::NotValidChar);
}
Ok(())
}
fn update_buffer_with_float(
&mut self,
index: Index,
mut value: f32,
mut fractional_digits: u8,
base: u8,
) -> Result<(), Error> {
let index = u8::from(index);
let mut numeric_digits = 4 - index;
let is_negative = if value < 0. {
numeric_digits -= 1;
value *= -1.;
true
} else {
false
};
let base = base as u32;
let basef = base as f32;
let mut to_int_factor = base.pow(fractional_digits as u32) as f32;
let mut display_number = ((value * to_int_factor) + 0.5) as u32;
let too_big = base.pow(numeric_digits as u32);
while display_number >= too_big {
fractional_digits -= 1;
to_int_factor /= basef;
display_number = ((value * to_int_factor) + 0.5) as u32;
}
if to_int_factor < 1. {
return Err(Error::InsufficientDigits);
}
let mut display_pos = (3 - index) as i8;
if display_number == 0 {
self.update_buffer_with_digit((index + (display_pos as u8)).into(), 0);
display_pos -= 1;
} else {
let mut i = 0;
while display_number != 0 || i <= fractional_digits {
let digit_index: Index = (index + (display_pos as u8)).into();
self.update_buffer_with_digit(digit_index, (display_number % base) as u8);
if fractional_digits != 0 && i == fractional_digits {
self.update_buffer_with_dot(digit_index, true);
}
display_pos -= 1;
display_number /= base;
i += 1;
}
}
if is_negative {
update_bits(self, (index + (display_pos as u8)).into(), MINUS_SIGN);
display_pos -= 1;
}
while display_pos >= 0 {
update_bits(self, (index + (display_pos as u8)).into(), 0);
display_pos -= 1;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate std;
use embedded_hal_mock as hal;
use self::hal::i2c::Mock as I2cMock;
use super::*;
const ADDRESS: u8 = 0;
#[test]
fn update_buffer_with_dot() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.update_buffer_with_dot(Index::One, true);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[2].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[3].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[4].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[5].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[6].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[7].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[8].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[9].bits(), 0b0000_0000);
ht16k33.update_buffer_with_dot(Index::Two, true);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[2].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[3].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[4].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[5].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[6].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[7].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[8].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[9].bits(), 0b0000_0000);
ht16k33.update_buffer_with_dot(Index::Three, true);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[2].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[3].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[4].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[5].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[6].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[7].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[8].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[9].bits(), 0b0000_0000);
ht16k33.update_buffer_with_dot(Index::Four, true);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[2].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[3].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[4].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[5].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[6].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[7].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[8].bits(), 0b1000_0000);
assert_eq!(ht16k33.display_buffer()[9].bits(), 0b0000_0000);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn update_buffer_with_colon() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.update_buffer_with_colon(true);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[2].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[3].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[4].bits(), 0b0000_0010);
assert_eq!(ht16k33.display_buffer()[5].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[6].bits(), 0b0000_0000);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn update_buffer_with_digit() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
ht16k33.update_buffer_with_digit(Index::One, 0x0A);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0111_0111);
ht16k33.update_buffer_with_digit(Index::One, 0x0B);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0111_1100);
ht16k33.update_buffer_with_digit(Index::One, 0x00);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0011_1111);
ht16k33.update_buffer_with_digit(Index::One, 0x09);
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0110_1111);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn update_buffer_with_char() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
assert!(ht16k33
.update_buffer_with_char(Index::One, AsciiChar::new('A'))
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0111_0111);
assert!(ht16k33
.update_buffer_with_char(Index::One, AsciiChar::new('a'))
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0111_0111);
assert!(ht16k33
.update_buffer_with_char(Index::One, AsciiChar::new('B'))
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0111_1100);
assert!(ht16k33
.update_buffer_with_char(Index::One, AsciiChar::new('b'))
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0111_1100);
assert!(ht16k33
.update_buffer_with_char(Index::One, AsciiChar::new('0'))
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0011_1111);
assert!(ht16k33
.update_buffer_with_char(Index::One, AsciiChar::new('9'))
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0110_1111);
assert!(ht16k33
.update_buffer_with_char(Index::One, AsciiChar::new('-'))
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0100_0000);
i2c = ht16k33.destroy();
i2c.done();
}
#[test]
fn update_buffer_with_float() {
let expectations = [];
let mut i2c = I2cMock::new(&expectations);
let mut ht16k33 = HT16K33::new(i2c, ADDRESS);
assert!(ht16k33
.update_buffer_with_float(Index::One, 99.9, 2, 10)
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0110_1111);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[2].bits(), 0b1110_1111);
assert_eq!(ht16k33.display_buffer()[3].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[4].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[5].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[6].bits(), 0b0110_1111);
assert_eq!(ht16k33.display_buffer()[7].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[8].bits(), 0b0011_1111);
assert_eq!(ht16k33.display_buffer()[9].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[10].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[11].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[12].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[13].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[14].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[15].bits(), 0b0000_0000);
assert!(ht16k33
.update_buffer_with_float(Index::One, -99.9, 2, 10)
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0100_0000);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[2].bits(), 0b0110_1111);
assert_eq!(ht16k33.display_buffer()[3].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[4].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[5].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[6].bits(), 0b1110_1111);
assert_eq!(ht16k33.display_buffer()[7].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[8].bits(), 0b0110_1111);
assert_eq!(ht16k33.display_buffer()[9].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[10].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[11].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[12].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[13].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[14].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[15].bits(), 0b0000_0000);
ht16k33.clear_display_buffer();
assert!(ht16k33
.update_buffer_with_float(Index::Two, 9.9, 1, 10)
.is_ok());
assert_eq!(ht16k33.display_buffer()[0].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[1].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[2].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[3].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[4].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[5].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[6].bits(), 0b1110_1111);
assert_eq!(ht16k33.display_buffer()[7].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[8].bits(), 0b0110_1111);
assert_eq!(ht16k33.display_buffer()[9].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[10].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[11].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[12].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[13].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[14].bits(), 0b0000_0000);
assert_eq!(ht16k33.display_buffer()[15].bits(), 0b0000_0000);
i2c = ht16k33.destroy();
i2c.done();
}
}