From 26e0823c3507a537d72fa5d6d7e97126314c0f0c Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 14 Aug 2023 16:50:57 -0400 Subject: [PATCH 1/8] rp2040 I2cDevice Move i2c to mod, split device and controller Remove mode generic: I don't think it's reasonable to use the i2c in device mode while blocking, so I'm cutting the generic. --- embassy-rp/src/{ => i2c}/i2c.rs | 171 +---------------- embassy-rp/src/i2c/i2c_device.rs | 313 +++++++++++++++++++++++++++++++ embassy-rp/src/i2c/mod.rs | 175 +++++++++++++++++ tests/rp/src/bin/i2c.rs | 215 +++++++++++++++++++++ 4 files changed, 709 insertions(+), 165 deletions(-) rename embassy-rp/src/{ => i2c}/i2c.rs (83%) create mode 100644 embassy-rp/src/i2c/i2c_device.rs create mode 100644 embassy-rp/src/i2c/mod.rs create mode 100644 tests/rp/src/bin/i2c.rs diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c/i2c.rs similarity index 83% rename from embassy-rp/src/i2c.rs rename to embassy-rp/src/i2c/i2c.rs index 7a5ddd325..13124dd81 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c/i2c.rs @@ -3,45 +3,19 @@ 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}; -use crate::{interrupt, pac, peripherals, Peripheral}; - -/// I2C error abort reason -#[derive(Debug)] -#[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, - Other(u32), -} - -/// I2C error -#[derive(Debug)] -#[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), -} +use crate::{pac, Peripheral}; #[non_exhaustive] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { pub frequency: u32, } @@ -52,8 +26,6 @@ impl Default for Config { } } -const FIFO_SIZE: u8 = 16; - pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } @@ -302,20 +274,6 @@ 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, @@ -636,6 +594,7 @@ mod eh1 { Self::Abort(AbortReason::NoAcknowledge) => { embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) } + Self::Abort(AbortReason::TxNotEmpty(_)) => embedded_hal_1::i2c::ErrorKind::Other, Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other, @@ -737,121 +696,3 @@ mod nightly { } } } - -fn i2c_reserved_addr(addr: u16) -> bool { - (addr & 0x78) == 0 || (addr & 0x78) == 0x78 -} - -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); diff --git a/embassy-rp/src/i2c/i2c_device.rs b/embassy-rp/src/i2c/i2c_device.rs new file mode 100644 index 000000000..915c0424e --- /dev/null +++ b/embassy-rp/src/i2c/i2c_device.rs @@ -0,0 +1,313 @@ +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), +} + +/// Device Configuration +#[non_exhaustive] +#[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DeviceConfig { + /// Target Address + pub addr: u16, +} + +impl Default for DeviceConfig { + fn default() -> Self { + Self { addr: 0x55 } + } +} + +pub struct I2cDevice<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> I2cDevice<'d, T> { + pub fn new( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + _irq: impl Binding>, + config: DeviceConfig, + ) -> 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(); + + info!("buff: {}", buffer); + 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() { + info!("ar: {}", 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 new file mode 100644 index 000000000..2b3523d69 --- /dev/null +++ b/embassy-rp/src/i2c/mod.rs @@ -0,0 +1,175 @@ +mod i2c; +mod i2c_device; + +use core::marker::PhantomData; + +use embassy_sync::waitqueue::AtomicWaker; +pub use i2c::{Config, I2c}; +pub use i2c_device::{Command, DeviceConfig, I2cDevice, ReadStatus}; + +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); diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs new file mode 100644 index 000000000..63dd00233 --- /dev/null +++ b/tests/rp/src/bin/i2c.rs @@ -0,0 +1,215 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, info, panic, unwrap}; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_rp::bind_interrupts; +use embassy_rp::i2c::{self, Async, InterruptHandler}; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::peripherals::{I2C0, I2C1}; +use embedded_hal_1::i2c::Operation; +use embedded_hal_async::i2c::I2c; +use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<1024> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); + +use crate::i2c::AbortReason; + +bind_interrupts!(struct Irqs { + I2C0_IRQ => InterruptHandler; + I2C1_IRQ => InterruptHandler; +}); + +const DEV_ADDR: u8 = 0x42; + +#[embassy_executor::task] +async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { + info!("Device start"); + + let mut count = 0xD0; + + loop { + let mut buf = [0u8; 128]; + match dev.listen(&mut buf).await { + Ok(i2c::Command::GeneralCall(len)) => { + assert_eq!(buf[..len], [0xCA, 0x11], "recieving the general call failed"); + info!("General Call - OK"); + } + Ok(i2c::Command::Read) => { + loop { + //info!("Responding to read, count {}", count); + let a = dev.respond_to_read(&[count]).await; + //info!("x {}", a); + match a { + Ok(x) => match x { + i2c::ReadStatus::Done => break, + i2c::ReadStatus::NeedMoreBytes => count += 1, + i2c::ReadStatus::LeftoverBytes(x) => { + info!("tried to write {} extra bytes", x); + break; + } + }, + Err(e) => match e { + embassy_rp::i2c::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), + _ => panic!("{}", e), + }, + } + } + count += 1; + } + Ok(i2c::Command::Write(len)) => match len { + 1 => { + assert_eq!(buf[..len], [0xAA], "recieving a single byte failed"); + info!("Single Byte Write - OK") + } + 4 => { + assert_eq!(buf[..len], [0xAA, 0xBB, 0xCC, 0xDD], "recieving 4 bytes failed"); + info!("4 Byte Write - OK") + } + 32 => { + assert_eq!( + buf[..len], + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31 + ], + "recieving 32 bytes failed" + ); + info!("32 Byte Write - OK") + } + _ => panic!("Invalid write length {}", len), + }, + Ok(i2c::Command::WriteRead(len)) => { + info!("device recieved write read: {:x}", buf[..len]); + match buf[0] { + 0xC2 => { + let resp_buff = [0xD1, 0xD2, 0xD3, 0xD4]; + dev.respond_to_read(&resp_buff).await.unwrap(); + } + 0xC8 => { + let mut resp_buff = [0u8; 32]; + for i in 0..32 { + resp_buff[i] = i as u8; + } + dev.respond_to_read(&resp_buff).await.unwrap(); + } + x => panic!("Invalid Write Read {:x}", x), + } + } + Err(e) => match e { + embassy_rp::i2c::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), + _ => panic!("{}", e), + }, + } + } +} + +#[embassy_executor::task] +async fn controller_task(mut con: i2c::I2c<'static, I2C0, Async>) { + info!("Device start"); + + { + let buf = [0xCA, 0x11]; + con.write(0u16, &buf).await.unwrap(); + info!("Controler general call write"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8]; + con.read(DEV_ADDR, &mut buf).await.unwrap(); + assert_eq!(buf, [0xD0], "single byte read failed"); + info!("single byte read - OK"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8; 4]; + con.read(DEV_ADDR, &mut buf).await.unwrap(); + assert_eq!(buf, [0xD1, 0xD2, 0xD3, 0xD4], "single byte read failed"); + info!("4 byte read - OK"); + embassy_futures::yield_now().await; + } + + { + let buf = [0xAA]; + con.write(DEV_ADDR, &buf).await.unwrap(); + info!("Controler single byte write"); + embassy_futures::yield_now().await; + } + + { + let buf = [0xAA, 0xBB, 0xCC, 0xDD]; + con.write(DEV_ADDR, &buf).await.unwrap(); + info!("Controler 4 byte write"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8; 32]; + for i in 0..32 { + buf[i] = i as u8; + } + con.write(DEV_ADDR, &buf).await.unwrap(); + info!("Controler 32 byte write"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8; 4]; + let mut ops = [Operation::Write(&[0xC2]), Operation::Read(&mut buf)]; + con.transaction(DEV_ADDR, &mut ops).await.unwrap(); + assert_eq!(buf, [0xD1, 0xD2, 0xD3, 0xD4], "write_read failed"); + info!("write_read - OK"); + embassy_futures::yield_now().await; + } + + { + let mut buf = [0u8; 32]; + let mut ops = [Operation::Write(&[0xC8]), Operation::Read(&mut buf)]; + con.transaction(DEV_ADDR, &mut ops).await.unwrap(); + assert_eq!( + buf, + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31 + ], + "write_read of 32 bytes failed" + ); + info!("large write_read - OK") + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let d_sda = p.PIN_19; + let d_scl = p.PIN_18; + let mut config = i2c::DeviceConfig::default(); + config.addr = DEV_ADDR as u16; + let device = i2c::I2cDevice::new(p.I2C1, d_sda, d_scl, Irqs, config); + + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device)))); + }); + + let executor0 = EXECUTOR0.init(Executor::new()); + + let c_sda = p.PIN_21; + let c_scl = p.PIN_20; + let mut config = i2c::Config::default(); + config.frequency = 5_000; + let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); + + executor0.run(|spawner| unwrap!(spawner.spawn(controller_task(controller)))); +} From 28e25705339e5c275c5025634aab919d52459770 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 31 Aug 2023 08:19:00 -0400 Subject: [PATCH 2/8] Remove debug prints --- embassy-rp/src/i2c/i2c_device.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-rp/src/i2c/i2c_device.rs b/embassy-rp/src/i2c/i2c_device.rs index 915c0424e..5c78a86e3 100644 --- a/embassy-rp/src/i2c/i2c_device.rs +++ b/embassy-rp/src/i2c/i2c_device.rs @@ -216,14 +216,12 @@ impl<'d, T: Instance> I2cDevice<'d, T> { pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result { let p = T::regs(); - info!("buff: {}", buffer); 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() { - info!("ar: {}", abort_reason); if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); } else { From 18da91e2529b7973d0476e5c239709b3851db14b Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 4 Sep 2023 16:51:13 -0400 Subject: [PATCH 3/8] Rename to match upstream docs --- embassy-rp/src/i2c/{i2c_device.rs => i2c_slave.rs} | 12 ++++++------ embassy-rp/src/i2c/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) rename embassy-rp/src/i2c/{i2c_device.rs => i2c_slave.rs} (98%) diff --git a/embassy-rp/src/i2c/i2c_device.rs b/embassy-rp/src/i2c/i2c_slave.rs similarity index 98% rename from embassy-rp/src/i2c/i2c_device.rs rename to embassy-rp/src/i2c/i2c_slave.rs index 5c78a86e3..154a0f32c 100644 --- a/embassy-rp/src/i2c/i2c_device.rs +++ b/embassy-rp/src/i2c/i2c_slave.rs @@ -35,32 +35,32 @@ pub enum ReadStatus { LeftoverBytes(u16), } -/// Device Configuration +/// Slave Configuration #[non_exhaustive] #[derive(Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DeviceConfig { +pub struct SlaveConfig { /// Target Address pub addr: u16, } -impl Default for DeviceConfig { +impl Default for SlaveConfig { fn default() -> Self { Self { addr: 0x55 } } } -pub struct I2cDevice<'d, T: Instance> { +pub struct I2cSlave<'d, T: Instance> { phantom: PhantomData<&'d mut T>, } -impl<'d, T: Instance> I2cDevice<'d, 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: DeviceConfig, + config: SlaveConfig, ) -> Self { into_ref!(_peri, scl, sda); diff --git a/embassy-rp/src/i2c/mod.rs b/embassy-rp/src/i2c/mod.rs index 2b3523d69..05662fa6a 100644 --- a/embassy-rp/src/i2c/mod.rs +++ b/embassy-rp/src/i2c/mod.rs @@ -1,11 +1,11 @@ mod i2c; -mod i2c_device; +mod i2c_slave; use core::marker::PhantomData; use embassy_sync::waitqueue::AtomicWaker; pub use i2c::{Config, I2c}; -pub use i2c_device::{Command, DeviceConfig, I2cDevice, ReadStatus}; +pub use i2c_slave::{Command, I2cSlave, ReadStatus, SlaveConfig}; use crate::{interrupt, pac, peripherals}; From 2d9f50addc5f509f5549e69f594c382cebe739e6 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sat, 9 Sep 2023 18:25:23 -0400 Subject: [PATCH 4/8] I2c slave take 2 refactored to split modules renamed to match upstream docs slight improvement to slave error handling --- embassy-rp/src/i2c/i2c.rs | 155 ++++++++++++++++- embassy-rp/src/i2c_slave.rs | 326 ++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 1 + tests/rp/src/bin/i2c.rs | 33 ++-- 4 files changed, 497 insertions(+), 18 deletions(-) create mode 100644 embassy-rp/src/i2c_slave.rs diff --git a/embassy-rp/src/i2c/i2c.rs b/embassy-rp/src/i2c/i2c.rs index 13124dd81..facc7185f 100644 --- a/embassy-rp/src/i2c/i2c.rs +++ b/embassy-rp/src/i2c/i2c.rs @@ -11,7 +11,38 @@ use super::{ use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::interrupt::typelevel::{Binding, Interrupt}; -use crate::{pac, Peripheral}; +use crate::{interrupt, pac, peripherals, Peripheral}; + +/// I2C error abort reason +#[derive(Debug)] +#[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)] +#[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), +} #[non_exhaustive] #[derive(Copy, Clone)] @@ -26,6 +57,8 @@ impl Default for Config { } } +pub const FIFO_SIZE: u8 = 16; + pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } @@ -696,3 +729,123 @@ 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 +} + +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); diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs new file mode 100644 index 000000000..a65250116 --- /dev/null +++ b/embassy-rp/src/i2c_slave.rs @@ -0,0 +1,326 @@ +use core::future; +use core::marker::PhantomData; +use core::task::Poll; + +use embassy_hal_internal::into_ref; +use pac::i2c; + +use crate::i2c::{i2c_reserved_addr, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE}; +use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::{pac, Peripheral}; + +/// I2C error +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// I2C abort with error + Abort(AbortReason), + /// User passed in a response buffer that was 0 length + InvalidResponseBufferLength, +} + +/// 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 Config { + /// Target Address + pub addr: u16, +} + +impl Default for Config { + 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: Config, + ) -> 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(); + + if buffer.len() == 0 { + return Err(Error::InvalidResponseBufferLength); + } + + 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/lib.rs b/embassy-rp/src/lib.rs index 49bd3533e..2a1bca4b9 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -16,6 +16,7 @@ pub mod flash; mod float; pub mod gpio; pub mod i2c; +pub mod i2c_slave; pub mod multicore; pub mod pwm; mod reset; diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs index 63dd00233..a6cf48afe 100644 --- a/tests/rp/src/bin/i2c.rs +++ b/tests/rp/src/bin/i2c.rs @@ -5,10 +5,9 @@ use defmt::{assert_eq, info, panic, unwrap}; use embassy_executor::Executor; use embassy_executor::_export::StaticCell; -use embassy_rp::bind_interrupts; -use embassy_rp::i2c::{self, Async, InterruptHandler}; use embassy_rp::multicore::{spawn_core1, Stack}; use embassy_rp::peripherals::{I2C0, I2C1}; +use embassy_rp::{bind_interrupts, i2c, i2c_slave}; use embedded_hal_1::i2c::Operation; use embedded_hal_async::i2c::I2c; use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _}; @@ -20,14 +19,14 @@ static EXECUTOR1: StaticCell = StaticCell::new(); use crate::i2c::AbortReason; bind_interrupts!(struct Irqs { - I2C0_IRQ => InterruptHandler; - I2C1_IRQ => InterruptHandler; + I2C0_IRQ => i2c::InterruptHandler; + I2C1_IRQ => i2c::InterruptHandler; }); const DEV_ADDR: u8 = 0x42; #[embassy_executor::task] -async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { +async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { info!("Device start"); let mut count = 0xD0; @@ -35,33 +34,33 @@ async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { loop { let mut buf = [0u8; 128]; match dev.listen(&mut buf).await { - Ok(i2c::Command::GeneralCall(len)) => { + Ok(i2c_slave::Command::GeneralCall(len)) => { assert_eq!(buf[..len], [0xCA, 0x11], "recieving the general call failed"); info!("General Call - OK"); } - Ok(i2c::Command::Read) => { + Ok(i2c_slave::Command::Read) => { loop { //info!("Responding to read, count {}", count); let a = dev.respond_to_read(&[count]).await; //info!("x {}", a); match a { Ok(x) => match x { - i2c::ReadStatus::Done => break, - i2c::ReadStatus::NeedMoreBytes => count += 1, - i2c::ReadStatus::LeftoverBytes(x) => { + i2c_slave::ReadStatus::Done => break, + i2c_slave::ReadStatus::NeedMoreBytes => count += 1, + i2c_slave::ReadStatus::LeftoverBytes(x) => { info!("tried to write {} extra bytes", x); break; } }, Err(e) => match e { - embassy_rp::i2c::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), + embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), _ => panic!("{}", e), }, } } count += 1; } - Ok(i2c::Command::Write(len)) => match len { + Ok(i2c_slave::Command::Write(len)) => match len { 1 => { assert_eq!(buf[..len], [0xAA], "recieving a single byte failed"); info!("Single Byte Write - OK") @@ -83,7 +82,7 @@ async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { } _ => panic!("Invalid write length {}", len), }, - Ok(i2c::Command::WriteRead(len)) => { + Ok(i2c_slave::Command::WriteRead(len)) => { info!("device recieved write read: {:x}", buf[..len]); match buf[0] { 0xC2 => { @@ -101,7 +100,7 @@ async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { } } Err(e) => match e { - embassy_rp::i2c::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), + embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), _ => panic!("{}", e), }, } @@ -109,7 +108,7 @@ async fn device_task(mut dev: i2c::I2cDevice<'static, I2C1>) -> ! { } #[embassy_executor::task] -async fn controller_task(mut con: i2c::I2c<'static, I2C0, Async>) { +async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { info!("Device start"); { @@ -194,9 +193,9 @@ fn main() -> ! { let d_sda = p.PIN_19; let d_scl = p.PIN_18; - let mut config = i2c::DeviceConfig::default(); + let mut config = i2c_slave::Config::default(); config.addr = DEV_ADDR as u16; - let device = i2c::I2cDevice::new(p.I2C1, d_sda, d_scl, Irqs, config); + let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { let executor1 = EXECUTOR1.init(Executor::new()); From 8201979d719f7f2647c7cb83b8e28bfc636b9d4a Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sun, 10 Sep 2023 02:05:58 -0400 Subject: [PATCH 5/8] Add example, fix small bug in respond_and_fill --- embassy-rp/src/i2c_slave.rs | 12 ++++ examples/rp/src/bin/i2c_slave.rs | 118 +++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 examples/rp/src/bin/i2c_slave.rs diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs index a65250116..6136d69c6 100644 --- a/embassy-rp/src/i2c_slave.rs +++ b/embassy-rp/src/i2c_slave.rs @@ -287,6 +287,18 @@ impl<'d, T: Instance> I2cSlave<'d, T> { } } + /// Respond to a master read, then fill any remaining read bytes with `fill` + pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result { + let resp_stat = self.respond_to_read(buffer).await?; + + if resp_stat == ReadStatus::NeedMoreBytes { + self.respond_till_stop(fill).await?; + Ok(ReadStatus::Done) + } else { + Ok(resp_stat) + } + } + #[inline(always)] fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { let p = T::regs(); diff --git a/examples/rp/src/bin/i2c_slave.rs b/examples/rp/src/bin/i2c_slave.rs new file mode 100644 index 000000000..7de300fb8 --- /dev/null +++ b/examples/rp/src/bin/i2c_slave.rs @@ -0,0 +1,118 @@ +//! This example shows how to use the 2040 as an i2c slave. +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::peripherals::{I2C0, I2C1}; +use embassy_rp::{bind_interrupts, i2c, i2c_slave}; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::i2c::I2c; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + I2C0_IRQ => i2c::InterruptHandler; + I2C1_IRQ => i2c::InterruptHandler; +}); + +const DEV_ADDR: u8 = 0x42; + +#[embassy_executor::task] +async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { + info!("Device start"); + + let mut state = 0; + + loop { + let mut buf = [0u8; 128]; + match dev.listen(&mut buf).await { + Ok(i2c_slave::Command::GeneralCall(len)) => info!("Device recieved general call write: {}", buf[..len]), + Ok(i2c_slave::Command::Read) => loop { + match dev.respond_to_read(&[state]).await { + Ok(x) => match x { + i2c_slave::ReadStatus::Done => break, + i2c_slave::ReadStatus::NeedMoreBytes => (), + i2c_slave::ReadStatus::LeftoverBytes(x) => { + info!("tried to write {} extra bytes", x); + break; + } + }, + Err(e) => error!("error while responding {}", e), + } + }, + Ok(i2c_slave::Command::Write(len)) => info!("Device recieved write: {}", buf[..len]), + Ok(i2c_slave::Command::WriteRead(len)) => { + info!("device recieved write read: {:x}", buf[..len]); + match buf[0] { + // Set the state + 0xC2 => { + state = buf[1]; + match dev.respond_and_fill(&[state], 0x00).await { + Ok(read_status) => info!("response read status {}", read_status), + Err(e) => error!("error while responding {}", e), + } + } + // Reset State + 0xC8 => { + state = 0; + match dev.respond_and_fill(&[state], 0x00).await { + Ok(read_status) => info!("response read status {}", read_status), + Err(e) => error!("error while responding {}", e), + } + } + x => error!("Invalid Write Read {:x}", x), + } + } + Err(e) => error!("{}", e), + } + } +} + +#[embassy_executor::task] +async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { + info!("Controller start"); + + loop { + let mut resp_buff = [0u8; 2]; + for i in 0..10 { + match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await { + Ok(_) => info!("write_read response: {}", resp_buff), + Err(e) => error!("Error writing {}", e), + } + + Timer::after(Duration::from_millis(100)).await; + } + match con.read(DEV_ADDR, &mut resp_buff).await { + Ok(_) => info!("read response: {}", resp_buff), + Err(e) => error!("Error writing {}", e), + } + match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await { + Ok(_) => info!("write_read response: {}", resp_buff), + Err(e) => error!("Error writing {}", e), + } + Timer::after(Duration::from_millis(100)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let d_sda = p.PIN_3; + let d_scl = p.PIN_2; + let mut config = i2c_slave::Config::default(); + config.addr = DEV_ADDR as u16; + let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); + + unwrap!(spawner.spawn(device_task(device))); + + let c_sda = p.PIN_1; + let c_scl = p.PIN_0; + let mut config = i2c::Config::default(); + config.frequency = 5_000; + let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); + + unwrap!(spawner.spawn(controller_task(controller))); +} From 8900f5f52b1140de960511f4ee6a13203d5c7b38 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sun, 10 Sep 2023 02:34:16 -0400 Subject: [PATCH 6/8] Fixing my git-based mistakes --- embassy-rp/src/{i2c => }/i2c.rs | 21 ++- embassy-rp/src/i2c/i2c_slave.rs | 311 -------------------------------- embassy-rp/src/i2c/mod.rs | 175 ------------------ 3 files changed, 15 insertions(+), 492 deletions(-) rename embassy-rp/src/{i2c => }/i2c.rs (98%) delete mode 100644 embassy-rp/src/i2c/i2c_slave.rs delete mode 100644 embassy-rp/src/i2c/mod.rs 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); From 8edb7bb012b43977ee57f9ebff9b57436a0a904f Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sun, 10 Sep 2023 03:05:54 -0400 Subject: [PATCH 7/8] Test cleanup --- tests/rp/src/bin/i2c.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs index a6cf48afe..4b6becbaf 100644 --- a/tests/rp/src/bin/i2c.rs +++ b/tests/rp/src/bin/i2c.rs @@ -40,10 +40,7 @@ async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { } Ok(i2c_slave::Command::Read) => { loop { - //info!("Responding to read, count {}", count); - let a = dev.respond_to_read(&[count]).await; - //info!("x {}", a); - match a { + match dev.respond_to_read(&[count]).await { Ok(x) => match x { i2c_slave::ReadStatus::Done => break, i2c_slave::ReadStatus::NeedMoreBytes => count += 1, From 5cf494113f7681ac6436bdbd7972e0074d1d26b7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 10 Sep 2023 22:20:05 +0200 Subject: [PATCH 8/8] tests/rp: add teleprobe meta. --- tests/rp/src/bin/i2c.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs index 4b6becbaf..425f2d086 100644 --- a/tests/rp/src/bin/i2c.rs +++ b/tests/rp/src/bin/i2c.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, info, panic, unwrap}; use embassy_executor::Executor;