From 96cdf9c9e046300b76969c39ac950d512e0184ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Kr=C3=B3lczyk?= Date: Thu, 27 Jun 2024 21:22:16 +0200 Subject: [PATCH] rp/i2c: add address flexibility and example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous i2c examples are using either blocking Embassy API or e-h traits, this example uses Embassy pub API directly. Signed-off-by: Krzysztof Królczyk --- embassy-rp/src/i2c.rs | 38 +++++------ examples/rp/src/bin/i2c_async_embassy.rs | 85 ++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 22 deletions(-) create mode 100644 examples/rp/src/bin/i2c_async_embassy.rs diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 10ccca674..10d3c86b3 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -313,25 +313,29 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } /// Read from address into buffer using DMA. - pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(addr)?; + pub async fn read_async(&mut self, addr: impl Into, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(addr.into())?; self.read_async_internal(buffer, true, true).await } /// Write to address from buffer using DMA. - pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator) -> Result<(), Error> { - Self::setup(addr)?; + pub async fn write_async( + &mut self, + addr: impl Into, + bytes: impl IntoIterator, + ) -> Result<(), Error> { + Self::setup(addr.into())?; self.write_async_internal(bytes, true).await } /// Write to address from bytes and read from address into buffer using DMA. pub async fn write_read_async( &mut self, - addr: u16, + addr: impl Into, bytes: impl IntoIterator, buffer: &mut [u8], ) -> Result<(), Error> { - Self::setup(addr)?; + Self::setup(addr.into())?; self.write_async_internal(bytes, false).await?; self.read_async_internal(buffer, true, true).await } @@ -595,20 +599,20 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { // ========================= /// Read from address into buffer blocking caller until done. - pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, address: impl Into, read: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; self.read_blocking_internal(read, true, true) // Automatic Stop } /// Write to address from buffer blocking caller until done. - pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, address: impl Into, write: &[u8]) -> Result<(), Error> { Self::setup(address.into())?; self.write_blocking_internal(write, true) } /// Write to address from bytes and read from address into buffer blocking caller until done. - pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + pub fn blocking_write_read(&mut self, address: impl Into, write: &[u8], read: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; self.write_blocking_internal(write, false)?; self.read_blocking_internal(read, true, true) @@ -719,25 +723,15 @@ where T: Instance + 'd, { async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { - let addr: u16 = address.into(); - - Self::setup(addr)?; - self.read_async_internal(read, false, true).await + self.read_async(address, read).await } async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { - let addr: u16 = address.into(); - - Self::setup(addr)?; - self.write_async_internal(write.iter().copied(), true).await + self.write_async(address, write.iter().copied()).await } async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - let addr: u16 = address.into(); - - Self::setup(addr)?; - self.write_async_internal(write.iter().cloned(), false).await?; - self.read_async_internal(read, true, true).await + self.write_read_async(address, write.iter().copied(), read).await } async fn transaction( diff --git a/examples/rp/src/bin/i2c_async_embassy.rs b/examples/rp/src/bin/i2c_async_embassy.rs new file mode 100644 index 000000000..a65b71b9f --- /dev/null +++ b/examples/rp/src/bin/i2c_async_embassy.rs @@ -0,0 +1,85 @@ +//! This example shows how to communicate asynchronous using i2c with external chip. +//! +//! It's using embassy's functions directly instead of traits from embedded_hal_async::i2c::I2c. +//! While most of i2c devices are addressed using 7 bits, an extension allows 10 bits too. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_rp::i2c::InterruptHandler; +use {defmt_rtt as _, panic_probe as _}; + +// Our anonymous hypotetical temperature sensor could be: +// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C +// It requires no configuration or calibration, works with all i2c bus speeds, +// never stretches clock or does anything complicated. Replies with one u16. +// It requires only one write to take it out of suspend mode, and stays on. +// Often result would be just on 12 bits, but here we'll simplify it to 16. + +enum UncomplicatedSensorId { + A(UncomplicatedSensorU8), + B(UncomplicatedSensorU16), +} +enum UncomplicatedSensorU8 { + First = 0x48, +} +enum UncomplicatedSensorU16 { + Other = 0x0049, +} + +impl Into for UncomplicatedSensorU16 { + fn into(self) -> u16 { + self as u16 + } +} +impl Into for UncomplicatedSensorU8 { + fn into(self) -> u16 { + 0x48 + } +} +impl From for u16 { + fn from(t: UncomplicatedSensorId) -> Self { + match t { + UncomplicatedSensorId::A(x) => x.into(), + UncomplicatedSensorId::B(x) => x.into(), + } + } +} + +embassy_rp::bind_interrupts!(struct Irqs { + I2C1_IRQ => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_task_spawner: embassy_executor::Spawner) { + let p = embassy_rp::init(Default::default()); + let sda = p.PIN_14; + let scl = p.PIN_15; + let config = embassy_rp::i2c::Config::default(); + let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config); + + const WAKEYWAKEY: u16 = 0xBABE; + let mut result: [u8; 2] = [0, 0]; + // wait for sensors to initialize + embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await; + + let _res_1 = bus + .write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes()) + .await; + let _res_2 = bus + .write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes()) + .await; + + loop { + let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First); + let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other); + let sensors = [s1, s2]; + for sensor in sensors { + if bus.read_async(sensor, &mut result).await.is_ok() { + info!("Result {}", u16::from_be_bytes(result.into())); + } + } + embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await; + } +}