1
0
Fork 0

i2c: fix GPIO register mapping, I2C control & EEPROM write operations

This commit is contained in:
Harry Ho 2020-08-07 11:10:18 +08:00
parent f7d3135ec7
commit 16b2df91ca
4 changed files with 106 additions and 61 deletions

View File

@ -1,20 +1,24 @@
use super::I2C; use super::I2C;
use crate::time::Microseconds; use crate::time::Milliseconds;
use embedded_hal::timer::CountDown; use embedded_hal::timer::CountDown;
pub struct EEPROM<'a> { pub struct EEPROM<'a> {
i2c: &'a mut I2C, i2c: &'a mut I2C,
port: u8, port: u8,
address: u8, address: u8,
page_size: u8,
count_down: crate::timer::global::CountDown<Milliseconds>
} }
impl<'a> EEPROM<'a> { impl<'a> EEPROM<'a> {
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
pub fn new(i2c: &'a mut I2C) -> Self { pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self {
EEPROM { EEPROM {
i2c: i2c, i2c: i2c,
port: 2, port: 2,
address: 0b1010100, address: 0b1010100,
page_size: page_size,
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
} }
} }
@ -45,30 +49,49 @@ impl<'a> EEPROM<'a> {
Ok(()) Ok(())
} }
/// Page write /// Smart multi-page writing
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
/// any roll-over when writing ambiguous memory regions.
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> { pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
self.select()?; self.select()?;
self.i2c.start()?; let buf_len = buf.len();
self.i2c.write(self.address << 1)?; let mut pb: u8 = addr % self.page_size;
self.i2c.write(addr)?;
for (i, byte) in buf.iter().enumerate() { for (i, byte) in buf.iter().enumerate() {
if (i == 0) || (pb == 0) {
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr + (i as u8))?;
}
self.i2c.write(*byte)?; self.i2c.write(*byte)?;
} pb += 1;
self.i2c.stop()?; if (i == buf_len-1) || (pb == self.page_size) {
self.poll(1_000_000_000)?; self.i2c.stop()?;
self.poll(1_000)?;
pb = 0;
}
}
Ok(()) Ok(())
} }
/// Poll /// Poll
pub fn poll(&mut self, timeout_us: u64) -> Result<(), &'static str> { pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
self.select()?; self.select()?;
self.i2c.count_down.start(Microseconds(timeout_us)); self.count_down.start(Milliseconds(timeout_ms));
while !self.i2c.write((self.address << 1) | 1)? { loop {
if !self.i2c.count_down.waiting() { self.i2c.start()?;
let ack = self.i2c.write(self.address << 1)?;
self.i2c.stop()?;
if ack {
break
};
if !self.count_down.waiting() {
return Err("I2C polling timeout") return Err("I2C polling timeout")
} }
} }

View File

