From 084903613f8c22aeb6830cf7e47a46e356fa688a Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 23 Jan 2024 21:50:57 +0100 Subject: [PATCH] i2c: Add embedded-hal 1 implementation --- src/i2c/impl_1.rs | 143 +++++++++++++++++++++++++++++++++++++++++++ src/i2c/mod.rs | 1 + tests/i2c/Cargo.toml | 2 +- tests/i2c/src/lib.rs | 2 +- 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/i2c/impl_1.rs diff --git a/src/i2c/impl_1.rs b/src/i2c/impl_1.rs new file mode 100644 index 0000000..72a1dcd --- /dev/null +++ b/src/i2c/impl_1.rs @@ -0,0 +1,143 @@ +use embedded_hal::i2c; + +use riot_sys::{i2c_acquire, i2c_read_bytes, i2c_release, i2c_write_bytes}; + +use crate::error::{NegativeErrorExt, NumericError}; + +use super::*; + +const I2C_NOSTOP: u8 = riot_sys::i2c_flags_t_I2C_NOSTOP; +const I2C_NOSTART: u8 = riot_sys::i2c_flags_t_I2C_NOSTART; + +#[derive(Debug)] +pub struct Error(NumericError); + +impl From for Error { + fn from(err: NumericError) -> Self { + Self(err) + } +} + +impl i2c::Error for Error { + fn kind(&self) -> i2c::ErrorKind { + match -self.0.number() as _ { + // The list documented with all the RIOT I2C functions + riot_sys::EIO => i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data), + riot_sys::ENXIO => i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address), + riot_sys::ETIMEDOUT => i2c::ErrorKind::Other, + riot_sys::EINVAL => i2c::ErrorKind::Other, + riot_sys::EOPNOTSUPP => i2c::ErrorKind::Other, // We should avoid this at type level, + // but can't because RIOT is not telling + // us at setup time. + riot_sys::EAGAIN => i2c::ErrorKind::ArbitrationLoss, + _ => i2c::ErrorKind::Other, + } + } +} + +impl i2c::ErrorType for I2CDevice { + type Error = Error; +} + +fn with_acquire(dev: &mut I2CDevice, f: impl FnOnce(&mut I2CDevice) -> R) -> R { + unsafe { i2c_acquire(dev.dev) }; + let result = f(dev); + unsafe { i2c_release(dev.dev) }; + result +} + +impl i2c::I2c for I2CDevice { + fn transaction( + &mut self, + address: i2c::SevenBitAddress, + operations: &mut [i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + with_acquire(self, |dev| { + #[derive(PartialEq)] + enum LastOperation { + Initial, + Read, + Write, + } + use LastOperation::*; + + impl LastOperation { + fn from(op: &i2c::Operation) -> Self { + match op { + i2c::Operation::Read(_) => Read, + i2c::Operation::Write(_) => Write, + } + } + } + + let mut last_operation = Initial; + let last_index = operations.len() - 1; + + for (i, op) in operations.iter_mut().enumerate() { + let this_operation = LastOperation::from(op); + let is_last = i == last_index; + + let maybe_nostop = if is_last { 0 } else { I2C_NOSTOP }; + + // The regular read and write functions in RIOT automatically issue a repeated + // start condition when called after a I2C_NOSTOP operation. + if last_operation != this_operation { + match op { + i2c::Operation::Read(slice) => { + slice[0] = 0xff; + let result = (unsafe { + i2c_read_bytes( + dev.dev, + address as u16, + slice.as_mut_ptr() as _, + slice.len() as _, + maybe_nostop, + ) + }) + .negative_to_error()?; + result + } + i2c::Operation::Write(slice) => (unsafe { + i2c_write_bytes( + dev.dev, + address as u16, + slice.as_ptr() as _, + slice.len() as _, + maybe_nostop, + ) + }) + .negative_to_error()?, + }; + } else { + // No "repeated start", no address, just a different scatter-gather slice + match op { + i2c::Operation::Read(slice) => (unsafe { + slice[0] = 0xff; + i2c_read_bytes( + dev.dev, + 0, + slice.as_mut_ptr() as _, + slice.len() as _, + I2C_NOSTART | maybe_nostop, + ) + }) + .negative_to_error()?, + i2c::Operation::Write(slice) => (unsafe { + i2c_write_bytes( + dev.dev, + 0, + slice.as_ptr() as _, + slice.len() as _, + I2C_NOSTART | maybe_nostop, + ) + }) + .negative_to_error()?, + }; + } + + last_operation = this_operation; + } + Ok(()) + }) + } +} diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs index bbb0799..7bd0968 100644 --- a/src/i2c/mod.rs +++ b/src/i2c/mod.rs @@ -1,6 +1,7 @@ //! Controlling the I²C bus pub mod impl_0_2; +pub mod impl_1; use riot_sys::i2c_t; diff --git a/tests/i2c/Cargo.toml b/tests/i2c/Cargo.toml index 95cddc1..baf2d86 100644 --- a/tests/i2c/Cargo.toml +++ b/tests/i2c/Cargo.toml @@ -14,4 +14,4 @@ panic = "abort" [dependencies] riot-wrappers = { version = "*", features = [ "set_panic_handler", "panic_handler_format" ] } riot-sys = "*" -embedded-hal = "0.2" +embedded-hal = "1" diff --git a/tests/i2c/src/lib.rs b/tests/i2c/src/lib.rs index 57d9f02..cb53903 100644 --- a/tests/i2c/src/lib.rs +++ b/tests/i2c/src/lib.rs @@ -2,7 +2,7 @@ //! device is connected. (And reading should be safe on any device / bus). #![no_std] -use embedded_hal::blocking::i2c::Read; +use embedded_hal::i2c::I2c; use riot_wrappers::println; use riot_wrappers::riot_main;