Re-introduce Data and Address, again

This commit is contained in:
Björn Quentin 2025-01-07 09:58:16 +01:00
parent 28e162fa03
commit e7278ac2f0

View File

@ -107,10 +107,19 @@ pub enum Error {
} }
/// I2C no acknowledge error reason. /// I2C no acknowledge error reason.
///
/// Consider this as a hint and make sure to always handle all cases.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive] #[non_exhaustive]
pub enum AcknowledgeCheckFailedReason { pub enum AcknowledgeCheckFailedReason {
/// The device did not acknowledge its address. The device may be missing.
Address,
/// The device did not acknowledge the data. It may not be ready to process
/// requests at the moment.
Data,
/// Either the device did not acknowledge its address or the data, but it is /// Either the device did not acknowledge its address or the data, but it is
/// unknown which. /// unknown which.
Unknown, Unknown,
@ -119,6 +128,8 @@ pub enum AcknowledgeCheckFailedReason {
impl core::fmt::Display for AcknowledgeCheckFailedReason { impl core::fmt::Display for AcknowledgeCheckFailedReason {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self { match self {
AcknowledgeCheckFailedReason::Address => write!(f, "Address"),
AcknowledgeCheckFailedReason::Data => write!(f, "Data"),
AcknowledgeCheckFailedReason::Unknown => write!(f, "Unknown"), AcknowledgeCheckFailedReason::Unknown => write!(f, "Unknown"),
} }
} }
@ -127,6 +138,10 @@ impl core::fmt::Display for AcknowledgeCheckFailedReason {
impl From<&AcknowledgeCheckFailedReason> for embedded_hal::i2c::NoAcknowledgeSource { impl From<&AcknowledgeCheckFailedReason> for embedded_hal::i2c::NoAcknowledgeSource {
fn from(value: &AcknowledgeCheckFailedReason) -> Self { fn from(value: &AcknowledgeCheckFailedReason) -> Self {
match value { match value {
AcknowledgeCheckFailedReason::Address => {
embedded_hal::i2c::NoAcknowledgeSource::Address
}
AcknowledgeCheckFailedReason::Data => embedded_hal::i2c::NoAcknowledgeSource::Data,
AcknowledgeCheckFailedReason::Unknown => { AcknowledgeCheckFailedReason::Unknown => {
embedded_hal::i2c::NoAcknowledgeSource::Unknown embedded_hal::i2c::NoAcknowledgeSource::Unknown
} }
@ -687,9 +702,9 @@ impl<'a> I2cFuture<'a> {
} }
if r.nack().bit_is_set() { if r.nack().bit_is_set() {
return Err(Error::AcknowledgeCheckFailed( return Err(Error::AcknowledgeCheckFailed(estimate_ack_failed_reason(
AcknowledgeCheckFailedReason::Unknown, self.info.register_block(),
)); )));
} }
#[cfg(not(esp32))] #[cfg(not(esp32))]
@ -703,7 +718,7 @@ impl<'a> I2cFuture<'a> {
.bit_is_clear() .bit_is_clear()
{ {
return Err(Error::AcknowledgeCheckFailed( return Err(Error::AcknowledgeCheckFailed(
AcknowledgeCheckFailedReason::Unknown, AcknowledgeCheckFailedReason::Data,
)); ));
} }
@ -1719,7 +1734,7 @@ impl Driver<'_> {
let retval = if interrupts.time_out().bit_is_set() { let retval = if interrupts.time_out().bit_is_set() {
Err(Error::Timeout) Err(Error::Timeout)
} else if interrupts.nack().bit_is_set() { } else if interrupts.nack().bit_is_set() {
Err(Error::AcknowledgeCheckFailed(AcknowledgeCheckFailedReason::Unknown)) Err(Error::AcknowledgeCheckFailed(estimate_ack_failed_reason(self.register_block())))
} else if interrupts.arbitration_lost().bit_is_set() { } else if interrupts.arbitration_lost().bit_is_set() {
Err(Error::ArbitrationLost) Err(Error::ArbitrationLost)
} else { } else {
@ -1730,11 +1745,11 @@ impl Driver<'_> {
let retval = if interrupts.time_out().bit_is_set() { let retval = if interrupts.time_out().bit_is_set() {
Err(Error::Timeout) Err(Error::Timeout)
} else if interrupts.nack().bit_is_set() { } else if interrupts.nack().bit_is_set() {
Err(Error::AcknowledgeCheckFailed(AcknowledgeCheckFailedReason::Unknown)) Err(Error::AcknowledgeCheckFailed(estimate_ack_failed_reason(self.register_block())))
} else if interrupts.arbitration_lost().bit_is_set() { } else if interrupts.arbitration_lost().bit_is_set() {
Err(Error::ArbitrationLost) Err(Error::ArbitrationLost)
} else if interrupts.trans_complete().bit_is_set() && self.register_block().sr().read().resp_rec().bit_is_clear() { } else if interrupts.trans_complete().bit_is_set() && self.register_block().sr().read().resp_rec().bit_is_clear() {
Err(Error::AcknowledgeCheckFailed(AcknowledgeCheckFailedReason::Unknown)) Err(Error::AcknowledgeCheckFailed(AcknowledgeCheckFailedReason::Data))
} else { } else {
Ok(()) Ok(())
}; };
@ -2318,6 +2333,24 @@ fn write_fifo(register_block: &RegisterBlock, data: u8) {
} }
} }
// Estimate the reason for an acknowledge check failure on a best effort basis.
// When in doubt it's better to return `Unknown` than to return a wrong reason.
fn estimate_ack_failed_reason(_register_block: &RegisterBlock) -> AcknowledgeCheckFailedReason {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2, esp32c2, esp32c3))] {
AcknowledgeCheckFailedReason::Unknown
} else {
// this is based on observations rather than documented behavior
if _register_block.fifo_st().read().txfifo_raddr().bits() <= 1 {
return AcknowledgeCheckFailedReason::Address;
} else {
return AcknowledgeCheckFailedReason::Data;
}
}
}
}
macro_rules! instance { macro_rules! instance {
($inst:ident, $peri:ident, $scl:ident, $sda:ident, $interrupt:ident) => { ($inst:ident, $peri:ident, $scl:ident, $sda:ident, $interrupt:ident) => {
impl Instance for crate::peripherals::$inst { impl Instance for crate::peripherals::$inst {