#![cfg_attr(docsrs, procmacros::doc_replace)]
use core::{
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use embedded_hal::i2c::Operation as EhalOperation;
use enumset::{EnumSet, EnumSetType};
use crate::{
Async,
Blocking,
DriverMode,
asynch::AtomicWaker,
gpio::{
DriveMode,
InputSignal,
OutputConfig,
OutputSignal,
PinGuard,
Pull,
interconnect::{self, PeripheralInput, PeripheralOutput},
},
handler,
interrupt::InterruptHandler,
pac::i2c0::{COMD, RegisterBlock},
private,
ram,
system::PeripheralGuard,
time::{Duration, Instant, Rate},
};
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 = "Note that 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(),
}
}
}
impl embedded_hal::i2c::Error for Error {
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
use embedded_hal::i2c::ErrorKind;
match self {
Self::FifoExceeded => ErrorKind::Overrun,
Self::ArbitrationLost => ErrorKind::ArbitrationLoss,
Self::AcknowledgeCheckFailed(reason) => ErrorKind::NoAcknowledge(reason.into()),
_ => ErrorKind::Other,
}
}
}
#[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,
}
#[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,
}
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(),
}
}
}
#[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,
}
#[instability::unstable]
impl<Dm: DriverMode> embassy_embedded_hal::SetConfig for I2c<'_, Dm> {
type Config = Config;
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.apply_config(config)
}
}
impl<Dm: DriverMode> embedded_hal::i2c::ErrorType for I2c<'_, Dm> {
type Error = Error;
}
impl<Dm: DriverMode> embedded_hal::i2c::I2c for I2c<'_, Dm> {
fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
self.driver()
.transaction_impl(
I2cAddress::SevenBit(address),
operations.iter_mut().map(Operation::from),
)
.inspect_err(|error| self.internal_recover(error))
}
}
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);
let sda_pin = PinGuard::new_unconnected();
let scl_pin = PinGuard::new_unconnected();
let i2c = I2c {
i2c: i2c.degrade(),
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,
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct I2cFuture<'a> {
events: EnumSet<Event>,
driver: Driver<'a>,
deadline: Option<Instant>,
finished: bool,
}
impl<'a> I2cFuture<'a> {
pub fn new(events: EnumSet<Event>, driver: Driver<'a>, deadline: Option<Instant>) -> Self {
driver.regs().int_ena().modify(|_, w| {
for event in events {
match event {
Event::EndDetect => w.end_detect().set_bit(),
Event::TxComplete => w.trans_complete().set_bit(),
#[cfg(i2c_master_has_tx_fifo_watermark)]
Event::TxFifoWatermark => w.txfifo_wm().set_bit(),
};
}
w.arbitration_lost().set_bit();
w.time_out().set_bit();
w.nack().set_bit();
#[cfg(i2c_master_has_fsm_timeouts)]
{
w.scl_main_st_to().set_bit();
w.scl_st_to().set_bit();
}
w
});
Self::new_blocking(events, driver, deadline)
}
pub fn new_blocking(
events: EnumSet<Event>,
driver: Driver<'a>,
deadline: Option<Instant>,
) -> Self {
Self {
events,
driver,
deadline,
finished: false,
}
}
fn is_done(&self) -> bool {
!self.driver.info.interrupts().is_disjoint(self.events)
}
fn poll_completion(&mut self) -> Poll<Result<(), Error>> {
let now = if self.deadline.is_some() {
Instant::now()
} else {
Instant::EPOCH
};
let error = self.driver.check_errors();
let result = if self.is_done() {
let result = if error == Err(Error::Timeout) {
Ok(())
} else {
error
};
Poll::Ready(result)
} else if error.is_err() {
Poll::Ready(error)
} else if let Some(deadline) = self.deadline
&& now > deadline
{
Poll::Ready(Err(Error::Timeout))
} else {
Poll::Pending
};
if result.is_ready() {
self.finished = true;
}
result
}
}
impl core::future::Future for I2cFuture<'_> {
type Output = Result<(), Error>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
self.driver.state.waker.register(ctx.waker());
let result = self.poll_completion();
if result.is_pending() && self.deadline.is_some() {
ctx.waker().wake_by_ref();
}
result
}
}
impl Drop for I2cFuture<'_> {
fn drop(&mut self) {
if !self.finished {
let result = self.poll_completion();
if result.is_pending() || result == Poll::Ready(Err(Error::Timeout)) {
self.driver.reset_fsm(true);
}
}
}
}
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> {
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> {
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(())
}
}
impl embedded_hal_async::i2c::I2c for I2c<'_, Async> {
async fn transaction(
&mut self,
address: u8,
operations: &mut [EhalOperation<'_>],
) -> Result<(), Self::Error> {
self.driver()
.transaction_impl_async(address.into(), operations.iter_mut().map(Operation::from))
.await
.inspect_err(|error| self.internal_recover(error))
}
}
#[ram]
fn async_handler(info: &Info, state: &State) {
info.regs().int_ena().write(|w| unsafe { w.bits(0) });
state.waker.wake();
}
fn set_filter(
register_block: &RegisterBlock,
sda_threshold: Option<u8>,
scl_threshold: Option<u8>,
) {
cfg_if::cfg_if! {
if #[cfg(i2c_master_separate_filter_config_registers)] {
register_block.sda_filter_cfg().modify(|_, w| {
if let Some(threshold) = sda_threshold {
unsafe { w.sda_filter_thres().bits(threshold) };
}
w.sda_filter_en().bit(sda_threshold.is_some())
});
register_block.scl_filter_cfg().modify(|_, w| {
if let Some(threshold) = scl_threshold {
unsafe { w.scl_filter_thres().bits(threshold) };
}
w.scl_filter_en().bit(scl_threshold.is_some())
});
} else {
register_block.filter_cfg().modify(|_, w| {
if let Some(threshold) = sda_threshold {
unsafe { w.sda_filter_thres().bits(threshold) };
}
if let Some(threshold) = scl_threshold {
unsafe { w.scl_filter_thres().bits(threshold) };
}
w.sda_filter_en().bit(sda_threshold.is_some());
w.scl_filter_en().bit(scl_threshold.is_some())
});
}
}
}
#[expect(clippy::too_many_arguments)]
#[allow(unused)]
fn configure_clock(
info: &Info,
sclk_div: u32,
scl_low_period: u32,
scl_high_period: u32,
scl_wait_high_period: u32,
sda_hold_time: u32,
sda_sample_time: u32,
scl_rstart_setup_time: u32,
scl_stop_setup_time: u32,
scl_start_hold_time: u32,
scl_stop_hold_time: u32,
timeout: Option<u32>,
) -> Result<(), ConfigError> {
unsafe {
cfg_if::cfg_if! {
if #[cfg(all(soc_has_pcr, soc_has_i2c1))] {
crate::peripherals::PCR::regs().i2c_sclk_conf(info.id as usize).modify(|_, w| {
w.i2c_sclk_sel().clear_bit();
w.i2c_sclk_div_num().bits((sclk_div - 1) as u8);
w.i2c_sclk_en().set_bit()
});
} else if #[cfg(soc_has_pcr)] {
crate::peripherals::PCR::regs().i2c_sclk_conf().modify(|_, w| {
w.i2c_sclk_sel().clear_bit();
w.i2c_sclk_div_num().bits((sclk_div - 1) as u8);
w.i2c_sclk_en().set_bit()
});
} else if #[cfg(not(any(esp32, esp32s2)))] { info.regs().clk_conf().modify(|_, w| {
w.sclk_sel().clear_bit();
w.sclk_div_num().bits((sclk_div - 1) as u8)
});
}
}
info.regs()
.scl_low_period()
.write(|w| w.scl_low_period().bits(scl_low_period as u16));
#[cfg(not(esp32))]
let scl_wait_high_period = scl_wait_high_period
.try_into()
.map_err(|_| ConfigError::FrequencyOutOfRange)?;
info.regs().scl_high_period().write(|w| {
#[cfg(not(esp32))] w.scl_wait_high_period().bits(scl_wait_high_period);
w.scl_high_period().bits(scl_high_period as u16)
});
info.regs()
.sda_hold()
.write(|w| w.time().bits(sda_hold_time as u16));
info.regs()
.sda_sample()
.write(|w| w.time().bits(sda_sample_time as u16));
info.regs()
.scl_rstart_setup()
.write(|w| w.time().bits(scl_rstart_setup_time as u16));
info.regs()
.scl_stop_setup()
.write(|w| w.time().bits(scl_stop_setup_time as u16));
info.regs()
.scl_start_hold()
.write(|w| w.time().bits(scl_start_hold_time as u16));
info.regs()
.scl_stop_hold()
.write(|w| w.time().bits(scl_stop_hold_time as u16));
cfg_if::cfg_if! {
if #[cfg(i2c_master_has_bus_timeout_enable)] {
info.regs().to().write(|w| {
w.time_out_en().bit(timeout.is_some());
w.time_out_value().bits(timeout.unwrap_or(1) as _)
});
} else {
info.regs()
.to()
.write(|w| w.time_out().bits(timeout.unwrap_or(1)));
}
}
}
Ok(())
}
#[doc(hidden)]
#[derive(Debug)]
#[non_exhaustive]
#[allow(private_interfaces, reason = "Unstable details")]
pub struct Info {
#[cfg(soc_has_i2c1)]
pub id: u8,
pub register_block: *const RegisterBlock,
pub peripheral: crate::system::Peripheral,
pub async_handler: InterruptHandler,
pub scl_output: OutputSignal,
pub scl_input: InputSignal,
pub sda_output: OutputSignal,
pub sda_input: InputSignal,
}
impl Info {
pub fn regs(&self) -> &RegisterBlock {
unsafe { &*self.register_block }
}
fn enable_listen(&self, interrupts: EnumSet<Event>, enable: bool) {
let reg_block = self.regs();
reg_block.int_ena().modify(|_, w| {
for interrupt in interrupts {
match interrupt {
Event::EndDetect => w.end_detect().bit(enable),
Event::TxComplete => w.trans_complete().bit(enable),
#[cfg(i2c_master_has_tx_fifo_watermark)]
Event::TxFifoWatermark => w.txfifo_wm().bit(enable),
};
}
w
});
}
fn interrupts(&self) -> EnumSet<Event> {
let mut res = EnumSet::new();
let reg_block = self.regs();
let ints = reg_block.int_raw().read();
if ints.end_detect().bit_is_set() {
res.insert(Event::EndDetect);
}
if ints.trans_complete().bit_is_set() {
res.insert(Event::TxComplete);
}
#[cfg(i2c_master_has_tx_fifo_watermark)]
if ints.txfifo_wm().bit_is_set() {
res.insert(Event::TxFifoWatermark);
}
res
}
fn clear_interrupts(&self, interrupts: EnumSet<Event>) {
let reg_block = self.regs();
reg_block.int_clr().write(|w| {
for interrupt in interrupts {
match interrupt {
Event::EndDetect => w.end_detect().clear_bit_by_one(),
Event::TxComplete => w.trans_complete().clear_bit_by_one(),
#[cfg(i2c_master_has_tx_fifo_watermark)]
Event::TxFifoWatermark => w.txfifo_wm().clear_bit_by_one(),
};
}
w
});
}
}
impl PartialEq for Info {
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.register_block, other.register_block)
}
}
unsafe impl Sync for Info {}
#[derive(Clone, Copy)]
enum Deadline {
None,
Fixed(Instant),
PerByte(Duration),
}
impl Deadline {
fn start(self, data_len: usize) -> Option<Instant> {
match self {
Deadline::None => None,
Deadline::Fixed(deadline) => Some(deadline),
Deadline::PerByte(duration) => Some(Instant::now() + duration * data_len as u32),
}
}
}
#[allow(dead_code)] #[derive(Clone, Copy)]
struct Driver<'a> {
info: &'a Info,
state: &'a State,
config: &'a DriverConfig,
}
impl Driver<'_> {
fn regs(&self) -> &RegisterBlock {
self.info.regs()
}
fn connect_pin(
pin: crate::gpio::interconnect::OutputSignal<'_>,
input: InputSignal,
output: OutputSignal,
guard: &mut PinGuard,
) {
pin.set_output_high(true);
pin.apply_output_config(
&OutputConfig::default()
.with_drive_mode(DriveMode::OpenDrain)
.with_pull(Pull::Up),
);
pin.set_output_enable(true);
pin.set_input_enable(true);
input.connect_to(&pin);
*guard = interconnect::OutputSignal::connect_with_guard(pin, output);
}
fn init_master(&self) {
self.regs().ctr().write(|w| {
w.ms_mode().set_bit();
w.sda_force_out().set_bit();
w.scl_force_out().set_bit();
w.tx_lsb_first().clear_bit();
w.rx_lsb_first().clear_bit();
#[cfg(i2c_master_has_arbitration_en)]
w.arbitration_en().clear_bit();
#[cfg(esp32s2)]
w.ref_always_on().set_bit();
w.clk_en().set_bit()
});
}
fn setup(&self, config: &Config) -> Result<(), ConfigError> {
self.init_master();
set_filter(self.regs(), Some(7), Some(7));
self.set_frequency(config)?;
#[cfg(i2c_master_has_fsm_timeouts)]
{
self.regs()
.scl_st_time_out()
.write(|w| unsafe { w.scl_st_to().bits(config.scl_st_timeout.value()) });
self.regs()
.scl_main_st_time_out()
.write(|w| unsafe { w.scl_main_st_to().bits(config.scl_main_st_timeout.value()) });
}
self.update_registers();
Ok(())
}
fn do_fsm_reset(&self) {
cfg_if::cfg_if! {
if #[cfg(i2c_master_has_reliable_fsm_reset)] {
self.regs().ctr().modify(|_, w| w.fsm_rst().set_bit());
} else {
crate::system::PeripheralClockControl::reset(self.info.peripheral);
self.setup(&self.config.config).ok();
}
}
}
fn reset_fsm(&self, clear_bus: bool) {
if clear_bus {
self.clear_bus_blocking(true);
} else {
self.do_fsm_reset();
}
}
fn bus_busy(&self) -> bool {
self.regs().sr().read().bus_busy().bit_is_set()
}
fn ensure_idle_blocking(&self) {
if self.bus_busy() {
self.clear_bus_blocking(false);
}
}
async fn ensure_idle(&self) {
if self.bus_busy() {
self.clear_bus().await;
}
}
fn reset_before_transmission(&self) {
self.clear_all_interrupts();
self.reset_fifo();
self.reset_command_list();
}
fn clear_bus_blocking(&self, reset_fsm: bool) {
let mut future = ClearBusFuture::new(*self, reset_fsm);
let start = Instant::now();
while future.poll_completion().is_pending() {
if start.elapsed() > CLEAR_BUS_TIMEOUT_MS {
break;
}
}
}
async fn clear_bus(&self) {
let clear_bus = ClearBusFuture::new(*self, true);
let start = Instant::now();
embassy_futures::select::select(clear_bus, async {
while start.elapsed() < CLEAR_BUS_TIMEOUT_MS {
embassy_futures::yield_now().await;
}
})
.await;
}
fn reset_command_list(&self) {
for cmd in self.regs().comd_iter() {
cmd.reset();
}
}
#[cfg(esp32)]
fn set_frequency(&self, clock_config: &Config) -> Result<(), ConfigError> {
let timeout = clock_config.timeout;
let source_clk = crate::soc::clocks::ClockTree::with(|clocks| {
crate::soc::clocks::apb_clk_frequency(clocks)
});
let bus_freq = clock_config.frequency.as_hz();
let half_cycle: u32 = source_clk / bus_freq / 2;
let scl_low = half_cycle;
let scl_high = half_cycle;
let sda_hold = half_cycle / 2;
let sda_sample = scl_high / 2;
let setup = half_cycle;
let hold = half_cycle;
let scl_low = scl_low - 1;
let mut scl_high = scl_high;
scl_high -= 7 + 6;
let scl_high_period = scl_high;
let scl_low_period = scl_low;
let sda_hold_time = sda_hold;
let sda_sample_time = sda_sample;
let scl_rstart_setup_time = setup;
let scl_stop_setup_time = setup;
let scl_start_hold_time = hold;
let scl_stop_hold_time = hold;
configure_clock(
self.info,
0,
scl_low_period,
scl_high_period,
0,
sda_hold_time,
sda_sample_time,
scl_rstart_setup_time,
scl_stop_setup_time,
scl_start_hold_time,
scl_stop_hold_time,
timeout.apb_cycles(half_cycle)?,
)?;
Ok(())
}
#[cfg(esp32s2)]
fn set_frequency(&self, clock_config: &Config) -> Result<(), ConfigError> {
let timeout = clock_config.timeout;
let source_clk = crate::soc::clocks::ClockTree::with(|clocks| {
crate::soc::clocks::apb_clk_frequency(clocks)
});
let bus_freq = clock_config.frequency.as_hz();
let half_cycle: u32 = source_clk / bus_freq / 2;
let scl_low = half_cycle;
let scl_high = half_cycle / 2 + 2;
let scl_wait_high = half_cycle - scl_high;
let sda_hold = half_cycle / 2;
let sda_sample = half_cycle / 2 - 1;
let setup = half_cycle;
let hold = half_cycle;
let scl_low_period = scl_low - 1;
let scl_high_period = scl_high;
let scl_wait_high_period = scl_wait_high;
let sda_hold_time = sda_hold;
let sda_sample_time = sda_sample;
let scl_rstart_setup_time = setup;
let scl_stop_setup_time = setup;
let scl_start_hold_time = hold - 1;
let scl_stop_hold_time = hold;
configure_clock(
self.info,
0,
scl_low_period,
scl_high_period,
scl_wait_high_period,
sda_hold_time,
sda_sample_time,
scl_rstart_setup_time,
scl_stop_setup_time,
scl_start_hold_time,
scl_stop_hold_time,
timeout.apb_cycles(half_cycle)?,
)?;
Ok(())
}
#[cfg(not(any(esp32, esp32s2)))]
fn set_frequency(&self, clock_config: &Config) -> Result<(), ConfigError> {
let timeout = clock_config.timeout;
let source_clk = crate::soc::clocks::ClockTree::with(|clocks| {
crate::soc::clocks::xtal_clk_frequency(clocks)
});
let bus_freq = clock_config.frequency.as_hz();
let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1;
let sclk_freq: u32 = source_clk / clkm_div;
let half_cycle: u32 = sclk_freq / bus_freq / 2;
let scl_low = half_cycle;
let scl_wait_high = if bus_freq >= 80 * 1000 {
half_cycle / 2 - 2
} else {
half_cycle / 4
};
let scl_high = half_cycle - scl_wait_high;
let sda_hold = half_cycle / 4;
let sda_sample = half_cycle / 2;
let setup = half_cycle;
let hold = half_cycle;
let scl_low_period = scl_low - 1;
let scl_high_period = scl_high;
let scl_wait_high_period = scl_wait_high;
let sda_hold_time = sda_hold - 1;
let sda_sample_time = sda_sample - 1;
let scl_rstart_setup_time = setup - 1;
let scl_stop_setup_time = setup - 1;
let scl_start_hold_time = hold - 1;
let scl_stop_hold_time = hold - 1;
configure_clock(
self.info,
clkm_div,
scl_low_period,
scl_high_period,
scl_wait_high_period,
sda_hold_time,
sda_sample_time,
scl_rstart_setup_time,
scl_stop_setup_time,
scl_start_hold_time,
scl_stop_hold_time,
timeout.apb_cycles(half_cycle)?,
)?;
Ok(())
}
fn setup_write<'a, I>(
&self,
addr: I2cAddress,
bytes: &[u8],
start: bool,
stop: bool,
cmd_iterator: &mut I,
) -> Result<(), Error>
where
I: Iterator<Item = &'a COMD>,
{
let max_len = if start {
I2C_CHUNK_SIZE
} else {
I2C_CHUNK_SIZE + 1
};
if bytes.len() > max_len {
return Err(Error::FifoExceeded);
}
if start {
add_cmd(cmd_iterator, Command::Start)?;
}
let write_len = if start { bytes.len() + 1 } else { bytes.len() };
if write_len > 0 {
if cfg!(esp32) && !(start || stop) {
add_cmd(
cmd_iterator,
Command::Write {
ack_exp: Ack::Ack,
ack_check_en: true,
length: (write_len as u8) - 1,
},
)?;
add_cmd(
cmd_iterator,
Command::Write {
ack_exp: Ack::Ack,
ack_check_en: true,
length: 1,
},
)?;
} else {
add_cmd(
cmd_iterator,
Command::Write {
ack_exp: Ack::Ack,
ack_check_en: true,
length: write_len as u8,
},
)?;
}
}
if start {
match addr {
I2cAddress::SevenBit(addr) => {
write_fifo(self.regs(), (addr << 1) | OperationType::Write as u8);
}
}
}
for b in bytes {
write_fifo(self.regs(), *b);
}
Ok(())
}
fn setup_read<'a, I>(
&self,
addr: I2cAddress,
buffer: &mut [u8],
start: bool,
stop: bool,
will_continue: bool,
cmd_iterator: &mut I,
) -> Result<(), Error>
where
I: Iterator<Item = &'a COMD>,
{
if buffer.is_empty() {
return Err(Error::ZeroLengthInvalid);
}
let (max_len, initial_len) = if will_continue {
(I2C_CHUNK_SIZE + 1, buffer.len())
} else {
(I2C_CHUNK_SIZE, buffer.len() - 1)
};
if buffer.len() > max_len {
return Err(Error::FifoExceeded);
}
if start {
add_cmd(cmd_iterator, Command::Start)?;
add_cmd(
cmd_iterator,
Command::Write {
ack_exp: Ack::Ack,
ack_check_en: true,
length: 1,
},
)?;
}
if initial_len > 0 {
let extra_commands = if cfg!(esp32) {
match (start, will_continue) {
(true, _) => 0,
(false, true) => 2,
(false, false) => 1 - stop as u8,
}
} else {
0
};
add_cmd(
cmd_iterator,
Command::Read {
ack_value: Ack::Ack,
length: initial_len as u8 - extra_commands,
},
)?;
for _ in 0..extra_commands {
add_cmd(
cmd_iterator,
Command::Read {
ack_value: Ack::Ack,
length: 1,
},
)?;
}
}
if !will_continue {
add_cmd(
cmd_iterator,
Command::Read {
ack_value: Ack::Nack,
length: 1,
},
)?;
}
self.update_registers();
if start {
match addr {
I2cAddress::SevenBit(addr) => {
write_fifo(self.regs(), (addr << 1) | OperationType::Read as u8);
}
}
}
Ok(())
}
fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> {
if self.regs().sr().read().rxfifo_cnt().bits() < buffer.len() as u8 {
return Err(Error::ExecutionIncomplete);
}
for byte in buffer.iter_mut() {
*byte = read_fifo(self.regs());
}
debug_assert!(self.regs().sr().read().rxfifo_cnt().bits() == 0);
Ok(())
}
fn clear_all_interrupts(&self) {
self.regs()
.int_clr()
.write(|w| unsafe { w.bits(property!("i2c_master.ll_intr_mask")) });
}
async fn wait_for_completion(&self, deadline: Option<Instant>) -> Result<(), Error> {
I2cFuture::new(Event::TxComplete | Event::EndDetect, *self, deadline).await?;
self.check_all_commands_done(deadline).await
}
fn wait_for_completion_blocking(&self, deadline: Option<Instant>) -> Result<(), Error> {
let mut future =
I2cFuture::new_blocking(Event::TxComplete | Event::EndDetect, *self, deadline);
loop {
if let Poll::Ready(result) = future.poll_completion() {
result?;
return self.check_all_commands_done_blocking(deadline);
}
}
}
fn all_commands_done(&self, deadline: Option<Instant>) -> Result<bool, Error> {
let now = if deadline.is_some() {
Instant::now()
} else {
Instant::EPOCH
};
self.check_errors()?;
for cmd_reg in self.regs().comd_iter() {
let cmd = cmd_reg.read();
if cmd.bits() != 0x0 && !cmd.opcode().is_end() && !cmd.command_done().bit_is_set() {
if let Some(deadline) = deadline
&& now > deadline
{
return Err(Error::ExecutionIncomplete);
}
return Ok(false);
}
if cmd.opcode().is_end() {
break;
}
if cmd.opcode().is_stop() {
#[cfg(esp32)]
if self.regs().sr().read().scl_state_last() == 6 {
self.check_errors()?;
} else {
continue;
}
break;
}
}
Ok(true)
}
fn check_all_commands_done_blocking(&self, deadline: Option<Instant>) -> Result<(), Error> {
while !self.all_commands_done(deadline)? {}
self.check_errors()?;
Ok(())
}
async fn check_all_commands_done(&self, deadline: Option<Instant>) -> Result<(), Error> {
while !self.all_commands_done(deadline)? {
embassy_futures::yield_now().await;
}
self.check_errors()?;
Ok(())
}
fn check_errors(&self) -> Result<(), Error> {
let r = self.regs().int_raw().read();
if r.nack().bit_is_set() {
return Err(Error::AcknowledgeCheckFailed(estimate_ack_failed_reason(
self.regs(),
)));
}
if r.arbitration_lost().bit_is_set() {
return Err(Error::ArbitrationLost);
}
#[cfg(not(esp32))]
if r.trans_complete().bit_is_set() && self.regs().sr().read().resp_rec().bit_is_clear() {
return Err(Error::AcknowledgeCheckFailed(
AcknowledgeCheckFailedReason::Data,
));
}
#[cfg(i2c_master_has_fsm_timeouts)]
{
if r.scl_st_to().bit_is_set() {
return Err(Error::Timeout);
}
if r.scl_main_st_to().bit_is_set() {
return Err(Error::Timeout);
}
}
if r.time_out().bit_is_set() {
return Err(Error::Timeout);
}
Ok(())
}
fn update_registers(&self) {
#[cfg(i2c_master_has_conf_update)]
self.regs().ctr().modify(|_, w| w.conf_upgate().set_bit());
}
fn start_transmission(&self) {
self.regs().ctr().modify(|_, w| w.trans_start().set_bit());
}
#[cfg(not(esp32))]
fn reset_fifo(&self) {
self.regs().fifo_conf().write(|w| unsafe {
w.tx_fifo_rst().set_bit();
w.rx_fifo_rst().set_bit();
w.nonfifo_en().clear_bit();
w.fifo_prt_en().set_bit();
w.rxfifo_wm_thrhd().bits(1);
w.txfifo_wm_thrhd().bits(8)
});
self.regs().fifo_conf().modify(|_, w| {
w.tx_fifo_rst().clear_bit();
w.rx_fifo_rst().clear_bit()
});
self.regs().int_clr().write(|w| {
w.rxfifo_wm().clear_bit_by_one();
w.txfifo_wm().clear_bit_by_one()
});
self.update_registers();
}
#[cfg(esp32)]
fn reset_fifo(&self) {
self.regs().fifo_conf().write(|w| unsafe {
w.tx_fifo_rst().set_bit();
w.rx_fifo_rst().set_bit();
w.nonfifo_en().clear_bit();
w.nonfifo_rx_thres().bits(1);
w.nonfifo_tx_thres().bits(32)
});
self.regs().fifo_conf().modify(|_, w| {
w.tx_fifo_rst().clear_bit();
w.rx_fifo_rst().clear_bit()
});
self.regs()
.int_clr()
.write(|w| w.rxfifo_full().clear_bit_by_one());
}
fn start_write_operation(
&self,
address: I2cAddress,
buffer: &[u8],
start: bool,
stop: bool,
deadline: Deadline,
) -> Result<Option<Instant>, Error> {
let cmd_iterator = &mut self.regs().comd_iter();
self.setup_write(address, buffer, start, stop, cmd_iterator)?;
if stop {
add_cmd(cmd_iterator, Command::Stop)?;
}
if !(start && stop) {
add_cmd(cmd_iterator, Command::End)?;
}
self.start_transmission();
Ok(deadline.start(buffer.len() + address.bytes()))
}
fn start_read_operation(
&self,
address: I2cAddress,
buffer: &mut [u8],
start: bool,
will_continue: bool,
stop: bool,
deadline: Deadline,
) -> Result<Option<Instant>, Error> {
debug_assert!(buffer.len() <= I2C_FIFO_SIZE);
let cmd_iterator = &mut self.regs().comd_iter();
self.setup_read(address, buffer, start, stop, will_continue, cmd_iterator)?;
if stop {
add_cmd(cmd_iterator, Command::Stop)?;
}
if !(start && stop) {
add_cmd(cmd_iterator, Command::End)?;
}
self.start_transmission();
Ok(deadline.start(buffer.len() + address.bytes()))
}
fn write_operation_blocking(
&self,
address: I2cAddress,
bytes: &[u8],
start: bool,
stop: bool,
deadline: Deadline,
) -> Result<(), Error> {
address.validate()?;
self.reset_before_transmission();
if bytes.is_empty() && !start && !stop {
return Ok(());
}
let deadline = self.start_write_operation(address, bytes, start, stop, deadline)?;
self.wait_for_completion_blocking(deadline)?;
Ok(())
}
fn read_operation_blocking(
&self,
address: I2cAddress,
buffer: &mut [u8],
start: bool,
stop: bool,
will_continue: bool,
deadline: Deadline,
) -> Result<(), Error> {
address.validate()?;
self.reset_before_transmission();
if buffer.is_empty() {
return Ok(());
}
let deadline =
self.start_read_operation(address, buffer, start, will_continue, stop, deadline)?;
self.wait_for_completion_blocking(deadline)?;
self.read_all_from_fifo(buffer)?;
Ok(())
}
async fn write_operation(
&self,
address: I2cAddress,
bytes: &[u8],
start: bool,
stop: bool,
deadline: Deadline,
) -> Result<(), Error> {
address.validate()?;
self.reset_before_transmission();
if bytes.is_empty() && !start && !stop {
return Ok(());
}
let deadline = self.start_write_operation(address, bytes, start, stop, deadline)?;
self.wait_for_completion(deadline).await?;
Ok(())
}
async fn read_operation(
&self,
address: I2cAddress,
buffer: &mut [u8],
start: bool,
stop: bool,
will_continue: bool,
deadline: Deadline,
) -> Result<(), Error> {
address.validate()?;
self.reset_before_transmission();
if buffer.is_empty() {
return Ok(());
}
let deadline =
self.start_read_operation(address, buffer, start, will_continue, stop, deadline)?;
self.wait_for_completion(deadline).await?;
self.read_all_from_fifo(buffer)?;
Ok(())
}
fn read_blocking(
&self,
address: I2cAddress,
buffer: &mut [u8],
start: bool,
stop: bool,
will_continue: bool,
deadline: Deadline,
) -> Result<(), Error> {
let chunk_count = VariableChunkIterMut::new(buffer).count();
for (idx, chunk) in VariableChunkIterMut::new(buffer).enumerate() {
self.read_operation_blocking(
address,
chunk,
start && idx == 0,
stop && idx == chunk_count - 1,
will_continue || idx < chunk_count - 1,
deadline,
)?;
}
Ok(())
}
fn write_blocking(
&self,
address: I2cAddress,
buffer: &[u8],
start: bool,
stop: bool,
deadline: Deadline,
) -> Result<(), Error> {
if buffer.is_empty() {
return self.write_operation_blocking(address, &[], start, stop, deadline);
}
let chunk_count = VariableChunkIter::new(buffer).count();
for (idx, chunk) in VariableChunkIter::new(buffer).enumerate() {
self.write_operation_blocking(
address,
chunk,
start && idx == 0,
stop && idx == chunk_count - 1,
deadline,
)?;
}
Ok(())
}
async fn read(
&self,
address: I2cAddress,
buffer: &mut [u8],
start: bool,
stop: bool,
will_continue: bool,
deadline: Deadline,
) -> Result<(), Error> {
let chunk_count = VariableChunkIterMut::new(buffer).count();
for (idx, chunk) in VariableChunkIterMut::new(buffer).enumerate() {
self.read_operation(
address,
chunk,
start && idx == 0,
stop && idx == chunk_count - 1,
will_continue || idx < chunk_count - 1,
deadline,
)
.await?;
}
Ok(())
}
async fn write(
&self,
address: I2cAddress,
buffer: &[u8],
start: bool,
stop: bool,
deadline: Deadline,
) -> Result<(), Error> {
if buffer.is_empty() {
return self
.write_operation(address, &[], start, stop, deadline)
.await;
}
let chunk_count = VariableChunkIter::new(buffer).count();
for (idx, chunk) in VariableChunkIter::new(buffer).enumerate() {
self.write_operation(
address,
chunk,
start && idx == 0,
stop && idx == chunk_count - 1,
deadline,
)
.await?;
}
Ok(())
}
fn transaction_impl<'a>(
&self,
address: I2cAddress,
operations: impl Iterator<Item = Operation<'a>>,
) -> Result<(), Error> {
address.validate()?;
self.ensure_idle_blocking();
let mut deadline = Deadline::None;
if let SoftwareTimeout::Transaction(timeout) = self.config.config.software_timeout {
deadline = Deadline::Fixed(Instant::now() + timeout);
}
let mut last_op: Option<OpKind> = None;
let mut op_iter = operations
.filter(|op| op.is_write() || !op.is_empty())
.peekable();
while let Some(op) = op_iter.next() {
let next_op = op_iter.peek().map(|v| v.kind());
let kind = op.kind();
match op {
Operation::Write(buffer) => {
if let SoftwareTimeout::PerByte(timeout) = self.config.config.software_timeout {
deadline = Deadline::PerByte(timeout);
}
self.write_blocking(
address,
buffer,
!matches!(last_op, Some(OpKind::Write)),
next_op.is_none(),
deadline,
)?;
}
Operation::Read(buffer) => {
if let SoftwareTimeout::PerByte(timeout) = self.config.config.software_timeout {
deadline = Deadline::PerByte(timeout);
}
self.read_blocking(
address,
buffer,
!matches!(last_op, Some(OpKind::Read)),
next_op.is_none(),
matches!(next_op, Some(OpKind::Read)),
deadline,
)?;
}
}
last_op = Some(kind);
}
Ok(())
}
async fn transaction_impl_async<'a>(
&self,
address: I2cAddress,
operations: impl Iterator<Item = Operation<'a>>,
) -> Result<(), Error> {
address.validate()?;
self.ensure_idle().await;
let mut deadline = Deadline::None;
if let SoftwareTimeout::Transaction(timeout) = self.config.config.software_timeout {
deadline = Deadline::Fixed(Instant::now() + timeout);
}
let mut last_op: Option<OpKind> = None;
let mut op_iter = operations
.filter(|op| op.is_write() || !op.is_empty())
.peekable();
while let Some(op) = op_iter.next() {
let next_op = op_iter.peek().map(|v| v.kind());
let kind = op.kind();
match op {
Operation::Write(buffer) => {
if let SoftwareTimeout::PerByte(timeout) = self.config.config.software_timeout {
deadline = Deadline::PerByte(timeout);
}
self.write(
address,
buffer,
!matches!(last_op, Some(OpKind::Write)),
next_op.is_none(),
deadline,
)
.await?;
}
Operation::Read(buffer) => {
if let SoftwareTimeout::PerByte(timeout) = self.config.config.software_timeout {
deadline = Deadline::PerByte(timeout);
}
self.read(
address,
buffer,
!matches!(last_op, Some(OpKind::Read)),
next_op.is_none(),
matches!(next_op, Some(OpKind::Read)),
deadline,
)
.await?;
}
}
last_op = Some(kind);
}
Ok(())
}
}
struct VariableChunkIterMut<'a, T> {
buffer: &'a mut [T],
}
impl<'a, T> VariableChunkIterMut<'a, T> {
fn new(buffer: &'a mut [T]) -> Self {
Self { buffer }
}
}
impl<'a, T> Iterator for VariableChunkIterMut<'a, T> {
type Item = &'a mut [T];
fn next(&mut self) -> Option<Self::Item> {
if self.buffer.is_empty() {
return None;
}
let s = calculate_chunk_size(self.buffer.len());
let (chunk, remaining) = core::mem::take(&mut self.buffer).split_at_mut(s);
self.buffer = remaining;
Some(chunk)
}
}
struct VariableChunkIter<'a, T> {
buffer: &'a [T],
}
impl<'a, T> VariableChunkIter<'a, T> {
fn new(buffer: &'a [T]) -> Self {
Self { buffer }
}
}
impl<'a, T> Iterator for VariableChunkIter<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<Self::Item> {
if self.buffer.is_empty() {
return None;
}
let s = calculate_chunk_size(self.buffer.len());
let (chunk, remaining) = core::mem::take(&mut self.buffer).split_at(s);
self.buffer = remaining;
Some(chunk)
}
}
fn calculate_chunk_size(remaining: usize) -> usize {
if remaining <= I2C_CHUNK_SIZE {
remaining
} else if remaining > I2C_CHUNK_SIZE + 2 {
I2C_CHUNK_SIZE
} else {
I2C_CHUNK_SIZE - 2
}
}
#[cfg(i2c_master_has_hw_bus_clear)]
mod bus_clear {
use esp_rom_sys::rom::ets_delay_us;
use super::*;
pub struct ClearBusFuture<'a> {
driver: Driver<'a>,
}
impl<'a> ClearBusFuture<'a> {
const BUS_CLEAR_BITS: u8 = 9;
const DELAY_US: u32 = 5;
pub fn new(driver: Driver<'a>, reset_fsm: bool) -> Self {
if reset_fsm {
driver.do_fsm_reset();
}
let mut this = Self { driver };
ets_delay_us(Self::DELAY_US);
this.configure(Self::BUS_CLEAR_BITS);
this
}
fn configure(&mut self, bits: u8) {
self.driver.regs().scl_sp_conf().modify(|_, w| {
unsafe { w.scl_rst_slv_num().bits(bits) };
w.scl_rst_slv_en().bit(bits > 0)
});
self.driver.update_registers();
}
fn is_done(&self) -> bool {
self.driver
.regs()
.scl_sp_conf()
.read()
.scl_rst_slv_en()
.bit_is_clear()
}
pub fn poll_completion(&mut self) -> Poll<()> {
if self.is_done() {
Poll::Ready(())
} else {
Poll::Pending
}
}
}
impl Drop for ClearBusFuture<'_> {
fn drop(&mut self) {
use crate::gpio::AnyPin;
if !self.is_done() {
self.configure(0);
}
let sda = self
.driver
.config
.sda_pin
.pin_number()
.map(|n| unsafe { AnyPin::steal(n) });
let scl = self
.driver
.config
.scl_pin
.pin_number()
.map(|n| unsafe { AnyPin::steal(n) });
if let (Some(sda), Some(scl)) = (sda, scl) {
ets_delay_us(Self::DELAY_US);
sda.set_output_high(true);
scl.set_output_high(false);
self.driver.info.scl_output.disconnect_from(&scl);
self.driver.info.sda_output.disconnect_from(&sda);
sda.set_output_high(false);
ets_delay_us(Self::DELAY_US);
scl.set_output_high(true);
ets_delay_us(Self::DELAY_US);
sda.set_output_high(true);
ets_delay_us(Self::DELAY_US);
self.driver.info.sda_output.connect_to(&sda);
self.driver.info.scl_output.connect_to(&scl);
}
self.driver.clear_all_interrupts();
}
}
}
#[cfg(not(i2c_master_has_hw_bus_clear))]
mod bus_clear {
use super::*;
use crate::gpio::AnyPin;
enum State {
Idle,
SendStop,
SendClock(u8, bool),
}
pub struct ClearBusFuture<'a> {
driver: Driver<'a>,
wait: Instant,
state: State,
reset_fsm: bool,
pins: Option<(AnyPin<'static>, AnyPin<'static>)>,
}
impl<'a> ClearBusFuture<'a> {
const BUS_CLEAR_BITS: u8 = 9;
const SCL_DELAY: Duration = Duration::from_micros(5);
pub fn new(driver: Driver<'a>, reset_fsm: bool) -> Self {
let sda = driver
.config
.sda_pin
.pin_number()
.map(|n| unsafe { AnyPin::steal(n) });
let scl = driver
.config
.scl_pin
.pin_number()
.map(|n| unsafe { AnyPin::steal(n) });
let (Some(sda), Some(scl)) = (sda, scl) else {
if reset_fsm {
driver.do_fsm_reset();
}
return Self {
driver,
wait: Instant::now(),
state: State::Idle,
reset_fsm: false,
pins: None,
};
};
sda.set_output_high(true);
scl.set_output_high(false);
driver.info.scl_output.disconnect_from(&scl);
driver.info.sda_output.disconnect_from(&sda);
let state = State::SendClock(Self::BUS_CLEAR_BITS, false);
Self {
driver,
wait: Instant::now() + Self::SCL_DELAY,
state,
reset_fsm,
pins: Some((sda, scl)),
}
}
}
impl ClearBusFuture<'_> {
pub fn poll_completion(&mut self) -> Poll<()> {
let now = Instant::now();
match self.state {
State::Idle => {
if let Some((sda, _scl)) = self.pins.as_ref() {
if !sda.is_input_high() {
return Poll::Pending;
}
}
return Poll::Ready(());
}
_ if now < self.wait => {
return Poll::Pending;
}
State::SendStop => {
if let Some((sda, _scl)) = self.pins.as_ref() {
sda.set_output_high(true); }
self.state = State::Idle;
return Poll::Pending;
}
State::SendClock(0, false) => {
if let Some((sda, scl)) = self.pins.as_ref() {
sda.set_output_high(false);
scl.set_output_high(true);
}
self.state = State::SendStop;
}
State::SendClock(n, false) => {
if let Some((sda, scl)) = self.pins.as_ref() {
scl.set_output_high(true);
if sda.is_input_high() {
sda.set_output_high(false);
self.wait = Instant::now() + Self::SCL_DELAY;
self.state = State::SendStop;
return Poll::Pending;
}
}
self.state = State::SendClock(n - 1, true);
}
State::SendClock(n, true) => {
if let Some((_sda, scl)) = self.pins.as_ref() {
scl.set_output_high(false);
}
self.state = State::SendClock(n, false);
}
}
self.wait = Instant::now() + Self::SCL_DELAY;
Poll::Pending
}
}
impl Drop for ClearBusFuture<'_> {
fn drop(&mut self) {
if let Some((sda, scl)) = self.pins.take() {
scl.set_output_high(true);
sda.set_output_high(true);
if self.reset_fsm {
self.driver.do_fsm_reset();
}
self.driver.info.sda_output.connect_to(&sda);
self.driver.info.scl_output.connect_to(&scl);
self.driver.clear_all_interrupts();
}
}
}
}
use bus_clear::ClearBusFuture;
impl Future for ClearBusFuture<'_> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
let pending = self.poll_completion();
if pending.is_pending() {
ctx.waker().wake_by_ref();
}
pending
}
}
#[doc(hidden)]
#[non_exhaustive]
pub struct State {
pub waker: AtomicWaker,
}
pub trait Instance: crate::private::Sealed + any::Degrade {
#[doc(hidden)]
fn parts(&self) -> (&Info, &State);
#[doc(hidden)]
#[inline(always)]
fn info(&self) -> &Info {
self.parts().0
}
#[doc(hidden)]
#[inline(always)]
fn state(&self) -> &State {
self.parts().1
}
}
fn add_cmd<'a, I>(cmd_iterator: &mut I, command: Command) -> Result<(), Error>
where
I: Iterator<Item = &'a COMD>,
{
let cmd = cmd_iterator.next().ok_or(Error::CommandNumberExceeded)?;
cmd.write(|w| match command {
Command::Start => w.opcode().rstart(),
Command::Stop => w.opcode().stop(),
Command::End => w.opcode().end(),
Command::Write {
ack_exp,
ack_check_en,
length,
} => unsafe {
w.opcode().write();
w.ack_exp().bit(ack_exp == Ack::Nack);
w.ack_check_en().bit(ack_check_en);
w.byte_num().bits(length);
w
},
Command::Read { ack_value, length } => unsafe {
w.opcode().read();
w.ack_value().bit(ack_value == Ack::Nack);
w.byte_num().bits(length);
w
},
});
Ok(())
}
fn read_fifo(register_block: &RegisterBlock) -> u8 {
cfg_if::cfg_if! {
if #[cfg(esp32s2)] {
let peri_offset = register_block as *const _ as usize - crate::peripherals::I2C0::ptr() as usize;
let fifo_ptr = (property!("i2c_master.i2c0_data_register_ahb_address") + peri_offset) as *mut u32;
unsafe { (fifo_ptr.read_volatile() & 0xff) as u8 }
} else {
register_block.data().read().fifo_rdata().bits()
}
}
}
fn write_fifo(register_block: &RegisterBlock, data: u8) {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2))] {
let peri_offset = register_block as *const _ as usize - crate::peripherals::I2C0::ptr() as usize;
let fifo_ptr = (property!("i2c_master.i2c0_data_register_ahb_address") + peri_offset) as *mut u32;
unsafe {
fifo_ptr.write_volatile(data as u32);
}
} else {
register_block
.data()
.write(|w| unsafe { w.fifo_rdata().bits(data) });
}
}
}
fn estimate_ack_failed_reason(_register_block: &RegisterBlock) -> AcknowledgeCheckFailedReason {
cfg_if::cfg_if! {
if #[cfg(i2c_master_can_estimate_nack_reason)] {
if _register_block.fifo_st().read().txfifo_raddr().bits() <= 1 {
AcknowledgeCheckFailedReason::Address
} else {
AcknowledgeCheckFailedReason::Data
}
} else {
AcknowledgeCheckFailedReason::Unknown
}
}
}
for_each_i2c_master!(
($id:literal, $inst:ident, $peri:ident, $scl:ident, $sda:ident) => {
impl Instance for crate::peripherals::$inst<'_> {
fn parts(&self) -> (&Info, &State) {
#[handler]
#[ram]
pub(super) fn irq_handler() {
async_handler(&PERIPHERAL, &STATE);
}
static STATE: State = State {
waker: AtomicWaker::new(),
};
static PERIPHERAL: Info = Info {
#[cfg(soc_has_i2c1)]
id: $id,
register_block: crate::peripherals::$inst::ptr(),
peripheral: crate::system::Peripheral::$peri,
async_handler: irq_handler,
scl_output: OutputSignal::$scl,
scl_input: InputSignal::$scl,
sda_output: OutputSignal::$sda,
sda_input: InputSignal::$sda,
};
(&PERIPHERAL, &STATE)
}
}
};
);
crate::any_peripheral! {
pub peripheral AnyI2c<'d> {
#[cfg(i2c_master_i2c0)]
I2c0(crate::peripherals::I2C0<'d>),
#[cfg(i2c_master_i2c1)]
I2c1(crate::peripherals::I2C1<'d>),
}
}
impl Instance for AnyI2c<'_> {
fn parts(&self) -> (&Info, &State) {
any::delegate!(self, i2c => { i2c.parts() })
}
}
impl AnyI2c<'_> {
fn bind_peri_interrupt(&self, handler: InterruptHandler) {
any::delegate!(self, i2c => { i2c.bind_peri_interrupt(handler) })
}
fn disable_peri_interrupt_on_all_cores(&self) {
any::delegate!(self, i2c => { i2c.disable_peri_interrupt_on_all_cores() })
}
fn set_interrupt_handler(&self, handler: InterruptHandler) {
self.disable_peri_interrupt_on_all_cores();
self.info().enable_listen(EnumSet::all(), false);
self.info().clear_interrupts(EnumSet::all());
self.bind_peri_interrupt(handler);
}
}