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