Merge #441
441: Add implementation of async trait for STM32 I2C v2 r=Dirbaio a=lulf * Add DMA read implementation for I2C v2 * Add example using DMA for I2C Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
This commit is contained in:
commit
f8bd9d2b1c
@ -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,7 +161,6 @@ 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);
|
||||
@ -170,7 +171,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
w.set_reload(reload);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) {
|
||||
assert!(length < 256 && length > 0);
|
||||
@ -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(
|
||||
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<T>,
|
||||
{
|
||||
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<T>,
|
||||
@ -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<T>,
|
||||
{
|
||||
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<T>, RXDMA: super::RxDma<T>> I2cTrait<u8>
|
||||
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<Output = Result<(), Self::Error>> + 'a;
|
||||
#[rustfmt::skip]
|
||||
type ReadFuture<'a> where 'd: 'a, T: 'a, TXDMA: 'a, RXDMA: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||
#[rustfmt::skip]
|
||||
type WriteReadFuture<'a> where 'd: 'a, T: 'a, TXDMA: 'a, RXDMA: 'a = impl Future<Output = Result<(), Self::Error>> + '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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
35
examples/stm32l4/src/bin/i2c_dma.rs
Normal file
35
examples/stm32l4/src/bin/i2c_dma.rs
Normal file
@ -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]);
|
||||
}
|
Loading…
Reference in New Issue
Block a user