Skip to content

Commit 066efcb

Browse files
committed
rp Implement timeout for I2C operations
1 parent fe95e33 commit 066efcb

File tree

2 files changed

+112
-21
lines changed

2 files changed

+112
-21
lines changed

embassy-rp/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Add PIO SPI
1212
- Add PIO I2S input
1313
- Add PIO onewire parasite power strong pullup
14+
- Add I2C Timeout.
1415

1516
## 0.8.0 - 2025-08-26
1617

embassy-rp/src/i2c.rs

Lines changed: 111 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use core::task::Poll;
77

88
use embassy_hal_internal::{Peri, PeripheralType};
99
use embassy_sync::waitqueue::AtomicWaker;
10+
use embassy_time::{Duration, Instant};
1011
use pac::i2c;
1112

1213
use crate::gpio::AnyPin;
@@ -44,6 +45,8 @@ pub enum Error {
4445
/// Target i2c address is reserved
4546
#[deprecated = "embassy_rp no longer prevents accesses to reserved addresses."]
4647
AddressReserved(u16),
48+
/// Operation timed out
49+
Timeout,
4750
}
4851

4952
/// I2C Config error
@@ -74,13 +77,16 @@ pub struct Config {
7477
/// Using external pullup resistors is recommended for I2C. If you do
7578
/// have external pullups you should not enable this.
7679
pub scl_pullup: bool,
80+
/// Timeout for I2C operations.
81+
pub timeout: Duration,
7782
}
7883
impl Default for Config {
7984
fn default() -> Self {
8085
Self {
8186
frequency: 100_000,
8287
sda_pullup: true,
8388
scl_pullup: true,
89+
timeout: Duration::from_millis(1000),
8490
}
8591
}
8692
}
@@ -91,6 +97,7 @@ pub const FIFO_SIZE: u8 = 16;
9197
#[derive(Debug)]
9298
pub struct I2c<'d, T: Instance, M: Mode> {
9399
phantom: PhantomData<(&'d mut T, M)>,
100+
timeout: Duration,
94101
}
95102

96103
impl<'d, T: Instance> I2c<'d, T, Blocking> {
@@ -105,6 +112,35 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> {
105112
}
106113
}
107114

115+
impl<'d, T: Instance> I2c<'d, T, Blocking> {
116+
fn timeout(&self) -> Timeout {
117+
Timeout::new(self.timeout)
118+
}
119+
}
120+
121+
#[derive(Copy, Clone)]
122+
struct Timeout {
123+
deadline: Instant,
124+
}
125+
126+
impl Timeout {
127+
#[inline]
128+
fn new(duration: Duration) -> Self {
129+
Self {
130+
deadline: Instant::now() + duration,
131+
}
132+
}
133+
134+
#[inline]
135+
fn check(self) -> Result<(), Error> {
136+
if Instant::now() > self.deadline {
137+
return Err(Error::Timeout);
138+
}
139+
140+
Ok(())
141+
}
142+
}
143+
108144
impl<'d, T: Instance> I2c<'d, T, Async> {
109145
/// Create a new driver instance in async mode.
110146
pub fn new_async(
@@ -330,7 +366,10 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
330366
/// Read from address into buffer asynchronously.
331367
pub async fn read_async(&mut self, addr: impl Into<u16>, buffer: &mut [u8]) -> Result<(), Error> {
332368
Self::setup(addr.into())?;
333-
self.read_async_internal(buffer, true, true).await
369+
match embassy_time::with_timeout(self.timeout, self.read_async_internal(buffer, true, true)).await {
370+
Ok(result) => result,
371+
Err(_) => Err(Error::Timeout),
372+
}
334373
}
335374

336375
/// Write to address from buffer asynchronously.
@@ -340,7 +379,10 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
340379
bytes: impl IntoIterator<Item = u8>,
341380
) -> Result<(), Error> {
342381
Self::setup(addr.into())?;
343-
self.write_async_internal(bytes, true).await
382+
match embassy_time::with_timeout(self.timeout, self.write_async_internal(bytes, true)).await {
383+
Ok(result) => result,
384+
Err(_) => Err(Error::Timeout),
385+
}
344386
}
345387

346388
/// Write to address from bytes and read from address into buffer asynchronously.
@@ -351,8 +393,18 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
351393
buffer: &mut [u8],
352394
) -> Result<(), Error> {
353395
Self::setup(addr.into())?;
354-
self.write_async_internal(bytes, false).await?;
355-
self.read_async_internal(buffer, true, true).await
396+
397+
// Apply timeout to the write operation
398+
match embassy_time::with_timeout(self.timeout, self.write_async_internal(bytes, false)).await {
399+
Ok(result) => result?,
400+
Err(_) => return Err(Error::Timeout),
401+
};
402+
403+
// Apply timeout to the read operation
404+
match embassy_time::with_timeout(self.timeout, self.read_async_internal(buffer, true, true)).await {
405+
Ok(result) => result,
406+
Err(_) => Err(Error::Timeout),
407+
}
356408
}
357409
}
358410

@@ -399,7 +451,10 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
399451
set_up_i2c_pin(&scl, config.scl_pullup);
400452
set_up_i2c_pin(&sda, config.sda_pullup);
401453

402-
let mut me = Self { phantom: PhantomData };
454+
let mut me = Self {
455+
phantom: PhantomData,
456+
timeout: config.timeout,
457+
};
403458

