diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e90d506..052972fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `Spi` now takes `Option` for `SCK`, `MISO`, `MOSI` [#514], add `SPIx::NoSck`, etc. [#537] - `spi::FrameSize` and `embedded-hal` 1.0 implementations +- Backport `I2c` implementation from `f4xx-hal` - move `Qei` mod inside `pwm_input` mod [#516] ### Changed diff --git a/Cargo.toml b/Cargo.toml index 7fe2a75a..5a6b0e93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,8 @@ optional = true panic-halt = "1.0.0" panic-semihosting = "0.6.0" panic-itm = "0.4.2" +panic-probe = "1.0" +rtt-target = "0.6.1" cortex-m-rtic = "1.1.3" cortex-m-semihosting = "0.5.0" heapless = "0.8.0" diff --git a/examples/i2c_scanner.rs b/examples/i2c_scanner.rs new file mode 100644 index 00000000..d8c8ddea --- /dev/null +++ b/examples/i2c_scanner.rs @@ -0,0 +1,59 @@ +//! Example of using I2C. +//! Scans available I2C devices on bus and print the result. + +#![no_std] +#![no_main] + +use panic_probe as _; + +use rtt_target::{rprint, rprintln, rtt_init_print}; + +use cortex_m_rt::entry; + +use stm32f1xx_hal::{self as hal, gpio::GpioExt, i2c::I2c, pac, prelude::*}; + +const VALID_ADDR_RANGE: core::ops::Range = 0x08..0x78; + +#[entry] +fn main() -> ! { + rtt_init_print!(); + let dp = pac::Peripherals::take().unwrap(); + + let mut rcc = dp.RCC.constrain(); + + let gpiob = dp.GPIOB.split(&mut rcc); + + // Configure I2C1 + let scl = gpiob.pb6; + let sda = gpiob.pb7; + let mut i2c = I2c::new( + dp.I2C1, + (scl, sda), + hal::i2c::Mode::standard(100.kHz()), + &mut rcc, + ); + + rprintln!("Start i2c scanning..."); + rprintln!(); + + for addr in 0x00_u8..0x80 { + // Write the empty array and check the slave response. + let byte: [u8; 1] = [0; 1]; + if VALID_ADDR_RANGE.contains(&addr) && i2c.write(addr, &byte).is_ok() { + rprint!("{:02x}", addr); + } else { + rprint!(".."); + } + if addr % 0x10 == 0x0F { + rprintln!(); + } else { + rprint!(" "); + } + } + + rprintln!(); + rprintln!("Done!"); + + #[allow(clippy::empty_loop)] + loop {} +} diff --git a/src/i2c.rs b/src/i2c.rs index 7b9d4a2e..5850806f 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -21,7 +21,7 @@ // https://www.st.com/content/ccc/resource/technical/document/application_note/5d/ae/a3/6f/08/69/4e/9b/CD00209826.pdf/files/CD00209826.pdf/jcr:content/translations/en.CD00209826.pdf use crate::afio::{self, RInto, Rmp}; -use crate::pac::{self, DWT}; +use crate::pac::{self, i2c1}; use crate::rcc::{BusClock, Clocks, Enable, Rcc, Reset}; use crate::time::{kHz, Hertz}; use core::ops::Deref; @@ -29,27 +29,12 @@ use core::ops::Deref; pub mod blocking; pub use blocking::BlockingI2c; +mod common; mod hal_02; mod hal_1; -pub use embedded_hal::i2c::NoAcknowledgeSource; - -/// I2C error -#[derive(Debug, Eq, PartialEq)] -#[non_exhaustive] -pub enum Error { - /// Bus error - Bus, - /// Arbitration loss - ArbitrationLoss, - /// No ack received - NoAcknowledge(NoAcknowledgeSource), - /// Overrun/underrun - Overrun, - // Pec, // SMBUS mode only - Timeout, - // Alert, // SMBUS mode only -} +pub use common::{Address, Error, NoAcknowledgeSource}; +use common::{Hal02Operation, Hal1Operation}; #[derive(Debug, Eq, PartialEq)] pub enum DutyCycle { @@ -108,26 +93,6 @@ pub trait I2cExt: Sized + Instance { mode: impl Into, rcc: &mut Rcc, ) -> I2c; - - #[allow(clippy::too_many_arguments)] - fn blocking_i2c( - self, - pins: (impl RInto, impl RInto), - mode: impl Into, - rcc: &mut Rcc, - start_timeout_us: u32, - start_retries: u8, - addr_timeout_us: u32, - data_timeout_us: u32, - ) -> BlockingI2c { - Self::i2c(self, pins, mode, rcc).blocking( - start_timeout_us, - start_retries, - addr_timeout_us, - data_timeout_us, - &rcc.clocks, - ) - } } impl I2cExt for I2C { @@ -146,7 +111,7 @@ pub struct I2c { i2c: I2C, pins: (I2C::Scl, I2C::Sda), mode: Mode, - pclk1: Hertz, + pclk: Hertz, } pub trait Instance: @@ -194,75 +159,76 @@ impl Rmp { i2c: self.0, pins: (pins.0.rinto(), pins.1.rinto()), mode, - pclk1, + pclk: pclk1, }; i2c.init(); i2c } - - #[allow(clippy::too_many_arguments)] - pub fn blocking_i2c( - self, - pins: (impl RInto, impl RInto), - mode: impl Into, - rcc: &mut Rcc, - start_timeout_us: u32, - start_retries: u8, - addr_timeout_us: u32, - data_timeout_us: u32, - ) -> BlockingI2c { - self.i2c(pins, mode, rcc).blocking( - start_timeout_us, - start_retries, - addr_timeout_us, - data_timeout_us, - &rcc.clocks, - ) - } } impl I2c { /// Initializes I2C. Configures the `I2C_TRISE`, `I2C_CRX`, and `I2C_CCR` registers /// according to the system frequency and I2C mode. fn init(&mut self) { - let freq = self.mode.get_frequency(); - let pclk1_mhz = self.pclk1.to_MHz() as u16; + let mode = &self.mode; + // Calculate settings for I2C speed modes + let clock = self.pclk.raw(); + let clc_mhz = clock / 1_000_000; + // Configure bus frequency into I2C peripheral self.i2c .cr2() - .write(|w| unsafe { w.freq().bits(pclk1_mhz as u8) }); - self.i2c.cr1().write(|w| w.pe().clear_bit()); + .write(|w| unsafe { w.freq().bits(clc_mhz as u8) }); - match self.mode { - Mode::Standard { .. } => { - self.i2c - .trise() - .write(|w| w.trise().set((pclk1_mhz + 1) as u8)); - self.i2c - .ccr() - .write(|w| unsafe { w.ccr().bits(((self.pclk1 / (freq * 2)) as u16).max(4)) }); - } - Mode::Fast { ref duty_cycle, .. } => { - self.i2c - .trise() - .write(|w| w.trise().set((pclk1_mhz * 300 / 1000 + 1) as u8)); + let trise = match mode { + Mode::Standard { .. } => clc_mhz + 1, + Mode::Fast { .. } => clc_mhz * 300 / 1000 + 1, + }; + + // Configure correct rise times + self.i2c.trise().write(|w| w.trise().set(trise as u8)); - self.i2c.ccr().write(|w| { - let (freq, duty) = match duty_cycle { - DutyCycle::Ratio2to1 => (((self.pclk1 / (freq * 3)) as u16).max(1), false), - DutyCycle::Ratio16to9 => (((self.pclk1 / (freq * 25)) as u16).max(1), true), - }; + match mode { + // I2C clock control calculation + Mode::Standard { frequency } => { + let ccr = (clock / (frequency.raw() * 2)).max(4); - unsafe { w.ccr().bits(freq).duty().bit(duty).f_s().set_bit() } + // Set clock to standard mode with appropriate parameters for selected speed + self.i2c.ccr().write(|w| unsafe { + w.f_s().clear_bit(); + w.duty().clear_bit(); + w.ccr().bits(ccr as u16) }); } - }; + Mode::Fast { + frequency, + duty_cycle, + } => match duty_cycle { + DutyCycle::Ratio2to1 => { + let ccr = (clock / (frequency.raw() * 3)).max(1); + + // Set clock to fast mode with appropriate parameters for selected speed (2:1 duty cycle) + self.i2c.ccr().write(|w| unsafe { + w.f_s().set_bit().duty().clear_bit().ccr().bits(ccr as u16) + }); + } + DutyCycle::Ratio16to9 => { + let ccr = (clock / (frequency.raw() * 25)).max(1); + + // Set clock to fast mode with appropriate parameters for selected speed (16:9 duty cycle) + self.i2c.ccr().write(|w| unsafe { + w.f_s().set_bit().duty().set_bit().ccr().bits(ccr as u16) + }); + } + }, + } + // Enable the I2C processing self.i2c.cr1().modify(|_, w| w.pe().set_bit()); } /// Perform an I2C software reset - fn reset(&mut self) { + pub fn reset(&mut self) { self.i2c.cr1().write(|w| w.pe().set_bit().swrst().set_bit()); self.i2c.cr1().reset(); self.init(); @@ -293,4 +259,479 @@ impl I2c { pub fn release(self) -> (I2C, (I2C::Scl, I2C::Sda)) { (self.i2c, self.pins) } + + fn check_and_clear_error_flags(&self) -> Result { + // Note that flags should only be cleared once they have been registered. If flags are + // cleared otherwise, there may be an inherent race condition and flags may be missed. + let sr1 = self.i2c.sr1().read(); + + if sr1.timeout().bit_is_set() { + self.i2c.sr1().write(|w| w.timeout().clear_bit()); + return Err(Error::Timeout); + } + + if sr1.pecerr().bit_is_set() { + self.i2c.sr1().write(|w| w.pecerr().clear_bit()); + return Err(Error::Crc); + } + + if sr1.ovr().bit_is_set() { + self.i2c.sr1().write(|w| w.ovr().clear_bit()); + return Err(Error::Overrun); + } + + if sr1.af().bit_is_set() { + self.i2c.sr1().write(|w| w.af().clear_bit()); + return Err(Error::NoAcknowledge(NoAcknowledgeSource::Unknown)); + } + + if sr1.arlo().bit_is_set() { + self.i2c.sr1().write(|w| w.arlo().clear_bit()); + return Err(Error::ArbitrationLoss); + } + + // The errata indicates that BERR may be incorrectly detected. It recommends ignoring and + // clearing the BERR bit instead. + if sr1.berr().bit_is_set() { + self.i2c.sr1().write(|w| w.berr().clear_bit()); + } + + Ok(sr1) + } + + /// Sends START and Address for writing + #[inline(always)] + fn prepare_write(&self, addr: Address) -> Result<(), Error> { + // Wait until a previous STOP condition finishes. When the previous + // STOP was generated inside an ISR (e.g. DMA interrupt handler), + // the ISR returns without waiting for the STOP condition to finish. + // It is possible that the STOP condition is still being generated + // when we reach here, so we wait until it finishes before proceeding + // to start a new transaction. + while self.i2c.cr1().read().stop().bit_is_set() {} + + // Clear all pending error bits + self.i2c.sr1().write(|w| unsafe { w.bits(0) }); + // Send a START condition + self.i2c.cr1().modify(|_, w| w.start().set_bit()); + + // Wait until START condition was generated + while self.check_and_clear_error_flags()?.sb().bit_is_clear() {} + + // Also wait until signalled we're master and everything is waiting for us + loop { + self.check_and_clear_error_flags()?; + + let sr2 = self.i2c.sr2().read(); + if !(sr2.msl().bit_is_clear() && sr2.busy().bit_is_clear()) { + break; + } + } + + // Set up current address, we're trying to talk to + match addr { + Address::Seven(addr) => { + self.i2c + .dr() + .write(|w| unsafe { w.bits(u16::from(addr) << 1) }); + } + Address::Ten(addr) => { + let [msbs, lsbs] = addr.to_be_bytes(); + let msbs = ((msbs & 0b11) << 1) & 0b11110000; + let dr = self.i2c.dr(); + dr.write(|w| unsafe { w.bits(u16::from(msbs)) }); + dr.write(|w| unsafe { w.bits(u16::from(lsbs)) }); + } + } + + // Wait until address was sent + loop { + // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. + let sr1 = self + .check_and_clear_error_flags() + .map_err(Error::nack_addr)?; + + // Wait for the address to be acknowledged + if sr1.addr().bit_is_set() { + break; + } + } + + // Clear condition by reading SR2 + self.i2c.sr2().read(); + + Ok(()) + } + + /// Sends START and Address for reading + fn prepare_read(&self, addr: Address, first_transaction: bool) -> Result<(), Error> { + // Wait until a previous STOP condition finishes. When the previous + // STOP was generated inside an ISR (e.g. DMA interrupt handler), + // the ISR returns without waiting for the STOP condition to finish. + // It is possible that the STOP condition is still being generated + // when we reach here, so we wait until it finishes before proceeding + // to start a new transaction. + while self.i2c.cr1().read().stop().bit_is_set() {} + + // Clear all pending error bits + self.i2c.sr1().write(|w| unsafe { w.bits(0) }); + // Send a START condition and set ACK bit + self.i2c + .cr1() + .modify(|_, w| w.start().set_bit().ack().set_bit()); + + // Wait until START condition was generated + while self.i2c.sr1().read().sb().bit_is_clear() {} + + // Also wait until signalled we're master and everything is waiting for us + while { + let sr2 = self.i2c.sr2().read(); + sr2.msl().bit_is_clear() && sr2.busy().bit_is_clear() + } {} + + // Set up current address, we're trying to talk to + match addr { + Address::Seven(addr) => { + self.i2c + .dr() + .write(|w| unsafe { w.bits((u16::from(addr) << 1) | 1) }); + } + Address::Ten(addr) => { + let [msbs, lsbs] = addr.to_be_bytes(); + let msbs = ((msbs & 0b11) << 1) | 0b11110000; + let dr = self.i2c.dr(); + if first_transaction { + dr.write(|w| unsafe { w.bits(u16::from(msbs)) }); + dr.write(|w| unsafe { w.bits(u16::from(lsbs)) }); + } + self.i2c.cr1().modify(|_, w| w.start().set_bit()); + // Wait until START condition was generated + while self.i2c.sr1().read().sb().bit_is_clear() {} + dr.write(|w| unsafe { w.bits(u16::from(msbs | 1)) }); + } + } + + // Wait until address was sent + loop { + self.check_and_clear_error_flags() + .map_err(Error::nack_addr)?; + if self.i2c.sr1().read().addr().bit_is_set() { + break; + } + } + + // Clear condition by reading SR2 + self.i2c.sr2().read(); + + Ok(()) + } + + fn write_bytes(&mut self, bytes: impl Iterator) -> Result<(), Error> { + // Send bytes + for c in bytes { + self.send_byte(c)?; + } + + // Fallthrough is success + Ok(()) + } + + fn send_byte(&self, byte: u8) -> Result<(), Error> { + // Wait until we're ready for sending + // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. + while self + .check_and_clear_error_flags() + .map_err(Error::nack_addr)? + .tx_e() + .bit_is_clear() + {} + + // Push out a byte of data + self.i2c.dr().write(|w| unsafe { w.bits(u16::from(byte)) }); + + // Wait until byte is transferred + // Check for any potential error conditions. + while self + .check_and_clear_error_flags() + .map_err(Error::nack_data)? + .btf() + .bit_is_clear() + {} + + Ok(()) + } + + fn recv_byte(&self) -> Result { + loop { + // Check for any potential error conditions. + self.check_and_clear_error_flags() + .map_err(Error::nack_data)?; + + if self.i2c.sr1().read().rx_ne().bit_is_set() { + break; + } + } + + let value = self.i2c.dr().read().bits() as u8; + Ok(value) + } + + fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + // Receive bytes into buffer + for c in buffer { + *c = self.recv_byte()?; + } + + Ok(()) + } + + pub fn read(&mut self, addr: impl Into
, buffer: &mut [u8]) -> Result<(), Error> { + self.read_inner(addr.into(), buffer, true) + } + + #[inline(always)] + fn read_inner( + &mut self, + addr: Address, + buffer: &mut [u8], + first_transaction: bool, + ) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::Overrun); + } + + self.prepare_read(addr, first_transaction)?; + self.read_wo_prepare(buffer) + } + + /// Reads like normal but does'n generate start and don't send address + fn read_wo_prepare(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + if let Some((last, buffer)) = buffer.split_last_mut() { + // Read all bytes but not last + self.read_bytes(buffer)?; + + // Prepare to send NACK then STOP after next byte + self.i2c + .cr1() + .modify(|_, w| w.ack().clear_bit().stop().set_bit()); + + // Receive last byte + *last = self.recv_byte()?; + + // Wait for the STOP to be sent. Otherwise, the interface will still be + // busy for a while after this function returns. Immediate following + // operations through the DMA handle might thus encounter `WouldBlock` + // error. Instead, we should make sure that the interface becomes idle + // before returning. + while self.i2c.cr1().read().stop().bit_is_set() {} + + // Fallthrough is success + Ok(()) + } else { + Err(Error::Overrun) + } + } + + pub fn write(&mut self, addr: impl Into
, bytes: &[u8]) -> Result<(), Error> { + self.prepare_write(addr.into())?; + self.write_wo_prepare(bytes) + } + + /// Writes like normal but does'n generate start and don't send address + fn write_wo_prepare(&mut self, bytes: &[u8]) -> Result<(), Error> { + self.write_bytes(bytes.iter().cloned())?; + + // Send a STOP condition + self.i2c.cr1().modify(|_, w| w.stop().set_bit()); + + // Wait for the STOP to be sent. Otherwise, the interface will still be + // busy for a while after this function returns. Immediate following + // operations through the DMA handle might thus encounter `WouldBlock` + // error. Instead, we should make sure that the interface becomes idle + // before returning. + while self.i2c.cr1().read().stop().bit_is_set() {} + + // Fallthrough is success + Ok(()) + } + + pub fn write_iter(&mut self, addr: impl Into
, bytes: B) -> Result<(), Error> + where + B: IntoIterator, + { + self.prepare_write(addr.into())?; + self.write_bytes(bytes.into_iter())?; + + // Send a STOP condition + self.i2c.cr1().modify(|_, w| w.stop().set_bit()); + + // Wait for the STOP to be sent. Otherwise, the interface will still be + // busy for a while after this function returns. Immediate following + // operations through the DMA handle might thus encounter `WouldBlock` + // error. Instead, we should make sure that the interface becomes idle + // before returning. + while self.i2c.cr1().read().stop().bit_is_set() {} + + // Fallthrough is success + Ok(()) + } + + pub fn write_read( + &mut self, + addr: impl Into
, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Error> { + let addr = addr.into(); + self.prepare_write(addr)?; + self.write_bytes(bytes.iter().cloned())?; + self.read_inner(addr, buffer, false) + } + + pub fn write_iter_read( + &mut self, + addr: impl Into
, + bytes: B, + buffer: &mut [u8], + ) -> Result<(), Error> + where + B: IntoIterator, + { + let addr = addr.into(); + self.prepare_write(addr)?; + self.write_bytes(bytes.into_iter())?; + self.read_inner(addr, buffer, false) + } + + pub fn transaction<'a>( + &mut self, + addr: impl Into
, + mut ops: impl Iterator>, + ) -> Result<(), Error> { + let addr = addr.into(); + if let Some(mut prev_op) = ops.next() { + // 1. Generate Start for operation + match &prev_op { + Hal1Operation::Read(_) => self.prepare_read(addr, true)?, + Hal1Operation::Write(_) => self.prepare_write(addr)?, + }; + + for op in ops { + // 2. Execute previous operations. + match &mut prev_op { + Hal1Operation::Read(rb) => self.read_bytes(rb)?, + Hal1Operation::Write(wb) => self.write_bytes(wb.iter().cloned())?, + }; + // 3. If operation changes type we must generate new start + match (&prev_op, &op) { + (Hal1Operation::Read(_), Hal1Operation::Write(_)) => { + self.prepare_write(addr)? + } + (Hal1Operation::Write(_), Hal1Operation::Read(_)) => { + self.prepare_read(addr, false)? + } + _ => {} // No changes if operation have not changed + } + + prev_op = op; + } + + // 4. Now, prev_op is last command use methods variations that will generate stop + match prev_op { + Hal1Operation::Read(rb) => self.read_wo_prepare(rb)?, + Hal1Operation::Write(wb) => self.write_wo_prepare(wb)?, + }; + } + + // Fallthrough is success + Ok(()) + } + + pub fn transaction_slice( + &mut self, + addr: impl Into
, + ops_slice: &mut [Hal1Operation<'_>], + ) -> Result<(), Error> { + let addr = addr.into(); + transaction_impl!(self, addr, ops_slice, Hal1Operation); + // Fallthrough is success + Ok(()) + } + + fn transaction_slice_hal_02( + &mut self, + addr: impl Into
, + ops_slice: &mut [Hal02Operation<'_>], + ) -> Result<(), Error> { + let addr = addr.into(); + transaction_impl!(self, addr, ops_slice, Hal02Operation); + // Fallthrough is success + Ok(()) + } +} + +macro_rules! transaction_impl { + ($self:ident, $addr:ident, $ops_slice:ident, $Operation:ident) => { + let i2c = $self; + let addr = $addr; + let mut ops = $ops_slice.iter_mut(); + + if let Some(mut prev_op) = ops.next() { + // 1. Generate Start for operation + match &prev_op { + $Operation::Read(_) => i2c.prepare_read(addr, true)?, + $Operation::Write(_) => i2c.prepare_write(addr)?, + }; + + for op in ops { + // 2. Execute previous operations. + match &mut prev_op { + $Operation::Read(rb) => i2c.read_bytes(rb)?, + $Operation::Write(wb) => i2c.write_bytes(wb.iter().cloned())?, + }; + // 3. If operation changes type we must generate new start + match (&prev_op, &op) { + ($Operation::Read(_), $Operation::Write(_)) => i2c.prepare_write(addr)?, + ($Operation::Write(_), $Operation::Read(_)) => i2c.prepare_read(addr, false)?, + _ => {} // No changes if operation have not changed + } + + prev_op = op; + } + + // 4. Now, prev_op is last command use methods variations that will generate stop + match prev_op { + $Operation::Read(rb) => i2c.read_wo_prepare(rb)?, + $Operation::Write(wb) => i2c.write_wo_prepare(wb)?, + }; + } + }; +} +use transaction_impl; + +impl embedded_hal_02::blocking::i2c::WriteIter for I2c { + type Error = Error; + + fn write(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator, + { + self.write_iter(addr, bytes) + } +} + +impl embedded_hal_02::blocking::i2c::WriteIterRead for I2c { + type Error = Error; + + fn write_iter_read( + &mut self, + addr: u8, + bytes: B, + buffer: &mut [u8], + ) -> Result<(), Self::Error> + where + B: IntoIterator, + { + self.write_iter_read(addr, bytes, buffer) + } } diff --git a/src/i2c/blocking.rs b/src/i2c/blocking.rs index 6f580ec8..84de01ae 100644 --- a/src/i2c/blocking.rs +++ b/src/i2c/blocking.rs @@ -1,5 +1,7 @@ use super::*; +use crate::pac::DWT; + /// embedded-hal compatible blocking I2C implementation /// /// **NOTE**: Before using blocking I2C, you need to enable the DWT cycle counter using the @@ -309,3 +311,47 @@ impl BlockingI2c { todo!(); } } + +pub trait BlockingI2cExt: I2cExt { + #[allow(clippy::too_many_arguments)] + fn blocking_i2c( + self, + pins: (impl RInto, impl RInto), + mode: impl Into, + rcc: &mut Rcc, + start_timeout_us: u32, + start_retries: u8, + addr_timeout_us: u32, + data_timeout_us: u32, + ) -> BlockingI2c { + Self::i2c(self, pins, mode, rcc).blocking( + start_timeout_us, + start_retries, + addr_timeout_us, + data_timeout_us, + &rcc.clocks, + ) + } +} + +impl Rmp { + #[allow(clippy::too_many_arguments)] + pub fn blocking_i2c( + self, + pins: (impl RInto, impl RInto), + mode: impl Into, + rcc: &mut Rcc, + start_timeout_us: u32, + start_retries: u8, + addr_timeout_us: u32, + data_timeout_us: u32, + ) -> BlockingI2c { + self.i2c(pins, mode, rcc).blocking( + start_timeout_us, + start_retries, + addr_timeout_us, + data_timeout_us, + &rcc.clocks, + ) + } +} diff --git a/src/i2c/common.rs b/src/i2c/common.rs new file mode 100644 index 00000000..1eb0f102 --- /dev/null +++ b/src/i2c/common.rs @@ -0,0 +1,72 @@ +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Address { + Seven(u8), + Ten(u16), +} + +impl From for Address { + fn from(value: u8) -> Self { + Self::Seven(value) + } +} + +impl From for Address { + fn from(value: u16) -> Self { + Self::Ten(value) + } +} + +pub use embedded_hal::i2c::NoAcknowledgeSource; + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[non_exhaustive] +pub enum Error { + /// Overrun/underrun + Overrun, + /// No ack received + NoAcknowledge(NoAcknowledgeSource), + Timeout, + /// Bus error + // Note: The Bus error type is not currently returned, but is maintained for compatibility. + Bus, + Crc, + /// Arbitration loss + ArbitrationLoss, + // Pec, // SMBUS mode only + // Alert, // SMBUS mode only +} + +impl Error { + pub(crate) fn nack_addr(self) -> Self { + match self { + Error::NoAcknowledge(NoAcknowledgeSource::Unknown) => { + Error::NoAcknowledge(NoAcknowledgeSource::Address) + } + e => e, + } + } + pub(crate) fn nack_data(self) -> Self { + match self { + Error::NoAcknowledge(NoAcknowledgeSource::Unknown) => { + Error::NoAcknowledge(NoAcknowledgeSource::Data) + } + e => e, + } + } +} + +use embedded_hal::i2c::ErrorKind; +impl embedded_hal::i2c::Error for Error { + fn kind(&self) -> ErrorKind { + match *self { + Self::Overrun => ErrorKind::Overrun, + Self::Bus => ErrorKind::Bus, + Self::ArbitrationLoss => ErrorKind::ArbitrationLoss, + Self::NoAcknowledge(nack) => ErrorKind::NoAcknowledge(nack), + Self::Crc | Self::Timeout => ErrorKind::Other, + } + } +} + +pub(crate) type Hal1Operation<'a> = embedded_hal::i2c::Operation<'a>; +pub(crate) type Hal02Operation<'a> = embedded_hal_02::blocking::i2c::Operation<'a>; diff --git a/src/i2c/hal_02.rs b/src/i2c/hal_02.rs index afcd3c4d..6b0b2d56 100644 --- a/src/i2c/hal_02.rs +++ b/src/i2c/hal_02.rs @@ -1,4 +1,4 @@ -use super::*; +use super::{BlockingI2c, Error, I2c, Instance}; use embedded_hal_02::blocking::i2c::{Operation, Read, Transactional, Write, WriteRead}; impl Write for BlockingI2c { @@ -32,3 +32,35 @@ impl Transactional for BlockingI2c { self.transaction_slice_hal_02(address, operations) } } + +impl WriteRead for I2c { + type Error = Error; + + fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { + self.write_read(addr, bytes, buffer) + } +} + +impl Write for I2c { + type Error = Error; + + fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(addr, bytes) + } +} + +impl Read for I2c { + type Error = Error; + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.read(addr, buffer) + } +} + +impl Transactional for I2c { + type Error = Error; + + fn exec(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { + self.transaction_slice_hal_02(address, operations) + } +} diff --git a/src/i2c/hal_1.rs b/src/i2c/hal_1.rs index 121aaed1..3e3b7e77 100644 --- a/src/i2c/hal_1.rs +++ b/src/i2c/hal_1.rs @@ -1,24 +1,14 @@ -use embedded_hal::i2c::{Error, ErrorKind, ErrorType}; - -impl Error for super::Error { - fn kind(&self) -> ErrorKind { - match *self { - Self::Overrun => ErrorKind::Overrun, - Self::Bus => ErrorKind::Bus, - Self::ArbitrationLoss => ErrorKind::ArbitrationLoss, - Self::NoAcknowledge(nack) => ErrorKind::NoAcknowledge(nack), - Self::Timeout => ErrorKind::Other, - } - } +impl embedded_hal::i2c::ErrorType for super::I2c { + type Error = super::Error; } -impl ErrorType for super::BlockingI2c { +impl embedded_hal::i2c::ErrorType for super::BlockingI2c { type Error = super::Error; } mod blocking { - use super::super::{BlockingI2c, Instance}; - use embedded_hal::i2c::Operation; + use super::super::{BlockingI2c, I2c, Instance}; + use embedded_hal::i2c::{Operation, SevenBitAddress, TenBitAddress}; impl embedded_hal::i2c::I2c for BlockingI2c { fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -46,4 +36,57 @@ mod blocking { self.transaction_slice(addr, operations) } } + impl embedded_hal::i2c::I2c for I2c { + fn read(&mut self, addr: SevenBitAddress, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.read(addr, buffer) + } + + fn write(&mut self, addr: SevenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(addr, bytes) + } + + fn write_read( + &mut self, + addr: SevenBitAddress, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + self.write_read(addr, bytes, buffer) + } + + fn transaction( + &mut self, + addr: SevenBitAddress, + operations: &mut [Operation<'_>], + ) -> Result<(), Self::Error> { + self.transaction_slice(addr, operations) + } + } + + impl embedded_hal::i2c::I2c for I2c { + fn read(&mut self, addr: TenBitAddress, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.read(addr, buffer) + } + + fn write(&mut self, addr: TenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(addr, bytes) + } + + fn write_read( + &mut self, + addr: TenBitAddress, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + self.write_read(addr, bytes, buffer) + } + + fn transaction( + &mut self, + addr: TenBitAddress, + operations: &mut [Operation<'_>], + ) -> Result<(), Self::Error> { + self.transaction_slice(addr, operations) + } + } } diff --git a/src/prelude.rs b/src/prelude.rs index 59f86d96..fae1a65c 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -16,6 +16,7 @@ pub use crate::flash::FlashExt as _stm32_hal_flash_FlashExt; pub use crate::gpio::GpioExt as _stm32_hal_gpio_GpioExt; pub use crate::hal_02::adc::OneShot as _embedded_hal_adc_OneShot; pub use crate::hal_02::prelude::*; +pub use crate::i2c::blocking::BlockingI2cExt as _; pub use crate::i2c::I2cExt as _; pub use crate::rcc::BkpExt as _; pub use crate::rcc::RccExt as _stm32_hal_rcc_RccExt;