From 30bfa206f98d7758460b3daadf46a5f6de7c0ca8 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Mon, 29 Jul 2024 06:40:31 +0300 Subject: [PATCH] FMPI2c embedded-hal implementations --- CHANGELOG.md | 2 + src/fmpi2c.rs | 288 ++++++++++++++++++++++++++++++++----------- src/fmpi2c/hal_02.rs | 28 ----- src/fmpi2c/hal_1.rs | 39 ------ src/i2c.rs | 27 ++++ src/i2c/hal_02.rs | 31 +---- 6 files changed, 248 insertions(+), 167 deletions(-) delete mode 100644 src/fmpi2c/hal_02.rs delete mode 100644 src/fmpi2c/hal_1.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 42116539..2c1320b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use `stm32f4-staging` until `stm32f4` is released [#706] - use GPIO pac fields instead of raw write [#777] - RTIC2 monotonics fix: CC1 instead of CC3 [#771] + - Fefactor FMPI2c `embedded-hal` implementations [#784] - Allow different lengths of buffers in hal_1 SpiBus impl [#566] - Clean SPI write impls [#774] - move `ptr()` to `Ptr` trait [#773] @@ -50,6 +51,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#777]: https://github.com/stm32-rs/stm32f4xx-hal/pull/777 [#778]: https://github.com/stm32-rs/stm32f4xx-hal/pull/778 [#783]: https://github.com/stm32-rs/stm32f4xx-hal/pull/783 +[#784]: https://github.com/stm32-rs/stm32f4xx-hal/pull/784 [#785]: https://github.com/stm32-rs/stm32f4xx-hal/pull/785 [#796]: https://github.com/stm32-rs/stm32f4xx-hal/pull/796 diff --git a/src/fmpi2c.rs b/src/fmpi2c.rs index efe6ab4b..967b6c51 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: @@ -222,6 +229,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 { @@ -251,72 +335,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 { @@ -326,24 +376,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 61d6ede0..12841841 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -658,3 +658,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;