diff options
| -rw-r--r-- | embassy-stm32/src/i2c/v2.rs | 169 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/i2c_dma.rs | 35 |
2 files changed, 184 insertions, 20 deletions
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 5e9e24392..11c8ab975 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | use core::cmp; | 1 | use core::cmp; |
| 2 | use core::future::Future; | ||
| 2 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 3 | use core::task::Poll; | 4 | use core::task::Poll; |
| 4 | 5 | ||
| 5 | use atomic_polyfill::{AtomicUsize, Ordering}; | 6 | use atomic_polyfill::{AtomicUsize, Ordering}; |
| 6 | use embassy::interrupt::InterruptExt; | 7 | use embassy::interrupt::InterruptExt; |
| 8 | use embassy::traits::i2c::I2c as I2cTrait; | ||
| 7 | use embassy::util::Unborrow; | 9 | use embassy::util::Unborrow; |
| 8 | use embassy::waitqueue::AtomicWaker; | 10 | use embassy::waitqueue::AtomicWaker; |
| 9 | use embassy_hal_common::drop::OnDrop; | 11 | use embassy_hal_common::drop::OnDrop; |
| @@ -139,14 +141,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 139 | } | 141 | } |
| 140 | } | 142 | } |
| 141 | 143 | ||
| 142 | fn master_read(&mut self, address: u8, length: usize, stop: Stop, reload: bool, restart: bool) { | 144 | unsafe fn master_read(address: u8, length: usize, stop: Stop, reload: bool, restart: bool) { |
| 143 | assert!(length < 256 && length > 0); | 145 | assert!(length < 256 && length > 0); |
| 144 | 146 | ||
| 145 | if !restart { | 147 | if !restart { |
| 146 | // Wait for any previous address sequence to end | 148 | // Wait for any previous address sequence to end |
| 147 | // automatically. This could be up to 50% of a bus | 149 | // automatically. This could be up to 50% of a bus |
| 148 | // cycle (ie. up to 0.5/freq) | 150 | // cycle (ie. up to 0.5/freq) |
| 149 | while unsafe { T::regs().cr2().read().start() == i2c::vals::Start::START } {} | 151 | while T::regs().cr2().read().start() == i2c::vals::Start::START {} |
| 150 | } | 152 | } |
| 151 | 153 | ||
| 152 | // Set START and prepare to receive bytes into | 154 | // Set START and prepare to receive bytes into |
| @@ -159,17 +161,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 159 | i2c::vals::Reload::COMPLETED | 161 | i2c::vals::Reload::COMPLETED |
| 160 | }; | 162 | }; |
| 161 | 163 | ||
| 162 | unsafe { | 164 | T::regs().cr2().modify(|w| { |
| 163 | T::regs().cr2().modify(|w| { | 165 | w.set_sadd((address << 1 | 0) as u16); |
| 164 | w.set_sadd((address << 1 | 0) as u16); | 166 | w.set_add10(i2c::vals::Add::BIT7); |
| 165 | w.set_add10(i2c::vals::Add::BIT7); | 167 | w.set_rd_wrn(i2c::vals::RdWrn::READ); |
| 166 | w.set_rd_wrn(i2c::vals::RdWrn::READ); | 168 | w.set_nbytes(length as u8); |
| 167 | w.set_nbytes(length as u8); | 169 | w.set_start(i2c::vals::Start::START); |
| 168 | w.set_start(i2c::vals::Start::START); | 170 | w.set_autoend(stop.autoend()); |
| 169 | w.set_autoend(stop.autoend()); | 171 | w.set_reload(reload); |
| 170 | w.set_reload(reload); | 172 | }); |
| 171 | }); | ||
| 172 | } | ||
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) { | 175 | unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) { |
| @@ -309,13 +309,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 309 | }; | 309 | }; |
| 310 | let last_chunk_idx = total_chunks.saturating_sub(1); | 310 | let last_chunk_idx = total_chunks.saturating_sub(1); |
| 311 | 311 | ||
| 312 | self.master_read( | 312 | unsafe { |
| 313 | address, | 313 | Self::master_read( |
| 314 | buffer.len().min(255), | 314 | address, |
| 315 | Stop::Automatic, | 315 | buffer.len().min(255), |
| 316 | last_chunk_idx != 0, | 316 | Stop::Automatic, |
| 317 | restart, | 317 | last_chunk_idx != 0, |
| 318 | ); | 318 | restart, |
| 319 | ); | ||
| 320 | } | ||
| 319 | 321 | ||
| 320 | for (number, chunk) in buffer.chunks_mut(255).enumerate() { | 322 | for (number, chunk) in buffer.chunks_mut(255).enumerate() { |
| 321 | if number != 0 { | 323 | if number != 0 { |
| @@ -482,6 +484,88 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 482 | Ok(()) | 484 | Ok(()) |
| 483 | } | 485 | } |
| 484 | 486 | ||
| 487 | async fn read_dma_internal( | ||
| 488 | &mut self, | ||
| 489 | address: u8, | ||
| 490 | buffer: &mut [u8], | ||
| 491 | restart: bool, | ||
| 492 | ) -> Result<(), Error> | ||
| 493 | where | ||
| 494 | RXDMA: crate::i2c::RxDma<T>, | ||
| 495 | { | ||
| 496 | let total_len = buffer.len(); | ||
| 497 | let completed_chunks = total_len / 255; | ||
| 498 | let total_chunks = if completed_chunks * 255 == total_len { | ||
| 499 | completed_chunks | ||
| 500 | } else { | ||
| 501 | completed_chunks + 1 | ||
| 502 | }; | ||
| 503 | |||
| 504 | let dma_transfer = unsafe { | ||
| 505 | let regs = T::regs(); | ||
| 506 | regs.cr1().modify(|w| { | ||
| 507 | w.set_rxdmaen(true); | ||
| 508 | w.set_tcie(true); | ||
| 509 | }); | ||
| 510 | let src = regs.rxdr().ptr() as *mut u8; | ||
| 511 | |||
| 512 | let ch = &mut self.rx_dma; | ||
| 513 | ch.read(ch.request(), src, buffer) | ||
| 514 | }; | ||
| 515 | |||
| 516 | let state_number = T::state_number(); | ||
| 517 | STATE.chunks_transferred[state_number].store(0, Ordering::Relaxed); | ||
| 518 | let mut remaining_len = total_len; | ||
| 519 | |||
| 520 | let _on_drop = OnDrop::new(|| { | ||
| 521 | let regs = T::regs(); | ||
| 522 | unsafe { | ||
| 523 | regs.cr1().modify(|w| { | ||
| 524 | w.set_rxdmaen(false); | ||
| 525 | w.set_tcie(false); | ||
| 526 | }) | ||
| 527 | } | ||
| 528 | }); | ||
| 529 | |||
| 530 | // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers | ||
| 531 | unsafe { | ||
| 532 | Self::master_read( | ||
| 533 | address, | ||
| 534 | total_len.min(255), | ||
| 535 | Stop::Software, | ||
| 536 | total_chunks != 1, | ||
| 537 | restart, | ||
| 538 | ); | ||
| 539 | } | ||
| 540 | |||
| 541 | poll_fn(|cx| { | ||
| 542 | STATE.waker[state_number].register(cx.waker()); | ||
| 543 | let chunks_transferred = STATE.chunks_transferred[state_number].load(Ordering::Relaxed); | ||
| 544 | |||
| 545 | if chunks_transferred == total_chunks { | ||
| 546 | return Poll::Ready(()); | ||
| 547 | } else if chunks_transferred != 0 { | ||
| 548 | remaining_len = remaining_len.saturating_sub(255); | ||
| 549 | let last_piece = chunks_transferred + 1 == total_chunks; | ||
| 550 | |||
| 551 | // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers | ||
| 552 | unsafe { | ||
| 553 | Self::master_continue(remaining_len.min(255), !last_piece); | ||
| 554 | T::regs().cr1().modify(|w| w.set_tcie(true)); | ||
| 555 | } | ||
| 556 | } | ||
| 557 | Poll::Pending | ||
| 558 | }) | ||
| 559 | .await; | ||
| 560 | |||
| 561 | dma_transfer.await; | ||
| 562 | |||
| 563 | // This should be done already | ||
| 564 | self.wait_tc()?; | ||
| 565 | self.master_stop(); | ||
| 566 | Ok(()) | ||
| 567 | } | ||
| 568 | |||
| 485 | pub async fn write_dma(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> | 569 | pub async fn write_dma(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> |
| 486 | where | 570 | where |
| 487 | TXDMA: crate::i2c::TxDma<T>, | 571 | TXDMA: crate::i2c::TxDma<T>, |
| @@ -511,6 +595,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 511 | Ok(()) | 595 | Ok(()) |
| 512 | } | 596 | } |
| 513 | 597 | ||
| 598 | pub async fn read_dma( | ||
| 599 | &mut self, | ||
| 600 | address: u8, | ||
| 601 | buffer: &mut [u8], | ||
| 602 | restart: bool, | ||
| 603 | ) -> Result<(), Error> | ||
| 604 | where | ||
| 605 | RXDMA: crate::i2c::RxDma<T>, | ||
| 606 | { | ||
| 607 | self.read_dma_internal(address, buffer, restart).await | ||
| 608 | } | ||
| 609 | |||
| 514 | pub fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { | 610 | pub fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { |
| 515 | if bytes.is_empty() { | 611 | if bytes.is_empty() { |
| 516 | return Err(Error::ZeroLengthTransfer); | 612 | return Err(Error::ZeroLengthTransfer); |
| @@ -754,3 +850,36 @@ impl Timings { | |||
| 754 | } | 850 | } |
| 755 | } | 851 | } |
| 756 | } | 852 | } |
| 853 | |||
| 854 | impl<'d, T: Instance, TXDMA: super::TxDma<T>, RXDMA: super::RxDma<T>> I2cTrait<u8> | ||
| 855 | for I2c<'d, T, TXDMA, RXDMA> | ||
| 856 | { | ||
| 857 | type Error = super::Error; | ||
| 858 | |||
| 859 | #[rustfmt::skip] | ||
| 860 | type WriteFuture<'a> where 'd: 'a, T: 'a, TXDMA: 'a, RXDMA: 'a = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 861 | #[rustfmt::skip] | ||
| 862 | type ReadFuture<'a> where 'd: 'a, T: 'a, TXDMA: 'a, RXDMA: 'a = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 863 | #[rustfmt::skip] | ||
| 864 | type WriteReadFuture<'a> where 'd: 'a, T: 'a, TXDMA: 'a, RXDMA: 'a = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 865 | |||
| 866 | fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 867 | self.read_dma(address, buffer, false) | ||
| 868 | } | ||
| 869 | |||
| 870 | fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 871 | self.write_dma(address, bytes) | ||
| 872 | } | ||
| 873 | |||
| 874 | fn write_read<'a>( | ||
| 875 | &'a mut self, | ||
| 876 | address: u8, | ||
| 877 | bytes: &'a [u8], | ||
| 878 | buffer: &'a mut [u8], | ||
| 879 | ) -> Self::WriteReadFuture<'a> { | ||
| 880 | async move { | ||
| 881 | self.write_dma(address, bytes).await?; | ||
| 882 | self.read_dma(address, buffer, true).await | ||
| 883 | } | ||
| 884 | } | ||
| 885 | } | ||
diff --git a/examples/stm32l4/src/bin/i2c_dma.rs b/examples/stm32l4/src/bin/i2c_dma.rs new file mode 100644 index 000000000..b0596aab8 --- /dev/null +++ b/examples/stm32l4/src/bin/i2c_dma.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | #[path = "../example_common.rs"] | ||
| 6 | mod example_common; | ||
| 7 | |||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy::traits::i2c::I2c as I2cTrait; | ||
| 10 | use embassy_stm32::i2c::I2c; | ||
| 11 | use embassy_stm32::interrupt; | ||
| 12 | use embassy_stm32::time::Hertz; | ||
| 13 | use embassy_stm32::Peripherals; | ||
| 14 | use example_common::{info, unwrap}; | ||
| 15 | |||
| 16 | const ADDRESS: u8 = 0x5F; | ||
| 17 | const WHOAMI: u8 = 0x0F; | ||
| 18 | |||
| 19 | #[embassy::main] | ||
| 20 | async fn main(_spawner: Spawner, p: Peripherals) -> ! { | ||
| 21 | let irq = interrupt::take!(I2C2_EV); | ||
| 22 | let mut i2c = I2c::new( | ||
| 23 | p.I2C2, | ||
| 24 | p.PB10, | ||
| 25 | p.PB11, | ||
| 26 | irq, | ||
| 27 | p.DMA1_CH4, | ||
| 28 | p.DMA1_CH5, | ||
| 29 | Hertz(100_000), | ||
| 30 | ); | ||
| 31 | |||
| 32 | let mut data = [0u8; 1]; | ||
| 33 | unwrap!(i2c.write_read(ADDRESS, &[WHOAMI], &mut data).await); | ||
| 34 | info!("Whoami: {}", data[0]); | ||
| 35 | } | ||
