#![cfg_attr(docsrs, procmacros::doc_replace)]
use core::time::Duration;
use crate::{
gpio::{InputPin, OutputPin, RtcFunction, RtcPin},
peripherals::{GPIO, RTC_I2C, RTC_IO, SENS},
};
const RC_FAST_CLK: u32 = property!("soc.rc_fast_clk_default");
pub trait Sda: RtcPin + OutputPin + InputPin {
#[doc(hidden)]
fn selector(&self) -> u8;
}
pub trait Scl: RtcPin + OutputPin + InputPin {
#[doc(hidden)]
fn selector(&self) -> u8;
}
for_each_lp_function! {
(($_func:ident, SAR_I2C_SCL_n, $n:literal), $gpio:ident) => {
impl Scl for crate::peripherals::$gpio<'_> {
fn selector(&self) -> u8 {
$n
}
}
};
(($_func:ident, SAR_I2C_SDA_n, $n:literal), $gpio:ident) => {
impl Sda for crate::peripherals::$gpio<'_> {
fn selector(&self) -> u8 {
$n
}
}
};
}
#[procmacros::doc_replace]
pub struct I2c<'d> {
i2c: RTC_I2C<'d>,
sda: u8,
scl: u8,
}
impl<'d> I2c<'d> {
#[procmacros::doc_replace]
pub fn new(
i2c: RTC_I2C<'d>,
config: Config,
sda: impl Sda + 'd,
scl: impl Scl + 'd,
) -> Result<Self, ConfigError> {
i2c.register_block().ctrl().reset();
SENS::regs().sar_i2c_ctrl().reset();
fn bind_pin(pin: &impl RtcPin) {
GPIO::regs()
.pin(pin.number() as usize)
.modify(|_, w| w.pad_driver().bit(true));
RTC_IO::regs()
.touch_pad(pin.number() as usize)
.modify(|_, w| w.fun_ie().bit(true).rue().bit(true).rde().bit(false));
RTC_IO::regs()
.rtc_gpio_enable_w1ts()
.write(|w| unsafe { w.rtc_gpio_enable_w1ts().bits(1 << pin.number()) });
pin.rtc_set_config(true, true, RtcFunction::I2c);
}
bind_pin(&sda);
bind_pin(&scl);
RTC_IO::regs().sar_i2c_io().write(|w| unsafe {
w.sar_i2c_sda_sel().bits(sda.selector());
w.sar_i2c_scl_sel().bits(scl.selector())
});
SENS::regs()
.sar_peri_reset_conf()
.modify(|_, w| w.sar_rtc_i2c_reset().set_bit());
i2c.register_block()
.ctrl()
.modify(|_, w| w.i2c_reset().set_bit());
i2c.register_block()
.ctrl()
.modify(|_, w| w.i2c_reset().clear_bit());
SENS::regs()
.sar_peri_reset_conf()
.modify(|_, w| w.sar_rtc_i2c_reset().clear_bit());
i2c.register_block().ctrl().modify(|_, w| {
w.sda_force_out().clear_bit();
w.scl_force_out().clear_bit()
});
SENS::regs()
.sar_peri_clk_gate_conf()
.modify(|_, w| w.rtc_i2c_clk_en().set_bit());
i2c.register_block()
.ctrl()
.modify(|_, w| w.ms_mode().set_bit());
i2c.register_block()
.ctrl()
.modify(|_, w| w.i2c_ctrl_clk_gate_en().set_bit());
let mut this = Self {
i2c,
sda: sda.number(),
scl: scl.number(),
};
this.apply_config(&config)?;
Ok(this)
}
#[procmacros::doc_replace]
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
self.i2c
.register_block()
.scl_low()
.write(|w| unsafe { w.period().bits(config.timing.scl_low_period) });
self.i2c
.register_block()
.scl_high()
.write(|w| unsafe { w.period().bits(config.timing.scl_high_period) });
self.i2c
.register_block()
.sda_duty()
.write(|w| unsafe { w.num().bits(config.timing.sda_duty) });
self.i2c
.register_block()
.scl_start_period()
.write(|w| unsafe { w.scl_start_period().bits(config.timing.scl_start_period) });
self.i2c
.register_block()
.scl_stop_period()
.write(|w| unsafe { w.scl_stop_period().bits(config.timing.scl_stop_period) });
let ticks = duration_to_clock(config.timeout);
let ticks = ticks.max(2u32.pow(19) - 1);
self.i2c
.register_block()
.to()
.write(|w| unsafe { w.time_out().bits(ticks) });
Ok(())
}
#[procmacros::doc_replace]
pub fn write(&mut self, address: u8, register: u8, data: &[u8]) -> Result<(), Error> {
let sens = unsafe { crate::pac::SENS::steal() };
if data.len() > u8::MAX as usize - 2 {
return Err(Error::TransactionSizeLimitExceeded);
}
self.write_cmd(
0,
Command::Write {
ack_exp: Ack::Ack,
ack_check_en: true,
length: 2 + (data.len() as u8),
},
);
self.write_cmd(1, Command::Stop);
self.clear_interrupts();
let ctrl = {
let mut result = 0;
result |= address as u32;
result |= (register as u32) << 11;
result |= (data[0] as u32) << 19;
result |= 1u32 << 27; result
};
sens.sar_i2c_ctrl()
.write(|w| unsafe { w.sar_i2c_ctrl().bits(ctrl) });
sens.sar_i2c_ctrl().modify(|_, w| {
w.sar_i2c_start_force().set_bit();
w.sar_i2c_start().set_bit()
});
for &byte in data.iter().skip(1) {
match self.wait_for_tx_interrupt() {
Ok(is_tx) => {
if is_tx {
sens.sar_i2c_ctrl().modify(|r, w| {
let mut value = r.sar_i2c_ctrl().bits();
value &= !(0xFF << 19);
value |= (byte as u32) << 19;
value |= 1 << 27;
unsafe { w.sar_i2c_ctrl().bits(value) }
});
self.i2c
.register_block()
.int_clr()
.write(|w| w.tx_data().clear_bit_by_one());
} else {
core::panic!("Peripheral didn't wait for data");
}
}
Err(err) => {
sens.sar_i2c_ctrl().modify(|_, w| {
w.sar_i2c_start_force().clear_bit();
w.sar_i2c_start().clear_bit()
});
return Err(err);
}
}
}
let result = self.wait_for_complete_interrupt();
sens.sar_i2c_ctrl().write(|w| {
w.sar_i2c_start_force().clear_bit();
w.sar_i2c_start().clear_bit()
});
result
}
#[procmacros::doc_replace]
pub fn read(&mut self, address: u8, register: u8, data: &mut [u8]) -> Result<(), Error> {
let sens = unsafe { crate::pac::SENS::steal() };
if data.len() > u8::MAX as usize {
return Err(Error::TransactionSizeLimitExceeded);
}
self.write_cmd(
2,
Command::Write {
ack_exp: Ack::Ack,
ack_check_en: true,
length: 2,
},
);
self.write_cmd(3, Command::Start);
self.write_cmd(
4,
Command::Write {
ack_exp: Ack::Ack,
ack_check_en: true,
length: 1,
},
);
if data.len() > 1 {
self.write_cmd(
5,
Command::Read {
ack_value: Ack::Ack,
length: (data.len() - 1) as _,
},
);
self.write_cmd(
6,
Command::Read {
ack_value: Ack::Nack,
length: 1,
},
);
self.write_cmd(7, Command::Stop);
} else {
self.write_cmd(
5,
Command::Read {
ack_value: Ack::Nack,
length: 1,
},
);
self.write_cmd(6, Command::Stop);
}
self.clear_interrupts();
let ctrl = {
let mut result = 0;
result |= address as u32;
result |= (register as u32) << 11;
result |= 0u32 << 27; result
};
sens.sar_i2c_ctrl().write(|w| {
unsafe { w.sar_i2c_ctrl().bits(ctrl) };
w.sar_i2c_start_force().set_bit();
w.sar_i2c_start().set_bit()
});
for byte in data {
match self.wait_for_rx_interrupt() {
Ok(is_rx) => {
if is_rx {
*byte = self.i2c.register_block().data().read().i2c_rdata().bits();
self.i2c
.register_block()
.int_clr()
.write(|w| w.rx_data().clear_bit_by_one());
} else {
core::panic!("Peripheral didn't wait for data to be read");
}
}
Err(err) => {
sens.sar_i2c_ctrl().modify(|_, w| {
w.sar_i2c_start_force().clear_bit();
w.sar_i2c_start().clear_bit()
});
return Err(err);
}
}
}
let result = self.wait_for_complete_interrupt();
sens.sar_i2c_ctrl().modify(|_, w| {
w.sar_i2c_start_force()
.clear_bit()
.sar_i2c_start()
.clear_bit()
});
result
}
fn clear_interrupts(&self) {
self.i2c.register_block().int_clr().write(|w| {
w.trans_complete()
.clear_bit_by_one()
.tx_data()
.clear_bit_by_one()
.rx_data()
.clear_bit_by_one()
.ack_err()
.clear_bit_by_one()
.time_out()
.clear_bit_by_one()
.arbitration_lost()
.clear_bit_by_one()
});
}
fn wait_for_tx_interrupt(&self) -> Result<bool, Error> {
loop {
let int_raw = self.i2c.register_block().int_raw().read();
if int_raw.tx_data().bit_is_set() {
break Ok(true);
} else if int_raw.trans_complete().bit_is_set() {
break Ok(false);
} else if int_raw.time_out().bit_is_set() {
break Err(Error::TimeOut);
} else if int_raw.ack_err().bit_is_set() {
break Err(Error::AckCheckFailed);
} else if int_raw.arbitration_lost().bit_is_set() {
break Err(Error::ArbitrationLost);
}
}
}
fn wait_for_rx_interrupt(&self) -> Result<bool, Error> {
loop {
let int_raw = self.i2c.register_block().int_raw().read();
if int_raw.rx_data().bit_is_set() {
break Ok(true);
} else if int_raw.trans_complete().bit_is_set() {
break Ok(false);
} else if int_raw.time_out().bit_is_set() {
break Err(Error::TimeOut);
} else if int_raw.ack_err().bit_is_set() {
break Err(Error::AckCheckFailed);
} else if int_raw.arbitration_lost().bit_is_set() {
break Err(Error::ArbitrationLost);
}
}
}
fn wait_for_complete_interrupt(&self) -> Result<(), Error> {
loop {
let int_raw = self.i2c.register_block().int_raw().read();
if int_raw.trans_complete().bit_is_set() {
break Ok(());
} else if int_raw.time_out().bit_is_set() {
break Err(Error::TimeOut);
} else if int_raw.ack_err().bit_is_set() {
break Err(Error::AckCheckFailed);
} else if int_raw.arbitration_lost().bit_is_set() {
break Err(Error::ArbitrationLost);
}
}
}
fn write_cmd(&self, idx: usize, command: Command) {
let cmd = command.into();
self.i2c
.register_block()
.cmd(idx)
.write(|w| unsafe { w.command().bits(cmd) });
}
}
impl Drop for I2c<'_> {
fn drop(&mut self) {
fn release_pin(pin: u8) {
GPIO::regs().pin(pin as usize).reset();
RTC_IO::regs()
.enable_w1tc()
.write(|w| unsafe { w.enable_w1tc().bits(1 << pin) });
RTC_IO::regs().touch_pad(pin as usize).reset();
}
SENS::regs()
.sar_peri_reset_conf()
.modify(|_, w| w.sar_rtc_i2c_reset().set_bit());
SENS::regs()
.sar_peri_clk_gate_conf()
.modify(|_, w| w.rtc_i2c_clk_en().clear_bit());
release_pin(self.scl);
release_pin(self.sda);
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ConfigError {}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
TransactionSizeLimitExceeded,
AckCheckFailed,
TimeOut,
ArbitrationLost,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
timing: Timing,
timeout: Duration,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Timing {
scl_low_period: u32,
scl_high_period: u32,
sda_duty: u32,
scl_start_period: u32,
scl_stop_period: u32,
}
impl Timing {
pub fn standard_mode() -> Self {
Self::default()
.with_scl_low_period(clock_from_micros(5))
.with_scl_high_period(clock_from_micros(5))
.with_sda_duty(clock_from_micros(2))
.with_scl_start_period(clock_from_micros(3))
.with_scl_stop_period(clock_from_micros(6))
}
pub fn fast_mode() -> Self {
Self::default()
.with_scl_low_period(clock_from_nanos(1_400))
.with_scl_high_period(clock_from_nanos(300))
.with_sda_duty(clock_from_nanos(1_000))
.with_scl_start_period(clock_from_nanos(2_000))
.with_scl_stop_period(clock_from_nanos(1_300))
}
}
const fn clock_from_micros(micros: u64) -> u32 {
duration_to_clock(Duration::from_micros(micros))
}
const fn clock_from_nanos(nanos: u64) -> u32 {
duration_to_clock(Duration::from_nanos(nanos))
}
const fn duration_to_clock(value: Duration) -> u32 {
((value.as_nanos() * RC_FAST_CLK as u128) / 1_000_000_000) as u32
}
enum Command {
Start,
Stop,
Write {
ack_exp: Ack,
ack_check_en: bool,
length: u8,
},
Read {
ack_value: Ack,
length: u8,
},
}
#[derive(Eq, PartialEq, Copy, Clone)]
enum Ack {
Ack,
Nack,
}
impl From<Command> for u16 {
fn from(c: Command) -> u16 {
let opcode = match c {
Command::Start => 0,
Command::Stop => 3,
Command::Write { .. } => 1,
Command::Read { .. } => 2,
};
let length = match c {
Command::Start | Command::Stop => 0,
Command::Write { length: l, .. } | Command::Read { length: l, .. } => l,
};
let ack_exp = match c {
Command::Start | Command::Stop | Command::Read { .. } => Ack::Nack,
Command::Write { ack_exp: exp, .. } => exp,
};
let ack_check_en = match c {
Command::Start | Command::Stop | Command::Read { .. } => false,
Command::Write {
ack_check_en: en, ..
} => en,
};
let ack_value = match c {
Command::Start | Command::Stop | Command::Write { .. } => Ack::Nack,
Command::Read { ack_value: ack, .. } => ack,
};
let mut cmd: u16 = length.into();
if ack_check_en {
cmd |= 1 << 8;
} else {
cmd &= !(1 << 8);
}
if ack_exp == Ack::Nack {
cmd |= 1 << 9;
} else {
cmd &= !(1 << 9);
}
if ack_value == Ack::Nack {
cmd |= 1 << 10;
} else {
cmd &= !(1 << 10);
}
cmd |= opcode << 11;
cmd
}
}