diff options
| author | HybridChild <[email protected]> | 2025-08-10 08:31:35 +0200 |
|---|---|---|
| committer | HybridChild <[email protected]> | 2025-08-23 08:52:03 +0200 |
| commit | b88c5195e030b6fac129ea9cb74eb169227f7335 (patch) | |
| tree | 73b7e9c87fa2f9bae64e6de6b0fbc71ee95f6a6b | |
| parent | c531af42c8dd2eaeb56ef1891396ac559918560e (diff) | |
stm32/i2c_v1: Add MultiMaster (Slave) mode implementation
| -rw-r--r-- | embassy-stm32/src/i2c/v1.rs | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index f2fd0147e..7b6ecf869 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -32,6 +32,21 @@ impl State { | |||
| 32 | } | 32 | } |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | #[derive(Debug, PartialEq)] | ||
| 36 | enum SlaveSendResult { | ||
| 37 | Acked, // Byte sent and ACK received from master | ||
| 38 | Nacked, // Byte sent but NACK received (normal end of transmission) | ||
| 39 | Stopped, // STOP condition detected | ||
| 40 | Restart, // RESTART condition detected | ||
| 41 | } | ||
| 42 | |||
| 43 | #[derive(Debug, PartialEq)] | ||
| 44 | enum SlaveReceiveResult { | ||
| 45 | Byte(u8), // Data byte received | ||
| 46 | Stop, // STOP condition detected | ||
| 47 | Restart, // RESTART condition (new ADDR) detected | ||
| 48 | } | ||
| 49 | |||
| 35 | // /!\ /!\ | 50 | // /!\ /!\ |
| 36 | // /!\ Implementation note! /!\ | 51 | // /!\ Implementation note! /!\ |
| 37 | // /!\ /!\ | 52 | // /!\ /!\ |
| @@ -370,6 +385,332 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 370 | } | 385 | } |
| 371 | } | 386 | } |
| 372 | 387 | ||
| 388 | impl<'d, M: Mode> I2c<'d, M, Master> { | ||
| 389 | /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) | ||
| 390 | pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { | ||
| 391 | let mut slave = I2c { | ||
| 392 | info: self.info, | ||
| 393 | state: self.state, | ||
| 394 | kernel_clock: self.kernel_clock, | ||
| 395 | tx_dma: self.tx_dma.take(), // Use take() to move ownership | ||
| 396 | rx_dma: self.rx_dma.take(), // Use take() to move ownership | ||
| 397 | #[cfg(feature = "time")] | ||
| 398 | timeout: self.timeout, | ||
| 399 | _phantom: PhantomData, | ||
| 400 | _phantom2: PhantomData, | ||
| 401 | _drop_guard: self._drop_guard, // Move the drop guard | ||
| 402 | }; | ||
| 403 | slave.init_slave(slave_addr_config); | ||
| 404 | slave | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | ||
| 409 | pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { | ||
| 410 | // Disable peripheral for configuration | ||
| 411 | self.info.regs.cr1().modify(|reg| { | ||
| 412 | reg.set_pe(false); | ||
| 413 | }); | ||
| 414 | |||
| 415 | // Configure v1-specific slave settings | ||
| 416 | self.configure_addresses(config); | ||
| 417 | |||
| 418 | // Enable slave mode interrupts and settings | ||
| 419 | self.info.regs.cr2().modify(|w| { | ||
| 420 | w.set_itevten(true); // Event interrupts | ||
| 421 | w.set_iterren(true); // Error interrupts | ||
| 422 | }); | ||
| 423 | |||
| 424 | // Re-enable peripheral | ||
| 425 | self.info.regs.cr1().modify(|reg| { | ||
| 426 | reg.set_pe(true); | ||
| 427 | }); | ||
| 428 | } | ||
| 429 | |||
| 430 | fn configure_oa1(&mut self, addr: Address) { | ||
| 431 | match addr { | ||
| 432 | Address::SevenBit(addr) => { | ||
| 433 | self.info.regs.oar1().write(|reg| { | ||
| 434 | // v1 uses left-shifted 7-bit address in bits [7:1] | ||
| 435 | // STM32 reference manual says bits 7:1 for address, bit 0 don't care for 7-bit | ||
| 436 | reg.set_add((addr as u16) << 1); | ||
| 437 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 438 | }); | ||
| 439 | }, | ||
| 440 | Address::TenBit(addr) => { | ||
| 441 | self.info.regs.oar1().modify(|reg| { | ||
| 442 | reg.set_add(addr); // Set address bits [9:0] | ||
| 443 | reg.set_addmode(i2c::vals::Addmode::BIT10); | ||
| 444 | // Manually set bit 14 as required by reference manual | ||
| 445 | reg.0 |= 1 << 14; | ||
| 446 | }); | ||
| 447 | } | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | fn configure_oa2_simple(&mut self, addr: u8) { | ||
| 452 | self.info.regs.oar2().write(|reg| { | ||
| 453 | // v1 OA2: 7-bit address only, no masking support | ||
| 454 | // Address goes in bits [7:1], enable dual addressing | ||
| 455 | reg.set_add2(addr); | ||
| 456 | reg.set_endual(i2c::vals::Endual::DUAL); | ||
| 457 | }); | ||
| 458 | } | ||
| 459 | |||
| 460 | fn configure_addresses(&mut self, config: SlaveAddrConfig) { | ||
| 461 | match config.addr { | ||
| 462 | OwnAddresses::OA1(addr) => { | ||
| 463 | self.configure_oa1(addr); | ||
| 464 | // Disable OA2 if not needed | ||
| 465 | self.info.regs.oar2().write(|reg| { | ||
| 466 | reg.set_endual(i2c::vals::Endual::SINGLE); | ||
| 467 | }); | ||
| 468 | }, | ||
| 469 | OwnAddresses::OA2(oa2) => { | ||
| 470 | // v1 limitation: ignore mask, only support simple OA2 | ||
| 471 | if !matches!(oa2.mask, AddrMask::NOMASK) { | ||
| 472 | // Could log a warning here that masking is ignored in v1 | ||
| 473 | #[cfg(feature = "defmt")] | ||
| 474 | defmt::warn!("I2C v1 does not support OA2 address masking, ignoring mask setting"); | ||
| 475 | } | ||
| 476 | |||
| 477 | // Must have a default OA1 when using OA2-only mode | ||
| 478 | // Set OA1 to a reserved address that won't conflict | ||
| 479 | self.info.regs.oar1().write(|reg| { | ||
| 480 | reg.set_add(0); // Address 0x00 is reserved, safe to use | ||
| 481 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 482 | }); | ||
| 483 | |||
| 484 | self.configure_oa2_simple(oa2.addr); | ||
| 485 | }, | ||
| 486 | OwnAddresses::Both { oa1, oa2 } => { | ||
| 487 | self.configure_oa1(oa1); | ||
| 488 | |||
| 489 | // Same masking limitation applies | ||
| 490 | if !matches!(oa2.mask, AddrMask::NOMASK) { | ||
| 491 | #[cfg(feature = "defmt")] | ||
| 492 | defmt::warn!("I2C v1 does not support OA2 address masking, ignoring mask setting"); | ||
| 493 | } | ||
| 494 | |||
| 495 | self.configure_oa2_simple(oa2.addr); | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | // Configure general call if requested | ||
| 500 | if config.general_call { | ||
| 501 | self.info.regs.cr1().modify(|w| w.set_engc(true)); | ||
| 502 | } | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | ||
| 507 | /// Listen for incoming I2C address match and return the command type | ||
| 508 | pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 509 | let timeout = self.timeout(); // Get timeout internally | ||
| 510 | self.blocking_listen_timeout(timeout) | ||
| 511 | } | ||
| 512 | |||
| 513 | /// Respond to master read request (master wants to read from us) | ||
| 514 | pub fn blocking_respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 515 | let timeout = self.timeout(); // Get timeout internally | ||
| 516 | self.blocking_respond_to_read_timeout(data, timeout) | ||
| 517 | } | ||
| 518 | |||
| 519 | /// Respond to master write request (master wants to write to us) | ||
| 520 | pub fn blocking_respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 521 | let timeout = self.timeout(); // Get timeout internally | ||
| 522 | self.blocking_respond_to_write_timeout(buffer, timeout) | ||
| 523 | } | ||
| 524 | |||
| 525 | // Private implementation methods with Timeout parameter | ||
| 526 | fn blocking_listen_timeout(&mut self, timeout: Timeout) -> Result<SlaveCommand, Error> { | ||
| 527 | // Enable address match interrupt for slave mode | ||
| 528 | self.info.regs.cr2().modify(|w| { | ||
| 529 | w.set_itevten(true); // Enable event interrupts | ||
| 530 | }); | ||
| 531 | |||
| 532 | // Wait for address match (ADDR flag) | ||
| 533 | loop { | ||
| 534 | let sr1 = Self::check_and_clear_error_flags(self.info)?; | ||
| 535 | |||
| 536 | if sr1.addr() { | ||
| 537 | // Address matched! Read SR2 to get direction and clear ADDR | ||
| 538 | let sr2 = self.info.regs.sr2().read(); | ||
| 539 | let direction = if sr2.tra() { | ||
| 540 | SlaveCommandKind::Read // Master wants to read from us (we transmit) | ||
| 541 | } else { | ||
| 542 | SlaveCommandKind::Write // Master wants to write to us (we receive) | ||
| 543 | }; | ||
| 544 | |||
| 545 | // Determine which address was matched | ||
| 546 | let matched_address = self.determine_matched_address(sr2)?; | ||
| 547 | |||
| 548 | // ADDR is automatically cleared by reading SR1 then SR2 | ||
| 549 | return Ok(SlaveCommand { | ||
| 550 | kind: direction, | ||
| 551 | address: matched_address, | ||
| 552 | }); | ||
| 553 | } | ||
| 554 | |||
| 555 | timeout.check()?; | ||
| 556 | } | ||
| 557 | } | ||
| 558 | |||
| 559 | fn blocking_respond_to_read_timeout(&mut self, data: &[u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 560 | let mut bytes_sent = 0; | ||
| 561 | |||
| 562 | for &byte in data { | ||
| 563 | match self.send_byte_or_nack(byte, timeout)? { | ||
| 564 | SlaveSendResult::Acked => { | ||
| 565 | bytes_sent += 1; | ||
| 566 | // Continue sending | ||
| 567 | }, | ||
| 568 | SlaveSendResult::Nacked | SlaveSendResult::Stopped => { | ||
| 569 | // Master finished reading or sent STOP | ||
| 570 | break; | ||
| 571 | } | ||
| 572 | SlaveSendResult::Restart => { | ||
| 573 | // Master wants to change direction (rare but possible) | ||
| 574 | break; | ||
| 575 | } | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | Ok(bytes_sent) | ||
| 580 | } | ||
| 581 | |||
| 582 | fn blocking_respond_to_write_timeout(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 583 | let mut bytes_received = 0; | ||
| 584 | while bytes_received < buffer.len() { | ||
| 585 | match self.recv_byte_or_stop(timeout)? { | ||
| 586 | SlaveReceiveResult::Byte(b) => { | ||
| 587 | buffer[bytes_received] = b; | ||
| 588 | bytes_received += 1; | ||
| 589 | }, | ||
| 590 | SlaveReceiveResult::Stop => break, | ||
| 591 | SlaveReceiveResult::Restart => break, | ||
| 592 | } | ||
| 593 | } | ||
| 594 | Ok(bytes_received) | ||
| 595 | } | ||
| 596 | |||
| 597 | fn determine_matched_address(&self, sr2: stm32_metapac::i2c::regs::Sr2) -> Result<Address, Error> { | ||
| 598 | // Check for general call first | ||
| 599 | if sr2.gencall() { | ||
| 600 | Ok(Address::SevenBit(0x00)) | ||
| 601 | } else if sr2.dualf() { | ||
| 602 | // OA2 was matched - verify it's actually enabled | ||
| 603 | let oar2 = self.info.regs.oar2().read(); | ||
| 604 | if oar2.endual() != i2c::vals::Endual::DUAL { | ||
| 605 | return Err(Error::Bus); // Hardware inconsistency | ||
| 606 | } | ||
| 607 | Ok(Address::SevenBit(oar2.add2())) | ||
| 608 | } else { | ||
| 609 | // OA1 was matched | ||
| 610 | let oar1 = self.info.regs.oar1().read(); | ||
| 611 | match oar1.addmode() { | ||
| 612 | i2c::vals::Addmode::BIT7 => { | ||
| 613 | Ok(Address::SevenBit((oar1.add() >> 1) as u8)) | ||
| 614 | }, | ||
| 615 | i2c::vals::Addmode::BIT10 => { | ||
| 616 | Ok(Address::TenBit(oar1.add())) | ||
| 617 | }, | ||
| 618 | } | ||
| 619 | } | ||
| 620 | } | ||
| 621 | |||
| 622 | /// Send a byte in slave transmitter mode and check for ACK/NACK/STOP | ||
| 623 | fn send_byte_or_nack(&mut self, byte: u8, timeout: Timeout) -> Result<SlaveSendResult, Error> { | ||
| 624 | // Wait until we're ready for sending (TXE flag set) | ||
| 625 | loop { | ||
| 626 | let sr1 = Self::check_and_clear_error_flags(self.info)?; | ||
| 627 | |||
| 628 | // Check for STOP condition first | ||
| 629 | if sr1.stopf() { | ||
| 630 | self.info.regs.cr1().modify(|_w| {}); | ||
| 631 | return Ok(SlaveSendResult::Stopped); | ||
| 632 | } | ||
| 633 | |||
| 634 | // Check for RESTART (new ADDR) | ||
| 635 | if sr1.addr() { | ||
| 636 | // Don't clear ADDR here - let next blocking_listen() handle it | ||
| 637 | return Ok(SlaveSendResult::Restart); | ||
| 638 | } | ||
| 639 | |||
| 640 | // Check for NACK (AF flag) | ||
| 641 | if sr1.af() { | ||
| 642 | self.info.regs.sr1().modify(|w| w.set_af(false)); | ||
| 643 | return Ok(SlaveSendResult::Nacked); | ||
| 644 | } | ||
| 645 | |||
| 646 | // Check if we can send data | ||
| 647 | if sr1.txe() { | ||
| 648 | break; // Ready to send | ||
| 649 | } | ||
| 650 | |||
| 651 | timeout.check()?; | ||
| 652 | } | ||
| 653 | |||
| 654 | // Send the byte | ||
| 655 | self.info.regs.dr().write(|w| w.set_dr(byte)); | ||
| 656 | |||
| 657 | // Wait for byte transfer to complete (BTF flag or error) | ||
| 658 | loop { | ||
| 659 | let sr1 = Self::check_and_clear_error_flags(self.info)?; | ||
| 660 | |||
| 661 | // Check for STOP condition | ||
| 662 | if sr1.stopf() { | ||
| 663 | self.info.regs.cr1().modify(|_w| {}); | ||
| 664 | return Ok(SlaveSendResult::Stopped); | ||
| 665 | } | ||
| 666 | |||
| 667 | // Check for RESTART (new ADDR) | ||
| 668 | if sr1.addr() { | ||
| 669 | return Ok(SlaveSendResult::Restart); | ||
| 670 | } | ||
| 671 | |||
| 672 | // Check for NACK (AF flag) | ||
| 673 | if sr1.af() { | ||
| 674 | self.info.regs.sr1().modify(|w| w.set_af(false)); | ||
| 675 | return Ok(SlaveSendResult::Nacked); | ||
| 676 | } | ||
| 677 | |||
| 678 | // Check for byte transfer finished | ||
| 679 | if sr1.btf() { | ||
| 680 | return Ok(SlaveSendResult::Acked); | ||
| 681 | } | ||
| 682 | |||
| 683 | timeout.check()?; | ||
| 684 | } | ||
| 685 | } | ||
| 686 | |||
| 687 | /// Receive a byte in slave receiver mode or detect STOP condition | ||
| 688 | fn recv_byte_or_stop(&mut self, timeout: Timeout) -> Result<SlaveReceiveResult, Error> { | ||
| 689 | loop { | ||
| 690 | let sr1 = Self::check_and_clear_error_flags(self.info)?; | ||
| 691 | |||
| 692 | // Check for STOP condition first | ||
| 693 | if sr1.stopf() { | ||
| 694 | self.info.regs.cr1().modify(|_w| {}); | ||
| 695 | return Ok(SlaveReceiveResult::Stop); | ||
| 696 | } | ||
| 697 | |||
| 698 | // Check for RESTART (new ADDR) | ||
| 699 | if sr1.addr() { | ||
| 700 | // Don't clear ADDR here - let next blocking_listen() handle it | ||
| 701 | return Ok(SlaveReceiveResult::Restart); | ||
| 702 | } | ||
| 703 | |||
| 704 | if sr1.rxne() { | ||
| 705 | let byte = self.info.regs.dr().read().dr(); | ||
| 706 | return Ok(SlaveReceiveResult::Byte(byte)); | ||
| 707 | } | ||
| 708 | |||
| 709 | timeout.check()?; | ||
| 710 | } | ||
| 711 | } | ||
| 712 | } | ||
| 713 | |||
| 373 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | 714 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { |
| 374 | async fn write_with_framing(&mut self, address: u8, write_buffer: &[u8], framing: OperationFraming) -> Result<(), Error> { | 715 | async fn write_with_framing(&mut self, address: u8, write_buffer: &[u8], framing: OperationFraming) -> Result<(), Error> { |
| 375 | self.info.regs.cr2().modify(|w| { | 716 | self.info.regs.cr2().modify(|w| { |
