use crate::{
gpio::lp_io::LowPowerOutputOpenDrain,
peripherals::{LP_AON, LP_I2C0, LP_IO, LP_PERI, LPWR},
time::Rate,
};
const LP_I2C_FILTER_CYC_NUM_DEF: u8 = 7;
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
ExceedingFifo,
AckCheckFailed,
TimeOut,
ArbitrationLost,
ExecIncomplete,
CommandNrExceeded,
InvalidResponse,
}
#[allow(unused)]
enum OperationType {
Write = 0,
Read = 1,
}
#[allow(unused)]
#[derive(Eq, PartialEq, Copy, Clone)]
enum Ack {
Ack,
Nack,
}
#[derive(PartialEq)]
#[allow(unused)]
enum Command {
Start,
Stop,
End,
Write {
ack_exp: Ack,
ack_check_en: bool,
length: u8,
},
Read {
ack_value: Ack,
length: u8,
},
}
pub struct LpI2c {
i2c: LP_I2C0<'static>,
}
impl LpI2c {
fn lp_i2c_configure_io(ionum: usize, pullup_en: bool) {
let lp_io = LP_IO::regs();
let lp_aon = LP_AON::regs();
unsafe {
lp_io
.out_data_w1ts()
.write(|w| w.out_data_w1ts().bits(1 << ionum));
lp_aon
.gpio_mux()
.modify(|r, w| w.sel().bits(r.sel().bits() | (1 << ionum)));
lp_io.pin(ionum).modify(|_, w| w.pad_driver().set_bit());
lp_io
.out_enable_w1ts()
.write(|w| w.enable_w1ts().bits(1 << ionum));
lp_io.gpio(ionum).modify(|_, w| {
w.fun_ie().set_bit();
w.fun_wpd().clear_bit();
w.fun_wpu().bit(pullup_en)
});
}
}
pub fn new(
i2c: LP_I2C0<'static>,
_sda: LowPowerOutputOpenDrain<'_, 6>,
_scl: LowPowerOutputOpenDrain<'_, 7>,
frequency: Rate,
) -> Self {
let me = Self { i2c };
Self::lp_i2c_configure_io(7, true);
Self::lp_i2c_configure_io(6, true);
unsafe {
let lp_io = LP_IO::regs();
lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1));
lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1));
}
me.i2c
.register_block()
.clk_conf()
.modify(|_, w| w.sclk_active().set_bit());
let lp_peri = LP_PERI::regs();
lp_peri
.clk_en()
.modify(|_, w| w.lp_ext_i2c_ck_en().set_bit());
lp_peri
.reset_en()
.modify(|_, w| w.lp_ext_i2c_reset_en().set_bit());
lp_peri
.reset_en()
.modify(|_, w| w.lp_ext_i2c_reset_en().clear_bit());
LPWR::regs()
.lpperi()
.modify(|_, w| w.lp_i2c_clk_sel().clear_bit());
me.i2c.register_block().ctr().modify(|_, w| unsafe {
w.bits(0);
w.sda_force_out().set_bit();
w.scl_force_out().set_bit();
w.clk_en().set_bit()
});
me.i2c
.register_block()
.fifo_conf()
.modify(|_, w| w.nonfifo_en().clear_bit());
me.i2c.register_block().ctr().modify(|_, w| {
w.tx_lsb_first().clear_bit();
w.rx_lsb_first().clear_bit()
});
me.reset_fifo();
LPWR::regs()
.lpperi()
.modify(|_, w| w.lp_i2c_clk_sel().clear_bit());
let source_clk = 16_000_000;
let bus_freq = frequency.as_hz();
let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1;
let sclk_freq: u32 = source_clk / clkm_div;
let half_cycle: u32 = sclk_freq / bus_freq / 2;
let scl_low = half_cycle;
let scl_wait_high = if bus_freq >= 80 * 1000 {
half_cycle / 2 - 2
} else {
half_cycle / 4
};
let scl_high = half_cycle - scl_wait_high;
let sda_hold = half_cycle / 4;
let sda_sample = half_cycle / 2; let setup = half_cycle;
let hold = half_cycle;
let tout = (4 * 8 - (5 * half_cycle).leading_zeros()) + 2;
let scl_low_period = scl_low - 1;
let scl_high_period = scl_high;
let scl_wait_high_period = scl_wait_high;
let sda_hold_time = sda_hold - 1;
let sda_sample_time = sda_sample - 1;
let scl_rstart_setup_time = setup - 1;
let scl_stop_setup_time = setup - 1;
let scl_start_hold_time = hold - 1;
let scl_stop_hold_time = hold - 1;
let time_out_value = tout;
let time_out_en = true;
unsafe {
me.i2c.register_block().clk_conf().modify(|_, w| {
w.sclk_sel().clear_bit();
w.sclk_div_num().bits((clkm_div - 1) as u8)
});
me.i2c
.register_block()
.scl_low_period()
.write(|w| w.scl_low_period().bits(scl_low_period as u16));
me.i2c.register_block().scl_high_period().write(|w| {
w.scl_high_period().bits(scl_high_period as u16);
w.scl_wait_high_period().bits(scl_wait_high_period as u8)
});
me.i2c
.register_block()
.sda_hold()
.write(|w| w.time().bits(sda_hold_time as u16));
me.i2c
.register_block()
.sda_sample()
.write(|w| w.time().bits(sda_sample_time as u16));
me.i2c
.register_block()
.scl_rstart_setup()
.write(|w| w.time().bits(scl_rstart_setup_time as u16));
me.i2c
.register_block()
.scl_stop_setup()
.write(|w| w.time().bits(scl_stop_setup_time as u16));
me.i2c
.register_block()
.scl_start_hold()
.write(|w| w.time().bits(scl_start_hold_time as u16));
me.i2c
.register_block()
.scl_stop_hold()
.write(|w| w.time().bits(scl_stop_hold_time as u16));
me.i2c.register_block().to().write(|w| {
w.time_out_en().bit(time_out_en);
w.time_out_value().bits(time_out_value.try_into().unwrap())
});
}
me.i2c
.register_block()
.filter_cfg()
.modify(|_, w| unsafe { w.sda_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) });
me.i2c
.register_block()
.filter_cfg()
.modify(|_, w| unsafe { w.scl_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) });
me.i2c
.register_block()
.filter_cfg()
.modify(|_, w| w.sda_filter_en().set_bit());
me.i2c
.register_block()
.filter_cfg()
.modify(|_, w| w.scl_filter_en().set_bit());
me.i2c
.register_block()
.ctr()
.modify(|_, w| w.rx_full_ack_level().set_bit());
me.lp_i2c_update();
me
}
fn lp_i2c_update(&self) {
self.i2c
.register_block()
.ctr()
.modify(|_, w| w.conf_upgate().set_bit());
}
fn reset_fifo(&self) {
self.i2c
.register_block()
.fifo_conf()
.modify(|_, w| w.tx_fifo_rst().set_bit());
self.i2c
.register_block()
.fifo_conf()
.modify(|_, w| w.tx_fifo_rst().clear_bit());
self.i2c
.register_block()
.fifo_conf()
.modify(|_, w| w.rx_fifo_rst().set_bit());
self.i2c
.register_block()
.fifo_conf()
.modify(|_, w| w.rx_fifo_rst().clear_bit());
}
}