diff --git a/CHANGELOG.md b/CHANGELOG.md index 71e68a02..f129835a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use `write` instead of `modify` to clear flags - Bump `stm32f4-staging` to 0.18, update other dependencies - `serial` mod refactor + - Fefactor FMPI2c `embedded-hal` implementations [#784] + +[#784]: https://github.com/stm32-rs/stm32f4xx-hal/pull/784 ## [v0.22.1] - 2024-11-03 diff --git a/src/fmpi2c.rs b/src/fmpi2c.rs index c581258d..ade18d5c 100644 --- a/src/fmpi2c.rs +++ b/src/fmpi2c.rs @@ -1,17 +1,24 @@ use core::ops::Deref; use crate::gpio; -use crate::i2c::{Error, NoAcknowledgeSource}; + use crate::pac::fmpi2c1 as i2c1; use crate::pac::{self, RCC}; use crate::rcc::{BusClock, Enable, Reset}; use fugit::{HertzU32 as Hertz, RateExtU32}; +#[path = "i2c/common.rs"] +mod common; +pub use common::{Address, Error, NoAcknowledgeSource}; +use common::{Hal02Operation, Hal1Operation}; + // Old names pub use I2c as FmpI2c; pub use Mode as FmpMode; +#[path = "i2c/hal_02.rs"] mod hal_02; +#[path = "i2c/hal_1.rs"] mod hal_1; pub trait Instance: @@ -213,6 +220,83 @@ impl I2c { Ok(()) } + /// Sends START and Address for writing + #[inline(always)] + fn prepare_write(&self, addr: Address, datalen: usize) -> Result<(), Error> { + // Set up current slave address for writing and disable autoending + self.i2c.cr2().modify(|_, w| { + match addr { + Address::Seven(addr) => { + w.add10().clear_bit(); + w.sadd().set(u16::from(addr) << 1); + } + Address::Ten(addr) => { + w.add10().set_bit(); + w.sadd().set(addr); + } + } + w.nbytes().set(datalen as u8); + w.rd_wrn().clear_bit(); + w.autoend().clear_bit() + }); + + // Send a START condition + self.i2c.cr2().modify(|_, w| w.start().set_bit()); + + // Wait until address was sent + while { + let isr = self.i2c.isr().read(); + self.check_and_clear_error_flags(&isr) + .map_err(Error::nack_addr)?; + isr.txis().bit_is_clear() && isr.tc().bit_is_clear() + } {} + + Ok(()) + } + + /// Sends START and Address for reading + fn prepare_read( + &self, + addr: Address, + buflen: usize, + first_transaction: bool, + ) -> Result<(), Error> { + // Set up current address for reading + self.i2c.cr2().modify(|_, w| { + match addr { + Address::Seven(addr) => { + w.add10().clear_bit(); + w.sadd().set(u16::from(addr) << 1); + } + Address::Ten(addr) => { + w.add10().set_bit(); + w.head10r().bit(!first_transaction); + w.sadd().set(addr); + } + } + w.nbytes().set(buflen as u8); + w.rd_wrn().set_bit() + }); + + // Send a START condition + self.i2c.cr2().modify(|_, w| w.start().set_bit()); + + // Send the autoend after setting the start to get a restart + self.i2c.cr2().modify(|_, w| w.autoend().set_bit()); + + 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 while { @@ -242,72 +326,38 @@ impl I2c { Ok(value) } - pub fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - // Set up current address for reading - self.i2c.cr2().modify(|_, w| { - w.sadd().set(u16::from(addr) << 1); - w.nbytes().set(buffer.len() as u8); - w.rd_wrn().set_bit() - }); - - // Send a START condition - self.i2c.cr2().modify(|_, w| w.start().set_bit()); - - // Send the autoend after setting the start to get a restart - self.i2c.cr2().modify(|_, w| w.autoend().set_bit()); - - // Now read in all bytes - for c in buffer.iter_mut() { + fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + // Receive bytes into buffer + for c in buffer { *c = self.recv_byte()?; } - self.end_transaction() + Ok(()) } - pub fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { - // Set up current slave address for writing and enable autoending - self.i2c.cr2().modify(|_, w| { - w.sadd().set(u16::from(addr) << 1); - w.nbytes().set(bytes.len() as u8); - w.rd_wrn().clear_bit(); - w.autoend().set_bit() - }); - - // Send a START condition - self.i2c.cr2().modify(|_, w| w.start().set_bit()); - - // Send out all individual bytes - for c in bytes { - self.send_byte(*c)?; - } + pub fn read(&mut self, addr: impl Into
, buffer: &mut [u8]) -> Result<(), Error> { + self.prepare_read(addr.into(), buffer.len(), true)?; + self.read_bytes(buffer)?; self.end_transaction() } - pub fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - // Set up current slave address for writing and disable autoending - self.i2c.cr2().modify(|_, w| { - w.sadd().set(u16::from(addr) << 1); - w.nbytes().set(bytes.len() as u8); - w.rd_wrn().clear_bit(); - w.autoend().clear_bit() - }); + pub fn write(&mut self, addr: impl Into
, bytes: &[u8]) -> Result<(), Error> { + self.prepare_write(addr.into(), bytes.len())?; + self.write_bytes(bytes.iter().cloned())?; - // Send a START condition - self.i2c.cr2().modify(|_, w| w.start().set_bit()); + self.end_transaction() + } - // Wait until the transmit buffer is empty and there hasn't been any error condition - while { - let isr = self.i2c.isr().read(); - self.check_and_clear_error_flags(&isr) - .map_err(Error::nack_addr)?; - isr.txis().bit_is_clear() && isr.tc().bit_is_clear() - } {} - - // Send out all individual bytes - for c in bytes { - self.send_byte(*c)?; - } + pub fn write_read( + &mut self, + addr: impl Into
, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Error> { + let addr = addr.into(); + self.prepare_write(addr, bytes.len())?; + self.write_bytes(bytes.iter().cloned())?; // Wait until data was sent while { @@ -317,24 +367,122 @@ impl I2c { isr.tc().bit_is_clear() } {} - // Set up current address for reading - self.i2c.cr2().modify(|_, w| { - w.sadd().set(u16::from(addr) << 1); - w.nbytes().set(buffer.len() as u8); - w.rd_wrn().set_bit() - }); + self.read(addr, buffer) + } - // Send another START condition - self.i2c.cr2().modify(|_, w| w.start().set_bit()); + 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(buf) => self.prepare_read(addr, buf.len(), true)?, + Hal1Operation::Write(data) => self.prepare_write(addr, data.len())?, + }; + + 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(data)) => { + self.prepare_write(addr, data.len())? + } + (Hal1Operation::Write(_), Hal1Operation::Read(buf)) => { + self.prepare_read(addr, buf.len(), false)? + } + _ => {} // No changes if operation have not changed + } + + prev_op = op; + } - // Send the autoend after setting the start to get a restart - self.i2c.cr2().modify(|_, w| w.autoend().set_bit()); + // 4. Now, prev_op is last command use methods variations that will generate stop + match prev_op { + Hal1Operation::Read(rb) => self.read_bytes(rb)?, + Hal1Operation::Write(wb) => self.write_bytes(wb.iter().cloned())?, + }; - // Now read in all bytes - for c in buffer.iter_mut() { - *c = self.recv_byte()?; + self.end_transaction()?; } - self.end_transaction() + // 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(buf) => i2c.prepare_read(addr, buf.len(), true)?, + $Operation::Write(data) => i2c.prepare_write(addr, data.len())?, + }; + + 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(data)) => { + i2c.prepare_write(addr, data.len())? + } + ($Operation::Write(_), $Operation::Read(buf)) => { + i2c.prepare_read(addr, buf.len(), 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_bytes(rb)?, + $Operation::Write(wb) => i2c.write_bytes(wb.iter().cloned())?, + }; + + i2c.end_transaction()?; + } + }; +} +use transaction_impl; + +// Note: implementation is from f0xx-hal +// TODO: check error handling. See https://github.com/stm32-rs/stm32f0xx-hal/pull/95/files diff --git a/src/fmpi2c/hal_02.rs b/src/fmpi2c/hal_02.rs deleted file mode 100644 index 0cfa55df..00000000 --- a/src/fmpi2c/hal_02.rs +++ /dev/null @@ -1,28 +0,0 @@ -mod blocking { - use super::super::{Error, I2c, Instance}; - use embedded_hal_02::blocking::i2c::{Read, Write, WriteRead}; - - impl WriteRead for I2c { - type Error = Error; - - fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.write_read(addr, bytes, buffer) - } - } - - impl Read for I2c { - type Error = Error; - - fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.read(addr, buffer) - } - } - - impl Write for I2c { - type Error = Error; - - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { - self.write(addr, bytes) - } - } -} diff --git a/src/fmpi2c/hal_1.rs b/src/fmpi2c/hal_1.rs deleted file mode 100644 index ad556c57..00000000 --- a/src/fmpi2c/hal_1.rs +++ /dev/null @@ -1,39 +0,0 @@ -use embedded_hal::i2c::ErrorType; - -use super::Instance; - -impl ErrorType for super::I2c { - type Error = super::Error; -} - -mod blocking { - use super::super::{I2c, Instance}; - use embedded_hal::i2c::Operation; - - impl embedded_hal::i2c::I2c for I2c { - fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.read(addr, buffer) - } - - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(addr, bytes) - } - - fn write_read( - &mut self, - addr: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - self.write_read(addr, bytes, buffer) - } - - fn transaction( - &mut self, - _addr: u8, - _operations: &mut [Operation<'_>], - ) -> Result<(), Self::Error> { - todo!() - } - } -} diff --git a/src/i2c.rs b/src/i2c.rs index eaa664d8..73d47b3f 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -655,3 +655,30 @@ macro_rules! transaction_impl { }; } 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/hal_02.rs b/src/i2c/hal_02.rs index 0204394b..f493abc0 100644 --- a/src/i2c/hal_02.rs +++ b/src/i2c/hal_02.rs @@ -1,8 +1,6 @@ mod blocking { use super::super::{Error, I2c, Instance}; - use embedded_hal_02::blocking::i2c::{ - Operation, Read, Transactional, Write, WriteIter, WriteIterRead, WriteRead, - }; + use embedded_hal_02::blocking::i2c::{Operation, Read, Transactional, Write, WriteRead}; impl WriteRead for I2c { type Error = Error; @@ -17,22 +15,6 @@ mod blocking { } } - impl 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) - } - } - impl Write for I2c { type Error = Error; @@ -41,17 +23,6 @@ mod blocking { } } - impl 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 Read for I2c { type Error = Error;