diff --git a/embassy-rp/src/i2c/i2c.rs b/embassy-rp/src/i2c.rs similarity index 98% rename from embassy-rp/src/i2c/i2c.rs rename to embassy-rp/src/i2c.rs index facc7185f..c358682c5 100644 --- a/embassy-rp/src/i2c/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -3,11 +3,9 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; -use super::{ - i2c_reserved_addr, AbortReason, Async, Blocking, Error, Instance, InterruptHandler, Mode, SclPin, SdaPin, FIFO_SIZE, -}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::interrupt::typelevel::{Binding, Interrupt}; @@ -46,7 +44,6 @@ pub enum Error { #[non_exhaustive] #[derive(Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { pub frequency: u32, } @@ -307,6 +304,20 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } } +pub struct InterruptHandler { + _uart: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + // Mask interrupts and wake any task waiting for this interrupt + unsafe fn on_interrupt() { + let i2c = T::regs(); + i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); + + T::waker().wake(); + } +} + impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { fn new_inner( _peri: impl Peripheral

+ 'd, @@ -729,8 +740,6 @@ mod nightly { } } } -<<<<<<< HEAD:embassy-rp/src/i2c/i2c.rs -======= pub fn i2c_reserved_addr(addr: u16) -> bool { ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 diff --git a/embassy-rp/src/i2c/i2c_slave.rs b/embassy-rp/src/i2c/i2c_slave.rs deleted file mode 100644 index 154a0f32c..000000000 --- a/embassy-rp/src/i2c/i2c_slave.rs +++ /dev/null @@ -1,311 +0,0 @@ -use core::future; -use core::marker::PhantomData; -use core::task::Poll; - -use embassy_hal_internal::into_ref; -use pac::i2c; - -use super::{i2c_reserved_addr, AbortReason, Error, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE}; -use crate::interrupt::typelevel::{Binding, Interrupt}; -use crate::{pac, Peripheral}; - -/// Received command -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Command { - /// General Call - GeneralCall(usize), - /// Read - Read, - /// Write+read - WriteRead(usize), - /// Write - Write(usize), -} - -/// Possible responses to responding to a read -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ReadStatus { - /// Transaction Complete, controller naked our last byte - Done, - /// Transaction Incomplete, controller trying to read more bytes than were provided - NeedMoreBytes, - /// Transaction Complere, but controller stopped reading bytes before we ran out - LeftoverBytes(u16), -} - -/// Slave Configuration -#[non_exhaustive] -#[derive(Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SlaveConfig { - /// Target Address - pub addr: u16, -} - -impl Default for SlaveConfig { - fn default() -> Self { - Self { addr: 0x55 } - } -} - -pub struct I2cSlave<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, -} - -impl<'d, T: Instance> I2cSlave<'d, T> { - pub fn new( - _peri: impl Peripheral

+ 'd, - scl: impl Peripheral

> + 'd, - sda: impl Peripheral

> + 'd, - _irq: impl Binding>, - config: SlaveConfig, - ) -> Self { - into_ref!(_peri, scl, sda); - - assert!(!i2c_reserved_addr(config.addr)); - assert!(config.addr != 0); - - let p = T::regs(); - - let reset = T::reset(); - crate::reset::reset(reset); - crate::reset::unreset_wait(reset); - - p.ic_enable().write(|w| w.set_enable(false)); - - p.ic_sar().write(|w| w.set_ic_sar(config.addr)); - p.ic_con().modify(|w| { - w.set_master_mode(false); - w.set_ic_slave_disable(false); - w.set_tx_empty_ctrl(true); - }); - - // Set FIFO watermarks to 1 to make things simpler. This is encoded - // by a register value of 0. Rx watermark should never change, but Tx watermark will be - // adjusted in operation. - p.ic_tx_tl().write(|w| w.set_tx_tl(0)); - p.ic_rx_tl().write(|w| w.set_rx_tl(0)); - - // Configure SCL & SDA pins - scl.gpio().ctrl().write(|w| w.set_funcsel(3)); - sda.gpio().ctrl().write(|w| w.set_funcsel(3)); - - scl.pad_ctrl().write(|w| { - w.set_schmitt(true); - w.set_ie(true); - w.set_od(false); - w.set_pue(true); - w.set_pde(false); - }); - sda.pad_ctrl().write(|w| { - w.set_schmitt(true); - w.set_ie(true); - w.set_od(false); - w.set_pue(true); - w.set_pde(false); - }); - - // Clear interrupts - p.ic_clr_intr().read(); - - // Enable I2C block - p.ic_enable().write(|w| w.set_enable(true)); - - // mask everything initially - p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - Self { phantom: PhantomData } - } - - /// Calls `f` to check if we are ready or not. - /// If not, `g` is called once the waker is set (to eg enable the required interrupts). - #[inline(always)] - async fn wait_on(&mut self, mut f: F, mut g: G) -> U - where - F: FnMut(&mut Self) -> Poll, - G: FnMut(&mut Self), - { - future::poll_fn(|cx| { - let r = f(self); - - trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0); - - if r.is_pending() { - T::waker().register(cx.waker()); - g(self); - } - - r - }) - .await - } - - #[inline(always)] - fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize { - let p = T::regs(); - let len = p.ic_rxflr().read().rxflr() as usize; - let end = offset + len; - for i in offset..end { - buffer[i] = p.ic_data_cmd().read().dat(); - } - end - } - - #[inline(always)] - fn write_to_fifo(&mut self, buffer: &[u8]) { - let p = T::regs(); - for byte in buffer { - p.ic_data_cmd().write(|w| w.set_dat(*byte)); - } - } - - /// Wait asynchronously for commands from an I2C master. - /// `buffer` is provided in case master does a 'write' and is unused for 'read'. - pub async fn listen(&mut self, buffer: &mut [u8]) -> Result { - let p = T::regs(); - - p.ic_clr_intr().read(); - // set rx fifo watermark to 1 byte - p.ic_rx_tl().write(|w| w.set_rx_tl(0)); - - let mut len = 0; - let ret = self - .wait_on( - |me| { - let stat = p.ic_raw_intr_stat().read(); - if p.ic_rxflr().read().rxflr() > 0 { - len = me.drain_fifo(buffer, len); - // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise - p.ic_rx_tl().write(|w| w.set_rx_tl(11)); - } - - if stat.restart_det() && stat.rd_req() { - Poll::Ready(Ok(Command::WriteRead(len))) - } else if stat.gen_call() && stat.stop_det() && len > 0 { - Poll::Ready(Ok(Command::GeneralCall(len))) - } else if stat.stop_det() { - Poll::Ready(Ok(Command::Write(len))) - } else if stat.rd_req() { - Poll::Ready(Ok(Command::Read)) - } else { - Poll::Pending - } - }, - |_me| { - p.ic_intr_mask().modify(|w| { - w.set_m_stop_det(true); - w.set_m_restart_det(true); - w.set_m_gen_call(true); - w.set_m_rd_req(true); - w.set_m_rx_full(true); - }); - }, - ) - .await; - - p.ic_clr_intr().read(); - - ret - } - - /// Respond to an I2C master READ command, asynchronously. - pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result { - let p = T::regs(); - - let mut chunks = buffer.chunks(FIFO_SIZE as usize); - - let ret = self - .wait_on( - |me| { - if let Err(abort_reason) = me.read_and_clear_abort_reason() { - if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { - return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); - } else { - return Poll::Ready(Err(abort_reason)); - } - } - - if let Some(chunk) = chunks.next() { - me.write_to_fifo(chunk); - - Poll::Pending - } else { - let stat = p.ic_raw_intr_stat().read(); - - if stat.rx_done() && stat.stop_det() { - Poll::Ready(Ok(ReadStatus::Done)) - } else if stat.rd_req() { - Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) - } else { - Poll::Pending - } - } - }, - |_me| { - p.ic_intr_mask().modify(|w| { - w.set_m_stop_det(true); - w.set_m_rx_done(true); - w.set_m_tx_empty(true); - w.set_m_tx_abrt(true); - }) - }, - ) - .await; - - p.ic_clr_intr().read(); - - ret - } - - /// Respond to reads with the fill byte until the controller stops asking - pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> { - loop { - match self.respond_to_read(&[fill]).await { - Ok(ReadStatus::NeedMoreBytes) => (), - Ok(_) => break Ok(()), - Err(e) => break Err(e), - } - } - } - - #[inline(always)] - fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { - let p = T::regs(); - let mut abort_reason = p.ic_tx_abrt_source().read(); - - // Mask off fifo flush count - let tx_flush_cnt = abort_reason.tx_flush_cnt(); - abort_reason.set_tx_flush_cnt(0); - - // Mask off master_dis - abort_reason.set_abrt_master_dis(false); - - if abort_reason.0 != 0 { - // Note clearing the abort flag also clears the reason, and this - // instance of flag is clear-on-read! Note also the - // IC_CLR_TX_ABRT register always reads as 0. - p.ic_clr_tx_abrt().read(); - - let reason = if abort_reason.abrt_7b_addr_noack() - | abort_reason.abrt_10addr1_noack() - | abort_reason.abrt_10addr2_noack() - { - AbortReason::NoAcknowledge - } else if abort_reason.arb_lost() { - AbortReason::ArbitrationLoss - } else if abort_reason.abrt_slvflush_txfifo() { - AbortReason::TxNotEmpty(tx_flush_cnt) - } else { - AbortReason::Other(abort_reason.0) - }; - - Err(Error::Abort(reason)) - } else { - Ok(()) - } - } -} diff --git a/embassy-rp/src/i2c/mod.rs b/embassy-rp/src/i2c/mod.rs deleted file mode 100644 index 05662fa6a..000000000 --- a/embassy-rp/src/i2c/mod.rs +++ /dev/null @@ -1,175 +0,0 @@ -mod i2c; -mod i2c_slave; - -use core::marker::PhantomData; - -use embassy_sync::waitqueue::AtomicWaker; -pub use i2c::{Config, I2c}; -pub use i2c_slave::{Command, I2cSlave, ReadStatus, SlaveConfig}; - -use crate::{interrupt, pac, peripherals}; - -const FIFO_SIZE: u8 = 16; - -/// I2C error abort reason -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AbortReason { - /// A bus operation was not acknowledged, e.g. due to the addressed device - /// not being available on the bus or the device not being ready to process - /// requests at the moment - NoAcknowledge, - /// The arbitration was lost, e.g. electrical problems with the clock signal - ArbitrationLoss, - /// Transmit ended with data still in fifo - TxNotEmpty(u16), - Other(u32), -} - -/// I2C error -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// I2C abort with error - Abort(AbortReason), - /// User passed in a read buffer that was 0 length - InvalidReadBufferLength, - /// User passed in a write buffer that was 0 length - InvalidWriteBufferLength, - /// Target i2c address is out of range - AddressOutOfRange(u16), - /// Target i2c address is reserved - AddressReserved(u16), -} - -pub struct InterruptHandler { - _uart: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - // Mask interrupts and wake any task waiting for this interrupt - unsafe fn on_interrupt() { - let i2c = T::regs(); - i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - - T::waker().wake(); - } -} - -fn i2c_reserved_addr(addr: u16) -> bool { - ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 -} - -mod sealed { - use embassy_sync::waitqueue::AtomicWaker; - - use crate::interrupt; - - pub trait Instance { - const TX_DREQ: u8; - const RX_DREQ: u8; - - type Interrupt: interrupt::typelevel::Interrupt; - - fn regs() -> crate::pac::i2c::I2c; - fn reset() -> crate::pac::resets::regs::Peripherals; - fn waker() -> &'static AtomicWaker; - } - - pub trait Mode {} - - pub trait SdaPin {} - pub trait SclPin {} -} - -pub trait Mode: sealed::Mode {} - -macro_rules! impl_mode { - ($name:ident) => { - impl sealed::Mode for $name {} - impl Mode for $name {} - }; -} - -pub struct Blocking; -pub struct Async; - -impl_mode!(Blocking); -impl_mode!(Async); - -pub trait Instance: sealed::Instance {} - -macro_rules! impl_instance { - ($type:ident, $irq:ident, $reset: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::typelevel::$irq; - - #[inline] - fn regs() -> pac::i2c::I2c { - pac::$type - } - - #[inline] - fn reset() -> pac::resets::regs::Peripherals { - let mut ret = pac::resets::regs::Peripherals::default(); - ret.$reset(true); - ret - } - - #[inline] - fn waker() -> &'static AtomicWaker { - static WAKER: AtomicWaker = AtomicWaker::new(); - - &WAKER - } - } - impl Instance for peripherals::$type {} - }; -} - -impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); -impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); - -pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} -pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} - -macro_rules! impl_pin { - ($pin:ident, $instance:ident, $function:ident) => { - impl sealed::$function for peripherals::$pin {} - impl $function for peripherals::$pin {} - }; -} - -impl_pin!(PIN_0, I2C0, SdaPin); -impl_pin!(PIN_1, I2C0, SclPin); -impl_pin!(PIN_2, I2C1, SdaPin); -impl_pin!(PIN_3, I2C1, SclPin); -impl_pin!(PIN_4, I2C0, SdaPin); -impl_pin!(PIN_5, I2C0, SclPin); -impl_pin!(PIN_6, I2C1, SdaPin); -impl_pin!(PIN_7, I2C1, SclPin); -impl_pin!(PIN_8, I2C0, SdaPin); -impl_pin!(PIN_9, I2C0, SclPin); -impl_pin!(PIN_10, I2C1, SdaPin); -impl_pin!(PIN_11, I2C1, SclPin); -impl_pin!(PIN_12, I2C0, SdaPin); -impl_pin!(PIN_13, I2C0, SclPin); -impl_pin!(PIN_14, I2C1, SdaPin); -impl_pin!(PIN_15, I2C1, SclPin); -impl_pin!(PIN_16, I2C0, SdaPin); -impl_pin!(PIN_17, I2C0, SclPin); -impl_pin!(PIN_18, I2C1, SdaPin); -impl_pin!(PIN_19, I2C1, SclPin); -impl_pin!(PIN_20, I2C0, SdaPin); -impl_pin!(PIN_21, I2C0, SclPin); -impl_pin!(PIN_22, I2C1, SdaPin); -impl_pin!(PIN_23, I2C1, SclPin); -impl_pin!(PIN_24, I2C0, SdaPin); -impl_pin!(PIN_25, I2C0, SclPin); -impl_pin!(PIN_26, I2C1, SdaPin); -impl_pin!(PIN_27, I2C1, SclPin); -impl_pin!(PIN_28, I2C0, SdaPin); -impl_pin!(PIN_29, I2C0, SclPin);