use core::{marker::PhantomData, ops::Deref};
use crate::{
gpio::pin::bank0::BankPinId,
gpio::pin::{FunctionI2C, Pin, PinId},
resets::SubsystemReset,
};
use pac::{i2c0::RegisterBlock as I2CBlock, RESETS};
use super::{Peripheral, SclPin, SdaPin, I2C};
#[derive(Debug, PartialEq, Eq)]
pub enum I2CEvent {
Start,
Restart,
TransferRead,
TransferWrite,
Stop,
}
#[derive(Debug, Clone, Copy)]
enum State {
Idle,
Active,
Read,
Write,
}
pub struct I2CPeripheralEventIterator<Block, Pins> {
i2c: I2C<Block, Pins, Peripheral>,
state: State,
}
impl<T, Sda, Scl> I2C<T, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>), Peripheral>
where
T: SubsystemReset + Deref<Target = I2CBlock>,
Sda: PinId + BankPinId,
Scl: PinId + BankPinId,
{
#[allow(clippy::type_complexity)]
pub fn new_peripheral_event_iterator(
i2c: T,
sda_pin: Pin<Sda, FunctionI2C>,
scl_pin: Pin<Scl, FunctionI2C>,
resets: &mut RESETS,
addr: u16,
) -> I2CPeripheralEventIterator<T, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>)>
where
Sda: SdaPin<T>,
Scl: SclPin<T>,
{
i2c.reset_bring_down(resets);
i2c.reset_bring_up(resets);
i2c.ic_enable.write(|w| w.enable().disabled());
i2c.ic_sar.write(|w| unsafe { w.ic_sar().bits(addr) });
i2c.ic_con.modify(|_, w| {
w.speed().fast();
w.master_mode().disabled();
w.ic_slave_disable().slave_enabled();
w.rx_fifo_full_hld_ctrl().enabled();
w.ic_restart_en().enabled();
w.tx_empty_ctrl().enabled()
});
i2c.ic_tx_tl.write(|w| unsafe { w.tx_tl().bits(0) });
i2c.ic_rx_tl.write(|w| unsafe { w.rx_tl().bits(0) });
i2c.ic_enable.write(|w| w.enable().enabled());
I2CPeripheralEventIterator {
i2c: Self {
i2c,
pins: (sda_pin, scl_pin),
mode: PhantomData,
},
state: State::Idle,
}
}
}
impl<T: Deref<Target = I2CBlock>, PINS> I2CPeripheralEventIterator<T, PINS> {
pub fn write(&mut self, buf: &[u8]) -> usize {
self.i2c.i2c.ic_clr_tx_abrt.read();
let mut sent = 0;
for &b in buf.iter() {
if self.i2c.tx_fifo_full() {
break;
}
self.i2c
.i2c
.ic_data_cmd
.write(|w| unsafe { w.dat().bits(b) });
sent += 1;
}
self.i2c.i2c.ic_clr_rd_req.read();
sent
}
pub fn read(&mut self, buf: &mut [u8]) -> usize {
let mut read = 0;
for b in buf.iter_mut() {
if self.i2c.rx_fifo_empty() {
break;
}
*b = self.i2c.i2c.ic_data_cmd.read().dat().bits();
read += 1;
}
read
}
}
impl<T: Deref<Target = I2CBlock>, PINS> Iterator for I2CPeripheralEventIterator<T, PINS> {
type Item = I2CEvent;
fn next(&mut self) -> Option<Self::Item> {
let stat = self.i2c.i2c.ic_raw_intr_stat.read();
self.i2c.i2c.ic_clr_activity.read();
match self.state {
State::Idle if stat.start_det().bit_is_set() => {
self.i2c.i2c.ic_clr_start_det.read();
self.state = State::Active;
Some(I2CEvent::Start)
}
State::Active if !self.i2c.rx_fifo_empty() => {
self.state = State::Write;
Some(I2CEvent::TransferWrite)
}
State::Active if stat.rd_req().bit_is_set() => {
self.state = State::Read;
Some(I2CEvent::TransferRead)
}
State::Read if stat.rd_req().bit_is_set() => Some(I2CEvent::TransferRead),
State::Read if stat.restart_det().bit_is_set() => {
self.i2c.i2c.ic_clr_restart_det.read();
self.state = State::Active;
Some(I2CEvent::Restart)
}
State::Write if !self.i2c.rx_fifo_empty() => Some(I2CEvent::TransferWrite),
State::Write if stat.restart_det().bit_is_set() => {
self.i2c.i2c.ic_clr_restart_det.read();
self.state = State::Active;
Some(I2CEvent::Restart)
}
_ if stat.stop_det().bit_is_set() => {
self.i2c.i2c.ic_clr_stop_det.read();
self.state = State::Idle;
Some(I2CEvent::Stop)
}
_ => None,
}
}
}
impl<Block, Sda, Scl>
I2CPeripheralEventIterator<Block, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>)>
where
Block: SubsystemReset + Deref<Target = I2CBlock>,
Sda: PinId + BankPinId,
Scl: PinId + BankPinId,
{
#[allow(clippy::type_complexity)]
pub fn free(
self,
resets: &mut RESETS,
) -> (Block, (Pin<Sda, FunctionI2C>, Pin<Scl, FunctionI2C>)) {
self.i2c.free(resets)
}
}