Skip to content

Commit

Permalink
Merge pull request #765 from ithinuel/i2c-impl-i2c-write-iter
Browse files Browse the repository at this point in the history
[i2c] Implement i2c-write-iter traits
  • Loading branch information
ithinuel authored Feb 22, 2024
2 parents e175d2a + edc3443 commit 54e6cfa
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 8 deletions.
8 changes: 5 additions & 3 deletions on-target-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ defmt-test = "0.3.1"
panic-probe = { version = "0.3", features = ["print-defmt"] }

rp2040-hal = { path = "../rp2040-hal", features = [
"defmt",
"critical-section-impl",
"rt",
"critical-section-impl",
"defmt",
"rt",
"i2c-write-iter",
] }
# Needed to set spi frequencies
fugit = "0.3.6"
Expand All @@ -70,4 +71,5 @@ nostd_async = { version = "0.6.1", features = ["wfe"] }
futures = { version = "0.3.30", default-features = false, features = [
"async-await",
] }
i2c-write-iter = { version = "1.0.0", features = ["async"] }
itertools = { version = "0.12.0", default-features = false }
6 changes: 6 additions & 0 deletions on-target-tests/tests/i2c_loopback_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ mod tests {
run_test(non_blocking::transaction(state, ADDR_10BIT, 7..=14));
}

#[test]
fn i2c_write_iter(state: &mut State) {
run_test(non_blocking::transaction_iter(state, ADDR_7BIT));
run_test(non_blocking::transaction_iter(state, ADDR_10BIT));
}

// Sad paths:
// invalid tx buf on write
// invalid rx buf on read
Expand Down
30 changes: 30 additions & 0 deletions on-target-tests/tests/i2c_tests/non_blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,33 @@ pub async fn transaction<A: ValidAddress>(
.collect();
assert_eq!(g, h);
}

pub async fn transaction_iter<A: ValidAddress>(state: &mut State, addr: A) {
use i2c_write_iter::non_blocking::I2cIter;
reset(state, addr);

let samples: FIFOBuffer = Generator::seq().take(25).collect();
let controller = state.controller.as_mut().expect("controller's missing.");
let case = async {
controller
.transaction_iter(
addr,
[i2c_write_iter::Operation::WriteIter(
samples.iter().cloned(),
)],
)
.await
.expect("Successful write_iter");
wait_with(&state.payload, |p| p.stop_cnt != 1).await;
};

futures::select_biased! {
_ = case.fuse() => {}
_ = target_handler(
&state.payload,
state.target.as_mut().take().expect("target’s missing"),
).fuse() => {}
}

assert_eq!(samples, state.payload.borrow().vec);
}
5 changes: 5 additions & 0 deletions rp2040-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ frunk = { version = "0.4.1", default-features = false }

bitfield = { version = "0.14.0" }

i2c-write-iter = { version = "1.0.0", features = ["async"], optional = true }

[dev-dependencies]
cortex-m-rt = "0.7"
cortex-m-rtic = "1.1.4"
Expand Down Expand Up @@ -101,6 +103,9 @@ defmt = ["dep:defmt"]
# Implement `rtic_monotonic::Monotonic` based on the RP2040 timer peripheral
rtic-monotonic = ["dep:rtic-monotonic"]

# Implement `i2c-write-iter` traits
i2c-write-iter = ["dep:i2c-write-iter"]

[[example]]
# irq example uses cortex-m-rt::interrupt, need rt feature for that
name = "gpio_irq_example"
Expand Down
36 changes: 36 additions & 0 deletions rp2040-hal/src/i2c/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,29 @@ impl<T: Deref<Target = Block>, PINS> I2C<T, PINS, Controller> {
}
Ok(())
}

#[cfg(feature = "i2c-write-iter")]
fn transaction_iter<'op, A, O, B>(&mut self, address: A, operations: O) -> Result<(), Error>
where
A: ValidAddress,
O: IntoIterator<Item = i2c_write_iter::Operation<'op, B>>,
B: IntoIterator<Item = u8>,
{
use i2c_write_iter::Operation;
self.setup(address)?;

let mut first = true;
let mut operations = operations.into_iter().peekable();
while let Some(operation) = operations.next() {
let last = operations.peek().is_none();
match operation {
Operation::Read(buf) => self.read_internal(first, buf, last)?,
Operation::WriteIter(buf) => self.write_internal(first, buf, last)?,
}
first = false;
}
Ok(())
}
}

impl<A: ValidAddress, T: Deref<Target = Block>, PINS> Read<A> for I2C<T, PINS, Controller> {
Expand Down Expand Up @@ -458,3 +481,16 @@ impl<A: ValidAddress, T: Deref<Target = Block>, PINS> eh1::I2c<A> for I2C<T, PIN
self.transaction(address, operations.iter_mut())
}
}

#[cfg(feature = "i2c-write-iter")]
impl<A: i2c_write_iter::AddressMode + ValidAddress, T: Deref<Target = Block>, PINS>
i2c_write_iter::I2cIter<A> for I2C<T, PINS, Controller>
{
fn transaction_iter<'a, O, B>(&mut self, address: A, operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = i2c_write_iter::Operation<'a, B>>,
B: IntoIterator<Item = u8>,
{
self.transaction_iter(address, operations)
}
}
63 changes: 58 additions & 5 deletions rp2040-hal/src/i2c/controller/non_blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ where
T: Deref<Target = RegisterBlock>,
Self: AsyncPeripheral,
{
/// `tx_empty`: true to unmask tx_empty
#[inline]
fn unmask_intr(&mut self) {
fn unmask_intr(&mut self, tx_empty: bool) {
unsafe {
self.i2c.ic_intr_mask.write_with_zero(|w| {
w.m_tx_empty()
.disabled()
.bit(tx_empty)
.m_rx_full()
.disabled()
.m_tx_abrt()
Expand All @@ -73,18 +74,18 @@ where
#[inline]
fn unmask_tx_empty(&mut self) {
self.configure_tx_empty(TxEmptyConfig::Empty);
self.unmask_intr()
self.unmask_intr(true)
}

#[inline]
fn unmask_tx_not_full(&mut self) {
self.configure_tx_empty(TxEmptyConfig::NotFull);
self.unmask_intr()
self.unmask_intr(true)
}

#[inline]
fn unmask_stop_det(&mut self) {
self.unmask_intr();
self.unmask_intr(false);
}

#[inline]
Expand Down Expand Up @@ -259,6 +260,38 @@ where
self.non_blocking_write_internal(true, bytes, false).await?;
self.non_blocking_read_internal(false, read, true).await
}

/// Writes to the i2c bus taking operations from and iterator, writing from iterator of bytes,
/// reading to slices of bytes.
#[cfg(feature = "i2c-write-iter")]
pub async fn transaction_iter_async<'b, A, O, B>(
&mut self,
address: A,
operations: O,
) -> Result<(), super::Error>
where
A: ValidAddress,
O: IntoIterator<Item = i2c_write_iter::Operation<'b, B>>,
B: IntoIterator<Item = u8>,
{
self.setup(address)?;

let mut first = true;
let mut operations = operations.into_iter().peekable();
while let Some(operation) = operations.next() {
let last = operations.peek().is_none();
match operation {
i2c_write_iter::Operation::Read(buf) => {
self.non_blocking_read_internal(first, buf, last).await?
}
i2c_write_iter::Operation::WriteIter(buf) => {
self.non_blocking_write_internal(first, buf, last).await?
}
}
first = false;
}
Ok(())
}
}

impl<T, PINS, A> embedded_hal_async::i2c::I2c<A> for I2C<T, PINS, Controller>
Expand Down Expand Up @@ -292,3 +325,23 @@ where
Ok(())
}
}

#[cfg(feature = "i2c-write-iter")]
impl<T, PINS, A> i2c_write_iter::non_blocking::I2cIter<A> for I2C<T, PINS, Controller>
where
Self: AsyncPeripheral,
A: 'static + ValidAddress + AddressMode,
T: Deref<Target = RegisterBlock>,
{
async fn transaction_iter<'a, O, B>(
&mut self,
address: A,
operations: O,
) -> Result<(), Self::Error>
where
O: IntoIterator<Item = i2c_write_iter::Operation<'a, B>>,
B: IntoIterator<Item = u8>,
{
self.transaction_iter_async(address, operations).await
}
}
2 changes: 2 additions & 0 deletions rp2040-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
//! * **rtic-monotonic** -
//! Implement
//! `rtic_monotonic::Monotonic` based on the RP2040 timer peripheral
//! * **i2c-write-iter** -
//! Implement `i2c_write_iter` traits for `I2C<_, _, Controller>`.

#![warn(missing_docs)]
#![no_std]
Expand Down

0 comments on commit 54e6cfa

Please sign in to comment.