From f8ebc967a93abb70ea882225f38814a7f2f45a4d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 20 Oct 2021 14:40:16 +0200 Subject: [PATCH] Add implementation of async trait for STM32 I2C v2 * Add DMA read implementation for I2C v2 * Add example using DMA for I2C --- embassy-stm32/src/i2c/v2.rs | 169 ++++++++++++++++++++++++---- examples/stm32l4/src/bin/i2c_dma.rs | 35 ++++++ 2 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 examples/stm32l4/src/bin/i2c_dma.rs 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 @@ use core::cmp; +use core::future::Future; use core::marker::PhantomData; use core::task::Poll; use atomic_polyfill::{AtomicUsize, Ordering}; use embassy::interrupt::InterruptExt; +use embassy::traits::i2c::I2c as I2cTrait; use embassy::util::Unborrow; use embassy::waitqueue::AtomicWaker; use embassy_hal_common::drop::OnDrop; @@ -139,14 +141,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - fn master_read(&mut self, address: u8, length: usize, stop: Stop, reload: bool, restart: bool) { + unsafe fn master_read(address: u8, length: usize, stop: Stop, reload: bool, restart: bool) { assert!(length < 256 && length > 0); if !restart { // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while unsafe { T::regs().cr2().read().start() == i2c::vals::Start::START } {} + while T::regs().cr2().read().start() == i2c::vals::Start::START {} } // Set START and prepare to receive bytes into @@ -159,17 +161,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { i2c::vals::Reload::COMPLETED }; - unsafe { - T::regs().cr2().modify(|w| { - w.set_sadd((address << 1 | 0) as u16); - w.set_add10(i2c::vals::Add::BIT7); - w.set_rd_wrn(i2c::vals::RdWrn::READ); - w.set_nbytes(length as u8); - w.set_start(i2c::vals::Start::START); - w.set_autoend(stop.autoend()); - w.set_reload(reload); - }); - } + T::regs().cr2().modify(|w| { + w.set_sadd((address << 1 | 0) as u16); + w.set_add10(i2c::vals::Add::BIT7); + w.set_rd_wrn(i2c::vals::RdWrn::READ); + w.set_nbytes(length as u8); + w.set_start(i2c::vals::Start::START); + w.set_autoend(stop.autoend()); + w.set_reload(reload); + }); } 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> { }; let last_chunk_idx = total_chunks.saturating_sub(1); - self.master_read( - address, - buffer.len().min(255), - Stop::Automatic, - last_chunk_idx != 0, - restart, - ); + unsafe { + Self::master_read( + address, + buffer.len().min(255), + Stop::Automatic, + last_chunk_idx != 0, + restart, + ); + } for (number, chunk) in buffer.chunks_mut(255).enumerate() { if number != 0 { @@ -482,6 +484,88 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } + async fn read_dma_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + ) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + let total_len = buffer.len(); + let completed_chunks = total_len / 255; + let total_chunks = if completed_chunks * 255 == total_len { + completed_chunks + } else { + completed_chunks + 1 + }; + + let dma_transfer = unsafe { + let regs = T::regs(); + regs.cr1().modify(|w| { + w.set_rxdmaen(true); + w.set_tcie(true); + }); + let src = regs.rxdr().ptr() as *mut u8; + + let ch = &mut self.rx_dma; + ch.read(ch.request(), src, buffer) + }; + + let state_number = T::state_number(); + STATE.chunks_transferred[state_number].store(0, Ordering::Relaxed); + let mut remaining_len = total_len; + + let _on_drop = OnDrop::new(|| { + let regs = T::regs(); + unsafe { + regs.cr1().modify(|w| { + w.set_rxdmaen(false); + w.set_tcie(false); + }) + } + }); + + // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers + unsafe { + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_chunks != 1, + restart, + ); + } + + poll_fn(|cx| { + STATE.waker[state_number].register(cx.waker()); + let chunks_transferred = STATE.chunks_transferred[state_number].load(Ordering::Relaxed); + + if chunks_transferred == total_chunks { + return Poll::Ready(()); + } else if chunks_transferred != 0 { + remaining_len = remaining_len.saturating_sub(255); + let last_piece = chunks_transferred + 1 == total_chunks; + + // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers + unsafe { + Self::master_continue(remaining_len.min(255), !last_piece); + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + } + Poll::Pending + }) + .await; + + dma_transfer.await; + + // This should be done already + self.wait_tc()?; + self.master_stop(); + Ok(()) + } + pub async fn write_dma(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -511,6 +595,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } + pub async fn read_dma( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + ) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.read_dma_internal(address, buffer, restart).await + } + pub fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { if bytes.is_empty() { return Err(Error::ZeroLengthTransfer); @@ -754,3 +850,36 @@ impl Timings { } } } + +impl<'d, T: Instance, TXDMA: super::TxDma, RXDMA: super::RxDma> I2cTrait + for I2c<'d, T, TXDMA, RXDMA> +{ + type Error = super::Error; + + #[rustfmt::skip] + type WriteFuture<'a> where 'd: 'a, T: 'a, TXDMA: 'a, RXDMA: 'a = impl Future> + 'a; + #[rustfmt::skip] + type ReadFuture<'a> where 'd: 'a, T: 'a, TXDMA: 'a, RXDMA: 'a = impl Future> + 'a; + #[rustfmt::skip] + type WriteReadFuture<'a> where 'd: 'a, T: 'a, TXDMA: 'a, RXDMA: 'a = impl Future> + 'a; + + fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read_dma(address, buffer, false) + } + + fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { + self.write_dma(address, bytes) + } + + fn write_read<'a>( + &'a mut self, + address: u8, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Self::WriteReadFuture<'a> { + async move { + self.write_dma(address, bytes).await?; + self.read_dma(address, buffer, true).await + } + } +} 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 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; + +use embassy::executor::Spawner; +use embassy::traits::i2c::I2c as I2cTrait; +use embassy_stm32::i2c::I2c; +use embassy_stm32::interrupt; +use embassy_stm32::time::Hertz; +use embassy_stm32::Peripherals; +use example_common::{info, unwrap}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) -> ! { + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + p.DMA1_CH4, + p.DMA1_CH5, + Hertz(100_000), + ); + + let mut data = [0u8; 1]; + unwrap!(i2c.write_read(ADDRESS, &[WHOAMI], &mut data).await); + info!("Whoami: {}", data[0]); +}