842 lines
25 KiB
Rust
842 lines
25 KiB
Rust
use core::future::poll_fn;
|
||
use core::marker::PhantomData;
|
||
use core::task::Poll;
|
||
|
||
use embassy_embedded_hal::SetConfig;
|
||
use embassy_futures::select::{select, Either};
|
||
use embassy_hal_internal::drop::OnDrop;
|
||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||
|
||
use super::*;
|
||
use crate::dma::{NoDma, Transfer};
|
||
use crate::gpio::sealed::AFType;
|
||
use crate::gpio::Pull;
|
||
use crate::interrupt::typelevel::Interrupt;
|
||
use crate::pac::i2c;
|
||
use crate::time::Hertz;
|
||
use crate::{interrupt, Peripheral};
|
||
|
||
pub unsafe fn on_interrupt<T: Instance>() {
|
||
let regs = T::regs();
|
||
// i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of
|
||
// other stuff, so we wake the task on every interrupt.
|
||
T::state().waker.wake();
|
||
critical_section::with(|_| {
|
||
// Clear event interrupt flag.
|
||
regs.cr2().modify(|w| {
|
||
w.set_itevten(false);
|
||
w.set_iterren(false);
|
||
});
|
||
});
|
||
}
|
||
|
||
#[non_exhaustive]
|
||
#[derive(Copy, Clone, Default)]
|
||
pub struct Config {
|
||
pub sda_pullup: bool,
|
||
pub scl_pullup: bool,
|
||
}
|
||
|
||
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||
phantom: PhantomData<&'d mut T>,
|
||
#[allow(dead_code)]
|
||
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<P = T> + 'd,
|
||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||
_irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
|
||
+ interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
|
||
+ 'd,
|
||
tx_dma: impl Peripheral<P = TXDMA> + 'd,
|
||
rx_dma: impl Peripheral<P = RXDMA> + 'd,
|
||
freq: Hertz,
|
||
config: Config,
|
||
) -> Self {
|
||
into_ref!(scl, sda, tx_dma, rx_dma);
|
||
|
||
T::enable_and_reset();
|
||
|
||
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,
|
||
},
|
||
);
|
||
|
||
T::regs().cr1().modify(|reg| {
|
||
reg.set_pe(false);
|
||
//reg.set_anfoff(false);
|
||
});
|
||
|
||
let timings = Timings::new(T::frequency(), freq);
|
||
|
||
T::regs().cr2().modify(|reg| {
|
||
reg.set_freq(timings.freq);
|
||
});
|
||
T::regs().ccr().modify(|reg| {
|
||
reg.set_f_s(timings.mode.f_s());
|
||
reg.set_duty(timings.duty.duty());
|
||
reg.set_ccr(timings.ccr);
|
||
});
|
||
T::regs().trise().modify(|reg| {
|
||
reg.set_trise(timings.trise);
|
||
});
|
||
|
||
T::regs().cr1().modify(|reg| {
|
||
reg.set_pe(true);
|
||
});
|
||
|
||
unsafe { T::EventInterrupt::enable() };
|
||
unsafe { T::ErrorInterrupt::enable() };
|
||
|
||
Self {
|
||
phantom: PhantomData,
|
||
tx_dma,
|
||
rx_dma,
|
||
}
|
||
}
|
||
|
||
fn check_and_clear_error_flags() -> Result<i2c::regs::Sr1, Error> {
|
||
// Note that flags should only be cleared once they have been registered. If flags are
|
||
// cleared otherwise, there may be an inherent race condition and flags may be missed.
|
||
let sr1 = T::regs().sr1().read();
|
||
|
||
if sr1.timeout() {
|
||
T::regs().sr1().write(|reg| {
|
||
reg.0 = !0;
|
||
reg.set_timeout(false);
|
||
});
|
||
return Err(Error::Timeout);
|
||
}
|
||
|
||
if sr1.pecerr() {
|
||
T::regs().sr1().write(|reg| {
|
||
reg.0 = !0;
|
||
reg.set_pecerr(false);
|
||
});
|
||
return Err(Error::Crc);
|
||
}
|
||
|
||
if sr1.ovr() {
|
||
T::regs().sr1().write(|reg| {
|
||
reg.0 = !0;
|
||
reg.set_ovr(false);
|
||
});
|
||
return Err(Error::Overrun);
|
||
}
|
||
|
||
if sr1.af() {
|
||
T::regs().sr1().write(|reg| {
|
||
reg.0 = !0;
|
||
reg.set_af(false);
|
||
});
|
||
return Err(Error::Nack);
|
||
}
|
||
|
||
if sr1.arlo() {
|
||
T::regs().sr1().write(|reg| {
|
||
reg.0 = !0;
|
||
reg.set_arlo(false);
|
||
});
|
||
return Err(Error::Arbitration);
|
||
}
|
||
|
||
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
||
// clearing the BERR bit instead.
|
||
if sr1.berr() {
|
||
T::regs().sr1().write(|reg| {
|
||
reg.0 = !0;
|
||
reg.set_berr(false);
|
||
});
|
||
}
|
||
|
||
Ok(sr1)
|
||
}
|
||
|
||
fn write_bytes(
|
||
&mut self,
|
||
addr: u8,
|
||
bytes: &[u8],
|
||
check_timeout: impl Fn() -> Result<(), Error>,
|
||
) -> Result<(), Error> {
|
||
// Send a START condition
|
||
|
||
T::regs().cr1().modify(|reg| {
|
||
reg.set_start(true);
|
||
});
|
||
|
||
// Wait until START condition was generated
|
||
while !Self::check_and_clear_error_flags()?.start() {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Also wait until signalled we're master and everything is waiting for us
|
||
while {
|
||
Self::check_and_clear_error_flags()?;
|
||
|
||
let sr2 = T::regs().sr2().read();
|
||
!sr2.msl() && !sr2.busy()
|
||
} {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Set up current address, we're trying to talk to
|
||
T::regs().dr().write(|reg| reg.set_dr(addr << 1));
|
||
|
||
// Wait until address was sent
|
||
// Wait for the address to be acknowledged
|
||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||
while !Self::check_and_clear_error_flags()?.addr() {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Clear condition by reading SR2
|
||
let _ = T::regs().sr2().read();
|
||
|
||
// Send bytes
|
||
for c in bytes {
|
||
self.send_byte(*c, &check_timeout)?;
|
||
}
|
||
|
||
// Fallthrough is success
|
||
Ok(())
|
||
}
|
||
|
||
fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
|
||
// Wait until we're ready for sending
|
||
while {
|
||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||
!Self::check_and_clear_error_flags()?.txe()
|
||
} {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Push out a byte of data
|
||
T::regs().dr().write(|reg| reg.set_dr(byte));
|
||
|
||
// Wait until byte is transferred
|
||
while {
|
||
// Check for any potential error conditions.
|
||
!Self::check_and_clear_error_flags()?.btf()
|
||
} {
|
||
check_timeout()?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
|
||
while {
|
||
// Check for any potential error conditions.
|
||
Self::check_and_clear_error_flags()?;
|
||
|
||
!T::regs().sr1().read().rxne()
|
||
} {
|
||
check_timeout()?;
|
||
}
|
||
|
||
let value = T::regs().dr().read().dr();
|
||
Ok(value)
|
||
}
|
||
|
||
pub fn blocking_read_timeout(
|
||
&mut self,
|
||
addr: u8,
|
||
buffer: &mut [u8],
|
||
check_timeout: impl Fn() -> Result<(), Error>,
|
||
) -> Result<(), Error> {
|
||
if let Some((last, buffer)) = buffer.split_last_mut() {
|
||
// Send a START condition and set ACK bit
|
||
T::regs().cr1().modify(|reg| {
|
||
reg.set_start(true);
|
||
reg.set_ack(true);
|
||
});
|
||
|
||
// Wait until START condition was generated
|
||
while !Self::check_and_clear_error_flags()?.start() {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Also wait until signalled we're master and everything is waiting for us
|
||
while {
|
||
let sr2 = T::regs().sr2().read();
|
||
!sr2.msl() && !sr2.busy()
|
||
} {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Set up current address, we're trying to talk to
|
||
T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1));
|
||
|
||
// Wait until address was sent
|
||
// Wait for the address to be acknowledged
|
||
while !Self::check_and_clear_error_flags()?.addr() {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Clear condition by reading SR2
|
||
let _ = T::regs().sr2().read();
|
||
|
||
// Receive bytes into buffer
|
||
for c in buffer {
|
||
*c = self.recv_byte(&check_timeout)?;
|
||
}
|
||
|
||
// Prepare to send NACK then STOP after next byte
|
||
T::regs().cr1().modify(|reg| {
|
||
reg.set_ack(false);
|
||
reg.set_stop(true);
|
||
});
|
||
|
||
// Receive last byte
|
||
*last = self.recv_byte(&check_timeout)?;
|
||
|
||
// Wait for the STOP to be sent.
|
||
while T::regs().cr1().read().stop() {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Fallthrough is success
|
||
Ok(())
|
||
} else {
|
||
Err(Error::Overrun)
|
||
}
|
||
}
|
||
|
||
pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> {
|
||
self.blocking_read_timeout(addr, read, || Ok(()))
|
||
}
|
||
|
||
pub fn blocking_write_timeout(
|
||
&mut self,
|
||
addr: u8,
|
||
write: &[u8],
|
||
check_timeout: impl Fn() -> Result<(), Error>,
|
||
) -> Result<(), Error> {
|
||
self.write_bytes(addr, write, &check_timeout)?;
|
||
// Send a STOP condition
|
||
T::regs().cr1().modify(|reg| reg.set_stop(true));
|
||
// Wait for STOP condition to transmit.
|
||
while T::regs().cr1().read().stop() {
|
||
check_timeout()?;
|
||
}
|
||
|
||
// Fallthrough is success
|
||
Ok(())
|
||
}
|
||
|
||
pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> {
|
||
self.blocking_write_timeout(addr, write, || Ok(()))
|
||
}
|
||
|
||
pub fn blocking_write_read_timeout(
|
||
&mut self,
|
||
addr: u8,
|
||
write: &[u8],
|
||
read: &mut [u8],
|
||
check_timeout: impl Fn() -> Result<(), Error>,
|
||
) -> Result<(), Error> {
|
||
self.write_bytes(addr, write, &check_timeout)?;
|
||
self.blocking_read_timeout(addr, read, &check_timeout)?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
||
self.blocking_write_read_timeout(addr, write, read, || Ok(()))
|
||
}
|
||
|
||
// Async
|
||
|
||
#[inline] // pretty sure this should always be inlined
|
||
fn enable_interrupts() -> () {
|
||
T::regs().cr2().modify(|w| {
|
||
w.set_iterren(true);
|
||
w.set_itevten(true);
|
||
});
|
||
}
|
||
|
||
async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error>
|
||
where
|
||
TXDMA: crate::i2c::TxDma<T>,
|
||
{
|
||
let dma_transfer = unsafe {
|
||
let regs = T::regs();
|
||
regs.cr2().modify(|w| {
|
||
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
|
||
w.set_dmaen(true);
|
||
w.set_itbufen(false);
|
||
});
|
||
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
|
||
let dst = regs.dr().as_ptr() as *mut u8;
|
||
|
||
let ch = &mut self.tx_dma;
|
||
let request = ch.request();
|
||
Transfer::new_write(ch, request, write, dst, Default::default())
|
||
};
|
||
|
||
let on_drop = OnDrop::new(|| {
|
||
let regs = T::regs();
|
||
regs.cr2().modify(|w| {
|
||
w.set_dmaen(false);
|
||
w.set_iterren(false);
|
||
w.set_itevten(false);
|
||
})
|
||
});
|
||
|
||
Self::enable_interrupts();
|
||
|
||
// Send a START condition
|
||
T::regs().cr1().modify(|reg| {
|
||
reg.set_start(true);
|
||
});
|
||
|
||
let state = T::state();
|
||
|
||
// Wait until START condition was generated
|
||
poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
|
||
match Self::check_and_clear_error_flags() {
|
||
Err(e) => Poll::Ready(Err(e)),
|
||
Ok(sr1) => {
|
||
if sr1.start() {
|
||
Poll::Ready(Ok(()))
|
||
} else {
|
||
Poll::Pending
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.await?;
|
||
|
||
// Also wait until signalled we're master and everything is waiting for us
|
||
Self::enable_interrupts();
|
||
poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
|
||
match Self::check_and_clear_error_flags() {
|
||
Err(e) => Poll::Ready(Err(e)),
|
||
Ok(_) => {
|
||
let sr2 = T::regs().sr2().read();
|
||
if !sr2.msl() && !sr2.busy() {
|
||
Poll::Pending
|
||
} else {
|
||
Poll::Ready(Ok(()))
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.await?;
|
||
|
||
// Set up current address, we're trying to talk to
|
||
Self::enable_interrupts();
|
||
T::regs().dr().write(|reg| reg.set_dr(address << 1));
|
||
|
||
poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
match Self::check_and_clear_error_flags() {
|
||
Err(e) => Poll::Ready(Err(e)),
|
||
Ok(sr1) => {
|
||
if sr1.addr() {
|
||
// Clear the ADDR condition by reading SR2.
|
||
T::regs().sr2().read();
|
||
Poll::Ready(Ok(()))
|
||
} else {
|
||
Poll::Pending
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.await?;
|
||
Self::enable_interrupts();
|
||
let poll_error = poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
|
||
match Self::check_and_clear_error_flags() {
|
||
// Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other
|
||
// identical poll_fn check_and_clear matches.
|
||
Err(e) => Poll::Ready(Err::<T, Error>(e)),
|
||
Ok(_) => Poll::Pending,
|
||
}
|
||
});
|
||
|
||
// Wait for either the DMA transfer to successfully finish, or an I2C error to occur.
|
||
match select(dma_transfer, poll_error).await {
|
||
Either::Second(Err(e)) => Err(e),
|
||
_ => Ok(()),
|
||
}?;
|
||
|
||
// The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too.
|
||
|
||
// 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA
|
||
// requests then wait for a BTF event before programming the Stop condition.”
|
||
|
||
// TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it?
|
||
T::regs().cr2().modify(|w| {
|
||
w.set_dmaen(false);
|
||
});
|
||
|
||
Self::enable_interrupts();
|
||
poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
|
||
match Self::check_and_clear_error_flags() {
|
||
Err(e) => Poll::Ready(Err(e)),
|
||
Ok(sr1) => {
|
||
if sr1.btf() {
|
||
if send_stop {
|
||
T::regs().cr1().modify(|w| {
|
||
w.set_stop(true);
|
||
});
|
||
}
|
||
|
||
Poll::Ready(Ok(()))
|
||
} else {
|
||
Poll::Pending
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.await?;
|
||
|
||
drop(on_drop);
|
||
|
||
// Fallthrough is success
|
||
Ok(())
|
||
}
|
||
|
||
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
|
||
where
|
||
TXDMA: crate::i2c::TxDma<T>,
|
||
{
|
||
self.write_with_stop(address, write, true).await?;
|
||
|
||
// Wait for STOP condition to transmit.
|
||
Self::enable_interrupts();
|
||
poll_fn(|cx| {
|
||
T::state().waker.register(cx.waker());
|
||
// TODO: error interrupts are enabled here, should we additional check for and return errors?
|
||
if T::regs().cr1().read().stop() {
|
||
Poll::Pending
|
||
} else {
|
||
Poll::Ready(Ok(()))
|
||
}
|
||
})
|
||
.await?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error>
|
||
where
|
||
RXDMA: crate::i2c::RxDma<T>,
|
||
{
|
||
let state = T::state();
|
||
let buffer_len = buffer.len();
|
||
|
||
let dma_transfer = unsafe {
|
||
let regs = T::regs();
|
||
regs.cr2().modify(|w| {
|
||
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
|
||
w.set_itbufen(false);
|
||
w.set_dmaen(true);
|
||
});
|
||
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
|
||
let src = regs.dr().as_ptr() as *mut u8;
|
||
|
||
let ch = &mut self.rx_dma;
|
||
let request = ch.request();
|
||
Transfer::new_read(ch, request, src, buffer, Default::default())
|
||
};
|
||
|
||
let on_drop = OnDrop::new(|| {
|
||
let regs = T::regs();
|
||
regs.cr2().modify(|w| {
|
||
w.set_dmaen(false);
|
||
w.set_iterren(false);
|
||
w.set_itevten(false);
|
||
})
|
||
});
|
||
|
||
Self::enable_interrupts();
|
||
|
||
// Send a START condition and set ACK bit
|
||
T::regs().cr1().modify(|reg| {
|
||
reg.set_start(true);
|
||
reg.set_ack(true);
|
||
});
|
||
|
||
// Wait until START condition was generated
|
||
poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
|
||
match Self::check_and_clear_error_flags() {
|
||
Err(e) => Poll::Ready(Err(e)),
|
||
Ok(sr1) => {
|
||
if sr1.start() {
|
||
Poll::Ready(Ok(()))
|
||
} else {
|
||
Poll::Pending
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.await?;
|
||
|
||
// Also wait until signalled we're master and everything is waiting for us
|
||
Self::enable_interrupts();
|
||
poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
|
||
// blocking read didn’t have a check_and_clear call here, but blocking write did so
|
||
// I’m adding it here in case that was an oversight.
|
||
match Self::check_and_clear_error_flags() {
|
||
Err(e) => Poll::Ready(Err(e)),
|
||
Ok(_) => {
|
||
let sr2 = T::regs().sr2().read();
|
||
if !sr2.msl() && !sr2.busy() {
|
||
Poll::Pending
|
||
} else {
|
||
Poll::Ready(Ok(()))
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.await?;
|
||
|
||
// Set up current address, we're trying to talk to
|
||
T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1));
|
||
|
||
// Wait for the address to be acknowledged
|
||
|
||
Self::enable_interrupts();
|
||
poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
|
||
match Self::check_and_clear_error_flags() {
|
||
Err(e) => Poll::Ready(Err(e)),
|
||
Ok(sr1) => {
|
||
if sr1.addr() {
|
||
// 18.3.8: When a single byte must be received: the NACK must be programmed during EV6
|
||
// event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag.
|
||
if buffer_len == 1 {
|
||
T::regs().cr1().modify(|w| {
|
||
w.set_ack(false);
|
||
});
|
||
}
|
||
Poll::Ready(Ok(()))
|
||
} else {
|
||
Poll::Pending
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.await?;
|
||
|
||
// Clear ADDR condition by reading SR2
|
||
T::regs().sr2().read();
|
||
|
||
// 18.3.8: When a single byte must be received: [snip] Then the
|
||
// user can program the STOP condition either after clearing ADDR flag, or in the
|
||
// DMA Transfer Complete interrupt routine.
|
||
if buffer_len == 1 {
|
||
T::regs().cr1().modify(|w| {
|
||
w.set_stop(true);
|
||
});
|
||
} else {
|
||
// If, in the I2C_CR2 register, the LAST bit is set, I2C
|
||
// automatically sends a NACK after the next byte following EOT_1. The user can
|
||
// generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled.
|
||
T::regs().cr2().modify(|w| {
|
||
w.set_last(true);
|
||
})
|
||
}
|
||
|
||
// Wait for bytes to be received, or an error to occur.
|
||
Self::enable_interrupts();
|
||
let poll_error = poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
|
||
match Self::check_and_clear_error_flags() {
|
||
Err(e) => Poll::Ready(Err::<T, Error>(e)),
|
||
_ => Poll::Pending,
|
||
}
|
||
});
|
||
|
||
match select(dma_transfer, poll_error).await {
|
||
Either::Second(Err(e)) => Err(e),
|
||
_ => Ok(()),
|
||
}?;
|
||
|
||
// Wait for the STOP to be sent (STOP bit cleared).
|
||
Self::enable_interrupts();
|
||
poll_fn(|cx| {
|
||
state.waker.register(cx.waker());
|
||
// TODO: error interrupts are enabled here, should we additional check for and return errors?
|
||
if T::regs().cr1().read().stop() {
|
||
Poll::Pending
|
||
} else {
|
||
Poll::Ready(Ok(()))
|
||
}
|
||
})
|
||
.await?;
|
||
drop(on_drop);
|
||
|
||
// Fallthrough is success
|
||
Ok(())
|
||
}
|
||
|
||
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
|
||
where
|
||
RXDMA: crate::i2c::RxDma<T>,
|
||
TXDMA: crate::i2c::TxDma<T>,
|
||
{
|
||
self.write_with_stop(address, write, false).await?;
|
||
self.read(address, read).await
|
||
}
|
||
}
|
||
|
||
impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
||
fn drop(&mut self) {
|
||
T::disable();
|
||
}
|
||
}
|
||
|
||
enum Mode {
|
||
Fast,
|
||
Standard,
|
||
}
|
||
|
||
impl Mode {
|
||
fn f_s(&self) -> i2c::vals::FS {
|
||
match self {
|
||
Mode::Fast => i2c::vals::FS::FAST,
|
||
Mode::Standard => i2c::vals::FS::STANDARD,
|
||
}
|
||
}
|
||
}
|
||
|
||
enum Duty {
|
||
Duty2_1,
|
||
Duty16_9,
|
||
}
|
||
|
||
impl Duty {
|
||
fn duty(&self) -> i2c::vals::Duty {
|
||
match self {
|
||
Duty::Duty2_1 => i2c::vals::Duty::DUTY2_1,
|
||
Duty::Duty16_9 => i2c::vals::Duty::DUTY16_9,
|
||
}
|
||
}
|
||
}
|
||
|
||
struct Timings {
|
||
freq: u8,
|
||
mode: Mode,
|
||
trise: u8,
|
||
ccr: u16,
|
||
duty: Duty,
|
||
}
|
||
|
||
impl Timings {
|
||
fn new(i2cclk: Hertz, speed: Hertz) -> Self {
|
||
// Calculate settings for I2C speed modes
|
||
let speed = speed.0;
|
||
let clock = i2cclk.0;
|
||
let freq = clock / 1_000_000;
|
||
assert!((2..=50).contains(&freq));
|
||
|
||
// Configure bus frequency into I2C peripheral
|
||
let trise = if speed <= 100_000 {
|
||
freq + 1
|
||
} else {
|
||
(freq * 300) / 1000 + 1
|
||
};
|
||
|
||
let mut ccr;
|
||
let duty;
|
||
let mode;
|
||
|
||
// I2C clock control calculation
|
||
if speed <= 100_000 {
|
||
duty = Duty::Duty2_1;
|
||
mode = Mode::Standard;
|
||
ccr = {
|
||
let ccr = clock / (speed * 2);
|
||
if ccr < 4 {
|
||
4
|
||
} else {
|
||
ccr
|
||
}
|
||
};
|
||
} else {
|
||
const DUTYCYCLE: u8 = 0;
|
||
mode = Mode::Fast;
|
||
if DUTYCYCLE == 0 {
|
||
duty = Duty::Duty2_1;
|
||
ccr = clock / (speed * 3);
|
||
ccr = if ccr < 1 { 1 } else { ccr };
|
||
|
||
// Set clock to fast mode with appropriate parameters for selected speed (2:1 duty cycle)
|
||
} else {
|
||
duty = Duty::Duty16_9;
|
||
ccr = clock / (speed * 25);
|
||
ccr = if ccr < 1 { 1 } else { ccr };
|
||
|
||
// Set clock to fast mode with appropriate parameters for selected speed (16:9 duty cycle)
|
||
}
|
||
}
|
||
|
||
Self {
|
||
freq: freq as u8,
|
||
trise: trise as u8,
|
||
ccr: ccr as u16,
|
||
duty,
|
||
mode,
|
||
//prescale: presc_reg,
|
||
//scll,
|
||
//sclh,
|
||
//sdadel,
|
||
//scldel,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'d, T: Instance> SetConfig for I2c<'d, T> {
|
||
type Config = Hertz;
|
||
type ConfigError = ();
|
||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
||
let timings = Timings::new(T::frequency(), *config);
|
||
T::regs().cr2().modify(|reg| {
|
||
reg.set_freq(timings.freq);
|
||
});
|
||
T::regs().ccr().modify(|reg| {
|
||
reg.set_f_s(timings.mode.f_s());
|
||
reg.set_duty(timings.duty.duty());
|
||
reg.set_ccr(timings.ccr);
|
||
});
|
||
T::regs().trise().modify(|reg| {
|
||
reg.set_trise(timings.trise);
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
}
|