#![no_std]
#![deny(
clippy::all,
clippy::cargo,
clippy::nursery,
// clippy::restriction,
// clippy::pedantic
)]
#![allow(
clippy::fallible_impl_from,
clippy::needless_doctest_main,
clippy::redundant_pub_crate,
clippy::suboptimal_flops
)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(rustdoc::all)]
#[macro_use] extern crate alloc;
#[cfg(feature = "fourdigit7segdis")]
pub mod fourdigit7segdis;
pub mod mappings;
pub mod gpio_api;
use crate::mappings::{LoCharBits, NumCharBits, SpecialCharBits, UpCharBits};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt::{Debug, Formatter};
pub const DISPLAY_REGISTERS_COUNT: usize = 6;
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum GpioPinValue {
LOW,
HIGH,
}
impl From<u8> for GpioPinValue {
fn from(x: u8) -> Self {
if x == 0 {
Self::LOW
} else {
Self::HIGH
}
}
}
pub struct TM1637Adapter {
pin_clock_write_fn: Box<dyn Fn(GpioPinValue)>,
pin_dio_write_fn: Box<dyn Fn(GpioPinValue)>,
pin_dio_read_fn: Box<dyn Fn() -> GpioPinValue>,
bit_delay_fn: Box<dyn Fn()>,
brightness: u8,
}
impl Debug for TM1637Adapter {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TM1637Adapter")
.field("brightness", &(self.brightness as *const u8))
.field("pin_clock_write_fn", &"<func>")
.field("pin_dio_write_fn", &"<func>")
.field("pin_dio_read_fn", &"<func>")
.field("bit_delay_fn", &"<func>")
.finish()
}
}
#[repr(u8)]
#[derive(Debug)]
pub enum Brightness {
L0 = 0b000,
L1 = 0b001,
L2 = 0b010,
L3 = 0b011,
L4 = 0b100,
L5 = 0b101,
L6 = 0b110,
L7 = 0b111,
}
#[repr(u8)]
#[derive(Debug)]
pub enum DisplayState {
OFF = 0b0000,
ON = 0b1000,
}
#[repr(u8)]
#[derive(Debug)]
pub enum ISA {
DataCommandWriteToDisplay = 0b0100_0000,
AddressCommandBase = 0b1100_0000,
DisplayCommandBase = 0b1000_0000,
}
impl TM1637Adapter {
pub fn new(
pin_clock_write_fn: Box<dyn Fn(GpioPinValue)>,
pin_dio_write_fn: Box<dyn Fn(GpioPinValue)>,
pin_dio_read_fn: Box<dyn Fn() -> GpioPinValue>,
bit_delay_fn: Box<dyn Fn()>,
) -> Self {
(pin_clock_write_fn)(GpioPinValue::LOW);
(pin_dio_write_fn)(GpioPinValue::LOW);
Self {
pin_clock_write_fn,
pin_dio_write_fn,
pin_dio_read_fn,
bit_delay_fn,
brightness: DisplayState::ON as u8 | Brightness::L7 as u8,
}
}
pub fn set_display_state(&mut self, ds: DisplayState) {
let old_brightness = self.brightness & 0b0000_0111;
self.brightness = ds as u8 | old_brightness;
}
pub fn set_brightness(&mut self, brightness: Brightness) {
let display_on = self.brightness as u8 & DisplayState::ON as u8;
self.brightness = display_on | brightness as u8;
}
pub fn write_segments_raw(&self, segments: &[u8], pos: u8) {
let mut n = segments.len() as u8;
if n == 0 {
return;
} let pos = pos % DISPLAY_REGISTERS_COUNT as u8;
if n + pos > DISPLAY_REGISTERS_COUNT as u8 {
n = DISPLAY_REGISTERS_COUNT as u8 - pos;
}
self.start();
self.write_byte_and_wait_ack(ISA::DataCommandWriteToDisplay as u8);
self.stop();
self.start();
self.write_byte_and_wait_ack(ISA::AddressCommandBase as u8 | (pos & 0x3));
for i in 0..n {
self.write_byte_and_wait_ack(segments[i as usize]);
}
self.stop();
self.write_display_state();
}
pub fn write_segment_raw(&self, segments: u8, position: u8) {
self.write_segments_raw(&[segments], position)
}
pub fn write_display_state(&self) {
self.start();
self.write_byte_and_wait_ack(ISA::DisplayCommandBase as u8 | self.brightness);
self.stop();
}
pub fn clear(&self) {
self.write_segments_raw(&[0, 0, 0, 0, 0, 0], 0);
}
fn write_byte_and_wait_ack(&self, byte: u8) {
let mut data = byte;
for _ in 0..8 {
(self.pin_clock_write_fn)(GpioPinValue::LOW);
(self.pin_dio_write_fn)(GpioPinValue::from(data & 0x01));
self.bit_delay();
(self.pin_clock_write_fn)(GpioPinValue::HIGH);
self.bit_delay();
data >>= 1;
}
self.recv_ack();
}
pub fn encode_number(num: u16) -> [u8; 4] {
let mut num = num % 10000;
let mut bits: [u8; 4] = [0; 4];
for i in 0..4 {
let digit = (num % 10) as u8;
bits[3 - i] = Self::encode_digit(digit);
num /= 10;
}
bits
}
pub const fn encode_digit(digit: u8) -> u8 {
let digit = digit % 10;
if digit == 0 {
NumCharBits::Zero as u8
} else if digit == 1 {
NumCharBits::One as u8
} else if digit == 2 {
NumCharBits::Two as u8
} else if digit == 3 {
NumCharBits::Three as u8
} else if digit == 4 {
NumCharBits::Four as u8
} else if digit == 5 {
NumCharBits::Five as u8
} else if digit == 6 {
NumCharBits::Six as u8
} else if digit == 7 {
NumCharBits::Seven as u8
} else if digit == 8 {
NumCharBits::Eight as u8
}
else {
NumCharBits::Nine as u8
}
}
#[allow(clippy::cognitive_complexity)]
#[allow(clippy::if_same_then_else)]
#[rustfmt::skip]
pub const fn encode_char(c: char) -> u8 {
if c == '0' { NumCharBits::Zero as u8 }
else if c == '1' { NumCharBits::One as u8 }
else if c == '2' { NumCharBits::Two as u8 }
else if c == '3' { NumCharBits::Three as u8 }
else if c == '4' { NumCharBits::Four as u8 }
else if c == '5' { NumCharBits::Five as u8 }
else if c == '6' { NumCharBits::Six as u8 }
else if c == '7' { NumCharBits::Seven as u8 }
else if c == '8' { NumCharBits::Eight as u8 }
else if c == '9' { NumCharBits::Nine as u8 }
else if c == 'A' { UpCharBits::UpA as u8 }
else if c == 'a' { LoCharBits::LoA as u8 }
else if c == 'B' { LoCharBits::LoB as u8 }
else if c == 'b' { LoCharBits::LoB as u8 }
else if c == 'C' { UpCharBits::UpC as u8 }
else if c == 'c' { UpCharBits::UpC as u8 }
else if c == 'D' { LoCharBits::LoD as u8 }
else if c == 'd' { LoCharBits::LoD as u8 }
else if c == 'E' { UpCharBits::UpE as u8 }
else if c == 'e' { UpCharBits::UpE as u8 }
else if c == 'F' { UpCharBits::UpF as u8 }
else if c == 'f' { UpCharBits::UpF as u8 }
else if c == 'G' { UpCharBits::UpG as u8 }
else if c == 'g' { UpCharBits::UpG as u8 }
else if c == 'H' { UpCharBits::UpH as u8 }
else if c == 'h' { LoCharBits::LoH as u8 }
else if c == 'I' { UpCharBits::UpI as u8 }
else if c == 'i' { UpCharBits::UpI as u8 }
else if c == 'J' { UpCharBits::UpJ as u8 }
else if c == 'j' { UpCharBits::UpJ as u8 }
else if c == 'L' { UpCharBits::UpL as u8 }
else if c == 'l' { UpCharBits::UpL as u8 }
else if c == 'N' { LoCharBits::LoN as u8 }
else if c == 'n' { LoCharBits::LoN as u8 }
else if c == 'O' { UpCharBits::UpO as u8 }
else if c == 'o' { LoCharBits::LoO as u8 }
else if c == 'P' { UpCharBits::UpP as u8 }
else if c == 'p' { UpCharBits::UpP as u8 }
else if c == 'Q' { LoCharBits::LoQ as u8 }
else if c == 'q' { LoCharBits::LoQ as u8 }
else if c == 'R' { LoCharBits::LoR as u8 }
else if c == 'r' { LoCharBits::LoR as u8 }
else if c == 'S' { UpCharBits::UpS as u8 }
else if c == 's' { UpCharBits::UpS as u8 }
else if c == 'T' { LoCharBits::LoT as u8 }
else if c == 't' { LoCharBits::LoT as u8 }
else if c == 'U' { UpCharBits::UpU as u8 }
else if c == 'u' { LoCharBits::LoU as u8 }
else if c == 'Y' { LoCharBits::LoY as u8 }
else if c == 'y' { LoCharBits::LoY as u8 }
else if c == ' ' { SpecialCharBits::Space as u8 }
else if c == '?' { SpecialCharBits::QuestionMark as u8 }
else if c == '-' { SpecialCharBits::Minus as u8 }
else if c == '_' { SpecialCharBits::Underscore as u8 }
else if c == '=' { SpecialCharBits::Equals as u8 }
else if c == '.' { SpecialCharBits::Dot as u8 }
else { SpecialCharBits::Space as u8 }
}
pub fn encode_string(str: &str) -> Vec<u8> {
str.chars().into_iter().map(Self::encode_char).collect()
}
#[inline]
fn start(&self) {
(self.pin_dio_write_fn)(GpioPinValue::HIGH);
(self.pin_clock_write_fn)(GpioPinValue::HIGH);
self.bit_delay();
(self.pin_dio_write_fn)(GpioPinValue::LOW);
self.bit_delay();
}
#[inline]
fn stop(&self) {
(self.pin_dio_write_fn)(GpioPinValue::LOW);
(self.pin_clock_write_fn)(GpioPinValue::HIGH);
self.bit_delay();
(self.pin_dio_write_fn)(GpioPinValue::HIGH);
self.bit_delay();
}
fn recv_ack(&self) {
(self.pin_clock_write_fn)(GpioPinValue::LOW);
(self.pin_dio_write_fn)(GpioPinValue::LOW);
self.bit_delay();
(self.pin_clock_write_fn)(GpioPinValue::HIGH);
let ack: GpioPinValue = (self.pin_dio_read_fn)();
for _ in 0..10 {
if ack as u8 == 0 {
break;
} else {
}
}
(self.pin_clock_write_fn)(GpioPinValue::LOW);
(self.pin_dio_write_fn)(GpioPinValue::LOW);
self.bit_delay();
}
#[inline]
fn bit_delay(&self) {
(self.bit_delay_fn)()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_number() {
let f = TM1637Adapter::encode_digit;
assert_eq!([f(1), f(2), f(3), f(4)], TM1637Adapter::encode_number(1234));
assert_eq!([f(9), f(9), f(9), f(9)], TM1637Adapter::encode_number(9999));
assert_eq!(
[f(0), f(0), f(0), f(0)],
TM1637Adapter::encode_number(10000)
);
assert_eq!([f(7), f(6), f(5), f(4)], TM1637Adapter::encode_number(7654));
}
}