Skip to content

Commit

Permalink
Async slave changes for supporting i2c 10 bit addressing
Browse files Browse the repository at this point in the history
  • Loading branch information
akshat2112 committed Feb 13, 2025
1 parent 2c3cc21 commit 2449425
Show file tree
Hide file tree
Showing 2 changed files with 251 additions and 16 deletions.
123 changes: 123 additions & 0 deletions examples/rt685s-evk/src/bin/i2c-loopback-async-10bit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#![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> = Address::new_10bit(ADDR);

bind_interrupts!(struct Irqs {
FLEXCOMM2 => i2c::InterruptHandler<peripherals::FLEXCOMM2>;
FLEXCOMM4 => i2c::InterruptHandler<peripherals::FLEXCOMM4>;
});

#[embassy_executor::task]
async fn slave_service(mut slave: I2cSlave<'static, Async>) {
loop {
let mut r_buf = [0xAA; SLAVE_BUFLEN];
let mut t_buf = [0xAA; SLAVE_BUFLEN];

// Initialize write buffer with increment numbers
for (i, e) in t_buf.iter_mut().enumerate() {
*e = i as u8;
}

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!("write data: {:x}", r_buf);
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;

let mut w_buf = [0xAA; MASTER_BUFLEN];
let mut r_buf = [0xAA; MASTER_BUFLEN];

// Initialize write buffer with increment numbers
for (i, e) in w_buf.iter_mut().enumerate() {
*e = i as u8;
}

let mut i: usize = 0;
loop {
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!("read data: {:x}", r_buf);
} 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!("read data: {:x}", r_buf);
}
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));
}
144 changes: 128 additions & 16 deletions src/i2c/slave.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,104 @@ 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<Self> {
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<Self> {
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 From<Address> for u8 {
fn from(value: Address) -> Self {
value.0
impl TryFrom<Address> for u8 {
type Error = AddressError;

fn try_from(value: Address) -> core::result::Result<Self, Self::Error> {
match value {
Address::SevenBit(addr) => Ok(addr),
Address::TenBit(_) => Err(AddressError::InvalidConversion),
}
}
}

impl TryFrom<Address> for u16 {
type Error = AddressError;

fn try_from(value: Address) -> core::result::Result<Self, Self::Error> {
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,
acked_write: bool,
acked_read: bool,
}

impl TenBitAddressInfo {
fn new(first_byte: u8, second_byte: u8) -> Self {
Self {
first_byte,
second_byte,
acked_write: false,
acked_read: false,
}
}
}

Expand Down Expand Up @@ -73,6 +135,7 @@ pub struct I2cSlave<'a, M: Mode> {
info: Info,
_phantom: PhantomData<M>,
dma_ch: Option<dma::channel::Channel<'a>>,
ten_bit_info: Option<TenBitAddressInfo>,
}

impl<'a, M: Mode> I2cSlave<'a, M> {
Expand All @@ -95,6 +158,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:
//
Expand All @@ -111,11 +175,33 @@ 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) => {
// 10 bit address have first byte between 0b1111_0000 and 0b1111_0110 as per spec
// using slvadr(0) and slvqual0 to check, per UM11147 24.6.15
i2c.slvqual0().write(|w| w.qualmode0().extend());
i2c.slvadr(0).modify(|_, w|
// SAFETY: unsafe only required due to use of unnamed "bits" field
unsafe{w.slvadr().bits(0b111_1000)}.sadisable().enabled());

i2c.slvqual0().write(|w|
// SAFETY: unsafe only required due to use of unnamed "bits" field
unsafe { w.slvqual0().bits(0b111_1011) });

// Save the 10 bit address to use later
ten_bit_info = Some(TenBitAddressInfo::new(
((addr >> 8) as u8) << 1 | TEN_BIT_PREFIX,
(addr & 0xFF) as u8,
));
}
}

// SLVEN = 1, per UM11147 24.3.2.1
i2c.cfg().write(|w| w.slven().enabled());
Expand All @@ -124,6 +210,7 @@ impl<'a, M: Mode> I2cSlave<'a, M> {
info,
_phantom: PhantomData,
dma_ch,
ten_bit_info,
})
}
}
Expand Down Expand Up @@ -336,6 +423,30 @@ impl I2cSlave<'_, Async> {
// Poll for HW to transitioning from addressed to receive/transmit
self.poll_sw_action().await;

let ten_bit_info = self.ten_bit_info.as_mut();

if let Some(info) = ten_bit_info {
if info.acked_write && !info.acked_read {
// We are addressed again, so this must be a restart
// Check if first byte of 10 bit address is received again with read bit set
if i2c.slvdat().read().data().bits() == info.first_byte | 1 {
i2c.slvctl().write(|w| w.slvcontinue().continue_());
info.acked_read = true;
}
}
// Compare the incoming data to the saved lower bits of 10 bit address
if !info.acked_write && !info.acked_read && i2c.slvdat().read().data().bits() == info.second_byte {
info.acked_write = true;
i2c.slvctl().write(|w| w.slvcontinue().continue_());
}

// Clear the acked bits
info.acked_write = false;
info.acked_read = false;

self.poll_sw_action().await;
}

// We are deselected, so it must be an 0 byte write transaction
if i2c.stat().read().slvdesel().is_deselected() {
// Clear the deselected bit
Expand All @@ -345,6 +456,7 @@ impl I2cSlave<'_, Async> {

let state = i2c.stat().read().slvstate().variant();
match state {
Some(Slvstate::SlaveAddress) => Ok(Command::Probe),
Some(Slvstate::SlaveReceive) => Ok(Command::Write),
Some(Slvstate::SlaveTransmit) => Ok(Command::Read),
_ => Err(TransferError::OtherBusError.into()),
Expand Down

0 comments on commit 2449425

Please sign in to comment.