use crate::I2cExt;
pub struct Mcp23x17<M>(M);
impl<I2C> Mcp23x17<core::cell::RefCell<Driver<Mcp23017Bus<I2C>>>>
where
I2C: crate::I2cBus,
{
pub fn new_mcp23017(bus: I2C, a0: bool, a1: bool, a2: bool) -> Self {
Self::with_mutex(Mcp23017Bus(bus), a0, a1, a2)
}
}
impl<SPI> Mcp23x17<core::cell::RefCell<Driver<Mcp23S17Bus<SPI>>>>
where
SPI: crate::SpiBus,
{
pub fn new_mcp23s17(bus: SPI) -> Self {
Self::with_mutex(Mcp23S17Bus(bus), false, false, false)
}
}
impl<B, M> Mcp23x17<M>
where
B: Mcp23x17Bus,
M: crate::PortMutex<Port = Driver<B>>,
{
pub fn with_mutex(bus: B, a0: bool, a1: bool, a2: bool) -> Self {
Self(crate::PortMutex::create(Driver::new(bus, a0, a1, a2)))
}
pub fn split<'a>(&'a mut self) -> Parts<'a, B, M> {
Parts {
gpa0: crate::Pin::new(0, &self.0),
gpa1: crate::Pin::new(1, &self.0),
gpa2: crate::Pin::new(2, &self.0),
gpa3: crate::Pin::new(3, &self.0),
gpa4: crate::Pin::new(4, &self.0),
gpa5: crate::Pin::new(5, &self.0),
gpa6: crate::Pin::new(6, &self.0),
gpa7: crate::Pin::new(7, &self.0),
gpb0: crate::Pin::new(8, &self.0),
gpb1: crate::Pin::new(9, &self.0),
gpb2: crate::Pin::new(10, &self.0),
gpb3: crate::Pin::new(11, &self.0),
gpb4: crate::Pin::new(12, &self.0),
gpb5: crate::Pin::new(13, &self.0),
gpb6: crate::Pin::new(14, &self.0),
gpb7: crate::Pin::new(15, &self.0),
}
}
}
pub struct Parts<'a, B, M = core::cell::RefCell<Driver<B>>>
where
B: Mcp23x17Bus,
M: crate::PortMutex<Port = Driver<B>>,
{
pub gpa0: crate::Pin<'a, crate::mode::Input, M>,
pub gpa1: crate::Pin<'a, crate::mode::Input, M>,
pub gpa2: crate::Pin<'a, crate::mode::Input, M>,
pub gpa3: crate::Pin<'a, crate::mode::Input, M>,
pub gpa4: crate::Pin<'a, crate::mode::Input, M>,
pub gpa5: crate::Pin<'a, crate::mode::Input, M>,
pub gpa6: crate::Pin<'a, crate::mode::Input, M>,
pub gpa7: crate::Pin<'a, crate::mode::Input, M>,
pub gpb0: crate::Pin<'a, crate::mode::Input, M>,
pub gpb1: crate::Pin<'a, crate::mode::Input, M>,
pub gpb2: crate::Pin<'a, crate::mode::Input, M>,
pub gpb3: crate::Pin<'a, crate::mode::Input, M>,
pub gpb4: crate::Pin<'a, crate::mode::Input, M>,
pub gpb5: crate::Pin<'a, crate::mode::Input, M>,
pub gpb6: crate::Pin<'a, crate::mode::Input, M>,
pub gpb7: crate::Pin<'a, crate::mode::Input, M>,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Regs {
IODIRA = 0x00,
IPOLA = 0x02,
GPINTENA = 0x04,
DEFVALA = 0x06,
INTCONA = 0x08,
IOCONA = 0x0a,
GPPUA = 0x0c,
INTFA = 0x0e,
INTCAPA = 0x10,
GPIOA = 0x12,
OLATA = 0x14,
IODIRB = 0x01,
IPOLB = 0x03,
GPINTENB = 0x05,
DEFVALB = 0x07,
INTCONB = 0x09,
IOCONB = 0x0b,
GPPUB = 0x0d,
INTFB = 0x0f,
INTCAPB = 0x11,
GPIOB = 0x13,
OLATB = 0x15,
}
impl From<Regs> for u8 {
fn from(r: Regs) -> u8 {
r as u8
}
}
pub struct Driver<B> {
bus: B,
out: u16,
addr: u8,
}
impl<B> Driver<B> {
pub fn new(bus: B, a0: bool, a1: bool, a2: bool) -> Self {
let addr = 0x20 | ((a2 as u8) << 2) | ((a1 as u8) << 1) | (a0 as u8);
Self {
bus,
out: 0x0000,
addr,
}
}
}
impl<B: Mcp23x17Bus> crate::PortDriver for Driver<B> {
type Error = B::BusError;
fn set(&mut self, mask_high: u32, mask_low: u32) -> Result<(), Self::Error> {
self.out |= mask_high as u16;
self.out &= !mask_low as u16;
if (mask_high | mask_low) & 0x00FF != 0 {
self.bus
.write_reg(self.addr, Regs::GPIOA, (self.out & 0xFF) as u8)?;
}
if (mask_high | mask_low) & 0xFF00 != 0 {
self.bus
.write_reg(self.addr, Regs::GPIOB, (self.out >> 8) as u8)?;
}
Ok(())
}
fn is_set(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
Ok(((self.out as u32) & mask_high) | (!(self.out as u32) & mask_low))
}
fn get(&mut self, mask_high: u32, mask_low: u32) -> Result<u32, Self::Error> {
let io0 = if (mask_high | mask_low) & 0x00FF != 0 {
self.bus.read_reg(self.addr, Regs::GPIOA)?
} else {
0
};
let io1 = if (mask_high | mask_low) & 0xFF00 != 0 {
self.bus.read_reg(self.addr, Regs::GPIOB)?
} else {
0
};
let in_ = ((io1 as u32) << 8) | io0 as u32;
Ok((in_ & mask_high) | (!in_ & mask_low))
}
}
impl<B: Mcp23x17Bus> crate::PortDriverTotemPole for Driver<B> {
fn set_direction(
&mut self,
mask: u32,
dir: crate::Direction,
_state: bool,
) -> Result<(), Self::Error> {
let (mask_set, mask_clear) = match dir {
crate::Direction::Input => (mask as u16, 0),
crate::Direction::Output => (0, mask as u16),
};
if mask & 0x00FF != 0 {
self.bus.update_reg(
self.addr,
Regs::IODIRA,
(mask_set & 0xFF) as u8,
(mask_clear & 0xFF) as u8,
)?;
}
if mask & 0xFF00 != 0 {
self.bus.update_reg(
self.addr,
Regs::IODIRB,
(mask_set >> 8) as u8,
(mask_clear >> 8) as u8,
)?;
}
Ok(())
}
}
impl<B: Mcp23x17Bus> crate::PortDriverPullUp for Driver<B> {
fn set_pull_up(&mut self, mask: u32, enable: bool) -> Result<(), Self::Error> {
let (mask_set, mask_clear) = match enable {
true => (mask as u16, 0),
false => (0, mask as u16),
};
if mask & 0x00FF != 0 {
self.bus.update_reg(
self.addr,
Regs::GPPUA,
(mask_set & 0xFF) as u8,
(mask_clear & 0xFF) as u8,
)?;
}
if mask & 0xFF00 != 0 {
self.bus.update_reg(
self.addr,
Regs::GPPUB,
(mask_set >> 8) as u8,
(mask_clear >> 8) as u8,
)?;
}
Ok(())
}
}
impl<B: Mcp23x17Bus> crate::PortDriverPolarity for Driver<B> {
fn set_polarity(&mut self, mask: u32, inverted: bool) -> Result<(), Self::Error> {
let (mask_set, mask_clear) = match inverted {
true => (mask as u16, 0),
false => (0, mask as u16),
};
if mask & 0x00FF != 0 {
self.bus.update_reg(
self.addr,
Regs::IPOLA,
(mask_set & 0xFF) as u8,
(mask_clear & 0xFF) as u8,
)?;
}
if mask & 0xFF00 != 0 {
self.bus.update_reg(
self.addr,
Regs::IPOLB,
(mask_set >> 8) as u8,
(mask_clear >> 8) as u8,
)?;
}
Ok(())
}
}
pub struct Mcp23017Bus<I2C>(I2C);
pub struct Mcp23S17Bus<SPI>(SPI);
pub trait Mcp23x17Bus {
type BusError;
fn write_reg<R: Into<u8>>(&mut self, addr: u8, reg: R, value: u8)
-> Result<(), Self::BusError>;
fn read_reg<R: Into<u8>>(&mut self, addr: u8, reg: R) -> Result<u8, Self::BusError>;
fn update_reg<R: Into<u8>>(
&mut self,
addr: u8,
reg: R,
mask_set: u8,
mask_clear: u8,
) -> Result<(), Self::BusError> {
let reg = reg.into();
let mut val = self.read_reg(addr, reg)?;
val |= mask_set;
val &= !mask_clear;
self.write_reg(addr, reg, val)?;
Ok(())
}
}
impl<SPI: crate::SpiBus> Mcp23x17Bus for Mcp23S17Bus<SPI> {
type BusError = SPI::BusError;
fn write_reg<R: Into<u8>>(
&mut self,
addr: u8,
reg: R,
value: u8,
) -> Result<(), Self::BusError> {
self.0.write(&[0x40 | addr << 1, reg.into(), value])?;
Ok(())
}
fn read_reg<R: Into<u8>>(&mut self, addr: u8, reg: R) -> Result<u8, Self::BusError> {
let mut val = [0; 1];
let write = [0x40 | addr << 1 | 0x1, reg.into()];
let mut tx = [
embedded_hal::spi::Operation::Write(&write),
embedded_hal::spi::Operation::Read(&mut val),
];
self.0.transaction(&mut tx)?;
Ok(val[0])
}
}
impl<I2C: crate::I2cBus> Mcp23x17Bus for Mcp23017Bus<I2C> {
type BusError = I2C::BusError;
fn write_reg<R: Into<u8>>(
&mut self,
addr: u8,
reg: R,
value: u8,
) -> Result<(), Self::BusError> {
self.0.write_reg(addr, reg, value)
}
fn read_reg<R: Into<u8>>(&mut self, addr: u8, reg: R) -> Result<u8, Self::BusError> {
self.0.read_reg(addr, reg)
}
}
#[cfg(test)]
mod tests {
use embedded_hal_mock::eh1::{i2c as mock_i2c, spi as mock_spi};
#[test]
fn mcp23017() {
let expectations = [
mock_i2c::Transaction::write_read(0x22, vec![0x00], vec![0xff]),
mock_i2c::Transaction::write(0x22, vec![0x00, 0xfe]),
mock_i2c::Transaction::write_read(0x22, vec![0x00], vec![0xfe]),
mock_i2c::Transaction::write(0x22, vec![0x00, 0x7e]),
mock_i2c::Transaction::write_read(0x22, vec![0x00], vec![0x7e]),
mock_i2c::Transaction::write(0x22, vec![0x00, 0xfe]),
mock_i2c::Transaction::write_read(0x22, vec![0x01], vec![0xff]),
mock_i2c::Transaction::write(0x22, vec![0x01, 0xfe]),
mock_i2c::Transaction::write_read(0x22, vec![0x01], vec![0xfe]),
mock_i2c::Transaction::write(0x22, vec![0x01, 0x7e]),
mock_i2c::Transaction::write_read(0x22, vec![0x01], vec![0x7e]),
mock_i2c::Transaction::write(0x22, vec![0x01, 0xfe]),
mock_i2c::Transaction::write(0x22, vec![0x12, 0x01]),
mock_i2c::Transaction::write(0x22, vec![0x12, 0x00]),
mock_i2c::Transaction::write(0x22, vec![0x13, 0x01]),
mock_i2c::Transaction::write(0x22, vec![0x13, 0x00]),
mock_i2c::Transaction::write_read(0x22, vec![0x12], vec![0x80]),
mock_i2c::Transaction::write_read(0x22, vec![0x12], vec![0x7f]),
mock_i2c::Transaction::write_read(0x22, vec![0x13], vec![0x80]),
mock_i2c::Transaction::write_read(0x22, vec![0x13], vec![0x7f]),
];
let mut bus = mock_i2c::Mock::new(&expectations);
let mut pca = super::Mcp23x17::new_mcp23017(bus.clone(), false, true, false);
let pca_pins = pca.split();
let mut gpa0 = pca_pins.gpa0.into_output().unwrap();
let gpa7 = pca_pins.gpa7.into_output().unwrap();
let gpa7 = gpa7.into_input().unwrap();
let mut gpb0 = pca_pins.gpb0.into_output().unwrap();
let gpb7 = pca_pins.gpb7.into_output().unwrap();
let gpb7 = gpb7.into_input().unwrap();
gpa0.set_high().unwrap();
gpa0.set_low().unwrap();
gpb0.set_high().unwrap();
gpb0.set_low().unwrap();
assert!(gpa7.is_high().unwrap());
assert!(gpa7.is_low().unwrap());
assert!(gpb7.is_high().unwrap());
assert!(gpb7.is_low().unwrap());
bus.done();
}
#[test]
fn mcp23s17() {
let expectations = [
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x00]),
mock_spi::Transaction::read(0xff),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x00, 0xfe]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x00]),
mock_spi::Transaction::read(0xfe),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x00, 0x7e]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x00]),
mock_spi::Transaction::read(0x7e),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x00, 0xfe]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x01]),
mock_spi::Transaction::read(0xff),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x01, 0xfe]),
mock_spi::Transaction::transaction_end(), mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x01]),
mock_spi::Transaction::read(0xfe),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x01, 0x7e]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x01]),
mock_spi::Transaction::read(0x7e),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x01, 0xfe]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x12, 0x01]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x12, 0x00]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x13, 0x01]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x40, 0x13, 0x00]),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x12]),
mock_spi::Transaction::read(0x80),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x12]),
mock_spi::Transaction::read(0x7f),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x13]),
mock_spi::Transaction::read(0x80),
mock_spi::Transaction::transaction_end(),
mock_spi::Transaction::transaction_start(),
mock_spi::Transaction::write_vec(vec![0x41, 0x13]),
mock_spi::Transaction::read(0x7f),
mock_spi::Transaction::transaction_end(),
];
let mut bus = mock_spi::Mock::new(&expectations);
let mut pca = super::Mcp23x17::new_mcp23s17(bus.clone());
let pca_pins = pca.split();
let mut gpa0 = pca_pins.gpa0.into_output().unwrap();
let gpa7 = pca_pins.gpa7.into_output().unwrap();
let gpa7 = gpa7.into_input().unwrap();
let mut gpb0 = pca_pins.gpb0.into_output().unwrap();
let gpb7 = pca_pins.gpb7.into_output().unwrap();
let gpb7 = gpb7.into_input().unwrap();
gpa0.set_high().unwrap();
gpa0.set_low().unwrap();
gpb0.set_high().unwrap();
gpb0.set_low().unwrap();
assert!(gpa7.is_high().unwrap());
assert!(gpa7.is_low().unwrap());
assert!(gpb7.is_high().unwrap());
assert!(gpb7.is_low().unwrap());
bus.done();
}
}