diff --git a/Cargo.toml b/Cargo.toml index d1ec0417..4a592bde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ license = "MIT OR Apache-2.0" [dependencies] -embedded-hal = { version = "0.2.4", features = ["unproven"] } +embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.4", features = ["unproven"] } +embedded-hal = "1" switch-hal = "0.4.0" nb = "0.1.1" riot-sys = "0.7.8" diff --git a/src/adc.rs b/src/adc.rs index df043957..d3567917 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -35,14 +35,14 @@ pub struct ADC { pub resolution: riot_sys::adc_res_t, } -impl embedded_hal::adc::Channel for ADCLine { +impl embedded_hal_0_2::adc::Channel for ADCLine { type ID = riot_sys::adc_t; fn channel() -> Self::ID { unimplemented!("See https://github.com/rust-embedded/embedded-hal/issues/110") } } -impl embedded_hal::adc::OneShot for ADC { +impl embedded_hal_0_2::adc::OneShot for ADC { type Error = Never; fn read(&mut self, pin: &mut ADCLine) -> nb::Result { diff --git a/src/gpio/impl_0_2.rs b/src/gpio/impl_0_2.rs new file mode 100644 index 00000000..0381aa35 --- /dev/null +++ b/src/gpio/impl_0_2.rs @@ -0,0 +1,73 @@ +use super::*; + +use embedded_hal_0_2::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; + +impl InputPin for InputGPIO { + type Error = Never; + + fn is_high(&self) -> Result { + Ok(unsafe { gpio_read(self.to_c()) } != 0) + } + + fn is_low(&self) -> Result { + Ok(unsafe { gpio_read(self.to_c()) } == 0) + } +} + +impl OutputPin for OutputGPIO { + type Error = Never; + + fn set_high(&mut self) -> Result<(), Never> { + unsafe { gpio_set(self.to_c()) }; + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Never> { + unsafe { gpio_clear(self.to_c()) }; + Ok(()) + } +} + +impl ToggleableOutputPin for OutputGPIO { + type Error = Never; + + fn toggle(&mut self) -> Result<(), Never> { + unsafe { gpio_toggle(self.to_c()) }; + Ok(()) + } +} + +impl InputPin for InOutGPIO { + type Error = Never; + + fn is_high(&self) -> Result { + Ok(unsafe { gpio_read(self.to_c()) } != 0) + } + + fn is_low(&self) -> Result { + Ok(unsafe { gpio_read(self.to_c()) } == 0) + } +} + +impl OutputPin for InOutGPIO { + type Error = Never; + + fn set_high(&mut self) -> Result<(), Never> { + unsafe { gpio_set(self.to_c()) }; + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Never> { + unsafe { gpio_clear(self.to_c()) }; + Ok(()) + } +} + +impl ToggleableOutputPin for InOutGPIO { + type Error = Never; + + fn toggle(&mut self) -> Result<(), Never> { + unsafe { gpio_toggle(self.to_c()) }; + Ok(()) + } +} diff --git a/src/gpio/impl_1.rs b/src/gpio/impl_1.rs new file mode 100644 index 00000000..793cab0a --- /dev/null +++ b/src/gpio/impl_1.rs @@ -0,0 +1,64 @@ +use super::*; + +use core::convert::Infallible; +use embedded_hal::digital::{ErrorType, InputPin, OutputPin, PinState}; + +impl ErrorType for InputGPIO { + type Error = Infallible; +} + +impl InputPin for InputGPIO { + fn is_high(&mut self) -> Result { + Ok(InputGPIO::is_high(self)) + } + + fn is_low(&mut self) -> Result { + Ok(InputGPIO::is_low(self)) + } +} + +impl ErrorType for OutputGPIO { + type Error = Infallible; +} + +impl OutputPin for OutputGPIO { + fn set_high(&mut self) -> Result<(), Infallible> { + Ok(OutputGPIO::set_high(self)) + } + + fn set_low(&mut self) -> Result<(), Infallible> { + Ok(OutputGPIO::set_low(self)) + } + + fn set_state(&mut self, state: PinState) -> Result<(), Infallible> { + Ok(OutputGPIO::set_state(self, state.into())) + } +} + +impl ErrorType for InOutGPIO { + type Error = Infallible; +} + +impl InputPin for InOutGPIO { + fn is_high(&mut self) -> Result { + Ok(InOutGPIO::is_high(self)) + } + + fn is_low(&mut self) -> Result { + Ok(InOutGPIO::is_low(self)) + } +} + +impl OutputPin for InOutGPIO { + fn set_high(&mut self) -> Result<(), Infallible> { + Ok(InOutGPIO::set_high(self)) + } + + fn set_low(&mut self) -> Result<(), Infallible> { + Ok(InOutGPIO::set_low(self)) + } + + fn set_state(&mut self, state: PinState) -> Result<(), Infallible> { + Ok(InOutGPIO::set_state(self, state.into())) + } +} diff --git a/src/gpio.rs b/src/gpio/mod.rs similarity index 78% rename from src/gpio.rs rename to src/gpio/mod.rs index ca418123..41bf5e92 100644 --- a/src/gpio.rs +++ b/src/gpio/mod.rs @@ -1,11 +1,13 @@ //! Access to [RIOT's GPIO pins](http://doc.riot-os.org/group__drivers__periph__gpio.html) //! //! The various configured GPIO types ([InputGPIO], [OutputGPIO], [InOutGPIO]) can be used through -//! the [embedded_hal::digital::v2] traits. +//! the [embedded_hal::digital::v2] traits. As recommended for infallible types, they also +//! provide identically named direct methods, which (for input pins) also work on shared reference. -use riot_sys::{gpio_clear, gpio_mode_t, gpio_read, gpio_set, gpio_t, gpio_toggle}; +mod impl_0_2; +mod impl_1; -use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; +use riot_sys::{gpio_clear, gpio_mode_t, gpio_read, gpio_set, gpio_t, gpio_toggle, gpio_write}; use crate::error::NegativeErrorExt; use crate::Never; @@ -142,28 +144,17 @@ impl OutputGPIO { pub fn deconfigured(self) -> GPIO { self.0 } -} - -impl OutputPin for OutputGPIO { - type Error = Never; - fn set_high(&mut self) -> Result<(), Never> { + pub fn set_high(&mut self) { unsafe { gpio_set(self.to_c()) }; - Ok(()) } - fn set_low(&mut self) -> Result<(), Never> { + pub fn set_low(&mut self) { unsafe { gpio_clear(self.to_c()) }; - Ok(()) } -} - -impl ToggleableOutputPin for OutputGPIO { - type Error = Never; - fn toggle(&mut self) -> Result<(), Never> { - unsafe { gpio_toggle(self.to_c()) }; - Ok(()) + pub fn set_state(&mut self, state: bool) { + unsafe { gpio_write(self.to_c(), state as _) }; } } @@ -180,17 +171,13 @@ impl InputGPIO { pub fn deconfigured(self) -> GPIO { self.0 } -} - -impl InputPin for InputGPIO { - type Error = Never; - fn is_high(&self) -> Result { - Ok(unsafe { gpio_read(self.to_c()) } != 0) + pub fn is_high(&self) -> bool { + unsafe { gpio_read(self.to_c()) != 0 } } - fn is_low(&self) -> Result { - Ok(unsafe { gpio_read(self.to_c()) } == 0) + pub fn is_low(&self) -> bool { + unsafe { gpio_read(self.to_c()) == 0 } } } @@ -207,39 +194,24 @@ impl InOutGPIO { pub fn deconfigured(self) -> GPIO { self.0 } -} - -impl InputPin for InOutGPIO { - type Error = Never; - fn is_high(&self) -> Result { - Ok(unsafe { gpio_read(self.to_c()) } != 0) + pub fn set_high(&mut self) { + unsafe { gpio_set(self.to_c()) }; } - fn is_low(&self) -> Result { - Ok(unsafe { gpio_read(self.to_c()) } == 0) + pub fn set_low(&mut self) { + unsafe { gpio_clear(self.to_c()) }; } -} - -impl OutputPin for InOutGPIO { - type Error = Never; - fn set_high(&mut self) -> Result<(), Never> { - unsafe { gpio_set(self.to_c()) }; - Ok(()) + pub fn set_state(&mut self, state: bool) { + unsafe { gpio_write(self.to_c(), state as _) }; } - fn set_low(&mut self) -> Result<(), Never> { - unsafe { gpio_clear(self.to_c()) }; - Ok(()) + pub fn is_high(&self) -> bool { + unsafe { gpio_read(self.to_c()) != 0 } } -} - -impl ToggleableOutputPin for InOutGPIO { - type Error = Never; - fn toggle(&mut self) -> Result<(), Never> { - unsafe { gpio_toggle(self.to_c()) }; - Ok(()) + pub fn is_low(&self) -> bool { + unsafe { gpio_read(self.to_c()) == 0 } } } diff --git a/src/i2c.rs b/src/i2c/impl_0_2.rs similarity index 77% rename from src/i2c.rs rename to src/i2c/impl_0_2.rs index a6fbf4d7..b3c0e98e 100644 --- a/src/i2c.rs +++ b/src/i2c/impl_0_2.rs @@ -1,28 +1,14 @@ -//! Controlling the I²C bus +//! Implementation of embedded-hal 0.2's I2C for [I2CDevice] +//! +//! As the implementation is on the [I2CDevice directly], all that is in this module is the +//! suitable [Error] type. -use embedded_hal::blocking; -use riot_sys::i2c_t; +use embedded_hal_0_2::blocking; -/// An I²C master backed by RIOT's [I2C implementation] -/// -/// [I2C implementation]: http://doc.riot-os.org/group__drivers__periph__i2c.html -/// -/// Actual transactions on this are performed through the [embedded_hal::blocking::i2c] traits -/// implemented by this. -#[derive(Debug)] -pub struct I2CDevice { - dev: i2c_t, -} +use super::*; -impl I2CDevice { - /// Create a new I2CDevice from a RIOT descriptor - /// - /// As all transactions on the bus are gated by acquire / release steps implied in the - /// individual reads or writes, multiple copies of the same device can safely coexist. - pub fn new(dev: i2c_t) -> Self { - I2CDevice { dev } - } -} +use riot_sys::libc; +use riot_sys::{i2c_acquire, i2c_read_bytes, i2c_release, i2c_write_bytes}; #[derive(Debug)] #[non_exhaustive] @@ -32,9 +18,6 @@ pub enum Error { ReadError(i32), } -use riot_sys::libc; -use riot_sys::{i2c_acquire, i2c_read_bytes, i2c_release, i2c_write_bytes}; - impl blocking::i2c::WriteRead for I2CDevice { type Error = Error; diff --git a/src/i2c/impl_1.rs b/src/i2c/impl_1.rs new file mode 100644 index 00000000..72a1dcd7 --- /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 new file mode 100644 index 00000000..7bd09683 --- /dev/null +++ b/src/i2c/mod.rs @@ -0,0 +1,32 @@ +//! Controlling the I²C bus + +pub mod impl_0_2; +pub mod impl_1; + +use riot_sys::i2c_t; + +/// An I²C master backed by RIOT's [I2C implementation] +/// +/// [I2C implementation]: http://doc.riot-os.org/group__drivers__periph__i2c.html +/// +/// Actual transactions on this are performed through the [mbedded_hal_0_2::blocking::i2c] traits +/// implemented by this. +#[derive(Debug)] +pub struct I2CDevice { + dev: i2c_t, +} + +impl I2CDevice { + /// Create a new I2CDevice from a RIOT descriptor + /// + /// As all transactions on the bus are gated by acquire / release steps implied in the + /// individual reads or writes, multiple copies of the same device can safely coexist. + pub fn new(dev: i2c_t) -> Self { + I2CDevice { dev } + } +} + +#[deprecated( + note = "This error type applies to embedded-hal 0.2 only, use it through the impl_0_2 module." +)] +pub use impl_0_2::Error; diff --git a/src/led.rs b/src/led.rs index 017ee1a5..4282ebb7 100644 --- a/src/led.rs +++ b/src/led.rs @@ -8,8 +8,9 @@ use crate::Never; /// /// LEDs are accessible safely; any not implemented on a board are silently ignored. /// -/// LEDs are wrapped into GPIOs for compatibility reasons; GPIO is interpreted such that "high" is -/// having the LED on, and "low" is off. +/// LEDs are wrapped into embedded-hal 0.2 GPIOs for compatibility reasons (but that integration is +/// discontinued with embedded-hal 1.0); GPIO is interpreted such that "high" is having the LED on, +/// and "low" is off. pub struct LED(()); impl LED { @@ -23,12 +24,12 @@ impl switch_hal::OutputSwitch for LED { type Error = Never; fn on(&mut self) -> Result<(), Self::Error> { - use embedded_hal::digital::v2::OutputPin; + use embedded_hal_0_2::digital::v2::OutputPin; self.set_high() } fn off(&mut self) -> Result<(), Self::Error> { - use embedded_hal::digital::v2::OutputPin; + use embedded_hal_0_2::digital::v2::OutputPin; self.set_low() } } @@ -37,11 +38,11 @@ impl switch_hal::ToggleableOutputSwitch for LED { type Error = Never; fn toggle(&mut self) -> Result<(), Self::Error> { - ::toggle(self) + ::toggle(self) } } -impl embedded_hal::digital::v2::OutputPin for LED { +impl embedded_hal_0_2::digital::v2::OutputPin for LED { type Error = Never; fn set_high(&mut self) -> Result<(), Never> { @@ -81,7 +82,7 @@ impl embedded_hal::digital::v2::OutputPin for LED { } } -impl embedded_hal::digital::v2::ToggleableOutputPin for LED { +impl embedded_hal_0_2::digital::v2::ToggleableOutputPin for LED { type Error = Never; fn toggle(&mut self) -> Result<(), Never> { diff --git a/src/microbit.rs b/src/microbit.rs index 4d10b34b..6d735381 100644 --- a/src/microbit.rs +++ b/src/microbit.rs @@ -14,7 +14,7 @@ use crate::Never; /// The 5x5 LED matrix of the micro:bit boards /// -/// Use the [embedded_hal] mechanisms to paint on them. +/// Use the [embedded_graphics] mechanisms to paint on them. pub struct LEDs { _private: (), } diff --git a/src/spi.rs b/src/spi/mod.rs similarity index 98% rename from src/spi.rs rename to src/spi/mod.rs index 84ffa48c..53c4888b 100644 --- a/src/spi.rs +++ b/src/spi/mod.rs @@ -1,5 +1,5 @@ use crate::Never; -use embedded_hal::blocking; +use embedded_hal_0_2::blocking; use riot_sys::{ spi_acquire, spi_clk_t, diff --git a/src/ztimer.rs b/src/ztimer/mod.rs similarity index 89% rename from src/ztimer.rs rename to src/ztimer/mod.rs index caf98c11..0be8b6ed 100644 --- a/src/ztimer.rs +++ b/src/ztimer/mod.rs @@ -197,18 +197,44 @@ impl Clock<1000000> { } } -impl embedded_hal::blocking::delay::DelayMs for Clock<1000> { +impl embedded_hal_0_2::blocking::delay::DelayMs for Clock<1000> { fn delay_ms(&mut self, ms: u32) { self.sleep_ticks(ms.into()); } } -impl embedded_hal::blocking::delay::DelayUs for Clock<1000000> { +impl embedded_hal_0_2::blocking::delay::DelayUs for Clock<1000000> { fn delay_us(&mut self, us: u32) { self.sleep_ticks(us); } } +impl embedded_hal::delay::DelayNs for Clock { + // FIXME: Provide delay_us and delay_ms, at least for the clocks where those fit, to avoid the + // loops where the provided function wakes up every 4.3s + + #[inline(always)] + fn delay_ns(&mut self, ns: u32) { + if F > NANOS_PER_SEC { + // On really fast ZTimers, we may need to loop (but let's implement this when anyone + // ever implements a faster-than-nanosecond timer) + todo!("Test for whether this needs to loop") + } else { + // No need to loop, but we need to take care not to overflow -- and we can't + // pre-calculate (F / NANOS_PER_SEC) because that's rounded to 0 + + // FIXME: There has to be a more efficient way -- for now we're relying on inlining and + // hope that constant propagation takes care of things + + // FIXME: This does not round correctly (it should round up the ticks), but ztimer + // ticks have some uncertainty on their own anyway. + + let ticks = (ns as u64) * (F as u64) / (NANOS_PER_SEC as u64); + self.sleep_ticks(ticks as u32); + } + } +} + /// The error type of fallible conversions to ticks. /// /// Overflow is the only ever indicated error type; lack of accuracy in the timer does not diff --git a/tests/gpio/Cargo.toml b/tests/gpio/Cargo.toml index ffd6e853..e23a3b41 100644 --- a/tests/gpio/Cargo.toml +++ b/tests/gpio/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.4" +embedded-hal = "1" diff --git a/tests/gpio/src/lib.rs b/tests/gpio/src/lib.rs index b1d13f04..1d006eb2 100644 --- a/tests/gpio/src/lib.rs +++ b/tests/gpio/src/lib.rs @@ -4,16 +4,28 @@ use riot_wrappers::gpio::{InputMode, OutputMode, GPIO}; use riot_wrappers::println; use riot_wrappers::riot_main; -use embedded_hal::digital::v2::{InputPin, OutputPin, PinState}; - riot_main!(main); +fn replicate_through_hal( + p_in: &mut impl embedded_hal::digital::InputPin, + p_out: &mut impl embedded_hal::digital::OutputPin, +) { + let value = p_in.is_high().unwrap(); + println!( + "Read GPIO value {}, writing it to the out port (through embedded-hal)", + value + ); + p_out.set_state(value.into()).unwrap(); +} + fn main() { let (out_port, out_pin, in_port, in_pin, in_mode) = match riot_wrappers::BOARD { // Won't work -- currently, native GPIO don't do anything (but let's not panic already) "native" => (0, 0, 0, 1, InputMode::In), // 0.17 is LED1, 0.13 is button 1 "nrf52dk" => (0, 17, 0, 13, InputMode::InPullUp), + // 0.20 is the MIC enable line (which is the only easily controlled LED), 0.14 is BTN_A + "microbit-v2" => (0, 20, 0, 14, InputMode::In), // Better safe than drive pins that were not supposed to be driven _ => panic!("For this board, no GPIO pins were deemed safe to reconfigure."), @@ -22,14 +34,17 @@ fn main() { .expect("Out pin does not exist") .configure_as_output(OutputMode::Out) .expect("Out pin could not be configured"); - let p_in = GPIO::from_port_and_pin(in_port, in_pin) + let mut p_in = GPIO::from_port_and_pin(in_port, in_pin) .expect("In pin does not exist") .configure_as_input(in_mode) .expect("In pin could not be configured"); loop { - let value = p_in.is_high().unwrap(); + let value = p_in.is_high(); println!("Read GPIO value {}, writing it to the out port", value); - p_out.set_state(if value { PinState::High } else { PinState::Low }); + p_out.set_state(value); + + // Does the same, but using the embedded-hal trats + replicate_through_hal(&mut p_in, &mut p_out); } } diff --git a/tests/i2c/Cargo.toml b/tests/i2c/Cargo.toml new file mode 100644 index 00000000..baf2d865 --- /dev/null +++ b/tests/i2c/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "riot-wrappers-test-i2c" +version = "0.1.0" +authors = ["Christian Amsüss "] +edition = "2021" +publish = false + +[lib] +crate-type = ["staticlib"] + +[profile.release] +panic = "abort" + +[dependencies] +riot-wrappers = { version = "*", features = [ "set_panic_handler", "panic_handler_format" ] } +riot-sys = "*" +embedded-hal = "1" diff --git a/tests/i2c/Makefile b/tests/i2c/Makefile new file mode 100644 index 00000000..422f406a --- /dev/null +++ b/tests/i2c/Makefile @@ -0,0 +1,8 @@ +# name of your application +APPLICATION = riot-wrappers-test-i2c +APPLICATION_RUST_MODULE = riot_wrappers_test_i2c +BASELIBS += $(APPLICATION_RUST_MODULE).module +FEATURES_REQUIRED += rust_target +FEATURES_REQUIRED += periph_i2c + +include $(RIOTBASE)/Makefile.include diff --git a/tests/i2c/src/lib.rs b/tests/i2c/src/lib.rs new file mode 100644 index 00000000..cb539039 --- /dev/null +++ b/tests/i2c/src/lib.rs @@ -0,0 +1,25 @@ +//! This is a primitive I2C scanner, and should thus report *something* interesting if any I2C +//! device is connected. (And reading should be safe on any device / bus). +#![no_std] + +use embedded_hal::i2c::I2c; + +use riot_wrappers::println; +use riot_wrappers::riot_main; + +riot_main!(main); + +fn main() { + let mut i2c = riot_wrappers::i2c::I2CDevice::new(0); // FIXME from_number? + + let mut buf = [0]; + + loop { + for i in 0..=127 { + match i2c.read(i, &mut buf) { + Ok(()) => println!("From {i}, read bytes: {:?}", &buf), + Err(e) => println!("From {i}, error {e:?}"), + } + } + } +}