diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
index 7a5ddd325..c358682c5 100644
--- a/embassy-rp/src/i2c.rs
+++ b/embassy-rp/src/i2c.rs
@@ -21,6 +21,8 @@ pub enum AbortReason {
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),
}
@@ -52,7 +54,7 @@ impl Default for Config {
}
}
-const FIFO_SIZE: u8 = 16;
+pub const FIFO_SIZE: u8 = 16;
pub struct I2c<'d, T: Instance, M: Mode> {
phantom: PhantomData<(&'d mut T, M)>,
@@ -636,6 +638,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,
@@ -738,8 +741,8 @@ mod nightly {
}
}
-fn i2c_reserved_addr(addr: u16) -> bool {
- (addr & 0x78) == 0 || (addr & 0x78) == 0x78
+pub fn i2c_reserved_addr(addr: u16) -> bool {
+ ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0
}
mod sealed {
diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs
new file mode 100644
index 000000000..6136d69c6
--- /dev/null
+++ b/embassy-rp/src/i2c_slave.rs
@@ -0,0 +1,338 @@
+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),
+ }
+ }
+ }
+
+ /// 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();
+ 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/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)));
+}
diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs
new file mode 100644
index 000000000..425f2d086
--- /dev/null
+++ b/tests/rp/src/bin/i2c.rs
@@ -0,0 +1,212 @@
+#![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;
+use embassy_executor::_export::StaticCell;
+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 _};
+
+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 => 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 count = 0xD0;
+
+ loop {
+ let mut buf = [0u8; 128];
+ match dev.listen(&mut buf).await {
+ Ok(i2c_slave::Command::GeneralCall(len)) => {
+ assert_eq!(buf[..len], [0xCA, 0x11], "recieving the general call failed");
+ info!("General Call - OK");
+ }
+ Ok(i2c_slave::Command::Read) => {
+ loop {
+ match dev.respond_to_read(&[count]).await {
+ Ok(x) => match 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_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n),
+ _ => panic!("{}", e),
+ },
+ }
+ }
+ count += 1;
+ }
+ Ok(i2c_slave::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_slave::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_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n),
+ _ => panic!("{}", e),
+ },
+ }
+ }
+}
+
+#[embassy_executor::task]
+async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::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_slave::Config::default();
+ config.addr = DEV_ADDR as u16;
+ 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());
+ 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))));
+}