diff --git a/examples/rt685s-evk/.cargo/config.toml b/examples/rt685s-evk/.cargo/config.toml
index db42be81..59c3a138 100644
--- a/examples/rt685s-evk/.cargo/config.toml
+++ b/examples/rt685s-evk/.cargo/config.toml
@@ -2,12 +2,16 @@
runner = 'probe-rs run --chip MIMXRT685SFVKB'
rustflags = [
- "-C", "linker=flip-link",
- "-C", "link-arg=-Tlink.x",
- "-C", "link-arg=-Tdefmt.x",
+ "-C",
+ "linker=flip-link",
+ "-C",
+ "link-arg=-Tlink.x",
+ "-C",
+ "link-arg=-Tdefmt.x",
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
- "-C", "link-arg=--nmagic",
+ "-C",
+ "link-arg=--nmagic",
]
[build]
@@ -15,3 +19,4 @@ target = "thumbv8m.main-none-eabihf" # Cortex-M33
[env]
DEFMT_LOG = "trace"
+EMBASSY_EXECUTOR_TASK_ARENA_SIZE = "16384"
diff --git a/examples/rt685s-evk/src/bin/i2c-loopback-largebuf.rs b/examples/rt685s-evk/src/bin/i2c-loopback-largebuf.rs
new file mode 100644
index 00000000..7877fde5
--- /dev/null
+++ b/examples/rt685s-evk/src/bin/i2c-loopback-largebuf.rs
@@ -0,0 +1,127 @@
+#![no_std]
+#![no_main]
+
+extern crate embassy_imxrt_examples;
+
+use defmt::info;
+use embassy_executor::Spawner;
+use embassy_imxrt::i2c::master::{I2cMaster, Speed};
+use embassy_imxrt::i2c::slave::{Address, Command, I2cSlave, Response};
+use embassy_imxrt::i2c::{self, Async, MAX_I2C_CHUNK_SIZE};
+use embassy_imxrt::{bind_interrupts, peripherals};
+use embedded_hal_async::i2c::I2c;
+
+const ADDR: u8 = 0x20;
+const BUFLEN: usize = 2500;
+const SLAVE_ADDR: Option
= Address::new(ADDR);
+
+bind_interrupts!(struct Irqs {
+ FLEXCOMM2 => i2c::InterruptHandler;
+ FLEXCOMM4 => i2c::InterruptHandler;
+});
+
+/// Generate a buffer with increment numbers in each segment
+fn generate_buffer() -> [u8; SIZE] {
+ let mut buf = [0xAA; SIZE];
+ for (i, e) in buf.iter_mut().enumerate() {
+ *e = ((i / MAX_I2C_CHUNK_SIZE) as u8) + 1;
+ }
+ buf
+}
+
+#[embassy_executor::task]
+async fn slave_service(mut slave: I2cSlave<'static, Async>) {
+ // Buffer containing data read by the master
+ let t_buf: [u8; BUFLEN] = generate_buffer();
+
+ // Buffer that the master writes to
+ let mut r_buf = [0xAA; BUFLEN];
+ // Buffer to compare with written data
+ let expected_buf: [u8; BUFLEN] = generate_buffer();
+
+ let mut r_offset = 0;
+ let mut t_offset = 0;
+
+ loop {
+ match slave.listen().await.unwrap() {
+ Command::Probe => {
+ info!("Probe, nothing to do");
+ }
+ Command::Read => {
+ info!("Read");
+ loop {
+ let end = (t_offset + MAX_I2C_CHUNK_SIZE).min(t_buf.len());
+ let t_chunk = &t_buf[t_offset..end];
+ match slave.respond_to_read(t_chunk).await.unwrap() {
+ Response::Complete(n) => {
+ t_offset += n;
+ info!("Response complete read with {} bytes", n);
+ break;
+ }
+ Response::Pending(n) => {
+ t_offset += n;
+ info!("Response to read got {} bytes, more bytes to fill", n);
+ }
+ }
+ }
+ }
+ Command::Write => {
+ info!("Write");
+ loop {
+ let end = (r_offset + MAX_I2C_CHUNK_SIZE).min(r_buf.len());
+ let r_chunk = &mut r_buf[r_offset..end];
+ match slave.respond_to_write(r_chunk).await.unwrap() {
+ Response::Complete(n) => {
+ r_offset += n;
+ info!("Response complete write with {} bytes", n);
+
+ // Compare written data with expected data
+ assert!(r_buf[0..n] == expected_buf[0..n]);
+ break;
+ }
+ Response::Pending(n) => {
+ r_offset += n;
+ info!("Response to write got {} bytes, more bytes pending", n);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#[embassy_executor::task]
+async fn master_service(mut master: I2cMaster<'static, Async>) {
+ const ADDR: u8 = 0x20;
+
+ // Buffer containing data to write to slave
+ let w_buf: [u8; BUFLEN] = generate_buffer();
+
+ // Buffer to compare with read data
+ let expected_buf: [u8; BUFLEN] = generate_buffer();
+ // Buffer to store data read from slave
+ let mut r_buf = [0xAA; BUFLEN];
+
+ let w_end = w_buf.len();
+ info!("i2cm write {} bytes", w_end);
+ master.write(ADDR, &w_buf[0..w_end]).await.unwrap();
+
+ let r_end = r_buf.len();
+ info!("i2cm read {} bytes", r_end);
+ master.read(ADDR, &mut r_buf[0..r_end]).await.unwrap();
+
+ assert!(r_buf[0..r_end] == expected_buf[0..r_end]);
+}
+
+#[embassy_executor::main]
+async fn main(spawner: Spawner) {
+ info!("i2c loopback bigbuffer example");
+ let p = embassy_imxrt::init(Default::default());
+
+ let slave = I2cSlave::new_async(p.FLEXCOMM2, p.PIO0_18, p.PIO0_17, Irqs, SLAVE_ADDR.unwrap(), p.DMA0_CH4).unwrap();
+
+ let master = I2cMaster::new_async(p.FLEXCOMM4, p.PIO0_29, p.PIO0_30, Irqs, Speed::Standard, p.DMA0_CH9).unwrap();
+
+ spawner.must_spawn(master_service(master));
+ spawner.must_spawn(slave_service(slave));
+}
diff --git a/src/i2c/master.rs b/src/i2c/master.rs
index 3c811647..509299b0 100644
--- a/src/i2c/master.rs
+++ b/src/i2c/master.rs
@@ -9,7 +9,7 @@ use embassy_hal_internal::into_ref;
use super::{
Async, Blocking, Error, Info, Instance, InterruptHandler, MasterDma, Mode, Result, SclPin, SdaPin, TransferError,
- I2C_WAKERS, TEN_BIT_PREFIX,
+ I2C_WAKERS, MAX_I2C_CHUNK_SIZE, TEN_BIT_PREFIX,
};
use crate::interrupt::typelevel::Interrupt;
use crate::{dma, interrupt, Peripheral};
@@ -157,7 +157,7 @@ impl<'a> I2cMaster<'a, Blocking> {
i2cregs.mstdat().write(|w|
// SAFETY: only unsafe due to .bits usage
- unsafe { w.data().bits(address << 1 | u8::from(is_read)) });
+ unsafe { w.data().bits((address << 1) | u8::from(is_read)) });
i2cregs.mstctl().write(|w| w.mststart().set_bit());
@@ -393,7 +393,7 @@ impl<'a> I2cMaster<'a, Async> {
i2cregs.mstdat().write(|w|
// SAFETY: only unsafe due to .bits usage
- unsafe { w.data().bits(address << 1 | u8::from(is_read)) });
+ unsafe { w.data().bits((address << 1) | u8::from(is_read)) });
i2cregs.mstctl().write(|w| w.mststart().set_bit());
@@ -761,6 +761,30 @@ impl<'a> I2cMaster<'a, Async> {
)
.await
}
+
+ async fn write_chunks(&mut self, address: u16, write: &[u8]) -> Result<()> {
+ // write of 0 size is allowed according to i2c spec
+ if write.is_empty() {
+ self.write_no_stop(address, write).await?;
+ }
+
+ for chunk in write.chunks(MAX_I2C_CHUNK_SIZE) {
+ self.write_no_stop(address, chunk).await?;
+ }
+ Ok(())
+ }
+
+ async fn read_chunks(&mut self, address: u16, read: &mut [u8]) -> Result<()> {
+ // read of 0 size is not allowed according to i2c spec
+ if read.is_empty() {
+ return Err(TransferError::OtherBusError.into());
+ }
+
+ for chunk in read.chunks_mut(MAX_I2C_CHUNK_SIZE) {
+ self.read_no_stop(address, chunk).await?;
+ }
+ Ok(())
+ }
}
/// Error Types for I2C communication
@@ -832,19 +856,19 @@ impl> embedded_hal_1::i2c::I2c> embedded_hal_async::i2c::I2c for I2cMaster<'_, Async> {
async fn read(&mut self, address: A, read: &mut [u8]) -> Result<()> {
- self.read_no_stop(address.into(), read).await?;
+ self.read_chunks(address.into(), read).await?;
self.stop().await
}
async fn write(&mut self, address: A, write: &[u8]) -> Result<()> {
- self.write_no_stop(address.into(), write).await?;
+ self.write_chunks(address.into(), write).await?;
self.stop().await
}
async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<()> {
let address = address.into();
- self.write_no_stop(address, write).await?;
- self.read_no_stop(address, read).await?;
+ self.write_chunks(address, write).await?;
+ self.read_chunks(address, read).await?;
self.stop().await
}
diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs
index 6e0e46aa..c3f5228d 100644
--- a/src/i2c/mod.rs
+++ b/src/i2c/mod.rs
@@ -127,6 +127,9 @@ impl_instance!(0, 1, 2, 3, 4, 5, 6, 7);
const I2C_COUNT: usize = 8;
static I2C_WAKERS: [AtomicWaker; I2C_COUNT] = [const { AtomicWaker::new() }; I2C_COUNT];
+/// Maximum I2C chunk size for DMA transfers
+pub const MAX_I2C_CHUNK_SIZE: usize = 1024;
+
/// Ten bit addresses start with first byte 0b11110XXX
pub const TEN_BIT_PREFIX: u8 = 0b11110 << 3;
diff --git a/src/i2c/slave.rs b/src/i2c/slave.rs
index 6b0bfd07..08693249 100644
--- a/src/i2c/slave.rs
+++ b/src/i2c/slave.rs
@@ -8,7 +8,7 @@ use embassy_hal_internal::{into_ref, Peripheral};
use super::{
Async, Blocking, Info, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin, SlaveDma, TransferError,
- I2C_WAKERS, TEN_BIT_PREFIX,
+ I2C_WAKERS, MAX_I2C_CHUNK_SIZE, TEN_BIT_PREFIX,
};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::i2c0::stat::Slvstate;
@@ -502,6 +502,24 @@ impl I2cSlave<'_, Async> {
/// Respond to write command from master
pub async fn respond_to_write(&mut self, buf: &mut [u8]) -> Result {
+ let mut xfer_count = 0;
+ for chunk in buf.chunks_mut(MAX_I2C_CHUNK_SIZE) {
+ let result = self.respond_to_write_inner(chunk).await?;
+ match result {
+ Response::Complete(count) => {
+ xfer_count += count;
+ return Ok(Response::Complete(xfer_count));
+ }
+ Response::Pending(count) => {
+ xfer_count += count;
+ }
+ }
+ }
+ Ok(Response::Complete(xfer_count))
+ }
+
+ /// Function to handle the actual write transaction in chunks
+ pub async fn respond_to_write_inner(&mut self, buf: &mut [u8]) -> Result {
let i2c = self.info.regs;
let buf_len = buf.len();
@@ -579,8 +597,26 @@ impl I2cSlave<'_, Async> {
/// Respond to read command from master
/// User must provide enough data to complete the transaction or else
- /// we will get stuck in this function
+ /// we will get stuck in this function
pub async fn respond_to_read(&mut self, buf: &[u8]) -> Result {
+ let mut xfer_count = 0;
+ for chunk in buf.chunks(MAX_I2C_CHUNK_SIZE) {
+ let result = self.respond_to_read_inner(chunk).await?;
+ match result {
+ Response::Complete(count) => {
+ xfer_count += count;
+ return Ok(Response::Complete(xfer_count));
+ }
+ Response::Pending(count) => {
+ xfer_count += count;
+ }
+ }
+ }
+ Ok(Response::Complete(xfer_count))
+ }
+
+ /// Function to handle the actual read transaction in chunks
+ pub async fn respond_to_read_inner(&mut self, buf: &[u8]) -> Result {
let i2c = self.info.regs;
// Verify that we are ready for transmit