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 crate::time::Microseconds;
use crate::time::Milliseconds;
use embedded_hal::timer::CountDown;
pub struct EEPROM<'a> {
i2c: &'a mut I2C,
port: u8,
address: u8,
page_size: u8,
count_down: crate::timer::global::CountDown<Milliseconds>
}
impl<'a> EEPROM<'a> {
#[cfg(feature = "target_zc706")]
pub fn new(i2c: &'a mut I2C) -> Self {
pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self {
EEPROM {
i2c: i2c,
port: 2,
address: 0b1010100,
page_size: page_size,
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
}
}
@ -45,30 +49,49 @@ impl<'a> EEPROM<'a> {
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> {
self.select()?;
let buf_len = buf.len();
let mut pb: u8 = addr % self.page_size;
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)?;
for (i, byte) in buf.iter().enumerate() {
self.i2c.write(*byte)?;
self.i2c.write(addr + (i as u8))?;
}
self.i2c.write(*byte)?;
pb += 1;
if (i == buf_len-1) || (pb == self.page_size) {
self.i2c.stop()?;
self.poll(1_000_000_000)?;
self.poll(1_000)?;
pb = 0;
}
}
Ok(())
}
/// 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.i2c.count_down.start(Microseconds(timeout_us));
while !self.i2c.write((self.address << 1) | 1)? {
if !self.i2c.count_down.waiting() {
self.count_down.start(Milliseconds(timeout_ms));
loop {
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")
}
}

View File

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

View File

@ -1,6 +1,9 @@
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:
//
@ -20,76 +23,71 @@ use libregister::{register, register_bit, register_bits};
// Current compatibility:
// zc706: GPIO 50, 51 == SCL, SDA
#[repr(C)]
pub struct RegisterBlock {
pub struct RegisterWrapper {
pub gpio_output_mask: &'static mut GPIOOutputMask,
pub gpio_input: &'static mut GPIOInput,
pub gpio_direction: &'static mut GPIODirection,
pub gpio_output_enable: &'static mut GPIOOutputEnable,
}
impl RegisterBlock {
pub unsafe fn new() -> Self {
impl RegisterWrapper {
pub fn new() -> Self {
Self {
gpio_output_mask: GPIOOutputMask::new(),
gpio_input: GPIOInput::new(),
gpio_direction: GPIODirection::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);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputMask, 0xE000A00C, new);
// 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
// Mask for keeping bits except SCL and SDA unchanged
#[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);
register_bits!(gpio_output_mask, mask, u16, 16, 31);
// DATA_1_RO:
// Input data for MIO[53:32]
register!(gpio_input, GPIOInput, RO, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOInput, 0xE000A064, new);
// Input for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, scl, 8);
register_bit!(gpio_input, scl, 18);
// Input for SDA
#[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:
// Output enable for MIO[53:32]
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputEnable, 0xE000A248, new);
// Output enable for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, scl, 8);
register_bit!(gpio_output_enable, scl, 18);
// Output enable for SDA
#[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 i2c_rst_ctrl: RW<u32>,
pub uart_rst_ctrl: UartRstCtrl,
pub gpio_rst_ctrl: RW<u32>,
pub gpio_rst_ctrl: GpioRstCtrl,
pub lqspi_rst_ctrl: LqspiRstCtrl,
pub smc_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_bit!(lqspi_clk_ctrl, clkact, 0);
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);