Skip to content

Commit

Permalink
Add i2c master 10-bit address support (#305)
Browse files Browse the repository at this point in the history
Adding 10-bit addressing support for I2C master.
  • Loading branch information
jerrysxie authored Mar 3, 2025
2 parents ef14b92 + 4f73955 commit 5567436
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 60 deletions.
283 changes: 223 additions & 60 deletions src/i2c/master.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
I2C_WAKERS, TEN_BIT_PREFIX,
};
use crate::interrupt::typelevel::Interrupt;
use crate::{dma, interrupt, Peripheral};
Expand Down Expand Up @@ -133,7 +133,19 @@ impl<'a> I2cMaster<'a, Blocking> {
Ok(this)
}

fn start(&mut self, address: u8, is_read: bool) -> Result<()> {
fn start(&mut self, address: u16, is_read: bool) -> Result<()> {
// check if the address is 10-bit
let is_10bit = address > 0x7F;

// start with the correct address
if is_10bit {
self.start_10bit(address, is_read)
} else {
self.start_7bit(address as u8, is_read)
}
}

fn start_7bit(&mut self, address: u8, is_read: bool) -> Result<()> {
let i2cregs = self.info.regs;

self.poll_ready()?;
Expand Down Expand Up @@ -169,7 +181,64 @@ impl<'a> I2cMaster<'a, Blocking> {
self.check_for_bus_errors()
}

fn read_no_stop(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
fn start_10bit(&mut self, address: u16, is_read: bool) -> Result<()> {
// check if the address is 10-bit and within the valid range
if address > 0x3FF {
return Err(Error::UnsupportedConfiguration);
}
let i2cregs = self.info.regs;

self.poll_ready()?;

// cannot start if the the bus is already busy
if i2cregs.stat().read().mstpending().is_in_progress() {
return Err(TransferError::OtherBusError.into());
}

// The first byte of a 10-bit address is 11110XXX,
// where XXX are the 2 most significant bits of the 10-bit address
// followed by the read/write bit
let addr_high = TEN_BIT_PREFIX | (((address >> 8) as u8) << 1);
i2cregs.mstdat().write(|w|
// SAFETY: only unsafe due to .bits usage
unsafe { w.data().bits(addr_high) });
i2cregs.mstctl().write(|w| w.mststart().set_bit());

self.poll_ready()?;
self.check_for_bus_errors()?;

// Send the second part of the 10-bit address
let addr_low = (address & 0xFF) as u8;
i2cregs.mstdat().write(|w|
// SAFETY: only unsafe due to .bits usage
unsafe { w.data().bits(addr_low) });
i2cregs.mstctl().write(|w| w.mstcontinue().set_bit());

self.poll_ready()?;
self.check_for_bus_errors()?;

// If this is a read operation, send a repeated start with the read bit set
if is_read {
let addr_high_read = addr_high | 0b1;
i2cregs.mstdat().write(|w| unsafe { w.data().bits(addr_high_read) });
i2cregs.mstctl().write(|w| w.mststart().set_bit());

self.poll_ready()?;
self.check_for_bus_errors()?;
}

if is_read && !i2cregs.stat().read().mststate().is_receive_ready() {
return Err(TransferError::ReadFail.into());
}

if !is_read && !i2cregs.stat().read().mststate().is_transmit_ready() {
return Err(TransferError::WriteFail.into());
}

Ok(())
}

fn read_no_stop(&mut self, address: u16, read: &mut [u8]) -> Result<()> {
let i2cregs = self.info.regs;

// read of 0 size is not allowed according to i2c spec
Expand Down Expand Up @@ -202,7 +271,7 @@ impl<'a> I2cMaster<'a, Blocking> {
Ok(())
}

fn write_no_stop(&mut self, address: u8, write: &[u8]) -> Result<()> {
fn write_no_stop(&mut self, address: u16, write: &[u8]) -> Result<()> {
// Procedure from 24.3.1.1 pg 545
let i2cregs = self.info.regs;

Expand Down Expand Up @@ -270,7 +339,19 @@ impl<'a> I2cMaster<'a, Async> {
Ok(this)
}

async fn start(&mut self, address: u8, is_read: bool) -> Result<()> {
async fn start(&mut self, address: u16, is_read: bool) -> Result<()> {
// check if the address is 10-bit
let is_10bit = address > 0x7F;

// start with the correct address
if is_10bit {
self.start_10bit(address, is_read).await
} else {
self.start_7bit(address as u8, is_read).await
}
}

async fn start_7bit(&mut self, address: u8, is_read: bool) -> Result<()> {
let i2cregs = self.info.regs;

self.wait_on(
Expand Down Expand Up @@ -316,53 +397,90 @@ impl<'a> I2cMaster<'a, Async> {

i2cregs.mstctl().write(|w| w.mststart().set_bit());

let res = self
.wait_on(
|me| {
let stat = me.info.regs.stat().read();

if stat.mstpending().is_pending() {
if is_read && stat.mststate().is_receive_ready()
|| !is_read && stat.mststate().is_transmit_ready()
{
Poll::Ready(Ok::<(), Error>(()))
} else if stat.mststate().is_nack_address() {
Poll::Ready(Err(TransferError::AddressNack.into()))
} else if is_read && !stat.mststate().is_receive_ready() {
Poll::Ready(Err(TransferError::ReadFail.into()))
} else if !is_read && !stat.mststate().is_transmit_ready() {
Poll::Ready(Err(TransferError::WriteFail.into()))
} else {
Poll::<Result<()>>::Pending
}
} else if stat.mstarbloss().is_arbitration_loss() {
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
} else if stat.mstststperr().is_error() {
Poll::Ready(Err(TransferError::StartStopError.into()))
} else {
Poll::<Result<()>>::Pending
}
},
|me| {
me.info.regs.intenset().write(|w| {
w.mstpendingen()
.set_bit()
.mstarblossen()
.set_bit()
.mstststperren()
.set_bit()
});
},
)
.await;
let res = self.poll_for_ready(is_read).await;

// Defuse the sentinel if future is not dropped
on_drop.defuse();

res
}

async fn read_no_stop(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
async fn start_10bit(&mut self, address: u16, is_read: bool) -> Result<()> {
// check if the address is 10-bit and within the valid range
if address > 0x3FF {
return Err(Error::UnsupportedConfiguration);
}
let i2cregs = self.info.regs;

self.wait_on(
|me| {
let stat = me.info.regs.stat().read();

if stat.mstpending().is_pending() {
Poll::Ready(Ok::<(), Error>(()))
} else if stat.mstarbloss().is_arbitration_loss() {
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
} else if stat.mstststperr().is_error() {
Poll::Ready(Err(TransferError::StartStopError.into()))
} else {
Poll::Pending
}
},
|me| {
me.info.regs.intenset().write(|w| {
w.mstpendingen()
.set_bit()
.mstarblossen()
.set_bit()
.mstststperren()
.set_bit()
});
},
)
.await?;

// Sentinel to perform corrective action if future is dropped
let on_drop = OnDrop::new(|| {
// Disable and re-enable master mode to clear out stalled HW state
// if we failed to complete sending of the address
// In practice, this seems to be only way to recover. Engaging with
// NXP to see if there is better way to handle this.
i2cregs.cfg().write(|w| w.msten().disabled());
i2cregs.cfg().write(|w| w.msten().enabled());
});

// The first byte of a 10-bit address is 11110XXX,
// where XXX are the 2 most significant bits of the 10-bit address
// followed by the read/write bit
let addr_high = TEN_BIT_PREFIX | (((address >> 8) as u8) << 1);
i2cregs.mstdat().write(|w| unsafe { w.data().bits(addr_high) });
i2cregs.mstctl().write(|w| w.mststart().set_bit());

// 10-bit address mode requires the two address bytes to be sent as a write operation
self.poll_for_ready(false).await?;

// Send the second part of the 10-bit address
let addr_low = (address & 0xFF) as u8;
i2cregs.mstdat().write(|w| unsafe { w.data().bits(addr_low) });
i2cregs.mstctl().write(|w| w.mstcontinue().set_bit());

// 10-bit address mode requires the two address bytes to be sent as a write operation
self.poll_for_ready(false).await?;

// If this is a read operation, send a repeated start with the read bit set
if is_read {
let addr_high_read = addr_high | 0b1;
i2cregs.mstdat().write(|w| unsafe { w.data().bits(addr_high_read) });
i2cregs.mstctl().write(|w| w.mststart().set_bit());

self.poll_for_ready(is_read).await?;
}
// Defuse the sentinel if future is not dropped
on_drop.defuse();
Ok(())
}

async fn read_no_stop(&mut self, address: u16, read: &mut [u8]) -> Result<()> {
let i2cregs = self.info.regs;

// read of 0 size is not allowed according to i2c spec
Expand Down Expand Up @@ -458,7 +576,7 @@ impl<'a> I2cMaster<'a, Async> {
Ok(())
}

async fn write_no_stop(&mut self, address: u8, write: &[u8]) -> Result<()> {
async fn write_no_stop(&mut self, address: u16, write: &[u8]) -> Result<()> {
// Procedure from 24.3.1.1 pg 545
let i2cregs = self.info.regs;

Expand Down Expand Up @@ -602,6 +720,47 @@ impl<'a> I2cMaster<'a, Async> {
})
.await
}

/// During i2c start, poll for ready state and check for errors
async fn poll_for_ready(&mut self, is_read: bool) -> Result<()> {
self.wait_on(
|me| {
let stat = me.info.regs.stat().read();

if stat.mstpending().is_pending() {
if is_read && stat.mststate().is_receive_ready() || !is_read && stat.mststate().is_transmit_ready()
{
Poll::Ready(Ok::<(), Error>(()))
} else if stat.mststate().is_nack_address() {
Poll::Ready(Err(TransferError::AddressNack.into()))
} else if is_read && !stat.mststate().is_receive_ready() {
Poll::Ready(Err(TransferError::ReadFail.into()))
} else if !is_read && !stat.mststate().is_transmit_ready() {
Poll::Ready(Err(TransferError::WriteFail.into()))
} else {
Poll::<Result<()>>::Pending
}
} else if stat.mstarbloss().is_arbitration_loss() {
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
} else if stat.mstststperr().is_error() {
Poll::Ready(Err(TransferError::StartStopError.into()))
} else {
Poll::<Result<()>>::Pending
}
},
|me| {
me.info.regs.intenset().write(|w| {
w.mstpendingen()
.set_bit()
.mstarblossen()
.set_bit()
.mstststperren()
.set_bit()
});
},
)
.await
}
}

/// Error Types for I2C communication
Expand Down Expand Up @@ -630,25 +789,27 @@ impl<M: Mode> embedded_hal_1::i2c::ErrorType for I2cMaster<'_, M> {
}

// implement generic i2c interface for peripheral master type
impl embedded_hal_1::i2c::I2c for I2cMaster<'_, Blocking> {
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
self.read_no_stop(address, read)?;
impl<A: embedded_hal_1::i2c::AddressMode + Into<u16>> embedded_hal_1::i2c::I2c<A> for I2cMaster<'_, Blocking> {
fn read(&mut self, address: A, read: &mut [u8]) -> Result<()> {
self.read_no_stop(address.into(), read)?;
self.stop()
}

fn write(&mut self, address: u8, write: &[u8]) -> Result<()> {
self.write_no_stop(address, write)?;
fn write(&mut self, address: A, write: &[u8]) -> Result<()> {
self.write_no_stop(address.into(), write)?;
self.stop()
}

fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<()> {
let address = address.into();
self.write_no_stop(address, write)?;
self.read_no_stop(address, read)?;
self.stop()
}

fn transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
fn transaction(&mut self, address: A, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
let needs_stop = !operations.is_empty();
let address = address.into();

for op in operations {
match op {
Expand All @@ -669,25 +830,27 @@ impl embedded_hal_1::i2c::I2c for I2cMaster<'_, Blocking> {
}
}

impl embedded_hal_async::i2c::I2c<embedded_hal_async::i2c::SevenBitAddress> for I2cMaster<'_, Async> {
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
self.read_no_stop(address, read).await?;
impl<A: embedded_hal_1::i2c::AddressMode + Into<u16>> embedded_hal_async::i2c::I2c<A> for I2cMaster<'_, Async> {
async fn read(&mut self, address: A, read: &mut [u8]) -> Result<()> {
self.read_no_stop(address.into(), read).await?;
self.stop().await
}

async fn write(&mut self, address: u8, write: &[u8]) -> Result<()> {
self.write_no_stop(address, write).await?;
async fn write(&mut self, address: A, write: &[u8]) -> Result<()> {
self.write_no_stop(address.into(), write).await?;
self.stop().await
}

async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
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.stop().await
}

async fn transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
async fn transaction(&mut self, address: A, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
let needs_stop = !operations.is_empty();
let address = address.into();

for op in operations {
match op {
Expand Down
3 changes: 3 additions & 0 deletions src/i2c/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];

/// Ten bit addresses start with first byte 0b11110XXX
pub const TEN_BIT_PREFIX: u8 = 0b11110 << 3;

/// I2C interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
Expand Down

0 comments on commit 5567436

Please sign in to comment.