use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal::i2c::{ErrorKind, ErrorType, NoAcknowledgeSource, Operation, SevenBitAddress};
use pac::peripherals::i2c::{I2C, I2C_TXDR};
use crate::hal::timeout::Timeout;
use crate::pac;
const CR2_RD_WRN: u32 = 1 << 10;
const MAX_NBYTES: usize = 255;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
Nack,
ArbitrationLoss,
Bus,
Timeout,
#[cfg(feature = "dma")]
Dma(crate::dma::Error),
}
impl embedded_hal::i2c::Error for Error {
fn kind(&self) -> ErrorKind {
match self {
Error::Nack => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown),
Error::ArbitrationLoss => ErrorKind::ArbitrationLoss,
Error::Bus => ErrorKind::Bus,
Error::Timeout => ErrorKind::Other,
#[cfg(feature = "dma")]
Error::Dma(_) => ErrorKind::Other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Timing {
presc: u8,
scll: u8,
sclh: u8,
scldel: u8,
sdadel: u8,
}
const fn timing(clock_hz: u32, frequency: u32) -> Timing {
assert!(frequency > 0);
let period = clock_hz / frequency;
let mut presc: u32 = 0;
while presc < 15 && period / (presc + 1) > 512 {
presc += 1;
}
let ticks = period / (presc + 1);
let half = ticks / 2;
let count = if half >= 1 {
let c = half - 1;
if c > 255 { 255 } else { c as u8 }
} else {
0
};
Timing {
presc: presc as u8,
scll: count,
sclh: count,
scldel: 4,
sdadel: 2,
}
}
pub struct State {
waker: AtomicWaker,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
fn on_interrupt(&self, r: I2C) {
let isr = r.ISR().read();
let cr1 = r.CR1().read();
let fire = (cr1.TXIE() && isr.TXIS())
|| (cr1.RXIE() && isr.RXNE())
|| (cr1.TCIE() && (isr.TC() || isr.TCR()))
|| (cr1.STOPIE() && isr.STOPF())
|| (cr1.NACKIE() && isr.NACKF())
|| (cr1.ERRIE() && (isr.ARLO() || isr.BERR()));
if fire {
r.CR1().modify(|w| {
w.set_TXIE(false);
w.set_RXIE(false);
w.set_TCIE(false);
w.set_STOPIE(false);
w.set_NACKIE(false);
w.set_ERRIE(false);
});
self.waker.wake();
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> I2C;
fn state() -> &'static State;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType {
const IRQ: Option<u16>;
}
macro_rules! impl_i2c {
($instance:ident, $base:expr, $irq:expr) => {
impl crate::i2c::SealedInstance for crate::peripherals::$instance {
#[inline]
fn regs() -> crate::pac::peripherals::i2c::I2C {
unsafe { crate::pac::peripherals::i2c::I2C::from_ptr($base as *mut ()) }
}
#[inline]
fn state() -> &'static crate::i2c::State {
static STATE: crate::i2c::State = crate::i2c::State::new();
&STATE
}
}
impl crate::i2c::Instance for crate::peripherals::$instance {
const IRQ: ::core::option::Option<u16> = $irq;
}
};
}
pub(crate) use impl_i2c;
pub fn on_interrupt<T: Instance>() {
T::state().on_interrupt(T::regs());
}
pub struct I2c {
regs: I2C,
state: &'static State,
timeout: Timeout,
}
impl I2c {
pub fn new<T: Instance>(peri: Peri<'static, T>, clock_hz: u32, frequency: u32) -> Self {
let _ = peri;
let r = T::regs();
r.CR1().modify(|w| w.set_PE(false));
let t = timing(clock_hz, frequency);
r.TIMINGR().write(|w| {
w.set_PRESC(t.presc);
w.set_SCLL(t.scll);
w.set_SCLH(t.sclh);
w.set_SCLDEL(t.scldel);
w.set_SDADEL(t.sdadel);
});
r.CR1().modify(|w| w.set_PE(true));
Self {
regs: r,
state: T::state(),
timeout: Timeout::DEFAULT,
}
}
pub fn set_timeout(&mut self, timeout: Timeout) {
self.timeout = timeout;
}
fn check_error(&self) -> Result<(), Error> {
let isr = self.regs.ISR().read();
if isr.NACKF() {
return Err(Error::Nack);
}
if isr.ARLO() {
return Err(Error::ArbitrationLoss);
}
if isr.BERR() {
return Err(Error::Bus);
}
Ok(())
}
fn setup(&self, address: u8, read: bool, nbytes: u8, reload: bool, start: bool) {
self.regs.CR2().write_value({
let mut cr2 = pac::peripherals::i2c::I2C_CR2(0);
cr2.set_SADD(u16::from(address) << 1);
cr2.set_NBYTES(nbytes);
cr2.set_RELOAD(reload);
cr2.set_AUTOEND(false);
cr2.set_START(start);
if read {
cr2.0 |= CR2_RD_WRN;
}
cr2
});
}
fn wait_blocking(&self, ready: impl Fn(&I2C) -> bool) -> Result<(), Error> {
let mut deadline = self.timeout.start();
loop {
self.check_error()?;
if ready(&self.regs) {
return Ok(());
}
deadline.tick().map_err(|_| Error::Timeout)?;
}
}
fn segment_blocking(
&self,
address: u8,
read: bool,
total: usize,
restart: bool,
mut byte: impl FnMut(&Self) -> Result<(), Error>,
) -> Result<(), Error> {
if restart {
self.wait_blocking(|r| r.ISR().read().TC())?;
} else {
self.wait_blocking(|r| !r.CR2().read().START() && !r.ISR().read().BUSY())?;
}
let mut remaining = total;
let mut chunk = remaining.min(MAX_NBYTES);
self.setup(address, read, chunk as u8, remaining > MAX_NBYTES, true);
loop {
for _ in 0..chunk {
byte(self)?;
}
remaining -= chunk;
if remaining == 0 {
return Ok(());
}
self.wait_blocking(|r| r.ISR().read().TCR())?;
chunk = remaining.min(MAX_NBYTES);
self.regs.CR2().modify(|w| {
w.set_NBYTES(chunk as u8);
w.set_RELOAD(remaining > MAX_NBYTES);
});
}
}
fn finish_blocking(&self) -> Result<(), Error> {
self.wait_blocking(|r| r.ISR().read().TC())?;
self.regs.CR2().modify(|w| w.set_STOP(true));
self.wait_blocking(|r| r.ISR().read().STOPF())?;
self.regs.ICR().write(|w| w.set_STOPCF(true));
Ok(())
}
fn transaction_blocking(
&mut self,
address: u8,
operations: &mut [Operation<'_>],
) -> Result<(), Error> {
if operations.is_empty() {
return Ok(());
}
let guard = RecoverGuard::new(self.regs);
let mut start = 0;
while start < operations.len() {
let end = group_end(operations, start);
let group = &mut operations[start..end];
let total = group_len(group);
let restart = start > 0;
if is_read(&group[0]) {
let mut bytes = read_bytes(group);
self.segment_blocking(address, true, total, restart, |s| {
s.wait_blocking(|r| r.ISR().read().RXNE())?;
*bytes.next().unwrap() = s.regs.RXDR().read().RXDATA();
Ok(())
})?;
} else {
let mut bytes = write_bytes(group);
self.segment_blocking(address, false, total, restart, |s| {
s.wait_blocking(|r| r.ISR().read().TXIS())?;
let b = *bytes.next().unwrap();
s.regs.TXDR().write_value(I2C_TXDR(u32::from(b)));
Ok(())
})?;
}
start = end;
}
self.finish_blocking()?;
guard.defuse();
Ok(())
}
async fn wait_async(&self, enable: fn(&I2C), ready: fn(&I2C) -> bool) -> Result<(), Error> {
poll_fn(|cx| {
self.check_error()?;
if ready(&self.regs) {
return Poll::Ready(Ok(()));
}
self.state.waker.register(cx.waker());
critical_section::with(|_| {
self.regs.CR1().modify(|w| {
w.set_NACKIE(true);
w.set_ERRIE(true);
});
enable(&self.regs);
});
self.check_error()?;
if ready(&self.regs) {
return Poll::Ready(Ok(()));
}
Poll::Pending
})
.await
}
async fn wait_txis_async(&self) -> Result<(), Error> {
self.wait_async(
|r| r.CR1().modify(|w| w.set_TXIE(true)),
|r| r.ISR().read().TXIS(),
)
.await
}
async fn wait_rxne_async(&self) -> Result<(), Error> {
self.wait_async(
|r| r.CR1().modify(|w| w.set_RXIE(true)),
|r| r.ISR().read().RXNE(),
)
.await
}
async fn wait_tc_async(&self) -> Result<(), Error> {
self.wait_async(
|r| r.CR1().modify(|w| w.set_TCIE(true)),
|r| r.ISR().read().TC(),
)
.await
}
async fn wait_tcr_async(&self) -> Result<(), Error> {
self.wait_async(
|r| r.CR1().modify(|w| w.set_TCIE(true)),
|r| r.ISR().read().TCR(),
)
.await
}
async fn begin_segment_async(&self, restart: bool) -> Result<(), Error> {
if restart {
self.wait_tc_async().await
} else {
poll_fn(|cx| {
self.check_error()?;
if !self.regs.CR2().read().START() && !self.regs.ISR().read().BUSY() {
return Poll::Ready(Ok(()));
}
cx.waker().wake_by_ref();
Poll::Pending
})
.await
}
}
async fn reload_async(&self, remaining: usize) -> Result<usize, Error> {
self.wait_tcr_async().await?;
let chunk = remaining.min(MAX_NBYTES);
self.regs.CR2().modify(|w| {
w.set_NBYTES(chunk as u8);
w.set_RELOAD(remaining > MAX_NBYTES);
});
Ok(chunk)
}
async fn finish_async(&self) -> Result<(), Error> {
self.wait_tc_async().await?;
self.regs.CR2().modify(|w| w.set_STOP(true));
self.wait_async(
|r| r.CR1().modify(|w| w.set_STOPIE(true)),
|r| r.ISR().read().STOPF(),
)
.await?;
self.regs.ICR().write(|w| w.set_STOPCF(true));
Ok(())
}
async fn transaction_async(
&mut self,
address: u8,
operations: &mut [Operation<'_>],
) -> Result<(), Error> {
if operations.is_empty() {
return Ok(());
}
let guard = RecoverGuard::new(self.regs);
let mut start = 0;
while start < operations.len() {
let end = group_end(operations, start);
let group = &mut operations[start..end];
let total = group_len(group);
let read = is_read(&group[0]);
self.begin_segment_async(start > 0).await?;
let mut remaining = total;
let mut chunk = remaining.min(MAX_NBYTES);
self.setup(address, read, chunk as u8, remaining > MAX_NBYTES, true);
if read {
let mut bytes = read_bytes(group);
loop {
for _ in 0..chunk {
self.wait_rxne_async().await?;
*bytes.next().unwrap() = self.regs.RXDR().read().RXDATA();
}
remaining -= chunk;
if remaining == 0 {
break;
}
chunk = self.reload_async(remaining).await?;
}
} else {
let mut bytes = write_bytes(group);
loop {
for _ in 0..chunk {
self.wait_txis_async().await?;
let b = *bytes.next().unwrap();
self.regs.TXDR().write_value(I2C_TXDR(u32::from(b)));
}
remaining -= chunk;
if remaining == 0 {
break;
}
chunk = self.reload_async(remaining).await?;
}
}
start = end;
}
self.finish_async().await?;
guard.defuse();
Ok(())
}
}
fn is_read(op: &Operation<'_>) -> bool {
matches!(op, Operation::Read(_))
}
fn group_end(operations: &[Operation<'_>], start: usize) -> usize {
let read = is_read(&operations[start]);
let mut end = start + 1;
while end < operations.len() && is_read(&operations[end]) == read {
end += 1;
}
end
}
fn group_len(group: &[Operation<'_>]) -> usize {
group
.iter()
.map(|op| match op {
Operation::Read(buf) => buf.len(),
Operation::Write(buf) => buf.len(),
})
.sum()
}
fn write_bytes<'a>(group: &'a [Operation<'_>]) -> impl Iterator<Item = &'a u8> {
group.iter().flat_map(|op| match op {
Operation::Write(buf) => buf.iter(),
Operation::Read(_) => [].iter(),
})
}
fn read_bytes<'a>(group: &'a mut [Operation<'_>]) -> impl Iterator<Item = &'a mut u8> {
group.iter_mut().flat_map(|op| match op {
Operation::Read(buf) => buf.iter_mut(),
Operation::Write(_) => [].iter_mut(),
})
}
const RECOVERY_SPINS: u32 = 200_000;
fn recover(r: I2C) {
critical_section::with(|_| {
r.CR1().modify(|w| {
w.set_TXIE(false);
w.set_RXIE(false);
w.set_TCIE(false);
w.set_STOPIE(false);
w.set_NACKIE(false);
w.set_ERRIE(false);
w.set_TXDMAEN(false);
w.set_RXDMAEN(false);
})
});
let isr = r.ISR().read();
if isr.BUSY() && !isr.ARLO() && !isr.BERR() {
let mut spins = RECOVERY_SPINS;
if !isr.NACKF() && !r.CR2().read().STOP() {
r.CR2().modify(|w| w.set_STOP(true));
}
while spins > 0 && !r.ISR().read().STOPF() {
spins -= 1;
}
}
r.CR1().modify(|w| w.set_PE(false));
let mut spins = RECOVERY_SPINS;
while spins > 0 && r.CR1().read().PE() {
spins -= 1;
}
r.ICR().write(|w| {
w.set_STOPCF(true);
w.set_NACKCF(true);
w.set_BERRCF(true);
w.set_ARLOCF(true);
w.set_OVRCF(true);
});
r.CR2().write_value(pac::peripherals::i2c::I2C_CR2(0));
r.CR1().modify(|w| w.set_PE(true));
}
struct RecoverGuard {
regs: I2C,
armed: bool,
}
impl RecoverGuard {
fn new(regs: I2C) -> Self {
Self { regs, armed: true }
}
fn defuse(mut self) {
self.armed = false;
}
}
impl Drop for RecoverGuard {
fn drop(&mut self) {
if self.armed {
recover(self.regs);
}
}
}
#[cfg(feature = "dma")]
impl I2c {
fn wait_dma(&self, transfer: &crate::dma::Transfer<'_>) -> Result<(), Error> {
let mut deadline = self.timeout.start();
loop {
self.check_error()?;
if let Some(result) = transfer.status() {
return result.map_err(Error::Dma);
}
deadline.tick().map_err(|_| Error::Timeout)?;
}
}
pub fn write_dma(
&mut self,
address: SevenBitAddress,
bytes: &[u8],
dma: &mut crate::dma::DmaChannel,
tx_request: u8,
) -> Result<(), Error> {
assert!(!bytes.is_empty() && bytes.len() <= MAX_NBYTES);
let guard = RecoverGuard::new(self.regs);
self.wait_blocking(|r| !r.CR2().read().START() && !r.ISR().read().BUSY())?;
self.setup(address, false, bytes.len() as u8, false, true);
self.regs.CR1().modify(|w| w.set_TXDMAEN(true));
let txdr = self.regs.TXDR().as_ptr() as *mut u8;
let transfer = unsafe { dma.write_peripheral(bytes, txdr, tx_request) };
let result = self.wait_dma(&transfer);
drop(transfer);
self.regs.CR1().modify(|w| w.set_TXDMAEN(false));
result?;
self.finish_blocking()?;
guard.defuse();
Ok(())
}
pub fn read_dma(
&mut self,
address: SevenBitAddress,
buffer: &mut [u8],
dma: &mut crate::dma::DmaChannel,
rx_request: u8,
) -> Result<(), Error> {
assert!(!buffer.is_empty() && buffer.len() <= MAX_NBYTES);
let guard = RecoverGuard::new(self.regs);
self.wait_blocking(|r| !r.CR2().read().START() && !r.ISR().read().BUSY())?;
self.setup(address, true, buffer.len() as u8, false, true);
self.regs.CR1().modify(|w| w.set_RXDMAEN(true));
let rxdr = self.regs.RXDR().as_ptr() as *const u8;
let transfer = unsafe { dma.read_peripheral(rxdr, buffer, rx_request) };
let result = self.wait_dma(&transfer);
drop(transfer);
self.regs.CR1().modify(|w| w.set_RXDMAEN(false));
result?;
self.finish_blocking()?;
guard.defuse();
Ok(())
}
}
impl ErrorType for I2c {
type Error = Error;
}
impl embedded_hal::i2c::I2c<SevenBitAddress> for I2c {
fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Error> {
self.transaction_blocking(address, operations)
}
}
impl embedded_hal_async::i2c::I2c<SevenBitAddress> for I2c {
async fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Error> {
self.transaction_async(address, operations).await
}
}
#[cfg(test)]
mod tests {
use embedded_hal::i2c::Operation;
use super::{group_end, group_len, read_bytes, timing, write_bytes};
#[test]
fn timing_standard_mode_fits() {
let t = timing(64_000_000, 100_000);
assert!(t.scll == t.sclh);
assert!(t.presc <= 15 && t.scldel <= 15 && t.sdadel <= 15);
}
#[test]
fn timing_fast_mode_fits() {
let t = timing(64_000_000, 400_000);
assert!(t.presc <= 15);
}
#[test]
fn same_direction_operations_form_one_segment() {
let (a, b, c) = ([1u8, 2], [3u8], [4u8, 5, 6]);
let (mut r1, mut r2) = ([0u8; 2], [0u8; 1]);
let ops = [
Operation::Write(&a),
Operation::Write(&b),
Operation::Read(&mut r1),
Operation::Read(&mut r2),
Operation::Write(&c),
];
assert_eq!(group_end(&ops, 0), 2);
assert_eq!(group_end(&ops, 2), 4);
assert_eq!(group_end(&ops, 4), 5);
assert_eq!(group_len(&ops[0..2]), 3);
assert_eq!(group_len(&ops[2..4]), 3);
}
#[test]
fn segment_bytes_span_operations_in_order() {
let (a, b) = ([1u8, 2], [3u8]);
let writes = [Operation::Write(&a), Operation::Write(&b)];
assert!(write_bytes(&writes).copied().eq([1, 2, 3]));
let (mut r1, mut r2) = ([0u8; 2], [0u8; 1]);
{
let mut reads = [Operation::Read(&mut r1), Operation::Read(&mut r2)];
for (slot, value) in read_bytes(&mut reads).zip([7u8, 8, 9]) {
*slot = value;
}
}
assert_eq!((r1, r2), ([7, 8], [9]));
}
}