diff --git a/examples/rt685s-evk/src/bin/i2c-loopback-async-10bit.rs b/examples/rt685s-evk/src/bin/i2c-loopback-async-10bit.rs
new file mode 100644
index 00000000..7d495312
--- /dev/null
+++ b/examples/rt685s-evk/src/bin/i2c-loopback-async-10bit.rs
@@ -0,0 +1,145 @@
+#![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};
+use embassy_imxrt::{bind_interrupts, peripherals};
+use embedded_hal_async::i2c::I2c;
+
+const ADDR: u16 = 0x0123;
+const MASTER_BUFLEN: usize = 8;
+// slave buffer has to be 1 bigger than master buffer because master does not
+// handle end of read properly
+const SLAVE_BUFLEN: usize = MASTER_BUFLEN + 1;
+const SLAVE_ADDR: Option
= Address::new_10bit(ADDR);
+
+bind_interrupts!(struct Irqs {
+ FLEXCOMM2 => i2c::InterruptHandler;
+ FLEXCOMM4 => i2c::InterruptHandler;
+});
+
+/// Generate a buffer with increment numbers
+fn generate_buffer() -> [u8; SIZE] {
+ let mut buf = [0xAA; SIZE];
+ for (i, e) in buf.iter_mut().enumerate() {
+ *e = i as u8;
+ }
+ buf
+}
+
+#[embassy_executor::task]
+async fn slave_service(mut slave: I2cSlave<'static, Async>) {
+ loop {
+ // Buffer containing data read by the master
+ let t_buf: [u8; SLAVE_BUFLEN] = generate_buffer();
+
+ // Buffer that the master writes to
+ let mut r_buf = [0xAA; SLAVE_BUFLEN];
+ // Buffer to compare with written data
+ let expected_buf: [u8; SLAVE_BUFLEN] = generate_buffer();
+
+ match slave.listen().await.unwrap() {
+ Command::Probe => {
+ info!("Probe, nothing to do");
+ }
+ Command::Read => {
+ info!("Read");
+ loop {
+ match slave.respond_to_read(&t_buf).await.unwrap() {
+ Response::Complete(n) => {
+ info!("Response complete read with {} bytes", n);
+ break;
+ }
+ Response::Pending(n) => info!("Response to read got {} bytes, more bytes to fill", n),
+ }
+ }
+ }
+ Command::Write => {
+ info!("Write");
+ loop {
+ match slave.respond_to_write(&mut r_buf).await.unwrap() {
+ Response::Complete(n) => {
+ info!("Response complete write with {} bytes", n);
+ info!("i2cm write data: {:x}", r_buf[0..n]);
+
+ // Compare written data with expected data
+ // Ensures that the second byte of 10 bit address is handled properly
+ assert!(r_buf[0..n] == expected_buf[0..n]);
+ break;
+ }
+ Response::Pending(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: u16 = 0x0123;
+
+ // Buffer containing data to write to slave
+ let w_buf: [u8; MASTER_BUFLEN] = generate_buffer();
+
+ // Buffer to compare with read data
+ let expected_buf: [u8; SLAVE_BUFLEN] = generate_buffer();
+
+ let mut i: usize = 0;
+ loop {
+ // Reset read buffer to ensure fresh data
+ let mut r_buf = [0xAA; MASTER_BUFLEN];
+
+ if i < 10 {
+ let w_end = i % w_buf.len();
+ info!("i2cm write {} bytes", w_end);
+ master.write(ADDR, &w_buf[0..w_end]).await.unwrap();
+ } else if i < 20 {
+ let r_end = i % (r_buf.len() - 1) + 2;
+ info!("i2cm read {} bytes", r_end);
+ master.read(ADDR, &mut r_buf[0..r_end]).await.unwrap();
+ info!("i2cm read data: {:x}", r_buf[0..r_end]);
+
+ // Compare read data with expected data
+ // Ensures that the second byte of 10 bit address is handled properly
+ assert!(r_buf[0..r_end] == expected_buf[0..r_end]);
+ } else {
+ let tend = i % w_buf.len() + 1;
+ let r_end = i % (r_buf.len() - 1) + 2;
+ info!("i2cm write {} bytes, read {} bytes", tend, r_end);
+ master
+ .write_read(ADDR, &w_buf[0..tend], &mut r_buf[0..r_end])
+ .await
+ .unwrap();
+ info!("i2cm read data: {:x}", r_buf[0..r_end]);
+
+ // Compare read data with expected data
+ // Ensures that the second byte of 10 bit address is handled properly
+ assert!(r_buf[0..r_end] == expected_buf[0..r_end]);
+ }
+ i += 1;
+
+ if i == 30 {
+ info!("i2c 10 bit loopback test end");
+ break;
+ }
+ }
+}
+
+#[embassy_executor::main]
+async fn main(spawner: Spawner) {
+ info!("i2c loopback 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/slave.rs b/src/i2c/slave.rs
index d510faee..6b0bfd07 100644
--- a/src/i2c/slave.rs
+++ b/src/i2c/slave.rs
@@ -8,42 +8,100 @@ use embassy_hal_internal::{into_ref, Peripheral};
use super::{
Async, Blocking, Info, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin, SlaveDma, TransferError,
- I2C_WAKERS,
+ I2C_WAKERS, TEN_BIT_PREFIX,
};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::i2c0::stat::Slvstate;
use crate::{dma, interrupt};
+/// Address errors
+#[derive(Copy, Clone, Debug)]
+pub enum AddressError {
+ /// Invalid address conversion
+ InvalidConversion,
+}
+
/// I2C address type
#[derive(Copy, Clone, Debug)]
-pub struct Address(u8);
+pub enum Address {
+ /// 7-bit address
+ SevenBit(u8),
+ /// 10-bit address
+ TenBit(u16),
+}
impl Address {
- /// Construct an address type
+ /// Construct a 7-bit address type
#[must_use]
pub const fn new(addr: u8) -> Option {
match addr {
- 0x08..=0x77 => Some(Self(addr)),
+ 0x08..=0x77 => Some(Self::SevenBit(addr)),
+ _ => None,
+ }
+ }
+
+ /// Construct a 10-bit address type
+ #[must_use]
+ pub const fn new_10bit(addr: u16) -> Option {
+ match addr {
+ 0x080..=0x3FF => Some(Self::TenBit(addr)),
_ => None,
}
}
/// interpret address as a read command
#[must_use]
- pub fn read(&self) -> u8 {
- (self.0 << 1) | 1
+ pub fn read(&self) -> [u8; 2] {
+ match self {
+ Self::SevenBit(addr) => [(addr << 1) | 1, 0],
+ Self::TenBit(addr) => [(((addr >> 8) as u8) << 1) | TEN_BIT_PREFIX | 1, (addr & 0xFF) as u8],
+ }
}
/// interpret address as a write command
#[must_use]
- pub fn write(&self) -> u8 {
- self.0 << 1
+ pub fn write(&self) -> [u8; 2] {
+ match self {
+ Self::SevenBit(addr) => [addr << 1, 0],
+ Self::TenBit(addr) => [(((addr >> 8) as u8) << 1) | TEN_BIT_PREFIX, (addr & 0xFF) as u8],
+ }
+ }
+}
+
+impl TryFrom for u8 {
+ type Error = AddressError;
+
+ fn try_from(value: Address) -> core::result::Result {
+ match value {
+ Address::SevenBit(addr) => Ok(addr),
+ Address::TenBit(_) => Err(AddressError::InvalidConversion),
+ }
}
}
-impl From for u8 {
- fn from(value: Address) -> Self {
- value.0
+impl TryFrom for u16 {
+ type Error = AddressError;
+
+ fn try_from(value: Address) -> core::result::Result {
+ match value {
+ Address::SevenBit(addr) => Ok(addr as u16),
+ Address::TenBit(addr) => Ok(addr),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+struct TenBitAddressInfo {
+ first_byte: u8,
+ second_byte: u8,
+}
+
+impl TenBitAddressInfo {
+ fn new(address: u16) -> Self {
+ Self {
+ first_byte: (((address >> 8) as u8) << 1) | TEN_BIT_PREFIX,
+ second_byte: (address & 0xFF) as u8,
+ }
}
}
@@ -64,7 +122,7 @@ pub enum Response {
/// I2C transaction complete with this amount of bytes
Complete(usize),
- /// I2C transaction pending wutg this amount of bytes completed so far
+ /// I2C transaction pending with this amount of bytes completed so far
Pending(usize),
}
@@ -73,6 +131,7 @@ pub struct I2cSlave<'a, M: Mode> {
info: Info,
_phantom: PhantomData,
dma_ch: Option>,
+ ten_bit_info: Option,
}
impl<'a, M: Mode> I2cSlave<'a, M> {
@@ -95,6 +154,7 @@ impl<'a, M: Mode> I2cSlave<'a, M> {
// this check should be redundant with T::set_mode()? above
let info = T::info();
let i2c = info.regs;
+ let mut ten_bit_info = None;
// rates taken assuming SFRO:
//
@@ -111,11 +171,25 @@ impl<'a, M: Mode> I2cSlave<'a, M> {
// SAFETY: only unsafe due to .bits usage
unsafe { w.divval().bits(0) });
- // address 0 match = addr, per UM11147 24.3.2.1
- i2c.slvadr(0).modify(|_, w|
- // note: shift is omitted as performed via w.slvadr()
- // SAFETY: unsafe only required due to use of unnamed "bits" field
- unsafe {w.slvadr().bits(address.0)}.sadisable().enabled());
+ match address {
+ Address::SevenBit(addr) => {
+ // address 0 match = addr, per UM11147 24.3.2.1
+ i2c.slvadr(0).modify(|_, w|
+ // note: shift is omitted as performed via w.slvadr()
+ // SAFETY: unsafe only required due to use of unnamed "bits" field
+ unsafe{w.slvadr().bits(addr)}.sadisable().enabled());
+ }
+ Address::TenBit(addr) => {
+ // Save the 10 bit address to use later
+ ten_bit_info = Some(TenBitAddressInfo::new(addr));
+
+ // address 0 match = addr first byte, per UM11147 24.7.4
+ i2c.slvadr(0).modify(|_, w|
+ // note: byte needs to be adjusted for shift performed via w.slvadr()
+ // SAFETY: unsafe only required due to use of unnamed "bits" field
+ unsafe{w.slvadr().bits(ten_bit_info.unwrap().first_byte >> 1)}.sadisable().enabled());
+ }
+ }
// SLVEN = 1, per UM11147 24.3.2.1
i2c.cfg().write(|w| w.slven().enabled());
@@ -124,6 +198,7 @@ impl<'a, M: Mode> I2cSlave<'a, M> {
info,
_phantom: PhantomData,
dma_ch,
+ ten_bit_info,
})
}
}
@@ -202,6 +277,43 @@ impl I2cSlave<'_, Blocking> {
//Block until we know it is read or write
self.poll()?;
+ if let Some(ten_bit_address) = self.ten_bit_info {
+ // For 10 bit address, the first byte received is the second byte of the address
+ if i2c.slvdat().read().data().bits() == ten_bit_address.second_byte {
+ i2c.slvctl().write(|w| w.slvcontinue().continue_());
+ self.poll()?;
+ } else {
+ // If the second byte of the 10 bit address is not received, then nack the address.
+ i2c.slvctl().write(|w| w.slvnack().nack());
+ return Ok(Command::Probe);
+ }
+
+ // Check slave is still selected, master has not sent a stop
+ if i2c.stat().read().slvsel().is_selected() {
+ // Check for a restart
+ if i2c.stat().read().slvstate().is_slave_address() {
+ // Check if first byte of 10 bit address is received again with read bit set
+ if i2c.slvdat().read().data().bits() == ten_bit_address.first_byte | 1 {
+ i2c.slvctl().write(|w| w.slvcontinue().continue_());
+ self.poll()?;
+ } else {
+ // If the first byte of the 10 bit address is not received again, then nack the address.
+ i2c.slvctl().write(|w| w.slvnack().nack());
+ return Ok(Command::Probe);
+ }
+ // Check slave is ready for transmit
+ if !i2c.stat().read().slvstate().is_slave_transmit() {
+ return Err(TransferError::WriteFail.into());
+ }
+ } else {
+ // Check slave is ready to receive
+ if !i2c.stat().read().slvstate().is_slave_receive() {
+ return Err(TransferError::ReadFail.into());
+ }
+ }
+ }
+ }
+
// We are already deselected, so it must be an 0 byte write transaction
if i2c.stat().read().slvdesel().is_deselected() {
// Clear the deselected bit
@@ -336,6 +448,43 @@ impl I2cSlave<'_, Async> {
// Poll for HW to transitioning from addressed to receive/transmit
self.poll_sw_action().await;
+ if let Some(ten_bit_address) = self.ten_bit_info {
+ // For 10 bit address, the first byte received is the second byte of the address
+ if i2c.slvdat().read().data().bits() == ten_bit_address.second_byte {
+ i2c.slvctl().write(|w| w.slvcontinue().continue_());
+ self.poll_sw_action().await;
+ } else {
+ // If the second byte of the 10 bit address is not received, then nack the address.
+ i2c.slvctl().write(|w| w.slvnack().nack());
+ return Ok(Command::Probe);
+ }
+
+ // Check slave is still selected, master has not sent a stop
+ if i2c.stat().read().slvsel().is_selected() {
+ // Check for a restart
+ if i2c.stat().read().slvstate().is_slave_address() {
+ // Check if first byte of 10 bit address is received again with read bit set
+ if i2c.slvdat().read().data().bits() == ten_bit_address.first_byte | 1 {
+ i2c.slvctl().write(|w| w.slvcontinue().continue_());
+ self.poll_sw_action().await;
+ } else {
+ // If the first byte of the 10 bit address is not received again, then nack the address.
+ i2c.slvctl().write(|w| w.slvnack().nack());
+ return Ok(Command::Probe);
+ }
+ // Check slave is ready for transmit
+ if !i2c.stat().read().slvstate().is_slave_transmit() {
+ return Err(TransferError::WriteFail.into());
+ }
+ } else {
+ // Check slave is ready to receive
+ if !i2c.stat().read().slvstate().is_slave_receive() {
+ return Err(TransferError::ReadFail.into());
+ }
+ }
+ }
+ }
+
// We are deselected, so it must be an 0 byte write transaction
if i2c.stat().read().slvdesel().is_deselected() {
// Clear the deselected bit