forked from M-Labs/zynq-rs
parent
e8ba73a8c7
commit
c60230af25
@ -0,0 +1,229 @@ |
||||
//! I2C Bit-banging Controller
|
||||
|
||||
mod regs; |
||||
use log::{error, info, warn}; |
||||
use crate::{print, println}; |
||||
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<Microseconds> |
||||
} |
||||
|
||||
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<bool, &'static str> { |
||||
// 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<u8, &'static str> { |
||||
// 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(()) |
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
use volatile_register::{RO, WO, RW}; |
||||
|
||||
use libregister::{register, register_bit, register_bits}; |
||||
|
||||
// With reference to:
|
||||
//
|
||||
// artiq:artiq/gateware/targets/kasli.py:
|
||||
// self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
//
|
||||
// misoc:misoc/cores/gpio.py:
|
||||
// class GPIOTristate(Module, AutoCSR):
|
||||
// def __init__(self, signals, reset_out=0, reset_oe=0):
|
||||
// l = len(signals)
|
||||
// self._in = CSRStatus(l)
|
||||
// self._out = CSRStorage(l, reset=reset_out)
|
||||
// self._oe = CSRStorage(l, reset=reset_oe)
|
||||
//
|
||||
// Hence, using GPIOs as SCL and SDA GPIOs respectively.
|
||||
//
|
||||
// Current compatibility:
|
||||
// zc706: GPIO 50, 51 == SCL, SDA
|
||||
|
||||
#[repr(C)] |
||||
pub struct RegisterBlock { |
||||
pub gpio_output_mask: &'static mut GPIOOutputMask, |
||||
pub gpio_input: &'static mut GPIOInput, |
||||
pub gpio_output_enable: &'static mut GPIOOutputEnable, |
||||
} |
||||
|
||||
impl RegisterBlock { |
||||
pub unsafe fn new() -> Self { |
||||
Self { |
||||
gpio_output_mask: GPIOOutputMask::new(), |
||||
gpio_input: GPIOInput::new(), |
||||
gpio_output_enable: GPIOOutputEnable::new() |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl GPIOOutputMask { |
||||
#[cfg(feature = "target_zc706")] |
||||
pub unsafe fn new() -> &'static mut Self { |
||||
&mut *(0xE000A00C as *mut _) |
||||
} |
||||
} |
||||
|
||||
impl GPIOInput { |
||||
#[cfg(feature = "target_zc706")] |
||||
pub unsafe fn new() -> &'static mut Self { |
||||
&mut *(0xE000A064 as *mut _) |
||||
} |
||||
} |
||||
|
||||
impl GPIOOutputEnable { |
||||
#[cfg(feature = "target_zc706")] |
||||
pub unsafe fn new() -> &'static mut Self { |
||||
&mut *(0xE000A248 as *mut _) |
||||
} |
||||
} |
||||
|
||||
// MASK_DATA_1_MSW:
|
||||
// Maskable output data for MIO[53:48]
|
||||
register!(gpio_output_mask, GPIOOutputMask, RW, u32); |
||||
// Output for SCL
|
||||
#[cfg(feature = "target_zc706")] |
||||
register_bit!(gpio_output_mask, scl_o, 2); |
||||
// Output for SDA
|
||||
#[cfg(feature = "target_zc706")] |
||||
register_bit!(gpio_output_mask, sda_o, 3); |
||||
// Mask for SCL; set to 1 to write to output
|
||||
#[cfg(feature = "target_zc706")] |
||||
register_bit!(gpio_output_mask, scl_m, 18); |
||||
// Mask for SDA; set to 1 to write to output
|
||||
#[cfg(feature = "target_zc706")] |
||||
register_bit!(gpio_output_mask, sda_m, 19); |
||||
|
||||
// DATA_1_RO:
|
||||
// Input data for MIO[53:32]
|
||||
register!(gpio_input, GPIOInput, RO, u32); |
||||
// Input for SCL
|
||||
#[cfg(feature = "target_zc706")] |
||||
register_bit!(gpio_input, scl, 8); |
||||
// Input for SDA
|
||||
#[cfg(feature = "target_zc706")] |
||||
register_bit!(gpio_input, sda, 9); |
||||
|
||||
// OEN_1:
|
||||
// Output enable for MIO[53:32]
|
||||
register!(gpio_output_enable, GPIOOutputEnable, RW, u32); |
||||
// Output enable for SCL
|
||||
#[cfg(feature = "target_zc706")] |
||||
register_bit!(gpio_output_enable, scl, 8); |
||||
// Output enable for SDA
|
||||
#[cfg(feature = "target_zc706")] |
||||
register_bit!(gpio_output_enable, sda, 9); |
Loading…
Reference in new issue