404459
if let Err(e) = me.set_config_inner(&config) {
405460
panic!("Error configuring i2c: {:?}", e);
@@ -529,6 +584,8 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
529584
}
530585

531586
fn read_blocking_internal(&mut self, read: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
587+
let timeout = Timeout::new(self.timeout);
588+
532589
if read.is_empty() {
533590
return Err(Error::InvalidReadBufferLength);
534591
}
@@ -540,7 +597,9 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
540597
let last = i == lastindex;
541598

542599
// wait until there is space in the FIFO to write the next byte
543-
while Self::tx_fifo_full() {}
600+
while Self::tx_fifo_full() {
601+
timeout.check()?;
602+
}
544603

545604
p.ic_data_cmd().write(|w| {
546605
w.set_restart(restart && first);
@@ -550,6 +609,7 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
550609
});
551610

552611
while Self::rx_fifo_len() == 0 {
612+
timeout.check()?;
553613
self.read_and_clear_abort_reason()?;
554614
}
555615

@@ -560,6 +620,8 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
560620
}
561621

562622
fn write_blocking_internal(&mut self, write: &[u8], send_stop: bool) -> Result<(), Error> {
623+
let timeout = Timeout::new(self.timeout);
624+
563625
if write.is_empty() {
564626
return Err(Error::InvalidWriteBufferLength);
565627
}
@@ -578,15 +640,19 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
578640
// internal shift register has completed. For this to function
579641
// correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The
580642
// TX_EMPTY_CTRL flag was set in i2c_init.
581-
while !p.ic_raw_intr_stat().read().tx_empty() {}
643+
while !p.ic_raw_intr_stat().read().tx_empty() {
644+
timeout.check()?;
645+
}
582646

583647
let abort_reason = self.read_and_clear_abort_reason();
584648

585649
if abort_reason.is_err() || (send_stop && last) {
586650
// If the transaction was aborted or if it completed
587651
// successfully wait until the STOP condition has occurred.
588652

589-
while !p.ic_raw_intr_stat().read().stop_det() {}
653+
while !p.ic_raw_intr_stat().read().stop_det() {
654+
timeout.check()?;
655+
}
590656

591657
p.ic_clr_stop_det().read().clr_stop_det();
592658
}
@@ -658,14 +724,20 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for
658724
address: u8,
659725
operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>],
660726
) -> Result<(), Self::Error> {
727+
let timeout = Timeout::new(self.timeout);
728+
661729
Self::setup(address.into())?;
662730
for i in 0..operations.len() {
663731
let last = i == operations.len() - 1;
664732
match &mut operations[i] {
665733
embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
734+
timeout.check()?;
666735
self.read_blocking_internal(buf, false, last)?
667736
}
668-
embedded_hal_02::blocking::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
737+
embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
738+
timeout.check()?;
739+
self.write_blocking_internal(buf, last)?
740+
}
669741
}
670742
}
671743
Ok(())
@@ -686,6 +758,7 @@ impl embedded_hal_1::i2c::Error for Error {
686758
Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other,
687759
#[allow(deprecated)]
688760
Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other,
761+
Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,
689762
}
690763
}
691764
}
@@ -712,12 +785,20 @@ impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
712785
address: u8,
713786
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
714787
) -> Result<(), Self::Error> {
788+
let timeout = Timeout::new(self.timeout);
789+
715790
Self::setup(address.into())?;
716791
for i in 0..operations.len() {
717792
let last = i == operations.len() - 1;
718793
match &mut operations[i] {
719-
embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,
720-
embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
794+
embedded_hal_1::i2c::Operation::Read(buf) => {
795+
timeout.check()?;
796+
self.read_blocking_internal(buf, false, last)?
797+
}
798+
embedded_hal_1::i2c::Operation::Write(buf) => {
799+
timeout.check()?;
800+
self.write_blocking_internal(buf, last)?
801+
}
721802
}
722803
}
723804
Ok(())
@@ -753,21 +834,30 @@ where
753834
if !operations.is_empty() {
754835
Self::setup(addr)?;
755836
}
756-
let mut iterator = operations.iter_mut();
757837

758-
while let Some(op) = iterator.next() {
759-
let last = iterator.len() == 0;
838+
// Apply timeout to the whole transaction
839+
match embassy_time::with_timeout(self.timeout, async {
840+
let mut iterator = operations.iter_mut();
760841

761-
match op {
762-
Operation::Read(buffer) => {
763-
self.read_async_internal(buffer, false, last).await?;
764-
}
765-
Operation::Write(buffer) => {
766-
self.write_async_internal(buffer.iter().cloned(), last).await?;
842+
while let Some(op) = iterator.next() {
843+
let last = iterator.len() == 0;
844+
845+
match op {
846+
Operation::Read(buffer) => {
847+
self.read_async_internal(buffer, false, last).await?;
848+
}
849+
Operation::Write(buffer) => {
850+
self.write_async_internal(buffer.iter().cloned(), last).await?;
851+
}
767852
}
768853
}
854+
Ok(())
855+
})
856+
.await
857+
{
858+
Ok(result) => result,
859+
Err(_) => Err(Error::Timeout),
769860
}
770-
Ok(())
771861
}
772862
}
773863

0 commit comments

Comments
 (0)