@ -10,8 +10,11 @@ use libregister::{RegisterR, RegisterRW, RegisterW};
const INVALID_BUS: &'static str = "Invalid I2C bus"; const INVALID_BUS: &'static str = "Invalid I2C bus";
#[cfg(feature = "target_zc706")]
const GPIO_OUTPUT_MASK: u16 = 0xFFFF - 0x000C;
pub struct I2C { pub struct I2C {
regs: regs::RegisterBlock, regs: regs::RegisterWrapper,
count_down: super::timer::global::CountDown<Microseconds> count_down: super::timer::global::CountDown<Microseconds>
} }
@ -23,17 +26,21 @@ impl I2C {
// SCL // SCL
slcr.mio_pin_50.write( slcr.mio_pin_50.write(
slcr::MioPin50::zeroed() slcr::MioPin50::zeroed()
.l3_sel(0b000) // GPIO 50 .l3_sel(0b000) // as GPIO 50
.io_type(slcr::IoBufferType::Lvcmos25) .io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true) .pullup(true)
.disable_rcvr(true)
); );
// SDA // SDA
slcr.mio_pin_51.write( slcr.mio_pin_51.write(
slcr::MioPin51::zeroed() slcr::MioPin51::zeroed()
.l3_sel(0b00) // GPIO 51 .l3_sel(0b000) // as GPIO 51
.io_type(slcr::IoBufferType::Lvcmos25) .io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true) .pullup(true)
.disable_rcvr(true)
); );
// Reset
slcr.gpio_rst_ctrl.reset_gpio();
}); });
Self::ctor_common() Self::ctor_common()
@ -42,27 +49,30 @@ impl I2C {
fn ctor_common() -> Self { fn ctor_common() -> Self {
// Setup register block // Setup register block
let clocks = Clocks::get(); let clocks = Clocks::get();
let mut self_ = Self { let self_ = Self {
regs: unsafe { regs::RegisterBlock::new() }, regs: regs::RegisterWrapper::new(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown() count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
}; };
// Setup GPIO output mask // Setup GPIO output mask
self_.regs.gpio_output_mask.modify(|_, w| { self_.regs.gpio_output_mask.modify(|_, w| {
w.scl_m(true).sda_m(true) w.mask(GPIO_OUTPUT_MASK)
});
// Setup GPIO driver direction
self_.regs.gpio_direction.modify(|_, w| {
w.scl(true).sda(true)
}); });
self_.init();
self_ self_
} }
/// Delay for I2C operations, simple wrapper for nb. /// Delay for I2C operations, simple wrapper for nb.
fn delay(&mut self, us: u64) { fn delay_us(&mut self, us: u64) {
self.count_down.start(Microseconds(us)); self.count_down.start(Microseconds(us));
nb::block!(self.count_down.wait()).unwrap(); nb::block!(self.count_down.wait()).unwrap();
} }
fn half_period(&mut self) { self.delay(100) } fn half_period(&mut self) { self.delay_us(100) }
fn sda_i(&mut self) -> bool { fn sda_i(&mut self) -> bool {
self.regs.gpio_input.read().sda() self.regs.gpio_input.read().sda()
@ -171,7 +181,7 @@ impl I2C {
for bit in (0..8).rev() { for bit in (0..8).rev() {
self.sda_oe(data & (1 << bit) == 0); self.sda_oe(data & (1 << bit) == 0);
self.half_period(); self.half_period();
self.scl_o(false); self.scl_oe(false);
self.half_period(); self.half_period();
self.scl_oe(true); self.scl_oe(true);
} }

View File

@ -1,6 +1,9 @@
use volatile_register::{RO, WO, RW}; use volatile_register::{RO, WO, RW};
use libregister::{register, register_bit, register_bits}; use libregister::{
register, register_at,
register_bit, register_bits
};
// With reference to: // With reference to:
// //
@ -20,76 +23,71 @@ use libregister::{register, register_bit, register_bits};
// Current compatibility: // Current compatibility:
// zc706: GPIO 50, 51 == SCL, SDA // zc706: GPIO 50, 51 == SCL, SDA
#[repr(C)] pub struct RegisterWrapper {
pub struct RegisterBlock {
pub gpio_output_mask: &'static mut GPIOOutputMask, pub gpio_output_mask: &'static mut GPIOOutputMask,
pub gpio_input: &'static mut GPIOInput, pub gpio_input: &'static mut GPIOInput,
pub gpio_direction: &'static mut GPIODirection,
pub gpio_output_enable: &'static mut GPIOOutputEnable, pub gpio_output_enable: &'static mut GPIOOutputEnable,
} }
impl RegisterBlock { impl RegisterWrapper {
pub unsafe fn new() -> Self { pub fn new() -> Self {
Self { Self {
gpio_output_mask: GPIOOutputMask::new(), gpio_output_mask: GPIOOutputMask::new(),
gpio_input: GPIOInput::new(), gpio_input: GPIOInput::new(),
gpio_direction: GPIODirection::new(),
gpio_output_enable: GPIOOutputEnable::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: // MASK_DATA_1_MSW:
// Maskable output data for MIO[53:48] // Maskable output data for MIO[53:48]
register!(gpio_output_mask, GPIOOutputMask, RW, u32); register!(gpio_output_mask, GPIOOutputMask, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputMask, 0xE000A00C, new);
// Output for SCL // Output for SCL
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, scl_o, 2); register_bit!(gpio_output_mask, scl_o, 2);
// Output for SDA // Output for SDA
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, sda_o, 3); register_bit!(gpio_output_mask, sda_o, 3);
// Mask for SCL; set to 1 to write to output // Mask for keeping bits except SCL and SDA unchanged
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, scl_m, 18); register_bits!(gpio_output_mask, mask, u16, 16, 31);
// 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: // DATA_1_RO:
// Input data for MIO[53:32] // Input data for MIO[53:32]
register!(gpio_input, GPIOInput, RO, u32); register!(gpio_input, GPIOInput, RO, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOInput, 0xE000A064, new);
// Input for SCL // Input for SCL
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
register_bit!(gpio_input, scl, 8); register_bit!(gpio_input, scl, 18);
// Input for SDA // Input for SDA
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
register_bit!(gpio_input, sda, 9); register_bit!(gpio_input, sda, 19);
// DIRM_1:
// Direction mode for MIO[53:32]; 0/1 = in/out
register!(gpio_direction, GPIODirection, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIODirection, 0xE000A244, new);
// Direction for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, scl, 18);
// Direction for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, sda, 19);
// OEN_1: // OEN_1:
// Output enable for MIO[53:32] // Output enable for MIO[53:32]
register!(gpio_output_enable, GPIOOutputEnable, RW, u32); register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputEnable, 0xE000A248, new);
// Output enable for SCL // Output enable for SCL
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, scl, 8); register_bit!(gpio_output_enable, scl, 18);
// Output enable for SDA // Output enable for SDA
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, sda, 9); register_bit!(gpio_output_enable, sda, 19);

View File

@ -132,7 +132,7 @@ pub struct RegisterBlock {
pub can_rst_ctrl: RW<u32>, pub can_rst_ctrl: RW<u32>,
pub i2c_rst_ctrl: RW<u32>, pub i2c_rst_ctrl: RW<u32>,
pub uart_rst_ctrl: UartRstCtrl, pub uart_rst_ctrl: UartRstCtrl,
pub gpio_rst_ctrl: RW<u32>, pub gpio_rst_ctrl: GpioRstCtrl,
pub lqspi_rst_ctrl: LqspiRstCtrl, pub lqspi_rst_ctrl: LqspiRstCtrl,
pub smc_rst_ctrl: RW<u32>, pub smc_rst_ctrl: RW<u32>,
pub ocm_rst_ctrl: RW<u32>, pub ocm_rst_ctrl: RW<u32>,
@ -531,6 +531,20 @@ impl UartRstCtrl {
} }
} }
register!(gpio_rst_ctrl, GpioRstCtrl, RW, u32);
register_bit!(gpio_rst_ctrl, gpio_cpu1x_rst, 0);
register_at!(GpioRstCtrl, 0xF800022C, new);
impl GpioRstCtrl {
pub fn reset_gpio(&mut self) {
self.modify(|_, w|
w.gpio_cpu1x_rst(true)
);
self.modify(|_, w|
w.gpio_cpu1x_rst(false)
);
}
}
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32); register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
register_bit!(lqspi_clk_ctrl, clkact, 0); register_bit!(lqspi_clk_ctrl, clkact, 0);
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5); register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);