diff options
| author | Sebastian Goll <[email protected]> | 2024-03-27 01:07:42 +0100 |
|---|---|---|
| committer | Sebastian Goll <[email protected]> | 2024-03-27 01:07:42 +0100 |
| commit | 54d7d495135a2d4a17457d7fdb5d9306f598acc2 (patch) | |
| tree | 82686ed0f4b5f6b0d42d6e5f08e55911674035e6 | |
| parent | 7e44db099ca6a475af9f22fd0ebb85845553a570 (diff) | |
Refactor DMA implementation of I2C v1, clarify flow of code
| -rw-r--r-- | embassy-stm32/src/i2c/v1.rs | 131 |
1 files changed, 73 insertions, 58 deletions
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index be7f91a9a..5c57a9ccc 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -142,7 +142,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 142 | return Err(Error::Arbitration); | 142 | return Err(Error::Arbitration); |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | // Set up current address, we're trying to talk to | 145 | // Set up current address we're trying to talk to |
| 146 | T::regs().dr().write(|reg| reg.set_dr(addr << 1)); | 146 | T::regs().dr().write(|reg| reg.set_dr(addr << 1)); |
| 147 | 147 | ||
| 148 | // Wait until address was sent | 148 | // Wait until address was sent |
| @@ -235,7 +235,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 235 | return Err(Error::Arbitration); | 235 | return Err(Error::Arbitration); |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | // Set up current address, we're trying to talk to | 238 | // Set up current address we're trying to talk to |
| 239 | T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)); | 239 | T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)); |
| 240 | 240 | ||
| 241 | // Wait until address was sent | 241 | // Wait until address was sent |
| @@ -331,27 +331,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 331 | where | 331 | where |
| 332 | TXDMA: crate::i2c::TxDma<T>, | 332 | TXDMA: crate::i2c::TxDma<T>, |
| 333 | { | 333 | { |
| 334 | let dma_transfer = unsafe { | 334 | T::regs().cr2().modify(|w| { |
| 335 | let regs = T::regs(); | 335 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| 336 | regs.cr2().modify(|w| { | 336 | // reception. |
| 337 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for reception. | 337 | w.set_itbufen(false); |
| 338 | w.set_itbufen(false); | 338 | // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 |
| 339 | // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. | 339 | // register. |
| 340 | w.set_dmaen(true); | 340 | w.set_dmaen(true); |
| 341 | // Sending NACK is not necessary (nor possible) for write transfer. | 341 | // Sending NACK is not necessary (nor possible) for write transfer. |
| 342 | w.set_last(false); | 342 | w.set_last(false); |
| 343 | }); | 343 | }); |
| 344 | // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. | ||
| 345 | let dst = regs.dr().as_ptr() as *mut u8; | ||
| 346 | |||
| 347 | let ch = &mut self.tx_dma; | ||
| 348 | let request = ch.request(); | ||
| 349 | Transfer::new_write(ch, request, write, dst, Default::default()) | ||
| 350 | }; | ||
| 351 | 344 | ||
| 345 | // Sentinel to disable transfer when an error occurs or future is canceled. | ||
| 346 | // TODO: Generate STOP condition on cancel? | ||
| 352 | let on_drop = OnDrop::new(|| { | 347 | let on_drop = OnDrop::new(|| { |
| 353 | let regs = T::regs(); | 348 | T::regs().cr2().modify(|w| { |
| 354 | regs.cr2().modify(|w| { | ||
| 355 | w.set_dmaen(false); | 349 | w.set_dmaen(false); |
| 356 | w.set_iterren(false); | 350 | w.set_iterren(false); |
| 357 | w.set_itevten(false); | 351 | w.set_itevten(false); |
| @@ -390,7 +384,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 390 | return Err(Error::Arbitration); | 384 | return Err(Error::Arbitration); |
| 391 | } | 385 | } |
| 392 | 386 | ||
| 393 | // Set up current address, we're trying to talk to | 387 | // Set up current address we're trying to talk to |
| 394 | T::regs().dr().write(|reg| reg.set_dr(address << 1)); | 388 | T::regs().dr().write(|reg| reg.set_dr(address << 1)); |
| 395 | 389 | ||
| 396 | // Wait for the address to be acknowledged | 390 | // Wait for the address to be acknowledged |
| @@ -416,14 +410,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 416 | T::regs().sr2().read(); | 410 | T::regs().sr2().read(); |
| 417 | } | 411 | } |
| 418 | 412 | ||
| 413 | let dma_transfer = unsafe { | ||
| 414 | // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to | ||
| 415 | // this address from the memory after each TxE event. | ||
| 416 | let dst = T::regs().dr().as_ptr() as *mut u8; | ||
| 417 | |||
| 418 | let ch = &mut self.tx_dma; | ||
| 419 | let request = ch.request(); | ||
| 420 | Transfer::new_write(ch, request, write, dst, Default::default()) | ||
| 421 | }; | ||
| 422 | |||
| 419 | // Wait for bytes to be sent, or an error to occur. | 423 | // Wait for bytes to be sent, or an error to occur. |
| 420 | let poll_error = poll_fn(|cx| { | 424 | let poll_error = poll_fn(|cx| { |
| 421 | state.waker.register(cx.waker()); | 425 | state.waker.register(cx.waker()); |
| 422 | 426 | ||
| 423 | match Self::check_and_clear_error_flags() { | 427 | match Self::check_and_clear_error_flags() { |
| 424 | // Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other | 428 | Err(e) => Poll::Ready(Err::<(), Error>(e)), |
| 425 | // identical poll_fn check_and_clear matches. | ||
| 426 | Err(e) => Poll::Ready(Err::<T, Error>(e)), | ||
| 427 | Ok(_) => { | 429 | Ok(_) => { |
| 428 | // When pending, (re-)enable interrupts to wake us up. | 430 | // When pending, (re-)enable interrupts to wake us up. |
| 429 | Self::enable_interrupts(); | 431 | Self::enable_interrupts(); |
| @@ -502,31 +504,30 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 502 | where | 504 | where |
| 503 | RXDMA: crate::i2c::RxDma<T>, | 505 | RXDMA: crate::i2c::RxDma<T>, |
| 504 | { | 506 | { |
| 505 | let buffer_len = buffer.len(); | 507 | if buffer.is_empty() { |
| 508 | return Err(Error::Overrun); | ||
| 509 | } | ||
| 506 | 510 | ||
| 507 | let dma_transfer = unsafe { | 511 | // Some branches below depend on whether the buffer contains only a single byte. |
| 508 | let regs = T::regs(); | 512 | let single_byte = buffer.len() == 1; |
| 509 | regs.cr2().modify(|w| { | ||
| 510 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for reception. | ||
| 511 | w.set_itbufen(false); | ||
| 512 | // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. | ||
| 513 | w.set_dmaen(true); | ||
| 514 | // If, in the I2C_CR2 register, the LAST bit is set, I2C | ||
| 515 | // automatically sends a NACK after the next byte following EOT_1. The user can | ||
| 516 | // generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled. | ||
| 517 | w.set_last(frame.send_nack() && buffer_len != 1); | ||
| 518 | }); | ||
| 519 | // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. | ||
| 520 | let src = regs.dr().as_ptr() as *mut u8; | ||
| 521 | 513 | ||
| 522 | let ch = &mut self.rx_dma; | 514 | T::regs().cr2().modify(|w| { |
| 523 | let request = ch.request(); | 515 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| 524 | Transfer::new_read(ch, request, src, buffer, Default::default()) | 516 | // reception. |
| 525 | }; | 517 | w.set_itbufen(false); |
| 518 | // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 | ||
| 519 | // register. | ||
| 520 | w.set_dmaen(true); | ||
| 521 | // If, in the I2C_CR2 register, the LAST bit is set, I2C automatically sends a NACK | ||
| 522 | // after the next byte following EOT_1. The user can generate a Stop condition in | ||
| 523 | // the DMA Transfer Complete interrupt routine if enabled. | ||
| 524 | w.set_last(frame.send_nack() && !single_byte); | ||
| 525 | }); | ||
| 526 | 526 | ||
| 527 | // Sentinel to disable transfer when an error occurs or future is canceled. | ||
| 528 | // TODO: Generate STOP condition on cancel? | ||
| 527 | let on_drop = OnDrop::new(|| { | 529 | let on_drop = OnDrop::new(|| { |
| 528 | let regs = T::regs(); | 530 | T::regs().cr2().modify(|w| { |
| 529 | regs.cr2().modify(|w| { | ||
| 530 | w.set_dmaen(false); | 531 | w.set_dmaen(false); |
| 531 | w.set_iterren(false); | 532 | w.set_iterren(false); |
| 532 | w.set_itevten(false); | 533 | w.set_itevten(false); |
| @@ -566,7 +567,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 566 | return Err(Error::Arbitration); | 567 | return Err(Error::Arbitration); |
| 567 | } | 568 | } |
| 568 | 569 | ||
| 569 | // Set up current address, we're trying to talk to | 570 | // Set up current address we're trying to talk to |
| 570 | T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); | 571 | T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); |
| 571 | 572 | ||
| 572 | // Wait for the address to be acknowledged | 573 | // Wait for the address to be acknowledged |
| @@ -590,7 +591,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 590 | 591 | ||
| 591 | // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 | 592 | // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 |
| 592 | // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. | 593 | // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. |
| 593 | if frame.send_nack() && buffer_len == 1 { | 594 | if frame.send_nack() && single_byte { |
| 594 | T::regs().cr1().modify(|w| { | 595 | T::regs().cr1().modify(|w| { |
| 595 | w.set_ack(false); | 596 | w.set_ack(false); |
| 596 | }); | 597 | }); |
| @@ -598,27 +599,41 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 598 | 599 | ||
| 599 | // Clear condition by reading SR2 | 600 | // Clear condition by reading SR2 |
| 600 | T::regs().sr2().read(); | 601 | T::regs().sr2().read(); |
| 601 | } else if frame.send_nack() && buffer_len == 1 { | 602 | } else { |
| 602 | T::regs().cr1().modify(|w| { | 603 | // Before starting reception of single byte (but without START condition, i.e. in case |
| 603 | w.set_ack(false); | 604 | // of continued frame), program NACK to emit at end of this byte. |
| 604 | }); | 605 | if frame.send_nack() && single_byte { |
| 606 | T::regs().cr1().modify(|w| { | ||
| 607 | w.set_ack(false); | ||
| 608 | }); | ||
| 609 | } | ||
| 605 | } | 610 | } |
| 606 | 611 | ||
| 607 | // 18.3.8: When a single byte must be received: [snip] Then the | 612 | // 18.3.8: When a single byte must be received: [snip] Then the user can program the STOP |
| 608 | // user can program the STOP condition either after clearing ADDR flag, or in the | 613 | // condition either after clearing ADDR flag, or in the DMA Transfer Complete interrupt |
| 609 | // DMA Transfer Complete interrupt routine. | 614 | // routine. |
| 610 | if frame.send_stop() && buffer_len == 1 { | 615 | if frame.send_stop() && single_byte { |
| 611 | T::regs().cr1().modify(|w| { | 616 | T::regs().cr1().modify(|w| { |
| 612 | w.set_stop(true); | 617 | w.set_stop(true); |
| 613 | }); | 618 | }); |
| 614 | } | 619 | } |
| 615 | 620 | ||
| 621 | let dma_transfer = unsafe { | ||
| 622 | // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved | ||
| 623 | // from this address from the memory after each RxE event. | ||
| 624 | let src = T::regs().dr().as_ptr() as *mut u8; | ||
| 625 | |||
| 626 | let ch = &mut self.rx_dma; | ||
| 627 | let request = ch.request(); | ||
| 628 | Transfer::new_read(ch, request, src, buffer, Default::default()) | ||
| 629 | }; | ||
| 630 | |||
| 616 | // Wait for bytes to be received, or an error to occur. | 631 | // Wait for bytes to be received, or an error to occur. |
| 617 | let poll_error = poll_fn(|cx| { | 632 | let poll_error = poll_fn(|cx| { |
| 618 | state.waker.register(cx.waker()); | 633 | state.waker.register(cx.waker()); |
| 619 | 634 | ||
| 620 | match Self::check_and_clear_error_flags() { | 635 | match Self::check_and_clear_error_flags() { |
| 621 | Err(e) => Poll::Ready(Err::<T, Error>(e)), | 636 | Err(e) => Poll::Ready(Err::<(), Error>(e)), |
| 622 | _ => { | 637 | _ => { |
| 623 | // When pending, (re-)enable interrupts to wake us up. | 638 | // When pending, (re-)enable interrupts to wake us up. |
| 624 | Self::enable_interrupts(); | 639 | Self::enable_interrupts(); |
| @@ -636,7 +651,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 636 | w.set_dmaen(false); | 651 | w.set_dmaen(false); |
| 637 | }); | 652 | }); |
| 638 | 653 | ||
| 639 | if frame.send_stop() && buffer_len != 1 { | 654 | if frame.send_stop() && !single_byte { |
| 640 | T::regs().cr1().modify(|w| { | 655 | T::regs().cr1().modify(|w| { |
| 641 | w.set_stop(true); | 656 | w.set_stop(true); |
| 642 | }); | 657 | }); |
