Skip to content
Snippets Groups Projects
Commit 44c46e3c authored by Mathias's avatar Mathias Committed by Dario Nieuwenhuis
Browse files

Move async i2c implementation to new PR, to merge working blocking implementation faster

parent b0d91e9f
No related merge requests found
use core::marker::PhantomData;
use atomic_polyfill::Ordering;
use embassy_cortex_m::interrupt::InterruptExt;
use embassy_hal_common::{into_ref, PeripheralRef};
use pac::i2c;
use crate::dma::{AnyChannel, Channel};
use crate::dma::AnyChannel;
use crate::gpio::sealed::Pin;
use crate::gpio::AnyPin;
use crate::{pac, peripherals, Peripheral};
......@@ -60,159 +58,6 @@ pub struct I2c<'d, T: Instance, M: Mode> {
phantom: PhantomData<(&'d mut T, M)>,
}
impl<'d, T: Instance> I2c<'d, T, Async> {
pub fn new(
_peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
tx_dma: impl Peripheral<P = impl Channel> + 'd,
rx_dma: impl Peripheral<P = impl Channel> + 'd,
config: Config,
) -> Self {
into_ref!(scl, sda, irq, tx_dma, rx_dma);
// Enable interrupts
unsafe {
T::regs().ic_intr_mask().modify(|w| {
w.set_m_rx_done(true);
});
}
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
Self::new_inner(
_peri,
scl.map_into(),
sda.map_into(),
Some(tx_dma.map_into()),
Some(rx_dma.map_into()),
config,
)
}
unsafe fn on_interrupt(_: *mut ()) {
let status = T::regs().ic_intr_stat().read();
// FIXME:
if status.tcr() || status.tc() {
let state = T::state();
state.chunks_transferred.fetch_add(1, Ordering::Relaxed);
state.waker.wake();
}
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
// the interrupt
// critical_section::with(|_| {
// regs.cr1().modify(|w| w.set_tcie(false));
// });
}
async fn write_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> {
let len = bytes.len();
for (idx, chunk) in bytes.chunks(self.dma_buf.len()).enumerate() {
let first = idx == 0;
let last = idx * self.dma_buf.len() + chunk.len() == len;
for (i, byte) in chunk.iter().enumerate() {
let mut b = i2c::regs::IcDataCmd::default();
b.set_dat(*byte);
b.set_stop(send_stop && last);
self.dma_buf[i] = b.0 as u16;
}
// Note(safety): Unwrap should be safe, as this can only be called
// when `Mode == Async`, where we have dma channels.
let ch = self.tx_dma.as_mut().unwrap();
let transfer = unsafe {
T::regs().ic_dma_cr().modify(|w| {
w.set_tdmae(true);
});
crate::dma::write(ch, &self.dma_buf, T::regs().ic_data_cmd().ptr() as *mut _, T::TX_DREQ)
};
transfer.await;
}
Ok(())
}
async fn read_internal(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
let len = buffer.len();
self.read_blocking_internal(&mut buffer[..1], true, len == 1)?;
if len >= 2 {
// Note(safety): Unwrap should be safe, as this can only be called
// when `Mode == Async`, where we have dma channels.
let ch = self.rx_dma.as_mut().unwrap();
let transfer = unsafe {
T::regs().ic_data_cmd().modify(|w| {
w.set_cmd(true);
});
T::regs().ic_dma_cr().modify(|reg| {
reg.set_rdmae(true);
});
// If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send.
crate::dma::read(
ch,
T::regs().ic_data_cmd().ptr() as *const _,
&mut buffer[1..len - 1],
T::RX_DREQ,
)
};
transfer.await;
}
if len >= 2 {
self.read_blocking_internal(&mut buffer[len - 1..], false, true)?;
}
Ok(())
}
// =========================
// Async public API
// =========================
pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> {
Self::setup(address.into())?;
if bytes.is_empty() {
self.write_blocking_internal(bytes, true)
} else {
self.write_internal(bytes, true).await
}
}
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
Self::setup(address.into())?;
if buffer.is_empty() {
self.read_blocking_internal(buffer, true, true)
} else {
self.read_internal(buffer).await
}
}
pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
Self::setup(address.into())?;
if bytes.is_empty() {
self.write_blocking_internal(bytes, false)?;
} else {
self.write_internal(bytes, true).await?;
}
if buffer.is_empty() {
self.read_blocking_internal(buffer, true, true)
} else {
self.read_internal(buffer).await
}
}
}
impl<'d, T: Instance> I2c<'d, T, Blocking> {
pub fn new_blocking(
_peri: impl Peripheral<P = T> + 'd,
......@@ -616,23 +461,7 @@ fn i2c_reserved_addr(addr: u16) -> bool {
}
mod sealed {
use atomic_polyfill::AtomicUsize;
use embassy_cortex_m::interrupt::Interrupt;
use embassy_sync::waitqueue::AtomicWaker;
pub(crate) struct State {
pub(crate) waker: AtomicWaker,
pub(crate) chunks_transferred: AtomicUsize,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
chunks_transferred: AtomicUsize::new(0),
}
}
}
pub trait Instance {
const TX_DREQ: u8;
......@@ -641,7 +470,6 @@ mod sealed {
type Interrupt: Interrupt;
fn regs() -> crate::pac::i2c::I2c;
fn state() -> &'static State;
}
pub trait Mode {}
......@@ -678,11 +506,6 @@ macro_rules! impl_instance {
fn regs() -> pac::i2c::I2c {
pac::$type
}
fn state() -> &'static sealed::State {
static STATE: sealed::State = sealed::State::new();
&STATE
}
}
impl Instance for peripherals::$type {}
};
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment