Skip to content

Commit

Permalink
implement embedded_hal::blocking::i2c::Transactional for I2C
Browse files Browse the repository at this point in the history
  • Loading branch information
dimpolo committed Jul 13, 2023
1 parent 7402fde commit 9407777
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 73 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Changed

- implement `embedded_hal::blocking::i2c::Transactional` for `I2c` [#671]

[#671]: https://github.com/stm32-rs/stm32f4xx-hal/pull/671

## [v0.17.0] - 2023-07-11

### Changed
Expand Down
116 changes: 44 additions & 72 deletions src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::rcc::{Enable, Reset};
use crate::gpio;

use crate::rcc::Clocks;
use embedded_hal_one::i2c::Operation;
use fugit::{HertzU32 as Hertz, RateExtU32};

mod hal_02;
Expand Down Expand Up @@ -504,84 +503,57 @@ impl<I2C: Instance> I2c<I2C> {
self.write_bytes(bytes.into_iter())?;
self.read(addr, buffer)
}
}

pub fn transaction<'a>(
&mut self,
addr: u8,
mut ops: impl Iterator<Item = Operation<'a>>,
) -> Result<(), Error> {
if let Some(mut prev_op) = ops.next() {
// 1. Generate Start for operation
match &prev_op {
Operation::Read(_) => self.prepare_read(addr)?,
Operation::Write(_) => self.prepare_write(addr)?,
};

for op in ops {
// 2. Execute previous operations.
match &mut prev_op {
Operation::Read(rb) => self.read_bytes(rb)?,
Operation::Write(wb) => self.write_bytes(wb.iter().cloned())?,
macro_rules! transaction_impl {
($vis:vis fn $function:ident: $operation:ty, $read:path, $write:path) => {
$vis fn $function(
&mut self,
addr: u8,
ops_slice: &mut [$operation],
) -> Result<(), Error> {
let mut ops = ops_slice.iter_mut();

if let Some(mut prev_op) = ops.next() {
// 1. Generate Start for operation
match &prev_op {
$read(_) => self.prepare_read(addr)?,
$write(_) => self.prepare_write(addr)?,
};
// 3. If operation changes type we must generate new start
match (&prev_op, &op) {
(Operation::Read(_), Operation::Write(_)) => self.prepare_write(addr)?,
(Operation::Write(_), Operation::Read(_)) => self.prepare_read(addr)?,
_ => {} // 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) => self.read_wo_prepare(rb)?,
Operation::Write(wb) => self.write_wo_prepare(wb)?,
};
}

// Fallthrough is success
Ok(())
}

pub fn transaction_slice(
&mut self,
addr: u8,
ops_slice: &mut [Operation<'_>],
) -> Result<(), Error> {
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(_) => self.prepare_read(addr)?,
Operation::Write(_) => self.prepare_write(addr)?,
};

for op in ops {
// 2. Execute previous operations.
match &mut prev_op {
Operation::Read(rb) => self.read_bytes(rb)?,
Operation::Write(wb) => self.write_bytes(wb.iter().cloned())?,
};
// 3. If operation changes type we must generate new start
match (&prev_op, &op) {
(Operation::Read(_), Operation::Write(_)) => self.prepare_write(addr)?,
(Operation::Write(_), Operation::Read(_)) => self.prepare_read(addr)?,
_ => {} // No changes if operation have not changed
for op in ops {
// 2. Execute previous operations.
match &mut prev_op {
$read(rb) => self.read_bytes(rb)?,
$write(wb) => self.write_bytes(wb.iter().cloned())?,
};
// 3. If operation changes type we must generate new start
match (&prev_op, &op) {
($read(_), $write(_)) => self.prepare_write(addr)?,
($write(_), $read(_)) => self.prepare_read(addr)?,
_ => {} // No changes if operation have not changed
}

prev_op = op;
}

prev_op = op;
// 4. Now, prev_op is last command use methods variations that will generate stop
match prev_op {
$read(rb) => self.read_wo_prepare(rb)?,
$write(wb) => self.write_wo_prepare(wb)?,
};
}

// 4. Now, prev_op is last command use methods variations that will generate stop
match prev_op {
Operation::Read(rb) => self.read_wo_prepare(rb)?,
Operation::Write(wb) => self.write_wo_prepare(wb)?,
};
// Fallthrough is success
Ok(())
}
};
}

// Fallthrough is success
Ok(())
}
type Hal1Operation<'a> = embedded_hal_one::i2c::Operation<'a>;
type Hal02Operation<'a> = embedded_hal::blocking::i2c::Operation<'a>;

impl<I2C: Instance> I2c<I2C> {
transaction_impl!(pub fn transaction_slice: Hal1Operation<'_>, Hal1Operation::Read, Hal1Operation::Write);
transaction_impl!(fn transaction_slice_hal_02: Hal02Operation<'_>, Hal02Operation::Read, Hal02Operation::Write);
}
19 changes: 18 additions & 1 deletion src/i2c/hal_02.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod blocking {
use super::super::{Error, I2c, Instance};
use embedded_hal::blocking::i2c::{Read, Write, WriteIter, WriteIterRead, WriteRead};
use embedded_hal::blocking::i2c::{
Operation, Read, Transactional, Write, WriteIter, WriteIterRead, WriteRead,
};

impl<I2C> WriteRead for I2c<I2C>
where
Expand Down Expand Up @@ -72,4 +74,19 @@ mod blocking {
self.read(addr, buffer)
}
}

impl<I2C> Transactional for I2c<I2C>
where
I2C: Instance,
{
type Error = Error;

fn exec<'a>(
&mut self,
address: u8,
operations: &mut [Operation<'a>],
) -> Result<(), Self::Error> {
self.transaction_slice_hal_02(address, operations)
}
}
}

0 comments on commit 9407777

Please sign in to comment.