#![cfg_attr(not(test), no_std)]
#![deny(missing_docs)]
use embedded_hal::blocking::i2c::{Write, WriteRead};
mod command {
pub const READ_PORT_0: u8 = 0x00;
pub const READ_PORT_1: u8 = 0x01;
pub const WRITE_PORT_0: u8 = 0x02;
pub const WRITE_PORT_1: u8 = 0x03;
pub const POLARITY_INVERT_PORT_0: u8 = 0x04;
pub const POLARITY_INVERT_PORT_1: u8 = 0x05;
pub const CONFIGURATION_PORT_0: u8 = 0x06;
pub const CONFIGURATION_PORT_1: u8 = 0x07;
}
use command::*;
#[derive(Copy, Clone, Debug)]
pub enum DeviceAddr {
Default,
Alternative(bool, bool, bool),
}
impl Default for DeviceAddr {
fn default() -> Self {
Self::Default
}
}
impl DeviceAddr {
const DEFAULT: u8 = 0x20;
pub fn addr(self) -> u8 {
match self {
DeviceAddr::Default => Self::DEFAULT,
DeviceAddr::Alternative(a0, a1, a2) => {
Self::DEFAULT | a0 as u8 | ((a1 as u8) << 1) | ((a2 as u8) << 2)
}
}
}
}
pub type Tca9535<I2C> = Tca9555<I2C>;
pub struct Tca9555<I2C> {
address: DeviceAddr,
i2c: I2C,
}
impl<I2C> Tca9555<I2C> {
pub fn new(i2c: I2C, address: DeviceAddr) -> Self {
Self { i2c, address }
}
}
impl<I2C, E> Tca9555<I2C>
where
I2C: WriteRead<Error = E>,
{
pub fn read_port_0(&mut self) -> Result<u8, E> {
let mut value = [0];
self.i2c
.write_read(self.address.addr(), &[READ_PORT_0], &mut value)
.and(Ok(value[0]))
}
pub fn read_port_1(&mut self) -> Result<u8, E> {
let mut value = [0];
self.i2c
.write_read(self.address.addr(), &[READ_PORT_1], &mut value)
.and(Ok(value[0]))
}
pub fn read_all(&mut self) -> Result<u16, E> {
let port0 = self.read_port_0()?;
let port1 = self.read_port_1()?;
Ok(u16::from_be_bytes([port1, port0]))
}
}
impl<I2C, E> Tca9555<I2C>
where
I2C: Write<Error = E>,
{
pub fn write_port_0(&mut self, value: u8) -> Result<(), E> {
self.i2c.write(self.address.addr(), &[WRITE_PORT_0, value])
}
pub fn set_port_0_direction(&mut self, dir_mask: u8) -> Result<(), E> {
self.i2c
.write(self.address.addr(), &[CONFIGURATION_PORT_0, dir_mask])
}
pub fn set_port_0_polarity_invert(
&mut self,
polarity_mask: u8,
) -> Result<(), E> {
self.i2c.write(
self.address.addr(),
&[POLARITY_INVERT_PORT_0, polarity_mask],
)
}
pub fn write_port_1(&mut self, value: u8) -> Result<(), E> {
self.i2c.write(self.address.addr(), &[WRITE_PORT_1, value])
}
pub fn set_port_1_direction(&mut self, dir_mask: u8) -> Result<(), E> {
self.i2c
.write(self.address.addr(), &[CONFIGURATION_PORT_1, dir_mask])
}
pub fn set_port_1_polarity_invert(
&mut self,
polarity_mask: u8,
) -> Result<(), E> {
self.i2c.write(
self.address.addr(),
&[POLARITY_INVERT_PORT_1, polarity_mask],
)
}
pub fn write_all(&mut self, value: u16) -> Result<(), E> {
let [port1, port0] = value.to_be_bytes();
self.write_port_0(port0)?;
self.write_port_1(port1)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn address_set() {
assert_eq!(DeviceAddr::DEFAULT, 0x20);
assert_eq!(DeviceAddr::Alternative(false, false, false).addr(), 0x20);
assert_eq!(DeviceAddr::Alternative(true, false, false).addr(), 0x21);
assert_eq!(DeviceAddr::Alternative(false, true, true).addr(), 0x26);
}
}