diff options
| author | Sebastian Goll <[email protected]> | 2024-03-21 00:25:39 +0100 |
|---|---|---|
| committer | Sebastian Goll <[email protected]> | 2024-03-26 22:53:14 +0100 |
| commit | 9c00a40e73f49aa0d46a47259fe8adc8c3f248b8 (patch) | |
| tree | 7f8a9d87cc7e03f5e2de83ffa073faf57b2faeea /embassy-stm32 | |
| parent | 0885c102d3c332591b2d913113cde08fca8fe41b (diff) | |
Extract frame options generation into iterator to reuse in async
Diffstat (limited to 'embassy-stm32')
| -rw-r--r-- | embassy-stm32/src/i2c/v1.rs | 102 |
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 | ||
| 7 | use core::future::poll_fn; | 7 | use core::future::poll_fn; |
| 8 | use core::task::Poll; | 8 | use core::{iter, task::Poll}; |
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| 11 | use embassy_futures::select::{select, Either}; | 11 | use 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 | ||
| 112 | fn 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 | |||
| 106 | impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | 162 | impl<'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(()) |
