//! I2C Bit-banging Controller mod regs; pub mod eeprom; use super::clocks::Clocks; use super::slcr; use super::time::Microseconds; use embedded_hal::timer::CountDown; use libregister::{RegisterR, RegisterRW, RegisterW}; const INVALID_BUS: &'static str = "Invalid I2C bus"; pub struct I2C { regs: regs::RegisterBlock, count_down: super::timer::global::CountDown } impl I2C { #[cfg(feature = "target_zc706")] pub fn i2c() -> Self { // Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51 slcr::RegisterBlock::unlocked(|slcr| { // SCL slcr.mio_pin_50.write( slcr::MioPin50::zeroed() .l3_sel(0b000) // GPIO 50 .io_type(slcr::IoBufferType::Lvcmos25) .pullup(true) ); // SDA slcr.mio_pin_51.write( slcr::MioPin51::zeroed() .l3_sel(0b00) // GPIO 51 .io_type(slcr::IoBufferType::Lvcmos25) .pullup(true) ); }); Self::ctor_common() } fn ctor_common() -> Self { // Setup register block let clocks = Clocks::get(); let mut self_ = Self { regs: unsafe { regs::RegisterBlock::new() }, count_down: unsafe { super::timer::GlobalTimer::get() }.countdown() }; // Setup GPIO output mask self_.regs.gpio_output_mask.modify(|_, w| { w.scl_m(true).sda_m(true) }); self_.init(); self_ } /// Delay for I2C operations, simple wrapper for nb. fn delay(&mut self, us: u64) { self.count_down.start(Microseconds(us)); nb::block!(self.count_down.wait()).unwrap(); } fn half_period(&mut self) { self.delay(100) } fn sda_i(&mut self) -> bool { self.regs.gpio_input.read().sda() } fn scl_i(&mut self) -> bool { self.regs.gpio_input.read().scl() } fn sda_oe(&mut self, oe: bool) { self.regs.gpio_output_enable.modify(|_, w| { w.sda(oe) }) } fn sda_o(&mut self, o: bool) { self.regs.gpio_output_mask.modify(|_, w| { w.sda_o(o) }) } fn scl_oe(&mut self, oe: bool) { self.regs.gpio_output_enable.modify(|_, w| { w.scl(oe) }) } fn scl_o(&mut self, o: bool) { self.regs.gpio_output_mask.modify(|_, w| { w.scl_o(o) }) } pub fn init(&mut self) -> Result<(), &'static str> { self.scl_oe(false); self.sda_oe(false); self.scl_o(false); self.sda_o(false); // Check the I2C bus is ready self.half_period(); self.half_period(); if !self.sda_i() { // Try toggling SCL a few times for _bit in 0..8 { self.scl_oe(true); self.half_period(); self.scl_oe(false); self.half_period(); } } if !self.sda_i() { return Err("SDA is stuck low and doesn't get unstuck"); } if !self.scl_i() { return Err("SCL is stuck low and doesn't get unstuck"); } // postcondition: SCL and SDA high Ok(()) } pub fn start(&mut self) -> Result<(), &'static str> { // precondition: SCL and SDA high if !self.scl_i() { return Err("SCL is stuck low and doesn't get unstuck"); } if !self.sda_i() { return Err("SDA arbitration lost"); } self.sda_oe(true); self.half_period(); self.scl_oe(true); // postcondition: SCL and SDA low Ok(()) } pub fn restart(&mut self) -> Result<(), &'static str> { // precondition SCL and SDA low self.sda_oe(false); self.half_period(); self.scl_oe(false); self.half_period(); self.start()?; // postcondition: SCL and SDA low Ok(()) } pub fn stop(&mut self) -> Result<(), &'static str> { // precondition: SCL and SDA low self.half_period(); self.scl_oe(false); self.half_period(); self.sda_oe(false); self.half_period(); if !self.sda_i() { return Err("SDA arbitration lost"); } // postcondition: SCL and SDA high Ok(()) } pub fn write(&mut self, data: u8) -> Result { // precondition: SCL and SDA low // MSB first for bit in (0..8).rev() { self.sda_oe(data & (1 << bit) == 0); self.half_period(); self.scl_o(false); self.half_period(); self.scl_oe(true); } self.sda_oe(false); self.half_period(); self.scl_oe(false); self.half_period(); // Read ack/nack let ack = !self.sda_i(); self.scl_oe(true); self.sda_oe(true); // postcondition: SCL and SDA low Ok(ack) } pub fn read(&mut self, ack: bool) -> Result { // precondition: SCL and SDA low self.sda_oe(false); let mut data: u8 = 0; // MSB first for bit in (0..8).rev() { self.half_period(); self.scl_oe(false); self.half_period(); if self.sda_i() { data |= 1 << bit } self.scl_oe(true); } // Send ack/nack self.sda_oe(ack); self.half_period(); self.scl_oe(false); self.half_period(); self.scl_oe(true); self.sda_oe(true); // postcondition: SCL and SDA low Ok(data) } pub fn pca9548_select(&mut self, address: u8, channels: u8) -> Result<(), &'static str> { self.start()?; if !self.write(address << 1)? { return Err("PCA9548 failed to ack write address") } if !self.write(channels)? { return Err("PCA9548 failed to ack control word") } self.stop()?; Ok(()) } }