#![cfg_attr(docsrs, procmacros::doc_replace)]
use core::{
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use enumset::{EnumSet, EnumSetType};
use crate::{
Async,
Blocking,
DriverMode,
asynch::AtomicWaker,
clock::ll::{ClockTree, I2cFunctionClockConfig},
gpio::{
DriveMode,
InputSignal,
Level,
OutputConfig,
OutputSignal,
PinGuard,
Pull,
interconnect::{self, PeripheralInput, PeripheralOutput},
},
handler,
interrupt::InterruptHandler,
pac::i2c0::{COMD, RegisterBlock},
private,
ram,
system::PeripheralGuard,
time::{Duration, Instant, Rate},
};
mod eh;
mod low_level;
pub use low_level::{AnyI2c, Instance};
use low_level::{Driver, I2cClockGuard};
const I2C_FIFO_SIZE: usize = property!("i2c_master.fifo_size");
const I2C_CHUNK_SIZE: usize = I2C_FIFO_SIZE - 1;
const CLEAR_BUS_TIMEOUT_MS: Duration = Duration::from_millis(50);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum I2cAddress {
SevenBit(u8),
}
impl I2cAddress {
fn validate(&self) -> Result<(), Error> {
match self {
I2cAddress::SevenBit(addr) => {
if *addr > 0x7F {
return Err(Error::AddressInvalid(*self));
}
}
}
Ok(())
}
fn bytes(self) -> usize {
match self {
I2cAddress::SevenBit(_) => 1,
}
}
}
impl From<u8> for I2cAddress {
fn from(value: u8) -> Self {
I2cAddress::SevenBit(value)
}
}
#[doc = ""]
#[cfg_attr(
i2c_master_bus_timeout_is_exponential,
doc = "The effective timeout may be longer than the value configured here."
)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, strum::Display)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[instability::unstable]
pub enum BusTimeout {
Maximum,
#[cfg(i2c_master_has_bus_timeout_enable)]
Disabled,
BusCycles(u32),
}
impl BusTimeout {
fn apb_cycles(self, half_bus_cycle: u32) -> Result<Option<u32>, ConfigError> {
match self {
BusTimeout::Maximum => Ok(Some(property!("i2c_master.max_bus_timeout"))),
#[cfg(i2c_master_has_bus_timeout_enable)]
BusTimeout::Disabled => Ok(None),
BusTimeout::BusCycles(cycles) => {
let raw = if cfg!(i2c_master_bus_timeout_is_exponential) {
let to_peri = (cycles * 2 * half_bus_cycle).max(1);
let log2 = to_peri.ilog2();
if to_peri != 1 << log2 { log2 + 1 } else { log2 }
} else {
cycles * 2 * half_bus_cycle
};
if raw <= property!("i2c_master.max_bus_timeout") {
Ok(Some(raw))
} else {
Err(ConfigError::TimeoutTooLong)
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SoftwareTimeout {
None,
Transaction(Duration),
PerByte(Duration),
}
#[instability::unstable]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(i2c_master_has_fsm_timeouts)]
pub struct FsmTimeout {
value: u8,
}
#[cfg(i2c_master_has_fsm_timeouts)]
impl FsmTimeout {
const FSM_TIMEOUT_MAX: u8 = 23;
#[instability::unstable]
pub const fn new_const<const VALUE: u8>() -> Self {
const {
core::assert!(VALUE <= Self::FSM_TIMEOUT_MAX, "Invalid timeout value");
}
Self { value: VALUE }
}
#[instability::unstable]
pub fn new(value: u8) -> Result<Self, ConfigError> {
if value > Self::FSM_TIMEOUT_MAX {
return Err(ConfigError::TimeoutTooLong);
}
Ok(Self { value })
}
fn value(&self) -> u8 {
self.value
}
}
#[cfg(i2c_master_has_fsm_timeouts)]
impl Default for FsmTimeout {
fn default() -> Self {
Self::new_const::<{ Self::FSM_TIMEOUT_MAX }>()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
FifoExceeded,
AcknowledgeCheckFailed(AcknowledgeCheckFailedReason),
Timeout,
ArbitrationLost,
ExecutionIncomplete,
CommandNumberExceeded,
ZeroLengthInvalid,
AddressInvalid(I2cAddress),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum AcknowledgeCheckFailedReason {
Address,
Data,
Unknown,
}
impl core::fmt::Display for AcknowledgeCheckFailedReason {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
AcknowledgeCheckFailedReason::Address => write!(f, "Address"),
AcknowledgeCheckFailedReason::Data => write!(f, "Data"),
AcknowledgeCheckFailedReason::Unknown => write!(f, "Unknown"),
}
}
}
impl From<&AcknowledgeCheckFailedReason> for embedded_hal::i2c::NoAcknowledgeSource {
fn from(value: &AcknowledgeCheckFailedReason) -> Self {
match value {
AcknowledgeCheckFailedReason::Address => {
embedded_hal::i2c::NoAcknowledgeSource::Address
}
AcknowledgeCheckFailedReason::Data => embedded_hal::i2c::NoAcknowledgeSource::Data,
AcknowledgeCheckFailedReason::Unknown => {
embedded_hal::i2c::NoAcknowledgeSource::Unknown
}
}
}
}
impl core::error::Error for Error {}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::FifoExceeded => write!(f, "The transmission exceeded the FIFO size"),
Error::AcknowledgeCheckFailed(reason) => {
write!(f, "The acknowledgment check failed. Reason: {reason}")
}
Error::Timeout => write!(f, "A timeout occurred during transmission"),
Error::ArbitrationLost => write!(f, "The arbitration for the bus was lost"),
Error::ExecutionIncomplete => {
write!(f, "The execution of the I2C command was incomplete")
}
Error::CommandNumberExceeded => {
write!(f, "The number of commands issued exceeded the limit")
}
Error::ZeroLengthInvalid => write!(f, "Zero length read or write operation"),
Error::AddressInvalid(address) => {
write!(f, "The given address ({address:?}) is invalid")
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ConfigError {
FrequencyOutOfRange,
TimeoutTooLong,
}
impl core::error::Error for ConfigError {}
impl core::fmt::Display for ConfigError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ConfigError::FrequencyOutOfRange => write!(
f,
"Provided bus frequency is invalid for the current configuration"
),
ConfigError::TimeoutTooLong => write!(
f,
"Provided timeout is invalid for the current configuration"
),
}
}
}
#[derive(PartialEq)]
enum OpKind {
Write,
Read,
}
#[derive(Debug, PartialEq, Eq, Hash, strum::Display)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Operation<'a> {
Write(&'a [u8]),
Read(&'a mut [u8]),
}
impl<'a, 'b> From<&'a mut embedded_hal::i2c::Operation<'b>> for Operation<'a> {
fn from(value: &'a mut embedded_hal::i2c::Operation<'b>) -> Self {
match value {
embedded_hal::i2c::Operation::Write(buffer) => Operation::Write(buffer),
embedded_hal::i2c::Operation::Read(buffer) => Operation::Read(buffer),
}
}
}
impl<'a, 'b> From<&'a mut Operation<'b>> for Operation<'a> {
fn from(value: &'a mut Operation<'b>) -> Self {
match value {
Operation::Write(buffer) => Operation::Write(buffer),
Operation::Read(buffer) => Operation::Read(buffer),
}
}
}
impl Operation<'_> {
fn is_write(&self) -> bool {
matches!(self, Operation::Write(_))
}
fn kind(&self) -> OpKind {
match self {
Operation::Write(_) => OpKind::Write,
Operation::Read(_) => OpKind::Read,
}
}
fn is_empty(&self) -> bool {
match self {
Operation::Write(buffer) => buffer.is_empty(),
Operation::Read(buffer) => buffer.is_empty(),
}
}
}
#[derive(Debug)]
enum Command {
Start,
Stop,
End,
Write {
ack_exp: Ack,
ack_check_en: bool,
#[doc = property!("i2c_master.fifo_size", str)]
length: u8,
},
Read {
ack_value: Ack,
#[doc = property!("i2c_master.fifo_size", str)]
length: u8,
},
}
enum OperationType {
Write = 0,
Read = 1,
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
enum Ack {
Ack = 0,
Nack = 1,
}
#[instability::unstable]
pub use crate::soc::clocks::I2cFunctionClockSclk as ClockSource;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
frequency: Rate,
#[cfg_attr(i2c_master_has_bus_timeout_enable, doc = "disabled")]
#[cfg_attr(not(i2c_master_has_bus_timeout_enable), doc = concat!(property!("i2c_master.max_bus_timeout", str), " bus cycles"))]
#[builder_lite(unstable)]
timeout: BusTimeout,
software_timeout: SoftwareTimeout,
#[cfg(i2c_master_has_fsm_timeouts)]
#[builder_lite(unstable)]
scl_st_timeout: FsmTimeout,
#[cfg(i2c_master_has_fsm_timeouts)]
#[builder_lite(unstable)]
scl_main_st_timeout: FsmTimeout,
#[builder_lite(unstable)]
clock_source: ClockSource,
#[builder_lite(unstable)]
scl_sample_level: Level,
#[cfg(i2c_master_has_arbitration_en)]
#[builder_lite(unstable)]
bus_arbitration: bool,
}
impl Default for Config {
fn default() -> Self {
Config {
frequency: Rate::from_khz(100),
#[cfg(i2c_master_has_bus_timeout_enable)]
timeout: BusTimeout::Disabled,
#[cfg(not(i2c_master_has_bus_timeout_enable))]
timeout: BusTimeout::Maximum,
software_timeout: SoftwareTimeout::None,
#[cfg(i2c_master_has_fsm_timeouts)]
scl_st_timeout: Default::default(),
#[cfg(i2c_master_has_fsm_timeouts)]
scl_main_st_timeout: Default::default(),
clock_source: Default::default(),
scl_sample_level: Level::High,
#[cfg(i2c_master_has_arbitration_en)]
bus_arbitration: false,
}
}
}
#[procmacros::doc_replace]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct I2c<'d, Dm: DriverMode> {
i2c: AnyI2c<'d>,
phantom: PhantomData<Dm>,
guard: PeripheralGuard,
config: DriverConfig,
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct DriverConfig {
config: Config,
sda_pin: PinGuard,
scl_pin: PinGuard,
}
impl<'d> I2c<'d, Blocking> {
#[procmacros::doc_replace]
pub fn new(i2c: impl Instance + 'd, config: Config) -> Result<Self, ConfigError> {
let guard = PeripheralGuard::new(i2c.info().peripheral);
ClockTree::with(|clocks| {
let clock = i2c.info().clock_instance;
let config = I2cFunctionClockConfig::new(
Default::default(),
#[cfg(any(i2c_master_version = "3", i2c_master_version = "4"))]
0,
);
clock.configure_function_clock(clocks, config);
});
let sda_pin = PinGuard::new_unconnected();
let scl_pin = PinGuard::new_unconnected();
let i2c_any = i2c.degrade();
let i2c = I2c {
i2c: i2c_any,
phantom: PhantomData,
guard,
config: DriverConfig {
config,
sda_pin,
scl_pin,
},
};
let i2c = i2c.with_scl(crate::gpio::Level::High);
let mut i2c = i2c.with_sda(crate::gpio::Level::High);
i2c.apply_config(&config)?;
Ok(i2c)
}
pub fn into_async(mut self) -> I2c<'d, Async> {
self.set_interrupt_handler(self.driver().info.async_handler);
I2c {
i2c: self.i2c,
phantom: PhantomData,
guard: self.guard,
config: self.config,
}
}
#[cfg_attr(
not(multi_core),
doc = "Registers an interrupt handler for the peripheral."
)]
#[cfg_attr(
multi_core,
doc = "Registers an interrupt handler for the peripheral on the current core."
)]
#[doc = ""]
#[instability::unstable]
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
self.i2c.set_interrupt_handler(handler);
}
#[instability::unstable]
pub fn listen(&mut self, interrupts: impl Into<EnumSet<Event>>) {
self.i2c.info().enable_listen(interrupts.into(), true)
}
#[instability::unstable]
pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<Event>>) {
self.i2c.info().enable_listen(interrupts.into(), false)
}
#[instability::unstable]
pub fn interrupts(&mut self) -> EnumSet<Event> {
self.i2c.info().interrupts()
}
#[instability::unstable]
pub fn clear_interrupts(&mut self, interrupts: EnumSet<Event>) {
self.i2c.info().clear_interrupts(interrupts)
}
}
impl private::Sealed for I2c<'_, Blocking> {}
#[instability::unstable]
impl crate::interrupt::InterruptConfigurable for I2c<'_, Blocking> {
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
self.i2c.set_interrupt_handler(handler);
}
}
#[derive(Debug, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[instability::unstable]
pub enum Event {
EndDetect,
TxComplete,
#[cfg(i2c_master_has_tx_fifo_watermark)]
TxFifoWatermark,
}
impl<'d> I2c<'d, Async> {
pub fn into_blocking(self) -> I2c<'d, Blocking> {
self.i2c.disable_peri_interrupt_on_all_cores();
I2c {
i2c: self.i2c,
phantom: PhantomData,
guard: self.guard,
config: self.config,
}
}
#[procmacros::doc_replace]
pub async fn write_async<A: Into<I2cAddress>>(
&mut self,
address: A,
buffer: &[u8],
) -> Result<(), Error> {
self.transaction_async(address, &mut [Operation::Write(buffer)])
.await
}
#[procmacros::doc_replace]
pub async fn read_async<A: Into<I2cAddress>>(
&mut self,
address: A,
buffer: &mut [u8],
) -> Result<(), Error> {
self.transaction_async(address, &mut [Operation::Read(buffer)])
.await
}
#[procmacros::doc_replace]
pub async fn write_read_async<A: Into<I2cAddress>>(
&mut self,
address: A,
write_buffer: &[u8],
read_buffer: &mut [u8],
) -> Result<(), Error> {
self.transaction_async(
address,
&mut [Operation::Write(write_buffer), Operation::Read(read_buffer)],
)
.await
}
#[procmacros::doc_replace]
#[cfg_attr(
any(esp32, esp32s2),
doc = "\n\nOn ESP32 and ESP32-S2 there might be issues combining large read/write operations with small (<3 bytes) read/write operations.\n\n"
)]
pub async fn transaction_async<'a, A: Into<I2cAddress>>(
&mut self,
address: A,
operations: impl IntoIterator<Item = &'a mut Operation<'a>>,
) -> Result<(), Error> {
let _clock_guard = I2cClockGuard::new(self.i2c.reborrow());
self.driver()
.transaction_impl_async(address.into(), operations.into_iter().map(Operation::from))
.await
.inspect_err(|error| self.internal_recover(error))
}
}
impl<'d, Dm> I2c<'d, Dm>
where
Dm: DriverMode,
{
fn driver(&self) -> Driver<'_> {
Driver {
info: self.i2c.info(),
state: self.i2c.state(),
config: &self.config,
}
}
fn internal_recover(&self, error: &Error) {
self.driver().reset_fsm(*error == Error::Timeout)
}
#[procmacros::doc_replace]
pub fn with_sda(mut self, sda: impl PeripheralInput<'d> + PeripheralOutput<'d>) -> Self {
let info = self.driver().info;
let input = info.sda_input;
let output = info.sda_output;
Driver::connect_pin(sda.into(), input, output, &mut self.config.sda_pin);
self
}
#[procmacros::doc_replace]
pub fn with_scl(mut self, scl: impl PeripheralInput<'d> + PeripheralOutput<'d>) -> Self {
let info = self.driver().info;
let input = info.scl_input;
let output = info.scl_output;
Driver::connect_pin(scl.into(), input, output, &mut self.config.scl_pin);
self
}
#[procmacros::doc_replace]
pub fn write<A: Into<I2cAddress>>(&mut self, address: A, buffer: &[u8]) -> Result<(), Error> {
self.transaction(address, &mut [Operation::Write(buffer)])
}
#[procmacros::doc_replace]
pub fn read<A: Into<I2cAddress>>(
&mut self,
address: A,
buffer: &mut [u8],
) -> Result<(), Error> {
self.transaction(address, &mut [Operation::Read(buffer)])
}
#[procmacros::doc_replace]
pub fn write_read<A: Into<I2cAddress>>(
&mut self,
address: A,
write_buffer: &[u8],
read_buffer: &mut [u8],
) -> Result<(), Error> {
self.transaction(
address,
&mut [Operation::Write(write_buffer), Operation::Read(read_buffer)],
)
}
#[procmacros::doc_replace]
#[cfg_attr(
any(esp32, esp32s2),
doc = "\n\nOn ESP32 and ESP32-S2 it is advisable to not combine large read/write operations with small (<3 bytes) read/write operations.\n\n"
)]
pub fn transaction<'a, A: Into<I2cAddress>>(
&mut self,
address: A,
operations: impl IntoIterator<Item = &'a mut Operation<'a>>,
) -> Result<(), Error> {
let _clock_guard = I2cClockGuard::new(self.i2c.reborrow());
self.driver()
.transaction_impl(address.into(), operations.into_iter().map(Operation::from))
.inspect_err(|error| self.internal_recover(error))
}
#[procmacros::doc_replace]
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
self.config.config = *config;
self.driver().setup(config)?;
self.driver().reset_fsm(false);
Ok(())
}
#[instability::unstable]
pub fn force_scl_low(&mut self, low: bool) {
self.driver().force_scl_low(low);
}
#[instability::unstable]
pub fn force_sda_low(&mut self, low: bool) {
self.driver().force_sda_low(low);
}
}