823 lines
27 KiB
Rust
823 lines
27 KiB
Rust
//! # I2Cv1
|
|
//!
|
|
//! This implementation is used for STM32F1, STM32F2, STM32F4, and STM32L1 devices.
|
|
//!
|
|
//! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead.
|
|
|
|
use core::future::poll_fn;
|
|
use core::task::Poll;
|
|
|
|
use embassy_embedded_hal::SetConfig;
|
|
use embassy_futures::select::{select, Either};
|
|
use embassy_hal_internal::drop::OnDrop;
|
|
use embedded_hal_1::i2c::Operation;
|
|
|
|
use super::*;
|
|
use crate::mode::Mode as PeriMode;
|
|
use crate::pac::i2c;
|
|
|
|
// /!\ /!\
|
|
// /!\ Implementation note! /!\
|
|
// /!\ /!\
|
|
//
|
|
// It's somewhat unclear whether using interrupts here in a *strictly* one-shot style is actually
|
|
// what we want! If you are looking in this file because you are doing async I2C and your code is
|
|
// just totally hanging (sometimes), maybe swing by this issue:
|
|
// <https://github.com/embassy-rs/embassy/issues/2372>.
|
|
//
|
|
// There's some more details there, and we might have a fix for you. But please let us know if you
|
|
// hit a case like this!
|
|
pub unsafe fn on_interrupt<T: Instance>() {
|
|
let regs = T::info().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);
|
|
});
|
|
});
|
|
}
|
|
|
|
impl<'d, M: PeriMode> I2c<'d, M> {
|
|
pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
|
|
self.info.regs.cr1().modify(|reg| {
|
|
reg.set_pe(false);
|
|
//reg.set_anfoff(false);
|
|
});
|
|
|
|
// Errata: "Start cannot be generated after a misplaced Stop"
|
|
//
|
|
// > If a master generates a misplaced Stop on the bus (bus error)
|
|
// > while the microcontroller I2C peripheral attempts to switch to
|
|
// > Master mode by setting the START bit, the Start condition is
|
|
// > not properly generated.
|
|
//
|
|
// This also can occur with falsely detected STOP events, for example
|
|
// if the SDA line is shorted to low.
|
|
//
|
|
// The workaround for this is to trigger the SWRST line AFTER power is
|
|
// enabled, AFTER PE is disabled and BEFORE making any other configuration.
|
|
//
|
|
// It COULD be possible to apply this workaround at runtime, instead of
|
|
// only on initialization, however this would require detecting the timeout
|
|
// or BUSY lockup condition, and re-configuring the peripheral after reset.
|
|
//
|
|
// This presents as an ~infinite hang on read or write, as the START condition
|
|
// is never generated, meaning the start event is never generated.
|
|
self.info.regs.cr1().modify(|reg| {
|
|
reg.set_swrst(true);
|
|
});
|
|
self.info.regs.cr1().modify(|reg| {
|
|
reg.set_swrst(false);
|
|
});
|
|
|
|
let timings = Timings::new(self.kernel_clock, freq);
|
|
|
|
self.info.regs.cr2().modify(|reg| {
|
|
reg.set_freq(timings.freq);
|
|
});
|
|
self.info.regs.ccr().modify(|reg| {
|
|
reg.set_f_s(timings.mode.f_s());
|
|
reg.set_duty(timings.duty.duty());
|
|
reg.set_ccr(timings.ccr);
|
|
});
|
|
self.info.regs.trise().modify(|reg| {
|
|
reg.set_trise(timings.trise);
|
|
});
|
|
|
|
self.info.regs.cr1().modify(|reg| {
|
|
reg.set_pe(true);
|
|
});
|
|
}
|
|
|
|
fn check_and_clear_error_flags(info: &'static Info) -> 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 = info.regs.sr1().read();
|
|
|
|
if sr1.timeout() {
|
|
info.regs.sr1().write(|reg| {
|
|
reg.0 = !0;
|
|
reg.set_timeout(false);
|
|
});
|
|
return Err(Error::Timeout);
|
|
}
|
|
|
|
if sr1.pecerr() {
|
|
info.regs.sr1().write(|reg| {
|
|
reg.0 = !0;
|
|
reg.set_pecerr(false);
|
|
});
|
|
return Err(Error::Crc);
|
|
}
|
|
|
|
if sr1.ovr() {
|
|
info.regs.sr1().write(|reg| {
|
|
reg.0 = !0;
|
|
reg.set_ovr(false);
|
|
});
|
|
return Err(Error::Overrun);
|
|
}
|
|
|
|
if sr1.af() {
|
|
info.regs.sr1().write(|reg| {
|
|
reg.0 = !0;
|
|
reg.set_af(false);
|
|
});
|
|
return Err(Error::Nack);
|
|
}
|
|
|
|
if sr1.arlo() {
|
|
info.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() {
|
|
info.regs.sr1().write(|reg| {
|
|
reg.0 = !0;
|
|
reg.set_berr(false);
|
|
});
|
|
}
|
|
|
|
Ok(sr1)
|
|
}
|
|
|
|
fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout, frame: FrameOptions) -> Result<(), Error> {
|
|
if frame.send_start() {
|
|
// Send a START condition
|
|
|
|
self.info.regs.cr1().modify(|reg| {
|
|
reg.set_start(true);
|
|
});
|
|
|
|
// Wait until START condition was generated
|
|
while !Self::check_and_clear_error_flags(self.info)?.start() {
|
|
timeout.check()?;
|
|
}
|
|
|
|
// Check if we were the ones to generate START
|
|
if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() {
|
|
return Err(Error::Arbitration);
|
|
}
|
|
|
|
// Set up current address we're trying to talk to
|
|
self.info.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(self.info)?.addr() {
|
|
timeout.check()?;
|
|
}
|
|
|
|
// Clear condition by reading SR2
|
|
let _ = self.info.regs.sr2().read();
|
|
}
|
|
|
|
// Send bytes
|
|
for c in bytes {
|
|
self.send_byte(*c, timeout)?;
|
|
}
|
|
|
|
if frame.send_stop() {
|
|
// Send a STOP condition
|
|
self.info.regs.cr1().modify(|reg| reg.set_stop(true));
|
|
}
|
|
|
|
// Fallthrough is success
|
|
Ok(())
|
|
}
|
|
|
|
fn send_byte(&self, byte: u8, timeout: Timeout) -> 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(self.info)?.txe()
|
|
} {
|
|
timeout.check()?;
|
|
}
|
|
|
|
// Push out a byte of data
|
|
self.info.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(self.info)?.btf()
|
|
} {
|
|
timeout.check()?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn recv_byte(&self, timeout: Timeout) -> Result<u8, Error> {
|
|
while {
|
|
// Check for any potential error conditions.
|
|
Self::check_and_clear_error_flags(self.info)?;
|
|
|
|
!self.info.regs.sr1().read().rxne()
|
|
} {
|
|
timeout.check()?;
|
|
}
|
|
|
|
let value = self.info.regs.dr().read().dr();
|
|
Ok(value)
|
|
}
|
|
|
|
fn blocking_read_timeout(
|
|
&mut self,
|
|
addr: u8,
|
|
buffer: &mut [u8],
|
|
timeout: Timeout,
|
|
frame: FrameOptions,
|
|
) -> Result<(), Error> {
|
|
let Some((last, buffer)) = buffer.split_last_mut() else {
|
|
return Err(Error::Overrun);
|
|
};
|
|
|
|
if frame.send_start() {
|
|
// Send a START condition and set ACK bit
|
|
self.info.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(self.info)?.start() {
|
|
timeout.check()?;
|
|
}
|
|
|
|
// Check if we were the ones to generate START
|
|
if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() {
|
|
return Err(Error::Arbitration);
|
|
}
|
|
|
|
// Set up current address we're trying to talk to
|
|
self.info.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(self.info)?.addr() {
|
|
timeout.check()?;
|
|
}
|
|
|
|
// Clear condition by reading SR2
|
|
let _ = self.info.regs.sr2().read();
|
|
}
|
|
|
|
// Receive bytes into buffer
|
|
for c in buffer {
|
|
*c = self.recv_byte(timeout)?;
|
|
}
|
|
|
|
// Prepare to send NACK then STOP after next byte
|
|
self.info.regs.cr1().modify(|reg| {
|
|
if frame.send_nack() {
|
|
reg.set_ack(false);
|
|
}
|
|
if frame.send_stop() {
|
|
reg.set_stop(true);
|
|
}
|
|
});
|
|
|
|
// Receive last byte
|
|
*last = self.recv_byte(timeout)?;
|
|
|
|
// Fallthrough is success
|
|
Ok(())
|
|
}
|
|
|
|
/// Blocking read.
|
|
pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> {
|
|
self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame)
|
|
}
|
|
|
|
/// Blocking write.
|
|
pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> {
|
|
self.write_bytes(addr, write, self.timeout(), FrameOptions::FirstAndLastFrame)?;
|
|
|
|
// Fallthrough is success
|
|
Ok(())
|
|
}
|
|
|
|
/// Blocking write, restart, read.
|
|
pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
|
// Check empty read buffer before starting transaction. Otherwise, we would not generate the
|
|
// stop condition below.
|
|
if read.is_empty() {
|
|
return Err(Error::Overrun);
|
|
}
|
|
|
|
let timeout = self.timeout();
|
|
|
|
self.write_bytes(addr, write, timeout, FrameOptions::FirstFrame)?;
|
|
self.blocking_read_timeout(addr, read, timeout, FrameOptions::FirstAndLastFrame)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Blocking transaction with operations.
|
|
///
|
|
/// Consecutive operations of same type are merged. See [transaction contract] for details.
|
|
///
|
|
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
|
|
pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
|
|
let timeout = self.timeout();
|
|
|
|
for (op, frame) in operation_frames(operations)? {
|
|
match op {
|
|
Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?,
|
|
Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?,
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// Async
|
|
|
|
#[inline] // pretty sure this should always be inlined
|
|
fn enable_interrupts(info: &'static Info) -> () {
|
|
info.regs.cr2().modify(|w| {
|
|
w.set_iterren(true);
|
|
w.set_itevten(true);
|
|
});
|
|
}
|
|
}
|
|
|
|
impl<'d> I2c<'d, Async> {
|
|
async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> {
|
|
self.info.regs.cr2().modify(|w| {
|
|
// Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for
|
|
// reception.
|
|
w.set_itbufen(false);
|
|
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2
|
|
// register.
|
|
w.set_dmaen(true);
|
|
// Sending NACK is not necessary (nor possible) for write transfer.
|
|
w.set_last(false);
|
|
});
|
|
|
|
// Sentinel to disable transfer when an error occurs or future is canceled.
|
|
// TODO: Generate STOP condition on cancel?
|
|
let on_drop = OnDrop::new(|| {
|
|
self.info.regs.cr2().modify(|w| {
|
|
w.set_dmaen(false);
|
|
w.set_iterren(false);
|
|
w.set_itevten(false);
|
|
})
|
|
});
|
|
|
|
if frame.send_start() {
|
|
// Send a START condition
|
|
self.info.regs.cr1().modify(|reg| {
|
|
reg.set_start(true);
|
|
});
|
|
|
|
// Wait until START condition was generated
|
|
poll_fn(|cx| {
|
|
self.state.waker.register(cx.waker());
|
|
|
|
match Self::check_and_clear_error_flags(self.info) {
|
|
Err(e) => Poll::Ready(Err(e)),
|
|
Ok(sr1) => {
|
|
if sr1.start() {
|
|
Poll::Ready(Ok(()))
|
|
} else {
|
|
// When pending, (re-)enable interrupts to wake us up.
|
|
Self::enable_interrupts(self.info);
|
|
Poll::Pending
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.await?;
|
|
|
|
// Check if we were the ones to generate START
|
|
if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() {
|
|
return Err(Error::Arbitration);
|
|
}
|
|
|
|
// Set up current address we're trying to talk to
|
|
self.info.regs.dr().write(|reg| reg.set_dr(address << 1));
|
|
|
|
// Wait for the address to be acknowledged
|
|
poll_fn(|cx| {
|
|
self.state.waker.register(cx.waker());
|
|
|
|
match Self::check_and_clear_error_flags(self.info) {
|
|
Err(e) => Poll::Ready(Err(e)),
|
|
Ok(sr1) => {
|
|
if sr1.addr() {
|
|
Poll::Ready(Ok(()))
|
|
} else {
|
|
// When pending, (re-)enable interrupts to wake us up.
|
|
Self::enable_interrupts(self.info);
|
|
Poll::Pending
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.await?;
|
|
|
|
// Clear condition by reading SR2
|
|
self.info.regs.sr2().read();
|
|
}
|
|
|
|
let dma_transfer = unsafe {
|
|
// 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 = self.info.regs.dr().as_ptr() as *mut u8;
|
|
|
|
self.tx_dma.as_mut().unwrap().write(write, dst, Default::default())
|
|
};
|
|
|
|
// Wait for bytes to be sent, or an error to occur.
|
|
let poll_error = poll_fn(|cx| {
|
|
self.state.waker.register(cx.waker());
|
|
|
|
match Self::check_and_clear_error_flags(self.info) {
|
|
Err(e) => Poll::Ready(Err::<(), Error>(e)),
|
|
Ok(_) => {
|
|
// When pending, (re-)enable interrupts to wake us up.
|
|
Self::enable_interrupts(self.info);
|
|
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(()),
|
|
}?;
|
|
|
|
self.info.regs.cr2().modify(|w| {
|
|
w.set_dmaen(false);
|
|
});
|
|
|
|
if frame.send_stop() {
|
|
// 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.”
|
|
poll_fn(|cx| {
|
|
self.state.waker.register(cx.waker());
|
|
|
|
match Self::check_and_clear_error_flags(self.info) {
|
|
Err(e) => Poll::Ready(Err(e)),
|
|
Ok(sr1) => {
|
|
if sr1.btf() {
|
|
Poll::Ready(Ok(()))
|
|
} else {
|
|
// When pending, (re-)enable interrupts to wake us up.
|
|
Self::enable_interrupts(self.info);
|
|
Poll::Pending
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.await?;
|
|
|
|
self.info.regs.cr1().modify(|w| {
|
|
w.set_stop(true);
|
|
});
|
|
}
|
|
|
|
drop(on_drop);
|
|
|
|
// Fallthrough is success
|
|
Ok(())
|
|
}
|
|
|
|
/// Write.
|
|
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
|
|
self.write_frame(address, write, FrameOptions::FirstAndLastFrame)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Read.
|
|
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
|
self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> {
|
|
if buffer.is_empty() {
|
|
return Err(Error::Overrun);
|
|
}
|
|
|
|
// Some branches below depend on whether the buffer contains only a single byte.
|
|
let single_byte = buffer.len() == 1;
|
|
|
|
self.info.regs.cr2().modify(|w| {
|
|
// Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for
|
|
// reception.
|
|
w.set_itbufen(false);
|
|
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2
|
|
// register.
|
|
w.set_dmaen(true);
|
|
// 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.
|
|
w.set_last(frame.send_nack() && !single_byte);
|
|
});
|
|
|
|
// Sentinel to disable transfer when an error occurs or future is canceled.
|
|
// TODO: Generate STOP condition on cancel?
|
|
let on_drop = OnDrop::new(|| {
|
|
self.info.regs.cr2().modify(|w| {
|
|
w.set_dmaen(false);
|
|
w.set_iterren(false);
|
|
w.set_itevten(false);
|
|
})
|
|
});
|
|
|
|
if frame.send_start() {
|
|
// Send a START condition and set ACK bit
|
|
self.info.regs.cr1().modify(|reg| {
|
|
reg.set_start(true);
|
|
reg.set_ack(true);
|
|
});
|
|
|
|
// Wait until START condition was generated
|
|
poll_fn(|cx| {
|
|
self.state.waker.register(cx.waker());
|
|
|
|
match Self::check_and_clear_error_flags(self.info) {
|
|
Err(e) => Poll::Ready(Err(e)),
|
|
Ok(sr1) => {
|
|
if sr1.start() {
|
|
Poll::Ready(Ok(()))
|
|
} else {
|
|
// When pending, (re-)enable interrupts to wake us up.
|
|
Self::enable_interrupts(self.info);
|
|
Poll::Pending
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.await?;
|
|
|
|
// Check if we were the ones to generate START
|
|
if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() {
|
|
return Err(Error::Arbitration);
|
|
}
|
|
|
|
// Set up current address we're trying to talk to
|
|
self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1));
|
|
|
|
// Wait for the address to be acknowledged
|
|
poll_fn(|cx| {
|
|
self.state.waker.register(cx.waker());
|
|
|
|
match Self::check_and_clear_error_flags(self.info) {
|
|
Err(e) => Poll::Ready(Err(e)),
|
|
Ok(sr1) => {
|
|
if sr1.addr() {
|
|
Poll::Ready(Ok(()))
|
|
} else {
|
|
// When pending, (re-)enable interrupts to wake us up.
|
|
Self::enable_interrupts(self.info);
|
|
Poll::Pending
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.await?;
|
|
|
|
// 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 frame.send_nack() && single_byte {
|
|
self.info.regs.cr1().modify(|w| {
|
|
w.set_ack(false);
|
|
});
|
|
}
|
|
|
|
// Clear condition by reading SR2
|
|
self.info.regs.sr2().read();
|
|
} else {
|
|
// Before starting reception of single byte (but without START condition, i.e. in case
|
|
// of continued frame), program NACK to emit at end of this byte.
|
|
if frame.send_nack() && single_byte {
|
|
self.info.regs.cr1().modify(|w| {
|
|
w.set_ack(false);
|
|
});
|
|
}
|
|
}
|
|
|
|
// 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 frame.send_stop() && single_byte {
|
|
self.info.regs.cr1().modify(|w| {
|
|
w.set_stop(true);
|
|
});
|
|
}
|
|
|
|
let dma_transfer = unsafe {
|
|
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved
|
|
// from this address from the memory after each RxE event.
|
|
let src = self.info.regs.dr().as_ptr() as *mut u8;
|
|
|
|
self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())
|
|
};
|
|
|
|
// Wait for bytes to be received, or an error to occur.
|
|
let poll_error = poll_fn(|cx| {
|
|
self.state.waker.register(cx.waker());
|
|
|
|
match Self::check_and_clear_error_flags(self.info) {
|
|
Err(e) => Poll::Ready(Err::<(), Error>(e)),
|
|
_ => {
|
|
// When pending, (re-)enable interrupts to wake us up.
|
|
Self::enable_interrupts(self.info);
|
|
Poll::Pending
|
|
}
|
|
}
|
|
});
|
|
|
|
match select(dma_transfer, poll_error).await {
|
|
Either::Second(Err(e)) => Err(e),
|
|
_ => Ok(()),
|
|
}?;
|
|
|
|
self.info.regs.cr2().modify(|w| {
|
|
w.set_dmaen(false);
|
|
});
|
|
|
|
if frame.send_stop() && !single_byte {
|
|
self.info.regs.cr1().modify(|w| {
|
|
w.set_stop(true);
|
|
});
|
|
}
|
|
|
|
drop(on_drop);
|
|
|
|
// Fallthrough is success
|
|
Ok(())
|
|
}
|
|
|
|
/// Write, restart, read.
|
|
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
|
// Check empty read buffer before starting transaction. Otherwise, we would not generate the
|
|
// stop condition below.
|
|
if read.is_empty() {
|
|
return Err(Error::Overrun);
|
|
}
|
|
|
|
self.write_frame(address, write, FrameOptions::FirstFrame).await?;
|
|
self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await
|
|
}
|
|
|
|
/// Transaction with operations.
|
|
///
|
|
/// Consecutive operations of same type are merged. See [transaction contract] for details.
|
|
///
|
|
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
|
|
pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
|
|
for (op, frame) in operation_frames(operations)? {
|
|
match op {
|
|
Operation::Read(read) => self.read_frame(addr, read, frame).await?,
|
|
Operation::Write(write) => self.write_frame(addr, write, frame).await?,
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
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, M: PeriMode> SetConfig for I2c<'d, M> {
|
|
type Config = Hertz;
|
|
type ConfigError = ();
|
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
|
let timings = Timings::new(self.kernel_clock, *config);
|
|
self.info.regs.cr2().modify(|reg| {
|
|
reg.set_freq(timings.freq);
|
|
});
|
|
self.info.regs.ccr().modify(|reg| {
|
|
reg.set_f_s(timings.mode.f_s());
|
|
reg.set_duty(timings.duty.duty());
|
|
reg.set_ccr(timings.ccr);
|
|
});
|
|
self.info.regs.trise().modify(|reg| {
|
|
reg.set_trise(timings.trise);
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
}
|