diff options
| author | HybridChild <[email protected]> | 2025-11-11 20:33:00 +0100 |
|---|---|---|
| committer | HybridChild <[email protected]> | 2025-11-12 09:34:05 +0100 |
| commit | 5e76be83cf693d2de4608fec4ef11fbeb32722d4 (patch) | |
| tree | 43ebdca03a20147dc978a7e114eadc2effe637f2 | |
| parent | f078c85454f09f28a85aa834a9496b37058695e0 (diff) | |
stm32/i2c_v2: Add initial transaction implementation
| -rw-r--r-- | embassy-stm32/src/i2c/v2.rs | 502 |
1 files changed, 474 insertions, 28 deletions
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 4527e55b9..061f4ff3a 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -172,20 +172,23 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 172 | length: usize, | 172 | length: usize, |
| 173 | stop: Stop, | 173 | stop: Stop, |
| 174 | reload: bool, | 174 | reload: bool, |
| 175 | restart: bool, | ||
| 175 | timeout: Timeout, | 176 | timeout: Timeout, |
| 176 | ) -> Result<(), Error> { | 177 | ) -> Result<(), Error> { |
| 177 | assert!(length < 256); | 178 | assert!(length < 256); |
| 178 | 179 | ||
| 179 | // Wait for any previous address sequence to end | 180 | if !restart { |
| 180 | // automatically. This could be up to 50% of a bus | 181 | // Wait for any previous address sequence to end |
| 181 | // cycle (ie. up to 0.5/freq) | 182 | // automatically. This could be up to 50% of a bus |
| 182 | while info.regs.cr2().read().start() { | 183 | // cycle (ie. up to 0.5/freq) |
| 183 | timeout.check()?; | 184 | while info.regs.cr2().read().start() { |
| 184 | } | 185 | timeout.check()?; |
| 186 | } | ||
| 185 | 187 | ||
| 186 | // Wait for the bus to be free | 188 | // Wait for the bus to be free |
| 187 | while info.regs.isr().read().busy() { | 189 | while info.regs.isr().read().busy() { |
| 188 | timeout.check()?; | 190 | timeout.check()?; |
| 191 | } | ||
| 189 | } | 192 | } |
| 190 | 193 | ||
| 191 | let reload = if reload { | 194 | let reload = if reload { |
| @@ -210,7 +213,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 210 | Ok(()) | 213 | Ok(()) |
| 211 | } | 214 | } |
| 212 | 215 | ||
| 213 | fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> { | 216 | fn reload(info: &'static Info, length: usize, will_reload: bool, stop: Stop, timeout: Timeout) -> Result<(), Error> { |
| 214 | assert!(length < 256 && length > 0); | 217 | assert!(length < 256 && length > 0); |
| 215 | 218 | ||
| 216 | while !info.regs.isr().read().tcr() { | 219 | while !info.regs.isr().read().tcr() { |
| @@ -226,6 +229,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 226 | info.regs.cr2().modify(|w| { | 229 | info.regs.cr2().modify(|w| { |
| 227 | w.set_nbytes(length as u8); | 230 | w.set_nbytes(length as u8); |
| 228 | w.set_reload(will_reload); | 231 | w.set_reload(will_reload); |
| 232 | w.set_autoend(stop.autoend()); | ||
| 229 | }); | 233 | }); |
| 230 | 234 | ||
| 231 | Ok(()) | 235 | Ok(()) |
| @@ -403,7 +407,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 403 | 407 | ||
| 404 | for (number, chunk) in read.chunks_mut(255).enumerate() { | 408 | for (number, chunk) in read.chunks_mut(255).enumerate() { |
| 405 | if number != 0 { | 409 | if number != 0 { |
| 406 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 410 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Automatic, timeout)?; |
| 407 | } | 411 | } |
| 408 | 412 | ||
| 409 | for byte in chunk { | 413 | for byte in chunk { |
| @@ -441,6 +445,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 441 | write.len().min(255), | 445 | write.len().min(255), |
| 442 | Stop::Software, | 446 | Stop::Software, |
| 443 | last_chunk_idx != 0, | 447 | last_chunk_idx != 0, |
| 448 | false, // restart | ||
| 444 | timeout, | 449 | timeout, |
| 445 | ) { | 450 | ) { |
| 446 | if send_stop { | 451 | if send_stop { |
| @@ -451,7 +456,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 451 | 456 | ||
| 452 | for (number, chunk) in write.chunks(255).enumerate() { | 457 | for (number, chunk) in write.chunks(255).enumerate() { |
| 453 | if number != 0 { | 458 | if number != 0 { |
| 454 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 459 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; |
| 455 | } | 460 | } |
| 456 | 461 | ||
| 457 | for byte in chunk { | 462 | for byte in chunk { |
| @@ -507,9 +512,219 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 507 | /// | 512 | /// |
| 508 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 513 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 509 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 514 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 510 | let _ = addr; | 515 | if operations.is_empty() { |
| 511 | let _ = operations; | 516 | return Err(Error::ZeroLengthTransfer); |
| 512 | todo!() | 517 | } |
| 518 | |||
| 519 | let address = addr.into(); | ||
| 520 | let timeout = self.timeout(); | ||
| 521 | |||
| 522 | // Group consecutive operations of the same type | ||
| 523 | let mut op_idx = 0; | ||
| 524 | let mut is_first_group = true; | ||
| 525 | |||
| 526 | while op_idx < operations.len() { | ||
| 527 | // Determine the type of current group and find all consecutive operations of same type | ||
| 528 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 529 | let group_start = op_idx; | ||
| 530 | |||
| 531 | // Find end of this group (consecutive operations of same type) | ||
| 532 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 533 | op_idx += 1; | ||
| 534 | } | ||
| 535 | let group_end = op_idx; | ||
| 536 | let is_last_group = op_idx >= operations.len(); | ||
| 537 | |||
| 538 | // Execute this group of operations | ||
| 539 | if is_read { | ||
| 540 | self.execute_read_group( | ||
| 541 | address, | ||
| 542 | &mut operations[group_start..group_end], | ||
| 543 | is_first_group, | ||
| 544 | is_last_group, | ||
| 545 | timeout, | ||
| 546 | )?; | ||
| 547 | } else { | ||
| 548 | self.execute_write_group( | ||
| 549 | address, | ||
| 550 | &operations[group_start..group_end], | ||
| 551 | is_first_group, | ||
| 552 | is_last_group, | ||
| 553 | timeout, | ||
| 554 | )?; | ||
| 555 | } | ||
| 556 | |||
| 557 | is_first_group = false; | ||
| 558 | } | ||
| 559 | |||
| 560 | Ok(()) | ||
| 561 | } | ||
| 562 | |||
| 563 | fn execute_write_group( | ||
| 564 | &mut self, | ||
| 565 | address: Address, | ||
| 566 | operations: &[Operation<'_>], | ||
| 567 | is_first_group: bool, | ||
| 568 | is_last_group: bool, | ||
| 569 | timeout: Timeout, | ||
| 570 | ) -> Result<(), Error> { | ||
| 571 | // Calculate total bytes across all operations in this group | ||
| 572 | let total_bytes: usize = operations | ||
| 573 | .iter() | ||
| 574 | .map(|op| match op { | ||
| 575 | Operation::Write(buf) => buf.len(), | ||
| 576 | _ => 0, | ||
| 577 | }) | ||
| 578 | .sum(); | ||
| 579 | |||
| 580 | if total_bytes == 0 { | ||
| 581 | // Handle empty write group - just send address | ||
| 582 | if is_first_group { | ||
| 583 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 584 | } | ||
| 585 | if is_last_group { | ||
| 586 | self.master_stop(); | ||
| 587 | } | ||
| 588 | return Ok(()); | ||
| 589 | } | ||
| 590 | |||
| 591 | let mut total_remaining = total_bytes; | ||
| 592 | let mut first_chunk = true; | ||
| 593 | |||
| 594 | for operation in operations { | ||
| 595 | if let Operation::Write(buffer) = operation { | ||
| 596 | for chunk in buffer.chunks(255) { | ||
| 597 | let chunk_len = chunk.len(); | ||
| 598 | total_remaining -= chunk_len; | ||
| 599 | let is_last_chunk = total_remaining == 0; | ||
| 600 | let will_reload = !is_last_chunk; | ||
| 601 | |||
| 602 | if first_chunk { | ||
| 603 | // First chunk: initiate transfer | ||
| 604 | // If not first group, use RESTART instead of START | ||
| 605 | Self::master_write(self.info, address, chunk_len, Stop::Software, will_reload, !is_first_group, timeout)?; | ||
| 606 | first_chunk = false; | ||
| 607 | } else { | ||
| 608 | // Subsequent chunks: use reload | ||
| 609 | // Always use Software stop for writes | ||
| 610 | Self::reload(self.info, chunk_len, will_reload, Stop::Software, timeout)?; | ||
| 611 | } | ||
| 612 | |||
| 613 | // Send data bytes | ||
| 614 | for byte in chunk { | ||
| 615 | self.wait_txis(timeout)?; | ||
| 616 | self.info.regs.txdr().write(|w| w.set_txdata(*byte)); | ||
| 617 | } | ||
| 618 | } | ||
| 619 | } | ||
| 620 | } | ||
| 621 | |||
| 622 | // Wait for transfer to complete | ||
| 623 | if is_last_group { | ||
| 624 | self.wait_tc(timeout)?; | ||
| 625 | self.master_stop(); | ||
| 626 | self.wait_stop(timeout)?; | ||
| 627 | } else { | ||
| 628 | // Wait for TC before next group (enables RESTART) | ||
| 629 | self.wait_tc(timeout)?; | ||
| 630 | } | ||
| 631 | |||
| 632 | Ok(()) | ||
| 633 | } | ||
| 634 | |||
| 635 | fn execute_read_group( | ||
| 636 | &mut self, | ||
| 637 | address: Address, | ||
| 638 | operations: &mut [Operation<'_>], | ||
| 639 | is_first_group: bool, | ||
| 640 | is_last_group: bool, | ||
| 641 | timeout: Timeout, | ||
| 642 | ) -> Result<(), Error> { | ||
| 643 | // Calculate total bytes across all operations in this group | ||
| 644 | let total_bytes: usize = operations | ||
| 645 | .iter() | ||
| 646 | .map(|op| match op { | ||
| 647 | Operation::Read(buf) => buf.len(), | ||
| 648 | _ => 0, | ||
| 649 | }) | ||
| 650 | .sum(); | ||
| 651 | |||
| 652 | if total_bytes == 0 { | ||
| 653 | // Handle empty read group | ||
| 654 | if is_first_group { | ||
| 655 | Self::master_read( | ||
| 656 | self.info, | ||
| 657 | address, | ||
| 658 | 0, | ||
| 659 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 660 | false, | ||
| 661 | !is_first_group, | ||
| 662 | timeout, | ||
| 663 | )?; | ||
| 664 | } | ||
| 665 | if is_last_group { | ||
| 666 | self.wait_stop(timeout)?; | ||
| 667 | } | ||
| 668 | return Ok(()); | ||
| 669 | } | ||
| 670 | |||
| 671 | let mut total_remaining = total_bytes; | ||
| 672 | let mut first_chunk = true; | ||
| 673 | |||
| 674 | for operation in operations { | ||
| 675 | if let Operation::Read(buffer) = operation { | ||
| 676 | for chunk in buffer.chunks_mut(255) { | ||
| 677 | let chunk_len = chunk.len(); | ||
| 678 | total_remaining -= chunk_len; | ||
| 679 | let is_last_chunk = total_remaining == 0; | ||
| 680 | let will_reload = !is_last_chunk; | ||
| 681 | |||
| 682 | if first_chunk { | ||
| 683 | // First chunk: initiate transfer | ||
| 684 | let stop = if is_last_group && is_last_chunk { | ||
| 685 | Stop::Automatic | ||
| 686 | } else { | ||
| 687 | Stop::Software | ||
| 688 | }; | ||
| 689 | |||
| 690 | Self::master_read( | ||
| 691 | self.info, | ||
| 692 | address, | ||
| 693 | chunk_len, | ||
| 694 | stop, | ||
| 695 | will_reload, | ||
| 696 | !is_first_group, // restart if not first group | ||
| 697 | timeout, | ||
| 698 | )?; | ||
| 699 | first_chunk = false; | ||
| 700 | } else { | ||
| 701 | // Subsequent chunks: use reload | ||
| 702 | let stop = if is_last_group && is_last_chunk { | ||
| 703 | Stop::Automatic | ||
| 704 | } else { | ||
| 705 | Stop::Software | ||
| 706 | }; | ||
| 707 | Self::reload(self.info, chunk_len, will_reload, stop, timeout)?; | ||
| 708 | } | ||
| 709 | |||
| 710 | // Receive data bytes | ||
| 711 | for byte in chunk { | ||
| 712 | self.wait_rxne(timeout)?; | ||
| 713 | *byte = self.info.regs.rxdr().read().rxdata(); | ||
| 714 | } | ||
| 715 | } | ||
| 716 | } | ||
| 717 | } | ||
| 718 | |||
| 719 | // Wait for transfer to complete | ||
| 720 | if is_last_group { | ||
| 721 | self.wait_stop(timeout)?; | ||
| 722 | } | ||
| 723 | // For non-last read groups, don't wait for TC - the peripheral may hold SCL low | ||
| 724 | // in Software AUTOEND mode until the next START is issued. Just proceed directly | ||
| 725 | // to the next group which will issue RESTART and release the clock. | ||
| 726 | |||
| 727 | Ok(()) | ||
| 513 | } | 728 | } |
| 514 | 729 | ||
| 515 | /// Blocking write multiple buffers. | 730 | /// Blocking write multiple buffers. |
| @@ -531,6 +746,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 531 | first_length.min(255), | 746 | first_length.min(255), |
| 532 | Stop::Software, | 747 | Stop::Software, |
| 533 | (first_length > 255) || (last_slice_index != 0), | 748 | (first_length > 255) || (last_slice_index != 0), |
| 749 | false, // restart | ||
| 534 | timeout, | 750 | timeout, |
| 535 | ) { | 751 | ) { |
| 536 | self.master_stop(); | 752 | self.master_stop(); |
| @@ -552,6 +768,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 552 | self.info, | 768 | self.info, |
| 553 | slice_len.min(255), | 769 | slice_len.min(255), |
| 554 | (idx != last_slice_index) || (slice_len > 255), | 770 | (idx != last_slice_index) || (slice_len > 255), |
| 771 | Stop::Software, | ||
| 555 | timeout, | 772 | timeout, |
| 556 | ) { | 773 | ) { |
| 557 | if err != Error::Nack { | 774 | if err != Error::Nack { |
| @@ -567,6 +784,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 567 | self.info, | 784 | self.info, |
| 568 | chunk.len(), | 785 | chunk.len(), |
| 569 | (number != last_chunk_idx) || (idx != last_slice_index), | 786 | (number != last_chunk_idx) || (idx != last_slice_index), |
| 787 | Stop::Software, | ||
| 570 | timeout, | 788 | timeout, |
| 571 | ) { | 789 | ) { |
| 572 | if err != Error::Nack { | 790 | if err != Error::Nack { |
| @@ -610,6 +828,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 610 | first_slice: bool, | 828 | first_slice: bool, |
| 611 | last_slice: bool, | 829 | last_slice: bool, |
| 612 | send_stop: bool, | 830 | send_stop: bool, |
| 831 | restart: bool, | ||
| 613 | timeout: Timeout, | 832 | timeout: Timeout, |
| 614 | ) -> Result<(), Error> { | 833 | ) -> Result<(), Error> { |
| 615 | let total_len = write.len(); | 834 | let total_len = write.len(); |
| @@ -676,10 +895,11 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 676 | total_len.min(255), | 895 | total_len.min(255), |
| 677 | Stop::Software, | 896 | Stop::Software, |
| 678 | (total_len > 255) || !last_slice, | 897 | (total_len > 255) || !last_slice, |
| 898 | restart, | ||
| 679 | timeout, | 899 | timeout, |
| 680 | )?; | 900 | )?; |
| 681 | } else { | 901 | } else { |
| 682 | Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; | 902 | Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, Stop::Software, timeout)?; |
| 683 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 903 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 684 | } | 904 | } |
| 685 | } else if !(isr.tcr() || isr.tc()) { | 905 | } else if !(isr.tcr() || isr.tc()) { |
| @@ -690,7 +910,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 690 | } else { | 910 | } else { |
| 691 | let last_piece = (remaining_len <= 255) && last_slice; | 911 | let last_piece = (remaining_len <= 255) && last_slice; |
| 692 | 912 | ||
| 693 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { | 913 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Software, timeout) { |
| 694 | return Poll::Ready(Err(e)); | 914 | return Poll::Ready(Err(e)); |
| 695 | } | 915 | } |
| 696 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 916 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| @@ -793,7 +1013,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 793 | } else { | 1013 | } else { |
| 794 | let last_piece = remaining_len <= 255; | 1014 | let last_piece = remaining_len <= 255; |
| 795 | 1015 | ||
| 796 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { | 1016 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Automatic, timeout) { |
| 797 | return Poll::Ready(Err(e)); | 1017 | return Poll::Ready(Err(e)); |
| 798 | } | 1018 | } |
| 799 | // Return here if we are on last chunk, | 1019 | // Return here if we are on last chunk, |
| @@ -826,7 +1046,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 826 | self.write_internal(address.into(), write, true, timeout) | 1046 | self.write_internal(address.into(), write, true, timeout) |
| 827 | } else { | 1047 | } else { |
| 828 | timeout | 1048 | timeout |
| 829 | .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) | 1049 | .with(self.write_dma_internal(address.into(), write, true, true, true, false, timeout)) |
| 830 | .await | 1050 | .await |
| 831 | } | 1051 | } |
| 832 | } | 1052 | } |
| @@ -850,7 +1070,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 850 | let next = iter.next(); | 1070 | let next = iter.next(); |
| 851 | let is_last = next.is_none(); | 1071 | let is_last = next.is_none(); |
| 852 | 1072 | ||
| 853 | let fut = self.write_dma_internal(address, c, first, is_last, is_last, timeout); | 1073 | let fut = self.write_dma_internal(address, c, first, is_last, is_last, false, timeout); |
| 854 | timeout.with(fut).await?; | 1074 | timeout.with(fut).await?; |
| 855 | first = false; | 1075 | first = false; |
| 856 | current = next; | 1076 | current = next; |
| @@ -881,7 +1101,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 881 | if write.is_empty() { | 1101 | if write.is_empty() { |
| 882 | self.write_internal(address.into(), write, false, timeout)?; | 1102 | self.write_internal(address.into(), write, false, timeout)?; |
| 883 | } else { | 1103 | } else { |
| 884 | let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); | 1104 | let fut = self.write_dma_internal(address.into(), write, true, true, false, false, timeout); |
| 885 | timeout.with(fut).await?; | 1105 | timeout.with(fut).await?; |
| 886 | } | 1106 | } |
| 887 | 1107 | ||
| @@ -903,9 +1123,235 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 903 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 1123 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 904 | #[cfg(all(feature = "low-power", stm32wlex))] | 1124 | #[cfg(all(feature = "low-power", stm32wlex))] |
| 905 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | 1125 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); |
| 906 | let _ = addr; | 1126 | |
| 907 | let _ = operations; | 1127 | if operations.is_empty() { |
| 908 | todo!() | 1128 | return Err(Error::ZeroLengthTransfer); |
| 1129 | } | ||
| 1130 | |||
| 1131 | let address = addr.into(); | ||
| 1132 | let timeout = self.timeout(); | ||
| 1133 | |||
| 1134 | // Group consecutive operations of the same type | ||
| 1135 | let mut op_idx = 0; | ||
| 1136 | let mut is_first_group = true; | ||
| 1137 | |||
| 1138 | while op_idx < operations.len() { | ||
| 1139 | // Determine the type of current group and find all consecutive operations of same type | ||
| 1140 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 1141 | let group_start = op_idx; | ||
| 1142 | |||
| 1143 | // Find end of this group (consecutive operations of same type) | ||
| 1144 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 1145 | op_idx += 1; | ||
| 1146 | } | ||
| 1147 | let group_end = op_idx; | ||
| 1148 | let is_last_group = op_idx >= operations.len(); | ||
| 1149 | |||
| 1150 | // Execute this group of operations | ||
| 1151 | if is_read { | ||
| 1152 | self.execute_read_group_async( | ||
| 1153 | address, | ||
| 1154 | &mut operations[group_start..group_end], | ||
| 1155 | is_first_group, | ||
| 1156 | is_last_group, | ||
| 1157 | timeout, | ||
| 1158 | ) | ||
| 1159 | .await?; | ||
| 1160 | } else { | ||
| 1161 | self.execute_write_group_async( | ||
| 1162 | address, | ||
| 1163 | &operations[group_start..group_end], | ||
| 1164 | is_first_group, | ||
| 1165 | is_last_group, | ||
| 1166 | timeout, | ||
| 1167 | ) | ||
| 1168 | .await?; | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | is_first_group = false; | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | Ok(()) | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | async fn execute_write_group_async( | ||
| 1178 | &mut self, | ||
| 1179 | address: Address, | ||
| 1180 | operations: &[Operation<'_>], | ||
| 1181 | is_first_group: bool, | ||
| 1182 | is_last_group: bool, | ||
| 1183 | timeout: Timeout, | ||
| 1184 | ) -> Result<(), Error> { | ||
| 1185 | // Calculate total bytes across all operations in this group | ||
| 1186 | let total_bytes: usize = operations | ||
| 1187 | .iter() | ||
| 1188 | .map(|op| match op { | ||
| 1189 | Operation::Write(buf) => buf.len(), | ||
| 1190 | _ => 0, | ||
| 1191 | }) | ||
| 1192 | .sum(); | ||
| 1193 | |||
| 1194 | if total_bytes == 0 { | ||
| 1195 | // Handle empty write group using blocking call | ||
| 1196 | if is_first_group { | ||
| 1197 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 1198 | } | ||
| 1199 | if is_last_group { | ||
| 1200 | self.master_stop(); | ||
| 1201 | } | ||
| 1202 | return Ok(()); | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | let send_stop = is_last_group; | ||
| 1206 | |||
| 1207 | // Use DMA for each operation in the group | ||
| 1208 | let mut first_in_group = true; | ||
| 1209 | let mut remaining_operations = operations.len(); | ||
| 1210 | |||
| 1211 | for operation in operations { | ||
| 1212 | if let Operation::Write(buffer) = operation { | ||
| 1213 | remaining_operations -= 1; | ||
| 1214 | let is_last_in_group = remaining_operations == 0; | ||
| 1215 | |||
| 1216 | if buffer.is_empty() { | ||
| 1217 | // Skip empty buffers | ||
| 1218 | continue; | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | let fut = self.write_dma_internal( | ||
| 1222 | address, | ||
| 1223 | buffer, | ||
| 1224 | first_in_group && is_first_group, // first_slice | ||
| 1225 | is_last_in_group && is_last_group, // last_slice | ||
| 1226 | send_stop && is_last_in_group, // send_stop | ||
| 1227 | !is_first_group && first_in_group, // restart | ||
| 1228 | timeout, | ||
| 1229 | ); | ||
| 1230 | timeout.with(fut).await?; | ||
| 1231 | first_in_group = false; | ||
| 1232 | } | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | // If not last group, wait for TC to enable RESTART | ||
| 1236 | if !is_last_group { | ||
| 1237 | self.wait_tc(timeout)?; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | Ok(()) | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | async fn execute_read_group_async( | ||
| 1244 | &mut self, | ||
| 1245 | address: Address, | ||
| 1246 | operations: &mut [Operation<'_>], | ||
| 1247 | is_first_group: bool, | ||
| 1248 | is_last_group: bool, | ||
| 1249 | timeout: Timeout, | ||
| 1250 | ) -> Result<(), Error> { | ||
| 1251 | // Calculate total bytes across all operations in this group | ||
| 1252 | let total_bytes: usize = operations | ||
| 1253 | .iter() | ||
| 1254 | .map(|op| match op { | ||
| 1255 | Operation::Read(buf) => buf.len(), | ||
| 1256 | _ => 0, | ||
| 1257 | }) | ||
| 1258 | .sum(); | ||
| 1259 | |||
| 1260 | if total_bytes == 0 { | ||
| 1261 | // Handle empty read group using blocking call | ||
| 1262 | if is_first_group { | ||
| 1263 | Self::master_read( | ||
| 1264 | self.info, | ||
| 1265 | address, | ||
| 1266 | 0, | ||
| 1267 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 1268 | false, | ||
| 1269 | !is_first_group, | ||
| 1270 | timeout, | ||
| 1271 | )?; | ||
| 1272 | } | ||
| 1273 | if is_last_group { | ||
| 1274 | self.wait_stop(timeout)?; | ||
| 1275 | } | ||
| 1276 | return Ok(()); | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | // For read operations, we need to handle them differently since read_dma_internal | ||
| 1280 | // expects a single buffer. We'll iterate through operations and use DMA for each. | ||
| 1281 | let mut total_remaining = total_bytes; | ||
| 1282 | let restart = !is_first_group; | ||
| 1283 | |||
| 1284 | for operation in operations { | ||
| 1285 | if let Operation::Read(buffer) = operation { | ||
| 1286 | if buffer.is_empty() { | ||
| 1287 | // Skip empty buffers | ||
| 1288 | continue; | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | let is_first_read = total_remaining == total_bytes; | ||
| 1292 | let buf_len = buffer.len(); | ||
| 1293 | total_remaining -= buf_len; | ||
| 1294 | let is_last_read = total_remaining == 0; | ||
| 1295 | |||
| 1296 | if is_first_read { | ||
| 1297 | // First read in the group | ||
| 1298 | let completed_chunks = buf_len / 255; | ||
| 1299 | let total_chunks = if completed_chunks * 255 == buf_len { | ||
| 1300 | completed_chunks | ||
| 1301 | } else { | ||
| 1302 | completed_chunks + 1 | ||
| 1303 | }; | ||
| 1304 | let last_chunk_idx = total_chunks.saturating_sub(1); | ||
| 1305 | |||
| 1306 | // Use master_read to initiate, then DMA for data | ||
| 1307 | Self::master_read( | ||
| 1308 | self.info, | ||
| 1309 | address, | ||
| 1310 | buf_len.min(255), | ||
| 1311 | if is_last_group && is_last_read { | ||
| 1312 | Stop::Automatic | ||
| 1313 | } else { | ||
| 1314 | Stop::Software | ||
| 1315 | }, | ||
| 1316 | last_chunk_idx != 0 || !is_last_read, | ||
| 1317 | restart, | ||
| 1318 | timeout, | ||
| 1319 | )?; | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | // Use the existing read_dma_internal, but we need to handle the reload logic ourselves | ||
| 1323 | // For simplicity with consecutive reads, fall back to blocking for now | ||
| 1324 | // This maintains correctness while keeping complexity manageable | ||
| 1325 | for (chunk_idx, chunk) in buffer.chunks_mut(255).enumerate() { | ||
| 1326 | let chunk_len = chunk.len(); | ||
| 1327 | let is_very_last = total_remaining == 0 && chunk_len == chunk.len(); | ||
| 1328 | |||
| 1329 | if !is_first_read || chunk_idx != 0 { | ||
| 1330 | let stop = if is_last_group && is_very_last { | ||
| 1331 | Stop::Automatic | ||
| 1332 | } else { | ||
| 1333 | Stop::Software | ||
| 1334 | }; | ||
| 1335 | Self::reload(self.info, chunk_len, !(is_last_group && is_very_last), stop, timeout)?; | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | for byte in chunk { | ||
| 1339 | self.wait_rxne(timeout)?; | ||
| 1340 | *byte = self.info.regs.rxdr().read().rxdata(); | ||
| 1341 | } | ||
| 1342 | } | ||
| 1343 | } | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | // Wait for transfer to complete | ||
| 1347 | if is_last_group { | ||
| 1348 | self.wait_stop(timeout)?; | ||
| 1349 | } | ||
| 1350 | // For non-last read groups, don't wait for TC - the peripheral may hold SCL low | ||
| 1351 | // in Software AUTOEND mode until the next START is issued. Just proceed directly | ||
| 1352 | // to the next group which will issue RESTART and release the clock. | ||
| 1353 | |||
| 1354 | Ok(()) | ||
| 909 | } | 1355 | } |
| 910 | } | 1356 | } |
| 911 | 1357 | ||
| @@ -1043,7 +1489,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1043 | if number == 0 { | 1489 | if number == 0 { |
| 1044 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1490 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1045 | } else { | 1491 | } else { |
| 1046 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1492 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; |
| 1047 | } | 1493 | } |
| 1048 | 1494 | ||
| 1049 | let mut index = 0; | 1495 | let mut index = 0; |
| @@ -1092,7 +1538,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1092 | if number == 0 { | 1538 | if number == 0 { |
| 1093 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1539 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1094 | } else { | 1540 | } else { |
| 1095 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1541 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; |
| 1096 | } | 1542 | } |
| 1097 | 1543 | ||
| 1098 | let mut index = 0; | 1544 | let mut index = 0; |
| @@ -1228,7 +1674,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1228 | Poll::Pending | 1674 | Poll::Pending |
| 1229 | } else if isr.tcr() { | 1675 | } else if isr.tcr() { |
| 1230 | let is_last_slice = remaining_len <= 255; | 1676 | let is_last_slice = remaining_len <= 255; |
| 1231 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1677 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, Stop::Software, timeout) { |
| 1232 | return Poll::Ready(Err(e)); | 1678 | return Poll::Ready(Err(e)); |
| 1233 | } | 1679 | } |
| 1234 | remaining_len = remaining_len.saturating_sub(255); | 1680 | remaining_len = remaining_len.saturating_sub(255); |
| @@ -1292,7 +1738,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1292 | Poll::Pending | 1738 | Poll::Pending |
| 1293 | } else if isr.tcr() { | 1739 | } else if isr.tcr() { |
| 1294 | let is_last_slice = remaining_len <= 255; | 1740 | let is_last_slice = remaining_len <= 255; |
| 1295 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1741 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, Stop::Software, timeout) { |
| 1296 | return Poll::Ready(Err(e)); | 1742 | return Poll::Ready(Err(e)); |
| 1297 | } | 1743 | } |
| 1298 | remaining_len = remaining_len.saturating_sub(255); | 1744 | remaining_len = remaining_len.saturating_sub(255); |
