use core::cmp; use core::future::poll_fn; use core::task::Poll; use atomic_polyfill::{AtomicUsize, Ordering}; use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::NoDma; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; use crate::interrupt::InterruptExt; use crate::pac::i2c; use crate::time::Hertz; use crate::Peripheral; #[non_exhaustive] #[derive(Copy, Clone)] pub struct Config { pub sda_pullup: bool, pub scl_pullup: bool, } impl Default for Config { fn default() -> Self { Self { sda_pullup: false, scl_pullup: false, } } } pub struct State { waker: AtomicWaker, chunks_transferred: AtomicUsize, } impl State { pub(crate) const fn new() -> Self { Self { waker: AtomicWaker::new(), chunks_transferred: AtomicUsize::new(0), } } } pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { _peri: PeripheralRef<'d, T>, tx_dma: PeripheralRef<'d, TXDMA>, #[allow(dead_code)] rx_dma: PeripheralRef<'d, RXDMA>, } impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub fn new( peri: impl Peripheral
+ 'd, scl: impl Peripheral
> + 'd, sda: impl Peripheral
> + 'd, irq: impl Peripheral
+ 'd, tx_dma: impl Peripheral
+ 'd, rx_dma: impl Peripheral
+ 'd,
freq: Hertz,
config: Config,
) -> Self {
into_ref!(peri, irq, scl, sda, tx_dma, rx_dma);
T::enable();
T::reset();
unsafe {
scl.set_as_af_pull(
scl.af_num(),
AFType::OutputOpenDrain,
match config.scl_pullup {
true => Pull::Up,
false => Pull::None,
},
);
sda.set_as_af_pull(
sda.af_num(),
AFType::OutputOpenDrain,
match config.sda_pullup {
true => Pull::Up,
false => Pull::None,
},
);
}
unsafe {
T::regs().cr1().modify(|reg| {
reg.set_pe(false);
reg.set_anfoff(false);
});
}
let timings = Timings::new(T::frequency(), freq.into());
unsafe {
T::regs().timingr().write(|reg| {
reg.set_presc(timings.prescale);
reg.set_scll(timings.scll);
reg.set_sclh(timings.sclh);
reg.set_sdadel(timings.sdadel);
reg.set_scldel(timings.scldel);
});
}
unsafe {
T::regs().cr1().modify(|reg| {
reg.set_pe(true);
});
}
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
Self {
_peri: peri,
tx_dma,
rx_dma,
}
}
unsafe fn on_interrupt(_: *mut ()) {
let regs = T::regs();
let isr = regs.isr().read();
if isr.tcr() || isr.tc() {
let state = T::state();
state.chunks_transferred.fetch_add(1, Ordering::Relaxed);
state.waker.wake();
}
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
// the interrupt
critical_section::with(|_| {
regs.cr1().modify(|w| w.set_tcie(false));
});
}
fn master_stop(&mut self) {
unsafe {
T::regs().cr2().write(|w| w.set_stop(true));
}
}
unsafe fn master_read(
address: u8,
length: usize,
stop: Stop,
reload: bool,
restart: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
assert!(length < 256);
if !restart {
// Wait for any previous address sequence to end
// automatically. This could be up to 50% of a bus
// cycle (ie. up to 0.5/freq)
while T::regs().cr2().read().start() {
check_timeout()?;
}
}
// Set START and prepare to receive bytes into
// `buffer`. The START bit can be set even if the bus
// is BUSY or I2C is in slave mode.
let reload = if reload {
i2c::vals::Reload::NOTCOMPLETED
} else {
i2c::vals::Reload::COMPLETED
};
T::regs().cr2().modify(|w| {
w.set_sadd((address << 1 | 0) as u16);
w.set_add10(i2c::vals::Addmode::BIT7);
w.set_dir(i2c::vals::Dir::READ);
w.set_nbytes(length as u8);
w.set_start(true);
w.set_autoend(stop.autoend());
w.set_reload(reload);
});
Ok(())
}
unsafe fn master_write(
address: u8,
length: usize,
stop: Stop,
reload: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
assert!(length < 256);
// Wait for any previous address sequence to end
// automatically. This could be up to 50% of a bus
// cycle (ie. up to 0.5/freq)
while T::regs().cr2().read().start() {
check_timeout()?;
}
let reload = if reload {
i2c::vals::Reload::NOTCOMPLETED
} else {
i2c::vals::Reload::COMPLETED
};
// Set START and prepare to send `bytes`. The
// START bit can be set even if the bus is BUSY or
// I2C is in slave mode.
T::regs().cr2().modify(|w| {
w.set_sadd((address << 1 | 0) as u16);
w.set_add10(i2c::vals::Addmode::BIT7);
w.set_dir(i2c::vals::Dir::WRITE);
w.set_nbytes(length as u8);
w.set_start(true);
w.set_autoend(stop.autoend());
w.set_reload(reload);
});
Ok(())
}
unsafe fn master_continue(
length: usize,
reload: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
assert!(length < 256 && length > 0);
while !T::regs().isr().read().tcr() {
check_timeout()?;
}
let reload = if reload {
i2c::vals::Reload::NOTCOMPLETED
} else {
i2c::vals::Reload::COMPLETED
};
T::regs().cr2().modify(|w| {
w.set_nbytes(length as u8);
w.set_reload(reload);
});
Ok(())
}
fn flush_txdr(&self) {
//if $i2c.isr.read().txis().bit_is_set() {
//$i2c.txdr.write(|w| w.txdata().bits(0));
//}
unsafe {
if T::regs().isr().read().txis() {
T::regs().txdr().write(|w| w.set_txdata(0));
}
if T::regs().isr().read().txe() {
T::regs().isr().modify(|w| w.set_txe(true))
}
}
// If TXDR is not flagged as empty, write 1 to flush it
//if $i2c.isr.read().txe().is_not_empty() {
//$i2c.isr.write(|w| w.txe().set_bit());
//}
}
fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
loop {
unsafe {
let isr = T::regs().isr().read();
if isr.txe() {
return Ok(());
} else if isr.berr() {
T::regs().icr().write(|reg| reg.set_berrcf(true));
return Err(Error::Bus);
} else if isr.arlo() {
T::regs().icr().write(|reg| reg.set_arlocf(true));
return Err(Error::Arbitration);
} else if isr.nackf() {
T::regs().icr().write(|reg| reg.set_nackcf(true));
self.flush_txdr();
return Err(Error::Nack);
}
}
check_timeout()?;
}
}
fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
loop {
unsafe {
let isr = T::regs().isr().read();
if isr.rxne() {
return Ok(());
} else if isr.berr() {
T::regs().icr().write(|reg| reg.set_berrcf(true));
return Err(Error::Bus);
} else if isr.arlo() {
T::regs().icr().write(|reg| reg.set_arlocf(true));
return Err(Error::Arbitration);
} else if isr.nackf() {
T::regs().icr().write(|reg| reg.set_nackcf(true));
self.flush_txdr();
return Err(Error::Nack);
}
}
check_timeout()?;
}
}
fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
loop {
unsafe {
let isr = T::regs().isr().read();
if isr.tc() {
return Ok(());
} else if isr.berr() {
T::regs().icr().write(|reg| reg.set_berrcf(true));
return Err(Error::Bus);
} else if isr.arlo() {
T::regs().icr().write(|reg| reg.set_arlocf(true));
return Err(Error::Arbitration);
} else if isr.nackf() {
T::regs().icr().write(|reg| reg.set_nackcf(true));
self.flush_txdr();
return Err(Error::Nack);
}
}
check_timeout()?;
}
}
fn read_internal(
&mut self,
address: u8,
buffer: &mut [u8],
restart: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
let completed_chunks = buffer.len() / 255;
let total_chunks = if completed_chunks * 255 == buffer.len() {
completed_chunks
} else {
completed_chunks + 1
};
let last_chunk_idx = total_chunks.saturating_sub(1);
unsafe {
Self::master_read(
address,
buffer.len().min(255),
Stop::Automatic,
last_chunk_idx != 0,
restart,
&check_timeout,
)?;
}
for (number, chunk) in buffer.chunks_mut(255).enumerate() {
if number != 0 {
// NOTE(unsafe) We have &mut self
unsafe {
Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
}
}
for byte in chunk {
// Wait until we have received something
self.wait_rxne(&check_timeout)?;
unsafe {
*byte = T::regs().rxdr().read().rxdata();
}
}
}
Ok(())
}
fn write_internal(
&mut self,
address: u8,
bytes: &[u8],
send_stop: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error> {
let completed_chunks = bytes.len() / 255;
let total_chunks = if completed_chunks * 255 == bytes.len() {
completed_chunks
} else {
completed_chunks + 1
};
let last_chunk_idx = total_chunks.saturating_sub(1);
// I2C start
//
// ST SAD+W
// NOTE(unsafe) We have &mut self
unsafe {
Self::master_write(
address,
bytes.len().min(255),
Stop::Software,
last_chunk_idx != 0,
&check_timeout,
)?;
}
for (number, chunk) in bytes.chunks(255).enumerate() {
if number != 0 {
// NOTE(unsafe) We have &mut self
unsafe {
Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
}
}
for byte in chunk {
// Wait until we are allowed to send data
// (START has been ACKed or last byte when
// through)
self.wait_txe(&check_timeout)?;
unsafe {
T::regs().txdr().write(|w| w.set_txdata(*byte));
}
}
}
// Wait until the write finishes
self.wait_tc(&check_timeout)?;
if send_stop {
self.master_stop();
}
Ok(())
}
async fn write_dma_internal(
&mut self,
address: u8,
bytes: &[u8],
first_slice: bool,
last_slice: bool,
check_timeout: impl Fn() -> Result<(), Error>,
) -> Result<(), Error>
where
TXDMA: crate::i2c::TxDma