use core::marker::PhantomData;
use core::ops::Not;
use bitmask_enum::bitmask;
use embedded_hal::i2c::I2c;
#[cfg(feature = "async")]
use embedded_hal_async::i2c::I2c as AsyncI2c;
#[cfg(feature = "async")]
use crate::Async;
use crate::{Blocking, Mode};
const TCA6424_ADDR: u8 = 0x22;
const IN_PORT0: u8 = 0x80;
const IN_PORT1: u8 = 0x81;
const OUT_PORT0: u8 = 0x84;
const OUT_PORT1: u8 = 0x85;
const OUT_PORT2: u8 = 0x86;
const CONFIG_PORT0: u8 = 0x8C;
const PORT0_DIR: u8 = 0xFF; const PORT1_DIR: u8 = 0xC0; const PORT2_DIR: u8 = 0x00;
#[bitmask(u8)]
enum IoExpPort0 {
Sw7Pos1, Sw7Pos2, P02, P03, Sw4, Sw3, Sw2, Sw1, }
#[bitmask(u8)]
enum IoExpPort1 {
Digit1, Digit2, Digit3, Digit4, Led2, Led3, Sw6Pos2, Sw6Pos1, }
#[bitmask(u8)]
enum IoExpPort2 {
SegA, SegB, SegC, SegD, SegE, SegF, Dp, SegG, }
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum SwPos {
Down,
Up,
Undefined,
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum SevenSegDigit {
Digit1,
Digit2,
Digit3,
Digit4,
}
pub struct IoExpander<I2C, M = Blocking> {
i2c: I2C,
_mode: PhantomData<M>,
}
impl<I2C, M: Mode> IoExpander<I2C, M> {
pub fn new(i2c: I2C) -> Self {
Self {
i2c,
_mode: PhantomData,
}
}
}
impl<I2C: I2c> IoExpander<I2C, Blocking> {
pub fn init(&mut self) -> Result<(), I2C::Error> {
self.i2c
.write(
TCA6424_ADDR,
&[CONFIG_PORT0, PORT0_DIR, PORT1_DIR, PORT2_DIR],
)
.unwrap();
self.i2c.write(TCA6424_ADDR, &[OUT_PORT0, 0, 0, 0]).unwrap();
Ok(())
}
pub fn led2_on(&mut self) -> Result<(), I2C::Error> {
let port2 = self.read_register(OUT_PORT1)?;
let new_port2 = port2 | IoExpPort1::Led2.bits();
self.write_register(OUT_PORT1, new_port2)
}
pub fn led2_off(&mut self) -> Result<(), I2C::Error> {
let port1 = self.read_register(OUT_PORT1)?;
let new_port1 = port1 & IoExpPort1::Led2.bits().not();
self.write_register(OUT_PORT1, new_port1)
}
pub fn led3_on(&mut self) -> Result<(), I2C::Error> {
let port1 = self.read_register(OUT_PORT1)?;
let new_port1 = port1 | IoExpPort1::Led3.bits();
self.write_register(OUT_PORT1, new_port1)
}
pub fn led3_off(&mut self) -> Result<(), I2C::Error> {
let port1 = self.read_register(OUT_PORT1)?;
let new_port1 = port1 & IoExpPort1::Led3.bits().not();
self.write_register(OUT_PORT1, new_port1)
}
pub fn read_sw1(&mut self) -> Result<bool, I2C::Error> {
let port0 = self.read_register(IN_PORT0)?;
if port0 & IoExpPort0::Sw1.bits() == 0 {
Ok(true)
} else {
Ok(false)
}
}
pub fn read_sw2(&mut self) -> Result<bool, I2C::Error> {
let port0 = self.read_register(IN_PORT0)?;
if port0 & IoExpPort0::Sw2.bits() == 0 {
Ok(true)
} else {
Ok(false)
}
}
pub fn read_sw3(&mut self) -> Result<bool, I2C::Error> {
let port0 = self.read_register(IN_PORT0)?;
if port0 & IoExpPort0::Sw3.bits() == 0 {
Ok(true)
} else {
Ok(false)
}
}
pub fn read_sw4(&mut self) -> Result<bool, I2C::Error> {
let port0 = self.read_register(IN_PORT0)?;
if port0 & IoExpPort0::Sw4.bits() == 0 {
Ok(true)
} else {
Ok(false)
}
}
pub fn read_slide_sw6_position(&mut self) -> Result<SwPos, I2C::Error> {
let port2 = self.read_register(IN_PORT1)?;
if port2 & IoExpPort1::Sw6Pos1.bits() == 0 {
Ok(SwPos::Down)
} else if port2 & IoExpPort1::Sw6Pos2.bits() == 0 {
Ok(SwPos::Up)
} else {
Ok(SwPos::Undefined)
}
}
pub fn read_slide_sw7_position(&mut self) -> Result<SwPos, I2C::Error> {
let port0 = self.read_register(IN_PORT0)?;
if port0 & IoExpPort0::Sw7Pos1.bits() == 0 {
Ok(SwPos::Down)
} else if port0 & IoExpPort0::Sw7Pos2.bits() == 0 {
Ok(SwPos::Up)
} else {
Ok(SwPos::Undefined)
}
}
pub fn write_seven_segment_digit(
&mut self,
digit: SevenSegDigit,
value: Option<u8>,
) -> Result<(), I2C::Error> {
let digit_bits = match digit {
SevenSegDigit::Digit1 => IoExpPort1::Digit1.bits(),
SevenSegDigit::Digit2 => IoExpPort1::Digit2.bits(),
SevenSegDigit::Digit3 => IoExpPort1::Digit3.bits(),
SevenSegDigit::Digit4 => IoExpPort1::Digit4.bits(),
};
let port1 = self.read_register(OUT_PORT1)?;
let new_port1 = (port1 & 0xF0) | digit_bits;
self.write_register(OUT_PORT1, new_port1)?;
let segment_map = match value {
Some(v) => match v {
0 => 0b00111111,
1 => 0b00000110,
2 => 0b10011011,
3 => 0b10001111,
4 => 0b10100110,
5 => 0b10101101,
6 => 0b10111101,
7 => 0b00000111,
8 => 0b10111111,
9 => 0b10101111,
_ => 0b00000000,
},
None => 0x00,
};
self.write_register(OUT_PORT2, segment_map) }
pub fn seven_segment_display_colon_en(&mut self, enable: bool) -> Result<(), I2C::Error> {
let digits = IoExpPort1::Digit2.or(IoExpPort1::Digit4).bits();
let mut port1 = self.read_register(OUT_PORT1)?;
if enable {
port1 |= digits;
} else {
port1 &= !digits;
}
self.write_register(OUT_PORT1, port1)?;
let mut port2 = self.read_register(OUT_PORT2)?;
if enable {
port2 |= IoExpPort2::Dp.bits();
} else {
port2 &= !IoExpPort2::Dp.bits();
}
self.write_register(OUT_PORT2, port2) }
fn read_register(&mut self, reg: u8) -> Result<u8, I2C::Error> {
let mut buf = [0u8];
self.i2c.write_read(TCA6424_ADDR, &[reg], &mut buf)?;
Ok(buf[0])
}
fn write_register(&mut self, reg: u8, value: u8) -> Result<(), I2C::Error> {
self.i2c.write(TCA6424_ADDR, &[reg, value])
}
}
#[cfg(feature = "async")]
impl<I2C: AsyncI2c> IoExpander<I2C, Async> {
pub async fn init(&mut self) -> Result<(), I2C::Error> {
self.i2c
.write(
TCA6424_ADDR,
&[CONFIG_PORT0, PORT0_DIR, PORT1_DIR, PORT2_DIR],
)
.await
.unwrap();
self.i2c
.write(TCA6424_ADDR, &[OUT_PORT0, 0, 0, 0])
.await
.unwrap();
Ok(())
}
pub async fn led2_on(&mut self) -> Result<(), I2C::Error> {
let port2 = self.read_register(OUT_PORT1).await?;
let new_port2 = port2 | IoExpPort1::Led2.bits();
self.write_register(OUT_PORT1, new_port2).await
}
pub async fn led2_off(&mut self) -> Result<(), I2C::Error> {
let port1 = self.read_register(OUT_PORT1).await?;
let new_port1 = port1 & IoExpPort1::Led2.bits().not();
self.write_register(OUT_PORT1, new_port1).await
}
pub async fn led3_on(&mut self) -> Result<(), I2C::Error> {
let port1 = self.read_register(OUT_PORT1).await?;
let new_port1 = port1 | IoExpPort1::Led3.bits();
self.write_register(OUT_PORT1, new_port1).await
}
pub async fn led3_off(&mut self) -> Result<(), I2C::Error> {
let port1 = self.read_register(OUT_PORT1).await?;
let new_port1 = port1 & IoExpPort1::Led3.bits().not();
self.write_register(OUT_PORT1, new_port1).await
}
pub async fn read_sw1(&mut self) -> Result<bool, I2C::Error> {
let port0 = self.read_register(IN_PORT0).await?;
if port0 & IoExpPort0::Sw1.bits() == 0 {
Ok(true)
} else {
Ok(false)
}
}
pub async fn read_sw2(&mut self) -> Result<bool, I2C::Error> {
let port0 = self.read_register(IN_PORT0).await?;
if port0 & IoExpPort0::Sw2.bits() == 0 {
Ok(true)
} else {
Ok(false)
}
}
pub async fn read_sw3(&mut self) -> Result<bool, I2C::Error> {
let port0 = self.read_register(IN_PORT0).await?;
if port0 & IoExpPort0::Sw3.bits() == 0 {
Ok(true)
} else {
Ok(false)
}
}
pub async fn read_sw4(&mut self) -> Result<bool, I2C::Error> {
let port0 = self.read_register(IN_PORT0).await?;
if port0 & IoExpPort0::Sw4.bits() == 0 {
Ok(true)
} else {
Ok(false)
}
}
pub async fn read_slide_sw6_position(&mut self) -> Result<SwPos, I2C::Error> {
let port2 = self.read_register(IN_PORT1).await?;
if port2 & IoExpPort1::Sw6Pos1.bits() == 0 {
Ok(SwPos::Down)
} else if port2 & IoExpPort1::Sw6Pos2.bits() == 0 {
Ok(SwPos::Up)
} else {
Ok(SwPos::Undefined)
}
}
pub async fn read_slide_sw7_position(&mut self) -> Result<SwPos, I2C::Error> {
let port0 = self.read_register(IN_PORT0).await?;
if port0 & IoExpPort0::Sw7Pos1.bits() == 0 {
Ok(SwPos::Down)
} else if port0 & IoExpPort0::Sw7Pos2.bits() == 0 {
Ok(SwPos::Up)
} else {
Ok(SwPos::Undefined)
}
}
pub async fn write_seven_segment_digit(
&mut self,
digit: SevenSegDigit,
value: Option<u8>,
) -> Result<(), I2C::Error> {
let digit_bits = match digit {
SevenSegDigit::Digit1 => IoExpPort1::Digit1.bits(),
SevenSegDigit::Digit2 => IoExpPort1::Digit2.bits(),
SevenSegDigit::Digit3 => IoExpPort1::Digit3.bits(),
SevenSegDigit::Digit4 => IoExpPort1::Digit4.bits(),
};
let port1 = self.read_register(OUT_PORT1).await?;
let new_port1 = (port1 & 0xF0) | digit_bits;
self.write_register(OUT_PORT1, new_port1).await?;
let segment_map = match value {
Some(v) => match v {
0 => 0b00111111,
1 => 0b00000110,
2 => 0b10011011,
3 => 0b10001111,
4 => 0b10100110,
5 => 0b10101101,
6 => 0b10111101,
7 => 0b00000111,
8 => 0b10111111,
9 => 0b10101111,
_ => 0b00000000,
},
None => 0x00,
};
self.write_register(OUT_PORT2, segment_map).await
}
pub async fn seven_segment_display_colon_en(&mut self, enable: bool) -> Result<(), I2C::Error> {
let digits = IoExpPort1::Digit2.or(IoExpPort1::Digit4).bits();
let mut port1 = self.read_register(OUT_PORT1).await?;
if enable {
port1 |= digits;
} else {
port1 &= !digits;
}
self.write_register(OUT_PORT1, port1).await?;
let mut port2 = self.read_register(OUT_PORT2).await?;
if enable {
port2 |= IoExpPort2::Dp.bits();
} else {
port2 &= !IoExpPort2::Dp.bits();
}
self.write_register(OUT_PORT2, port2).await
}
async fn read_register(&mut self, reg: u8) -> Result<u8, I2C::Error> {
let mut buf = [0u8];
self.i2c.write_read(TCA6424_ADDR, &[reg], &mut buf).await?;
Ok(buf[0])
}
async fn write_register(&mut self, reg: u8, value: u8) -> Result<(), I2C::Error> {
self.i2c.write(TCA6424_ADDR, &[reg, value]).await
}
}