aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/i2c/v2.rs169
-rw-r--r--examples/stm32l4/src/bin/i2c_dma.rs35
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 @@
1use core::cmp; 1use core::cmp;
2use core::future::Future;
2use core::marker::PhantomData; 3use core::marker::PhantomData;
3use core::task::Poll; 4use core::task::Poll;
4 5
5use atomic_polyfill::{AtomicUsize, Ordering}; 6use atomic_polyfill::{AtomicUsize, Ordering};
6use embassy::interrupt::InterruptExt; 7use embassy::interrupt::InterruptExt;
8use embassy::traits::i2c::I2c as I2cTrait;
7use embassy::util::Unborrow; 9use embassy::util::Unborrow;
8use embassy::waitqueue::AtomicWaker; 10use embassy::waitqueue::AtomicWaker;
9use embassy_hal_common::drop::OnDrop; 11use 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
854impl<'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"]
6mod example_common;
7
8use embassy::executor::Spawner;
9use embassy::traits::i2c::I2c as I2cTrait;
10use embassy_stm32::i2c::I2c;
11use embassy_stm32::interrupt;
12use embassy_stm32::time::Hertz;
13use embassy_stm32::Peripherals;
14use example_common::{info, unwrap};
15
16const ADDRESS: u8 = 0x5F;
17const WHOAMI: u8 = 0x0F;
18
19#[embassy::main]
20async 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}