#![macro_use]
#[cfg_attr(i2c_v1, path = "v1.rs")]
#[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")]
mod _version;
use core::future::Future;
use core::iter;
use core::marker::PhantomData;
use embassy_hal_internal::{Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};
use crate::dma::ChannelAndRequest;
#[cfg(gpio_v2)]
use crate::gpio::Pull;
use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed};
use crate::interrupt::typelevel::Interrupt;
use crate::mode::{Async, Blocking, Mode};
use crate::rcc::{RccInfo, SealedRccPeripheral};
use crate::time::Hertz;
use crate::{interrupt, peripherals};
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
Bus,
Arbitration,
Nack,
Timeout,
Crc,
Overrun,
ZeroLengthTransfer,
}
#[non_exhaustive]
#[derive(Copy, Clone)]
pub struct Config {
#[cfg(gpio_v2)]
pub sda_pullup: bool,
#[cfg(gpio_v2)]
pub scl_pullup: bool,
#[cfg(feature = "time")]
pub timeout: embassy_time::Duration,
}
impl Default for Config {
fn default() -> Self {
Self {
#[cfg(gpio_v2)]
sda_pullup: false,
#[cfg(gpio_v2)]
scl_pullup: false,
#[cfg(feature = "time")]
timeout: embassy_time::Duration::from_millis(1000),
}
}
}
impl Config {
fn scl_af(&self) -> AfType {
#[cfg(gpio_v1)]
return AfType::output(OutputType::OpenDrain, Speed::Medium);
#[cfg(gpio_v2)]
return AfType::output_pull(
OutputType::OpenDrain,
Speed::Medium,
match self.scl_pullup {
true => Pull::Up,
false => Pull::None,
},
);
}
fn sda_af(&self) -> AfType {
#[cfg(gpio_v1)]
return AfType::output(OutputType::OpenDrain, Speed::Medium);
#[cfg(gpio_v2)]
return AfType::output_pull(
OutputType::OpenDrain,
Speed::Medium,
match self.sda_pullup {
true => Pull::Up,
false => Pull::None,
},
);
}
}
pub struct I2c<'d, M: Mode> {
info: &'static Info,
state: &'static State,
kernel_clock: Hertz,
scl: Option<PeripheralRef<'d, AnyPin>>,
sda: Option<PeripheralRef<'d, AnyPin>>,
tx_dma: Option<ChannelAndRequest<'d>>,
rx_dma: Option<ChannelAndRequest<'d>>,
#[cfg(feature = "time")]
timeout: Duration,
_phantom: PhantomData<M>,
}
impl<'d> I2c<'d, Async> {
pub fn new<T: Instance>(
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 = impl TxDma<T>> + 'd,
rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
freq: Hertz,
config: Config,
) -> Self {
Self::new_inner(
peri,
new_pin!(scl, config.scl_af()),
new_pin!(sda, config.sda_af()),
new_dma!(tx_dma),
new_dma!(rx_dma),
freq,
config,
)
}
}
impl<'d> I2c<'d, Blocking> {
pub fn new_blocking<T: Instance>(
peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
freq: Hertz,
config: Config,
) -> Self {
Self::new_inner(
peri,
new_pin!(scl, config.scl_af()),
new_pin!(sda, config.sda_af()),
None,
None,
freq,
config,
)
}
}
impl<'d, M: Mode> I2c<'d, M> {
fn new_inner<T: Instance>(
_peri: impl Peripheral<P = T> + 'd,
scl: Option<PeripheralRef<'d, AnyPin>>,
sda: Option<PeripheralRef<'d, AnyPin>>,
tx_dma: Option<ChannelAndRequest<'d>>,
rx_dma: Option<ChannelAndRequest<'d>>,
freq: Hertz,
config: Config,
) -> Self {
unsafe { T::EventInterrupt::enable() };
unsafe { T::ErrorInterrupt::enable() };
let mut this = Self {
info: T::info(),
state: T::state(),
kernel_clock: T::frequency(),
scl,
sda,
tx_dma,
rx_dma,
#[cfg(feature = "time")]
timeout: config.timeout,
_phantom: PhantomData,
};
this.enable_and_init(freq, config);
this
}
fn enable_and_init(&mut self, freq: Hertz, config: Config) {
self.info.rcc.enable_and_reset();
self.init(freq, config);
}
fn timeout(&self) -> Timeout {
Timeout {
#[cfg(feature = "time")]
deadline: Instant::now() + self.timeout,
}
}
}
impl<'d, M: Mode> Drop for I2c<'d, M> {
fn drop(&mut self) {
self.scl.as_ref().map(|x| x.set_as_disconnected());
self.sda.as_ref().map(|x| x.set_as_disconnected());
self.info.rcc.disable()
}
}
#[derive(Copy, Clone)]
struct Timeout {
#[cfg(feature = "time")]
deadline: Instant,
}
#[allow(dead_code)]
impl Timeout {
#[inline]
fn check(self) -> Result<(), Error> {
#[cfg(feature = "time")]
if Instant::now() > self.deadline {
return Err(Error::Timeout);
}
Ok(())
}
#[inline]
fn with<R>(self, fut: impl Future<Output = Result<R, Error>>) -> impl Future<Output = Result<R, Error>> {
#[cfg(feature = "time")]
{
use futures_util::FutureExt;
embassy_futures::select::select(embassy_time::Timer::at(self.deadline), fut).map(|r| match r {
embassy_futures::select::Either::First(_) => Err(Error::Timeout),
embassy_futures::select::Either::Second(r) => r,
})
}
#[cfg(not(feature = "time"))]
fut
}
}
struct State {
#[allow(unused)]
waker: AtomicWaker,
}
impl State {
const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
struct Info {
regs: crate::pac::i2c::I2c,
rcc: RccInfo,
}
peri_trait!(
irqs: [EventInterrupt, ErrorInterrupt],
);
pin_trait!(SclPin, Instance);
pin_trait!(SdaPin, Instance);
dma_trait!(RxDma, Instance);
dma_trait!(TxDma, Instance);
pub struct EventInterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInterruptHandler<T> {
unsafe fn on_interrupt() {
_version::on_interrupt::<T>()
}
}
pub struct ErrorInterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::ErrorInterrupt> for ErrorInterruptHandler<T> {
unsafe fn on_interrupt() {
_version::on_interrupt::<T>()
}
}
foreach_peripheral!(
(i2c, $inst:ident) => {
#[allow(private_interfaces)]
impl SealedInstance for peripherals::$inst {
fn info() -> &'static Info {
static INFO: Info = Info{
regs: crate::pac::$inst,
rcc: crate::peripherals::$inst::RCC_INFO,
};
&INFO
}
fn state() -> &'static State {
static STATE: State = State::new();
&STATE
}
}
impl Instance for peripherals::$inst {
type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV;
type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER;
}
};
);
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> {
type Error = Error;
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
}
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> {
type Error = Error;
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, write)
}
}
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> {
type Error = Error;
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, write, read)
}
}
impl embedded_hal_1::i2c::Error for Error {
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
match *self {
Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,
Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
Self::Nack => {
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)
}
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,
Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,
Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,
}
}
}
impl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, M> {
type Error = Error;
}
impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> {
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, read)
}
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, write)
}
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, write, read)
}
fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
self.blocking_transaction(address, operations)
}
}
impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> {
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.read(address, read).await
}
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.write(address, write).await
}
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.write_read(address, write, read).await
}
async fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
self.transaction(address, operations).await
}
}
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum FrameOptions {
FirstAndLastFrame,
FirstFrame,
FirstAndNextFrame,
NextFrame,
LastFrame,
LastFrameNoStop,
}
#[allow(dead_code)]
impl FrameOptions {
fn send_start(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true,
Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false,
}
}
fn send_stop(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::LastFrame => true,
Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false,
}
}
fn send_nack(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true,
Self::FirstAndNextFrame | Self::NextFrame => false,
}
}
}
#[allow(dead_code)]
fn operation_frames<'a, 'b: 'a>(
operations: &'a mut [embedded_hal_1::i2c::Operation<'b>],
) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> {
use embedded_hal_1::i2c::Operation::{Read, Write};
if operations.iter().any(|op| match op {
Read(read) => read.is_empty(),
Write(_) => false,
}) {
return Err(Error::Overrun);
}
let mut operations = operations.iter_mut().peekable();
let mut next_first_frame = true;
Ok(iter::from_fn(move || {
let Some(op) = operations.next() else {
return None;
};
let first_frame = next_first_frame;
let next_op = operations.peek();
let frame = match (first_frame, next_op) {
(true, None) => FrameOptions::FirstAndLastFrame,
(true, Some(Read(_))) => FrameOptions::FirstAndNextFrame,
(true, Some(Write(_))) => FrameOptions::FirstFrame,
(false, None) => FrameOptions::LastFrame,
(false, Some(Read(_))) => FrameOptions::NextFrame,
(false, Some(Write(_))) => FrameOptions::LastFrameNoStop,
};
next_first_frame = match (&op, next_op) {
(_, None) => false,
(Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true,
(Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false,
};
Some((op, frame))
}))
}