From 53c34ccc399c7bec98880657e52842477af09f49 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 29 Aug 2022 13:31:17 +0200 Subject: [PATCH] Add async API for I2C --- embassy-rp/src/i2c.rs | 281 +++++++++++++++++++++++++++++++++--------- 1 file changed, 225 insertions(+), 56 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 20616cd6..19cdef13 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,8 +1,13 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +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::gpio::sealed::Pin; +use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; /// I2C error abort reason @@ -49,9 +54,165 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { + tx_dma: Option>, + rx_dma: Option>, + dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } +impl<'d, T: Instance> I2c<'d, T, Async> { + pub fn new( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ '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, false).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

+ 'd, @@ -59,7 +220,21 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { sda: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(_peri, scl, sda); + into_ref!(scl, sda); + Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config) + } +} + +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + scl: PeripheralRef<'d, AnyPin>, + sda: PeripheralRef<'d, AnyPin>, + tx_dma: Option>, + rx_dma: Option>, + config: Config, + ) -> Self { + into_ref!(_peri); assert!(config.frequency <= 1_000_000); assert!(config.frequency > 0); @@ -152,11 +327,14 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_enable().write(|w| w.set_enable(true)); } - Self { phantom: PhantomData } + Self { + tx_dma, + rx_dma, + dma_buf: [0; 256], + phantom: PhantomData, + } } -} -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { fn setup(addr: u16) -> Result<(), Error> { if addr >= 0x80 { return Err(Error::AddressOutOfRange(addr)); @@ -304,46 +482,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } -// impl<'d, T: Instance> I2c<'d, T, Async> { // ========================= // -// Async public API // ========================= - -// pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), -// Error> { if bytes.is_empty() { self.write_blocking_internal(address, -// bytes, true) } else { self.write_dma_internal(address, bytes, -// true, true).await } } - -// pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> -// Result<(), Error> { if bytes.is_empty() { return -// Err(Error::ZeroLengthTransfer); } let mut iter = bytes.iter(); - -// let mut first = true; let mut current = iter.next(); while let -// Some(c) = current { let next = iter.next(); let is_last = -// next.is_none(); - -// self.write_dma_internal(address, c, first, is_last).await?; -// first = false; -// current = next; -// } Ok(()) -// } - -// pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> -// Result<(), Error> { if buffer.is_empty() { -// self.read_blocking_internal(address, buffer, false) } else { -// self.read_dma_internal(address, buffer, false).await } } - -// pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: -// &mut [u8]) -> Result<(), Error> { if bytes.is_empty() { -// self.write_blocking_internal(address, bytes, false)?; } else { -// self.write_dma_internal(address, bytes, true, true).await?; } - -// if buffer.is_empty() { self.read_blocking_internal(address, buffer, -// true)?; } else { self.read_dma_internal(address, buffer, -// true).await?; } - -// Ok(()) -// } -// } - mod eh02 { use super::*; @@ -478,7 +616,34 @@ fn i2c_reserved_addr(addr: u16) -> bool { } mod sealed { - pub trait Instance {} + 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; + const RX_DREQ: u8; + + type Interrupt: Interrupt; + + fn regs() -> crate::pac::i2c::I2c; + fn state() -> &'static State; + } + pub trait Mode {} pub trait SdaPin {} @@ -500,27 +665,31 @@ pub struct Async; impl_mode!(Blocking); impl_mode!(Async); -pub trait Instance: sealed::Instance { - type Interrupt; - - fn regs() -> pac::i2c::I2c; -} +pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident) => { - impl sealed::Instance for peripherals::$type {} - impl Instance for peripherals::$type { + ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { + impl sealed::Instance for peripherals::$type { + const TX_DREQ: u8 = $tx_dreq; + const RX_DREQ: u8 = $rx_dreq; + type Interrupt = crate::interrupt::$irq; 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 {} }; } -impl_instance!(I2C0, I2C0_IRQ); -impl_instance!(I2C1, I2C1_IRQ); +impl_instance!(I2C0, I2C0_IRQ, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {}