aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/i2c
diff options
context:
space:
mode:
authorSebastian Goll <[email protected]>2024-03-21 00:25:39 +0100
committerSebastian Goll <[email protected]>2024-03-26 22:53:14 +0100
commit9c00a40e73f49aa0d46a47259fe8adc8c3f248b8 (patch)
tree7f8a9d87cc7e03f5e2de83ffa073faf57b2faeea /embassy-stm32/src/i2c
parent0885c102d3c332591b2d913113cde08fca8fe41b (diff)
Extract frame options generation into iterator to reuse in async
Diffstat (limited to 'embassy-stm32/src/i2c')
-rw-r--r--embassy-stm32/src/i2c/v1.rs102
1 files changed, 58 insertions, 44 deletions
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs
index 2751d443f..dd2cea6b8 100644
--- a/embassy-stm32/src/i2c/v1.rs
+++ b/embassy-stm32/src/i2c/v1.rs
@@ -5,7 +5,7 @@
5//! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead. 5//! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead.
6 6
7use core::future::poll_fn; 7use core::future::poll_fn;
8use core::task::Poll; 8use core::{iter, task::Poll};
9 9
10use embassy_embedded_hal::SetConfig; 10use embassy_embedded_hal::SetConfig;
11use embassy_futures::select::{select, Either}; 11use embassy_futures::select::{select, Either};
@@ -103,6 +103,62 @@ impl FrameOptions {
103 } 103 }
104} 104}
105 105
106/// Iterates over operations in transaction.
107///
108/// Returns necessary frame options for each operation to uphold the [transaction contract] and have
109/// the right start/stop/(N)ACK conditions on the wire.
110///
111/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
112fn operation_frames<'a, 'b: 'a>(
113 operations: &'a mut [Operation<'b>],
114) -> impl IntoIterator<Item = (&'a mut Operation<'b>, FrameOptions)> {
115 let mut operations = operations.iter_mut().peekable();
116
117 let mut next_first_frame = true;
118
119 iter::from_fn(move || {
120 let Some(op) = operations.next() else {
121 return None;
122 };
123
124 // Is `op` first frame of its type?
125 let first_frame = next_first_frame;
126 let next_op = operations.peek();
127
128 // Get appropriate frame options as combination of the following properties:
129 //
130 // - For each first operation of its type, generate a (repeated) start condition.
131 // - For the last operation overall in the entire transaction, generate a stop condition.
132 // - For read operations, check the next operation: if it is also a read operation, we merge
133 // these and send ACK for all bytes in the current operation; send NACK only for the final
134 // read operation's last byte (before write or end of entire transaction) to indicate last
135 // byte read and release the bus for transmission of the bus master's next byte (or stop).
136 //
137 // We check the third property unconditionally, i.e. even for write opeartions. This is okay
138 // because the resulting frame options are identical for write operations.
139 let frame = match (first_frame, next_op) {
140 (true, None) => FrameOptions::FirstAndLastFrame,
141 (true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame,
142 (true, Some(Operation::Write(_))) => FrameOptions::FirstFrame,
143 //
144 (false, None) => FrameOptions::LastFrame,
145 (false, Some(Operation::Read(_))) => FrameOptions::NextFrame,
146 (false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop,
147 };
148
149 // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at
150 // the beginning of the loop because we hand out `op` as iterator value and cannot access it
151 // anymore in the next iteration.
152 next_first_frame = match (&op, next_op) {
153 (_, None) => false,
154 (Operation::Read(_), Some(Operation::Write(_))) | (Operation::Write(_), Some(Operation::Read(_))) => true,
155 (Operation::Read(_), Some(Operation::Read(_))) | (Operation::Write(_), Some(Operation::Write(_))) => false,
156 };
157
158 Some((op, frame))
159 })
160}
161
106impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { 162impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
107 pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { 163 pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
108 T::regs().cr1().modify(|reg| { 164 T::regs().cr1().modify(|reg| {
@@ -397,53 +453,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
397 453
398 let timeout = self.timeout(); 454 let timeout = self.timeout();
399 455
400 let mut operations = operations.iter_mut(); 456 for (op, frame) in operation_frames(operations) {
401
402 let mut prev_op: Option<&mut Operation<'_>> = None;
403 let mut next_op = operations.next();
404
405 while let Some(op) = next_op {
406 next_op = operations.next();
407
408 // Check if this is the first frame of this type. This is the case for the first overall
409 // frame in the transaction and whenever the type of operation changes.
410 let first_frame =
411 match (prev_op.as_ref(), &op) {
412 (None, _) => true,
413 (Some(Operation::Read(_)), Operation::Write(_))
414 | (Some(Operation::Write(_)), Operation::Read(_)) => true,
415 (Some(Operation::Read(_)), Operation::Read(_))
416 | (Some(Operation::Write(_)), Operation::Write(_)) => false,
417 };
418
419 let frame = match (first_frame, next_op.as_ref()) {
420 // If this is the first frame of this type, we generate a (repeated) start condition
421 // but have to consider the next operation: if it is the last, we generate the final
422 // stop condition. Otherwise, we branch on the operation: with read operations, only
423 // the last byte overall (before a write operation or the end of the transaction) is
424 // to be NACK'd, i.e. if another read operation follows, we must ACK this last byte.
425 (true, None) => FrameOptions::FirstAndLastFrame,
426 // Make sure to keep sending ACK for last byte in read operation when it is followed
427 // by another consecutive read operation. If the current operation is write, this is
428 // identical to `FirstFrame`.
429 (true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame,
430 // Otherwise, send NACK for last byte (in read operation). (For write, this does not
431 // matter and could also be `FirstAndNextFrame`.)
432 (true, Some(Operation::Write(_))) => FrameOptions::FirstFrame,
433
434 // If this is not the first frame of its type, we do not generate a (repeated) start
435 // condition. Otherwise, we branch the same way as above.
436 (false, None) => FrameOptions::LastFrame,
437 (false, Some(Operation::Read(_))) => FrameOptions::NextFrame,
438 (false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop,
439 };
440
441 match op { 457 match op {
442 Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?, 458 Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?,
443 Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?, 459 Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?,
444 } 460 }
445
446 prev_op = Some(op);
447 } 461 }
448 462
449 Ok(()) 463 Ok(())